manage_profile_handler.cc revision 3551c9c881056c480085172ff9840cab31610854
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 47namespace options { 48 49namespace { 50 51const char kCreateProfileIconGridName[] = "create-profile-icon-grid"; 52const 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. 56bool 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 64void 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 80ManageProfileHandler::ManageProfileHandler() 81 : weak_factory_(this) { 82} 83 84ManageProfileHandler::~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 92void 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 { "createProfileShortcutCheckbox", IDS_PROFILES_CREATE_SHORTCUT_CHECKBOX }, 121 { "createProfileShortcutButton", IDS_PROFILES_CREATE_SHORTCUT_BUTTON }, 122 { "removeProfileShortcutButton", IDS_PROFILES_REMOVE_SHORTCUT_BUTTON }, 123 }; 124 125 RegisterStrings(localized_strings, resources, arraysize(resources)); 126 RegisterTitle(localized_strings, "manageProfile", 127 IDS_PROFILES_MANAGE_TITLE); 128 RegisterTitle(localized_strings, "createProfile", 129 IDS_PROFILES_CREATE_TITLE); 130 131 localized_strings->SetBoolean("profileShortcutsEnabled", 132 ProfileShortcutManager::IsFeatureEnabled()); 133 localized_strings->SetBoolean("managedUsersEnabled", 134 ManagedUserService::AreManagedUsersEnabled()); 135 136 localized_strings->SetBoolean( 137 "allowCreateExistingManagedUsers", 138 CommandLine::ForCurrentProcess()->HasSwitch( 139 switches::kAllowCreateExistingManagedUsers)); 140} 141 142void ManageProfileHandler::InitializeHandler() { 143 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED, 144 content::NotificationService::AllSources()); 145 146 Profile* profile = Profile::FromWebUI(web_ui()); 147 pref_change_registrar_.Init(profile->GetPrefs()); 148 pref_change_registrar_.Add( 149 prefs::kManagedUserCreationAllowed, 150 base::Bind(&ManageProfileHandler::OnCreateManagedUserPrefChange, 151 base::Unretained(this))); 152 ProfileSyncService* service = 153 ProfileSyncServiceFactory::GetForProfile(profile); 154 // Sync may be disabled for tests. 155 if (service) 156 service->AddObserver(this); 157} 158 159void ManageProfileHandler::InitializePage() { 160 SendProfileNames(); 161 OnCreateManagedUserPrefChange(); 162} 163 164void ManageProfileHandler::RegisterMessages() { 165 web_ui()->RegisterMessageCallback("setProfileIconAndName", 166 base::Bind(&ManageProfileHandler::SetProfileIconAndName, 167 base::Unretained(this))); 168 web_ui()->RegisterMessageCallback("requestDefaultProfileIcons", 169 base::Bind(&ManageProfileHandler::RequestDefaultProfileIcons, 170 base::Unretained(this))); 171 web_ui()->RegisterMessageCallback("requestNewProfileDefaults", 172 base::Bind(&ManageProfileHandler::RequestNewProfileDefaults, 173 base::Unretained(this))); 174 web_ui()->RegisterMessageCallback("requestHasProfileShortcuts", 175 base::Bind(&ManageProfileHandler::RequestHasProfileShortcuts, 176 base::Unretained(this))); 177 web_ui()->RegisterMessageCallback("requestCreateProfileUpdate", 178 base::Bind(&ManageProfileHandler::RequestCreateProfileUpdate, 179 base::Unretained(this))); 180 web_ui()->RegisterMessageCallback("profileIconSelectionChanged", 181 base::Bind(&ManageProfileHandler::ProfileIconSelectionChanged, 182 base::Unretained(this))); 183#if defined(ENABLE_SETTINGS_APP) 184 web_ui()->RegisterMessageCallback("switchAppListProfile", 185 base::Bind(&ManageProfileHandler::SwitchAppListProfile, 186 base::Unretained(this))); 187#endif 188 web_ui()->RegisterMessageCallback("addProfileShortcut", 189 base::Bind(&ManageProfileHandler::AddProfileShortcut, 190 base::Unretained(this))); 191 web_ui()->RegisterMessageCallback("removeProfileShortcut", 192 base::Bind(&ManageProfileHandler::RemoveProfileShortcut, 193 base::Unretained(this))); 194 web_ui()->RegisterMessageCallback("requestExistingManagedUsers", 195 base::Bind(&ManageProfileHandler::RequestExistingManagedUsers, 196 base::Unretained(this))); 197} 198 199void ManageProfileHandler::Observe( 200 int type, 201 const content::NotificationSource& source, 202 const content::NotificationDetails& details) { 203 if (type == chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED) { 204 // If the browser shuts down during supervised-profile creation, deleting 205 // the unregistered supervised-user profile triggers this notification, 206 // but the RenderViewHost the profile info would be sent to has already been 207 // destroyed. 208 if (!web_ui()->GetWebContents()->GetRenderViewHost()) 209 return; 210 SendProfileNames(); 211 base::StringValue value(kManageProfileIconGridName); 212 SendProfileIcons(value); 213 } else { 214 OptionsPageUIHandler::Observe(type, source, details); 215 } 216} 217 218void ManageProfileHandler::OnStateChanged() { 219 RequestCreateProfileUpdate(NULL); 220} 221 222void ManageProfileHandler::RequestDefaultProfileIcons(const ListValue* args) { 223 base::StringValue create_value(kCreateProfileIconGridName); 224 base::StringValue manage_value(kManageProfileIconGridName); 225 SendProfileIcons(manage_value); 226 SendProfileIcons(create_value); 227} 228 229void ManageProfileHandler::RequestNewProfileDefaults(const ListValue* args) { 230 const ProfileInfoCache& cache = 231 g_browser_process->profile_manager()->GetProfileInfoCache(); 232 const size_t icon_index = cache.ChooseAvatarIconIndexForNewProfile(); 233 234 DictionaryValue profile_info; 235 profile_info.SetString("name", cache.ChooseNameForNewProfile(icon_index)); 236 profile_info.SetString("iconURL", cache.GetDefaultAvatarIconUrl(icon_index)); 237 238 web_ui()->CallJavascriptFunction( 239 "ManageProfileOverlay.receiveNewProfileDefaults", profile_info); 240} 241 242void ManageProfileHandler::RequestExistingManagedUsers(const ListValue* args) { 243 Profile* profile = Profile::FromWebUI(web_ui()); 244 if (profile->IsManaged()) 245 return; 246 247 const ProfileInfoCache& cache = 248 g_browser_process->profile_manager()->GetProfileInfoCache(); 249 std::set<std::string> managed_user_ids; 250 for (size_t i = 0; i < cache.GetNumberOfProfiles(); ++i) 251 managed_user_ids.insert(cache.GetManagedUserIdOfProfileAtIndex(i)); 252 253 const DictionaryValue* dict = 254 profile->GetPrefs()->GetDictionary(prefs::kManagedUsers); 255 DictionaryValue id_names_dict; 256 for (DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) { 257 if (managed_user_ids.find(it.key()) != managed_user_ids.end()) 258 continue; 259 const DictionaryValue* value = NULL; 260 bool success = it.value().GetAsDictionary(&value); 261 DCHECK(success); 262 std::string name; 263 value->GetString("name", &name); 264 id_names_dict.SetString(it.key(), name); 265 } 266 267 web_ui()->CallJavascriptFunction( 268 "CreateProfileOverlay.receiveExistingManagedUsers", id_names_dict); 269} 270 271void ManageProfileHandler::SendProfileIcons( 272 const base::StringValue& icon_grid) { 273 ListValue image_url_list; 274 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); 283 if (icon) { 284 gfx::Image icon2 = profiles::GetAvatarIconForWebUI(*icon, true); 285 gaia_picture_url_ = webui::GetBitmapDataUrl(icon2.AsBitmap()); 286 image_url_list.Append(new base::StringValue(gaia_picture_url_)); 287 } 288 } 289 290 // Next add the default avatar icons. 291 for (size_t i = 0; i < ProfileInfoCache::GetDefaultAvatarIconCount(); i++) { 292 std::string url = ProfileInfoCache::GetDefaultAvatarIconUrl(i); 293 image_url_list.Append(new base::StringValue(url)); 294 } 295 296 web_ui()->CallJavascriptFunction( 297 "ManageProfileOverlay.receiveDefaultProfileIcons", icon_grid, 298 image_url_list); 299} 300 301void ManageProfileHandler::SendProfileNames() { 302 const ProfileInfoCache& cache = 303 g_browser_process->profile_manager()->GetProfileInfoCache(); 304 DictionaryValue profile_name_dict; 305 for (size_t i = 0, e = cache.GetNumberOfProfiles(); i < e; ++i) 306 profile_name_dict.SetBoolean(UTF16ToUTF8(cache.GetNameOfProfileAtIndex(i)), 307 true); 308 309 web_ui()->CallJavascriptFunction("ManageProfileOverlay.receiveProfileNames", 310 profile_name_dict); 311} 312 313void ManageProfileHandler::SetProfileIconAndName(const 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 (cache.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 string16 new_profile_name; 362 if (!args->GetString(2, &new_profile_name)) 363 return; 364 365 if (new_profile_name == cache.GetGAIANameOfProfileAtIndex(profile_index)) { 366 // Set the profile to use the GAIA name as the profile name. Note, this 367 // is a little weird if the user typed their GAIA name manually but 368 // it's not a big deal. 369 cache.SetIsUsingGAIANameOfProfileAtIndex(profile_index, true); 370 } else { 371 PrefService* pref_service = profile->GetPrefs(); 372 // Updating the profile preference will cause the cache to be updated for 373 // this preference. 374 pref_service->SetString(prefs::kProfileName, UTF16ToUTF8(new_profile_name)); 375 376 // Changing the profile name can invalidate the profile index. 377 profile_index = cache.GetIndexOfProfileWithPath(profile_file_path); 378 if (profile_index == std::string::npos) 379 return; 380 381 cache.SetIsUsingGAIANameOfProfileAtIndex(profile_index, false); 382 } 383} 384 385#if defined(ENABLE_SETTINGS_APP) 386void ManageProfileHandler::SwitchAppListProfile(const ListValue* args) { 387 DCHECK(args); 388 DCHECK(profiles::IsMultipleProfilesEnabled()); 389 390 const Value* file_path_value; 391 base::FilePath profile_file_path; 392 if (!args->Get(0, &file_path_value) || 393 !base::GetValueAsFilePath(*file_path_value, &profile_file_path)) 394 return; 395 396 AppListService* app_list_service = AppListService::Get(); 397 app_list_service->SetProfilePath(profile_file_path); 398 app_list_service->Show(); 399 400 // Close the settings app, since it will now be for the wrong profile. 401 web_ui()->GetWebContents()->Close(); 402} 403#endif // defined(ENABLE_SETTINGS_APP) 404 405void ManageProfileHandler::ProfileIconSelectionChanged( 406 const base::ListValue* args) { 407 DCHECK(args); 408 409 base::FilePath profile_file_path; 410 if (!GetProfilePathFromArgs(args, &profile_file_path)) 411 return; 412 413 // Currently this only supports editing the current profile's info. 414 if (profile_file_path != Profile::FromWebUI(web_ui())->GetPath()) 415 return; 416 417 std::string icon_url; 418 if (!args->GetString(1, &icon_url)) 419 return; 420 421 if (icon_url != gaia_picture_url_) 422 return; 423 424 // If the selection is the GAIA picture then also show the GAIA name in the 425 // text field. 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 string16 gaia_name = cache.GetGAIANameOfProfileAtIndex(profile_index); 432 if (gaia_name.empty()) 433 return; 434 435 StringValue gaia_name_value(gaia_name); 436 web_ui()->CallJavascriptFunction("ManageProfileOverlay.setProfileName", 437 gaia_name_value); 438} 439 440void ManageProfileHandler::RequestHasProfileShortcuts(const ListValue* args) { 441 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 442 DCHECK(ProfileShortcutManager::IsFeatureEnabled()); 443 444 base::FilePath profile_file_path; 445 if (!GetProfilePathFromArgs(args, &profile_file_path)) 446 return; 447 448 const ProfileInfoCache& cache = 449 g_browser_process->profile_manager()->GetProfileInfoCache(); 450 size_t profile_index = cache.GetIndexOfProfileWithPath(profile_file_path); 451 if (profile_index == std::string::npos) 452 return; 453 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())); 461} 462 463void ManageProfileHandler::RequestCreateProfileUpdate( 464 const base::ListValue* args) { 465 Profile* profile = Profile::FromWebUI(web_ui()); 466 SigninManagerBase* manager = 467 SigninManagerFactory::GetForProfile(profile); 468 string16 username = UTF8ToUTF16(manager->GetAuthenticatedUsername()); 469 ProfileSyncService* service = 470 ProfileSyncServiceFactory::GetForProfile(profile); 471 GoogleServiceAuthError::State state = service->GetAuthError().state(); 472 bool has_error = (state == GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS || 473 state == GoogleServiceAuthError::USER_NOT_SIGNED_UP || 474 state == GoogleServiceAuthError::ACCOUNT_DELETED || 475 state == GoogleServiceAuthError::ACCOUNT_DISABLED); 476 web_ui()->CallJavascriptFunction("CreateProfileOverlay.updateSignedInStatus", 477 base::StringValue(username), 478 base::FundamentalValue(has_error)); 479 OnCreateManagedUserPrefChange(); 480} 481 482void ManageProfileHandler::OnCreateManagedUserPrefChange() { 483 PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs(); 484 base::FundamentalValue allowed( 485 prefs->GetBoolean(prefs::kManagedUserCreationAllowed)); 486 web_ui()->CallJavascriptFunction( 487 "CreateProfileOverlay.updateManagedUsersAllowed", allowed); 488} 489 490void ManageProfileHandler::OnHasProfileShortcuts(bool has_shortcuts) { 491 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 492 493 const base::FundamentalValue has_shortcuts_value(has_shortcuts); 494 web_ui()->CallJavascriptFunction( 495 "ManageProfileOverlay.receiveHasProfileShortcuts", has_shortcuts_value); 496} 497 498void ManageProfileHandler::AddProfileShortcut(const base::ListValue* args) { 499 base::FilePath profile_file_path; 500 if (!GetProfilePathFromArgs(args, &profile_file_path)) 501 return; 502 503 DCHECK(ProfileShortcutManager::IsFeatureEnabled()); 504 ProfileShortcutManager* shortcut_manager = 505 g_browser_process->profile_manager()->profile_shortcut_manager(); 506 DCHECK(shortcut_manager); 507 508 shortcut_manager->CreateProfileShortcut(profile_file_path); 509 510 // Update the UI buttons. 511 OnHasProfileShortcuts(true); 512} 513 514void ManageProfileHandler::RemoveProfileShortcut(const base::ListValue* args) { 515 base::FilePath profile_file_path; 516 if (!GetProfilePathFromArgs(args, &profile_file_path)) 517 return; 518 519 DCHECK(ProfileShortcutManager::IsFeatureEnabled()); 520 ProfileShortcutManager* shortcut_manager = 521 g_browser_process->profile_manager()->profile_shortcut_manager(); 522 DCHECK(shortcut_manager); 523 524 shortcut_manager->RemoveProfileShortcuts(profile_file_path); 525 526 // Update the UI buttons. 527 OnHasProfileShortcuts(false); 528} 529 530} // namespace options 531