1 // Copyright 2015 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 <utility>
6 #include <vector>
7 
8 #include "base/hash/hash.h"
9 #include "base/logging.h"
10 #include "base/notreached.h"
11 #include "base/optional.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "build/build_config.h"
15 #include "chrome/app/vector_icons/vector_icons.h"
16 #include "chrome/browser/browser_process.h"
17 #include "chrome/browser/profiles/profile_attributes_entry.h"
18 #include "chrome/browser/profiles/profile_avatar_icon_util.h"
19 #include "chrome/browser/profiles/profile_info_cache.h"
20 #include "chrome/browser/profiles/profiles_state.h"
21 #include "chrome/browser/signin/signin_util.h"
22 #include "chrome/browser/ui/signin/profile_colors_util.h"
23 #include "chrome/browser/ui/ui_features.h"
24 #include "chrome/common/pref_names.h"
25 #include "components/policy/core/browser/browser_policy_connector.h"
26 #include "components/prefs/pref_registry_simple.h"
27 #include "components/prefs/pref_service.h"
28 #include "components/prefs/scoped_user_pref_update.h"
29 #include "components/profile_metrics/state.h"
30 #include "components/signin/public/base/signin_pref_names.h"
31 #include "components/signin/public/identity_manager/account_info.h"
32 #include "ui/base/resource/resource_bundle.h"
33 #include "ui/gfx/canvas.h"
34 #include "ui/gfx/color_utils.h"
35 #include "ui/gfx/image/canvas_image_source.h"
36 #include "ui/gfx/paint_vector_icon.h"
37 #include "ui/native_theme/native_theme.h"
38 
39 #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
40 #include "chrome/browser/supervised_user/supervised_user_constants.h"
41 #endif
42 
43 #if !defined(OS_ANDROID)
44 #include "chrome/browser/themes/theme_properties.h"  // nogncheck crbug.com/1125897
45 #endif
46 
47 namespace {
48 
49 const char kGAIAGivenNameKey[] = "gaia_given_name";
50 const char kGAIANameKey[] = "gaia_name";
51 const char kShortcutNameKey[] = "shortcut_name";
52 const char kActiveTimeKey[] = "active_time";
53 const char kAuthCredentialsKey[] = "local_auth_credentials";
54 const char kPasswordTokenKey[] = "gaia_password_token";
55 const char kIsAuthErrorKey[] = "is_auth_error";
56 const char kMetricsBucketIndex[] = "metrics_bucket_index";
57 const char kSigninRequiredKey[] = "signin_required";
58 const char kHostedDomain[] = "hosted_domain";
59 
60 // Profile colors info.
61 const char kProfileHighlightColorKey[] = "profile_highlight_color";
62 const char kDefaultAvatarFillColorKey[] = "default_avatar_fill_color";
63 const char kDefaultAvatarStrokeColorKey[] = "default_avatar_stroke_color";
64 
65 // Low-entropy accounts info, for metrics only.
66 const char kFirstAccountNameHash[] = "first_account_name_hash";
67 const char kHasMultipleAccountNames[] = "has_multiple_account_names";
68 const char kAccountCategories[] = "account_categories";
69 
70 // Local state pref to keep track of the next available profile bucket.
71 const char kNextMetricsBucketIndex[] = "profile.metrics.next_bucket_index";
72 
73 constexpr int kIntegerNotSet = -1;
74 
75 // Persisted in prefs.
76 constexpr int kAccountCategoriesConsumerOnly = 0;
77 constexpr int kAccountCategoriesEnterpriseOnly = 1;
78 constexpr int kAccountCategoriesBoth = 2;
79 
80 // Number of distinct low-entropy hash values. Changing this value invalidates
81 // existing persisted hashes.
82 constexpr int kNumberOfLowEntropyHashValues = 1024;
83 
84 // Returns the next available metrics bucket index and increases the index
85 // counter. I.e. two consecutive calls will return two consecutive numbers.
NextAvailableMetricsBucketIndex()86 int NextAvailableMetricsBucketIndex() {
87   PrefService* local_prefs = g_browser_process->local_state();
88   int next_index = local_prefs->GetInteger(kNextMetricsBucketIndex);
89   DCHECK_GT(next_index, 0);
90 
91   local_prefs->SetInteger(kNextMetricsBucketIndex, next_index + 1);
92 
93   return next_index;
94 }
95 
GetLowEntropyHashValue(const std::string & value)96 int GetLowEntropyHashValue(const std::string& value) {
97   return base::PersistentHash(value) % kNumberOfLowEntropyHashValues;
98 }
99 
ShouldShowGenericColoredAvatar(size_t avatar_icon_index)100 bool ShouldShowGenericColoredAvatar(size_t avatar_icon_index) {
101   return base::FeatureList::IsEnabled(features::kNewProfilePicker) &&
102          avatar_icon_index == profiles::GetPlaceholderAvatarIndex();
103 }
104 
105 }  // namespace
106 
107 const char ProfileAttributesEntry::kSupervisedUserId[] = "managed_user_id";
108 const char ProfileAttributesEntry::kIsOmittedFromProfileListKey[] =
109     "is_omitted_from_profile_list";
110 const char ProfileAttributesEntry::kAvatarIconKey[] = "avatar_icon";
111 const char ProfileAttributesEntry::kBackgroundAppsKey[] = "background_apps";
112 const char ProfileAttributesEntry::kProfileIsEphemeral[] = "is_ephemeral";
113 const char ProfileAttributesEntry::kUserNameKey[] = "user_name";
114 const char ProfileAttributesEntry::kGAIAIdKey[] = "gaia_id";
115 const char ProfileAttributesEntry::kIsConsentedPrimaryAccountKey[] =
116     "is_consented_primary_account";
117 const char ProfileAttributesEntry::kNameKey[] = "name";
118 const char ProfileAttributesEntry::kIsUsingDefaultNameKey[] =
119     "is_using_default_name";
120 
121 // static
RegisterLocalStatePrefs(PrefRegistrySimple * registry)122 void ProfileAttributesEntry::RegisterLocalStatePrefs(
123     PrefRegistrySimple* registry) {
124   // Bucket 0 is reserved for the guest profile, so start new bucket indices
125   // at 1.
126   registry->RegisterIntegerPref(kNextMetricsBucketIndex, 1);
127 }
128 
129 ProfileAttributesEntry::ProfileAttributesEntry() = default;
130 
Initialize(ProfileInfoCache * cache,const base::FilePath & path,PrefService * prefs)131 void ProfileAttributesEntry::Initialize(ProfileInfoCache* cache,
132                                         const base::FilePath& path,
133                                         PrefService* prefs) {
134   DCHECK(!profile_info_cache_);
135   DCHECK(cache);
136   profile_info_cache_ = cache;
137 
138   DCHECK(profile_path_.empty());
139   DCHECK(!path.empty());
140   profile_path_ = path;
141 
142   DCHECK(!prefs_);
143   DCHECK(prefs);
144   prefs_ = prefs;
145 
146   DCHECK(profile_info_cache_->GetUserDataDir() == profile_path_.DirName());
147   storage_key_ = profile_path_.BaseName().MaybeAsASCII();
148 
149   const base::Value* entry_data = GetEntryData();
150   if (entry_data) {
151     if (!entry_data->FindKey(kIsConsentedPrimaryAccountKey)) {
152       SetBool(kIsConsentedPrimaryAccountKey,
153               !GetGAIAId().empty() || !GetUserName().empty());
154     }
155   }
156 
157   is_force_signin_enabled_ = signin_util::IsForceSigninEnabled();
158   if (is_force_signin_enabled_) {
159     if (!IsAuthenticated())
160       is_force_signin_profile_locked_ = true;
161 #if defined(OS_MAC) || defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_BSD) || \
162     defined(OS_WIN)
163   } else if (IsSigninRequired()) {
164     // Profiles that require signin in the absence of an enterprise policy are
165     // left-overs from legacy supervised users. Just unlock them, so users can
166     // keep using them.
167     SetLocalAuthCredentials(std::string());
168     SetAuthInfo(std::string(), base::string16(), false);
169     SetIsSigninRequired(false);
170 #endif
171   }
172 
173   DCHECK(last_name_to_display_.empty());
174   last_name_to_display_ = GetName();
175 }
176 
GetLocalProfileName() const177 base::string16 ProfileAttributesEntry::GetLocalProfileName() const {
178   return GetString16(kNameKey);
179 }
180 
GetGAIANameToDisplay() const181 base::string16 ProfileAttributesEntry::GetGAIANameToDisplay() const {
182   base::string16 gaia_given_name = GetGAIAGivenName();
183   return gaia_given_name.empty() ? GetGAIAName() : gaia_given_name;
184 }
185 
ShouldShowProfileLocalName(const base::string16 & gaia_name_to_display) const186 bool ProfileAttributesEntry::ShouldShowProfileLocalName(
187     const base::string16& gaia_name_to_display) const {
188   // Never show the profile name if it is equal to GAIA given name,
189   // e.g. Matt (Matt), in that case we should only show the GAIA name.
190   if (base::EqualsCaseInsensitiveASCII(gaia_name_to_display,
191                                        GetLocalProfileName())) {
192     return false;
193   }
194 
195   // Customized profile name that is not equal to Gaia name, e.g. Matt (Work).
196   if (!IsUsingDefaultName())
197     return true;
198 
199   // The profile local name is a default profile name : Person n.
200   std::vector<ProfileAttributesEntry*> entries =
201       profile_info_cache_->GetAllProfilesAttributes();
202 
203   for (ProfileAttributesEntry* entry : entries) {
204     if (entry == this)
205       continue;
206 
207     base::string16 other_gaia_name_to_display = entry->GetGAIANameToDisplay();
208     if (other_gaia_name_to_display.empty() ||
209         other_gaia_name_to_display != gaia_name_to_display)
210       continue;
211 
212     // Another profile with the same GAIA name.
213     bool other_profile_name_equal_GAIA_name = base::EqualsCaseInsensitiveASCII(
214         other_gaia_name_to_display, entry->GetLocalProfileName());
215     // If for the other profile, the profile name is equal to GAIA name then it
216     // will not be shown. For disambiguation, show for the current profile the
217     // profile name even if it is Person n.
218     if (other_profile_name_equal_GAIA_name)
219       return true;
220 
221     bool other_is_using_default_name = entry->IsUsingDefaultName();
222     // Both profiles have a default profile name,
223     // e.g. Matt (Person 1), Matt (Person 2).
224     if (other_is_using_default_name) {
225       return true;
226     }
227   }
228   return false;
229 }
230 
GetLastNameToDisplay() const231 base::string16 ProfileAttributesEntry::GetLastNameToDisplay() const {
232   return last_name_to_display_;
233 }
234 
HasProfileNameChanged()235 bool ProfileAttributesEntry::HasProfileNameChanged() {
236   base::string16 name = GetName();
237   if (last_name_to_display_ == name)
238     return false;
239 
240   last_name_to_display_ = name;
241   return true;
242 }
243 
GetNameForm() const244 NameForm ProfileAttributesEntry::GetNameForm() const {
245   base::string16 name_to_display = GetGAIANameToDisplay();
246   if (name_to_display.empty())
247     return NameForm::kLocalName;
248   if (!ShouldShowProfileLocalName(name_to_display))
249     return NameForm::kGaiaName;
250   return NameForm::kGaiaAndLocalName;
251 }
252 
GetName() const253 base::string16 ProfileAttributesEntry::GetName() const {
254   switch (GetNameForm()) {
255     case NameForm::kGaiaName:
256       return GetGAIANameToDisplay();
257     case NameForm::kLocalName:
258       return GetLocalProfileName();
259     case NameForm::kGaiaAndLocalName:
260       return GetGAIANameToDisplay() + base::UTF8ToUTF16(" (") +
261              GetLocalProfileName() + base::UTF8ToUTF16(")");
262   }
263 }
264 
GetShortcutName() const265 base::string16 ProfileAttributesEntry::GetShortcutName() const {
266   return GetString16(kShortcutNameKey);
267 }
268 
GetPath() const269 base::FilePath ProfileAttributesEntry::GetPath() const {
270   return profile_path_;
271 }
272 
GetActiveTime() const273 base::Time ProfileAttributesEntry::GetActiveTime() const {
274   if (IsDouble(kActiveTimeKey)) {
275     return base::Time::FromDoubleT(GetDouble(kActiveTimeKey));
276   } else {
277     return base::Time();
278   }
279 }
280 
GetUserName() const281 base::string16 ProfileAttributesEntry::GetUserName() const {
282   return GetString16(kUserNameKey);
283 }
284 
GetAvatarIcon(int size_for_placeholder_avatar,bool use_high_res_file) const285 gfx::Image ProfileAttributesEntry::GetAvatarIcon(
286     int size_for_placeholder_avatar,
287     bool use_high_res_file) const {
288   if (IsUsingGAIAPicture()) {
289     const gfx::Image* image = GetGAIAPicture();
290     if (image)
291       return *image;
292   }
293 
294   // TODO(crbug.com/1100835): After launch, remove the treatment of placeholder
295   // avatars from GetHighResAvatar() and from any other places.
296   if (ShouldShowGenericColoredAvatar(GetAvatarIconIndex())) {
297     return GetPlaceholderAvatarIcon(size_for_placeholder_avatar);
298   }
299 
300 #if !defined(OS_ANDROID)
301   // Use the high resolution version of the avatar if it exists. Mobile doesn't
302   // need the high resolution version so no need to fetch it.
303   if (use_high_res_file) {
304     const gfx::Image* image = GetHighResAvatar();
305     if (image)
306       return *image;
307   }
308 #endif
309 
310   const int icon_index = GetAvatarIconIndex();
311 #if defined(OS_WIN)
312   if (!profiles::IsModernAvatarIconIndex(icon_index)) {
313     // Return the 2x version of the old avatar, defined specifically for
314     // Windows. No special treatment is needed for modern avatars as they
315     // already have high enough resolution.
316     const int win_resource_id =
317         profiles::GetOldDefaultAvatar2xIconResourceIDAtIndex(icon_index);
318     return ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed(
319         win_resource_id);
320   }
321 #endif
322   int resource_id = profiles::GetDefaultAvatarIconResourceIDAtIndex(icon_index);
323   return ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed(
324       resource_id);
325 }
326 
GetLocalAuthCredentials() const327 std::string ProfileAttributesEntry::GetLocalAuthCredentials() const {
328   return GetString(kAuthCredentialsKey);
329 }
330 
GetPasswordChangeDetectionToken() const331 std::string ProfileAttributesEntry::GetPasswordChangeDetectionToken() const {
332   return GetString(kPasswordTokenKey);
333 }
334 
GetBackgroundStatus() const335 bool ProfileAttributesEntry::GetBackgroundStatus() const {
336   return GetBool(kBackgroundAppsKey);
337 }
338 
GetGAIAName() const339 base::string16 ProfileAttributesEntry::GetGAIAName() const {
340   return GetString16(kGAIANameKey);
341 }
342 
GetGAIAGivenName() const343 base::string16 ProfileAttributesEntry::GetGAIAGivenName() const {
344   return GetString16(kGAIAGivenNameKey);
345 }
346 
GetGAIAId() const347 std::string ProfileAttributesEntry::GetGAIAId() const {
348   return GetString(ProfileAttributesEntry::kGAIAIdKey);
349 }
350 
GetGAIAPicture() const351 const gfx::Image* ProfileAttributesEntry::GetGAIAPicture() const {
352   return profile_info_cache_->GetGAIAPictureOfProfileAtIndex(profile_index());
353 }
354 
IsUsingGAIAPicture() const355 bool ProfileAttributesEntry::IsUsingGAIAPicture() const {
356   return profile_info_cache_->IsUsingGAIAPictureOfProfileAtIndex(
357       profile_index());
358 }
359 
IsGAIAPictureLoaded() const360 bool ProfileAttributesEntry::IsGAIAPictureLoaded() const {
361   return profile_info_cache_->IsGAIAPictureOfProfileAtIndexLoaded(
362       profile_index());
363 }
364 
IsSupervised() const365 bool ProfileAttributesEntry::IsSupervised() const {
366   return !GetSupervisedUserId().empty();
367 }
368 
IsChild() const369 bool ProfileAttributesEntry::IsChild() const {
370 #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
371   return GetSupervisedUserId() == supervised_users::kChildAccountSUID;
372 #else
373   return false;
374 #endif
375 }
376 
IsLegacySupervised() const377 bool ProfileAttributesEntry::IsLegacySupervised() const {
378   return IsSupervised() && !IsChild();
379 }
380 
IsOmitted() const381 bool ProfileAttributesEntry::IsOmitted() const {
382   return GetBool(kIsOmittedFromProfileListKey);
383 }
384 
IsSigninRequired() const385 bool ProfileAttributesEntry::IsSigninRequired() const {
386   return GetBool(kSigninRequiredKey) || is_force_signin_profile_locked_;
387 }
388 
GetSupervisedUserId() const389 std::string ProfileAttributesEntry::GetSupervisedUserId() const {
390   return GetString(kSupervisedUserId);
391 }
392 
IsEphemeral() const393 bool ProfileAttributesEntry::IsEphemeral() const {
394   return GetBool(kProfileIsEphemeral);
395 }
396 
IsUsingDefaultName() const397 bool ProfileAttributesEntry::IsUsingDefaultName() const {
398   return GetBool(kIsUsingDefaultNameKey);
399 }
400 
GetSigninState() const401 SigninState ProfileAttributesEntry::GetSigninState() const {
402   bool is_consented_primary_account = GetBool(kIsConsentedPrimaryAccountKey);
403   if (!GetGAIAId().empty() || !GetUserName().empty()) {
404     return is_consented_primary_account
405                ? SigninState::kSignedInWithConsentedPrimaryAccount
406                : SigninState::kSignedInWithUnconsentedPrimaryAccount;
407   }
408   DCHECK(!is_consented_primary_account);
409   return SigninState::kNotSignedIn;
410 }
411 
IsAuthenticated() const412 bool ProfileAttributesEntry::IsAuthenticated() const {
413   return GetBool(kIsConsentedPrimaryAccountKey);
414 }
415 
IsUsingDefaultAvatar() const416 bool ProfileAttributesEntry::IsUsingDefaultAvatar() const {
417   return profile_info_cache_->ProfileIsUsingDefaultAvatarAtIndex(
418       profile_index());
419 }
420 
IsAuthError() const421 bool ProfileAttributesEntry::IsAuthError() const {
422   return GetBool(kIsAuthErrorKey);
423 }
424 
IsSignedInWithCredentialProvider() const425 bool ProfileAttributesEntry::IsSignedInWithCredentialProvider() const {
426   return GetBool(prefs::kSignedInWithCredentialProvider);
427 }
428 
GetAvatarIconIndex() const429 size_t ProfileAttributesEntry::GetAvatarIconIndex() const {
430   std::string icon_url = GetString(kAvatarIconKey);
431   size_t icon_index = 0;
432   if (!profiles::IsDefaultAvatarIconUrl(icon_url, &icon_index))
433     DLOG(WARNING) << "Unknown avatar icon: " << icon_url;
434 
435   return icon_index;
436 }
437 
438 base::Optional<ProfileThemeColors>
GetProfileThemeColorsIfSet() const439 ProfileAttributesEntry::GetProfileThemeColorsIfSet() const {
440   base::Optional<SkColor> profile_highlight_color =
441       GetProfileThemeColor(kProfileHighlightColorKey);
442   base::Optional<SkColor> default_avatar_fill_color =
443       GetProfileThemeColor(kDefaultAvatarFillColorKey);
444   base::Optional<SkColor> default_avatar_stroke_color =
445       GetProfileThemeColor(kDefaultAvatarStrokeColorKey);
446 
447   DCHECK_EQ(profile_highlight_color.has_value(),
448             default_avatar_stroke_color.has_value());
449   DCHECK_EQ(profile_highlight_color.has_value(),
450             default_avatar_fill_color.has_value());
451 
452   if (!profile_highlight_color.has_value()) {
453     return base::nullopt;
454   }
455 
456   ProfileThemeColors colors;
457   colors.profile_highlight_color = profile_highlight_color.value();
458   colors.default_avatar_fill_color = default_avatar_fill_color.value();
459   colors.default_avatar_stroke_color = default_avatar_stroke_color.value();
460   return colors;
461 }
462 
GetProfileThemeColors() const463 ProfileThemeColors ProfileAttributesEntry::GetProfileThemeColors() const {
464 #if defined(OS_ANDROID)
465   // Profile theme colors shouldn't be queried on Android.
466   NOTREACHED();
467   return {SK_ColorRED, SK_ColorRED, SK_ColorRED};
468 #else
469   base::Optional<ProfileThemeColors> theme_colors =
470       GetProfileThemeColorsIfSet();
471   if (theme_colors)
472     return *theme_colors;
473 
474   return GetDefaultProfileThemeColors(
475       ui::NativeTheme::GetInstanceForNativeUi()->ShouldUseDarkColors());
476 #endif
477 }
478 
GetMetricsBucketIndex()479 size_t ProfileAttributesEntry::GetMetricsBucketIndex() {
480   int bucket_index = GetInteger(kMetricsBucketIndex);
481   if (bucket_index == kIntegerNotSet) {
482     bucket_index = NextAvailableMetricsBucketIndex();
483     SetInteger(kMetricsBucketIndex, bucket_index);
484   }
485   return bucket_index;
486 }
487 
GetHostedDomain() const488 std::string ProfileAttributesEntry::GetHostedDomain() const {
489   return GetString(kHostedDomain);
490 }
491 
SetLocalProfileName(const base::string16 & name)492 void ProfileAttributesEntry::SetLocalProfileName(const base::string16& name) {
493   if (SetString16(kNameKey, name))
494     profile_info_cache_->NotifyIfProfileNamesHaveChanged();
495 }
496 
SetShortcutName(const base::string16 & name)497 void ProfileAttributesEntry::SetShortcutName(const base::string16& name) {
498   SetString16(kShortcutNameKey, name);
499 }
500 
SetActiveTimeToNow()501 void ProfileAttributesEntry::SetActiveTimeToNow() {
502   if (IsDouble(kActiveTimeKey) &&
503       base::Time::Now() - GetActiveTime() < base::TimeDelta::FromHours(1)) {
504     return;
505   }
506   SetDouble(kActiveTimeKey, base::Time::Now().ToDoubleT());
507 }
508 
SetIsOmitted(bool is_omitted)509 void ProfileAttributesEntry::SetIsOmitted(bool is_omitted) {
510   if (SetBool(kIsOmittedFromProfileListKey, is_omitted))
511     profile_info_cache_->NotifyProfileIsOmittedChanged(GetPath());
512 }
513 
SetSupervisedUserId(const std::string & id)514 void ProfileAttributesEntry::SetSupervisedUserId(const std::string& id) {
515   if (SetString(kSupervisedUserId, id))
516     profile_info_cache_->NotifyProfileSupervisedUserIdChanged(GetPath());
517 }
518 
SetLocalAuthCredentials(const std::string & auth)519 void ProfileAttributesEntry::SetLocalAuthCredentials(const std::string& auth) {
520   SetString(kAuthCredentialsKey, auth);
521 }
522 
SetPasswordChangeDetectionToken(const std::string & token)523 void ProfileAttributesEntry::SetPasswordChangeDetectionToken(
524     const std::string& token) {
525   SetString(kPasswordTokenKey, token);
526 }
527 
SetBackgroundStatus(bool running_background_apps)528 void ProfileAttributesEntry::SetBackgroundStatus(bool running_background_apps) {
529   SetBool(kBackgroundAppsKey, running_background_apps);
530 }
531 
SetGAIAName(const base::string16 & name)532 void ProfileAttributesEntry::SetGAIAName(const base::string16& name) {
533   if (SetString16(kGAIANameKey, name))
534     profile_info_cache_->NotifyIfProfileNamesHaveChanged();
535 }
536 
SetGAIAGivenName(const base::string16 & name)537 void ProfileAttributesEntry::SetGAIAGivenName(const base::string16& name) {
538   if (SetString16(kGAIAGivenNameKey, name))
539     profile_info_cache_->NotifyIfProfileNamesHaveChanged();
540 }
541 
SetGAIAPicture(const std::string & image_url_with_size,gfx::Image image)542 void ProfileAttributesEntry::SetGAIAPicture(
543     const std::string& image_url_with_size,
544     gfx::Image image) {
545   profile_info_cache_->SetGAIAPictureOfProfileAtIndex(
546       profile_index(), image_url_with_size, image);
547 }
548 
SetIsUsingGAIAPicture(bool value)549 void ProfileAttributesEntry::SetIsUsingGAIAPicture(bool value) {
550   profile_info_cache_->SetIsUsingGAIAPictureOfProfileAtIndex(
551       profile_index(), value);
552 }
553 
SetIsSigninRequired(bool value)554 void ProfileAttributesEntry::SetIsSigninRequired(bool value) {
555   if (value != GetBool(kSigninRequiredKey)) {
556     SetBool(kSigninRequiredKey, value);
557     profile_info_cache_->NotifyIsSigninRequiredChanged(GetPath());
558   }
559   if (is_force_signin_enabled_)
560     LockForceSigninProfile(value);
561 }
562 
SetSignedInWithCredentialProvider(bool value)563 void ProfileAttributesEntry::SetSignedInWithCredentialProvider(bool value) {
564   if (value != GetBool(prefs::kSignedInWithCredentialProvider)) {
565     SetBool(prefs::kSignedInWithCredentialProvider, value);
566   }
567 }
568 
LockForceSigninProfile(bool is_lock)569 void ProfileAttributesEntry::LockForceSigninProfile(bool is_lock) {
570   DCHECK(is_force_signin_enabled_);
571   if (is_force_signin_profile_locked_ == is_lock)
572     return;
573   is_force_signin_profile_locked_ = is_lock;
574   profile_info_cache_->NotifyIsSigninRequiredChanged(GetPath());
575 }
576 
RecordAccountMetrics() const577 void ProfileAttributesEntry::RecordAccountMetrics() const {
578   RecordAccountCategoriesMetric();
579   RecordAccountNamesMetric();
580 }
581 
SetIsEphemeral(bool value)582 void ProfileAttributesEntry::SetIsEphemeral(bool value) {
583   SetBool(kProfileIsEphemeral, value);
584 }
585 
SetIsUsingDefaultName(bool value)586 void ProfileAttributesEntry::SetIsUsingDefaultName(bool value) {
587   if (SetBool(kIsUsingDefaultNameKey, value))
588     profile_info_cache_->NotifyIfProfileNamesHaveChanged();
589 }
590 
SetIsUsingDefaultAvatar(bool value)591 void ProfileAttributesEntry::SetIsUsingDefaultAvatar(bool value) {
592   profile_info_cache_->SetProfileIsUsingDefaultAvatarAtIndex(
593       profile_index(), value);
594 }
595 
SetIsAuthError(bool value)596 void ProfileAttributesEntry::SetIsAuthError(bool value) {
597   SetBool(kIsAuthErrorKey, value);
598 }
599 
SetAvatarIconIndex(size_t icon_index)600 void ProfileAttributesEntry::SetAvatarIconIndex(size_t icon_index) {
601   std::string default_avatar_icon_url =
602       profiles::GetDefaultAvatarIconUrl(icon_index);
603   if (default_avatar_icon_url == GetString(kAvatarIconKey)) {
604     // On Windows, Taskbar and Desktop icons are refreshed every time
605     // |OnProfileAvatarChanged| notification is fired.
606     // As the current avatar icon is already set to |default_avatar_icon_url|,
607     // it is important to avoid firing |OnProfileAvatarChanged| in this case.
608     // See http://crbug.com/900374
609     return;
610   }
611 
612   SetString(kAvatarIconKey, default_avatar_icon_url);
613 
614   base::FilePath profile_path = GetPath();
615   if (!profile_info_cache_->GetDisableAvatarDownloadForTesting()) {
616     profile_info_cache_->DownloadHighResAvatarIfNeeded(icon_index,
617                                                        profile_path);
618   }
619 
620   profile_info_cache_->NotifyOnProfileAvatarChanged(profile_path);
621 }
622 
SetProfileThemeColors(const base::Optional<ProfileThemeColors> & colors)623 void ProfileAttributesEntry::SetProfileThemeColors(
624     const base::Optional<ProfileThemeColors>& colors) {
625   bool changed = false;
626   if (colors.has_value()) {
627     changed |=
628         SetInteger(kProfileHighlightColorKey, colors->profile_highlight_color);
629     changed |= SetInteger(kDefaultAvatarFillColorKey,
630                           colors->default_avatar_fill_color);
631     changed |= SetInteger(kDefaultAvatarStrokeColorKey,
632                           colors->default_avatar_stroke_color);
633   } else {
634     changed |= ClearValue(kProfileHighlightColorKey);
635     changed |= ClearValue(kDefaultAvatarFillColorKey);
636     changed |= ClearValue(kDefaultAvatarStrokeColorKey);
637   }
638 
639   if (changed) {
640     profile_info_cache_->NotifyProfileThemeColorsChanged(GetPath());
641     if (ShouldShowGenericColoredAvatar(GetAvatarIconIndex()))
642       profile_info_cache_->NotifyOnProfileAvatarChanged(GetPath());
643   }
644 }
645 
SetHostedDomain(std::string hosted_domain)646 void ProfileAttributesEntry::SetHostedDomain(std::string hosted_domain) {
647   SetString(kHostedDomain, hosted_domain);
648 }
649 
SetAuthInfo(const std::string & gaia_id,const base::string16 & user_name,bool is_consented_primary_account)650 void ProfileAttributesEntry::SetAuthInfo(const std::string& gaia_id,
651                                          const base::string16& user_name,
652                                          bool is_consented_primary_account) {
653   // If gaia_id, username and consent state are unchanged, abort early.
654   if (GetBool(kIsConsentedPrimaryAccountKey) == is_consented_primary_account &&
655       gaia_id == GetGAIAId() && user_name == GetUserName()) {
656     return;
657   }
658 
659   const base::Value* old_data = GetEntryData();
660   base::Value new_data = old_data ? GetEntryData()->Clone()
661                                   : base::Value(base::Value::Type::DICTIONARY);
662   new_data.SetStringKey(kGAIAIdKey, gaia_id);
663   new_data.SetStringKey(kUserNameKey, user_name);
664   DCHECK(!is_consented_primary_account || !gaia_id.empty() ||
665          !user_name.empty());
666   new_data.SetBoolKey(kIsConsentedPrimaryAccountKey,
667                       is_consented_primary_account);
668   SetEntryData(std::move(new_data));
669   profile_info_cache_->NotifyProfileAuthInfoChanged(profile_path_);
670 }
671 
AddAccountName(const std::string & name)672 void ProfileAttributesEntry::AddAccountName(const std::string& name) {
673   int hash = GetLowEntropyHashValue(name);
674   int first_hash = GetInteger(kFirstAccountNameHash);
675   if (first_hash == kIntegerNotSet) {
676     SetInteger(kFirstAccountNameHash, hash);
677     return;
678   }
679 
680   if (first_hash != hash) {
681     SetBool(kHasMultipleAccountNames, true);
682   }
683 }
684 
AddAccountCategory(AccountCategory category)685 void ProfileAttributesEntry::AddAccountCategory(AccountCategory category) {
686   int current_categories = GetInteger(kAccountCategories);
687   if (current_categories == kAccountCategoriesBoth)
688     return;
689 
690   int new_category = category == AccountCategory::kConsumer
691                          ? kAccountCategoriesConsumerOnly
692                          : kAccountCategoriesEnterpriseOnly;
693   if (current_categories == kIntegerNotSet) {
694     SetInteger(kAccountCategories, new_category);
695   } else if (current_categories != new_category) {
696     SetInteger(kAccountCategories, kAccountCategoriesBoth);
697   }
698 }
699 
ClearAccountNames()700 void ProfileAttributesEntry::ClearAccountNames() {
701   ClearValue(kFirstAccountNameHash);
702   ClearValue(kHasMultipleAccountNames);
703 }
704 
ClearAccountCategories()705 void ProfileAttributesEntry::ClearAccountCategories() {
706   ClearValue(kAccountCategories);
707 }
708 
profile_index() const709 size_t ProfileAttributesEntry::profile_index() const {
710   size_t index = profile_info_cache_->GetIndexOfProfileWithPath(profile_path_);
711   DCHECK(index < profile_info_cache_->GetNumberOfProfiles());
712   return index;
713 }
714 
GetHighResAvatar() const715 const gfx::Image* ProfileAttributesEntry::GetHighResAvatar() const {
716   const size_t avatar_index = GetAvatarIconIndex();
717 
718   // If this is the placeholder avatar, it is already included in the
719   // resources, so it doesn't need to be downloaded.
720   if (avatar_index == profiles::GetPlaceholderAvatarIndex()) {
721     return &ui::ResourceBundle::GetSharedInstance().GetImageNamed(
722         profiles::GetPlaceholderAvatarIconResourceID());
723   }
724 
725   const std::string key =
726       profiles::GetDefaultAvatarIconFileNameAtIndex(avatar_index);
727   const base::FilePath image_path =
728       profiles::GetPathOfHighResAvatarAtIndex(avatar_index);
729   return profile_info_cache_->LoadAvatarPictureFromPath(GetPath(), key,
730                                                         image_path);
731 }
732 
GetPlaceholderAvatarIcon(int size) const733 gfx::Image ProfileAttributesEntry::GetPlaceholderAvatarIcon(int size) const {
734   ProfileThemeColors colors = GetProfileThemeColors();
735   return profiles::GetPlaceholderAvatarIconWithColors(
736       colors.default_avatar_fill_color, colors.default_avatar_stroke_color,
737       size);
738 }
739 
HasMultipleAccountNames() const740 bool ProfileAttributesEntry::HasMultipleAccountNames() const {
741   // If the value is not set, GetBool() returns false.
742   return GetBool(kHasMultipleAccountNames);
743 }
744 
HasBothAccountCategories() const745 bool ProfileAttributesEntry::HasBothAccountCategories() const {
746   // If the value is not set, GetInteger returns kIntegerNotSet which does not
747   // equal kAccountTypeBoth.
748   return GetInteger(kAccountCategories) == kAccountCategoriesBoth;
749 }
750 
RecordAccountCategoriesMetric() const751 void ProfileAttributesEntry::RecordAccountCategoriesMetric() const {
752   if (HasBothAccountCategories()) {
753     if (IsAuthenticated()) {
754       bool consumer_syncing = GetHostedDomain() == kNoHostedDomainFound;
755       profile_metrics::LogProfileAllAccountsCategories(
756           consumer_syncing ? profile_metrics::AllAccountsCategories::
757                                  kBothConsumerAndEnterpriseSyncingConsumer
758                            : profile_metrics::AllAccountsCategories::
759                                  kBothConsumerAndEnterpriseSyncingEnterprise);
760     } else {
761       profile_metrics::LogProfileAllAccountsCategories(
762           profile_metrics::AllAccountsCategories::
763               kBothConsumerAndEnterpriseNoSync);
764     }
765   } else {
766     profile_metrics::LogProfileAllAccountsCategories(
767         profile_metrics::AllAccountsCategories::kSingleCategory);
768   }
769 }
770 
RecordAccountNamesMetric() const771 void ProfileAttributesEntry::RecordAccountNamesMetric() const {
772   if (HasMultipleAccountNames()) {
773     profile_metrics::LogProfileAllAccountsNames(
774         IsAuthenticated()
775             ? profile_metrics::AllAccountsNames::kMultipleNamesWithSync
776             : profile_metrics::AllAccountsNames::kMultipleNamesWithoutSync);
777   } else {
778     profile_metrics::LogProfileAllAccountsNames(
779         profile_metrics::AllAccountsNames::kLikelySingleName);
780   }
781 }
782 
GetEntryData() const783 const base::Value* ProfileAttributesEntry::GetEntryData() const {
784   const base::DictionaryValue* cache =
785       prefs_->GetDictionary(prefs::kProfileInfoCache);
786   return cache->FindKeyOfType(storage_key_, base::Value::Type::DICTIONARY);
787 }
788 
SetEntryData(base::Value data)789 void ProfileAttributesEntry::SetEntryData(base::Value data) {
790   DCHECK(data.is_dict());
791 
792   DictionaryPrefUpdate update(prefs_, prefs::kProfileInfoCache);
793   base::DictionaryValue* cache = update.Get();
794   cache->SetKey(storage_key_, std::move(data));
795 }
796 
GetValue(const char * key) const797 const base::Value* ProfileAttributesEntry::GetValue(const char* key) const {
798   const base::Value* entry_data = GetEntryData();
799   return entry_data ? entry_data->FindKey(key) : nullptr;
800 }
801 
GetString(const char * key) const802 std::string ProfileAttributesEntry::GetString(const char* key) const {
803   const base::Value* value = GetValue(key);
804   if (!value || !value->is_string())
805     return std::string();
806   return value->GetString();
807 }
808 
GetString16(const char * key) const809 base::string16 ProfileAttributesEntry::GetString16(const char* key) const {
810   const base::Value* value = GetValue(key);
811   if (!value || !value->is_string())
812     return base::string16();
813   return base::UTF8ToUTF16(value->GetString());
814 }
815 
GetDouble(const char * key) const816 double ProfileAttributesEntry::GetDouble(const char* key) const {
817   const base::Value* value = GetValue(key);
818   if (!value || !value->is_double())
819     return 0.0;
820   return value->GetDouble();
821 }
822 
GetBool(const char * key) const823 bool ProfileAttributesEntry::GetBool(const char* key) const {
824   const base::Value* value = GetValue(key);
825   return value && value->is_bool() && value->GetBool();
826 }
827 
GetInteger(const char * key) const828 int ProfileAttributesEntry::GetInteger(const char* key) const {
829   const base::Value* value = GetValue(key);
830   if (!value || !value->is_int())
831     return kIntegerNotSet;
832   return value->GetInt();
833 }
834 
GetProfileThemeColor(const char * key) const835 base::Optional<SkColor> ProfileAttributesEntry::GetProfileThemeColor(
836     const char* key) const {
837   const base::Value* value = GetValue(key);
838   if (!value || !value->is_int())
839     return base::nullopt;
840   return value->GetInt();
841 }
842 
843 // Type checking. Only IsDouble is implemented because others do not have
844 // callsites.
IsDouble(const char * key) const845 bool ProfileAttributesEntry::IsDouble(const char* key) const {
846   const base::Value* value = GetValue(key);
847   return value && value->is_double();
848 }
849 
850 // Internal setters using keys;
SetString(const char * key,std::string value)851 bool ProfileAttributesEntry::SetString(const char* key, std::string value) {
852   const base::Value* old_data = GetEntryData();
853   if (old_data) {
854     const base::Value* old_value = old_data->FindKey(key);
855     if (old_value && old_value->is_string() && old_value->GetString() == value)
856       return false;
857   }
858 
859   base::Value new_data = old_data ? GetEntryData()->Clone()
860                                   : base::Value(base::Value::Type::DICTIONARY);
861   new_data.SetKey(key, base::Value(value));
862   SetEntryData(std::move(new_data));
863   return true;
864 }
865 
SetString16(const char * key,base::string16 value)866 bool ProfileAttributesEntry::SetString16(const char* key,
867                                          base::string16 value) {
868   const base::Value* old_data = GetEntryData();
869   if (old_data) {
870     const base::Value* old_value = old_data->FindKey(key);
871     if (old_value && old_value->is_string() &&
872         base::UTF8ToUTF16(old_value->GetString()) == value)
873       return false;
874   }
875 
876   base::Value new_data = old_data ? GetEntryData()->Clone()
877                                   : base::Value(base::Value::Type::DICTIONARY);
878   new_data.SetKey(key, base::Value(value));
879   SetEntryData(std::move(new_data));
880   return true;
881 }
882 
SetDouble(const char * key,double value)883 bool ProfileAttributesEntry::SetDouble(const char* key, double value) {
884   const base::Value* old_data = GetEntryData();
885   if (old_data) {
886     const base::Value* old_value = old_data->FindKey(key);
887     if (old_value && old_value->is_double() && old_value->GetDouble() == value)
888       return false;
889   }
890 
891   base::Value new_data = old_data ? GetEntryData()->Clone()
892                                   : base::Value(base::Value::Type::DICTIONARY);
893   new_data.SetKey(key, base::Value(value));
894   SetEntryData(std::move(new_data));
895   return true;
896 }
897 
SetBool(const char * key,bool value)898 bool ProfileAttributesEntry::SetBool(const char* key, bool value) {
899   const base::Value* old_data = GetEntryData();
900   if (old_data) {
901     const base::Value* old_value = old_data->FindKey(key);
902     if (old_value && old_value->is_bool() && old_value->GetBool() == value)
903       return false;
904   }
905 
906   base::Value new_data = old_data ? GetEntryData()->Clone()
907                                   : base::Value(base::Value::Type::DICTIONARY);
908   new_data.SetKey(key, base::Value(value));
909   SetEntryData(std::move(new_data));
910   return true;
911 }
912 
SetInteger(const char * key,int value)913 bool ProfileAttributesEntry::SetInteger(const char* key, int value) {
914   const base::Value* old_data = GetEntryData();
915   if (old_data) {
916     const base::Value* old_value = old_data->FindKey(key);
917     if (old_value && old_value->is_int() && old_value->GetInt() == value)
918       return false;
919   }
920 
921   base::Value new_data = old_data ? GetEntryData()->Clone()
922                                   : base::Value(base::Value::Type::DICTIONARY);
923   new_data.SetKey(key, base::Value(value));
924   SetEntryData(std::move(new_data));
925   return true;
926 }
927 
ClearValue(const char * key)928 bool ProfileAttributesEntry::ClearValue(const char* key) {
929   const base::Value* old_data = GetEntryData();
930   if (!old_data || !old_data->FindKey(key))
931     return false;
932 
933   base::Value new_data = GetEntryData()->Clone();
934   new_data.RemoveKey(key);
935   SetEntryData(std::move(new_data));
936   return true;
937 }
938