1 // Copyright 2020 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/ui/webui/signin/profile_picker_handler.h"
6 
7 #include "base/bind.h"
8 #include "base/callback_helpers.h"
9 #include "base/metrics/histogram_functions.h"
10 #include "base/optional.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "base/util/values/values_util.h"
13 #include "chrome/browser/browser_process.h"
14 #include "chrome/browser/profiles/profile_attributes_entry.h"
15 #include "chrome/browser/profiles/profile_attributes_storage.h"
16 #include "chrome/browser/profiles/profile_avatar_icon_util.h"
17 #include "chrome/browser/profiles/profile_manager.h"
18 #include "chrome/browser/profiles/profile_statistics.h"
19 #include "chrome/browser/profiles/profile_statistics_factory.h"
20 #include "chrome/browser/profiles/profile_window.h"
21 #include "chrome/browser/profiles/profiles_state.h"
22 #include "chrome/browser/search/chrome_colors/chrome_colors_service.h"
23 #include "chrome/browser/signin/signin_util.h"
24 #include "chrome/browser/themes/theme_properties.h"
25 #include "chrome/browser/themes/theme_service.h"
26 #include "chrome/browser/themes/theme_service_factory.h"
27 #include "chrome/browser/ui/browser_finder.h"
28 #include "chrome/browser/ui/chrome_pages.h"
29 #include "chrome/browser/ui/profile_picker.h"
30 #include "chrome/browser/ui/signin/profile_colors_util.h"
31 #include "chrome/browser/ui/webui/profile_helper.h"
32 #include "chrome/common/pref_names.h"
33 #include "chrome/common/search/generated_colors_info.h"
34 #include "chrome/common/themes/autogenerated_theme_util.h"
35 #include "chrome/common/webui_url_constants.h"
36 #include "components/signin/public/identity_manager/account_info.h"
37 #include "third_party/skia/include/core/SkColor.h"
38 #include "ui/base/l10n/l10n_util.h"
39 #include "ui/base/webui/web_ui_util.h"
40 #include "ui/gfx/color_utils.h"
41 #include "ui/gfx/image/image.h"
42 
43 namespace {
44 const size_t kProfileCardAvatarSize = 74;
45 const size_t kProfileCreationAvatarSize = 100;
46 
47 constexpr int kDefaultThemeColorId = -1;
48 constexpr int kManuallyPickedColorId = 0;
49 
50 // These values are persisted to logs. Entries should not be renumbered and
51 // numeric values should never be reused.
52 enum class ProfilePickerAction {
53   kLaunchExistingProfile = 0,
54   kLaunchExistingProfileCustomizeSettings = 1,
55   kLaunchGuestProfile = 2,
56   kLaunchNewProfile = 3,
57   kDeleteProfile = 4,
58   kMaxValue = kDeleteProfile,
59 };
60 
IsManaged(const std::string & hosted_domain)61 bool IsManaged(const std::string& hosted_domain) {
62   return !hosted_domain.empty() && hosted_domain != kNoHostedDomainFound;
63 }
64 
GetChromeColorColorById(int color_id)65 base::Optional<SkColor> GetChromeColorColorById(int color_id) {
66   for (chrome_colors::ColorInfo color_info :
67        chrome_colors::kGeneratedColorsInfo) {
68     if (color_id == color_info.id)
69       return color_info.color;
70   }
71 
72   return base::nullopt;
73 }
74 
RecordProfilePickerAction(ProfilePickerAction action)75 void RecordProfilePickerAction(ProfilePickerAction action) {
76   base::UmaHistogramEnumeration("ProfilePicker.UserAction", action);
77 }
78 
RecordAskOnStartupChanged(bool value)79 void RecordAskOnStartupChanged(bool value) {
80   base::UmaHistogramBoolean("ProfilePicker.AskOnStartupChanged", value);
81 }
82 
RecordNewProfileSpec(base::Optional<SkColor> profile_color,bool create_shortcut)83 void RecordNewProfileSpec(base::Optional<SkColor> profile_color,
84                           bool create_shortcut) {
85   int theme_id =
86       profile_color.has_value()
87           ? chrome_colors::ChromeColorsService::GetColorId(*profile_color)
88           : -1;
89   base::UmaHistogramSparse("ProfilePicker.NewProfileTheme", theme_id);
90 
91   if (ProfileShortcutManager::IsFeatureEnabled()) {
92     base::UmaHistogramBoolean("ProfilePicker.NewProfileCreateShortcut",
93                               create_shortcut);
94   }
95 }
96 
GetAutogeneratedProfileThemeInfoValue(int color_id,base::Optional<SkColor> color,SkColor frame_color,SkColor active_tab_color,SkColor frame_text_color,int avatar_icon_size)97 base::Value GetAutogeneratedProfileThemeInfoValue(int color_id,
98                                                   base::Optional<SkColor> color,
99                                                   SkColor frame_color,
100                                                   SkColor active_tab_color,
101                                                   SkColor frame_text_color,
102                                                   int avatar_icon_size) {
103   base::Value dict(base::Value::Type::DICTIONARY);
104   dict.SetIntKey("colorId", color_id);
105   if (color.has_value())
106     dict.SetIntKey("color", *color);
107   dict.SetStringKey("themeFrameColor",
108                     color_utils::SkColorToRgbaString(frame_color));
109   dict.SetStringKey("themeShapeColor",
110                     color_utils::SkColorToRgbaString(active_tab_color));
111   dict.SetStringKey("themeFrameTextColor",
112                     color_utils::SkColorToRgbaString(frame_text_color));
113   gfx::Image icon = profiles::GetPlaceholderAvatarIconWithColors(
114       /*fill_color=*/frame_color,
115       /*stroke_color=*/GetAvatarStrokeColor(frame_color), avatar_icon_size);
116   dict.SetStringKey("themeGenericAvatar",
117                     webui::GetBitmapDataUrl(icon.AsBitmap()));
118   return dict;
119 }
120 
CreateDefaultProfileThemeInfo(int avatar_icon_size)121 base::Value CreateDefaultProfileThemeInfo(int avatar_icon_size) {
122   bool dark_mode =
123       ui::NativeTheme::GetInstanceForNativeUi()->ShouldUseDarkColors();
124   SkColor frame_color = ThemeProperties::GetDefaultColor(
125       ThemeProperties::COLOR_FRAME_ACTIVE, /*incognito=*/false, dark_mode);
126   SkColor active_tab_color = ThemeProperties::GetDefaultColor(
127       ThemeProperties::COLOR_TOOLBAR, /*incognito=*/false, dark_mode);
128   SkColor frame_text_color = ThemeProperties::GetDefaultColor(
129       ThemeProperties::COLOR_TAB_FOREGROUND_INACTIVE_FRAME_ACTIVE,
130       /*incognito=*/false, dark_mode);
131   return GetAutogeneratedProfileThemeInfoValue(
132       kDefaultThemeColorId, base::nullopt, frame_color, active_tab_color,
133       frame_text_color, avatar_icon_size);
134 }
135 
CreateAutogeneratedProfileThemeInfo(int color_id,SkColor color,int avatar_icon_size)136 base::Value CreateAutogeneratedProfileThemeInfo(int color_id,
137                                                 SkColor color,
138                                                 int avatar_icon_size) {
139   auto theme_colors = GetAutogeneratedThemeColors(color);
140   SkColor frame_color = theme_colors.frame_color;
141   SkColor active_tab_color = theme_colors.active_tab_color;
142   SkColor frame_text_color = theme_colors.frame_text_color;
143   return GetAutogeneratedProfileThemeInfoValue(
144       color_id, color, frame_color, active_tab_color, frame_text_color,
145       avatar_icon_size);
146 }
147 
148 }  // namespace
149 
150 ProfilePickerHandler::ProfilePickerHandler() = default;
151 
~ProfilePickerHandler()152 ProfilePickerHandler::~ProfilePickerHandler() {
153   OnJavascriptDisallowed();
154 }
155 
EnableStartupMetrics()156 void ProfilePickerHandler::EnableStartupMetrics() {
157   DCHECK(creation_time_on_startup_.is_null());
158   content::WebContents* contents = web_ui()->GetWebContents();
159   if (contents->GetVisibility() == content::Visibility::VISIBLE) {
160     // Only record paint event if the window is visible.
161     creation_time_on_startup_ = base::TimeTicks::Now();
162     Observe(web_ui()->GetWebContents());
163   }
164 }
165 
RegisterMessages()166 void ProfilePickerHandler::RegisterMessages() {
167   web_ui()->RegisterMessageCallback(
168       "mainViewInitialize",
169       base::BindRepeating(&ProfilePickerHandler::HandleMainViewInitialize,
170                           base::Unretained(this)));
171   web_ui()->RegisterMessageCallback(
172       "launchSelectedProfile",
173       base::BindRepeating(&ProfilePickerHandler::HandleLaunchSelectedProfile,
174                           base::Unretained(this), /*open_settings=*/false));
175   web_ui()->RegisterMessageCallback(
176       "openManageProfileSettingsSubPage",
177       base::BindRepeating(&ProfilePickerHandler::HandleLaunchSelectedProfile,
178                           base::Unretained(this), /*open_settings=*/true));
179   web_ui()->RegisterMessageCallback(
180       "launchGuestProfile",
181       base::BindRepeating(&ProfilePickerHandler::HandleLaunchGuestProfile,
182                           base::Unretained(this)));
183   web_ui()->RegisterMessageCallback(
184       "askOnStartupChanged",
185       base::BindRepeating(&ProfilePickerHandler::HandleAskOnStartupChanged,
186                           base::Unretained(this)));
187   web_ui()->RegisterMessageCallback(
188       "getNewProfileSuggestedThemeInfo",
189       base::BindRepeating(
190           &ProfilePickerHandler::HandleGetNewProfileSuggestedThemeInfo,
191           base::Unretained(this)));
192   web_ui()->RegisterMessageCallback(
193       "getProfileThemeInfo",
194       base::BindRepeating(&ProfilePickerHandler::HandleGetProfileThemeInfo,
195                           base::Unretained(this)));
196   web_ui()->RegisterMessageCallback(
197       "removeProfile",
198       base::BindRepeating(&ProfilePickerHandler::HandleRemoveProfile,
199                           base::Unretained(this)));
200   web_ui()->RegisterMessageCallback(
201       "getProfileStatistics",
202       base::BindRepeating(&ProfilePickerHandler::HandleGetProfileStatistics,
203                           base::Unretained(this)));
204   web_ui()->RegisterMessageCallback(
205       "loadSignInProfileCreationFlow",
206       base::BindRepeating(
207           &ProfilePickerHandler::HandleLoadSignInProfileCreationFlow,
208           base::Unretained(this)));
209   web_ui()->RegisterMessageCallback(
210       "createProfile",
211       base::BindRepeating(&ProfilePickerHandler::HandleCreateProfile,
212                           base::Unretained(this)));
213 }
214 
OnJavascriptAllowed()215 void ProfilePickerHandler::OnJavascriptAllowed() {
216   g_browser_process->profile_manager()
217       ->GetProfileAttributesStorage()
218       .AddObserver(this);
219 }
OnJavascriptDisallowed()220 void ProfilePickerHandler::OnJavascriptDisallowed() {
221   g_browser_process->profile_manager()
222       ->GetProfileAttributesStorage()
223       .RemoveObserver(this);
224 }
225 
HandleMainViewInitialize(const base::ListValue * args)226 void ProfilePickerHandler::HandleMainViewInitialize(
227     const base::ListValue* args) {
228   if (!creation_time_on_startup_.is_null() && !main_view_initialized_) {
229     // This function can be called multiple times if the page is reloaded. The
230     // histogram is only recorded once.
231     main_view_initialized_ = true;
232     base::UmaHistogramTimes("ProfilePicker.StartupTime.MainViewInitialized",
233                             base::TimeTicks::Now() - creation_time_on_startup_);
234   }
235 
236   AllowJavascript();
237   PushProfilesList();
238 }
239 
HandleLaunchSelectedProfile(bool open_settings,const base::ListValue * args)240 void ProfilePickerHandler::HandleLaunchSelectedProfile(
241     bool open_settings,
242     const base::ListValue* args) {
243   const base::Value* profile_path_value = nullptr;
244   if (!args->Get(0, &profile_path_value))
245     return;
246 
247   base::Optional<base::FilePath> profile_path =
248       util::ValueToFilePath(*profile_path_value);
249   if (!profile_path)
250     return;
251 
252   ProfileAttributesEntry* entry;
253   if (!g_browser_process->profile_manager()
254            ->GetProfileAttributesStorage()
255            .GetProfileAttributesWithPath(*profile_path, &entry)) {
256     NOTREACHED();
257     return;
258   }
259 
260   if (entry->IsSigninRequired()) {
261     // The new profile picker does not yet support force signin policy and
262     // should not be accessible for devices with this policy.
263     NOTREACHED();
264     return;
265   }
266 
267   profiles::SwitchToProfile(
268       *profile_path, /*always_create=*/false,
269       base::BindRepeating(&ProfilePickerHandler::OnSwitchToProfileComplete,
270                           weak_factory_.GetWeakPtr(), false, open_settings));
271 }
272 
HandleLaunchGuestProfile(const base::ListValue * args)273 void ProfilePickerHandler::HandleLaunchGuestProfile(
274     const base::ListValue* args) {
275   // TODO(crbug.com/1063856): Add check |IsGuestModeEnabled| once policy
276   // checking has been added to the UI.
277   profiles::SwitchToGuestProfile(
278       base::Bind(&ProfilePickerHandler::OnSwitchToProfileComplete,
279                  weak_factory_.GetWeakPtr(), false, false));
280 }
281 
HandleAskOnStartupChanged(const base::ListValue * args)282 void ProfilePickerHandler::HandleAskOnStartupChanged(
283     const base::ListValue* args) {
284   bool show_on_startup;
285   if (!args->GetBoolean(0, &show_on_startup))
286     return;
287 
288   PrefService* prefs = g_browser_process->local_state();
289   prefs->SetBoolean(prefs::kBrowserShowProfilePickerOnStartup, show_on_startup);
290   RecordAskOnStartupChanged(show_on_startup);
291 }
292 
HandleGetNewProfileSuggestedThemeInfo(const base::ListValue * args)293 void ProfilePickerHandler::HandleGetNewProfileSuggestedThemeInfo(
294     const base::ListValue* args) {
295   AllowJavascript();
296   CHECK_EQ(1U, args->GetSize());
297   const base::Value& callback_id = args->GetList()[0];
298   chrome_colors::ColorInfo color_info = GenerateNewProfileColor();
299   int avatar_icon_size =
300       kProfileCreationAvatarSize * web_ui()->GetDeviceScaleFactor();
301   base::Value dict = CreateAutogeneratedProfileThemeInfo(
302       color_info.id, color_info.color, avatar_icon_size);
303   ResolveJavascriptCallback(callback_id, std::move(dict));
304 }
305 
HandleGetProfileThemeInfo(const base::ListValue * args)306 void ProfilePickerHandler::HandleGetProfileThemeInfo(
307     const base::ListValue* args) {
308   AllowJavascript();
309   CHECK_EQ(2U, args->GetList().size());
310   const base::Value& callback_id = args->GetList()[0];
311   const base::Value& user_theme_choice = args->GetList()[1];
312   int color_id = user_theme_choice.FindIntKey("colorId").value();
313   base::Optional<SkColor> color = user_theme_choice.FindDoubleKey("color");
314   int avatar_icon_size =
315       kProfileCreationAvatarSize * web_ui()->GetDeviceScaleFactor();
316   base::Value dict;
317   switch (color_id) {
318     case kDefaultThemeColorId:
319       dict = CreateDefaultProfileThemeInfo(avatar_icon_size);
320       break;
321     case kManuallyPickedColorId:
322       dict = CreateAutogeneratedProfileThemeInfo(color_id, *color,
323                                                  avatar_icon_size);
324       break;
325     default:
326       dict = CreateAutogeneratedProfileThemeInfo(
327           color_id, *GetChromeColorColorById(color_id), avatar_icon_size);
328       break;
329   }
330   ResolveJavascriptCallback(callback_id, std::move(dict));
331 }
332 
HandleCreateProfile(const base::ListValue * args)333 void ProfilePickerHandler::HandleCreateProfile(const base::ListValue* args) {
334   // profileName, profileColor, avatarUrl, isGeneric, createShortcut
335   CHECK_EQ(5U, args->GetList().size());
336   base::string16 profile_name =
337       base::UTF8ToUTF16(args->GetList()[0].GetString());
338   // profileColor is undefined for the default theme.
339   base::Optional<SkColor> profile_color;
340   if (args->GetList()[1].is_int())
341     profile_color = args->GetList()[1].GetInt();
342   std::string avatar_url = args->GetList()[2].GetString();
343   bool is_generic = args->GetList()[3].GetBool();
344   bool create_shortcut = args->GetList()[4].GetBool();
345   DCHECK(base::IsStringASCII(avatar_url));
346   base::TrimWhitespace(profile_name, base::TRIM_ALL, &profile_name);
347   CHECK(!profile_name.empty());
348   if (is_generic) {
349     avatar_url = profiles::GetDefaultAvatarIconUrl(
350         profiles::GetPlaceholderAvatarIndex());
351   }
352 
353 #ifndef NDEBUG
354   size_t icon_index;
355   DCHECK(profiles::IsDefaultAvatarIconUrl(avatar_url, &icon_index));
356 #endif
357 
358   ProfileMetrics::LogProfileAddNewUser(ProfileMetrics::ADD_NEW_PROFILE_PICKER);
359   ProfileManager::CreateMultiProfileAsync(
360       profile_name, avatar_url,
361       base::BindRepeating(&ProfilePickerHandler::OnProfileCreated,
362                           weak_factory_.GetWeakPtr(), profile_color,
363                           create_shortcut));
364 }
365 
OnProfileCreated(base::Optional<SkColor> profile_color,bool create_shortcut,Profile * profile,Profile::CreateStatus status)366 void ProfilePickerHandler::OnProfileCreated(
367     base::Optional<SkColor> profile_color,
368     bool create_shortcut,
369     Profile* profile,
370     Profile::CreateStatus status) {
371   switch (status) {
372     case Profile::CREATE_STATUS_LOCAL_FAIL: {
373       NOTREACHED() << "Local fail in creating new profile";
374       break;
375     }
376 
377     case Profile::CREATE_STATUS_CREATED:
378       // Do nothing for an intermediate status.
379       return;
380 
381     case Profile::CREATE_STATUS_INITIALIZED: {
382       OnProfileCreationSuccess(profile_color, create_shortcut, profile);
383       break;
384     }
385     // User-initiated cancellation is handled in CancelProfileRegistration and
386     // does not call this callback.
387     case Profile::CREATE_STATUS_CANCELED:
388     case Profile::CREATE_STATUS_REMOTE_FAIL:
389     case Profile::MAX_CREATE_STATUS: {
390       NOTREACHED();
391       break;
392     }
393   }
394 
395   if (IsJavascriptAllowed())
396     FireWebUIListener("create-profile-finished", base::Value());
397 }
398 
OnProfileCreationSuccess(base::Optional<SkColor> profile_color,bool create_shortcut,Profile * profile)399 void ProfilePickerHandler::OnProfileCreationSuccess(
400     base::Optional<SkColor> profile_color,
401     bool create_shortcut,
402     Profile* profile) {
403   DCHECK(profile);
404   DCHECK(!signin_util::IsForceSigninEnabled());
405 
406   // Apply a new color to the profile or use the default theme.
407   auto* theme_service = ThemeServiceFactory::GetForProfile(profile);
408   if (profile_color.has_value())
409     theme_service->BuildAutogeneratedThemeFromColor(*profile_color);
410   else
411     theme_service->UseDefaultTheme();
412 
413   // Create shortcut if edded.
414   if (create_shortcut) {
415     DCHECK(ProfileShortcutManager::IsFeatureEnabled());
416     ProfileShortcutManager* shortcut_manager =
417         g_browser_process->profile_manager()->profile_shortcut_manager();
418     DCHECK(shortcut_manager);
419     if (shortcut_manager)
420       shortcut_manager->CreateProfileShortcut(profile->GetPath());
421   }
422 
423   RecordNewProfileSpec(profile_color, create_shortcut);
424   // Launch profile and close the picker.
425   profiles::OpenBrowserWindowForProfile(
426       base::Bind(&ProfilePickerHandler::OnSwitchToProfileComplete,
427                  weak_factory_.GetWeakPtr(), true, false),
428       false,  // Don't create a window if one already exists.
429       true,   // Create a first run window.
430       false,  // There is no need to unblock all extensions because we only open
431               // browser window if the Profile is not locked. Hence there is no
432               // extension blocked.
433       profile, Profile::CREATE_STATUS_INITIALIZED);
434 }
435 
HandleRemoveProfile(const base::ListValue * args)436 void ProfilePickerHandler::HandleRemoveProfile(const base::ListValue* args) {
437   CHECK_EQ(1U, args->GetSize());
438   const base::Value& profile_path_value = args->GetList()[0];
439   base::Optional<base::FilePath> profile_path =
440       util::ValueToFilePath(profile_path_value);
441 
442   if (!profile_path) {
443     NOTREACHED();
444     return;
445   }
446   RecordProfilePickerAction(ProfilePickerAction::kDeleteProfile);
447   webui::DeleteProfileAtPath(*profile_path,
448                              ProfileMetrics::DELETE_PROFILE_USER_MANAGER);
449 }
450 
HandleGetProfileStatistics(const base::ListValue * args)451 void ProfilePickerHandler::HandleGetProfileStatistics(
452     const base::ListValue* args) {
453   AllowJavascript();
454   CHECK_EQ(1U, args->GetSize());
455   const base::Value& profile_path_value = args->GetList()[0];
456   base::Optional<base::FilePath> profile_path =
457       util::ValueToFilePath(profile_path_value);
458   if (!profile_path)
459     return;
460 
461   Profile* profile =
462       g_browser_process->profile_manager()->GetProfileByPath(*profile_path);
463 
464   if (profile) {
465     GatherProfileStatistics(profile);
466   } else {
467     g_browser_process->profile_manager()->LoadProfileByPath(
468         *profile_path, false,
469         base::BindOnce(&ProfilePickerHandler::GatherProfileStatistics,
470                        weak_factory_.GetWeakPtr()));
471   }
472 }
473 
GatherProfileStatistics(Profile * profile)474 void ProfilePickerHandler::GatherProfileStatistics(Profile* profile) {
475   if (!profile) {
476     return;
477   }
478 
479   ProfileStatisticsFactory::GetForProfile(profile)->GatherStatistics(
480       base::BindRepeating(&ProfilePickerHandler::OnProfileStatisticsReceived,
481                           weak_factory_.GetWeakPtr(), profile->GetPath()));
482 }
483 
OnProfileStatisticsReceived(base::FilePath profile_path,profiles::ProfileCategoryStats result)484 void ProfilePickerHandler::OnProfileStatisticsReceived(
485     base::FilePath profile_path,
486     profiles::ProfileCategoryStats result) {
487   if (!IsJavascriptAllowed())
488     return;
489 
490   base::Value dict(base::Value::Type::DICTIONARY);
491   dict.SetKey("profilePath", util::FilePathToValue(profile_path));
492   base::Value stats(base::Value::Type::DICTIONARY);
493   // Categories are defined in |kProfileStatisticsCategories|
494   // {"BrowsingHistory", "Passwords", "Bookmarks", "Autofill"}.
495   for (const auto& item : result) {
496     stats.SetIntKey(item.category, item.count);
497   }
498   dict.SetKey("statistics", std::move(stats));
499   FireWebUIListener("profile-statistics-received", std::move(dict));
500 }
501 
HandleLoadSignInProfileCreationFlow(const base::ListValue * args)502 void ProfilePickerHandler::HandleLoadSignInProfileCreationFlow(
503     const base::ListValue* args) {
504   DCHECK(args->GetList()[0].is_int());
505   SkColor profile_color = args->GetList()[0].GetInt();
506   ProfilePicker::SwitchToSignIn(
507       profile_color, base::BindOnce(&ProfilePickerHandler::OnLoadSigninFailed,
508                                     weak_factory_.GetWeakPtr()));
509 }
510 
OnLoadSigninFailed()511 void ProfilePickerHandler::OnLoadSigninFailed() {
512   if (IsJavascriptAllowed())
513     FireWebUIListener("load-signin-failed", base::Value());
514 }
515 
OnSwitchToProfileComplete(bool new_profile,bool open_settings,Profile * profile,Profile::CreateStatus profile_create_status)516 void ProfilePickerHandler::OnSwitchToProfileComplete(
517     bool new_profile,
518     bool open_settings,
519     Profile* profile,
520     Profile::CreateStatus profile_create_status) {
521   Browser* browser = chrome::FindAnyBrowser(profile, false);
522   DCHECK(browser);
523   DCHECK(browser->window());
524   if (open_settings) {
525     chrome::ShowSettingsSubPage(browser, chrome::kManageProfileSubPage);
526   }
527 
528   if (new_profile) {
529     RecordProfilePickerAction(ProfilePickerAction::kLaunchNewProfile);
530   } else if (profile->IsGuestSession()) {
531     RecordProfilePickerAction(ProfilePickerAction::kLaunchGuestProfile);
532   } else {
533     RecordProfilePickerAction(
534         open_settings
535             ? ProfilePickerAction::kLaunchExistingProfileCustomizeSettings
536             : ProfilePickerAction::kLaunchExistingProfile);
537   }
538   ProfilePicker::Hide();
539 }
540 
PushProfilesList()541 void ProfilePickerHandler::PushProfilesList() {
542   DCHECK(IsJavascriptAllowed());
543   FireWebUIListener("profiles-list-changed", GetProfilesList());
544 }
545 
GetProfilesList()546 base::Value ProfilePickerHandler::GetProfilesList() {
547   base::ListValue profiles_list;
548   std::vector<ProfileAttributesEntry*> entries =
549       g_browser_process->profile_manager()
550           ->GetProfileAttributesStorage()
551           .GetAllProfilesAttributesSortedByName();
552   const int avatar_icon_size =
553       kProfileCardAvatarSize * web_ui()->GetDeviceScaleFactor();
554   for (const ProfileAttributesEntry* entry : entries) {
555     auto profile_entry = std::make_unique<base::DictionaryValue>();
556     profile_entry->SetKey("profilePath",
557                           util::FilePathToValue(entry->GetPath()));
558     profile_entry->SetString("localProfileName", entry->GetLocalProfileName());
559     profile_entry->SetBoolPath(
560         "isSyncing", entry->GetSigninState() ==
561                          SigninState::kSignedInWithConsentedPrimaryAccount);
562     // GAIA name/user name can be empty, if the profile is not signed in to
563     // chrome.
564     profile_entry->SetString("gaiaName", entry->GetGAIANameToDisplay());
565     profile_entry->SetString("userName", entry->GetUserName());
566     profile_entry->SetBoolPath("isManaged",
567                                IsManaged(entry->GetHostedDomain()));
568     gfx::Image icon =
569         profiles::GetSizedAvatarIcon(entry->GetAvatarIcon(avatar_icon_size),
570                                      true, avatar_icon_size, avatar_icon_size);
571     std::string icon_url = webui::GetBitmapDataUrl(icon.AsBitmap());
572     profile_entry->SetString("avatarIcon", icon_url);
573     profiles_list.Append(std::move(profile_entry));
574   }
575   return std::move(profiles_list);
576 }
577 
OnProfileAdded(const base::FilePath & profile_path)578 void ProfilePickerHandler::OnProfileAdded(const base::FilePath& profile_path) {
579   PushProfilesList();
580 }
581 
OnProfileWasRemoved(const base::FilePath & profile_path,const base::string16 & profile_name)582 void ProfilePickerHandler::OnProfileWasRemoved(
583     const base::FilePath& profile_path,
584     const base::string16& profile_name) {
585   DCHECK(IsJavascriptAllowed());
586   FireWebUIListener("profile-removed", util::FilePathToValue(profile_path));
587 }
588 
OnProfileAvatarChanged(const base::FilePath & profile_path)589 void ProfilePickerHandler::OnProfileAvatarChanged(
590     const base::FilePath& profile_path) {
591   PushProfilesList();
592 }
593 
OnProfileHighResAvatarLoaded(const base::FilePath & profile_path)594 void ProfilePickerHandler::OnProfileHighResAvatarLoaded(
595     const base::FilePath& profile_path) {
596   PushProfilesList();
597 }
598 
OnProfileNameChanged(const base::FilePath & profile_path,const base::string16 & old_profile_name)599 void ProfilePickerHandler::OnProfileNameChanged(
600     const base::FilePath& profile_path,
601     const base::string16& old_profile_name) {
602   PushProfilesList();
603 }
604 
DidFirstVisuallyNonEmptyPaint()605 void ProfilePickerHandler::DidFirstVisuallyNonEmptyPaint() {
606   DCHECK(!creation_time_on_startup_.is_null());
607   base::UmaHistogramTimes("ProfilePicker.StartupTime.FirstPaint",
608                           base::TimeTicks::Now() - creation_time_on_startup_);
609   // Stop observing so that the histogram is only recorded once.
610   Observe(nullptr);
611 }
612 
OnVisibilityChanged(content::Visibility visibility)613 void ProfilePickerHandler::OnVisibilityChanged(content::Visibility visibility) {
614   // If the profile picker is hidden, the first paint will be delayed until the
615   // picker is visible again. Stop monitoring the first paint to avoid polluting
616   // the metrics.
617   if (visibility != content::Visibility::VISIBLE)
618     Observe(nullptr);
619 }
620