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 1216