// Copyright (c) 2012 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. #include "chrome/browser/profiles/profile_manager.h" #include #include "base/bind.h" #include "base/command_line.h" #include "base/file_util.h" #include "base/files/file_path.h" #include "base/metrics/histogram.h" #include "base/prefs/pref_registry_simple.h" #include "base/prefs/pref_service.h" #include "base/string_util.h" #include "base/strings/string_number_conversions.h" #include "base/utf_string_conversions.h" #include "chrome/browser/bookmarks/bookmark_model.h" #include "chrome/browser/bookmarks/bookmark_model_factory.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/content_settings/host_content_settings_map.h" #include "chrome/browser/prefs/scoped_user_pref_update.h" #include "chrome/browser/profiles/bookmark_model_loaded_observer.h" #include "chrome/browser/profiles/profile_destroyer.h" #include "chrome/browser/profiles/profile_info_cache.h" #include "chrome/browser/profiles/profile_metrics.h" #include "chrome/browser/sync/profile_sync_service.h" #include "chrome/browser/sync/profile_sync_service_factory.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/webui/sync_promo/sync_promo_ui.h" #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_notification_types.h" #include "chrome/common/chrome_paths_internal.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/logging_chrome.h" #include "chrome/common/pref_names.h" #include "chrome/common/url_constants.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/user_metrics.h" #include "grit/generated_resources.h" #include "net/http/http_transaction_factory.h" #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_getter.h" #include "net/url_request/url_request_job.h" #include "ui/base/l10n/l10n_util.h" #if defined(ENABLE_MANAGED_USERS) #include "chrome/browser/managed_mode/managed_user_service.h" #include "chrome/browser/managed_mode/managed_user_service_factory.h" #endif #if !defined(OS_IOS) #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" #include "chrome/browser/sessions/session_service_factory.h" #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_list.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/startup/startup_browser_creator.h" #endif // !defined (OS_IOS) #if defined(OS_WIN) #include "base/win/metro.h" #include "chrome/installer/util/browser_distribution.h" #endif #if defined(OS_CHROMEOS) #include "base/chromeos/chromeos_version.h" #include "chrome/browser/chromeos/login/user.h" #include "chrome/browser/chromeos/login/user_manager.h" #include "chromeos/chromeos_switches.h" #include "chromeos/dbus/cryptohome_client.h" #include "chromeos/dbus/dbus_thread_manager.h" #endif using content::BrowserThread; using content::UserMetricsAction; namespace { // Profiles that should be deleted on shutdown. std::vector& ProfilesToDelete() { CR_DEFINE_STATIC_LOCAL(std::vector, profiles_to_delete, ()); return profiles_to_delete; } int64 ComputeFilesSize(const base::FilePath& directory, const base::FilePath::StringType& pattern) { int64 running_size = 0; file_util::FileEnumerator iter(directory, false, file_util::FileEnumerator::FILES, pattern); while (!iter.Next().empty()) { file_util::FileEnumerator::FindInfo info; iter.GetFindInfo(&info); running_size += file_util::FileEnumerator::GetFilesize(info); } return running_size; } // Simple task to log the size of the current profile. void ProfileSizeTask(const base::FilePath& path, int extension_count) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); int64 size = ComputeFilesSize(path, FILE_PATH_LITERAL("*")); int size_MB = static_cast(size / (1024 * 1024)); UMA_HISTOGRAM_COUNTS_10000("Profile.TotalSize", size_MB); size = ComputeFilesSize(path, FILE_PATH_LITERAL("History")); size_MB = static_cast(size / (1024 * 1024)); UMA_HISTOGRAM_COUNTS_10000("Profile.HistorySize", size_MB); size = ComputeFilesSize(path, FILE_PATH_LITERAL("History*")); size_MB = static_cast(size / (1024 * 1024)); UMA_HISTOGRAM_COUNTS_10000("Profile.TotalHistorySize", size_MB); size = ComputeFilesSize(path, FILE_PATH_LITERAL("Cookies")); size_MB = static_cast(size / (1024 * 1024)); UMA_HISTOGRAM_COUNTS_10000("Profile.CookiesSize", size_MB); size = ComputeFilesSize(path, FILE_PATH_LITERAL("Bookmarks")); size_MB = static_cast(size / (1024 * 1024)); UMA_HISTOGRAM_COUNTS_10000("Profile.BookmarksSize", size_MB); size = ComputeFilesSize(path, FILE_PATH_LITERAL("Favicons")); size_MB = static_cast(size / (1024 * 1024)); UMA_HISTOGRAM_COUNTS_10000("Profile.FaviconsSize", size_MB); size = ComputeFilesSize(path, FILE_PATH_LITERAL("Top Sites")); size_MB = static_cast(size / (1024 * 1024)); UMA_HISTOGRAM_COUNTS_10000("Profile.TopSitesSize", size_MB); size = ComputeFilesSize(path, FILE_PATH_LITERAL("Visited Links")); size_MB = static_cast(size / (1024 * 1024)); UMA_HISTOGRAM_COUNTS_10000("Profile.VisitedLinksSize", size_MB); size = ComputeFilesSize(path, FILE_PATH_LITERAL("Web Data")); size_MB = static_cast(size / (1024 * 1024)); UMA_HISTOGRAM_COUNTS_10000("Profile.WebDataSize", size_MB); size = ComputeFilesSize(path, FILE_PATH_LITERAL("Extension*")); size_MB = static_cast(size / (1024 * 1024)); UMA_HISTOGRAM_COUNTS_10000("Profile.ExtensionSize", size_MB); size = ComputeFilesSize(path, FILE_PATH_LITERAL("Policy")); size_MB = static_cast(size / (1024 * 1024)); UMA_HISTOGRAM_COUNTS_10000("Profile.PolicySize", size_MB); // Count number of extensions in this profile, if we know. if (extension_count != -1) UMA_HISTOGRAM_COUNTS_10000("Profile.AppCount", extension_count); } void QueueProfileDirectoryForDeletion(const base::FilePath& path) { ProfilesToDelete().push_back(path); } // Called upon completion of profile creation. This function takes care of // launching a new browser window and signing the user in to their Google // account. void OnOpenWindowForNewProfile( chrome::HostDesktopType desktop_type, const ProfileManager::CreateCallback& callback, Profile* profile, Profile::CreateStatus status) { // Invoke the callback before we open a window for this new profile, so the // callback has a chance to update the profile state first (to do things like // sign in the profile). if (!callback.is_null()) callback.Run(profile, status); if (status == Profile::CREATE_STATUS_INITIALIZED) { ProfileManager::FindOrCreateNewWindowForProfile( profile, chrome::startup::IS_PROCESS_STARTUP, chrome::startup::IS_FIRST_RUN, desktop_type, false); } } #if defined(OS_CHROMEOS) void CheckCryptohomeIsMounted(chromeos::DBusMethodCallStatus call_status, bool is_mounted) { if (call_status != chromeos::DBUS_METHOD_CALL_SUCCESS) { LOG(ERROR) << "IsMounted call failed."; return; } if (!is_mounted) LOG(ERROR) << "Cryptohome is not mounted."; } // TODO(nkostylev): Remove this hack when http://crbug.com/224291 is fixed. // Now user homedirs are mounted to /home/user which is different from // user data dir (/home/chronos). base::FilePath GetChromeOSProfileDir(const base::FilePath& path) { base::FilePath profile_dir(FILE_PATH_LITERAL("/home/user/")); profile_dir = profile_dir.Append(path); return profile_dir; } #endif } // namespace #if defined(ENABLE_SESSION_SERVICE) // static void ProfileManager::ShutdownSessionServices() { ProfileManager* pm = g_browser_process->profile_manager(); if (!pm) // Is NULL when running unit tests. return; std::vector profiles(pm->GetLoadedProfiles()); for (size_t i = 0; i < profiles.size(); ++i) SessionServiceFactory::ShutdownForProfile(profiles[i]); } #endif // static void ProfileManager::NukeDeletedProfilesFromDisk() { for (std::vector::iterator it = ProfilesToDelete().begin(); it != ProfilesToDelete().end(); ++it) { // Delete both the profile directory and its corresponding cache. base::FilePath cache_path; chrome::GetUserCacheDirectory(*it, &cache_path); file_util::Delete(*it, true); file_util::Delete(cache_path, true); } ProfilesToDelete().clear(); } // static // TODO(nkostylev): Remove this method once all clients are migrated. Profile* ProfileManager::GetDefaultProfile() { ProfileManager* profile_manager = g_browser_process->profile_manager(); return profile_manager->GetDefaultProfile(profile_manager->user_data_dir_); } // static // TODO(nkostylev): Remove this method once all clients are migrated. Profile* ProfileManager::GetDefaultProfileOrOffTheRecord() { // TODO (mukai,nkostylev): In the long term we should fix those cases that // crash on Guest mode and have only one GetDefaultProfile() method. Profile* profile = GetDefaultProfile(); #if defined(OS_CHROMEOS) if (chromeos::UserManager::Get()->IsLoggedInAsGuest()) profile = profile->GetOffTheRecordProfile(); #endif return profile; } // static Profile* ProfileManager::GetLastUsedProfile() { ProfileManager* profile_manager = g_browser_process->profile_manager(); return profile_manager->GetLastUsedProfile(profile_manager->user_data_dir_); } // static std::vector ProfileManager::GetLastOpenedProfiles() { ProfileManager* profile_manager = g_browser_process->profile_manager(); return profile_manager->GetLastOpenedProfiles( profile_manager->user_data_dir_); } ProfileManager::ProfileManager(const base::FilePath& user_data_dir) : user_data_dir_(user_data_dir), logged_in_(false), will_import_(false), profile_shortcut_manager_(NULL), #if !defined(OS_ANDROID) && !defined(OS_IOS) ALLOW_THIS_IN_INITIALIZER_LIST( browser_list_observer_(this)), #endif closing_all_browsers_(false) { #if defined(OS_CHROMEOS) registrar_.Add( this, chrome::NOTIFICATION_LOGIN_USER_CHANGED, content::NotificationService::AllSources()); if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kMultiProfiles)) { registrar_.Add( this, chrome::NOTIFICATION_ACTIVE_USER_CHANGED, content::NotificationService::AllSources()); } #endif registrar_.Add( this, chrome::NOTIFICATION_BROWSER_OPENED, content::NotificationService::AllSources()); registrar_.Add( this, chrome::NOTIFICATION_BROWSER_CLOSED, content::NotificationService::AllSources()); registrar_.Add( this, chrome::NOTIFICATION_CLOSE_ALL_BROWSERS_REQUEST, content::NotificationService::AllSources()); registrar_.Add( this, chrome::NOTIFICATION_BROWSER_CLOSE_CANCELLED, content::NotificationService::AllSources()); if (ProfileShortcutManager::IsFeatureEnabled() && !user_data_dir_.empty()) profile_shortcut_manager_.reset(ProfileShortcutManager::Create( this)); } ProfileManager::~ProfileManager() { } base::FilePath ProfileManager::GetDefaultProfileDir( const base::FilePath& user_data_dir) { base::FilePath default_profile_dir(user_data_dir); default_profile_dir = default_profile_dir.AppendASCII(chrome::kInitialProfile); return default_profile_dir; } base::FilePath ProfileManager::GetProfilePrefsPath( const base::FilePath &profile_dir) { base::FilePath default_prefs_path(profile_dir); default_prefs_path = default_prefs_path.Append(chrome::kPreferencesFilename); return default_prefs_path; } base::FilePath ProfileManager::GetInitialProfileDir() { base::FilePath relative_profile_dir; #if defined(OS_CHROMEOS) const CommandLine& command_line = *CommandLine::ForCurrentProcess(); if (logged_in_) { base::FilePath profile_dir; // If the user has logged in, pick up the new profile. if (command_line.HasSwitch(chromeos::switches::kLoginProfile)) { profile_dir = command_line.GetSwitchValuePath( chromeos::switches::kLoginProfile); } else { // We should never be logged in with no profile dir. NOTREACHED(); return base::FilePath(""); } // In case of multi-profiles ignore --login-profile switch. // TODO(nkostylev): Some cases like Guest mode will have empty username_hash // so default kLoginProfile dir will be used. if (command_line.HasSwitch(switches::kMultiProfiles) && !active_profile_username_hash_.empty()) { profile_dir = base::FilePath(active_profile_username_hash_); } relative_profile_dir = relative_profile_dir.Append(profile_dir); return relative_profile_dir; } #endif // TODO(mirandac): should not automatically be default profile. relative_profile_dir = relative_profile_dir.AppendASCII(chrome::kInitialProfile); return relative_profile_dir; } Profile* ProfileManager::GetLastUsedProfile( const base::FilePath& user_data_dir) { #if defined(OS_CHROMEOS) // Use default login profile if user has not logged in yet. if (!logged_in_) return GetDefaultProfile(user_data_dir); #endif return GetProfile(GetLastUsedProfileDir(user_data_dir)); } base::FilePath ProfileManager::GetLastUsedProfileDir( const base::FilePath& user_data_dir) { base::FilePath last_used_profile_dir(user_data_dir); std::string last_used_profile; PrefService* local_state = g_browser_process->local_state(); DCHECK(local_state); if (local_state->HasPrefPath(prefs::kProfileLastUsed)) { return last_used_profile_dir.AppendASCII( local_state->GetString(prefs::kProfileLastUsed)); } return last_used_profile_dir.AppendASCII(chrome::kInitialProfile); } std::vector ProfileManager::GetLastOpenedProfiles( const base::FilePath& user_data_dir) { PrefService* local_state = g_browser_process->local_state(); DCHECK(local_state); std::vector to_return; if (local_state->HasPrefPath(prefs::kProfilesLastActive)) { const ListValue* profile_list = local_state->GetList(prefs::kProfilesLastActive); if (profile_list) { ListValue::const_iterator it; std::string profile; for (it = profile_list->begin(); it != profile_list->end(); ++it) { if (!(*it)->GetAsString(&profile) || profile.empty()) { LOG(WARNING) << "Invalid entry in " << prefs::kProfilesLastActive; continue; } to_return.push_back(GetProfile(user_data_dir.AppendASCII(profile))); } } } return to_return; } Profile* ProfileManager::GetDefaultProfile( const base::FilePath& user_data_dir) { #if defined(OS_CHROMEOS) base::FilePath default_profile_dir(user_data_dir); if (logged_in_) { if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kMultiProfiles) && base::chromeos::IsRunningOnChromeOS()) { // TODO(nkostylev): Change to [user_data_dir]/profile-[hash] default_profile_dir = GetChromeOSProfileDir(GetInitialProfileDir()); } else { default_profile_dir = default_profile_dir.Append(GetInitialProfileDir()); } } else { default_profile_dir = GetDefaultProfileDir(user_data_dir); } #else base::FilePath default_profile_dir(user_data_dir); default_profile_dir = default_profile_dir.Append(GetInitialProfileDir()); #endif #if defined(OS_CHROMEOS) if (!logged_in_) { Profile* profile = GetProfile(default_profile_dir); // For cros, return the OTR profile so we never accidentally keep // user data in an unencrypted profile. But doing this makes // many of the browser and ui tests fail. We do return the OTR profile // if the login-profile switch is passed so that we can test this. // TODO(davemoore) Fix the tests so they allow OTR profiles. if (ShouldGoOffTheRecord()) return profile->GetOffTheRecordProfile(); return profile; } ProfileInfo* profile_info = GetProfileInfoByPath(default_profile_dir); // Fallback to default off-the-record profile, if user profile has not fully // loaded yet. if (profile_info && !profile_info->created) default_profile_dir = GetDefaultProfileDir(user_data_dir); #endif return GetProfile(default_profile_dir); } bool ProfileManager::IsValidProfile(Profile* profile) { 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; } 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 base::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; profile = CreateProfileHelper(profile_dir); DCHECK(profile); if (profile) { bool result = AddProfile(profile); DCHECK(result); } return profile; } void ProfileManager::CreateProfileAsync( const base::FilePath& profile_path, const CreateCallback& callback, const string16& name, const string16& icon_url, bool is_managed) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); // Make sure that this profile is not pending deletion. if (std::find(ProfilesToDelete().begin(), ProfilesToDelete().end(), profile_path) != ProfilesToDelete().end()) { callback.Run(NULL, Profile::CREATE_STATUS_FAIL); return; } ProfilesInfoMap::iterator iter = profiles_info_.find(profile_path); if (iter != profiles_info_.end()) { ProfileInfo* info = iter->second.get(); if (info->created) { // Profile has already been created. Run callback immediately. callback.Run(info->profile.get(), Profile::CREATE_STATUS_INITIALIZED); } else { // Profile is being created. Add callback to list. info->callbacks.push_back(callback); } } else { // Initiate asynchronous creation process. ProfileInfo* info = RegisterProfile(CreateProfileAsyncHelper(profile_path, this), false); ProfileInfoCache& cache = GetProfileInfoCache(); // Get the icon index from the user's icon url size_t icon_index; std::string icon_url_std = UTF16ToASCII(icon_url); if (cache.IsDefaultAvatarIconUrl(icon_url_std, &icon_index)) { // add profile to cache with user selected name and avatar cache.AddProfileToCache(profile_path, name, string16(), icon_index, is_managed); } info->callbacks.push_back(callback); if (is_managed) { content::RecordAction( UserMetricsAction("ManagedMode_LocallyManagedUserCreated")); } } } // static void ProfileManager::CreateDefaultProfileAsync(const CreateCallback& callback) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); ProfileManager* profile_manager = g_browser_process->profile_manager(); base::FilePath default_profile_dir = profile_manager->user_data_dir_; // TODO(mirandac): current directory will not always be default in the future default_profile_dir = default_profile_dir.Append( profile_manager->GetInitialProfileDir()); #if defined(OS_CHROMEOS) // TODO(nkostylev): Change to [user_data_dir]/profile-[hash] if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kMultiProfiles) && base::chromeos::IsRunningOnChromeOS()) { default_profile_dir = GetChromeOSProfileDir( profile_manager->GetInitialProfileDir()); } #endif // Chrome OS specific note: since we pass string16() here as the icon_url, // profile cache information will not get updated with the is_managed value // so we're fine with passing all default values here. // On Chrome OS |is_managed| preference will get initialized in // Profile::CREATE_STATUS_CREATED callback. profile_manager->CreateProfileAsync( default_profile_dir, callback, string16(), string16(), false); } bool ProfileManager::AddProfile(Profile* profile) { DCHECK(profile); // Make sure that we're not loading a profile with the same ID as a profile // that's already loaded. if (GetProfileByPath(profile->GetPath())) { NOTREACHED() << "Attempted to add profile with the same path (" << profile->GetPath().value() << ") as an already-loaded profile."; return false; } RegisterProfile(profile, true); DoFinalInit(profile, ShouldGoOffTheRecord()); return true; } ProfileManager::ProfileInfo* ProfileManager::RegisterProfile( Profile* profile, bool created) { ProfileInfo* info = new ProfileInfo(profile, created); profiles_info_.insert( std::make_pair(profile->GetPath(), linked_ptr(info))); return info; } ProfileManager::ProfileInfo* ProfileManager::GetProfileInfoByPath( const base::FilePath& path) const { ProfilesInfoMap::const_iterator iter = profiles_info_.find(path); return (iter == profiles_info_.end()) ? NULL : iter->second.get(); } Profile* ProfileManager::GetProfileByPath(const base::FilePath& path) const { ProfileInfo* profile_info = GetProfileInfoByPath(path); return profile_info ? profile_info->profile.get() : NULL; } // static void ProfileManager::FindOrCreateNewWindowForProfile( Profile* profile, chrome::startup::IsProcessStartup process_startup, chrome::startup::IsFirstRun is_first_run, chrome::HostDesktopType desktop_type, bool always_create) { #if defined(OS_IOS) NOTREACHED(); #else DCHECK(profile); if (!always_create) { Browser* browser = chrome::FindTabbedBrowser(profile, false, desktop_type); if (browser) { browser->window()->Activate(); return; } } content::RecordAction(UserMetricsAction("NewWindow")); CommandLine command_line(CommandLine::NO_PROGRAM); int return_code; StartupBrowserCreator browser_creator; browser_creator.LaunchBrowser(command_line, profile, base::FilePath(), process_startup, is_first_run, &return_code); #endif // defined(OS_IOS) } void ProfileManager::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { #if defined(OS_CHROMEOS) if (type == chrome::NOTIFICATION_LOGIN_USER_CHANGED) { logged_in_ = true; chromeos::User* user = content::Details(details).ptr(); DCHECK(user); const CommandLine& command_line = *CommandLine::ForCurrentProcess(); if (!command_line.HasSwitch(switches::kTestType)) { // If we don't have a mounted profile directory we're in trouble. // TODO(davemoore) Once we have better api this check should ensure that // our profile directory is the one that's mounted, and that it's mounted // as the current user. chromeos::DBusThreadManager::Get()->GetCryptohomeClient()->IsMounted( base::Bind(&CheckCryptohomeIsMounted)); // Confirm that we hadn't loaded the new profile previously. if (command_line.HasSwitch(switches::kMultiProfiles)) { // TODO(nkostylev): We could not enforce username_hash not being // empty here till all cases like Guest mode are migrated. active_profile_username_hash_ = user->username_hash(); LOG(INFO) << "Switching to custom profile_dir: " << active_profile_username_hash_; } base::FilePath default_profile_dir = user_data_dir_.Append( GetInitialProfileDir(/*active_profile_username_hash_*/)); CHECK(!GetProfileByPath(default_profile_dir)) << "The default profile was loaded before we mounted the cryptohome."; } return; } else if (type == chrome::NOTIFICATION_ACTIVE_USER_CHANGED) { chromeos::User* user = content::Details(details).ptr(); active_profile_username_hash_ = user->username_hash(); LOG(INFO) << "Switching to custom profile_dir: " << active_profile_username_hash_; return; } #endif bool save_active_profiles = false; switch (type) { case chrome::NOTIFICATION_CLOSE_ALL_BROWSERS_REQUEST: { // Ignore any browsers closing from now on. closing_all_browsers_ = true; break; } case chrome::NOTIFICATION_BROWSER_CLOSE_CANCELLED: { // This will cancel the shutdown process, so the active profiles are // tracked again. Also, as the active profiles may have changed (i.e. if // some windows were closed) we save the current list of active profiles // again. closing_all_browsers_ = false; save_active_profiles = true; break; } case chrome::NOTIFICATION_BROWSER_OPENED: { Browser* browser = content::Source(source).ptr(); DCHECK(browser); Profile* profile = browser->profile(); DCHECK(profile); if (!profile->IsOffTheRecord() && ++browser_counts_[profile] == 1) { active_profiles_.push_back(profile); save_active_profiles = true; } // If browsers are opening, we can't be closing all the browsers. This // can happen if the application was exited, but background mode or // packaged apps prevented the process from shutting down, and then // a new browser window was opened. closing_all_browsers_ = false; break; } case chrome::NOTIFICATION_BROWSER_CLOSED: { Browser* browser = content::Source(source).ptr(); DCHECK(browser); Profile* profile = browser->profile(); DCHECK(profile); if (!profile->IsOffTheRecord() && --browser_counts_[profile] == 0) { active_profiles_.erase(std::find(active_profiles_.begin(), active_profiles_.end(), profile)); save_active_profiles = !closing_all_browsers_; } break; } default: { NOTREACHED(); break; } } if (save_active_profiles) { PrefService* local_state = g_browser_process->local_state(); DCHECK(local_state); ListPrefUpdate update(local_state, prefs::kProfilesLastActive); ListValue* profile_list = update.Get(); profile_list->Clear(); // crbug.com/120112 -> several non-incognito profiles might have the same // GetPath().BaseName(). In that case, we cannot restore both // profiles. Include each base name only once in the last active profile // list. std::set profile_paths; std::vector::const_iterator it; for (it = active_profiles_.begin(); it != active_profiles_.end(); ++it) { std::string profile_path = (*it)->GetPath().BaseName().MaybeAsASCII(); if (profile_paths.find(profile_path) == profile_paths.end()) { profile_paths.insert(profile_path); profile_list->Append(new StringValue(profile_path)); } } } } // static bool ProfileManager::IsImportProcess(const CommandLine& command_line) { return (command_line.HasSwitch(switches::kImport) || command_line.HasSwitch(switches::kImportFromFile)); } void ProfileManager::SetWillImport() { will_import_ = true; } void ProfileManager::OnImportFinished(Profile* profile) { will_import_ = false; DCHECK(profile); #if !defined(OS_CHROMEOS) // If the import process was not run, it means this branch was not called, // and it was handled by ProfileImpl::DoFinalInit(). BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile); model->AddObserver(new BookmarkModelLoadedObserver(profile)); #endif content::NotificationService::current()->Notify( chrome::NOTIFICATION_IMPORT_FINISHED, content::Source(profile), content::NotificationService::NoDetails()); } #if !defined(OS_ANDROID) && !defined(OS_IOS) ProfileManager::BrowserListObserver::BrowserListObserver( ProfileManager* manager) : profile_manager_(manager) { BrowserList::AddObserver(this); } ProfileManager::BrowserListObserver::~BrowserListObserver() { BrowserList::RemoveObserver(this); } void ProfileManager::BrowserListObserver::OnBrowserAdded( Browser* browser) {} void ProfileManager::BrowserListObserver::OnBrowserRemoved( Browser* browser) {} void ProfileManager::BrowserListObserver::OnBrowserSetLastActive( Browser* browser) { // If all browsers are being closed (e.g. the user is in the process of // shutting down), this event will be fired after each browser is // closed. This does not represent a user intention to change the active // browser so is not handled here. if (profile_manager_->closing_all_browsers_) return; Profile* last_active = browser->profile(); PrefService* local_state = g_browser_process->local_state(); DCHECK(local_state); // Only keep track of profiles that we are managing; tests may create others. if (profile_manager_->profiles_info_.find( last_active->GetPath()) != profile_manager_->profiles_info_.end()) { local_state->SetString(prefs::kProfileLastUsed, last_active->GetPath().BaseName().MaybeAsASCII()); } } #endif // !defined(OS_ANDROID) && !defined(OS_IOS) void ProfileManager::DoFinalInit(Profile* profile, bool go_off_the_record) { InitProfileUserPrefs(profile); DoFinalInitForServices(profile, go_off_the_record); AddProfileToCache(profile); DoFinalInitLogging(profile); ProfileMetrics::LogNumberOfProfiles(this); content::NotificationService::current()->Notify( chrome::NOTIFICATION_PROFILE_ADDED, content::Source(profile), content::NotificationService::NoDetails()); } void ProfileManager::DoFinalInitForServices(Profile* profile, bool go_off_the_record) { #if defined(ENABLE_EXTENSIONS) const CommandLine& command_line = *CommandLine::ForCurrentProcess(); if (!IsImportProcess(command_line)) { extensions::ExtensionSystem::Get(profile)->InitForRegularProfile( !go_off_the_record); // During tests, when |profile| is an instance of TestingProfile, // ExtensionSystem might not create an ExtensionService. if (extensions::ExtensionSystem::Get(profile)->extension_service()) { profile->GetHostContentSettingsMap()->RegisterExtensionService( extensions::ExtensionSystem::Get(profile)->extension_service()); } } #endif #if defined(ENABLE_MANAGED_USERS) // Initialization needs to happen after extension system initialization (for // extension::ManagementPolicy) and InitProfileUserPrefs (for setting the // initializing the managed flag if necessary). ManagedUserServiceFactory::GetForProfile(profile)->Init(); #endif } void ProfileManager::DoFinalInitLogging(Profile* profile) { // Count number of extensions in this profile. int extension_count = -1; #if defined(ENABLE_EXTENSIONS) ExtensionService* extension_service = profile->GetExtensionService(); if (extension_service) extension_count = extension_service->GetAppIds().size(); #endif // Log the profile size after a reasonable startup delay. BrowserThread::PostDelayedTask( BrowserThread::FILE, FROM_HERE, base::Bind(&ProfileSizeTask, profile->GetPath(), extension_count), base::TimeDelta::FromSeconds(112)); } Profile* ProfileManager::CreateProfileHelper(const base::FilePath& path) { return Profile::CreateProfile(path, NULL, Profile::CREATE_MODE_SYNCHRONOUS); } Profile* ProfileManager::CreateProfileAsyncHelper(const base::FilePath& path, Delegate* delegate) { return Profile::CreateProfile(path, delegate, Profile::CREATE_MODE_ASYNCHRONOUS); } void ProfileManager::OnProfileCreated(Profile* profile, bool success, bool is_new_profile) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); ProfilesInfoMap::iterator iter = profiles_info_.find(profile->GetPath()); DCHECK(iter != profiles_info_.end()); ProfileInfo* info = iter->second.get(); std::vector callbacks; info->callbacks.swap(callbacks); // Invoke CREATED callback for normal profiles. bool go_off_the_record = ShouldGoOffTheRecord(); if (success && !go_off_the_record) RunCallbacks(callbacks, profile, Profile::CREATE_STATUS_CREATED); // Perform initialization. if (success) { DoFinalInit(profile, go_off_the_record); if (go_off_the_record) profile = profile->GetOffTheRecordProfile(); info->created = true; } else { profile = NULL; profiles_info_.erase(iter); } // Invoke CREATED callback for incognito profiles. if (profile && go_off_the_record) RunCallbacks(callbacks, profile, Profile::CREATE_STATUS_CREATED); // Invoke INITIALIZED or FAIL for all profiles. RunCallbacks(callbacks, profile, profile ? Profile::CREATE_STATUS_INITIALIZED : Profile::CREATE_STATUS_FAIL); } base::FilePath ProfileManager::GenerateNextProfileDirectoryPath() { PrefService* local_state = g_browser_process->local_state(); DCHECK(local_state); DCHECK(IsMultipleProfilesEnabled()); // Create the next profile in the next available directory slot. int next_directory = local_state->GetInteger(prefs::kProfilesNumCreated); std::string profile_name = chrome::kMultiProfileDirPrefix; profile_name.append(base::IntToString(next_directory)); base::FilePath new_path = user_data_dir_; #if defined(OS_WIN) new_path = new_path.Append(ASCIIToUTF16(profile_name)); #else new_path = new_path.Append(profile_name); #endif local_state->SetInteger(prefs::kProfilesNumCreated, ++next_directory); return new_path; } // TODO(robertshield): ProfileManager should not be opening windows and should // not have to care about HostDesktopType. See http://crbug.com/153864 // static void ProfileManager::CreateMultiProfileAsync( const string16& name, const string16& icon_url, const CreateCallback& callback, chrome::HostDesktopType desktop_type, bool is_managed) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); ProfileManager* profile_manager = g_browser_process->profile_manager(); base::FilePath new_path = profile_manager->GenerateNextProfileDirectoryPath(); profile_manager->CreateProfileAsync(new_path, base::Bind(&OnOpenWindowForNewProfile, desktop_type, callback), name, icon_url, is_managed); } // static void ProfileManager::RegisterPrefs(PrefRegistrySimple* registry) { registry->RegisterStringPref(prefs::kProfileLastUsed, std::string()); registry->RegisterIntegerPref(prefs::kProfilesNumCreated, 1); registry->RegisterListPref(prefs::kProfilesLastActive); } size_t ProfileManager::GetNumberOfProfiles() { return GetProfileInfoCache().GetNumberOfProfiles(); } bool ProfileManager::CompareProfilePathAndName( const ProfileManager::ProfilePathAndName& pair1, const ProfileManager::ProfilePathAndName& pair2) { int name_compare = pair1.second.compare(pair2.second); if (name_compare < 0) { return true; } else if (name_compare > 0) { return false; } else { return pair1.first < pair2.first; } } ProfileInfoCache& ProfileManager::GetProfileInfoCache() { if (!profile_info_cache_.get()) { profile_info_cache_.reset(new ProfileInfoCache( g_browser_process->local_state(), user_data_dir_)); } return *profile_info_cache_.get(); } ProfileShortcutManager* ProfileManager::profile_shortcut_manager() { return profile_shortcut_manager_.get(); } void ProfileManager::AddProfileToCache(Profile* profile) { ProfileInfoCache& cache = GetProfileInfoCache(); if (profile->GetPath().DirName() != cache.GetUserDataDir()) return; if (cache.GetIndexOfProfileWithPath(profile->GetPath()) != std::string::npos) return; string16 username = UTF8ToUTF16(profile->GetPrefs()->GetString( prefs::kGoogleServicesUsername)); // Profile name and avatar are set by InitProfileUserPrefs and stored in the // profile. Use those values to setup the cache entry. string16 profile_name = UTF8ToUTF16(profile->GetPrefs()->GetString( prefs::kProfileName)); size_t icon_index = profile->GetPrefs()->GetInteger( prefs::kProfileAvatarIndex); bool is_managed = profile->GetPrefs()->GetBoolean(prefs::kProfileIsManaged); cache.AddProfileToCache(profile->GetPath(), profile_name, username, icon_index, is_managed); } void ProfileManager::InitProfileUserPrefs(Profile* profile) { ProfileInfoCache& cache = GetProfileInfoCache(); if (profile->GetPath().DirName() != cache.GetUserDataDir()) return; size_t avatar_index; std::string profile_name; bool is_managed = false; size_t profile_cache_index = cache.GetIndexOfProfileWithPath(profile->GetPath()); // If the cache has an entry for this profile, use the cache data. if (profile_cache_index != std::string::npos) { avatar_index = cache.GetAvatarIconIndexOfProfileAtIndex(profile_cache_index); profile_name = UTF16ToUTF8(cache.GetNameOfProfileAtIndex(profile_cache_index)); is_managed = cache.ProfileIsManagedAtIndex(profile_cache_index); } else if (profile->GetPath() == GetDefaultProfileDir(cache.GetUserDataDir())) { avatar_index = 0; profile_name = l10n_util::GetStringUTF8(IDS_DEFAULT_PROFILE_NAME); } else { avatar_index = cache.ChooseAvatarIconIndexForNewProfile(); profile_name = UTF16ToUTF8(cache.ChooseNameForNewProfile(avatar_index)); } if (!profile->GetPrefs()->HasPrefPath(prefs::kProfileAvatarIndex)) profile->GetPrefs()->SetInteger(prefs::kProfileAvatarIndex, avatar_index); if (!profile->GetPrefs()->HasPrefPath(prefs::kProfileName)) profile->GetPrefs()->SetString(prefs::kProfileName, profile_name); if (!profile->GetPrefs()->HasPrefPath(prefs::kProfileIsManaged)) profile->GetPrefs()->SetBoolean(prefs::kProfileIsManaged, is_managed); } bool ProfileManager::ShouldGoOffTheRecord() { bool go_off_the_record = false; #if defined(OS_CHROMEOS) const CommandLine& command_line = *CommandLine::ForCurrentProcess(); if (!logged_in_ && (!command_line.HasSwitch(switches::kTestType) || command_line.HasSwitch(chromeos::switches::kLoginProfile))) { go_off_the_record = true; } #endif return go_off_the_record; } // TODO(robertshield): ProfileManager should not be opening windows and should // not have to care about HostDesktopType. See http://crbug.com/153864 void ProfileManager::ScheduleProfileForDeletion( const base::FilePath& profile_dir, chrome::HostDesktopType desktop_type) { DCHECK(IsMultipleProfilesEnabled()); PrefService* local_state = g_browser_process->local_state(); ProfileInfoCache& cache = GetProfileInfoCache(); if (profile_dir.BaseName().MaybeAsASCII() == local_state->GetString(prefs::kProfileLastUsed)) { // Update the last used profile pref before closing browser windows. This // way the correct last used profile is set for any notification observers. std::string last_non_managed_profile; for (size_t i = 0; i < cache.GetNumberOfProfiles(); ++i) { base::FilePath cur_path = cache.GetPathOfProfileAtIndex(i); if (cur_path != profile_dir && !cache.ProfileIsManagedAtIndex(i)) { last_non_managed_profile = cur_path.BaseName().MaybeAsASCII(); break; } } // If we're deleting the last (non-managed) profile, then create a new // profile in its place. if (last_non_managed_profile.empty()) { base::FilePath new_path = GenerateNextProfileDirectoryPath(); // Make sure the last used profile path is pointing at it. This way the // correct last used profile is set for any notification observers. local_state->SetString(prefs::kProfileLastUsed, new_path.BaseName().MaybeAsASCII()); // TODO(robertshield): This desktop type needs to come from the invoker, // currently that involves plumbing this through web UI. chrome::HostDesktopType desktop_type = chrome::HOST_DESKTOP_TYPE_NATIVE; CreateProfileAsync(new_path, base::Bind(&OnOpenWindowForNewProfile, desktop_type, CreateCallback()), string16(), string16(), false); } else { local_state->SetString(prefs::kProfileLastUsed, last_non_managed_profile); } } // TODO(sail): Due to bug 88586 we don't delete the profile instance. Once we // start deleting the profile instance we need to close background apps too. Profile* profile = GetProfileByPath(profile_dir); if (profile) { BrowserList::CloseAllBrowsersWithProfile(profile); // Disable sync for doomed profile. if (ProfileSyncServiceFactory::GetInstance()->HasProfileSyncService( profile)) { ProfileSyncServiceFactory::GetInstance()->GetForProfile( profile)->DisableForUser(); } } QueueProfileDirectoryForDeletion(profile_dir); cache.DeleteProfileFromCache(profile_dir); } // static bool ProfileManager::IsMultipleProfilesEnabled() { #if defined(OS_ANDROID) return false; #endif #if defined(OS_CHROMEOS) if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kMultiProfiles)) return false; #endif return true; } void ProfileManager::AutoloadProfiles() { // If running in the background is disabled for the browser, do not autoload // any profiles. PrefService* local_state = g_browser_process->local_state(); if (!local_state->HasPrefPath(prefs::kBackgroundModeEnabled) || !local_state->GetBoolean(prefs::kBackgroundModeEnabled)) { return; } ProfileInfoCache& cache = GetProfileInfoCache(); size_t number_of_profiles = cache.GetNumberOfProfiles(); for (size_t p = 0; p < number_of_profiles; ++p) { if (cache.GetBackgroundStatusOfProfileAtIndex(p)) { // If status is true, that profile is running background apps. By calling // GetProfile, we automatically cause the profile to be loaded which will // register it with the BackgroundModeManager. GetProfile(cache.GetPathOfProfileAtIndex(p)); } } } ProfileManagerWithoutInit::ProfileManagerWithoutInit( const base::FilePath& user_data_dir) : ProfileManager(user_data_dir) { } void ProfileManager::RegisterTestingProfile(Profile* profile, bool add_to_cache) { RegisterProfile(profile, true); if (add_to_cache) { InitProfileUserPrefs(profile); AddProfileToCache(profile); } } void ProfileManager::RunCallbacks(const std::vector& callbacks, Profile* profile, Profile::CreateStatus status) { for (size_t i = 0; i < callbacks.size(); ++i) callbacks[i].Run(profile, status); } ProfileManager::ProfileInfo::ProfileInfo( Profile* profile, bool created) : profile(profile), created(created) { } ProfileManager::ProfileInfo::~ProfileInfo() { ProfileDestroyer::DestroyProfileWhenAppropriate(profile.release()); }