blob: 043222196244f263550ed493f008b88b032e0593 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/webui/signin/profile_picker_handler.h"
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/metrics/histogram_functions.h"
#include "base/optional.h"
#include "base/strings/utf_string_conversions.h"
#include "base/util/values/values_util.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile_attributes_entry.h"
#include "chrome/browser/profiles/profile_attributes_storage.h"
#include "chrome/browser/profiles/profile_avatar_icon_util.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/profiles/profile_statistics.h"
#include "chrome/browser/profiles/profile_statistics_factory.h"
#include "chrome/browser/profiles/profile_window.h"
#include "chrome/browser/profiles/profiles_state.h"
#include "chrome/browser/search/chrome_colors/chrome_colors_service.h"
#include "chrome/browser/signin/signin_util.h"
#include "chrome/browser/themes/theme_properties.h"
#include "chrome/browser/themes/theme_service.h"
#include "chrome/browser/themes/theme_service_factory.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/chrome_pages.h"
#include "chrome/browser/ui/profile_picker.h"
#include "chrome/browser/ui/signin/profile_colors_util.h"
#include "chrome/browser/ui/user_manager.h"
#include "chrome/browser/ui/webui/profile_helper.h"
#include "chrome/browser/ui/webui/signin/login_ui_service.h"
#include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/search/generated_colors_info.h"
#include "chrome/common/themes/autogenerated_theme_util.h"
#include "chrome/common/webui_url_constants.h"
#include "components/signin/public/identity_manager/account_info.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/webui/web_ui_util.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/image/image.h"
namespace {
const size_t kProfileCardAvatarSize = 74;
const size_t kProfileCreationAvatarSize = 100;
constexpr int kDefaultThemeColorId = -1;
constexpr int kManuallyPickedColorId = 0;
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class ProfilePickerAction {
kLaunchExistingProfile = 0,
kLaunchExistingProfileCustomizeSettings = 1,
kLaunchGuestProfile = 2,
kLaunchNewProfile = 3,
kDeleteProfile = 4,
kMaxValue = kDeleteProfile,
};
base::Optional<SkColor> GetChromeColorColorById(int color_id) {
for (chrome_colors::ColorInfo color_info :
chrome_colors::kGeneratedColorsInfo) {
if (color_id == color_info.id)
return color_info.color;
}
return base::nullopt;
}
void RecordProfilePickerAction(ProfilePickerAction action) {
base::UmaHistogramEnumeration("ProfilePicker.UserAction", action);
}
void RecordAskOnStartupChanged(bool value) {
base::UmaHistogramBoolean("ProfilePicker.AskOnStartupChanged", value);
}
void RecordNewProfileSpec(base::Optional<SkColor> profile_color,
bool create_shortcut) {
int theme_id =
profile_color.has_value()
? chrome_colors::ChromeColorsService::GetColorId(*profile_color)
: chrome_colors::kDefaultColorId;
base::UmaHistogramSparse("ProfilePicker.NewProfileTheme", theme_id);
if (ProfileShortcutManager::IsFeatureEnabled()) {
base::UmaHistogramBoolean("ProfilePicker.NewProfileCreateShortcut",
create_shortcut);
}
}
base::Value GetAutogeneratedProfileThemeInfoValue(int color_id,
base::Optional<SkColor> color,
SkColor frame_color,
SkColor active_tab_color,
SkColor frame_text_color,
int avatar_icon_size) {
base::Value dict(base::Value::Type::DICTIONARY);
dict.SetIntKey("colorId", color_id);
if (color.has_value())
dict.SetIntKey("color", *color);
dict.SetStringKey("themeFrameColor",
color_utils::SkColorToRgbaString(frame_color));
dict.SetStringKey("themeShapeColor",
color_utils::SkColorToRgbaString(active_tab_color));
dict.SetStringKey("themeFrameTextColor",
color_utils::SkColorToRgbaString(frame_text_color));
gfx::Image icon = profiles::GetPlaceholderAvatarIconWithColors(
/*fill_color=*/frame_color,
/*stroke_color=*/GetAvatarStrokeColor(frame_color), avatar_icon_size);
dict.SetStringKey("themeGenericAvatar",
webui::GetBitmapDataUrl(icon.AsBitmap()));
return dict;
}
base::Value CreateDefaultProfileThemeInfo(int avatar_icon_size) {
bool dark_mode =
ui::NativeTheme::GetInstanceForNativeUi()->ShouldUseDarkColors();
SkColor frame_color = ThemeProperties::GetDefaultColor(
ThemeProperties::COLOR_FRAME_ACTIVE, /*incognito=*/false, dark_mode);
SkColor active_tab_color = ThemeProperties::GetDefaultColor(
ThemeProperties::COLOR_TOOLBAR, /*incognito=*/false, dark_mode);
SkColor frame_text_color = ThemeProperties::GetDefaultColor(
ThemeProperties::COLOR_TAB_FOREGROUND_INACTIVE_FRAME_ACTIVE,
/*incognito=*/false, dark_mode);
return GetAutogeneratedProfileThemeInfoValue(
kDefaultThemeColorId, base::nullopt, frame_color, active_tab_color,
frame_text_color, avatar_icon_size);
}
base::Value CreateAutogeneratedProfileThemeInfo(int color_id,
SkColor color,
int avatar_icon_size) {
auto theme_colors = GetAutogeneratedThemeColors(color);
SkColor frame_color = theme_colors.frame_color;
SkColor active_tab_color = theme_colors.active_tab_color;
SkColor frame_text_color = theme_colors.frame_text_color;
return GetAutogeneratedProfileThemeInfoValue(
color_id, color, frame_color, active_tab_color, frame_text_color,
avatar_icon_size);
}
} // namespace
ProfilePickerHandler::ProfilePickerHandler() = default;
ProfilePickerHandler::~ProfilePickerHandler() {
OnJavascriptDisallowed();
}
void ProfilePickerHandler::EnableStartupMetrics() {
DCHECK(creation_time_on_startup_.is_null());
content::WebContents* contents = web_ui()->GetWebContents();
if (contents->GetVisibility() == content::Visibility::VISIBLE) {
// Only record paint event if the window is visible.
creation_time_on_startup_ = base::TimeTicks::Now();
Observe(web_ui()->GetWebContents());
}
}
void ProfilePickerHandler::RegisterMessages() {
web_ui()->RegisterMessageCallback(
"mainViewInitialize",
base::BindRepeating(&ProfilePickerHandler::HandleMainViewInitialize,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"launchSelectedProfile",
base::BindRepeating(&ProfilePickerHandler::HandleLaunchSelectedProfile,
base::Unretained(this), /*open_settings=*/false));
web_ui()->RegisterMessageCallback(
"openManageProfileSettingsSubPage",
base::BindRepeating(&ProfilePickerHandler::HandleLaunchSelectedProfile,
base::Unretained(this), /*open_settings=*/true));
web_ui()->RegisterMessageCallback(
"launchGuestProfile",
base::BindRepeating(&ProfilePickerHandler::HandleLaunchGuestProfile,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"askOnStartupChanged",
base::BindRepeating(&ProfilePickerHandler::HandleAskOnStartupChanged,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"getNewProfileSuggestedThemeInfo",
base::BindRepeating(
&ProfilePickerHandler::HandleGetNewProfileSuggestedThemeInfo,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"getProfileThemeInfo",
base::BindRepeating(&ProfilePickerHandler::HandleGetProfileThemeInfo,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"removeProfile",
base::BindRepeating(&ProfilePickerHandler::HandleRemoveProfile,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"getProfileStatistics",
base::BindRepeating(&ProfilePickerHandler::HandleGetProfileStatistics,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"loadSignInProfileCreationFlow",
base::BindRepeating(
&ProfilePickerHandler::HandleLoadSignInProfileCreationFlow,
base::Unretained(this)));
// TODO(crbug.com/1115056): Consider renaming this message to
// 'createLocalProfile' as this is only used for local profiles.
web_ui()->RegisterMessageCallback(
"createProfile",
base::BindRepeating(&ProfilePickerHandler::HandleCreateProfile,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"setProfileName",
base::BindRepeating(&ProfilePickerHandler::HandleSetProfileName,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"recordSignInPromoImpression",
base::BindRepeating(
&ProfilePickerHandler::HandleRecordSignInPromoImpression,
base::Unretained(this)));
}
void ProfilePickerHandler::OnJavascriptAllowed() {
g_browser_process->profile_manager()
->GetProfileAttributesStorage()
.AddObserver(this);
}
void ProfilePickerHandler::OnJavascriptDisallowed() {
g_browser_process->profile_manager()
->GetProfileAttributesStorage()
.RemoveObserver(this);
}
void ProfilePickerHandler::HandleMainViewInitialize(
const base::ListValue* args) {
if (!creation_time_on_startup_.is_null() && !main_view_initialized_) {
// This function can be called multiple times if the page is reloaded. The
// histogram is only recorded once.
main_view_initialized_ = true;
base::UmaHistogramTimes("ProfilePicker.StartupTime.MainViewInitialized",
base::TimeTicks::Now() - creation_time_on_startup_);
}
AllowJavascript();
PushProfilesList();
}
void ProfilePickerHandler::HandleLaunchSelectedProfile(
bool open_settings,
const base::ListValue* args) {
const base::Value* profile_path_value = nullptr;
if (!args->Get(0, &profile_path_value))
return;
base::Optional<base::FilePath> profile_path =
util::ValueToFilePath(*profile_path_value);
if (!profile_path)
return;
ProfileAttributesEntry* entry;
if (!g_browser_process->profile_manager()
->GetProfileAttributesStorage()
.GetProfileAttributesWithPath(*profile_path, &entry)) {
NOTREACHED();
return;
}
if (entry->IsSigninRequired()) {
DCHECK(signin_util::IsForceSigninEnabled());
if (entry->GetActiveTime() != base::Time()) {
// If force-sign-in is enabled, do not allow users to sign in to a
// pre-existing locked profile, as this may force unexpected profile data
// merge. We consider a profile as pre-existing if it has been actived
// previously. A pre-existed profile can still be used if it has been
// signed in with an email address matched RestrictSigninToPattern policy
// already.
LoginUIServiceFactory::GetForProfile(
Profile::FromWebUI(web_ui())->GetOriginalProfile())
->SetProfileBlockingErrorMessage();
UserManagerProfileDialog::ShowDialogAndDisplayErrorMessage(
web_ui()->GetWebContents()->GetBrowserContext());
} else {
// Fresh sign in via user manager without existing email address.
UserManagerProfileDialog::ShowForceSigninDialog(
web_ui()->GetWebContents()->GetBrowserContext(), *profile_path);
}
return;
}
profiles::SwitchToProfile(
*profile_path, /*always_create=*/false,
base::BindRepeating(&ProfilePickerHandler::OnSwitchToProfileComplete,
weak_factory_.GetWeakPtr(), false, open_settings));
}
void ProfilePickerHandler::HandleLaunchGuestProfile(
const base::ListValue* args) {
// TODO(crbug.com/1063856): Add check |IsGuestModeEnabled| once policy
// checking has been added to the UI.
profiles::SwitchToGuestProfile(
base::BindRepeating(&ProfilePickerHandler::OnSwitchToProfileComplete,
weak_factory_.GetWeakPtr(), false, false));
}
void ProfilePickerHandler::HandleAskOnStartupChanged(
const base::ListValue* args) {
bool show_on_startup;
if (!args->GetBoolean(0, &show_on_startup))
return;
PrefService* prefs = g_browser_process->local_state();
prefs->SetBoolean(prefs::kBrowserShowProfilePickerOnStartup, show_on_startup);
RecordAskOnStartupChanged(show_on_startup);
}
void ProfilePickerHandler::HandleGetNewProfileSuggestedThemeInfo(
const base::ListValue* args) {
AllowJavascript();
CHECK_EQ(1U, args->GetSize());
const base::Value& callback_id = args->GetList()[0];
chrome_colors::ColorInfo color_info = GenerateNewProfileColor();
int avatar_icon_size =
kProfileCreationAvatarSize * web_ui()->GetDeviceScaleFactor();
base::Value dict = CreateAutogeneratedProfileThemeInfo(
color_info.id, color_info.color, avatar_icon_size);
ResolveJavascriptCallback(callback_id, std::move(dict));
}
void ProfilePickerHandler::HandleGetProfileThemeInfo(
const base::ListValue* args) {
AllowJavascript();
CHECK_EQ(2U, args->GetList().size());
const base::Value& callback_id = args->GetList()[0];
const base::Value& user_theme_choice = args->GetList()[1];
int color_id = user_theme_choice.FindIntKey("colorId").value();
base::Optional<SkColor> color = user_theme_choice.FindDoubleKey("color");
int avatar_icon_size =
kProfileCreationAvatarSize * web_ui()->GetDeviceScaleFactor();
base::Value dict;
switch (color_id) {
case kDefaultThemeColorId:
dict = CreateDefaultProfileThemeInfo(avatar_icon_size);
break;
case kManuallyPickedColorId:
dict = CreateAutogeneratedProfileThemeInfo(color_id, *color,
avatar_icon_size);
break;
default:
dict = CreateAutogeneratedProfileThemeInfo(
color_id, *GetChromeColorColorById(color_id), avatar_icon_size);
break;
}
ResolveJavascriptCallback(callback_id, std::move(dict));
}
void ProfilePickerHandler::HandleCreateProfile(const base::ListValue* args) {
// profileName, profileColor, avatarUrl, isGeneric, createShortcut
CHECK_EQ(5U, args->GetList().size());
base::string16 profile_name =
base::UTF8ToUTF16(args->GetList()[0].GetString());
// profileColor is undefined for the default theme.
base::Optional<SkColor> profile_color;
if (args->GetList()[1].is_int())
profile_color = args->GetList()[1].GetInt();
std::string avatar_url = args->GetList()[2].GetString();
bool is_generic = args->GetList()[3].GetBool();
bool create_shortcut = args->GetList()[4].GetBool();
DCHECK(base::IsStringASCII(avatar_url));
base::TrimWhitespace(profile_name, base::TRIM_ALL, &profile_name);
CHECK(!profile_name.empty());
if (is_generic) {
avatar_url = profiles::GetDefaultAvatarIconUrl(
profiles::GetPlaceholderAvatarIndex());
}
#ifndef NDEBUG
size_t icon_index;
DCHECK(profiles::IsDefaultAvatarIconUrl(avatar_url, &icon_index));
#endif
ProfileMetrics::LogProfileAddNewUser(
ProfileMetrics::ADD_NEW_PROFILE_PICKER_LOCAL);
ProfileManager::CreateMultiProfileAsync(
profile_name, avatar_url,
base::BindRepeating(&ProfilePickerHandler::OnProfileCreated,
weak_factory_.GetWeakPtr(), profile_color,
create_shortcut));
}
void ProfilePickerHandler::OnProfileCreated(
base::Optional<SkColor> profile_color,
bool create_shortcut,
Profile* profile,
Profile::CreateStatus status) {
switch (status) {
case Profile::CREATE_STATUS_LOCAL_FAIL: {
NOTREACHED() << "Local fail in creating new profile";
break;
}
case Profile::CREATE_STATUS_CREATED:
// Do nothing for an intermediate status.
return;
case Profile::CREATE_STATUS_INITIALIZED: {
OnProfileCreationSuccess(profile_color, create_shortcut, profile);
break;
}
// User-initiated cancellation is handled in CancelProfileRegistration and
// does not call this callback.
case Profile::CREATE_STATUS_CANCELED:
case Profile::CREATE_STATUS_REMOTE_FAIL:
case Profile::MAX_CREATE_STATUS: {
NOTREACHED();
break;
}
}
if (IsJavascriptAllowed())
FireWebUIListener("create-profile-finished", base::Value());
}
void ProfilePickerHandler::OnProfileCreationSuccess(
base::Optional<SkColor> profile_color,
bool create_shortcut,
Profile* profile) {
DCHECK(profile);
DCHECK(!signin_util::IsForceSigninEnabled());
// Apply a new color to the profile or use the default theme.
auto* theme_service = ThemeServiceFactory::GetForProfile(profile);
if (profile_color.has_value())
theme_service->BuildAutogeneratedThemeFromColor(*profile_color);
else
theme_service->UseDefaultTheme();
// Create shortcut if edded.
if (create_shortcut) {
DCHECK(ProfileShortcutManager::IsFeatureEnabled());
ProfileShortcutManager* shortcut_manager =
g_browser_process->profile_manager()->profile_shortcut_manager();
DCHECK(shortcut_manager);
if (shortcut_manager)
shortcut_manager->CreateProfileShortcut(profile->GetPath());
}
RecordNewProfileSpec(profile_color, create_shortcut);
// Launch profile and close the picker.
profiles::OpenBrowserWindowForProfile(
base::BindRepeating(&ProfilePickerHandler::OnSwitchToProfileComplete,
weak_factory_.GetWeakPtr(), true, false),
false, // Don't create a window if one already exists.
true, // Create a first run window.
false, // There is no need to unblock all extensions because we only open
// browser window if the Profile is not locked. Hence there is no
// extension blocked.
profile, Profile::CREATE_STATUS_INITIALIZED);
}
void ProfilePickerHandler::HandleRecordSignInPromoImpression(
const base::ListValue* /*args*/) {
signin_metrics::RecordSigninImpressionUserActionForAccessPoint(
signin_metrics::AccessPoint::ACCESS_POINT_USER_MANAGER);
}
void ProfilePickerHandler::HandleSetProfileName(const base::ListValue* args) {
CHECK_EQ(2U, args->GetSize());
const base::Value& profile_path_value = args->GetList()[0];
base::Optional<base::FilePath> profile_path =
util::ValueToFilePath(profile_path_value);
if (!profile_path) {
NOTREACHED();
return;
}
base::string16 profile_name =
base::UTF8ToUTF16(args->GetList()[1].GetString());
base::TrimWhitespace(profile_name, base::TRIM_ALL, &profile_name);
CHECK(!profile_name.empty());
ProfileAttributesEntry* entry;
CHECK(g_browser_process->profile_manager()
->GetProfileAttributesStorage()
.GetProfileAttributesWithPath(profile_path.value(), &entry));
entry->SetLocalProfileName(profile_name);
}
void ProfilePickerHandler::HandleRemoveProfile(const base::ListValue* args) {
CHECK_EQ(1U, args->GetSize());
const base::Value& profile_path_value = args->GetList()[0];
base::Optional<base::FilePath> profile_path =
util::ValueToFilePath(profile_path_value);
if (!profile_path) {
NOTREACHED();
return;
}
RecordProfilePickerAction(ProfilePickerAction::kDeleteProfile);
webui::DeleteProfileAtPath(*profile_path,
ProfileMetrics::DELETE_PROFILE_USER_MANAGER);
}
void ProfilePickerHandler::HandleGetProfileStatistics(
const base::ListValue* args) {
AllowJavascript();
CHECK_EQ(1U, args->GetSize());
const base::Value& profile_path_value = args->GetList()[0];
base::Optional<base::FilePath> profile_path =
util::ValueToFilePath(profile_path_value);
if (!profile_path)
return;
Profile* profile =
g_browser_process->profile_manager()->GetProfileByPath(*profile_path);
if (profile) {
GatherProfileStatistics(profile);
} else {
g_browser_process->profile_manager()->LoadProfileByPath(
*profile_path, false,
base::BindOnce(&ProfilePickerHandler::GatherProfileStatistics,
weak_factory_.GetWeakPtr()));
}
}
void ProfilePickerHandler::GatherProfileStatistics(Profile* profile) {
if (!profile) {
return;
}
ProfileStatisticsFactory::GetForProfile(profile)->GatherStatistics(
base::BindRepeating(&ProfilePickerHandler::OnProfileStatisticsReceived,
weak_factory_.GetWeakPtr(), profile->GetPath()));
}
void ProfilePickerHandler::OnProfileStatisticsReceived(
base::FilePath profile_path,
profiles::ProfileCategoryStats result) {
if (!IsJavascriptAllowed())
return;
base::Value dict(base::Value::Type::DICTIONARY);
dict.SetKey("profilePath", util::FilePathToValue(profile_path));
base::Value stats(base::Value::Type::DICTIONARY);
// Categories are defined in |kProfileStatisticsCategories|
// {"BrowsingHistory", "Passwords", "Bookmarks", "Autofill"}.
for (const auto& item : result) {
stats.SetIntKey(item.category, item.count);
}
dict.SetKey("statistics", std::move(stats));
FireWebUIListener("profile-statistics-received", std::move(dict));
}
void ProfilePickerHandler::HandleLoadSignInProfileCreationFlow(
const base::ListValue* args) {
CHECK_EQ(1U, args->GetSize());
SkColor profile_color;
if (args->GetList()[0].is_int()) {
profile_color = args->GetList()[0].GetInt();
} else {
// The profile color must provided in `args` unless the force signin is
// enabled.
DCHECK(signin_util::IsForceSigninEnabled());
profile_color = GenerateNewProfileColor().color;
}
ProfilePicker::SwitchToSignIn(
profile_color, base::BindOnce(&ProfilePickerHandler::OnLoadSigninFinished,
weak_factory_.GetWeakPtr()));
}
void ProfilePickerHandler::OnLoadSigninFinished(bool success) {
if (IsJavascriptAllowed())
FireWebUIListener("load-signin-finished", base::Value(success));
}
void ProfilePickerHandler::OnSwitchToProfileComplete(
bool new_profile,
bool open_settings,
Profile* profile,
Profile::CreateStatus profile_create_status) {
Browser* browser = chrome::FindAnyBrowser(profile, false);
DCHECK(browser);
DCHECK(browser->window());
if (open_settings) {
chrome::ShowSettingsSubPage(browser, chrome::kManageProfileSubPage);
}
if (new_profile) {
RecordProfilePickerAction(ProfilePickerAction::kLaunchNewProfile);
} else if (profile->IsGuestSession() || profile->IsEphemeralGuestProfile()) {
RecordProfilePickerAction(ProfilePickerAction::kLaunchGuestProfile);
} else {
RecordProfilePickerAction(
open_settings
? ProfilePickerAction::kLaunchExistingProfileCustomizeSettings
: ProfilePickerAction::kLaunchExistingProfile);
}
ProfilePicker::Hide();
}
void ProfilePickerHandler::PushProfilesList() {
DCHECK(IsJavascriptAllowed());
FireWebUIListener("profiles-list-changed", GetProfilesList());
}
void ProfilePickerHandler::SetProfilesOrder(
const std::vector<ProfileAttributesEntry*>& entries) {
profiles_order_.clear();
size_t index = 0;
for (const ProfileAttributesEntry* entry : entries) {
if (entry->IsGuest())
continue;
profiles_order_[entry->GetPath()] = index++;
}
}
std::vector<ProfileAttributesEntry*>
ProfilePickerHandler::GetProfileAttributes() {
size_t number_of_profiles =
g_browser_process->profile_manager()->GetNumberOfProfiles();
std::vector<ProfileAttributesEntry*> ordered_entries =
g_browser_process->profile_manager()
->GetProfileAttributesStorage()
.GetAllProfilesAttributesSortedByLocalProfilName();
if (profiles_order_.size() != number_of_profiles) {
// Should only happen the first time the function is called.
// Profile creation and deletion are handled at
// 'OnProfileAdded', 'OnProfileWasRemoved'.
DCHECK(!profiles_order_.size());
SetProfilesOrder(ordered_entries);
return ordered_entries;
}
// Vector of nullptr entries.
std::vector<ProfileAttributesEntry*> entries(number_of_profiles);
for (ProfileAttributesEntry* entry : ordered_entries) {
DCHECK(profiles_order_.find(entry->GetPath()) != profiles_order_.end());
size_t index = profiles_order_[entry->GetPath()];
DCHECK_LT(index, number_of_profiles);
DCHECK(!entries[index]);
entries[index] = entry;
}
return entries;
}
base::Value ProfilePickerHandler::GetProfilesList() {
base::ListValue profiles_list;
std::vector<ProfileAttributesEntry*> entries = GetProfileAttributes();
const int avatar_icon_size =
kProfileCardAvatarSize * web_ui()->GetDeviceScaleFactor();
for (const ProfileAttributesEntry* entry : entries) {
auto profile_entry = std::make_unique<base::DictionaryValue>();
profile_entry->SetKey("profilePath",
util::FilePathToValue(entry->GetPath()));
profile_entry->SetString("localProfileName", entry->GetLocalProfileName());
profile_entry->SetBoolPath(
"isSyncing", entry->GetSigninState() ==
SigninState::kSignedInWithConsentedPrimaryAccount);
profile_entry->SetBoolPath("needsSignin", entry->IsSigninRequired());
// GAIA name/user name can be empty, if the profile is not signed in to
// chrome.
profile_entry->SetString("gaiaName", entry->GetGAIANameToDisplay());
profile_entry->SetString("userName", entry->GetUserName());
profile_entry->SetBoolPath(
"isManaged", AccountInfo::IsManaged(entry->GetHostedDomain()));
gfx::Image icon =
profiles::GetSizedAvatarIcon(entry->GetAvatarIcon(avatar_icon_size),
true, avatar_icon_size, avatar_icon_size);
std::string icon_url = webui::GetBitmapDataUrl(icon.AsBitmap());
profile_entry->SetString("avatarIcon", icon_url);
profiles_list.Append(std::move(profile_entry));
}
return std::move(profiles_list);
}
void ProfilePickerHandler::OnProfileAdded(const base::FilePath& profile_path) {
size_t number_of_profiles = profiles_order_.size();
profiles_order_[profile_path] = number_of_profiles;
PushProfilesList();
}
void ProfilePickerHandler::OnProfileWasRemoved(
const base::FilePath& profile_path,
const base::string16& profile_name) {
DCHECK(IsJavascriptAllowed());
size_t index = profiles_order_[profile_path];
profiles_order_.erase(profile_path);
for (auto it : profiles_order_) {
if (it.second > index)
profiles_order_[it.first] = it.second - 1;
}
FireWebUIListener("profile-removed", util::FilePathToValue(profile_path));
}
void ProfilePickerHandler::OnProfileAvatarChanged(
const base::FilePath& profile_path) {
PushProfilesList();
}
void ProfilePickerHandler::OnProfileHighResAvatarLoaded(
const base::FilePath& profile_path) {
PushProfilesList();
}
void ProfilePickerHandler::OnProfileNameChanged(
const base::FilePath& profile_path,
const base::string16& old_profile_name) {
PushProfilesList();
}
void ProfilePickerHandler::OnProfileHostedDomainChanged(
const base::FilePath& profile_path) {
PushProfilesList();
}
void ProfilePickerHandler::DidFirstVisuallyNonEmptyPaint() {
DCHECK(!creation_time_on_startup_.is_null());
base::UmaHistogramTimes("ProfilePicker.StartupTime.FirstPaint",
base::TimeTicks::Now() - creation_time_on_startup_);
// Stop observing so that the histogram is only recorded once.
Observe(nullptr);
}
void ProfilePickerHandler::OnVisibilityChanged(content::Visibility visibility) {
// If the profile picker is hidden, the first paint will be delayed until the
// picker is visible again. Stop monitoring the first paint to avoid polluting
// the metrics.
if (visibility != content::Visibility::VISIBLE)
Observe(nullptr);
}