1 // Copyright (c) 2012 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/profiles/profile_info_cache.h" 6 7 #include "base/bind.h" 8 #include "base/files/file_util.h" 9 #include "base/i18n/case_conversion.h" 10 #include "base/logging.h" 11 #include "base/memory/scoped_ptr.h" 12 #include "base/prefs/pref_registry_simple.h" 13 #include "base/prefs/pref_service.h" 14 #include "base/prefs/scoped_user_pref_update.h" 15 #include "base/rand_util.h" 16 #include "base/stl_util.h" 17 #include "base/strings/string_number_conversions.h" 18 #include "base/strings/string_piece.h" 19 #include "base/strings/utf_string_conversions.h" 20 #include "base/values.h" 21 #include "chrome/browser/browser_process.h" 22 #include "chrome/browser/chrome_notification_types.h" 23 #include "chrome/browser/profiles/profile_avatar_downloader.h" 24 #include "chrome/browser/profiles/profile_avatar_icon_util.h" 25 #include "chrome/browser/profiles/profiles_state.h" 26 #include "chrome/common/pref_names.h" 27 #include "chrome/grit/generated_resources.h" 28 #include "components/signin/core/common/profile_management_switches.h" 29 #include "content/public/browser/browser_thread.h" 30 #include "content/public/browser/notification_service.h" 31 #include "ui/base/l10n/l10n_util.h" 32 #include "ui/base/resource/resource_bundle.h" 33 #include "ui/gfx/image/image.h" 34 #include "ui/gfx/image/image_util.h" 35 36 using content::BrowserThread; 37 38 namespace { 39 40 const char kNameKey[] = "name"; 41 const char kShortcutNameKey[] = "shortcut_name"; 42 const char kGAIANameKey[] = "gaia_name"; 43 const char kGAIAGivenNameKey[] = "gaia_given_name"; 44 const char kUserNameKey[] = "user_name"; 45 const char kIsUsingDefaultNameKey[] = "is_using_default_name"; 46 const char kIsUsingDefaultAvatarKey[] = "is_using_default_avatar"; 47 const char kAvatarIconKey[] = "avatar_icon"; 48 const char kAuthCredentialsKey[] = "local_auth_credentials"; 49 const char kUseGAIAPictureKey[] = "use_gaia_picture"; 50 const char kBackgroundAppsKey[] = "background_apps"; 51 const char kGAIAPictureFileNameKey[] = "gaia_picture_file_name"; 52 const char kIsOmittedFromProfileListKey[] = "is_omitted_from_profile_list"; 53 const char kSigninRequiredKey[] = "signin_required"; 54 const char kSupervisedUserId[] = "managed_user_id"; 55 const char kProfileIsEphemeral[] = "is_ephemeral"; 56 const char kActiveTimeKey[] = "active_time"; 57 58 // First eight are generic icons, which use IDS_NUMBERED_PROFILE_NAME. 59 const int kDefaultNames[] = { 60 IDS_DEFAULT_AVATAR_NAME_8, 61 IDS_DEFAULT_AVATAR_NAME_9, 62 IDS_DEFAULT_AVATAR_NAME_10, 63 IDS_DEFAULT_AVATAR_NAME_11, 64 IDS_DEFAULT_AVATAR_NAME_12, 65 IDS_DEFAULT_AVATAR_NAME_13, 66 IDS_DEFAULT_AVATAR_NAME_14, 67 IDS_DEFAULT_AVATAR_NAME_15, 68 IDS_DEFAULT_AVATAR_NAME_16, 69 IDS_DEFAULT_AVATAR_NAME_17, 70 IDS_DEFAULT_AVATAR_NAME_18, 71 IDS_DEFAULT_AVATAR_NAME_19, 72 IDS_DEFAULT_AVATAR_NAME_20, 73 IDS_DEFAULT_AVATAR_NAME_21, 74 IDS_DEFAULT_AVATAR_NAME_22, 75 IDS_DEFAULT_AVATAR_NAME_23, 76 IDS_DEFAULT_AVATAR_NAME_24, 77 IDS_DEFAULT_AVATAR_NAME_25, 78 IDS_DEFAULT_AVATAR_NAME_26 79 }; 80 81 typedef std::vector<unsigned char> ImageData; 82 83 // Writes |data| to disk and takes ownership of the pointer. On successful 84 // completion, it runs |callback|. 85 void SaveBitmap(scoped_ptr<ImageData> data, 86 const base::FilePath& image_path, 87 const base::Closure& callback) { 88 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 89 90 // Make sure the destination directory exists. 91 base::FilePath dir = image_path.DirName(); 92 if (!base::DirectoryExists(dir) && !base::CreateDirectory(dir)) { 93 LOG(ERROR) << "Failed to create parent directory."; 94 return; 95 } 96 97 if (base::WriteFile(image_path, reinterpret_cast<char*>(&(*data)[0]), 98 data->size()) == -1) { 99 LOG(ERROR) << "Failed to save image to file."; 100 return; 101 } 102 103 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, callback); 104 } 105 106 // Reads a PNG from disk and decodes it. If the bitmap was successfully read 107 // from disk the then |out_image| will contain the bitmap image, otherwise it 108 // will be NULL. 109 void ReadBitmap(const base::FilePath& image_path, 110 gfx::Image** out_image) { 111 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 112 *out_image = NULL; 113 114 // If the path doesn't exist, don't even try reading it. 115 if (!base::PathExists(image_path)) 116 return; 117 118 std::string image_data; 119 if (!base::ReadFileToString(image_path, &image_data)) { 120 LOG(ERROR) << "Failed to read PNG file from disk."; 121 return; 122 } 123 124 gfx::Image image = gfx::Image::CreateFrom1xPNGBytes( 125 base::RefCountedString::TakeString(&image_data)); 126 if (image.IsEmpty()) { 127 LOG(ERROR) << "Failed to decode PNG file."; 128 return; 129 } 130 131 *out_image = new gfx::Image(image); 132 } 133 134 void DeleteBitmap(const base::FilePath& image_path) { 135 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 136 base::DeleteFile(image_path, false); 137 } 138 139 } // namespace 140 141 ProfileInfoCache::ProfileInfoCache(PrefService* prefs, 142 const base::FilePath& user_data_dir) 143 : prefs_(prefs), 144 user_data_dir_(user_data_dir) { 145 // Populate the cache 146 DictionaryPrefUpdate update(prefs_, prefs::kProfileInfoCache); 147 base::DictionaryValue* cache = update.Get(); 148 for (base::DictionaryValue::Iterator it(*cache); 149 !it.IsAtEnd(); it.Advance()) { 150 base::DictionaryValue* info = NULL; 151 cache->GetDictionaryWithoutPathExpansion(it.key(), &info); 152 base::string16 name; 153 info->GetString(kNameKey, &name); 154 sorted_keys_.insert(FindPositionForProfile(it.key(), name), it.key()); 155 156 bool using_default_name; 157 if (!info->GetBoolean(kIsUsingDefaultNameKey, &using_default_name)) { 158 // If the preference hasn't been set, and the name is default, assume 159 // that the user hasn't done this on purpose. 160 using_default_name = IsDefaultProfileName(name); 161 info->SetBoolean(kIsUsingDefaultNameKey, using_default_name); 162 } 163 164 // For profiles that don't have the "using default avatar" state set yet, 165 // assume it's the same as the "using default name" state. 166 if (!info->HasKey(kIsUsingDefaultAvatarKey)) { 167 info->SetBoolean(kIsUsingDefaultAvatarKey, using_default_name); 168 } 169 } 170 171 // If needed, start downloading the high-res avatars and migrate any legacy 172 // profile names. 173 if (switches::IsNewAvatarMenu()) 174 MigrateLegacyProfileNamesAndDownloadAvatars(); 175 } 176 177 ProfileInfoCache::~ProfileInfoCache() { 178 STLDeleteContainerPairSecondPointers( 179 cached_avatar_images_.begin(), cached_avatar_images_.end()); 180 STLDeleteContainerPairSecondPointers( 181 avatar_images_downloads_in_progress_.begin(), 182 avatar_images_downloads_in_progress_.end()); 183 } 184 185 void ProfileInfoCache::AddProfileToCache( 186 const base::FilePath& profile_path, 187 const base::string16& name, 188 const base::string16& username, 189 size_t icon_index, 190 const std::string& supervised_user_id) { 191 std::string key = CacheKeyFromProfilePath(profile_path); 192 DictionaryPrefUpdate update(prefs_, prefs::kProfileInfoCache); 193 base::DictionaryValue* cache = update.Get(); 194 195 scoped_ptr<base::DictionaryValue> info(new base::DictionaryValue); 196 info->SetString(kNameKey, name); 197 info->SetString(kUserNameKey, username); 198 info->SetString(kAvatarIconKey, 199 profiles::GetDefaultAvatarIconUrl(icon_index)); 200 // Default value for whether background apps are running is false. 201 info->SetBoolean(kBackgroundAppsKey, false); 202 info->SetString(kSupervisedUserId, supervised_user_id); 203 info->SetBoolean(kIsOmittedFromProfileListKey, !supervised_user_id.empty()); 204 info->SetBoolean(kProfileIsEphemeral, false); 205 info->SetBoolean(kIsUsingDefaultNameKey, IsDefaultProfileName(name)); 206 // Assume newly created profiles use a default avatar. 207 info->SetBoolean(kIsUsingDefaultAvatarKey, true); 208 cache->SetWithoutPathExpansion(key, info.release()); 209 210 sorted_keys_.insert(FindPositionForProfile(key, name), key); 211 212 if (switches::IsNewAvatarMenu()) 213 DownloadHighResAvatar(icon_index, profile_path); 214 215 FOR_EACH_OBSERVER(ProfileInfoCacheObserver, 216 observer_list_, 217 OnProfileAdded(profile_path)); 218 219 content::NotificationService::current()->Notify( 220 chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED, 221 content::NotificationService::AllSources(), 222 content::NotificationService::NoDetails()); 223 } 224 225 void ProfileInfoCache::AddObserver(ProfileInfoCacheObserver* obs) { 226 observer_list_.AddObserver(obs); 227 } 228 229 void ProfileInfoCache::RemoveObserver(ProfileInfoCacheObserver* obs) { 230 observer_list_.RemoveObserver(obs); 231 } 232 233 void ProfileInfoCache::DeleteProfileFromCache( 234 const base::FilePath& profile_path) { 235 size_t profile_index = GetIndexOfProfileWithPath(profile_path); 236 if (profile_index == std::string::npos) { 237 NOTREACHED(); 238 return; 239 } 240 base::string16 name = GetNameOfProfileAtIndex(profile_index); 241 242 FOR_EACH_OBSERVER(ProfileInfoCacheObserver, 243 observer_list_, 244 OnProfileWillBeRemoved(profile_path)); 245 246 DictionaryPrefUpdate update(prefs_, prefs::kProfileInfoCache); 247 base::DictionaryValue* cache = update.Get(); 248 std::string key = CacheKeyFromProfilePath(profile_path); 249 cache->Remove(key, NULL); 250 sorted_keys_.erase(std::find(sorted_keys_.begin(), sorted_keys_.end(), key)); 251 252 FOR_EACH_OBSERVER(ProfileInfoCacheObserver, 253 observer_list_, 254 OnProfileWasRemoved(profile_path, name)); 255 256 content::NotificationService::current()->Notify( 257 chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED, 258 content::NotificationService::AllSources(), 259 content::NotificationService::NoDetails()); 260 } 261 262 size_t ProfileInfoCache::GetNumberOfProfiles() const { 263 return sorted_keys_.size(); 264 } 265 266 size_t ProfileInfoCache::GetIndexOfProfileWithPath( 267 const base::FilePath& profile_path) const { 268 if (profile_path.DirName() != user_data_dir_) 269 return std::string::npos; 270 std::string search_key = CacheKeyFromProfilePath(profile_path); 271 for (size_t i = 0; i < sorted_keys_.size(); ++i) { 272 if (sorted_keys_[i] == search_key) 273 return i; 274 } 275 return std::string::npos; 276 } 277 278 base::string16 ProfileInfoCache::GetNameOfProfileAtIndex(size_t index) const { 279 base::string16 name; 280 // Unless the user has customized the profile name, we should use the 281 // profile's Gaia given name, if it's available. 282 if (ProfileIsUsingDefaultNameAtIndex(index)) { 283 base::string16 given_name = GetGAIAGivenNameOfProfileAtIndex(index); 284 name = given_name.empty() ? GetGAIANameOfProfileAtIndex(index) : given_name; 285 } 286 if (name.empty()) 287 GetInfoForProfileAtIndex(index)->GetString(kNameKey, &name); 288 return name; 289 } 290 291 base::string16 ProfileInfoCache::GetShortcutNameOfProfileAtIndex(size_t index) 292 const { 293 base::string16 shortcut_name; 294 GetInfoForProfileAtIndex(index)->GetString( 295 kShortcutNameKey, &shortcut_name); 296 return shortcut_name; 297 } 298 299 base::FilePath ProfileInfoCache::GetPathOfProfileAtIndex(size_t index) const { 300 return user_data_dir_.AppendASCII(sorted_keys_[index]); 301 } 302 303 base::Time ProfileInfoCache::GetProfileActiveTimeAtIndex(size_t index) const { 304 double dt; 305 if (GetInfoForProfileAtIndex(index)->GetDouble(kActiveTimeKey, &dt)) { 306 return base::Time::FromDoubleT(dt); 307 } else { 308 return base::Time(); 309 } 310 } 311 312 base::string16 ProfileInfoCache::GetUserNameOfProfileAtIndex( 313 size_t index) const { 314 base::string16 user_name; 315 GetInfoForProfileAtIndex(index)->GetString(kUserNameKey, &user_name); 316 return user_name; 317 } 318 319 const gfx::Image& ProfileInfoCache::GetAvatarIconOfProfileAtIndex( 320 size_t index) const { 321 if (IsUsingGAIAPictureOfProfileAtIndex(index)) { 322 const gfx::Image* image = GetGAIAPictureOfProfileAtIndex(index); 323 if (image) 324 return *image; 325 } 326 327 // Use the high resolution version of the avatar if it exists. 328 if (switches::IsNewAvatarMenu()) { 329 const gfx::Image* image = GetHighResAvatarOfProfileAtIndex(index); 330 if (image) 331 return *image; 332 } 333 334 int resource_id = profiles::GetDefaultAvatarIconResourceIDAtIndex( 335 GetAvatarIconIndexOfProfileAtIndex(index)); 336 return ResourceBundle::GetSharedInstance().GetNativeImageNamed(resource_id); 337 } 338 339 std::string ProfileInfoCache::GetLocalAuthCredentialsOfProfileAtIndex( 340 size_t index) const { 341 std::string credentials; 342 GetInfoForProfileAtIndex(index)->GetString(kAuthCredentialsKey, &credentials); 343 return credentials; 344 } 345 346 bool ProfileInfoCache::GetBackgroundStatusOfProfileAtIndex( 347 size_t index) const { 348 bool background_app_status; 349 if (!GetInfoForProfileAtIndex(index)->GetBoolean(kBackgroundAppsKey, 350 &background_app_status)) { 351 return false; 352 } 353 return background_app_status; 354 } 355 356 base::string16 ProfileInfoCache::GetGAIANameOfProfileAtIndex( 357 size_t index) const { 358 base::string16 name; 359 GetInfoForProfileAtIndex(index)->GetString(kGAIANameKey, &name); 360 return name; 361 } 362 363 base::string16 ProfileInfoCache::GetGAIAGivenNameOfProfileAtIndex( 364 size_t index) const { 365 base::string16 name; 366 GetInfoForProfileAtIndex(index)->GetString(kGAIAGivenNameKey, &name); 367 return name; 368 } 369 370 const gfx::Image* ProfileInfoCache::GetGAIAPictureOfProfileAtIndex( 371 size_t index) const { 372 base::FilePath path = GetPathOfProfileAtIndex(index); 373 std::string key = CacheKeyFromProfilePath(path); 374 375 std::string file_name; 376 GetInfoForProfileAtIndex(index)->GetString( 377 kGAIAPictureFileNameKey, &file_name); 378 379 // If the picture is not on disk then return NULL. 380 if (file_name.empty()) 381 return NULL; 382 383 base::FilePath image_path = path.AppendASCII(file_name); 384 return LoadAvatarPictureFromPath(key, image_path); 385 } 386 387 bool ProfileInfoCache::IsUsingGAIAPictureOfProfileAtIndex(size_t index) const { 388 bool value = false; 389 GetInfoForProfileAtIndex(index)->GetBoolean(kUseGAIAPictureKey, &value); 390 if (!value) { 391 // Prefer the GAIA avatar over a non-customized avatar. 392 value = ProfileIsUsingDefaultAvatarAtIndex(index) && 393 GetGAIAPictureOfProfileAtIndex(index); 394 } 395 return value; 396 } 397 398 bool ProfileInfoCache::ProfileIsSupervisedAtIndex(size_t index) const { 399 return !GetSupervisedUserIdOfProfileAtIndex(index).empty(); 400 } 401 402 bool ProfileInfoCache::IsOmittedProfileAtIndex(size_t index) const { 403 bool value = false; 404 GetInfoForProfileAtIndex(index)->GetBoolean(kIsOmittedFromProfileListKey, 405 &value); 406 return value; 407 } 408 409 bool ProfileInfoCache::ProfileIsSigninRequiredAtIndex(size_t index) const { 410 bool value = false; 411 GetInfoForProfileAtIndex(index)->GetBoolean(kSigninRequiredKey, &value); 412 return value; 413 } 414 415 std::string ProfileInfoCache::GetSupervisedUserIdOfProfileAtIndex( 416 size_t index) const { 417 std::string supervised_user_id; 418 GetInfoForProfileAtIndex(index)->GetString(kSupervisedUserId, 419 &supervised_user_id); 420 return supervised_user_id; 421 } 422 423 bool ProfileInfoCache::ProfileIsEphemeralAtIndex(size_t index) const { 424 bool value = false; 425 GetInfoForProfileAtIndex(index)->GetBoolean(kProfileIsEphemeral, &value); 426 return value; 427 } 428 429 bool ProfileInfoCache::ProfileIsUsingDefaultNameAtIndex(size_t index) const { 430 bool value = false; 431 GetInfoForProfileAtIndex(index)->GetBoolean(kIsUsingDefaultNameKey, &value); 432 return value; 433 } 434 435 bool ProfileInfoCache::ProfileIsUsingDefaultAvatarAtIndex(size_t index) const { 436 bool value = false; 437 GetInfoForProfileAtIndex(index)->GetBoolean(kIsUsingDefaultAvatarKey, &value); 438 return value; 439 } 440 441 size_t ProfileInfoCache::GetAvatarIconIndexOfProfileAtIndex(size_t index) 442 const { 443 std::string icon_url; 444 GetInfoForProfileAtIndex(index)->GetString(kAvatarIconKey, &icon_url); 445 size_t icon_index = 0; 446 if (!profiles::IsDefaultAvatarIconUrl(icon_url, &icon_index)) 447 DLOG(WARNING) << "Unknown avatar icon: " << icon_url; 448 449 return icon_index; 450 } 451 452 void ProfileInfoCache::SetProfileActiveTimeAtIndex(size_t index) { 453 scoped_ptr<base::DictionaryValue> info( 454 GetInfoForProfileAtIndex(index)->DeepCopy()); 455 info->SetDouble(kActiveTimeKey, base::Time::Now().ToDoubleT()); 456 // This takes ownership of |info|. 457 SetInfoQuietlyForProfileAtIndex(index, info.release()); 458 } 459 460 void ProfileInfoCache::SetNameOfProfileAtIndex(size_t index, 461 const base::string16& name) { 462 scoped_ptr<base::DictionaryValue> info( 463 GetInfoForProfileAtIndex(index)->DeepCopy()); 464 base::string16 current_name; 465 info->GetString(kNameKey, ¤t_name); 466 if (name == current_name) 467 return; 468 469 base::string16 old_display_name = GetNameOfProfileAtIndex(index); 470 info->SetString(kNameKey, name); 471 472 // This takes ownership of |info|. 473 SetInfoForProfileAtIndex(index, info.release()); 474 475 base::string16 new_display_name = GetNameOfProfileAtIndex(index); 476 base::FilePath profile_path = GetPathOfProfileAtIndex(index); 477 UpdateSortForProfileIndex(index); 478 479 if (old_display_name != new_display_name) { 480 FOR_EACH_OBSERVER(ProfileInfoCacheObserver, 481 observer_list_, 482 OnProfileNameChanged(profile_path, old_display_name)); 483 } 484 } 485 486 void ProfileInfoCache::SetShortcutNameOfProfileAtIndex( 487 size_t index, 488 const base::string16& shortcut_name) { 489 if (shortcut_name == GetShortcutNameOfProfileAtIndex(index)) 490 return; 491 scoped_ptr<base::DictionaryValue> info( 492 GetInfoForProfileAtIndex(index)->DeepCopy()); 493 info->SetString(kShortcutNameKey, shortcut_name); 494 // This takes ownership of |info|. 495 SetInfoForProfileAtIndex(index, info.release()); 496 } 497 498 void ProfileInfoCache::SetUserNameOfProfileAtIndex( 499 size_t index, 500 const base::string16& user_name) { 501 if (user_name == GetUserNameOfProfileAtIndex(index)) 502 return; 503 504 scoped_ptr<base::DictionaryValue> info( 505 GetInfoForProfileAtIndex(index)->DeepCopy()); 506 info->SetString(kUserNameKey, user_name); 507 // This takes ownership of |info|. 508 SetInfoForProfileAtIndex(index, info.release()); 509 } 510 511 void ProfileInfoCache::SetAvatarIconOfProfileAtIndex(size_t index, 512 size_t icon_index) { 513 scoped_ptr<base::DictionaryValue> info( 514 GetInfoForProfileAtIndex(index)->DeepCopy()); 515 info->SetString(kAvatarIconKey, 516 profiles::GetDefaultAvatarIconUrl(icon_index)); 517 // This takes ownership of |info|. 518 SetInfoForProfileAtIndex(index, info.release()); 519 520 base::FilePath profile_path = GetPathOfProfileAtIndex(index); 521 522 // If needed, start downloading the high-res avatar. 523 if (switches::IsNewAvatarMenu()) 524 DownloadHighResAvatar(icon_index, profile_path); 525 526 FOR_EACH_OBSERVER(ProfileInfoCacheObserver, 527 observer_list_, 528 OnProfileAvatarChanged(profile_path)); 529 } 530 531 void ProfileInfoCache::SetIsOmittedProfileAtIndex(size_t index, 532 bool is_omitted) { 533 if (IsOmittedProfileAtIndex(index) == is_omitted) 534 return; 535 scoped_ptr<base::DictionaryValue> info( 536 GetInfoForProfileAtIndex(index)->DeepCopy()); 537 info->SetBoolean(kIsOmittedFromProfileListKey, is_omitted); 538 // This takes ownership of |info|. 539 SetInfoForProfileAtIndex(index, info.release()); 540 } 541 542 void ProfileInfoCache::SetSupervisedUserIdOfProfileAtIndex( 543 size_t index, 544 const std::string& id) { 545 if (GetSupervisedUserIdOfProfileAtIndex(index) == id) 546 return; 547 scoped_ptr<base::DictionaryValue> info( 548 GetInfoForProfileAtIndex(index)->DeepCopy()); 549 info->SetString(kSupervisedUserId, id); 550 // This takes ownership of |info|. 551 SetInfoForProfileAtIndex(index, info.release()); 552 553 base::FilePath profile_path = GetPathOfProfileAtIndex(index); 554 FOR_EACH_OBSERVER(ProfileInfoCacheObserver, 555 observer_list_, 556 OnProfileSupervisedUserIdChanged(profile_path)); 557 } 558 559 void ProfileInfoCache::SetLocalAuthCredentialsOfProfileAtIndex( 560 size_t index, 561 const std::string& credentials) { 562 scoped_ptr<base::DictionaryValue> info( 563 GetInfoForProfileAtIndex(index)->DeepCopy()); 564 info->SetString(kAuthCredentialsKey, credentials); 565 // This takes ownership of |info|. 566 SetInfoForProfileAtIndex(index, info.release()); 567 } 568 569 void ProfileInfoCache::SetBackgroundStatusOfProfileAtIndex( 570 size_t index, 571 bool running_background_apps) { 572 if (GetBackgroundStatusOfProfileAtIndex(index) == running_background_apps) 573 return; 574 scoped_ptr<base::DictionaryValue> info( 575 GetInfoForProfileAtIndex(index)->DeepCopy()); 576 info->SetBoolean(kBackgroundAppsKey, running_background_apps); 577 // This takes ownership of |info|. 578 SetInfoForProfileAtIndex(index, info.release()); 579 } 580 581 void ProfileInfoCache::SetGAIANameOfProfileAtIndex(size_t index, 582 const base::string16& name) { 583 if (name == GetGAIANameOfProfileAtIndex(index)) 584 return; 585 586 base::string16 old_display_name = GetNameOfProfileAtIndex(index); 587 scoped_ptr<base::DictionaryValue> info( 588 GetInfoForProfileAtIndex(index)->DeepCopy()); 589 info->SetString(kGAIANameKey, name); 590 // This takes ownership of |info|. 591 SetInfoForProfileAtIndex(index, info.release()); 592 base::string16 new_display_name = GetNameOfProfileAtIndex(index); 593 base::FilePath profile_path = GetPathOfProfileAtIndex(index); 594 UpdateSortForProfileIndex(index); 595 596 if (old_display_name != new_display_name) { 597 FOR_EACH_OBSERVER(ProfileInfoCacheObserver, 598 observer_list_, 599 OnProfileNameChanged(profile_path, old_display_name)); 600 } 601 } 602 603 void ProfileInfoCache::SetGAIAGivenNameOfProfileAtIndex( 604 size_t index, 605 const base::string16& name) { 606 if (name == GetGAIAGivenNameOfProfileAtIndex(index)) 607 return; 608 609 base::string16 old_display_name = GetNameOfProfileAtIndex(index); 610 scoped_ptr<base::DictionaryValue> info( 611 GetInfoForProfileAtIndex(index)->DeepCopy()); 612 info->SetString(kGAIAGivenNameKey, name); 613 // This takes ownership of |info|. 614 SetInfoForProfileAtIndex(index, info.release()); 615 base::string16 new_display_name = GetNameOfProfileAtIndex(index); 616 base::FilePath profile_path = GetPathOfProfileAtIndex(index); 617 UpdateSortForProfileIndex(index); 618 619 if (old_display_name != new_display_name) { 620 FOR_EACH_OBSERVER(ProfileInfoCacheObserver, 621 observer_list_, 622 OnProfileNameChanged(profile_path, old_display_name)); 623 } 624 } 625 626 void ProfileInfoCache::SetGAIAPictureOfProfileAtIndex(size_t index, 627 const gfx::Image* image) { 628 base::FilePath path = GetPathOfProfileAtIndex(index); 629 std::string key = CacheKeyFromProfilePath(path); 630 631 // Delete the old bitmap from cache. 632 std::map<std::string, gfx::Image*>::iterator it = 633 cached_avatar_images_.find(key); 634 if (it != cached_avatar_images_.end()) { 635 delete it->second; 636 cached_avatar_images_.erase(it); 637 } 638 639 std::string old_file_name; 640 GetInfoForProfileAtIndex(index)->GetString( 641 kGAIAPictureFileNameKey, &old_file_name); 642 std::string new_file_name; 643 644 if (!image) { 645 // Delete the old bitmap from disk. 646 if (!old_file_name.empty()) { 647 base::FilePath image_path = path.AppendASCII(old_file_name); 648 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 649 base::Bind(&DeleteBitmap, image_path)); 650 } 651 } else { 652 // Save the new bitmap to disk. 653 new_file_name = 654 old_file_name.empty() ? profiles::kGAIAPictureFileName : old_file_name; 655 base::FilePath image_path = path.AppendASCII(new_file_name); 656 SaveAvatarImageAtPath( 657 image, key, image_path, GetPathOfProfileAtIndex(index)); 658 } 659 660 scoped_ptr<base::DictionaryValue> info( 661 GetInfoForProfileAtIndex(index)->DeepCopy()); 662 info->SetString(kGAIAPictureFileNameKey, new_file_name); 663 // This takes ownership of |info|. 664 SetInfoForProfileAtIndex(index, info.release()); 665 666 FOR_EACH_OBSERVER(ProfileInfoCacheObserver, 667 observer_list_, 668 OnProfileAvatarChanged(path)); 669 } 670 671 void ProfileInfoCache::SetIsUsingGAIAPictureOfProfileAtIndex(size_t index, 672 bool value) { 673 scoped_ptr<base::DictionaryValue> info( 674 GetInfoForProfileAtIndex(index)->DeepCopy()); 675 info->SetBoolean(kUseGAIAPictureKey, value); 676 // This takes ownership of |info|. 677 SetInfoForProfileAtIndex(index, info.release()); 678 679 // Retrieve some info to update observers who care about avatar changes. 680 base::FilePath profile_path = GetPathOfProfileAtIndex(index); 681 FOR_EACH_OBSERVER(ProfileInfoCacheObserver, 682 observer_list_, 683 OnProfileAvatarChanged(profile_path)); 684 } 685 686 void ProfileInfoCache::SetProfileSigninRequiredAtIndex(size_t index, 687 bool value) { 688 if (value == ProfileIsSigninRequiredAtIndex(index)) 689 return; 690 691 scoped_ptr<base::DictionaryValue> info( 692 GetInfoForProfileAtIndex(index)->DeepCopy()); 693 info->SetBoolean(kSigninRequiredKey, value); 694 // This takes ownership of |info|. 695 SetInfoForProfileAtIndex(index, info.release()); 696 697 base::FilePath profile_path = GetPathOfProfileAtIndex(index); 698 FOR_EACH_OBSERVER(ProfileInfoCacheObserver, 699 observer_list_, 700 OnProfileSigninRequiredChanged(profile_path)); 701 } 702 703 void ProfileInfoCache::SetProfileIsEphemeralAtIndex(size_t index, bool value) { 704 if (value == ProfileIsEphemeralAtIndex(index)) 705 return; 706 707 scoped_ptr<base::DictionaryValue> info( 708 GetInfoForProfileAtIndex(index)->DeepCopy()); 709 info->SetBoolean(kProfileIsEphemeral, value); 710 // This takes ownership of |info|. 711 SetInfoForProfileAtIndex(index, info.release()); 712 } 713 714 void ProfileInfoCache::SetProfileIsUsingDefaultNameAtIndex( 715 size_t index, bool value) { 716 if (value == ProfileIsUsingDefaultNameAtIndex(index)) 717 return; 718 719 scoped_ptr<base::DictionaryValue> info( 720 GetInfoForProfileAtIndex(index)->DeepCopy()); 721 info->SetBoolean(kIsUsingDefaultNameKey, value); 722 // This takes ownership of |info|. 723 SetInfoForProfileAtIndex(index, info.release()); 724 } 725 726 void ProfileInfoCache::SetProfileIsUsingDefaultAvatarAtIndex( 727 size_t index, bool value) { 728 if (value == ProfileIsUsingDefaultAvatarAtIndex(index)) 729 return; 730 731 scoped_ptr<base::DictionaryValue> info( 732 GetInfoForProfileAtIndex(index)->DeepCopy()); 733 info->SetBoolean(kIsUsingDefaultAvatarKey, value); 734 // This takes ownership of |info|. 735 SetInfoForProfileAtIndex(index, info.release()); 736 } 737 738 bool ProfileInfoCache::IsDefaultProfileName(const base::string16& name) const { 739 // Check if it's a "First user" old-style name. 740 if (name == l10n_util::GetStringUTF16(IDS_DEFAULT_PROFILE_NAME) || 741 name == l10n_util::GetStringUTF16(IDS_LEGACY_DEFAULT_PROFILE_NAME)) 742 return true; 743 744 // Check if it's one of the old-style profile names. 745 for (size_t i = 0; i < arraysize(kDefaultNames); ++i) { 746 if (name == l10n_util::GetStringUTF16(kDefaultNames[i])) 747 return true; 748 } 749 750 // Check whether it's one of the "Person %d" style names. 751 std::string default_name_format = l10n_util::GetStringFUTF8( 752 IDS_NEW_NUMBERED_PROFILE_NAME, base::ASCIIToUTF16("%d")); 753 754 int generic_profile_number; // Unused. Just a placeholder for sscanf. 755 int assignments = sscanf(base::UTF16ToUTF8(name).c_str(), 756 default_name_format.c_str(), 757 &generic_profile_number); 758 // Unless it matched the format, this is a custom name. 759 return assignments == 1; 760 } 761 762 base::string16 ProfileInfoCache::ChooseNameForNewProfile( 763 size_t icon_index) const { 764 base::string16 name; 765 for (int name_index = 1; ; ++name_index) { 766 if (switches::IsNewAvatarMenu()) { 767 name = l10n_util::GetStringFUTF16Int(IDS_NEW_NUMBERED_PROFILE_NAME, 768 name_index); 769 } else if (icon_index < profiles::GetGenericAvatarIconCount()) { 770 name = l10n_util::GetStringFUTF16Int(IDS_NUMBERED_PROFILE_NAME, 771 name_index); 772 } else { 773 name = l10n_util::GetStringUTF16( 774 kDefaultNames[icon_index - profiles::GetGenericAvatarIconCount()]); 775 if (name_index > 1) 776 name.append(base::UTF8ToUTF16(base::IntToString(name_index))); 777 } 778 779 // Loop through previously named profiles to ensure we're not duplicating. 780 bool name_found = false; 781 for (size_t i = 0; i < GetNumberOfProfiles(); ++i) { 782 if (GetNameOfProfileAtIndex(i) == name) { 783 name_found = true; 784 break; 785 } 786 } 787 if (!name_found) 788 return name; 789 } 790 } 791 792 size_t ProfileInfoCache::ChooseAvatarIconIndexForNewProfile() const { 793 size_t icon_index = 0; 794 // Try to find a unique, non-generic icon. 795 if (ChooseAvatarIconIndexForNewProfile(false, true, &icon_index)) 796 return icon_index; 797 // Try to find any unique icon. 798 if (ChooseAvatarIconIndexForNewProfile(true, true, &icon_index)) 799 return icon_index; 800 // Settle for any random icon, even if it's not unique. 801 if (ChooseAvatarIconIndexForNewProfile(true, false, &icon_index)) 802 return icon_index; 803 804 NOTREACHED(); 805 return 0; 806 } 807 808 const base::FilePath& ProfileInfoCache::GetUserDataDir() const { 809 return user_data_dir_; 810 } 811 812 // static 813 std::vector<base::string16> ProfileInfoCache::GetProfileNames() { 814 std::vector<base::string16> names; 815 PrefService* local_state = g_browser_process->local_state(); 816 const base::DictionaryValue* cache = local_state->GetDictionary( 817 prefs::kProfileInfoCache); 818 base::string16 name; 819 for (base::DictionaryValue::Iterator it(*cache); !it.IsAtEnd(); 820 it.Advance()) { 821 const base::DictionaryValue* info = NULL; 822 it.value().GetAsDictionary(&info); 823 info->GetString(kNameKey, &name); 824 names.push_back(name); 825 } 826 return names; 827 } 828 829 // static 830 void ProfileInfoCache::RegisterPrefs(PrefRegistrySimple* registry) { 831 registry->RegisterDictionaryPref(prefs::kProfileInfoCache); 832 } 833 834 void ProfileInfoCache::DownloadHighResAvatar( 835 size_t icon_index, 836 const base::FilePath& profile_path) { 837 // Downloading is only supported on desktop. 838 #if defined(OS_ANDROID) || defined(OS_IOS) || defined(OS_CHROMEOS) 839 return; 840 #endif 841 842 // TODO(noms): We should check whether the file already exists on disk 843 // before trying to re-download it. For now, since this is behind a flag and 844 // the resources are still changing, re-download it every time the profile 845 // avatar changes, to make sure we have the latest copy. 846 std::string file_name = profiles::GetDefaultAvatarIconFileNameAtIndex( 847 icon_index); 848 // If the file is already being downloaded, don't start another download. 849 if (avatar_images_downloads_in_progress_[file_name]) 850 return; 851 852 // Start the download for this file. The cache takes ownership of the 853 // |avatar_downloader|, which will be deleted when the download completes, or 854 // if that never happens, when the ProfileInfoCache is destroyed. 855 ProfileAvatarDownloader* avatar_downloader = new ProfileAvatarDownloader( 856 icon_index, 857 profile_path, 858 this); 859 avatar_images_downloads_in_progress_[file_name] = avatar_downloader; 860 avatar_downloader->Start(); 861 } 862 863 void ProfileInfoCache::SaveAvatarImageAtPath( 864 const gfx::Image* image, 865 const std::string& key, 866 const base::FilePath& image_path, 867 const base::FilePath& profile_path) { 868 cached_avatar_images_[key] = new gfx::Image(*image); 869 870 scoped_ptr<ImageData> data(new ImageData); 871 scoped_refptr<base::RefCountedMemory> png_data = image->As1xPNGBytes(); 872 data->assign(png_data->front(), png_data->front() + png_data->size()); 873 874 if (!data->size()) { 875 LOG(ERROR) << "Failed to PNG encode the image."; 876 } else { 877 base::Closure callback = base::Bind(&ProfileInfoCache::OnAvatarPictureSaved, 878 AsWeakPtr(), key, profile_path); 879 880 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 881 base::Bind(&SaveBitmap, base::Passed(&data), image_path, callback)); 882 } 883 } 884 885 const base::DictionaryValue* ProfileInfoCache::GetInfoForProfileAtIndex( 886 size_t index) const { 887 DCHECK_LT(index, GetNumberOfProfiles()); 888 const base::DictionaryValue* cache = 889 prefs_->GetDictionary(prefs::kProfileInfoCache); 890 const base::DictionaryValue* info = NULL; 891 cache->GetDictionaryWithoutPathExpansion(sorted_keys_[index], &info); 892 return info; 893 } 894 895 void ProfileInfoCache::SetInfoQuietlyForProfileAtIndex( 896 size_t index, base::DictionaryValue* info) { 897 DictionaryPrefUpdate update(prefs_, prefs::kProfileInfoCache); 898 base::DictionaryValue* cache = update.Get(); 899 cache->SetWithoutPathExpansion(sorted_keys_[index], info); 900 } 901 902 // TODO(noms): Switch to newer notification system. 903 void ProfileInfoCache::SetInfoForProfileAtIndex(size_t index, 904 base::DictionaryValue* info) { 905 SetInfoQuietlyForProfileAtIndex(index, info); 906 907 content::NotificationService::current()->Notify( 908 chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED, 909 content::NotificationService::AllSources(), 910 content::NotificationService::NoDetails()); 911 } 912 913 std::string ProfileInfoCache::CacheKeyFromProfilePath( 914 const base::FilePath& profile_path) const { 915 DCHECK(user_data_dir_ == profile_path.DirName()); 916 base::FilePath base_name = profile_path.BaseName(); 917 return base_name.MaybeAsASCII(); 918 } 919 920 std::vector<std::string>::iterator ProfileInfoCache::FindPositionForProfile( 921 const std::string& search_key, 922 const base::string16& search_name) { 923 base::string16 search_name_l = base::i18n::ToLower(search_name); 924 for (size_t i = 0; i < GetNumberOfProfiles(); ++i) { 925 base::string16 name_l = base::i18n::ToLower(GetNameOfProfileAtIndex(i)); 926 int name_compare = search_name_l.compare(name_l); 927 if (name_compare < 0) 928 return sorted_keys_.begin() + i; 929 if (name_compare == 0) { 930 int key_compare = search_key.compare(sorted_keys_[i]); 931 if (key_compare < 0) 932 return sorted_keys_.begin() + i; 933 } 934 } 935 return sorted_keys_.end(); 936 } 937 938 bool ProfileInfoCache::IconIndexIsUnique(size_t icon_index) const { 939 for (size_t i = 0; i < GetNumberOfProfiles(); ++i) { 940 if (GetAvatarIconIndexOfProfileAtIndex(i) == icon_index) 941 return false; 942 } 943 return true; 944 } 945 946 bool ProfileInfoCache::ChooseAvatarIconIndexForNewProfile( 947 bool allow_generic_icon, 948 bool must_be_unique, 949 size_t* out_icon_index) const { 950 // Always allow all icons for new profiles if using the 951 // --new-avatar-menu flag. 952 if (switches::IsNewAvatarMenu()) 953 allow_generic_icon = true; 954 size_t start = allow_generic_icon ? 0 : profiles::GetGenericAvatarIconCount(); 955 size_t end = profiles::GetDefaultAvatarIconCount(); 956 size_t count = end - start; 957 958 int rand = base::RandInt(0, count); 959 for (size_t i = 0; i < count; ++i) { 960 size_t icon_index = start + (rand + i) % count; 961 if (!must_be_unique || IconIndexIsUnique(icon_index)) { 962 *out_icon_index = icon_index; 963 return true; 964 } 965 } 966 967 return false; 968 } 969 970 void ProfileInfoCache::UpdateSortForProfileIndex(size_t index) { 971 base::string16 name = GetNameOfProfileAtIndex(index); 972 973 // Remove and reinsert key in |sorted_keys_| to alphasort. 974 std::string key = CacheKeyFromProfilePath(GetPathOfProfileAtIndex(index)); 975 std::vector<std::string>::iterator key_it = 976 std::find(sorted_keys_.begin(), sorted_keys_.end(), key); 977 DCHECK(key_it != sorted_keys_.end()); 978 sorted_keys_.erase(key_it); 979 sorted_keys_.insert(FindPositionForProfile(key, name), key); 980 981 content::NotificationService::current()->Notify( 982 chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED, 983 content::NotificationService::AllSources(), 984 content::NotificationService::NoDetails()); 985 } 986 987 const gfx::Image* ProfileInfoCache::GetHighResAvatarOfProfileAtIndex( 988 size_t index) const { 989 int avatar_index = GetAvatarIconIndexOfProfileAtIndex(index); 990 std::string key = profiles::GetDefaultAvatarIconFileNameAtIndex(avatar_index); 991 992 if (!strcmp(key.c_str(), profiles::GetNoHighResAvatarFileName())) 993 return NULL; 994 995 base::FilePath image_path = 996 profiles::GetPathOfHighResAvatarAtIndex(avatar_index); 997 return LoadAvatarPictureFromPath(key, image_path); 998 } 999 1000 const gfx::Image* ProfileInfoCache::LoadAvatarPictureFromPath( 1001 const std::string& key, 1002 const base::FilePath& image_path) const { 1003 // If the picture is already loaded then use it. 1004 if (cached_avatar_images_.count(key)) { 1005 if (cached_avatar_images_[key]->IsEmpty()) 1006 return NULL; 1007 return cached_avatar_images_[key]; 1008 } 1009 1010 // If the picture is already being loaded then don't try loading it again. 1011 if (cached_avatar_images_loading_[key]) 1012 return NULL; 1013 cached_avatar_images_loading_[key] = true; 1014 1015 gfx::Image** image = new gfx::Image*; 1016 BrowserThread::PostTaskAndReply(BrowserThread::FILE, FROM_HERE, 1017 base::Bind(&ReadBitmap, image_path, image), 1018 base::Bind(&ProfileInfoCache::OnAvatarPictureLoaded, 1019 const_cast<ProfileInfoCache*>(this)->AsWeakPtr(), key, image)); 1020 return NULL; 1021 } 1022 1023 void ProfileInfoCache::OnAvatarPictureLoaded(const std::string& key, 1024 gfx::Image** image) const { 1025 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1026 1027 cached_avatar_images_loading_[key] = false; 1028 delete cached_avatar_images_[key]; 1029 1030 if (*image) { 1031 cached_avatar_images_[key] = *image; 1032 } else { 1033 // Place an empty image in the cache to avoid reloading it again. 1034 cached_avatar_images_[key] = new gfx::Image(); 1035 } 1036 delete image; 1037 1038 content::NotificationService::current()->Notify( 1039 chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED, 1040 content::NotificationService::AllSources(), 1041 content::NotificationService::NoDetails()); 1042 } 1043 1044 void ProfileInfoCache::OnAvatarPictureSaved( 1045 const std::string& file_name, 1046 const base::FilePath& profile_path) { 1047 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1048 1049 content::NotificationService::current()->Notify( 1050 chrome::NOTIFICATION_PROFILE_CACHE_PICTURE_SAVED, 1051 content::NotificationService::AllSources(), 1052 content::NotificationService::NoDetails()); 1053 1054 FOR_EACH_OBSERVER(ProfileInfoCacheObserver, 1055 observer_list_, 1056 OnProfileAvatarChanged(profile_path)); 1057 1058 // Remove the file from the list of downloads in progress. Note that this list 1059 // only contains the high resolution avatars, and not the Gaia profile images. 1060 if (!avatar_images_downloads_in_progress_[file_name]) 1061 return; 1062 1063 delete avatar_images_downloads_in_progress_[file_name]; 1064 avatar_images_downloads_in_progress_[file_name] = NULL; 1065 } 1066 1067 void ProfileInfoCache::MigrateLegacyProfileNamesAndDownloadAvatars() { 1068 DCHECK(switches::IsNewAvatarMenu()); 1069 1070 // Only do this on desktop platforms. 1071 #if !defined(OS_ANDROID) && !defined(OS_IOS) && !defined(OS_CHROMEOS) 1072 // Migrate any legacy profile names ("First user", "Default Profile") to 1073 // new style default names ("Person 1"). The problem here is that every 1074 // time you rename a profile, the ProfileInfoCache sorts itself, so 1075 // whatever you were iterating through is no longer valid. We need to 1076 // save a list of the profile paths (which thankfully do not change) that 1077 // need to be renamed. We also can't pre-compute the new names, as they 1078 // depend on the names of all the other profiles in the info cache, so they 1079 // need to be re-computed after each rename. 1080 std::vector<base::FilePath> profiles_to_rename; 1081 1082 const base::string16 default_profile_name = base::i18n::ToLower( 1083 l10n_util::GetStringUTF16(IDS_DEFAULT_PROFILE_NAME)); 1084 const base::string16 default_legacy_profile_name = base::i18n::ToLower( 1085 l10n_util::GetStringUTF16(IDS_LEGACY_DEFAULT_PROFILE_NAME)); 1086 1087 for (size_t i = 0; i < GetNumberOfProfiles(); i++) { 1088 // If needed, start downloading the high-res avatar for this profile. 1089 DownloadHighResAvatar(GetAvatarIconIndexOfProfileAtIndex(i), 1090 GetPathOfProfileAtIndex(i)); 1091 1092 base::string16 name = base::i18n::ToLower(GetNameOfProfileAtIndex(i)); 1093 if (name == default_profile_name || name == default_legacy_profile_name) 1094 profiles_to_rename.push_back(GetPathOfProfileAtIndex(i)); 1095 } 1096 1097 // Rename the necessary profiles. 1098 std::vector<base::FilePath>::const_iterator it; 1099 for (it = profiles_to_rename.begin(); it != profiles_to_rename.end(); ++it) { 1100 size_t profile_index = GetIndexOfProfileWithPath(*it); 1101 SetProfileIsUsingDefaultNameAtIndex(profile_index, true); 1102 // This will assign a new "Person %d" type name and re-sort the cache. 1103 SetNameOfProfileAtIndex(profile_index, ChooseNameForNewProfile( 1104 GetAvatarIconIndexOfProfileAtIndex(profile_index))); 1105 } 1106 #endif 1107 } 1108