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/chrome_notification_types.h" 17 #include "chrome/browser/managed_mode/managed_user_service.h" 18 #include "chrome/browser/prefs/scoped_user_pref_update.h" 19 #include "chrome/browser/profiles/gaia_info_update_service.h" 20 #include "chrome/browser/profiles/profile.h" 21 #include "chrome/browser/profiles/profile_info_cache.h" 22 #include "chrome/browser/profiles/profile_info_util.h" 23 #include "chrome/browser/profiles/profile_manager.h" 24 #include "chrome/browser/profiles/profile_metrics.h" 25 #include "chrome/browser/profiles/profile_shortcut_manager.h" 26 #include "chrome/browser/profiles/profile_window.h" 27 #include "chrome/browser/profiles/profiles_state.h" 28 #include "chrome/browser/signin/signin_manager.h" 29 #include "chrome/browser/signin/signin_manager_factory.h" 30 #include "chrome/browser/sync/profile_sync_service.h" 31 #include "chrome/browser/sync/profile_sync_service_factory.h" 32 #include "chrome/browser/ui/browser_finder.h" 33 #include "chrome/common/chrome_switches.h" 34 #include "chrome/common/pref_names.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 "grit/generated_resources.h" 39 #include "ui/base/l10n/l10n_util.h" 40 #include "ui/webui/web_ui_util.h" 41 42 #if defined(ENABLE_SETTINGS_APP) 43 #include "chrome/browser/ui/app_list/app_list_service.h" 44 #include "content/public/browser/web_contents.h" 45 #endif 46 47 namespace options { 48 49 namespace { 50 51 const char kCreateProfileIconGridName[] = "create-profile-icon-grid"; 52 const char kManageProfileIconGridName[] = "manage-profile-icon-grid"; 53 54 // Given |args| from the WebUI, parses value 0 as a FilePath |profile_file_path| 55 // and returns true on success. 56 bool GetProfilePathFromArgs(const ListValue* args, 57 base::FilePath* profile_file_path) { 58 const Value* file_path_value; 59 if (!args->Get(0, &file_path_value)) 60 return false; 61 return base::GetValueAsFilePath(*file_path_value, profile_file_path); 62 } 63 64 void OnNewDefaultProfileCreated( 65 chrome::HostDesktopType desktop_type, 66 Profile* profile, 67 Profile::CreateStatus status) { 68 if (status == Profile::CREATE_STATUS_INITIALIZED) { 69 profiles::FindOrCreateNewWindowForProfile( 70 profile, 71 chrome::startup::IS_PROCESS_STARTUP, 72 chrome::startup::IS_FIRST_RUN, 73 desktop_type, 74 false); 75 } 76 } 77 78 } // namespace 79 80 ManageProfileHandler::ManageProfileHandler() 81 : weak_factory_(this) { 82 } 83 84 ManageProfileHandler::~ManageProfileHandler() { 85 ProfileSyncService* service = 86 ProfileSyncServiceFactory::GetForProfile(Profile::FromWebUI(web_ui())); 87 // Sync may be disabled in tests. 88 if (service) 89 service->RemoveObserver(this); 90 } 91 92 void ManageProfileHandler::GetLocalizedValues( 93 DictionaryValue* localized_strings) { 94 DCHECK(localized_strings); 95 96 static OptionsStringResource resources[] = { 97 { "manageProfilesNameLabel", IDS_PROFILES_MANAGE_NAME_LABEL }, 98 { "manageProfilesDuplicateNameError", 99 IDS_PROFILES_MANAGE_DUPLICATE_NAME_ERROR }, 100 { "manageProfilesIconLabel", IDS_PROFILES_MANAGE_ICON_LABEL }, 101 { "manageProfilesManagedSignedInLabel", 102 IDS_PROFILES_CREATE_MANAGED_SIGNED_IN_LABEL }, 103 { "manageProfilesManagedNotSignedInLabel", 104 IDS_PROFILES_CREATE_MANAGED_NOT_SIGNED_IN_LABEL }, 105 { "manageProfilesManagedAccountDetailsOutOfDate", 106 IDS_PROFILES_CREATE_MANAGED_ACCOUNT_DETAILS_OUT_OF_DATE_LABEL }, 107 { "manageProfilesManagedSignInAgainLink", 108 IDS_PROFILES_CREATE_MANAGED_ACCOUNT_SIGN_IN_AGAIN_LINK }, 109 { "manageProfilesManagedNotSignedInLink", 110 IDS_PROFILES_CREATE_MANAGED_NOT_SIGNED_IN_LINK }, 111 { "manageProfilesSelectExistingManagedProfileLabel", 112 IDS_PROFILES_CREATE_MANAGED_FROM_EXISTING_LABEL}, 113 { "deleteProfileTitle", IDS_PROFILES_DELETE_TITLE }, 114 { "deleteProfileOK", IDS_PROFILES_DELETE_OK_BUTTON_LABEL }, 115 { "deleteProfileMessage", IDS_PROFILES_DELETE_MESSAGE }, 116 { "deleteManagedProfileAddendum", IDS_PROFILES_DELETE_MANAGED_ADDENDUM }, 117 { "createProfileTitle", IDS_PROFILES_CREATE_TITLE }, 118 { "createProfileInstructions", IDS_PROFILES_CREATE_INSTRUCTIONS }, 119 { "createProfileConfirm", IDS_PROFILES_CREATE_CONFIRM }, 120 { "createProfileLocalError", IDS_PROFILES_CREATE_LOCAL_ERROR }, 121 { "createProfileRemoteError", IDS_PROFILES_CREATE_REMOTE_ERROR }, 122 { "createProfileShortcutCheckbox", IDS_PROFILES_CREATE_SHORTCUT_CHECKBOX }, 123 { "createProfileShortcutButton", IDS_PROFILES_CREATE_SHORTCUT_BUTTON }, 124 { "removeProfileShortcutButton", IDS_PROFILES_REMOVE_SHORTCUT_BUTTON }, 125 }; 126 127 RegisterStrings(localized_strings, resources, arraysize(resources)); 128 RegisterTitle(localized_strings, "manageProfile", 129 IDS_PROFILES_MANAGE_TITLE); 130 RegisterTitle(localized_strings, "createProfile", 131 IDS_PROFILES_CREATE_TITLE); 132 133 localized_strings->SetBoolean("profileShortcutsEnabled", 134 ProfileShortcutManager::IsFeatureEnabled()); 135 localized_strings->SetBoolean("managedUsersEnabled", 136 ManagedUserService::AreManagedUsersEnabled()); 137 138 localized_strings->SetBoolean( 139 "allowCreateExistingManagedUsers", 140 CommandLine::ForCurrentProcess()->HasSwitch( 141 switches::kAllowCreateExistingManagedUsers)); 142 } 143 144 void ManageProfileHandler::InitializeHandler() { 145 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED, 146 content::NotificationService::AllSources()); 147 148 Profile* profile = Profile::FromWebUI(web_ui()); 149 pref_change_registrar_.Init(profile->GetPrefs()); 150 pref_change_registrar_.Add( 151 prefs::kManagedUserCreationAllowed, 152 base::Bind(&ManageProfileHandler::OnCreateManagedUserPrefChange, 153 base::Unretained(this))); 154 ProfileSyncService* service = 155 ProfileSyncServiceFactory::GetForProfile(profile); 156 // Sync may be disabled for tests. 157 if (service) 158 service->AddObserver(this); 159 } 160 161 void ManageProfileHandler::InitializePage() { 162 SendProfileNames(); 163 OnCreateManagedUserPrefChange(); 164 } 165 166 void ManageProfileHandler::RegisterMessages() { 167 web_ui()->RegisterMessageCallback("setProfileIconAndName", 168 base::Bind(&ManageProfileHandler::SetProfileIconAndName, 169 base::Unretained(this))); 170 web_ui()->RegisterMessageCallback("requestDefaultProfileIcons", 171 base::Bind(&ManageProfileHandler::RequestDefaultProfileIcons, 172 base::Unretained(this))); 173 web_ui()->RegisterMessageCallback("requestNewProfileDefaults", 174 base::Bind(&ManageProfileHandler::RequestNewProfileDefaults, 175 base::Unretained(this))); 176 web_ui()->RegisterMessageCallback("requestHasProfileShortcuts", 177 base::Bind(&ManageProfileHandler::RequestHasProfileShortcuts, 178 base::Unretained(this))); 179 web_ui()->RegisterMessageCallback("requestCreateProfileUpdate", 180 base::Bind(&ManageProfileHandler::RequestCreateProfileUpdate, 181 base::Unretained(this))); 182 web_ui()->RegisterMessageCallback("profileIconSelectionChanged", 183 base::Bind(&ManageProfileHandler::ProfileIconSelectionChanged, 184 base::Unretained(this))); 185 #if defined(ENABLE_SETTINGS_APP) 186 web_ui()->RegisterMessageCallback("switchAppListProfile", 187 base::Bind(&ManageProfileHandler::SwitchAppListProfile, 188 base::Unretained(this))); 189 #endif 190 web_ui()->RegisterMessageCallback("addProfileShortcut", 191 base::Bind(&ManageProfileHandler::AddProfileShortcut, 192 base::Unretained(this))); 193 web_ui()->RegisterMessageCallback("removeProfileShortcut", 194 base::Bind(&ManageProfileHandler::RemoveProfileShortcut, 195 base::Unretained(this))); 196 web_ui()->RegisterMessageCallback("requestExistingManagedUsers", 197 base::Bind(&ManageProfileHandler::RequestExistingManagedUsers, 198 base::Unretained(this))); 199 } 200 201 void ManageProfileHandler::Observe( 202 int type, 203 const content::NotificationSource& source, 204 const content::NotificationDetails& details) { 205 if (type == chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED) { 206 // If the browser shuts down during supervised-profile creation, deleting 207 // the unregistered supervised-user profile triggers this notification, 208 // but the RenderViewHost the profile info would be sent to has already been 209 // destroyed. 210 if (!web_ui()->GetWebContents()->GetRenderViewHost()) 211 return; 212 SendProfileNames(); 213 base::StringValue value(kManageProfileIconGridName); 214 SendProfileIcons(value); 215 } else { 216 OptionsPageUIHandler::Observe(type, source, details); 217 } 218 } 219 220 void ManageProfileHandler::OnStateChanged() { 221 RequestCreateProfileUpdate(NULL); 222 } 223 224 void ManageProfileHandler::RequestDefaultProfileIcons(const ListValue* args) { 225 base::StringValue create_value(kCreateProfileIconGridName); 226 base::StringValue manage_value(kManageProfileIconGridName); 227 SendProfileIcons(manage_value); 228 SendProfileIcons(create_value); 229 } 230 231 void ManageProfileHandler::RequestNewProfileDefaults(const ListValue* args) { 232 const ProfileInfoCache& cache = 233 g_browser_process->profile_manager()->GetProfileInfoCache(); 234 const size_t icon_index = cache.ChooseAvatarIconIndexForNewProfile(); 235 236 DictionaryValue profile_info; 237 profile_info.SetString("name", cache.ChooseNameForNewProfile(icon_index)); 238 profile_info.SetString("iconURL", cache.GetDefaultAvatarIconUrl(icon_index)); 239 240 web_ui()->CallJavascriptFunction( 241 "ManageProfileOverlay.receiveNewProfileDefaults", profile_info); 242 } 243 244 void ManageProfileHandler::RequestExistingManagedUsers(const ListValue* args) { 245 Profile* profile = Profile::FromWebUI(web_ui()); 246 if (profile->IsManaged()) 247 return; 248 249 const ProfileInfoCache& cache = 250 g_browser_process->profile_manager()->GetProfileInfoCache(); 251 std::set<std::string> managed_user_ids; 252 for (size_t i = 0; i < cache.GetNumberOfProfiles(); ++i) 253 managed_user_ids.insert(cache.GetManagedUserIdOfProfileAtIndex(i)); 254 255 const DictionaryValue* dict = 256 profile->GetPrefs()->GetDictionary(prefs::kManagedUsers); 257 DictionaryValue id_names_dict; 258 for (DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) { 259 if (managed_user_ids.find(it.key()) != managed_user_ids.end()) 260 continue; 261 const DictionaryValue* value = NULL; 262 bool success = it.value().GetAsDictionary(&value); 263 DCHECK(success); 264 std::string name; 265 value->GetString("name", &name); 266 id_names_dict.SetString(it.key(), name); 267 } 268 269 web_ui()->CallJavascriptFunction( 270 "CreateProfileOverlay.receiveExistingManagedUsers", id_names_dict); 271 } 272 273 void ManageProfileHandler::SendProfileIcons( 274 const base::StringValue& icon_grid) { 275 ListValue image_url_list; 276 277 // First add the GAIA picture if it's available. 278 const ProfileInfoCache& cache = 279 g_browser_process->profile_manager()->GetProfileInfoCache(); 280 Profile* profile = Profile::FromWebUI(web_ui()); 281 size_t profile_index = cache.GetIndexOfProfileWithPath(profile->GetPath()); 282 if (profile_index != std::string::npos) { 283 const gfx::Image* icon = 284 cache.GetGAIAPictureOfProfileAtIndex(profile_index); 285 if (icon) { 286 gfx::Image icon2 = profiles::GetAvatarIconForWebUI(*icon, true); 287 gaia_picture_url_ = webui::GetBitmapDataUrl(icon2.AsBitmap()); 288 image_url_list.Append(new base::StringValue(gaia_picture_url_)); 289 } 290 } 291 292 // Next add the default avatar icons. 293 for (size_t i = 0; i < ProfileInfoCache::GetDefaultAvatarIconCount(); i++) { 294 std::string url = ProfileInfoCache::GetDefaultAvatarIconUrl(i); 295 image_url_list.Append(new base::StringValue(url)); 296 } 297 298 web_ui()->CallJavascriptFunction( 299 "ManageProfileOverlay.receiveDefaultProfileIcons", icon_grid, 300 image_url_list); 301 } 302 303 void ManageProfileHandler::SendProfileNames() { 304 const ProfileInfoCache& cache = 305 g_browser_process->profile_manager()->GetProfileInfoCache(); 306 DictionaryValue profile_name_dict; 307 for (size_t i = 0, e = cache.GetNumberOfProfiles(); i < e; ++i) 308 profile_name_dict.SetBoolean(UTF16ToUTF8(cache.GetNameOfProfileAtIndex(i)), 309 true); 310 311 web_ui()->CallJavascriptFunction("ManageProfileOverlay.receiveProfileNames", 312 profile_name_dict); 313 } 314 315 void ManageProfileHandler::SetProfileIconAndName(const ListValue* args) { 316 DCHECK(args); 317 318 base::FilePath profile_file_path; 319 if (!GetProfilePathFromArgs(args, &profile_file_path)) 320 return; 321 322 ProfileInfoCache& cache = 323 g_browser_process->profile_manager()->GetProfileInfoCache(); 324 size_t profile_index = cache.GetIndexOfProfileWithPath(profile_file_path); 325 if (profile_index == std::string::npos) 326 return; 327 328 Profile* profile = 329 g_browser_process->profile_manager()->GetProfile(profile_file_path); 330 if (!profile) 331 return; 332 333 std::string icon_url; 334 if (!args->GetString(1, &icon_url)) 335 return; 336 337 // Metrics logging variable. 338 bool previously_using_gaia_icon = 339 cache.IsUsingGAIAPictureOfProfileAtIndex(profile_index); 340 341 size_t new_icon_index; 342 if (icon_url == gaia_picture_url_) { 343 cache.SetIsUsingGAIAPictureOfProfileAtIndex(profile_index, true); 344 if (!previously_using_gaia_icon) { 345 // Only log if they changed to the GAIA photo. 346 // Selection of GAIA photo as avatar is logged as part of the function 347 // below. 348 ProfileMetrics::LogProfileSwitchGaia(ProfileMetrics::GAIA_OPT_IN); 349 } 350 } else if (cache.IsDefaultAvatarIconUrl(icon_url, &new_icon_index)) { 351 ProfileMetrics::LogProfileAvatarSelection(new_icon_index); 352 PrefService* pref_service = profile->GetPrefs(); 353 // Updating the profile preference will cause the cache to be updated for 354 // this preference. 355 pref_service->SetInteger(prefs::kProfileAvatarIndex, new_icon_index); 356 cache.SetIsUsingGAIAPictureOfProfileAtIndex(profile_index, false); 357 } 358 ProfileMetrics::LogProfileUpdate(profile_file_path); 359 360 if (profile->IsManaged()) 361 return; 362 363 string16 new_profile_name; 364 if (!args->GetString(2, &new_profile_name)) 365 return; 366 367 if (new_profile_name == cache.GetGAIANameOfProfileAtIndex(profile_index)) { 368 // Set the profile to use the GAIA name as the profile name. Note, this 369 // is a little weird if the user typed their GAIA name manually but 370 // it's not a big deal. 371 cache.SetIsUsingGAIANameOfProfileAtIndex(profile_index, true); 372 } else { 373 PrefService* pref_service = profile->GetPrefs(); 374 // Updating the profile preference will cause the cache to be updated for 375 // this preference. 376 pref_service->SetString(prefs::kProfileName, UTF16ToUTF8(new_profile_name)); 377 378 // Changing the profile name can invalidate the profile index. 379 profile_index = cache.GetIndexOfProfileWithPath(profile_file_path); 380 if (profile_index == std::string::npos) 381 return; 382 383 cache.SetIsUsingGAIANameOfProfileAtIndex(profile_index, false); 384 } 385 } 386 387 #if defined(ENABLE_SETTINGS_APP) 388 void ManageProfileHandler::SwitchAppListProfile(const ListValue* args) { 389 DCHECK(args); 390 DCHECK(profiles::IsMultipleProfilesEnabled()); 391 392 const Value* file_path_value; 393 base::FilePath profile_file_path; 394 if (!args->Get(0, &file_path_value) || 395 !base::GetValueAsFilePath(*file_path_value, &profile_file_path)) 396 return; 397 398 AppListService* app_list_service = AppListService::Get(); 399 app_list_service->SetProfilePath(profile_file_path); 400 app_list_service->Show(); 401 402 // Close the settings app, since it will now be for the wrong profile. 403 web_ui()->GetWebContents()->Close(); 404 } 405 #endif // defined(ENABLE_SETTINGS_APP) 406 407 void ManageProfileHandler::ProfileIconSelectionChanged( 408 const base::ListValue* args) { 409 DCHECK(args); 410 411 base::FilePath profile_file_path; 412 if (!GetProfilePathFromArgs(args, &profile_file_path)) 413 return; 414 415 // Currently this only supports editing the current profile's info. 416 if (profile_file_path != Profile::FromWebUI(web_ui())->GetPath()) 417 return; 418 419 std::string icon_url; 420 if (!args->GetString(1, &icon_url)) 421 return; 422 423 if (icon_url != gaia_picture_url_) 424 return; 425 426 // If the selection is the GAIA picture then also show the GAIA name in the 427 // text field. 428 ProfileInfoCache& cache = 429 g_browser_process->profile_manager()->GetProfileInfoCache(); 430 size_t profile_index = cache.GetIndexOfProfileWithPath(profile_file_path); 431 if (profile_index == std::string::npos) 432 return; 433 string16 gaia_name = cache.GetGAIANameOfProfileAtIndex(profile_index); 434 if (gaia_name.empty()) 435 return; 436 437 StringValue gaia_name_value(gaia_name); 438 web_ui()->CallJavascriptFunction("ManageProfileOverlay.setProfileName", 439 gaia_name_value); 440 } 441 442 void ManageProfileHandler::RequestHasProfileShortcuts(const ListValue* args) { 443 DCHECK(content::BrowserThread::CurrentlyOn(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 465 void ManageProfileHandler::RequestCreateProfileUpdate( 466 const base::ListValue* args) { 467 Profile* profile = Profile::FromWebUI(web_ui()); 468 SigninManagerBase* manager = 469 SigninManagerFactory::GetForProfile(profile); 470 string16 username = UTF8ToUTF16(manager->GetAuthenticatedUsername()); 471 ProfileSyncService* service = 472 ProfileSyncServiceFactory::GetForProfile(profile); 473 GoogleServiceAuthError::State state = service->GetAuthError().state(); 474 bool has_error = (state == GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS || 475 state == GoogleServiceAuthError::USER_NOT_SIGNED_UP || 476 state == GoogleServiceAuthError::ACCOUNT_DELETED || 477 state == GoogleServiceAuthError::ACCOUNT_DISABLED); 478 web_ui()->CallJavascriptFunction("CreateProfileOverlay.updateSignedInStatus", 479 base::StringValue(username), 480 base::FundamentalValue(has_error)); 481 OnCreateManagedUserPrefChange(); 482 } 483 484 void ManageProfileHandler::OnCreateManagedUserPrefChange() { 485 PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs(); 486 base::FundamentalValue allowed( 487 prefs->GetBoolean(prefs::kManagedUserCreationAllowed)); 488 web_ui()->CallJavascriptFunction( 489 "CreateProfileOverlay.updateManagedUsersAllowed", allowed); 490 } 491 492 void ManageProfileHandler::OnHasProfileShortcuts(bool has_shortcuts) { 493 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 494 495 const base::FundamentalValue has_shortcuts_value(has_shortcuts); 496 web_ui()->CallJavascriptFunction( 497 "ManageProfileOverlay.receiveHasProfileShortcuts", has_shortcuts_value); 498 } 499 500 void ManageProfileHandler::AddProfileShortcut(const base::ListValue* args) { 501 base::FilePath profile_file_path; 502 if (!GetProfilePathFromArgs(args, &profile_file_path)) 503 return; 504 505 DCHECK(ProfileShortcutManager::IsFeatureEnabled()); 506 ProfileShortcutManager* shortcut_manager = 507 g_browser_process->profile_manager()->profile_shortcut_manager(); 508 DCHECK(shortcut_manager); 509 510 shortcut_manager->CreateProfileShortcut(profile_file_path); 511 512 // Update the UI buttons. 513 OnHasProfileShortcuts(true); 514 } 515 516 void ManageProfileHandler::RemoveProfileShortcut(const base::ListValue* args) { 517 base::FilePath profile_file_path; 518 if (!GetProfilePathFromArgs(args, &profile_file_path)) 519 return; 520 521 DCHECK(ProfileShortcutManager::IsFeatureEnabled()); 522 ProfileShortcutManager* shortcut_manager = 523 g_browser_process->profile_manager()->profile_shortcut_manager(); 524 DCHECK(shortcut_manager); 525 526 shortcut_manager->RemoveProfileShortcuts(profile_file_path); 527 528 // Update the UI buttons. 529 OnHasProfileShortcuts(false); 530 } 531 532 } // namespace options 533