blob: 43ca4f90080f0fefb71a2fd9955cdbc185db5401 [file] [log] [blame]
// 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 <set>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/debug/trace_event.h"
#include "base/deferred_sequenced_task_runner.h"
#include "base/file_util.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/histogram.h"
#include "base/prefs/pref_service.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/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/chrome_notification_types.h"
#include "chrome/browser/content_settings/host_content_settings_map.h"
#include "chrome/browser/prefs/incognito_mode_prefs.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/profiles/profiles_state.h"
#include "chrome/browser/profiles/startup_task_runner_service.h"
#include "chrome/browser/profiles/startup_task_runner_service_factory.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/browser_iterator.h"
#include "chrome/browser/ui/sync/sync_promo_ui.h"
#include "chrome/common/chrome_constants.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_list.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 "chrome/browser/browser_process_platform_part_chromeos.h"
#include "chrome/browser/chromeos/login/user.h"
#include "chrome/browser/chromeos/login/user_manager.h"
#include "chrome/browser/chromeos/profiles/profile_helper.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<base::FilePath>& ProfilesToDelete() {
CR_DEFINE_STATIC_LOCAL(std::vector<base::FilePath>, profiles_to_delete, ());
return profiles_to_delete;
}
int64 ComputeFilesSize(const base::FilePath& directory,
const base::FilePath::StringType& pattern) {
int64 running_size = 0;
base::FileEnumerator iter(directory, false, base::FileEnumerator::FILES,
pattern);
while (!iter.Next().empty())
running_size += iter.GetInfo().GetSize();
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));
const int64 kBytesInOneMB = 1024 * 1024;
int64 size = ComputeFilesSize(path, FILE_PATH_LITERAL("*"));
int size_MB = static_cast<int>(size / kBytesInOneMB);
UMA_HISTOGRAM_COUNTS_10000("Profile.TotalSize", size_MB);
size = ComputeFilesSize(path, FILE_PATH_LITERAL("History"));
size_MB = static_cast<int>(size / kBytesInOneMB);
UMA_HISTOGRAM_COUNTS_10000("Profile.HistorySize", size_MB);
size = ComputeFilesSize(path, FILE_PATH_LITERAL("History*"));
size_MB = static_cast<int>(size / kBytesInOneMB);
UMA_HISTOGRAM_COUNTS_10000("Profile.TotalHistorySize", size_MB);
size = ComputeFilesSize(path, FILE_PATH_LITERAL("Cookies"));
size_MB = static_cast<int>(size / kBytesInOneMB);
UMA_HISTOGRAM_COUNTS_10000("Profile.CookiesSize", size_MB);
size = ComputeFilesSize(path, FILE_PATH_LITERAL("Bookmarks"));
size_MB = static_cast<int>(size / kBytesInOneMB);
UMA_HISTOGRAM_COUNTS_10000("Profile.BookmarksSize", size_MB);
size = ComputeFilesSize(path, FILE_PATH_LITERAL("Favicons"));
size_MB = static_cast<int>(size / kBytesInOneMB);
UMA_HISTOGRAM_COUNTS_10000("Profile.FaviconsSize", size_MB);
size = ComputeFilesSize(path, FILE_PATH_LITERAL("Top Sites"));
size_MB = static_cast<int>(size / kBytesInOneMB);
UMA_HISTOGRAM_COUNTS_10000("Profile.TopSitesSize", size_MB);
size = ComputeFilesSize(path, FILE_PATH_LITERAL("Visited Links"));
size_MB = static_cast<int>(size / kBytesInOneMB);
UMA_HISTOGRAM_COUNTS_10000("Profile.VisitedLinksSize", size_MB);
size = ComputeFilesSize(path, FILE_PATH_LITERAL("Web Data"));
size_MB = static_cast<int>(size / kBytesInOneMB);
UMA_HISTOGRAM_COUNTS_10000("Profile.WebDataSize", size_MB);
size = ComputeFilesSize(path, FILE_PATH_LITERAL("Extension*"));
size_MB = static_cast<int>(size / kBytesInOneMB);
UMA_HISTOGRAM_COUNTS_10000("Profile.ExtensionSize", size_MB);
size = ComputeFilesSize(path, FILE_PATH_LITERAL("Policy"));
size_MB = static_cast<int>(size / kBytesInOneMB);
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);
}
bool IsProfileMarkedForDeletion(const base::FilePath& profile_path) {
return std::find(ProfilesToDelete().begin(), ProfilesToDelete().end(),
profile_path) != ProfilesToDelete().end();
}
#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.";
}
#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<Profile*> profiles(pm->GetLoadedProfiles());
for (size_t i = 0; i < profiles.size(); ++i)
SessionServiceFactory::ShutdownForProfile(profiles[i]);
}
#endif
// static
void ProfileManager::NukeDeletedProfilesFromDisk() {
for (std::vector<base::FilePath>::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);
base::DeleteFile(*it, true);
base::DeleteFile(cache_path, true);
}
ProfilesToDelete().clear();
}
namespace {
bool s_allow_get_default_profile = false;
} // namespace
// static
void ProfileManager::AllowGetDefaultProfile() {
s_allow_get_default_profile = true;
}
// static
bool ProfileManager::IsGetDefaultProfileAllowed() {
return s_allow_get_default_profile;
}
// static
// TODO(nkostylev): Remove this method once all clients are migrated.
Profile* ProfileManager::GetDefaultProfile() {
CHECK(s_allow_get_default_profile)
<< "GetDefaultProfile() caled befofre allowed.";
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() {
CHECK(s_allow_get_default_profile)
<< "GetDefaultProfileOrOffTheRecord() caled befofre allowed.";
// 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
Profile* ProfileManager::GetLastUsedProfileAllowedByPolicy() {
Profile* profile = GetLastUsedProfile();
if (IncognitoModePrefs::GetAvailability(profile->GetPrefs()) ==
IncognitoModePrefs::FORCED) {
return profile->GetOffTheRecordProfile();
}
return profile;
}
// static
std::vector<Profile*> 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),
#if !defined(OS_ANDROID) && !defined(OS_IOS)
browser_list_observer_(this),
#endif
closing_all_browsers_(false) {
#if defined(OS_CHROMEOS)
registrar_.Add(
this,
chrome::NOTIFICATION_LOGIN_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::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)) {
// TODO(nkostylev): Remove this code completely once we eliminate
// legacy --login-profile=user switch and enable multi-profiles on CrOS
// by default. http://crbug.com/294628
std::string login_profile_value =
command_line.GetSwitchValueASCII(chromeos::switches::kLoginProfile);
if (login_profile_value == chrome::kLegacyProfileDir ||
login_profile_value == chrome::kTestUserProfileDir) {
profile_dir = base::FilePath(login_profile_value);
} else {
profile_dir = g_browser_process->platform_part()->profile_helper()->
GetUserProfileDir(login_profile_value);
}
} else if (!command_line.HasSwitch(switches::kMultiProfiles)) {
// We should never be logged in with no profile dir unless
// multi-profiles are enabled.
// In that case profile dir will be defined by user_id hash.
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.
std::string user_id_hash = g_browser_process->platform_part()->
profile_helper()->active_user_id_hash();
if (command_line.HasSwitch(switches::kMultiProfiles) &&
!user_id_hash.empty()) {
profile_dir = g_browser_process->platform_part()->
profile_helper()->GetActiveUserProfileDir();
}
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);
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<Profile*> ProfileManager::GetLastOpenedProfiles(
const base::FilePath& user_data_dir) {
PrefService* local_state = g_browser_process->local_state();
DCHECK(local_state);
std::vector<Profile*> 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_) {
default_profile_dir = default_profile_dir.Append(GetInitialProfileDir());
} else {
default_profile_dir = profiles::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(profile))
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 = profiles::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<Profile*> ProfileManager::GetLoadedProfiles() const {
std::vector<Profile*> 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) {
TRACE_EVENT0("browser", "ProfileManager::GetProfile")
// 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,
const std::string& managed_user_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// Make sure that this profile is not pending deletion.
if (IsProfileMarkedForDeletion(profile_path)) {
if (!callback.is_null())
callback.Run(NULL, Profile::CREATE_STATUS_LOCAL_FAIL);
return;
}
// Create the profile if needed and collect its ProfileInfo.
ProfilesInfoMap::iterator iter = profiles_info_.find(profile_path);
ProfileInfo* info = NULL;
if (iter != profiles_info_.end()) {
info = iter->second.get();
} else {
// Initiate asynchronous creation process.
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,
managed_user_id);
}
if (!managed_user_id.empty()) {
content::RecordAction(
UserMetricsAction("ManagedMode_LocallyManagedUserCreated"));
}
}
// Call or enqueue the callback.
if (!callback.is_null()) {
if (iter != profiles_info_.end() && info->created) {
Profile* profile = info->profile.get();
// If this was the guest profile, apply settings.
if (profile->GetPath() == ProfileManager::GetGuestProfilePath())
SetGuestProfilePrefs(profile);
// Profile has already been created. Run callback immediately.
callback.Run(profile, Profile::CREATE_STATUS_INITIALIZED);
} else {
// Profile is either already in the process of being created, or new.
// Add callback to the list.
info->callbacks.push_back(callback);
}
}
}
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);
InitProfileUserPrefs(profile);
DoFinalInit(profile, ShouldGoOffTheRecord(profile));
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<ProfileInfo>(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;
}
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;
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.
base::FilePath default_profile_dir = user_data_dir_.Append(
GetInitialProfileDir());
CHECK(!GetProfileByPath(default_profile_dir))
<< "The default profile was loaded before we mounted the cryptohome.";
}
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<Browser>(source).ptr();
DCHECK(browser);
Profile* profile = browser->profile();
DCHECK(profile);
bool is_ephemeral =
profile->GetPrefs()->GetBoolean(prefs::kForceEphemeralProfiles);
if (!profile->IsOffTheRecord() && !is_ephemeral &&
++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<Browser>(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<std::string> profile_paths;
std::vector<Profile*>::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));
}
}
}
}
#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) {
Profile* profile = browser->profile();
for (chrome::BrowserIterator it; !it.done(); it.Next()) {
if (it->profile()->GetOriginalProfile() == profile->GetOriginalProfile())
// Not the last window for this profile.
return;
}
// If the last browser of a profile that is scheduled for deletion is closed
// do that now.
base::FilePath path = profile->GetPath();
if (profile->GetPrefs()->GetBoolean(prefs::kForceEphemeralProfiles) &&
!IsProfileMarkedForDeletion(path)) {
g_browser_process->profile_manager()->ScheduleProfileForDeletion(
path, ProfileManager::CreateCallback());
}
}
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();
// Don't remember ephemeral profiles as last because they are not going to
// persist after restart.
if (last_active->GetPrefs()->GetBoolean(prefs::kForceEphemeralProfiles))
return;
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) {
DoFinalInitForServices(profile, go_off_the_record);
AddProfileToCache(profile);
DoFinalInitLogging(profile);
ProfileMetrics::LogNumberOfProfiles(this);
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_PROFILE_ADDED,
content::Source<Profile>(profile),
content::NotificationService::NoDetails());
}
void ProfileManager::DoFinalInitForServices(Profile* profile,
bool go_off_the_record) {
#if defined(ENABLE_EXTENSIONS)
// Set up a field trial to determine the effectiveness of deferring
// creation of background extension RenderViews.
CR_DEFINE_STATIC_LOCAL(scoped_refptr<base::FieldTrial>, trial, ());
static bool defer_creation = false;
if (!trial.get()) {
const base::FieldTrial::Probability kDivisor = 100;
// Enable the deferred creation for 50% of the users.
base::FieldTrial::Probability probability_per_group = 50;
// After August 31, 2014 builds, it will always be in default group
// (defer_creation == false).
trial = base::FieldTrialList::FactoryGetFieldTrial(
"DeferBackgroundExtensionCreation",
kDivisor,
"RateLimited",
2014,
8,
31,
base::FieldTrial::ONE_TIME_RANDOMIZED,
NULL);
// Add group for deferred creation of background extension RenderViews.
int defer_creation_group =
trial->AppendGroup("Deferred", probability_per_group);
defer_creation = trial->group() == defer_creation_group;
}
extensions::ExtensionSystem::Get(profile)->InitForRegularProfile(
!go_off_the_record, defer_creation);
// 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
// Start the deferred task runners once the profile is loaded.
StartupTaskRunnerServiceFactory::GetForProfile(profile)->
StartDeferredTaskRunners();
}
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<CreateCallback> callbacks;
info->callbacks.swap(callbacks);
// Invoke CREATED callback for normal profiles.
bool go_off_the_record = ShouldGoOffTheRecord(profile);
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);
}
// If this was the guest profile, finish setting its incognito status.
if (profile->GetPath() == ProfileManager::GetGuestProfilePath())
SetGuestProfilePrefs(profile);
// 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_LOCAL_FAIL);
}
base::FilePath ProfileManager::GenerateNextProfileDirectoryPath() {
PrefService* local_state = g_browser_process->local_state();
DCHECK(local_state);
DCHECK(profiles::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;
}
// static
base::FilePath ProfileManager::CreateMultiProfileAsync(
const string16& name,
const string16& icon_url,
const CreateCallback& callback,
const std::string& managed_user_id) {
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,
callback,
name,
icon_url,
managed_user_id);
return new_path;
}
// static
base::FilePath ProfileManager::GetGuestProfilePath() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
ProfileManager* profile_manager = g_browser_process->profile_manager();
base::FilePath guest_path = profile_manager->user_data_dir();
return guest_path.Append(chrome::kGuestProfileDir);
}
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_) {
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) {
if (profile->IsGuestSession())
return;
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);
std::string managed_user_id =
profile->GetPrefs()->GetString(prefs::kManagedUserId);
cache.AddProfileToCache(profile->GetPath(),
profile_name,
username,
icon_index,
managed_user_id);
}
void ProfileManager::InitProfileUserPrefs(Profile* profile) {
ProfileInfoCache& cache = GetProfileInfoCache();
if (profile->GetPath().DirName() != cache.GetUserDataDir())
return;
size_t avatar_index;
std::string profile_name;
std::string managed_user_id;
if (profile->IsGuestSession()) {
profile_name = l10n_util::GetStringUTF8(IDS_PROFILES_GUEST_PROFILE_NAME);
avatar_index = 0;
} else {
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));
managed_user_id =
cache.GetManagedUserIdOfProfileAtIndex(profile_cache_index);
} else if (profile->GetPath() ==
profiles::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::kManagedUserId)) {
if (managed_user_id.empty() &&
CommandLine::ForCurrentProcess()->HasSwitch(
switches::kNewProfileIsSupervised)) {
managed_user_id = "Test ID";
}
profile->GetPrefs()->SetString(prefs::kManagedUserId, managed_user_id);
}
}
void ProfileManager::SetGuestProfilePrefs(Profile* profile) {
IncognitoModePrefs::SetAvailability(profile->GetPrefs(),
IncognitoModePrefs::FORCED);
profile->GetPrefs()->SetBoolean(prefs::kShowBookmarkBar, false);
}
bool ProfileManager::ShouldGoOffTheRecord(Profile* profile) {
bool go_off_the_record = false;
#if defined(OS_CHROMEOS)
const CommandLine& command_line = *CommandLine::ForCurrentProcess();
if (profile->GetPath().BaseName().value() == chrome::kInitialProfile &&
(!command_line.HasSwitch(switches::kTestType) ||
command_line.HasSwitch(chromeos::switches::kLoginProfile))) {
go_off_the_record = true;
}
#endif
return go_off_the_record;
}
void ProfileManager::ScheduleProfileForDeletion(
const base::FilePath& profile_dir,
const CreateCallback& callback) {
DCHECK(profiles::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.
base::FilePath last_non_managed_profile_path;
for (size_t i = 0; i < cache.GetNumberOfProfiles(); ++i) {
base::FilePath cur_path = cache.GetPathOfProfileAtIndex(i);
// Make sure that this profile is not pending deletion.
if (cur_path != profile_dir && !cache.ProfileIsManagedAtIndex(i) &&
!IsProfileMarkedForDeletion(cur_path)) {
last_non_managed_profile_path = cur_path;
break;
}
}
// If we're deleting the last (non-managed) profile, then create a new
// profile in its place.
const std::string last_non_managed_profile =
last_non_managed_profile_path.BaseName().MaybeAsASCII();
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());
CreateProfileAsync(new_path,
callback,
string16(),
string16(),
std::string());
} else {
// On the Mac, the browser process is not killed when all browser windows
// are closed, so just in case we are deleting the active profile, and no
// other profile has been loaded, we must pre-load a next one.
#if defined(OS_MACOSX)
CreateProfileAsync(last_non_managed_profile_path,
base::Bind(&ProfileManager::OnNewActiveProfileLoaded,
base::Unretained(this),
profile_dir,
last_non_managed_profile_path,
callback),
string16(),
string16(),
std::string());
return;
#else
// For OS_MACOSX the pref is updated in the callback to make sure that
// it isn't used before the profile is actually loaded.
local_state->SetString(prefs::kProfileLastUsed, last_non_managed_profile);
#endif
}
}
FinishDeletingProfile(profile_dir);
}
void ProfileManager::OnNewActiveProfileLoaded(
const base::FilePath& profile_to_delete_path,
const base::FilePath& last_non_managed_profile_path,
const CreateCallback& original_callback,
Profile* loaded_profile,
Profile::CreateStatus status) {
DCHECK(status != Profile::CREATE_STATUS_LOCAL_FAIL &&
status != Profile::CREATE_STATUS_REMOTE_FAIL);
// Only run the code if the profile initialization has finished completely.
if (status == Profile::CREATE_STATUS_INITIALIZED) {
if (IsProfileMarkedForDeletion(last_non_managed_profile_path)) {
// If the profile we tried to load as the next active profile has been
// deleted, then retry deleting this profile to redo the logic to load
// the next available profile.
ScheduleProfileForDeletion(profile_to_delete_path, original_callback);
} else {
// Update the local state as promised in the ScheduleProfileForDeletion.
g_browser_process->local_state()->SetString(
prefs::kProfileLastUsed,
last_non_managed_profile_path.BaseName().MaybeAsASCII());
FinishDeletingProfile(profile_to_delete_path);
}
}
}
void ProfileManager::FinishDeletingProfile(const base::FilePath& profile_dir) {
ProfileInfoCache& cache = GetProfileInfoCache();
// 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);
}
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,
bool start_deferred_task_runners) {
RegisterProfile(profile, true);
if (add_to_cache) {
InitProfileUserPrefs(profile);
AddProfileToCache(profile);
}
if (start_deferred_task_runners) {
StartupTaskRunnerServiceFactory::GetForProfile(profile)->
StartDeferredTaskRunners();
}
}
void ProfileManager::RunCallbacks(const std::vector<CreateCallback>& 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());
}