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.
5 #include "chrome/browser/ui/webui/options/manage_profile_handler.h"
8 #include "base/bind_helpers.h"
9 #include "base/prefs/pref_service.h"
10 #include "base/prefs/scoped_user_pref_update.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/value_conversions.h"
15 #include "base/values.h"
16 #include "chrome/browser/browser_process.h"
17 #include "chrome/browser/chrome_notification_types.h"
18 #include "chrome/browser/profiles/gaia_info_update_service.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/profiles/profile_avatar_icon_util.h"
21 #include "chrome/browser/profiles/profile_info_cache.h"
22 #include "chrome/browser/profiles/profile_manager.h"
23 #include "chrome/browser/profiles/profile_metrics.h"
24 #include "chrome/browser/profiles/profile_shortcut_manager.h"
25 #include "chrome/browser/profiles/profile_window.h"
26 #include "chrome/browser/profiles/profiles_state.h"
27 #include "chrome/browser/signin/signin_manager_factory.h"
28 #include "chrome/browser/sync/profile_sync_service.h"
29 #include "chrome/browser/sync/profile_sync_service_factory.h"
30 #include "chrome/browser/ui/browser_finder.h"
31 #include "chrome/browser/ui/webui/options/options_handlers_helper.h"
32 #include "chrome/common/pref_names.h"
33 #include "chrome/common/url_constants.h"
34 #include "chrome/grit/generated_resources.h"
35 #include "chrome/grit/google_chrome_strings.h"
36 #include "components/signin/core/browser/signin_manager.h"
37 #include "content/public/browser/browser_thread.h"
38 #include "content/public/browser/notification_service.h"
39 #include "content/public/browser/web_ui.h"
40 #include "google_apis/gaia/gaia_auth_util.h"
41 #include "ui/base/l10n/l10n_util.h"
42 #include "ui/base/webui/web_ui_util.h"
44 #if defined(ENABLE_SETTINGS_APP)
45 #include "chrome/browser/ui/app_list/app_list_service.h"
46 #include "content/public/browser/web_contents.h"
53 const char kCreateProfileIdentifier[] = "create";
54 const char kManageProfileIdentifier[] = "manage";
56 // Given |args| from the WebUI, parses value 0 as a FilePath |profile_file_path|
57 // and returns true on success.
58 bool GetProfilePathFromArgs(const base::ListValue* args,
59 base::FilePath* profile_file_path) {
60 const base::Value* file_path_value;
61 if (!args->Get(0, &file_path_value))
63 return base::GetValueAsFilePath(*file_path_value, profile_file_path);
68 ManageProfileHandler::ManageProfileHandler()
69 : weak_factory_(this) {
72 ManageProfileHandler::~ManageProfileHandler() {
73 ProfileSyncService* service =
74 ProfileSyncServiceFactory::GetForProfile(Profile::FromWebUI(web_ui()));
75 // Sync may be disabled in tests.
77 service->RemoveObserver(this);
80 void ManageProfileHandler::GetLocalizedValues(
81 base::DictionaryValue* localized_strings) {
82 DCHECK(localized_strings);
84 static OptionsStringResource resources[] = {
85 { "manageProfilesNameLabel", IDS_PROFILES_MANAGE_NAME_LABEL },
86 { "manageProfilesDuplicateNameError",
87 IDS_PROFILES_MANAGE_DUPLICATE_NAME_ERROR },
88 { "manageProfilesIconLabel", IDS_PROFILES_MANAGE_ICON_LABEL },
89 { "manageProfilesExistingSupervisedUser",
90 IDS_PROFILES_CREATE_EXISTING_SUPERVISED_USER_ERROR },
91 { "manageProfilesSupervisedSignedInLabel",
92 IDS_PROFILES_CREATE_SUPERVISED_SIGNED_IN_LABEL },
93 { "manageProfilesSupervisedNotSignedIn",
94 IDS_PROFILES_CREATE_SUPERVISED_NOT_SIGNED_IN_HTML },
95 { "manageProfilesSupervisedAccountDetailsOutOfDate",
96 IDS_PROFILES_CREATE_SUPERVISED_ACCOUNT_DETAILS_OUT_OF_DATE_LABEL },
97 { "manageProfilesSupervisedSignInAgainLink",
98 IDS_PROFILES_CREATE_SUPERVISED_SIGN_IN_AGAIN_LINK },
99 { "manageProfilesConfirm", IDS_SAVE },
100 { "deleteProfileTitle", IDS_PROFILES_DELETE_TITLE },
101 { "deleteProfileOK", IDS_PROFILES_DELETE_OK_BUTTON_LABEL },
102 { "deleteProfileMessage", IDS_PROFILES_DELETE_MESSAGE },
103 { "deleteSupervisedProfileAddendum",
104 IDS_PROFILES_DELETE_SUPERVISED_ADDENDUM },
105 { "disconnectManagedProfileTitle",
106 IDS_PROFILES_DISCONNECT_MANAGED_PROFILE_TITLE },
107 { "disconnectManagedProfileOK",
108 IDS_PROFILES_DISCONNECT_MANAGED_PROFILE_OK_BUTTON_LABEL },
109 { "createProfileTitle", IDS_PROFILES_CREATE_TITLE },
110 { "createProfileInstructions", IDS_PROFILES_CREATE_INSTRUCTIONS },
111 { "createProfileConfirm", IDS_ADD },
112 { "createProfileShortcutCheckbox", IDS_PROFILES_CREATE_SHORTCUT_CHECKBOX },
113 { "createProfileShortcutButton", IDS_PROFILES_CREATE_SHORTCUT_BUTTON },
114 { "removeProfileShortcutButton", IDS_PROFILES_REMOVE_SHORTCUT_BUTTON },
115 { "importExistingSupervisedUserLink",
116 IDS_PROFILES_IMPORT_EXISTING_SUPERVISED_USER_LINK },
119 RegisterStrings(localized_strings, resources, arraysize(resources));
120 RegisterTitle(localized_strings, "manageProfile", IDS_PROFILES_MANAGE_TITLE);
121 RegisterTitle(localized_strings, "createProfile", IDS_PROFILES_CREATE_TITLE);
123 localized_strings->SetBoolean("profileShortcutsEnabled",
124 ProfileShortcutManager::IsFeatureEnabled());
126 GenerateSignedinUserSpecificStrings(localized_strings);
129 void ManageProfileHandler::InitializeHandler() {
130 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED,
131 content::NotificationService::AllSources());
133 Profile* profile = Profile::FromWebUI(web_ui());
134 pref_change_registrar_.Init(profile->GetPrefs());
135 pref_change_registrar_.Add(
136 prefs::kSupervisedUserCreationAllowed,
137 base::Bind(&ManageProfileHandler::OnCreateSupervisedUserPrefChange,
138 base::Unretained(this)));
139 ProfileSyncService* service =
140 ProfileSyncServiceFactory::GetForProfile(profile);
141 // Sync may be disabled for tests.
143 service->AddObserver(this);
146 void ManageProfileHandler::InitializePage() {
147 SendExistingProfileNames();
148 OnCreateSupervisedUserPrefChange();
151 void ManageProfileHandler::RegisterMessages() {
152 web_ui()->RegisterMessageCallback("setProfileIconAndName",
153 base::Bind(&ManageProfileHandler::SetProfileIconAndName,
154 base::Unretained(this)));
155 web_ui()->RegisterMessageCallback("requestDefaultProfileIcons",
156 base::Bind(&ManageProfileHandler::RequestDefaultProfileIcons,
157 base::Unretained(this)));
158 web_ui()->RegisterMessageCallback("requestNewProfileDefaults",
159 base::Bind(&ManageProfileHandler::RequestNewProfileDefaults,
160 base::Unretained(this)));
161 web_ui()->RegisterMessageCallback("requestHasProfileShortcuts",
162 base::Bind(&ManageProfileHandler::RequestHasProfileShortcuts,
163 base::Unretained(this)));
164 web_ui()->RegisterMessageCallback("requestCreateProfileUpdate",
165 base::Bind(&ManageProfileHandler::RequestCreateProfileUpdate,
166 base::Unretained(this)));
167 web_ui()->RegisterMessageCallback("profileIconSelectionChanged",
168 base::Bind(&ManageProfileHandler::ProfileIconSelectionChanged,
169 base::Unretained(this)));
170 #if defined(ENABLE_SETTINGS_APP)
171 web_ui()->RegisterMessageCallback("switchAppListProfile",
172 base::Bind(&ManageProfileHandler::SwitchAppListProfile,
173 base::Unretained(this)));
175 web_ui()->RegisterMessageCallback("addProfileShortcut",
176 base::Bind(&ManageProfileHandler::AddProfileShortcut,
177 base::Unretained(this)));
178 web_ui()->RegisterMessageCallback("removeProfileShortcut",
179 base::Bind(&ManageProfileHandler::RemoveProfileShortcut,
180 base::Unretained(this)));
181 web_ui()->RegisterMessageCallback("refreshGaiaPicture",
182 base::Bind(&ManageProfileHandler::RefreshGaiaPicture,
183 base::Unretained(this)));
184 web_ui()->RegisterMessageCallback(
185 "showDisconnectManagedProfileDialog",
186 base::Bind(&ManageProfileHandler::ShowDisconnectManagedProfileDialog,
187 base::Unretained(this)));
190 void ManageProfileHandler::Uninitialize() {
191 registrar_.RemoveAll();
194 void ManageProfileHandler::Observe(
196 const content::NotificationSource& source,
197 const content::NotificationDetails& details) {
198 if (type == chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED) {
199 SendExistingProfileNames();
200 base::StringValue value(kManageProfileIdentifier);
201 SendProfileIconsAndNames(value);
205 void ManageProfileHandler::OnStateChanged() {
206 RequestCreateProfileUpdate(NULL);
209 void ManageProfileHandler::GenerateSignedinUserSpecificStrings(
210 base::DictionaryValue* dictionary) {
211 std::string username;
212 std::string domain_name;
214 #if !defined(OS_CHROMEOS)
215 Profile* profile = Profile::FromWebUI(web_ui());
217 SigninManagerBase* manager = SigninManagerFactory::GetForProfile(profile);
219 username = manager->GetAuthenticatedUsername();
220 // If there is no one logged in or if the profile name is empty then the
221 // domain name is empty. This happens in browser tests.
222 if (!username.empty()) {
223 domain_name = "<span id=disconnect-managed-profile-domain-name>" +
224 gaia::ExtractDomainName(username) + "</span>";
229 dictionary->SetString(
230 "disconnectManagedProfileDomainInformation",
231 l10n_util::GetStringFUTF16(
232 IDS_PROFILES_DISCONNECT_MANAGED_PROFILE_DOMAIN_INFORMATION,
233 base::ASCIIToUTF16(domain_name)));
235 dictionary->SetString(
236 "disconnectManagedProfileText",
237 l10n_util::GetStringFUTF16(
238 IDS_PROFILES_DISCONNECT_MANAGED_PROFILE_TEXT,
239 base::UTF8ToUTF16(username),
240 base::UTF8ToUTF16(chrome::kSyncGoogleDashboardURL)));
243 void ManageProfileHandler::RequestDefaultProfileIcons(
244 const base::ListValue* args) {
246 bool ok = args->GetString(0, &mode);
248 DCHECK(mode == kCreateProfileIdentifier || mode == kManageProfileIdentifier);
250 base::StringValue value(mode);
251 SendProfileIconsAndNames(value);
255 void ManageProfileHandler::RequestNewProfileDefaults(
256 const base::ListValue* args) {
257 const ProfileInfoCache& cache =
258 g_browser_process->profile_manager()->GetProfileInfoCache();
259 const size_t icon_index = cache.ChooseAvatarIconIndexForNewProfile();
261 base::DictionaryValue profile_info;
262 profile_info.SetString("name", cache.ChooseNameForNewProfile(icon_index));
263 profile_info.SetString("iconURL",
264 profiles::GetDefaultAvatarIconUrl(icon_index));
266 web_ui()->CallJavascriptFunction(
267 "ManageProfileOverlay.receiveNewProfileDefaults", profile_info);
270 void ManageProfileHandler::SendProfileIconsAndNames(
271 const base::StringValue& mode) {
272 base::ListValue image_url_list;
273 base::ListValue default_name_list;
275 // First add the GAIA picture if it's available.
276 const ProfileInfoCache& cache =
277 g_browser_process->profile_manager()->GetProfileInfoCache();
278 Profile* profile = Profile::FromWebUI(web_ui());
279 size_t profile_index = cache.GetIndexOfProfileWithPath(profile->GetPath());
280 if (profile_index != std::string::npos) {
281 const gfx::Image* icon =
282 cache.GetGAIAPictureOfProfileAtIndex(profile_index);
284 gfx::Image icon2 = profiles::GetAvatarIconForWebUI(*icon, true);
285 gaia_picture_url_ = webui::GetBitmapDataUrl(icon2.AsBitmap());
286 image_url_list.AppendString(gaia_picture_url_);
287 default_name_list.AppendString(std::string());
291 // Next add the default avatar icons and names.
292 for (size_t i = 0; i < profiles::GetDefaultAvatarIconCount(); i++) {
293 std::string url = profiles::GetDefaultAvatarIconUrl(i);
294 image_url_list.AppendString(url);
295 default_name_list.AppendString(cache.ChooseNameForNewProfile(i));
298 web_ui()->CallJavascriptFunction(
299 "ManageProfileOverlay.receiveDefaultProfileIconsAndNames", mode,
300 image_url_list, default_name_list);
303 void ManageProfileHandler::SendExistingProfileNames() {
304 const ProfileInfoCache& cache =
305 g_browser_process->profile_manager()->GetProfileInfoCache();
306 base::DictionaryValue profile_name_dict;
307 for (size_t i = 0, e = cache.GetNumberOfProfiles(); i < e; ++i) {
308 profile_name_dict.SetBoolean(
309 base::UTF16ToUTF8(cache.GetNameOfProfileAtIndex(i)), true);
312 web_ui()->CallJavascriptFunction(
313 "ManageProfileOverlay.receiveExistingProfileNames", profile_name_dict);
316 void ManageProfileHandler::ShowDisconnectManagedProfileDialog(
317 const base::ListValue* args) {
318 base::DictionaryValue replacements;
319 GenerateSignedinUserSpecificStrings(&replacements);
320 web_ui()->CallJavascriptFunction(
321 "ManageProfileOverlay.showDisconnectManagedProfileDialog", replacements);
324 void ManageProfileHandler::SetProfileIconAndName(const base::ListValue* args) {
327 base::FilePath profile_file_path;
328 if (!GetProfilePathFromArgs(args, &profile_file_path))
332 g_browser_process->profile_manager()->GetProfile(profile_file_path);
336 std::string icon_url;
337 if (!args->GetString(1, &icon_url))
340 PrefService* pref_service = profile->GetPrefs();
341 // Updating the profile preferences will cause the cache to be updated.
343 // Metrics logging variable.
344 bool previously_using_gaia_icon =
345 pref_service->GetBoolean(prefs::kProfileUsingGAIAAvatar);
347 size_t new_icon_index;
348 if (icon_url == gaia_picture_url_) {
349 pref_service->SetBoolean(prefs::kProfileUsingDefaultAvatar, false);
350 pref_service->SetBoolean(prefs::kProfileUsingGAIAAvatar, true);
351 if (!previously_using_gaia_icon) {
352 // Only log if they changed to the GAIA photo.
353 // Selection of GAIA photo as avatar is logged as part of the function
355 ProfileMetrics::LogProfileSwitchGaia(ProfileMetrics::GAIA_OPT_IN);
357 } else if (profiles::IsDefaultAvatarIconUrl(icon_url, &new_icon_index)) {
358 ProfileMetrics::LogProfileAvatarSelection(new_icon_index);
359 pref_service->SetInteger(prefs::kProfileAvatarIndex, new_icon_index);
360 pref_service->SetBoolean(prefs::kProfileUsingDefaultAvatar, false);
361 pref_service->SetBoolean(prefs::kProfileUsingGAIAAvatar, false);
363 ProfileMetrics::LogProfileUpdate(profile_file_path);
365 if (profile->IsSupervised())
368 base::string16 new_profile_name;
369 if (!args->GetString(2, &new_profile_name))
372 base::TrimWhitespace(new_profile_name, base::TRIM_ALL, &new_profile_name);
373 CHECK(!new_profile_name.empty());
374 profiles::UpdateProfileName(profile, new_profile_name);
377 #if defined(ENABLE_SETTINGS_APP)
378 void ManageProfileHandler::SwitchAppListProfile(const base::ListValue* args) {
380 DCHECK(profiles::IsMultipleProfilesEnabled());
382 const base::Value* file_path_value;
383 base::FilePath profile_file_path;
384 if (!args->Get(0, &file_path_value) ||
385 !base::GetValueAsFilePath(*file_path_value, &profile_file_path))
388 AppListService* app_list_service = AppListService::Get(
389 options::helper::GetDesktopType(web_ui()));
390 app_list_service->SetProfilePath(profile_file_path);
391 app_list_service->Show();
393 // Close the settings app, since it will now be for the wrong profile.
394 web_ui()->GetWebContents()->Close();
396 #endif // defined(ENABLE_SETTINGS_APP)
398 void ManageProfileHandler::ProfileIconSelectionChanged(
399 const base::ListValue* args) {
402 base::FilePath profile_file_path;
403 if (!GetProfilePathFromArgs(args, &profile_file_path))
406 // Currently this only supports editing the current profile's info.
407 if (profile_file_path != Profile::FromWebUI(web_ui())->GetPath())
410 std::string icon_url;
411 if (!args->GetString(1, &icon_url))
414 if (icon_url != gaia_picture_url_)
417 // If the selection is the GAIA picture then also show the profile name in the
418 // text field. This will display either the GAIA given name, if available,
419 // or the first name.
420 ProfileInfoCache& cache =
421 g_browser_process->profile_manager()->GetProfileInfoCache();
422 size_t profile_index = cache.GetIndexOfProfileWithPath(profile_file_path);
423 if (profile_index == std::string::npos)
425 base::string16 gaia_name = cache.GetNameOfProfileAtIndex(profile_index);
426 if (gaia_name.empty())
429 base::StringValue gaia_name_value(gaia_name);
430 base::StringValue mode_value(kManageProfileIdentifier);
431 web_ui()->CallJavascriptFunction("ManageProfileOverlay.setProfileName",
432 gaia_name_value, mode_value);
435 void ManageProfileHandler::RequestHasProfileShortcuts(
436 const base::ListValue* args) {
437 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
438 DCHECK(ProfileShortcutManager::IsFeatureEnabled());
440 base::FilePath profile_file_path;
441 if (!GetProfilePathFromArgs(args, &profile_file_path))
444 const ProfileInfoCache& cache =
445 g_browser_process->profile_manager()->GetProfileInfoCache();
446 size_t profile_index = cache.GetIndexOfProfileWithPath(profile_file_path);
447 if (profile_index == std::string::npos)
450 // Don't show the add/remove desktop shortcut button in the single user case.
451 if (cache.GetNumberOfProfiles() <= 1)
454 const base::FilePath profile_path =
455 cache.GetPathOfProfileAtIndex(profile_index);
456 ProfileShortcutManager* shortcut_manager =
457 g_browser_process->profile_manager()->profile_shortcut_manager();
458 shortcut_manager->HasProfileShortcuts(
459 profile_path, base::Bind(&ManageProfileHandler::OnHasProfileShortcuts,
460 weak_factory_.GetWeakPtr()));
463 void ManageProfileHandler::RequestCreateProfileUpdate(
464 const base::ListValue* args) {
465 Profile* profile = Profile::FromWebUI(web_ui());
466 SigninManagerBase* manager =
467 SigninManagerFactory::GetForProfile(profile);
468 base::string16 username =
469 base::UTF8ToUTF16(manager->GetAuthenticatedUsername());
470 ProfileSyncService* service =
471 ProfileSyncServiceFactory::GetForProfile(profile);
472 GoogleServiceAuthError::State state = service->GetAuthError().state();
473 bool has_error = (state == GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS ||
474 state == GoogleServiceAuthError::USER_NOT_SIGNED_UP ||
475 state == GoogleServiceAuthError::ACCOUNT_DELETED ||
476 state == GoogleServiceAuthError::ACCOUNT_DISABLED);
477 web_ui()->CallJavascriptFunction("CreateProfileOverlay.updateSignedInStatus",
478 base::StringValue(username),
479 base::FundamentalValue(has_error));
481 OnCreateSupervisedUserPrefChange();
484 void ManageProfileHandler::OnCreateSupervisedUserPrefChange() {
485 PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs();
486 base::FundamentalValue allowed(
487 prefs->GetBoolean(prefs::kSupervisedUserCreationAllowed));
488 web_ui()->CallJavascriptFunction(
489 "CreateProfileOverlay.updateSupervisedUsersAllowed", allowed);
492 void ManageProfileHandler::OnHasProfileShortcuts(bool has_shortcuts) {
493 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
495 const base::FundamentalValue has_shortcuts_value(has_shortcuts);
496 web_ui()->CallJavascriptFunction(
497 "ManageProfileOverlay.receiveHasProfileShortcuts", has_shortcuts_value);
500 void ManageProfileHandler::AddProfileShortcut(const base::ListValue* args) {
501 base::FilePath profile_file_path;
502 if (!GetProfilePathFromArgs(args, &profile_file_path))
505 DCHECK(ProfileShortcutManager::IsFeatureEnabled());
506 ProfileShortcutManager* shortcut_manager =
507 g_browser_process->profile_manager()->profile_shortcut_manager();
508 DCHECK(shortcut_manager);
510 shortcut_manager->CreateProfileShortcut(profile_file_path);
512 // Update the UI buttons.
513 OnHasProfileShortcuts(true);
516 void ManageProfileHandler::RemoveProfileShortcut(const base::ListValue* args) {
517 base::FilePath profile_file_path;
518 if (!GetProfilePathFromArgs(args, &profile_file_path))
521 DCHECK(ProfileShortcutManager::IsFeatureEnabled());
522 ProfileShortcutManager* shortcut_manager =
523 g_browser_process->profile_manager()->profile_shortcut_manager();
524 DCHECK(shortcut_manager);
526 shortcut_manager->RemoveProfileShortcuts(profile_file_path);
528 // Update the UI buttons.
529 OnHasProfileShortcuts(false);
532 void ManageProfileHandler::RefreshGaiaPicture(const base::ListValue* args) {
533 profiles::UpdateGaiaProfilePhotoIfNeeded(Profile::FromWebUI(web_ui()));
536 } // namespace options