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