blob: 6e6de945a5b5ee289f3a0786c58a1e5f80fc05cb [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// 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/settings/people_handler.h"
#include <memory>
#include <optional>
#include <string>
#include "base/check_op.h"
#include "base/compiler_specific.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/i18n/time_formatting.h"
#include "base/json/json_reader.h"
#include "base/metrics/user_metrics.h"
#include "base/notreached.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/enterprise/util/managed_browser_utils.h"
#include "chrome/browser/lifetime/application_lifetime.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_avatar_icon_util.h"
#include "chrome/browser/profiles/profile_metrics.h"
#include "chrome/browser/signin/chrome_signin_client_factory.h"
#include "chrome/browser/signin/chrome_signin_pref_names.h"
#include "chrome/browser/signin/dice_web_signin_interceptor.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/signin/signin_error_controller_factory.h"
#include "chrome/browser/signin/signin_promo.h"
#include "chrome/browser/signin/signin_ui_util.h"
#include "chrome/browser/signin/signin_util.h"
#include "chrome/browser/sync/sync_service_factory.h"
#include "chrome/browser/sync/sync_ui_util.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/chrome_pages.h"
#include "chrome/browser/ui/signin/signin_view_controller.h"
#include "chrome/browser/ui/singleton_tabs.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/url_constants.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/grit/generated_resources.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/prefs/pref_service.h"
#include "components/signin/core/browser/signin_error_controller.h"
#include "components/signin/public/base/consent_level.h"
#include "components/signin/public/base/signin_metrics.h"
#include "components/signin/public/base/signin_pref_names.h"
#include "components/signin/public/base/signin_switches.h"
#include "components/signin/public/identity_manager/account_info.h"
#include "components/signin/public/identity_manager/accounts_mutator.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/signin/public/identity_manager/identity_utils.h"
#include "components/signin/public/identity_manager/primary_account_mutator.h"
#include "components/strings/grit/components_strings.h"
#include "components/sync/base/passphrase_enums.h"
#include "components/sync/base/user_selectable_type.h"
#include "components/sync/service/sync_service_utils.h"
#include "components/sync/service/sync_user_settings.h"
#include "components/unified_consent/unified_consent_metrics.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_delegate.h"
#include "google_apis/gaia/gaia_auth_util.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/webui/web_ui_util.h"
#include "ui/gfx/image/image.h"
#if !BUILDFLAG(IS_CHROMEOS_ASH)
#include "chrome/browser/ui/webui/profile_helper.h"
#endif
#if BUILDFLAG(ENABLE_DICE_SUPPORT) || BUILDFLAG(IS_CHROMEOS_LACROS)
#include "chrome/browser/signin/account_consistency_mode_manager.h"
#endif
#if BUILDFLAG(IS_CHROMEOS_LACROS)
#include "components/trusted_vault/features.h"
#endif
using content::WebContents;
using l10n_util::GetStringFUTF16;
using l10n_util::GetStringUTF16;
using signin::ConsentLevel;
namespace {
const char kTrustedVaultBannerStateChangedEvent[] =
"trusted-vault-banner-state-changed";
// WARNING: Keep synced with
// chrome/browser/resources/settings/people_page/sync_browser_proxy.ts.
enum class TrustedVaultBannerState {
kNotShown = 0,
kOfferOptIn = 1,
kOptedIn = 2,
};
// A structure which contains all the configuration information for sync.
struct SyncConfigInfo {
SyncConfigInfo();
~SyncConfigInfo();
bool sync_everything;
syncer::UserSelectableTypeSet selected_types;
};
bool IsSyncSubpage(const GURL& current_url) {
return current_url == chrome::GetSettingsUrl(chrome::kSyncSetupSubPage);
}
SyncConfigInfo::SyncConfigInfo() : sync_everything(false) {}
SyncConfigInfo::~SyncConfigInfo() {}
bool GetConfiguration(const std::string& json, SyncConfigInfo* config) {
std::optional<base::Value> parsed_value = base::JSONReader::Read(json);
if (!parsed_value.has_value() || !parsed_value->is_dict()) {
DLOG(ERROR) << "GetConfiguration() not passed a Dictionary";
return false;
}
const base::Value::Dict& root = parsed_value->GetDict();
std::optional<bool> sync_everything = root.FindBool("syncAllDataTypes");
if (!sync_everything.has_value()) {
DLOG(ERROR) << "GetConfiguration() not passed a syncAllDataTypes value";
return false;
}
config->sync_everything = *sync_everything;
for (syncer::UserSelectableType type : syncer::UserSelectableTypeSet::All()) {
std::string key_name =
syncer::GetUserSelectableTypeName(type) + std::string("Synced");
std::optional<bool> type_synced = root.FindBool(key_name);
if (!type_synced.has_value()) {
DLOG(ERROR) << "GetConfiguration() not passed a value for " << key_name;
return false;
}
if (*type_synced) {
config->selected_types.Put(type);
}
}
return true;
}
// Guaranteed to return a valid result (or crash).
void ParseConfigurationArguments(const base::Value::List& args,
SyncConfigInfo* config,
const base::Value** callback_id) {
const std::string& json = args[1].GetString();
if ((*callback_id = &args[0]) && !json.empty())
CHECK(GetConfiguration(json, config));
else
NOTREACHED();
}
std::string GetSyncErrorAction(SyncStatusActionType action_type) {
switch (action_type) {
case SyncStatusActionType::kReauthenticate:
return "reauthenticate";
case SyncStatusActionType::kUpgradeClient:
return "upgradeClient";
case SyncStatusActionType::kEnterPassphrase:
return "enterPassphrase";
case SyncStatusActionType::kRetrieveTrustedVaultKeys:
return "retrieveTrustedVaultKeys";
case SyncStatusActionType::kConfirmSyncSettings:
return "confirmSyncSettings";
case SyncStatusActionType::kNoAction:
return "noAction";
}
NOTREACHED();
return std::string();
}
// Returns the base::Value associated with the account, to use in the stored
// accounts list.
base::Value::Dict GetAccountValue(signin::IdentityManager* identity_manager,
const AccountInfo& account) {
DCHECK(!account.IsEmpty());
auto dict =
base::Value::Dict()
.Set("email", account.email)
.Set("fullName", account.full_name)
.Set("givenName", account.given_name)
.Set("isPrimaryAccount",
account.account_id ==
identity_manager
->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin)
.account_id);
if (!account.account_image.IsEmpty()) {
dict.Set("avatarImage",
webui::GetBitmapDataUrl(account.account_image.AsBitmap()));
}
return dict;
}
#if !BUILDFLAG(IS_CHROMEOS_ASH)
bool IsChangePrimaryAccountAllowed(Profile* profile, const std::string& email) {
signin::IdentityManager* identity_manager =
IdentityManagerFactory::GetForProfile(profile);
if (ChromeSigninClientFactory::GetForProfile(profile)
->IsClearPrimaryAccountAllowed(
identity_manager->HasPrimaryAccount(ConsentLevel::kSync)) ||
!identity_manager->HasPrimaryAccount(ConsentLevel::kSignin)) {
return true;
}
return gaia::AreEmailsSame(
email,
identity_manager->GetPrimaryAccountInfo(ConsentLevel::kSignin).email);
}
#endif // !BUILDFLAG(IS_CHROMEOS_ASH)
} // namespace
namespace settings {
// static
const char PeopleHandler::kConfigurePageStatus[] = "configure";
const char PeopleHandler::kDonePageStatus[] = "done";
const char PeopleHandler::kPassphraseFailedPageStatus[] = "passphraseFailed";
// TODO(crbug.com/40258836): Delete parts needed only by PasswordManager once
// kPasswordManagerRedesign is launched.
PeopleHandler::PeopleHandler(Profile* profile)
: profile_(profile), configuring_sync_(false) {}
PeopleHandler::~PeopleHandler() {
// Early exit if running unit tests (no actual WebUI is attached).
if (!web_ui())
return;
// Remove this class as an observer to prevent calls back into this class
// while destroying.
OnJavascriptDisallowed();
// If unified consent is enabled and the user left the sync page by closing
// the tab, refresh, or via the back navigation, the sync setup needs to be
// closed. If this was the first time setup, sync will be cancelled.
// Note, if unified consent is disabled, it will first go through
// |OnDidClosePage()|.
CloseSyncSetup();
}
void PeopleHandler::RegisterMessages() {
InitializeSyncBlocker();
web_ui()->RegisterMessageCallback(
"SyncSetupDidClosePage",
base::BindRepeating(&PeopleHandler::OnDidClosePage,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"SyncSetupSetDatatypes",
base::BindRepeating(&PeopleHandler::HandleSetDatatypes,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"SyncSetupSetEncryptionPassphrase",
base::BindRepeating(&PeopleHandler::HandleSetEncryptionPassphrase,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"SyncSetupSetDecryptionPassphrase",
base::BindRepeating(&PeopleHandler::HandleSetDecryptionPassphrase,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"SyncSetupShowSetupUI",
base::BindRepeating(&PeopleHandler::HandleShowSyncSetupUI,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"SyncSetupGetSyncStatus",
base::BindRepeating(&PeopleHandler::HandleGetSyncStatus,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"SyncPrefsDispatch",
base::BindRepeating(&PeopleHandler::HandleSyncPrefsDispatch,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"SyncTrustedVaultBannerStateDispatch",
base::BindRepeating(&PeopleHandler::HandleTrustedVaultBannerStateDispatch,
base::Unretained(this)));
#if BUILDFLAG(IS_CHROMEOS_ASH)
web_ui()->RegisterMessageCallback(
"AttemptUserExit",
base::BindRepeating(&PeopleHandler::HandleAttemptUserExit,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"TurnOnSync", base::BindRepeating(&PeopleHandler::HandleTurnOnSync,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"TurnOffSync", base::BindRepeating(&PeopleHandler::HandleTurnOffSync,
base::Unretained(this)));
#else
web_ui()->RegisterMessageCallback(
"SyncSetupStartSignIn",
base::BindRepeating(&PeopleHandler::HandleStartSignin,
base::Unretained(this)));
#endif
#if BUILDFLAG(ENABLE_DICE_SUPPORT) || BUILDFLAG(IS_CHROMEOS_LACROS)
web_ui()->RegisterMessageCallback(
"SyncSetupSignout", base::BindRepeating(&PeopleHandler::HandleSignout,
base::Unretained(this)));
#endif
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
web_ui()->RegisterMessageCallback(
"SyncSetupPauseSync", base::BindRepeating(&PeopleHandler::HandlePauseSync,
base::Unretained(this)));
#endif
web_ui()->RegisterMessageCallback(
"SyncSetupGetStoredAccounts",
base::BindRepeating(&PeopleHandler::HandleGetStoredAccounts,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"SyncSetupStartSyncingWithEmail",
base::BindRepeating(&PeopleHandler::HandleStartSyncingWithEmail,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"SyncStartKeyRetrieval",
base::BindRepeating(&PeopleHandler::HandleStartKeyRetrieval,
base::Unretained(this)));
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
web_ui()->RegisterMessageCallback(
"GetChromeSigninUserChoiceInfo",
base::BindRepeating(&PeopleHandler::HandleGetChromeSigninUserChoiceInfo,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"SetChromeSigninUserChoice",
base::BindRepeating(&PeopleHandler::HandleSetChromeSigninUserChoice,
base::Unretained(this)));
#endif
}
void PeopleHandler::OnJavascriptAllowed() {
PrefService* prefs = profile_->GetPrefs();
profile_pref_registrar_ = std::make_unique<PrefChangeRegistrar>();
profile_pref_registrar_->Init(prefs);
profile_pref_registrar_->Add(
prefs::kSigninAllowed,
base::BindRepeating(&PeopleHandler::UpdateSyncStatus,
base::Unretained(this)));
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
profile_pref_registrar_->Add(
prefs::kChromeSigninInterceptionUserChoice,
base::BindRepeating(&PeopleHandler::UpdateChromeSigninUserChoiceInfo,
base::Unretained(this)));
#endif
signin::IdentityManager* identity_manager(
IdentityManagerFactory::GetInstance()->GetForProfile(profile_));
if (identity_manager)
identity_manager_observation_.Observe(identity_manager);
// This is intentionally not using GetSyncService(), to go around the
// Profile::IsSyncAllowed() check.
syncer::SyncService* sync_service =
SyncServiceFactory::GetForProfile(profile_);
if (sync_service)
sync_service_observation_.Observe(sync_service);
}
void PeopleHandler::OnJavascriptDisallowed() {
profile_pref_registrar_.reset();
identity_manager_observation_.Reset();
sync_service_observation_.Reset();
}
#if !BUILDFLAG(IS_CHROMEOS_ASH)
void PeopleHandler::DisplayGaiaLogin(signin_metrics::AccessPoint access_point) {
// Advanced options are no longer being configured if the login screen is
// visible. If the user exits the signin wizard after this without
// configuring sync, CloseSyncSetup() will ensure they are logged out.
configuring_sync_ = false;
DisplayGaiaLoginInNewTabOrWindow(access_point);
}
void PeopleHandler::DisplayGaiaLoginInNewTabOrWindow(
signin_metrics::AccessPoint access_point) {
auto* identity_manager = IdentityManagerFactory::GetForProfile(profile_);
syncer::SyncService* service = GetSyncService();
if (service && service->HasUnrecoverableError() &&
identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync)) {
// When the user has an unrecoverable error, they first have to sign out and
// then sign in again.
identity_manager->GetPrimaryAccountMutator()->RevokeSyncConsent(
signin_metrics::ProfileSignout::kRevokeSyncFromSettings);
}
// If the identity manager already has a primary account, this is a
// re-auth scenario, and we need to ensure that the user signs in with the
// same email address.
if (identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync)) {
SigninErrorController* error_controller =
SigninErrorControllerFactory::GetForProfile(profile_);
DCHECK(error_controller->HasError());
signin_ui_util::ShowReauthForPrimaryAccountWithAuthError(profile_,
access_point);
} else {
signin_ui_util::EnableSyncFromSingleAccountPromo(
profile_, CoreAccountInfo(), access_point);
}
}
#endif // !BUILDFLAG(IS_CHROMEOS_ASH)
void PeopleHandler::OnDidClosePage(const base::Value::List& args) {
// Don't mark setup as complete if "didAbort" is true, or if authentication
// is still needed.
if (!args[0].GetBool() && !IsProfileAuthNeededOrHasErrors()) {
MarkFirstSetupComplete();
}
CloseSyncSetup();
}
syncer::SyncService* PeopleHandler::GetSyncService() const {
return SyncServiceFactory::IsSyncAllowed(profile_)
? SyncServiceFactory::GetForProfile(profile_)
: nullptr;
}
void PeopleHandler::HandleSetDatatypes(const base::Value::List& args) {
SyncConfigInfo configuration;
const base::Value* callback_id = nullptr;
ParseConfigurationArguments(args, &configuration, &callback_id);
// Start configuring the SyncService using the configuration passed to us from
// the JS layer.
syncer::SyncService* service = GetSyncService();
// If the sync engine has shutdown for some reason, just close the sync
// dialog.
if (!service || !service->IsEngineInitialized()) {
CloseSyncSetup();
ResolveJavascriptCallback(*callback_id, base::Value(kDonePageStatus));
return;
}
// Don't enable non-registered types (for example, kApps may not be registered
// on Chrome OS).
configuration.selected_types.RetainAll(
service->GetUserSettings()->GetRegisteredSelectableTypes());
service->GetUserSettings()->SetSelectedTypes(configuration.sync_everything,
configuration.selected_types);
// Choosing data types to sync never fails.
ResolveJavascriptCallback(*callback_id, base::Value(kConfigurePageStatus));
}
void PeopleHandler::HandleGetStoredAccounts(const base::Value::List& args) {
AllowJavascript();
CHECK_EQ(1U, args.size());
const base::Value& callback_id = args[0];
ResolveJavascriptCallback(callback_id, GetStoredAccountsList());
}
void PeopleHandler::OnExtendedAccountInfoUpdated(const AccountInfo& info) {
FireWebUIListener("stored-accounts-updated", GetStoredAccountsList());
}
void PeopleHandler::OnExtendedAccountInfoRemoved(const AccountInfo& info) {
FireWebUIListener("stored-accounts-updated", GetStoredAccountsList());
}
void PeopleHandler::OnRefreshTokenUpdatedForAccount(
const CoreAccountInfo& account_info) {
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
UpdateChromeSigninUserChoiceInfo();
#endif
}
void PeopleHandler::OnAccountsInCookieUpdated(
const signin::AccountsInCookieJarInfo& accounts_in_cookie_jar_info,
const GoogleServiceAuthError& error) {
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
UpdateChromeSigninUserChoiceInfo();
#endif
}
base::Value::List PeopleHandler::GetStoredAccountsList() {
base::Value::List accounts;
bool populate_accounts_list = false;
signin::IdentityManager* identity_manager =
IdentityManagerFactory::GetForProfile(profile_);
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
populate_accounts_list =
AccountConsistencyModeManager::IsDiceEnabledForProfile(profile_);
#elif BUILDFLAG(IS_CHROMEOS_LACROS)
populate_accounts_list = !profile_->IsMainProfile();
#endif
if (populate_accounts_list) {
// If dice is enabled, show all the accounts.
for (const auto& account : signin_ui_util::GetOrderedAccountsForDisplay(
identity_manager,
/*restrict_to_accounts_eligible_for_sync=*/true)) {
accounts.Append(GetAccountValue(identity_manager, account));
}
return accounts;
}
// Guest mode does not have a primary account (or an IdentityManager).
if (profile_->IsGuestSession())
return base::Value::List();
// If DICE is disabled for this profile or unsupported on this platform (e.g.
// Chrome OS) or Lacros main profile (sync with a different account than the
// device account is not allowed), then show only the primary account,
// whether or not that account has consented to sync.
AccountInfo primary_account_info = identity_manager->FindExtendedAccountInfo(
identity_manager->GetPrimaryAccountInfo(ConsentLevel::kSignin));
if (!primary_account_info.IsEmpty())
accounts.Append(GetAccountValue(identity_manager, primary_account_info));
return accounts;
}
void PeopleHandler::HandleStartSyncingWithEmail(const base::Value::List& args) {
#if BUILDFLAG(ENABLE_DICE_SUPPORT) || BUILDFLAG(IS_CHROMEOS_LACROS)
DCHECK(AccountConsistencyModeManager::IsDiceEnabledForProfile(profile_) ||
AccountConsistencyModeManager::IsMirrorEnabledForProfile(profile_));
const base::Value& email = args[0];
const base::Value& is_default_promo_account = args[1];
DCHECK(IsChangePrimaryAccountAllowed(profile_, email.GetString()))
<< "Changing the primary account is not allowed!";
AccountInfo maybe_account =
IdentityManagerFactory::GetForProfile(profile_)
->FindExtendedAccountInfoByEmailAddress(email.GetString());
signin_ui_util::EnableSyncFromMultiAccountPromo(
profile_, maybe_account,
signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS,
is_default_promo_account.GetBool());
#else
NOTIMPLEMENTED();
#endif
}
void PeopleHandler::HandleSetEncryptionPassphrase(
const base::Value::List& args) {
const base::Value& callback_id = args[0];
// Check the SyncService is up and running before retrieving SyncUserSettings,
// which contains the encryption-related APIs.
if (!GetSyncService() || !GetSyncService()->IsEngineInitialized()) {
// TODO(crbug.com/1139060): HandleSetDatatypes() also returns a success
// status in this case. Consider returning a failure in both methods. Maybe
// the CloseSyncSetup() call can also be removed.
CloseSyncSetup();
ResolveJavascriptCallback(callback_id, base::Value(true));
return;
}
syncer::SyncUserSettings* sync_user_settings =
GetSyncService()->GetUserSettings();
const std::string& passphrase = args[1].GetString();
bool successfully_set = false;
if (passphrase.empty()) {
successfully_set = false;
} else if (!sync_user_settings->IsCustomPassphraseAllowed()) {
successfully_set = false;
} else if (sync_user_settings->IsUsingExplicitPassphrase()) {
// In case a passphrase is already being used, changing to a new one isn't
// currently supported (one must reset all the Sync data).
successfully_set = false;
} else if (sync_user_settings->IsPassphraseRequired() ||
sync_user_settings->IsTrustedVaultKeyRequired()) {
// Can't re-encrypt the data with |passphrase| if some of it hasn't even
// been decrypted yet due to a pending passphrase / trusted vault key.
successfully_set = false;
} else {
sync_user_settings->SetEncryptionPassphrase(passphrase);
successfully_set = true;
}
ResolveJavascriptCallback(callback_id, base::Value(successfully_set));
}
void PeopleHandler::HandleSetDecryptionPassphrase(
const base::Value::List& args) {
const base::Value& callback_id = args[0];
// Check the SyncService is up and running before retrieving SyncUserSettings,
// which contains the encryption-related APIs.
if (!GetSyncService() || !GetSyncService()->IsEngineInitialized()) {
// TODO(crbug.com/1139060): HandleSetDatatypes() also returns a success
// status in this case. Consider returning a failure in both methods. Maybe
// the CloseSyncSetup() call can also be removed.
CloseSyncSetup();
ResolveJavascriptCallback(callback_id, base::Value(true));
return;
}
syncer::SyncUserSettings* sync_user_settings =
GetSyncService()->GetUserSettings();
const std::string& passphrase = args[1].GetString();
bool successfully_set = false;
if (!passphrase.empty() && sync_user_settings->IsPassphraseRequired()) {
successfully_set = sync_user_settings->SetDecryptionPassphrase(passphrase);
}
ResolveJavascriptCallback(callback_id, base::Value(successfully_set));
}
void PeopleHandler::HandleShowSyncSetupUI(const base::Value::List& args) {
AllowJavascript();
syncer::SyncService* service = GetSyncService();
if (service && !sync_blocker_)
sync_blocker_ = service->GetSetupInProgressHandle();
// Mark Sync as requested by the user. It might already be requested, but
// it's not if this is either the first time the user is setting up Sync, or
// Sync was set up but then was reset via the dashboard. This also pokes the
// SyncService to start up immediately, i.e. bypass deferred startup.
if (service)
service->SetSyncFeatureRequested();
GetLoginUIService()->SetLoginUI(this);
// Observe the web contents for a before unload event.
Observe(web_ui()->GetWebContents());
MaybeMarkSyncConfiguring();
PushSyncPrefs();
// Focus the web contents in case the location bar was focused before. This
// makes sure that page elements for resolving sync errors can be focused.
web_ui()->GetWebContents()->Focus();
}
#if BUILDFLAG(IS_CHROMEOS_ASH)
// On ChromeOS, we need to sign out the user session to fix an auth error, so
// the user goes through the real signin flow to generate a new auth token.
void PeopleHandler::HandleAttemptUserExit(const base::Value::List& args) {
DVLOG(1) << "Signing out the user to fix a sync error.";
chrome::AttemptUserExit();
}
void PeopleHandler::HandleTurnOnSync(const base::Value::List& args) {
NOTREACHED() << "It is not possible to toggle Sync on Ash";
}
void PeopleHandler::HandleTurnOffSync(const base::Value::List& args) {
NOTREACHED() << "It is not possible to toggle Sync on Ash";
}
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
#if !BUILDFLAG(IS_CHROMEOS_ASH)
void PeopleHandler::HandleStartSignin(const base::Value::List& args) {
AllowJavascript();
// Should only be called if the user is not already signed in, has a auth
// error, or a unrecoverable sync error requiring re-auth.
syncer::SyncService* service = GetSyncService();
DCHECK(IsProfileAuthNeededOrHasErrors() ||
(service && service->HasUnrecoverableError()));
DCHECK(IsChangePrimaryAccountAllowed(profile_, /*email=*/std::string()))
<< "Primary account already set and change is not allowed";
DisplayGaiaLogin(signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS);
}
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
#if BUILDFLAG(ENABLE_DICE_SUPPORT) || BUILDFLAG(IS_CHROMEOS_LACROS)
void PeopleHandler::HandleSignout(const base::Value::List& args) {
bool delete_profile = false;
if (args[0].is_bool()) {
delete_profile = args[0].GetBool();
}
auto* identity_manager = IdentityManagerFactory::GetForProfile(profile_);
bool is_syncing =
identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync);
DCHECK(is_syncing || !delete_profile)
<< "Deleting the profile should only be offered if the user is "
"syncing.";
bool is_clear_primary_account_allowed =
ChromeSigninClientFactory::GetForProfile(profile_)
->IsClearPrimaryAccountAllowed(is_syncing);
if (is_syncing) {
HandleTurnOffSync(delete_profile, is_clear_primary_account_allowed);
return;
}
if (!is_clear_primary_account_allowed) {
// 'Signout' should not be offered in the UI if clear primary account is
// not allowed.
NOTREACHED()
<< "Signout should not be offered if clear primary account is not "
"allowed.";
return;
}
#if BUILDFLAG(IS_CHROMEOS_LACROS)
identity_manager->GetPrimaryAccountMutator()->ClearPrimaryAccount(
signin_metrics::ProfileSignout::kUserClickedSignoutSettings);
#else
Browser* browser = chrome::FindBrowserWithTab(web_ui()->GetWebContents());
if (!browser) {
return;
}
browser->signin_view_controller()->SignoutOrReauthWithPrompt(
signin_metrics::AccessPoint::
ACCESS_POINT_SETTINGS_SIGNOUT_CONFIRMATION_PROMPT,
signin_metrics::ProfileSignout::kUserClickedSignoutSettings,
signin_metrics::SourceForRefreshTokenOperation::kSettings_Signout);
#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
}
void PeopleHandler::HandleTurnOffSync(bool delete_profile,
bool is_clear_primary_account_allowed) {
base::FilePath profile_path = profile_->GetPath();
bool delete_profile_allowed = signin_util::IsProfileDeletionAllowed(profile_);
DCHECK(!delete_profile || delete_profile_allowed)
<< "Profile deletion is not allowed!";
auto* identity_manager = IdentityManagerFactory::GetForProfile(profile_);
auto* signin_client = ChromeSigninClientFactory::GetForProfile(profile_);
if (!signin_client->IsRevokeSyncConsentAllowed()) {
// If the user can't revoke sync the profile must be destroyed.
if (delete_profile && delete_profile_allowed) {
webui::DeleteProfileAtPath(profile_path,
ProfileMetrics::DELETE_PROFILE_SETTINGS);
} else {
DCHECK(delete_profile) << "User signout requires profile destruction.";
}
return;
}
if (!is_clear_primary_account_allowed) {
DCHECK(signin_client->IsRevokeSyncConsentAllowed());
identity_manager->GetPrimaryAccountMutator()->RevokeSyncConsent(
signin_metrics::ProfileSignout::kRevokeSyncFromSettings);
} else {
#if BUILDFLAG(IS_CHROMEOS_LACROS)
identity_manager->GetPrimaryAccountMutator()->ClearPrimaryAccount(
signin_metrics::ProfileSignout::kUserClickedSignoutSettings);
#else
Browser* browser = chrome::FindBrowserWithTab(web_ui()->GetWebContents());
if (browser) {
// Clearing the primary account isn't sufficient to signout SAML accounts,
// see http://crbug.com/1114646.
browser->signin_view_controller()->ShowGaiaLogoutTab(
signin_metrics::SourceForRefreshTokenOperation::kSettings_Signout);
}
if (switches::IsExplicitBrowserSigninUIOnDesktopEnabled(
switches::ExplicitBrowserSigninPhase::kFull)) {
// In Uno, Gaia logout tab invalidating the account will lead to a sign in
// paused state. Unset the primary account to ensure it is removed from
// chrome. The `AccountReconcilor` will revoke refresh tokens for accounts
// not in the Gaia cookie on next reconciliation.
identity_manager->GetPrimaryAccountMutator()
->RemovePrimaryAccountButKeepTokens(
signin_metrics::ProfileSignout::kUserClickedSignoutSettings);
} else {
// Only revoke the sync consent.
// * If the primary account is still valid, then it will be removed by
// the Gaia logout tab (see http://crbug.com/1068978).
// * If the account is already invalid, drop the token now because it's
// already invalid on the web, so the Gaia logout tab won't affect it
// (see http://crbug.com/1114646).
//
// This operation may delete the current browser that owns |this| if force
// signin is enabled (see https://crbug.com/1153120).
identity_manager->GetPrimaryAccountMutator()->RevokeSyncConsent(
signin_metrics::ProfileSignout::kRevokeSyncFromSettings);
}
#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
}
// CAUTION: |this| may be deleted at this point.
if (delete_profile && delete_profile_allowed) {
webui::DeleteProfileAtPath(profile_path,
ProfileMetrics::DELETE_PROFILE_SETTINGS);
}
}
#endif // BUILDFLAG(ENABLE_DICE_SUPPORT) || BUILDFLAG(IS_CHROMEOS_LACROS)
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
void PeopleHandler::HandlePauseSync(const base::Value::List& args) {
DCHECK(AccountConsistencyModeManager::IsDiceEnabledForProfile(profile_));
auto* identity_manager = IdentityManagerFactory::GetForProfile(profile_);
DCHECK(identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync));
identity_manager->GetAccountsMutator()
->InvalidateRefreshTokenForPrimaryAccount(
signin_metrics::SourceForRefreshTokenOperation::kSettings_PauseSync);
}
#endif // BUILDFLAG(ENABLE_DICE_SUPPORT)
void PeopleHandler::HandleStartKeyRetrieval(const base::Value::List& args) {
#if BUILDFLAG(IS_CHROMEOS_LACROS)
if (base::FeatureList::IsEnabled(
trusted_vault::kChromeOSTrustedVaultUseWebUIDialog)) {
OpenDialogForSyncKeyRetrieval(
profile_, syncer::TrustedVaultUserActionTriggerForUMA::kProfileMenu);
return;
}
#endif
Browser* browser = chrome::FindBrowserWithTab(web_ui()->GetWebContents());
if (!browser)
return;
OpenTabForSyncKeyRetrieval(
browser, syncer::TrustedVaultUserActionTriggerForUMA::kSettings);
}
void PeopleHandler::HandleGetSyncStatus(const base::Value::List& args) {
AllowJavascript();
CHECK_EQ(1U, args.size());
const base::Value& callback_id = args[0];
ResolveJavascriptCallback(callback_id, GetSyncStatusDictionary());
}
void PeopleHandler::HandleSyncPrefsDispatch(const base::Value::List& args) {
AllowJavascript();
PushSyncPrefs();
}
void PeopleHandler::HandleTrustedVaultBannerStateDispatch(
const base::Value::List& args) {
AllowJavascript();
PushTrustedVaultBannerState();
}
void PeopleHandler::CloseSyncSetup() {
// Stop a timer to handle timeout in waiting for checking network connection.
engine_start_timer_.reset();
// LoginUIService can be nullptr if page is brought up in incognito mode
// (i.e. if the user is running in guest mode in cros and brings up settings).
LoginUIService* service = GetLoginUIService();
if (service) {
auto self_weak_ptr = weak_factory_.GetWeakPtr();
// ChromeOS Ash doesn't support signing out and hence the code below
// cannot build (RevokeSyncConsent() doesn't exist). However, the code is
// unreachable on Ash because IsInitialSyncFeatureSetupComplete() in the
// condition below always returns true.
#if !BUILDFLAG(IS_CHROMEOS_ASH)
syncer::SyncService* sync_service = GetSyncService();
// Don't log a cancel event if the sync setup dialog is being
// automatically closed due to an auth error.
if (service->current_login_ui() == this && sync_service &&
configuring_sync_ &&
!sync_service->GetUserSettings()->IsInitialSyncFeatureSetupComplete() &&
sync_service->GetAuthError().state() == GoogleServiceAuthError::NONE) {
DVLOG(1) << "Sync setup aborted by user action";
// Revoke sync consent on desktop Chrome if they click cancel during
// initial setup or close sync setup without confirming sync.
IdentityManagerFactory::GetForProfile(profile_)
->GetPrimaryAccountMutator()
->RevokeSyncConsent(signin_metrics::ProfileSignout::kAbortSignin);
}
#endif // !BUILDFLAG(IS_CHROMEOS_ASH)
service->LoginUIClosed(this);
// The call to RevokeSyncConsent() above may delete the current browser that
// owns `this` if force signin is enabled. Accessing instance members caused
// crashes (see https://crbug.com/1441820) which we guard against by
// checking a weak pointer to the current instance.
if (!self_weak_ptr) {
return;
}
}
// Alert the sync service anytime the sync setup dialog is closed. This can
// happen due to the user clicking the OK or Cancel button, or due to the
// dialog being closed by virtue of sync being disabled in the background.
sync_blocker_.reset();
configuring_sync_ = false;
// Stop observing the web contents.
Observe(nullptr);
}
void PeopleHandler::InitializeSyncBlocker() {
DCHECK(web_ui());
WebContents* web_contents = web_ui()->GetWebContents();
if (!web_contents)
return;
syncer::SyncService* service = GetSyncService();
if (!service)
return;
// The user opened settings directly to the syncSetup sub-page, because they
// clicked "Settings" in the browser sync consent dialog or because they
// clicked "Review sync options" in the Chrome OS out-of-box experience.
// Don't start syncing until they finish setup.
if (IsSyncSubpage(web_contents->GetVisibleURL())) {
sync_blocker_ = service->GetSetupInProgressHandle();
}
}
void PeopleHandler::FocusUI() {
WebContents* web_contents = web_ui()->GetWebContents();
web_contents->GetDelegate()->ActivateContents(web_contents);
}
void PeopleHandler::OnPrimaryAccountChanged(
const signin::PrimaryAccountChangeEvent& event) {
switch (event.GetEventTypeFor(signin::ConsentLevel::kSync)) {
case signin::PrimaryAccountChangeEvent::Type::kSet: {
// After a primary account was set, the Sync setup will start soon. Grab a
// SetupInProgressHandle right now to avoid a temporary "missing Sync
// confirmation" error in the avatar menu. See crbug.com/928696.
syncer::SyncService* service = GetSyncService();
if (service && !sync_blocker_)
sync_blocker_ = service->GetSetupInProgressHandle();
UpdateSyncStatus();
break;
}
case signin::PrimaryAccountChangeEvent::Type::kCleared:
sync_blocker_.reset();
configuring_sync_ = false;
UpdateSyncStatus();
break;
case signin::PrimaryAccountChangeEvent::Type::kNone:
break;
}
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
switch (event.GetEventTypeFor(signin::ConsentLevel::kSignin)) {
case signin::PrimaryAccountChangeEvent::Type::kSet:
case signin::PrimaryAccountChangeEvent::Type::kCleared:
UpdateChromeSigninUserChoiceInfo();
break;
case signin::PrimaryAccountChangeEvent::Type::kNone:
break;
}
#endif
}
void PeopleHandler::OnStateChanged(syncer::SyncService* sync_service) {
UpdateSyncStatus();
// TODO(crbug.com/40140566): Re-evaluate marking sync as configuring here,
// since this gets called whenever SyncService changes state. Inline
// MaybeMarkSyncConfiguring() then.
MaybeMarkSyncConfiguring();
PushSyncPrefs();
PushTrustedVaultBannerState();
}
void PeopleHandler::BeforeUnloadDialogCancelled() {
// The before unload dialog is only shown during the first sync setup.
DCHECK(IdentityManagerFactory::GetForProfile(profile_)->HasPrimaryAccount(
signin::ConsentLevel::kSync));
syncer::SyncService* service = GetSyncService();
DCHECK(service && service->IsSetupInProgress() &&
!service->GetUserSettings()->IsInitialSyncFeatureSetupComplete());
base::RecordAction(
base::UserMetricsAction("Signin_Signin_CancelAbortAdvancedSyncSettings"));
}
base::Value::Dict PeopleHandler::GetSyncStatusDictionary() const {
base::Value::Dict sync_status;
if (profile_->IsGuestSession()) {
// Cannot display signin status when running in guest mode on chromeos
// because there is no IdentityManager.
return sync_status;
}
sync_status.Set("supervisedUser", profile_->IsChild());
auto* identity_manager = IdentityManagerFactory::GetForProfile(profile_);
DCHECK(identity_manager);
if (identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSignin)) {
CoreAccountInfo primary_account_info =
identity_manager->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin);
// If there is no one logged in or if the profile name is empty then the
// domain name is empty. This happens in browser tests.
if (chrome::enterprise_util::UserAcceptedAccountManagement(profile_) &&
!primary_account_info.email.empty()) {
sync_status.Set("domain",
gaia::ExtractDomainName(primary_account_info.email));
}
}
// This is intentionally not using GetSyncService(), in order to access more
// nuanced information, since GetSyncService() returns nullptr if anything
// makes Profile::IsSyncAllowed() false.
syncer::SyncService* service = SyncServiceFactory::GetForProfile(profile_);
bool disallowed_by_policy =
service && service->HasDisableReason(
syncer::SyncService::DISABLE_REASON_ENTERPRISE_POLICY);
sync_status.Set("syncSystemEnabled", (service != nullptr));
sync_status.Set(
"firstSetupInProgress",
service && !disallowed_by_policy && service->IsSetupInProgress() &&
!service->GetUserSettings()->IsInitialSyncFeatureSetupComplete() &&
identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync));
const SyncStatusLabels status_labels = GetSyncStatusLabels(profile_);
// TODO(crbug.com/40660240): Consider unifying some of the fields below to
// avoid redundancy.
sync_status.Set("statusText",
GetStringUTF16(status_labels.status_label_string_id));
sync_status.Set("statusActionText",
GetStringUTF16(status_labels.button_string_id));
sync_status.Set(
"hasError",
status_labels.message_type == SyncStatusMessageType::kSyncError ||
status_labels.message_type ==
SyncStatusMessageType::kPasswordsOnlySyncError);
sync_status.Set("hasPasswordsOnlyError",
status_labels.message_type ==
SyncStatusMessageType::kPasswordsOnlySyncError);
sync_status.Set("statusAction",
GetSyncErrorAction(status_labels.action_type));
sync_status.Set("managed", disallowed_by_policy);
// TODO(crbug.com/40745012): audit js usages of |disabled| and |signedIn|
// fields, update it to use the right field, comments around and conditions
// here. Perhaps removal of one of these to fields is possible.
sync_status.Set("disabled", !service || disallowed_by_policy);
// NOTE: This means signed-in for *sync*. It can be false when the user is
// signed-in to the content area or to the browser.
sync_status.Set("signedIn", identity_manager->HasPrimaryAccount(
signin::ConsentLevel::kSync));
sync_status.Set("signedInUsername",
signin_ui_util::GetAuthenticatedUsername(profile_));
sync_status.Set("hasUnrecoverableError",
service && service->HasUnrecoverableError());
return sync_status;
}
void PeopleHandler::PushSyncPrefs() {
syncer::SyncService* service = GetSyncService();
// The sync service may be nullptr if it has been just disabled by policy.
if (!service || !service->IsEngineInitialized()) {
return;
}
// Setup values for the JSON response:
// syncAllDataTypes: true if the user wants to sync everything
// <data_type>Registered: true if the associated data type is supported
// <data_type>Synced: true if the user wants to sync that specific data type
// customPassphraseAllowed: true if sync allows setting a custom passphrase
// to encrypt data.
// encryptAllData: true if user wants to encrypt all data (not just
// passwords)
// passphraseRequired: true if a passphrase is needed to start sync
// trustedVaultKeysRequired: true if trusted vault keys are needed to start
// sync.
// explicitPassphraseTime: the stringified time when the current explicit
// passphrase was set (in milliseconds since the Unix
// epoch); undefined if the time is unknown or no explicit
// passphrase is set.
//
base::Value::Dict args;
syncer::SyncUserSettings* sync_user_settings = service->GetUserSettings();
// Tell the UI layer which data types are registered/enabled by the user.
const syncer::UserSelectableTypeSet registered_types =
sync_user_settings->GetRegisteredSelectableTypes();
const syncer::UserSelectableTypeSet selected_types =
sync_user_settings->GetSelectedTypes();
for (syncer::UserSelectableType type : syncer::UserSelectableTypeSet::All()) {
const std::string type_name = syncer::GetUserSelectableTypeName(type);
args.Set(type_name + "Registered", registered_types.Has(type));
args.Set(type_name + "Synced", selected_types.Has(type));
args.Set(type_name + "Managed",
sync_user_settings->IsTypeManagedByPolicy(type));
}
args.Set("syncAllDataTypes", sync_user_settings->IsSyncEverythingEnabled());
args.Set("encryptAllData", sync_user_settings->IsEncryptEverythingEnabled());
args.Set("customPassphraseAllowed",
sync_user_settings->IsCustomPassphraseAllowed());
// We call IsPassphraseRequired() here, instead of calling
// IsPassphraseRequiredForPreferredDataTypes(), because we want to show the
// passphrase UI even if no encrypted data types are enabled.
// IsInitialSyncFeatureSetupComplete()==false is special-cased to avoid that
// the user enters the custom passphrase before confirming they want to
// complete the sync setup flow.
args.Set("passphraseRequired",
sync_user_settings->IsPassphraseRequired() &&
sync_user_settings->IsInitialSyncFeatureSetupComplete());
// Same as above, we call IsTrustedVaultKeyRequired() here instead of.
// IsTrustedVaultKeyRequiredForPreferredDataTypes().
args.Set("trustedVaultKeysRequired",
sync_user_settings->IsTrustedVaultKeyRequired());
base::Time passphrase_time = sync_user_settings->GetExplicitPassphraseTime();
if (!passphrase_time.is_null()) {
args.Set("explicitPassphraseTime",
base::TimeFormatShortDate(passphrase_time));
}
FireWebUIListener("sync-prefs-changed", args);
}
void PeopleHandler::PushTrustedVaultBannerState() {
syncer::SyncService* sync_service = GetSyncService();
auto state = TrustedVaultBannerState::kNotShown;
if (sync_service && sync_service->GetUserSettings()->GetPassphraseType() ==
syncer::PassphraseType::kTrustedVaultPassphrase) {
state = TrustedVaultBannerState::kOptedIn;
} else if (syncer::ShouldOfferTrustedVaultOptIn(sync_service)) {
state = TrustedVaultBannerState::kOfferOptIn;
}
FireWebUIListener(kTrustedVaultBannerStateChangedEvent,
base::Value(static_cast<int>(state)));
}
LoginUIService* PeopleHandler::GetLoginUIService() const {
return LoginUIServiceFactory::GetForProfile(profile_);
}
void PeopleHandler::UpdateSyncStatus() {
FireWebUIListener("sync-status-changed", GetSyncStatusDictionary());
}
void PeopleHandler::MarkFirstSetupComplete() {
syncer::SyncService* service = GetSyncService();
// The sync service may be nullptr if it has been just disabled by policy.
if (!service)
return;
// Sync is usually already requested at this point, but it might not be if
// Sync was reset from the dashboard while this page was open. (In most
// situations, resetting Sync also signs the user out of Chrome so this
// doesn't come up, but on ChromeOS or for managed (enterprise) accounts
// signout isn't possible.)
// Note that this has to happen *before* checking if first-time setup is
// already marked complete, because on some platforms (e.g. ChromeOS) that
// gets set automatically.
service->SetSyncFeatureRequested();
#if !BUILDFLAG(IS_CHROMEOS_ASH)
// If the first-time setup is already complete, there's nothing else to do.
if (service->GetUserSettings()->IsInitialSyncFeatureSetupComplete()) {
return;
}
unified_consent::metrics::RecordSyncSetupDataTypesHistrogam(
service->GetUserSettings());
// We're done configuring, so notify SyncService that it is OK to start
// syncing.
service->GetUserSettings()->SetInitialSyncFeatureSetupComplete(
syncer::SyncFirstSetupCompleteSource::ADVANCED_FLOW_CONFIRM);
FireWebUIListener("sync-settings-saved");
#endif // !BUILDFLAG(IS_CHROMEOS_ASH)
}
void PeopleHandler::MaybeMarkSyncConfiguring() {
#if !BUILDFLAG(IS_CHROMEOS_ASH)
if (IsProfileAuthNeededOrHasErrors())
return;
#endif
syncer::SyncService* service = GetSyncService();
// The sync service may be nullptr if it has been just disabled by policy.
if (service && service->IsEngineInitialized())
configuring_sync_ = true;
}
bool PeopleHandler::IsProfileAuthNeededOrHasErrors() {
return !IdentityManagerFactory::GetForProfile(profile_)->HasPrimaryAccount(
signin::ConsentLevel::kSync) ||
SigninErrorControllerFactory::GetForProfile(profile_)->HasError();
}
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
base::Value::Dict PeopleHandler::GetChromeSigninUserChoiceInfo() {
signin::IdentityManager* identity_manager =
IdentityManagerFactory::GetForProfile(profile_);
// Gets the Chrome signed in account or the first signed in account in the
// cooke jar, refresh token should be available too.
std::string signed_in_email =
signin_ui_util::GetSingleAccountForPromos(identity_manager).email;
bool should_show_settings =
!signin::IsImplicitBrowserSigninOrExplicitDisabled(
identity_manager, profile_->GetPrefs()) &&
!signed_in_email.empty();
ChromeSigninUserChoice choice =
should_show_settings
? DiceWebSigninInterceptor::GetChromeSigninUserChoice(
*profile_->GetPrefs(), signed_in_email)
: ChromeSigninUserChoice::kNoChoice;
base::Value::Dict chrome_signin_user_choice_info;
chrome_signin_user_choice_info.Set("shouldShowSettings",
should_show_settings);
chrome_signin_user_choice_info.Set("choice", static_cast<int>(choice));
chrome_signin_user_choice_info.Set("signedInEmail", signed_in_email);
return chrome_signin_user_choice_info;
}
void PeopleHandler::HandleGetChromeSigninUserChoiceInfo(
const base::Value::List& args) {
AllowJavascript();
CHECK_EQ(1U, args.size());
ResolveJavascriptCallback(args[0], GetChromeSigninUserChoiceInfo());
}
void PeopleHandler::HandleSetChromeSigninUserChoice(
const base::Value::List& args) {
CHECK(!signin::IsImplicitBrowserSigninOrExplicitDisabled(
IdentityManagerFactory::GetForProfile(profile_), profile_->GetPrefs()));
CHECK_EQ(2U, args.size());
CHECK(args[0].is_int());
ChromeSigninUserChoice user_choice =
static_cast<ChromeSigninUserChoice>(args[0].GetInt());
CHECK(args[1].is_string());
std::string signed_in_email = args[1].GetString();
CHECK(!signed_in_email.empty());
DiceWebSigninInterceptor::SetChromeSigninUserChoice(
*profile_->GetPrefs(), signed_in_email, user_choice);
}
void PeopleHandler::UpdateChromeSigninUserChoiceInfo() {
if (switches::IsExplicitBrowserSigninUIOnDesktopEnabled(
switches::ExplicitBrowserSigninPhase::kFull)) {
FireWebUIListener("chrome-signin-user-choice-info-change",
GetChromeSigninUserChoiceInfo());
}
}
#endif
} // namespace settings