blob: 7c1b58fc0d0ab56070fca366269f6f6993452658 [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/signin/dice_tab_helper.h"
#include "base/check_op.h"
#include "base/functional/bind.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/user_metrics.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/signin/signin_util.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/webui/signin/login_ui_service.h"
#include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
#include "chrome/browser/ui/webui/signin/turn_sync_on_helper.h"
#include "components/signin/public/base/signin_metrics.h"
#include "components/signin/public/base/signin_switches.h"
#include "components/signin/public/identity_manager/account_info.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/navigation_handle.h"
#include "google_apis/gaia/gaia_auth_util.h"
// static
DiceTabHelper::EnableSyncCallback
DiceTabHelper::GetEnableSyncCallbackForBrowser() {
return base::BindRepeating([](Profile* profile,
signin_metrics::AccessPoint access_point,
signin_metrics::PromoAction promo_action,
content::WebContents* web_contents,
const CoreAccountInfo& account_info) {
DCHECK(profile);
Browser* browser = web_contents ? chrome::FindBrowserWithTab(web_contents)
: chrome::FindBrowserWithProfile(profile);
if (!browser) {
return;
}
bool is_sync_promo = access_point ==
signin_metrics::AccessPoint::
ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN_WITH_SYNC_PROMO;
TurnSyncOnHelper::SigninAbortedMode abort_mode =
is_sync_promo ? TurnSyncOnHelper::SigninAbortedMode::KEEP_ACCOUNT
: TurnSyncOnHelper::SigninAbortedMode::REMOVE_ACCOUNT;
// TurnSyncOnHelper is suicidal (it will kill itself once it
// finishes enabling sync).
new TurnSyncOnHelper(profile, browser, access_point, promo_action,
account_info.account_id, abort_mode, is_sync_promo);
});
}
// static
DiceTabHelper::ShowSigninErrorCallback
DiceTabHelper::GetShowSigninErrorCallbackForBrowser() {
return base::BindRepeating([](Profile* profile,
content::WebContents* web_contents,
const SigninUIError& error) {
if (!profile) {
return;
}
Browser* browser = web_contents ? chrome::FindBrowserWithTab(web_contents)
: chrome::FindBrowserWithProfile(profile);
if (!browser) {
return;
}
LoginUIServiceFactory::GetForProfile(profile)->DisplayLoginResult(
browser, error, /*from_profile_picker=*/false);
});
}
DiceTabHelper::ResetableState::ResetableState() = default;
DiceTabHelper::ResetableState::~ResetableState() = default;
DiceTabHelper::ResetableState::ResetableState(ResetableState&& other) = default;
DiceTabHelper::ResetableState& DiceTabHelper::ResetableState::operator=(
ResetableState&& other) = default;
DiceTabHelper::DiceTabHelper(content::WebContents* web_contents)
: content::WebContentsUserData<DiceTabHelper>(*web_contents),
content::WebContentsObserver(web_contents) {}
DiceTabHelper::~DiceTabHelper() = default;
void DiceTabHelper::InitializeSigninFlow(
const GURL& signin_url,
signin_metrics::AccessPoint access_point,
signin_metrics::Reason reason,
signin_metrics::PromoAction promo_action,
const GURL& redirect_url,
bool record_signin_started_metrics,
EnableSyncCallback enable_sync_callback,
OnSigninHeaderReceived on_signin_header_received_callback,
ShowSigninErrorCallback show_signin_error_callback) {
DCHECK(signin_url.is_valid());
DCHECK(state_.signin_url.is_empty() || state_.signin_url == signin_url);
Reset();
state_.redirect_url = redirect_url;
state_.signin_url = signin_url;
state_.signin_access_point = access_point;
state_.signin_promo_action = promo_action;
state_.signin_reason = reason;
state_.enable_sync_callback = std::move(enable_sync_callback);
state_.on_signin_header_received_callback =
std::move(on_signin_header_received_callback);
state_.show_signin_error_callback = std::move(show_signin_error_callback);
is_chrome_signin_page_ = true;
signin_page_load_recorded_ = false;
if (reason == signin_metrics::Reason::kSigninPrimaryAccount) {
state_.sync_signin_flow_status = SyncSigninFlowStatus::kStarted;
}
// This profile creation may lead to the user signing in. To speed up a
// potential subsequent account capabililties fetch, notify IdentityManager.
signin::IdentityManager* identity_manager =
IdentityManagerFactory::GetForProfile(
Profile::FromBrowserContext(web_contents()->GetBrowserContext()));
identity_manager->PrepareForAddingNewAccount();
if (!record_signin_started_metrics) {
return;
}
// Note: if a Dice signin tab is reused, `InitializeSigninFlow()` is not
// called again, and the tab reuse does not generate new metrics.
if (reason == signin_metrics::Reason::kSigninPrimaryAccount ||
reason == signin_metrics::Reason::kAddSecondaryAccount) {
// See details at go/chrome-signin-metrics-revamp.
signin_metrics::LogSignInStarted(access_point);
}
if (reason == signin_metrics::Reason::kSigninPrimaryAccount) {
signin_metrics::LogSigninAccessPointStarted(access_point, promo_action);
signin_metrics::RecordSigninUserActionForAccessPoint(access_point);
base::RecordAction(base::UserMetricsAction("Signin_SigninPage_Loading"));
}
if (signin_util::IsSigninPending(identity_manager)) {
base::UmaHistogramEnumeration(
"Signin.SigninPending.ResolutionSourceStarted", access_point,
signin_metrics::AccessPoint::ACCESS_POINT_MAX);
}
}
bool DiceTabHelper::IsChromeSigninPage() const {
return is_chrome_signin_page_;
}
bool DiceTabHelper::IsSyncSigninInProgress() const {
return state_.sync_signin_flow_status == SyncSigninFlowStatus::kStarted;
}
void DiceTabHelper::OnSyncSigninFlowComplete() {
// The flow is complete, reset to initial state.
Reset();
}
void DiceTabHelper::DidStartNavigation(
content::NavigationHandle* navigation_handle) {
if (!is_chrome_signin_page_) {
return;
}
// Ignore internal navigations.
if (!navigation_handle->IsInPrimaryMainFrame() ||
navigation_handle->IsSameDocument()) {
return;
}
if (!IsSigninPageNavigation(navigation_handle)) {
// Navigating away from the signin page.
// Note that currently any indication of a navigation is enough to consider
// this tab unsuitable for re-use, even if the navigation does not end up
// committing.
is_chrome_signin_page_ = false;
}
}
void DiceTabHelper::DidFinishNavigation(
content::NavigationHandle* navigation_handle) {
if (!is_chrome_signin_page_) {
return;
}
// Ignore internal navigations.
if (!navigation_handle->IsInPrimaryMainFrame() ||
navigation_handle->IsSameDocument()) {
return;
}
if (!IsSigninPageNavigation(navigation_handle)) {
// Navigating away from the signin page.
// Note that currently any indication of a navigation is enough to consider
// this tab unsuitable for re-use, even if the navigation does not end up
// committing.
is_chrome_signin_page_ = false;
return;
}
if (state_.signin_reason == signin_metrics::Reason::kSigninPrimaryAccount &&
!signin_page_load_recorded_) {
signin_page_load_recorded_ = true;
base::RecordAction(base::UserMetricsAction("Signin_SigninPage_Shown"));
}
}
bool DiceTabHelper::IsSigninPageNavigation(
content::NavigationHandle* navigation_handle) const {
return !navigation_handle->IsErrorPage() &&
navigation_handle->GetRedirectChain()[0] == state_.signin_url &&
gaia::HasGaiaSchemeHostPort(navigation_handle->GetURL());
}
void DiceTabHelper::Reset() {
state_ = {};
}
WEB_CONTENTS_USER_DATA_KEY_IMPL(DiceTabHelper);