Cast: Stop logging kVideoFrameSentToEncoder and rename a couple events.
[chromium-blink-merge.git] / chrome / test / chromedriver / chrome_launcher.cc
blob24037f5c12df1cc6ad4521d6b3a63871c22514a7
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
3 // found in the LICENSE file.
5 #include "chrome/test/chromedriver/chrome_launcher.h"
7 #include <algorithm>
8 #include <vector>
10 #include "base/base64.h"
11 #include "base/basictypes.h"
12 #include "base/command_line.h"
13 #include "base/file_util.h"
14 #include "base/files/file_path.h"
15 #include "base/files/scoped_file.h"
16 #include "base/format_macros.h"
17 #include "base/json/json_reader.h"
18 #include "base/json/json_writer.h"
19 #include "base/logging.h"
20 #include "base/process/kill.h"
21 #include "base/process/launch.h"
22 #include "base/strings/string_number_conversions.h"
23 #include "base/strings/string_util.h"
24 #include "base/strings/stringprintf.h"
25 #include "base/strings/utf_string_conversions.h"
26 #include "base/threading/platform_thread.h"
27 #include "base/time/time.h"
28 #include "base/values.h"
29 #include "chrome/common/chrome_constants.h"
30 #include "chrome/test/chromedriver/chrome/chrome_android_impl.h"
31 #include "chrome/test/chromedriver/chrome/chrome_desktop_impl.h"
32 #include "chrome/test/chromedriver/chrome/chrome_existing_impl.h"
33 #include "chrome/test/chromedriver/chrome/chrome_finder.h"
34 #include "chrome/test/chromedriver/chrome/device_manager.h"
35 #include "chrome/test/chromedriver/chrome/devtools_http_client.h"
36 #include "chrome/test/chromedriver/chrome/embedded_automation_extension.h"
37 #include "chrome/test/chromedriver/chrome/status.h"
38 #include "chrome/test/chromedriver/chrome/user_data_dir.h"
39 #include "chrome/test/chromedriver/chrome/version.h"
40 #include "chrome/test/chromedriver/chrome/web_view.h"
41 #include "chrome/test/chromedriver/net/port_server.h"
42 #include "chrome/test/chromedriver/net/url_request_context_getter.h"
43 #include "crypto/rsa_private_key.h"
44 #include "crypto/sha2.h"
45 #include "third_party/zlib/google/zip.h"
47 #if defined(OS_POSIX)
48 #include <fcntl.h>
49 #include <sys/stat.h>
50 #include <sys/types.h>
51 #endif
53 namespace {
55 const char* kCommonSwitches[] = {
56 "ignore-certificate-errors", "metrics-recording-only"};
58 #if defined(OS_LINUX)
59 const char* kEnableCrashReport = "enable-crash-reporter-for-testing";
60 #endif
62 Status UnpackAutomationExtension(const base::FilePath& temp_dir,
63 base::FilePath* automation_extension) {
64 std::string decoded_extension;
65 if (!base::Base64Decode(kAutomationExtension, &decoded_extension))
66 return Status(kUnknownError, "failed to base64decode automation extension");
68 base::FilePath extension_zip = temp_dir.AppendASCII("internal.zip");
69 int size = static_cast<int>(decoded_extension.length());
70 if (base::WriteFile(extension_zip, decoded_extension.c_str(), size)
71 != size) {
72 return Status(kUnknownError, "failed to write automation extension zip");
75 base::FilePath extension_dir = temp_dir.AppendASCII("internal");
76 if (!zip::Unzip(extension_zip, extension_dir))
77 return Status(kUnknownError, "failed to unzip automation extension");
79 *automation_extension = extension_dir;
80 return Status(kOk);
83 Status PrepareCommandLine(int port,
84 const Capabilities& capabilities,
85 CommandLine* prepared_command,
86 base::ScopedTempDir* user_data_dir,
87 base::ScopedTempDir* extension_dir,
88 std::vector<std::string>* extension_bg_pages) {
89 base::FilePath program = capabilities.binary;
90 if (program.empty()) {
91 if (!FindChrome(&program))
92 return Status(kUnknownError, "cannot find Chrome binary");
93 } else if (!base::PathExists(program)) {
94 return Status(kUnknownError,
95 base::StringPrintf("no chrome binary at %" PRFilePath,
96 program.value().c_str()));
98 CommandLine command(program);
99 Switches switches;
101 for (size_t i = 0; i < arraysize(kCommonSwitches); ++i)
102 switches.SetSwitch(kCommonSwitches[i]);
103 switches.SetSwitch("disable-hang-monitor");
104 switches.SetSwitch("disable-prompt-on-repost");
105 switches.SetSwitch("disable-sync");
106 switches.SetSwitch("no-first-run");
107 switches.SetSwitch("disable-background-networking");
108 switches.SetSwitch("disable-web-resources");
109 switches.SetSwitch("safebrowsing-disable-auto-update");
110 switches.SetSwitch("safebrowsing-disable-download-protection");
111 switches.SetSwitch("disable-client-side-phishing-detection");
112 switches.SetSwitch("disable-component-update");
113 switches.SetSwitch("disable-default-apps");
114 switches.SetSwitch("enable-logging");
115 switches.SetSwitch("logging-level", "1");
116 switches.SetSwitch("password-store", "basic");
117 switches.SetSwitch("use-mock-keychain");
118 switches.SetSwitch("remote-debugging-port", base::IntToString(port));
120 for (std::set<std::string>::const_iterator iter =
121 capabilities.exclude_switches.begin();
122 iter != capabilities.exclude_switches.end();
123 ++iter) {
124 switches.RemoveSwitch(*iter);
126 switches.SetFromSwitches(capabilities.switches);
128 if (!switches.HasSwitch("user-data-dir")) {
129 command.AppendArg("data:,");
130 if (!user_data_dir->CreateUniqueTempDir())
131 return Status(kUnknownError, "cannot create temp dir for user data dir");
132 switches.SetSwitch("user-data-dir", user_data_dir->path().value());
133 Status status = internal::PrepareUserDataDir(
134 user_data_dir->path(), capabilities.prefs.get(),
135 capabilities.local_state.get());
136 if (status.IsError())
137 return status;
140 if (!extension_dir->CreateUniqueTempDir()) {
141 return Status(kUnknownError,
142 "cannot create temp dir for unpacking extensions");
144 Status status = internal::ProcessExtensions(capabilities.extensions,
145 extension_dir->path(),
146 true,
147 &switches,
148 extension_bg_pages);
149 if (status.IsError())
150 return status;
151 switches.AppendToCommandLine(&command);
152 *prepared_command = command;
153 return Status(kOk);
156 Status WaitForDevToolsAndCheckVersion(
157 const NetAddress& address,
158 URLRequestContextGetter* context_getter,
159 const SyncWebSocketFactory& socket_factory,
160 scoped_ptr<DevToolsHttpClient>* user_client) {
161 scoped_ptr<DevToolsHttpClient> client(new DevToolsHttpClient(
162 address, context_getter, socket_factory));
163 base::TimeTicks deadline =
164 base::TimeTicks::Now() + base::TimeDelta::FromSeconds(60);
165 Status status = client->Init(deadline - base::TimeTicks::Now());
166 if (status.IsError())
167 return status;
168 if (client->browser_info()->build_no < kMinimumSupportedChromeBuildNo) {
169 return Status(kUnknownError, "Chrome version must be >= " +
170 GetMinimumSupportedChromeVersion());
173 while (base::TimeTicks::Now() < deadline) {
174 WebViewsInfo views_info;
175 client->GetWebViewsInfo(&views_info);
176 for (size_t i = 0; i < views_info.GetSize(); ++i) {
177 if (views_info.Get(i).type == WebViewInfo::kPage) {
178 *user_client = client.Pass();
179 return Status(kOk);
182 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(50));
184 return Status(kUnknownError, "unable to discover open pages");
187 Status LaunchExistingChromeSession(
188 URLRequestContextGetter* context_getter,
189 const SyncWebSocketFactory& socket_factory,
190 const Capabilities& capabilities,
191 ScopedVector<DevToolsEventListener>& devtools_event_listeners,
192 scoped_ptr<Chrome>* chrome) {
193 Status status(kOk);
194 scoped_ptr<DevToolsHttpClient> devtools_client;
195 status = WaitForDevToolsAndCheckVersion(
196 capabilities.debugger_address, context_getter, socket_factory,
197 &devtools_client);
198 if (status.IsError()) {
199 return Status(kUnknownError, "cannot connect to chrome at " +
200 capabilities.debugger_address.ToString(),
201 status);
203 chrome->reset(new ChromeExistingImpl(devtools_client.Pass(),
204 devtools_event_listeners));
205 return Status(kOk);
208 Status LaunchDesktopChrome(
209 URLRequestContextGetter* context_getter,
210 int port,
211 scoped_ptr<PortReservation> port_reservation,
212 const SyncWebSocketFactory& socket_factory,
213 const Capabilities& capabilities,
214 ScopedVector<DevToolsEventListener>& devtools_event_listeners,
215 scoped_ptr<Chrome>* chrome) {
216 CommandLine command(CommandLine::NO_PROGRAM);
217 base::ScopedTempDir user_data_dir;
218 base::ScopedTempDir extension_dir;
219 std::vector<std::string> extension_bg_pages;
220 Status status = PrepareCommandLine(port,
221 capabilities,
222 &command,
223 &user_data_dir,
224 &extension_dir,
225 &extension_bg_pages);
226 if (status.IsError())
227 return status;
229 base::LaunchOptions options;
231 #if defined(OS_LINUX)
232 // If minidump path is set in the capability, enable minidump for crashes.
233 if (!capabilities.minidump_path.empty()) {
234 VLOG(0) << "Minidump generation specified. Will save dumps to: "
235 << capabilities.minidump_path;
237 options.environ["CHROME_HEADLESS"] = 1;
238 options.environ["BREAKPAD_DUMP_LOCATION"] = capabilities.minidump_path;
240 if (!command.HasSwitch(kEnableCrashReport))
241 command.AppendSwitch(kEnableCrashReport);
244 // We need to allow new privileges so that chrome's setuid sandbox can run.
245 options.allow_new_privs = true;
246 #endif
248 #if !defined(OS_WIN)
249 if (!capabilities.log_path.empty())
250 options.environ["CHROME_LOG_FILE"] = capabilities.log_path;
251 if (capabilities.detach)
252 options.new_process_group = true;
253 #endif
255 #if defined(OS_POSIX)
256 base::FileHandleMappingVector no_stderr;
257 base::ScopedFD devnull;
258 if (!CommandLine::ForCurrentProcess()->HasSwitch("verbose")) {
259 // Redirect stderr to /dev/null, so that Chrome log spew doesn't confuse
260 // users.
261 devnull.reset(HANDLE_EINTR(open("/dev/null", O_WRONLY)));
262 if (!devnull.is_valid())
263 return Status(kUnknownError, "couldn't open /dev/null");
264 no_stderr.push_back(std::make_pair(devnull.get(), STDERR_FILENO));
265 options.fds_to_remap = &no_stderr;
267 #endif
269 #if defined(OS_WIN)
270 std::string command_string = base::WideToUTF8(command.GetCommandLineString());
271 #else
272 std::string command_string = command.GetCommandLineString();
273 #endif
274 VLOG(0) << "Launching chrome: " << command_string;
275 base::ProcessHandle process;
276 if (!base::LaunchProcess(command, options, &process))
277 return Status(kUnknownError, "chrome failed to start");
279 scoped_ptr<DevToolsHttpClient> devtools_client;
280 status = WaitForDevToolsAndCheckVersion(
281 NetAddress(port), context_getter, socket_factory, &devtools_client);
283 if (status.IsError()) {
284 int exit_code;
285 base::TerminationStatus chrome_status =
286 base::GetTerminationStatus(process, &exit_code);
287 if (chrome_status != base::TERMINATION_STATUS_STILL_RUNNING) {
288 std::string termination_reason;
289 switch (chrome_status) {
290 case base::TERMINATION_STATUS_NORMAL_TERMINATION:
291 termination_reason = "exited normally";
292 break;
293 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
294 termination_reason = "exited abnormally";
295 break;
296 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED:
297 termination_reason = "was killed";
298 break;
299 case base::TERMINATION_STATUS_PROCESS_CRASHED:
300 termination_reason = "crashed";
301 break;
302 default:
303 termination_reason = "unknown";
304 break;
306 return Status(kUnknownError,
307 "Chrome failed to start: " + termination_reason);
309 if (!base::KillProcess(process, 0, true)) {
310 int exit_code;
311 if (base::GetTerminationStatus(process, &exit_code) ==
312 base::TERMINATION_STATUS_STILL_RUNNING)
313 return Status(kUnknownError, "cannot kill Chrome", status);
315 return status;
317 scoped_ptr<ChromeDesktopImpl> chrome_desktop(
318 new ChromeDesktopImpl(devtools_client.Pass(),
319 devtools_event_listeners,
320 port_reservation.Pass(),
321 process,
322 command,
323 &user_data_dir,
324 &extension_dir));
325 for (size_t i = 0; i < extension_bg_pages.size(); ++i) {
326 VLOG(0) << "Waiting for extension bg page load: " << extension_bg_pages[i];
327 scoped_ptr<WebView> web_view;
328 Status status = chrome_desktop->WaitForPageToLoad(
329 extension_bg_pages[i], base::TimeDelta::FromSeconds(10), &web_view);
330 if (status.IsError()) {
331 return Status(kUnknownError,
332 "failed to wait for extension background page to load: " +
333 extension_bg_pages[i],
334 status);
337 *chrome = chrome_desktop.Pass();
338 return Status(kOk);
341 Status LaunchAndroidChrome(
342 URLRequestContextGetter* context_getter,
343 int port,
344 scoped_ptr<PortReservation> port_reservation,
345 const SyncWebSocketFactory& socket_factory,
346 const Capabilities& capabilities,
347 ScopedVector<DevToolsEventListener>& devtools_event_listeners,
348 DeviceManager* device_manager,
349 scoped_ptr<Chrome>* chrome) {
350 Status status(kOk);
351 scoped_ptr<Device> device;
352 if (capabilities.android_device_serial.empty()) {
353 status = device_manager->AcquireDevice(&device);
354 } else {
355 status = device_manager->AcquireSpecificDevice(
356 capabilities.android_device_serial, &device);
358 if (status.IsError())
359 return status;
361 Switches switches(capabilities.switches);
362 for (size_t i = 0; i < arraysize(kCommonSwitches); ++i)
363 switches.SetSwitch(kCommonSwitches[i]);
364 switches.SetSwitch("disable-fre");
365 switches.SetSwitch("enable-remote-debugging");
366 status = device->SetUp(capabilities.android_package,
367 capabilities.android_activity,
368 capabilities.android_process,
369 switches.ToString(),
370 capabilities.android_use_running_app,
371 port);
372 if (status.IsError()) {
373 device->TearDown();
374 return status;
377 scoped_ptr<DevToolsHttpClient> devtools_client;
378 status = WaitForDevToolsAndCheckVersion(NetAddress(port),
379 context_getter,
380 socket_factory,
381 &devtools_client);
382 if (status.IsError()) {
383 device->TearDown();
384 return status;
387 chrome->reset(new ChromeAndroidImpl(devtools_client.Pass(),
388 devtools_event_listeners,
389 port_reservation.Pass(),
390 device.Pass()));
391 return Status(kOk);
394 } // namespace
396 Status LaunchChrome(
397 URLRequestContextGetter* context_getter,
398 const SyncWebSocketFactory& socket_factory,
399 DeviceManager* device_manager,
400 PortServer* port_server,
401 PortManager* port_manager,
402 const Capabilities& capabilities,
403 ScopedVector<DevToolsEventListener>& devtools_event_listeners,
404 scoped_ptr<Chrome>* chrome) {
405 if (capabilities.IsExistingBrowser()) {
406 return LaunchExistingChromeSession(
407 context_getter, socket_factory,
408 capabilities, devtools_event_listeners, chrome);
411 int port = 0;
412 scoped_ptr<PortReservation> port_reservation;
413 Status port_status(kOk);
415 if (capabilities.IsAndroid()) {
416 port_status = port_manager->ReservePortFromPool(&port, &port_reservation);
417 if (port_status.IsError())
418 return Status(kUnknownError, "cannot reserve port for Chrome",
419 port_status);
420 return LaunchAndroidChrome(context_getter,
421 port,
422 port_reservation.Pass(),
423 socket_factory,
424 capabilities,
425 devtools_event_listeners,
426 device_manager,
427 chrome);
428 } else {
429 if (port_server)
430 port_status = port_server->ReservePort(&port, &port_reservation);
431 else
432 port_status = port_manager->ReservePort(&port, &port_reservation);
433 if (port_status.IsError())
434 return Status(kUnknownError, "cannot reserve port for Chrome",
435 port_status);
436 return LaunchDesktopChrome(context_getter,
437 port,
438 port_reservation.Pass(),
439 socket_factory,
440 capabilities,
441 devtools_event_listeners,
442 chrome);
446 namespace internal {
448 void ConvertHexadecimalToIDAlphabet(std::string* id) {
449 for (size_t i = 0; i < id->size(); ++i) {
450 int val;
451 if (base::HexStringToInt(base::StringPiece(id->begin() + i,
452 id->begin() + i + 1),
453 &val)) {
454 (*id)[i] = val + 'a';
455 } else {
456 (*id)[i] = 'a';
461 std::string GenerateExtensionId(const std::string& input) {
462 uint8 hash[16];
463 crypto::SHA256HashString(input, hash, sizeof(hash));
464 std::string output = StringToLowerASCII(base::HexEncode(hash, sizeof(hash)));
465 ConvertHexadecimalToIDAlphabet(&output);
466 return output;
469 Status GetExtensionBackgroundPage(const base::DictionaryValue* manifest,
470 const std::string& id,
471 std::string* bg_page) {
472 std::string bg_page_name;
473 bool persistent = true;
474 manifest->GetBoolean("background.persistent", &persistent);
475 const base::Value* unused_value;
476 if (manifest->Get("background.scripts", &unused_value))
477 bg_page_name = "_generated_background_page.html";
478 manifest->GetString("background.page", &bg_page_name);
479 manifest->GetString("background_page", &bg_page_name);
480 if (bg_page_name.empty() || !persistent)
481 return Status(kOk);
482 *bg_page = "chrome-extension://" + id + "/" + bg_page_name;
483 return Status(kOk);
486 Status ProcessExtension(const std::string& extension,
487 const base::FilePath& temp_dir,
488 base::FilePath* path,
489 std::string* bg_page) {
490 // Decodes extension string.
491 // Some WebDriver client base64 encoders follow RFC 1521, which require that
492 // 'encoded lines be no more than 76 characters long'. Just remove any
493 // newlines.
494 std::string extension_base64;
495 base::RemoveChars(extension, "\n", &extension_base64);
496 std::string decoded_extension;
497 if (!base::Base64Decode(extension_base64, &decoded_extension))
498 return Status(kUnknownError, "cannot base64 decode");
500 // If the file is a crx file, extract the extension's ID from its public key.
501 // Otherwise generate a random public key and use its derived extension ID.
502 std::string public_key;
503 std::string magic_header = decoded_extension.substr(0, 4);
504 if (magic_header.size() != 4)
505 return Status(kUnknownError, "cannot extract magic number");
507 const bool is_crx_file = magic_header == "Cr24";
509 if (is_crx_file) {
510 // Assume a CRX v2 file - see https://developer.chrome.com/extensions/crx.
511 std::string key_len_str = decoded_extension.substr(8, 4);
512 if (key_len_str.size() != 4)
513 return Status(kUnknownError, "cannot extract public key length");
514 uint32 key_len = *reinterpret_cast<const uint32*>(key_len_str.c_str());
515 public_key = decoded_extension.substr(16, key_len);
516 if (key_len != public_key.size())
517 return Status(kUnknownError, "invalid public key length");
518 } else {
519 // Not a CRX file. Generate RSA keypair to get a valid extension id.
520 scoped_ptr<crypto::RSAPrivateKey> key_pair(
521 crypto::RSAPrivateKey::Create(2048));
522 if (!key_pair)
523 return Status(kUnknownError, "cannot generate RSA key pair");
524 std::vector<uint8> public_key_vector;
525 if (!key_pair->ExportPublicKey(&public_key_vector))
526 return Status(kUnknownError, "cannot extract public key");
527 public_key =
528 std::string(reinterpret_cast<char*>(&public_key_vector.front()),
529 public_key_vector.size());
531 std::string public_key_base64;
532 base::Base64Encode(public_key, &public_key_base64);
533 std::string id = GenerateExtensionId(public_key);
535 // Unzip the crx file.
536 base::ScopedTempDir temp_crx_dir;
537 if (!temp_crx_dir.CreateUniqueTempDir())
538 return Status(kUnknownError, "cannot create temp dir");
539 base::FilePath extension_crx = temp_crx_dir.path().AppendASCII("temp.crx");
540 int size = static_cast<int>(decoded_extension.length());
541 if (base::WriteFile(extension_crx, decoded_extension.c_str(), size) !=
542 size) {
543 return Status(kUnknownError, "cannot write file");
545 base::FilePath extension_dir = temp_dir.AppendASCII("extension_" + id);
546 if (!zip::Unzip(extension_crx, extension_dir))
547 return Status(kUnknownError, "cannot unzip");
549 // Parse the manifest and set the 'key' if not already present.
550 base::FilePath manifest_path(extension_dir.AppendASCII("manifest.json"));
551 std::string manifest_data;
552 if (!base::ReadFileToString(manifest_path, &manifest_data))
553 return Status(kUnknownError, "cannot read manifest");
554 scoped_ptr<base::Value> manifest_value(base::JSONReader::Read(manifest_data));
555 base::DictionaryValue* manifest;
556 if (!manifest_value || !manifest_value->GetAsDictionary(&manifest))
557 return Status(kUnknownError, "invalid manifest");
559 std::string manifest_key_base64;
560 if (manifest->GetString("key", &manifest_key_base64)) {
561 // If there is a key in both the header and the manifest, use the key in the
562 // manifest. This allows chromedriver users users who generate dummy crxs
563 // to set the manifest key and have a consistent ID.
564 std::string manifest_key;
565 if (!base::Base64Decode(manifest_key_base64, &manifest_key))
566 return Status(kUnknownError, "'key' in manifest is not base64 encoded");
567 std::string manifest_id = GenerateExtensionId(manifest_key);
568 if (id != manifest_id) {
569 if (is_crx_file) {
570 LOG(WARNING)
571 << "Public key in crx header is different from key in manifest"
572 << std::endl << "key from header: " << public_key_base64
573 << std::endl << "key from manifest: " << manifest_key_base64
574 << std::endl << "generated extension id from header key: " << id
575 << std::endl << "generated extension id from manifest key: "
576 << manifest_id;
578 id = manifest_id;
580 } else {
581 manifest->SetString("key", public_key_base64);
582 base::JSONWriter::Write(manifest, &manifest_data);
583 if (base::WriteFile(
584 manifest_path, manifest_data.c_str(), manifest_data.size()) !=
585 static_cast<int>(manifest_data.size())) {
586 return Status(kUnknownError, "cannot add 'key' to manifest");
590 // Get extension's background page URL, if there is one.
591 std::string bg_page_tmp;
592 Status status = GetExtensionBackgroundPage(manifest, id, &bg_page_tmp);
593 if (status.IsError())
594 return status;
596 *path = extension_dir;
597 if (bg_page_tmp.size())
598 *bg_page = bg_page_tmp;
599 return Status(kOk);
602 void UpdateExtensionSwitch(Switches* switches,
603 const char name[],
604 const base::FilePath::StringType& extension) {
605 base::FilePath::StringType value = switches->GetSwitchValueNative(name);
606 if (value.length())
607 value += FILE_PATH_LITERAL(",");
608 value += extension;
609 switches->SetSwitch(name, value);
612 Status ProcessExtensions(const std::vector<std::string>& extensions,
613 const base::FilePath& temp_dir,
614 bool include_automation_extension,
615 Switches* switches,
616 std::vector<std::string>* bg_pages) {
617 std::vector<std::string> bg_pages_tmp;
618 std::vector<base::FilePath::StringType> extension_paths;
619 for (size_t i = 0; i < extensions.size(); ++i) {
620 base::FilePath path;
621 std::string bg_page;
622 Status status = ProcessExtension(extensions[i], temp_dir, &path, &bg_page);
623 if (status.IsError()) {
624 return Status(
625 kUnknownError,
626 base::StringPrintf("cannot process extension #%" PRIuS, i + 1),
627 status);
629 extension_paths.push_back(path.value());
630 if (bg_page.length())
631 bg_pages_tmp.push_back(bg_page);
634 if (include_automation_extension) {
635 base::FilePath automation_extension;
636 Status status = UnpackAutomationExtension(temp_dir, &automation_extension);
637 if (status.IsError())
638 return status;
639 if (switches->HasSwitch("disable-extensions")) {
640 UpdateExtensionSwitch(switches, "load-component-extension",
641 automation_extension.value());
642 } else {
643 extension_paths.push_back(automation_extension.value());
647 if (extension_paths.size()) {
648 base::FilePath::StringType extension_paths_value = JoinString(
649 extension_paths, FILE_PATH_LITERAL(','));
650 UpdateExtensionSwitch(switches, "load-extension", extension_paths_value);
652 bg_pages->swap(bg_pages_tmp);
653 return Status(kOk);
656 Status WritePrefsFile(
657 const std::string& template_string,
658 const base::DictionaryValue* custom_prefs,
659 const base::FilePath& path) {
660 int code;
661 std::string error_msg;
662 scoped_ptr<base::Value> template_value(base::JSONReader::ReadAndReturnError(
663 template_string, 0, &code, &error_msg));
664 base::DictionaryValue* prefs;
665 if (!template_value || !template_value->GetAsDictionary(&prefs)) {
666 return Status(kUnknownError,
667 "cannot parse internal JSON template: " + error_msg);
670 if (custom_prefs) {
671 for (base::DictionaryValue::Iterator it(*custom_prefs); !it.IsAtEnd();
672 it.Advance()) {
673 prefs->Set(it.key(), it.value().DeepCopy());
677 std::string prefs_str;
678 base::JSONWriter::Write(prefs, &prefs_str);
679 VLOG(0) << "Populating " << path.BaseName().value()
680 << " file: " << PrettyPrintValue(*prefs);
681 if (static_cast<int>(prefs_str.length()) != base::WriteFile(
682 path, prefs_str.c_str(), prefs_str.length())) {
683 return Status(kUnknownError, "failed to write prefs file");
685 return Status(kOk);
688 Status PrepareUserDataDir(
689 const base::FilePath& user_data_dir,
690 const base::DictionaryValue* custom_prefs,
691 const base::DictionaryValue* custom_local_state) {
692 base::FilePath default_dir =
693 user_data_dir.AppendASCII(chrome::kInitialProfile);
694 if (!base::CreateDirectory(default_dir))
695 return Status(kUnknownError, "cannot create default profile directory");
697 Status status =
698 WritePrefsFile(kPreferences,
699 custom_prefs,
700 default_dir.Append(chrome::kPreferencesFilename));
701 if (status.IsError())
702 return status;
704 status = WritePrefsFile(kLocalState,
705 custom_local_state,
706 user_data_dir.Append(chrome::kLocalStateFilename));
707 if (status.IsError())
708 return status;
710 // Write empty "First Run" file, otherwise Chrome will wipe the default
711 // profile that was written.
712 if (base::WriteFile(
713 user_data_dir.Append(chrome::kFirstRunSentinel), "", 0) != 0) {
714 return Status(kUnknownError, "failed to write first run file");
716 return Status(kOk);
719 } // namespace internal