| // Copyright 2015 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/settings/people_handler.h" |
| |
| #include <string> |
| |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/compiler_specific.h" |
| #include "base/i18n/time_formatting.h" |
| #include "base/json/json_reader.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/metrics/user_metrics.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/values.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/lifetime/application_lifetime.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/profiles/profile_metrics.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/sync/profile_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/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/grit/generated_resources.h" |
| #include "components/autofill/core/common/autofill_prefs.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/signin/core/browser/account_consistency_method.h" |
| #include "components/signin/core/browser/signin_error_controller.h" |
| #include "components/signin/core/browser/signin_header_helper.h" |
| #include "components/signin/core/browser/signin_metrics.h" |
| #include "components/signin/core/browser/signin_pref_names.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/driver/sync_service.h" |
| #include "components/sync/driver/sync_service_utils.h" |
| #include "components/sync/driver/sync_user_settings.h" |
| #include "components/unified_consent/feature.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 "services/identity/public/cpp/accounts_mutator.h" |
| #include "services/identity/public/cpp/identity_manager.h" |
| #include "services/identity/public/cpp/primary_account_mutator.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/base/webui/web_ui_util.h" |
| |
| #if defined(OS_CHROMEOS) |
| #include "chrome/browser/chromeos/login/quick_unlock/pin_backend.h" |
| #else |
| #include "chrome/browser/signin/signin_util.h" |
| #include "chrome/browser/ui/webui/profile_helper.h" |
| #endif |
| |
| #if BUILDFLAG(ENABLE_DICE_SUPPORT) |
| #include "chrome/browser/profiles/profile_avatar_icon_util.h" |
| #include "chrome/browser/signin/account_consistency_mode_manager.h" |
| #include "third_party/skia/include/core/SkBitmap.h" |
| #include "ui/gfx/image/image.h" |
| #endif |
| |
| using content::WebContents; |
| using l10n_util::GetStringFUTF16; |
| using l10n_util::GetStringUTF16; |
| |
| namespace { |
| |
| // A structure which contains all the configuration information for sync. |
| struct SyncConfigInfo { |
| SyncConfigInfo(); |
| ~SyncConfigInfo(); |
| |
| bool encrypt_all; |
| bool sync_everything; |
| syncer::UserSelectableTypeSet selected_types; |
| bool payments_integration_enabled; |
| std::string passphrase; |
| bool set_new_passphrase; |
| }; |
| |
| SyncConfigInfo::SyncConfigInfo() |
| : encrypt_all(false), |
| sync_everything(false), |
| payments_integration_enabled(false), |
| set_new_passphrase(false) {} |
| |
| SyncConfigInfo::~SyncConfigInfo() {} |
| |
| bool GetConfiguration(const std::string& json, SyncConfigInfo* config) { |
| std::unique_ptr<base::Value> parsed_value = |
| base::JSONReader::ReadDeprecated(json); |
| base::DictionaryValue* result; |
| if (!parsed_value || !parsed_value->GetAsDictionary(&result)) { |
| DLOG(ERROR) << "GetConfiguration() not passed a Dictionary"; |
| return false; |
| } |
| |
| if (!result->GetBoolean("syncAllDataTypes", &config->sync_everything)) { |
| DLOG(ERROR) << "GetConfiguration() not passed a syncAllDataTypes value"; |
| return false; |
| } |
| |
| if (!result->GetBoolean("paymentsIntegrationEnabled", |
| &config->payments_integration_enabled)) { |
| DLOG(ERROR) << "GetConfiguration() not passed a paymentsIntegrationEnabled " |
| << "value"; |
| return false; |
| } |
| |
| for (syncer::UserSelectableType type : syncer::UserSelectableTypeSet::All()) { |
| std::string key_name = |
| syncer::GetUserSelectableTypeName(type) + std::string("Synced"); |
| bool sync_value; |
| if (!result->GetBoolean(key_name, &sync_value)) { |
| DLOG(ERROR) << "GetConfiguration() not passed a value for " << key_name; |
| return false; |
| } |
| if (sync_value) |
| config->selected_types.Put(type); |
| } |
| |
| // Encryption settings. |
| if (!result->GetBoolean("encryptAllData", &config->encrypt_all)) { |
| DLOG(ERROR) << "GetConfiguration() not passed a value for encryptAllData"; |
| return false; |
| } |
| |
| // Passphrase settings. |
| if (result->GetString("passphrase", &config->passphrase) && |
| !config->passphrase.empty() && |
| !result->GetBoolean("setNewPassphrase", &config->set_new_passphrase)) { |
| DLOG(ERROR) << "GetConfiguration() not passed a set_new_passphrase value"; |
| return false; |
| } |
| return true; |
| } |
| |
| // Guaranteed to return a valid result (or crash). |
| void ParseConfigurationArguments(const base::ListValue* args, |
| SyncConfigInfo* config, |
| const base::Value** callback_id) { |
| std::string json; |
| if (args->Get(0, callback_id) && args->GetString(1, &json) && !json.empty()) |
| CHECK(GetConfiguration(json, config)); |
| else |
| NOTREACHED(); |
| } |
| |
| std::string GetSyncErrorAction(sync_ui_util::ActionType action_type) { |
| switch (action_type) { |
| case sync_ui_util::REAUTHENTICATE: |
| return "reauthenticate"; |
| case sync_ui_util::SIGNOUT_AND_SIGNIN: |
| return "signOutAndSignIn"; |
| case sync_ui_util::UPGRADE_CLIENT: |
| return "upgradeClient"; |
| case sync_ui_util::ENTER_PASSPHRASE: |
| return "enterPassphrase"; |
| case sync_ui_util::CONFIRM_SYNC_SETTINGS: |
| return "confirmSyncSettings"; |
| default: |
| return "noAction"; |
| } |
| } |
| |
| #if BUILDFLAG(ENABLE_DICE_SUPPORT) |
| // Returns the base::Value associated with the account, to use in the stored |
| // accounts list. |
| base::Value GetAccountValue(const AccountInfo& account) { |
| DCHECK(!account.IsEmpty()); |
| base::Value dictionary(base::Value::Type::DICTIONARY); |
| dictionary.SetKey("email", base::Value(account.email)); |
| dictionary.SetKey("fullName", base::Value(account.full_name)); |
| dictionary.SetKey("givenName", base::Value(account.given_name)); |
| if (!account.account_image.IsEmpty()) { |
| dictionary.SetKey( |
| "avatarImage", |
| base::Value(webui::GetBitmapDataUrl(account.account_image.AsBitmap()))); |
| } |
| return dictionary; |
| } |
| #endif // BUILDFLAG(ENABLE_DICE_SUPPORT) |
| |
| base::string16 GetEnterPassphraseBody(syncer::PassphraseType passphrase_type, |
| base::Time passphrase_time) { |
| DCHECK(syncer::IsExplicitPassphrase(passphrase_type)); |
| switch (passphrase_type) { |
| case syncer::PassphraseType::FROZEN_IMPLICIT_PASSPHRASE: |
| if (passphrase_time.is_null()) { |
| return GetStringUTF16(IDS_SYNC_ENTER_GOOGLE_PASSPHRASE_BODY); |
| } |
| return GetStringFUTF16(IDS_SYNC_ENTER_GOOGLE_PASSPHRASE_BODY_WITH_DATE, |
| base::ASCIIToUTF16(chrome::kSyncErrorsHelpURL), |
| base::TimeFormatShortDate(passphrase_time)); |
| case syncer::PassphraseType::CUSTOM_PASSPHRASE: |
| if (passphrase_time.is_null()) { |
| return GetStringUTF16(IDS_SYNC_ENTER_PASSPHRASE_BODY); |
| } |
| return GetStringFUTF16(IDS_SYNC_ENTER_PASSPHRASE_BODY_WITH_DATE, |
| base::ASCIIToUTF16(chrome::kSyncErrorsHelpURL), |
| base::TimeFormatShortDate(passphrase_time)); |
| case syncer::PassphraseType::IMPLICIT_PASSPHRASE: |
| case syncer::PassphraseType::KEYSTORE_PASSPHRASE: |
| case syncer::PassphraseType::PASSPHRASE_TYPE_SIZE: |
| break; |
| } |
| NOTREACHED(); |
| return base::string16(); |
| } |
| |
| base::string16 GetFullEncryptionBody(syncer::PassphraseType passphrase_type, |
| base::Time passphrase_time) { |
| DCHECK(syncer::IsExplicitPassphrase(passphrase_type)); |
| if (passphrase_time.is_null()) { |
| return GetStringUTF16(IDS_SYNC_FULL_ENCRYPTION_BODY_CUSTOM); |
| } |
| switch (passphrase_type) { |
| case syncer::PassphraseType::FROZEN_IMPLICIT_PASSPHRASE: |
| return GetStringFUTF16(IDS_SYNC_FULL_ENCRYPTION_BODY_GOOGLE_WITH_DATE, |
| base::TimeFormatShortDate(passphrase_time)); |
| case syncer::PassphraseType::CUSTOM_PASSPHRASE: |
| return GetStringFUTF16(IDS_SYNC_FULL_ENCRYPTION_BODY_CUSTOM_WITH_DATE, |
| base::TimeFormatShortDate(passphrase_time)); |
| case syncer::PassphraseType::IMPLICIT_PASSPHRASE: |
| case syncer::PassphraseType::KEYSTORE_PASSPHRASE: |
| case syncer::PassphraseType::PASSPHRASE_TYPE_SIZE: |
| break; |
| } |
| NOTREACHED(); |
| return base::string16(); |
| } |
| |
| } // namespace |
| |
| namespace settings { |
| |
| // static |
| const char PeopleHandler::kSpinnerPageStatus[] = "spinner"; |
| const char PeopleHandler::kConfigurePageStatus[] = "configure"; |
| const char PeopleHandler::kTimeoutPageStatus[] = "timeout"; |
| const char PeopleHandler::kDonePageStatus[] = "done"; |
| const char PeopleHandler::kPassphraseFailedPageStatus[] = "passphraseFailed"; |
| |
| PeopleHandler::PeopleHandler(Profile* profile) |
| : profile_(profile), |
| configuring_sync_(false), |
| identity_manager_observer_(this), |
| sync_service_observer_(this) { |
| } |
| |
| PeopleHandler::~PeopleHandler() { |
| // Early exit if running unit tests (no actual WebUI is attached). |
| if (!web_ui()) |
| return; |
| |
| // 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( |
| "SyncSetupSetEncryption", |
| base::BindRepeating(&PeopleHandler::HandleSetEncryption, |
| base::Unretained(this))); |
| web_ui()->RegisterMessageCallback( |
| "SyncSetupShowSetupUI", |
| base::BindRepeating(&PeopleHandler::HandleShowSetupUI, |
| base::Unretained(this))); |
| web_ui()->RegisterMessageCallback( |
| "SyncSetupGetSyncStatus", |
| base::BindRepeating(&PeopleHandler::HandleGetSyncStatus, |
| base::Unretained(this))); |
| #if defined(OS_CHROMEOS) |
| web_ui()->RegisterMessageCallback( |
| "AttemptUserExit", |
| base::BindRepeating(&PeopleHandler::HandleAttemptUserExit, |
| base::Unretained(this))); |
| web_ui()->RegisterMessageCallback( |
| "RequestPinLoginState", |
| base::BindRepeating(&PeopleHandler::HandleRequestPinLoginState, |
| base::Unretained(this))); |
| #else |
| web_ui()->RegisterMessageCallback( |
| "SyncSetupSignout", base::BindRepeating(&PeopleHandler::HandleSignout, |
| base::Unretained(this))); |
| web_ui()->RegisterMessageCallback( |
| "SyncSetupPauseSync", base::BindRepeating(&PeopleHandler::HandlePauseSync, |
| base::Unretained(this))); |
| web_ui()->RegisterMessageCallback( |
| "SyncSetupStartSignIn", |
| base::BindRepeating(&PeopleHandler::HandleStartSignin, |
| base::Unretained(this))); |
| #endif |
| #if BUILDFLAG(ENABLE_DICE_SUPPORT) |
| web_ui()->RegisterMessageCallback( |
| "SyncSetupGetStoredAccounts", |
| base::BindRepeating(&PeopleHandler::HandleGetStoredAccounts, |
| base::Unretained(this))); |
| web_ui()->RegisterMessageCallback( |
| "SyncSetupStartSyncingWithEmail", |
| base::BindRepeating(&PeopleHandler::HandleStartSyncingWithEmail, |
| base::Unretained(this))); |
| #endif |
| } |
| |
| void PeopleHandler::OnJavascriptAllowed() { |
| PrefService* prefs = profile_->GetPrefs(); |
| profile_pref_registrar_.Init(prefs); |
| profile_pref_registrar_.Add( |
| prefs::kSigninAllowed, |
| base::Bind(&PeopleHandler::UpdateSyncStatus, base::Unretained(this))); |
| |
| identity::IdentityManager* identity_manager( |
| IdentityManagerFactory::GetInstance()->GetForProfile(profile_)); |
| if (identity_manager) |
| identity_manager_observer_.Add(identity_manager); |
| |
| // This is intentionally not using GetSyncService(), to go around the |
| // Profile::IsSyncAllowed() check. |
| syncer::SyncService* sync_service = |
| ProfileSyncServiceFactory::GetForProfile(profile_); |
| if (sync_service) |
| sync_service_observer_.Add(sync_service); |
| } |
| |
| void PeopleHandler::OnJavascriptDisallowed() { |
| profile_pref_registrar_.RemoveAll(); |
| identity_manager_observer_.RemoveAll(); |
| sync_service_observer_.RemoveAll(); |
| } |
| |
| #if !defined(OS_CHROMEOS) |
| void PeopleHandler::DisplayGaiaLogin(signin_metrics::AccessPoint access_point) { |
| DCHECK(!sync_startup_tracker_); |
| // 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) { |
| Browser* browser = |
| chrome::FindBrowserWithWebContents(web_ui()->GetWebContents()); |
| if (!browser) |
| return; |
| |
| auto* identity_manager = |
| IdentityManagerFactory::GetForProfile(browser->profile()); |
| |
| syncer::SyncService* service = GetSyncService(); |
| if (service && service->HasUnrecoverableError()) { |
| // When the user has an unrecoverable error, they first have to sign out and |
| // then sign in again. |
| |
| identity_manager->GetPrimaryAccountMutator()->ClearPrimaryAccount( |
| identity::PrimaryAccountMutator::ClearAccountsAction::kDefault, |
| signin_metrics::USER_CLICKED_SIGNOUT_SETTINGS, |
| signin_metrics::SignoutDelete::IGNORE_METRIC); |
| } |
| |
| // If the signin manager already has an authenticated username, 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()) { |
| UMA_HISTOGRAM_ENUMERATION("Signin.Reauth", |
| signin_metrics::HISTOGRAM_REAUTH_SHOWN, |
| signin_metrics::HISTOGRAM_REAUTH_MAX); |
| |
| SigninErrorController* error_controller = |
| SigninErrorControllerFactory::GetForProfile(browser->profile()); |
| DCHECK(error_controller->HasError()); |
| browser->window()->ShowAvatarBubbleFromAvatarButton( |
| BrowserWindow::AVATAR_BUBBLE_MODE_REAUTH, |
| signin::ManageAccountsParams(), access_point, false); |
| } else { |
| browser->window()->ShowAvatarBubbleFromAvatarButton( |
| BrowserWindow::AVATAR_BUBBLE_MODE_SIGNIN, |
| signin::ManageAccountsParams(), access_point, false); |
| } |
| } |
| #endif |
| |
| #if defined(OS_CHROMEOS) |
| void PeopleHandler::OnPinLoginAvailable(bool is_available) { |
| FireWebUIListener("pin-login-available-changed", base::Value(is_available)); |
| } |
| #endif |
| |
| void PeopleHandler::DisplaySpinner() { |
| configuring_sync_ = true; |
| |
| const int kTimeoutSec = 30; |
| DCHECK(!engine_start_timer_); |
| engine_start_timer_.reset(new base::OneShotTimer()); |
| engine_start_timer_->Start(FROM_HERE, |
| base::TimeDelta::FromSeconds(kTimeoutSec), this, |
| &PeopleHandler::DisplayTimeout); |
| |
| FireWebUIListener("page-status-changed", base::Value(kSpinnerPageStatus)); |
| } |
| |
| void PeopleHandler::DisplayTimeout() { |
| // Stop a timer to handle timeout in waiting for checking network connection. |
| engine_start_timer_.reset(); |
| |
| // Do not listen to sync startup events. |
| sync_startup_tracker_.reset(); |
| |
| FireWebUIListener("page-status-changed", base::Value(kTimeoutPageStatus)); |
| } |
| |
| void PeopleHandler::OnDidClosePage(const base::ListValue* args) { |
| // Don't mark setup as complete if "didAbort" is true, or if authentication |
| // is still needed. |
| if (!args->GetList()[0].GetBool() && !IsProfileAuthNeededOrHasErrors()) { |
| MarkFirstSetupComplete(); |
| } |
| |
| CloseSyncSetup(); |
| } |
| |
| void PeopleHandler::SyncStartupFailed() { |
| // Stop a timer to handle timeout in waiting for checking network connection. |
| engine_start_timer_.reset(); |
| |
| // Just close the sync overlay (the idea is that the base settings page will |
| // display the current error.) |
| CloseUI(); |
| } |
| |
| void PeopleHandler::SyncStartupCompleted() { |
| syncer::SyncService* service = GetSyncService(); |
| DCHECK(service->IsEngineInitialized()); |
| |
| // Stop a timer to handle timeout in waiting for checking network connection. |
| engine_start_timer_.reset(); |
| |
| sync_startup_tracker_.reset(); |
| |
| PushSyncPrefs(); |
| } |
| |
| syncer::SyncService* PeopleHandler::GetSyncService() const { |
| return profile_->IsSyncAllowed() |
| ? ProfileSyncServiceFactory::GetForProfile(profile_) |
| : nullptr; |
| } |
| |
| void PeopleHandler::HandleSetDatatypes(const base::ListValue* args) { |
| DCHECK(!sync_startup_tracker_); |
| |
| SyncConfigInfo configuration; |
| const base::Value* callback_id = nullptr; |
| ParseConfigurationArguments(args, &configuration, &callback_id); |
| |
| autofill::prefs::SetPaymentsIntegrationEnabled( |
| profile_->GetPrefs(), configuration.payments_integration_enabled); |
| |
| // 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; |
| } |
| |
| service->GetUserSettings()->SetSelectedTypes(configuration.sync_everything, |
| configuration.selected_types); |
| |
| // Choosing data types to sync never fails. |
| ResolveJavascriptCallback(*callback_id, base::Value(kConfigurePageStatus)); |
| |
| ProfileMetrics::LogProfileSyncInfo(ProfileMetrics::SYNC_CUSTOMIZE); |
| if (!configuration.sync_everything) |
| ProfileMetrics::LogProfileSyncInfo(ProfileMetrics::SYNC_CHOOSE); |
| } |
| |
| #if BUILDFLAG(ENABLE_DICE_SUPPORT) |
| void PeopleHandler::HandleGetStoredAccounts(const base::ListValue* args) { |
| CHECK_EQ(1U, args->GetSize()); |
| const base::Value* callback_id; |
| CHECK(args->Get(0, &callback_id)); |
| |
| 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()); |
| } |
| |
| base::Value PeopleHandler::GetStoredAccountsList() { |
| base::Value accounts(base::Value::Type::LIST); |
| const bool dice_enabled = |
| AccountConsistencyModeManager::IsDiceEnabledForProfile(profile_); |
| |
| // Dice and unified consent both disabled: do not show the list of accounts. |
| if (!dice_enabled && !unified_consent::IsUnifiedConsentFeatureEnabled()) |
| return accounts; |
| |
| base::Value::ListStorage& accounts_list = accounts.GetList(); |
| if (dice_enabled) { |
| // If dice is enabled, show all the accounts. |
| std::vector<AccountInfo> accounts = |
| signin_ui_util::GetAccountsForDicePromos(profile_); |
| accounts_list.reserve(accounts.size()); |
| for (auto const& account : accounts) { |
| accounts_list.push_back(GetAccountValue(account)); |
| } |
| } else { |
| // If dice is disabled (and unified consent enabled), show only the primary |
| // account. |
| auto* identity_manager = IdentityManagerFactory::GetForProfile(profile_); |
| base::Optional<AccountInfo> primary_account_info = |
| identity_manager->FindExtendedAccountInfoForAccount( |
| identity_manager->GetPrimaryAccountInfo()); |
| if (primary_account_info.has_value()) |
| accounts_list.push_back(GetAccountValue(primary_account_info.value())); |
| } |
| |
| return accounts; |
| } |
| |
| void PeopleHandler::HandleStartSyncingWithEmail(const base::ListValue* args) { |
| DCHECK(AccountConsistencyModeManager::IsDiceEnabledForProfile(profile_)); |
| const base::Value* email; |
| const base::Value* is_default_promo_account; |
| CHECK(args->Get(0, &email)); |
| CHECK(args->Get(1, &is_default_promo_account)); |
| |
| Browser* browser = |
| chrome::FindBrowserWithWebContents(web_ui()->GetWebContents()); |
| |
| base::Optional<AccountInfo> maybe_account = |
| IdentityManagerFactory::GetForProfile(profile_) |
| ->FindAccountInfoForAccountWithRefreshTokenByEmailAddress( |
| email->GetString()); |
| |
| signin_ui_util::EnableSyncFromPromo( |
| browser, |
| maybe_account.has_value() ? maybe_account.value() : AccountInfo(), |
| signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS, |
| is_default_promo_account->GetBool()); |
| } |
| #endif |
| |
| void PeopleHandler::HandleSetEncryption(const base::ListValue* args) { |
| DCHECK(!sync_startup_tracker_); |
| |
| 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 allow "encrypt all" if the SyncService doesn't allow it. |
| // The UI is hidden, but the user may have enabled it e.g. by fiddling with |
| // the web inspector. |
| if (!service->GetUserSettings()->IsEncryptEverythingAllowed()) { |
| configuration.encrypt_all = false; |
| configuration.set_new_passphrase = false; |
| } |
| |
| // Note: Data encryption will not occur until configuration is complete |
| // (when the PSS receives its CONFIGURE_DONE notification from the sync |
| // engine), so the user still has a chance to cancel out of the operation |
| // if (for example) some kind of passphrase error is encountered. |
| if (configuration.encrypt_all) |
| service->GetUserSettings()->EnableEncryptEverything(); |
| |
| bool passphrase_failed = false; |
| if (!configuration.passphrase.empty()) { |
| // We call IsPassphraseRequired() here (instead of |
| // IsPassphraseRequiredForDecryption()) because the user may try to enter |
| // a passphrase even though no encrypted data types are enabled. |
| if (service->GetUserSettings()->IsPassphraseRequired()) { |
| // If we have pending keys, try to decrypt them with the provided |
| // passphrase. We track if this succeeds or fails because a failed |
| // decryption should result in an error even if there aren't any encrypted |
| // data types. |
| passphrase_failed = !service->GetUserSettings()->SetDecryptionPassphrase( |
| configuration.passphrase); |
| } else { |
| // OK, the user sent us a passphrase, but we don't have pending keys. So |
| // it either means that the pending keys were resolved somehow since the |
| // time the UI was displayed (re-encryption, pending passphrase change, |
| // etc) or the user wants to re-encrypt. |
| if (configuration.set_new_passphrase && |
| !service->GetUserSettings()->IsUsingSecondaryPassphrase()) { |
| service->GetUserSettings()->SetEncryptionPassphrase( |
| configuration.passphrase); |
| } |
| } |
| } |
| |
| if (passphrase_failed || |
| service->GetUserSettings()->IsPassphraseRequiredForDecryption()) { |
| // If the user doesn't enter any passphrase, we won't call |
| // SetDecryptionPassphrase() (passphrase_failed == false), but we still |
| // want to display an error message to let the user know that their blank |
| // passphrase entry is not acceptable. |
| |
| // TODO(tommycli): Switch this to RejectJavascriptCallback once the |
| // Sync page JavaScript has been further refactored. |
| ResolveJavascriptCallback(*callback_id, |
| base::Value(kPassphraseFailedPageStatus)); |
| } else { |
| ResolveJavascriptCallback(*callback_id, base::Value(kConfigurePageStatus)); |
| } |
| |
| if (configuration.encrypt_all) |
| ProfileMetrics::LogProfileSyncInfo(ProfileMetrics::SYNC_ENCRYPT); |
| if (!configuration.set_new_passphrase && !configuration.passphrase.empty()) |
| ProfileMetrics::LogProfileSyncInfo(ProfileMetrics::SYNC_PASSPHRASE); |
| } |
| |
| void PeopleHandler::HandleShowSetupUI(const base::ListValue* args) { |
| AllowJavascript(); |
| |
| syncer::SyncService* service = GetSyncService(); |
| |
| if (unified_consent::IsUnifiedConsentFeatureEnabled()) { |
| 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->GetUserSettings()->SetSyncRequested(true); |
| |
| GetLoginUIService()->SetLoginUI(this); |
| |
| // Observe the web contents for a before unload event. |
| Observe(web_ui()->GetWebContents()); |
| |
| 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(); |
| |
| // Always let the page open when unified consent is enabled. |
| return; |
| } |
| |
| if (!service) { |
| CloseUI(); |
| return; |
| } |
| |
| // This if-statement is not using IsProfileAuthNeededOrHasErrors(), because |
| // in some error cases (e.g. "confirmSyncSettings") the UI still needs to |
| // show. |
| if (!IdentityManagerFactory::GetForProfile(profile_)->HasPrimaryAccount()) { |
| // For web-based signin, the signin page is not displayed in an overlay |
| // on the settings page. So if we get here, it must be due to the user |
| // cancelling signin (by reloading the sync settings page during initial |
| // signin) or by directly navigating to settings/syncSetup |
| // (http://crbug.com/229836). So just exit and go back to the settings page. |
| DLOG(WARNING) << "Cannot display sync setup UI when not signed in"; |
| CloseUI(); |
| return; |
| } |
| |
| // Notify services that login UI is now active. |
| GetLoginUIService()->SetLoginUI(this); |
| |
| if (!sync_blocker_) |
| sync_blocker_ = service->GetSetupInProgressHandle(); |
| |
| // Early exit if there is already a preferences push pending sync startup. |
| if (sync_startup_tracker_) |
| return; |
| |
| if (!service->IsEngineInitialized() || |
| !service->GetUserSettings()->IsSyncRequested()) { |
| // SetSyncRequested(true) does two things: |
| // 1) As the name says, it marks Sync as requested by the user (it might not |
| // be requested yet because either this is the first time they're setting |
| // it up, or Sync was reset via the dashboard). |
| // 2) Pokes the sync service to start *immediately*, i.e. bypass deferred |
| // startup. |
| // It's possible that both of these are already the case, i.e. the engine is |
| // already in the process of initializing, in which case |
| // SetSyncRequested(true) will effectively do nothing. It's also possible |
| // that the sync service is already running in standalone transport mode and |
| // so the engine is already initialized. In that case, this will trigger the |
| // service to switch to full Sync-the-feature mode. |
| service->GetUserSettings()->SetSyncRequested(true); |
| |
| // See if it's even possible to bring up the sync engine - if not |
| // (unrecoverable error?), don't bother displaying a spinner that will be |
| // immediately closed because this leads to some ugly infinite UI loop (see |
| // http://crbug.com/244769). |
| if (SyncStartupTracker::GetSyncServiceState(service) != |
| SyncStartupTracker::SYNC_STARTUP_ERROR) { |
| DisplaySpinner(); |
| } |
| |
| // Finally, wait for the Sync engine to get initialized. Note that if it is |
| // already initialized (probably because Sync-the-transport was already |
| // running), then this will call us back immediately. |
| sync_startup_tracker_ = std::make_unique<SyncStartupTracker>(service, this); |
| |
| return; |
| } |
| |
| // User is already logged in. They must have brought up the config wizard |
| // via the "Advanced..." button or through One-Click signin (cases 4-6), or |
| // they are re-enabling sync after having disabled it (case 7). |
| PushSyncPrefs(); |
| } |
| |
| #if defined(OS_CHROMEOS) |
| // 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::ListValue* args) { |
| DVLOG(1) << "Signing out the user to fix a sync error."; |
| chrome::AttemptUserExit(); |
| } |
| |
| void PeopleHandler::HandleRequestPinLoginState(const base::ListValue* args) { |
| AllowJavascript(); |
| chromeos::quick_unlock::PinBackend::GetInstance()->HasLoginSupport( |
| base::BindOnce(&PeopleHandler::OnPinLoginAvailable, |
| weak_factory_.GetWeakPtr())); |
| } |
| #endif |
| |
| #if !defined(OS_CHROMEOS) |
| void PeopleHandler::HandleStartSignin(const base::ListValue* 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())); |
| |
| DisplayGaiaLogin(signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS); |
| } |
| |
| void PeopleHandler::HandleSignout(const base::ListValue* args) { |
| bool delete_profile = false; |
| args->GetBoolean(0, &delete_profile); |
| |
| if (!signin_util::IsUserSignoutAllowedForProfile(profile_)) { |
| // If the user cannot signout, the profile must be destroyed. |
| DCHECK(delete_profile); |
| } else { |
| auto* identity_manager = IdentityManagerFactory::GetForProfile(profile_); |
| if (identity_manager->HasPrimaryAccount()) { |
| if (GetSyncService()) |
| syncer::RecordSyncEvent(syncer::STOP_FROM_OPTIONS); |
| |
| signin_metrics::SignoutDelete delete_metric = |
| delete_profile ? signin_metrics::SignoutDelete::DELETED |
| : signin_metrics::SignoutDelete::KEEPING; |
| |
| identity_manager->GetPrimaryAccountMutator()->ClearPrimaryAccount( |
| identity::PrimaryAccountMutator::ClearAccountsAction::kRemoveAll, |
| signin_metrics::USER_CLICKED_SIGNOUT_SETTINGS, delete_metric); |
| } else { |
| DCHECK(!delete_profile) |
| << "Deleting the profile should only be offered the user is syncing."; |
| |
| identity_manager->GetAccountsMutator()->RemoveAllAccounts( |
| signin_metrics::SourceForRefreshTokenOperation::kSettings_Signout); |
| } |
| } |
| |
| if (delete_profile) { |
| webui::DeleteProfileAtPath(profile_->GetPath(), |
| ProfileMetrics::DELETE_PROFILE_SETTINGS); |
| } |
| } |
| |
| void PeopleHandler::HandlePauseSync(const base::ListValue* args) { |
| DCHECK(AccountConsistencyModeManager::IsDiceEnabledForProfile(profile_)); |
| auto* identity_manager = IdentityManagerFactory::GetForProfile(profile_); |
| DCHECK(identity_manager->HasPrimaryAccount()); |
| |
| identity_manager->GetAccountsMutator() |
| ->InvalidateRefreshTokenForPrimaryAccount( |
| signin_metrics::SourceForRefreshTokenOperation::kSettings_PauseSync); |
| } |
| #endif |
| |
| void PeopleHandler::HandleGetSyncStatus(const base::ListValue* args) { |
| AllowJavascript(); |
| |
| CHECK_EQ(1U, args->GetSize()); |
| const base::Value* callback_id; |
| CHECK(args->Get(0, &callback_id)); |
| |
| ResolveJavascriptCallback(*callback_id, *GetSyncStatusDictionary()); |
| } |
| |
| void PeopleHandler::CloseSyncSetup() { |
| // Stop a timer to handle timeout in waiting for checking network connection. |
| engine_start_timer_.reset(); |
| |
| // Clear the sync startup tracker, since the setup wizard is being closed. |
| sync_startup_tracker_.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) { |
| 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 || |
| (!sync_service->GetUserSettings()->IsFirstSetupComplete() && |
| sync_service->GetAuthError().state() == |
| GoogleServiceAuthError::NONE))) { |
| if (configuring_sync_) { |
| syncer::RecordSyncEvent(syncer::CANCEL_DURING_CONFIGURE); |
| |
| // If the user clicked "Cancel" while setting up sync, disable sync |
| // because we don't want the sync engine to remain in the |
| // first-setup-incomplete state. |
| // Note: In order to disable sync across restarts on Chrome OS, |
| // we must call StopAndClear(), which suppresses sync startup in |
| // addition to disabling it. |
| if (sync_service) { |
| DVLOG(1) << "Sync setup aborted by user action"; |
| sync_service->StopAndClear(); |
| #if !defined(OS_CHROMEOS) |
| // Sign out the user on desktop Chrome if they click cancel during |
| // initial setup. |
| if (!sync_service->GetUserSettings()->IsFirstSetupComplete()) { |
| IdentityManagerFactory::GetForProfile(profile_) |
| ->GetPrimaryAccountMutator() |
| ->ClearPrimaryAccount( |
| identity::PrimaryAccountMutator::ClearAccountsAction:: |
| kDefault, |
| signin_metrics::ABORT_SIGNIN, |
| signin_metrics::SignoutDelete::IGNORE_METRIC); |
| } |
| #endif |
| } |
| } |
| } |
| |
| service->LoginUIClosed(this); |
| } |
| |
| // 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) { |
| syncer::SyncService* service = GetSyncService(); |
| const GURL current_url = web_contents->GetVisibleURL(); |
| if (service && |
| current_url == chrome::GetSettingsUrl(chrome::kSyncSetupSubPage)) { |
| sync_blocker_ = service->GetSetupInProgressHandle(); |
| } |
| } |
| } |
| |
| void PeopleHandler::FocusUI() { |
| WebContents* web_contents = web_ui()->GetWebContents(); |
| web_contents->GetDelegate()->ActivateContents(web_contents); |
| } |
| |
| void PeopleHandler::CloseUI() { |
| CloseSyncSetup(); |
| FireWebUIListener("page-status-changed", base::Value(kDonePageStatus)); |
| } |
| |
| void PeopleHandler::OnPrimaryAccountSet( |
| const CoreAccountInfo& primary_account_info) { |
| // 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(); |
| } |
| |
| void PeopleHandler::OnPrimaryAccountCleared( |
| const CoreAccountInfo& previous_primary_account_info) { |
| sync_blocker_.reset(); |
| UpdateSyncStatus(); |
| } |
| |
| void PeopleHandler::OnStateChanged(syncer::SyncService* sync) { |
| UpdateSyncStatus(); |
| |
| // When the SyncService changes its state, we should also push the updated |
| // sync preferences. |
| PushSyncPrefs(); |
| } |
| |
| void PeopleHandler::BeforeUnloadDialogCancelled() { |
| // The before unload dialog is only shown during the first sync setup. |
| DCHECK(IdentityManagerFactory::GetForProfile(profile_)->HasPrimaryAccount()); |
| syncer::SyncService* service = GetSyncService(); |
| DCHECK(service && service->IsSetupInProgress() && |
| !service->GetUserSettings()->IsFirstSetupComplete()); |
| |
| base::RecordAction( |
| base::UserMetricsAction("Signin_Signin_CancelAbortAdvancedSyncSettings")); |
| } |
| |
| std::unique_ptr<base::DictionaryValue> PeopleHandler::GetSyncStatusDictionary() |
| const { |
| std::unique_ptr<base::DictionaryValue> sync_status(new base::DictionaryValue); |
| if (profile_->IsGuestSession()) { |
| // Cannot display signin status when running in guest mode on chromeos |
| // because there is no SigninManager. |
| sync_status->SetBoolean("signinAllowed", false); |
| return sync_status; |
| } |
| |
| sync_status->SetBoolean("supervisedUser", profile_->IsSupervised()); |
| sync_status->SetBoolean("childUser", profile_->IsChild()); |
| |
| auto* identity_manager = IdentityManagerFactory::GetForProfile(profile_); |
| DCHECK(identity_manager); |
| |
| #if !defined(OS_CHROMEOS) |
| // Signout is not allowed if the user has policy (crbug.com/172204). |
| if (!signin_util::IsUserSignoutAllowedForProfile(profile_)) { |
| std::string username = identity_manager->GetPrimaryAccountInfo().email; |
| |
| // 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 (!username.empty()) |
| sync_status->SetString("domain", gaia::ExtractDomainName(username)); |
| } |
| #endif |
| |
| // 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 = |
| ProfileSyncServiceFactory::GetForProfile(profile_); |
| bool disallowed_by_policy = |
| service && service->HasDisableReason( |
| syncer::SyncService::DISABLE_REASON_ENTERPRISE_POLICY); |
| sync_status->SetBoolean( |
| "signinAllowed", profile_->GetPrefs()->GetBoolean(prefs::kSigninAllowed)); |
| sync_status->SetBoolean("syncSystemEnabled", (service != nullptr)); |
| // TODO(crbug.com/953641): Rename "setupInProgress" to "firstSetupInProgress". |
| sync_status->SetBoolean( |
| "setupInProgress", |
| service && !disallowed_by_policy && service->IsSetupInProgress() && |
| !service->GetUserSettings()->IsFirstSetupComplete() && |
| identity_manager->HasPrimaryAccount()); |
| |
| base::string16 status_label; |
| base::string16 link_label; |
| sync_ui_util::ActionType action_type = sync_ui_util::NO_ACTION; |
| |
| bool status_has_error = |
| sync_ui_util::GetStatusLabels(profile_, &status_label, &link_label, |
| &action_type) == sync_ui_util::SYNC_ERROR; |
| sync_status->SetString("statusText", status_label); |
| sync_status->SetString("statusActionText", link_label); |
| sync_status->SetBoolean("hasError", status_has_error); |
| sync_status->SetString("statusAction", GetSyncErrorAction(action_type)); |
| |
| sync_status->SetBoolean("managed", disallowed_by_policy); |
| sync_status->SetBoolean( |
| "disabled", !service || disallowed_by_policy || |
| !service->GetUserSettings()->IsSyncAllowedByPlatform()); |
| sync_status->SetBoolean("signedIn", identity_manager->HasPrimaryAccount()); |
| sync_status->SetString( |
| "signedInUsername", |
| signin_ui_util::GetAuthenticatedUsername(identity_manager)); |
| sync_status->SetBoolean("hasUnrecoverableError", |
| service && service->HasUnrecoverableError()); |
| return sync_status; |
| } |
| |
| void PeopleHandler::PushSyncPrefs() { |
| #if !defined(OS_CHROMEOS) |
| // Early exit if the user has not signed in yet. |
| 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()) { |
| return; |
| } |
| |
| configuring_sync_ = true; |
| |
| // Setup args for the sync configure screen: |
| // 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 |
| // paymentsIntegrationEnabled: true if the user wants Payments integration |
| // encryptionEnabled: true if sync supports encryption |
| // encryptAllData: true if user wants to encrypt all data (not just |
| // passwords) |
| // passphraseRequired: true if a passphrase is needed to start sync |
| // |
| base::DictionaryValue 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(); |
| const syncer::UserSelectableTypeSet enforced_types = |
| sync_user_settings->GetForcedTypes(); |
| for (syncer::UserSelectableType type : syncer::UserSelectableTypeSet::All()) { |
| const std::string type_name = syncer::GetUserSelectableTypeName(type); |
| args.SetBoolean(type_name + "Registered", registered_types.Has(type)); |
| args.SetBoolean(type_name + "Synced", selected_types.Has(type)); |
| args.SetBoolean(type_name + "Enforced", enforced_types.Has(type)); |
| } |
| args.SetBoolean("syncAllDataTypes", |
| sync_user_settings->IsSyncEverythingEnabled()); |
| args.SetBoolean( |
| "paymentsIntegrationEnabled", |
| autofill::prefs::IsPaymentsIntegrationEnabled(profile_->GetPrefs())); |
| args.SetBoolean("encryptAllData", |
| sync_user_settings->IsEncryptEverythingEnabled()); |
| args.SetBoolean("encryptAllDataAllowed", |
| sync_user_settings->IsEncryptEverythingAllowed()); |
| |
| // We call IsPassphraseRequired() here, instead of calling |
| // IsPassphraseRequiredForDecryption(), because we want to show the passphrase |
| // UI even if no encrypted data types are enabled. |
| args.SetBoolean("passphraseRequired", |
| sync_user_settings->IsPassphraseRequired()); |
| |
| syncer::PassphraseType passphrase_type = |
| sync_user_settings->GetPassphraseType(); |
| if (syncer::IsExplicitPassphrase(passphrase_type)) { |
| base::Time passphrase_time = |
| sync_user_settings->GetExplicitPassphraseTime(); |
| args.SetString("enterPassphraseBody", |
| GetEnterPassphraseBody(passphrase_type, passphrase_time)); |
| args.SetString("fullEncryptionBody", |
| GetFullEncryptionBody(passphrase_type, passphrase_time)); |
| } |
| |
| FireWebUIListener("sync-prefs-changed", args); |
| } |
| |
| 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->GetUserSettings()->SetSyncRequested(true); |
| |
| // If the first-time setup is already complete, there's nothing else to do. |
| if (service->GetUserSettings()->IsFirstSetupComplete()) |
| return; |
| |
| unified_consent::metrics::RecordSyncSetupDataTypesHistrogam( |
| service->GetUserSettings(), profile_->GetPrefs()); |
| |
| // We're done configuring, so notify SyncService that it is OK to start |
| // syncing. |
| service->GetUserSettings()->SetFirstSetupComplete(); |
| FireWebUIListener("sync-settings-saved"); |
| } |
| |
| bool PeopleHandler::IsProfileAuthNeededOrHasErrors() { |
| return !IdentityManagerFactory::GetForProfile(profile_) |
| ->HasPrimaryAccount() || |
| SigninErrorControllerFactory::GetForProfile(profile_)->HasError(); |
| } |
| |
| } // namespace settings |