1 // Copyright (c) 2011 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/browser/chromeos/login/login_utils.h"
6
7 #include <vector>
8
9 #include "base/command_line.h"
10 #include "base/file_path.h"
11 #include "base/file_util.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/memory/singleton.h"
14 #include "base/path_service.h"
15 #include "base/string_util.h"
16 #include "base/stringprintf.h"
17 #include "base/synchronization/lock.h"
18 #include "base/threading/thread_restrictions.h"
19 #include "base/time.h"
20 #include "base/utf_string_conversions.h"
21 #include "chrome/browser/browser_process.h"
22 #include "chrome/browser/chromeos/boot_times_loader.h"
23 #include "chrome/browser/chromeos/cros/login_library.h"
24 #include "chrome/browser/chromeos/cros/network_library.h"
25 #include "chrome/browser/chromeos/input_method/input_method_util.h"
26 #include "chrome/browser/chromeos/login/background_view.h"
27 #include "chrome/browser/chromeos/login/cookie_fetcher.h"
28 #include "chrome/browser/chromeos/login/google_authenticator.h"
29 #include "chrome/browser/chromeos/login/language_switch_menu.h"
30 #include "chrome/browser/chromeos/login/ownership_service.h"
31 #include "chrome/browser/chromeos/login/parallel_authenticator.h"
32 #include "chrome/browser/chromeos/login/user_image_downloader.h"
33 #include "chrome/browser/chromeos/login/user_manager.h"
34 #include "chrome/browser/chromeos/proxy_config_service.h"
35 #include "chrome/browser/extensions/extension_service.h"
36 #include "chrome/browser/net/chrome_url_request_context.h"
37 #include "chrome/browser/net/gaia/token_service.h"
38 #include "chrome/browser/net/preconnect.h"
39 #include "chrome/browser/net/pref_proxy_config_service.h"
40 #include "chrome/browser/plugin_updater.h"
41 #include "chrome/browser/prefs/pref_member.h"
42 #include "chrome/browser/profiles/profile.h"
43 #include "chrome/browser/profiles/profile_manager.h"
44 #include "chrome/browser/sync/profile_sync_service.h"
45 #include "chrome/browser/ui/browser_init.h"
46 #include "chrome/common/chrome_paths.h"
47 #include "chrome/common/chrome_switches.h"
48 #include "chrome/common/logging_chrome.h"
49 #include "chrome/common/net/gaia/gaia_auth_fetcher.h"
50 #include "chrome/common/net/gaia/gaia_constants.h"
51 #include "chrome/common/pref_names.h"
52 #include "chrome/common/url_constants.h"
53 #include "content/browser/browser_thread.h"
54 #include "googleurl/src/gurl.h"
55 #include "net/base/cookie_store.h"
56 #include "net/proxy/proxy_config_service.h"
57 #include "net/url_request/url_request_context.h"
58 #include "net/url_request/url_request_context_getter.h"
59 #include "views/widget/widget_gtk.h"
60 #include "ui/gfx/gl/gl_switches.h"
61
62 namespace chromeos {
63
64 namespace {
65
66 // Affixes for Auth token received from ClientLogin request.
67 const char kAuthPrefix[] = "Auth=";
68 const char kAuthSuffix[] = "\n";
69
70 // Increase logging level for Guest mode to avoid LOG(INFO) messages in logs.
71 const char kGuestModeLoggingLevel[] = "1";
72
73 // Format of command line switch.
74 const char kSwitchFormatString[] = " --%s=\"%s\"";
75
76 // User name which is used in the Guest session.
77 const char kGuestUserName[] = "";
78
79 // Resets the proxy configuration service for the default request context.
80 class ResetDefaultProxyConfigServiceTask : public Task {
81 public:
ResetDefaultProxyConfigServiceTask(net::ProxyConfigService * proxy_config_service)82 ResetDefaultProxyConfigServiceTask(
83 net::ProxyConfigService* proxy_config_service)
84 : proxy_config_service_(proxy_config_service) {}
~ResetDefaultProxyConfigServiceTask()85 virtual ~ResetDefaultProxyConfigServiceTask() {}
86
87 // Task override.
Run()88 virtual void Run() {
89 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
90 net::URLRequestContextGetter* getter = Profile::GetDefaultRequestContext();
91 DCHECK(getter);
92 if (getter) {
93 getter->GetURLRequestContext()->proxy_service()->ResetConfigService(
94 proxy_config_service_.release());
95 }
96 }
97
98 private:
99 scoped_ptr<net::ProxyConfigService> proxy_config_service_;
100
101 DISALLOW_COPY_AND_ASSIGN(ResetDefaultProxyConfigServiceTask);
102 };
103
104 } // namespace
105
106 class LoginUtilsImpl : public LoginUtils,
107 public ProfileManager::Observer {
108 public:
LoginUtilsImpl()109 LoginUtilsImpl()
110 : background_view_(NULL) {
111 }
112
113 virtual void PrepareProfile(
114 const std::string& username,
115 const std::string& password,
116 const GaiaAuthConsumer::ClientLoginResult& credentials,
117 bool pending_requests,
118 LoginUtils::Delegate* delegate);
119
120 // Invoked after the tmpfs is successfully mounted.
121 // Launches a browser in the incognito mode.
122 virtual void CompleteOffTheRecordLogin(const GURL& start_url);
123
124 // Invoked when the user is logging in for the first time, or is logging in as
125 // a guest user.
126 virtual void SetFirstLoginPrefs(PrefService* prefs);
127
128 // Creates and returns the authenticator to use. The caller owns the returned
129 // Authenticator and must delete it when done.
130 virtual Authenticator* CreateAuthenticator(LoginStatusConsumer* consumer);
131
132 // Warms the url used by authentication.
133 virtual void PrewarmAuthentication();
134
135 // Given the credentials try to exchange them for
136 // full-fledged Google authentication cookies.
137 virtual void FetchCookies(
138 Profile* profile,
139 const GaiaAuthConsumer::ClientLoginResult& credentials);
140
141 // Supply credentials for sync and others to use.
142 virtual void FetchTokens(
143 Profile* profile,
144 const GaiaAuthConsumer::ClientLoginResult& credentials);
145
146 // Sets the current background view.
147 virtual void SetBackgroundView(chromeos::BackgroundView* background_view);
148
149 // Gets the current background view.
150 virtual chromeos::BackgroundView* GetBackgroundView();
151
152 // ProfileManager::Observer implementation:
153 virtual void OnProfileCreated(Profile* profile);
154
155 protected:
156 virtual std::string GetOffTheRecordCommandLine(
157 const GURL& start_url,
158 const CommandLine& base_command_line,
159 CommandLine *command_line);
160
161 private:
162 // Check user's profile for kApplicationLocale setting.
163 void RespectLocalePreference(Profile* pref);
164
165 // The current background view.
166 chromeos::BackgroundView* background_view_;
167
168 std::string username_;
169 std::string password_;
170 GaiaAuthConsumer::ClientLoginResult credentials_;
171 bool pending_requests_;
172
173 // Delegate to be fired when the profile will be prepared.
174 LoginUtils::Delegate* delegate_;
175
176 DISALLOW_COPY_AND_ASSIGN(LoginUtilsImpl);
177 };
178
179 class LoginUtilsWrapper {
180 public:
GetInstance()181 static LoginUtilsWrapper* GetInstance() {
182 return Singleton<LoginUtilsWrapper>::get();
183 }
184
get()185 LoginUtils* get() {
186 base::AutoLock create(create_lock_);
187 if (!ptr_.get())
188 reset(new LoginUtilsImpl);
189 return ptr_.get();
190 }
191
reset(LoginUtils * ptr)192 void reset(LoginUtils* ptr) {
193 ptr_.reset(ptr);
194 }
195
196 private:
197 friend struct DefaultSingletonTraits<LoginUtilsWrapper>;
198
LoginUtilsWrapper()199 LoginUtilsWrapper() {}
200
201 base::Lock create_lock_;
202 scoped_ptr<LoginUtils> ptr_;
203
204 DISALLOW_COPY_AND_ASSIGN(LoginUtilsWrapper);
205 };
206
PrepareProfile(const std::string & username,const std::string & password,const GaiaAuthConsumer::ClientLoginResult & credentials,bool pending_requests,LoginUtils::Delegate * delegate)207 void LoginUtilsImpl::PrepareProfile(
208 const std::string& username,
209 const std::string& password,
210 const GaiaAuthConsumer::ClientLoginResult& credentials,
211 bool pending_requests,
212 LoginUtils::Delegate* delegate) {
213 BootTimesLoader* btl = BootTimesLoader::Get();
214
215 VLOG(1) << "Completing login for " << username;
216 btl->AddLoginTimeMarker("CompletingLogin", false);
217
218 if (CrosLibrary::Get()->EnsureLoaded()) {
219 CrosLibrary::Get()->GetLoginLibrary()->StartSession(username, "");
220 btl->AddLoginTimeMarker("StartedSession", false);
221 }
222
223 UserManager::Get()->UserLoggedIn(username);
224 btl->AddLoginTimeMarker("UserLoggedIn", false);
225
226 // Switch log file as soon as possible.
227 logging::RedirectChromeLogging(*(CommandLine::ForCurrentProcess()));
228 btl->AddLoginTimeMarker("LoggingRedirected", false);
229
230 username_ = username;
231 password_ = password;
232 credentials_ = credentials;
233 pending_requests_ = pending_requests;
234 delegate_ = delegate;
235
236 // The default profile will have been changed because the ProfileManager
237 // will process the notification that the UserManager sends out.
238 ProfileManager::CreateDefaultProfileAsync(this);
239 }
240
OnProfileCreated(Profile * profile)241 void LoginUtilsImpl::OnProfileCreated(Profile* profile) {
242 CHECK(profile);
243
244 BootTimesLoader* btl = BootTimesLoader::Get();
245 btl->AddLoginTimeMarker("UserProfileGotten", false);
246
247 // Change the proxy configuration service of the default request context to
248 // use the preference configuration from the logged-in profile. This ensures
249 // that requests done through the default context use the proxy configuration
250 // provided by configuration policy.
251 //
252 // Note: Many of the clients of the default request context should probably be
253 // fixed to use the request context of the profile they are associated with.
254 // This includes preconnect, autofill, metrics service to only name a few;
255 // see http://code.google.com/p/chromium/issues/detail?id=64339 for details.
256 //
257 // TODO(mnissler) Revisit when support for device-specific policy arrives, at
258 // which point the default request context can directly be managed through
259 // device policy.
260 net::ProxyConfigService* proxy_config_service =
261 new PrefProxyConfigService(
262 profile->GetProxyConfigTracker(),
263 new chromeos::ProxyConfigService(
264 g_browser_process->chromeos_proxy_config_service_impl()));
265 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
266 new ResetDefaultProxyConfigServiceTask(
267 proxy_config_service));
268
269 // Since we're doing parallel authentication, only new user sign in
270 // would perform online auth before calling PrepareProfile.
271 // For existing users there's usually a pending online auth request.
272 // Cookies will be fetched after it's is succeeded.
273 if (!pending_requests_) {
274 FetchCookies(profile, credentials_);
275 }
276
277 // Init extension event routers; this normally happens in browser_main
278 // but on Chrome OS it has to be deferred until the user finishes
279 // logging in and the profile is not OTR.
280 if (profile->GetExtensionService() &&
281 profile->GetExtensionService()->extensions_enabled()) {
282 profile->GetExtensionService()->InitEventRouters();
283 }
284 btl->AddLoginTimeMarker("ExtensionsServiceStarted", false);
285
286 // Supply credentials for sync and others to use. Load tokens from disk.
287 TokenService* token_service = profile->GetTokenService();
288 token_service->Initialize(GaiaConstants::kChromeOSSource,
289 profile);
290 token_service->LoadTokensFromDB();
291
292 // For existing users there's usually a pending online auth request.
293 // Tokens will be fetched after it's is succeeded.
294 if (!pending_requests_) {
295 FetchTokens(profile, credentials_);
296 }
297 btl->AddLoginTimeMarker("TokensGotten", false);
298
299 // Set the CrOS user by getting this constructor run with the
300 // user's email on first retrieval.
301 profile->GetProfileSyncService(username_)->SetPassphrase(password_,
302 false,
303 true);
304 btl->AddLoginTimeMarker("SyncStarted", false);
305
306 // Own TPM device if, for any reason, it has not been done in EULA
307 // wizard screen.
308 if (CrosLibrary::Get()->EnsureLoaded()) {
309 CryptohomeLibrary* cryptohome = CrosLibrary::Get()->GetCryptohomeLibrary();
310 if (cryptohome->TpmIsEnabled() && !cryptohome->TpmIsBeingOwned()) {
311 if (cryptohome->TpmIsOwned()) {
312 cryptohome->TpmClearStoredPassword();
313 } else {
314 cryptohome->TpmCanAttemptOwnership();
315 }
316 }
317 }
318 btl->AddLoginTimeMarker("TPMOwned", false);
319
320 RespectLocalePreference(profile);
321
322 if (UserManager::Get()->current_user_is_new()) {
323 SetFirstLoginPrefs(profile->GetPrefs());
324 }
325
326 // Enable/disable plugins based on user preferences.
327 PluginUpdater::GetInstance()->UpdatePluginGroupsStateFromPrefs(profile);
328 btl->AddLoginTimeMarker("PluginsStateUpdated", false);
329
330 // We suck. This is a hack since we do not have the enterprise feature
331 // done yet to pull down policies from the domain admin. We'll take this
332 // out when we get that done properly.
333 // TODO(xiyuan): Remove this once enterprise feature is ready.
334 if (EndsWith(username_, "@google.com", true)) {
335 PrefService* pref_service = profile->GetPrefs();
336 pref_service->SetBoolean(prefs::kEnableScreenLock, true);
337 }
338
339 profile->OnLogin();
340
341 delegate_->OnProfilePrepared(profile);
342
343 // TODO(altimofeev): Need to sanitize memory used to store password.
344 password_ = "";
345 username_ = "";
346 credentials_ = GaiaAuthConsumer::ClientLoginResult();
347 }
348
FetchCookies(Profile * profile,const GaiaAuthConsumer::ClientLoginResult & credentials)349 void LoginUtilsImpl::FetchCookies(
350 Profile* profile,
351 const GaiaAuthConsumer::ClientLoginResult& credentials) {
352 // Take the credentials passed in and try to exchange them for
353 // full-fledged Google authentication cookies. This is
354 // best-effort; it's possible that we'll fail due to network
355 // troubles or some such.
356 // CookieFetcher will delete itself once done.
357 CookieFetcher* cf = new CookieFetcher(profile);
358 cf->AttemptFetch(credentials.data);
359 BootTimesLoader::Get()->AddLoginTimeMarker("CookieFetchStarted", false);
360 }
361
FetchTokens(Profile * profile,const GaiaAuthConsumer::ClientLoginResult & credentials)362 void LoginUtilsImpl::FetchTokens(
363 Profile* profile,
364 const GaiaAuthConsumer::ClientLoginResult& credentials) {
365 TokenService* token_service = profile->GetTokenService();
366 token_service->UpdateCredentials(credentials);
367 if (token_service->AreCredentialsValid()) {
368 token_service->StartFetchingTokens();
369 }
370 }
371
RespectLocalePreference(Profile * profile)372 void LoginUtilsImpl::RespectLocalePreference(Profile* profile) {
373 DCHECK(profile != NULL);
374 PrefService* prefs = profile->GetPrefs();
375 DCHECK(prefs != NULL);
376 if (g_browser_process == NULL)
377 return;
378
379 std::string pref_locale = prefs->GetString(prefs::kApplicationLocale);
380 if (pref_locale.empty())
381 pref_locale = prefs->GetString(prefs::kApplicationLocaleBackup);
382 if (pref_locale.empty())
383 pref_locale = g_browser_process->GetApplicationLocale();
384 DCHECK(!pref_locale.empty());
385 profile->ChangeAppLocale(pref_locale, Profile::APP_LOCALE_CHANGED_VIA_LOGIN);
386 // Here we don't enable keyboard layouts. Input methods are set up when
387 // the user first logs in. Then the user may customize the input methods.
388 // Hence changing input methods here, just because the user's UI language
389 // is different from the login screen UI language, is not desirable. Note
390 // that input method preferences are synced, so users can use their
391 // farovite input methods as soon as the preferences are synced.
392 LanguageSwitchMenu::SwitchLanguage(pref_locale);
393 }
394
CompleteOffTheRecordLogin(const GURL & start_url)395 void LoginUtilsImpl::CompleteOffTheRecordLogin(const GURL& start_url) {
396 VLOG(1) << "Completing incognito login";
397
398 UserManager::Get()->OffTheRecordUserLoggedIn();
399
400 if (CrosLibrary::Get()->EnsureLoaded()) {
401 // For guest session we ask session manager to restart Chrome with --bwsi
402 // flag. We keep only some of the arguments of this process.
403 const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess();
404 CommandLine command_line(browser_command_line.GetProgram());
405 std::string cmd_line_str =
406 GetOffTheRecordCommandLine(start_url,
407 browser_command_line,
408 &command_line);
409
410 CrosLibrary::Get()->GetLoginLibrary()->RestartJob(getpid(), cmd_line_str);
411 }
412 }
413
GetOffTheRecordCommandLine(const GURL & start_url,const CommandLine & base_command_line,CommandLine * command_line)414 std::string LoginUtilsImpl::GetOffTheRecordCommandLine(
415 const GURL& start_url,
416 const CommandLine& base_command_line,
417 CommandLine* command_line) {
418 static const char* kForwardSwitches[] = {
419 switches::kEnableLogging,
420 switches::kEnableAcceleratedPlugins,
421 switches::kUseGL,
422 switches::kUserDataDir,
423 switches::kScrollPixels,
424 switches::kEnableGView,
425 switches::kNoFirstRun,
426 switches::kLoginProfile,
427 switches::kCompressSystemFeedback,
428 switches::kDisableSeccompSandbox,
429 switches::kPpapiFlashInProcess,
430 switches::kPpapiFlashPath,
431 switches::kPpapiFlashVersion,
432 #if defined(HAVE_XINPUT2)
433 switches::kTouchDevices,
434 #endif
435 };
436 command_line->CopySwitchesFrom(base_command_line,
437 kForwardSwitches,
438 arraysize(kForwardSwitches));
439 command_line->AppendSwitch(switches::kGuestSession);
440 command_line->AppendSwitch(switches::kIncognito);
441 command_line->AppendSwitchASCII(switches::kLoggingLevel,
442 kGuestModeLoggingLevel);
443
444 command_line->AppendSwitchASCII(switches::kLoginUser, kGuestUserName);
445
446 if (start_url.is_valid())
447 command_line->AppendArg(start_url.spec());
448
449 // Override the value of the homepage that is set in first run mode.
450 // TODO(altimofeev): extend action of the |kNoFirstRun| to cover this case.
451 command_line->AppendSwitchASCII(
452 switches::kHomePage,
453 GURL(chrome::kChromeUINewTabURL).spec());
454
455 std::string cmd_line_str = command_line->command_line_string();
456 // Special workaround for the arguments that should be quoted.
457 // Copying switches won't be needed when Guest mode won't need restart
458 // http://crosbug.com/6924
459 if (base_command_line.HasSwitch(switches::kRegisterPepperPlugins)) {
460 cmd_line_str += base::StringPrintf(
461 kSwitchFormatString,
462 switches::kRegisterPepperPlugins,
463 base_command_line.GetSwitchValueNative(
464 switches::kRegisterPepperPlugins).c_str());
465 }
466
467 return cmd_line_str;
468 }
469
SetFirstLoginPrefs(PrefService * prefs)470 void LoginUtilsImpl::SetFirstLoginPrefs(PrefService* prefs) {
471 VLOG(1) << "Setting first login prefs";
472 BootTimesLoader* btl = BootTimesLoader::Get();
473 std::string locale = g_browser_process->GetApplicationLocale();
474
475 // First, we'll set kLanguagePreloadEngines.
476 InputMethodLibrary* library = CrosLibrary::Get()->GetInputMethodLibrary();
477 std::vector<std::string> input_method_ids;
478 input_method::GetFirstLoginInputMethodIds(locale,
479 library->current_input_method(),
480 &input_method_ids);
481 // Save the input methods in the user's preferences.
482 StringPrefMember language_preload_engines;
483 language_preload_engines.Init(prefs::kLanguagePreloadEngines,
484 prefs, NULL);
485 language_preload_engines.SetValue(JoinString(input_method_ids, ','));
486 btl->AddLoginTimeMarker("IMEStarted", false);
487
488 // Second, we'll set kLanguagePreferredLanguages.
489 std::vector<std::string> language_codes;
490 // The current locale should be on the top.
491 language_codes.push_back(locale);
492
493 // Add input method IDs based on the input methods, as there may be
494 // input methods that are unrelated to the current locale. Example: the
495 // hardware keyboard layout xkb:us::eng is used for logging in, but the
496 // UI language is set to French. In this case, we should set "fr,en"
497 // to the preferred languages preference.
498 std::vector<std::string> candidates;
499 input_method::GetLanguageCodesFromInputMethodIds(
500 input_method_ids, &candidates);
501 for (size_t i = 0; i < candidates.size(); ++i) {
502 const std::string& candidate = candidates[i];
503 // Skip if it's already in language_codes.
504 if (std::count(language_codes.begin(), language_codes.end(),
505 candidate) == 0) {
506 language_codes.push_back(candidate);
507 }
508 }
509 // Save the preferred languages in the user's preferences.
510 StringPrefMember language_preferred_languages;
511 language_preferred_languages.Init(prefs::kLanguagePreferredLanguages,
512 prefs, NULL);
513 language_preferred_languages.SetValue(JoinString(language_codes, ','));
514 prefs->ScheduleSavePersistentPrefs();
515 }
516
CreateAuthenticator(LoginStatusConsumer * consumer)517 Authenticator* LoginUtilsImpl::CreateAuthenticator(
518 LoginStatusConsumer* consumer) {
519 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kParallelAuth))
520 return new ParallelAuthenticator(consumer);
521 else
522 return new GoogleAuthenticator(consumer);
523 }
524
525 // We use a special class for this so that it can be safely leaked if we
526 // never connect. At shutdown the order is not well defined, and it's possible
527 // for the infrastructure needed to unregister might be unstable and crash.
528 class WarmingObserver : public NetworkLibrary::NetworkManagerObserver {
529 public:
WarmingObserver()530 WarmingObserver() {
531 NetworkLibrary *netlib = CrosLibrary::Get()->GetNetworkLibrary();
532 netlib->AddNetworkManagerObserver(this);
533 }
534
535 // If we're now connected, prewarm the auth url.
OnNetworkManagerChanged(NetworkLibrary * netlib)536 void OnNetworkManagerChanged(NetworkLibrary* netlib) {
537 if (netlib->Connected()) {
538 const int kConnectionsNeeded = 1;
539 chrome_browser_net::PreconnectOnUIThread(
540 GURL(GaiaAuthFetcher::kClientLoginUrl),
541 chrome_browser_net::UrlInfo::EARLY_LOAD_MOTIVATED,
542 kConnectionsNeeded);
543 netlib->RemoveNetworkManagerObserver(this);
544 delete this;
545 }
546 }
547 };
548
PrewarmAuthentication()549 void LoginUtilsImpl::PrewarmAuthentication() {
550 if (CrosLibrary::Get()->EnsureLoaded()) {
551 NetworkLibrary *network = CrosLibrary::Get()->GetNetworkLibrary();
552 if (network->Connected()) {
553 const int kConnectionsNeeded = 1;
554 chrome_browser_net::PreconnectOnUIThread(
555 GURL(GaiaAuthFetcher::kClientLoginUrl),
556 chrome_browser_net::UrlInfo::EARLY_LOAD_MOTIVATED,
557 kConnectionsNeeded);
558 } else {
559 new WarmingObserver();
560 }
561 }
562 }
563
SetBackgroundView(BackgroundView * background_view)564 void LoginUtilsImpl::SetBackgroundView(BackgroundView* background_view) {
565 background_view_ = background_view;
566 }
567
GetBackgroundView()568 BackgroundView* LoginUtilsImpl::GetBackgroundView() {
569 return background_view_;
570 }
571
Get()572 LoginUtils* LoginUtils::Get() {
573 return LoginUtilsWrapper::GetInstance()->get();
574 }
575
Set(LoginUtils * mock)576 void LoginUtils::Set(LoginUtils* mock) {
577 LoginUtilsWrapper::GetInstance()->reset(mock);
578 }
579
DoBrowserLaunch(Profile * profile)580 void LoginUtils::DoBrowserLaunch(Profile* profile) {
581 BootTimesLoader::Get()->AddLoginTimeMarker("BrowserLaunched", false);
582
583 // Update command line in case loose values were added.
584 CommandLine::ForCurrentProcess()->InitFromArgv(
585 CommandLine::ForCurrentProcess()->argv());
586
587 VLOG(1) << "Launching browser...";
588 BrowserInit browser_init;
589 int return_code;
590 browser_init.LaunchBrowser(*CommandLine::ForCurrentProcess(),
591 profile,
592 FilePath(),
593 true,
594 &return_code);
595 }
596
597 } // namespace chromeos
598