manage_profile_handler.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
1// Copyright (c) 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/ui/webui/options/manage_profile_handler.h"
6
7#include "base/bind.h"
8#include "base/bind_helpers.h"
9#include "base/command_line.h"
10#include "base/prefs/pref_service.h"
11#include "base/strings/string_number_conversions.h"
12#include "base/strings/utf_string_conversions.h"
13#include "base/value_conversions.h"
14#include "base/values.h"
15#include "chrome/browser/browser_process.h"
16#include "chrome/browser/profiles/gaia_info_update_service.h"
17#include "chrome/browser/profiles/profile.h"
18#include "chrome/browser/profiles/profile_info_cache.h"
19#include "chrome/browser/profiles/profile_info_util.h"
20#include "chrome/browser/profiles/profile_manager.h"
21#include "chrome/browser/profiles/profile_metrics.h"
22#include "chrome/browser/profiles/profile_shortcut_manager.h"
23#include "chrome/browser/signin/signin_manager.h"
24#include "chrome/browser/signin/signin_manager_factory.h"
25#include "chrome/browser/ui/browser_finder.h"
26#include "chrome/common/chrome_notification_types.h"
27#include "chrome/common/chrome_switches.h"
28#include "chrome/common/pref_names.h"
29#include "content/public/browser/browser_thread.h"
30#include "content/public/browser/notification_service.h"
31#include "content/public/browser/web_ui.h"
32#include "grit/generated_resources.h"
33#include "ui/base/l10n/l10n_util.h"
34#include "ui/webui/web_ui_util.h"
35
36#if defined(ENABLE_MANAGED_USERS)
37#include "chrome/browser/managed_mode/managed_user_service.h"
38#include "chrome/browser/managed_mode/managed_user_service_factory.h"
39#endif
40
41#if defined(ENABLE_SETTINGS_APP)
42#include "chrome/browser/ui/app_list/app_list_service.h"
43#include "content/public/browser/web_contents.h"
44#endif
45
46namespace options {
47
48namespace {
49
50const char kCreateProfileIconGridName[] = "create-profile-icon-grid";
51const char kManageProfileIconGridName[] = "manage-profile-icon-grid";
52
53// Given |args| from the WebUI, parses value 0 as a FilePath |profile_file_path|
54// and returns true on success.
55bool GetProfilePathFromArgs(const ListValue* args,
56                            base::FilePath* profile_file_path) {
57  const Value* file_path_value;
58  if (!args->Get(0, &file_path_value))
59    return false;
60  return base::GetValueAsFilePath(*file_path_value, profile_file_path);
61}
62
63void OnNewDefaultProfileCreated(
64    chrome::HostDesktopType desktop_type,
65    Profile* profile,
66    Profile::CreateStatus status) {
67  if (status == Profile::CREATE_STATUS_INITIALIZED) {
68    ProfileManager::FindOrCreateNewWindowForProfile(
69      profile,
70      chrome::startup::IS_PROCESS_STARTUP,
71      chrome::startup::IS_FIRST_RUN,
72      desktop_type,
73      false);
74  }
75}
76
77}  // namespace
78
79ManageProfileHandler::ManageProfileHandler()
80    : weak_factory_(this) {
81}
82
83ManageProfileHandler::~ManageProfileHandler() {
84}
85
86void ManageProfileHandler::GetLocalizedValues(
87    DictionaryValue* localized_strings) {
88  DCHECK(localized_strings);
89
90  static OptionsStringResource resources[] = {
91    { "manageProfilesNameLabel", IDS_PROFILES_MANAGE_NAME_LABEL },
92    { "manageProfilesDuplicateNameError",
93        IDS_PROFILES_MANAGE_DUPLICATE_NAME_ERROR },
94    { "manageProfilesIconLabel", IDS_PROFILES_MANAGE_ICON_LABEL },
95    { "manageProfilesManagedSignedInLabel",
96    IDS_PROFILES_CREATE_MANAGED_SIGNED_IN_LABEL },
97    { "manageProfilesManagedNotSignedInLabel",
98        IDS_PROFILES_CREATE_MANAGED_NOT_SIGNED_IN_LABEL },
99    { "manageProfilesManagedNotSignedInLink",
100        IDS_PROFILES_CREATE_MANAGED_NOT_SIGNED_IN_LINK },
101    { "deleteProfileTitle", IDS_PROFILES_DELETE_TITLE },
102    { "deleteProfileOK", IDS_PROFILES_DELETE_OK_BUTTON_LABEL },
103    { "deleteProfileMessage", IDS_PROFILES_DELETE_MESSAGE },
104    { "deleteManagedProfileAddendum", IDS_PROFILES_DELETE_MANAGED_ADDENDUM },
105    { "createProfileTitle", IDS_PROFILES_CREATE_TITLE },
106    { "createProfileInstructions", IDS_PROFILES_CREATE_INSTRUCTIONS },
107    { "createProfileConfirm", IDS_PROFILES_CREATE_CONFIRM },
108    { "createProfileLocalError", IDS_PROFILES_CREATE_LOCAL_ERROR },
109    { "createProfileRemoteError", IDS_PROFILES_CREATE_REMOTE_ERROR },
110    { "createProfileShortcutCheckbox", IDS_PROFILES_CREATE_SHORTCUT_CHECKBOX },
111    { "createProfileShortcutButton", IDS_PROFILES_CREATE_SHORTCUT_BUTTON },
112    { "removeProfileShortcutButton", IDS_PROFILES_REMOVE_SHORTCUT_BUTTON },
113  };
114
115  RegisterStrings(localized_strings, resources, arraysize(resources));
116  RegisterTitle(localized_strings, "manageProfile",
117                IDS_PROFILES_MANAGE_TITLE);
118  RegisterTitle(localized_strings, "createProfile",
119                IDS_PROFILES_CREATE_TITLE);
120
121  localized_strings->SetBoolean("profileShortcutsEnabled",
122                                ProfileShortcutManager::IsFeatureEnabled());
123  localized_strings->SetBoolean("managedUsersEnabled",
124                                ManagedUserService::AreManagedUsersEnabled());
125}
126
127void ManageProfileHandler::InitializeHandler() {
128  registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED,
129                 content::NotificationService::AllSources());
130
131  pref_change_registrar_.Init(Profile::FromWebUI(web_ui())->GetPrefs());
132  pref_change_registrar_.Add(
133      prefs::kManagedUserCreationAllowed,
134      base::Bind(&ManageProfileHandler::OnCreateManagedUserPrefChange,
135                 base::Unretained(this)));
136}
137
138void ManageProfileHandler::InitializePage() {
139  SendProfileNames();
140  OnCreateManagedUserPrefChange();
141}
142
143void ManageProfileHandler::RegisterMessages() {
144  web_ui()->RegisterMessageCallback("setProfileNameAndIcon",
145      base::Bind(&ManageProfileHandler::SetProfileNameAndIcon,
146                 base::Unretained(this)));
147  web_ui()->RegisterMessageCallback("requestDefaultProfileIcons",
148      base::Bind(&ManageProfileHandler::RequestDefaultProfileIcons,
149                 base::Unretained(this)));
150  web_ui()->RegisterMessageCallback("requestNewProfileDefaults",
151      base::Bind(&ManageProfileHandler::RequestNewProfileDefaults,
152                 base::Unretained(this)));
153  web_ui()->RegisterMessageCallback("requestHasProfileShortcuts",
154      base::Bind(&ManageProfileHandler::RequestHasProfileShortcuts,
155                 base::Unretained(this)));
156  web_ui()->RegisterMessageCallback("requestCreateProfileUpdate",
157      base::Bind(&ManageProfileHandler::RequestCreateProfileUpdate,
158                 base::Unretained(this)));
159  web_ui()->RegisterMessageCallback("profileIconSelectionChanged",
160      base::Bind(&ManageProfileHandler::ProfileIconSelectionChanged,
161                 base::Unretained(this)));
162#if defined(ENABLE_SETTINGS_APP)
163  web_ui()->RegisterMessageCallback("switchAppListProfile",
164      base::Bind(&ManageProfileHandler::SwitchAppListProfile,
165                 base::Unretained(this)));
166#endif
167  web_ui()->RegisterMessageCallback("addProfileShortcut",
168      base::Bind(&ManageProfileHandler::AddProfileShortcut,
169                 base::Unretained(this)));
170  web_ui()->RegisterMessageCallback("removeProfileShortcut",
171      base::Bind(&ManageProfileHandler::RemoveProfileShortcut,
172                 base::Unretained(this)));
173}
174
175void ManageProfileHandler::Observe(
176    int type,
177    const content::NotificationSource& source,
178    const content::NotificationDetails& details) {
179  if (type == chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED) {
180    SendProfileNames();
181    base::StringValue value(kManageProfileIconGridName);
182    SendProfileIcons(value);
183  } else {
184    OptionsPageUIHandler::Observe(type, source, details);
185  }
186}
187
188void ManageProfileHandler::RequestDefaultProfileIcons(const ListValue* args) {
189  base::StringValue create_value(kCreateProfileIconGridName);
190  base::StringValue manage_value(kManageProfileIconGridName);
191  SendProfileIcons(manage_value);
192  SendProfileIcons(create_value);
193}
194
195void ManageProfileHandler::RequestNewProfileDefaults(const ListValue* args) {
196  const ProfileInfoCache& cache =
197      g_browser_process->profile_manager()->GetProfileInfoCache();
198  const size_t icon_index = cache.ChooseAvatarIconIndexForNewProfile();
199
200  DictionaryValue profile_info;
201  profile_info.SetString("name", cache.ChooseNameForNewProfile(icon_index));
202  profile_info.SetString("iconURL", cache.GetDefaultAvatarIconUrl(icon_index));
203
204  web_ui()->CallJavascriptFunction(
205      "ManageProfileOverlay.receiveNewProfileDefaults", profile_info);
206}
207
208void ManageProfileHandler::SendProfileIcons(
209    const base::StringValue& icon_grid) {
210  ListValue image_url_list;
211
212  // First add the GAIA picture if it's available.
213  const ProfileInfoCache& cache =
214      g_browser_process->profile_manager()->GetProfileInfoCache();
215  Profile* profile = Profile::FromWebUI(web_ui());
216  size_t profile_index = cache.GetIndexOfProfileWithPath(profile->GetPath());
217  if (profile_index != std::string::npos) {
218    const gfx::Image* icon =
219        cache.GetGAIAPictureOfProfileAtIndex(profile_index);
220    if (icon) {
221      gfx::Image icon2 = profiles::GetAvatarIconForWebUI(*icon, true);
222      gaia_picture_url_ = webui::GetBitmapDataUrl(icon2.AsBitmap());
223      image_url_list.Append(new base::StringValue(gaia_picture_url_));
224    }
225  }
226
227  // Next add the default avatar icons.
228  for (size_t i = 0; i < ProfileInfoCache::GetDefaultAvatarIconCount(); i++) {
229    std::string url = ProfileInfoCache::GetDefaultAvatarIconUrl(i);
230    image_url_list.Append(new base::StringValue(url));
231  }
232
233  web_ui()->CallJavascriptFunction(
234      "ManageProfileOverlay.receiveDefaultProfileIcons", icon_grid,
235      image_url_list);
236}
237
238void ManageProfileHandler::SendProfileNames() {
239  const ProfileInfoCache& cache =
240      g_browser_process->profile_manager()->GetProfileInfoCache();
241  DictionaryValue profile_name_dict;
242  for (size_t i = 0, e = cache.GetNumberOfProfiles(); i < e; ++i)
243    profile_name_dict.SetBoolean(UTF16ToUTF8(cache.GetNameOfProfileAtIndex(i)),
244                                 true);
245
246  web_ui()->CallJavascriptFunction("ManageProfileOverlay.receiveProfileNames",
247                                   profile_name_dict);
248}
249
250void ManageProfileHandler::SetProfileNameAndIcon(const ListValue* args) {
251  DCHECK(args);
252
253  base::FilePath profile_file_path;
254  if (!GetProfilePathFromArgs(args, &profile_file_path))
255    return;
256
257  ProfileInfoCache& cache =
258      g_browser_process->profile_manager()->GetProfileInfoCache();
259  size_t profile_index = cache.GetIndexOfProfileWithPath(profile_file_path);
260  if (profile_index == std::string::npos)
261    return;
262
263  Profile* profile =
264      g_browser_process->profile_manager()->GetProfile(profile_file_path);
265  if (!profile)
266    return;
267
268  string16 new_profile_name;
269  if (!args->GetString(1, &new_profile_name))
270    return;
271  if (new_profile_name == cache.GetGAIANameOfProfileAtIndex(profile_index)) {
272    // Set the profile to use the GAIA name as the profile name. Note, this
273    // is a little weird if the user typed their GAIA name manually but
274    // it's not a big deal.
275    cache.SetIsUsingGAIANameOfProfileAtIndex(profile_index, true);
276    // Using the GAIA name as the profile name can invalidate the profile index.
277    profile_index = cache.GetIndexOfProfileWithPath(profile_file_path);
278    if (profile_index == std::string::npos)
279      return;
280  } else {
281    PrefService* pref_service = profile->GetPrefs();
282    // Updating the profile preference will cause the cache to be updated for
283    // this preference.
284    pref_service->SetString(prefs::kProfileName, UTF16ToUTF8(new_profile_name));
285
286    // Changing the profile name can invalidate the profile index.
287    profile_index = cache.GetIndexOfProfileWithPath(profile_file_path);
288    if (profile_index == std::string::npos)
289      return;
290
291    cache.SetIsUsingGAIANameOfProfileAtIndex(profile_index, false);
292    // Unsetting the GAIA name as the profile name can invalidate the profile
293    // index.
294    profile_index = cache.GetIndexOfProfileWithPath(profile_file_path);
295    if (profile_index == std::string::npos)
296      return;
297  }
298
299  std::string icon_url;
300  if (!args->GetString(2, &icon_url))
301    return;
302
303  // Metrics logging variable.
304  bool previously_using_gaia_icon =
305      cache.IsUsingGAIANameOfProfileAtIndex(profile_index);
306
307  size_t new_icon_index;
308  if (icon_url == gaia_picture_url_) {
309    cache.SetIsUsingGAIAPictureOfProfileAtIndex(profile_index, true);
310    if (!previously_using_gaia_icon) {
311      // Only log if they changed to the GAIA photo.
312      // Selection of GAIA photo as avatar is logged as part of the function
313      // below.
314      ProfileMetrics::LogProfileSwitchGaia(ProfileMetrics::GAIA_OPT_IN);
315    }
316  } else if (cache.IsDefaultAvatarIconUrl(icon_url, &new_icon_index)) {
317    ProfileMetrics::LogProfileAvatarSelection(new_icon_index);
318    PrefService* pref_service = profile->GetPrefs();
319    // Updating the profile preference will cause the cache to be updated for
320    // this preference.
321    pref_service->SetInteger(prefs::kProfileAvatarIndex, new_icon_index);
322    cache.SetIsUsingGAIAPictureOfProfileAtIndex(profile_index, false);
323  }
324  ProfileMetrics::LogProfileUpdate(profile_file_path);
325}
326
327#if defined(ENABLE_SETTINGS_APP)
328void ManageProfileHandler::SwitchAppListProfile(const ListValue* args) {
329  DCHECK(args);
330  DCHECK(ProfileManager::IsMultipleProfilesEnabled());
331
332  const Value* file_path_value;
333  base::FilePath profile_file_path;
334  if (!args->Get(0, &file_path_value) ||
335      !base::GetValueAsFilePath(*file_path_value, &profile_file_path))
336    return;
337
338  AppListService::Get()->SetAppListProfile(profile_file_path);
339  // Close the settings app, since it will now be for the wrong profile.
340  web_ui()->GetWebContents()->Close();
341}
342#endif  // defined(ENABLE_SETTINGS_APP)
343
344void ManageProfileHandler::ProfileIconSelectionChanged(
345    const base::ListValue* args) {
346  DCHECK(args);
347
348  base::FilePath profile_file_path;
349  if (!GetProfilePathFromArgs(args, &profile_file_path))
350    return;
351
352  // Currently this only supports editing the current profile's info.
353  if (profile_file_path != Profile::FromWebUI(web_ui())->GetPath())
354    return;
355
356  std::string icon_url;
357  if (!args->GetString(1, &icon_url))
358    return;
359
360  if (icon_url != gaia_picture_url_)
361    return;
362
363  // If the selection is the GAIA picture then also show the GAIA name in the
364  // text field.
365  ProfileInfoCache& cache =
366      g_browser_process->profile_manager()->GetProfileInfoCache();
367  size_t profile_index = cache.GetIndexOfProfileWithPath(profile_file_path);
368  if (profile_index == std::string::npos)
369    return;
370  string16 gaia_name = cache.GetGAIANameOfProfileAtIndex(profile_index);
371  if (gaia_name.empty())
372    return;
373
374  StringValue gaia_name_value(gaia_name);
375  web_ui()->CallJavascriptFunction("ManageProfileOverlay.setProfileName",
376                                   gaia_name_value);
377}
378
379void ManageProfileHandler::RequestHasProfileShortcuts(const ListValue* args) {
380  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
381  DCHECK(ProfileShortcutManager::IsFeatureEnabled());
382
383  base::FilePath profile_file_path;
384  if (!GetProfilePathFromArgs(args, &profile_file_path))
385    return;
386
387  const ProfileInfoCache& cache =
388      g_browser_process->profile_manager()->GetProfileInfoCache();
389  size_t profile_index = cache.GetIndexOfProfileWithPath(profile_file_path);
390  if (profile_index == std::string::npos)
391    return;
392
393  const base::FilePath profile_path =
394      cache.GetPathOfProfileAtIndex(profile_index);
395  ProfileShortcutManager* shortcut_manager =
396      g_browser_process->profile_manager()->profile_shortcut_manager();
397  shortcut_manager->HasProfileShortcuts(
398      profile_path, base::Bind(&ManageProfileHandler::OnHasProfileShortcuts,
399                               weak_factory_.GetWeakPtr()));
400}
401
402void ManageProfileHandler::RequestCreateProfileUpdate(
403    const base::ListValue* args) {
404  SigninManagerBase* manager =
405      SigninManagerFactory::GetForProfile(Profile::FromWebUI(web_ui()));
406  string16 username = UTF8ToUTF16(manager->GetAuthenticatedUsername());
407  StringValue username_value(username);
408  web_ui()->CallJavascriptFunction("CreateProfileOverlay.updateSignedInStatus",
409                                   username_value);
410
411  OnCreateManagedUserPrefChange();
412}
413
414void ManageProfileHandler::OnCreateManagedUserPrefChange() {
415  PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs();
416  base::FundamentalValue allowed(
417      prefs->GetBoolean(prefs::kManagedUserCreationAllowed));
418  web_ui()->CallJavascriptFunction(
419      "CreateProfileOverlay.updateManagedUsersAllowed", allowed);
420}
421
422void ManageProfileHandler::OnHasProfileShortcuts(bool has_shortcuts) {
423  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
424
425  const base::FundamentalValue has_shortcuts_value(has_shortcuts);
426  web_ui()->CallJavascriptFunction(
427      "ManageProfileOverlay.receiveHasProfileShortcuts", has_shortcuts_value);
428}
429
430void ManageProfileHandler::AddProfileShortcut(const base::ListValue* args) {
431  base::FilePath profile_file_path;
432  if (!GetProfilePathFromArgs(args, &profile_file_path))
433    return;
434
435  DCHECK(ProfileShortcutManager::IsFeatureEnabled());
436  ProfileShortcutManager* shortcut_manager =
437      g_browser_process->profile_manager()->profile_shortcut_manager();
438  DCHECK(shortcut_manager);
439
440  shortcut_manager->CreateProfileShortcut(profile_file_path);
441
442  // Update the UI buttons.
443  OnHasProfileShortcuts(true);
444}
445
446void ManageProfileHandler::RemoveProfileShortcut(const base::ListValue* args) {
447  base::FilePath profile_file_path;
448  if (!GetProfilePathFromArgs(args, &profile_file_path))
449    return;
450
451  DCHECK(ProfileShortcutManager::IsFeatureEnabled());
452  ProfileShortcutManager* shortcut_manager =
453    g_browser_process->profile_manager()->profile_shortcut_manager();
454  DCHECK(shortcut_manager);
455
456  shortcut_manager->RemoveProfileShortcuts(profile_file_path);
457
458  // Update the UI buttons.
459  OnHasProfileShortcuts(false);
460}
461
462}  // namespace options
463