blob: fdd2349e19ccd2ba969fb1845175a9f5f6caa5e8 [file] [log] [blame]
// Copyright 2013 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/signin/signin_promo.h"
#include <limits.h>
#include "base/strings/string_number_conversions.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/extensions/signin/gaia_auth_extension_loader.h"
#include "chrome/browser/first_run/first_run.h"
#include "chrome/browser/google/google_brand.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/search_engines/ui_thread_search_terms_data.h"
#include "chrome/browser/signin/account_consistency_mode_manager.h"
#include "chrome/browser/signin/account_tracker_service_factory.h"
#include "chrome/browser/signin/signin_error_controller_factory.h"
#include "chrome/browser/signin/signin_manager_factory.h"
#include "chrome/browser/signin/signin_promo_util.h"
#include "chrome/browser/ui/webui/theme_source.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "components/google/core/common/google_util.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h"
#include "components/signin/core/browser/account_tracker_service.h"
#include "components/signin/core/browser/profile_management_switches.h"
#include "components/signin/core/browser/signin_manager.h"
#include "content/public/browser/url_data_source.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_data_source.h"
#include "google_apis/gaia/gaia_urls.h"
#include "net/base/url_util.h"
#include "url/gurl.h"
#if defined(OS_WIN)
#include "base/win/windows_version.h"
#endif
using content::WebContents;
namespace {
// The maximum number of times we want to show the sign in promo at startup.
const int kSignInPromoShowAtStartupMaximum = 10;
// Forces the web based signin flow when set.
bool g_force_web_based_signin_flow = false;
// Checks we want to show the sign in promo for the given brand.
bool AllowPromoAtStartupForCurrentBrand() {
std::string brand;
google_brand::GetBrand(&brand);
if (brand.empty())
return true;
if (google_brand::IsInternetCafeBrandCode(brand))
return false;
// Enable for both organic and distribution.
return true;
}
// Returns true if a user has seen the sign in promo at startup previously.
bool HasShownPromoAtStartup(Profile* profile) {
return profile->GetPrefs()->HasPrefPath(prefs::kSignInPromoStartupCount);
}
// Returns true if the user has previously skipped the sign in promo.
bool HasUserSkippedPromo(Profile* profile) {
return profile->GetPrefs()->GetBoolean(prefs::kSignInPromoUserSkipped);
}
// Returns the sign in promo URL with the given arguments in the query.
// |access_point| indicates where the sign in is being initiated.
// |reason| indicates the purpose of using this URL.
// |auto_close| whether to close the sign in promo automatically when done.
GURL GetPromoURL(signin_metrics::AccessPoint access_point,
signin_metrics::Reason reason,
bool auto_close) {
CHECK_LT(static_cast<int>(access_point),
static_cast<int>(signin_metrics::AccessPoint::ACCESS_POINT_MAX));
CHECK_NE(static_cast<int>(access_point),
static_cast<int>(signin_metrics::AccessPoint::ACCESS_POINT_UNKNOWN));
CHECK_LT(static_cast<int>(reason),
static_cast<int>(signin_metrics::Reason::REASON_MAX));
CHECK_NE(static_cast<int>(reason),
static_cast<int>(signin_metrics::Reason::REASON_UNKNOWN_REASON));
GURL url(chrome::kChromeUIChromeSigninURL);
url = net::AppendQueryParameter(
url, signin::kSignInPromoQueryKeyAccessPoint,
base::IntToString(static_cast<int>(access_point)));
url = net::AppendQueryParameter(url, signin::kSignInPromoQueryKeyReason,
base::IntToString(static_cast<int>(reason)));
if (auto_close) {
url = net::AppendQueryParameter(url, signin::kSignInPromoQueryKeyAutoClose,
"1");
}
return url;
}
GURL GetReauthURL(signin_metrics::AccessPoint access_point,
signin_metrics::Reason reason,
const std::string& email,
bool auto_close) {
GURL url = GetPromoURL(access_point, reason, auto_close);
url = net::AppendQueryParameter(url, "email", email);
url = net::AppendQueryParameter(url, "validateEmail", "1");
return net::AppendQueryParameter(url, "readOnlyEmail", "1");
}
} // namespace
namespace signin {
const char kSignInPromoQueryKeyAccessPoint[] = "access_point";
const char kSignInPromoQueryKeyAutoClose[] = "auto_close";
const char kSignInPromoQueryKeyContinue[] = "continue";
const char kSignInPromoQueryKeyForceKeepData[] = "force_keep_data";
const char kSignInPromoQueryKeyReason[] = "reason";
const char kSignInPromoQueryKeySource[] = "source";
const char kSigninPromoLandingURLSuccessPage[] = "success.html";
bool ShouldShowPromoAtStartup(Profile* profile, bool is_new_profile) {
DCHECK(profile);
// Don't show if the profile is an incognito.
if (profile->IsOffTheRecord())
return false;
if (!ShouldShowPromo(profile))
return false;
if (!is_new_profile) {
if (!HasShownPromoAtStartup(profile))
return false;
}
#if defined(OS_WIN)
// Do not show the promo on first run on Win10 and newer.
if (is_new_profile && base::win::GetVersion() >= base::win::VERSION_WIN10)
return false;
#endif
if (HasUserSkippedPromo(profile))
return false;
// For Chinese users skip the sign in promo.
if (g_browser_process->GetApplicationLocale() == "zh-CN")
return false;
PrefService* prefs = profile->GetPrefs();
int show_count = prefs->GetInteger(prefs::kSignInPromoStartupCount);
if (show_count >= kSignInPromoShowAtStartupMaximum)
return false;
// This pref can be set in the master preferences file to allow or disallow
// showing the sign in promo at startup.
if (prefs->HasPrefPath(prefs::kSignInPromoShowOnFirstRunAllowed))
return prefs->GetBoolean(prefs::kSignInPromoShowOnFirstRunAllowed);
// For now don't show the promo for some brands.
if (!AllowPromoAtStartupForCurrentBrand())
return false;
// Default to show the promo for Google Chrome builds.
#if defined(GOOGLE_CHROME_BUILD)
return true;
#else
return false;
#endif
}
void DidShowPromoAtStartup(Profile* profile) {
int show_count = profile->GetPrefs()->GetInteger(
prefs::kSignInPromoStartupCount);
show_count++;
profile->GetPrefs()->SetInteger(prefs::kSignInPromoStartupCount, show_count);
}
void SetUserSkippedPromo(Profile* profile) {
profile->GetPrefs()->SetBoolean(prefs::kSignInPromoUserSkipped, true);
}
GURL GetLandingURL(signin_metrics::AccessPoint access_point) {
GURL url(extensions::kGaiaAuthExtensionOrigin);
GURL::Replacements replacements;
replacements.SetPathStr(kSigninPromoLandingURLSuccessPage);
url = url.ReplaceComponents(replacements);
url = net::AppendQueryParameter(
url, kSignInPromoQueryKeyAccessPoint,
base::IntToString(static_cast<int>(access_point)));
// TODO(gogerald): right now, gaia server needs to distinguish the source from
// signin_metrics::SOURCE_START_PAGE, signin_metrics::SOURCE_SETTINGS and
// the others to show advanced sync settings, remove them after
// switching to Minute Maid sign in flow.
signin_metrics::Source source = signin_metrics::SOURCE_OTHERS;
if (access_point == signin_metrics::AccessPoint::ACCESS_POINT_START_PAGE) {
source = signin_metrics::SOURCE_START_PAGE;
} else if (access_point ==
signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS) {
source = signin_metrics::SOURCE_SETTINGS;
}
url = net::AppendQueryParameter(url, signin::kSignInPromoQueryKeySource,
base::IntToString(static_cast<int>(source)));
return GURL(url);
}
GURL GetPromoURLForTab(signin_metrics::AccessPoint access_point,
signin_metrics::Reason reason,
bool auto_close) {
return GetPromoURL(access_point, reason, auto_close);
}
GURL GetPromoURLForDialog(signin_metrics::AccessPoint access_point,
signin_metrics::Reason reason,
bool auto_close) {
return GetPromoURL(access_point, reason, auto_close);
}
GURL GetReauthURLForDialog(signin_metrics::AccessPoint access_point,
signin_metrics::Reason reason,
Profile* profile,
const std::string& account_id) {
AccountInfo info = AccountTrackerServiceFactory::GetForProfile(profile)
->GetAccountInfo(account_id);
return GetReauthURL(access_point, reason, info.email, true /* auto_close */);
}
GURL GetReauthURLForTab(signin_metrics::AccessPoint access_point,
signin_metrics::Reason reason,
Profile* profile,
const std::string& account_id) {
AccountInfo info =
AccountTrackerServiceFactory::GetForProfile(profile)->GetAccountInfo(
account_id);
return GetReauthURL(access_point, reason, info.email, true /* auto_close */);
}
GURL GetReauthURLWithEmailForDialog(signin_metrics::AccessPoint access_point,
signin_metrics::Reason reason,
const std::string& email) {
return GetReauthURL(access_point, reason, email, true /* auto_close */);
}
GURL GetSigninURLForDice(Profile* profile, const std::string& email) {
DCHECK(signin::DiceMethodGreaterOrEqual(
AccountConsistencyModeManager::GetMethodForProfile(profile),
signin::AccountConsistencyMethod::kDiceMigration));
GURL url = GaiaUrls::GetInstance()->signin_chrome_sync_dice();
if (!email.empty())
url = net::AppendQueryParameter(url, "email_hint", email);
// Pass www.gooogle.com as the continue URL as otherwise Gaia navigates to
// myaccount which may be very confusing for the user.
return net::AppendQueryParameter(
url, "continue", UIThreadSearchTermsData(profile).GoogleBaseURLValue());
}
GURL GetSigninPartitionURL() {
return GURL("chrome-guest://chrome-signin/?");
}
GURL GetSigninURLFromBubbleViewMode(Profile* profile,
profiles::BubbleViewMode mode,
signin_metrics::AccessPoint access_point) {
switch (mode) {
case profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN:
return GetPromoURLForDialog(
access_point, signin_metrics::Reason::REASON_SIGNIN_PRIMARY_ACCOUNT,
false /* auto_close */);
break;
case profiles::BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT:
return GetPromoURLForDialog(
access_point, signin_metrics::Reason::REASON_ADD_SECONDARY_ACCOUNT,
false /* auto_close */);
break;
case profiles::BUBBLE_VIEW_MODE_GAIA_REAUTH: {
const SigninErrorController* error_controller =
SigninErrorControllerFactory::GetForProfile(profile);
CHECK(error_controller);
DCHECK(error_controller->HasError());
return GetReauthURLForDialog(
access_point, signin_metrics::Reason::REASON_REAUTHENTICATION,
profile, error_controller->error_account_id());
break;
}
default:
NOTREACHED() << "Called with invalid mode=" << mode;
return GURL();
}
}
signin_metrics::AccessPoint GetAccessPointForPromoURL(const GURL& url) {
std::string value;
if (!net::GetValueForKeyInQuery(url, kSignInPromoQueryKeyAccessPoint,
&value)) {
return signin_metrics::AccessPoint::ACCESS_POINT_UNKNOWN;
}
int access_point = -1;
base::StringToInt(value, &access_point);
if (access_point <
static_cast<int>(
signin_metrics::AccessPoint::ACCESS_POINT_START_PAGE) ||
access_point >=
static_cast<int>(signin_metrics::AccessPoint::ACCESS_POINT_MAX)) {
return signin_metrics::AccessPoint::ACCESS_POINT_UNKNOWN;
}
return static_cast<signin_metrics::AccessPoint>(access_point);
}
signin_metrics::Reason GetSigninReasonForPromoURL(const GURL& url) {
std::string value;
if (!net::GetValueForKeyInQuery(url, kSignInPromoQueryKeyReason, &value))
return signin_metrics::Reason::REASON_UNKNOWN_REASON;
int reason = -1;
base::StringToInt(value, &reason);
if (reason < static_cast<int>(
signin_metrics::Reason::REASON_SIGNIN_PRIMARY_ACCOUNT) ||
reason >= static_cast<int>(signin_metrics::Reason::REASON_MAX)) {
return signin_metrics::Reason::REASON_UNKNOWN_REASON;
}
return static_cast<signin_metrics::Reason>(reason);
}
bool IsAutoCloseEnabledInURL(const GURL& url) {
std::string value;
if (net::GetValueForKeyInQuery(url, kSignInPromoQueryKeyAutoClose, &value)) {
int enabled = 0;
if (base::StringToInt(value, &enabled) && enabled == 1)
return true;
}
return false;
}
void ForceWebBasedSigninFlowForTesting(bool force) {
g_force_web_based_signin_flow = force;
}
void RegisterProfilePrefs(
user_prefs::PrefRegistrySyncable* registry) {
registry->RegisterIntegerPref(prefs::kSignInPromoStartupCount, 0);
registry->RegisterBooleanPref(prefs::kSignInPromoUserSkipped, false);
registry->RegisterBooleanPref(prefs::kSignInPromoShowOnFirstRunAllowed, true);
registry->RegisterBooleanPref(prefs::kSignInPromoShowNTPBubble, false);
registry->RegisterIntegerPref(prefs::kDiceSigninUserMenuPromoCount, 0);
}
} // namespace signin