Home | History | Annotate | Download | only in login
      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:
     82   ResetDefaultProxyConfigServiceTask(
     83       net::ProxyConfigService* proxy_config_service)
     84       : proxy_config_service_(proxy_config_service) {}
     85   virtual ~ResetDefaultProxyConfigServiceTask() {}
     86 
     87   // Task override.
     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:
    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:
    181   static LoginUtilsWrapper* GetInstance() {
    182     return Singleton<LoginUtilsWrapper>::get();
    183   }
    184 
    185   LoginUtils* get() {
    186     base::AutoLock create(create_lock_);
    187     if (!ptr_.get())
    188       reset(new LoginUtilsImpl);
    189     return ptr_.get();
    190   }
    191 
    192   void reset(LoginUtils* ptr) {
    193     ptr_.reset(ptr);
    194   }
    195 
    196  private:
    197   friend struct DefaultSingletonTraits<LoginUtilsWrapper>;
    198 
    199   LoginUtilsWrapper() {}
    200 
    201   base::Lock create_lock_;
    202   scoped_ptr<LoginUtils> ptr_;
    203 
    204   DISALLOW_COPY_AND_ASSIGN(LoginUtilsWrapper);
    205 };
    206 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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:
    530   WarmingObserver() {
    531     NetworkLibrary *netlib = CrosLibrary::Get()->GetNetworkLibrary();
    532     netlib->AddNetworkManagerObserver(this);
    533   }
    534 
    535   // If we're now connected, prewarm the auth url.
    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 
    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 
    564 void LoginUtilsImpl::SetBackgroundView(BackgroundView* background_view) {
    565   background_view_ = background_view;
    566 }
    567 
    568 BackgroundView* LoginUtilsImpl::GetBackgroundView() {
    569   return background_view_;
    570 }
    571 
    572 LoginUtils* LoginUtils::Get() {
    573   return LoginUtilsWrapper::GetInstance()->get();
    574 }
    575 
    576 void LoginUtils::Set(LoginUtils* mock) {
    577   LoginUtilsWrapper::GetInstance()->reset(mock);
    578 }
    579 
    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