manage_profile_handler.cc revision 010d83a9304c5a91596085d917d248abff47903a
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/prefs/scoped_user_pref_update.h" 12#include "base/strings/string_number_conversions.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 "components/signin/core/browser/signin_manager.h" 35#include "content/public/browser/browser_thread.h" 36#include "content/public/browser/notification_service.h" 37#include "content/public/browser/web_ui.h" 38#include "google_apis/gaia/gaia_auth_util.h" 39#include "grit/generated_resources.h" 40#include "grit/google_chrome_strings.h" 41#include "ui/base/l10n/l10n_util.h" 42#include "ui/base/webui/web_ui_util.h" 43 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" 47#endif 48 49namespace options { 50 51namespace { 52 53const char kCreateProfileIdentifier[] = "create"; 54const char kManageProfileIdentifier[] = "manage"; 55 56// Given |args| from the WebUI, parses value 0 as a FilePath |profile_file_path| 57// and returns true on success. 58bool 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)) 62 return false; 63 return base::GetValueAsFilePath(*file_path_value, profile_file_path); 64} 65 66} // namespace 67 68ManageProfileHandler::ManageProfileHandler() 69 : weak_factory_(this) { 70} 71 72ManageProfileHandler::~ManageProfileHandler() { 73 ProfileSyncService* service = 74 ProfileSyncServiceFactory::GetForProfile(Profile::FromWebUI(web_ui())); 75 // Sync may be disabled in tests. 76 if (service) 77 service->RemoveObserver(this); 78} 79 80void ManageProfileHandler::GetLocalizedValues( 81 base::DictionaryValue* localized_strings) { 82 DCHECK(localized_strings); 83 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_MANAGED_USER_ERROR }, 91 { "manageProfilesManagedSignedInLabel", 92 IDS_PROFILES_CREATE_MANAGED_SIGNED_IN_LABEL }, 93 { "manageProfilesManagedNotSignedInLabel", 94 IDS_PROFILES_CREATE_MANAGED_NOT_SIGNED_IN_LABEL }, 95 { "manageProfilesManagedAccountDetailsOutOfDate", 96 IDS_PROFILES_CREATE_MANAGED_ACCOUNT_DETAILS_OUT_OF_DATE_LABEL }, 97 { "manageProfilesManagedSignInAgainLink", 98 IDS_PROFILES_CREATE_MANAGED_ACCOUNT_SIGN_IN_AGAIN_LINK }, 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 { "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 { "importExistingManagedUserLink", 116 IDS_PROFILES_IMPORT_EXISTING_MANAGED_USER_LINK }, 117 { "signInToImportManagedUsers", 118 IDS_PROFILES_IMPORT_MANAGED_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::kManagedUserCreationAllowed, 141 base::Bind(&ManageProfileHandler::OnCreateManagedUserPrefChange, 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 OnCreateManagedUserPrefChange(); 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} 186 187void ManageProfileHandler::Uninitialize() { 188 registrar_.RemoveAll(); 189} 190 191void ManageProfileHandler::Observe( 192 int type, 193 const content::NotificationSource& source, 194 const content::NotificationDetails& details) { 195 if (type == chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED) { 196 SendExistingProfileNames(); 197 base::StringValue value(kManageProfileIdentifier); 198 SendProfileIconsAndNames(value); 199 } 200} 201 202void ManageProfileHandler::OnStateChanged() { 203 RequestCreateProfileUpdate(NULL); 204} 205 206void ManageProfileHandler::GenerateSignedinUserSpecificStrings( 207 base::DictionaryValue* dictionary) { 208 std::string username; 209 std::string domain_name; 210 211#if !defined(OS_CHROMEOS) 212 Profile* profile = Profile::FromWebUI(web_ui()); 213 DCHECK(profile); 214 SigninManagerBase* manager = SigninManagerFactory::GetForProfile(profile); 215 if (manager) { 216 username = manager->GetAuthenticatedUsername(); 217 // If there is no one logged in or if the profile name is empty then the 218 // domain name is empty. This happens in browser tests. 219 if (!username.empty()) { 220 domain_name = "<span id=disconnect-managed-profile-domain-name>" + 221 gaia::ExtractDomainName(username) + "</span>"; 222 } 223 } 224#endif 225 226 dictionary->SetString( 227 "disconnectManagedProfileDomainInformation", 228 l10n_util::GetStringFUTF16( 229 IDS_PROFILES_DISCONNECT_MANAGED_PROFILE_DOMAIN_INFORMATION, 230 base::ASCIIToUTF16(domain_name))); 231 232 dictionary->SetString( 233 "disconnectManagedProfileText", 234 l10n_util::GetStringFUTF16( 235 IDS_PROFILES_DISCONNECT_MANAGED_PROFILE_TEXT, 236 base::UTF8ToUTF16(username), 237 base::UTF8ToUTF16(chrome::kSyncGoogleDashboardURL))); 238} 239 240void ManageProfileHandler::RequestDefaultProfileIcons( 241 const base::ListValue* args) { 242 std::string mode; 243 bool ok = args->GetString(0, &mode); 244 DCHECK(ok); 245 DCHECK(mode == kCreateProfileIdentifier || mode == kManageProfileIdentifier); 246 if (ok) { 247 base::StringValue value(mode); 248 SendProfileIconsAndNames(value); 249 } 250} 251 252void ManageProfileHandler::RequestNewProfileDefaults( 253 const base::ListValue* args) { 254 const ProfileInfoCache& cache = 255 g_browser_process->profile_manager()->GetProfileInfoCache(); 256 const size_t icon_index = cache.ChooseAvatarIconIndexForNewProfile(); 257 258 base::DictionaryValue profile_info; 259 profile_info.SetString("name", cache.ChooseNameForNewProfile(icon_index)); 260 profile_info.SetString("iconURL", 261 profiles::GetDefaultAvatarIconUrl(icon_index)); 262 263 web_ui()->CallJavascriptFunction( 264 "ManageProfileOverlay.receiveNewProfileDefaults", profile_info); 265} 266 267void ManageProfileHandler::SendProfileIconsAndNames( 268 const base::StringValue& mode) { 269 base::ListValue image_url_list; 270 base::ListValue default_name_list; 271 272 // First add the GAIA picture if it's available. 273 const ProfileInfoCache& cache = 274 g_browser_process->profile_manager()->GetProfileInfoCache(); 275 Profile* profile = Profile::FromWebUI(web_ui()); 276 size_t profile_index = cache.GetIndexOfProfileWithPath(profile->GetPath()); 277 if (profile_index != std::string::npos) { 278 const gfx::Image* icon = 279 cache.GetGAIAPictureOfProfileAtIndex(profile_index); 280 if (icon) { 281 gfx::Image icon2 = profiles::GetAvatarIconForWebUI(*icon, true); 282 gaia_picture_url_ = webui::GetBitmapDataUrl(icon2.AsBitmap()); 283 image_url_list.AppendString(gaia_picture_url_); 284 default_name_list.AppendString(std::string()); 285 } 286 } 287 288 // Next add the default avatar icons and names. 289 for (size_t i = 0; i < profiles::GetDefaultAvatarIconCount(); i++) { 290 std::string url = profiles::GetDefaultAvatarIconUrl(i); 291 image_url_list.AppendString(url); 292 default_name_list.AppendString(cache.ChooseNameForNewProfile(i)); 293 } 294 295 web_ui()->CallJavascriptFunction( 296 "ManageProfileOverlay.receiveDefaultProfileIconsAndNames", mode, 297 image_url_list, default_name_list); 298} 299 300void ManageProfileHandler::SendExistingProfileNames() { 301 const ProfileInfoCache& cache = 302 g_browser_process->profile_manager()->GetProfileInfoCache(); 303 base::DictionaryValue profile_name_dict; 304 for (size_t i = 0, e = cache.GetNumberOfProfiles(); i < e; ++i) { 305 profile_name_dict.SetBoolean( 306 base::UTF16ToUTF8(cache.GetNameOfProfileAtIndex(i)), true); 307 } 308 309 web_ui()->CallJavascriptFunction( 310 "ManageProfileOverlay.receiveExistingProfileNames", profile_name_dict); 311} 312 313void ManageProfileHandler::SetProfileIconAndName(const base::ListValue* args) { 314 DCHECK(args); 315 316 base::FilePath profile_file_path; 317 if (!GetProfilePathFromArgs(args, &profile_file_path)) 318 return; 319 320 ProfileInfoCache& cache = 321 g_browser_process->profile_manager()->GetProfileInfoCache(); 322 size_t profile_index = cache.GetIndexOfProfileWithPath(profile_file_path); 323 if (profile_index == std::string::npos) 324 return; 325 326 Profile* profile = 327 g_browser_process->profile_manager()->GetProfile(profile_file_path); 328 if (!profile) 329 return; 330 331 std::string icon_url; 332 if (!args->GetString(1, &icon_url)) 333 return; 334 335 // Metrics logging variable. 336 bool previously_using_gaia_icon = 337 cache.IsUsingGAIAPictureOfProfileAtIndex(profile_index); 338 339 size_t new_icon_index; 340 if (icon_url == gaia_picture_url_) { 341 cache.SetIsUsingGAIAPictureOfProfileAtIndex(profile_index, true); 342 if (!previously_using_gaia_icon) { 343 // Only log if they changed to the GAIA photo. 344 // Selection of GAIA photo as avatar is logged as part of the function 345 // below. 346 ProfileMetrics::LogProfileSwitchGaia(ProfileMetrics::GAIA_OPT_IN); 347 } 348 } else if (profiles::IsDefaultAvatarIconUrl(icon_url, &new_icon_index)) { 349 ProfileMetrics::LogProfileAvatarSelection(new_icon_index); 350 PrefService* pref_service = profile->GetPrefs(); 351 // Updating the profile preference will cause the cache to be updated for 352 // this preference. 353 pref_service->SetInteger(prefs::kProfileAvatarIndex, new_icon_index); 354 cache.SetIsUsingGAIAPictureOfProfileAtIndex(profile_index, false); 355 } 356 ProfileMetrics::LogProfileUpdate(profile_file_path); 357 358 if (profile->IsManaged()) 359 return; 360 361 base::string16 new_profile_name; 362 if (!args->GetString(2, &new_profile_name)) 363 return; 364 365 profiles::UpdateProfileName(profile, new_profile_name); 366} 367 368#if defined(ENABLE_SETTINGS_APP) 369void ManageProfileHandler::SwitchAppListProfile(const base::ListValue* args) { 370 DCHECK(args); 371 DCHECK(profiles::IsMultipleProfilesEnabled()); 372 373 const base::Value* file_path_value; 374 base::FilePath profile_file_path; 375 if (!args->Get(0, &file_path_value) || 376 !base::GetValueAsFilePath(*file_path_value, &profile_file_path)) 377 return; 378 379 AppListService* app_list_service = AppListService::Get( 380 options::helper::GetDesktopType(web_ui())); 381 app_list_service->SetProfilePath(profile_file_path); 382 app_list_service->Show(); 383 384 // Close the settings app, since it will now be for the wrong profile. 385 web_ui()->GetWebContents()->Close(); 386} 387#endif // defined(ENABLE_SETTINGS_APP) 388 389void ManageProfileHandler::ProfileIconSelectionChanged( 390 const base::ListValue* args) { 391 DCHECK(args); 392 393 base::FilePath profile_file_path; 394 if (!GetProfilePathFromArgs(args, &profile_file_path)) 395 return; 396 397 // Currently this only supports editing the current profile's info. 398 if (profile_file_path != Profile::FromWebUI(web_ui())->GetPath()) 399 return; 400 401 std::string icon_url; 402 if (!args->GetString(1, &icon_url)) 403 return; 404 405 if (icon_url != gaia_picture_url_) 406 return; 407 408 // If the selection is the GAIA picture then also show the profile name in the 409 // text field. This will display either the GAIA given name, if available, 410 // or the first name. 411 ProfileInfoCache& cache = 412 g_browser_process->profile_manager()->GetProfileInfoCache(); 413 size_t profile_index = cache.GetIndexOfProfileWithPath(profile_file_path); 414 if (profile_index == std::string::npos) 415 return; 416 base::string16 gaia_name = cache.GetNameOfProfileAtIndex(profile_index); 417 if (gaia_name.empty()) 418 return; 419 420 base::StringValue gaia_name_value(gaia_name); 421 base::StringValue mode_value(kManageProfileIdentifier); 422 web_ui()->CallJavascriptFunction("ManageProfileOverlay.setProfileName", 423 gaia_name_value, mode_value); 424} 425 426void ManageProfileHandler::RequestHasProfileShortcuts( 427 const base::ListValue* args) { 428 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 429 DCHECK(ProfileShortcutManager::IsFeatureEnabled()); 430 431 base::FilePath profile_file_path; 432 if (!GetProfilePathFromArgs(args, &profile_file_path)) 433 return; 434 435 const ProfileInfoCache& cache = 436 g_browser_process->profile_manager()->GetProfileInfoCache(); 437 size_t profile_index = cache.GetIndexOfProfileWithPath(profile_file_path); 438 if (profile_index == std::string::npos) 439 return; 440 441 const base::FilePath profile_path = 442 cache.GetPathOfProfileAtIndex(profile_index); 443 ProfileShortcutManager* shortcut_manager = 444 g_browser_process->profile_manager()->profile_shortcut_manager(); 445 shortcut_manager->HasProfileShortcuts( 446 profile_path, base::Bind(&ManageProfileHandler::OnHasProfileShortcuts, 447 weak_factory_.GetWeakPtr())); 448} 449 450void ManageProfileHandler::RequestCreateProfileUpdate( 451 const base::ListValue* args) { 452 Profile* profile = Profile::FromWebUI(web_ui()); 453 SigninManagerBase* manager = 454 SigninManagerFactory::GetForProfile(profile); 455 base::string16 username = 456 base::UTF8ToUTF16(manager->GetAuthenticatedUsername()); 457 ProfileSyncService* service = 458 ProfileSyncServiceFactory::GetForProfile(profile); 459 GoogleServiceAuthError::State state = service->GetAuthError().state(); 460 bool has_error = (state == GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS || 461 state == GoogleServiceAuthError::USER_NOT_SIGNED_UP || 462 state == GoogleServiceAuthError::ACCOUNT_DELETED || 463 state == GoogleServiceAuthError::ACCOUNT_DISABLED); 464 web_ui()->CallJavascriptFunction("CreateProfileOverlay.updateSignedInStatus", 465 base::StringValue(username), 466 base::FundamentalValue(has_error)); 467 468 base::DictionaryValue replacements; 469 GenerateSignedinUserSpecificStrings(&replacements); 470 web_ui()->CallJavascriptFunction("loadTimeData.overrideValues", replacements); 471 472 OnCreateManagedUserPrefChange(); 473} 474 475void ManageProfileHandler::OnCreateManagedUserPrefChange() { 476 PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs(); 477 base::FundamentalValue allowed( 478 prefs->GetBoolean(prefs::kManagedUserCreationAllowed)); 479 web_ui()->CallJavascriptFunction( 480 "CreateProfileOverlay.updateManagedUsersAllowed", allowed); 481} 482 483void ManageProfileHandler::OnHasProfileShortcuts(bool has_shortcuts) { 484 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 485 486 const base::FundamentalValue has_shortcuts_value(has_shortcuts); 487 web_ui()->CallJavascriptFunction( 488 "ManageProfileOverlay.receiveHasProfileShortcuts", has_shortcuts_value); 489} 490 491void ManageProfileHandler::AddProfileShortcut(const base::ListValue* args) { 492 base::FilePath profile_file_path; 493 if (!GetProfilePathFromArgs(args, &profile_file_path)) 494 return; 495 496 DCHECK(ProfileShortcutManager::IsFeatureEnabled()); 497 ProfileShortcutManager* shortcut_manager = 498 g_browser_process->profile_manager()->profile_shortcut_manager(); 499 DCHECK(shortcut_manager); 500 501 shortcut_manager->CreateProfileShortcut(profile_file_path); 502 503 // Update the UI buttons. 504 OnHasProfileShortcuts(true); 505} 506 507void ManageProfileHandler::RemoveProfileShortcut(const base::ListValue* args) { 508 base::FilePath profile_file_path; 509 if (!GetProfilePathFromArgs(args, &profile_file_path)) 510 return; 511 512 DCHECK(ProfileShortcutManager::IsFeatureEnabled()); 513 ProfileShortcutManager* shortcut_manager = 514 g_browser_process->profile_manager()->profile_shortcut_manager(); 515 DCHECK(shortcut_manager); 516 517 shortcut_manager->RemoveProfileShortcuts(profile_file_path); 518 519 // Update the UI buttons. 520 OnHasProfileShortcuts(false); 521} 522 523} // namespace options 524