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