diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-05-15 10:20:33 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-05-15 10:28:57 +0000 |
commit | d17ea114e5ef69ad5d5d7413280a13e6428098aa (patch) | |
tree | 2c01a75df69f30d27b1432467cfe7c1467a498da /chromium/components/signin | |
parent | 8c5c43c7b138c9b4b0bf56d946e61d3bbc111bec (diff) | |
download | qtwebengine-chromium-d17ea114e5ef69ad5d5d7413280a13e6428098aa.tar.gz |
BASELINE: Update Chromium to 67.0.3396.47
Change-Id: Idcb1341782e417561a2473eeecc82642dafda5b7
Reviewed-by: Michal Klocek <michal.klocek@qt.io>
Diffstat (limited to 'chromium/components/signin')
40 files changed, 1316 insertions, 299 deletions
diff --git a/chromium/components/signin/core/browser/BUILD.gn b/chromium/components/signin/core/browser/BUILD.gn index cf446098cc5..9f7fb37b111 100644 --- a/chromium/components/signin/core/browser/BUILD.gn +++ b/chromium/components/signin/core/browser/BUILD.gn @@ -9,8 +9,8 @@ if (is_android) { import("//build/config/android/rules.gni") } -buildflag_header("signin_features") { - header = "signin_features.h" +buildflag_header("signin_buildflags") { + header = "signin_buildflags.h" flags = [ "ENABLE_DICE_SUPPORT=$enable_dice_support", "ENABLE_MIRROR=$enable_mirror", @@ -28,6 +28,20 @@ static_library("account_info") { ] } +# Split into its own target to allow browser clients of the Identity Service to +# record browser-specific signin metrics without having to depend on all of +# //components/signin/core/browser. +static_library("signin_metrics") { + sources = [ + "signin_metrics.cc", + "signin_metrics.h", + ] + public_deps = [ + "//base", + "//google_apis", + ] +} + static_library("browser") { sources = [ "about_signin_internals.cc", @@ -54,6 +68,8 @@ static_library("browser") { "child_account_info_fetcher_impl.h", "chrome_connected_header_helper.cc", "chrome_connected_header_helper.h", + "cookie_settings_util.cc", + "cookie_settings_util.h", "dice_account_reconcilor_delegate.cc", "dice_account_reconcilor_delegate.h", "dice_header_helper.cc", @@ -84,8 +100,6 @@ static_library("browser") { "signin_manager.h", "signin_manager_base.cc", "signin_manager_base.h", - "signin_metrics.cc", - "signin_metrics.h", "signin_pref_names.cc", "signin_pref_names.h", "signin_status_metrics_provider.cc", @@ -108,6 +122,8 @@ static_library("browser") { public_deps = [ ":account_info", + ":signin_buildflags", + ":signin_metrics", "//base", "//components/content_settings/core/browser", "//components/content_settings/core/common", @@ -115,7 +131,6 @@ static_library("browser") { "//components/keyed_service/core", "//components/prefs", "//components/signin/core/account_id", - "//components/signin/core/browser:signin_features", "//google_apis", "//net", "//ui/gfx", @@ -214,18 +229,19 @@ source_set("unit_tests") { "signin_header_helper_unittest.cc", "signin_investigator_unittest.cc", "signin_manager_unittest.cc", + "signin_metrics_unittest.cc", "signin_status_metrics_provider_unittest.cc", "webdata/token_service_table_unittest.cc", ] deps = [ + ":signin_buildflags", ":test_support", "//base/test:test_support", "//components/content_settings/core/browser", "//components/os_crypt:test_support", "//components/pref_registry:pref_registry", "//components/prefs", - "//components/signin/core/browser:signin_features", "//components/sync_preferences", "//components/sync_preferences:test_support", "//testing/gmock", diff --git a/chromium/components/signin/core/browser/account_fetcher_service.cc b/chromium/components/signin/core/browser/account_fetcher_service.cc index 7027fef0164..255c520d0ec 100644 --- a/chromium/components/signin/core/browser/account_fetcher_service.cc +++ b/chromium/components/signin/core/browser/account_fetcher_service.cc @@ -188,7 +188,7 @@ void AccountFetcherService::ScheduleNextRefresh() { DCHECK(network_fetches_enabled_); const base::TimeDelta time_since_update = base::Time::Now() - last_updated_; - if(time_since_update > kRefreshFromTokenServiceDelay) { + if (time_since_update > kRefreshFromTokenServiceDelay) { RefreshAllAccountsAndScheduleNext(); } else { timer_.Start(FROM_HERE, kRefreshFromTokenServiceDelay - time_since_update, @@ -267,7 +267,6 @@ AccountFetcherService::GetOrCreateImageFetcher() { if (!image_fetcher_) { image_fetcher_ = std::make_unique<image_fetcher::ImageFetcherImpl>( std::move(image_decoder_), signin_client_->GetURLRequestContext()); - image_fetcher_->SetImageFetcherDelegate(this); } return image_fetcher_.get(); } @@ -305,9 +304,10 @@ void AccountFetcherService::FetchAccountImage(const std::string& account_id) { })"); GURL image_url_with_size(signin::GetAvatarImageURLWithOptions( picture_url, kAccountImageDownloadSize, true /* no_silhouette */)); - GetOrCreateImageFetcher()->StartOrQueueNetworkRequest( - account_id, image_url_with_size, - image_fetcher::ImageFetcher::ImageFetcherCallback(), traffic_annotation); + auto callback = base::BindRepeating(&AccountFetcherService::OnImageFetched, + base::Unretained(this)); + GetOrCreateImageFetcher()->FetchImage(account_id, image_url_with_size, + callback, traffic_annotation); } void AccountFetcherService::SetIsChildAccount(const std::string& account_id, @@ -326,8 +326,7 @@ void AccountFetcherService::OnUserInfoFetchFailure( void AccountFetcherService::OnRefreshTokenAvailable( const std::string& account_id) { TRACE_EVENT1("AccountFetcherService", - "AccountFetcherService::OnRefreshTokenAvailable", - "account_id", + "AccountFetcherService::OnRefreshTokenAvailable", "account_id", account_id); DVLOG(1) << "AVAILABLE " << account_id; @@ -347,8 +346,7 @@ void AccountFetcherService::OnRefreshTokenAvailable( void AccountFetcherService::OnRefreshTokenRevoked( const std::string& account_id) { TRACE_EVENT1("AccountFetcherService", - "AccountFetcherService::OnRefreshTokenRevoked", - "account_id", + "AccountFetcherService::OnRefreshTokenRevoked", "account_id", account_id); DVLOG(1) << "REVOKED " << account_id; @@ -367,7 +365,9 @@ void AccountFetcherService::OnRefreshTokensLoaded() { MaybeEnableNetworkFetches(); } -void AccountFetcherService::OnImageFetched(const std::string& id, - const gfx::Image& image) { +void AccountFetcherService::OnImageFetched( + const std::string& id, + const gfx::Image& image, + const image_fetcher::RequestMetadata&) { account_tracker_service_->SetAccountImage(id, image); } diff --git a/chromium/components/signin/core/browser/account_fetcher_service.h b/chromium/components/signin/core/browser/account_fetcher_service.h index 6e864355521..993fa675821 100644 --- a/chromium/components/signin/core/browser/account_fetcher_service.h +++ b/chromium/components/signin/core/browser/account_fetcher_service.h @@ -13,9 +13,9 @@ #include "base/macros.h" #include "base/sequence_checker.h" #include "base/timer/timer.h" -#include "components/image_fetcher/core/image_fetcher_delegate.h" #include "components/keyed_service/core/keyed_service.h" #include "google_apis/gaia/oauth2_token_service.h" +#include "ui/gfx/image/image.h" class AccountInfoFetcher; class AccountTrackerService; @@ -23,7 +23,12 @@ class ChildAccountInfoFetcher; class OAuth2TokenService; class SigninClient; +namespace base { +class DictionaryValue; +} + namespace image_fetcher { +struct RequestMetadata; class ImageDecoder; class ImageFetcherImpl; } // namespace image_fetcher @@ -40,8 +45,7 @@ class PrefRegistrySyncable; // to child account info fetching. class AccountFetcherService : public KeyedService, - public OAuth2TokenService::Observer, - public image_fetcher::ImageFetcherDelegate { + public OAuth2TokenService::Observer { public: // Name of the preference that tracks the int64_t representation of the last // time the AccountTrackerService was updated. @@ -125,12 +129,13 @@ class AccountFetcherService : public KeyedService, // Called in |OnUserInfoFetchSuccess| after the account info has been fetched. void FetchAccountImage(const std::string& account_id); - // image_fetcher::ImageFetcherDelegate: - void OnImageFetched(const std::string& id, const gfx::Image& image) override; + void OnImageFetched(const std::string& id, + const gfx::Image& image, + const image_fetcher::RequestMetadata& image_metadata); - AccountTrackerService* account_tracker_service_; // Not owned. - OAuth2TokenService* token_service_; // Not owned. - SigninClient* signin_client_; // Not owned. + AccountTrackerService* account_tracker_service_; // Not owned. + OAuth2TokenService* token_service_; // Not owned. + SigninClient* signin_client_; // Not owned. invalidation::InvalidationService* invalidation_service_; // Not owned. bool network_fetches_enabled_; bool profile_loaded_; diff --git a/chromium/components/signin/core/browser/account_reconcilor.cc b/chromium/components/signin/core/browser/account_reconcilor.cc index 3b2304f9233..46f6ae05c21 100644 --- a/chromium/components/signin/core/browser/account_reconcilor.cc +++ b/chromium/components/signin/core/browser/account_reconcilor.cc @@ -20,13 +20,15 @@ #include "components/signin/core/browser/account_reconcilor_delegate.h" #include "components/signin/core/browser/profile_management_switches.h" #include "components/signin/core/browser/profile_oauth2_token_service.h" +#include "components/signin/core/browser/signin_buildflags.h" #include "components/signin/core/browser/signin_client.h" -#include "components/signin/core/browser/signin_features.h" #include "components/signin/core/browser/signin_metrics.h" #include "google_apis/gaia/gaia_auth_util.h" #include "google_apis/gaia/gaia_urls.h" #include "google_apis/gaia/google_service_auth_error.h" +using signin::AccountReconcilorDelegate; + namespace { // String used for source parameter in GAIA cookie manager calls. @@ -260,6 +262,20 @@ void AccountReconcilor::OnRefreshTokensLoaded() { StartReconcile(); } +void AccountReconcilor::OnAuthErrorChanged( + const std::string& account_id, + const GoogleServiceAuthError& error) { + // Gaia cookies may be invalidated server-side and the client does not get any + // notification when this happens. + // Gaia cookies derived from refresh tokens are always invalidated server-side + // when the tokens are revoked. Trigger a ListAccounts to Gaia when this + // happens to make sure that the cookies accounts are up-to-date. + // This should cover well the Mirror and Desktop Identity Consistency cases as + // the cookies are always bound to the refresh tokens in these cases. + if (error != GoogleServiceAuthError::AuthErrorNone()) + cookie_manager_service_->TriggerListAccounts(kSource); +} + void AccountReconcilor::PerformMergeAction(const std::string& account_id) { reconcile_is_noop_ = false; if (!delegate_->IsAccountConsistencyEnforced()) { @@ -312,11 +328,10 @@ void AccountReconcilor::StartReconcile() { base::Unretained(this))); } - primary_account_ = signin_manager_->GetAuthenticatedAccountId(); - if (token_service_->GetDelegate()->RefreshTokenHasError(primary_account_) && + if (token_service_->RefreshTokenHasError( + signin_manager_->GetAuthenticatedAccountId()) && delegate_->ShouldAbortReconcileIfPrimaryHasError()) { VLOG(1) << "AccountReconcilor::StartReconcile: primary has error, abort."; - primary_account_.clear(); return; } @@ -353,29 +368,48 @@ void AccountReconcilor::OnGaiaAccountsInCookieUpdated( return; } + std::string primary_account = signin_manager_->GetAuthenticatedAccountId(); std::vector<gaia::ListedAccount> verified_gaia_accounts = FilterUnverifiedAccounts(accounts); VLOG_IF(1, verified_gaia_accounts.size() < accounts.size()) - << "Ignoring " << accounts.size() - verified_gaia_accounts.size() + << "Ignore " << accounts.size() - verified_gaia_accounts.size() << " unverified account(s)."; - if (delegate_->ShouldRevokeAllSecondaryTokensBeforeReconcile( - verified_gaia_accounts)) { - for (const std::string& account : token_service_->GetAccounts()) { - if (account != primary_account_) + // Revoking tokens for secondary accounts causes the AccountTracker to + // completely remove them from Chrome. + // Revoking the token for the primary account is not supported (it should be + // signed out or put to auth error state instead). + AccountReconcilorDelegate::RevokeTokenOption revoke_option = + delegate_->ShouldRevokeSecondaryTokensBeforeReconcile( + verified_gaia_accounts); + for (const std::string& account : token_service_->GetAccounts()) { + if (account == primary_account) + continue; + switch (revoke_option) { + case AccountReconcilorDelegate::RevokeTokenOption::kRevokeIfInError: + if (token_service_->RefreshTokenHasError(account)) { + VLOG(1) << "Revoke token for " << account; + token_service_->RevokeCredentials(account); + } + break; + case AccountReconcilorDelegate::RevokeTokenOption::kRevoke: + VLOG(1) << "Revoke token for " << account; token_service_->RevokeCredentials(account); + break; + case AccountReconcilorDelegate::RevokeTokenOption::kDoNotRevoke: + // Do nothing. + break; } } if (delegate_->ShouldAbortReconcileIfPrimaryHasError() && - token_service_->GetDelegate()->RefreshTokenHasError(primary_account_)) { + token_service_->RefreshTokenHasError(primary_account)) { VLOG(1) << "Primary account has error, abort."; - primary_account_.clear(); is_reconcile_started_ = false; return; } - FinishReconcile(LoadValidAccountsFromTokenService(), + FinishReconcile(primary_account, LoadValidAccountsFromTokenService(), std::move(verified_gaia_accounts)); } else { if (is_reconcile_started_) @@ -392,9 +426,9 @@ std::vector<std::string> AccountReconcilor::LoadValidAccountsFromTokenService() // reconcile them, since it won't work anyway. If the list ends up being // empty then don't reconcile any accounts. for (auto i = chrome_accounts.begin(); i != chrome_accounts.end(); ++i) { - if (token_service_->GetDelegate()->RefreshTokenHasError(*i)) { + if (token_service_->RefreshTokenHasError(*i)) { VLOG(1) << "AccountReconcilor::ValidateAccountsFromTokenService: " << *i - << " has error, won't reconcile"; + << " has error, don't reconcile"; i->clear(); } } @@ -417,13 +451,14 @@ void AccountReconcilor::OnReceivedManageAccountsResponse( } void AccountReconcilor::FinishReconcile( + const std::string& primary_account, const std::vector<std::string>& chrome_accounts, std::vector<gaia::ListedAccount>&& gaia_accounts) { VLOG(1) << "AccountReconcilor::FinishReconcile"; DCHECK(add_to_cookie_.empty()); std::string first_account = delegate_->GetFirstGaiaAccountForReconcile( - chrome_accounts, gaia_accounts, primary_account_, first_execution_); + chrome_accounts, gaia_accounts, primary_account, first_execution_); // |first_account| must be in |chrome_accounts|. DCHECK(first_account.empty() || (std::find(chrome_accounts.begin(), chrome_accounts.end(), @@ -461,7 +496,7 @@ void AccountReconcilor::FinishReconcile( // Gaia cookie has been cleared or was already empty. DCHECK((first_account_mismatch && rebuild_cookie) || (number_gaia_accounts == 0)); - RevokeAllSecondaryTokens(chrome_accounts); + RevokeAllSecondaryTokens(primary_account, chrome_accounts); } else { // Create a list of accounts that need to be added to the Gaia cookie. add_to_cookie_.push_back(first_account); @@ -506,7 +541,7 @@ void AccountReconcilor::FinishReconcile( } void AccountReconcilor::AbortReconcile() { - VLOG(1) << "AccountReconcilor::AbortReconcile: we'll try again later"; + VLOG(1) << "AccountReconcilor::AbortReconcile: try again later"; add_to_cookie_.clear(); CalculateIfReconcileIsDone(); } @@ -540,12 +575,13 @@ void AccountReconcilor::ScheduleStartReconcileIfChromeAccountsChanged() { } void AccountReconcilor::RevokeAllSecondaryTokens( + const std::string& primary_account, const std::vector<std::string>& chrome_accounts) { for (const std::string& account : chrome_accounts) { - if (account != primary_account_) { + if (account != primary_account) { reconcile_is_noop_ = false; if (delegate_->IsAccountConsistencyEnforced()) { - VLOG(1) << "Revoking token for " << account; + VLOG(1) << "Revoke token for " << account; token_service_->RevokeCredentials(account); } } diff --git a/chromium/components/signin/core/browser/account_reconcilor.h b/chromium/components/signin/core/browser/account_reconcilor.h index db4adb51b2a..7aaed2aec30 100644 --- a/chromium/components/signin/core/browser/account_reconcilor.h +++ b/chromium/components/signin/core/browser/account_reconcilor.h @@ -121,6 +121,7 @@ class AccountReconcilor : public KeyedService, FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTest, DiceLastKnownFirstAccount); FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTest, UnverifiedAccountNoop); FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTest, UnverifiedAccountMerge); + FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTest, HandleSigninDuringReconcile); FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTest, DiceMigrationAfterNoop); FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTest, DiceNoMigrationWhenTokensNotReady); @@ -148,6 +149,7 @@ class AccountReconcilor : public KeyedService, FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTest, StartReconcileNoopWithDots); FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTest, StartReconcileNoopMultiple); FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTest, StartReconcileAddToCookie); + FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTest, AuthErrorTriggersListAccount); FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTest, SignoutAfterErrorDoesNotRecordUma); FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTest, @@ -192,13 +194,15 @@ class AccountReconcilor : public KeyedService, // Used during periodic reconciliation. void StartReconcile(); // |gaia_accounts| are the accounts in the Gaia cookie. - void FinishReconcile(const std::vector<std::string>& chrome_accounts, + void FinishReconcile(const std::string& primary_account, + const std::vector<std::string>& chrome_accounts, std::vector<gaia::ListedAccount>&& gaia_accounts); void AbortReconcile(); void CalculateIfReconcileIsDone(); void ScheduleStartReconcileIfChromeAccountsChanged(); - // Revokes tokens for all accounts in chrome_accounts but primary_account_. + // Revokes tokens for all accounts in chrome_accounts but the primary account. void RevokeAllSecondaryTokens( + const std::string& primary_account, const std::vector<std::string>& chrome_accounts); // Returns the list of valid accounts from the TokenService. @@ -229,6 +233,8 @@ class AccountReconcilor : public KeyedService, // Overriden from OAuth2TokenService::Observer. void OnEndBatchChanges() override; void OnRefreshTokensLoaded() override; + void OnAuthErrorChanged(const std::string& account_id, + const GoogleServiceAuthError& error) override; // Lock related methods. void IncrementLockCount(); @@ -274,7 +280,6 @@ class AccountReconcilor : public KeyedService, // Used during reconcile action. // These members are used to validate the tokens in OAuth2TokenService. - std::string primary_account_; std::vector<std::string> add_to_cookie_; bool chrome_accounts_changed_; diff --git a/chromium/components/signin/core/browser/account_reconcilor_delegate.cc b/chromium/components/signin/core/browser/account_reconcilor_delegate.cc index fceec4c55d5..00ac6dde298 100644 --- a/chromium/components/signin/core/browser/account_reconcilor_delegate.cc +++ b/chromium/components/signin/core/browser/account_reconcilor_delegate.cc @@ -29,9 +29,10 @@ std::string AccountReconcilorDelegate::GetFirstGaiaAccountForReconcile( return std::string(); } -bool AccountReconcilorDelegate::ShouldRevokeAllSecondaryTokensBeforeReconcile( +AccountReconcilorDelegate::RevokeTokenOption +AccountReconcilorDelegate::ShouldRevokeSecondaryTokensBeforeReconcile( const std::vector<gaia::ListedAccount>& gaia_accounts) { - return false; + return RevokeTokenOption::kDoNotRevoke; } base::TimeDelta AccountReconcilorDelegate::GetReconcileTimeout() const { diff --git a/chromium/components/signin/core/browser/account_reconcilor_delegate.h b/chromium/components/signin/core/browser/account_reconcilor_delegate.h index 8960987632a..03553a98cee 100644 --- a/chromium/components/signin/core/browser/account_reconcilor_delegate.h +++ b/chromium/components/signin/core/browser/account_reconcilor_delegate.h @@ -19,6 +19,17 @@ namespace signin { // Base class for AccountReconcilorDelegate. class AccountReconcilorDelegate { public: + // Options for revoking refresh tokens. + enum class RevokeTokenOption { + // Do not revoke the token. + kDoNotRevoke, + // Revoke the token if it is in auth error state. + kRevokeIfInError, + // Revoke the token. + // TODO(droger): remove this when Dice is launched. + kRevoke + }; + virtual ~AccountReconcilorDelegate() {} // Returns true if the reconcilor should reconcile the profile. Defaults to @@ -43,9 +54,9 @@ class AccountReconcilorDelegate { const std::string& primary_account, bool first_execution) const; - // Returns true if all secondary accounts should be cleared at the beginning - // of the reconcile. - virtual bool ShouldRevokeAllSecondaryTokensBeforeReconcile( + // Returns whether secondary accounts should be cleared at the beginning of + // the reconcile. + virtual RevokeTokenOption ShouldRevokeSecondaryTokensBeforeReconcile( const std::vector<gaia::ListedAccount>& gaia_accounts); // Called when reconcile is finished. diff --git a/chromium/components/signin/core/browser/account_reconcilor_unittest.cc b/chromium/components/signin/core/browser/account_reconcilor_unittest.cc index abbd9b6df6a..de00a76dc77 100644 --- a/chromium/components/signin/core/browser/account_reconcilor_unittest.cc +++ b/chromium/components/signin/core/browser/account_reconcilor_unittest.cc @@ -26,14 +26,13 @@ #include "components/signin/core/browser/mirror_account_reconcilor_delegate.h" #include "components/signin/core/browser/profile_management_switches.h" #include "components/signin/core/browser/profile_oauth2_token_service.h" -#include "components/signin/core/browser/signin_features.h" +#include "components/signin/core/browser/signin_buildflags.h" #include "components/signin/core/browser/signin_manager.h" #include "components/signin/core/browser/signin_metrics.h" #include "components/signin/core/browser/signin_pref_names.h" #include "components/signin/core/browser/test_signin_client.h" #include "components/sync_preferences/pref_service_syncable.h" #include "components/sync_preferences/testing_pref_service_syncable.h" -#include "google_apis/gaia/fake_oauth2_token_service_delegate.h" #include "google_apis/gaia/gaia_constants.h" #include "google_apis/gaia/gaia_urls.h" #include "google_apis/gaia/google_service_auth_error.h" @@ -228,10 +227,6 @@ class AccountReconcilorTest : public ::testing::Test { FakeSigninManagerForTesting* signin_manager() { return &signin_manager_; } FakeProfileOAuth2TokenService* token_service() { return &token_service_; } - FakeOAuth2TokenServiceDelegate* token_service_delegate() { - return static_cast<FakeOAuth2TokenServiceDelegate*>( - token_service_.GetDelegate()); - } DiceTestSigninClient* test_signin_client() { return &test_signin_client_; } AccountTrackerService* account_tracker() { return &account_tracker_; } FakeGaiaCookieManagerService* cookie_manager_service() { @@ -499,17 +494,17 @@ const AccountReconcilorTestDiceParam kDiceParams[] = { { "*xAB", "B", true, "", "*xAB", "B"}, { "*xAB", "", true, "B", "*xAB", "B"}, // Sync enabled, token error on secondary. - { "*AxB", "AB", true, "XA", "*AxB", "A"}, - { "*AxB", "BA", true, "XA", "*AxB", "A"}, - { "*AxB", "A", true, "", "*AxB", "A"}, - { "*AxB", "B", true, "XA", "*AxB", "A"}, - { "*AxB", "", true, "A", "*AxB", "A"}, + { "*AxB", "AB", true, "XA", "*A", "A"}, + { "*AxB", "BA", true, "XA", "*A", "A"}, + { "*AxB", "A", true, "", "*A", "A"}, + { "*AxB", "B", true, "XA", "*A", "A"}, + { "*AxB", "", true, "A", "*A", "A"}, // Sync enabled, token error on both accounts. - { "*xAxB", "AB", true, "X", "*xAxB", ""}, - { "*xAxB", "BA", true, "X", "*xAxB", ""}, - { "*xAxB", "A", true, "X", "*xAxB", ""}, - { "*xAxB", "B", true, "X", "*xAxB", ""}, - { "*xAxB", "", true, "", "*xAxB", ""}, + { "*xAxB", "AB", true, "X", "*xA", ""}, + { "*xAxB", "BA", true, "X", "*xA", ""}, + { "*xAxB", "A", true, "X", "*xA", ""}, + { "*xAxB", "B", true, "X", "*xA", ""}, + { "*xAxB", "", true, "", "*xA", ""}, // Sync disabled. { "AB", "AB", true, "", "AB", "AB"}, { "AB", "BA", true, "", "AB", "BA"}, @@ -517,23 +512,23 @@ const AccountReconcilorTestDiceParam kDiceParams[] = { { "AB", "B", true, "A", "AB", "BA"}, { "AB", "", true, "AB", "AB", "AB"}, // Sync disabled, token error on first account. - { "xAB", "AB", true, "XB", "xAB", "B"}, - { "xAB", "BA", true, "XB", "xAB", "B"}, - { "xAB", "A", true, "XB", "xAB", "B"}, - { "xAB", "B", true, "", "xAB", "B"}, - { "xAB", "", true, "B", "xAB", "B"}, + { "xAB", "AB", true, "XB", "B", "B"}, + { "xAB", "BA", true, "XB", "B", "B"}, + { "xAB", "A", true, "XB", "B", "B"}, + { "xAB", "B", true, "", "B", "B"}, + { "xAB", "", true, "B", "B", "B"}, // Sync disabled, token error on second account . - { "AxB", "AB", true, "XA", "AxB", "A"}, - { "AxB", "BA", true, "XA", "AxB", "A"}, - { "AxB", "A", true, "", "AxB", "A"}, - { "AxB", "B", true, "XA", "AxB", "A"}, - { "AxB", "", true, "A", "AxB", "A"}, + { "AxB", "AB", true, "XA", "A", "A"}, + { "AxB", "BA", true, "XA", "A", "A"}, + { "AxB", "A", true, "", "A", "A"}, + { "AxB", "B", true, "XA", "A", "A"}, + { "AxB", "", true, "A", "A", "A"}, // Sync disabled, token error on both accounts. - { "xAxB", "AB", true, "X", "xAxB", ""}, - { "xAxB", "BA", true, "X", "xAxB", ""}, - { "xAxB", "A", true, "X", "xAxB", ""}, - { "xAxB", "B", true, "X", "xAxB", ""}, - { "xAxB", "", true, "", "xAxB", ""}, + { "xAxB", "AB", true, "X", "", ""}, + { "xAxB", "BA", true, "X", "", ""}, + { "xAxB", "A", true, "X", "", ""}, + { "xAxB", "B", true, "X", "", ""}, + { "xAxB", "", true, "", "", ""}, // Chrome is running: Do not change the order of accounts already present in // the Gaia cookies. @@ -550,17 +545,17 @@ const AccountReconcilorTestDiceParam kDiceParams[] = { { "*xAB", "B", false, "", "*xAB", "B"}, { "*xAB", "", false, "B", "*xAB", "B"}, // Sync enabled, token error on secondary. - { "*AxB", "AB", false, "XA", "*AxB", "A"}, - { "*AxB", "BA", false, "XA", "*AxB", "A"}, - { "*AxB", "A", false, "", "*AxB", "A"}, - { "*AxB", "B", false, "XA", "*AxB", "A"}, - { "*AxB", "", false, "A", "*AxB", "A"}, + { "*AxB", "AB", false, "XA", "*A", "A"}, + { "*AxB", "BA", false, "XA", "*A", "A"}, + { "*AxB", "A", false, "", "*A", "A"}, + { "*AxB", "B", false, "XA", "*A", "A"}, + { "*AxB", "", false, "A", "*A", "A"}, // Sync enabled, token error on both accounts. - { "*xAxB", "AB", false, "X", "*xAxB", ""}, - { "*xAxB", "BA", false, "X", "*xAxB", ""}, - { "*xAxB", "A", false, "X", "*xAxB", ""}, - { "*xAxB", "B", false, "X", "*xAxB", ""}, - { "*xAxB", "", false, "", "*xAxB", ""}, + { "*xAxB", "AB", false, "X", "*xA", ""}, + { "*xAxB", "BA", false, "X", "*xA", ""}, + { "*xAxB", "A", false, "X", "*xA", ""}, + { "*xAxB", "B", false, "X", "*xA", ""}, + { "*xAxB", "", false, "", "*xA", ""}, // Sync disabled. { "AB", "AB", false, "", "AB", "AB"}, { "AB", "BA", false, "", "AB", "BA"}, @@ -568,23 +563,23 @@ const AccountReconcilorTestDiceParam kDiceParams[] = { { "AB", "B", false, "A", "AB", "BA"}, { "AB", "", false, "AB", "AB", "AB"}, // Sync disabled, token error on first account. - { "xAB", "AB", false, "X", "xA", ""}, - { "xAB", "BA", false, "XB", "xAB", "B"}, - { "xAB", "A", false, "X", "xA", ""}, - { "xAB", "B", false, "", "xAB", "B"}, - { "xAB", "", false, "B", "xAB", "B"}, + { "xAB", "AB", false, "X", "", ""}, + { "xAB", "BA", false, "XB", "B", "B"}, + { "xAB", "A", false, "X", "", ""}, + { "xAB", "B", false, "", "B", "B"}, + { "xAB", "", false, "B", "B", "B"}, // Sync disabled, token error on second account. - { "AxB", "AB", false, "XA", "AxB", "A"}, - { "AxB", "BA", false, "X", "xB", ""}, - { "AxB", "A", false, "", "AxB", "A"}, - { "AxB", "B", false, "X", "xB", ""}, - { "AxB", "", false, "A", "AxB", "A"}, + { "AxB", "AB", false, "XA", "A", "A"}, + { "AxB", "BA", false, "X", "", ""}, + { "AxB", "A", false, "", "A", "A"}, + { "AxB", "B", false, "X", "", ""}, + { "AxB", "", false, "A", "A", "A"}, // Sync disabled, token error on both accounts. - { "xAxB", "AB", false, "X", "xAxB", ""}, - { "xAxB", "BA", false, "X", "xAxB", ""}, - { "xAxB", "A", false, "X", "xAxB", ""}, - { "xAxB", "B", false, "X", "xAxB", ""}, - { "xAxB", "", false, "", "xAxB", ""}, + { "xAxB", "AB", false, "X", "", ""}, + { "xAxB", "BA", false, "X", "", ""}, + { "xAxB", "A", false, "X", "", ""}, + { "xAxB", "B", false, "X", "", ""}, + { "xAxB", "", false, "", "", ""}, // Miscellaneous cases. // Check that unknown Gaia accounts are signed out. @@ -598,8 +593,8 @@ const AccountReconcilorTestDiceParam kDiceParams[] = { // Required for idempotency check. { "", "", false, "", "", ""}, { "*A", "A", false, "", "*A", "A"}, - { "xB", "", false, "", "xB", ""}, - { "xA", "", false, "", "xA", ""}, + { "A", "A", false, "", "A", "A"}, + { "B", "B", false, "", "B", "B"}, { "*xA", "", false, "", "*xA", ""}, { "*xAB", "B", false, "", "*xAB", "B"}, }; @@ -660,9 +655,8 @@ class AccountReconcilorTestDice std::string account_id = PickAccountIdForAccount(token.gaia_id, token.email); EXPECT_TRUE(token_service()->RefreshTokenIsAvailable(account_id)); - EXPECT_EQ( - token.has_error, - token_service()->GetDelegate()->RefreshTokenHasError(account_id)); + EXPECT_EQ(token.has_error, + token_service()->RefreshTokenHasError(account_id)); if (token.is_authenticated) { EXPECT_EQ(account_id, signin_manager()->GetAuthenticatedAccountId()); authenticated_account_found = true; @@ -691,6 +685,24 @@ class AccountReconcilorTestDice ADD_FAILURE() << "Could not check that reconcile is idempotent."; } + void ConfigureCookieManagerService(const std::string& cookies) { + if (cookies.size() == 0) { + cookie_manager_service()->SetListAccountsResponseNoAccounts(); + } else if (cookies.size() == 1) { + cookie_manager_service()->SetListAccountsResponseOneAccount( + accounts_[cookies[0]].email.c_str(), + accounts_[cookies[0]].gaia_id.c_str()); + } else { + ASSERT_EQ(2u, cookies.size()); + cookie_manager_service()->SetListAccountsResponseTwoAccounts( + accounts_[cookies[0]].email.c_str(), + accounts_[cookies[0]].gaia_id.c_str(), + accounts_[cookies[1]].email.c_str(), + accounts_[cookies[1]].gaia_id.c_str()); + } + cookie_manager_service()->set_list_accounts_stale_for_testing(true); + } + std::map<char, Account> accounts_; }; @@ -714,7 +726,7 @@ TEST_P(AccountReconcilorTestDice, TableRowTest) { else token_service()->UpdateCredentials(account_id, "refresh_token"); if (token.has_error) { - token_service_delegate()->SetLastErrorForAccount( + token_service()->UpdateAuthErrorForTesting( account_id, GoogleServiceAuthError( GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS)); } @@ -723,20 +735,7 @@ TEST_P(AccountReconcilorTestDice, TableRowTest) { // Setup cookies. std::string cookies(GetParam().cookies); - if (cookies.size() == 0) { - cookie_manager_service()->SetListAccountsResponseNoAccounts(); - } else if (cookies.size() == 1) { - cookie_manager_service()->SetListAccountsResponseOneAccount( - accounts_[GetParam().cookies[0]].email.c_str(), - accounts_[GetParam().cookies[0]].gaia_id.c_str()); - } else { - ASSERT_EQ(2u, cookies.size()); - cookie_manager_service()->SetListAccountsResponseTwoAccounts( - accounts_[GetParam().cookies[0]].email.c_str(), - accounts_[GetParam().cookies[0]].gaia_id.c_str(), - accounts_[GetParam().cookies[1]].email.c_str(), - accounts_[GetParam().cookies[1]].gaia_id.c_str()); - } + ConfigureCookieManagerService(cookies); // Call list accounts now so that the next call completes synchronously. cookie_manager_service()->ListAccounts(nullptr, nullptr, "foo"); @@ -782,10 +781,16 @@ TEST_P(AccountReconcilorTestDice, TableRowTest) { ASSERT_FALSE(reconcilor->is_reconcile_started_); ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor->GetState()); VerifyCurrentTokens(ParseTokenString(GetParam().tokens_after_reconcile)); + + testing::Mock::VerifyAndClearExpectations(GetMockReconcilor()); + // Another reconcile is sometimes triggered if Chrome accounts have changed. // Allow it to finish. - cookie_manager_service()->SetListAccountsResponseNoAccounts(); - cookie_manager_service()->set_list_accounts_stale_for_testing(true); + EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(testing::_)) + .WillRepeatedly(testing::Return()); + EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction()) + .WillRepeatedly(testing::Return()); + ConfigureCookieManagerService(""); base::RunLoop().RunUntilIdle(); } @@ -1014,6 +1019,34 @@ TEST_F(AccountReconcilorTest, UnverifiedAccountMerge) { ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor->GetState()); } +// Regression test for https://crbug.com/825143 +// Checks that the primary account is not signed out when it changes during the +// reconcile. +TEST_F(AccountReconcilorTest, HandleSigninDuringReconcile) { + SetAccountConsistency( + signin::AccountConsistencyMethod::kDicePrepareMigration); + + cookie_manager_service()->SetListAccountsResponseNoAccounts(); + AccountReconcilor* reconcilor = GetMockReconcilor(); + ASSERT_EQ(signin::AccountReconcilorDelegate::RevokeTokenOption::kRevoke, + reconcilor->delegate_->ShouldRevokeSecondaryTokensBeforeReconcile( + std::vector<gaia::ListedAccount>())); + + // Signin during reconcile. + reconcilor->StartReconcile(); + ASSERT_TRUE(reconcilor->is_reconcile_started_); + const std::string account_id = + ConnectProfileToAccount("12345", "user@gmail.com"); + EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(account_id)).Times(1); + base::RunLoop().RunUntilIdle(); + SimulateAddAccountToCookieCompleted(reconcilor, account_id, + GoogleServiceAuthError::AuthErrorNone()); + ASSERT_FALSE(reconcilor->is_reconcile_started_); + + // The account has not been deleted. + EXPECT_TRUE(token_service()->RefreshTokenIsAvailable(account_id)); +} + // Tests that the Dice migration happens after a no-op reconcile. TEST_F(AccountReconcilorTest, DiceMigrationAfterNoop) { // Enable Dice migration. @@ -1456,6 +1489,60 @@ TEST_F(AccountReconcilorTest, StartReconcileAddToCookie) { testing::ContainerEq(expected_counts)); } +TEST_F(AccountReconcilorTest, AuthErrorTriggersListAccount) { + class TestGaiaCookieObserver : public GaiaCookieManagerService::Observer { + public: + void OnGaiaAccountsInCookieUpdated( + const std::vector<gaia::ListedAccount>& accounts, + const std::vector<gaia::ListedAccount>& signed_out_accounts, + const GoogleServiceAuthError& error) override { + cookies_updated_ = true; + } + + bool cookies_updated_ = false; + }; + +#if BUILDFLAG(ENABLE_DICE_SUPPORT) + signin::AccountConsistencyMethod account_consistency = + signin::AccountConsistencyMethod::kDice; + SetAccountConsistency(account_consistency); +#else + signin::AccountConsistencyMethod account_consistency = + signin::AccountConsistencyMethod::kMirror; + SetAccountConsistency(account_consistency); +#endif + + // Add one account to Chrome and instantiate the reconcilor. + const std::string account_id = + ConnectProfileToAccount("12345", "user@gmail.com"); + token_service()->UpdateCredentials(account_id, "refresh_token"); + TestGaiaCookieObserver observer; + cookie_manager_service()->AddObserver(&observer); + AccountReconcilor* reconcilor = GetMockReconcilor(); + base::RunLoop().RunUntilIdle(); + ASSERT_FALSE(reconcilor->is_reconcile_started_); + cookie_manager_service()->SetListAccountsResponseOneAccount("user@gmail.com", + "12345"); + if (account_consistency == signin::AccountConsistencyMethod::kDice) { + EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction()) + .Times(1); + } + + // Set an authentication error. + ASSERT_FALSE(observer.cookies_updated_); + token_service()->UpdateAuthErrorForTesting( + account_id, GoogleServiceAuthError::FromInvalidGaiaCredentialsReason( + GoogleServiceAuthError::InvalidGaiaCredentialsReason:: + CREDENTIALS_REJECTED_BY_SERVER)); + base::RunLoop().RunUntilIdle(); + + // Check that a call to ListAccount was triggered. + EXPECT_TRUE(observer.cookies_updated_); + testing::Mock::VerifyAndClearExpectations(GetMockReconcilor()); + + cookie_manager_service()->RemoveObserver(&observer); +} + #if !defined(OS_CHROMEOS) // This test does not run on ChromeOS because it calls // FakeSigninManagerForTesting::SignOut() which doesn't exist for ChromeOS. @@ -1807,7 +1894,7 @@ TEST_F(AccountReconcilorTest, NoLoopWithBadPrimary) { // Now that we've tried once, the token service knows that the primary // account has an auth error. - token_service_delegate()->SetLastErrorForAccount(account_id1, error); + token_service()->UpdateAuthErrorForTesting(account_id1, error); // A second attempt to reconcile should be a noop. reconcilor->StartReconcile(); @@ -1826,7 +1913,7 @@ TEST_F(AccountReconcilorTest, WontMergeAccountsWithError) { token_service()->UpdateCredentials(account_id2, "refresh_token"); // Mark the secondary account in auth error state. - token_service_delegate()->SetLastErrorForAccount( + token_service()->UpdateAuthErrorForTesting( account_id2, GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS)); diff --git a/chromium/components/signin/core/browser/android/BUILD.gn b/chromium/components/signin/core/browser/android/BUILD.gn index 2c571ace8a7..ad4424314ee 100644 --- a/chromium/components/signin/core/browser/android/BUILD.gn +++ b/chromium/components/signin/core/browser/android/BUILD.gn @@ -30,6 +30,7 @@ android_library("java") { "java/src/org/chromium/components/signin/AccountsChangeObserver.java", "java/src/org/chromium/components/signin/AuthException.java", "java/src/org/chromium/components/signin/ChildAccountInfoFetcher.java", + "java/src/org/chromium/components/signin/ChildAccountStatus.java", "java/src/org/chromium/components/signin/ChromeSigninController.java", "java/src/org/chromium/components/signin/GmsAvailabilityException.java", "java/src/org/chromium/components/signin/GmsJustUpdatedException.java", diff --git a/chromium/components/signin/core/browser/child_account_info_fetcher_android.h b/chromium/components/signin/core/browser/child_account_info_fetcher_android.h index aee7ed9a473..0b73cce4592 100644 --- a/chromium/components/signin/core/browser/child_account_info_fetcher_android.h +++ b/chromium/components/signin/core/browser/child_account_info_fetcher_android.h @@ -9,7 +9,6 @@ #include <string> #include "base/android/scoped_java_ref.h" -#include "base/memory/ptr_util.h" #include "components/signin/core/browser/child_account_info_fetcher.h" class AccountFetcherService; diff --git a/chromium/components/signin/core/browser/cookie_settings_util.cc b/chromium/components/signin/core/browser/cookie_settings_util.cc new file mode 100644 index 00000000000..5423303500f --- /dev/null +++ b/chromium/components/signin/core/browser/cookie_settings_util.cc @@ -0,0 +1,36 @@ +// Copyright 2018 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 "components/signin/core/browser/cookie_settings_util.h" + +#include "components/content_settings/core/browser/cookie_settings.h" +#include "google_apis/gaia/gaia_urls.h" +#include "url/gurl.h" + +namespace signin { + +bool SettingsAllowSigninCookies( + const content_settings::CookieSettings* cookie_settings) { + GURL gaia_url = GaiaUrls::GetInstance()->gaia_url(); + GURL google_url = GaiaUrls::GetInstance()->google_url(); + return cookie_settings && + cookie_settings->IsCookieAccessAllowed(gaia_url, gaia_url) && + cookie_settings->IsCookieAccessAllowed(google_url, google_url); +} + +bool SettingsDeleteSigninCookiesOnExit( + const content_settings::CookieSettings* cookie_settings) { + GURL gaia_url = GaiaUrls::GetInstance()->gaia_url(); + GURL google_url = GaiaUrls::GetInstance()->google_url(); + ContentSettingsForOneType settings; + cookie_settings->GetCookieSettings(&settings); + + return !cookie_settings || + cookie_settings->ShouldDeleteCookieOnExit( + settings, "." + gaia_url.host(), true) || + cookie_settings->ShouldDeleteCookieOnExit( + settings, "." + google_url.host(), true); +} + +} // namespace signin diff --git a/chromium/components/signin/core/browser/cookie_settings_util.h b/chromium/components/signin/core/browser/cookie_settings_util.h new file mode 100644 index 00000000000..d2fdcd45a79 --- /dev/null +++ b/chromium/components/signin/core/browser/cookie_settings_util.h @@ -0,0 +1,24 @@ +// Copyright 2018 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. + +#ifndef COMPONENTS_SIGNIN_CORE_BROWSER_COOKIE_SETTINGS_UTIL_H_ +#define COMPONENTS_SIGNIN_CORE_BROWSER_COOKIE_SETTINGS_UTIL_H_ + +namespace content_settings { +class CookieSettings; +} + +namespace signin { + +// Returns true if signin cookies are allowed. +bool SettingsAllowSigninCookies( + const content_settings::CookieSettings* cookie_settings); + +// Returns true if signin cookies are cleared on exit. +bool SettingsDeleteSigninCookiesOnExit( + const content_settings::CookieSettings* cookie_settings); + +} // namespace signin + +#endif // COMPONENTS_SIGNIN_CORE_BROWSER_COOKIE_SETTINGS_UTIL_H_ diff --git a/chromium/components/signin/core/browser/dice_account_reconcilor_delegate.cc b/chromium/components/signin/core/browser/dice_account_reconcilor_delegate.cc index eeadbe97c15..9da846066c5 100644 --- a/chromium/components/signin/core/browser/dice_account_reconcilor_delegate.cc +++ b/chromium/components/signin/core/browser/dice_account_reconcilor_delegate.cc @@ -100,16 +100,22 @@ std::string DiceAccountReconcilorDelegate::GetFirstGaiaAccountForReconcile( return std::string(); } -bool DiceAccountReconcilorDelegate:: - ShouldRevokeAllSecondaryTokensBeforeReconcile( - const std::vector<gaia::ListedAccount>& gaia_accounts) { +AccountReconcilorDelegate::RevokeTokenOption +DiceAccountReconcilorDelegate::ShouldRevokeSecondaryTokensBeforeReconcile( + const std::vector<gaia::ListedAccount>& gaia_accounts) { // During the Dice migration step, before Dice is actually enabled, chrome // tokens must be cleared when the cookies are cleared. - return DiceMethodGreaterOrEqual( - account_consistency_, - AccountConsistencyMethod::kDicePrepareMigration) && - (account_consistency_ != AccountConsistencyMethod::kDice) && - gaia_accounts.empty(); + if (DiceMethodGreaterOrEqual( + account_consistency_, + AccountConsistencyMethod::kDicePrepareMigration) && + (account_consistency_ != AccountConsistencyMethod::kDice) && + gaia_accounts.empty()) { + return RevokeTokenOption::kRevoke; + } + + return (account_consistency_ == AccountConsistencyMethod::kDice) + ? RevokeTokenOption::kRevokeIfInError + : RevokeTokenOption::kDoNotRevoke; } void DiceAccountReconcilorDelegate::OnReconcileFinished( diff --git a/chromium/components/signin/core/browser/dice_account_reconcilor_delegate.h b/chromium/components/signin/core/browser/dice_account_reconcilor_delegate.h index 7acc62604fd..b4cb70586b2 100644 --- a/chromium/components/signin/core/browser/dice_account_reconcilor_delegate.h +++ b/chromium/components/signin/core/browser/dice_account_reconcilor_delegate.h @@ -30,7 +30,7 @@ class DiceAccountReconcilorDelegate : public AccountReconcilorDelegate { const std::vector<gaia::ListedAccount>& gaia_accounts, const std::string& primary_account, bool first_execution) const override; - bool ShouldRevokeAllSecondaryTokensBeforeReconcile( + RevokeTokenOption ShouldRevokeSecondaryTokensBeforeReconcile( const std::vector<gaia::ListedAccount>& gaia_accounts) override; void OnReconcileFinished(const std::string& first_account, bool reconcile_is_noop) override; diff --git a/chromium/components/signin/core/browser/dice_account_reconcilor_delegate_unittest.cc b/chromium/components/signin/core/browser/dice_account_reconcilor_delegate_unittest.cc index 784dcddb25f..78ef2db7cbe 100644 --- a/chromium/components/signin/core/browser/dice_account_reconcilor_delegate_unittest.cc +++ b/chromium/components/signin/core/browser/dice_account_reconcilor_delegate_unittest.cc @@ -33,11 +33,13 @@ TEST(DiceAccountReconcilorDelegateTest, RevokeTokens) { sync_preferences::TestingPrefServiceSyncable pref_service; TestSigninClient client(&pref_service); { - // Dice is enabled, don't revoke. + // Dice is enabled, revoke only tokens in error state. DiceAccountReconcilorDelegate delegate(&client, AccountConsistencyMethod::kDice); - EXPECT_FALSE(delegate.ShouldRevokeAllSecondaryTokensBeforeReconcile( - std::vector<gaia::ListedAccount>())); + EXPECT_EQ( + signin::AccountReconcilorDelegate::RevokeTokenOption::kRevokeIfInError, + delegate.ShouldRevokeSecondaryTokensBeforeReconcile( + std::vector<gaia::ListedAccount>())); } { DiceAccountReconcilorDelegate delegate( @@ -45,11 +47,14 @@ TEST(DiceAccountReconcilorDelegateTest, RevokeTokens) { // Gaia accounts are not empty, don't revoke. gaia::ListedAccount gaia_account; gaia_account.id = "other"; - EXPECT_FALSE(delegate.ShouldRevokeAllSecondaryTokensBeforeReconcile( - std::vector<gaia::ListedAccount>{gaia_account})); + EXPECT_EQ( + signin::AccountReconcilorDelegate::RevokeTokenOption::kDoNotRevoke, + delegate.ShouldRevokeSecondaryTokensBeforeReconcile( + std::vector<gaia::ListedAccount>{gaia_account})); // Revoke. - EXPECT_TRUE(delegate.ShouldRevokeAllSecondaryTokensBeforeReconcile( - std::vector<gaia::ListedAccount>())); + EXPECT_EQ(signin::AccountReconcilorDelegate::RevokeTokenOption::kRevoke, + delegate.ShouldRevokeSecondaryTokensBeforeReconcile( + std::vector<gaia::ListedAccount>())); } } diff --git a/chromium/components/signin/core/browser/fake_gaia_cookie_manager_service.cc b/chromium/components/signin/core/browser/fake_gaia_cookie_manager_service.cc index e7416d1acf2..897b8739b66 100644 --- a/chromium/components/signin/core/browser/fake_gaia_cookie_manager_service.cc +++ b/chromium/components/signin/core/browser/fake_gaia_cookie_manager_service.cc @@ -4,7 +4,6 @@ #include "components/signin/core/browser/fake_gaia_cookie_manager_service.h" -#include "base/memory/ptr_util.h" #include "base/strings/stringprintf.h" #include "components/signin/core/browser/profile_oauth2_token_service.h" #include "google_apis/gaia/gaia_constants.h" diff --git a/chromium/components/signin/core/browser/fake_profile_oauth2_token_service.cc b/chromium/components/signin/core/browser/fake_profile_oauth2_token_service.cc index e8a19250234..9710636cf6e 100644 --- a/chromium/components/signin/core/browser/fake_profile_oauth2_token_service.cc +++ b/chromium/components/signin/core/browser/fake_profile_oauth2_token_service.cc @@ -35,6 +35,7 @@ void FakeProfileOAuth2TokenService::IssueAllTokensForAccount( const std::string& account_id, const std::string& access_token, const base::Time& expiration) { + DCHECK(!auto_post_fetch_response_on_message_loop_); CompleteRequests(account_id, true, ScopeSet(), GoogleServiceAuthError::AuthErrorNone(), access_token, expiration); @@ -43,6 +44,7 @@ void FakeProfileOAuth2TokenService::IssueAllTokensForAccount( void FakeProfileOAuth2TokenService::IssueErrorForAllPendingRequestsForAccount( const std::string& account_id, const GoogleServiceAuthError& error) { + DCHECK(!auto_post_fetch_response_on_message_loop_); CompleteRequests(account_id, true, ScopeSet(), error, std::string(), base::Time()); } @@ -51,6 +53,7 @@ void FakeProfileOAuth2TokenService::IssueTokenForScope( const ScopeSet& scope, const std::string& access_token, const base::Time& expiration) { + DCHECK(!auto_post_fetch_response_on_message_loop_); CompleteRequests("", false, scope, GoogleServiceAuthError::AuthErrorNone(), access_token, expiration); } @@ -58,22 +61,31 @@ void FakeProfileOAuth2TokenService::IssueTokenForScope( void FakeProfileOAuth2TokenService::IssueErrorForScope( const ScopeSet& scope, const GoogleServiceAuthError& error) { + DCHECK(!auto_post_fetch_response_on_message_loop_); CompleteRequests("", false, scope, error, std::string(), base::Time()); } void FakeProfileOAuth2TokenService::IssueErrorForAllPendingRequests( const GoogleServiceAuthError& error) { + DCHECK(!auto_post_fetch_response_on_message_loop_); CompleteRequests("", true, ScopeSet(), error, std::string(), base::Time()); } void FakeProfileOAuth2TokenService::IssueTokenForAllPendingRequests( const std::string& access_token, const base::Time& expiration) { + DCHECK(!auto_post_fetch_response_on_message_loop_); CompleteRequests("", true, ScopeSet(), GoogleServiceAuthError::AuthErrorNone(), access_token, expiration); } +void FakeProfileOAuth2TokenService::UpdateAuthErrorForTesting( + const std::string& account_id, + const GoogleServiceAuthError& error) { + ProfileOAuth2TokenService::UpdateAuthError(account_id, error); +} + void FakeProfileOAuth2TokenService::CompleteRequests( const std::string& account_id, bool all_scopes, @@ -125,9 +137,11 @@ void FakeProfileOAuth2TokenService::FetchOAuth2Token( if (auto_post_fetch_response_on_message_loop_) { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, - base::Bind(&FakeProfileOAuth2TokenService::IssueAllTokensForAccount, - weak_ptr_factory_.GetWeakPtr(), account_id, "access_token", - base::Time::Max())); + base::BindOnce(&FakeProfileOAuth2TokenService::CompleteRequests, + weak_ptr_factory_.GetWeakPtr(), account_id, + /*all_scoped=*/true, ScopeSet(), + GoogleServiceAuthError::AuthErrorNone(), "access_token", + base::Time::Max())); } } diff --git a/chromium/components/signin/core/browser/fake_profile_oauth2_token_service.h b/chromium/components/signin/core/browser/fake_profile_oauth2_token_service.h index f2301fdbd80..9edcd086071 100644 --- a/chromium/components/signin/core/browser/fake_profile_oauth2_token_service.h +++ b/chromium/components/signin/core/browser/fake_profile_oauth2_token_service.h @@ -81,6 +81,10 @@ class FakeProfileOAuth2TokenService : public ProfileOAuth2TokenService { auto_post_fetch_response_on_message_loop_ = auto_post_response; } + // Calls ProfileOAuth2TokenService::UpdateAuthError(). Exposed for testing. + void UpdateAuthErrorForTesting(const std::string& account_id, + const GoogleServiceAuthError& error); + protected: // OAuth2TokenService overrides. void FetchOAuth2Token(RequestImpl* request, diff --git a/chromium/components/signin/core/browser/fake_signin_manager.cc b/chromium/components/signin/core/browser/fake_signin_manager.cc index ab4c9747d9c..a59df220373 100644 --- a/chromium/components/signin/core/browser/fake_signin_manager.cc +++ b/chromium/components/signin/core/browser/fake_signin_manager.cc @@ -88,7 +88,7 @@ void FakeSigninManager::FailSignin(const GoogleServiceAuthError& error) { void FakeSigninManager::DoSignOut( signin_metrics::ProfileSignout signout_source_metric, signin_metrics::SignoutDelete signout_delete_metric, - bool remove_all_accounts) { + RemoveAccountsOption remove_option) { if (IsSignoutProhibited()) return; set_auth_in_progress(std::string()); @@ -97,8 +97,19 @@ void FakeSigninManager::DoSignOut( const std::string account_id = GetAuthenticatedAccountId(); const std::string username = account_info.email; authenticated_account_id_.clear(); - if (token_service_ && remove_all_accounts) - token_service_->RevokeAllCredentials(); + switch (remove_option) { + case RemoveAccountsOption::kRemoveAllAccounts: + if (token_service_) + token_service_->RevokeAllCredentials(); + break; + case RemoveAccountsOption::kRemoveAuthenticatedAccountIfInError: + if (token_service_ && token_service_->RefreshTokenHasError(account_id)) + token_service_->RevokeCredentials(account_id); + break; + case RemoveAccountsOption::kKeepAllAccounts: + // Do nothing. + break; + } FireGoogleSignedOut(account_id, account_info); } diff --git a/chromium/components/signin/core/browser/fake_signin_manager.h b/chromium/components/signin/core/browser/fake_signin_manager.h index 26cc7f8289e..3f3e9cee7a1 100644 --- a/chromium/components/signin/core/browser/fake_signin_manager.h +++ b/chromium/components/signin/core/browser/fake_signin_manager.h @@ -66,7 +66,7 @@ class FakeSigninManager : public SigninManager { protected: void DoSignOut(signin_metrics::ProfileSignout signout_source_metric, signin_metrics::SignoutDelete signout_delete_metric, - bool remove_all_accounts) override; + RemoveAccountsOption remove_option) override; // Username specified in StartSignInWithRefreshToken() call. std::string username_; diff --git a/chromium/components/signin/core/browser/profile_management_switches.cc b/chromium/components/signin/core/browser/profile_management_switches.cc index 851bf9f851b..a26592c75da 100644 --- a/chromium/components/signin/core/browser/profile_management_switches.cc +++ b/chromium/components/signin/core/browser/profile_management_switches.cc @@ -12,7 +12,7 @@ #include "base/metrics/field_trial_params.h" #include "build/build_config.h" #include "components/pref_registry/pref_registry_syncable.h" -#include "components/signin/core/browser/signin_features.h" +#include "components/signin/core/browser/signin_buildflags.h" #include "components/signin/core/browser/signin_switches.h" #if defined(OS_CHROMEOS) @@ -74,6 +74,9 @@ const char kAccountConsistencyFeatureMethodDicePrepareMigration[] = const char kAccountConsistencyFeatureMethodDiceMigration[] = "dice_migration"; const char kAccountConsistencyFeatureMethodDice[] = "dice"; +const base::Feature kUnifiedConsent{"UnifiedConsent", + base::FEATURE_DISABLED_BY_DEFAULT}; + bool DiceMethodGreaterOrEqual(AccountConsistencyMethod a, AccountConsistencyMethod b) { DCHECK_NE(AccountConsistencyMethod::kMirror, a); @@ -116,7 +119,10 @@ AccountConsistencyMethod GetAccountConsistencyMethod() { std::string method_value = base::GetFieldTrialParamValueByFeature( kAccountConsistencyFeature, kAccountConsistencyFeatureMethodParameter); - if (method_value == kAccountConsistencyFeatureMethodDiceFixAuthErrors) + if (method_value == kAccountConsistencyFeatureMethodMirror) + return AccountConsistencyMethod::kMirror; +#if BUILDFLAG(ENABLE_DICE_SUPPORT) + else if (method_value == kAccountConsistencyFeatureMethodDiceFixAuthErrors) return AccountConsistencyMethod::kDiceFixAuthErrors; else if (method_value == kAccountConsistencyFeatureMethodDicePrepareMigration) return AccountConsistencyMethod::kDicePrepareMigration; @@ -124,8 +130,7 @@ AccountConsistencyMethod GetAccountConsistencyMethod() { return AccountConsistencyMethod::kDiceMigration; else if (method_value == kAccountConsistencyFeatureMethodDice) return AccountConsistencyMethod::kDice; - else if (method_value == kAccountConsistencyFeatureMethodMirror) - return AccountConsistencyMethod::kMirror; +#endif return kDefaultMethod; } diff --git a/chromium/components/signin/core/browser/profile_management_switches.h b/chromium/components/signin/core/browser/profile_management_switches.h index 330a1a37a13..abc74d67ade 100644 --- a/chromium/components/signin/core/browser/profile_management_switches.h +++ b/chromium/components/signin/core/browser/profile_management_switches.h @@ -37,6 +37,9 @@ extern const char kAccountConsistencyFeatureMethodDicePrepareMigration[]; extern const char kAccountConsistencyFeatureMethodDiceMigration[]; extern const char kAccountConsistencyFeatureMethodDice[]; +// Improved and unified consent for privacy-related features. +extern const base::Feature kUnifiedConsent; + // TODO(https://crbug.com/777774): Cleanup this enum and remove related // functions once Dice is fully rolled out, and/or Mirror code is removed on // desktop. diff --git a/chromium/components/signin/core/browser/profile_management_switches_unittest.cc b/chromium/components/signin/core/browser/profile_management_switches_unittest.cc index 8aa9c2b2907..c0f40463050 100644 --- a/chromium/components/signin/core/browser/profile_management_switches_unittest.cc +++ b/chromium/components/signin/core/browser/profile_management_switches_unittest.cc @@ -11,7 +11,7 @@ #include "base/message_loop/message_loop.h" #include "components/prefs/pref_member.h" #include "components/signin/core/browser/scoped_account_consistency.h" -#include "components/signin/core/browser/signin_features.h" +#include "components/signin/core/browser/signin_buildflags.h" #include "components/sync_preferences/testing_pref_service_syncable.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/chromium/components/signin/core/browser/scoped_account_consistency.cc b/chromium/components/signin/core/browser/scoped_account_consistency.cc index 952a08f0188..820386156d7 100644 --- a/chromium/components/signin/core/browser/scoped_account_consistency.cc +++ b/chromium/components/signin/core/browser/scoped_account_consistency.cc @@ -12,7 +12,7 @@ #include "base/feature_list.h" #include "base/logging.h" #include "base/test/scoped_feature_list.h" -#include "components/signin/core/browser/signin_features.h" +#include "components/signin/core/browser/signin_buildflags.h" namespace signin { diff --git a/chromium/components/signin/core/browser/signin_error_controller.cc b/chromium/components/signin/core/browser/signin_error_controller.cc index c9d53d0f29f..312037770f0 100644 --- a/chromium/components/signin/core/browser/signin_error_controller.cc +++ b/chromium/components/signin/core/browser/signin_error_controller.cc @@ -96,7 +96,7 @@ void SigninErrorController::AuthStatusChanged() { } if (error_changed) { - signin_metrics::LogAuthError(auth_error_.state()); + signin_metrics::LogAuthError(auth_error_); for (auto& observer : observer_list_) observer.OnErrorChanged(); } diff --git a/chromium/components/signin/core/browser/signin_header_helper.cc b/chromium/components/signin/core/browser/signin_header_helper.cc index 6c5a0eca4aa..720d7266f01 100644 --- a/chromium/components/signin/core/browser/signin_header_helper.cc +++ b/chromium/components/signin/core/browser/signin_header_helper.cc @@ -9,11 +9,10 @@ #include "base/logging.h" #include "base/macros.h" #include "base/strings/string_split.h" -#include "components/content_settings/core/browser/cookie_settings.h" #include "components/google/core/browser/google_util.h" #include "components/signin/core/browser/chrome_connected_header_helper.h" +#include "components/signin/core/browser/cookie_settings_util.h" #include "google_apis/gaia/gaia_auth_util.h" -#include "google_apis/gaia/gaia_urls.h" #include "net/base/escape.h" #include "net/url_request/url_request.h" @@ -66,15 +65,6 @@ DiceResponseParams::EnableSyncInfo::~EnableSyncInfo() {} DiceResponseParams::EnableSyncInfo::EnableSyncInfo(const EnableSyncInfo&) = default; -bool SettingsAllowSigninCookies( - const content_settings::CookieSettings* cookie_settings) { - GURL gaia_url = GaiaUrls::GetInstance()->gaia_url(); - GURL google_url = GaiaUrls::GetInstance()->google_url(); - return cookie_settings && - cookie_settings->IsCookieAccessAllowed(gaia_url, gaia_url) && - cookie_settings->IsCookieAccessAllowed(google_url, google_url); -} - std::string BuildMirrorRequestCookieIfPossible( const GURL& url, const std::string& account_id, diff --git a/chromium/components/signin/core/browser/signin_header_helper.h b/chromium/components/signin/core/browser/signin_header_helper.h index 2d0f0b2ae49..7e10bf1fc28 100644 --- a/chromium/components/signin/core/browser/signin_header_helper.h +++ b/chromium/components/signin/core/browser/signin_header_helper.h @@ -11,7 +11,7 @@ #include "components/prefs/pref_member.h" #include "components/signin/core/browser/profile_management_switches.h" -#include "components/signin/core/browser/signin_features.h" +#include "components/signin/core/browser/signin_buildflags.h" #include "url/gurl.h" namespace content_settings { @@ -178,9 +178,6 @@ class SigninHeaderHelper { virtual bool IsUrlEligibleForRequestHeader(const GURL& url) = 0; }; -// Returns true if signin cookies are allowed. -bool SettingsAllowSigninCookies( - const content_settings::CookieSettings* cookie_settings); // Returns the CHROME_CONNECTED cookie, or an empty string if it should not be // added to the request to |url|. diff --git a/chromium/components/signin/core/browser/signin_header_helper_unittest.cc b/chromium/components/signin/core/browser/signin_header_helper_unittest.cc index 9056b83e57f..870bd70ce0b 100644 --- a/chromium/components/signin/core/browser/signin_header_helper_unittest.cc +++ b/chromium/components/signin/core/browser/signin_header_helper_unittest.cc @@ -14,7 +14,7 @@ #include "components/prefs/pref_member.h" #include "components/signin/core/browser/chrome_connected_header_helper.h" #include "components/signin/core/browser/profile_management_switches.h" -#include "components/signin/core/browser/signin_features.h" +#include "components/signin/core/browser/signin_buildflags.h" #include "components/sync_preferences/testing_pref_service_syncable.h" #include "google_apis/gaia/gaia_urls.h" #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" diff --git a/chromium/components/signin/core/browser/signin_manager.cc b/chromium/components/signin/core/browser/signin_manager.cc index a7460b60024..5b92a3473b2 100644 --- a/chromium/components/signin/core/browser/signin_manager.cc +++ b/chromium/components/signin/core/browser/signin_manager.cc @@ -160,38 +160,41 @@ void SigninManager::HandleAuthError(const GoogleServiceAuthError& error) { void SigninManager::SignOut( signin_metrics::ProfileSignout signout_source_metric, signin_metrics::SignoutDelete signout_delete_metric) { - StartSignOut(signout_source_metric, signout_delete_metric, - account_consistency_ != signin::AccountConsistencyMethod::kDice); + RemoveAccountsOption remove_option = + (account_consistency_ == signin::AccountConsistencyMethod::kDice) + ? RemoveAccountsOption::kRemoveAuthenticatedAccountIfInError + : RemoveAccountsOption::kRemoveAllAccounts; + StartSignOut(signout_source_metric, signout_delete_metric, remove_option); } void SigninManager::SignOutAndRemoveAllAccounts( signin_metrics::ProfileSignout signout_source_metric, signin_metrics::SignoutDelete signout_delete_metric) { StartSignOut(signout_source_metric, signout_delete_metric, - true /* remove_all_tokens */); + RemoveAccountsOption::kRemoveAllAccounts); } void SigninManager::SignOutAndKeepAllAccounts( signin_metrics::ProfileSignout signout_source_metric, signin_metrics::SignoutDelete signout_delete_metric) { StartSignOut(signout_source_metric, signout_delete_metric, - false /* remove_all_tokens */); + RemoveAccountsOption::kKeepAllAccounts); } void SigninManager::StartSignOut( signin_metrics::ProfileSignout signout_source_metric, signin_metrics::SignoutDelete signout_delete_metric, - bool remove_all_accounts) { - client_->PreSignOut(base::Bind(&SigninManager::DoSignOut, - base::Unretained(this), signout_source_metric, - signout_delete_metric, remove_all_accounts), - signout_source_metric); + RemoveAccountsOption remove_option) { + client_->PreSignOut( + base::Bind(&SigninManager::DoSignOut, base::Unretained(this), + signout_source_metric, signout_delete_metric, remove_option), + signout_source_metric); } void SigninManager::DoSignOut( signin_metrics::ProfileSignout signout_source_metric, signin_metrics::SignoutDelete signout_delete_metric, - bool remove_all_accounts) { + RemoveAccountsOption remove_option) { DCHECK(IsInitialized()); signin_metrics::LogSignout(signout_source_metric, signout_delete_metric); @@ -241,10 +244,19 @@ void SigninManager::DoSignOut( // Revoke all tokens before sending signed_out notification, because there // may be components that don't listen for token service events when the // profile is not connected to an account. - if (remove_all_accounts) { - LOG(WARNING) << "Revoking all refresh tokens on server. Reason: sign out, " - << "IsSigninAllowed: " << IsSigninAllowed(); - token_service_->RevokeAllCredentials(); + switch (remove_option) { + case RemoveAccountsOption::kRemoveAllAccounts: + VLOG(0) << "Revoking all refresh tokens on server. Reason: sign out, " + << "IsSigninAllowed: " << IsSigninAllowed(); + token_service_->RevokeAllCredentials(); + break; + case RemoveAccountsOption::kRemoveAuthenticatedAccountIfInError: + if (token_service_->RefreshTokenHasError(account_id)) + token_service_->RevokeCredentials(account_id); + break; + case RemoveAccountsOption::kKeepAllAccounts: + // Do nothing. + break; } FireGoogleSignedOut(account_id, account_info); diff --git a/chromium/components/signin/core/browser/signin_manager.h b/chromium/components/signin/core/browser/signin_manager.h index 72f26974c6f..8b7d54cf9a8 100644 --- a/chromium/components/signin/core/browser/signin_manager.h +++ b/chromium/components/signin/core/browser/signin_manager.h @@ -62,6 +62,16 @@ class SigninManager : public SigninManagerBase, // signin. The callback is passed the just-fetched OAuth login refresh token. typedef base::Callback<void(const std::string&)> OAuthTokenFetchedCallback; + // Used to remove accounts from the token service and the account tracker. + enum class RemoveAccountsOption { + // Do not remove accounts. + kKeepAllAccounts, + // Remove all the accounts. + kRemoveAllAccounts, + // Removes the authenticated account if it is in authentication error. + kRemoveAuthenticatedAccountIfInError + }; + // This is used to distinguish URLs belonging to the special web signin flow // running in the special signin process from other URLs on the same domain. // We do not grant WebUI privilieges / bindings to this process or to URLs of @@ -109,8 +119,9 @@ class SigninManager : public SigninManagerBase, // associated with the authenticated user, and canceling all auth in progress. // On mobile and on desktop pre-DICE, this also removes all accounts from // Chrome by revoking all refresh tokens. - // On desktop with DICE enabled, this will not remove all accounts from - // Chrome. + // On desktop with DICE enabled, this will remove the authenticated account + // from Chrome only if it is in authentication error. No other accounts are + // removed. void SignOut(signin_metrics::ProfileSignout signout_source_metric, signin_metrics::SignoutDelete signout_delete_metric); @@ -183,7 +194,7 @@ class SigninManager : public SigninManagerBase, // The sign out process which is started by SigninClient::PreSignOut() virtual void DoSignOut(signin_metrics::ProfileSignout signout_source_metric, signin_metrics::SignoutDelete signout_delete_metric, - bool remove_all_accounts); + RemoveAccountsOption remove_option); private: // Interface that gives information on internal SigninManager operations. Only @@ -265,7 +276,7 @@ class SigninManager : public SigninManagerBase, // Starts the sign out process. void StartSignOut(signin_metrics::ProfileSignout signout_source_metric, signin_metrics::SignoutDelete signout_delete_metric, - bool remove_all_accounts); + RemoveAccountsOption remove_option); void OnSigninAllowedPrefChanged(); void OnGoogleServicesUsernamePatternChanged(); diff --git a/chromium/components/signin/core/browser/signin_manager_unittest.cc b/chromium/components/signin/core/browser/signin_manager_unittest.cc index 7fa807de9d3..1ae77c52090 100644 --- a/chromium/components/signin/core/browser/signin_manager_unittest.cc +++ b/chromium/components/signin/core/browser/signin_manager_unittest.cc @@ -85,7 +85,8 @@ class SigninManagerTest : public testing::Test { cookie_manager_service_(&token_service_, GaiaConstants::kChromeSource, &test_signin_client_), - url_fetcher_factory_(nullptr) { + url_fetcher_factory_(nullptr), + account_consistency_(signin::AccountConsistencyMethod::kDisabled) { test_signin_client_.SetURLRequestContext( new net::TestURLRequestContextGetter(loop_.task_runner())); cookie_manager_service_.Init(&url_fetcher_factory_); @@ -131,7 +132,7 @@ class SigninManagerTest : public testing::Test { manager_ = std::make_unique<SigninManager>( &test_signin_client_, &token_service_, &account_tracker_, &cookie_manager_service_, nullptr /* signin_error_controller */, - signin::AccountConsistencyMethod::kDisabled); + account_consistency_); manager_->Initialize(&local_state_); manager_->AddObserver(&test_observer_); } @@ -176,6 +177,7 @@ class SigninManagerTest : public testing::Test { TestSigninManagerObserver test_observer_; std::vector<std::string> oauth_tokens_fetched_; std::vector<std::string> cookies_; + signin::AccountConsistencyMethod account_consistency_; }; TEST_F(SigninManagerTest, SignInWithRefreshToken) { @@ -258,6 +260,79 @@ TEST_F(SigninManagerTest, SignOut) { EXPECT_TRUE(manager_->GetAuthenticatedAccountId().empty()); } +TEST_F(SigninManagerTest, SignOutRevoke) { + CreateSigninManager(); + std::string main_account_id = + AddToAccountTracker("main_id", "user@gmail.com"); + std::string other_account_id = + AddToAccountTracker("other_id", "other@gmail.com"); + token_service_.UpdateCredentials(main_account_id, "token"); + token_service_.UpdateCredentials(other_account_id, "token"); + manager_->OnExternalSigninCompleted("user@gmail.com"); + EXPECT_TRUE(manager_->IsAuthenticated()); + EXPECT_EQ(main_account_id, manager_->GetAuthenticatedAccountId()); + + manager_->SignOut(signin_metrics::SIGNOUT_TEST, + signin_metrics::SignoutDelete::IGNORE_METRIC); + + // Tokens are revoked. + EXPECT_FALSE(manager_->IsAuthenticated()); + EXPECT_TRUE(token_service_.GetAccounts().empty()); +} + +TEST_F(SigninManagerTest, SignOutDiceNoRevoke) { + account_consistency_ = signin::AccountConsistencyMethod::kDice; + CreateSigninManager(); + std::string main_account_id = + AddToAccountTracker("main_id", "user@gmail.com"); + std::string other_account_id = + AddToAccountTracker("other_id", "other@gmail.com"); + token_service_.UpdateCredentials(main_account_id, "token"); + token_service_.UpdateCredentials(other_account_id, "token"); + manager_->OnExternalSigninCompleted("user@gmail.com"); + EXPECT_TRUE(manager_->IsAuthenticated()); + EXPECT_EQ(main_account_id, manager_->GetAuthenticatedAccountId()); + + manager_->SignOut(signin_metrics::SIGNOUT_TEST, + signin_metrics::SignoutDelete::IGNORE_METRIC); + + // Tokens are not revoked. + EXPECT_FALSE(manager_->IsAuthenticated()); + std::vector<std::string> expected_tokens = {main_account_id, + other_account_id}; + EXPECT_EQ(expected_tokens, token_service_.GetAccounts()); +} + +TEST_F(SigninManagerTest, SignOutDiceWithError) { + account_consistency_ = signin::AccountConsistencyMethod::kDice; + CreateSigninManager(); + std::string main_account_id = + AddToAccountTracker("main_id", "user@gmail.com"); + std::string other_account_id = + AddToAccountTracker("other_id", "other@gmail.com"); + token_service_.UpdateCredentials(main_account_id, "token"); + token_service_.UpdateCredentials(other_account_id, "token"); + manager_->OnExternalSigninCompleted("user@gmail.com"); + + GoogleServiceAuthError error( + GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); + token_service_.UpdateAuthErrorForTesting(main_account_id, error); + token_service_.UpdateAuthErrorForTesting(other_account_id, error); + ASSERT_TRUE(token_service_.RefreshTokenHasError(main_account_id)); + ASSERT_TRUE(token_service_.RefreshTokenHasError(other_account_id)); + + EXPECT_TRUE(manager_->IsAuthenticated()); + EXPECT_EQ(main_account_id, manager_->GetAuthenticatedAccountId()); + + manager_->SignOut(signin_metrics::SIGNOUT_TEST, + signin_metrics::SignoutDelete::IGNORE_METRIC); + + // Only main token is revoked. + EXPECT_FALSE(manager_->IsAuthenticated()); + std::vector<std::string> expected_tokens = {other_account_id}; + EXPECT_EQ(expected_tokens, token_service_.GetAccounts()); +} + TEST_F(SigninManagerTest, SignOutWhileProhibited) { CreateSigninManager(); EXPECT_FALSE(manager_->IsAuthenticated()); diff --git a/chromium/components/signin/core/browser/signin_metrics.cc b/chromium/components/signin/core/browser/signin_metrics.cc index 1fdbd889b95..21f5feee326 100644 --- a/chromium/components/signin/core/browser/signin_metrics.cc +++ b/chromium/components/signin/core/browser/signin_metrics.cc @@ -13,6 +13,304 @@ namespace signin_metrics { +namespace { +void RecordSigninUserActionForAccessPoint(AccessPoint access_point) { + switch (access_point) { + case AccessPoint::ACCESS_POINT_START_PAGE: + base::RecordAction( + base::UserMetricsAction("Signin_Signin_FromStartPage")); + break; + case AccessPoint::ACCESS_POINT_NTP_LINK: + base::RecordAction(base::UserMetricsAction("Signin_Signin_FromNTP")); + break; + case AccessPoint::ACCESS_POINT_MENU: + base::RecordAction(base::UserMetricsAction("Signin_Signin_FromMenu")); + break; + case AccessPoint::ACCESS_POINT_SETTINGS: + base::RecordAction(base::UserMetricsAction("Signin_Signin_FromSettings")); + break; + case AccessPoint::ACCESS_POINT_SUPERVISED_USER: + base::RecordAction( + base::UserMetricsAction("Signin_Signin_FromSupervisedUser")); + break; + case AccessPoint::ACCESS_POINT_EXTENSION_INSTALL_BUBBLE: + base::RecordAction( + base::UserMetricsAction("Signin_Signin_FromExtensionInstallBubble")); + break; + case AccessPoint::ACCESS_POINT_EXTENSIONS: + base::RecordAction( + base::UserMetricsAction("Signin_Signin_FromExtensions")); + break; + case AccessPoint::ACCESS_POINT_APPS_PAGE_LINK: + base::RecordAction( + base::UserMetricsAction("Signin_Signin_FromAppsPageLink")); + break; + case AccessPoint::ACCESS_POINT_BOOKMARK_BUBBLE: + base::RecordAction( + base::UserMetricsAction("Signin_Signin_FromBookmarkBubble")); + break; + case AccessPoint::ACCESS_POINT_BOOKMARK_MANAGER: + base::RecordAction( + base::UserMetricsAction("Signin_Signin_FromBookmarkManager")); + break; + case AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN: + base::RecordAction( + base::UserMetricsAction("Signin_Signin_FromAvatarBubbleSignin")); + break; + case AccessPoint::ACCESS_POINT_USER_MANAGER: + base::RecordAction( + base::UserMetricsAction("Signin_Signin_FromUserManager")); + break; + case AccessPoint::ACCESS_POINT_DEVICES_PAGE: + base::RecordAction( + base::UserMetricsAction("Signin_Signin_FromDevicesPage")); + break; + case AccessPoint::ACCESS_POINT_CLOUD_PRINT: + base::RecordAction( + base::UserMetricsAction("Signin_Signin_FromCloudPrint")); + break; + case AccessPoint::ACCESS_POINT_CONTENT_AREA: + base::RecordAction( + base::UserMetricsAction("Signin_Signin_FromContentArea")); + break; + case AccessPoint::ACCESS_POINT_SIGNIN_PROMO: + base::RecordAction( + base::UserMetricsAction("Signin_Signin_FromSigninPromo")); + break; + case AccessPoint::ACCESS_POINT_RECENT_TABS: + base::RecordAction( + base::UserMetricsAction("Signin_Signin_FromRecentTabs")); + break; + case AccessPoint::ACCESS_POINT_UNKNOWN: + base::RecordAction( + base::UserMetricsAction("Signin_Signin_FromUnknownAccessPoint")); + break; + case AccessPoint::ACCESS_POINT_PASSWORD_BUBBLE: + base::RecordAction( + base::UserMetricsAction("Signin_Signin_FromPasswordBubble")); + break; + case AccessPoint::ACCESS_POINT_AUTOFILL_DROPDOWN: + base::RecordAction( + base::UserMetricsAction("Signin_Signin_FromAutofillDropdown")); + break; + case AccessPoint::ACCESS_POINT_NTP_CONTENT_SUGGESTIONS: + base::RecordAction( + base::UserMetricsAction("Signin_Signin_FromNTPContentSuggestions")); + break; + case AccessPoint::ACCESS_POINT_RESIGNIN_INFOBAR: + base::RecordAction( + base::UserMetricsAction("Signin_Signin_FromReSigninInfobar")); + break; + case AccessPoint::ACCESS_POINT_TAB_SWITCHER: + base::RecordAction( + base::UserMetricsAction("Signin_Signin_FromTabSwitcher")); + break; + case AccessPoint::ACCESS_POINT_FORCE_SIGNIN_WARNING: + base::RecordAction( + base::UserMetricsAction("Signin_Signin_FromForceSigninWarning")); + break; + case AccessPoint::ACCESS_POINT_MAX: + NOTREACHED(); + break; + } +} + +void RecordSigninWithDefaultUserActionForAccessPoint( + signin_metrics::AccessPoint access_point) { + switch (access_point) { + case signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS: + base::RecordAction( + base::UserMetricsAction("Signin_SigninWithDefault_FromSettings")); + break; + case AccessPoint::ACCESS_POINT_EXTENSION_INSTALL_BUBBLE: + base::RecordAction(base::UserMetricsAction( + "Signin_SigninWithDefault_FromExtensionInstallBubble")); + break; + case AccessPoint::ACCESS_POINT_BOOKMARK_BUBBLE: + base::RecordAction(base::UserMetricsAction( + "Signin_SigninWithDefault_FromBookmarkBubble")); + break; + case signin_metrics::AccessPoint::ACCESS_POINT_BOOKMARK_MANAGER: + base::RecordAction(base::UserMetricsAction( + "Signin_SigninWithDefault_FromBookmarkManager")); + break; + case AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN: + base::RecordAction(base::UserMetricsAction( + "Signin_SigninWithDefault_FromAvatarBubbleSignin")); + break; + case signin_metrics::AccessPoint::ACCESS_POINT_RECENT_TABS: + base::RecordAction( + base::UserMetricsAction("Signin_SigninWithDefault_FromRecentTabs")); + break; + case AccessPoint::ACCESS_POINT_PASSWORD_BUBBLE: + base::RecordAction(base::UserMetricsAction( + "Signin_SigninWithDefault_FromPasswordBubble")); + break; + case signin_metrics::AccessPoint::ACCESS_POINT_TAB_SWITCHER: + base::RecordAction( + base::UserMetricsAction("Signin_SigninWithDefault_FromTabSwitcher")); + break; + case AccessPoint::ACCESS_POINT_NTP_CONTENT_SUGGESTIONS: + base::RecordAction(base::UserMetricsAction( + "Signin_SigninWithDefault_FromNTPContentSuggestions")); + break; + case AccessPoint::ACCESS_POINT_START_PAGE: + case AccessPoint::ACCESS_POINT_NTP_LINK: + case AccessPoint::ACCESS_POINT_MENU: + case AccessPoint::ACCESS_POINT_SUPERVISED_USER: + case AccessPoint::ACCESS_POINT_EXTENSIONS: + case AccessPoint::ACCESS_POINT_APPS_PAGE_LINK: + case AccessPoint::ACCESS_POINT_USER_MANAGER: + case AccessPoint::ACCESS_POINT_DEVICES_PAGE: + case AccessPoint::ACCESS_POINT_CLOUD_PRINT: + case AccessPoint::ACCESS_POINT_CONTENT_AREA: + case AccessPoint::ACCESS_POINT_SIGNIN_PROMO: + case AccessPoint::ACCESS_POINT_AUTOFILL_DROPDOWN: + case AccessPoint::ACCESS_POINT_RESIGNIN_INFOBAR: + case AccessPoint::ACCESS_POINT_UNKNOWN: + case AccessPoint::ACCESS_POINT_FORCE_SIGNIN_WARNING: + NOTREACHED() << "Signin_SigninWithDefault_From* user actions" + << " are not recorded for access_point " + << static_cast<int>(access_point) + << " as it does not support a personalized sign-in promo."; + break; + case AccessPoint::ACCESS_POINT_MAX: + NOTREACHED(); + break; + } +} + +void RecordSigninNotDefaultUserActionForAccessPoint( + signin_metrics::AccessPoint access_point) { + switch (access_point) { + case signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS: + base::RecordAction( + base::UserMetricsAction("Signin_SigninNotDefault_FromSettings")); + break; + case AccessPoint::ACCESS_POINT_EXTENSION_INSTALL_BUBBLE: + base::RecordAction(base::UserMetricsAction( + "Signin_SigninNotDefault_FromExtensionInstallBubble")); + break; + case AccessPoint::ACCESS_POINT_BOOKMARK_BUBBLE: + base::RecordAction(base::UserMetricsAction( + "Signin_SigninNotDefault_FromBookmarkBubble")); + break; + case signin_metrics::AccessPoint::ACCESS_POINT_BOOKMARK_MANAGER: + base::RecordAction(base::UserMetricsAction( + "Signin_SigninNotDefault_FromBookmarkManager")); + break; + case AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN: + base::RecordAction(base::UserMetricsAction( + "Signin_SigninNotDefault_FromAvatarBubbleSignin")); + break; + case signin_metrics::AccessPoint::ACCESS_POINT_RECENT_TABS: + base::RecordAction( + base::UserMetricsAction("Signin_SigninNotDefault_FromRecentTabs")); + break; + case AccessPoint::ACCESS_POINT_PASSWORD_BUBBLE: + base::RecordAction(base::UserMetricsAction( + "Signin_SigninNotDefault_FromPasswordBubble")); + break; + case signin_metrics::AccessPoint::ACCESS_POINT_TAB_SWITCHER: + base::RecordAction( + base::UserMetricsAction("Signin_SigninNotDefault_FromTabSwitcher")); + break; + case AccessPoint::ACCESS_POINT_NTP_CONTENT_SUGGESTIONS: + base::RecordAction(base::UserMetricsAction( + "Signin_SigninNotDefault_FromNTPContentSuggestions")); + break; + case AccessPoint::ACCESS_POINT_START_PAGE: + case AccessPoint::ACCESS_POINT_NTP_LINK: + case AccessPoint::ACCESS_POINT_MENU: + case AccessPoint::ACCESS_POINT_SUPERVISED_USER: + case AccessPoint::ACCESS_POINT_EXTENSIONS: + case AccessPoint::ACCESS_POINT_APPS_PAGE_LINK: + case AccessPoint::ACCESS_POINT_USER_MANAGER: + case AccessPoint::ACCESS_POINT_DEVICES_PAGE: + case AccessPoint::ACCESS_POINT_CLOUD_PRINT: + case AccessPoint::ACCESS_POINT_CONTENT_AREA: + case AccessPoint::ACCESS_POINT_SIGNIN_PROMO: + case AccessPoint::ACCESS_POINT_AUTOFILL_DROPDOWN: + case AccessPoint::ACCESS_POINT_RESIGNIN_INFOBAR: + case AccessPoint::ACCESS_POINT_UNKNOWN: + case AccessPoint::ACCESS_POINT_FORCE_SIGNIN_WARNING: + NOTREACHED() << "Signin_SigninNotDefault_From* user actions" + << " are not recorded for access_point " + << static_cast<int>(access_point) + << " as it does not support a personalized sign-in promo."; + break; + case AccessPoint::ACCESS_POINT_MAX: + NOTREACHED(); + break; + } +} + +void RecordSigninNewAccountUserActionForAccessPoint( + signin_metrics::AccessPoint access_point) { + switch (access_point) { + case signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS: + base::RecordAction( + base::UserMetricsAction("Signin_SigninNewAccount_FromSettings")); + break; + case AccessPoint::ACCESS_POINT_EXTENSION_INSTALL_BUBBLE: + base::RecordAction(base::UserMetricsAction( + "Signin_SigninNewAccount_FromExtensionInstallBubble")); + break; + case AccessPoint::ACCESS_POINT_BOOKMARK_BUBBLE: + base::RecordAction(base::UserMetricsAction( + "Signin_SigninNewAccount_FromBookmarkBubble")); + break; + case signin_metrics::AccessPoint::ACCESS_POINT_BOOKMARK_MANAGER: + base::RecordAction(base::UserMetricsAction( + "Signin_SigninNewAccount_FromBookmarkManager")); + break; + case AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN: + base::RecordAction(base::UserMetricsAction( + "Signin_SigninNewAccount_FromAvatarBubbleSignin")); + break; + case signin_metrics::AccessPoint::ACCESS_POINT_RECENT_TABS: + base::RecordAction( + base::UserMetricsAction("Signin_SigninNewAccount_FromRecentTabs")); + break; + case AccessPoint::ACCESS_POINT_PASSWORD_BUBBLE: + base::RecordAction(base::UserMetricsAction( + "Signin_SigninNewAccount_FromPasswordBubble")); + break; + case signin_metrics::AccessPoint::ACCESS_POINT_TAB_SWITCHER: + base::RecordAction( + base::UserMetricsAction("Signin_SigninNewAccount_FromTabSwitcher")); + break; + case AccessPoint::ACCESS_POINT_NTP_CONTENT_SUGGESTIONS: + base::RecordAction(base::UserMetricsAction( + "Signin_SigninNewAccount_FromNTPContentSuggestions")); + break; + case AccessPoint::ACCESS_POINT_START_PAGE: + case AccessPoint::ACCESS_POINT_NTP_LINK: + case AccessPoint::ACCESS_POINT_MENU: + case AccessPoint::ACCESS_POINT_SUPERVISED_USER: + case AccessPoint::ACCESS_POINT_EXTENSIONS: + case AccessPoint::ACCESS_POINT_APPS_PAGE_LINK: + case AccessPoint::ACCESS_POINT_USER_MANAGER: + case AccessPoint::ACCESS_POINT_DEVICES_PAGE: + case AccessPoint::ACCESS_POINT_CLOUD_PRINT: + case AccessPoint::ACCESS_POINT_CONTENT_AREA: + case AccessPoint::ACCESS_POINT_SIGNIN_PROMO: + case AccessPoint::ACCESS_POINT_AUTOFILL_DROPDOWN: + case AccessPoint::ACCESS_POINT_RESIGNIN_INFOBAR: + case AccessPoint::ACCESS_POINT_UNKNOWN: + case AccessPoint::ACCESS_POINT_FORCE_SIGNIN_WARNING: + NOTREACHED() << "Signin_SigninNewAccount_From* user actions" + << " are not recorded for access_point " + << static_cast<int>(access_point) + << " as it does not support a personalized sign-in promo."; + break; + case AccessPoint::ACCESS_POINT_MAX: + NOTREACHED(); + break; + } +} +} // namespace + // These intermediate macros are necessary when we may emit to different // histograms from the same logical place in the code. The base histogram macros // expand in a way that can only work for a single histogram name, so these @@ -60,16 +358,6 @@ DifferentPrimaryAccounts ComparePrimaryAccounts(bool primary_accounts_same, return COOKIE_AND_TOKEN_PRIMARIES_DIFFERENT; } -void LogSigninAccessPointStarted(AccessPoint access_point) { - LogSigninAccessPointStarted( - access_point, signin_metrics::PromoAction::PROMO_ACTION_NO_SIGNIN_PROMO); -} - -void LogSigninAccessPointCompleted(AccessPoint access_point) { - LogSigninAccessPointCompleted( - access_point, signin_metrics::PromoAction::PROMO_ACTION_NO_SIGNIN_PROMO); -} - void LogSigninAccessPointStarted(AccessPoint access_point, PromoAction promo_action) { UMA_HISTOGRAM_ENUMERATION("Signin.SigninStartedAccessPoint", @@ -219,14 +507,20 @@ void LogExternalCcResultFetches( } } -void LogAuthError(GoogleServiceAuthError::State auth_error) { - UMA_HISTOGRAM_ENUMERATION("Signin.AuthError", auth_error, - GoogleServiceAuthError::State::NUM_STATES); +void LogAuthError(const GoogleServiceAuthError& auth_error) { + UMA_HISTOGRAM_ENUMERATION("Signin.AuthError", auth_error.state(), + GoogleServiceAuthError::State::NUM_STATES); + if (auth_error.state() == GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS) { + UMA_HISTOGRAM_ENUMERATION( + "Signin.InvalidGaiaCredentialsReason", + auth_error.GetInvalidGaiaCredentialsReason(), + GoogleServiceAuthError::InvalidGaiaCredentialsReason::NUM_REASONS); + } } void LogSigninConfirmHistogramValue(ConfirmationUsage action) { UMA_HISTOGRAM_ENUMERATION("Signin.OneClickConfirmation", action, - signin_metrics::HISTOGRAM_CONFIRM_MAX); + HISTOGRAM_CONFIRM_MAX); } void LogXDevicePromoEligible(CrossDevicePromoEligibility metric) { @@ -294,103 +588,218 @@ void LogIsShared(const bool is_shared, const ReportingType type) { // User actions // -------------------------------------------------------------- -void RecordSigninUserActionForAccessPoint( - signin_metrics::AccessPoint access_point) { - switch (access_point) { - case signin_metrics::AccessPoint::ACCESS_POINT_START_PAGE: - base::RecordAction( - base::UserMetricsAction("Signin_Signin_FromStartPage")); +void RecordSigninUserActionForAccessPoint(AccessPoint access_point, + PromoAction promo_action) { + RecordSigninUserActionForAccessPoint(access_point); + switch (promo_action) { + case PromoAction::PROMO_ACTION_NO_SIGNIN_PROMO: break; - case signin_metrics::AccessPoint::ACCESS_POINT_NTP_LINK: - base::RecordAction(base::UserMetricsAction("Signin_Signin_FromNTP")); + case PromoAction::PROMO_ACTION_WITH_DEFAULT: + RecordSigninWithDefaultUserActionForAccessPoint(access_point); break; - case signin_metrics::AccessPoint::ACCESS_POINT_MENU: - base::RecordAction(base::UserMetricsAction("Signin_Signin_FromMenu")); + case PromoAction::PROMO_ACTION_NOT_DEFAULT: + RecordSigninNotDefaultUserActionForAccessPoint(access_point); break; - case signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS: - base::RecordAction(base::UserMetricsAction("Signin_Signin_FromSettings")); + case PromoAction::PROMO_ACTION_NEW_ACCOUNT: + RecordSigninNewAccountUserActionForAccessPoint(access_point); break; - case signin_metrics::AccessPoint::ACCESS_POINT_SUPERVISED_USER: + } +} + +void RecordSigninImpressionUserActionForAccessPoint(AccessPoint access_point) { + switch (access_point) { + case AccessPoint::ACCESS_POINT_START_PAGE: base::RecordAction( - base::UserMetricsAction("Signin_Signin_FromSupervisedUser")); + base::UserMetricsAction("Signin_Impression_FromStartPage")); break; - case signin_metrics::AccessPoint::ACCESS_POINT_EXTENSION_INSTALL_BUBBLE: - base::RecordAction( - base::UserMetricsAction("Signin_Signin_FromExtensionInstallBubble")); + case AccessPoint::ACCESS_POINT_NTP_LINK: + base::RecordAction(base::UserMetricsAction("Signin_Impression_FromNTP")); break; - case signin_metrics::AccessPoint::ACCESS_POINT_EXTENSIONS: - base::RecordAction( - base::UserMetricsAction("Signin_Signin_FromExtensions")); + case AccessPoint::ACCESS_POINT_MENU: + base::RecordAction(base::UserMetricsAction("Signin_Impression_FromMenu")); break; - case signin_metrics::AccessPoint::ACCESS_POINT_APPS_PAGE_LINK: + case AccessPoint::ACCESS_POINT_SETTINGS: base::RecordAction( - base::UserMetricsAction("Signin_Signin_FromAppsPageLink")); + base::UserMetricsAction("Signin_Impression_FromSettings")); break; - case signin_metrics::AccessPoint::ACCESS_POINT_BOOKMARK_BUBBLE: - base::RecordAction( - base::UserMetricsAction("Signin_Signin_FromBookmarkBubble")); + case AccessPoint::ACCESS_POINT_EXTENSION_INSTALL_BUBBLE: + base::RecordAction(base::UserMetricsAction( + "Signin_Impression_FromExtensionInstallBubble")); break; - case signin_metrics::AccessPoint::ACCESS_POINT_BOOKMARK_MANAGER: + case AccessPoint::ACCESS_POINT_APPS_PAGE_LINK: base::RecordAction( - base::UserMetricsAction("Signin_Signin_FromBookmarkManager")); + base::UserMetricsAction("Signin_Impression_FromAppsPageLink")); break; - case signin_metrics::AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN: + case AccessPoint::ACCESS_POINT_BOOKMARK_BUBBLE: base::RecordAction( - base::UserMetricsAction("Signin_Signin_FromAvatarBubbleSignin")); + base::UserMetricsAction("Signin_Impression_FromBookmarkBubble")); break; - case signin_metrics::AccessPoint::ACCESS_POINT_USER_MANAGER: + case AccessPoint::ACCESS_POINT_BOOKMARK_MANAGER: base::RecordAction( - base::UserMetricsAction("Signin_Signin_FromUserManager")); + base::UserMetricsAction("Signin_Impression_FromBookmarkManager")); break; - case signin_metrics::AccessPoint::ACCESS_POINT_DEVICES_PAGE: + case AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN: base::RecordAction( - base::UserMetricsAction("Signin_Signin_FromDevicesPage")); - break; - case signin_metrics::AccessPoint::ACCESS_POINT_CLOUD_PRINT: - base::RecordAction( - base::UserMetricsAction("Signin_Signin_FromCloudPrint")); - break; - case signin_metrics::AccessPoint::ACCESS_POINT_CONTENT_AREA: - base::RecordAction( - base::UserMetricsAction("Signin_Signin_FromContentArea")); + base::UserMetricsAction("Signin_Impression_FromAvatarBubbleSignin")); break; - case signin_metrics::AccessPoint::ACCESS_POINT_SIGNIN_PROMO: + case AccessPoint::ACCESS_POINT_DEVICES_PAGE: base::RecordAction( - base::UserMetricsAction("Signin_Signin_FromSigninPromo")); + base::UserMetricsAction("Signin_Impression_FromDevicesPage")); break; - case signin_metrics::AccessPoint::ACCESS_POINT_RECENT_TABS: + case AccessPoint::ACCESS_POINT_CLOUD_PRINT: base::RecordAction( - base::UserMetricsAction("Signin_Signin_FromRecentTabs")); + base::UserMetricsAction("Signin_Impression_FromCloudPrint")); break; - case signin_metrics::AccessPoint::ACCESS_POINT_UNKNOWN: + case AccessPoint::ACCESS_POINT_SIGNIN_PROMO: base::RecordAction( - base::UserMetricsAction("Signin_Signin_FromUnknownAccessPoint")); + base::UserMetricsAction("Signin_Impression_FromSigninPromo")); break; - case signin_metrics::AccessPoint::ACCESS_POINT_PASSWORD_BUBBLE: + case AccessPoint::ACCESS_POINT_RECENT_TABS: base::RecordAction( - base::UserMetricsAction("Signin_Signin_FromPasswordBubble")); + base::UserMetricsAction("Signin_Impression_FromRecentTabs")); break; - case signin_metrics::AccessPoint::ACCESS_POINT_AUTOFILL_DROPDOWN: + case AccessPoint::ACCESS_POINT_PASSWORD_BUBBLE: base::RecordAction( - base::UserMetricsAction("Signin_Signin_FromAutofillDropdown")); + base::UserMetricsAction("Signin_Impression_FromPasswordBubble")); break; - case signin_metrics::AccessPoint::ACCESS_POINT_NTP_CONTENT_SUGGESTIONS: + case AccessPoint::ACCESS_POINT_AUTOFILL_DROPDOWN: base::RecordAction( - base::UserMetricsAction("Signin_Signin_FromNTPContentSuggestions")); + base::UserMetricsAction("Signin_Impression_FromAutofillDropdown")); break; - case signin_metrics::AccessPoint::ACCESS_POINT_RESIGNIN_INFOBAR: - base::RecordAction( - base::UserMetricsAction("Signin_Signin_FromReSigninInfobar")); + case AccessPoint::ACCESS_POINT_NTP_CONTENT_SUGGESTIONS: + base::RecordAction(base::UserMetricsAction( + "Signin_Impression_FromNTPContentSuggestions")); break; - case signin_metrics::AccessPoint::ACCESS_POINT_TAB_SWITCHER: + case AccessPoint::ACCESS_POINT_RESIGNIN_INFOBAR: base::RecordAction( - base::UserMetricsAction("Signin_Signin_FromTabSwitcher")); + base::UserMetricsAction("Signin_Impression_FromReSigninInfobar")); break; - case signin_metrics::AccessPoint::ACCESS_POINT_FORCE_SIGNIN_WARNING: + case AccessPoint::ACCESS_POINT_TAB_SWITCHER: base::RecordAction( - base::UserMetricsAction("Signin_Signin_FromForceSigninWarning")); + base::UserMetricsAction("Signin_Impression_FromTabSwitcher")); + break; + case AccessPoint::ACCESS_POINT_CONTENT_AREA: + case AccessPoint::ACCESS_POINT_EXTENSIONS: + case AccessPoint::ACCESS_POINT_FORCE_SIGNIN_WARNING: + case AccessPoint::ACCESS_POINT_SUPERVISED_USER: + case AccessPoint::ACCESS_POINT_USER_MANAGER: + case AccessPoint::ACCESS_POINT_UNKNOWN: + NOTREACHED() << "Signin_Impression_From* user actions" + << " are not recorded for access_point " + << static_cast<int>(access_point); + break; + case AccessPoint::ACCESS_POINT_MAX: + NOTREACHED(); break; - case signin_metrics::AccessPoint::ACCESS_POINT_MAX: + } +} + +void RecordSigninImpressionWithAccountUserActionForAccessPoint( + AccessPoint access_point, + bool with_account) { + switch (access_point) { + case AccessPoint::ACCESS_POINT_SETTINGS: + if (with_account) { + base::RecordAction(base::UserMetricsAction( + "Signin_ImpressionWithAccount_FromSettings")); + } else { + base::RecordAction(base::UserMetricsAction( + "Signin_ImpressionWithNoAccount_FromSettings")); + } + break; + case AccessPoint::ACCESS_POINT_EXTENSION_INSTALL_BUBBLE: + if (with_account) { + base::RecordAction(base::UserMetricsAction( + "Signin_ImpressionWithAccount_FromExtensionInstallBubble")); + } else { + base::RecordAction(base::UserMetricsAction( + "Signin_ImpressionWithNoAccount_FromExtensionInstallBubble")); + } + break; + case AccessPoint::ACCESS_POINT_BOOKMARK_BUBBLE: + if (with_account) { + base::RecordAction(base::UserMetricsAction( + "Signin_ImpressionWithAccount_FromBookmarkBubble")); + } else { + base::RecordAction(base::UserMetricsAction( + "Signin_ImpressionWithNoAccount_FromBookmarkBubble")); + } + break; + case AccessPoint::ACCESS_POINT_BOOKMARK_MANAGER: + if (with_account) { + base::RecordAction(base::UserMetricsAction( + "Signin_ImpressionWithAccount_FromBookmarkManager")); + } else { + base::RecordAction(base::UserMetricsAction( + "Signin_ImpressionWithNoAccount_FromBookmarkManager")); + } + break; + case AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN: + if (with_account) { + base::RecordAction(base::UserMetricsAction( + "Signin_ImpressionWithAccount_FromAvatarBubbleSignin")); + } else { + base::RecordAction(base::UserMetricsAction( + "Signin_ImpressionWithNoAccount_FromAvatarBubbleSignin")); + } + break; + case AccessPoint::ACCESS_POINT_RECENT_TABS: + if (with_account) { + base::RecordAction(base::UserMetricsAction( + "Signin_ImpressionWithAccount_FromRecentTabs")); + } else { + base::RecordAction(base::UserMetricsAction( + "Signin_ImpressionWithNoAccount_FromRecentTabs")); + } + break; + case AccessPoint::ACCESS_POINT_PASSWORD_BUBBLE: + if (with_account) { + base::RecordAction(base::UserMetricsAction( + "Signin_ImpressionWithAccount_FromPasswordBubble")); + } else { + base::RecordAction(base::UserMetricsAction( + "Signin_ImpressionWithNoAccount_FromPasswordBubble")); + } + break; + case AccessPoint::ACCESS_POINT_TAB_SWITCHER: + if (with_account) { + base::RecordAction(base::UserMetricsAction( + "Signin_ImpressionWithAccount_FromTabSwitcher")); + } else { + base::RecordAction(base::UserMetricsAction( + "Signin_ImpressionWithNoAccount_FromTabSwitcher")); + } + break; + case AccessPoint::ACCESS_POINT_NTP_CONTENT_SUGGESTIONS: + if (with_account) { + base::RecordAction(base::UserMetricsAction( + "Signin_ImpressionWithAccount_FromNTPContentSuggestions")); + } else { + base::RecordAction(base::UserMetricsAction( + "Signin_ImpressionWithNoAccount_FromNTPContentSuggestions")); + } + break; + case AccessPoint::ACCESS_POINT_START_PAGE: + case AccessPoint::ACCESS_POINT_NTP_LINK: + case AccessPoint::ACCESS_POINT_MENU: + case AccessPoint::ACCESS_POINT_SUPERVISED_USER: + case AccessPoint::ACCESS_POINT_EXTENSIONS: + case AccessPoint::ACCESS_POINT_APPS_PAGE_LINK: + case AccessPoint::ACCESS_POINT_USER_MANAGER: + case AccessPoint::ACCESS_POINT_DEVICES_PAGE: + case AccessPoint::ACCESS_POINT_CLOUD_PRINT: + case AccessPoint::ACCESS_POINT_CONTENT_AREA: + case AccessPoint::ACCESS_POINT_SIGNIN_PROMO: + case AccessPoint::ACCESS_POINT_AUTOFILL_DROPDOWN: + case AccessPoint::ACCESS_POINT_RESIGNIN_INFOBAR: + case AccessPoint::ACCESS_POINT_UNKNOWN: + case AccessPoint::ACCESS_POINT_FORCE_SIGNIN_WARNING: + NOTREACHED() << "Signin_Impression{With|WithNo}Account_From* user actions" + << " are not recorded for access_point " + << static_cast<int>(access_point) + << " as it does not support a personalized sign-in promo."; + break; + case AccessPoint::ACCESS_POINT_MAX: NOTREACHED(); break; } diff --git a/chromium/components/signin/core/browser/signin_metrics.h b/chromium/components/signin/core/browser/signin_metrics.h index f288eba4258..132b33b0428 100644 --- a/chromium/components/signin/core/browser/signin_metrics.h +++ b/chromium/components/signin/core/browser/signin_metrics.h @@ -301,11 +301,7 @@ enum class ReportingType { PERIODIC, ON_CHANGE }; // Histograms // ----------------------------------------------------------------------------- -// Tracks the access point of sign in on desktop. -void LogSigninAccessPointStarted(AccessPoint access_point); -void LogSigninAccessPointCompleted(AccessPoint access_point); - -// Tracks the access point of sign in on iOS. +// Tracks the access point of sign in. void LogSigninAccessPointStarted(AccessPoint access_point, PromoAction promo_action); void LogSigninAccessPointCompleted(AccessPoint access_point, @@ -358,7 +354,7 @@ void LogExternalCcResultFetches( const base::TimeDelta& time_to_check_connections); // Track when the current authentication error changed. -void LogAuthError(GoogleServiceAuthError::State auth_error); +void LogAuthError(const GoogleServiceAuthError& auth_error); void LogSigninConfirmHistogramValue(ConfirmationUsage action); @@ -401,8 +397,16 @@ void LogIsShared(const bool is_shared, const ReportingType type); // ----------------------------------------------------------------------------- // Records corresponding sign in user action for an access point. -void RecordSigninUserActionForAccessPoint( - signin_metrics::AccessPoint access_point); +void RecordSigninUserActionForAccessPoint(AccessPoint access_point, + PromoAction promo_action); + +// Records |Signin_ImpressionWithAccount_From*| user action. +void RecordSigninImpressionUserActionForAccessPoint(AccessPoint access_point); + +// Records |Signin_Impression{With|No}Account_From*| user action. +void RecordSigninImpressionWithAccountUserActionForAccessPoint( + AccessPoint access_point, + bool with_account); } // namespace signin_metrics diff --git a/chromium/components/signin/core/browser/signin_metrics_unittest.cc b/chromium/components/signin/core/browser/signin_metrics_unittest.cc new file mode 100644 index 00000000000..5361634a935 --- /dev/null +++ b/chromium/components/signin/core/browser/signin_metrics_unittest.cc @@ -0,0 +1,190 @@ +// Copyright 2018 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 "components/signin/core/browser/signin_metrics.h" + +#include <string> +#include <vector> + +#include "base/test/user_action_tester.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace signin_metrics { + +namespace { + +const AccessPoint kAccessPointsThatSupportImpression[] = { + AccessPoint::ACCESS_POINT_START_PAGE, + AccessPoint::ACCESS_POINT_NTP_LINK, + AccessPoint::ACCESS_POINT_MENU, + AccessPoint::ACCESS_POINT_SETTINGS, + AccessPoint::ACCESS_POINT_EXTENSION_INSTALL_BUBBLE, + AccessPoint::ACCESS_POINT_APPS_PAGE_LINK, + AccessPoint::ACCESS_POINT_BOOKMARK_BUBBLE, + AccessPoint::ACCESS_POINT_BOOKMARK_MANAGER, + AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN, + AccessPoint::ACCESS_POINT_DEVICES_PAGE, + AccessPoint::ACCESS_POINT_CLOUD_PRINT, + AccessPoint::ACCESS_POINT_SIGNIN_PROMO, + AccessPoint::ACCESS_POINT_RECENT_TABS, + AccessPoint::ACCESS_POINT_PASSWORD_BUBBLE, + AccessPoint::ACCESS_POINT_AUTOFILL_DROPDOWN, + AccessPoint::ACCESS_POINT_NTP_CONTENT_SUGGESTIONS, + AccessPoint::ACCESS_POINT_RESIGNIN_INFOBAR, + AccessPoint::ACCESS_POINT_TAB_SWITCHER}; + +const AccessPoint kAccessPointsThatSupportPersonalizedPromos[] = { + AccessPoint::ACCESS_POINT_BOOKMARK_MANAGER, + AccessPoint::ACCESS_POINT_RECENT_TABS, + AccessPoint::ACCESS_POINT_SETTINGS, + AccessPoint::ACCESS_POINT_TAB_SWITCHER, + AccessPoint::ACCESS_POINT_EXTENSION_INSTALL_BUBBLE, + AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN, + AccessPoint::ACCESS_POINT_PASSWORD_BUBBLE, + AccessPoint::ACCESS_POINT_BOOKMARK_BUBBLE, + AccessPoint::ACCESS_POINT_NTP_CONTENT_SUGGESTIONS}; + +class SigninMetricsTest : public ::testing::Test { + public: + static std::string GetAccessPointDescription(AccessPoint access_point) { + switch (access_point) { + case AccessPoint::ACCESS_POINT_START_PAGE: + return "StartPage"; + case AccessPoint::ACCESS_POINT_NTP_LINK: + return "NTP"; + case AccessPoint::ACCESS_POINT_MENU: + return "Menu"; + case AccessPoint::ACCESS_POINT_SETTINGS: + return "Settings"; + case AccessPoint::ACCESS_POINT_SUPERVISED_USER: + return "SupervisedUser"; + case AccessPoint::ACCESS_POINT_EXTENSION_INSTALL_BUBBLE: + return "ExtensionInstallBubble"; + case AccessPoint::ACCESS_POINT_EXTENSIONS: + return "Extensions"; + case AccessPoint::ACCESS_POINT_APPS_PAGE_LINK: + return "AppsPageLink"; + case AccessPoint::ACCESS_POINT_BOOKMARK_BUBBLE: + return "BookmarkBubble"; + case AccessPoint::ACCESS_POINT_BOOKMARK_MANAGER: + return "BookmarkManager"; + case AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN: + return "AvatarBubbleSignin"; + case AccessPoint::ACCESS_POINT_USER_MANAGER: + return "UserManager"; + case AccessPoint::ACCESS_POINT_DEVICES_PAGE: + return "DevicesPage"; + case AccessPoint::ACCESS_POINT_CLOUD_PRINT: + return "CloudPrint"; + case AccessPoint::ACCESS_POINT_CONTENT_AREA: + return "ContentArea"; + case AccessPoint::ACCESS_POINT_SIGNIN_PROMO: + return "SigninPromo"; + case AccessPoint::ACCESS_POINT_RECENT_TABS: + return "RecentTabs"; + case AccessPoint::ACCESS_POINT_UNKNOWN: + return "UnknownAccessPoint"; + case AccessPoint::ACCESS_POINT_PASSWORD_BUBBLE: + return "PasswordBubble"; + case AccessPoint::ACCESS_POINT_AUTOFILL_DROPDOWN: + return "AutofillDropdown"; + case AccessPoint::ACCESS_POINT_NTP_CONTENT_SUGGESTIONS: + return "NTPContentSuggestions"; + case AccessPoint::ACCESS_POINT_RESIGNIN_INFOBAR: + return "ReSigninInfobar"; + case AccessPoint::ACCESS_POINT_TAB_SWITCHER: + return "TabSwitcher"; + case AccessPoint::ACCESS_POINT_FORCE_SIGNIN_WARNING: + return "ForceSigninWarning"; + case AccessPoint::ACCESS_POINT_MAX: + NOTREACHED(); + return ""; + } + NOTREACHED(); + return ""; + } + + static std::vector<AccessPoint> GetAllAccessPoints() { + std::vector<AccessPoint> access_points; + for (int ap = 0; ap < static_cast<int>(AccessPoint::ACCESS_POINT_MAX); + ++ap) { + access_points.push_back(static_cast<AccessPoint>(ap)); + } + return access_points; + } +}; + +TEST_F(SigninMetricsTest, RecordSigninUserActionForAccessPoint) { + for (const AccessPoint& ap : GetAllAccessPoints()) { + base::UserActionTester user_action_tester; + RecordSigninUserActionForAccessPoint( + ap, signin_metrics::PromoAction::PROMO_ACTION_NO_SIGNIN_PROMO); + EXPECT_EQ(1, user_action_tester.GetActionCount( + "Signin_Signin_From" + GetAccessPointDescription(ap))); + } +} + +TEST_F(SigninMetricsTest, RecordSigninUserActionWithPromoAction) { + for (const AccessPoint& ap : kAccessPointsThatSupportPersonalizedPromos) { + { + // PROMO_ACTION_WITH_DEFAULT promo action + base::UserActionTester user_action_tester; + RecordSigninUserActionForAccessPoint( + ap, signin_metrics::PromoAction::PROMO_ACTION_WITH_DEFAULT); + EXPECT_EQ( + 1, user_action_tester.GetActionCount("Signin_SigninWithDefault_From" + + GetAccessPointDescription(ap))); + } + { + // PROMO_ACTION_NOT_DEFAULT promo action + base::UserActionTester user_action_tester; + RecordSigninUserActionForAccessPoint( + ap, signin_metrics::PromoAction::PROMO_ACTION_NOT_DEFAULT); + EXPECT_EQ( + 1, user_action_tester.GetActionCount("Signin_SigninNotDefault_From" + + GetAccessPointDescription(ap))); + } + { + // PROMO_ACTION_NEW_ACCOUNT promo action + base::UserActionTester user_action_tester; + RecordSigninUserActionForAccessPoint( + ap, signin_metrics::PromoAction::PROMO_ACTION_NEW_ACCOUNT); + EXPECT_EQ( + 1, user_action_tester.GetActionCount("Signin_SigninNewAccount_From" + + GetAccessPointDescription(ap))); + } + } +} + +TEST_F(SigninMetricsTest, RecordSigninImpressionUserAction) { + for (const AccessPoint& ap : kAccessPointsThatSupportImpression) { + base::UserActionTester user_action_tester; + RecordSigninImpressionUserActionForAccessPoint(ap); + EXPECT_EQ(1, user_action_tester.GetActionCount( + "Signin_Impression_From" + GetAccessPointDescription(ap))); + } +} + +TEST_F(SigninMetricsTest, RecordSigninImpressionWithAccountUserAction) { + for (const AccessPoint& ap : kAccessPointsThatSupportPersonalizedPromos) { + base::UserActionTester user_action_tester; + RecordSigninImpressionWithAccountUserActionForAccessPoint(ap, true); + EXPECT_EQ(1, user_action_tester.GetActionCount( + "Signin_ImpressionWithAccount_From" + + GetAccessPointDescription(ap))); + } +} + +TEST_F(SigninMetricsTest, RecordSigninImpressionWithNoAccountUserAction) { + for (const AccessPoint& ap : kAccessPointsThatSupportPersonalizedPromos) { + base::UserActionTester user_action_tester; + RecordSigninImpressionWithAccountUserActionForAccessPoint(ap, false); + EXPECT_EQ(1, user_action_tester.GetActionCount( + "Signin_ImpressionWithNoAccount_From" + + GetAccessPointDescription(ap))); + } +} + +} // namespace +} // namespace signin_metrics diff --git a/chromium/components/signin/core/browser/signin_switches.h b/chromium/components/signin/core/browser/signin_switches.h index 291c6023968..71cf2a59dca 100644 --- a/chromium/components/signin/core/browser/signin_switches.h +++ b/chromium/components/signin/core/browser/signin_switches.h @@ -5,7 +5,7 @@ #ifndef COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_SWITCHES_H_ #define COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_SWITCHES_H_ -#include "components/signin/core/browser/signin_features.h" +#include "components/signin/core/browser/signin_buildflags.h" namespace switches { diff --git a/chromium/components/signin/core/browser/test_signin_client.cc b/chromium/components/signin/core/browser/test_signin_client.cc index 63a7e044708..8eb66ddba2f 100644 --- a/chromium/components/signin/core/browser/test_signin_client.cc +++ b/chromium/components/signin/core/browser/test_signin_client.cc @@ -14,7 +14,9 @@ #include "testing/gtest/include/gtest/gtest.h" TestSigninClient::TestSigninClient(PrefService* pref_service) - : pref_service_(pref_service), are_signin_cookies_allowed_(true) {} + : pref_service_(pref_service), + are_signin_cookies_allowed_(true), + network_calls_delayed_(false) {} TestSigninClient::~TestSigninClient() {} @@ -79,6 +81,16 @@ TestSigninClient::AddCookieChangeCallback(const GURL& url, return std::make_unique<SigninClient::CookieChangeSubscription>(); } +void TestSigninClient::SetNetworkCallsDelayed(bool value) { + network_calls_delayed_ = value; + + if (!network_calls_delayed_) { + for (base::OnceClosure& call : delayed_network_calls_) + std::move(call).Run(); + delayed_network_calls_.clear(); + } +} + bool TestSigninClient::IsFirstRun() const { return false; } @@ -100,7 +112,11 @@ void TestSigninClient::RemoveContentSettingsObserver( } void TestSigninClient::DelayNetworkCall(const base::Closure& callback) { - callback.Run(); + if (network_calls_delayed_) { + delayed_network_calls_.push_back(callback); + } else { + callback.Run(); + } } std::unique_ptr<GaiaAuthFetcher> TestSigninClient::CreateGaiaAuthFetcher( diff --git a/chromium/components/signin/core/browser/test_signin_client.h b/chromium/components/signin/core/browser/test_signin_client.h index b3f416b509a..3aeb831f664 100644 --- a/chromium/components/signin/core/browser/test_signin_client.h +++ b/chromium/components/signin/core/browser/test_signin_client.h @@ -6,7 +6,10 @@ #define COMPONENTS_SIGNIN_CORE_BROWSER_TEST_SIGNIN_CLIENT_H_ #include <memory> +#include <string> +#include <vector> +#include "base/callback_forward.h" #include "base/compiler_specific.h" #include "base/files/scoped_temp_dir.h" #include "base/macros.h" @@ -78,6 +81,12 @@ class TestSigninClient : public SigninClient { are_signin_cookies_allowed_ = value; } + // When |value| is true, network calls posted through DelayNetworkCall() are + // delayed indefinitely. + // When |value| is false, all pending calls are unblocked, and new calls are + // executed immediately. + void SetNetworkCallsDelayed(bool value); + // SigninClient overrides: bool IsFirstRun() const override; base::Time GetInstallDate() override; @@ -102,6 +111,8 @@ class TestSigninClient : public SigninClient { scoped_refptr<TokenWebData> database_; PrefService* pref_service_; bool are_signin_cookies_allowed_; + bool network_calls_delayed_; + std::vector<base::OnceClosure> delayed_network_calls_; // Pointer to be filled by PostSignedIn. std::string signed_in_password_; diff --git a/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.h b/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.h index d437a8b44a7..79ae397f64a 100644 --- a/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.h +++ b/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.h @@ -35,7 +35,8 @@ class ProfileOAuth2TokenServiceIOSDelegate : public OAuth2TokenServiceDelegate { void Shutdown() override; bool RefreshTokenIsAvailable(const std::string& account_id) const override; - bool RefreshTokenHasError(const std::string& account_id) const override; + GoogleServiceAuthError GetAuthError( + const std::string& account_id) const override; void UpdateAuthError(const std::string& account_id, const GoogleServiceAuthError& error) override; diff --git a/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.mm b/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.mm index 5e93fc97e51..a34d4127db6 100644 --- a/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.mm +++ b/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.mm @@ -51,8 +51,9 @@ GoogleServiceAuthError GetGoogleServiceAuthErrorFromNSError( return GoogleServiceAuthError( GoogleServiceAuthError::UNEXPECTED_SERVICE_RESPONSE); case kAuthenticationErrorCategoryAuthorizationErrors: - return GoogleServiceAuthError( - GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); + return GoogleServiceAuthError::FromInvalidGaiaCredentialsReason( + GoogleServiceAuthError::InvalidGaiaCredentialsReason:: + CREDENTIALS_REJECTED_BY_SERVER); case kAuthenticationErrorCategoryAuthorizationForbiddenErrors: // HTTP_FORBIDDEN (403) is treated as temporary error, because it may be // '403 Rate Limit Exceeded.' (for more details, see @@ -163,10 +164,8 @@ ProfileOAuth2TokenServiceIOSDelegate::AccountStatus::~AccountStatus() { void ProfileOAuth2TokenServiceIOSDelegate::AccountStatus::SetLastAuthError( const GoogleServiceAuthError& error) { - if (error.state() != last_auth_error_.state()) { last_auth_error_ = error; signin_error_controller_->AuthStatusChanged(); - } } std::string ProfileOAuth2TokenServiceIOSDelegate::AccountStatus::GetAccountId() @@ -324,12 +323,11 @@ bool ProfileOAuth2TokenServiceIOSDelegate::RefreshTokenIsAvailable( return accounts_.count(account_id) > 0; } -bool ProfileOAuth2TokenServiceIOSDelegate::RefreshTokenHasError( +GoogleServiceAuthError ProfileOAuth2TokenServiceIOSDelegate::GetAuthError( const std::string& account_id) const { - DCHECK(thread_checker_.CalledOnValidThread()); auto it = accounts_.find(account_id); - // TODO(rogerta): should we distinguish between transient and persistent? - return it == accounts_.end() ? false : IsError(it->second->GetAuthStatus()); + return (it == accounts_.end()) ? GoogleServiceAuthError::AuthErrorNone() + : it->second->GetAuthStatus(); } void ProfileOAuth2TokenServiceIOSDelegate::UpdateAuthError( @@ -340,16 +338,19 @@ void ProfileOAuth2TokenServiceIOSDelegate::UpdateAuthError( // Do not report connection errors as these are not actually auth errors. // We also want to avoid masking a "real" auth error just because we // subsequently get a transient network error. - if (error.state() == GoogleServiceAuthError::CONNECTION_FAILED || - error.state() == GoogleServiceAuthError::SERVICE_UNAVAILABLE) { + if (error.IsTransientError()) return; - } if (accounts_.count(account_id) == 0) { // Nothing to update as the account has already been removed. return; } - accounts_[account_id]->SetLastAuthError(error); + + AccountStatus* status = accounts_[account_id].get(); + if (error.state() != status->GetAuthStatus().state()) { + status->SetLastAuthError(error); + FireAuthErrorChanged(account_id, error); + } } // Clear the authentication error state and notify all observers that a new @@ -374,6 +375,7 @@ void ProfileOAuth2TokenServiceIOSDelegate::AddOrUpdateAccount( if (!account_present) { accounts_[account_id].reset( new AccountStatus(signin_error_controller_, account_id)); + FireAuthErrorChanged(account_id, accounts_[account_id]->GetAuthStatus()); } UpdateAuthError(account_id, GoogleServiceAuthError::AuthErrorNone()); diff --git a/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate_unittest.mm b/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate_unittest.mm index b6a26c0ee4a..9131b9bb76b 100644 --- a/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate_unittest.mm +++ b/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate_unittest.mm @@ -43,6 +43,8 @@ class ProfileOAuth2TokenServiceIOSDelegateTest tokens_loaded_count_(0), access_token_success_(0), access_token_failure_(0), + error_changed_count_(0), + auth_error_changed_count_(0), last_access_token_error_(GoogleServiceAuthError::NONE) {} void SetUp() override { @@ -93,7 +95,12 @@ class ProfileOAuth2TokenServiceIOSDelegateTest ++token_revoked_count_; } void OnRefreshTokensLoaded() override { ++tokens_loaded_count_; } + void OnAuthErrorChanged(const std::string& account_id, + const GoogleServiceAuthError& error) override { + ++auth_error_changed_count_; + } + // SigninErrorController::Observer implementation. void OnErrorChanged() override { ++error_changed_count_; } void ResetObserverCounts() { @@ -103,6 +110,7 @@ class ProfileOAuth2TokenServiceIOSDelegateTest token_available_count_ = 0; access_token_failure_ = 0; error_changed_count_ = 0; + auth_error_changed_count_ = 0; } std::string GetAccountId(const ProviderAccount& provider_account) { @@ -126,6 +134,7 @@ class ProfileOAuth2TokenServiceIOSDelegateTest int access_token_success_; int access_token_failure_; int error_changed_count_; + int auth_error_changed_count_; GoogleServiceAuthError last_access_token_error_; }; @@ -298,10 +307,32 @@ TEST_F(ProfileOAuth2TokenServiceIOSDelegateTest, ResetObserverCounts(); GoogleServiceAuthError error(GoogleServiceAuthError::SERVICE_ERROR); oauth2_delegate_->UpdateAuthError(GetAccountId(account1), error); + EXPECT_EQ(error, oauth2_delegate_->GetAuthError("gaia_1")); + EXPECT_EQ(1, auth_error_changed_count_); EXPECT_EQ(1, error_changed_count_); oauth2_delegate_->RevokeAllCredentials(); ResetObserverCounts(); oauth2_delegate_->UpdateAuthError(GetAccountId(account1), error); + EXPECT_EQ(0, auth_error_changed_count_); EXPECT_EQ(0, error_changed_count_); } + +TEST_F(ProfileOAuth2TokenServiceIOSDelegateTest, GetAuthError) { + // Accounts have no error by default. + ProviderAccount account1 = fake_provider_->AddAccount("gaia_1", "email_1@x"); + oauth2_delegate_->ReloadCredentials(GetAccountId(account1)); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(GoogleServiceAuthError::AuthErrorNone(), + oauth2_delegate_->GetAuthError("gaia_1")); + // Update the error. + GoogleServiceAuthError error = + GoogleServiceAuthError::FromInvalidGaiaCredentialsReason( + GoogleServiceAuthError::InvalidGaiaCredentialsReason:: + CREDENTIALS_REJECTED_BY_SERVER); + oauth2_delegate_->UpdateAuthError("gaia_1", error); + EXPECT_EQ(error, oauth2_delegate_->GetAuthError("gaia_1")); + // Unknown account has no error. + EXPECT_EQ(GoogleServiceAuthError::AuthErrorNone(), + oauth2_delegate_->GetAuthError("gaia_2")); +} |