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