| 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 "base/base64.h" | 7 #include "base/base64.h" |
| 8 #include "base/command_line.h" | 8 #include "base/command_line.h" |
| 9 #include "base/file_util.h" | 9 #include "base/file_util.h" |
| 10 #include "base/files/file_path.h" | 10 #include "base/files/file_path.h" |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 49 | 49 |
| 50 base::FilePath extension_dir = temp_dir.AppendASCII("internal"); | 50 base::FilePath extension_dir = temp_dir.AppendASCII("internal"); |
| 51 if (!zip::Unzip(extension_zip, extension_dir)) | 51 if (!zip::Unzip(extension_zip, extension_dir)) |
| 52 return Status(kUnknownError, "failed to unzip automation extension"); | 52 return Status(kUnknownError, "failed to unzip automation extension"); |
| 53 | 53 |
| 54 *automation_extension = extension_dir; | 54 *automation_extension = extension_dir; |
| 55 return Status(kOk); | 55 return Status(kOk); |
| 56 } | 56 } |
| 57 | 57 |
| 58 Status PrepareCommandLine(int port, | 58 Status PrepareCommandLine(int port, |
| 59 const base::FilePath& exe, | 59 const Capabilities& capabilities, |
| 60 const base::ListValue* args, | |
| 61 const base::ListValue* extensions, | |
| 62 const base::DictionaryValue* prefs, | |
| 63 const base::DictionaryValue* local_state, | |
| 64 CommandLine* prepared_command, | 60 CommandLine* prepared_command, |
| 65 base::ScopedTempDir* user_data_dir, | 61 base::ScopedTempDir* user_data_dir, |
| 66 base::ScopedTempDir* extension_dir) { | 62 base::ScopedTempDir* extension_dir) { |
| 67 base::FilePath program = exe; | 63 CommandLine command = capabilities.command; |
| 64 base::FilePath program = command.GetProgram(); |
| 68 if (program.empty()) { | 65 if (program.empty()) { |
| 69 if (!FindChrome(&program)) | 66 if (!FindChrome(&program)) |
| 70 return Status(kUnknownError, "cannot find Chrome binary"); | 67 return Status(kUnknownError, "cannot find Chrome binary"); |
| 68 command.SetProgram(program); |
| 69 } else if (!file_util::PathExists(program)) { |
| 70 return Status(kUnknownError, |
| 71 base::StringPrintf("no chrome binary at %" PRFilePath, |
| 72 program.value().c_str())); |
| 71 } | 73 } |
| 72 LOG(INFO) << "Using chrome from " << program.value(); | 74 LOG(INFO) << "Using chrome from " << program.value(); |
| 73 | 75 |
| 74 CommandLine command(program); | |
| 75 command.AppendSwitchASCII("remote-debugging-port", base::IntToString(port)); | 76 command.AppendSwitchASCII("remote-debugging-port", base::IntToString(port)); |
| 76 command.AppendSwitch("no-first-run"); | 77 command.AppendSwitch("no-first-run"); |
| 77 command.AppendSwitch("enable-logging"); | 78 command.AppendSwitch("enable-logging"); |
| 78 command.AppendSwitchASCII("logging-level", "1"); | 79 command.AppendSwitchASCII("logging-level", "1"); |
| 79 command.AppendArg("data:text/html;charset=utf-8,"); | 80 command.AppendArg("data:text/html;charset=utf-8,"); |
| 80 | 81 |
| 81 if (args) { | |
| 82 Status status = internal::ProcessCommandLineArgs(args, &command); | |
| 83 if (status.IsError()) | |
| 84 return status; | |
| 85 } | |
| 86 | |
| 87 if (!command.HasSwitch("user-data-dir")) { | 82 if (!command.HasSwitch("user-data-dir")) { |
| 88 if (!user_data_dir->CreateUniqueTempDir()) | 83 if (!user_data_dir->CreateUniqueTempDir()) |
| 89 return Status(kUnknownError, "cannot create temp dir for user data dir"); | 84 return Status(kUnknownError, "cannot create temp dir for user data dir"); |
| 90 command.AppendSwitchPath("user-data-dir", user_data_dir->path()); | 85 command.AppendSwitchPath("user-data-dir", user_data_dir->path()); |
| 91 Status status = internal::PrepareUserDataDir( | 86 Status status = internal::PrepareUserDataDir( |
| 92 user_data_dir->path(), prefs, local_state); | 87 user_data_dir->path(), capabilities.prefs.get(), |
| 88 capabilities.local_state.get()); |
| 93 if (status.IsError()) | 89 if (status.IsError()) |
| 94 return status; | 90 return status; |
| 95 } | 91 } |
| 96 | 92 |
| 97 if (!extension_dir->CreateUniqueTempDir()) { | 93 if (!extension_dir->CreateUniqueTempDir()) { |
| 98 return Status(kUnknownError, | 94 return Status(kUnknownError, |
| 99 "cannot create temp dir for unpacking extensions"); | 95 "cannot create temp dir for unpacking extensions"); |
| 100 } | 96 } |
| 101 Status status = internal::ProcessExtensions( | 97 Status status = internal::ProcessExtensions( |
| 102 extensions, extension_dir->path(), true, &command); | 98 capabilities.extensions, extension_dir->path(), true, &command); |
| 103 if (status.IsError()) | 99 if (status.IsError()) |
| 104 return status; | 100 return status; |
| 105 | 101 |
| 106 *prepared_command = command; | 102 *prepared_command = command; |
| 107 return Status(kOk); | 103 return Status(kOk); |
| 108 } | 104 } |
| 109 | 105 |
| 110 Status ParseAndCheckVersion(const std::string& devtools_version, | 106 Status ParseAndCheckVersion(const std::string& devtools_version, |
| 111 std::string* version, | 107 std::string* version, |
| 112 int* build_no) { | 108 int* build_no) { |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 174 client->GetWebViewsInfo(&views_info); | 170 client->GetWebViewsInfo(&views_info); |
| 175 if (views_info.GetSize()) { | 171 if (views_info.GetSize()) { |
| 176 *user_client = client.Pass(); | 172 *user_client = client.Pass(); |
| 177 return Status(kOk); | 173 return Status(kOk); |
| 178 } | 174 } |
| 179 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100)); | 175 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100)); |
| 180 } | 176 } |
| 181 return Status(kUnknownError, "unable to discover open pages"); | 177 return Status(kUnknownError, "unable to discover open pages"); |
| 182 } | 178 } |
| 183 | 179 |
| 184 } // namespace | |
| 185 | |
| 186 Status LaunchDesktopChrome(URLRequestContextGetter* context_getter, | 180 Status LaunchDesktopChrome(URLRequestContextGetter* context_getter, |
| 187 int port, | 181 int port, |
| 188 const SyncWebSocketFactory& socket_factory, | 182 const SyncWebSocketFactory& socket_factory, |
| 189 const base::FilePath& exe, | 183 const Capabilities& capabilities, |
| 190 const base::ListValue* args, | |
| 191 const base::ListValue* extensions, | |
| 192 const base::DictionaryValue* prefs, | |
| 193 const base::DictionaryValue* local_state, | |
| 194 const std::string& log_path, | |
| 195 scoped_ptr<Chrome>* chrome) { | 184 scoped_ptr<Chrome>* chrome) { |
| 196 CommandLine command(CommandLine::NO_PROGRAM); | 185 CommandLine command(CommandLine::NO_PROGRAM); |
| 197 base::ScopedTempDir user_data_dir; | 186 base::ScopedTempDir user_data_dir; |
| 198 base::ScopedTempDir extension_dir; | 187 base::ScopedTempDir extension_dir; |
| 199 PrepareCommandLine(port, exe, args, extensions, prefs, local_state, | 188 PrepareCommandLine(port, capabilities, |
| 200 &command, &user_data_dir, &extension_dir); | 189 &command, &user_data_dir, &extension_dir); |
| 201 base::LaunchOptions options; | 190 base::LaunchOptions options; |
| 202 | 191 |
| 203 #if !defined(OS_WIN) | 192 #if !defined(OS_WIN) |
| 204 base::EnvironmentVector environ; | 193 base::EnvironmentVector environ; |
| 205 if (!log_path.empty()) { | 194 if (!capabilities.log_path.empty()) { |
| 206 environ.push_back(base::EnvironmentVector::value_type("CHROME_LOG_FILE", | 195 environ.push_back( |
| 207 log_path)); | 196 base::EnvironmentVector::value_type("CHROME_LOG_FILE", |
| 197 capabilities.log_path)); |
| 208 options.environ = &environ; | 198 options.environ = &environ; |
| 209 } | 199 } |
| 210 #endif | 200 #endif |
| 211 | 201 |
| 212 base::ProcessHandle process; | 202 base::ProcessHandle process; |
| 213 if (!base::LaunchProcess(command, options, &process)) | 203 if (!base::LaunchProcess(command, options, &process)) |
| 214 return Status(kUnknownError, "chrome failed to start"); | 204 return Status(kUnknownError, "chrome failed to start"); |
| 215 | 205 |
| 216 scoped_ptr<DevToolsHttpClient> devtools_client; | 206 scoped_ptr<DevToolsHttpClient> devtools_client; |
| 217 std::string version; | 207 std::string version; |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 256 } | 246 } |
| 257 chrome->reset(new ChromeDesktopImpl( | 247 chrome->reset(new ChromeDesktopImpl( |
| 258 devtools_client.Pass(), version, build_no, process, &user_data_dir, | 248 devtools_client.Pass(), version, build_no, process, &user_data_dir, |
| 259 &extension_dir)); | 249 &extension_dir)); |
| 260 return Status(kOk); | 250 return Status(kOk); |
| 261 } | 251 } |
| 262 | 252 |
| 263 Status LaunchAndroidChrome(URLRequestContextGetter* context_getter, | 253 Status LaunchAndroidChrome(URLRequestContextGetter* context_getter, |
| 264 int port, | 254 int port, |
| 265 const SyncWebSocketFactory& socket_factory, | 255 const SyncWebSocketFactory& socket_factory, |
| 266 const std::string& package_name, | 256 const Capabilities& capabilities, |
| 267 scoped_ptr<Chrome>* chrome) { | 257 scoped_ptr<Chrome>* chrome) { |
| 268 // TODO(frankf): Figure out how this should be installed to | 258 // TODO(frankf): Figure out how this should be installed to |
| 269 // make this work for all platforms. | 259 // make this work for all platforms. |
| 270 base::FilePath adb_commands(FILE_PATH_LITERAL("adb_commands.py")); | 260 base::FilePath adb_commands(FILE_PATH_LITERAL("adb_commands.py")); |
| 271 CommandLine command(adb_commands); | 261 CommandLine command(adb_commands); |
| 272 command.AppendSwitchASCII("package", package_name); | 262 command.AppendSwitchASCII("package", capabilities.android_package); |
| 273 command.AppendSwitch("launch"); | 263 command.AppendSwitch("launch"); |
| 274 command.AppendSwitchASCII("port", base::IntToString(port)); | 264 command.AppendSwitchASCII("port", base::IntToString(port)); |
| 275 | 265 |
| 276 std::string output; | 266 std::string output; |
| 277 if (!base::GetAppOutput(command, &output)) { | 267 if (!base::GetAppOutput(command, &output)) { |
| 278 if (output.empty()) | 268 if (output.empty()) |
| 279 return Status( | 269 return Status( |
| 280 kUnknownError, | 270 kUnknownError, |
| 281 "failed to run adb_commands.py. Make sure it is set in PATH."); | 271 "failed to run adb_commands.py. Make sure it is set in PATH."); |
| 282 else | 272 else |
| 283 return Status(kUnknownError, "android app failed to start.\n" + output); | 273 return Status(kUnknownError, "android app failed to start.\n" + output); |
| 284 } | 274 } |
| 285 | 275 |
| 286 scoped_ptr<DevToolsHttpClient> devtools_client; | 276 scoped_ptr<DevToolsHttpClient> devtools_client; |
| 287 std::string version; | 277 std::string version; |
| 288 int build_no; | 278 int build_no; |
| 289 Status status = WaitForDevToolsAndCheckVersion( | 279 Status status = WaitForDevToolsAndCheckVersion( |
| 290 port, context_getter, socket_factory, &devtools_client, &version, | 280 port, context_getter, socket_factory, &devtools_client, &version, |
| 291 &build_no); | 281 &build_no); |
| 292 if (status.IsError()) | 282 if (status.IsError()) |
| 293 return status; | 283 return status; |
| 294 | 284 |
| 295 chrome->reset(new ChromeAndroidImpl( | 285 chrome->reset(new ChromeAndroidImpl( |
| 296 devtools_client.Pass(), version, build_no)); | 286 devtools_client.Pass(), version, build_no)); |
| 297 return Status(kOk); | 287 return Status(kOk); |
| 298 } | 288 } |
| 299 | 289 |
| 290 } // namespace |
| 291 |
| 292 Status LaunchChrome(URLRequestContextGetter* context_getter, |
| 293 int port, |
| 294 const SyncWebSocketFactory& socket_factory, |
| 295 const Capabilities& capabilities, |
| 296 scoped_ptr<Chrome>* chrome) { |
| 297 if (capabilities.IsAndroid()) { |
| 298 return LaunchAndroidChrome( |
| 299 context_getter, port, socket_factory, capabilities, chrome); |
| 300 } else { |
| 301 return LaunchDesktopChrome( |
| 302 context_getter, port, socket_factory, capabilities, chrome); |
| 303 } |
| 304 } |
| 305 |
| 300 namespace internal { | 306 namespace internal { |
| 301 | 307 |
| 302 Status ProcessCommandLineArgs(const base::ListValue* args, | 308 Status ProcessExtensions(const std::vector<std::string>& extensions, |
| 303 CommandLine* command) { | |
| 304 for (size_t i = 0; i < args->GetSize(); ++i) { | |
| 305 std::string arg_string; | |
| 306 if (!args->GetString(i, &arg_string)) | |
| 307 return Status(kUnknownError, "invalid chrome command line argument"); | |
| 308 size_t separator_index = arg_string.find("="); | |
| 309 if (separator_index != std::string::npos) { | |
| 310 CommandLine::StringType arg_string_native; | |
| 311 if (!args->GetString(i, &arg_string_native)) | |
| 312 return Status(kUnknownError, "invalid chrome command line argument"); | |
| 313 command->AppendSwitchNative( | |
| 314 arg_string.substr(0, separator_index), | |
| 315 arg_string_native.substr(separator_index + 1)); | |
| 316 } else { | |
| 317 command->AppendSwitch(arg_string); | |
| 318 } | |
| 319 } | |
| 320 return Status(kOk); | |
| 321 } | |
| 322 | |
| 323 Status ProcessExtensions(const base::ListValue* extensions, | |
| 324 const base::FilePath& temp_dir, | 309 const base::FilePath& temp_dir, |
| 325 bool include_automation_extension, | 310 bool include_automation_extension, |
| 326 CommandLine* command) { | 311 CommandLine* command) { |
| 327 std::vector<base::FilePath::StringType> extension_paths; | 312 std::vector<base::FilePath::StringType> extension_paths; |
| 328 for (size_t i = 0; i < (extensions ? extensions->GetSize() : 0); ++i) { | 313 size_t count = 0; |
| 314 for (std::vector<std::string>::const_iterator it = extensions.begin(); |
| 315 it != extensions.end(); ++it) { |
| 329 std::string extension_base64; | 316 std::string extension_base64; |
| 330 if (!extensions->GetString(i, &extension_base64)) { | |
| 331 return Status(kUnknownError, | |
| 332 "each extension must be a base64 encoded string"); | |
| 333 } | |
| 334 | |
| 335 // Decodes extension string. | 317 // Decodes extension string. |
| 336 // Some WebDriver client base64 encoders follow RFC 1521, which require that | 318 // Some WebDriver client base64 encoders follow RFC 1521, which require that |
| 337 // 'encoded lines be no more than 76 characters long'. Just remove any | 319 // 'encoded lines be no more than 76 characters long'. Just remove any |
| 338 // newlines. | 320 // newlines. |
| 339 RemoveChars(extension_base64, "\n", &extension_base64); | 321 RemoveChars(*it, "\n", &extension_base64); |
| 340 std::string decoded_extension; | 322 std::string decoded_extension; |
| 341 if (!base::Base64Decode(extension_base64, &decoded_extension)) | 323 if (!base::Base64Decode(extension_base64, &decoded_extension)) |
| 342 return Status(kUnknownError, "failed to base64 decode extension"); | 324 return Status(kUnknownError, "failed to base64 decode extension"); |
| 343 | 325 |
| 344 // Writes decoded extension into a temporary .crx file. | 326 // Writes decoded extension into a temporary .crx file. |
| 345 base::ScopedTempDir temp_crx_dir; | 327 base::ScopedTempDir temp_crx_dir; |
| 346 if (!temp_crx_dir.CreateUniqueTempDir()) | 328 if (!temp_crx_dir.CreateUniqueTempDir()) |
| 347 return Status(kUnknownError, | 329 return Status(kUnknownError, |
| 348 "cannot create temp dir for writing extension CRX file"); | 330 "cannot create temp dir for writing extension CRX file"); |
| 349 base::FilePath extension_crx = temp_crx_dir.path().AppendASCII("temp.crx"); | 331 base::FilePath extension_crx = temp_crx_dir.path().AppendASCII("temp.crx"); |
| 350 int size = static_cast<int>(decoded_extension.length()); | 332 int size = static_cast<int>(decoded_extension.length()); |
| 351 if (file_util::WriteFile(extension_crx, decoded_extension.c_str(), size) | 333 if (file_util::WriteFile(extension_crx, decoded_extension.c_str(), size) |
| 352 != size) | 334 != size) { |
| 353 return Status(kUnknownError, "failed to write extension file"); | 335 return Status(kUnknownError, "failed to write extension file"); |
| 336 } |
| 354 | 337 |
| 355 // Unzips the temporary .crx file. | 338 // Unzips the temporary .crx file. |
| 339 count++; |
| 356 base::FilePath extension_dir = temp_dir.AppendASCII( | 340 base::FilePath extension_dir = temp_dir.AppendASCII( |
| 357 base::StringPrintf("extension%" PRIuS, i)); | 341 base::StringPrintf("extension%" PRIuS, count)); |
| 358 if (!zip::Unzip(extension_crx, extension_dir)) | 342 if (!zip::Unzip(extension_crx, extension_dir)) |
| 359 return Status(kUnknownError, "failed to unzip the extension CRX file"); | 343 return Status(kUnknownError, "failed to unzip the extension CRX file"); |
| 360 extension_paths.push_back(extension_dir.value()); | 344 extension_paths.push_back(extension_dir.value()); |
| 361 } | 345 } |
| 362 | 346 |
| 363 if (include_automation_extension) { | 347 if (include_automation_extension) { |
| 364 base::FilePath automation_extension; | 348 base::FilePath automation_extension; |
| 365 Status status = UnpackAutomationExtension(temp_dir, &automation_extension); | 349 Status status = UnpackAutomationExtension(temp_dir, &automation_extension); |
| 366 if (status.IsError()) | 350 if (status.IsError()) |
| 367 return status; | 351 return status; |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 427 // Write empty "First Run" file, otherwise Chrome will wipe the default | 411 // Write empty "First Run" file, otherwise Chrome will wipe the default |
| 428 // profile that was written. | 412 // profile that was written. |
| 429 if (file_util::WriteFile( | 413 if (file_util::WriteFile( |
| 430 user_data_dir.AppendASCII("First Run"), "", 0) != 0) { | 414 user_data_dir.AppendASCII("First Run"), "", 0) != 0) { |
| 431 return Status(kUnknownError, "failed to write first run file"); | 415 return Status(kUnknownError, "failed to write first run file"); |
| 432 } | 416 } |
| 433 return Status(kOk); | 417 return Status(kOk); |
| 434 } | 418 } |
| 435 | 419 |
| 436 } // namespace internal | 420 } // namespace internal |
| OLD | NEW |