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, ¤t_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