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