OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "chrome/test/webdriver/webdriver_automation.h" | |
6 | |
7 #if defined(OS_WIN) | |
8 #include <windows.h> | |
9 #endif | |
10 | |
11 #include "base/base_paths.h" | |
12 #include "base/basictypes.h" | |
13 #include "base/callback.h" | |
14 #include "base/environment.h" | |
15 #include "base/file_util.h" | |
16 #include "base/files/file_path.h" | |
17 #include "base/json/json_writer.h" | |
18 #include "base/memory/ref_counted.h" | |
19 #include "base/path_service.h" | |
20 #include "base/strings/string_number_conversions.h" | |
21 #include "base/strings/string_split.h" | |
22 #include "base/strings/stringprintf.h" | |
23 #include "base/strings/utf_string_conversions.h" | |
24 #include "base/synchronization/waitable_event.h" | |
25 #include "base/values.h" | |
26 #include "chrome/common/automation_constants.h" | |
27 #include "chrome/common/automation_messages.h" | |
28 #include "chrome/common/chrome_constants.h" | |
29 #include "chrome/common/chrome_switches.h" | |
30 #include "chrome/common/url_constants.h" | |
31 #include "chrome/test/automation/automation_json_requests.h" | |
32 #include "chrome/test/automation/automation_proxy.h" | |
33 #include "chrome/test/automation/browser_proxy.h" | |
34 #include "chrome/test/automation/proxy_launcher.h" | |
35 #include "chrome/test/automation/tab_proxy.h" | |
36 #include "chrome/test/base/chrome_process_util.h" | |
37 #include "chrome/test/webdriver/frame_path.h" | |
38 #include "chrome/test/webdriver/webdriver_basic_types.h" | |
39 #include "chrome/test/webdriver/webdriver_error.h" | |
40 #include "chrome/test/webdriver/webdriver_util.h" | |
41 | |
42 #if defined(OS_WIN) | |
43 #include "base/win/registry.h" | |
44 #include "base/win/windows_version.h" | |
45 #endif | |
46 | |
47 namespace { | |
48 | |
49 // Iterates through each browser executable path, and checks if the path exists | |
50 // in any of the given locations. If found, returns true and sets |browser_exe|. | |
51 bool CheckForChromeExe(const std::vector<base::FilePath>& browser_exes, | |
52 const std::vector<base::FilePath>& locations, | |
53 base::FilePath* browser_exe) { | |
54 for (size_t i = 0; i < browser_exes.size(); ++i) { | |
55 for (size_t j = 0; j < locations.size(); ++j) { | |
56 base::FilePath path = locations[j].Append(browser_exes[i]); | |
57 if (base::PathExists(path)) { | |
58 *browser_exe = path; | |
59 return true; | |
60 } | |
61 } | |
62 } | |
63 return false; | |
64 } | |
65 | |
66 // Gets the path to the default Chrome executable. Returns true on success. | |
67 bool GetDefaultChromeExe(base::FilePath* browser_exe) { | |
68 // Instead of using chrome constants, we hardcode these constants here so | |
69 // that we can locate chrome or chromium regardless of the branding | |
70 // chromedriver is built with. It may be argued that then we need to keep | |
71 // these in sync with chrome constants. However, if chrome constants changes, | |
72 // we need to look for the previous and new versions to support some | |
73 // backwards compatibility. | |
74 #if defined(OS_WIN) | |
75 base::FilePath browser_exes_array[] = { | |
76 base::FilePath(L"chrome.exe") | |
77 }; | |
78 #elif defined(OS_MACOSX) | |
79 base::FilePath browser_exes_array[] = { | |
80 base::FilePath("Google Chrome.app/Contents/MacOS/Google Chrome"), | |
81 base::FilePath("Chromium.app/Contents/MacOS/Chromium") | |
82 }; | |
83 #elif defined(OS_LINUX) | |
84 base::FilePath browser_exes_array[] = { | |
85 base::FilePath("google-chrome"), | |
86 base::FilePath("chrome"), | |
87 base::FilePath("chromium"), | |
88 base::FilePath("chromium-browser") | |
89 }; | |
90 #endif | |
91 std::vector<base::FilePath> browser_exes( | |
92 browser_exes_array, browser_exes_array + arraysize(browser_exes_array)); | |
93 | |
94 // Step 1: Check the directory this module resides in. This is done | |
95 // before all else so that the tests will pickup the built chrome. | |
96 base::FilePath module_dir; | |
97 if (PathService::Get(base::DIR_MODULE, &module_dir)) { | |
98 for (size_t j = 0; j < browser_exes.size(); ++j) { | |
99 base::FilePath path = module_dir.Append(browser_exes[j]); | |
100 if (base::PathExists(path)) { | |
101 *browser_exe = path; | |
102 return true; | |
103 } | |
104 } | |
105 } | |
106 | |
107 // Step 2: Add all possible install locations, in order they should be | |
108 // searched. If a location can only hold a chromium install, add it to | |
109 // |chromium_locations|. Since on some platforms we cannot tell by the binary | |
110 // name whether it is chrome or chromium, we search these locations last. | |
111 // We attempt to run chrome before chromium, if any install can be found. | |
112 std::vector<base::FilePath> locations; | |
113 std::vector<base::FilePath> chromium_locations; | |
114 #if defined(OS_WIN) | |
115 // Add the App Paths registry key location. | |
116 const wchar_t kSubKey[] = | |
117 L"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\chrome.exe"; | |
118 base::win::RegKey key(HKEY_CURRENT_USER, kSubKey, KEY_READ); | |
119 std::wstring path; | |
120 if (key.ReadValue(L"path", &path) == ERROR_SUCCESS) | |
121 locations.push_back(base::FilePath(path)); | |
122 base::win::RegKey sys_key(HKEY_LOCAL_MACHINE, kSubKey, KEY_READ); | |
123 if (sys_key.ReadValue(L"path", &path) == ERROR_SUCCESS) | |
124 locations.push_back(base::FilePath(path)); | |
125 | |
126 // Add the user-level location for Chrome. | |
127 base::FilePath app_from_google(L"Google\\Chrome\\Application"); | |
128 base::FilePath app_from_chromium(L"Chromium\\Application"); | |
129 scoped_ptr<base::Environment> env(base::Environment::Create()); | |
130 std::string home_dir; | |
131 if (env->GetVar("userprofile", &home_dir)) { | |
132 base::FilePath default_location(UTF8ToWide(home_dir)); | |
133 if (base::win::GetVersion() < base::win::VERSION_VISTA) { | |
134 default_location = default_location.Append( | |
135 L"Local Settings\\Application Data"); | |
136 } else { | |
137 default_location = default_location.Append(L"AppData\\Local"); | |
138 } | |
139 locations.push_back(default_location.Append(app_from_google)); | |
140 chromium_locations.push_back(default_location.Append(app_from_chromium)); | |
141 } | |
142 | |
143 // Add the system-level location for Chrome. | |
144 std::string program_dir; | |
145 if (env->GetVar("ProgramFiles", &program_dir)) { | |
146 locations.push_back(base::FilePath(UTF8ToWide(program_dir)) | |
147 .Append(app_from_google)); | |
148 chromium_locations.push_back(base::FilePath(UTF8ToWide(program_dir)) | |
149 .Append(app_from_chromium)); | |
150 } | |
151 if (env->GetVar("ProgramFiles(x86)", &program_dir)) { | |
152 locations.push_back(base::FilePath(UTF8ToWide(program_dir)) | |
153 .Append(app_from_google)); | |
154 chromium_locations.push_back(base::FilePath(UTF8ToWide(program_dir)) | |
155 .Append(app_from_chromium)); | |
156 } | |
157 #elif defined(OS_MACOSX) | |
158 std::vector<base::FilePath> app_dirs; | |
159 webdriver::GetApplicationDirs(&app_dirs); | |
160 locations.insert(locations.end(), app_dirs.begin(), app_dirs.end()); | |
161 #elif defined(OS_LINUX) | |
162 locations.push_back(base::FilePath("/opt/google/chrome")); | |
163 locations.push_back(base::FilePath("/usr/local/bin")); | |
164 locations.push_back(base::FilePath("/usr/local/sbin")); | |
165 locations.push_back(base::FilePath("/usr/bin")); | |
166 locations.push_back(base::FilePath("/usr/sbin")); | |
167 locations.push_back(base::FilePath("/bin")); | |
168 locations.push_back(base::FilePath("/sbin")); | |
169 #endif | |
170 | |
171 // Add the current directory. | |
172 base::FilePath current_dir; | |
173 if (file_util::GetCurrentDirectory(¤t_dir)) | |
174 locations.push_back(current_dir); | |
175 | |
176 // Step 3: For each browser exe path, check each location to see if the | |
177 // browser is installed there. Check the chromium locations lastly. | |
178 return CheckForChromeExe(browser_exes, locations, browser_exe) || | |
179 CheckForChromeExe(browser_exes, chromium_locations, browser_exe); | |
180 } | |
181 | |
182 // Message that duplicates a given message but uses a different type. | |
183 class MessageWithAlternateType : public IPC::Message { | |
184 public: | |
185 MessageWithAlternateType(const IPC::Message& msg, int type) | |
186 : IPC::Message(msg) { | |
187 header()->type = type; | |
188 } | |
189 virtual ~MessageWithAlternateType() {} | |
190 }; | |
191 | |
192 // Filters incoming and outgoing messages on the IO thread. Translates messages | |
193 // from old Chrome versions to the new version. This is needed so that new | |
194 // ChromeDriver releases support the current stable and beta channels of Chrome. | |
195 // TODO(kkania): Delete this when Chrome v21 is stable. | |
196 class BackwardsCompatAutomationMessageFilter | |
197 : public IPC::ChannelProxy::MessageFilter, | |
198 public IPC::ChannelProxy::OutgoingMessageFilter { | |
199 public: | |
200 explicit BackwardsCompatAutomationMessageFilter(AutomationProxy* server); | |
201 | |
202 // Overriden from IPC::ChannelProxy::MessageFiler. | |
203 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; | |
204 | |
205 // Overriden from IPC::ChannelProxy::OutgoingMessageFiler. | |
206 virtual IPC::Message* Rewrite(IPC::Message* message) OVERRIDE; | |
207 | |
208 private: | |
209 virtual ~BackwardsCompatAutomationMessageFilter(); | |
210 | |
211 // The first version of Chrome using the new IPC automation message set, | |
212 // after r137672 changed most of the message IDs. | |
213 static const int kNewAutomationVersion = 1142; | |
214 | |
215 // The first version of Chrome using the new JSON interface which takes a | |
216 // browser index instead of a browser handle. | |
217 static const int kNewJSONInterfaceVersion = 1195; | |
218 | |
219 AutomationProxy* server_; | |
220 bool old_version_; | |
221 | |
222 DISALLOW_COPY_AND_ASSIGN(BackwardsCompatAutomationMessageFilter); | |
223 }; | |
224 | |
225 BackwardsCompatAutomationMessageFilter::BackwardsCompatAutomationMessageFilter( | |
226 AutomationProxy* server) | |
227 : server_(server), old_version_(false) { | |
228 } | |
229 | |
230 bool BackwardsCompatAutomationMessageFilter::OnMessageReceived( | |
231 const IPC::Message& message) { | |
232 const uint32 kOldHelloType = 44, | |
233 kOldInitialLoadsCompleteType = 47, | |
234 kOldInitialNewTabUILoadCompleteType = 267; | |
235 if (message.type() == kOldHelloType) { | |
236 old_version_ = true; | |
237 std::string server_version; | |
238 PickleIterator iter(message); | |
239 CHECK(message.ReadString(&iter, &server_version)); | |
240 server_->SignalAppLaunch(server_version); | |
241 return true; | |
242 } | |
243 if (!old_version_) | |
244 return false; | |
245 | |
246 switch (message.type()) { | |
247 case kOldInitialLoadsCompleteType: | |
248 server_->SignalInitialLoads(); | |
249 break; | |
250 case kOldInitialNewTabUILoadCompleteType: | |
251 server_->SignalNewTabUITab(-1); | |
252 break; | |
253 default: | |
254 return false; | |
255 } | |
256 return true; | |
257 } | |
258 | |
259 IPC::Message* BackwardsCompatAutomationMessageFilter::Rewrite( | |
260 IPC::Message* message) { | |
261 int build_no = -1; | |
262 std::string version = server_->server_version(); | |
263 std::vector<std::string> version_parts; | |
264 base::SplitString(version, '.', &version_parts); | |
265 CHECK(version_parts.size() == 4 && | |
266 base::StringToInt(version_parts[2], &build_no)) | |
267 << "Can't rewrite message (type: " << message->type() | |
268 << ") because unknown server (version: " << version << ")"; | |
269 CHECK_EQ(static_cast<uint32>(AutomationMsg_SendJSONRequest::ID), | |
270 message->type()); | |
271 int type = AutomationMsg_SendJSONRequest::ID; | |
272 // These old message types are determined by inspecting the line number | |
273 // of the SendJSONRequest message in older versions of | |
274 // automation_messages_internal.h. | |
275 if (build_no < kNewAutomationVersion) | |
276 type = 1301; | |
277 else if (build_no < kNewJSONInterfaceVersion) | |
278 type = 863; | |
279 IPC::Message* new_message = new MessageWithAlternateType(*message, type); | |
280 delete message; | |
281 return new_message; | |
282 } | |
283 | |
284 BackwardsCompatAutomationMessageFilter:: | |
285 ~BackwardsCompatAutomationMessageFilter() { | |
286 } | |
287 | |
288 void AddBackwardsCompatFilter(AutomationProxy* proxy) { | |
289 // The filter is ref-counted in AddFilter. | |
290 BackwardsCompatAutomationMessageFilter* filter = | |
291 new BackwardsCompatAutomationMessageFilter(proxy); | |
292 proxy->channel()->AddFilter(filter); | |
293 proxy->channel()->set_outgoing_message_filter(filter); | |
294 } | |
295 | |
296 class WebDriverAnonymousProxyLauncher : public AnonymousProxyLauncher { | |
297 public: | |
298 WebDriverAnonymousProxyLauncher() | |
299 : AnonymousProxyLauncher(false) {} | |
300 virtual ~WebDriverAnonymousProxyLauncher() {} | |
301 | |
302 virtual AutomationProxy* CreateAutomationProxy( | |
303 base::TimeDelta execution_timeout) OVERRIDE { | |
304 AutomationProxy* proxy = | |
305 AnonymousProxyLauncher::CreateAutomationProxy(execution_timeout); | |
306 AddBackwardsCompatFilter(proxy); | |
307 return proxy; | |
308 } | |
309 }; | |
310 | |
311 class WebDriverNamedProxyLauncher : public NamedProxyLauncher { | |
312 public: | |
313 WebDriverNamedProxyLauncher(const std::string& channel_id, | |
314 bool launch_browser) | |
315 : NamedProxyLauncher(channel_id, launch_browser, false) {} | |
316 virtual ~WebDriverNamedProxyLauncher() {} | |
317 | |
318 virtual AutomationProxy* CreateAutomationProxy( | |
319 base::TimeDelta execution_timeout) OVERRIDE { | |
320 AutomationProxy* proxy = | |
321 NamedProxyLauncher::CreateAutomationProxy(execution_timeout); | |
322 // We can only add the filter here if the browser has not already been | |
323 // started. Otherwise the filter is not guaranteed to receive the | |
324 // first message. The only occasion where we don't launch the browser is | |
325 // in PyAuto, in which case the backwards compat filter isn't needed | |
326 // anyways because ChromeDriver and Chrome are at the same version there. | |
327 if (launch_browser_) | |
328 AddBackwardsCompatFilter(proxy); | |
329 return proxy; | |
330 } | |
331 }; | |
332 | |
333 } // namespace | |
334 | |
335 namespace webdriver { | |
336 | |
337 Automation::BrowserOptions::BrowserOptions() | |
338 : command(CommandLine::NO_PROGRAM), | |
339 detach_process(false), | |
340 ignore_certificate_errors(false) {} | |
341 | |
342 Automation::BrowserOptions::~BrowserOptions() {} | |
343 | |
344 Automation::Automation(const Logger& logger) | |
345 : logger_(logger), | |
346 build_no_(0) {} | |
347 | |
348 Automation::~Automation() {} | |
349 | |
350 void Automation::Init( | |
351 const BrowserOptions& options, | |
352 int* build_no, | |
353 Error** error) { | |
354 // Prepare Chrome's command line. | |
355 CommandLine command(CommandLine::NO_PROGRAM); | |
356 if (CommandLine::ForCurrentProcess()->HasSwitch("no-sandbox")) { | |
357 command.AppendSwitch(switches::kNoSandbox); | |
358 } | |
359 | |
360 const char* excludable_switches[] = { | |
361 switches::kDisableHangMonitor, | |
362 switches::kDisablePromptOnRepost, | |
363 switches::kDomAutomationController, | |
364 switches::kFullMemoryCrashReport, | |
365 switches::kNoDefaultBrowserCheck, | |
366 switches::kNoFirstRun, | |
367 switches::kDisableBackgroundNetworking, | |
368 switches::kDisableSync, | |
369 switches::kDisableTranslate, | |
370 switches::kDisableWebResources, | |
371 switches::kSbDisableAutoUpdate, | |
372 switches::kSbDisableDownloadProtection, | |
373 switches::kDisableClientSidePhishingDetection, | |
374 switches::kDisableComponentUpdate, | |
375 switches::kDisableDefaultApps | |
376 }; | |
377 std::vector<std::string> switches(excludable_switches, | |
378 excludable_switches + arraysize(excludable_switches)); | |
379 for (size_t i = 0; i < switches.size(); ++i) { | |
380 const std::string& switch_name = switches[i]; | |
381 if (options.exclude_switches.find(switch_name) == | |
382 options.exclude_switches.end()) { | |
383 command.AppendSwitch(switch_name); | |
384 } | |
385 } | |
386 | |
387 command.AppendSwitch(switches::kEnableLogging); | |
388 command.AppendSwitchASCII(switches::kLoggingLevel, "1"); | |
389 | |
390 #if defined(OS_LINUX) && !defined(OS_CHROMEOS) | |
391 command.AppendSwitchASCII(switches::kPasswordStore, "basic"); | |
392 #endif | |
393 #if defined(OS_MACOSX) | |
394 command.AppendSwitch(switches::kUseMockKeychain); | |
395 #endif | |
396 if (options.detach_process) | |
397 command.AppendSwitch(switches::kAutomationReinitializeOnChannelError); | |
398 if (options.ignore_certificate_errors) | |
399 command.AppendSwitch(switches::kIgnoreCertificateErrors); | |
400 if (options.user_data_dir.empty()) | |
401 command.AppendArg(content::kAboutBlankURL); | |
402 | |
403 command.AppendArguments(options.command, true /* include_program */); | |
404 | |
405 // Find the Chrome binary. | |
406 if (command.GetProgram().empty()) { | |
407 base::FilePath browser_exe; | |
408 if (!GetDefaultChromeExe(&browser_exe)) { | |
409 *error = new Error(kUnknownError, "Could not find default Chrome binary"); | |
410 return; | |
411 } | |
412 command.SetProgram(browser_exe); | |
413 } | |
414 if (!base::PathExists(command.GetProgram())) { | |
415 std::string message = base::StringPrintf( | |
416 "Could not find Chrome binary at: %" PRFilePath, | |
417 command.GetProgram().value().c_str()); | |
418 *error = new Error(kUnknownError, message); | |
419 return; | |
420 } | |
421 std::string chrome_details = base::StringPrintf( | |
422 "Using Chrome binary at: %" PRFilePath, | |
423 command.GetProgram().value().c_str()); | |
424 | |
425 // Create the ProxyLauncher and launch Chrome. | |
426 // In Chrome 13/14, the only way to detach the browser process is to use a | |
427 // named proxy launcher. | |
428 // TODO(kkania): Remove this when Chrome 15 is stable. | |
429 std::string channel_id = options.channel_id; | |
430 bool launch_browser = false; | |
431 if (options.detach_process) { | |
432 launch_browser = true; | |
433 if (!channel_id.empty()) { | |
434 *error = new Error( | |
435 kUnknownError, "Cannot detach an already running browser process"); | |
436 return; | |
437 } | |
438 #if defined(OS_WIN) | |
439 channel_id = "chromedriver" + GenerateRandomID(); | |
440 #elif defined(OS_POSIX) | |
441 base::FilePath temp_file; | |
442 if (!file_util::CreateTemporaryFile(&temp_file) || | |
443 !base::DeleteFile(temp_file, false /* recursive */)) { | |
444 *error = new Error(kUnknownError, "Could not create temporary filename"); | |
445 return; | |
446 } | |
447 channel_id = temp_file.value(); | |
448 #endif | |
449 } | |
450 if (channel_id.empty()) { | |
451 std::string command_line_str; | |
452 #if defined(OS_WIN) | |
453 command_line_str = WideToUTF8(command.GetCommandLineString()); | |
454 #elif defined(OS_POSIX) | |
455 command_line_str = command.GetCommandLineString(); | |
456 #endif | |
457 logger_.Log(kInfoLogLevel, "Launching chrome: " + command_line_str); | |
458 launcher_.reset(new WebDriverAnonymousProxyLauncher()); | |
459 } else { | |
460 logger_.Log(kInfoLogLevel, "Using named testing interface"); | |
461 launcher_.reset(new WebDriverNamedProxyLauncher( | |
462 channel_id, launch_browser)); | |
463 } | |
464 ProxyLauncher::LaunchState launch_props = { | |
465 false, // clear_profile | |
466 options.user_data_dir, // template_user_data | |
467 base::Closure(), | |
468 command, | |
469 true, // include_testing_id | |
470 true // show_window | |
471 }; | |
472 if (!launcher_->InitializeConnection(launch_props, true)) { | |
473 logger_.Log(kSevereLogLevel, "Failed to initialize connection"); | |
474 *error = new Error( | |
475 kUnknownError, | |
476 "Unable to either launch or connect to Chrome. Please check that " | |
477 "ChromeDriver is up-to-date. " + chrome_details); | |
478 return; | |
479 } | |
480 | |
481 launcher_->automation()->set_action_timeout( | |
482 base::TimeDelta::FromMilliseconds(base::kNoTimeout)); | |
483 logger_.Log(kInfoLogLevel, "Connected to Chrome successfully. Version: " + | |
484 automation()->server_version()); | |
485 | |
486 *error = DetermineBuildNumber(); | |
487 if (*error) | |
488 return; | |
489 *build_no = build_no_; | |
490 | |
491 // Check the version of Chrome is compatible with this ChromeDriver. | |
492 chrome_details += ", version (" + automation()->server_version() + ")"; | |
493 int version = 0; | |
494 automation::Error auto_error; | |
495 if (!SendGetChromeDriverAutomationVersion( | |
496 automation(), &version, &auto_error)) { | |
497 *error = Error::FromAutomationError(auto_error); | |
498 return; | |
499 } | |
500 if (version > automation::kChromeDriverAutomationVersion) { | |
501 *error = new Error( | |
502 kUnknownError, | |
503 "ChromeDriver is not compatible with this version of Chrome. " + | |
504 chrome_details); | |
505 return; | |
506 } | |
507 } | |
508 | |
509 void Automation::Terminate() { | |
510 if (launcher_.get() && launcher_->process() != base::kNullProcessHandle) { | |
511 #if defined(OS_MACOSX) | |
512 // There's currently no way to shutdown gracefully with mac chrome. | |
513 // An alert could be open or (open before shutdown is started) and stop | |
514 // the whole process. Close any current dialog, try to kill gently, and | |
515 // if all else fails, kill it hard. | |
516 Error* error = NULL; | |
517 AcceptOrDismissAppModalDialog(true /* accept */, &error); | |
518 scoped_ptr<Error> scoped_error(error); | |
519 | |
520 kill(launcher_->process(), SIGTERM); | |
521 #else | |
522 automation()->Disconnect(); | |
523 #endif | |
524 int exit_code = -1; | |
525 if (!launcher_->WaitForBrowserProcessToQuit( | |
526 base::TimeDelta::FromSeconds(10), &exit_code)) { | |
527 logger_.Log(kWarningLogLevel, "Chrome still running, terminating..."); | |
528 ChromeProcessList process_pids(GetRunningChromeProcesses( | |
529 launcher_->process_id())); | |
530 TerminateAllChromeProcesses(process_pids); | |
531 } | |
532 base::CloseProcessHandle(launcher_->process()); | |
533 logger_.Log(kInfoLogLevel, "Chrome shutdown"); | |
534 } | |
535 } | |
536 | |
537 void Automation::ExecuteScript(const WebViewId& view_id, | |
538 const FramePath& frame_path, | |
539 const std::string& script, | |
540 std::string* result, | |
541 Error** error) { | |
542 WebViewLocator view_locator; | |
543 *error = ConvertViewIdToLocator(view_id, &view_locator); | |
544 if (*error) | |
545 return; | |
546 | |
547 automation::Error auto_error; | |
548 scoped_ptr<Value> value; | |
549 if (!SendExecuteJavascriptJSONRequest(automation(), view_locator, | |
550 frame_path.value(), script, | |
551 &value, &auto_error)) { | |
552 *error = Error::FromAutomationError(auto_error); | |
553 return; | |
554 } | |
555 if (!value->GetAsString(result)) | |
556 *error = new Error(kUnknownError, "Execute script did not return string"); | |
557 } | |
558 | |
559 void Automation::MouseMoveDeprecated( | |
560 const WebViewId& view_id, | |
561 const Point& p, | |
562 Error** error) { | |
563 WebViewLocator view_locator; | |
564 *error = ConvertViewIdToLocator(view_id, &view_locator); | |
565 if (*error) | |
566 return; | |
567 | |
568 automation::Error auto_error; | |
569 if (!SendMouseMoveJSONRequestDeprecated( | |
570 automation(), view_locator, p.rounded_x(), p.rounded_y(), | |
571 &auto_error)) { | |
572 *error = Error::FromAutomationError(auto_error); | |
573 } | |
574 } | |
575 | |
576 void Automation::MouseClickDeprecated( | |
577 const WebViewId& view_id, | |
578 const Point& p, | |
579 automation::MouseButton button, | |
580 Error** error) { | |
581 WebViewLocator view_locator; | |
582 *error = ConvertViewIdToLocator(view_id, &view_locator); | |
583 if (*error) | |
584 return; | |
585 | |
586 automation::Error auto_error; | |
587 if (!SendMouseClickJSONRequestDeprecated( | |
588 automation(), view_locator, button, p.rounded_x(), | |
589 p.rounded_y(), &auto_error)) { | |
590 *error = Error::FromAutomationError(auto_error); | |
591 } | |
592 } | |
593 | |
594 void Automation::MouseDragDeprecated( | |
595 const WebViewId& view_id, | |
596 const Point& start, | |
597 const Point& end, | |
598 Error** error) { | |
599 WebViewLocator view_locator; | |
600 *error = ConvertViewIdToLocator(view_id, &view_locator); | |
601 if (*error) | |
602 return; | |
603 | |
604 automation::Error auto_error; | |
605 if (!SendMouseDragJSONRequestDeprecated( | |
606 automation(), view_locator, start.rounded_x(), start.rounded_y(), | |
607 end.rounded_x(), end.rounded_y(), &auto_error)) { | |
608 *error = Error::FromAutomationError(auto_error); | |
609 } | |
610 } | |
611 | |
612 void Automation::MouseButtonUpDeprecated( | |
613 const WebViewId& view_id, | |
614 const Point& p, | |
615 Error** error) { | |
616 *error = CheckAdvancedInteractionsSupported(); | |
617 if (*error) | |
618 return; | |
619 | |
620 WebViewLocator view_locator; | |
621 *error = ConvertViewIdToLocator(view_id, &view_locator); | |
622 if (*error) | |
623 return; | |
624 | |
625 automation::Error auto_error; | |
626 if (!SendMouseButtonUpJSONRequestDeprecated( | |
627 automation(), view_locator, p.rounded_x(), p.rounded_y(), | |
628 &auto_error)) { | |
629 *error = Error::FromAutomationError(auto_error); | |
630 } | |
631 } | |
632 | |
633 void Automation::MouseButtonDownDeprecated( | |
634 const WebViewId& view_id, | |
635 const Point& p, | |
636 Error** error) { | |
637 *error = CheckAdvancedInteractionsSupported(); | |
638 if (*error) | |
639 return; | |
640 | |
641 WebViewLocator view_locator; | |
642 *error = ConvertViewIdToLocator(view_id, &view_locator); | |
643 if (*error) | |
644 return; | |
645 | |
646 automation::Error auto_error; | |
647 if (!SendMouseButtonDownJSONRequestDeprecated( | |
648 automation(), view_locator, p.rounded_x(), p.rounded_y(), | |
649 &auto_error)) { | |
650 *error = Error::FromAutomationError(auto_error); | |
651 } | |
652 } | |
653 | |
654 void Automation::MouseDoubleClickDeprecated( | |
655 const WebViewId& view_id, | |
656 const Point& p, | |
657 Error** error) { | |
658 *error = CheckAdvancedInteractionsSupported(); | |
659 if (*error) | |
660 return; | |
661 | |
662 WebViewLocator view_locator; | |
663 *error = ConvertViewIdToLocator(view_id, &view_locator); | |
664 if (*error) | |
665 return; | |
666 | |
667 automation::Error auto_error; | |
668 if (!SendMouseDoubleClickJSONRequestDeprecated( | |
669 automation(), view_locator, p.rounded_x(), p.rounded_y(), | |
670 &auto_error)) { | |
671 *error = Error::FromAutomationError(auto_error); | |
672 } | |
673 } | |
674 | |
675 void Automation::DragAndDropFilePaths( | |
676 const WebViewId& view_id, const Point& location, | |
677 const std::vector<base::FilePath::StringType>& paths, Error** error) { | |
678 WebViewLocator view_locator; | |
679 *error = ConvertViewIdToLocator(view_id, &view_locator); | |
680 if (*error) { | |
681 return; | |
682 } | |
683 | |
684 automation::Error auto_error; | |
685 if (!SendDragAndDropFilePathsJSONRequest( | |
686 automation(), view_locator, location.rounded_x(), | |
687 location.rounded_y(), paths, &auto_error)) { | |
688 *error = Error::FromAutomationError(auto_error); | |
689 } | |
690 } | |
691 | |
692 void Automation::SendWebKeyEvent(const WebViewId& view_id, | |
693 const WebKeyEvent& key_event, | |
694 Error** error) { | |
695 WebViewLocator view_locator; | |
696 *error = ConvertViewIdToLocator(view_id, &view_locator); | |
697 if (*error) | |
698 return; | |
699 | |
700 automation::Error auto_error; | |
701 if (!SendWebKeyEventJSONRequest( | |
702 automation(), view_locator, key_event, &auto_error)) { | |
703 *error = Error::FromAutomationError(auto_error); | |
704 } | |
705 } | |
706 | |
707 void Automation::SendWebMouseEvent(const WebViewId& view_id, | |
708 const WebMouseEvent& event, | |
709 Error** error) { | |
710 WebViewLocator view_locator; | |
711 *error = ConvertViewIdToLocator(view_id, &view_locator); | |
712 if (*error) | |
713 return; | |
714 | |
715 automation::Error auto_error; | |
716 if (!SendWebMouseEventJSONRequestDeprecated( | |
717 automation(), view_locator, event, &auto_error)) { | |
718 *error = Error::FromAutomationError(auto_error); | |
719 } | |
720 } | |
721 | |
722 void Automation::CaptureEntirePageAsPNG(const WebViewId& view_id, | |
723 const base::FilePath& path, | |
724 Error** error) { | |
725 WebViewLocator view_locator; | |
726 *error = ConvertViewIdToLocator(view_id, &view_locator); | |
727 if (*error) | |
728 return; | |
729 | |
730 automation::Error auto_error; | |
731 if (!SendCaptureEntirePageJSONRequestDeprecated( | |
732 automation(), view_locator, path, &auto_error)) { | |
733 *error = Error::FromAutomationError(auto_error); | |
734 } | |
735 } | |
736 | |
737 #if !defined(NO_TCMALLOC) && (defined(OS_LINUX) || defined(OS_CHROMEOS)) | |
738 void Automation::HeapProfilerDump(const WebViewId& view_id, | |
739 const std::string& reason, | |
740 Error** error) { | |
741 WebViewLocator view_locator; | |
742 *error = ConvertViewIdToLocator(view_id, &view_locator); | |
743 if (*error) | |
744 return; | |
745 | |
746 automation::Error auto_error; | |
747 if (!SendHeapProfilerDumpJSONRequestDeprecated( | |
748 automation(), view_locator, reason, &auto_error)) { | |
749 *error = Error::FromAutomationError(auto_error); | |
750 } | |
751 } | |
752 #endif // !defined(NO_TCMALLOC) && (defined(OS_LINUX) || defined(OS_CHROMEOS)) | |
753 | |
754 void Automation::NavigateToURL(const WebViewId& view_id, | |
755 const std::string& url, | |
756 Error** error) { | |
757 WebViewLocator view_locator; | |
758 *error = ConvertViewIdToLocator(view_id, &view_locator); | |
759 if (*error) | |
760 return; | |
761 | |
762 AutomationMsg_NavigationResponseValues navigate_response; | |
763 automation::Error auto_error; | |
764 if (!SendNavigateToURLJSONRequest(automation(), view_locator, | |
765 url, 1, &navigate_response, | |
766 &auto_error)) { | |
767 *error = Error::FromAutomationError(auto_error); | |
768 return; | |
769 } | |
770 // TODO(kkania): Do not rely on this enum. | |
771 if (navigate_response == AUTOMATION_MSG_NAVIGATION_ERROR) | |
772 *error = new Error(kUnknownError, "Navigation error occurred"); | |
773 } | |
774 | |
775 void Automation::NavigateToURLAsync(const WebViewId& view_id, | |
776 const std::string& url, | |
777 Error** error) { | |
778 WebViewLocator view_locator; | |
779 *error = ConvertViewIdToLocator(view_id, &view_locator); | |
780 if (*error) | |
781 return; | |
782 | |
783 automation::Error auto_error; | |
784 if (!view_id.old_style()) { | |
785 AutomationMsg_NavigationResponseValues navigate_response; | |
786 if (!SendNavigateToURLJSONRequest(automation(), view_locator, url, | |
787 0, &navigate_response, &auto_error)) { | |
788 *error = Error::FromAutomationError(auto_error); | |
789 return; | |
790 } | |
791 } else { | |
792 scoped_refptr<BrowserProxy> browser = | |
793 automation()->GetBrowserWindow(view_locator.browser_index()); | |
794 if (!browser.get()) { | |
795 *error = new Error(kUnknownError, "Couldn't obtain browser proxy"); | |
796 return; | |
797 } | |
798 scoped_refptr<TabProxy> tab = browser->GetTab(view_locator.tab_index()); | |
799 if (!tab.get()) { | |
800 *error = new Error(kUnknownError, "Couldn't obtain tab proxy"); | |
801 return; | |
802 } | |
803 if (!tab->NavigateToURLAsync(GURL(url))) { | |
804 *error = new Error(kUnknownError, "Unable to navigate to url"); | |
805 return; | |
806 } | |
807 } | |
808 } | |
809 | |
810 void Automation::GoForward(const WebViewId& view_id, Error** error) { | |
811 WebViewLocator view_locator; | |
812 *error = ConvertViewIdToLocator(view_id, &view_locator); | |
813 if (*error) | |
814 return; | |
815 | |
816 automation::Error auto_error; | |
817 if (!SendGoForwardJSONRequest( | |
818 automation(), view_locator, &auto_error)) { | |
819 *error = Error::FromAutomationError(auto_error); | |
820 } | |
821 } | |
822 | |
823 void Automation::GoBack(const WebViewId& view_id, Error** error) { | |
824 WebViewLocator view_locator; | |
825 *error = ConvertViewIdToLocator(view_id, &view_locator); | |
826 if (*error) | |
827 return; | |
828 | |
829 automation::Error auto_error; | |
830 if (!SendGoBackJSONRequest(automation(), view_locator, &auto_error)) | |
831 *error = Error::FromAutomationError(auto_error); | |
832 } | |
833 | |
834 void Automation::Reload(const WebViewId& view_id, Error** error) { | |
835 WebViewLocator view_locator; | |
836 *error = ConvertViewIdToLocator(view_id, &view_locator); | |
837 if (*error) | |
838 return; | |
839 | |
840 automation::Error auto_error; | |
841 if (!SendReloadJSONRequest(automation(), view_locator, &auto_error)) | |
842 *error = Error::FromAutomationError(auto_error); | |
843 } | |
844 | |
845 void Automation::GetCookies(const std::string& url, | |
846 scoped_ptr<ListValue>* cookies, | |
847 Error** error) { | |
848 automation::Error auto_error; | |
849 if (!SendGetCookiesJSONRequest(automation(), url, cookies, &auto_error)) | |
850 *error = Error::FromAutomationError(auto_error); | |
851 } | |
852 | |
853 void Automation::DeleteCookie(const std::string& url, | |
854 const std::string& cookie_name, | |
855 Error** error) { | |
856 automation::Error auto_error; | |
857 if (!SendDeleteCookieJSONRequest( | |
858 automation(), url, cookie_name, &auto_error)) { | |
859 *error = Error::FromAutomationError(auto_error); | |
860 } | |
861 } | |
862 | |
863 void Automation::SetCookie(const std::string& url, | |
864 DictionaryValue* cookie_dict, | |
865 Error** error) { | |
866 automation::Error auto_error; | |
867 if (!SendSetCookieJSONRequest(automation(), url, cookie_dict, &auto_error)) | |
868 *error = Error::FromAutomationError(auto_error); | |
869 } | |
870 | |
871 void Automation::GetViews(std::vector<WebViewInfo>* views, | |
872 Error** error) { | |
873 automation::Error auto_error; | |
874 if (build_no_ >= 963) { | |
875 if (!SendGetWebViewsJSONRequest(automation(), views, &auto_error)) | |
876 *error = Error::FromAutomationError(auto_error); | |
877 } else { | |
878 if (!SendGetTabIdsJSONRequest(automation(), views, &auto_error)) | |
879 *error = Error::FromAutomationError(auto_error); | |
880 } | |
881 } | |
882 | |
883 void Automation::DoesViewExist( | |
884 const WebViewId& view_id, bool* does_exist, Error** error) { | |
885 automation::Error auto_error; | |
886 if (view_id.old_style()) { | |
887 if (!SendIsTabIdValidJSONRequest( | |
888 automation(), view_id, does_exist, &auto_error)) | |
889 *error = Error::FromAutomationError(auto_error); | |
890 } else { | |
891 if (!SendDoesAutomationObjectExistJSONRequest( | |
892 automation(), view_id, does_exist, &auto_error)) | |
893 *error = Error::FromAutomationError(auto_error); | |
894 } | |
895 } | |
896 | |
897 void Automation::CloseView(const WebViewId& view_id, Error** error) { | |
898 WebViewLocator view_locator; | |
899 *error = ConvertViewIdToLocator(view_id, &view_locator); | |
900 if (*error) | |
901 return; | |
902 | |
903 automation::Error auto_error; | |
904 if (!SendCloseViewJSONRequest(automation(), view_locator, &auto_error)) | |
905 *error = Error::FromAutomationError(auto_error); | |
906 } | |
907 | |
908 void Automation::SetViewBounds(const WebViewId& view_id, | |
909 const Rect& bounds, | |
910 Error** error) { | |
911 automation::Error auto_error; | |
912 if (!SendSetViewBoundsJSONRequest( | |
913 automation(), view_id, bounds.x(), bounds.y(), | |
914 bounds.width(), bounds.height(), &auto_error)) | |
915 *error = Error::FromAutomationError(auto_error); | |
916 } | |
917 | |
918 void Automation::MaximizeView(const WebViewId& view_id, Error** error) { | |
919 *error = CheckMaximizeSupported(); | |
920 if (*error) | |
921 return; | |
922 | |
923 automation::Error auto_error; | |
924 if (!SendMaximizeJSONRequest( | |
925 automation(), view_id, &auto_error)) | |
926 *error = Error::FromAutomationError(auto_error); | |
927 } | |
928 | |
929 void Automation::GetAppModalDialogMessage(std::string* message, Error** error) { | |
930 *error = CheckAlertsSupported(); | |
931 if (*error) | |
932 return; | |
933 | |
934 automation::Error auto_error; | |
935 if (!SendGetAppModalDialogMessageJSONRequest( | |
936 automation(), message, &auto_error)) { | |
937 *error = Error::FromAutomationError(auto_error); | |
938 } | |
939 } | |
940 | |
941 void Automation::AcceptOrDismissAppModalDialog(bool accept, Error** error) { | |
942 *error = CheckAlertsSupported(); | |
943 if (*error) | |
944 return; | |
945 | |
946 automation::Error auto_error; | |
947 if (!SendAcceptOrDismissAppModalDialogJSONRequest( | |
948 automation(), accept, &auto_error)) { | |
949 *error = Error::FromAutomationError(auto_error); | |
950 } | |
951 } | |
952 | |
953 void Automation::AcceptPromptAppModalDialog(const std::string& prompt_text, | |
954 Error** error) { | |
955 *error = CheckAlertsSupported(); | |
956 if (*error) | |
957 return; | |
958 | |
959 automation::Error auto_error; | |
960 if (!SendAcceptPromptAppModalDialogJSONRequest( | |
961 automation(), prompt_text, &auto_error)) { | |
962 *error = Error::FromAutomationError(auto_error); | |
963 } | |
964 } | |
965 | |
966 void Automation::GetBrowserVersion(std::string* version) { | |
967 *version = automation()->server_version(); | |
968 } | |
969 | |
970 void Automation::GetChromeDriverAutomationVersion(int* version, Error** error) { | |
971 automation::Error auto_error; | |
972 if (!SendGetChromeDriverAutomationVersion(automation(), version, &auto_error)) | |
973 *error = Error::FromAutomationError(auto_error); | |
974 } | |
975 | |
976 void Automation::WaitForAllViewsToStopLoading(Error** error) { | |
977 automation::Error auto_error; | |
978 if (!SendWaitForAllViewsToStopLoadingJSONRequestDeprecated( | |
979 automation(), &auto_error)) | |
980 *error = Error::FromAutomationError(auto_error); | |
981 } | |
982 | |
983 void Automation::InstallExtension( | |
984 const base::FilePath& path, std::string* extension_id, Error** error) { | |
985 *error = CheckNewExtensionInterfaceSupported(); | |
986 if (*error) | |
987 return; | |
988 | |
989 automation::Error auto_error; | |
990 if (!SendInstallExtensionJSONRequest( | |
991 automation(), path, false /* with_ui */, extension_id, | |
992 &auto_error)) | |
993 *error = Error::FromAutomationError(auto_error); | |
994 } | |
995 | |
996 void Automation::GetExtensionsInfo( | |
997 base::ListValue* extensions_list, Error** error) { | |
998 *error = CheckNewExtensionInterfaceSupported(); | |
999 if (*error) | |
1000 return; | |
1001 | |
1002 automation::Error auto_error; | |
1003 if (!SendGetExtensionsInfoJSONRequest( | |
1004 automation(), extensions_list, &auto_error)) | |
1005 *error = Error::FromAutomationError(auto_error); | |
1006 } | |
1007 | |
1008 void Automation::IsPageActionVisible( | |
1009 const WebViewId& tab_id, | |
1010 const std::string& extension_id, | |
1011 bool* is_visible, | |
1012 Error** error) { | |
1013 *error = CheckNewExtensionInterfaceSupported(); | |
1014 if (*error) | |
1015 return; | |
1016 | |
1017 automation::Error auto_error; | |
1018 if (!SendIsPageActionVisibleJSONRequest( | |
1019 automation(), tab_id, extension_id, is_visible, &auto_error)) | |
1020 *error = Error::FromAutomationError(auto_error); | |
1021 } | |
1022 | |
1023 void Automation::SetExtensionState( | |
1024 const std::string& extension_id, | |
1025 bool enable, | |
1026 Error** error) { | |
1027 *error = CheckNewExtensionInterfaceSupported(); | |
1028 if (*error) | |
1029 return; | |
1030 | |
1031 automation::Error auto_error; | |
1032 if (!SendSetExtensionStateJSONRequest( | |
1033 automation(), extension_id, enable /* enable */, | |
1034 false /* allow_in_incognito */, &auto_error)) | |
1035 *error = Error::FromAutomationError(auto_error); | |
1036 } | |
1037 | |
1038 void Automation::ClickExtensionButton( | |
1039 const std::string& extension_id, | |
1040 bool browser_action, | |
1041 Error** error) { | |
1042 automation::Error auto_error; | |
1043 if (!SendClickExtensionButtonJSONRequest( | |
1044 automation(), extension_id, browser_action, &auto_error)) | |
1045 *error = Error::FromAutomationError(auto_error); | |
1046 } | |
1047 | |
1048 void Automation::UninstallExtension( | |
1049 const std::string& extension_id, Error** error) { | |
1050 *error = CheckNewExtensionInterfaceSupported(); | |
1051 if (*error) | |
1052 return; | |
1053 | |
1054 automation::Error auto_error; | |
1055 if (!SendUninstallExtensionJSONRequest( | |
1056 automation(), extension_id, &auto_error)) | |
1057 *error = Error::FromAutomationError(auto_error); | |
1058 } | |
1059 | |
1060 void Automation::SetLocalStatePreference(const std::string& pref, | |
1061 base::Value* value, | |
1062 Error** error) { | |
1063 scoped_ptr<Value> scoped_value(value); | |
1064 // In version 927, SetLocalStatePrefs was changed from taking a browser | |
1065 // handle to a browser index. | |
1066 if (build_no_ >= 927) { | |
1067 automation::Error auto_error; | |
1068 if (!SendSetLocalStatePreferenceJSONRequest( | |
1069 automation(), pref, scoped_value.release(), &auto_error)) | |
1070 *error = Error::FromAutomationError(auto_error); | |
1071 } else { | |
1072 std::string request, response; | |
1073 DictionaryValue request_dict; | |
1074 request_dict.SetString("command", "SetLocalStatePrefs"); | |
1075 request_dict.SetString("path", pref); | |
1076 request_dict.Set("value", scoped_value.release()); | |
1077 base::JSONWriter::Write(&request_dict, &request); | |
1078 if (!automation()->GetBrowserWindow(0)->SendJSONRequest( | |
1079 request, -1, &response)) { | |
1080 *error = new Error(kUnknownError, base::StringPrintf( | |
1081 "Failed to set local state pref '%s'", pref.c_str())); | |
1082 } | |
1083 } | |
1084 } | |
1085 | |
1086 void Automation::SetPreference(const std::string& pref, | |
1087 base::Value* value, | |
1088 Error** error) { | |
1089 scoped_ptr<Value> scoped_value(value); | |
1090 // Chrome 17 is on the 963 branch. The first released 18 build should have | |
1091 // the new SetPrefs method which uses a browser index instead of handle. | |
1092 if (build_no_ >= 964) { | |
1093 automation::Error auto_error; | |
1094 if (!SendSetPreferenceJSONRequest( | |
1095 automation(), pref, scoped_value.release(), &auto_error)) | |
1096 *error = Error::FromAutomationError(auto_error); | |
1097 } else { | |
1098 std::string request, response; | |
1099 DictionaryValue request_dict; | |
1100 request_dict.SetString("command", "SetPrefs"); | |
1101 request_dict.SetInteger("windex", 0); | |
1102 request_dict.SetString("path", pref); | |
1103 request_dict.Set("value", scoped_value.release()); | |
1104 base::JSONWriter::Write(&request_dict, &request); | |
1105 if (!automation()->GetBrowserWindow(0)->SendJSONRequest( | |
1106 request, -1, &response)) { | |
1107 *error = new Error(kUnknownError, base::StringPrintf( | |
1108 "Failed to set pref '%s'", pref.c_str())); | |
1109 } | |
1110 } | |
1111 } | |
1112 | |
1113 void Automation::GetGeolocation(scoped_ptr<DictionaryValue>* geolocation, | |
1114 Error** error) { | |
1115 *error = CheckGeolocationSupported(); | |
1116 if (*error) | |
1117 return; | |
1118 | |
1119 if (geolocation_.get()) { | |
1120 geolocation->reset(geolocation_->DeepCopy()); | |
1121 } else { | |
1122 *error = new Error(kUnknownError, | |
1123 "Location must be set before it can be retrieved"); | |
1124 } | |
1125 } | |
1126 | |
1127 void Automation::OverrideGeolocation(const DictionaryValue* geolocation, | |
1128 Error** error) { | |
1129 *error = CheckGeolocationSupported(); | |
1130 if (*error) | |
1131 return; | |
1132 | |
1133 automation::Error auto_error; | |
1134 if (SendOverrideGeolocationJSONRequest( | |
1135 automation(), geolocation, &auto_error)) { | |
1136 geolocation_.reset(geolocation->DeepCopy()); | |
1137 } else { | |
1138 *error = Error::FromAutomationError(auto_error); | |
1139 } | |
1140 } | |
1141 | |
1142 AutomationProxy* Automation::automation() const { | |
1143 return launcher_->automation(); | |
1144 } | |
1145 | |
1146 Error* Automation::ConvertViewIdToLocator( | |
1147 const WebViewId& view_id, WebViewLocator* view_locator) { | |
1148 if (view_id.old_style()) { | |
1149 int browser_index, tab_index; | |
1150 automation::Error auto_error; | |
1151 if (!SendGetIndicesFromTabIdJSONRequest( | |
1152 automation(), view_id.tab_id(), &browser_index, &tab_index, | |
1153 &auto_error)) | |
1154 return Error::FromAutomationError(auto_error); | |
1155 *view_locator = WebViewLocator::ForIndexPair(browser_index, tab_index); | |
1156 } else { | |
1157 *view_locator = WebViewLocator::ForViewId(view_id.GetId()); | |
1158 } | |
1159 return NULL; | |
1160 } | |
1161 | |
1162 Error* Automation::DetermineBuildNumber() { | |
1163 std::string version = automation()->server_version(); | |
1164 std::vector<std::string> split_version; | |
1165 base::SplitString(version, '.', &split_version); | |
1166 if (split_version.size() != 4) { | |
1167 return new Error( | |
1168 kUnknownError, "Browser version has unrecognized format: " + version); | |
1169 } | |
1170 if (!base::StringToInt(split_version[2], &build_no_)) { | |
1171 return new Error( | |
1172 kUnknownError, "Browser version has unrecognized format: " + version); | |
1173 } | |
1174 return NULL; | |
1175 } | |
1176 | |
1177 Error* Automation::CheckVersion(int min_required_build_no, | |
1178 const std::string& error_msg) { | |
1179 if (build_no_ < min_required_build_no) | |
1180 return new Error(kUnknownError, error_msg); | |
1181 return NULL; | |
1182 } | |
1183 | |
1184 Error* Automation::CheckAlertsSupported() { | |
1185 return CheckVersion( | |
1186 768, "Alerts are not supported for this version of Chrome"); | |
1187 } | |
1188 | |
1189 Error* Automation::CheckAdvancedInteractionsSupported() { | |
1190 const char* message = | |
1191 "Advanced user interactions are not supported for this version of Chrome"; | |
1192 return CheckVersion(750, message); | |
1193 } | |
1194 | |
1195 Error* Automation::CheckNewExtensionInterfaceSupported() { | |
1196 const char* message = | |
1197 "Extension interface is not supported for this version of Chrome"; | |
1198 return CheckVersion(947, message); | |
1199 } | |
1200 | |
1201 Error* Automation::CheckGeolocationSupported() { | |
1202 const char* message = | |
1203 "Geolocation automation interface is not supported for this version of " | |
1204 "Chrome."; | |
1205 return CheckVersion(1119, message); | |
1206 } | |
1207 | |
1208 Error* Automation::CheckMaximizeSupported() { | |
1209 const char* message = | |
1210 "Maximize automation interface is not supported for this version of " | |
1211 "Chrome."; | |
1212 return CheckVersion(1160, message); | |
1213 } | |
1214 | |
1215 } // namespace webdriver | |
OLD | NEW |