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:
BrowserAddedForProfileObserver(Profile * profile,ProfileManager::CreateCallback callback)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 }
~BrowserAddedForProfileObserver()60 virtual ~BrowserAddedForProfileObserver() {
61 }
62
63 private:
64 // Overridden from BrowserListObserver:
OnBrowserAdded(Browser * browser)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
OpenBrowserWindowForProfile(ProfileManager::CreateCallback callback,bool always_create,bool is_new_profile,chrome::HostDesktopType desktop_type,Profile * profile,Profile::CreateStatus status)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|.
OnUserManagerGuestProfileCreated(const base::FilePath & profile_path_to_focus,profiles::UserManagerTutorialMode tutorial_mode,profiles::UserManagerProfileSelected profile_open_action,const base::Callback<void (Profile *,const std::string &)> & callback,Profile * guest_profile,Profile::CreateStatus status)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.
UpdateServicesWithNewProfileManagementFlag(Profile * profile,bool new_flag_status)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
FindOrCreateNewWindowForProfile(Profile * profile,chrome::startup::IsProcessStartup process_startup,chrome::startup::IsFirstRun is_first_run,chrome::HostDesktopType desktop_type,bool always_create)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
SwitchToProfile(const base::FilePath & path,chrome::HostDesktopType desktop_type,bool always_create,ProfileManager::CreateCallback callback,ProfileMetrics::ProfileOpen metric)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
SwitchToGuestProfile(chrome::HostDesktopType desktop_type,ProfileManager::CreateCallback callback)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
CreateAndSwitchToNewProfile(chrome::HostDesktopType desktop_type,ProfileManager::CreateCallback callback,ProfileMetrics::ProfileAdd metric)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
GuestBrowserCloseSuccess(const base::FilePath & profile_path)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
CloseGuestProfileWindows()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
LockBrowserCloseSuccess(const base::FilePath & profile_path)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
LockProfile(Profile * profile)303 void LockProfile(Profile* profile) {
304 DCHECK(profile);
305 if (profile) {
306 BrowserList::CloseAllBrowsersWithProfile(
307 profile, base::Bind(&LockBrowserCloseSuccess));
308 }
309 }
310
CreateGuestProfileForUserManager(const base::FilePath & profile_path_to_focus,profiles::UserManagerTutorialMode tutorial_mode,profiles::UserManagerProfileSelected profile_open_action,const base::Callback<void (Profile *,const std::string &)> & callback)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
ShowUserManagerMaybeWithTutorial(Profile * profile)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
EnableNewProfileManagementPreview(Profile * profile)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
DisableNewProfileManagementPreview(Profile * profile)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
BubbleViewModeFromAvatarBubbleMode(BrowserWindow::AvatarBubbleMode mode,BubbleViewMode * bubble_view_mode,TutorialMode * tutorial_mode)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