blob: 335301673fdf4ad3cfdcd1d1b33f7d77fab3361b [file] [log] [blame]
// Copyright (c) 2006-2008 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/user_data_manager.h"
#include <windows.h>
#include <string>
#include "base/command_line.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "base/message_loop.h"
#include "base/path_service.h"
#include "base/process_util.h"
#include "base/string_util.h"
#include "chrome/browser/chrome_thread.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/l10n_util.h"
#include "chrome/installer/util/browser_distribution.h"
#include "chrome/installer/util/shell_util.h"
#include "chromium_strings.h"
namespace {
// Helper to start chrome for a given profile index. The helper takes care of
// enumerating profiles on the file thread and then it launches Chrome for the
// appropriate profile on the original thread.
// An instance of this class should always be created on the heap, and it will
// delete itself when the launch is done.
class LaunchChromeForProfileIndexHelper : GetProfilesHelper::Delegate {
public:
// Creates an instance with the given data manager and to launch chrome for
// the profile with the given index.
LaunchChromeForProfileIndexHelper(const UserDataManager* manager, int index);
virtual ~LaunchChromeForProfileIndexHelper();
// Starts the asynchronous launch.
void StartLaunch();
// GetProfilesHelper::Delegate method.
void OnGetProfilesDone(const std::vector<std::wstring>& profiles);
private:
int index_;
const UserDataManager* manager_;
scoped_refptr<GetProfilesHelper> profiles_helper_;
DISALLOW_COPY_AND_ASSIGN(LaunchChromeForProfileIndexHelper);
};
} // namespace
LaunchChromeForProfileIndexHelper::LaunchChromeForProfileIndexHelper(
const UserDataManager* manager,
int index)
: manager_(manager),
index_(index),
// Don't complain about using "this" in initializer list.
MSVC_PUSH_DISABLE_WARNING(4355)
profiles_helper_(new GetProfilesHelper(this)) {
MSVC_POP_WARNING()
DCHECK(manager);
}
LaunchChromeForProfileIndexHelper::~LaunchChromeForProfileIndexHelper() {
profiles_helper_->OnDelegateDeleted();
}
void LaunchChromeForProfileIndexHelper::StartLaunch() {
profiles_helper_->GetProfiles(NULL);
}
void LaunchChromeForProfileIndexHelper::OnGetProfilesDone(
const std::vector<std::wstring>& profiles) {
if (index_ >= 0 && index_ < static_cast<int>(profiles.size()))
manager_->LaunchChromeForProfile(profiles[index_]);
// We are done, delete ourselves.
delete this;
}
// Separator used in folder names between the prefix and the profile name.
// For e.g. a folder for the profile "Joe" would be named "User Data-Joe".
static const wchar_t kProfileFolderSeparator[] = L"-";
// static
UserDataManager* UserDataManager::instance_ = NULL;
// static
void UserDataManager::Create() {
DCHECK(!instance_);
std::wstring user_data;
PathService::Get(chrome::DIR_USER_DATA, &user_data);
instance_ = new UserDataManager(user_data);
}
// static
UserDataManager* UserDataManager::Get() {
DCHECK(instance_);
return instance_;
}
UserDataManager::UserDataManager(const std::wstring& user_data_root)
: user_data_root_(user_data_root) {
// Determine current profile name and current folder name.
current_folder_name_ = file_util::GetFilenameFromPath(user_data_root);
bool success = GetProfileNameFromFolderName(current_folder_name_,
&current_profile_name_);
// The current profile is a default profile if the current user data folder
// name is just kUserDataDirname or when the folder name doesn't have the
// kUserDataDirname as prefix.
is_current_profile_default_ =
!success || (current_folder_name_ == chrome::kUserDataDirname);
// (TODO:munjal) Fix issue 5070:
// http://code.google.com/p/chromium/issues/detail?id=5070
file_util::UpOneDirectory(&user_data_root_);
}
UserDataManager::~UserDataManager() {
}
// static
bool UserDataManager::GetProfileNameFromFolderName(
const std::wstring& folder_name,
std::wstring* profile_name) {
// The folder name should start with a specific prefix for it to be a valid
// profile folder.
if (folder_name.find(chrome::kUserDataDirname) != 0)
return false;
// Seems like we cannot use arraysize macro for externally defined constants.
// Is there a way?
unsigned int prefix_length = wcslen(chrome::kUserDataDirname);
// Subtract 1 from the size of the array for trailing null character.
unsigned int separator_length = arraysize(kProfileFolderSeparator) - 1;
// It's safe to use profile_name variable for intermediate values since we
// will always return true now.
*profile_name = folder_name.substr(prefix_length);
// Remove leading separator if present.
if (profile_name->find_first_of(kProfileFolderSeparator) == 0)
*profile_name = profile_name->substr(separator_length);
if (profile_name->empty())
*profile_name = chrome::kNotSignedInProfile;
return true;
}
// static
std::wstring UserDataManager::GetFolderNameFromProfileName(
const std::wstring& profile_name) {
std::wstring folder_name = chrome::kUserDataDirname;
if (profile_name != chrome::kNotSignedInProfile) {
folder_name += kProfileFolderSeparator;
folder_name += profile_name;
}
return folder_name;
}
std::wstring UserDataManager::GetUserDataFolderForProfile(
const std::wstring& profile_name) const {
std::wstring folder_name = GetFolderNameFromProfileName(profile_name);
std::wstring folder_path(user_data_root_);
file_util::AppendToPath(&folder_path, folder_name);
return folder_path;
}
std::wstring UserDataManager::GetCommandForProfile(
const std::wstring& profile_name) const {
std::wstring user_data_dir = GetUserDataFolderForProfile(profile_name);
std::wstring command;
PathService::Get(base::FILE_EXE, &command);
CommandLine::AppendSwitchWithValue(&command,
switches::kUserDataDir,
user_data_dir);
std::wstring local_state_path;
PathService::Get(chrome::FILE_LOCAL_STATE, &local_state_path);
CommandLine::AppendSwitchWithValue(&command,
switches::kParentProfile,
local_state_path);
return command;
}
void UserDataManager::LaunchChromeForProfile(
const std::wstring& profile_name) const {
std::wstring command = GetCommandForProfile(profile_name);
base::LaunchApp(command, false, false, NULL);
}
void UserDataManager::LaunchChromeForProfile(int index) const {
// Helper deletes itself when done.
LaunchChromeForProfileIndexHelper* helper =
new LaunchChromeForProfileIndexHelper(this, index);
helper->StartLaunch();
}
void UserDataManager::GetProfiles(std::vector<std::wstring>* profiles) const {
// This function should be called on the file thread.
DCHECK(MessageLoop::current() ==
ChromeThread::GetMessageLoop(ChromeThread::FILE));
file_util::FileEnumerator file_enum(
FilePath::FromWStringHack(user_data_root_),
false, file_util::FileEnumerator::DIRECTORIES);
std::wstring folder_name;
while (!(folder_name = file_enum.Next().ToWStringHack()).empty()) {
folder_name = file_util::GetFilenameFromPath(folder_name);
std::wstring profile_name;
if (GetProfileNameFromFolderName(folder_name, &profile_name))
profiles->push_back(profile_name);
}
}
bool UserDataManager::CreateDesktopShortcutForProfile(
const std::wstring& profile_name) const {
std::wstring exe_path;
std::wstring shortcut_path;
if (!PathService::Get(base::FILE_EXE, &exe_path) ||
!ShellUtil::GetDesktopPath(false, &shortcut_path))
return false;
// Working directory.
std::wstring exe_folder = file_util::GetDirectoryFromPath(exe_path);
// Command and arguments.
std::wstring cmd;
cmd = StringPrintf(L"\"%ls\"", exe_path.c_str());
std::wstring user_data_dir = GetUserDataFolderForProfile(profile_name);
std::wstring args = CommandLine::PrefixedSwitchStringWithValue(
switches::kUserDataDir,
user_data_dir);
args = StringPrintf(L"\"%ls\"", args.c_str());
// Shortcut path.
std::wstring shortcut_name = l10n_util::GetStringF(
IDS_START_IN_PROFILE_SHORTCUT_NAME,
profile_name);
shortcut_name.append(L".lnk");
file_util::AppendToPath(&shortcut_path, shortcut_name);
return file_util::CreateShortcutLink(cmd.c_str(),
shortcut_path.c_str(),
exe_folder.c_str(),
args.c_str(),
NULL,
exe_path.c_str(),
0);
}
GetProfilesHelper::GetProfilesHelper(Delegate* delegate)
: delegate_(delegate) {
}
void GetProfilesHelper::GetProfiles(MessageLoop* target_loop) {
// If the target loop is not NULL then use the target loop, or if it's NULL
// then use the current message loop to post a task on it later when we are
// done building a list of profiles.
if (target_loop) {
message_loop_ = target_loop;
} else {
message_loop_ = MessageLoop::current();
}
DCHECK(message_loop_);
MessageLoop* file_loop = ChromeThread::GetMessageLoop(ChromeThread::FILE);
file_loop->PostTask(
FROM_HERE,
NewRunnableMethod(this, &GetProfilesHelper::GetProfilesFromManager));
}
// Records that the delegate is closed.
void GetProfilesHelper::OnDelegateDeleted() {
delegate_ = NULL;
}
void GetProfilesHelper::GetProfilesFromManager() {
// This function should be called on the file thread.
DCHECK(MessageLoop::current() ==
ChromeThread::GetMessageLoop(ChromeThread::FILE));
// If the delegate is gone by now, no need to do any work.
if (!delegate_)
return;
scoped_ptr< std::vector<std::wstring> > profiles(
new std::vector<std::wstring>);
UserDataManager::Get()->GetProfiles(profiles.get());
// Post a task on the original thread to call the delegate.
message_loop_->PostTask(
FROM_HERE,
NewRunnableMethod(this,
&GetProfilesHelper::InvokeDelegate,
profiles.release()));
}
void GetProfilesHelper::InvokeDelegate(std::vector<std::wstring>* profiles) {
scoped_ptr< std::vector<std::wstring> > udd_profiles(profiles);
// If the delegate is gone by now, no need to do any work.
if (delegate_)
delegate_->OnGetProfilesDone(*udd_profiles.get());
}