| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ui/webui/signin/login_ui_service.h" |
| |
| #include "build/build_config.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/profiles/profile_attributes_entry.h" |
| #include "chrome/browser/profiles/profile_attributes_storage.h" |
| #include "chrome/browser/profiles/profile_manager.h" |
| #include "chrome/browser/signin/signin_promo.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_window.h" |
| #include "chrome/browser/ui/chrome_pages.h" |
| #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h" |
| #include "chrome/browser/ui/webui/signin/login_ui_service_factory.h" |
| #include "chrome/common/url_constants.h" |
| #include "components/signin/core/browser/account_consistency_method.h" |
| #include "components/signin/core/browser/signin_header_helper.h" |
| |
| #if !defined(OS_CHROMEOS) |
| #include "base/scoped_observer.h" |
| #include "chrome/browser/signin/profile_oauth2_token_service_factory.h" |
| #include "chrome/browser/signin/signin_manager_factory.h" |
| #include "chrome/browser/sync/profile_sync_service_factory.h" |
| #include "chrome/browser/sync/sync_ui_util.h" |
| #include "chrome/browser/ui/browser_finder.h" |
| #include "chrome/browser/ui/browser_list.h" |
| #include "chrome/browser/ui/browser_list_observer.h" |
| #include "chrome/browser/ui/user_manager.h" |
| #include "chrome/browser/unified_consent/unified_consent_service_factory.h" |
| #include "components/signin/core/browser/profile_oauth2_token_service.h" |
| #include "components/sync/base/sync_prefs.h" |
| #include "components/unified_consent/feature.h" |
| #include "components/unified_consent/pref_names.h" |
| #include "components/unified_consent/unified_consent_service.h" |
| |
| // The sync consent bump is shown after startup when a profile's browser |
| // instance becomes active or when there is already an active instance. |
| // It is only shown when |ShouldShowConsentBumpFor(profile)| returns true for a |
| // given profile |profile|. |
| class ConsentBumpActivator : public BrowserListObserver, |
| public LoginUIService::Observer, |
| public OAuth2TokenService::Observer { |
| public: |
| // Creates a ConsentBumpActivator for |profile| which is owned by |
| // |login_ui_service|. |
| ConsentBumpActivator(LoginUIService* login_ui_service, Profile* profile) |
| : login_ui_service_(login_ui_service), |
| profile_(profile), |
| scoped_browser_list_observer_(this), |
| scoped_login_ui_service_observer_(this), |
| scoped_token_service_observer_(this) { |
| ProfileOAuth2TokenService* token_service = |
| ProfileOAuth2TokenServiceFactory::GetForProfile(profile_); |
| if (token_service->AreAllCredentialsLoaded()) |
| OnRefreshTokensLoaded(); |
| else |
| scoped_token_service_observer_.Add(token_service); |
| } |
| |
| // OAuth2TokenService::Observer: |
| void OnRefreshTokensLoaded() override { |
| scoped_token_service_observer_.RemoveAll(); |
| |
| SigninManager* signin_manager = |
| SigninManagerFactory::GetForProfile(profile_); |
| ProfileOAuth2TokenService* token_service = |
| ProfileOAuth2TokenServiceFactory::GetForProfile(profile_); |
| |
| // Avoid showing the consent bump if the refresh token is missing or is in |
| // an permanent auth error state. When the tokens are loaded, this |
| // corresponds to the case when the refresh token was invalidated |
| // client-side after the user signed out of a Google website (e.g. the user |
| // signed out of Gmail). |
| if (token_service->RefreshTokenHasError( |
| signin_manager->GetAuthenticatedAccountId())) { |
| unified_consent::UnifiedConsentService* consent_service = |
| UnifiedConsentServiceFactory::GetForProfile(profile_); |
| if (consent_service->ShouldShowConsentBump()) { |
| consent_service->RecordConsentBumpSuppressReason( |
| unified_consent::metrics::ConsentBumpSuppressReason::kSyncPaused); |
| } |
| return; |
| } |
| |
| // Check if there is already an active browser window for |profile|. |
| Browser* active_browser = chrome::FindLastActiveWithProfile(profile_); |
| if (active_browser) |
| OnBrowserSetLastActive(active_browser); |
| else |
| scoped_browser_list_observer_.Add(BrowserList::GetInstance()); |
| } |
| |
| // BrowserListObserver: |
| void OnBrowserSetLastActive(Browser* browser) override { |
| if (browser->profile() != profile_) |
| return; |
| // We only try to show the consent bump once after startup, so remove |this| |
| // as a |BrowserListObserver|. |
| scoped_browser_list_observer_.RemoveAll(); |
| |
| if (ShouldShowConsentBumpFor(profile_)) { |
| selected_browser_ = browser; |
| scoped_login_ui_service_observer_.Add(login_ui_service_); |
| selected_browser_->signin_view_controller()->ShowModalSyncConsentBump( |
| selected_browser_); |
| } |
| } |
| |
| // LoginUIService::Observer: |
| void OnSyncConfirmationUIClosed( |
| LoginUIService::SyncConfirmationUIClosedResult result) override { |
| scoped_login_ui_service_observer_.RemoveAll(); |
| |
| unified_consent::UnifiedConsentService* consent_service = |
| UnifiedConsentServiceFactory::GetForProfile(profile_); |
| |
| consent_service->MarkConsentBumpShown(); |
| |
| switch (result) { |
| case LoginUIService::CONFIGURE_SYNC_FIRST: |
| // Opt into Unity and open settings page to make configurations. |
| consent_service->SetUnifiedConsentGiven(true); |
| chrome::ShowSettingsSubPage(selected_browser_, |
| chrome::kSyncSetupSubPage); |
| break; |
| case LoginUIService::SYNC_WITH_DEFAULT_SETTINGS: |
| // Opt into Unity with default configuration. |
| consent_service->SetUnifiedConsentGiven(true); |
| break; |
| case LoginUIService::ABORT_SIGNIN: |
| // "Make no changes" was selected. |
| DCHECK(!consent_service->IsUnifiedConsentGiven()); |
| break; |
| } |
| } |
| |
| // This should only be called after the browser has been set up, otherwise |
| // this might crash because the profile has not been fully initialized yet. |
| static bool ShouldShowConsentBumpFor(Profile* profile) { |
| if (!profile->IsSyncAllowed() || |
| !unified_consent::IsUnifiedConsentFeatureWithBumpEnabled() || |
| !ProfileSyncServiceFactory::HasProfileSyncService(profile)) |
| return false; |
| |
| unified_consent::UnifiedConsentService* consent_service = |
| UnifiedConsentServiceFactory::GetForProfile(profile); |
| if (!consent_service->ShouldShowConsentBump()) |
| return false; |
| |
| sync_ui_util::MessageType sync_status = sync_ui_util::GetStatus( |
| profile, ProfileSyncServiceFactory::GetForProfile(profile), |
| *SigninManagerFactory::GetForProfile(profile)); |
| |
| return sync_status == sync_ui_util::SYNCED; |
| } |
| |
| private: |
| LoginUIService* login_ui_service_; // owner |
| |
| Profile* profile_; |
| |
| ScopedObserver<BrowserList, ConsentBumpActivator> |
| scoped_browser_list_observer_; |
| ScopedObserver<LoginUIService, ConsentBumpActivator> |
| scoped_login_ui_service_observer_; |
| ScopedObserver<OAuth2TokenService, ConsentBumpActivator> |
| scoped_token_service_observer_; |
| |
| // Used for the action handling of the consent bump. |
| Browser* selected_browser_ = nullptr; |
| |
| DISALLOW_COPY_AND_ASSIGN(ConsentBumpActivator); |
| }; |
| |
| #endif // !defined(OS_CHROMEOS) |
| |
| LoginUIService::LoginUIService(Profile* profile) |
| #if !defined(OS_CHROMEOS) |
| : profile_(profile) |
| #endif |
| { |
| #if !defined(OS_CHROMEOS) |
| if (unified_consent::IsUnifiedConsentFeatureWithBumpEnabled()) { |
| consent_bump_activator_ = |
| std::make_unique<ConsentBumpActivator>(this, profile); |
| } |
| #endif |
| } |
| |
| LoginUIService::~LoginUIService() {} |
| |
| void LoginUIService::AddObserver(LoginUIService::Observer* observer) { |
| observer_list_.AddObserver(observer); |
| } |
| |
| void LoginUIService::RemoveObserver(LoginUIService::Observer* observer) { |
| observer_list_.RemoveObserver(observer); |
| } |
| |
| LoginUIService::LoginUI* LoginUIService::current_login_ui() const { |
| return ui_list_.empty() ? nullptr : ui_list_.front(); |
| } |
| |
| void LoginUIService::SetLoginUI(LoginUI* ui) { |
| ui_list_.remove(ui); |
| ui_list_.push_front(ui); |
| } |
| |
| void LoginUIService::LoginUIClosed(LoginUI* ui) { |
| ui_list_.remove(ui); |
| } |
| |
| void LoginUIService::SyncConfirmationUIClosed( |
| SyncConfirmationUIClosedResult result) { |
| for (Observer& observer : observer_list_) |
| observer.OnSyncConfirmationUIClosed(result); |
| } |
| |
| void LoginUIService::ShowLoginPopup() { |
| #if defined(OS_CHROMEOS) |
| NOTREACHED(); |
| #else |
| // There is no sign-in flow for guest or system profile. |
| if (profile_->IsGuestSession() || profile_->IsSystemProfile()) |
| return; |
| // Locked profile should be unlocked with UserManager only. |
| ProfileAttributesEntry* entry; |
| if (g_browser_process->profile_manager() |
| ->GetProfileAttributesStorage() |
| .GetProfileAttributesWithPath(profile_->GetPath(), &entry) && |
| entry->IsSigninRequired()) { |
| return; |
| } |
| |
| chrome::ScopedTabbedBrowserDisplayer displayer(profile_); |
| chrome::ShowBrowserSignin( |
| displayer.browser(), |
| signin_metrics::AccessPoint::ACCESS_POINT_EXTENSIONS); |
| #endif |
| } |
| |
| void LoginUIService::DisplayLoginResult(Browser* browser, |
| const base::string16& error_message, |
| const base::string16& email) { |
| #if defined(OS_CHROMEOS) |
| // ChromeOS doesn't have the avatar bubble so it never calls this function. |
| NOTREACHED(); |
| #else |
| is_displaying_profile_blocking_error_message_ = false; |
| last_login_result_ = error_message; |
| last_login_error_email_ = email; |
| if (!error_message.empty()) { |
| if (browser) |
| browser->signin_view_controller()->ShowModalSigninErrorDialog(browser); |
| else |
| UserManagerProfileDialog::DisplayErrorMessage(); |
| } else if (browser) { |
| browser->window()->ShowAvatarBubbleFromAvatarButton( |
| error_message.empty() ? BrowserWindow::AVATAR_BUBBLE_MODE_CONFIRM_SIGNIN |
| : BrowserWindow::AVATAR_BUBBLE_MODE_SHOW_ERROR, |
| signin::ManageAccountsParams(), |
| signin_metrics::AccessPoint::ACCESS_POINT_EXTENSIONS, false); |
| } |
| #endif |
| } |
| |
| void LoginUIService::SetProfileBlockingErrorMessage() { |
| last_login_result_ = base::string16(); |
| last_login_error_email_ = base::string16(); |
| is_displaying_profile_blocking_error_message_ = true; |
| } |
| |
| bool LoginUIService::IsDisplayingProfileBlockedErrorMessage() const { |
| return is_displaying_profile_blocking_error_message_; |
| } |
| |
| const base::string16& LoginUIService::GetLastLoginResult() const { |
| return last_login_result_; |
| } |
| |
| const base::string16& LoginUIService::GetLastLoginErrorEmail() const { |
| return last_login_error_email_; |
| } |