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