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