signin_promo.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/signin/signin_promo.h"
6
7#include "base/prefs/pref_service.h"
8#include "base/strings/string_number_conversions.h"
9#include "base/strings/string_util.h"
10#include "base/strings/stringprintf.h"
11#include "base/strings/utf_string_conversions.h"
12#include "chrome/browser/browser_process.h"
13#include "chrome/browser/first_run/first_run.h"
14#include "chrome/browser/google/google_util.h"
15#include "chrome/browser/profiles/profile.h"
16#include "chrome/browser/profiles/profile_info_cache.h"
17#include "chrome/browser/profiles/profile_manager.h"
18#include "chrome/browser/signin/signin_manager_factory.h"
19#include "chrome/browser/ui/webui/options/core_options_handler.h"
20#include "chrome/browser/ui/webui/theme_source.h"
21#include "chrome/common/net/url_util.h"
22#include "chrome/common/pref_names.h"
23#include "chrome/common/url_constants.h"
24#include "components/pref_registry/pref_registry_syncable.h"
25#include "components/signin/core/browser/signin_manager.h"
26#include "components/signin/core/common/profile_management_switches.h"
27#include "content/public/browser/url_data_source.h"
28#include "content/public/browser/web_contents.h"
29#include "content/public/browser/web_ui.h"
30#include "content/public/browser/web_ui_data_source.h"
31#include "google_apis/gaia/gaia_urls.h"
32#include "grit/browser_resources.h"
33#include "grit/generated_resources.h"
34#include "grit/theme_resources.h"
35#include "net/base/escape.h"
36#include "net/base/network_change_notifier.h"
37#include "net/base/url_util.h"
38#include "ui/base/l10n/l10n_util.h"
39#include "url/gurl.h"
40
41using content::WebContents;
42using net::GetValueForKeyInQuery;
43
44namespace {
45
46const char kSignInPromoQueryKeyAutoClose[] = "auto_close";
47const char kSignInPromoQueryKeyContinue[] = "continue";
48const char kSignInPromoQueryKeySource[] = "source";
49const char kSignInPromoQueryKeyConstrained[] = "constrained";
50
51// Gaia cannot support about:blank as a continue URL, so using a hosted blank
52// page instead.
53const char kSignInLandingUrlPrefix[] =
54    "https://www.google.com/intl/%s/chrome/blank.html";
55
56// The maximum number of times we want to show the sign in promo at startup.
57const int kSignInPromoShowAtStartupMaximum = 10;
58
59// Forces the web based signin flow when set.
60bool g_force_web_based_signin_flow = false;
61
62// Checks we want to show the sign in promo for the given brand.
63bool AllowPromoAtStartupForCurrentBrand() {
64  std::string brand;
65  google_util::GetBrand(&brand);
66
67  if (brand.empty())
68    return true;
69
70  if (google_util::IsInternetCafeBrandCode(brand))
71    return false;
72
73  // Enable for both organic and distribution.
74  return true;
75}
76
77// Returns true if a user has seen the sign in promo at startup previously.
78bool HasShownPromoAtStartup(Profile* profile) {
79  return profile->GetPrefs()->HasPrefPath(prefs::kSignInPromoStartupCount);
80}
81
82// Returns true if the user has previously skipped the sign in promo.
83bool HasUserSkippedPromo(Profile* profile) {
84  return profile->GetPrefs()->GetBoolean(prefs::kSignInPromoUserSkipped);
85}
86
87}  // namespace
88
89namespace signin {
90
91bool ShouldShowPromo(Profile* profile) {
92#if defined(OS_CHROMEOS)
93  // There's no need to show the sign in promo on cros since cros users are
94  // already logged in.
95  return false;
96#else
97
98  // Don't bother if we don't have any kind of network connection.
99  if (net::NetworkChangeNotifier::IsOffline())
100    return false;
101
102  // Don't show for managed profiles.
103  if (profile->IsManaged())
104    return false;
105
106  // Display the signin promo if the user is not signed in.
107  SigninManager* signin = SigninManagerFactory::GetForProfile(
108      profile->GetOriginalProfile());
109  return !signin->AuthInProgress() && signin->IsSigninAllowed() &&
110      signin->GetAuthenticatedUsername().empty();
111#endif
112}
113
114bool ShouldShowPromoAtStartup(Profile* profile, bool is_new_profile) {
115  DCHECK(profile);
116
117  // Don't show if the profile is an incognito.
118  if (profile->IsOffTheRecord())
119    return false;
120
121  if (!ShouldShowPromo(profile))
122    return false;
123
124  if (!is_new_profile) {
125    if (!HasShownPromoAtStartup(profile))
126      return false;
127  }
128
129  if (HasUserSkippedPromo(profile))
130    return false;
131
132  // For Chinese users skip the sign in promo.
133  if (g_browser_process->GetApplicationLocale() == "zh-CN")
134    return false;
135
136  PrefService* prefs = profile->GetPrefs();
137  int show_count = prefs->GetInteger(prefs::kSignInPromoStartupCount);
138  if (show_count >= kSignInPromoShowAtStartupMaximum)
139    return false;
140
141  // This pref can be set in the master preferences file to allow or disallow
142  // showing the sign in promo at startup.
143  if (prefs->HasPrefPath(prefs::kSignInPromoShowOnFirstRunAllowed))
144    return prefs->GetBoolean(prefs::kSignInPromoShowOnFirstRunAllowed);
145
146  // For now don't show the promo for some brands.
147  if (!AllowPromoAtStartupForCurrentBrand())
148    return false;
149
150  // Default to show the promo for Google Chrome builds.
151#if defined(GOOGLE_CHROME_BUILD)
152  return true;
153#else
154  return false;
155#endif
156}
157
158void DidShowPromoAtStartup(Profile* profile) {
159  int show_count = profile->GetPrefs()->GetInteger(
160      prefs::kSignInPromoStartupCount);
161  show_count++;
162  profile->GetPrefs()->SetInteger(prefs::kSignInPromoStartupCount, show_count);
163}
164
165void SetUserSkippedPromo(Profile* profile) {
166  profile->GetPrefs()->SetBoolean(prefs::kSignInPromoUserSkipped, true);
167}
168
169GURL GetLandingURL(const char* option, int value) {
170  const std::string& locale = g_browser_process->GetApplicationLocale();
171  std::string url = base::StringPrintf(kSignInLandingUrlPrefix, locale.c_str());
172  base::StringAppendF(&url, "?%s=%d", option, value);
173  return GURL(url);
174}
175
176GURL GetPromoURL(Source source, bool auto_close) {
177  return GetPromoURL(source, auto_close, false /* is_constrained */);
178}
179
180GURL GetPromoURL(Source source, bool auto_close, bool is_constrained) {
181  return GetPromoURLWithContinueURL(source, auto_close, is_constrained, GURL());
182}
183
184GURL GetPromoURLWithContinueURL(Source source,
185                                bool auto_close,
186                                bool is_constrained,
187                                GURL continue_url) {
188  DCHECK_NE(SOURCE_UNKNOWN, source);
189
190  if (!switches::IsEnableWebBasedSignin()) {
191    std::string url(chrome::kChromeUIChromeSigninURL);
192    base::StringAppendF(&url, "?%s=%d", kSignInPromoQueryKeySource, source);
193    if (auto_close)
194      base::StringAppendF(&url, "&%s=1", kSignInPromoQueryKeyAutoClose);
195    if (is_constrained)
196      base::StringAppendF(&url, "&%s=1", kSignInPromoQueryKeyConstrained);
197    if (!continue_url.is_empty()) {
198      DCHECK(continue_url.is_valid());
199      std::string escaped_continue_url =
200          net::EscapeQueryParamValue(continue_url.spec(), false);
201      base::StringAppendF(&url,
202                          "&%s=%s",
203                          kSignInPromoQueryKeyContinue,
204                          escaped_continue_url.c_str());
205    }
206    return GURL(url);
207  }
208
209  // Build a Gaia-based URL that can be used to sign the user into chrome.
210  // There are required request parameters:
211  //
212  //  - tell Gaia which service the user is signing into.  In this case,
213  //    a chrome sign in uses the service "chromiumsync"
214  //  - provide a continue URL.  This is the URL that Gaia will redirect to
215  //    once the sign is complete.
216  //
217  // The continue URL includes a source parameter that can be extracted using
218  // the function GetSourceForSignInPromoURL() below.  This is used to know
219  // which of the chrome sign in access points was used to sign the user in.
220  // It is also parsed for the |auto_close| flag, which indicates that the tab
221  // must be closed after sync setup is successful.
222  // See OneClickSigninHelper for details.
223  std::string query_string = "?service=chromiumsync&sarp=1";
224
225  DCHECK(continue_url.is_empty());
226  std::string continue_url_str = GetLandingURL(kSignInPromoQueryKeySource,
227                                               static_cast<int>(source)).spec();
228  if (auto_close) {
229    base::StringAppendF(
230        &continue_url_str, "&%s=1", kSignInPromoQueryKeyAutoClose);
231  }
232
233  base::StringAppendF(
234      &query_string,
235      "&%s=%s",
236      kSignInPromoQueryKeyContinue,
237      net::EscapeQueryParamValue(continue_url_str, false).c_str());
238
239  return GaiaUrls::GetInstance()->service_login_url().Resolve(query_string);
240}
241
242GURL GetReauthURL(Profile* profile, const std::string& account_id) {
243  if (switches::IsEnableWebBasedSignin()) {
244    return net::AppendQueryParameter(
245        signin::GetPromoURL(signin::SOURCE_SETTINGS, true),
246        "Email",
247        account_id);
248  }
249
250  signin::Source source = switches::IsNewProfileManagement() ?
251      signin::SOURCE_REAUTH : signin::SOURCE_SETTINGS;
252
253  GURL url = signin::GetPromoURL(
254      source, true /* auto_close */,
255      switches::IsNewProfileManagement() /* is_constrained */);
256  url = net::AppendQueryParameter(url, "email", account_id);
257  url = net::AppendQueryParameter(url, "validateEmail", "1");
258  return net::AppendQueryParameter(url, "readOnlyEmail", "1");
259}
260
261GURL GetNextPageURLForPromoURL(const GURL& url) {
262  std::string value;
263  if (GetValueForKeyInQuery(url, kSignInPromoQueryKeyContinue, &value)) {
264    GURL continue_url = GURL(value);
265    if (continue_url.is_valid())
266      return continue_url;
267  }
268
269  return GURL();
270}
271
272Source GetSourceForPromoURL(const GURL& url) {
273  std::string value;
274  if (GetValueForKeyInQuery(url, kSignInPromoQueryKeySource, &value)) {
275    int source = 0;
276    if (base::StringToInt(value, &source) && source >= SOURCE_START_PAGE &&
277        source < SOURCE_UNKNOWN) {
278      return static_cast<Source>(source);
279    }
280  }
281  return SOURCE_UNKNOWN;
282}
283
284bool IsAutoCloseEnabledInURL(const GURL& url) {
285  std::string value;
286  if (GetValueForKeyInQuery(url, kSignInPromoQueryKeyAutoClose, &value)) {
287    int enabled = 0;
288    if (base::StringToInt(value, &enabled) && enabled == 1)
289      return true;
290  }
291  return false;
292}
293
294bool IsContinueUrlForWebBasedSigninFlow(const GURL& url) {
295  GURL::Replacements replacements;
296  replacements.ClearQuery();
297  const std::string& locale = g_browser_process->GetApplicationLocale();
298  GURL continue_url =
299      GURL(base::StringPrintf(kSignInLandingUrlPrefix, locale.c_str()));
300  return (
301      google_util::IsGoogleDomainUrl(
302          url,
303          google_util::ALLOW_SUBDOMAIN,
304          google_util::DISALLOW_NON_STANDARD_PORTS) &&
305      url.ReplaceComponents(replacements).path() ==
306        continue_url.ReplaceComponents(replacements).path());
307}
308
309void ForceWebBasedSigninFlowForTesting(bool force) {
310  g_force_web_based_signin_flow = force;
311}
312
313void RegisterProfilePrefs(
314    user_prefs::PrefRegistrySyncable* registry) {
315  registry->RegisterIntegerPref(
316      prefs::kSignInPromoStartupCount,
317      0,
318      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
319  registry->RegisterBooleanPref(
320      prefs::kSignInPromoUserSkipped,
321      false,
322      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
323  registry->RegisterBooleanPref(
324      prefs::kSignInPromoShowOnFirstRunAllowed,
325      true,
326      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
327  registry->RegisterBooleanPref(
328      prefs::kSignInPromoShowNTPBubble,
329      false,
330      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
331}
332
333}  // namespace signin
334