| OLD | NEW |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/test/chromedriver/chrome_launcher.h" | 5 #include "chrome/test/chromedriver/chrome_launcher.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <vector> | 8 #include <vector> |
| 9 | 9 |
| 10 #include "base/base64.h" | 10 #include "base/base64.h" |
| (...skipping 27 matching lines...) Expand all Loading... |
| 38 #include "chrome/test/chromedriver/chrome/version.h" | 38 #include "chrome/test/chromedriver/chrome/version.h" |
| 39 #include "chrome/test/chromedriver/chrome/web_view.h" | 39 #include "chrome/test/chromedriver/chrome/web_view.h" |
| 40 #include "chrome/test/chromedriver/chrome/zip.h" | 40 #include "chrome/test/chromedriver/chrome/zip.h" |
| 41 #include "chrome/test/chromedriver/net/net_util.h" | 41 #include "chrome/test/chromedriver/net/net_util.h" |
| 42 #include "chrome/test/chromedriver/net/url_request_context_getter.h" | 42 #include "chrome/test/chromedriver/net/url_request_context_getter.h" |
| 43 #include "crypto/sha2.h" | 43 #include "crypto/sha2.h" |
| 44 | 44 |
| 45 namespace { | 45 namespace { |
| 46 | 46 |
| 47 const char* kCommonSwitches[] = { | 47 const char* kCommonSwitches[] = { |
| 48 "ignore-certificate-errors", "metrics-recording-only"}; | 48 "ignore-certificate-errors", "metrics-recording-only"}; |
| 49 | 49 |
| 50 Status UnpackAutomationExtension(const base::FilePath& temp_dir, | 50 Status UnpackAutomationExtension(const base::FilePath& temp_dir, |
| 51 base::FilePath* automation_extension) { | 51 base::FilePath* automation_extension) { |
| 52 std::string decoded_extension; | 52 std::string decoded_extension; |
| 53 if (!base::Base64Decode(kAutomationExtension, &decoded_extension)) | 53 if (!base::Base64Decode(kAutomationExtension, &decoded_extension)) |
| 54 return Status(kUnknownError, "failed to base64decode automation extension"); | 54 return Status(kUnknownError, "failed to base64decode automation extension"); |
| 55 | 55 |
| 56 base::FilePath extension_zip = temp_dir.AppendASCII("internal.zip"); | 56 base::FilePath extension_zip = temp_dir.AppendASCII("internal.zip"); |
| 57 int size = static_cast<int>(decoded_extension.length()); | 57 int size = static_cast<int>(decoded_extension.length()); |
| 58 if (file_util::WriteFile(extension_zip, decoded_extension.c_str(), size) | 58 if (file_util::WriteFile(extension_zip, decoded_extension.c_str(), size) |
| 59 != size) { | 59 != size) { |
| 60 return Status(kUnknownError, "failed to write automation extension zip"); | 60 return Status(kUnknownError, "failed to write automation extension zip"); |
| 61 } | 61 } |
| 62 | 62 |
| 63 base::FilePath extension_dir = temp_dir.AppendASCII("internal"); | 63 base::FilePath extension_dir = temp_dir.AppendASCII("internal"); |
| 64 if (!zip::Unzip(extension_zip, extension_dir)) | 64 if (!zip::Unzip(extension_zip, extension_dir)) |
| 65 return Status(kUnknownError, "failed to unzip automation extension"); | 65 return Status(kUnknownError, "failed to unzip automation extension"); |
| 66 | 66 |
| 67 *automation_extension = extension_dir; | 67 *automation_extension = extension_dir; |
| 68 return Status(kOk); | 68 return Status(kOk); |
| 69 } | 69 } |
| 70 | 70 |
| 71 void AddSwitches(CommandLine* command, | |
| 72 const char* switches[], | |
| 73 size_t switch_count, | |
| 74 const std::set<std::string>& exclude_switches) { | |
| 75 for (size_t i = 0; i < switch_count; ++i) { | |
| 76 if (exclude_switches.find(switches[i]) == exclude_switches.end()) | |
| 77 command->AppendSwitch(switches[i]); | |
| 78 } | |
| 79 } | |
| 80 | |
| 81 Status PrepareCommandLine(int port, | 71 Status PrepareCommandLine(int port, |
| 82 const Capabilities& capabilities, | 72 const Capabilities& capabilities, |
| 83 CommandLine* prepared_command, | 73 CommandLine* prepared_command, |
| 84 base::ScopedTempDir* user_data_dir, | 74 base::ScopedTempDir* user_data_dir, |
| 85 base::ScopedTempDir* extension_dir, | 75 base::ScopedTempDir* extension_dir, |
| 86 std::vector<std::string>* extension_bg_pages) { | 76 std::vector<std::string>* extension_bg_pages) { |
| 87 CommandLine command = capabilities.command; | 77 base::FilePath program = capabilities.binary; |
| 88 base::FilePath program = command.GetProgram(); | |
| 89 if (program.empty()) { | 78 if (program.empty()) { |
| 90 if (!FindChrome(&program)) | 79 if (!FindChrome(&program)) |
| 91 return Status(kUnknownError, "cannot find Chrome binary"); | 80 return Status(kUnknownError, "cannot find Chrome binary"); |
| 92 command.SetProgram(program); | |
| 93 } else if (!base::PathExists(program)) { | 81 } else if (!base::PathExists(program)) { |
| 94 return Status(kUnknownError, | 82 return Status(kUnknownError, |
| 95 base::StringPrintf("no chrome binary at %" PRFilePath, | 83 base::StringPrintf("no chrome binary at %" PRFilePath, |
| 96 program.value().c_str())); | 84 program.value().c_str())); |
| 97 } | 85 } |
| 86 CommandLine command(program); |
| 87 Switches switches; |
| 98 | 88 |
| 99 const char* excludable_switches[] = { | 89 // TODO(chrisgao): Add "disable-sync" when chrome 30- is not supported. |
| 100 "disable-hang-monitor", | 90 // For chrome 30-, it leads to crash when opening chrome://settings. |
| 101 "disable-prompt-on-repost", | 91 for (size_t i = 0; i < arraysize(kCommonSwitches); ++i) |
| 102 "full-memory-crash-report", | 92 switches.SetSwitch(kCommonSwitches[i]); |
| 103 "no-first-run", | 93 switches.SetSwitch("disable-hang-monitor"); |
| 104 "disable-background-networking", | 94 switches.SetSwitch("disable-prompt-on-repost"); |
| 105 // TODO(chrisgao): Add "disable-sync" when chrome 30- is not supported. | 95 switches.SetSwitch("full-memory-crash-report"); |
| 106 // For chrome 30-, it leads to crash when opening chrome://settings. | 96 switches.SetSwitch("no-first-run"); |
| 107 "disable-web-resources", | 97 switches.SetSwitch("disable-background-networking"); |
| 108 "safebrowsing-disable-auto-update", | 98 switches.SetSwitch("disable-web-resources"); |
| 109 "safebrowsing-disable-download-protection", | 99 switches.SetSwitch("safebrowsing-disable-auto-update"); |
| 110 "disable-client-side-phishing-detection", | 100 switches.SetSwitch("safebrowsing-disable-download-protection"); |
| 111 "disable-component-update", | 101 switches.SetSwitch("disable-client-side-phishing-detection"); |
| 112 "disable-default-apps", | 102 switches.SetSwitch("disable-component-update"); |
| 113 }; | 103 switches.SetSwitch("disable-default-apps"); |
| 104 switches.SetSwitch("enable-logging"); |
| 105 switches.SetSwitch("logging-level", "1"); |
| 106 switches.SetSwitch("password-store", "basic"); |
| 107 switches.SetSwitch("use-mock-keychain"); |
| 108 switches.SetSwitch("remote-debugging-port", base::IntToString(port)); |
| 114 | 109 |
| 115 AddSwitches(&command, excludable_switches, arraysize(excludable_switches), | 110 for (std::set<std::string>::const_iterator iter = |
| 116 capabilities.exclude_switches); | 111 capabilities.exclude_switches.begin(); |
| 117 AddSwitches(&command, kCommonSwitches, arraysize(kCommonSwitches), | 112 iter != capabilities.exclude_switches.end(); |
| 118 capabilities.exclude_switches); | 113 ++iter) { |
| 114 switches.RemoveSwitch(*iter); |
| 115 } |
| 116 switches.SetFromSwitches(capabilities.switches); |
| 119 | 117 |
| 120 command.AppendSwitch("enable-logging"); | 118 if (!switches.HasSwitch("user-data-dir")) { |
| 121 command.AppendSwitchASCII("logging-level", "1"); | |
| 122 command.AppendSwitchASCII("password-store", "basic"); | |
| 123 command.AppendSwitch("use-mock-keychain"); | |
| 124 command.AppendSwitchASCII("remote-debugging-port", base::IntToString(port)); | |
| 125 | |
| 126 if (!command.HasSwitch("user-data-dir")) { | |
| 127 command.AppendArg("about:blank"); | 119 command.AppendArg("about:blank"); |
| 128 if (!user_data_dir->CreateUniqueTempDir()) | 120 if (!user_data_dir->CreateUniqueTempDir()) |
| 129 return Status(kUnknownError, "cannot create temp dir for user data dir"); | 121 return Status(kUnknownError, "cannot create temp dir for user data dir"); |
| 130 command.AppendSwitchPath("user-data-dir", user_data_dir->path()); | 122 switches.SetSwitch("user-data-dir", user_data_dir->path().value()); |
| 131 Status status = internal::PrepareUserDataDir( | 123 Status status = internal::PrepareUserDataDir( |
| 132 user_data_dir->path(), capabilities.prefs.get(), | 124 user_data_dir->path(), capabilities.prefs.get(), |
| 133 capabilities.local_state.get()); | 125 capabilities.local_state.get()); |
| 134 if (status.IsError()) | 126 if (status.IsError()) |
| 135 return status; | 127 return status; |
| 136 } | 128 } |
| 137 | 129 |
| 138 if (!extension_dir->CreateUniqueTempDir()) { | 130 if (!extension_dir->CreateUniqueTempDir()) { |
| 139 return Status(kUnknownError, | 131 return Status(kUnknownError, |
| 140 "cannot create temp dir for unpacking extensions"); | 132 "cannot create temp dir for unpacking extensions"); |
| 141 } | 133 } |
| 142 Status status = internal::ProcessExtensions(capabilities.extensions, | 134 Status status = internal::ProcessExtensions(capabilities.extensions, |
| 143 extension_dir->path(), | 135 extension_dir->path(), |
| 144 true, | 136 true, |
| 145 &command, | 137 &switches, |
| 146 extension_bg_pages); | 138 extension_bg_pages); |
| 147 if (status.IsError()) | 139 if (status.IsError()) |
| 148 return status; | 140 return status; |
| 149 | 141 switches.AppendToCommandLine(&command); |
| 150 *prepared_command = command; | 142 *prepared_command = command; |
| 151 return Status(kOk); | 143 return Status(kOk); |
| 152 } | 144 } |
| 153 | 145 |
| 154 Status WaitForDevToolsAndCheckVersion( | 146 Status WaitForDevToolsAndCheckVersion( |
| 155 const NetAddress& address, | 147 const NetAddress& address, |
| 156 URLRequestContextGetter* context_getter, | 148 URLRequestContextGetter* context_getter, |
| 157 const SyncWebSocketFactory& socket_factory, | 149 const SyncWebSocketFactory& socket_factory, |
| 158 Log* log, | 150 Log* log, |
| 159 scoped_ptr<DevToolsHttpClient>* user_client) { | 151 scoped_ptr<DevToolsHttpClient>* user_client) { |
| (...skipping 23 matching lines...) Expand all Loading... |
| 183 Status LaunchExistingChromeSession( | 175 Status LaunchExistingChromeSession( |
| 184 URLRequestContextGetter* context_getter, | 176 URLRequestContextGetter* context_getter, |
| 185 const SyncWebSocketFactory& socket_factory, | 177 const SyncWebSocketFactory& socket_factory, |
| 186 Log* log, | 178 Log* log, |
| 187 const Capabilities& capabilities, | 179 const Capabilities& capabilities, |
| 188 ScopedVector<DevToolsEventListener>& devtools_event_listeners, | 180 ScopedVector<DevToolsEventListener>& devtools_event_listeners, |
| 189 scoped_ptr<Chrome>* chrome) { | 181 scoped_ptr<Chrome>* chrome) { |
| 190 Status status(kOk); | 182 Status status(kOk); |
| 191 scoped_ptr<DevToolsHttpClient> devtools_client; | 183 scoped_ptr<DevToolsHttpClient> devtools_client; |
| 192 status = WaitForDevToolsAndCheckVersion( | 184 status = WaitForDevToolsAndCheckVersion( |
| 193 capabilities.use_existing_browser, context_getter, socket_factory, log, | 185 capabilities.debugger_address, context_getter, socket_factory, log, |
| 194 &devtools_client); | 186 &devtools_client); |
| 195 if (status.IsError()) { | 187 if (status.IsError()) { |
| 196 return Status(kUnknownError, "cannot connect to chrome at " + | 188 return Status(kUnknownError, "cannot connect to chrome at " + |
| 197 capabilities.use_existing_browser.ToString(), | 189 capabilities.debugger_address.ToString(), |
| 198 status); | 190 status); |
| 199 } | 191 } |
| 200 chrome->reset(new ChromeExistingImpl(devtools_client.Pass(), | 192 chrome->reset(new ChromeExistingImpl(devtools_client.Pass(), |
| 201 devtools_event_listeners, | 193 devtools_event_listeners, |
| 202 log)); | 194 log)); |
| 203 return Status(kOk); | 195 return Status(kOk); |
| 204 } | 196 } |
| 205 | 197 |
| 206 Status LaunchDesktopChrome( | 198 Status LaunchDesktopChrome( |
| 207 URLRequestContextGetter* context_getter, | 199 URLRequestContextGetter* context_getter, |
| (...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 321 scoped_ptr<Device> device; | 313 scoped_ptr<Device> device; |
| 322 if (capabilities.android_device_serial.empty()) { | 314 if (capabilities.android_device_serial.empty()) { |
| 323 status = device_manager->AcquireDevice(&device); | 315 status = device_manager->AcquireDevice(&device); |
| 324 } else { | 316 } else { |
| 325 status = device_manager->AcquireSpecificDevice( | 317 status = device_manager->AcquireSpecificDevice( |
| 326 capabilities.android_device_serial, &device); | 318 capabilities.android_device_serial, &device); |
| 327 } | 319 } |
| 328 if (!status.IsOk()) | 320 if (!status.IsOk()) |
| 329 return status; | 321 return status; |
| 330 | 322 |
| 331 std::string args(capabilities.android_args); | 323 Switches switches(capabilities.switches); |
| 332 for (size_t i = 0; i < arraysize(kCommonSwitches); i++) | 324 for (size_t i = 0; i < arraysize(kCommonSwitches); ++i) |
| 333 args += "--" + std::string(kCommonSwitches[i]) + " "; | 325 switches.SetSwitch(kCommonSwitches[i]); |
| 334 args += "--disable-fre --enable-remote-debugging"; | 326 switches.SetSwitch("disable-fre"); |
| 335 | 327 switches.SetSwitch("enable-remote-debugging"); |
| 336 status = device->StartApp(capabilities.android_package, | 328 status = device->StartApp(capabilities.android_package, |
| 337 capabilities.android_activity, | 329 capabilities.android_activity, |
| 338 capabilities.android_process, | 330 capabilities.android_process, |
| 339 args, port); | 331 switches.ToString(), port); |
| 340 if (!status.IsOk()) { | 332 if (!status.IsOk()) { |
| 341 device->StopApp(); | 333 device->StopApp(); |
| 342 return status; | 334 return status; |
| 343 } | 335 } |
| 344 | 336 |
| 345 scoped_ptr<DevToolsHttpClient> devtools_client; | 337 scoped_ptr<DevToolsHttpClient> devtools_client; |
| 346 status = WaitForDevToolsAndCheckVersion(NetAddress(port), | 338 status = WaitForDevToolsAndCheckVersion(NetAddress(port), |
| 347 context_getter, | 339 context_getter, |
| 348 socket_factory, | 340 socket_factory, |
| 349 log, | 341 log, |
| (...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 493 Status status = GetExtensionBackgroundPage(manifest, id, &bg_page_tmp); | 485 Status status = GetExtensionBackgroundPage(manifest, id, &bg_page_tmp); |
| 494 if (status.IsError()) | 486 if (status.IsError()) |
| 495 return status; | 487 return status; |
| 496 | 488 |
| 497 *path = extension_dir; | 489 *path = extension_dir; |
| 498 if (bg_page_tmp.size()) | 490 if (bg_page_tmp.size()) |
| 499 *bg_page = bg_page_tmp; | 491 *bg_page = bg_page_tmp; |
| 500 return Status(kOk); | 492 return Status(kOk); |
| 501 } | 493 } |
| 502 | 494 |
| 495 void UpdateExtensionSwitch(Switches* switches, |
| 496 const char name[], |
| 497 const base::FilePath::StringType& extension) { |
| 498 base::FilePath::StringType value = switches->GetSwitchValueNative(name); |
| 499 if (value.length()) |
| 500 value += FILE_PATH_LITERAL(","); |
| 501 value += extension; |
| 502 switches->SetSwitch(name, value); |
| 503 } |
| 504 |
| 503 Status ProcessExtensions(const std::vector<std::string>& extensions, | 505 Status ProcessExtensions(const std::vector<std::string>& extensions, |
| 504 const base::FilePath& temp_dir, | 506 const base::FilePath& temp_dir, |
| 505 bool include_automation_extension, | 507 bool include_automation_extension, |
| 506 CommandLine* command, | 508 Switches* switches, |
| 507 std::vector<std::string>* bg_pages) { | 509 std::vector<std::string>* bg_pages) { |
| 508 std::vector<std::string> bg_pages_tmp; | 510 std::vector<std::string> bg_pages_tmp; |
| 509 std::vector<base::FilePath::StringType> extension_paths; | 511 std::vector<base::FilePath::StringType> extension_paths; |
| 510 for (size_t i = 0; i < extensions.size(); ++i) { | 512 for (size_t i = 0; i < extensions.size(); ++i) { |
| 511 base::FilePath path; | 513 base::FilePath path; |
| 512 std::string bg_page; | 514 std::string bg_page; |
| 513 Status status = ProcessExtension(extensions[i], temp_dir, &path, &bg_page); | 515 Status status = ProcessExtension(extensions[i], temp_dir, &path, &bg_page); |
| 514 if (status.IsError()) { | 516 if (status.IsError()) { |
| 515 return Status( | 517 return Status( |
| 516 kUnknownError, | 518 kUnknownError, |
| 517 base::StringPrintf("cannot process extension #%" PRIuS, i + 1), | 519 base::StringPrintf("cannot process extension #%" PRIuS, i + 1), |
| 518 status); | 520 status); |
| 519 } | 521 } |
| 520 extension_paths.push_back(path.value()); | 522 extension_paths.push_back(path.value()); |
| 521 if (bg_page.length()) | 523 if (bg_page.length()) |
| 522 bg_pages_tmp.push_back(bg_page); | 524 bg_pages_tmp.push_back(bg_page); |
| 523 } | 525 } |
| 524 | 526 |
| 525 if (include_automation_extension) { | 527 if (include_automation_extension) { |
| 526 base::FilePath automation_extension; | 528 base::FilePath automation_extension; |
| 527 Status status = UnpackAutomationExtension(temp_dir, &automation_extension); | 529 Status status = UnpackAutomationExtension(temp_dir, &automation_extension); |
| 528 if (status.IsError()) | 530 if (status.IsError()) |
| 529 return status; | 531 return status; |
| 530 if (command->HasSwitch("disable-extensions")) { | 532 if (switches->HasSwitch("disable-extensions")) { |
| 531 command->AppendSwitchNative("load-component-extension", | 533 UpdateExtensionSwitch(switches, "load-component-extension", |
| 532 automation_extension.value()); | 534 automation_extension.value()); |
| 533 } else { | 535 } else { |
| 534 extension_paths.push_back(automation_extension.value()); | 536 extension_paths.push_back(automation_extension.value()); |
| 535 } | 537 } |
| 536 } | 538 } |
| 537 | 539 |
| 538 if (extension_paths.size()) { | 540 if (extension_paths.size()) { |
| 539 base::FilePath::StringType extension_paths_value = JoinString( | 541 base::FilePath::StringType extension_paths_value = JoinString( |
| 540 extension_paths, FILE_PATH_LITERAL(',')); | 542 extension_paths, FILE_PATH_LITERAL(',')); |
| 541 command->AppendSwitchNative("load-extension", extension_paths_value); | 543 UpdateExtensionSwitch(switches, "load-extension", extension_paths_value); |
| 542 } | 544 } |
| 543 bg_pages->swap(bg_pages_tmp); | 545 bg_pages->swap(bg_pages_tmp); |
| 544 return Status(kOk); | 546 return Status(kOk); |
| 545 } | 547 } |
| 546 | 548 |
| 547 Status WritePrefsFile( | 549 Status WritePrefsFile( |
| 548 const std::string& template_string, | 550 const std::string& template_string, |
| 549 const base::DictionaryValue* custom_prefs, | 551 const base::DictionaryValue* custom_prefs, |
| 550 const base::FilePath& path) { | 552 const base::FilePath& path) { |
| 551 int code; | 553 int code; |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 599 // Write empty "First Run" file, otherwise Chrome will wipe the default | 601 // Write empty "First Run" file, otherwise Chrome will wipe the default |
| 600 // profile that was written. | 602 // profile that was written. |
| 601 if (file_util::WriteFile( | 603 if (file_util::WriteFile( |
| 602 user_data_dir.AppendASCII("First Run"), "", 0) != 0) { | 604 user_data_dir.AppendASCII("First Run"), "", 0) != 0) { |
| 603 return Status(kUnknownError, "failed to write first run file"); | 605 return Status(kUnknownError, "failed to write first run file"); |
| 604 } | 606 } |
| 605 return Status(kOk); | 607 return Status(kOk); |
| 606 } | 608 } |
| 607 | 609 |
| 608 } // namespace internal | 610 } // namespace internal |
| OLD | NEW |