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 <algorithm>
8 #include <memory>
9 #include <utility>
10 
11 #include "base/bind.h"
12 #include "base/files/file_util.h"
13 #include "base/i18n/case_conversion.h"
14 #include "base/logging.h"
15 #include "base/macros.h"
16 #include "base/stl_util.h"
17 #include "base/strings/string_piece.h"
18 #include "base/task/post_task.h"
19 #include "base/threading/scoped_blocking_call.h"
20 #include "base/values.h"
21 #include "build/build_config.h"
22 #include "chrome/browser/browser_process.h"
23 #include "chrome/browser/profiles/profile_avatar_icon_util.h"
24 #include "chrome/browser/profiles/profiles_state.h"
25 #include "chrome/common/buildflags.h"
26 #include "chrome/common/pref_names.h"
27 #include "chrome/grit/generated_resources.h"
28 #include "components/account_id/account_id.h"
29 #include "components/prefs/pref_registry_simple.h"
30 #include "components/prefs/pref_service.h"
31 #include "components/prefs/scoped_user_pref_update.h"
32 #include "ui/base/l10n/l10n_util.h"
33 #include "ui/base/resource/resource_bundle.h"
34 #include "ui/gfx/image/image.h"
35 #include "ui/gfx/image/image_util.h"
36 
37 #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
38 #include "chrome/browser/supervised_user/supervised_user_constants.h"
39 #endif
40 
41 namespace {
42 
43 const char kIsUsingDefaultAvatarKey[] = "is_using_default_avatar";
44 const char kUseGAIAPictureKey[] = "use_gaia_picture";
45 const char kGAIAPictureFileNameKey[] = "gaia_picture_file_name";
46 const char kLastDownloadedGAIAPictureUrlWithSizeKey[] =
47     "last_downloaded_gaia_picture_url_with_size";
48 const char kAccountIdKey[] = "account_id_key";
49 const char kProfileCountLastUpdatePref[] = "profile.profile_counts_reported";
50 #if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
51 const char kLegacyProfileNameMigrated[] = "legacy.profile.name.migrated";
52 bool migration_enabled_for_testing = false;
53 #endif  // !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
54 
DeleteBitmap(const base::FilePath & image_path)55 void DeleteBitmap(const base::FilePath& image_path) {
56   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
57                                                 base::BlockingType::MAY_BLOCK);
58   base::DeleteFile(image_path);
59 }
60 
61 }  // namespace
62 
ProfileInfoCache(PrefService * prefs,const base::FilePath & user_data_dir)63 ProfileInfoCache::ProfileInfoCache(PrefService* prefs,
64                                    const base::FilePath& user_data_dir)
65     : ProfileAttributesStorage(prefs), user_data_dir_(user_data_dir) {
66   // Populate the cache
67   DictionaryPrefUpdate update(prefs_, prefs::kProfileInfoCache);
68   base::DictionaryValue* cache = update.Get();
69   for (base::DictionaryValue::Iterator it(*cache);
70        !it.IsAtEnd(); it.Advance()) {
71     base::DictionaryValue* info = nullptr;
72     cache->GetDictionaryWithoutPathExpansion(it.key(), &info);
73 #if BUILDFLAG(ENABLE_SUPERVISED_USERS) && !defined(OS_ANDROID) && \
74     !defined(OS_CHROMEOS)
75     std::string supervised_user_id;
76     info->GetString(ProfileAttributesEntry::kSupervisedUserId,
77                     &supervised_user_id);
78     // Silently ignore legacy supervised user profiles.
79     if (!supervised_user_id.empty() &&
80         supervised_user_id != supervised_users::kChildAccountSUID) {
81       continue;
82     }
83 #endif
84     base::string16 name;
85     info->GetString(ProfileAttributesEntry::kNameKey, &name);
86     keys_.push_back(it.key());
87     profile_attributes_entries_[user_data_dir_.AppendASCII(it.key()).value()] =
88         std::unique_ptr<ProfileAttributesEntry>(nullptr);
89 
90     bool using_default_name;
91     if (!info->GetBoolean(ProfileAttributesEntry::kIsUsingDefaultNameKey,
92                           &using_default_name)) {
93       // If the preference hasn't been set, and the name is default, assume
94       // that the user hasn't done this on purpose.
95       // |include_check_for_legacy_profile_name| is true as this is an old
96       // pre-existing profile and might have a legacy default profile name.
97       using_default_name = IsDefaultProfileName(
98           name, /*include_check_for_legacy_profile_name=*/true);
99       info->SetBoolean(ProfileAttributesEntry::kIsUsingDefaultNameKey,
100                        using_default_name);
101     }
102 
103     // For profiles that don't have the "using default avatar" state set yet,
104     // assume it's the same as the "using default name" state.
105     if (!info->HasKey(kIsUsingDefaultAvatarKey)) {
106       info->SetBoolean(kIsUsingDefaultAvatarKey, using_default_name);
107     }
108   }
109 
110   // If needed, start downloading the high-res avatars and migrate any legacy
111   // profile names.
112   if (!disable_avatar_download_for_testing_)
113     DownloadAvatars();
114 
115 #if !defined(OS_ANDROID)
116   LoadGAIAPictureIfNeeded();
117 #endif
118 
119 #if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
120   bool migrate_legacy_profile_names =
121       (!prefs_->GetBoolean(kLegacyProfileNameMigrated) ||
122        migration_enabled_for_testing);
123   if (migrate_legacy_profile_names) {
124     MigrateLegacyProfileNamesAndRecomputeIfNeeded();
125     prefs_->SetBoolean(kLegacyProfileNameMigrated, true);
126   }
127 
128   repeating_timer_ = std::make_unique<signin::PersistentRepeatingTimer>(
129       prefs_, kProfileCountLastUpdatePref, base::TimeDelta::FromHours(24),
130       base::Bind(&ProfileMetrics::LogNumberOfProfiles, this));
131   repeating_timer_->Start();
132 #endif  // !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
133 }
134 
135 ProfileInfoCache::~ProfileInfoCache() = default;
136 
AddProfileToCache(const base::FilePath & profile_path,const base::string16 & name,const std::string & gaia_id,const base::string16 & user_name,bool is_consented_primary_account,size_t icon_index,const std::string & supervised_user_id,const AccountId & account_id)137 void ProfileInfoCache::AddProfileToCache(const base::FilePath& profile_path,
138                                          const base::string16& name,
139                                          const std::string& gaia_id,
140                                          const base::string16& user_name,
141                                          bool is_consented_primary_account,
142                                          size_t icon_index,
143                                          const std::string& supervised_user_id,
144                                          const AccountId& account_id) {
145 #if BUILDFLAG(ENABLE_SUPERVISED_USERS) && !defined(OS_ANDROID) && \
146     !defined(OS_CHROMEOS)
147   // Silently ignore legacy supervised user profiles.
148   if (!supervised_user_id.empty() &&
149       supervised_user_id != supervised_users::kChildAccountSUID) {
150     return;
151   }
152 #endif
153   std::string key = CacheKeyFromProfilePath(profile_path);
154   DictionaryPrefUpdate update(prefs_, prefs::kProfileInfoCache);
155   base::DictionaryValue* cache = update.Get();
156 
157   std::unique_ptr<base::DictionaryValue> info(new base::DictionaryValue);
158   info->SetString(ProfileAttributesEntry::kNameKey, name);
159   info->SetString(ProfileAttributesEntry::kGAIAIdKey, gaia_id);
160   info->SetString(ProfileAttributesEntry::kUserNameKey, user_name);
161   DCHECK(!is_consented_primary_account || !gaia_id.empty() ||
162          !user_name.empty());
163   info->SetBoolean(ProfileAttributesEntry::kIsConsentedPrimaryAccountKey,
164                    is_consented_primary_account);
165   info->SetString(ProfileAttributesEntry::kAvatarIconKey,
166                   profiles::GetDefaultAvatarIconUrl(icon_index));
167   // Default value for whether background apps are running is false.
168   info->SetBoolean(ProfileAttributesEntry::kBackgroundAppsKey, false);
169   info->SetString(ProfileAttributesEntry::kSupervisedUserId,
170                   supervised_user_id);
171   info->SetBoolean(ProfileAttributesEntry::kIsOmittedFromProfileListKey,
172                    !supervised_user_id.empty());
173   info->SetBoolean(ProfileAttributesEntry::kProfileIsEphemeral, false);
174   // Either the user has provided a name manually on purpose, and in this case
175   // we should not check for legacy profile names or this a new profile but then
176   // it is not a legacy name, so we dont need to check for legacy names.
177   info->SetBoolean(ProfileAttributesEntry::kIsUsingDefaultNameKey,
178                    IsDefaultProfileName(
179                        name, /*include_check_for_legacy_profile_name*/ false));
180   // Assume newly created profiles use a default avatar.
181   info->SetBoolean(kIsUsingDefaultAvatarKey, true);
182   if (account_id.HasAccountIdKey())
183     info->SetString(kAccountIdKey, account_id.GetAccountIdKey());
184   cache->SetWithoutPathExpansion(key, std::move(info));
185   keys_.push_back(key);
186   profile_attributes_entries_[user_data_dir_.AppendASCII(key).value()] =
187       std::unique_ptr<ProfileAttributesEntry>();
188 
189   if (!disable_avatar_download_for_testing_)
190     DownloadHighResAvatarIfNeeded(icon_index, profile_path);
191 
192   NotifyIfProfileNamesHaveChanged();
193   for (auto& observer : observer_list_)
194     observer.OnProfileAdded(profile_path);
195 }
196 
DisableProfileMetricsForTesting()197 void ProfileInfoCache::DisableProfileMetricsForTesting() {
198 #if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
199   repeating_timer_.reset();
200 #endif
201 }
202 
NotifyIfProfileNamesHaveChanged()203 void ProfileInfoCache::NotifyIfProfileNamesHaveChanged() {
204   std::vector<ProfileAttributesEntry*> entries = GetAllProfilesAttributes();
205   for (ProfileAttributesEntry* entry : entries) {
206     base::string16 old_display_name = entry->GetLastNameToDisplay();
207     if (entry->HasProfileNameChanged()) {
208       for (auto& observer : observer_list_)
209         observer.OnProfileNameChanged(entry->GetPath(), old_display_name);
210     }
211   }
212 }
213 
NotifyProfileSupervisedUserIdChanged(const base::FilePath & profile_path)214 void ProfileInfoCache::NotifyProfileSupervisedUserIdChanged(
215     const base::FilePath& profile_path) {
216   for (auto& observer : observer_list_)
217     observer.OnProfileSupervisedUserIdChanged(profile_path);
218 }
219 
NotifyProfileIsOmittedChanged(const base::FilePath & profile_path)220 void ProfileInfoCache::NotifyProfileIsOmittedChanged(
221     const base::FilePath& profile_path) {
222   for (auto& observer : observer_list_)
223     observer.OnProfileIsOmittedChanged(profile_path);
224 }
225 
NotifyProfileThemeColorsChanged(const base::FilePath & profile_path)226 void ProfileInfoCache::NotifyProfileThemeColorsChanged(
227     const base::FilePath& profile_path) {
228   for (auto& observer : observer_list_)
229     observer.OnProfileThemeColorsChanged(profile_path);
230 }
231 
DeleteProfileFromCache(const base::FilePath & profile_path)232 void ProfileInfoCache::DeleteProfileFromCache(
233     const base::FilePath& profile_path) {
234   ProfileAttributesEntry* entry;
235   if (!GetProfileAttributesWithPath(profile_path, &entry)) {
236     NOTREACHED();
237     return;
238   }
239 
240   base::string16 name = entry->GetName();
241 
242   for (auto& observer : observer_list_)
243     observer.OnProfileWillBeRemoved(profile_path);
244 
245   DictionaryPrefUpdate update(prefs_, prefs::kProfileInfoCache);
246   base::DictionaryValue* cache = update.Get();
247   std::string key = CacheKeyFromProfilePath(profile_path);
248   cache->Remove(key, nullptr);
249   keys_.erase(std::find(keys_.begin(), keys_.end(), key));
250   profile_attributes_entries_.erase(profile_path.value());
251 
252   NotifyIfProfileNamesHaveChanged();
253   for (auto& observer : observer_list_) {
254     observer.OnProfileWasRemoved(profile_path, name);
255   }
256 }
257 
GetNumberOfProfiles() const258 size_t ProfileInfoCache::GetNumberOfProfiles() const {
259   return keys_.size();
260 }
261 
GetIndexOfProfileWithPath(const base::FilePath & profile_path) const262 size_t ProfileInfoCache::GetIndexOfProfileWithPath(
263     const base::FilePath& profile_path) const {
264   if (profile_path.DirName() != user_data_dir_)
265     return std::string::npos;
266   std::string search_key = CacheKeyFromProfilePath(profile_path);
267   for (size_t i = 0; i < keys_.size(); ++i) {
268     if (keys_[i] == search_key)
269       return i;
270   }
271   return std::string::npos;
272 }
273 
GetPathOfProfileAtIndex(size_t index) const274 base::FilePath ProfileInfoCache::GetPathOfProfileAtIndex(size_t index) const {
275   return user_data_dir_.AppendASCII(keys_[index]);
276 }
277 
GetGAIAPictureOfProfileAtIndex(size_t index) const278 const gfx::Image* ProfileInfoCache::GetGAIAPictureOfProfileAtIndex(
279     size_t index) const {
280   base::FilePath path = GetPathOfProfileAtIndex(index);
281   std::string key = CacheKeyFromProfilePath(path);
282 
283   std::string file_name;
284   GetInfoForProfileAtIndex(index)->GetString(
285       kGAIAPictureFileNameKey, &file_name);
286 
287   // If the picture is not on disk then return NULL.
288   if (file_name.empty())
289     return nullptr;
290 
291   base::FilePath image_path = path.AppendASCII(file_name);
292   return LoadAvatarPictureFromPath(path, key, image_path);
293 }
294 
IsUsingGAIAPictureOfProfileAtIndex(size_t index) const295 bool ProfileInfoCache::IsUsingGAIAPictureOfProfileAtIndex(size_t index) const {
296   bool value = false;
297   GetInfoForProfileAtIndex(index)->GetBoolean(kUseGAIAPictureKey, &value);
298   if (!value) {
299     // Prefer the GAIA avatar over a non-customized avatar.
300     value = ProfileIsUsingDefaultAvatarAtIndex(index) &&
301         GetGAIAPictureOfProfileAtIndex(index);
302   }
303   return value;
304 }
305 
ProfileIsUsingDefaultAvatarAtIndex(size_t index) const306 bool ProfileInfoCache::ProfileIsUsingDefaultAvatarAtIndex(size_t index) const {
307   bool value = false;
308   GetInfoForProfileAtIndex(index)->GetBoolean(kIsUsingDefaultAvatarKey, &value);
309   return value;
310 }
311 
IsGAIAPictureOfProfileAtIndexLoaded(size_t index) const312 bool ProfileInfoCache::IsGAIAPictureOfProfileAtIndexLoaded(size_t index) const {
313   return cached_avatar_images_.count(
314       CacheKeyFromProfilePath(GetPathOfProfileAtIndex(index)));
315 }
316 
GetAvatarIconIndexOfProfileAtIndex(size_t index) const317 size_t ProfileInfoCache::GetAvatarIconIndexOfProfileAtIndex(size_t index)
318     const {
319   std::string icon_url;
320   GetInfoForProfileAtIndex(index)->GetString(
321       ProfileAttributesEntry::kAvatarIconKey, &icon_url);
322   size_t icon_index = 0;
323   if (!profiles::IsDefaultAvatarIconUrl(icon_url, &icon_index))
324     DLOG(WARNING) << "Unknown avatar icon: " << icon_url;
325 
326   return icon_index;
327 }
328 
NotifyProfileAuthInfoChanged(const base::FilePath & profile_path)329 void ProfileInfoCache::NotifyProfileAuthInfoChanged(
330     const base::FilePath& profile_path) {
331   for (auto& observer : observer_list_)
332     observer.OnProfileAuthInfoChanged(profile_path);
333 }
334 
SetAvatarIconOfProfileAtIndex(size_t index,size_t icon_index)335 void ProfileInfoCache::SetAvatarIconOfProfileAtIndex(size_t index,
336                                                      size_t icon_index) {
337   if (!profiles::IsDefaultAvatarIconIndex(icon_index)) {
338     DLOG(WARNING) << "Unknown avatar icon index: " << icon_index;
339     // switch to generic avatar
340     icon_index = 0;
341   }
342   std::unique_ptr<base::DictionaryValue> info(
343       GetInfoForProfileAtIndex(index)->DeepCopy());
344   info->SetString(ProfileAttributesEntry::kAvatarIconKey,
345                   profiles::GetDefaultAvatarIconUrl(icon_index));
346   SetInfoForProfileAtIndex(index, std::move(info));
347 
348   base::FilePath profile_path = GetPathOfProfileAtIndex(index);
349 
350   if (!disable_avatar_download_for_testing_)
351     DownloadHighResAvatarIfNeeded(icon_index, profile_path);
352 
353   for (auto& observer : observer_list_)
354     observer.OnProfileAvatarChanged(profile_path);
355 }
356 
357 std::string
GetLastDownloadedGAIAPictureUrlWithSizeOfProfileAtIndex(size_t index) const358 ProfileInfoCache::GetLastDownloadedGAIAPictureUrlWithSizeOfProfileAtIndex(
359     size_t index) const {
360   std::string current_gaia_image_url;
361   GetInfoForProfileAtIndex(index)->GetString(
362       kLastDownloadedGAIAPictureUrlWithSizeKey, &current_gaia_image_url);
363   return current_gaia_image_url;
364 }
365 
SetLastDownloadedGAIAPictureUrlWithSizeOfProfileAtIndex(size_t index,const std::string & image_url_with_size)366 void ProfileInfoCache::SetLastDownloadedGAIAPictureUrlWithSizeOfProfileAtIndex(
367     size_t index,
368     const std::string& image_url_with_size) {
369   std::unique_ptr<base::DictionaryValue> info(
370       GetInfoForProfileAtIndex(index)->DeepCopy());
371   info->SetString(kLastDownloadedGAIAPictureUrlWithSizeKey,
372                   image_url_with_size);
373   SetInfoForProfileAtIndex(index, std::move(info));
374 }
375 
ShouldUpdateGAIAPictureOfProfileAtIndex(size_t index,const std::string & old_file_name,const std::string & key,const std::string & image_url_with_size,bool image_is_empty) const376 bool ProfileInfoCache::ShouldUpdateGAIAPictureOfProfileAtIndex(
377     size_t index,
378     const std::string& old_file_name,
379     const std::string& key,
380     const std::string& image_url_with_size,
381     bool image_is_empty) const {
382   if (old_file_name.empty() && image_is_empty) {
383     // On Windows, Taskbar and Desktop icons are refreshed every time
384     // |OnProfileAvatarChanged| notification is fired.
385     // Updating from an empty image to a null image is a no-op and it is
386     // important to avoid firing |OnProfileAvatarChanged| in this case.
387     // See http://crbug.com/900374
388     DCHECK_EQ(0U, cached_avatar_images_.count(key));
389     return false;
390   }
391 
392   std::string current_gaia_image_url =
393       GetLastDownloadedGAIAPictureUrlWithSizeOfProfileAtIndex(index);
394   if (old_file_name.empty() || image_is_empty ||
395       current_gaia_image_url != image_url_with_size) {
396     return true;
397   }
398   const gfx::Image* gaia_picture = GetGAIAPictureOfProfileAtIndex(index);
399   if (gaia_picture && !gaia_picture->IsEmpty()) {
400     return false;
401   }
402 
403   // We either did not load the GAIA image or we failed to. In that case, only
404   // update if the GAIA picture is used as the profile avatar.
405   return ProfileIsUsingDefaultAvatarAtIndex(index) ||
406          IsUsingGAIAPictureOfProfileAtIndex(index);
407 }
408 
SetGAIAPictureOfProfileAtIndex(size_t index,const std::string & image_url_with_size,gfx::Image image)409 void ProfileInfoCache::SetGAIAPictureOfProfileAtIndex(
410     size_t index,
411     const std::string& image_url_with_size,
412     gfx::Image image) {
413   base::FilePath path = GetPathOfProfileAtIndex(index);
414   std::string key = CacheKeyFromProfilePath(path);
415 
416   std::string old_file_name;
417   GetInfoForProfileAtIndex(index)->GetString(kGAIAPictureFileNameKey,
418                                              &old_file_name);
419 
420   if (!ShouldUpdateGAIAPictureOfProfileAtIndex(
421           index, old_file_name, key, image_url_with_size, image.IsEmpty())) {
422     return;
423   }
424 
425   // Delete the old bitmap from cache.
426   cached_avatar_images_.erase(key);
427   std::string new_file_name;
428   if (image.IsEmpty()) {
429     // Delete the old bitmap from disk.
430     base::FilePath image_path = path.AppendASCII(old_file_name);
431     file_task_runner_->PostTask(FROM_HERE,
432                                 base::BindOnce(&DeleteBitmap, image_path));
433     SetLastDownloadedGAIAPictureUrlWithSizeOfProfileAtIndex(index,
434                                                             std::string());
435   } else {
436     // Save the new bitmap to disk.
437     new_file_name =
438         old_file_name.empty()
439             ? base::FilePath(profiles::kGAIAPictureFileName).MaybeAsASCII()
440             : old_file_name;
441     base::FilePath image_path = path.AppendASCII(new_file_name);
442     SaveAvatarImageAtPath(
443         GetPathOfProfileAtIndex(index), image, key, image_path,
444         base::BindOnce(
445             &ProfileInfoCache::
446                 SetLastDownloadedGAIAPictureUrlWithSizeOfProfileAtIndex,
447             weak_factory_.GetWeakPtr(), index, image_url_with_size));
448   }
449 
450   std::unique_ptr<base::DictionaryValue> info(
451       GetInfoForProfileAtIndex(index)->DeepCopy());
452   info->SetString(kGAIAPictureFileNameKey, new_file_name);
453   SetInfoForProfileAtIndex(index, std::move(info));
454 
455   for (auto& observer : observer_list_)
456     observer.OnProfileAvatarChanged(path);
457 }
458 
SetIsUsingGAIAPictureOfProfileAtIndex(size_t index,bool value)459 void ProfileInfoCache::SetIsUsingGAIAPictureOfProfileAtIndex(size_t index,
460                                                              bool value) {
461   std::unique_ptr<base::DictionaryValue> info(
462       GetInfoForProfileAtIndex(index)->DeepCopy());
463   info->SetBoolean(kUseGAIAPictureKey, value);
464   SetInfoForProfileAtIndex(index, std::move(info));
465 
466   base::FilePath profile_path = GetPathOfProfileAtIndex(index);
467   for (auto& observer : observer_list_)
468     observer.OnProfileAvatarChanged(profile_path);
469 }
470 
SetProfileIsUsingDefaultAvatarAtIndex(size_t index,bool value)471 void ProfileInfoCache::SetProfileIsUsingDefaultAvatarAtIndex(
472     size_t index, bool value) {
473   if (value == ProfileIsUsingDefaultAvatarAtIndex(index))
474     return;
475 
476   std::unique_ptr<base::DictionaryValue> info(
477       GetInfoForProfileAtIndex(index)->DeepCopy());
478   info->SetBoolean(kIsUsingDefaultAvatarKey, value);
479   SetInfoForProfileAtIndex(index, std::move(info));
480 }
481 
NotifyIsSigninRequiredChanged(const base::FilePath & profile_path)482 void ProfileInfoCache::NotifyIsSigninRequiredChanged(
483     const base::FilePath& profile_path) {
484   for (auto& observer : observer_list_)
485     observer.OnProfileSigninRequiredChanged(profile_path);
486 }
487 
GetUserDataDir() const488 const base::FilePath& ProfileInfoCache::GetUserDataDir() const {
489   return user_data_dir_;
490 }
491 
492 // static
RegisterPrefs(PrefRegistrySimple * registry)493 void ProfileInfoCache::RegisterPrefs(PrefRegistrySimple* registry) {
494   registry->RegisterDictionaryPref(prefs::kProfileInfoCache);
495   registry->RegisterTimePref(kProfileCountLastUpdatePref, base::Time());
496 #if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
497   registry->RegisterBooleanPref(kLegacyProfileNameMigrated, false);
498 #endif  // !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
499 }
500 
GetInfoForProfileAtIndex(size_t index) const501 const base::DictionaryValue* ProfileInfoCache::GetInfoForProfileAtIndex(
502     size_t index) const {
503   DCHECK_LT(index, GetNumberOfProfiles());
504   const base::DictionaryValue* cache =
505       prefs_->GetDictionary(prefs::kProfileInfoCache);
506   const base::DictionaryValue* info = nullptr;
507   cache->GetDictionaryWithoutPathExpansion(keys_[index], &info);
508   return info;
509 }
510 
SetInfoForProfileAtIndex(size_t index,std::unique_ptr<base::DictionaryValue> info)511 void ProfileInfoCache::SetInfoForProfileAtIndex(
512     size_t index,
513     std::unique_ptr<base::DictionaryValue> info) {
514   DictionaryPrefUpdate update(prefs_, prefs::kProfileInfoCache);
515   base::DictionaryValue* cache = update.Get();
516   cache->SetWithoutPathExpansion(keys_[index], std::move(info));
517 }
518 
CacheKeyFromProfilePath(const base::FilePath & profile_path) const519 std::string ProfileInfoCache::CacheKeyFromProfilePath(
520     const base::FilePath& profile_path) const {
521   DCHECK(user_data_dir_ == profile_path.DirName());
522   base::FilePath base_name = profile_path.BaseName();
523   return base_name.MaybeAsASCII();
524 }
525 
GetHighResAvatarOfProfileAtIndex(size_t index) const526 const gfx::Image* ProfileInfoCache::GetHighResAvatarOfProfileAtIndex(
527     size_t index) const {
528   const size_t avatar_index = GetAvatarIconIndexOfProfileAtIndex(index);
529 
530   // If this is the placeholder avatar, it is already included in the
531   // resources, so it doesn't need to be downloaded.
532   if (avatar_index == profiles::GetPlaceholderAvatarIndex()) {
533     return &ui::ResourceBundle::GetSharedInstance().GetImageNamed(
534         profiles::GetPlaceholderAvatarIconResourceID());
535   }
536 
537   const std::string file_name =
538       profiles::GetDefaultAvatarIconFileNameAtIndex(avatar_index);
539   const base::FilePath image_path =
540       profiles::GetPathOfHighResAvatarAtIndex(avatar_index);
541   return LoadAvatarPictureFromPath(GetPathOfProfileAtIndex(index), file_name,
542                                    image_path);
543 }
544 
545 #if !defined(OS_ANDROID)
LoadGAIAPictureIfNeeded()546 void ProfileInfoCache::LoadGAIAPictureIfNeeded() {
547   std::vector<ProfileAttributesEntry*> entries = GetAllProfilesAttributes();
548   for (ProfileAttributesEntry* entry : entries) {
549     if (entry->GetSigninState() == SigninState::kNotSignedIn)
550       continue;
551 
552     bool is_using_GAIA_picture = entry->GetBool(kUseGAIAPictureKey);
553     bool is_using_default_avatar = entry->IsUsingDefaultAvatar();
554     // Load from disk into memory GAIA picture if it exists.
555     if (is_using_GAIA_picture || is_using_default_avatar)
556       entry->GetGAIAPicture();
557   }
558 }
559 #endif
560 
561 #if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
MigrateLegacyProfileNamesAndRecomputeIfNeeded()562 void ProfileInfoCache::MigrateLegacyProfileNamesAndRecomputeIfNeeded() {
563   std::vector<ProfileAttributesEntry*> entries = GetAllProfilesAttributes();
564   for (size_t i = 0; i < entries.size(); i++) {
565     base::string16 profile_name = entries[i]->GetLocalProfileName();
566     if (!entries[i]->IsUsingDefaultName())
567       continue;
568 
569     // Migrate any legacy profile names ("First user", "Default Profile",
570     // "Saratoga", ...) to new style default names Person %n ("Person 1").
571     if (!IsDefaultProfileName(
572             profile_name, /*include_check_for_legacy_profile_name=*/false)) {
573       entries[i]->SetLocalProfileName(
574           ChooseNameForNewProfile(entries[i]->GetAvatarIconIndex()));
575       continue;
576     }
577 
578     if (i == (entries.size() - 1))
579       continue;
580 
581     // Current profile name is Person %n.
582     // Rename duplicate default profile names, e.g.: Person 1, Person 1 to
583     // Person 1, Person 2.
584     for (size_t j = i + 1; j < entries.size(); j++) {
585       if (profile_name == entries[j]->GetLocalProfileName()) {
586         entries[j]->SetLocalProfileName(
587             ChooseNameForNewProfile(entries[j]->GetAvatarIconIndex()));
588       }
589     }
590   }
591 }
592 
593 // static
SetLegacyProfileMigrationForTesting(bool value)594 void ProfileInfoCache::SetLegacyProfileMigrationForTesting(bool value) {
595   migration_enabled_for_testing = value;
596 }
597 #endif  // !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
598 
DownloadAvatars()599 void ProfileInfoCache::DownloadAvatars() {
600 #if !defined(OS_ANDROID)
601   std::vector<ProfileAttributesEntry*> entries = GetAllProfilesAttributes();
602   for (ProfileAttributesEntry* entry : entries) {
603     DownloadHighResAvatarIfNeeded(entry->GetAvatarIconIndex(),
604                                   entry->GetPath());
605   }
606 #endif
607 }
608 
AddProfile(const base::FilePath & profile_path,const base::string16 & name,const std::string & gaia_id,const base::string16 & user_name,bool is_consented_primary_account,size_t icon_index,const std::string & supervised_user_id,const AccountId & account_id)609 void ProfileInfoCache::AddProfile(const base::FilePath& profile_path,
610                                   const base::string16& name,
611                                   const std::string& gaia_id,
612                                   const base::string16& user_name,
613                                   bool is_consented_primary_account,
614                                   size_t icon_index,
615                                   const std::string& supervised_user_id,
616                                   const AccountId& account_id) {
617   AddProfileToCache(profile_path, name, gaia_id, user_name,
618                     is_consented_primary_account, icon_index,
619                     supervised_user_id, account_id);
620 }
621 
RemoveProfileByAccountId(const AccountId & account_id)622 void ProfileInfoCache::RemoveProfileByAccountId(const AccountId& account_id) {
623   for (size_t i = 0; i < GetNumberOfProfiles(); i++) {
624     std::string account_id_key;
625     std::string gaia_id;
626     std::string user_name;
627     const base::DictionaryValue* info = GetInfoForProfileAtIndex(i);
628     if ((account_id.HasAccountIdKey() &&
629          info->GetString(kAccountIdKey, &account_id_key) &&
630          account_id_key == account_id.GetAccountIdKey()) ||
631         (info->GetString(ProfileAttributesEntry::kGAIAIdKey, &gaia_id) &&
632          !gaia_id.empty() && account_id.GetGaiaId() == gaia_id) ||
633         (info->GetString(ProfileAttributesEntry::kUserNameKey, &user_name) &&
634          !user_name.empty() && account_id.GetUserEmail() == user_name)) {
635       RemoveProfile(GetPathOfProfileAtIndex(i));
636       return;
637     }
638   }
639   LOG(ERROR) << "Failed to remove profile.info_cache entry for account type "
640              << static_cast<int>(account_id.GetAccountType())
641              << ": matching entry not found.";
642 }
643 
RemoveProfile(const base::FilePath & profile_path)644 void ProfileInfoCache::RemoveProfile(const base::FilePath& profile_path) {
645   DeleteProfileFromCache(profile_path);
646 }
647 
GetProfileAttributesWithPath(const base::FilePath & path,ProfileAttributesEntry ** entry)648 bool ProfileInfoCache::GetProfileAttributesWithPath(
649     const base::FilePath& path, ProfileAttributesEntry** entry) {
650   const auto entry_iter = profile_attributes_entries_.find(path.value());
651   if (entry_iter == profile_attributes_entries_.end())
652     return false;
653 
654   std::unique_ptr<ProfileAttributesEntry>& current_entry = entry_iter->second;
655   if (!current_entry) {
656     // The profile info is in the cache but its entry isn't created yet, insert
657     // it in the map.
658     current_entry.reset(new ProfileAttributesEntry());
659     current_entry->Initialize(this, path, prefs_);
660   }
661 
662   *entry = current_entry.get();
663   return true;
664 }
665