// Copyright 2013 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_window.h" #include "base/command_line.h" #include "base/files/file_path.h" #include "base/prefs/pref_service.h" #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" #include "chrome/browser/about_flags.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/lifetime/application_lifetime.h" #include "chrome/browser/pref_service_flags_storage.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_avatar_icon_util.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/signin/account_reconcilor_factory.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_dialogs.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/pref_names.h" #include "chrome/common/url_constants.h" #include "components/signin/core/browser/account_reconcilor.h" #include "components/signin/core/common/profile_management_switches.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/user_metrics.h" #if !defined(OS_IOS) #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_list.h" #include "chrome/browser/ui/browser_list_observer.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/startup/startup_browser_creator.h" #endif // !defined (OS_IOS) using base::UserMetricsAction; using content::BrowserThread; namespace { const char kNewProfileManagementExperimentInternalName[] = "enable-new-profile-management"; // Handles running a callback when a new Browser for the given profile // has been completely created. class BrowserAddedForProfileObserver : public chrome::BrowserListObserver { public: BrowserAddedForProfileObserver( Profile* profile, profiles::ProfileSwitchingDoneCallback callback) : profile_(profile), callback_(callback) { DCHECK(!callback_.is_null()); BrowserList::AddObserver(this); } virtual ~BrowserAddedForProfileObserver() { } private: // Overridden from BrowserListObserver: virtual void OnBrowserAdded(Browser* browser) OVERRIDE { if (browser->profile() == profile_) { BrowserList::RemoveObserver(this); callback_.Run(); base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); } } // Profile for which the browser should be opened. Profile* profile_; profiles::ProfileSwitchingDoneCallback callback_; DISALLOW_COPY_AND_ASSIGN(BrowserAddedForProfileObserver); }; void OpenBrowserWindowForProfile( profiles::ProfileSwitchingDoneCallback callback, bool always_create, bool is_new_profile, chrome::HostDesktopType desktop_type, Profile* profile, Profile::CreateStatus status) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (status != Profile::CREATE_STATUS_INITIALIZED) return; chrome::startup::IsProcessStartup is_process_startup = chrome::startup::IS_NOT_PROCESS_STARTUP; chrome::startup::IsFirstRun is_first_run = chrome::startup::IS_NOT_FIRST_RUN; // If this is a brand new profile, then start a first run window. if (is_new_profile) { is_process_startup = chrome::startup::IS_PROCESS_STARTUP; is_first_run = chrome::startup::IS_FIRST_RUN; } // If |always_create| is false, and we have a |callback| to run, check // whether a browser already exists so that we can run the callback. We don't // want to rely on the observer listening to OnBrowserSetLastActive in this // case, as you could manually activate an incorrect browser and trigger // a false positive. if (!always_create) { Browser* browser = chrome::FindTabbedBrowser(profile, false, desktop_type); if (browser) { browser->window()->Activate(); if (!callback.is_null()) callback.Run(); return; } } // If there is a callback, create an observer to make sure it is only // run when the browser has been completely created. This observer will // delete itself once that happens. This should not leak, because we are // passing |always_create| = true to FindOrCreateNewWindow below, which ends // up calling LaunchBrowser and opens a new window. If for whatever reason // that fails, either something has crashed, or the observer will be cleaned // up when a different browser for this profile is opened. if (!callback.is_null()) new BrowserAddedForProfileObserver(profile, callback); // We already dealt with the case when |always_create| was false and a browser // existed, which means that here a browser definitely needs to be created. // Passing true for |always_create| means we won't duplicate the code that // tries to find a browser. profiles::FindOrCreateNewWindowForProfile( profile, is_process_startup, is_first_run, desktop_type, true); } // Called after a |guest_profile| is available to be used by the user manager. // Based on the value of |tutorial_mode| we determine a url to be displayed // by the webui and run the |callback|, if it exists. void OnUserManagerGuestProfileCreated( const base::FilePath& profile_path_to_focus, profiles::UserManagerTutorialMode tutorial_mode, const base::Callback<void(Profile*, const std::string&)>& callback, Profile* guest_profile, Profile::CreateStatus status) { if (status != Profile::CREATE_STATUS_INITIALIZED || callback.is_null()) return; // Tell the webui which user should be focused. std::string page = chrome::kChromeUIUserManagerURL; if (tutorial_mode == profiles::USER_MANAGER_TUTORIAL_OVERVIEW) { page += "#tutorial"; } else if (!profile_path_to_focus.empty()) { const ProfileInfoCache& cache = g_browser_process->profile_manager()->GetProfileInfoCache(); size_t index = cache.GetIndexOfProfileWithPath(profile_path_to_focus); if (index != std::string::npos) { page += "#"; page += base::IntToString(index); } } callback.Run(guest_profile, page); } // Updates Chrome services that require notification when // the new_profile_management's status changes. void UpdateServicesWithNewProfileManagementFlag(Profile* profile, bool new_flag_status) { AccountReconcilor* account_reconcilor = AccountReconcilorFactory::GetForProfile(profile); account_reconcilor->OnNewProfileManagementFlagChanged(new_flag_status); } } // namespace namespace profiles { void 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 SwitchToProfile(const base::FilePath& path, chrome::HostDesktopType desktop_type, bool always_create, ProfileSwitchingDoneCallback callback, ProfileMetrics::ProfileOpen metric) { g_browser_process->profile_manager()->CreateProfileAsync( path, base::Bind(&OpenBrowserWindowForProfile, callback, always_create, false, desktop_type), base::string16(), base::string16(), std::string()); ProfileMetrics::LogProfileSwitchUser(metric); } void SwitchToGuestProfile(chrome::HostDesktopType desktop_type, ProfileSwitchingDoneCallback callback) { g_browser_process->profile_manager()->CreateProfileAsync( ProfileManager::GetGuestProfilePath(), base::Bind(&OpenBrowserWindowForProfile, callback, false, false, desktop_type), base::string16(), base::string16(), std::string()); ProfileMetrics::LogProfileSwitchUser(ProfileMetrics::SWITCH_PROFILE_GUEST); } void CreateAndSwitchToNewProfile(chrome::HostDesktopType desktop_type, ProfileSwitchingDoneCallback callback, ProfileMetrics::ProfileAdd metric) { ProfileInfoCache& cache = g_browser_process->profile_manager()->GetProfileInfoCache(); int placeholder_avatar_index = profiles::GetPlaceholderAvatarIndex(); ProfileManager::CreateMultiProfileAsync( cache.ChooseNameForNewProfile(placeholder_avatar_index), base::UTF8ToUTF16(profiles::GetDefaultAvatarIconUrl( placeholder_avatar_index)), base::Bind(&OpenBrowserWindowForProfile, callback, true, true, desktop_type), std::string()); ProfileMetrics::LogProfileAddNewUser(metric); } void CloseGuestProfileWindows() { ProfileManager* profile_manager = g_browser_process->profile_manager(); Profile* profile = profile_manager->GetProfileByPath( ProfileManager::GetGuestProfilePath()); if (profile) { BrowserList::CloseAllBrowsersWithProfile(profile); } } void LockProfile(Profile* profile) { DCHECK(profile); ProfileInfoCache& cache = g_browser_process->profile_manager()->GetProfileInfoCache(); size_t index = cache.GetIndexOfProfileWithPath(profile->GetPath()); cache.SetProfileSigninRequiredAtIndex(index, true); chrome::ShowUserManager(profile->GetPath()); BrowserList::CloseAllBrowsersWithProfile(profile); } void CreateGuestProfileForUserManager( const base::FilePath& profile_path_to_focus, profiles::UserManagerTutorialMode tutorial_mode, const base::Callback<void(Profile*, const std::string&)>& callback) { // Create the guest profile, if necessary, and open the User Manager // from the guest profile. g_browser_process->profile_manager()->CreateProfileAsync( ProfileManager::GetGuestProfilePath(), base::Bind(&OnUserManagerGuestProfileCreated, profile_path_to_focus, tutorial_mode, callback), base::string16(), base::string16(), std::string()); } void ShowUserManagerMaybeWithTutorial(Profile* profile) { // Guest users cannot appear in the User Manager, nor display a tutorial. if (!profile || profile->IsGuestSession()) { chrome::ShowUserManager(base::FilePath()); return; } // Show the tutorial if the profile has not shown it before. PrefService* pref_service = profile->GetPrefs(); bool tutorial_shown = pref_service->GetBoolean( prefs::kProfileUserManagerTutorialShown); if (!tutorial_shown) pref_service->SetBoolean(prefs::kProfileUserManagerTutorialShown, true); if (tutorial_shown) { chrome::ShowUserManager(profile->GetPath()); } else { chrome::ShowUserManagerWithTutorial( profiles::USER_MANAGER_TUTORIAL_OVERVIEW); } } void EnableNewProfileManagementPreview(Profile* profile) { #if defined(OS_ANDROID) NOTREACHED(); #else // TODO(rogerta): instead of setting experiment flags and command line // args, we should set a profile preference. const about_flags::Experiment experiment = { kNewProfileManagementExperimentInternalName, 0, // string id for title of experiment 0, // string id for description of experiment 0, // supported platforms about_flags::Experiment::ENABLE_DISABLE_VALUE, switches::kEnableNewProfileManagement, "", // not used with ENABLE_DISABLE_VALUE type switches::kDisableNewProfileManagement, "", // not used with ENABLE_DISABLE_VALUE type NULL, // not used with ENABLE_DISABLE_VALUE type 3 }; about_flags::PrefServiceFlagsStorage flags_storage( g_browser_process->local_state()); about_flags::SetExperimentEnabled( &flags_storage, experiment.NameForChoice(1), true); switches::EnableNewProfileManagementForTesting( CommandLine::ForCurrentProcess()); chrome::ShowUserManagerWithTutorial(profiles::USER_MANAGER_TUTORIAL_OVERVIEW); UpdateServicesWithNewProfileManagementFlag(profile, true); #endif } void DisableNewProfileManagementPreview(Profile* profile) { about_flags::PrefServiceFlagsStorage flags_storage( g_browser_process->local_state()); about_flags::SetExperimentEnabled( &flags_storage, kNewProfileManagementExperimentInternalName, false); chrome::AttemptRestart(); UpdateServicesWithNewProfileManagementFlag(profile, false); } } // namespace profiles