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.
5 #include "chrome/browser/profiles/profile_info_cache.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/profiler/scoped_tracker.h"
16 #include "base/rand_util.h"
17 #include "base/stl_util.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/string_piece.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "base/values.h"
22 #include "chrome/browser/browser_process.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 "ui/base/l10n/l10n_util.h"
31 #include "ui/base/resource/resource_bundle.h"
32 #include "ui/gfx/image/image.h"
33 #include "ui/gfx/image/image_util.h"
35 #if defined(ENABLE_SUPERVISED_USERS)
36 #include "chrome/browser/supervised_user/supervised_user_constants.h"
39 using content::BrowserThread
;
43 const char kNameKey
[] = "name";
44 const char kShortcutNameKey
[] = "shortcut_name";
45 const char kGAIANameKey
[] = "gaia_name";
46 const char kGAIAGivenNameKey
[] = "gaia_given_name";
47 const char kGAIAIdKey
[] = "gaia_id";
48 const char kUserNameKey
[] = "user_name";
49 const char kIsUsingDefaultNameKey
[] = "is_using_default_name";
50 const char kIsUsingDefaultAvatarKey
[] = "is_using_default_avatar";
51 const char kAvatarIconKey
[] = "avatar_icon";
52 const char kAuthCredentialsKey
[] = "local_auth_credentials";
53 const char kPasswordTokenKey
[] = "gaia_password_token";
54 const char kUseGAIAPictureKey
[] = "use_gaia_picture";
55 const char kBackgroundAppsKey
[] = "background_apps";
56 const char kGAIAPictureFileNameKey
[] = "gaia_picture_file_name";
57 const char kIsOmittedFromProfileListKey
[] = "is_omitted_from_profile_list";
58 const char kSigninRequiredKey
[] = "signin_required";
59 const char kSupervisedUserId
[] = "managed_user_id";
60 const char kProfileIsEphemeral
[] = "is_ephemeral";
61 const char kActiveTimeKey
[] = "active_time";
62 const char kIsAuthErrorKey
[] = "is_auth_error";
64 // First eight are generic icons, which use IDS_NUMBERED_PROFILE_NAME.
65 const int kDefaultNames
[] = {
66 IDS_DEFAULT_AVATAR_NAME_8
,
67 IDS_DEFAULT_AVATAR_NAME_9
,
68 IDS_DEFAULT_AVATAR_NAME_10
,
69 IDS_DEFAULT_AVATAR_NAME_11
,
70 IDS_DEFAULT_AVATAR_NAME_12
,
71 IDS_DEFAULT_AVATAR_NAME_13
,
72 IDS_DEFAULT_AVATAR_NAME_14
,
73 IDS_DEFAULT_AVATAR_NAME_15
,
74 IDS_DEFAULT_AVATAR_NAME_16
,
75 IDS_DEFAULT_AVATAR_NAME_17
,
76 IDS_DEFAULT_AVATAR_NAME_18
,
77 IDS_DEFAULT_AVATAR_NAME_19
,
78 IDS_DEFAULT_AVATAR_NAME_20
,
79 IDS_DEFAULT_AVATAR_NAME_21
,
80 IDS_DEFAULT_AVATAR_NAME_22
,
81 IDS_DEFAULT_AVATAR_NAME_23
,
82 IDS_DEFAULT_AVATAR_NAME_24
,
83 IDS_DEFAULT_AVATAR_NAME_25
,
84 IDS_DEFAULT_AVATAR_NAME_26
87 typedef std::vector
<unsigned char> ImageData
;
89 // Writes |data| to disk and takes ownership of the pointer. On successful
90 // completion, it runs |callback|.
91 void SaveBitmap(scoped_ptr
<ImageData
> data
,
92 const base::FilePath
& image_path
,
93 const base::Closure
& callback
) {
94 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
96 // Make sure the destination directory exists.
97 base::FilePath dir
= image_path
.DirName();
98 if (!base::DirectoryExists(dir
) && !base::CreateDirectory(dir
)) {
99 LOG(ERROR
) << "Failed to create parent directory.";
103 if (base::WriteFile(image_path
, reinterpret_cast<char*>(&(*data
)[0]),
104 data
->size()) == -1) {
105 LOG(ERROR
) << "Failed to save image to file.";
109 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
, callback
);
112 // Reads a PNG from disk and decodes it. If the bitmap was successfully read
113 // from disk the then |out_image| will contain the bitmap image, otherwise it
115 void ReadBitmap(const base::FilePath
& image_path
,
116 gfx::Image
** out_image
) {
117 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
120 // If the path doesn't exist, don't even try reading it.
121 if (!base::PathExists(image_path
))
124 std::string image_data
;
125 if (!base::ReadFileToString(image_path
, &image_data
)) {
126 LOG(ERROR
) << "Failed to read PNG file from disk.";
130 gfx::Image image
= gfx::Image::CreateFrom1xPNGBytes(
131 base::RefCountedString::TakeString(&image_data
));
132 if (image
.IsEmpty()) {
133 LOG(ERROR
) << "Failed to decode PNG file.";
137 *out_image
= new gfx::Image(image
);
140 void RunCallbackIfFileMissing(const base::FilePath
& file_path
,
141 const base::Closure
& callback
) {
142 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
143 if (!base::PathExists(file_path
))
144 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
, callback
);
147 void DeleteBitmap(const base::FilePath
& image_path
) {
148 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
149 base::DeleteFile(image_path
, false);
154 ProfileInfoCache::ProfileInfoCache(PrefService
* prefs
,
155 const base::FilePath
& user_data_dir
)
157 user_data_dir_(user_data_dir
),
158 disable_avatar_download_for_testing_(false) {
159 // Populate the cache
160 DictionaryPrefUpdate
update(prefs_
, prefs::kProfileInfoCache
);
161 base::DictionaryValue
* cache
= update
.Get();
162 for (base::DictionaryValue::Iterator
it(*cache
);
163 !it
.IsAtEnd(); it
.Advance()) {
164 base::DictionaryValue
* info
= NULL
;
165 cache
->GetDictionaryWithoutPathExpansion(it
.key(), &info
);
167 info
->GetString(kNameKey
, &name
);
168 sorted_keys_
.insert(FindPositionForProfile(it
.key(), name
), it
.key());
170 bool using_default_name
;
171 if (!info
->GetBoolean(kIsUsingDefaultNameKey
, &using_default_name
)) {
172 // If the preference hasn't been set, and the name is default, assume
173 // that the user hasn't done this on purpose.
174 using_default_name
= IsDefaultProfileName(name
);
175 info
->SetBoolean(kIsUsingDefaultNameKey
, using_default_name
);
178 // For profiles that don't have the "using default avatar" state set yet,
179 // assume it's the same as the "using default name" state.
180 if (!info
->HasKey(kIsUsingDefaultAvatarKey
)) {
181 info
->SetBoolean(kIsUsingDefaultAvatarKey
, using_default_name
);
185 // If needed, start downloading the high-res avatars and migrate any legacy
187 if (switches::IsNewAvatarMenu() && !disable_avatar_download_for_testing_
)
188 MigrateLegacyProfileNamesAndDownloadAvatars();
191 ProfileInfoCache::~ProfileInfoCache() {
192 STLDeleteContainerPairSecondPointers(
193 cached_avatar_images_
.begin(), cached_avatar_images_
.end());
194 STLDeleteContainerPairSecondPointers(
195 avatar_images_downloads_in_progress_
.begin(),
196 avatar_images_downloads_in_progress_
.end());
199 void ProfileInfoCache::AddProfileToCache(
200 const base::FilePath
& profile_path
,
201 const base::string16
& name
,
202 const std::string
& gaia_id
,
203 const base::string16
& user_name
,
205 const std::string
& supervised_user_id
) {
206 std::string key
= CacheKeyFromProfilePath(profile_path
);
207 DictionaryPrefUpdate
update(prefs_
, prefs::kProfileInfoCache
);
208 base::DictionaryValue
* cache
= update
.Get();
210 scoped_ptr
<base::DictionaryValue
> info(new base::DictionaryValue
);
211 info
->SetString(kNameKey
, name
);
212 info
->SetString(kGAIAIdKey
, gaia_id
);
213 info
->SetString(kUserNameKey
, user_name
);
214 info
->SetString(kAvatarIconKey
,
215 profiles::GetDefaultAvatarIconUrl(icon_index
));
216 // Default value for whether background apps are running is false.
217 info
->SetBoolean(kBackgroundAppsKey
, false);
218 info
->SetString(kSupervisedUserId
, supervised_user_id
);
219 info
->SetBoolean(kIsOmittedFromProfileListKey
, !supervised_user_id
.empty());
220 info
->SetBoolean(kProfileIsEphemeral
, false);
221 info
->SetBoolean(kIsUsingDefaultNameKey
, IsDefaultProfileName(name
));
222 // Assume newly created profiles use a default avatar.
223 info
->SetBoolean(kIsUsingDefaultAvatarKey
, true);
224 cache
->SetWithoutPathExpansion(key
, info
.release());
226 sorted_keys_
.insert(FindPositionForProfile(key
, name
), key
);
228 if (switches::IsNewAvatarMenu() && !disable_avatar_download_for_testing_
)
229 DownloadHighResAvatarIfNeeded(icon_index
, profile_path
);
231 FOR_EACH_OBSERVER(ProfileInfoCacheObserver
,
233 OnProfileAdded(profile_path
));
236 void ProfileInfoCache::AddObserver(ProfileInfoCacheObserver
* obs
) {
237 observer_list_
.AddObserver(obs
);
240 void ProfileInfoCache::RemoveObserver(ProfileInfoCacheObserver
* obs
) {
241 observer_list_
.RemoveObserver(obs
);
244 void ProfileInfoCache::DeleteProfileFromCache(
245 const base::FilePath
& profile_path
) {
246 size_t profile_index
= GetIndexOfProfileWithPath(profile_path
);
247 if (profile_index
== std::string::npos
) {
251 base::string16 name
= GetNameOfProfileAtIndex(profile_index
);
253 FOR_EACH_OBSERVER(ProfileInfoCacheObserver
,
255 OnProfileWillBeRemoved(profile_path
));
257 DictionaryPrefUpdate
update(prefs_
, prefs::kProfileInfoCache
);
258 base::DictionaryValue
* cache
= update
.Get();
259 std::string key
= CacheKeyFromProfilePath(profile_path
);
260 cache
->Remove(key
, NULL
);
261 sorted_keys_
.erase(std::find(sorted_keys_
.begin(), sorted_keys_
.end(), key
));
262 profile_attributes_entries_
.erase(profile_path
);
264 FOR_EACH_OBSERVER(ProfileInfoCacheObserver
,
266 OnProfileWasRemoved(profile_path
, name
));
269 size_t ProfileInfoCache::GetNumberOfProfiles() const {
270 return sorted_keys_
.size();
273 size_t ProfileInfoCache::GetIndexOfProfileWithPath(
274 const base::FilePath
& profile_path
) const {
275 if (profile_path
.DirName() != user_data_dir_
)
276 return std::string::npos
;
277 std::string search_key
= CacheKeyFromProfilePath(profile_path
);
278 for (size_t i
= 0; i
< sorted_keys_
.size(); ++i
) {
279 if (sorted_keys_
[i
] == search_key
)
282 return std::string::npos
;
285 base::string16
ProfileInfoCache::GetNameOfProfileAtIndex(size_t index
) const {
287 // Unless the user has customized the profile name, we should use the
288 // profile's Gaia given name, if it's available.
289 if (ProfileIsUsingDefaultNameAtIndex(index
)) {
290 base::string16 given_name
= GetGAIAGivenNameOfProfileAtIndex(index
);
291 name
= given_name
.empty() ? GetGAIANameOfProfileAtIndex(index
) : given_name
;
294 GetInfoForProfileAtIndex(index
)->GetString(kNameKey
, &name
);
298 base::string16
ProfileInfoCache::GetShortcutNameOfProfileAtIndex(size_t index
)
300 base::string16 shortcut_name
;
301 GetInfoForProfileAtIndex(index
)->GetString(
302 kShortcutNameKey
, &shortcut_name
);
303 return shortcut_name
;
306 base::FilePath
ProfileInfoCache::GetPathOfProfileAtIndex(size_t index
) const {
307 return user_data_dir_
.AppendASCII(sorted_keys_
[index
]);
310 base::Time
ProfileInfoCache::GetProfileActiveTimeAtIndex(size_t index
) const {
312 if (GetInfoForProfileAtIndex(index
)->GetDouble(kActiveTimeKey
, &dt
)) {
313 return base::Time::FromDoubleT(dt
);
319 base::string16
ProfileInfoCache::GetUserNameOfProfileAtIndex(
320 size_t index
) const {
321 base::string16 user_name
;
322 GetInfoForProfileAtIndex(index
)->GetString(kUserNameKey
, &user_name
);
326 const gfx::Image
& ProfileInfoCache::GetAvatarIconOfProfileAtIndex(
328 if (IsUsingGAIAPictureOfProfileAtIndex(index
)) {
329 const gfx::Image
* image
= GetGAIAPictureOfProfileAtIndex(index
);
334 // Use the high resolution version of the avatar if it exists.
335 if (switches::IsNewAvatarMenu()) {
336 const gfx::Image
* image
= GetHighResAvatarOfProfileAtIndex(index
);
341 int resource_id
= profiles::GetDefaultAvatarIconResourceIDAtIndex(
342 GetAvatarIconIndexOfProfileAtIndex(index
));
343 return ResourceBundle::GetSharedInstance().GetNativeImageNamed(resource_id
);
346 std::string
ProfileInfoCache::GetLocalAuthCredentialsOfProfileAtIndex(
347 size_t index
) const {
348 std::string credentials
;
349 GetInfoForProfileAtIndex(index
)->GetString(kAuthCredentialsKey
, &credentials
);
353 std::string
ProfileInfoCache::GetPasswordChangeDetectionTokenAtIndex(
354 size_t index
) const {
356 GetInfoForProfileAtIndex(index
)->GetString(kPasswordTokenKey
, &token
);
360 bool ProfileInfoCache::GetBackgroundStatusOfProfileAtIndex(
361 size_t index
) const {
362 bool background_app_status
;
363 if (!GetInfoForProfileAtIndex(index
)->GetBoolean(kBackgroundAppsKey
,
364 &background_app_status
)) {
367 return background_app_status
;
370 base::string16
ProfileInfoCache::GetGAIANameOfProfileAtIndex(
371 size_t index
) const {
373 GetInfoForProfileAtIndex(index
)->GetString(kGAIANameKey
, &name
);
377 base::string16
ProfileInfoCache::GetGAIAGivenNameOfProfileAtIndex(
378 size_t index
) const {
380 GetInfoForProfileAtIndex(index
)->GetString(kGAIAGivenNameKey
, &name
);
384 std::string
ProfileInfoCache::GetGAIAIdOfProfileAtIndex(
385 size_t index
) const {
387 GetInfoForProfileAtIndex(index
)->GetString(kGAIAIdKey
, &gaia_id
);
391 const gfx::Image
* ProfileInfoCache::GetGAIAPictureOfProfileAtIndex(
392 size_t index
) const {
393 base::FilePath path
= GetPathOfProfileAtIndex(index
);
394 std::string key
= CacheKeyFromProfilePath(path
);
396 std::string file_name
;
397 GetInfoForProfileAtIndex(index
)->GetString(
398 kGAIAPictureFileNameKey
, &file_name
);
400 // If the picture is not on disk then return NULL.
401 if (file_name
.empty())
404 base::FilePath image_path
= path
.AppendASCII(file_name
);
405 return LoadAvatarPictureFromPath(path
, key
, image_path
);
408 bool ProfileInfoCache::IsUsingGAIAPictureOfProfileAtIndex(size_t index
) const {
410 GetInfoForProfileAtIndex(index
)->GetBoolean(kUseGAIAPictureKey
, &value
);
412 // Prefer the GAIA avatar over a non-customized avatar.
413 value
= ProfileIsUsingDefaultAvatarAtIndex(index
) &&
414 GetGAIAPictureOfProfileAtIndex(index
);
419 bool ProfileInfoCache::ProfileIsSupervisedAtIndex(size_t index
) const {
420 return !GetSupervisedUserIdOfProfileAtIndex(index
).empty();
423 bool ProfileInfoCache::ProfileIsChildAtIndex(size_t index
) const {
424 #if defined(ENABLE_SUPERVISED_USERS)
425 return GetSupervisedUserIdOfProfileAtIndex(index
) ==
426 supervised_users::kChildAccountSUID
;
432 bool ProfileInfoCache::ProfileIsLegacySupervisedAtIndex(size_t index
) const {
433 return ProfileIsSupervisedAtIndex(index
) && !ProfileIsChildAtIndex(index
);
436 bool ProfileInfoCache::IsOmittedProfileAtIndex(size_t index
) const {
438 GetInfoForProfileAtIndex(index
)->GetBoolean(kIsOmittedFromProfileListKey
,
443 bool ProfileInfoCache::ProfileIsSigninRequiredAtIndex(size_t index
) const {
445 GetInfoForProfileAtIndex(index
)->GetBoolean(kSigninRequiredKey
, &value
);
449 std::string
ProfileInfoCache::GetSupervisedUserIdOfProfileAtIndex(
450 size_t index
) const {
451 std::string supervised_user_id
;
452 GetInfoForProfileAtIndex(index
)->GetString(kSupervisedUserId
,
453 &supervised_user_id
);
454 return supervised_user_id
;
457 bool ProfileInfoCache::ProfileIsEphemeralAtIndex(size_t index
) const {
459 GetInfoForProfileAtIndex(index
)->GetBoolean(kProfileIsEphemeral
, &value
);
463 bool ProfileInfoCache::ProfileIsUsingDefaultNameAtIndex(size_t index
) const {
465 GetInfoForProfileAtIndex(index
)->GetBoolean(kIsUsingDefaultNameKey
, &value
);
469 bool ProfileInfoCache::ProfileIsAuthenticatedAtIndex(size_t index
) const {
470 // The profile is authenticated if the gaia_id of the info is not empty.
471 // If it is empty, also check if the user name is not empty. This latter
472 // check is needed in case the profile has not been loaded yet and the
473 // gaia_id property has not yet been written.
474 return !GetGAIAIdOfProfileAtIndex(index
).empty() ||
475 !GetUserNameOfProfileAtIndex(index
).empty();
478 bool ProfileInfoCache::ProfileIsUsingDefaultAvatarAtIndex(size_t index
) const {
480 GetInfoForProfileAtIndex(index
)->GetBoolean(kIsUsingDefaultAvatarKey
, &value
);
484 bool ProfileInfoCache::ProfileIsAuthErrorAtIndex(size_t index
) const {
486 GetInfoForProfileAtIndex(index
)->GetBoolean(kIsAuthErrorKey
, &value
);
490 size_t ProfileInfoCache::GetAvatarIconIndexOfProfileAtIndex(size_t index
)
492 std::string icon_url
;
493 GetInfoForProfileAtIndex(index
)->GetString(kAvatarIconKey
, &icon_url
);
494 size_t icon_index
= 0;
495 if (!profiles::IsDefaultAvatarIconUrl(icon_url
, &icon_index
))
496 DLOG(WARNING
) << "Unknown avatar icon: " << icon_url
;
501 void ProfileInfoCache::SetProfileActiveTimeAtIndex(size_t index
) {
502 if (base::Time::Now() - GetProfileActiveTimeAtIndex(index
) <
503 base::TimeDelta::FromHours(1)) {
507 scoped_ptr
<base::DictionaryValue
> info(
508 GetInfoForProfileAtIndex(index
)->DeepCopy());
509 info
->SetDouble(kActiveTimeKey
, base::Time::Now().ToDoubleT());
510 // This takes ownership of |info|.
511 SetInfoForProfileAtIndex(index
, info
.release());
514 void ProfileInfoCache::SetNameOfProfileAtIndex(size_t index
,
515 const base::string16
& name
) {
516 scoped_ptr
<base::DictionaryValue
> info(
517 GetInfoForProfileAtIndex(index
)->DeepCopy());
518 base::string16 current_name
;
519 info
->GetString(kNameKey
, ¤t_name
);
520 if (name
== current_name
)
523 base::string16 old_display_name
= GetNameOfProfileAtIndex(index
);
524 info
->SetString(kNameKey
, name
);
526 // This takes ownership of |info|.
527 SetInfoForProfileAtIndex(index
, info
.release());
529 base::string16 new_display_name
= GetNameOfProfileAtIndex(index
);
530 base::FilePath profile_path
= GetPathOfProfileAtIndex(index
);
531 UpdateSortForProfileIndex(index
);
533 if (old_display_name
!= new_display_name
) {
534 FOR_EACH_OBSERVER(ProfileInfoCacheObserver
,
536 OnProfileNameChanged(profile_path
, old_display_name
));
540 void ProfileInfoCache::SetShortcutNameOfProfileAtIndex(
542 const base::string16
& shortcut_name
) {
543 if (shortcut_name
== GetShortcutNameOfProfileAtIndex(index
))
545 scoped_ptr
<base::DictionaryValue
> info(
546 GetInfoForProfileAtIndex(index
)->DeepCopy());
547 info
->SetString(kShortcutNameKey
, shortcut_name
);
548 // This takes ownership of |info|.
549 SetInfoForProfileAtIndex(index
, info
.release());
552 void ProfileInfoCache::SetAuthInfoOfProfileAtIndex(
554 const std::string
& gaia_id
,
555 const base::string16
& user_name
) {
556 // If both gaia_id and username are unchanged, abort early.
557 if (gaia_id
== GetGAIAIdOfProfileAtIndex(index
) &&
558 user_name
== GetUserNameOfProfileAtIndex(index
)) {
562 scoped_ptr
<base::DictionaryValue
> info(
563 GetInfoForProfileAtIndex(index
)->DeepCopy());
565 info
->SetString(kGAIAIdKey
, gaia_id
);
566 info
->SetString(kUserNameKey
, user_name
);
568 // This takes ownership of |info|.
569 SetInfoForProfileAtIndex(index
, info
.release());
571 base::FilePath profile_path
= GetPathOfProfileAtIndex(index
);
572 FOR_EACH_OBSERVER(ProfileInfoCacheObserver
,
574 OnProfileAuthInfoChanged(profile_path
));
577 void ProfileInfoCache::SetAvatarIconOfProfileAtIndex(size_t index
,
579 scoped_ptr
<base::DictionaryValue
> info(
580 GetInfoForProfileAtIndex(index
)->DeepCopy());
581 info
->SetString(kAvatarIconKey
,
582 profiles::GetDefaultAvatarIconUrl(icon_index
));
583 // This takes ownership of |info|.
584 SetInfoForProfileAtIndex(index
, info
.release());
586 base::FilePath profile_path
= GetPathOfProfileAtIndex(index
);
588 if (switches::IsNewAvatarMenu() && !disable_avatar_download_for_testing_
)
589 DownloadHighResAvatarIfNeeded(icon_index
, profile_path
);
591 FOR_EACH_OBSERVER(ProfileInfoCacheObserver
,
593 OnProfileAvatarChanged(profile_path
));
596 void ProfileInfoCache::SetIsOmittedProfileAtIndex(size_t index
,
598 if (IsOmittedProfileAtIndex(index
) == is_omitted
)
600 scoped_ptr
<base::DictionaryValue
> info(
601 GetInfoForProfileAtIndex(index
)->DeepCopy());
602 info
->SetBoolean(kIsOmittedFromProfileListKey
, is_omitted
);
603 // This takes ownership of |info|.
604 SetInfoForProfileAtIndex(index
, info
.release());
606 base::FilePath profile_path
= GetPathOfProfileAtIndex(index
);
607 FOR_EACH_OBSERVER(ProfileInfoCacheObserver
,
609 OnProfileIsOmittedChanged(profile_path
));
612 void ProfileInfoCache::SetSupervisedUserIdOfProfileAtIndex(
614 const std::string
& id
) {
615 if (GetSupervisedUserIdOfProfileAtIndex(index
) == id
)
617 scoped_ptr
<base::DictionaryValue
> info(
618 GetInfoForProfileAtIndex(index
)->DeepCopy());
619 info
->SetString(kSupervisedUserId
, id
);
620 // This takes ownership of |info|.
621 SetInfoForProfileAtIndex(index
, info
.release());
623 base::FilePath profile_path
= GetPathOfProfileAtIndex(index
);
624 FOR_EACH_OBSERVER(ProfileInfoCacheObserver
,
626 OnProfileSupervisedUserIdChanged(profile_path
));
629 void ProfileInfoCache::SetLocalAuthCredentialsOfProfileAtIndex(
631 const std::string
& credentials
) {
632 scoped_ptr
<base::DictionaryValue
> info(
633 GetInfoForProfileAtIndex(index
)->DeepCopy());
634 info
->SetString(kAuthCredentialsKey
, credentials
);
635 // This takes ownership of |info|.
636 SetInfoForProfileAtIndex(index
, info
.release());
639 void ProfileInfoCache::SetPasswordChangeDetectionTokenAtIndex(
641 const std::string
& token
) {
642 scoped_ptr
<base::DictionaryValue
> info(
643 GetInfoForProfileAtIndex(index
)->DeepCopy());
644 info
->SetString(kPasswordTokenKey
, token
);
645 // This takes ownership of |info|.
646 SetInfoForProfileAtIndex(index
, info
.release());
649 void ProfileInfoCache::SetBackgroundStatusOfProfileAtIndex(
651 bool running_background_apps
) {
652 if (GetBackgroundStatusOfProfileAtIndex(index
) == running_background_apps
)
654 scoped_ptr
<base::DictionaryValue
> info(
655 GetInfoForProfileAtIndex(index
)->DeepCopy());
656 info
->SetBoolean(kBackgroundAppsKey
, running_background_apps
);
657 // This takes ownership of |info|.
658 SetInfoForProfileAtIndex(index
, info
.release());
661 void ProfileInfoCache::SetGAIANameOfProfileAtIndex(size_t index
,
662 const base::string16
& name
) {
663 if (name
== GetGAIANameOfProfileAtIndex(index
))
666 base::string16 old_display_name
= GetNameOfProfileAtIndex(index
);
667 scoped_ptr
<base::DictionaryValue
> info(
668 GetInfoForProfileAtIndex(index
)->DeepCopy());
669 info
->SetString(kGAIANameKey
, name
);
670 // This takes ownership of |info|.
671 SetInfoForProfileAtIndex(index
, info
.release());
672 base::string16 new_display_name
= GetNameOfProfileAtIndex(index
);
673 base::FilePath profile_path
= GetPathOfProfileAtIndex(index
);
674 UpdateSortForProfileIndex(index
);
676 if (old_display_name
!= new_display_name
) {
677 FOR_EACH_OBSERVER(ProfileInfoCacheObserver
,
679 OnProfileNameChanged(profile_path
, old_display_name
));
683 void ProfileInfoCache::SetGAIAGivenNameOfProfileAtIndex(
685 const base::string16
& name
) {
686 if (name
== GetGAIAGivenNameOfProfileAtIndex(index
))
689 base::string16 old_display_name
= GetNameOfProfileAtIndex(index
);
690 scoped_ptr
<base::DictionaryValue
> info(
691 GetInfoForProfileAtIndex(index
)->DeepCopy());
692 info
->SetString(kGAIAGivenNameKey
, name
);
693 // This takes ownership of |info|.
694 SetInfoForProfileAtIndex(index
, info
.release());
695 base::string16 new_display_name
= GetNameOfProfileAtIndex(index
);
696 base::FilePath profile_path
= GetPathOfProfileAtIndex(index
);
697 UpdateSortForProfileIndex(index
);
699 if (old_display_name
!= new_display_name
) {
700 FOR_EACH_OBSERVER(ProfileInfoCacheObserver
,
702 OnProfileNameChanged(profile_path
, old_display_name
));
706 void ProfileInfoCache::SetGAIAPictureOfProfileAtIndex(size_t index
,
707 const gfx::Image
* image
) {
708 base::FilePath path
= GetPathOfProfileAtIndex(index
);
709 std::string key
= CacheKeyFromProfilePath(path
);
711 // Delete the old bitmap from cache.
712 std::map
<std::string
, gfx::Image
*>::iterator it
=
713 cached_avatar_images_
.find(key
);
714 if (it
!= cached_avatar_images_
.end()) {
716 cached_avatar_images_
.erase(it
);
719 std::string old_file_name
;
720 GetInfoForProfileAtIndex(index
)->GetString(
721 kGAIAPictureFileNameKey
, &old_file_name
);
722 std::string new_file_name
;
725 // Delete the old bitmap from disk.
726 if (!old_file_name
.empty()) {
727 base::FilePath image_path
= path
.AppendASCII(old_file_name
);
728 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE
,
729 base::Bind(&DeleteBitmap
, image_path
));
732 // Save the new bitmap to disk.
734 old_file_name
.empty() ? profiles::kGAIAPictureFileName
: old_file_name
;
735 base::FilePath image_path
= path
.AppendASCII(new_file_name
);
736 SaveAvatarImageAtPath(
737 image
, key
, image_path
, GetPathOfProfileAtIndex(index
));
740 scoped_ptr
<base::DictionaryValue
> info(
741 GetInfoForProfileAtIndex(index
)->DeepCopy());
742 info
->SetString(kGAIAPictureFileNameKey
, new_file_name
);
743 // This takes ownership of |info|.
744 SetInfoForProfileAtIndex(index
, info
.release());
746 FOR_EACH_OBSERVER(ProfileInfoCacheObserver
,
748 OnProfileAvatarChanged(path
));
751 void ProfileInfoCache::SetIsUsingGAIAPictureOfProfileAtIndex(size_t index
,
753 scoped_ptr
<base::DictionaryValue
> info(
754 GetInfoForProfileAtIndex(index
)->DeepCopy());
755 info
->SetBoolean(kUseGAIAPictureKey
, value
);
756 // This takes ownership of |info|.
757 SetInfoForProfileAtIndex(index
, info
.release());
759 base::FilePath profile_path
= GetPathOfProfileAtIndex(index
);
760 FOR_EACH_OBSERVER(ProfileInfoCacheObserver
,
762 OnProfileAvatarChanged(profile_path
));
765 void ProfileInfoCache::SetProfileSigninRequiredAtIndex(size_t index
,
767 if (value
== ProfileIsSigninRequiredAtIndex(index
))
770 scoped_ptr
<base::DictionaryValue
> info(
771 GetInfoForProfileAtIndex(index
)->DeepCopy());
772 info
->SetBoolean(kSigninRequiredKey
, value
);
773 // This takes ownership of |info|.
774 SetInfoForProfileAtIndex(index
, info
.release());
776 base::FilePath profile_path
= GetPathOfProfileAtIndex(index
);
777 FOR_EACH_OBSERVER(ProfileInfoCacheObserver
,
779 OnProfileSigninRequiredChanged(profile_path
));
782 void ProfileInfoCache::SetProfileIsEphemeralAtIndex(size_t index
, bool value
) {
783 if (value
== ProfileIsEphemeralAtIndex(index
))
786 scoped_ptr
<base::DictionaryValue
> info(
787 GetInfoForProfileAtIndex(index
)->DeepCopy());
788 info
->SetBoolean(kProfileIsEphemeral
, value
);
789 // This takes ownership of |info|.
790 SetInfoForProfileAtIndex(index
, info
.release());
793 void ProfileInfoCache::SetProfileIsUsingDefaultNameAtIndex(
794 size_t index
, bool value
) {
795 if (value
== ProfileIsUsingDefaultNameAtIndex(index
))
798 base::string16 old_display_name
= GetNameOfProfileAtIndex(index
);
800 scoped_ptr
<base::DictionaryValue
> info(
801 GetInfoForProfileAtIndex(index
)->DeepCopy());
802 info
->SetBoolean(kIsUsingDefaultNameKey
, value
);
803 // This takes ownership of |info|.
804 SetInfoForProfileAtIndex(index
, info
.release());
806 base::string16 new_display_name
= GetNameOfProfileAtIndex(index
);
807 const base::FilePath profile_path
= GetPathOfProfileAtIndex(index
);
809 if (old_display_name
!= new_display_name
) {
810 FOR_EACH_OBSERVER(ProfileInfoCacheObserver
,
812 OnProfileNameChanged(profile_path
, old_display_name
));
816 void ProfileInfoCache::SetProfileIsUsingDefaultAvatarAtIndex(
817 size_t index
, bool value
) {
818 if (value
== ProfileIsUsingDefaultAvatarAtIndex(index
))
821 scoped_ptr
<base::DictionaryValue
> info(
822 GetInfoForProfileAtIndex(index
)->DeepCopy());
823 info
->SetBoolean(kIsUsingDefaultAvatarKey
, value
);
824 // This takes ownership of |info|.
825 SetInfoForProfileAtIndex(index
, info
.release());
828 void ProfileInfoCache::SetProfileIsAuthErrorAtIndex(size_t index
, bool value
) {
829 if (value
== ProfileIsAuthErrorAtIndex(index
))
832 scoped_ptr
<base::DictionaryValue
> info(
833 GetInfoForProfileAtIndex(index
)->DeepCopy());
834 info
->SetBoolean(kIsAuthErrorKey
, value
);
835 // This takes ownership of |info|.
836 SetInfoForProfileAtIndex(index
, info
.release());
839 bool ProfileInfoCache::IsDefaultProfileName(const base::string16
& name
) const {
840 // Check if it's a "First user" old-style name.
841 if (name
== l10n_util::GetStringUTF16(IDS_DEFAULT_PROFILE_NAME
) ||
842 name
== l10n_util::GetStringUTF16(IDS_LEGACY_DEFAULT_PROFILE_NAME
))
845 // Check if it's one of the old-style profile names.
846 for (size_t i
= 0; i
< arraysize(kDefaultNames
); ++i
) {
847 if (name
== l10n_util::GetStringUTF16(kDefaultNames
[i
]))
851 // Check whether it's one of the "Person %d" style names.
852 std::string default_name_format
= l10n_util::GetStringFUTF8(
853 IDS_NEW_NUMBERED_PROFILE_NAME
, base::ASCIIToUTF16("%d"));
855 int generic_profile_number
; // Unused. Just a placeholder for sscanf.
856 int assignments
= sscanf(base::UTF16ToUTF8(name
).c_str(),
857 default_name_format
.c_str(),
858 &generic_profile_number
);
859 // Unless it matched the format, this is a custom name.
860 return assignments
== 1;
863 base::string16
ProfileInfoCache::ChooseNameForNewProfile(
864 size_t icon_index
) const {
866 for (int name_index
= 1; ; ++name_index
) {
867 if (switches::IsNewAvatarMenu()) {
868 name
= l10n_util::GetStringFUTF16Int(IDS_NEW_NUMBERED_PROFILE_NAME
,
870 } else if (icon_index
< profiles::GetGenericAvatarIconCount()) {
871 name
= l10n_util::GetStringFUTF16Int(IDS_NUMBERED_PROFILE_NAME
,
874 name
= l10n_util::GetStringUTF16(
875 kDefaultNames
[icon_index
- profiles::GetGenericAvatarIconCount()]);
877 name
.append(base::UTF8ToUTF16(base::IntToString(name_index
)));
880 // Loop through previously named profiles to ensure we're not duplicating.
881 bool name_found
= false;
882 for (size_t i
= 0; i
< GetNumberOfProfiles(); ++i
) {
883 if (GetNameOfProfileAtIndex(i
) == name
) {
893 size_t ProfileInfoCache::ChooseAvatarIconIndexForNewProfile() const {
894 size_t icon_index
= 0;
895 // Try to find a unique, non-generic icon.
896 if (ChooseAvatarIconIndexForNewProfile(false, true, &icon_index
))
898 // Try to find any unique icon.
899 if (ChooseAvatarIconIndexForNewProfile(true, true, &icon_index
))
901 // Settle for any random icon, even if it's not unique.
902 if (ChooseAvatarIconIndexForNewProfile(true, false, &icon_index
))
909 const base::FilePath
& ProfileInfoCache::GetUserDataDir() const {
910 return user_data_dir_
;
914 void ProfileInfoCache::RegisterPrefs(PrefRegistrySimple
* registry
) {
915 registry
->RegisterDictionaryPref(prefs::kProfileInfoCache
);
918 void ProfileInfoCache::DownloadHighResAvatarIfNeeded(
920 const base::FilePath
& profile_path
) {
921 // Downloading is only supported on desktop.
922 #if defined(OS_ANDROID) || defined(OS_IOS) || defined(OS_CHROMEOS)
925 DCHECK(!disable_avatar_download_for_testing_
);
927 // If this is the placeholder avatar, it is already included in the
928 // resources, so it doesn't need to be downloaded (and it will never be
929 // requested from disk by GetHighResAvatarOfProfileAtIndex).
930 if (icon_index
== profiles::GetPlaceholderAvatarIndex())
933 const base::FilePath
& file_path
=
934 profiles::GetPathOfHighResAvatarAtIndex(icon_index
);
935 base::Closure callback
=
936 base::Bind(&ProfileInfoCache::DownloadHighResAvatar
,
940 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE
,
941 base::Bind(&RunCallbackIfFileMissing
, file_path
, callback
));
944 void ProfileInfoCache::SaveAvatarImageAtPath(
945 const gfx::Image
* image
,
946 const std::string
& key
,
947 const base::FilePath
& image_path
,
948 const base::FilePath
& profile_path
) {
949 cached_avatar_images_
[key
] = new gfx::Image(*image
);
951 scoped_ptr
<ImageData
> data(new ImageData
);
952 scoped_refptr
<base::RefCountedMemory
> png_data
= image
->As1xPNGBytes();
953 data
->assign(png_data
->front(), png_data
->front() + png_data
->size());
955 // Remove the file from the list of downloads in progress. Note that this list
956 // only contains the high resolution avatars, and not the Gaia profile images.
957 auto downloader_iter
= avatar_images_downloads_in_progress_
.find(key
);
958 if (downloader_iter
!= avatar_images_downloads_in_progress_
.end()) {
959 // We mustn't delete the avatar downloader right here, since we're being
961 BrowserThread::DeleteSoon(BrowserThread::UI
, FROM_HERE
,
962 downloader_iter
->second
);
963 avatar_images_downloads_in_progress_
.erase(downloader_iter
);
967 LOG(ERROR
) << "Failed to PNG encode the image.";
969 base::Closure callback
= base::Bind(&ProfileInfoCache::OnAvatarPictureSaved
,
970 AsWeakPtr(), key
, profile_path
);
971 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE
,
972 base::Bind(&SaveBitmap
, base::Passed(&data
), image_path
, callback
));
976 const base::DictionaryValue
* ProfileInfoCache::GetInfoForProfileAtIndex(
977 size_t index
) const {
978 DCHECK_LT(index
, GetNumberOfProfiles());
979 const base::DictionaryValue
* cache
=
980 prefs_
->GetDictionary(prefs::kProfileInfoCache
);
981 const base::DictionaryValue
* info
= NULL
;
982 cache
->GetDictionaryWithoutPathExpansion(sorted_keys_
[index
], &info
);
986 void ProfileInfoCache::SetInfoForProfileAtIndex(size_t index
,
987 base::DictionaryValue
* info
) {
988 DictionaryPrefUpdate
update(prefs_
, prefs::kProfileInfoCache
);
989 base::DictionaryValue
* cache
= update
.Get();
990 cache
->SetWithoutPathExpansion(sorted_keys_
[index
], info
);
993 std::string
ProfileInfoCache::CacheKeyFromProfilePath(
994 const base::FilePath
& profile_path
) const {
995 DCHECK(user_data_dir_
== profile_path
.DirName());
996 base::FilePath base_name
= profile_path
.BaseName();
997 return base_name
.MaybeAsASCII();
1000 std::vector
<std::string
>::iterator
ProfileInfoCache::FindPositionForProfile(
1001 const std::string
& search_key
,
1002 const base::string16
& search_name
) {
1003 base::string16 search_name_l
= base::i18n::ToLower(search_name
);
1004 for (size_t i
= 0; i
< GetNumberOfProfiles(); ++i
) {
1005 base::string16 name_l
= base::i18n::ToLower(GetNameOfProfileAtIndex(i
));
1006 int name_compare
= search_name_l
.compare(name_l
);
1007 if (name_compare
< 0)
1008 return sorted_keys_
.begin() + i
;
1009 if (name_compare
== 0) {
1010 int key_compare
= search_key
.compare(sorted_keys_
[i
]);
1011 if (key_compare
< 0)
1012 return sorted_keys_
.begin() + i
;
1015 return sorted_keys_
.end();
1018 bool ProfileInfoCache::IconIndexIsUnique(size_t icon_index
) const {
1019 for (size_t i
= 0; i
< GetNumberOfProfiles(); ++i
) {
1020 if (GetAvatarIconIndexOfProfileAtIndex(i
) == icon_index
)
1026 bool ProfileInfoCache::ChooseAvatarIconIndexForNewProfile(
1027 bool allow_generic_icon
,
1028 bool must_be_unique
,
1029 size_t* out_icon_index
) const {
1030 // Always allow all icons for new profiles if using the
1031 // --new-avatar-menu flag.
1032 if (switches::IsNewAvatarMenu())
1033 allow_generic_icon
= true;
1034 size_t start
= allow_generic_icon
? 0 : profiles::GetGenericAvatarIconCount();
1035 size_t end
= profiles::GetDefaultAvatarIconCount();
1036 size_t count
= end
- start
;
1038 int rand
= base::RandInt(0, count
);
1039 for (size_t i
= 0; i
< count
; ++i
) {
1040 size_t icon_index
= start
+ (rand
+ i
) % count
;
1041 if (!must_be_unique
|| IconIndexIsUnique(icon_index
)) {
1042 *out_icon_index
= icon_index
;
1050 void ProfileInfoCache::UpdateSortForProfileIndex(size_t index
) {
1051 base::string16 name
= GetNameOfProfileAtIndex(index
);
1053 // Remove and reinsert key in |sorted_keys_| to alphasort.
1054 std::string key
= CacheKeyFromProfilePath(GetPathOfProfileAtIndex(index
));
1055 std::vector
<std::string
>::iterator key_it
=
1056 std::find(sorted_keys_
.begin(), sorted_keys_
.end(), key
);
1057 DCHECK(key_it
!= sorted_keys_
.end());
1058 sorted_keys_
.erase(key_it
);
1059 sorted_keys_
.insert(FindPositionForProfile(key
, name
), key
);
1062 const gfx::Image
* ProfileInfoCache::GetHighResAvatarOfProfileAtIndex(
1063 size_t index
) const {
1064 const size_t avatar_index
= GetAvatarIconIndexOfProfileAtIndex(index
);
1066 // If this is the placeholder avatar, it is already included in the
1067 // resources, so it doesn't need to be downloaded.
1068 if (avatar_index
== profiles::GetPlaceholderAvatarIndex()) {
1069 return &ui::ResourceBundle::GetSharedInstance().GetImageNamed(
1070 profiles::GetPlaceholderAvatarIconResourceID());
1073 const std::string file_name
=
1074 profiles::GetDefaultAvatarIconFileNameAtIndex(avatar_index
);
1075 const base::FilePath image_path
=
1076 profiles::GetPathOfHighResAvatarAtIndex(avatar_index
);
1077 return LoadAvatarPictureFromPath(GetPathOfProfileAtIndex(index
), file_name
,
1081 void ProfileInfoCache::DownloadHighResAvatar(
1083 const base::FilePath
& profile_path
) {
1084 // Downloading is only supported on desktop.
1085 #if defined(OS_ANDROID) || defined(OS_IOS) || defined(OS_CHROMEOS)
1088 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461175
1090 tracked_objects::ScopedTracker
tracking_profile1(
1091 FROM_HERE_WITH_EXPLICIT_FUNCTION(
1092 "461175 ProfileInfoCache::DownloadHighResAvatar::GetFileName"));
1093 const char* file_name
=
1094 profiles::GetDefaultAvatarIconFileNameAtIndex(icon_index
);
1096 // If the file is already being downloaded, don't start another download.
1097 if (avatar_images_downloads_in_progress_
.count(file_name
))
1100 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461175
1102 tracked_objects::ScopedTracker
tracking_profile2(
1103 FROM_HERE_WITH_EXPLICIT_FUNCTION(
1104 "461175 ProfileInfoCache::DownloadHighResAvatar::MakeDownloader"));
1105 // Start the download for this file. The cache takes ownership of the
1106 // |avatar_downloader|, which will be deleted when the download completes, or
1107 // if that never happens, when the ProfileInfoCache is destroyed.
1108 ProfileAvatarDownloader
* avatar_downloader
= new ProfileAvatarDownloader(
1112 avatar_images_downloads_in_progress_
[file_name
] = avatar_downloader
;
1114 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461175
1116 tracked_objects::ScopedTracker
tracking_profile3(
1117 FROM_HERE_WITH_EXPLICIT_FUNCTION(
1118 "461175 ProfileInfoCache::DownloadHighResAvatar::StartDownload"));
1119 avatar_downloader
->Start();
1122 const gfx::Image
* ProfileInfoCache::LoadAvatarPictureFromPath(
1123 const base::FilePath
& profile_path
,
1124 const std::string
& key
,
1125 const base::FilePath
& image_path
) const {
1126 // If the picture is already loaded then use it.
1127 if (cached_avatar_images_
.count(key
)) {
1128 if (cached_avatar_images_
[key
]->IsEmpty())
1130 return cached_avatar_images_
[key
];
1133 // Don't download the image if downloading is disabled for tests.
1134 if (disable_avatar_download_for_testing_
)
1137 // If the picture is already being loaded then don't try loading it again.
1138 if (cached_avatar_images_loading_
[key
])
1140 cached_avatar_images_loading_
[key
] = true;
1142 gfx::Image
** image
= new gfx::Image
*;
1143 BrowserThread::PostTaskAndReply(BrowserThread::FILE, FROM_HERE
,
1144 base::Bind(&ReadBitmap
, image_path
, image
),
1145 base::Bind(&ProfileInfoCache::OnAvatarPictureLoaded
,
1146 const_cast<ProfileInfoCache
*>(this)->AsWeakPtr(),
1147 profile_path
, key
, image
));
1151 void ProfileInfoCache::OnAvatarPictureLoaded(const base::FilePath
& profile_path
,
1152 const std::string
& key
,
1153 gfx::Image
** image
) const {
1154 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
1155 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461175
1157 tracked_objects::ScopedTracker
tracking_profile1(
1158 FROM_HERE_WITH_EXPLICIT_FUNCTION(
1159 "461175 ProfileInfoCache::OnAvatarPictureLoaded::Start"));
1161 cached_avatar_images_loading_
[key
] = false;
1162 delete cached_avatar_images_
[key
];
1165 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461175
1167 tracked_objects::ScopedTracker
tracking_profile2(
1168 FROM_HERE_WITH_EXPLICIT_FUNCTION(
1169 "461175 ProfileInfoCache::OnAvatarPictureLoaded::SetImage"));
1170 cached_avatar_images_
[key
] = *image
;
1172 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461175
1174 tracked_objects::ScopedTracker
tracking_profile3(
1175 FROM_HERE_WITH_EXPLICIT_FUNCTION(
1176 "461175 ProfileInfoCache::OnAvatarPictureLoaded::MakeEmptyImage"));
1177 // Place an empty image in the cache to avoid reloading it again.
1178 cached_avatar_images_
[key
] = new gfx::Image();
1180 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461175
1182 tracked_objects::ScopedTracker
tracking_profile4(
1183 FROM_HERE_WITH_EXPLICIT_FUNCTION(
1184 "461175 ProfileInfoCache::OnAvatarPictureLoaded::DeleteImage"));
1187 FOR_EACH_OBSERVER(ProfileInfoCacheObserver
,
1189 OnProfileHighResAvatarLoaded(profile_path
));
1192 void ProfileInfoCache::OnAvatarPictureSaved(
1193 const std::string
& file_name
,
1194 const base::FilePath
& profile_path
) {
1195 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
1197 FOR_EACH_OBSERVER(ProfileInfoCacheObserver
,
1199 OnProfileHighResAvatarLoaded(profile_path
));
1202 void ProfileInfoCache::MigrateLegacyProfileNamesAndDownloadAvatars() {
1203 DCHECK(switches::IsNewAvatarMenu());
1205 // Only do this on desktop platforms.
1206 #if !defined(OS_ANDROID) && !defined(OS_IOS) && !defined(OS_CHROMEOS)
1207 // Migrate any legacy profile names ("First user", "Default Profile") to
1208 // new style default names ("Person 1"). The problem here is that every
1209 // time you rename a profile, the ProfileInfoCache sorts itself, so
1210 // whatever you were iterating through is no longer valid. We need to
1211 // save a list of the profile paths (which thankfully do not change) that
1212 // need to be renamed. We also can't pre-compute the new names, as they
1213 // depend on the names of all the other profiles in the info cache, so they
1214 // need to be re-computed after each rename.
1215 std::vector
<base::FilePath
> profiles_to_rename
;
1217 const base::string16 default_profile_name
= base::i18n::ToLower(
1218 l10n_util::GetStringUTF16(IDS_DEFAULT_PROFILE_NAME
));
1219 const base::string16 default_legacy_profile_name
= base::i18n::ToLower(
1220 l10n_util::GetStringUTF16(IDS_LEGACY_DEFAULT_PROFILE_NAME
));
1222 for (size_t i
= 0; i
< GetNumberOfProfiles(); i
++) {
1223 DownloadHighResAvatarIfNeeded(GetAvatarIconIndexOfProfileAtIndex(i
),
1224 GetPathOfProfileAtIndex(i
));
1226 base::string16 name
= base::i18n::ToLower(GetNameOfProfileAtIndex(i
));
1227 if (name
== default_profile_name
|| name
== default_legacy_profile_name
)
1228 profiles_to_rename
.push_back(GetPathOfProfileAtIndex(i
));
1231 // Rename the necessary profiles.
1232 std::vector
<base::FilePath
>::const_iterator it
;
1233 for (it
= profiles_to_rename
.begin(); it
!= profiles_to_rename
.end(); ++it
) {
1234 size_t profile_index
= GetIndexOfProfileWithPath(*it
);
1235 SetProfileIsUsingDefaultNameAtIndex(profile_index
, true);
1236 // This will assign a new "Person %d" type name and re-sort the cache.
1237 SetNameOfProfileAtIndex(profile_index
, ChooseNameForNewProfile(
1238 GetAvatarIconIndexOfProfileAtIndex(profile_index
)));
1243 void ProfileInfoCache::AddProfile(
1244 const base::FilePath
& profile_path
,
1245 const base::string16
& name
,
1246 const std::string
& gaia_id
,
1247 const base::string16
& user_name
,
1249 const std::string
& supervised_user_id
) {
1251 profile_path
, name
, gaia_id
, user_name
, icon_index
, supervised_user_id
);
1254 void ProfileInfoCache::RemoveProfile(const base::FilePath
& profile_path
) {
1255 DeleteProfileFromCache(profile_path
);
1258 std::vector
<ProfileAttributesEntry
*>
1259 ProfileInfoCache::GetAllProfilesAttributes() {
1260 std::vector
<ProfileAttributesEntry
*> ret
;
1261 for (size_t i
= 0; i
< GetNumberOfProfiles(); ++i
) {
1262 ProfileAttributesEntry
* entry
;
1263 if (GetProfileAttributesWithPath(GetPathOfProfileAtIndex(i
), &entry
)) {
1264 ret
.push_back(entry
);
1270 bool ProfileInfoCache::GetProfileAttributesWithPath(
1271 const base::FilePath
& path
, ProfileAttributesEntry
** entry
) {
1272 if (GetNumberOfProfiles() == 0)
1275 if (GetIndexOfProfileWithPath(path
) == std::string::npos
)
1278 if (profile_attributes_entries_
.find(path
) ==
1279 profile_attributes_entries_
.end()) {
1280 // The profile info is in the cache but its entry isn't created yet, insert
1282 scoped_ptr
<ProfileAttributesEntry
> new_entry(new ProfileAttributesEntry());
1283 profile_attributes_entries_
.add(path
, new_entry
.Pass());
1284 profile_attributes_entries_
.get(path
)->Initialize(this, path
);
1287 *entry
= profile_attributes_entries_
.get(path
);