Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(201)

Side by Side Diff: chrome/test/chromedriver/chrome_launcher.cc

Issue 23643005: [chromedriver] Load the automation extension as a component extension. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: . Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 26 matching lines...) Expand all
37 #include "chrome/test/chromedriver/chrome/user_data_dir.h" 37 #include "chrome/test/chromedriver/chrome/user_data_dir.h"
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[] = {
48 "ignore-certificate-errors", "metrics-recording-only"};
49
50 Status UnpackAutomationExtension(const base::FilePath& temp_dir, 47 Status UnpackAutomationExtension(const base::FilePath& temp_dir,
51 base::FilePath* automation_extension) { 48 base::FilePath* automation_extension) {
52 std::string decoded_extension; 49 std::string decoded_extension;
53 if (!base::Base64Decode(kAutomationExtension, &decoded_extension)) 50 if (!base::Base64Decode(kAutomationExtension, &decoded_extension))
54 return Status(kUnknownError, "failed to base64decode automation extension"); 51 return Status(kUnknownError, "failed to base64decode automation extension");
55 52
56 base::FilePath extension_zip = temp_dir.AppendASCII("internal.zip"); 53 base::FilePath extension_zip = temp_dir.AppendASCII("internal.zip");
57 int size = static_cast<int>(decoded_extension.length()); 54 int size = static_cast<int>(decoded_extension.length());
58 if (file_util::WriteFile(extension_zip, decoded_extension.c_str(), size) 55 if (file_util::WriteFile(extension_zip, decoded_extension.c_str(), size)
59 != size) { 56 != size) {
60 return Status(kUnknownError, "failed to write automation extension zip"); 57 return Status(kUnknownError, "failed to write automation extension zip");
61 } 58 }
62 59
63 base::FilePath extension_dir = temp_dir.AppendASCII("internal"); 60 base::FilePath extension_dir = temp_dir.AppendASCII("internal");
64 if (!zip::Unzip(extension_zip, extension_dir)) 61 if (!zip::Unzip(extension_zip, extension_dir))
65 return Status(kUnknownError, "failed to unzip automation extension"); 62 return Status(kUnknownError, "failed to unzip automation extension");
66 63
67 *automation_extension = extension_dir; 64 *automation_extension = extension_dir;
68 return Status(kOk); 65 return Status(kOk);
69 } 66 }
70 67
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, 68 Status PrepareCommandLine(int port,
82 const Capabilities& capabilities, 69 const Capabilities& capabilities,
70 Switches* switches,
83 CommandLine* prepared_command, 71 CommandLine* prepared_command,
84 base::ScopedTempDir* user_data_dir, 72 base::ScopedTempDir* user_data_dir,
85 base::ScopedTempDir* extension_dir, 73 base::ScopedTempDir* extension_dir,
86 std::vector<std::string>* extension_bg_pages) { 74 std::vector<std::string>* extension_bg_pages) {
87 CommandLine command = capabilities.command; 75 base::FilePath program = capabilities.binary;
88 base::FilePath program = command.GetProgram();
89 if (program.empty()) { 76 if (program.empty()) {
90 if (!FindChrome(&program)) 77 if (!FindChrome(&program))
91 return Status(kUnknownError, "cannot find Chrome binary"); 78 return Status(kUnknownError, "cannot find Chrome binary");
92 command.SetProgram(program);
93 } else if (!base::PathExists(program)) { 79 } else if (!base::PathExists(program)) {
94 return Status(kUnknownError, 80 return Status(kUnknownError,
95 base::StringPrintf("no chrome binary at %" PRFilePath, 81 base::StringPrintf("no chrome binary at %" PRFilePath,
96 program.value().c_str())); 82 program.value().c_str()));
97 } 83 }
84 CommandLine command(program);
85 Switches required_switches;
98 86
99 const char* excludable_switches[] = { 87 // TODO(chrisgao): Add "disable-sync" when chrome 30- is not supported.
100 "disable-hang-monitor", 88 // For chrome 30-, it leads to crash when opening chrome://settings.
101 "disable-prompt-on-repost", 89 switches->SetSwitch("disable-hang-monitor");
102 "full-memory-crash-report", 90 switches->SetSwitch("disable-prompt-on-repost");
103 "no-first-run", 91 switches->SetSwitch("full-memory-crash-report");
104 "disable-background-networking", 92 switches->SetSwitch("no-first-run");
105 // TODO(chrisgao): Add "disable-sync" when chrome 30- is not supported. 93 switches->SetSwitch("disable-background-networking");
106 // For chrome 30-, it leads to crash when opening chrome://settings. 94 switches->SetSwitch("disable-web-resources");
107 "disable-web-resources", 95 switches->SetSwitch("safebrowsing-disable-auto-update");
108 "safebrowsing-disable-auto-update", 96 switches->SetSwitch("safebrowsing-disable-download-protection");
109 "safebrowsing-disable-download-protection", 97 switches->SetSwitch("disable-client-side-phishing-detection");
110 "disable-client-side-phishing-detection", 98 switches->SetSwitch("disable-component-update");
111 "disable-component-update", 99 switches->SetSwitch("disable-default-apps");
112 "disable-default-apps", 100 switches->SetSwitch("enable-logging");
113 }; 101 switches->SetSwitch("logging-level", "1");
102 switches->SetSwitch("password-store", "basic");
103 switches->SetSwitch("use-mock-keychain");
104 switches->SetSwitch("remote-debugging-port", base::IntToString(port));
114 105
115 AddSwitches(&command, excludable_switches, arraysize(excludable_switches), 106 if (!switches->HasSwitch("user-data-dir")) {
kkania 2013/08/30 03:14:57 capabilities.switches
116 capabilities.exclude_switches);
117 AddSwitches(&command, kCommonSwitches, arraysize(kCommonSwitches),
118 capabilities.exclude_switches);
119
120 command.AppendSwitch("enable-logging");
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"); 107 command.AppendArg("about:blank");
128 if (!user_data_dir->CreateUniqueTempDir()) 108 if (!user_data_dir->CreateUniqueTempDir())
129 return Status(kUnknownError, "cannot create temp dir for user data dir"); 109 return Status(kUnknownError, "cannot create temp dir for user data dir");
130 command.AppendSwitchPath("user-data-dir", user_data_dir->path()); 110 switches->SetSwitch("user-data-dir", user_data_dir->path().value());
131 Status status = internal::PrepareUserDataDir( 111 Status status = internal::PrepareUserDataDir(
132 user_data_dir->path(), capabilities.prefs.get(), 112 user_data_dir->path(), capabilities.prefs.get(),
133 capabilities.local_state.get()); 113 capabilities.local_state.get());
134 if (status.IsError()) 114 if (status.IsError())
135 return status; 115 return status;
136 } 116 }
137 117
138 if (!extension_dir->CreateUniqueTempDir()) { 118 if (!extension_dir->CreateUniqueTempDir()) {
139 return Status(kUnknownError, 119 return Status(kUnknownError,
140 "cannot create temp dir for unpacking extensions"); 120 "cannot create temp dir for unpacking extensions");
141 } 121 }
142 Status status = internal::ProcessExtensions(capabilities.extensions, 122 Status status = internal::ProcessExtensions(capabilities.extensions,
143 extension_dir->path(), 123 extension_dir->path(),
144 true, 124 true,
145 &command, 125 &required_switches,
146 extension_bg_pages); 126 extension_bg_pages);
147 if (status.IsError()) 127 if (status.IsError())
148 return status; 128 return status;
149 129
130 // First, exclude switches user requested. Then add extra switches user
131 // requested, and finally add the required switches.
132 for (std::set<std::string>::const_iterator iter =
133 capabilities.exclude_switches.begin();
134 iter != capabilities.exclude_switches.end();
135 ++iter) {
136 switches->RemoveSwitch(*iter);
137 }
138 switches->SetFromSwitches(capabilities.switches);
139 switches->SetFromSwitches(required_switches);
140 switches->AppendToCommandLine(&command);
150 *prepared_command = command; 141 *prepared_command = command;
151 return Status(kOk); 142 return Status(kOk);
152 } 143 }
153 144
154 Status WaitForDevToolsAndCheckVersion( 145 Status WaitForDevToolsAndCheckVersion(
155 const NetAddress& address, 146 const NetAddress& address,
156 URLRequestContextGetter* context_getter, 147 URLRequestContextGetter* context_getter,
157 const SyncWebSocketFactory& socket_factory, 148 const SyncWebSocketFactory& socket_factory,
158 Log* log, 149 Log* log,
159 scoped_ptr<DevToolsHttpClient>* user_client) { 150 scoped_ptr<DevToolsHttpClient>* user_client) {
(...skipping 23 matching lines...) Expand all
183 Status LaunchExistingChromeSession( 174 Status LaunchExistingChromeSession(
184 URLRequestContextGetter* context_getter, 175 URLRequestContextGetter* context_getter,
185 const SyncWebSocketFactory& socket_factory, 176 const SyncWebSocketFactory& socket_factory,
186 Log* log, 177 Log* log,
187 const Capabilities& capabilities, 178 const Capabilities& capabilities,
188 ScopedVector<DevToolsEventListener>& devtools_event_listeners, 179 ScopedVector<DevToolsEventListener>& devtools_event_listeners,
189 scoped_ptr<Chrome>* chrome) { 180 scoped_ptr<Chrome>* chrome) {
190 Status status(kOk); 181 Status status(kOk);
191 scoped_ptr<DevToolsHttpClient> devtools_client; 182 scoped_ptr<DevToolsHttpClient> devtools_client;
192 status = WaitForDevToolsAndCheckVersion( 183 status = WaitForDevToolsAndCheckVersion(
193 capabilities.use_existing_browser, context_getter, socket_factory, log, 184 capabilities.debugger_address, context_getter, socket_factory, log,
194 &devtools_client); 185 &devtools_client);
195 if (status.IsError()) { 186 if (status.IsError()) {
196 return Status(kUnknownError, "cannot connect to chrome at " + 187 return Status(kUnknownError, "cannot connect to chrome at " +
197 capabilities.use_existing_browser.ToString(), 188 capabilities.debugger_address.ToString(),
198 status); 189 status);
199 } 190 }
200 chrome->reset(new ChromeExistingImpl(devtools_client.Pass(), 191 chrome->reset(new ChromeExistingImpl(devtools_client.Pass(),
201 devtools_event_listeners, 192 devtools_event_listeners,
202 log)); 193 log));
203 return Status(kOk); 194 return Status(kOk);
204 } 195 }
205 196
206 Status LaunchDesktopChrome( 197 Status LaunchDesktopChrome(
207 URLRequestContextGetter* context_getter, 198 URLRequestContextGetter* context_getter,
208 int port, 199 int port,
209 const SyncWebSocketFactory& socket_factory, 200 const SyncWebSocketFactory& socket_factory,
210 Log* log, 201 Log* log,
211 const Capabilities& capabilities, 202 const Capabilities& capabilities,
203 Switches* switches,
212 ScopedVector<DevToolsEventListener>& devtools_event_listeners, 204 ScopedVector<DevToolsEventListener>& devtools_event_listeners,
213 scoped_ptr<Chrome>* chrome) { 205 scoped_ptr<Chrome>* chrome) {
214 CommandLine command(CommandLine::NO_PROGRAM); 206 CommandLine command(CommandLine::NO_PROGRAM);
215 base::ScopedTempDir user_data_dir; 207 base::ScopedTempDir user_data_dir;
216 base::ScopedTempDir extension_dir; 208 base::ScopedTempDir extension_dir;
217 std::vector<std::string> extension_bg_pages; 209 std::vector<std::string> extension_bg_pages;
218 Status status = PrepareCommandLine(port, 210 Status status = PrepareCommandLine(port,
219 capabilities, 211 capabilities,
212 switches,
220 &command, 213 &command,
221 &user_data_dir, 214 &user_data_dir,
222 &extension_dir, 215 &extension_dir,
223 &extension_bg_pages); 216 &extension_bg_pages);
224 if (status.IsError()) 217 if (status.IsError())
225 return status; 218 return status;
226 219
227 base::LaunchOptions options; 220 base::LaunchOptions options;
228 221
229 #if !defined(OS_WIN) 222 #if !defined(OS_WIN)
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
307 *chrome = chrome_desktop.Pass(); 300 *chrome = chrome_desktop.Pass();
308 return Status(kOk); 301 return Status(kOk);
309 } 302 }
310 303
311 Status LaunchAndroidChrome( 304 Status LaunchAndroidChrome(
312 URLRequestContextGetter* context_getter, 305 URLRequestContextGetter* context_getter,
313 int port, 306 int port,
314 const SyncWebSocketFactory& socket_factory, 307 const SyncWebSocketFactory& socket_factory,
315 Log* log, 308 Log* log,
316 const Capabilities& capabilities, 309 const Capabilities& capabilities,
310 Switches* switches,
317 ScopedVector<DevToolsEventListener>& devtools_event_listeners, 311 ScopedVector<DevToolsEventListener>& devtools_event_listeners,
318 DeviceManager* device_manager, 312 DeviceManager* device_manager,
319 scoped_ptr<Chrome>* chrome) { 313 scoped_ptr<Chrome>* chrome) {
320 Status status(kOk); 314 Status status(kOk);
321 scoped_ptr<Device> device; 315 scoped_ptr<Device> device;
322 if (capabilities.android_device_serial.empty()) { 316 if (capabilities.android_device_serial.empty()) {
323 status = device_manager->AcquireDevice(&device); 317 status = device_manager->AcquireDevice(&device);
324 } else { 318 } else {
325 status = device_manager->AcquireSpecificDevice( 319 status = device_manager->AcquireSpecificDevice(
326 capabilities.android_device_serial, &device); 320 capabilities.android_device_serial, &device);
327 } 321 }
328 if (!status.IsOk()) 322 if (!status.IsOk())
329 return status; 323 return status;
330 324
331 std::string args(capabilities.android_args); 325 switches->SetSwitch("disable-fre");
332 for (size_t i = 0; i < arraysize(kCommonSwitches); i++) 326 switches->SetSwitch("enable-remote-debugging");
333 args += "--" + std::string(kCommonSwitches[i]) + " ";
334 args += "--disable-fre --enable-remote-debugging";
335
336 status = device->StartApp(capabilities.android_package, 327 status = device->StartApp(capabilities.android_package,
337 capabilities.android_activity, 328 capabilities.android_activity,
338 capabilities.android_process, 329 capabilities.android_process,
339 args, port); 330 switches->ToString(), port);
340 if (!status.IsOk()) { 331 if (!status.IsOk()) {
341 device->StopApp(); 332 device->StopApp();
342 return status; 333 return status;
343 } 334 }
344 335
345 scoped_ptr<DevToolsHttpClient> devtools_client; 336 scoped_ptr<DevToolsHttpClient> devtools_client;
346 status = WaitForDevToolsAndCheckVersion(NetAddress(port), 337 status = WaitForDevToolsAndCheckVersion(NetAddress(port),
347 context_getter, 338 context_getter,
348 socket_factory, 339 socket_factory,
349 log, 340 log,
(...skipping 19 matching lines...) Expand all
369 if (capabilities.IsExistingBrowser()) { 360 if (capabilities.IsExistingBrowser()) {
370 return LaunchExistingChromeSession( 361 return LaunchExistingChromeSession(
371 context_getter, socket_factory, 362 context_getter, socket_factory,
372 log, capabilities, devtools_event_listeners, chrome); 363 log, capabilities, devtools_event_listeners, chrome);
373 } 364 }
374 365
375 int port; 366 int port;
376 if (!FindOpenPort(&port)) 367 if (!FindOpenPort(&port))
377 return Status(kUnknownError, "failed to find an open port for Chrome"); 368 return Status(kUnknownError, "failed to find an open port for Chrome");
378 369
370 Switches switches;
371 const char* kCommonSwitches[] = {
372 "ignore-certificate-errors", "metrics-recording-only"};
373 for (size_t i = 0; i < arraysize(kCommonSwitches); i++)
kkania 2013/08/30 03:14:57 ++i
374 switches.SetSwitch(kCommonSwitches[i]);
375
379 if (capabilities.IsAndroid()) { 376 if (capabilities.IsAndroid()) {
380 return LaunchAndroidChrome( 377 return LaunchAndroidChrome(
381 context_getter, port, socket_factory, log, capabilities, 378 context_getter, port, socket_factory, log, capabilities,
382 devtools_event_listeners, device_manager, chrome); 379 &switches, devtools_event_listeners, device_manager, chrome);
383 } else { 380 } else {
384 return LaunchDesktopChrome( 381 return LaunchDesktopChrome(
385 context_getter, port, socket_factory, log, capabilities, 382 context_getter, port, socket_factory, log, capabilities,
386 devtools_event_listeners, chrome); 383 &switches, devtools_event_listeners, chrome);
387 } 384 }
388 } 385 }
389 386
390 namespace internal { 387 namespace internal {
391 388
392 void ConvertHexadecimalToIDAlphabet(std::string* id) { 389 void ConvertHexadecimalToIDAlphabet(std::string* id) {
393 for (size_t i = 0; i < id->size(); ++i) { 390 for (size_t i = 0; i < id->size(); ++i) {
394 int val; 391 int val;
395 if (base::HexStringToInt(base::StringPiece(id->begin() + i, 392 if (base::HexStringToInt(base::StringPiece(id->begin() + i,
396 id->begin() + i + 1), 393 id->begin() + i + 1),
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after
493 Status status = GetExtensionBackgroundPage(manifest, id, &bg_page_tmp); 490 Status status = GetExtensionBackgroundPage(manifest, id, &bg_page_tmp);
494 if (status.IsError()) 491 if (status.IsError())
495 return status; 492 return status;
496 493
497 *path = extension_dir; 494 *path = extension_dir;
498 if (bg_page_tmp.size()) 495 if (bg_page_tmp.size())
499 *bg_page = bg_page_tmp; 496 *bg_page = bg_page_tmp;
500 return Status(kOk); 497 return Status(kOk);
501 } 498 }
502 499
500 void UpdateExtensionSwitch(Switches* switches,
501 const char name[],
502 const base::FilePath::StringType& extension) {
503 base::FilePath::StringType value = switches->GetSwitchValueNative(name);
504 if (value.length())
505 value += FILE_PATH_LITERAL(",");
506 value += extension;
507 switches->SetSwitch(name, value);
508 }
509
503 Status ProcessExtensions(const std::vector<std::string>& extensions, 510 Status ProcessExtensions(const std::vector<std::string>& extensions,
504 const base::FilePath& temp_dir, 511 const base::FilePath& temp_dir,
505 bool include_automation_extension, 512 bool include_automation_extension,
506 CommandLine* command, 513 Switches* switches,
kkania 2013/08/30 03:14:57 required_switches
507 std::vector<std::string>* bg_pages) { 514 std::vector<std::string>* bg_pages) {
508 std::vector<std::string> bg_pages_tmp; 515 std::vector<std::string> bg_pages_tmp;
509 std::vector<base::FilePath::StringType> extension_paths; 516 std::vector<base::FilePath::StringType> extension_paths;
510 for (size_t i = 0; i < extensions.size(); ++i) { 517 for (size_t i = 0; i < extensions.size(); ++i) {
511 base::FilePath path; 518 base::FilePath path;
512 std::string bg_page; 519 std::string bg_page;
513 Status status = ProcessExtension(extensions[i], temp_dir, &path, &bg_page); 520 Status status = ProcessExtension(extensions[i], temp_dir, &path, &bg_page);
514 if (status.IsError()) { 521 if (status.IsError()) {
515 return Status( 522 return Status(
516 kUnknownError, 523 kUnknownError,
517 base::StringPrintf("cannot process extension #%" PRIuS, i + 1), 524 base::StringPrintf("cannot process extension #%" PRIuS, i + 1),
518 status); 525 status);
519 } 526 }
520 extension_paths.push_back(path.value()); 527 extension_paths.push_back(path.value());
521 if (bg_page.length()) 528 if (bg_page.length())
522 bg_pages_tmp.push_back(bg_page); 529 bg_pages_tmp.push_back(bg_page);
523 } 530 }
524 531
525 if (include_automation_extension) { 532 if (include_automation_extension) {
526 base::FilePath automation_extension; 533 base::FilePath automation_extension;
527 Status status = UnpackAutomationExtension(temp_dir, &automation_extension); 534 Status status = UnpackAutomationExtension(temp_dir, &automation_extension);
528 if (status.IsError()) 535 if (status.IsError())
529 return status; 536 return status;
530 if (command->HasSwitch("disable-extensions")) { 537 if (switches->HasSwitch("disable-extensions")) {
531 command->AppendSwitchNative("load-component-extension", 538 UpdateExtensionSwitch(switches, "load-component-extension",
532 automation_extension.value()); 539 automation_extension.value());
533 } else { 540 } else {
534 extension_paths.push_back(automation_extension.value()); 541 extension_paths.push_back(automation_extension.value());
535 } 542 }
536 } 543 }
537 544
538 if (extension_paths.size()) { 545 if (extension_paths.size()) {
539 base::FilePath::StringType extension_paths_value = JoinString( 546 base::FilePath::StringType extension_paths_value = JoinString(
540 extension_paths, FILE_PATH_LITERAL(',')); 547 extension_paths, FILE_PATH_LITERAL(','));
541 command->AppendSwitchNative("load-extension", extension_paths_value); 548 UpdateExtensionSwitch(switches, "load-extension", extension_paths_value);
542 } 549 }
543 bg_pages->swap(bg_pages_tmp); 550 bg_pages->swap(bg_pages_tmp);
544 return Status(kOk); 551 return Status(kOk);
545 } 552 }
546 553
547 Status WritePrefsFile( 554 Status WritePrefsFile(
548 const std::string& template_string, 555 const std::string& template_string,
549 const base::DictionaryValue* custom_prefs, 556 const base::DictionaryValue* custom_prefs,
550 const base::FilePath& path) { 557 const base::FilePath& path) {
551 int code; 558 int code;
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
599 // Write empty "First Run" file, otherwise Chrome will wipe the default 606 // Write empty "First Run" file, otherwise Chrome will wipe the default
600 // profile that was written. 607 // profile that was written.
601 if (file_util::WriteFile( 608 if (file_util::WriteFile(
602 user_data_dir.AppendASCII("First Run"), "", 0) != 0) { 609 user_data_dir.AppendASCII("First Run"), "", 0) != 0) {
603 return Status(kUnknownError, "failed to write first run file"); 610 return Status(kUnknownError, "failed to write first run file");
604 } 611 }
605 return Status(kOk); 612 return Status(kOk);
606 } 613 }
607 614
608 } // namespace internal 615 } // namespace internal
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698