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:
BrowserAddedForProfileObserver(Profile * profile,profiles::ProfileSwitchingDoneCallback callback)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 }
~BrowserAddedForProfileObserver()58 virtual ~BrowserAddedForProfileObserver() {
59 }
60
61 private:
62 // Overridden from BrowserListObserver:
OnBrowserAdded(Browser * browser)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
OpenBrowserWindowForProfile(profiles::ProfileSwitchingDoneCallback callback,bool always_create,bool is_new_profile,chrome::HostDesktopType desktop_type,Profile * profile,Profile::CreateStatus status)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.
OnUserManagerGuestProfileCreated(const base::FilePath & profile_path_to_focus,profiles::UserManagerTutorialMode tutorial_mode,const base::Callback<void (Profile *,const std::string &)> & callback,Profile * guest_profile,Profile::CreateStatus status)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.
UpdateServicesWithNewProfileManagementFlag(Profile * profile,bool new_flag_status)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
FindOrCreateNewWindowForProfile(Profile * profile,chrome::startup::IsProcessStartup process_startup,chrome::startup::IsFirstRun is_first_run,chrome::HostDesktopType desktop_type,bool always_create)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
SwitchToProfile(const base::FilePath & path,chrome::HostDesktopType desktop_type,bool always_create,ProfileSwitchingDoneCallback callback,ProfileMetrics::ProfileOpen metric)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
SwitchToGuestProfile(chrome::HostDesktopType desktop_type,ProfileSwitchingDoneCallback callback)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
CreateAndSwitchToNewProfile(chrome::HostDesktopType desktop_type,ProfileSwitchingDoneCallback callback,ProfileMetrics::ProfileAdd metric)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
CloseGuestProfileWindows()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
LockProfile(Profile * profile)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
CreateGuestProfileForUserManager(const base::FilePath & profile_path_to_focus,profiles::UserManagerTutorialMode tutorial_mode,const base::Callback<void (Profile *,const std::string &)> & callback)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
ShowUserManagerMaybeWithTutorial(Profile * profile)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
EnableNewProfileManagementPreview(Profile * profile)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
DisableNewProfileManagementPreview(Profile * profile)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