| // 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 <stddef.h> |
| |
| #include <string> |
| #include <vector> |
| |
| #include "base/command_line.h" |
| #include "base/files/file_path.h" |
| #include "base/location.h" |
| #include "base/macros.h" |
| #include "base/metrics/user_metrics.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/about_flags.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/lifetime/application_lifetime.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/profiles/profile_attributes_entry.h" |
| #include "chrome/browser/profiles/profile_attributes_storage.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/signin/account_tracker_service_factory.h" |
| #include "chrome/browser/signin/signin_manager_factory.h" |
| #include "chrome/browser/signin/signin_ui_util.h" |
| #include "chrome/browser/sync/profile_sync_service_factory.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_dialogs.h" |
| #include "chrome/browser/ui/profile_chooser_constants.h" |
| #include "chrome/common/chrome_features.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/common/url_constants.h" |
| #include "components/browser_sync/profile_sync_service.h" |
| #include "components/flags_ui/pref_service_flags_storage.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/signin/core/browser/account_reconcilor.h" |
| #include "components/signin/core/browser/account_tracker_service.h" |
| #include "components/signin/core/browser/signin_manager.h" |
| #include "components/signin/core/browser/signin_pref_names.h" |
| #include "components/signin/core/browser/signin_switches.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "extensions/buildflags/buildflags.h" |
| #include "net/base/escape.h" |
| |
| #if BUILDFLAG(ENABLE_EXTENSIONS) |
| #include "chrome/browser/extensions/extension_service.h" |
| #include "extensions/browser/extension_prefs.h" |
| #include "extensions/browser/extension_registry.h" |
| #include "extensions/browser/extension_registry_factory.h" |
| #include "extensions/browser/extension_system.h" |
| #endif // BUILDFLAG(ENABLE_EXTENSIONS) |
| |
| #if !defined(OS_ANDROID) |
| #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_ANDROID) |
| |
| #if !defined(OS_CHROMEOS) |
| #include "chrome/browser/ui/user_manager.h" |
| #endif // !defined(OS_CHROMEOS) |
| |
| using base::UserMetricsAction; |
| using content::BrowserThread; |
| |
| namespace { |
| |
| #if BUILDFLAG(ENABLE_EXTENSIONS) |
| void BlockExtensions(Profile* profile) { |
| extensions::ExtensionService* extension_service = |
| extensions::ExtensionSystem::Get(profile)->extension_service(); |
| extension_service->BlockAllExtensions(); |
| } |
| |
| void UnblockExtensions(Profile* profile) { |
| extensions::ExtensionService* extension_service = |
| extensions::ExtensionSystem::Get(profile)->extension_service(); |
| extension_service->UnblockAllExtensions(); |
| } |
| #endif // BUILDFLAG(ENABLE_EXTENSIONS) |
| |
| // Called after a |system_profile| is available to be used by the user manager. |
| // Runs |callback|, if it exists. Depending on the value of |
| // |user_manager_action|, executes an action once the user manager displays or |
| // after a profile is opened. |
| void OnUserManagerSystemProfileCreated( |
| const base::FilePath& profile_path_to_focus, |
| profiles::UserManagerAction user_manager_action, |
| const base::Callback<void(Profile*, const std::string&)>& callback, |
| Profile* system_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::kChromeUIMdUserManagerUrl; |
| |
| if (!profile_path_to_focus.empty()) { |
| // The file path is processed in the same way as base::CreateFilePathValue |
| // (i.e. convert to std::string with AsUTF8Unsafe()), and then URI encoded. |
| page += "#"; |
| page += net::EscapeUrlEncodedData(profile_path_to_focus.AsUTF8Unsafe(), |
| false); |
| } else if (user_manager_action == |
| profiles::USER_MANAGER_OPEN_CREATE_USER_PAGE) { |
| page += profiles::kUserManagerOpenCreateUserPage; |
| } else if (user_manager_action == |
| profiles::USER_MANAGER_SELECT_PROFILE_TASK_MANAGER) { |
| page += profiles::kUserManagerSelectProfileTaskManager; |
| } else if (user_manager_action == |
| profiles::USER_MANAGER_SELECT_PROFILE_ABOUT_CHROME) { |
| page += profiles::kUserManagerSelectProfileAboutChrome; |
| } else if (user_manager_action == |
| profiles::USER_MANAGER_SELECT_PROFILE_CHROME_SETTINGS) { |
| page += profiles::kUserManagerSelectProfileChromeSettings; |
| } |
| callback.Run(system_profile, page); |
| } |
| |
| // Called in profiles::LoadProfileAsync once profile is loaded. It runs |
| // |callback| if it isn't null. |
| void ProfileLoadedCallback(ProfileManager::CreateCallback callback, |
| Profile* profile, |
| Profile::CreateStatus status) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| if (status != Profile::CREATE_STATUS_INITIALIZED) |
| return; |
| |
| if (!callback.is_null()) |
| callback.Run(profile, Profile::CREATE_STATUS_INITIALIZED); |
| } |
| |
| } // namespace |
| |
| namespace profiles { |
| |
| // User Manager parameters are prefixed with hash. |
| const char kUserManagerOpenCreateUserPage[] = "#create-user"; |
| const char kUserManagerSelectProfileTaskManager[] = "#task-manager"; |
| const char kUserManagerSelectProfileAboutChrome[] = "#about-chrome"; |
| const char kUserManagerSelectProfileChromeSettings[] = "#chrome-settings"; |
| |
| base::FilePath GetPathOfProfileWithEmail(ProfileManager* profile_manager, |
| const std::string& email) { |
| base::string16 profile_email = base::UTF8ToUTF16(email); |
| std::vector<ProfileAttributesEntry*> entries = |
| profile_manager->GetProfileAttributesStorage().GetAllProfilesAttributes(); |
| for (ProfileAttributesEntry* entry : entries) { |
| if (entry->GetUserName() == profile_email) |
| return entry->GetPath(); |
| } |
| return base::FilePath(); |
| } |
| |
| void FindOrCreateNewWindowForProfile( |
| Profile* profile, |
| chrome::startup::IsProcessStartup process_startup, |
| chrome::startup::IsFirstRun is_first_run, |
| bool always_create) { |
| DCHECK(profile); |
| |
| if (!always_create) { |
| Browser* browser = chrome::FindTabbedBrowser(profile, false); |
| if (browser) { |
| browser->window()->Activate(); |
| return; |
| } |
| } |
| |
| base::RecordAction(UserMetricsAction("NewWindow")); |
| base::CommandLine command_line(base::CommandLine::NO_PROGRAM); |
| StartupBrowserCreator browser_creator; |
| browser_creator.LaunchBrowser( |
| command_line, profile, base::FilePath(), process_startup, is_first_run); |
| } |
| |
| void OpenBrowserWindowForProfile(ProfileManager::CreateCallback callback, |
| bool always_create, |
| bool is_new_profile, |
| bool unblock_extensions, |
| Profile* profile, |
| Profile::CreateStatus status) { |
| DCHECK_CURRENTLY_ON(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 !defined(OS_CHROMEOS) |
| if (!profile->IsGuestSession()) { |
| ProfileAttributesEntry* entry; |
| if (g_browser_process->profile_manager()->GetProfileAttributesStorage(). |
| GetProfileAttributesWithPath(profile->GetPath(), &entry) && |
| entry->IsSigninRequired()) { |
| UserManager::Show(profile->GetPath(), |
| profiles::USER_MANAGER_SELECT_PROFILE_NO_ACTION); |
| return; |
| } |
| } |
| #endif // !defined(OS_CHROMEOS) |
| |
| #if BUILDFLAG(ENABLE_EXTENSIONS) |
| if (unblock_extensions) |
| UnblockExtensions(profile); |
| #endif // BUILDFLAG(ENABLE_EXTENSIONS) |
| |
| // 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); |
| if (browser) { |
| browser->window()->Activate(); |
| if (!callback.is_null()) |
| callback.Run(profile, Profile::CREATE_STATUS_INITIALIZED); |
| 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, true); |
| } |
| |
| #if !defined(OS_ANDROID) |
| |
| void LoadProfileAsync(const base::FilePath& path, |
| ProfileManager::CreateCallback callback) { |
| g_browser_process->profile_manager()->CreateProfileAsync( |
| path, base::Bind(&ProfileLoadedCallback, callback), base::string16(), |
| std::string()); |
| } |
| |
| void SwitchToProfile(const base::FilePath& path, |
| bool always_create, |
| ProfileManager::CreateCallback callback, |
| ProfileMetrics::ProfileOpen metric) { |
| ProfileMetrics::LogProfileSwitch(metric, |
| g_browser_process->profile_manager(), |
| path); |
| g_browser_process->profile_manager()->CreateProfileAsync( |
| path, |
| base::Bind(&profiles::OpenBrowserWindowForProfile, callback, |
| always_create, false, false), |
| base::string16(), std::string()); |
| } |
| |
| void SwitchToGuestProfile(ProfileManager::CreateCallback callback) { |
| const base::FilePath& path = ProfileManager::GetGuestProfilePath(); |
| ProfileMetrics::LogProfileSwitch(ProfileMetrics::SWITCH_PROFILE_GUEST, |
| g_browser_process->profile_manager(), |
| path); |
| g_browser_process->profile_manager()->CreateProfileAsync( |
| path, |
| base::Bind(&profiles::OpenBrowserWindowForProfile, callback, false, false, |
| false), |
| base::string16(), std::string()); |
| } |
| #endif |
| |
| bool HasProfileSwitchTargets(Profile* profile) { |
| size_t min_profiles = profile->IsGuestSession() ? 1 : 2; |
| size_t number_of_profiles = |
| g_browser_process->profile_manager()->GetNumberOfProfiles(); |
| return number_of_profiles >= min_profiles; |
| } |
| |
| void CreateAndSwitchToNewProfile(ProfileManager::CreateCallback callback, |
| ProfileMetrics::ProfileAdd metric) { |
| ProfileAttributesStorage& storage = |
| g_browser_process->profile_manager()->GetProfileAttributesStorage(); |
| |
| int placeholder_avatar_index = profiles::GetPlaceholderAvatarIndex(); |
| ProfileManager::CreateMultiProfileAsync( |
| storage.ChooseNameForNewProfile(placeholder_avatar_index), |
| profiles::GetDefaultAvatarIconUrl(placeholder_avatar_index), |
| base::Bind(&profiles::OpenBrowserWindowForProfile, callback, true, true, |
| false)); |
| ProfileMetrics::LogProfileAddNewUser(metric); |
| } |
| |
| void ProfileBrowserCloseSuccess(const base::FilePath& profile_path) { |
| #if !defined(OS_CHROMEOS) |
| UserManager::Show(base::FilePath(), |
| profiles::USER_MANAGER_SELECT_PROFILE_NO_ACTION); |
| #endif // !defined(OS_CHROMEOS) |
| } |
| |
| void CloseGuestProfileWindows() { |
| ProfileManager* profile_manager = g_browser_process->profile_manager(); |
| Profile* profile = profile_manager->GetProfileByPath( |
| ProfileManager::GetGuestProfilePath()); |
| |
| if (profile) { |
| BrowserList::CloseAllBrowsersWithProfile( |
| profile, base::Bind(&ProfileBrowserCloseSuccess), |
| BrowserList::CloseCallback(), false); |
| } |
| } |
| |
| void LockBrowserCloseSuccess(const base::FilePath& profile_path) { |
| ProfileManager* profile_manager = g_browser_process->profile_manager(); |
| ProfileAttributesEntry* entry; |
| bool has_entry = profile_manager->GetProfileAttributesStorage(). |
| GetProfileAttributesWithPath(profile_path, &entry); |
| DCHECK(has_entry); |
| entry->SetIsSigninRequired(true); |
| |
| #if BUILDFLAG(ENABLE_EXTENSIONS) |
| // Profile guaranteed to exist for it to have been locked. |
| BlockExtensions(profile_manager->GetProfileByPath(profile_path)); |
| #endif // BUILDFLAG(ENABLE_EXTENSIONS) |
| |
| chrome::HideTaskManager(); |
| #if !defined(OS_CHROMEOS) |
| UserManager::Show(profile_path, |
| profiles::USER_MANAGER_SELECT_PROFILE_NO_ACTION); |
| #endif // !defined(OS_CHROMEOS) |
| } |
| |
| void LockProfile(Profile* profile) { |
| DCHECK(profile); |
| if (profile) { |
| BrowserList::CloseAllBrowsersWithProfile( |
| profile, base::Bind(&LockBrowserCloseSuccess), |
| BrowserList::CloseCallback(), false); |
| } |
| } |
| |
| bool IsLockAvailable(Profile* profile) { |
| DCHECK(profile); |
| if (profile->IsGuestSession() || profile->IsSystemProfile()) |
| return false; |
| |
| std::string hosted_domain = profile->GetPrefs()-> |
| GetString(prefs::kGoogleServicesHostedDomain); |
| // TODO(mlerman): After one release remove any hosted_domain reference to the |
| // pref, since all users will have this in the AccountTrackerService. |
| if (hosted_domain.empty()) { |
| AccountTrackerService* account_tracker = |
| AccountTrackerServiceFactory::GetForProfile(profile); |
| std::string account_id = |
| SigninManagerFactory::GetForProfile(profile)->GetAuthenticatedAccountId(); |
| hosted_domain = account_tracker->GetAccountInfo(account_id).hosted_domain; |
| } |
| // TODO(mlerman): Prohibit only users who authenticate using SAML. Until then, |
| // prohibited users who use hosted domains (aside from google.com). |
| if (hosted_domain != AccountTrackerService::kNoHostedDomainFound && |
| hosted_domain != "google.com") { |
| return false; |
| } |
| |
| // Lock only when there is at least one supervised user on the machine. |
| std::vector<ProfileAttributesEntry*> entries = |
| g_browser_process->profile_manager()->GetProfileAttributesStorage(). |
| GetAllProfilesAttributes(); |
| for (ProfileAttributesEntry* entry : entries) { |
| if (entry->IsSupervised()) |
| return true; |
| } |
| return false; |
| } |
| |
| void CloseProfileWindows(Profile* profile) { |
| DCHECK(profile); |
| BrowserList::CloseAllBrowsersWithProfile( |
| profile, base::Bind(&ProfileBrowserCloseSuccess), |
| BrowserList::CloseCallback(), false); |
| } |
| |
| void CreateSystemProfileForUserManager( |
| const base::FilePath& profile_path_to_focus, |
| profiles::UserManagerAction user_manager_action, |
| const base::Callback<void(Profile*, const std::string&)>& callback) { |
| // Create the system profile, if necessary, and open the User Manager |
| // from the system profile. |
| g_browser_process->profile_manager()->CreateProfileAsync( |
| ProfileManager::GetSystemProfilePath(), |
| base::Bind(&OnUserManagerSystemProfileCreated, |
| profile_path_to_focus, |
| user_manager_action, |
| callback), |
| base::string16(), |
| std::string()); |
| } |
| |
| void BubbleViewModeFromAvatarBubbleMode(BrowserWindow::AvatarBubbleMode mode, |
| BubbleViewMode* bubble_view_mode) { |
| switch (mode) { |
| case BrowserWindow::AVATAR_BUBBLE_MODE_ACCOUNT_MANAGEMENT: |
| *bubble_view_mode = BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT; |
| return; |
| case BrowserWindow::AVATAR_BUBBLE_MODE_SIGNIN: |
| *bubble_view_mode = BUBBLE_VIEW_MODE_GAIA_SIGNIN; |
| return; |
| case BrowserWindow::AVATAR_BUBBLE_MODE_ADD_ACCOUNT: |
| *bubble_view_mode = BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT; |
| return; |
| case BrowserWindow::AVATAR_BUBBLE_MODE_REAUTH: |
| *bubble_view_mode = BUBBLE_VIEW_MODE_GAIA_REAUTH; |
| return; |
| case BrowserWindow::AVATAR_BUBBLE_MODE_CONFIRM_SIGNIN: |
| *bubble_view_mode = BUBBLE_VIEW_MODE_PROFILE_CHOOSER; |
| return; |
| case BrowserWindow::AVATAR_BUBBLE_MODE_SHOW_ERROR: |
| *bubble_view_mode = BUBBLE_VIEW_MODE_PROFILE_CHOOSER; |
| return; |
| default: |
| *bubble_view_mode = profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER; |
| } |
| } |
| |
| BrowserAddedForProfileObserver::BrowserAddedForProfileObserver( |
| Profile* profile, |
| ProfileManager::CreateCallback callback) |
| : profile_(profile), callback_(callback) { |
| DCHECK(!callback_.is_null()); |
| BrowserList::AddObserver(this); |
| } |
| |
| BrowserAddedForProfileObserver::~BrowserAddedForProfileObserver() {} |
| |
| void BrowserAddedForProfileObserver::OnBrowserAdded(Browser* browser) { |
| if (browser->profile() == profile_) { |
| BrowserList::RemoveObserver(this); |
| // By the time the browser is added a tab (or multiple) are about to be |
| // added. Post the callback to the message loop so it gets executed after |
| // the tabs are created. |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, base::BindOnce(callback_, profile_, |
| Profile::CREATE_STATUS_INITIALIZED)); |
| base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this); |
| } |
| } |
| |
| } // namespace profiles |