manage_profile_handler.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
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/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/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/chrome_notification_types.h" 17#include "chrome/browser/profiles/gaia_info_update_service.h" 18#include "chrome/browser/profiles/profile.h" 19#include "chrome/browser/profiles/profile_avatar_icon_util.h" 20#include "chrome/browser/profiles/profile_info_cache.h" 21#include "chrome/browser/profiles/profile_manager.h" 22#include "chrome/browser/profiles/profile_metrics.h" 23#include "chrome/browser/profiles/profile_shortcut_manager.h" 24#include "chrome/browser/profiles/profile_window.h" 25#include "chrome/browser/profiles/profiles_state.h" 26#include "chrome/browser/signin/signin_manager_factory.h" 27#include "chrome/browser/sync/profile_sync_service.h" 28#include "chrome/browser/sync/profile_sync_service_factory.h" 29#include "chrome/browser/ui/browser_finder.h" 30#include "chrome/browser/ui/webui/options/options_handlers_helper.h" 31#include "chrome/common/pref_names.h" 32#include "chrome/common/url_constants.h" 33#include "components/signin/core/browser/signin_manager.h" 34#include "content/public/browser/browser_thread.h" 35#include "content/public/browser/notification_service.h" 36#include "content/public/browser/web_ui.h" 37#include "google_apis/gaia/gaia_auth_util.h" 38#include "grit/generated_resources.h" 39#include "grit/google_chrome_strings.h" 40#include "ui/base/l10n/l10n_util.h" 41#include "ui/base/webui/web_ui_util.h" 42 43#if defined(ENABLE_SETTINGS_APP) 44#include "chrome/browser/ui/app_list/app_list_service.h" 45#include "content/public/browser/web_contents.h" 46#endif 47 48namespace options { 49 50namespace { 51 52const char kCreateProfileIdentifier[] = "create"; 53const char kManageProfileIdentifier[] = "manage"; 54 55// Given |args| from the WebUI, parses value 0 as a FilePath |profile_file_path| 56// and returns true on success. 57bool GetProfilePathFromArgs(const base::ListValue* args, 58 base::FilePath* profile_file_path) { 59 const base::Value* file_path_value; 60 if (!args->Get(0, &file_path_value)) 61 return false; 62 return base::GetValueAsFilePath(*file_path_value, profile_file_path); 63} 64 65} // namespace 66 67ManageProfileHandler::ManageProfileHandler() 68 : weak_factory_(this) { 69} 70 71ManageProfileHandler::~ManageProfileHandler() { 72 ProfileSyncService* service = 73 ProfileSyncServiceFactory::GetForProfile(Profile::FromWebUI(web_ui())); 74 // Sync may be disabled in tests. 75 if (service) 76 service->RemoveObserver(this); 77} 78 79void ManageProfileHandler::GetLocalizedValues( 80 base::DictionaryValue* localized_strings) { 81 DCHECK(localized_strings); 82 83 static OptionsStringResource resources[] = { 84 { "manageProfilesNameLabel", IDS_PROFILES_MANAGE_NAME_LABEL }, 85 { "manageProfilesDuplicateNameError", 86 IDS_PROFILES_MANAGE_DUPLICATE_NAME_ERROR }, 87 { "manageProfilesIconLabel", IDS_PROFILES_MANAGE_ICON_LABEL }, 88 { "manageProfilesExistingSupervisedUser", 89 IDS_PROFILES_CREATE_EXISTING_SUPERVISED_USER_ERROR }, 90 { "manageProfilesSupervisedSignedInLabel", 91 IDS_PROFILES_CREATE_SUPERVISED_SIGNED_IN_LABEL }, 92 { "manageProfilesSupervisedNotSignedInLabel", 93 IDS_PROFILES_CREATE_SUPERVISED_NOT_SIGNED_IN_LABEL }, 94 { "manageProfilesSupervisedAccountDetailsOutOfDate", 95 IDS_PROFILES_CREATE_SUPERVISED_ACCOUNT_DETAILS_OUT_OF_DATE_LABEL }, 96 { "manageProfilesSupervisedSignInAgainLink", 97 IDS_PROFILES_CREATE_SUPERVISED_SIGN_IN_AGAIN_LINK }, 98 { "manageProfilesSupervisedNotSignedInLink", 99 IDS_PROFILES_CREATE_SUPERVISED_NOT_SIGNED_IN_LINK }, 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_PROFILES_CREATE_CONFIRM }, 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 }, 117 { "signInToImportSupervisedUsers", 118 IDS_PROFILES_IMPORT_SUPERVISED_USER_NOT_SIGNED_IN }, 119 }; 120 121 RegisterStrings(localized_strings, resources, arraysize(resources)); 122 RegisterTitle(localized_strings, "manageProfile", 123 IDS_PROFILES_MANAGE_TITLE); 124 RegisterTitle(localized_strings, "createProfile", 125 IDS_PROFILES_CREATE_TITLE); 126 127 localized_strings->SetBoolean("profileShortcutsEnabled", 128 ProfileShortcutManager::IsFeatureEnabled()); 129 130 GenerateSignedinUserSpecificStrings(localized_strings); 131} 132 133void ManageProfileHandler::InitializeHandler() { 134 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED, 135 content::NotificationService::AllSources()); 136 137 Profile* profile = Profile::FromWebUI(web_ui()); 138 pref_change_registrar_.Init(profile->GetPrefs()); 139 pref_change_registrar_.Add( 140 prefs::kSupervisedUserCreationAllowed, 141 base::Bind(&ManageProfileHandler::OnCreateSupervisedUserPrefChange, 142 base::Unretained(this))); 143 ProfileSyncService* service = 144 ProfileSyncServiceFactory::GetForProfile(profile); 145 // Sync may be disabled for tests. 146 if (service) 147 service->AddObserver(this); 148} 149 150void ManageProfileHandler::InitializePage() { 151 SendExistingProfileNames(); 152 OnCreateSupervisedUserPrefChange(); 153} 154 155void ManageProfileHandler::RegisterMessages() { 156 web_ui()->RegisterMessageCallback("setProfileIconAndName", 157 base::Bind(&ManageProfileHandler::SetProfileIconAndName, 158 base::Unretained(this))); 159 web_ui()->RegisterMessageCallback("requestDefaultProfileIcons", 160 base::Bind(&ManageProfileHandler::RequestDefaultProfileIcons, 161 base::Unretained(this))); 162 web_ui()->RegisterMessageCallback("requestNewProfileDefaults", 163 base::Bind(&ManageProfileHandler::RequestNewProfileDefaults, 164 base::Unretained(this))); 165 web_ui()->RegisterMessageCallback("requestHasProfileShortcuts", 166 base::Bind(&ManageProfileHandler::RequestHasProfileShortcuts, 167 base::Unretained(this))); 168 web_ui()->RegisterMessageCallback("requestCreateProfileUpdate", 169 base::Bind(&ManageProfileHandler::RequestCreateProfileUpdate, 170 base::Unretained(this))); 171 web_ui()->RegisterMessageCallback("profileIconSelectionChanged", 172 base::Bind(&ManageProfileHandler::ProfileIconSelectionChanged, 173 base::Unretained(this))); 174#if defined(ENABLE_SETTINGS_APP) 175 web_ui()->RegisterMessageCallback("switchAppListProfile", 176 base::Bind(&ManageProfileHandler::SwitchAppListProfile, 177 base::Unretained(this))); 178#endif 179 web_ui()->RegisterMessageCallback("addProfileShortcut", 180 base::Bind(&ManageProfileHandler::AddProfileShortcut, 181 base::Unretained(this))); 182 web_ui()->RegisterMessageCallback("removeProfileShortcut", 183 base::Bind(&ManageProfileHandler::RemoveProfileShortcut, 184 base::Unretained(this))); 185 web_ui()->RegisterMessageCallback("refreshGaiaPicture", 186 base::Bind(&ManageProfileHandler::RefreshGaiaPicture, 187 base::Unretained(this))); 188 web_ui()->RegisterMessageCallback( 189 "showDisconnectManagedProfileDialog", 190 base::Bind(&ManageProfileHandler::ShowDisconnectManagedProfileDialog, 191 base::Unretained(this))); 192} 193 194void ManageProfileHandler::Uninitialize() { 195 registrar_.RemoveAll(); 196} 197 198void ManageProfileHandler::Observe( 199 int type, 200 const content::NotificationSource& source, 201 const content::NotificationDetails& details) { 202 if (type == chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED) { 203 SendExistingProfileNames(); 204 base::StringValue value(kManageProfileIdentifier); 205 SendProfileIconsAndNames(value); 206 } 207} 208 209void ManageProfileHandler::OnStateChanged() { 210 RequestCreateProfileUpdate(NULL); 211} 212 213void ManageProfileHandler::GenerateSignedinUserSpecificStrings( 214 base::DictionaryValue* dictionary) { 215 std::string username; 216 std::string domain_name; 217 218#if !defined(OS_CHROMEOS) 219 Profile* profile = Profile::FromWebUI(web_ui()); 220 DCHECK(profile); 221 SigninManagerBase* manager = SigninManagerFactory::GetForProfile(profile); 222 if (manager) { 223 username = manager->GetAuthenticatedUsername(); 224 // If there is no one logged in or if the profile name is empty then the 225 // domain name is empty. This happens in browser tests. 226 if (!username.empty()) { 227 domain_name = "<span id=disconnect-managed-profile-domain-name>" + 228 gaia::ExtractDomainName(username) + "</span>"; 229 } 230 } 231#endif 232 233 dictionary->SetString( 234 "disconnectManagedProfileDomainInformation", 235 l10n_util::GetStringFUTF16( 236 IDS_PROFILES_DISCONNECT_MANAGED_PROFILE_DOMAIN_INFORMATION, 237 base::ASCIIToUTF16(domain_name))); 238 239 dictionary->SetString( 240 "disconnectManagedProfileText", 241 l10n_util::GetStringFUTF16( 242 IDS_PROFILES_DISCONNECT_MANAGED_PROFILE_TEXT, 243 base::UTF8ToUTF16(username), 244 base::UTF8ToUTF16(chrome::kSyncGoogleDashboardURL))); 245} 246 247void ManageProfileHandler::RequestDefaultProfileIcons( 248 const base::ListValue* args) { 249 std::string mode; 250 bool ok = args->GetString(0, &mode); 251 DCHECK(ok); 252 DCHECK(mode == kCreateProfileIdentifier || mode == kManageProfileIdentifier); 253 if (ok) { 254 base::StringValue value(mode); 255 SendProfileIconsAndNames(value); 256 } 257} 258 259void ManageProfileHandler::RequestNewProfileDefaults( 260 const base::ListValue* args) { 261 const ProfileInfoCache& cache = 262 g_browser_process->profile_manager()->GetProfileInfoCache(); 263 const size_t icon_index = cache.ChooseAvatarIconIndexForNewProfile(); 264 265 base::DictionaryValue profile_info; 266 profile_info.SetString("name", cache.ChooseNameForNewProfile(icon_index)); 267 profile_info.SetString("iconURL", 268 profiles::GetDefaultAvatarIconUrl(icon_index)); 269 270 web_ui()->CallJavascriptFunction( 271 "ManageProfileOverlay.receiveNewProfileDefaults", profile_info); 272} 273 274void ManageProfileHandler::SendProfileIconsAndNames( 275 const base::StringValue& mode) { 276 base::ListValue image_url_list; 277 base::ListValue default_name_list; 278 279 // First add the GAIA picture if it's available. 280 const ProfileInfoCache& cache = 281 g_browser_process->profile_manager()->GetProfileInfoCache(); 282 Profile* profile = Profile::FromWebUI(web_ui()); 283 size_t profile_index = cache.GetIndexOfProfileWithPath(profile->GetPath()); 284 if (profile_index != std::string::npos) { 285 const gfx::Image* icon = 286 cache.GetGAIAPictureOfProfileAtIndex(profile_index); 287 if (icon) { 288 gfx::Image icon2 = profiles::GetAvatarIconForWebUI(*icon, true); 289 gaia_picture_url_ = webui::GetBitmapDataUrl(icon2.AsBitmap()); 290 image_url_list.AppendString(gaia_picture_url_); 291 default_name_list.AppendString(std::string()); 292 } 293 } 294 295 // Next add the default avatar icons and names. 296 for (size_t i = 0; i < profiles::GetDefaultAvatarIconCount(); i++) { 297 std::string url = profiles::GetDefaultAvatarIconUrl(i); 298 image_url_list.AppendString(url); 299 default_name_list.AppendString(cache.ChooseNameForNewProfile(i)); 300 } 301 302 web_ui()->CallJavascriptFunction( 303 "ManageProfileOverlay.receiveDefaultProfileIconsAndNames", mode, 304 image_url_list, default_name_list); 305} 306 307void ManageProfileHandler::SendExistingProfileNames() { 308 const ProfileInfoCache& cache = 309 g_browser_process->profile_manager()->GetProfileInfoCache(); 310 base::DictionaryValue profile_name_dict; 311 for (size_t i = 0, e = cache.GetNumberOfProfiles(); i < e; ++i) { 312 profile_name_dict.SetBoolean( 313 base::UTF16ToUTF8(cache.GetNameOfProfileAtIndex(i)), true); 314 } 315 316 web_ui()->CallJavascriptFunction( 317 "ManageProfileOverlay.receiveExistingProfileNames", profile_name_dict); 318} 319 320void ManageProfileHandler::ShowDisconnectManagedProfileDialog( 321 const base::ListValue* args) { 322 base::DictionaryValue replacements; 323 GenerateSignedinUserSpecificStrings(&replacements); 324 web_ui()->CallJavascriptFunction( 325 "ManageProfileOverlay.showDisconnectManagedProfileDialog", replacements); 326} 327 328void ManageProfileHandler::SetProfileIconAndName(const base::ListValue* args) { 329 DCHECK(args); 330 331 base::FilePath profile_file_path; 332 if (!GetProfilePathFromArgs(args, &profile_file_path)) 333 return; 334 335 ProfileInfoCache& cache = 336 g_browser_process->profile_manager()->GetProfileInfoCache(); 337 size_t profile_index = cache.GetIndexOfProfileWithPath(profile_file_path); 338 if (profile_index == std::string::npos) 339 return; 340 341 Profile* profile = 342 g_browser_process->profile_manager()->GetProfile(profile_file_path); 343 if (!profile) 344 return; 345 346 std::string icon_url; 347 if (!args->GetString(1, &icon_url)) 348 return; 349 350 // Metrics logging variable. 351 bool previously_using_gaia_icon = 352 cache.IsUsingGAIAPictureOfProfileAtIndex(profile_index); 353 354 size_t new_icon_index; 355 if (icon_url == gaia_picture_url_) { 356 cache.SetIsUsingGAIAPictureOfProfileAtIndex(profile_index, true); 357 if (!previously_using_gaia_icon) { 358 // Only log if they changed to the GAIA photo. 359 // Selection of GAIA photo as avatar is logged as part of the function 360 // below. 361 ProfileMetrics::LogProfileSwitchGaia(ProfileMetrics::GAIA_OPT_IN); 362 } 363 } else if (profiles::IsDefaultAvatarIconUrl(icon_url, &new_icon_index)) { 364 ProfileMetrics::LogProfileAvatarSelection(new_icon_index); 365 PrefService* pref_service = profile->GetPrefs(); 366 // Updating the profile preference will cause the cache to be updated for 367 // this preference. 368 pref_service->SetInteger(prefs::kProfileAvatarIndex, new_icon_index); 369 cache.SetIsUsingGAIAPictureOfProfileAtIndex(profile_index, false); 370 } 371 ProfileMetrics::LogProfileUpdate(profile_file_path); 372 373 if (profile->IsSupervised()) 374 return; 375 376 base::string16 new_profile_name; 377 if (!args->GetString(2, &new_profile_name)) 378 return; 379 380 profiles::UpdateProfileName(profile, new_profile_name); 381} 382 383#if defined(ENABLE_SETTINGS_APP) 384void ManageProfileHandler::SwitchAppListProfile(const base::ListValue* args) { 385 DCHECK(args); 386 DCHECK(profiles::IsMultipleProfilesEnabled()); 387 388 const base::Value* file_path_value; 389 base::FilePath profile_file_path; 390 if (!args->Get(0, &file_path_value) || 391 !base::GetValueAsFilePath(*file_path_value, &profile_file_path)) 392 return; 393 394 AppListService* app_list_service = AppListService::Get( 395 options::helper::GetDesktopType(web_ui())); 396 app_list_service->SetProfilePath(profile_file_path); 397 app_list_service->Show(); 398 399 // Close the settings app, since it will now be for the wrong profile. 400 web_ui()->GetWebContents()->Close(); 401} 402#endif // defined(ENABLE_SETTINGS_APP) 403 404void ManageProfileHandler::ProfileIconSelectionChanged( 405 const base::ListValue* args) { 406 DCHECK(args); 407 408 base::FilePath profile_file_path; 409 if (!GetProfilePathFromArgs(args, &profile_file_path)) 410 return; 411 412 // Currently this only supports editing the current profile's info. 413 if (profile_file_path != Profile::FromWebUI(web_ui())->GetPath()) 414 return; 415 416 std::string icon_url; 417 if (!args->GetString(1, &icon_url)) 418 return; 419 420 if (icon_url != gaia_picture_url_) 421 return; 422 423 // If the selection is the GAIA picture then also show the profile name in the 424 // text field. This will display either the GAIA given name, if available, 425 // or the first name. 426 ProfileInfoCache& cache = 427 g_browser_process->profile_manager()->GetProfileInfoCache(); 428 size_t profile_index = cache.GetIndexOfProfileWithPath(profile_file_path); 429 if (profile_index == std::string::npos) 430 return; 431 base::string16 gaia_name = cache.GetNameOfProfileAtIndex(profile_index); 432 if (gaia_name.empty()) 433 return; 434 435 base::StringValue gaia_name_value(gaia_name); 436 base::StringValue mode_value(kManageProfileIdentifier); 437 web_ui()->CallJavascriptFunction("ManageProfileOverlay.setProfileName", 438 gaia_name_value, mode_value); 439} 440 441void ManageProfileHandler::RequestHasProfileShortcuts( 442 const base::ListValue* args) { 443 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 444 DCHECK(ProfileShortcutManager::IsFeatureEnabled()); 445 446 base::FilePath profile_file_path; 447 if (!GetProfilePathFromArgs(args, &profile_file_path)) 448 return; 449 450 const ProfileInfoCache& cache = 451 g_browser_process->profile_manager()->GetProfileInfoCache(); 452 size_t profile_index = cache.GetIndexOfProfileWithPath(profile_file_path); 453 if (profile_index == std::string::npos) 454 return; 455 456 const base::FilePath profile_path = 457 cache.GetPathOfProfileAtIndex(profile_index); 458 ProfileShortcutManager* shortcut_manager = 459 g_browser_process->profile_manager()->profile_shortcut_manager(); 460 shortcut_manager->HasProfileShortcuts( 461 profile_path, base::Bind(&ManageProfileHandler::OnHasProfileShortcuts, 462 weak_factory_.GetWeakPtr())); 463} 464 465void ManageProfileHandler::RequestCreateProfileUpdate( 466 const base::ListValue* args) { 467 Profile* profile = Profile::FromWebUI(web_ui()); 468 SigninManagerBase* manager = 469 SigninManagerFactory::GetForProfile(profile); 470 base::string16 username = 471 base::UTF8ToUTF16(manager->GetAuthenticatedUsername()); 472 ProfileSyncService* service = 473 ProfileSyncServiceFactory::GetForProfile(profile); 474 GoogleServiceAuthError::State state = service->GetAuthError().state(); 475 bool has_error = (state == GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS || 476 state == GoogleServiceAuthError::USER_NOT_SIGNED_UP || 477 state == GoogleServiceAuthError::ACCOUNT_DELETED || 478 state == GoogleServiceAuthError::ACCOUNT_DISABLED); 479 web_ui()->CallJavascriptFunction("CreateProfileOverlay.updateSignedInStatus", 480 base::StringValue(username), 481 base::FundamentalValue(has_error)); 482 483 OnCreateSupervisedUserPrefChange(); 484} 485 486void ManageProfileHandler::OnCreateSupervisedUserPrefChange() { 487 PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs(); 488 base::FundamentalValue allowed( 489 prefs->GetBoolean(prefs::kSupervisedUserCreationAllowed)); 490 web_ui()->CallJavascriptFunction( 491 "CreateProfileOverlay.updateSupervisedUsersAllowed", allowed); 492} 493 494void ManageProfileHandler::OnHasProfileShortcuts(bool has_shortcuts) { 495 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 496 497 const base::FundamentalValue has_shortcuts_value(has_shortcuts); 498 web_ui()->CallJavascriptFunction( 499 "ManageProfileOverlay.receiveHasProfileShortcuts", has_shortcuts_value); 500} 501 502void ManageProfileHandler::AddProfileShortcut(const base::ListValue* args) { 503 base::FilePath profile_file_path; 504 if (!GetProfilePathFromArgs(args, &profile_file_path)) 505 return; 506 507 DCHECK(ProfileShortcutManager::IsFeatureEnabled()); 508 ProfileShortcutManager* shortcut_manager = 509 g_browser_process->profile_manager()->profile_shortcut_manager(); 510 DCHECK(shortcut_manager); 511 512 shortcut_manager->CreateProfileShortcut(profile_file_path); 513 514 // Update the UI buttons. 515 OnHasProfileShortcuts(true); 516} 517 518void ManageProfileHandler::RemoveProfileShortcut(const base::ListValue* args) { 519 base::FilePath profile_file_path; 520 if (!GetProfilePathFromArgs(args, &profile_file_path)) 521 return; 522 523 DCHECK(ProfileShortcutManager::IsFeatureEnabled()); 524 ProfileShortcutManager* shortcut_manager = 525 g_browser_process->profile_manager()->profile_shortcut_manager(); 526 DCHECK(shortcut_manager); 527 528 shortcut_manager->RemoveProfileShortcuts(profile_file_path); 529 530 // Update the UI buttons. 531 OnHasProfileShortcuts(false); 532} 533 534void ManageProfileHandler::RefreshGaiaPicture(const base::ListValue* args) { 535 profiles::UpdateGaiaProfilePhotoIfNeeded(Profile::FromWebUI(web_ui())); 536} 537 538} // namespace options 539