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
40using base::UserMetricsAction;
41using content::BrowserThread;
42
43namespace {
44
45const 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.
50class 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
80void 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|.
143void 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.
178void 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
187namespace profiles {
188
189// User Manager parameters are prefixed with hash.
190const char kUserManagerDisplayTutorial[] = "#tutorial";
191const char kUserManagerSelectProfileTaskManager[] = "#task-manager";
192const char kUserManagerSelectProfileAboutChrome[] = "#about-chrome";
193
194void 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
222void 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
240void 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
255void 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
275void 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
281void 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
292void 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
303void LockProfile(Profile* profile) {
304  DCHECK(profile);
305  if (profile) {
306    BrowserList::CloseAllBrowsersWithProfile(
307        profile, base::Bind(&LockBrowserCloseSuccess));
308  }
309}
310
311void 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
330void 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
343void 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
378void 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
389void 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