blob: 94af5449c4561cf17e30e2f261bf857cf286a39a [file] [log] [blame]
// 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.
#include <set>
#include "chrome/browser/profiles/profile_manager.h"
#include "base/command_line.h"
#include "base/file_util.h"
#include "base/path_service.h"
#include "base/string_util.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/logging_chrome.h"
#include "content/browser/browser_thread.h"
#include "content/common/notification_service.h"
#include "content/common/notification_type.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 "net/url_request/url_request_job_tracker.h"
#if defined(OS_CHROMEOS)
#include "chrome/browser/chromeos/cros/cros_library.h"
#include "chrome/browser/chromeos/cros/cryptohome_library.h"
#endif
namespace {
void SuspendURLRequestJobs() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
for (net::URLRequestJobTracker::JobIterator i =
net::g_url_request_job_tracker.begin();
i != net::g_url_request_job_tracker.end(); ++i)
(*i)->Kill();
}
void SuspendRequestContext(
net::URLRequestContextGetter* request_context_getter) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
scoped_refptr<net::URLRequestContext> request_context =
request_context_getter->GetURLRequestContext();
request_context->http_transaction_factory()->Suspend(true);
}
void ResumeRequestContext(
net::URLRequestContextGetter* request_context_getter) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
scoped_refptr<net::URLRequestContext> request_context =
request_context_getter->GetURLRequestContext();
request_context->http_transaction_factory()->Suspend(false);
}
} // namespace
// 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)
profiles[i]->ShutdownSessionService();
}
// static
Profile* ProfileManager::GetDefaultProfile() {
FilePath user_data_dir;
PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
ProfileManager* profile_manager = g_browser_process->profile_manager();
return profile_manager->GetDefaultProfile(user_data_dir);
}
ProfileManager::ProfileManager() : logged_in_(false) {
ui::SystemMonitor::Get()->AddObserver(this);
#if defined(OS_CHROMEOS)
registrar_.Add(
this,
NotificationType::LOGIN_USER_CHANGED,
NotificationService::AllSources());
#endif
}
ProfileManager::~ProfileManager() {
ui::SystemMonitor* system_monitor = ui::SystemMonitor::Get();
if (system_monitor)
system_monitor->RemoveObserver(this);
}
FilePath ProfileManager::GetDefaultProfileDir(
const FilePath& user_data_dir) {
FilePath default_profile_dir(user_data_dir);
default_profile_dir =
default_profile_dir.AppendASCII(chrome::kNotSignedInProfile);
return default_profile_dir;
}
FilePath ProfileManager::GetProfilePrefsPath(
const FilePath &profile_dir) {
FilePath default_prefs_path(profile_dir);
default_prefs_path = default_prefs_path.Append(chrome::kPreferencesFilename);
return default_prefs_path;
}
FilePath ProfileManager::GetCurrentProfileDir() {
FilePath relative_profile_dir;
#if defined(OS_CHROMEOS)
const CommandLine& command_line = *CommandLine::ForCurrentProcess();
if (logged_in_) {
FilePath profile_dir;
// If the user has logged in, pick up the new profile.
if (command_line.HasSwitch(switches::kLoginProfile)) {
profile_dir = command_line.GetSwitchValuePath(switches::kLoginProfile);
} else {
// We should never be logged in with no profile dir.
NOTREACHED();
return FilePath("");
}
relative_profile_dir = relative_profile_dir.Append(profile_dir);
return relative_profile_dir;
}
#endif
relative_profile_dir =
relative_profile_dir.AppendASCII(chrome::kNotSignedInProfile);
return relative_profile_dir;
}
Profile* ProfileManager::GetDefaultProfile(const FilePath& user_data_dir) {
FilePath default_profile_dir(user_data_dir);
default_profile_dir = default_profile_dir.Append(GetCurrentProfileDir());
#if defined(OS_CHROMEOS)
if (!logged_in_) {
Profile* profile;
const CommandLine& command_line = *CommandLine::ForCurrentProcess();
// 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 (!command_line.HasSwitch(switches::kTestType) ||
command_line.HasSwitch(switches::kLoginProfile)) {
// Don't init extensions for this profile
profile = GetProfile(default_profile_dir);
profile = profile->GetOffTheRecordProfile();
} else {
profile = GetProfile(default_profile_dir);
}
return profile;
}
#endif
return GetProfile(default_profile_dir);
}
Profile* ProfileManager::GetProfileWithId(ProfileId profile_id) {
DCHECK_NE(Profile::kInvalidProfileId, profile_id);
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 (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 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 = Profile::CreateProfile(profile_dir);
DCHECK(profile);
if (profile) {
bool result = AddProfile(profile);
DCHECK(result);
}
return 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) {
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);
return true;
}
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;
}
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() {
DCHECK(CalledOnValidThread());
bool posted = BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
NewRunnableFunction(&SuspendURLRequestJobs));
DCHECK(posted);
scoped_refptr<net::URLRequestContextGetter> request_context;
std::vector<Profile*> 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 = profiles[i]->GetRequestContextForMedia();
posted = BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
NewRunnableFunction(&SuspendRequestContext, request_context));
DCHECK(posted);
}
}
void ProfileManager::OnResume() {
DCHECK(CalledOnValidThread());
scoped_refptr<net::URLRequestContextGetter> request_context;
std::vector<Profile*> 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 = profiles[i]->GetRequestContextForMedia();
posted = BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
NewRunnableFunction(&ResumeRequestContext, request_context));
DCHECK(posted);
}
}
void ProfileManager::Observe(
NotificationType type,
const NotificationSource& source,
const NotificationDetails& details) {
#if defined(OS_CHROMEOS)
if (type == NotificationType::LOGIN_USER_CHANGED) {
const CommandLine& command_line = *CommandLine::ForCurrentProcess();
if (!command_line.HasSwitch(switches::kTestType)) {
// This will fail when running on non cros os.
// TODO(davemoore) Need to mock this enough to enable testing.
CHECK(chromeos::CrosLibrary::Get()->EnsureLoaded());
// 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.
CHECK(chromeos::CrosLibrary::Get()->GetCryptohomeLibrary()->IsMounted());
}
logged_in_ = true;
}
#endif
}
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
profile->InitExtensions(init_extensions);
if (!command_line.HasSwitch(switches::kDisableWebResources))
profile->InitPromoResources();
}
void ProfileManager::OnProfileCreated(Profile* profile, bool success) {
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<Observer*> 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);
}
for (size_t i = 0; i < observers.size(); ++i) {
observers[i]->OnProfileCreated(profile);
}
}