Home | History | Annotate | Download | only in profiles
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/profiles/profile_window.h"
      6 
      7 #include "base/command_line.h"
      8 #include "base/files/file_path.h"
      9 #include "base/prefs/pref_service.h"
     10 #include "base/strings/string_number_conversions.h"
     11 #include "base/strings/utf_string_conversions.h"
     12 #include "chrome/browser/about_flags.h"
     13 #include "chrome/browser/browser_process.h"
     14 #include "chrome/browser/lifetime/application_lifetime.h"
     15 #include "chrome/browser/pref_service_flags_storage.h"
     16 #include "chrome/browser/profiles/profile.h"
     17 #include "chrome/browser/profiles/profile_avatar_icon_util.h"
     18 #include "chrome/browser/profiles/profile_manager.h"
     19 #include "chrome/browser/signin/account_reconcilor_factory.h"
     20 #include "chrome/browser/ui/browser.h"
     21 #include "chrome/browser/ui/browser_dialogs.h"
     22 #include "chrome/common/chrome_switches.h"
     23 #include "chrome/common/pref_names.h"
     24 #include "chrome/common/url_constants.h"
     25 #include "components/signin/core/browser/account_reconcilor.h"
     26 #include "components/signin/core/common/profile_management_switches.h"
     27 #include "content/public/browser/browser_thread.h"
     28 #include "content/public/browser/user_metrics.h"
     29 
     30 #if !defined(OS_IOS)
     31 #include "chrome/browser/ui/browser_finder.h"
     32 #include "chrome/browser/ui/browser_list.h"
     33 #include "chrome/browser/ui/browser_list_observer.h"
     34 #include "chrome/browser/ui/browser_window.h"
     35 #include "chrome/browser/ui/startup/startup_browser_creator.h"
     36 #endif  // !defined (OS_IOS)
     37 
     38 using base::UserMetricsAction;
     39 using content::BrowserThread;
     40 
     41 namespace {
     42 
     43 const char kNewProfileManagementExperimentInternalName[] =
     44     "enable-new-profile-management";
     45 
     46 // Handles running a callback when a new Browser for the given profile
     47 // has been completely created.
     48 class BrowserAddedForProfileObserver : public chrome::BrowserListObserver {
     49  public:
     50   BrowserAddedForProfileObserver(
     51       Profile* profile,
     52       profiles::ProfileSwitchingDoneCallback callback)
     53       : profile_(profile),
     54         callback_(callback) {
     55     DCHECK(!callback_.is_null());
     56     BrowserList::AddObserver(this);
     57   }
     58   virtual ~BrowserAddedForProfileObserver() {
     59   }
     60 
     61  private:
     62   // Overridden from BrowserListObserver:
     63   virtual void OnBrowserAdded(Browser* browser) OVERRIDE {
     64     if (browser->profile() == profile_) {
     65       BrowserList::RemoveObserver(this);
     66       callback_.Run();
     67       base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
     68     }
     69   }
     70 
     71   // Profile for which the browser should be opened.
     72   Profile* profile_;
     73   profiles::ProfileSwitchingDoneCallback callback_;
     74 
     75   DISALLOW_COPY_AND_ASSIGN(BrowserAddedForProfileObserver);
     76 };
     77 
     78 void OpenBrowserWindowForProfile(
     79     profiles::ProfileSwitchingDoneCallback callback,
     80     bool always_create,
     81     bool is_new_profile,
     82     chrome::HostDesktopType desktop_type,
     83     Profile* profile,
     84     Profile::CreateStatus status) {
     85   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     86 
     87   if (status != Profile::CREATE_STATUS_INITIALIZED)
     88     return;
     89 
     90   chrome::startup::IsProcessStartup is_process_startup =
     91       chrome::startup::IS_NOT_PROCESS_STARTUP;
     92   chrome::startup::IsFirstRun is_first_run = chrome::startup::IS_NOT_FIRST_RUN;
     93 
     94   // If this is a brand new profile, then start a first run window.
     95   if (is_new_profile) {
     96     is_process_startup = chrome::startup::IS_PROCESS_STARTUP;
     97     is_first_run = chrome::startup::IS_FIRST_RUN;
     98   }
     99 
    100   // If |always_create| is false, and we have a |callback| to run, check
    101   // whether a browser already exists so that we can run the callback. We don't
    102   // want to rely on the observer listening to OnBrowserSetLastActive in this
    103   // case, as you could manually activate an incorrect browser and trigger
    104   // a false positive.
    105   if (!always_create) {
    106     Browser* browser = chrome::FindTabbedBrowser(profile, false, desktop_type);
    107     if (browser) {
    108       browser->window()->Activate();
    109       if (!callback.is_null())
    110         callback.Run();
    111       return;
    112     }
    113   }
    114 
    115   // If there is a callback, create an observer to make sure it is only
    116   // run when the browser has been completely created. This observer will
    117   // delete itself once that happens. This should not leak, because we are
    118   // passing |always_create| = true to FindOrCreateNewWindow below, which ends
    119   // up calling LaunchBrowser and opens a new window. If for whatever reason
    120   // that fails, either something has crashed, or the observer will be cleaned
    121   // up when a different browser for this profile is opened.
    122   if (!callback.is_null())
    123     new BrowserAddedForProfileObserver(profile, callback);
    124 
    125   // We already dealt with the case when |always_create| was false and a browser
    126   // existed, which means that here a browser definitely needs to be created.
    127   // Passing true for |always_create| means we won't duplicate the code that
    128   // tries to find a browser.
    129   profiles::FindOrCreateNewWindowForProfile(
    130       profile,
    131       is_process_startup,
    132       is_first_run,
    133       desktop_type,
    134       true);
    135 }
    136 
    137 // Called after a |guest_profile| is available to be used by the user manager.
    138 // Based on the value of |tutorial_mode| we determine a url to be displayed
    139 // by the webui and run the |callback|, if it exists.
    140 void OnUserManagerGuestProfileCreated(
    141     const base::FilePath& profile_path_to_focus,
    142     profiles::UserManagerTutorialMode tutorial_mode,
    143     const base::Callback<void(Profile*, const std::string&)>& callback,
    144     Profile* guest_profile,
    145     Profile::CreateStatus status) {
    146   if (status != Profile::CREATE_STATUS_INITIALIZED || callback.is_null())
    147     return;
    148 
    149   // Tell the webui which user should be focused.
    150   std::string page = chrome::kChromeUIUserManagerURL;
    151 
    152   if (tutorial_mode == profiles::USER_MANAGER_TUTORIAL_OVERVIEW) {
    153     page += "#tutorial";
    154   } else if (!profile_path_to_focus.empty()) {
    155     const ProfileInfoCache& cache =
    156         g_browser_process->profile_manager()->GetProfileInfoCache();
    157     size_t index = cache.GetIndexOfProfileWithPath(profile_path_to_focus);
    158     if (index != std::string::npos) {
    159       page += "#";
    160       page += base::IntToString(index);
    161     }
    162   }
    163 
    164   callback.Run(guest_profile, page);
    165 }
    166 
    167 // Updates Chrome services that require notification when
    168 // the new_profile_management's status changes.
    169 void UpdateServicesWithNewProfileManagementFlag(Profile* profile,
    170                                                 bool new_flag_status) {
    171   AccountReconcilor* account_reconcilor =
    172       AccountReconcilorFactory::GetForProfile(profile);
    173   account_reconcilor->OnNewProfileManagementFlagChanged(new_flag_status);
    174 }
    175 
    176 }  // namespace
    177 
    178 namespace profiles {
    179 
    180 void FindOrCreateNewWindowForProfile(
    181     Profile* profile,
    182     chrome::startup::IsProcessStartup process_startup,
    183     chrome::startup::IsFirstRun is_first_run,
    184     chrome::HostDesktopType desktop_type,
    185     bool always_create) {
    186 #if defined(OS_IOS)
    187   NOTREACHED();
    188 #else
    189   DCHECK(profile);
    190 
    191   if (!always_create) {
    192     Browser* browser = chrome::FindTabbedBrowser(profile, false, desktop_type);
    193     if (browser) {
    194       browser->window()->Activate();
    195       return;
    196     }
    197   }
    198 
    199   content::RecordAction(UserMetricsAction("NewWindow"));
    200   CommandLine command_line(CommandLine::NO_PROGRAM);
    201   int return_code;
    202   StartupBrowserCreator browser_creator;
    203   browser_creator.LaunchBrowser(command_line, profile, base::FilePath(),
    204                                 process_startup, is_first_run, &return_code);
    205 #endif  // defined(OS_IOS)
    206 }
    207 
    208 void SwitchToProfile(const base::FilePath& path,
    209                      chrome::HostDesktopType desktop_type,
    210                      bool always_create,
    211                      ProfileSwitchingDoneCallback callback,
    212                      ProfileMetrics::ProfileOpen metric) {
    213   g_browser_process->profile_manager()->CreateProfileAsync(
    214       path,
    215       base::Bind(&OpenBrowserWindowForProfile,
    216                  callback,
    217                  always_create,
    218                  false,
    219                  desktop_type),
    220       base::string16(),
    221       base::string16(),
    222       std::string());
    223   ProfileMetrics::LogProfileSwitchUser(metric);
    224 }
    225 
    226 void SwitchToGuestProfile(chrome::HostDesktopType desktop_type,
    227                           ProfileSwitchingDoneCallback callback) {
    228   g_browser_process->profile_manager()->CreateProfileAsync(
    229       ProfileManager::GetGuestProfilePath(),
    230       base::Bind(&OpenBrowserWindowForProfile,
    231                  callback,
    232                  false,
    233                  false,
    234                  desktop_type),
    235       base::string16(),
    236       base::string16(),
    237       std::string());
    238   ProfileMetrics::LogProfileSwitchUser(ProfileMetrics::SWITCH_PROFILE_GUEST);
    239 }
    240 
    241 void CreateAndSwitchToNewProfile(chrome::HostDesktopType desktop_type,
    242                                  ProfileSwitchingDoneCallback callback,
    243                                  ProfileMetrics::ProfileAdd metric) {
    244   ProfileInfoCache& cache =
    245       g_browser_process->profile_manager()->GetProfileInfoCache();
    246 
    247   int placeholder_avatar_index = profiles::GetPlaceholderAvatarIndex();
    248   ProfileManager::CreateMultiProfileAsync(
    249       cache.ChooseNameForNewProfile(placeholder_avatar_index),
    250       base::UTF8ToUTF16(profiles::GetDefaultAvatarIconUrl(
    251           placeholder_avatar_index)),
    252       base::Bind(&OpenBrowserWindowForProfile,
    253                  callback,
    254                  true,
    255                  true,
    256                  desktop_type),
    257       std::string());
    258   ProfileMetrics::LogProfileAddNewUser(metric);
    259 }
    260 
    261 void CloseGuestProfileWindows() {
    262   ProfileManager* profile_manager = g_browser_process->profile_manager();
    263   Profile* profile = profile_manager->GetProfileByPath(
    264       ProfileManager::GetGuestProfilePath());
    265 
    266   if (profile) {
    267     BrowserList::CloseAllBrowsersWithProfile(profile);
    268   }
    269 }
    270 
    271 void LockProfile(Profile* profile) {
    272   DCHECK(profile);
    273   ProfileInfoCache& cache =
    274       g_browser_process->profile_manager()->GetProfileInfoCache();
    275 
    276   size_t index = cache.GetIndexOfProfileWithPath(profile->GetPath());
    277   cache.SetProfileSigninRequiredAtIndex(index, true);
    278   chrome::ShowUserManager(profile->GetPath());
    279   BrowserList::CloseAllBrowsersWithProfile(profile);
    280 }
    281 
    282 void CreateGuestProfileForUserManager(
    283     const base::FilePath& profile_path_to_focus,
    284     profiles::UserManagerTutorialMode tutorial_mode,
    285     const base::Callback<void(Profile*, const std::string&)>& callback) {
    286   // Create the guest profile, if necessary, and open the User Manager
    287   // from the guest profile.
    288   g_browser_process->profile_manager()->CreateProfileAsync(
    289       ProfileManager::GetGuestProfilePath(),
    290       base::Bind(&OnUserManagerGuestProfileCreated,
    291                  profile_path_to_focus,
    292                  tutorial_mode,
    293                  callback),
    294       base::string16(),
    295       base::string16(),
    296       std::string());
    297 }
    298 
    299 void ShowUserManagerMaybeWithTutorial(Profile* profile) {
    300   // Guest users cannot appear in the User Manager, nor display a tutorial.
    301   if (!profile || profile->IsGuestSession()) {
    302     chrome::ShowUserManager(base::FilePath());
    303     return;
    304   }
    305   // Show the tutorial if the profile has not shown it before.
    306   PrefService* pref_service = profile->GetPrefs();
    307   bool tutorial_shown = pref_service->GetBoolean(
    308       prefs::kProfileUserManagerTutorialShown);
    309   if (!tutorial_shown)
    310     pref_service->SetBoolean(prefs::kProfileUserManagerTutorialShown, true);
    311 
    312   if (tutorial_shown) {
    313     chrome::ShowUserManager(profile->GetPath());
    314   } else {
    315     chrome::ShowUserManagerWithTutorial(
    316         profiles::USER_MANAGER_TUTORIAL_OVERVIEW);
    317   }
    318 }
    319 
    320 void EnableNewProfileManagementPreview(Profile* profile) {
    321 #if defined(OS_ANDROID)
    322   NOTREACHED();
    323 #else
    324   // TODO(rogerta): instead of setting experiment flags and command line
    325   // args, we should set a profile preference.
    326   const about_flags::Experiment experiment = {
    327       kNewProfileManagementExperimentInternalName,
    328       0,  // string id for title of experiment
    329       0,  // string id for description of experiment
    330       0,  // supported platforms
    331       about_flags::Experiment::ENABLE_DISABLE_VALUE,
    332       switches::kEnableNewProfileManagement,
    333       "",  // not used with ENABLE_DISABLE_VALUE type
    334       switches::kDisableNewProfileManagement,
    335       "",  // not used with ENABLE_DISABLE_VALUE type
    336       NULL,  // not used with ENABLE_DISABLE_VALUE type
    337       3
    338   };
    339   about_flags::PrefServiceFlagsStorage flags_storage(
    340       g_browser_process->local_state());
    341   about_flags::SetExperimentEnabled(
    342       &flags_storage,
    343       experiment.NameForChoice(1),
    344       true);
    345 
    346   switches::EnableNewProfileManagementForTesting(
    347       CommandLine::ForCurrentProcess());
    348   chrome::ShowUserManagerWithTutorial(profiles::USER_MANAGER_TUTORIAL_OVERVIEW);
    349   UpdateServicesWithNewProfileManagementFlag(profile, true);
    350 #endif
    351 }
    352 
    353 void DisableNewProfileManagementPreview(Profile* profile) {
    354   about_flags::PrefServiceFlagsStorage flags_storage(
    355       g_browser_process->local_state());
    356   about_flags::SetExperimentEnabled(
    357       &flags_storage,
    358       kNewProfileManagementExperimentInternalName,
    359       false);
    360   chrome::AttemptRestart();
    361   UpdateServicesWithNewProfileManagementFlag(profile, false);
    362 }
    363 
    364 }  // namespace profiles
    365