From 334c59d927639aaae464f76b082a893b9fdf6987 Mon Sep 17 00:00:00 2001 From: "altimofeev@chromium.org" Date: Wed, 13 Apr 2011 11:27:06 +0000 Subject: This CL implements alternative asynchronous methods for profile and preferences loading. BUG=chromium-os:11104 TEST=UserProfileGotten (see "/tmp/login-times-sent") time doesn't increase, while UI jankness decreases. Review URL: http://codereview.chromium.org/6716025 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@81394 0039d316-1c4b-4281-b951-d872f2087c98 --- chrome/browser/app_controller_mac.mm | 14 +- .../automation/testing_automation_provider.cc | 6 +- chrome/browser/browser_main.cc | 18 +- chrome/browser/browser_process_impl.cc | 5 +- .../chromeos/login/existing_user_controller.cc | 41 ++-- .../chromeos/login/existing_user_controller.h | 11 ++ chrome/browser/chromeos/login/login_performer.cc | 48 +++-- chrome/browser/chromeos/login/login_performer.h | 9 +- chrome/browser/chromeos/login/login_utils.cc | 107 +++++------ chrome/browser/chromeos/login/login_utils.h | 23 ++- chrome/browser/chromeos/login/mock_authenticator.h | 20 +- chrome/browser/chromeos/login/screen_locker.cc | 1 + chrome/browser/chromeos/login/wizard_controller.cc | 1 - .../content_settings_pref_provider_unittest.cc | 2 +- .../debugger/extension_ports_remote_service.cc | 13 +- .../extensions/extension_event_router_forwarder.cc | 9 +- .../extension_event_router_forwarder_unittest.cc | 4 +- chrome/browser/memory_purger.cc | 13 +- chrome/browser/prefs/pref_service.cc | 74 +++++--- chrome/browser/prefs/pref_service.h | 26 ++- chrome/browser/prefs/pref_service_mock_builder.cc | 3 +- chrome/browser/profiles/profile.h | 10 + chrome/browser/profiles/profile_impl.cc | 134 +++++++++---- chrome/browser/profiles/profile_impl.h | 15 +- chrome/browser/profiles/profile_manager.cc | 209 +++++++++++++-------- chrome/browser/profiles/profile_manager.h | 98 ++++++---- .../browser/profiles/profile_manager_unittest.cc | 140 +++++++++----- chrome/browser/task_manager/task_manager.cc | 11 +- .../task_manager_resource_providers.cc | 16 +- 29 files changed, 691 insertions(+), 390 deletions(-) (limited to 'chrome/browser') diff --git a/chrome/browser/app_controller_mac.mm b/chrome/browser/app_controller_mac.mm index 6934219..3be6c92 100644 --- a/chrome/browser/app_controller_mac.mm +++ b/chrome/browser/app_controller_mac.mm @@ -599,18 +599,17 @@ void RecordLastRunAppBundlePath() { if (!profile_manager) return YES; - ProfileManager::const_iterator it = profile_manager->begin(); - for (; it != profile_manager->end(); ++it) { - Profile* profile = *it; - DownloadManager* download_manager = profile->GetDownloadManager(); + std::vector profiles(profile_manager->GetLoadedProfiles()); + for (size_t i = 0; i < profiles.size(); ++i) { + DownloadManager* download_manager = profiles[i]->GetDownloadManager(); if (download_manager && download_manager->in_progress_count() > 0) { int downloadCount = download_manager->in_progress_count(); if ([self userWillWaitForInProgressDownloads:downloadCount]) { // Create a new browser window (if necessary) and navigate to the // downloads page if the user chooses to wait. - Browser* browser = BrowserList::FindBrowserWithProfile(profile); + Browser* browser = BrowserList::FindBrowserWithProfile(profiles[i]); if (!browser) { - browser = Browser::Create(profile); + browser = Browser::Create(profiles[i]); browser->window()->Show(); } DCHECK(browser); @@ -984,9 +983,8 @@ void RecordLastRunAppBundlePath() { } - (Profile*)defaultProfile { - // TODO(jrg): Find a better way to get the "default" profile. if (g_browser_process->profile_manager()) - return *g_browser_process->profile_manager()->begin(); + return g_browser_process->profile_manager()->GetDefaultProfile(); return NULL; } diff --git a/chrome/browser/automation/testing_automation_provider.cc b/chrome/browser/automation/testing_automation_provider.cc index a2d9055..2a344d7 100644 --- a/chrome/browser/automation/testing_automation_provider.cc +++ b/chrome/browser/automation/testing_automation_provider.cc @@ -2563,10 +2563,10 @@ void TestingAutomationProvider::GetBrowserInfo( // item per extension process. ListValue* extension_processes = new ListValue; ProfileManager* profile_manager = g_browser_process->profile_manager(); - for (ProfileManager::const_iterator it = profile_manager->begin(); - it != profile_manager->end(); ++it) { + std::vector profiles(profile_manager->GetLoadedProfiles()); + for (size_t i = 0; i < profiles.size(); ++i) { ExtensionProcessManager* process_manager = - (*it)->GetExtensionProcessManager(); + profiles[i]->GetExtensionProcessManager(); if (!process_manager) continue; ExtensionProcessManager::const_iterator jt; diff --git a/chrome/browser/browser_main.cc b/chrome/browser/browser_main.cc index 746ae30..6c5a59b 100644 --- a/chrome/browser/browser_main.cc +++ b/chrome/browser/browser_main.cc @@ -955,7 +955,8 @@ void InitializeToolkit(const MainFunctionParams& parameters) { // Class is used to login using passed username and password. // The instance will be deleted upon success or failure. -class StubLogin : public chromeos::LoginStatusConsumer { +class StubLogin : public chromeos::LoginStatusConsumer, + public chromeos::LoginUtils::Delegate { public: explicit StubLogin(std::string username, std::string password) { authenticator_ = chromeos::LoginUtils::Get()->CreateAuthenticator(this); @@ -976,10 +977,17 @@ class StubLogin : public chromeos::LoginStatusConsumer { const std::string& password, const GaiaAuthConsumer::ClientLoginResult& credentials, bool pending_requests) { - chromeos::LoginUtils::Get()->CompleteLogin(username, - password, - credentials, - pending_requests); + // Will call OnProfilePrepared in the end. + chromeos::LoginUtils::Get()->PrepareProfile(username, + password, + credentials, + pending_requests, + this); + } + + // LoginUtils::Delegate implementation: + virtual void OnProfilePrepared(Profile* profile) { + chromeos::LoginUtils::DoBrowserLaunch(profile); delete this; } diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc index c521e11..b14403e 100644 --- a/chrome/browser/browser_process_impl.cc +++ b/chrome/browser/browser_process_impl.cc @@ -324,8 +324,9 @@ void BrowserProcessImpl::EndSession() { // Mark all the profiles as clean. ProfileManager* pm = profile_manager(); - for (ProfileManager::const_iterator i = pm->begin(); i != pm->end(); ++i) - (*i)->MarkAsCleanShutdown(); + std::vector profiles(pm->GetLoadedProfiles()); + for (size_t i = 0; i < profiles.size(); ++i) + profiles[i]->MarkAsCleanShutdown(); // Tell the metrics service it was cleanly shutdown. MetricsService* metrics = g_browser_process->metrics_service(); diff --git a/chrome/browser/chromeos/login/existing_user_controller.cc b/chrome/browser/chromeos/login/existing_user_controller.cc index dcba46b..d080e73 100644 --- a/chrome/browser/chromeos/login/existing_user_controller.cc +++ b/chrome/browser/chromeos/login/existing_user_controller.cc @@ -16,7 +16,6 @@ #include "chrome/browser/chromeos/cros/network_library.h" #include "chrome/browser/chromeos/login/helper.h" #include "chrome/browser/chromeos/login/login_display_host.h" -#include "chrome/browser/chromeos/login/login_utils.h" #include "chrome/browser/chromeos/login/views_login_display.h" #include "chrome/browser/chromeos/login/wizard_accessibility_helper.h" #include "chrome/browser/chromeos/login/wizard_controller.h" @@ -321,6 +320,14 @@ void ExistingUserController::OnLoginSuccess( const std::string& password, const GaiaAuthConsumer::ClientLoginResult& credentials, bool pending_requests) { + bool known_user = UserManager::Get()->IsKnownUser(username); + bool login_only = + CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + switches::kLoginScreen) == WizardController::kLoginScreenName; + ready_for_browser_launch_ = known_user || login_only; + + two_factor_credentials_ = credentials.two_factor; + // LoginPerformer instance will delete itself once online auth result is OK. // In case of failure it'll bring up ScreenLock and ask for // correct password/display error message. @@ -329,12 +336,19 @@ void ExistingUserController::OnLoginSuccess( login_performer_->set_delegate(NULL); LoginPerformer* performer = login_performer_.release(); performer = NULL; - bool known_user = UserManager::Get()->IsKnownUser(username); - bool login_only = - CommandLine::ForCurrentProcess()->GetSwitchValueASCII( - switches::kLoginScreen) == WizardController::kLoginScreenName; + + // Will call OnProfilePrepared() in the end. + LoginUtils::Get()->PrepareProfile(username, + password, + credentials, + pending_requests, + this); + +} + +void ExistingUserController::OnProfilePrepared(Profile* profile) { // TODO(nkostylev): May add login UI implementation callback call. - if (!known_user && !login_only) { + if (!ready_for_browser_launch_) { #if defined(OFFICIAL_BUILD) CommandLine::ForCurrentProcess()->AppendArg(kGetStartedURL); #endif // OFFICIAL_BUILD @@ -345,29 +359,18 @@ void ExistingUserController::OnLoginSuccess( CommandLine::ForCurrentProcess()->AppendArg(initial_start_page_); } - if (credentials.two_factor) { + if (two_factor_credentials_) { // If we have a two factor error and and this is a new user, // load the personal settings page. // TODO(stevenjb): direct the user to a lightweight sync login page. CommandLine::ForCurrentProcess()->AppendArg(kSettingsSyncLoginURL); } - // For new user login don't launch browser until we pass image screen. - LoginUtils::Get()->EnableBrowserLaunch(false); - LoginUtils::Get()->CompleteLogin(username, - password, - credentials, - pending_requests); - ActivateWizard(WizardController::IsDeviceRegistered() ? WizardController::kUserImageScreenName : WizardController::kRegistrationScreenName); } else { - LoginUtils::Get()->CompleteLogin(username, - password, - credentials, - pending_requests); - + LoginUtils::DoBrowserLaunch(profile); // Delay deletion as we're on the stack. host_->OnSessionStart(); } diff --git a/chrome/browser/chromeos/login/existing_user_controller.h b/chrome/browser/chromeos/login/existing_user_controller.h index d7a02d6..aff1bfc 100644 --- a/chrome/browser/chromeos/login/existing_user_controller.h +++ b/chrome/browser/chromeos/login/existing_user_controller.h @@ -16,6 +16,7 @@ #include "chrome/browser/chromeos/login/captcha_view.h" #include "chrome/browser/chromeos/login/login_display.h" #include "chrome/browser/chromeos/login/login_performer.h" +#include "chrome/browser/chromeos/login/login_utils.h" #include "chrome/browser/chromeos/login/ownership_status_checker.h" #include "chrome/browser/chromeos/login/password_changed_view.h" #include "chrome/browser/chromeos/login/user_manager.h" @@ -41,6 +42,7 @@ class UserCrosSettingsProvider; class ExistingUserController : public LoginDisplay::Delegate, public NotificationObserver, public LoginPerformer::Delegate, + public LoginUtils::Delegate, public CaptchaView::Delegate, public PasswordChangedView::Delegate { public: @@ -91,6 +93,9 @@ class ExistingUserController : public LoginDisplay::Delegate, const GaiaAuthConsumer::ClientLoginResult& credentials); virtual void WhiteListCheckFailed(const std::string& email); + // LoginUtils::Delegate implementation: + virtual void OnProfilePrepared(Profile* profile); + // CaptchaView::Delegate: virtual void OnCaptchaEntered(const std::string& captcha); @@ -159,6 +164,12 @@ class ExistingUserController : public LoginDisplay::Delegate, // Factory of callbacks. ScopedRunnableMethodFactory method_factory_; + // Whether everything is ready to launch the browser. + bool ready_for_browser_launch_; + + // Whether two factor credentials were used. + bool two_factor_credentials_; + // Used to verify ownership before starting enterprise enrollment. scoped_ptr ownership_checker_; diff --git a/chrome/browser/chromeos/login/login_performer.cc b/chrome/browser/chromeos/login/login_performer.cc index 4aad0fa..8d333e6 100644 --- a/chrome/browser/chromeos/login/login_performer.cc +++ b/chrome/browser/chromeos/login/login_performer.cc @@ -135,31 +135,39 @@ void LoginPerformer::OnLoginSuccess( pending_requests); return; } else { + // Online login has succeeded. DCHECK(!pending_requests) << "Pending request w/o delegate_ should not happen!"; - // Online login has succeeded. - Profile* profile = - g_browser_process->profile_manager()->GetDefaultProfile(); - LoginUtils::Get()->FetchCookies(profile, credentials); - LoginUtils::Get()->FetchTokens(profile, credentials); - - // Don't unlock screen if it was locked while we're waiting - // for initial online auth. - if (ScreenLocker::default_screen_locker() && - !initial_online_auth_pending_) { - DVLOG(1) << "Online login OK - unlocking screen."; - RequestScreenUnlock(); - // Do not delete itself just yet, wait for unlock. - // See ResolveScreenUnlocked(). - return; - } - initial_online_auth_pending_ = false; - // There's nothing else that's holding LP from deleting itself - - // no ScreenLock, no pending requests. - MessageLoop::current()->DeleteSoon(FROM_HERE, this); + // It is not guaranted, that profile creation has been finished yet. So use + // async version here. + credentials_ = credentials; + ProfileManager::CreateDefaultProfileAsync(this); } } +void LoginPerformer::OnProfileCreated(Profile* profile) { + CHECK(profile); + + LoginUtils::Get()->FetchCookies(profile, credentials_); + LoginUtils::Get()->FetchTokens(profile, credentials_); + credentials_ = GaiaAuthConsumer::ClientLoginResult(); + + // Don't unlock screen if it was locked while we're waiting + // for initial online auth. + if (ScreenLocker::default_screen_locker() && + !initial_online_auth_pending_) { + DVLOG(1) << "Online login OK - unlocking screen."; + RequestScreenUnlock(); + // Do not delete itself just yet, wait for unlock. + // See ResolveScreenUnlocked(). + return; + } + initial_online_auth_pending_ = false; + // There's nothing else that's holding LP from deleting itself - + // no ScreenLock, no pending requests. + MessageLoop::current()->DeleteSoon(FROM_HERE, this); +} + void LoginPerformer::OnOffTheRecordLoginSuccess() { UserMetrics::RecordAction( UserMetricsAction("Login_GuestLoginSuccess")); diff --git a/chrome/browser/chromeos/login/login_performer.h b/chrome/browser/chromeos/login/login_performer.h index ff3c829..fb847c7 100644 --- a/chrome/browser/chromeos/login/login_performer.h +++ b/chrome/browser/chromeos/login/login_performer.h @@ -14,6 +14,7 @@ #include "chrome/browser/chromeos/login/authenticator.h" #include "chrome/browser/chromeos/login/login_status_consumer.h" #include "chrome/browser/chromeos/login/signed_settings_helper.h" +#include "chrome/browser/profiles/profile_manager.h" #include "chrome/common/net/gaia/google_service_auth_error.h" #include "content/common/notification_observer.h" #include "content/common/notification_registrar.h" @@ -52,7 +53,8 @@ namespace chromeos { // 2. Pending online auth request. class LoginPerformer : public LoginStatusConsumer, public SignedSettingsHelper::Callback, - public NotificationObserver { + public NotificationObserver, + public ProfileManager::Observer { public: // Delegate class to get notifications from the LoginPerformer. class Delegate : public LoginStatusConsumer { @@ -117,6 +119,9 @@ class LoginPerformer : public LoginStatusConsumer, void set_delegate(Delegate* delegate) { delegate_ = delegate; } private: + // ProfeleManager::Observer implementation: + void OnProfileCreated(Profile* profile); + // Requests screen lock and subscribes to screen lock notifications. void RequestScreenLock(); @@ -183,6 +188,8 @@ class LoginPerformer : public LoginStatusConsumer, // is locked during that stage. No need to resolve screen lock action then. bool initial_online_auth_pending_; + GaiaAuthConsumer::ClientLoginResult credentials_; + ScopedRunnableMethodFactory method_factory_; DISALLOW_COPY_AND_ASSIGN(LoginPerformer); diff --git a/chrome/browser/chromeos/login/login_utils.cc b/chrome/browser/chromeos/login/login_utils.cc index f71a5a5..5045ba9 100644 --- a/chrome/browser/chromeos/login/login_utils.cc +++ b/chrome/browser/chromeos/login/login_utils.cc @@ -103,20 +103,19 @@ class ResetDefaultProxyConfigServiceTask : public Task { } // namespace -class LoginUtilsImpl : public LoginUtils { +class LoginUtilsImpl : public LoginUtils, + public ProfileManager::Observer { public: LoginUtilsImpl() - : browser_launch_enabled_(true), - background_view_(NULL) { + : background_view_(NULL) { } - // Invoked after the user has successfully logged in. This launches a browser - // and does other bookkeeping after logging in. - virtual void CompleteLogin( + virtual void PrepareProfile( const std::string& username, const std::string& password, const GaiaAuthConsumer::ClientLoginResult& credentials, - bool pending_requests); + bool pending_requests, + LoginUtils::Delegate* delegate); // Invoked after the tmpfs is successfully mounted. // Launches a browser in the incognito mode. @@ -130,13 +129,6 @@ class LoginUtilsImpl : public LoginUtils { // Authenticator and must delete it when done. virtual Authenticator* CreateAuthenticator(LoginStatusConsumer* consumer); - // Used to postpone browser launch via DoBrowserLaunch() if some post - // login screen is to be shown. - virtual void EnableBrowserLaunch(bool enable); - - // Returns if browser launch enabled now or not. - virtual bool IsBrowserLaunchEnabled() const; - // Warms the url used by authentication. virtual void PrewarmAuthentication(); @@ -157,6 +149,9 @@ class LoginUtilsImpl : public LoginUtils { // Gets the current background view. virtual chromeos::BackgroundView* GetBackgroundView(); + // ProfileManager::Observer implementation: + virtual void OnProfileCreated(Profile* profile); + protected: virtual std::string GetOffTheRecordCommandLine( const GURL& start_url, @@ -167,12 +162,17 @@ class LoginUtilsImpl : public LoginUtils { // Check user's profile for kApplicationLocale setting. void RespectLocalePreference(Profile* pref); - // Indicates if DoBrowserLaunch will actually launch the browser or not. - bool browser_launch_enabled_; - // The current background view. chromeos::BackgroundView* background_view_; + std::string username_; + std::string password_; + GaiaAuthConsumer::ClientLoginResult credentials_; + bool pending_requests_; + + // Delegate to be fired when the profile will be prepared. + LoginUtils::Delegate* delegate_; + DISALLOW_COPY_AND_ASSIGN(LoginUtilsImpl); }; @@ -204,11 +204,12 @@ class LoginUtilsWrapper { DISALLOW_COPY_AND_ASSIGN(LoginUtilsWrapper); }; -void LoginUtilsImpl::CompleteLogin( +void LoginUtilsImpl::PrepareProfile( const std::string& username, const std::string& password, const GaiaAuthConsumer::ClientLoginResult& credentials, - bool pending_requests) { + bool pending_requests, + LoginUtils::Delegate* delegate) { BootTimesLoader* btl = BootTimesLoader::Get(); VLOG(1) << "Completing login for " << username; @@ -219,30 +220,31 @@ void LoginUtilsImpl::CompleteLogin( btl->AddLoginTimeMarker("StartedSession", false); } - bool first_login = !UserManager::Get()->IsKnownUser(username); UserManager::Get()->UserLoggedIn(username); btl->AddLoginTimeMarker("UserLoggedIn", false); - // Now get the new profile. - FilePath user_data_dir; - PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); - ProfileManager* profile_manager = g_browser_process->profile_manager(); - // Switch log file as soon as possible. logging::RedirectChromeLogging(*(CommandLine::ForCurrentProcess())); btl->AddLoginTimeMarker("LoggingRedirected", false); - Profile* profile = NULL; - { - // Loading user profile causes us to do blocking IO on UI thread. - // Temporarily allow it until we fix http://crosbug.com/11104 - base::ThreadRestrictions::ScopedAllowIO allow_io; - // The default profile will have been changed because the ProfileManager - // will process the notification that the UserManager sends out. - profile = profile_manager->GetDefaultProfile(user_data_dir); - } + username_ = username; + password_ = password; + credentials_ = credentials; + pending_requests_ = pending_requests; + delegate_ = delegate; + + // The default profile will have been changed because the ProfileManager + // will process the notification that the UserManager sends out. + ProfileManager::CreateDefaultProfileAsync(this); +} + +void LoginUtilsImpl::OnProfileCreated(Profile* profile) { + CHECK(profile); + + BootTimesLoader* btl = BootTimesLoader::Get(); btl->AddLoginTimeMarker("UserProfileGotten", false); + bool first_login = !UserManager::Get()->IsKnownUser(username_); // Change the proxy configuration service of the default request context to // use the preference configuration from the logged-in profile. This ensures // that requests done through the default context use the proxy configuration @@ -266,11 +268,11 @@ void LoginUtilsImpl::CompleteLogin( proxy_config_service)); // Since we're doing parallel authentication, only new user sign in - // would perform online auth before calling CompleteLogin. + // would perform online auth before calling PrepareProfile. // For existing users there's usually a pending online auth request. // Cookies will be fetched after it's is succeeded. - if (!pending_requests) { - FetchCookies(profile, credentials); + if (!pending_requests_) { + FetchCookies(profile, credentials_); } // Init extension event routers; this normally happens in browser_main @@ -290,16 +292,16 @@ void LoginUtilsImpl::CompleteLogin( // For existing users there's usually a pending online auth request. // Tokens will be fetched after it's is succeeded. - if (!pending_requests) { - FetchTokens(profile, credentials); + if (!pending_requests_) { + FetchTokens(profile, credentials_); } btl->AddLoginTimeMarker("TokensGotten", false); // Set the CrOS user by getting this constructor run with the // user's email on first retrieval. - profile->GetProfileSyncService(username)->SetPassphrase(password, - false, - true); + profile->GetProfileSyncService(username_)->SetPassphrase(password_, + false, + true); btl->AddLoginTimeMarker("SyncStarted", false); // Own TPM device if, for any reason, it has not been done in EULA @@ -330,13 +332,19 @@ void LoginUtilsImpl::CompleteLogin( // done yet to pull down policies from the domain admin. We'll take this // out when we get that done properly. // TODO(xiyuan): Remove this once enterprise feature is ready. - if (EndsWith(username, "@google.com", true)) { + if (EndsWith(username_, "@google.com", true)) { PrefService* pref_service = profile->GetPrefs(); pref_service->SetBoolean(prefs::kEnableScreenLock, true); } profile->OnLogin(); - DoBrowserLaunch(profile); + + delegate_->OnProfilePrepared(profile); + + // TODO(altimofeev): Need to sanitize memory used to store password. + password_ = ""; + username_ = ""; + credentials_ = GaiaAuthConsumer::ClientLoginResult(); } void LoginUtilsImpl::FetchCookies( @@ -515,14 +523,6 @@ Authenticator* LoginUtilsImpl::CreateAuthenticator( return new GoogleAuthenticator(consumer); } -void LoginUtilsImpl::EnableBrowserLaunch(bool enable) { - browser_launch_enabled_ = enable; -} - -bool LoginUtilsImpl::IsBrowserLaunchEnabled() const { - return browser_launch_enabled_; -} - // We use a special class for this so that it can be safely leaked if we // never connect. At shutdown the order is not well defined, and it's possible // for the infrastructure needed to unregister might be unstable and crash. @@ -580,9 +580,6 @@ void LoginUtils::Set(LoginUtils* mock) { void LoginUtils::DoBrowserLaunch(Profile* profile) { BootTimesLoader::Get()->AddLoginTimeMarker("BrowserLaunched", false); - // Browser launch was disabled due to some post login screen. - if (!LoginUtils::Get()->IsBrowserLaunchEnabled()) - return; // Update command line in case loose values were added. CommandLine::ForCurrentProcess()->InitFromArgv( diff --git a/chrome/browser/chromeos/login/login_utils.h b/chrome/browser/chromeos/login/login_utils.h index 05a1a6c..d0eaeb7 100644 --- a/chrome/browser/chromeos/login/login_utils.h +++ b/chrome/browser/chromeos/login/login_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -27,6 +27,12 @@ class LoginStatusConsumer; class LoginUtils { public: + class Delegate { + public: + // Called after profile is loaded and prepared for the session. + virtual void OnProfilePrepared(Profile* profile) = 0; + }; + // Get LoginUtils singleton object. If it was not set before, new default // instance will be created. static LoginUtils* Get(); @@ -40,14 +46,14 @@ class LoginUtils { virtual ~LoginUtils() {} - // Invoked after the user has successfully logged in. This launches a browser - // and does other bookkeeping after logging in. + // Loads and prepares profile for the session. Fires |delegate| in the end. // If |pending_requests| is true, there's a pending online auth request. - virtual void CompleteLogin( + virtual void PrepareProfile( const std::string& username, const std::string& password, const GaiaAuthConsumer::ClientLoginResult& credentials, - bool pending_requests) = 0; + bool pending_requests, + Delegate* delegate) = 0; // Invoked after the tmpfs is successfully mounted. // Asks session manager to restart Chrome in Browse Without Sign In mode. @@ -62,13 +68,6 @@ class LoginUtils { // Authenticator and must delete it when done. virtual Authenticator* CreateAuthenticator(LoginStatusConsumer* consumer) = 0; - // Used to postpone browser launch via DoBrowserLaunch() if some post - // login screen is to be shown. - virtual void EnableBrowserLaunch(bool enable) = 0; - - // Returns if browser launch enabled now or not. - virtual bool IsBrowserLaunchEnabled() const = 0; - // Prewarms the authentication network connection. virtual void PrewarmAuthentication() = 0; diff --git a/chrome/browser/chromeos/login/mock_authenticator.h b/chrome/browser/chromeos/login/mock_authenticator.h index 4816442..73c5352 100644 --- a/chrome/browser/chromeos/login/mock_authenticator.h +++ b/chrome/browser/chromeos/login/mock_authenticator.h @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -111,12 +111,15 @@ class MockLoginUtils : public LoginUtils { return false; } - virtual void CompleteLogin(const std::string& username, - const std::string& password, - const GaiaAuthConsumer::ClientLoginResult& res, - bool pending_requests) { + virtual void PrepareProfile(const std::string& username, + const std::string& password, + const GaiaAuthConsumer::ClientLoginResult& res, + bool pending_requests, + Delegate* delegate) { EXPECT_EQ(expected_username_, username); EXPECT_EQ(expected_password_, password); + // Profile hasn't been loaded. + delegate->OnProfilePrepared(NULL); } virtual void CompleteOffTheRecordLogin(const GURL& start_url) { @@ -130,13 +133,6 @@ class MockLoginUtils : public LoginUtils { consumer, expected_username_, expected_password_); } - virtual void EnableBrowserLaunch(bool enable) { - } - - virtual bool IsBrowserLaunchEnabled() const { - return true; - } - virtual void PrewarmAuthentication() { } diff --git a/chrome/browser/chromeos/login/screen_locker.cc b/chrome/browser/chromeos/login/screen_locker.cc index 09d7675..0d74e11 100644 --- a/chrome/browser/chromeos/login/screen_locker.cc +++ b/chrome/browser/chromeos/login/screen_locker.cc @@ -36,6 +36,7 @@ #include "chrome/browser/chromeos/view_ids.h" #include "chrome/browser/chromeos/wm_ipc.h" #include "chrome/browser/metrics/user_metrics.h" +#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/sync/profile_sync_service.h" #include "chrome/browser/ui/browser.h" diff --git a/chrome/browser/chromeos/login/wizard_controller.cc b/chrome/browser/chromeos/login/wizard_controller.cc index d28a56c..02111db 100644 --- a/chrome/browser/chromeos/login/wizard_controller.cc +++ b/chrome/browser/chromeos/login/wizard_controller.cc @@ -550,7 +550,6 @@ void WizardController::OnUserImageSelected() { // Host will mark itself (and all controllers/windows) for deletion. host_->OnSessionStart(); // Launch browser after controller is deleted and its windows are closed. - chromeos::LoginUtils::Get()->EnableBrowserLaunch(true); BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, diff --git a/chrome/browser/content_settings/content_settings_pref_provider_unittest.cc b/chrome/browser/content_settings/content_settings_pref_provider_unittest.cc index 6f9827d..105cb96 100644 --- a/chrome/browser/content_settings/content_settings_pref_provider_unittest.cc +++ b/chrome/browser/content_settings/content_settings_pref_provider_unittest.cc @@ -36,7 +36,7 @@ class ContentSettingsPrefService : public PrefService { : PrefService( managed_platform_prefs, managed_cloud_prefs, extension_prefs, command_line_prefs, user_prefs, recommended_platform_prefs, - recommended_cloud_prefs, default_store) {} + recommended_cloud_prefs, default_store, NULL) {} virtual ~ContentSettingsPrefService() {} }; } diff --git a/chrome/browser/debugger/extension_ports_remote_service.cc b/chrome/browser/debugger/extension_ports_remote_service.cc index b769ca0..9db706c 100644 --- a/chrome/browser/debugger/extension_ports_remote_service.cc +++ b/chrome/browser/debugger/extension_ports_remote_service.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -121,12 +121,11 @@ ExtensionPortsRemoteService::ExtensionPortsRemoteService( LOG(WARNING) << "No profile manager for ExtensionPortsRemoteService"; return; } - for (ProfileManager::ProfileVector::const_iterator it - = profile_manager->begin(); - it != profile_manager->end(); - ++it) { - if (!(*it)->IsOffTheRecord()) { - service_ = (*it)->GetExtensionMessageService(); + + std::vector profiles(profile_manager->GetLoadedProfiles()); + for (size_t i = 0; i < profiles.size(); ++i) { + if (!profiles[i]->IsOffTheRecord()) { + service_ = profiles[i]->GetExtensionMessageService(); break; } } diff --git a/chrome/browser/extensions/extension_event_router_forwarder.cc b/chrome/browser/extensions/extension_event_router_forwarder.cc index 5147453..969c61d 100644 --- a/chrome/browser/extensions/extension_event_router_forwarder.cc +++ b/chrome/browser/extensions/extension_event_router_forwarder.cc @@ -80,11 +80,11 @@ void ExtensionEventRouterForwarder::HandleEvent( profile, extension_id, event_name, event_args, use_profile_to_restrict_events ? profile : NULL, event_url); } else { - ProfileManager::iterator i; - for (i = profile_manager->begin(); i != profile_manager->end(); ++i) { + std::vector profiles(profile_manager->GetLoadedProfiles()); + for (size_t i = 0; i < profiles.size(); ++i) { CallExtensionEventRouter( - *i, extension_id, event_name, event_args, - use_profile_to_restrict_events ? (*i) : NULL, event_url); + profiles[i], extension_id, event_name, event_args, + use_profile_to_restrict_events ? profiles[i] : NULL, event_url); } } } @@ -112,4 +112,3 @@ void ExtensionEventRouterForwarder::CallExtensionEventRouter( event_name, event_args, restrict_to_profile, event_url); } } - diff --git a/chrome/browser/extensions/extension_event_router_forwarder_unittest.cc b/chrome/browser/extensions/extension_event_router_forwarder_unittest.cc index ac4405e..7d82ea5 100644 --- a/chrome/browser/extensions/extension_event_router_forwarder_unittest.cc +++ b/chrome/browser/extensions/extension_event_router_forwarder_unittest.cc @@ -52,8 +52,8 @@ class ExtensionEventRouterForwarderTest : public TestingBrowserProcessTest { profile1_ = new TestingProfile(); profile2_ = new TestingProfile(); - browser_process->profile_manager()->RegisterProfile(profile1_); - browser_process->profile_manager()->RegisterProfile(profile2_); + browser_process->profile_manager()->RegisterProfile(profile1_, true); + browser_process->profile_manager()->RegisterProfile(profile2_, true); } TestingProfile* CreateIncognitoProfile(TestingProfile* base) { diff --git a/chrome/browser/memory_purger.cc b/chrome/browser/memory_purger.cc index 78d009d..80a884e 100644 --- a/chrome/browser/memory_purger.cc +++ b/chrome/browser/memory_purger.cc @@ -98,11 +98,10 @@ void MemoryPurger::PurgeBrowser() { new PurgeMemoryIOHelper(g_browser_process->resource_dispatcher_host()-> safe_browsing_service())); ProfileManager* profile_manager = g_browser_process->profile_manager(); - for (ProfileManager::iterator i(profile_manager->begin()); - i != profile_manager->end(); ++i) { - Profile* profile = *i; + std::vector profiles(profile_manager->GetLoadedProfiles()); + for (size_t i = 0; i < profiles.size(); ++i) { purge_memory_io_helper->AddRequestContextGetter( - make_scoped_refptr(profile->GetRequestContext())); + make_scoped_refptr(profiles[i]->GetRequestContext())); // NOTE: Some objects below may be duplicates across profiles. We could // conceivably put all these in sets and then iterate over the sets. @@ -111,20 +110,20 @@ void MemoryPurger::PurgeBrowser() { // Spinning up the history service is expensive, so we avoid doing it if it // hasn't been done already. HistoryService* history_service = - profile->GetHistoryServiceWithoutCreating(); + profiles[i]->GetHistoryServiceWithoutCreating(); if (history_service) history_service->UnloadBackend(); // Unload all web databases (freeing memory used to cache sqlite). WebDataService* web_data_service = - profile->GetWebDataServiceWithoutCreating(); + profiles[i]->GetWebDataServiceWithoutCreating(); if (web_data_service) web_data_service->UnloadDatabase(); // Ask all WebKitContexts to purge memory (freeing memory used to cache // the LocalStorage sqlite DB). WebKitContext creation is basically free so // we don't bother with a "...WithoutCreating()" function. - profile->GetWebKitContext()->PurgeMemory(); + profiles[i]->GetWebKitContext()->PurgeMemory(); } BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, diff --git a/chrome/browser/prefs/pref_service.cc b/chrome/browser/prefs/pref_service.cc index 0a2f381..e960eb4 100644 --- a/chrome/browser/prefs/pref_service.cc +++ b/chrome/browser/prefs/pref_service.cc @@ -25,7 +25,6 @@ #include "chrome/browser/prefs/overlay_persistent_pref_store.h" #include "chrome/browser/prefs/pref_notifier_impl.h" #include "chrome/browser/prefs/pref_value_store.h" -#include "chrome/common/json_pref_store.h" #include "content/browser/browser_thread.h" #include "content/common/notification_service.h" #include "grit/chromium_strings.h" @@ -88,6 +87,15 @@ void NotifyReadError(PrefService* pref, int message_id) { PrefService* PrefService::CreatePrefService(const FilePath& pref_filename, PrefStore* extension_prefs, Profile* profile) { + return CreatePrefServiceAsync(pref_filename, extension_prefs, profile, NULL); +} + +// static +PrefService* PrefService::CreatePrefServiceAsync( + const FilePath& pref_filename, + PrefStore* extension_prefs, + Profile* profile, + PrefService::Delegate* delegate) { using policy::ConfigurationPolicyPrefStore; #if defined(OS_LINUX) @@ -121,7 +129,7 @@ PrefService* PrefService::CreatePrefService(const FilePath& pref_filename, return new PrefService(managed_platform, managed_cloud, extension_prefs, command_line, user, recommended_platform, - recommended_cloud, default_pref_store); + recommended_cloud, default_pref_store, delegate); } PrefService* PrefService::CreateIncognitoPrefService( @@ -136,9 +144,11 @@ PrefService::PrefService(PrefStore* managed_platform_prefs, PersistentPrefStore* user_prefs, PrefStore* recommended_platform_prefs, PrefStore* recommended_cloud_prefs, - DefaultPrefStore* default_store) + DefaultPrefStore* default_store, + PrefService::Delegate* delegate) : user_pref_store_(user_prefs), - default_store_(default_store) { + default_store_(default_store), + delegate_(delegate) { pref_notifier_.reset(new PrefNotifierImpl(this)); pref_value_store_.reset( new PrefValueStore(managed_platform_prefs, @@ -157,7 +167,8 @@ PrefService::PrefService(const PrefService& original, PrefStore* incognito_extension_prefs) : user_pref_store_( new OverlayPersistentPrefStore(original.user_pref_store_.get())), - default_store_(original.default_store_.get()){ + default_store_(original.default_store_.get()), + delegate_(NULL) { pref_notifier_.reset(new PrefNotifierImpl(this)); pref_value_store_.reset(original.pref_value_store_->CloneAndSpecialize( NULL, // managed_platform_prefs @@ -183,27 +194,48 @@ PrefService::~PrefService() { default_store_ = NULL; } -void PrefService::InitFromStorage() { - const PersistentPrefStore::PrefReadError error = - user_pref_store_->ReadPrefs(); - if (error == PersistentPrefStore::PREF_READ_ERROR_NONE) +void PrefService::OnPrefsRead(PersistentPrefStore::PrefReadError error, + bool no_dir) { + if (no_dir) { + // Bad news. When profile is created, the process that creates the directory + // is explicitly started. So if directory is missing it probably means that + // Chromium hasn't sufficient privileges. + CHECK(delegate_); + delegate_->OnPrefsLoaded(this, false); return; + } + + if (error != PersistentPrefStore::PREF_READ_ERROR_NONE) { + // Failing to load prefs on startup is a bad thing(TM). See bug 38352 for + // an example problem that this can cause. + // Do some diagnosis and try to avoid losing data. + int message_id = 0; + if (error <= PersistentPrefStore::PREF_READ_ERROR_JSON_TYPE) { + message_id = IDS_PREFERENCES_CORRUPT_ERROR; + } else if (error != PersistentPrefStore::PREF_READ_ERROR_NO_FILE) { + message_id = IDS_PREFERENCES_UNREADABLE_ERROR; + } - // Failing to load prefs on startup is a bad thing(TM). See bug 38352 for - // an example problem that this can cause. - // Do some diagnosis and try to avoid losing data. - int message_id = 0; - if (error <= PersistentPrefStore::PREF_READ_ERROR_JSON_TYPE) { - message_id = IDS_PREFERENCES_CORRUPT_ERROR; - } else if (error != PersistentPrefStore::PREF_READ_ERROR_NO_FILE) { - message_id = IDS_PREFERENCES_UNREADABLE_ERROR; + if (message_id) { + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + NewRunnableFunction(&NotifyReadError, this, message_id)); + } + UMA_HISTOGRAM_ENUMERATION("PrefService.ReadError", error, 20); } - if (message_id) { - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - NewRunnableFunction(&NotifyReadError, this, message_id)); + if (delegate_) + delegate_->OnPrefsLoaded(this, true); +} + +void PrefService::InitFromStorage() { + if (!delegate_) { + const PersistentPrefStore::PrefReadError error = + user_pref_store_->ReadPrefs(); + OnPrefsRead(error, false); + } else { + // todo(altimofeev): move this method to PersistentPrefStore interface. + (static_cast(user_pref_store_.get()))->ReadPrefs(this); } - UMA_HISTOGRAM_ENUMERATION("PrefService.ReadError", error, 20); } bool PrefService::ReloadPersistentPrefs() { diff --git a/chrome/browser/prefs/pref_service.h b/chrome/browser/prefs/pref_service.h index 690b8c2..59e4b51 100644 --- a/chrome/browser/prefs/pref_service.h +++ b/chrome/browser/prefs/pref_service.h @@ -15,6 +15,7 @@ #include "base/memory/scoped_ptr.h" #include "base/threading/non_thread_safe.h" #include "base/values.h" +#include "chrome/common/json_pref_store.h" class DefaultPrefStore; class FilePath; @@ -32,7 +33,8 @@ class PrefMemberBase; class ScopedUserPrefUpdateBase; }; -class PrefService : public base::NonThreadSafe { +class PrefService : public base::NonThreadSafe, + public JsonPrefStore::Delegate { public: // A helper class to store all the information associated with a preference. class Preference { @@ -111,6 +113,15 @@ class PrefService : public base::NonThreadSafe { DISALLOW_COPY_AND_ASSIGN(Preference); }; + class Delegate { + public: + virtual void OnPrefsLoaded(PrefService* prefs, bool success) = 0; + }; + + // JsonPrefStore::Delegate implementaion. + virtual void OnPrefsRead(PersistentPrefStore::PrefReadError error, + bool no_dir); + // Factory method that creates a new instance of a PrefService with the // applicable PrefStores. The |pref_filename| points to the user preference // file. The |profile| is the one to which these preferences apply; it may be @@ -122,6 +133,12 @@ class PrefService : public base::NonThreadSafe { PrefStore* extension_pref_store, Profile* profile); + // Same as above, but with async initialization. + static PrefService* CreatePrefServiceAsync(const FilePath& pref_filename, + PrefStore* extension_pref_store, + Profile* profile, + Delegate* delegate); + // Creates an incognito copy of the pref service that shares most pref stores // but uses a fresh non-persistent overlay for the user pref store and an // individual extension pref store (to cache the effective extension prefs for @@ -238,7 +255,8 @@ class PrefService : public base::NonThreadSafe { PersistentPrefStore* user_prefs, PrefStore* recommended_platform_prefs, PrefStore* recommended_cloud_prefs, - DefaultPrefStore* default_store); + DefaultPrefStore* default_store, + Delegate* delegate); // The PrefNotifier handles registering and notifying preference observers. // It is created and owned by this PrefService. Subclasses may access it for @@ -320,6 +338,10 @@ class PrefService : public base::NonThreadSafe { // of registered preferences are. mutable PreferenceSet prefs_; + // Holds delegator to be called after initialization, if async version + // is used. + Delegate* delegate_; + DISALLOW_COPY_AND_ASSIGN(PrefService); }; diff --git a/chrome/browser/prefs/pref_service_mock_builder.cc b/chrome/browser/prefs/pref_service_mock_builder.cc index 14012d1..1de0403 100644 --- a/chrome/browser/prefs/pref_service_mock_builder.cc +++ b/chrome/browser/prefs/pref_service_mock_builder.cc @@ -113,7 +113,8 @@ PrefService* PrefServiceMockBuilder::Create() { user_prefs_.get(), recommended_platform_prefs_.get(), recommended_cloud_prefs_.get(), - new DefaultPrefStore()); + new DefaultPrefStore(), + NULL); managed_platform_prefs_ = NULL; managed_cloud_prefs_ = NULL; extension_prefs_ = NULL; diff --git a/chrome/browser/profiles/profile.h b/chrome/browser/profiles/profile.h index 1054aad..d63413f 100644 --- a/chrome/browser/profiles/profile.h +++ b/chrome/browser/profiles/profile.h @@ -130,6 +130,12 @@ class Profile { IMPLICIT_ACCESS }; + class Delegate { + public: + // Called when creation of the profile is finished. + virtual void OnProfileCreated(Profile* profile, bool success) = 0; + }; + // Key used to bind profile to the widget with which it is associated. static const char* kProfileKey; @@ -146,6 +152,10 @@ class Profile { // Create a new profile given a path. static Profile* CreateProfile(const FilePath& path); + // Same as above, but uses async initialization. + static Profile* CreateProfileAsync(const FilePath& path, + Delegate* delegate); + // Returns the request context for the "default" profile. This may be called // from any thread. This CAN return NULL if a first request context has not // yet been created. If necessary, listen on the UI thread for diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc index 66a28b1..844642a 100644 --- a/chrome/browser/profiles/profile_impl.cc +++ b/chrome/browser/profiles/profile_impl.cc @@ -231,7 +231,27 @@ void ProfileSizeTask::Run() { // static Profile* Profile::CreateProfile(const FilePath& path) { - return new ProfileImpl(path); + if (!file_util::PathExists(path)) { + // TODO(tc): http://b/1094718 Bad things happen if we can't write to the + // profile directory. We should eventually be able to run in this + // situation. + if (!file_util::CreateDirectory(path)) + return NULL; + } + return new ProfileImpl(path, NULL); +} + +// static +Profile* Profile::CreateProfileAsync(const FilePath&path, + Profile::Delegate* delegate) { + DCHECK(delegate); + // This is safe while all file opeartions are done on the FILE thread. + BrowserThread::PostTask(BrowserThread::FILE, + FROM_HERE, + NewRunnableFunction(&file_util::CreateDirectory, + path)); + // Async version. + return new ProfileImpl(path, delegate); } // static @@ -241,7 +261,8 @@ void ProfileImpl::RegisterUserPrefs(PrefService* prefs) { DefaultApps::RegisterUserPrefs(prefs); } -ProfileImpl::ProfileImpl(const FilePath& path) +ProfileImpl::ProfileImpl(const FilePath& path, + Profile::Delegate* delegate) : path_(path), visited_link_event_listener_(new VisitedLinkEventListener()), extension_devtools_manager_(NULL), @@ -259,13 +280,31 @@ ProfileImpl::ProfileImpl(const FilePath& path) #if defined(OS_WIN) checked_instant_promo_(false), #endif - shutdown_session_service_(false) { + shutdown_session_service_(false), + delegate_(delegate) { DCHECK(!path.empty()) << "Using an empty path will attempt to write " << "profile files to the root directory!"; create_session_service_timer_.Start( TimeDelta::FromMilliseconds(kCreateSessionServiceDelayMS), this, &ProfileImpl::EnsureSessionServiceCreated); + if (delegate_) { + prefs_.reset(PrefService::CreatePrefServiceAsync( + GetPrefFilePath(), + new ExtensionPrefStore(GetExtensionPrefValueMap(), false), + GetOriginalProfile(), + this)); // Ask to notify us in the end. + } else { + // Load prefs synchronously. + prefs_.reset(PrefService::CreatePrefService( + GetPrefFilePath(), + new ExtensionPrefStore(GetExtensionPrefValueMap(), false), + GetOriginalProfile())); + OnPrefsLoaded(prefs_.get(), true); + } +} + +void ProfileImpl::DoFinalInit() { PrefService* prefs = GetPrefs(); pref_change_registrar_.Init(prefs); pref_change_registrar_.Add(prefs::kSpellCheckDictionary, this); @@ -277,7 +316,16 @@ ProfileImpl::ProfileImpl(const FilePath& path) // the cache directory depends on the profile directory, which isn't available // to PathService. chrome::GetUserCacheDirectory(path_, &base_cache_path_); - file_util::CreateDirectory(base_cache_path_); + if (!delegate_) { + file_util::CreateDirectory(base_cache_path_); + } else { + // Async profile loading is used, so call this on the FILE thread instead. + // It is safe since all other file operations should also be done there. + BrowserThread::PostTask(BrowserThread::FILE, + FROM_HERE, + NewRunnableFunction(&file_util::CreateDirectory, + base_cache_path_)); + } #if !defined(OS_CHROMEOS) // Listen for bookmark model load, to bootstrap the sync service. @@ -344,6 +392,10 @@ ProfileImpl::ProfileImpl(const FilePath& path) // Initialize the ProfilePolicyConnector after |io_data_| since it requires // the URLRequestContextGetter to be initialized. GetPolicyConnector()->Initialize(); + + // Creation has been finished. + if (delegate_) + delegate_->OnProfileCreated(this, true); } void ProfileImpl::InitExtensions() { @@ -742,44 +794,50 @@ net::TransportSecurityState* return transport_security_state_.get(); } -PrefService* ProfileImpl::GetPrefs() { - if (!prefs_.get()) { - prefs_.reset(PrefService::CreatePrefService( - GetPrefFilePath(), - new ExtensionPrefStore(GetExtensionPrefValueMap(), false), - GetOriginalProfile())); +void ProfileImpl::OnPrefsLoaded(PrefService* prefs, bool success) { + DCHECK(prefs == prefs_.get()); - // The Profile class and ProfileManager class may read some prefs so - // register known prefs as soon as possible. - Profile::RegisterUserPrefs(prefs_.get()); - browser::RegisterUserPrefs(prefs_.get()); - // TODO(mirandac): remove migration code after 6 months (crbug.com/69995). - if (g_browser_process->local_state()) { - browser::MigrateBrowserPrefs(prefs_.get(), - g_browser_process->local_state()); - } + if (!success) { + DCHECK(delegate_); + delegate_->OnProfileCreated(this, false); + return; + } - // The last session exited cleanly if there is no pref for - // kSessionExitedCleanly or the value for kSessionExitedCleanly is true. - last_session_exited_cleanly_ = - prefs_->GetBoolean(prefs::kSessionExitedCleanly); - // Mark the session as open. - prefs_->SetBoolean(prefs::kSessionExitedCleanly, false); - // Make sure we save to disk that the session has opened. - prefs_->ScheduleSavePersistentPrefs(); - - // Ensure that preferences set by extensions are restored in the profile - // as early as possible. The constructor takes care of that. - extension_prefs_.reset(new ExtensionPrefs( - prefs_.get(), - GetPath().AppendASCII(ExtensionService::kInstallDirectoryName), - GetExtensionPrefValueMap())); - - DCHECK(!net_pref_observer_.get()); - net_pref_observer_.reset( - new NetPrefObserver(prefs_.get(), GetPrerenderManager())); + // The Profile class and ProfileManager class may read some prefs so + // register known prefs as soon as possible. + Profile::RegisterUserPrefs(prefs_.get()); + browser::RegisterUserPrefs(prefs_.get()); + // TODO(mirandac): remove migration code after 6 months (crbug.com/69995). + if (g_browser_process->local_state()) { + browser::MigrateBrowserPrefs(prefs_.get(), + g_browser_process->local_state()); } + // The last session exited cleanly if there is no pref for + // kSessionExitedCleanly or the value for kSessionExitedCleanly is true. + last_session_exited_cleanly_ = + prefs_->GetBoolean(prefs::kSessionExitedCleanly); + // Mark the session as open. + prefs_->SetBoolean(prefs::kSessionExitedCleanly, false); + // Make sure we save to disk that the session has opened. + prefs_->ScheduleSavePersistentPrefs(); + + // Ensure that preferences set by extensions are restored in the profile + // as early as possible. The constructor takes care of that. + extension_prefs_.reset(new ExtensionPrefs( + prefs_.get(), + GetPath().AppendASCII(ExtensionService::kInstallDirectoryName), + GetExtensionPrefValueMap())); + + DCHECK(!net_pref_observer_.get()); + net_pref_observer_.reset( + new NetPrefObserver(prefs_.get(), GetPrerenderManager())); + + DoFinalInit(); +} + +PrefService* ProfileImpl::GetPrefs() { + DCHECK(prefs_.get()); // Should explicitly be initialized. return prefs_.get(); } diff --git a/chrome/browser/profiles/profile_impl.h b/chrome/browser/profiles/profile_impl.h index 7e7095f..617e3bb 100644 --- a/chrome/browser/profiles/profile_impl.h +++ b/chrome/browser/profiles/profile_impl.h @@ -37,7 +37,8 @@ class NetPrefObserver; // The default profile implementation. class ProfileImpl : public Profile, public SpellCheckHostObserver, - public NotificationObserver { + public NotificationObserver, + public PrefService::Delegate { public: virtual ~ProfileImpl(); @@ -156,7 +157,15 @@ class ProfileImpl : public Profile, private: friend class Profile; - explicit ProfileImpl(const FilePath& path); + ProfileImpl(const FilePath& path, + Profile::Delegate* delegate); + + // Does final initialization. Should be called after prefs were loaded. + void DoFinalInit(); + + // PrefService::Delegate implementation. Does final prefs initialization and + // calls Init(). + void OnPrefsLoaded(PrefService* prefs, bool success); void CreateWebDataService(); FilePath GetPrefFilePath(); @@ -309,6 +318,8 @@ class ProfileImpl : public Profile, scoped_ptr chrome_url_data_manager_; + Profile::Delegate* delegate_; + DISALLOW_COPY_AND_ASSIGN(ProfileImpl); }; diff --git a/chrome/browser/profiles/profile_manager.cc b/chrome/browser/profiles/profile_manager.cc index b893b25..0f0576a 100644 --- a/chrome/browser/profiles/profile_manager.cc +++ b/chrome/browser/profiles/profile_manager.cc @@ -68,8 +68,9 @@ void ProfileManager::ShutdownSessionServices() { ProfileManager* pm = g_browser_process->profile_manager(); if (!pm) // Is NULL when running unit tests. return; - for (ProfileManager::const_iterator i = pm->begin(); i != pm->end(); ++i) - (*i)->ShutdownSessionService(); + std::vector profiles(pm->GetLoadedProfiles()); + for (size_t i = 0; i < profiles.size(); ++i) + profiles[i]->ShutdownSessionService(); } // static @@ -94,11 +95,6 @@ ProfileManager::~ProfileManager() { ui::SystemMonitor* system_monitor = ui::SystemMonitor::Get(); if (system_monitor) system_monitor->RemoveObserver(this); - - // Destroy all profiles that we're keeping track of. - for (const_iterator i(begin()); i != end(); ++i) - delete *i; - profiles_.clear(); } FilePath ProfileManager::GetDefaultProfileDir( @@ -155,10 +151,10 @@ Profile* ProfileManager::GetDefaultProfile(const FilePath& user_data_dir) { if (!command_line.HasSwitch(switches::kTestType) || command_line.HasSwitch(switches::kLoginProfile)) { // Don't init extensions for this profile - profile = GetProfile(default_profile_dir, false); + profile = GetProfile(default_profile_dir); profile = profile->GetOffTheRecordProfile(); } else { - profile = GetProfile(default_profile_dir, true); + profile = GetProfile(default_profile_dir); } return profile; } @@ -166,63 +162,102 @@ Profile* ProfileManager::GetDefaultProfile(const FilePath& user_data_dir) { return GetProfile(default_profile_dir); } -Profile* ProfileManager::GetProfile(const FilePath& profile_dir) { - return GetProfile(profile_dir, true); -} - Profile* ProfileManager::GetProfileWithId(ProfileId profile_id) { DCHECK_NE(Profile::kInvalidProfileId, profile_id); - for (iterator i = begin(); i != end(); ++i) { - if ((*i)->GetRuntimeId() == profile_id) - return *i; - if ((*i)->HasOffTheRecordProfile() && - (*i)->GetOffTheRecordProfile()->GetRuntimeId() == profile_id) { - return (*i)->GetOffTheRecordProfile(); + for (ProfilesInfoMap::iterator iter = profiles_info_.begin(); + iter != profiles_info_.end(); ++iter) { + if (iter->second->created) { + Profile* candidate = iter->second->profile.get(); + if (candidate->GetRuntimeId() == profile_id) + return candidate; + if (candidate->HasOffTheRecordProfile()) { + candidate = candidate->GetOffTheRecordProfile(); + if (candidate->GetRuntimeId() == profile_id) + return candidate; + } } } return NULL; } bool ProfileManager::IsValidProfile(Profile* profile) { - for (iterator i = begin(); i != end(); ++i) { - if (*i == profile) - return true; - if ((*i)->HasOffTheRecordProfile() && - (*i)->GetOffTheRecordProfile() == profile) { - return true; + for (ProfilesInfoMap::iterator iter = profiles_info_.begin(); + iter != profiles_info_.end(); ++iter) { + if (iter->second->created) { + Profile* candidate = iter->second->profile.get(); + if (candidate == profile || + (candidate->HasOffTheRecordProfile() && + candidate->GetOffTheRecordProfile() == profile)) { + return true; + } } } return false; } -Profile* ProfileManager::GetProfile( - const FilePath& profile_dir, bool init_extensions) { +std::vector ProfileManager::GetLoadedProfiles() const { + std::vector profiles; + for (ProfilesInfoMap::const_iterator iter = profiles_info_.begin(); + iter != profiles_info_.end(); ++iter) { + if (iter->second->created) + profiles.push_back(iter->second->profile.get()); + } + return profiles; +} + +Profile* ProfileManager::GetProfile(const FilePath& profile_dir) { // If the profile is already loaded (e.g., chrome.exe launched twice), just // return it. Profile* profile = GetProfileByPath(profile_dir); if (NULL != profile) return profile; - if (!ProfileManager::IsProfile(profile_dir)) { - // If the profile directory doesn't exist, create it. - profile = ProfileManager::CreateProfile(profile_dir); - } else { - // The profile already exists on disk, just load it. - profile = Profile::CreateProfile(profile_dir); - } + profile = Profile::CreateProfile(profile_dir); DCHECK(profile); if (profile) { - bool result = AddProfile(profile, init_extensions); + bool result = AddProfile(profile); DCHECK(result); } return profile; } -void ProfileManager::RegisterProfile(Profile* profile) { - profiles_.insert(profiles_.end(), profile); +void ProfileManager::CreateProfileAsync(const FilePath& user_data_dir, + Observer* observer) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + ProfilesInfoMap::iterator iter = profiles_info_.find(user_data_dir); + if (iter != profiles_info_.end()) { + ProfileInfo* info = iter->second.get(); + if (info->created) { + // Profile has already been created. Call observer immediately. + observer->OnProfileCreated(info->profile.get()); + } else { + // Profile is being created. Add observer to list. + info->observers.push_back(observer); + } + } else { + // Initiate asynchronous creation process. + ProfileInfo* info = + RegisterProfile(Profile::CreateProfileAsync(user_data_dir, this), + false); + info->observers.push_back(observer); + } +} + +// static +void ProfileManager::CreateDefaultProfileAsync(Observer* observer) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + ProfileManager* profile_manager = g_browser_process->profile_manager(); + + FilePath default_profile_dir; + PathService::Get(chrome::DIR_USER_DATA, &default_profile_dir); + default_profile_dir = default_profile_dir.Append( + profile_manager->GetCurrentProfileDir()); + + profile_manager->CreateProfileAsync(default_profile_dir, + observer); } -bool ProfileManager::AddProfile(Profile* profile, bool init_extensions) { +bool ProfileManager::AddProfile(Profile* profile) { DCHECK(profile); // Make sure that we're not loading a profile with the same ID as a profile @@ -234,22 +269,22 @@ bool ProfileManager::AddProfile(Profile* profile, bool init_extensions) { return false; } - profiles_.insert(profiles_.end(), profile); - if (init_extensions) - profile->InitExtensions(); - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); - if (!command_line.HasSwitch(switches::kDisableWebResources)) - profile->InitPromoResources(); + RegisterProfile(profile, true); + DoFinalInit(profile); return true; } -Profile* ProfileManager::GetProfileByPath(const FilePath& path) const { - for (const_iterator i(begin()); i != end(); ++i) { - if ((*i)->GetPath() == path) - return *i; - } +ProfileManager::ProfileInfo* ProfileManager::RegisterProfile(Profile* profile, + bool created) { + ProfileInfo* info = new ProfileInfo(profile, created); + ProfilesInfoMap::iterator new_elem = + (profiles_info_.insert(std::make_pair(profile->GetPath(), info))).first; + return info; +} - return NULL; +Profile* ProfileManager::GetProfileByPath(const FilePath& path) const { + ProfilesInfoMap::const_iterator iter = profiles_info_.find(path); + return (iter == profiles_info_.end()) ? NULL : iter->second->profile.get(); } void ProfileManager::OnSuspend() { @@ -261,13 +296,14 @@ void ProfileManager::OnSuspend() { DCHECK(posted); scoped_refptr request_context; - for (const_iterator i(begin()); i != end(); ++i) { - request_context = (*i)->GetRequestContext(); + std::vector profiles(GetLoadedProfiles()); + for (size_t i = 0; i < profiles.size(); ++i) { + request_context = profiles[i]->GetRequestContext(); posted = BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, NewRunnableFunction(&SuspendRequestContext, request_context)); DCHECK(posted); - request_context = (*i)->GetRequestContextForMedia(); + request_context = profiles[i]->GetRequestContextForMedia(); posted = BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, NewRunnableFunction(&SuspendRequestContext, request_context)); @@ -279,13 +315,14 @@ void ProfileManager::OnResume() { DCHECK(CalledOnValidThread()); scoped_refptr request_context; - for (const_iterator i(begin()); i != end(); ++i) { - request_context = (*i)->GetRequestContext(); + std::vector profiles(GetLoadedProfiles()); + for (size_t i = 0; i < profiles.size(); ++i) { + request_context = profiles[i]->GetRequestContext(); bool posted = BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, NewRunnableFunction(&ResumeRequestContext, request_context)); DCHECK(posted); - request_context = (*i)->GetRequestContextForMedia(); + request_context = profiles[i]->GetRequestContextForMedia(); posted = BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, NewRunnableFunction(&ResumeRequestContext, request_context)); @@ -315,30 +352,50 @@ void ProfileManager::Observe( #endif } -// static -bool ProfileManager::IsProfile(const FilePath& path) { - FilePath prefs_path = GetProfilePrefsPath(path); - FilePath history_path = path; - history_path = history_path.Append(chrome::kHistoryFilename); +void ProfileManager::DoFinalInit(Profile* profile) { + const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + bool init_extensions = true; +#if defined(OS_CHROMEOS) + if (!logged_in_ && + (!command_line.HasSwitch(switches::kTestType) || + command_line.HasSwitch(switches::kLoginProfile))) { + init_extensions = false; + } +#endif + if (init_extensions) + profile->InitExtensions(); - return file_util::PathExists(prefs_path) && - file_util::PathExists(history_path); + if (!command_line.HasSwitch(switches::kDisableWebResources)) + profile->InitPromoResources(); } -// static -Profile* ProfileManager::CreateProfile(const FilePath& path) { - if (IsProfile(path)) { - DCHECK(false) << "Attempted to create a profile with the path:\n" - << path.value() << "\n but that path already contains a profile"; - } +void ProfileManager::OnProfileCreated(Profile* profile, bool success) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - if (!file_util::PathExists(path)) { - // TODO(tc): http://b/1094718 Bad things happen if we can't write to the - // profile directory. We should eventually be able to run in this - // situation. - if (!file_util::CreateDirectory(path)) - return NULL; + ProfilesInfoMap::iterator iter = profiles_info_.find(profile->GetPath()); + DCHECK(iter != profiles_info_.end()); + ProfileInfo* info = iter->second.get(); + + std::vector observers; + info->observers.swap(observers); + + if (success) { + DoFinalInit(profile); + info->created = true; +#if defined(OS_CHROMEOS) + const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + if (!logged_in_ && + (!command_line.HasSwitch(switches::kTestType) || + command_line.HasSwitch(switches::kLoginProfile))) { + profile = profile->GetOffTheRecordProfile(); + } +#endif + } else { + profile = NULL; + profiles_info_.erase(iter); } - return Profile::CreateProfile(path); + for (size_t i = 0; i < observers.size(); ++i) { + observers[i]->OnProfileCreated(profile); + } } diff --git a/chrome/browser/profiles/profile_manager.h b/chrome/browser/profiles/profile_manager.h index c90834f..eeca447 100644 --- a/chrome/browser/profiles/profile_manager.h +++ b/chrome/browser/profiles/profile_manager.h @@ -11,6 +11,10 @@ #include #include "base/basictypes.h" +#include "base/file_path.h" +#include "base/hash_tables.h" +#include "base/memory/linked_ptr.h" +#include "base/memory/scoped_ptr.h" #include "base/message_loop.h" #include "base/threading/non_thread_safe.h" #include "chrome/browser/profiles/profile.h" @@ -22,8 +26,16 @@ class FilePath; class ProfileManager : public base::NonThreadSafe, public ui::SystemMonitor::PowerObserver, - public NotificationObserver { + public NotificationObserver, + public Profile::Delegate { public: + class Observer { + public: + // This method is called when profile is ready. If profile creation has been + // failed, method is called with |profile| equals to NULL. + virtual void OnProfileCreated(Profile* profile) = 0; + }; + ProfileManager(); virtual ~ProfileManager(); @@ -44,6 +56,16 @@ class ProfileManager : public base::NonThreadSafe, // otherwise it will create and manage it. Profile* GetProfile(const FilePath& profile_dir); + // Explicit asynchronous creation of the profile. |observer| is called + // when profile is created. If profile has already been created, observer + // is called immediately. Should be called on the UI thread. + void CreateProfileAsync(const FilePath& user_data_dir, + Observer* observer); + + // Initiates default profile creation. If default profile has already been + // created, observer is called immediately. Should be called on the UI thread. + static void CreateDefaultProfileAsync(Observer* observer); + // Returns the profile with the given |profile_id| or NULL if no such profile // exists. Profile* GetProfileWithId(ProfileId profile_id); @@ -52,28 +74,13 @@ class ProfileManager : public base::NonThreadSafe, // profile. bool IsValidProfile(Profile* profile); - // Returns a profile for a specific profile directory within the user data - // dir with the option of controlling whether extensions are initialized - // or not. This will return an existing profile it had already been created, - // otherwise it will create and manage it. - // Note that if the profile has already been created, extensions may have - // been initialized. If this matters to you, you should call GetProfileByPath - // first to see if the profile already exists. - Profile* GetProfile(const FilePath& profile_dir, bool init_extensions); - // Returns the directory where the currently active profile is // stored, relative to the user data directory currently in use.. FilePath GetCurrentProfileDir(); - // These allow iteration through the current list of profiles. - typedef std::vector ProfileVector; - typedef ProfileVector::iterator iterator; - typedef ProfileVector::const_iterator const_iterator; - - iterator begin() { return profiles_.begin(); } - const_iterator begin() const { return profiles_.begin(); } - iterator end() { return profiles_.end(); } - const_iterator end() const { return profiles_.end(); } + // Returns created profiles. Note, profiles order is NOT guaranteed to be + // related with the creation order. + std::vector GetLoadedProfiles() const; // PowerObserver notifications virtual void OnSuspend(); @@ -93,35 +100,48 @@ class ProfileManager : public base::NonThreadSafe, // Returns the path to the preferences file given the user profile directory. static FilePath GetProfilePrefsPath(const FilePath& profile_dir); - // Tries to determine whether the given path represents a profile - // directory, and returns true if it thinks it does. - static bool IsProfile(const FilePath& path); - // If a profile with the given path is currently managed by this object, // return a pointer to the corresponding Profile object; // otherwise return NULL. Profile* GetProfileByPath(const FilePath& path) const; - // Creates a new profile at the specified path. - // This method should always return a valid Profile (i.e., should never - // return NULL). - static Profile* CreateProfile(const FilePath& path); + // Profile::Delegate implementation: + virtual void OnProfileCreated(Profile* profile, bool success); + + protected: + // Does final initial actions. + virtual void DoFinalInit(Profile* profile); private: friend class ExtensionEventRouterForwarderTest; - // Helper method for unit tests to inject |profile| into the ProfileManager. - void RegisterProfile(Profile* profile); + // This struct contains information about profiles which are being loaded or + // were loaded. + struct ProfileInfo { + ProfileInfo(Profile* profile, bool created) + : profile(profile), created(created) { + } + + scoped_ptr profile; + // Whether profile has been fully loaded (created and initialized). + bool created; + // List of observers which should be notified when profile initialization is + // done. Note, when profile is fully loaded this vector will be empty. + std::vector observers; + + private: + DISALLOW_COPY_AND_ASSIGN(ProfileInfo); + }; // Adds a pre-existing Profile object to the set managed by this // ProfileManager. This ProfileManager takes ownership of the Profile. // The Profile should not already be managed by this ProfileManager. // Returns true if the profile was added, false otherwise. - bool AddProfile(Profile* profile, bool init_extensions); + bool AddProfile(Profile* profile); - // We keep a simple vector of profiles rather than something fancier - // because we expect there to be a small number of profiles active. - ProfileVector profiles_; + // Registers profile with given info. Returns pointer to created ProfileInfo + // entry. + ProfileInfo* RegisterProfile(Profile* profile, bool created); NotificationRegistrar registrar_; @@ -130,7 +150,19 @@ class ProfileManager : public base::NonThreadSafe, // default. bool logged_in_; + // Maps profile path to ProfileInfo (if profile has been created). Use + // RegisterProfile() to add into this map. + typedef std::map > ProfilesInfoMap; + ProfilesInfoMap profiles_info_; + DISALLOW_COPY_AND_ASSIGN(ProfileManager); }; +// Same as the ProfileManager, but doesn't initialize some services of the +// profile. This one is useful in unittests. +class ProfileManagerWithoutInit : public ProfileManager { + protected: + virtual void DoFinalInit(Profile*) {} +}; + #endif // CHROME_BROWSER_PROFILES_PROFILE_MANAGER_H_ diff --git a/chrome/browser/profiles/profile_manager_unittest.cc b/chrome/browser/profiles/profile_manager_unittest.cc index eeea273..7080055 100644 --- a/chrome/browser/profiles/profile_manager_unittest.cc +++ b/chrome/browser/profiles/profile_manager_unittest.cc @@ -18,14 +18,23 @@ #include "chrome/test/testing_pref_service.h" #include "content/browser/browser_thread.h" #include "content/common/notification_service.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/system_monitor/system_monitor.h" +namespace { +// This global variable is used to check that value returned to different +// observers is the same. +Profile* g_created_profile; + +} // namespace + class ProfileManagerTest : public testing::Test { protected: ProfileManagerTest() : ui_thread_(BrowserThread::UI, &message_loop_), - file_thread_(BrowserThread::FILE, &message_loop_) { + file_thread_(BrowserThread::FILE, &message_loop_), + profile_manager_(new ProfileManagerWithoutInit) { } virtual void SetUp() { @@ -40,11 +49,18 @@ class ProfileManagerTest : public testing::Test { } virtual void TearDown() { + profile_manager_.reset(); + TestingBrowserProcess* testing_browser_process = static_cast(g_browser_process); testing_browser_process->SetPrefService(NULL); } + class MockObserver : public ProfileManager::Observer { + public: + MOCK_METHOD1(OnProfileCreated, void(Profile* profile)); + }; + // The path to temporary directory used to contain the test operations. ScopedTempDir temp_dir_; @@ -52,40 +68,30 @@ class ProfileManagerTest : public testing::Test { BrowserThread ui_thread_; BrowserThread file_thread_; + ui::SystemMonitor system_monitor_dummy_; + + // Also will test profile deletion. + scoped_ptr profile_manager_; + TestingPrefService test_local_state_; }; -TEST_F(ProfileManagerTest, CreateProfile) { - FilePath source_path; - PathService::Get(chrome::DIR_TEST_DATA, &source_path); - source_path = source_path.Append(FILE_PATH_LITERAL("profiles")); - source_path = source_path.Append(FILE_PATH_LITERAL("sample")); - +TEST_F(ProfileManagerTest, GetProfile) { FilePath dest_path = temp_dir_.path(); dest_path = dest_path.Append(FILE_PATH_LITERAL("New Profile")); - scoped_ptr profile; + Profile* profile; // Successfully create a profile. - profile.reset(ProfileManager::CreateProfile(dest_path)); - ASSERT_TRUE(profile.get()); - - profile.reset(); + profile = profile_manager_->GetProfile(dest_path); + EXPECT_TRUE(profile); -#ifdef NDEBUG - // In Release mode, we always try to always return a profile. In debug, - // these cases would trigger DCHECKs. - - // The profile already exists when we call CreateProfile. Just load it. - profile.reset(ProfileManager::CreateProfile(dest_path)); - ASSERT_TRUE(profile.get()); -#endif + // The profile already exists when we call GetProfile. Just load it. + EXPECT_EQ(profile, profile_manager_->GetProfile(dest_path)); } TEST_F(ProfileManagerTest, DefaultProfileDir) { CommandLine *cl = CommandLine::ForCurrentProcess(); - ui::SystemMonitor dummy; - ProfileManager profile_manager; std::string profile_dir("my_user"); cl->AppendSwitch(switches::kTestType); @@ -93,15 +99,13 @@ TEST_F(ProfileManagerTest, DefaultProfileDir) { FilePath expected_default = FilePath().AppendASCII(chrome::kNotSignedInProfile); EXPECT_EQ(expected_default.value(), - profile_manager.GetCurrentProfileDir().value()); + profile_manager_->GetCurrentProfileDir().value()); } #if defined(OS_CHROMEOS) // This functionality only exists on Chrome OS. TEST_F(ProfileManagerTest, LoggedInProfileDir) { CommandLine *cl = CommandLine::ForCurrentProcess(); - ui::SystemMonitor dummy; - ProfileManager profile_manager; std::string profile_dir("my_user"); cl->AppendSwitchASCII(switches::kLoginProfile, profile_dir); @@ -110,41 +114,36 @@ TEST_F(ProfileManagerTest, LoggedInProfileDir) { FilePath expected_default = FilePath().AppendASCII(chrome::kNotSignedInProfile); EXPECT_EQ(expected_default.value(), - profile_manager.GetCurrentProfileDir().value()); + profile_manager_->GetCurrentProfileDir().value()); - profile_manager.Observe(NotificationType::LOGIN_USER_CHANGED, - NotificationService::AllSources(), - NotificationService::NoDetails()); + profile_manager_->Observe(NotificationType::LOGIN_USER_CHANGED, + NotificationService::AllSources(), + NotificationService::NoDetails()); FilePath expected_logged_in(profile_dir); EXPECT_EQ(expected_logged_in.value(), - profile_manager.GetCurrentProfileDir().value()); + profile_manager_->GetCurrentProfileDir().value()); VLOG(1) << temp_dir_.path().Append( - profile_manager.GetCurrentProfileDir()).value(); + profile_manager_->GetCurrentProfileDir()).value(); } #endif TEST_F(ProfileManagerTest, CreateAndUseTwoProfiles) { - FilePath source_path; - PathService::Get(chrome::DIR_TEST_DATA, &source_path); - source_path = source_path.Append(FILE_PATH_LITERAL("profiles")); - source_path = source_path.Append(FILE_PATH_LITERAL("sample")); - FilePath dest_path1 = temp_dir_.path(); dest_path1 = dest_path1.Append(FILE_PATH_LITERAL("New Profile 1")); FilePath dest_path2 = temp_dir_.path(); dest_path2 = dest_path2.Append(FILE_PATH_LITERAL("New Profile 2")); - scoped_ptr profile1; - scoped_ptr profile2; + Profile* profile1; + Profile* profile2; // Successfully create the profiles. - profile1.reset(ProfileManager::CreateProfile(dest_path1)); - ASSERT_TRUE(profile1.get()); + profile1 = profile_manager_->GetProfile(dest_path1); + ASSERT_TRUE(profile1); - profile2.reset(ProfileManager::CreateProfile(dest_path2)); - ASSERT_TRUE(profile2.get()); + profile2 = profile_manager_->GetProfile(dest_path2); + ASSERT_TRUE(profile2); // Force lazy-init of some profile services to simulate use. EXPECT_TRUE(profile1->GetHistoryService(Profile::EXPLICIT_ACCESS)); @@ -155,9 +154,62 @@ TEST_F(ProfileManagerTest, CreateAndUseTwoProfiles) { // Make sure any pending tasks run before we destroy the profiles. message_loop_.RunAllPending(); - profile1.reset(); - profile2.reset(); + profile_manager_.reset(); // Make sure history cleans up correctly. message_loop_.RunAllPending(); } + +// Tests asynchronous profile creation mechanism. +TEST_F(ProfileManagerTest, CreateProfileAsync) { + FilePath dest_path = + temp_dir_.path().Append(FILE_PATH_LITERAL("New Profile")); + + MockObserver mock_observer; + EXPECT_CALL(mock_observer, OnProfileCreated(testing::NotNull())).Times(1); + + profile_manager_->CreateProfileAsync(dest_path, &mock_observer); + + message_loop_.RunAllPending(); +} + +MATCHER(SameNotNull, "The same non-NULL value for all cals.") { + if (!g_created_profile) + g_created_profile = arg; + return g_created_profile == arg; +} + +TEST_F(ProfileManagerTest, CreateProfileAsyncMultipleRequests) { + FilePath dest_path = + temp_dir_.path().Append(FILE_PATH_LITERAL("New Profile")); + + g_created_profile = NULL; + + MockObserver mock_observer1; + EXPECT_CALL(mock_observer1, OnProfileCreated(SameNotNull())).Times(1); + MockObserver mock_observer2; + EXPECT_CALL(mock_observer2, OnProfileCreated(SameNotNull())).Times(1); + MockObserver mock_observer3; + EXPECT_CALL(mock_observer3, OnProfileCreated(SameNotNull())).Times(1); + + profile_manager_->CreateProfileAsync(dest_path, &mock_observer1); + profile_manager_->CreateProfileAsync(dest_path, &mock_observer2); + profile_manager_->CreateProfileAsync(dest_path, &mock_observer3); + + message_loop_.RunAllPending(); +} + +TEST_F(ProfileManagerTest, CreateProfilesAsync) { + FilePath dest_path1 = + temp_dir_.path().Append(FILE_PATH_LITERAL("New Profile 1")); + FilePath dest_path2 = + temp_dir_.path().Append(FILE_PATH_LITERAL("New Profile 2")); + + MockObserver mock_observer; + EXPECT_CALL(mock_observer, OnProfileCreated(testing::NotNull())).Times(2); + + profile_manager_->CreateProfileAsync(dest_path1, &mock_observer); + profile_manager_->CreateProfileAsync(dest_path2, &mock_observer); + + message_loop_.RunAllPending(); +} diff --git a/chrome/browser/task_manager/task_manager.cc b/chrome/browser/task_manager/task_manager.cc index 7f8a5d3..746c07a 100644 --- a/chrome/browser/task_manager/task_manager.cc +++ b/chrome/browser/task_manager/task_manager.cc @@ -979,12 +979,13 @@ void TaskManager::OpenAboutMemory() { if (!browser) { // On OS X, the task manager can be open without any open browser windows. - if (!g_browser_process || - !g_browser_process->profile_manager() || - g_browser_process->profile_manager()->begin() == - g_browser_process->profile_manager()->end()) + if (!g_browser_process || !g_browser_process->profile_manager()) return; - browser = Browser::Create(*g_browser_process->profile_manager()->begin()); + Profile* profile = + g_browser_process->profile_manager()->GetDefaultProfile(); + if (!profile) + return; + browser = Browser::Create(profile); browser->OpenURL(GURL(chrome::kAboutMemoryURL), GURL(), NEW_FOREGROUND_TAB, PageTransition::LINK); browser->window()->Show(); diff --git a/chrome/browser/task_manager/task_manager_resource_providers.cc b/chrome/browser/task_manager/task_manager_resource_providers.cc index c4dfbf8..05d17b8 100644 --- a/chrome/browser/task_manager/task_manager_resource_providers.cc +++ b/chrome/browser/task_manager/task_manager_resource_providers.cc @@ -681,11 +681,11 @@ void TaskManagerBackgroundContentsResourceProvider::StartUpdating() { // Add all the existing BackgroundContents from every profile. ProfileManager* profile_manager = g_browser_process->profile_manager(); - for (ProfileManager::const_iterator it = profile_manager->begin(); - it != profile_manager->end(); ++it) { + std::vector profiles(profile_manager->GetLoadedProfiles()); + for (size_t i = 0; i < profiles.size(); ++i) { BackgroundContentsService* background_contents_service = - (*it)->GetBackgroundContentsService(); - ExtensionService* extensions_service = (*it)->GetExtensionService(); + profiles[i]->GetBackgroundContentsService(); + ExtensionService* extensions_service = profiles[i]->GetExtensionService(); std::vector contents = background_contents_service->GetBackgroundContents(); for (std::vector::iterator iterator = contents.begin(); @@ -1207,10 +1207,10 @@ void TaskManagerExtensionProcessResourceProvider::StartUpdating() { // Add all the existing ExtensionHosts. ProfileManager* profile_manager = g_browser_process->profile_manager(); - for (ProfileManager::const_iterator it = profile_manager->begin(); - it != profile_manager->end(); ++it) { + std::vector profiles(profile_manager->GetLoadedProfiles()); + for (size_t i = 0; i < profiles.size(); ++i) { ExtensionProcessManager* process_manager = - (*it)->GetExtensionProcessManager(); + profiles[i]->GetExtensionProcessManager(); if (process_manager) { ExtensionProcessManager::const_iterator jt; for (jt = process_manager->begin(); jt != process_manager->end(); ++jt) @@ -1221,7 +1221,7 @@ void TaskManagerExtensionProcessResourceProvider::StartUpdating() { // extensions. if (BrowserList::IsOffTheRecordSessionActive()) { ExtensionProcessManager* process_manager = - (*it)->GetOffTheRecordProfile()->GetExtensionProcessManager(); + profiles[i]->GetOffTheRecordProfile()->GetExtensionProcessManager(); if (process_manager) { ExtensionProcessManager::const_iterator jt; for (jt = process_manager->begin(); jt != process_manager->end(); ++jt) -- cgit v1.1