1 // Copyright (c) 2012 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.
5 #ifndef CHROME_BROWSER_UI_SYNC_ONE_CLICK_SIGNIN_HELPER_H_
6 #define CHROME_BROWSER_UI_SYNC_ONE_CLICK_SIGNIN_HELPER_H_
10 #include "base/gtest_prod_util.h"
11 #include "base/memory/weak_ptr.h"
12 #include "chrome/browser/signin/signin_promo.h"
13 #include "chrome/browser/sync/profile_sync_service_observer.h"
14 #include "chrome/browser/ui/sync/one_click_signin_sync_starter.h"
15 #include "content/public/browser/navigation_controller.h"
16 #include "content/public/browser/web_contents_observer.h"
17 #include "content/public/browser/web_contents_user_data.h"
18 #include "google_apis/gaia/google_service_auth_error.h"
22 class PasswordManager;
31 struct FrameNavigateParams;
32 struct LoadCommittedDetails;
39 // Per-tab one-click signin helper. When a user signs in to a Google service
40 // and the profile is not yet connected to a Google account, will start the
41 // process of helping the user connect his profile with one click. The process
42 // begins with an infobar and is followed with a confirmation dialog explaining
43 // more about what this means.
44 class OneClickSigninHelper
45 : public content::WebContentsObserver,
46 public content::WebContentsUserData<OneClickSigninHelper>,
47 public ProfileSyncServiceObserver {
49 // Represents user's decision about sign in process.
51 // User decision not yet known. Assume cancel.
54 // User has explicitly accepted to sign in. A bubble is shown with the
55 // option to start sync, configure it first, or abort.
58 // User has explicitly accepted to sign in, but wants to configure sync
59 // settings before turning it on.
60 AUTO_ACCEPT_CONFIGURE,
62 // User has explicitly rejected to sign in. Furthermore, the user does
63 // not want to be prompted to see the interstitial again in this profile.
64 AUTO_ACCEPT_REJECTED_FOR_PROFILE,
66 // This is an explicit sign in from either first run, NTP, wrench menu,
67 // or settings page. The user will be signed in automatically with sync
68 // enabled using default settings.
72 // Return value of CanOfferOnIOThread().
79 // Argument to CanOffer().
82 CAN_OFFER_FOR_INTERSTITAL_ONLY,
83 CAN_OFFER_FOR_SECONDARY_ACCOUNT
84 // TODO(guohui): needs to handle adding secondary account through
88 static void LogHistogramValue(signin::Source source, int action);
90 static void CreateForWebContentsWithPasswordManager(
91 content::WebContents* contents,
92 PasswordManager* password_manager);
94 // Returns true if the one-click signin feature can be offered at this time.
95 // If |email| is not empty, then the profile is checked to see if it's
96 // already connected to a google account or if the user has already rejected
97 // one-click sign-in with this email, in which cases a one click signin
98 // should not be offered.
100 // If |can_offer_for| is |CAN_OFFER_FOR_INTERSTITAL_ONLY|, then only do the
101 // checks that would affect the interstitial page. Otherwise, do the checks
102 // that would affect the interstitial and the explicit sign ins.
104 // Returns in |error_message_id| an explanation as a string resource ID for
105 // why one-clicked cannot be offered. |error_message_id| is valid only if
106 // the return value is false. If no explanation is needed, |error_message_id|
108 static bool CanOffer(content::WebContents* web_contents,
109 CanOfferFor can_offer_for,
110 const std::string& email,
111 std::string* error_message);
113 // Returns true if the one-click signin feature can be offered at this time.
114 // It can be offered if the io_data is not in an incognito window and if the
115 // origin of |url| is a valid Gaia sign in origin. This function is meant
116 // to called only from the IO thread.
117 static Offer CanOfferOnIOThread(net::URLRequest* request,
118 ProfileIOData* io_data);
120 // Looks for the Google-Accounts-SignIn response header, and if found,
121 // tries to display an infobar in the tab contents identified by the
123 static void ShowInfoBarIfPossible(net::URLRequest* request,
124 ProfileIOData* io_data,
128 // Handles cross account sign in error. If the supplied |email| does not match
129 // the last signed in email of the current profile, then Chrome will show a
130 // confirmation dialog before starting sync. It returns true if there is a
131 // cross ccount error, and false otherwise.
132 static bool HandleCrossAccountError(
133 content::WebContents* contents,
134 const std::string& session_index,
135 const std::string& email,
136 const std::string& password,
137 const std::string& oauth_code,
138 OneClickSigninHelper::AutoAccept auto_accept,
139 signin::Source source,
140 OneClickSigninSyncStarter::StartSyncMode start_mode,
141 OneClickSigninSyncStarter::Callback sync_callback);
143 static void RedirectToNtpOrAppsPage(
144 content::WebContents* contents, signin::Source source);
146 // If the |source| is not settings page/webstore, redirects to
147 // the NTP/Apps page.
148 static void RedirectToNtpOrAppsPageIfNecessary(
149 content::WebContents* contents, signin::Source source);
151 static void ShowSigninErrorBubble(Browser* browser, const std::string& error);
153 // Remove the item currently at the top of the history list if it's
154 // the Gaia redirect URL. Due to limitations of the NavigationController
155 // this cannot be done until a new page becomes "current".
156 static void RemoveSigninRedirectURLHistoryItem(
157 content::WebContents* web_contents);
159 static void LogConfirmHistogramValue(int action);
162 friend class content::WebContentsUserData<OneClickSigninHelper>;
163 friend class OneClickSigninHelperTest;
164 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIncognitoTest,
165 ShowInfoBarUIThreadIncognito);
166 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperTest,
167 SigninFromWebstoreWithConfigSyncfirst);
168 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperTest,
169 ShowSigninBubbleAfterSigninComplete);
170 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperTest, SigninCancelled);
171 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperTest, SigninFailed);
172 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperTest,
173 CleanTransientStateOnNavigate);
174 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperTest,
175 RemoveObserverFromProfileSyncService);
176 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest, CanOfferOnIOThread);
177 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
178 CanOfferOnIOThreadIncognito);
179 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
180 CanOfferOnIOThreadNoIOData);
181 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
182 CanOfferOnIOThreadBadURL);
183 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
184 CanOfferOnIOThreadDisabled);
185 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
186 CanOfferOnIOThreadSignedIn);
187 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
188 CanOfferOnIOThreadEmailNotAllowed);
189 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
190 CanOfferOnIOThreadEmailAlreadyUsed);
191 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
192 CreateTestProfileIOData);
193 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
194 CanOfferOnIOThreadWithRejectedEmail);
195 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
196 CanOfferOnIOThreadNoSigninCookies);
197 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
198 CanOfferOnIOThreadDisabledByPolicy);
200 // Maximum number of navigations away from the set of valid Gaia URLs before
201 // clearing the internal state of the helper. This is necessary to support
202 // SAML-based accounts, but causes bug crbug.com/181163.
203 static const int kMaxNavigationsSince;
205 OneClickSigninHelper(content::WebContents* web_contents,
206 PasswordManager* password_manager);
208 virtual ~OneClickSigninHelper();
210 // Returns true if the one-click signin feature can be offered at this time.
211 // It can be offered if the io_data is not in an incognito window and if the
212 // origin of |url| is a valid Gaia sign in origin. This function is meant
213 // to called only from the IO thread.
214 static Offer CanOfferOnIOThreadImpl(const GURL& url,
215 base::SupportsUserData* request,
216 ProfileIOData* io_data);
218 // The portion of ShowInfoBarIfPossible() that needs to run on the UI thread.
219 // |session_index| and |email| are extracted from the Google-Accounts-SignIn
220 // header. |auto_accept| is extracted from the Google-Chrome-SignIn header.
221 // |source| is used to determine which of the explicit sign in mechanism is
224 // |continue_url| is where Gaia will continue to when the sign in process is
225 // done. For explicit sign ins, this is a URL chrome controls. For one-click
226 // sign in, this could be any google property. This URL is used to know
227 // when the sign process is over and to collect infomation from the user
228 // entered on the Gaia sign in page (for explicit sign ins).
229 static void ShowInfoBarUIThread(const std::string& session_index,
230 const std::string& email,
231 AutoAccept auto_accept,
232 signin::Source source,
233 const GURL& continue_url,
237 void RedirectToSignin();
239 // Clear all data member of the helper, except for the error.
240 void CleanTransientState();
242 // Unitests that use a TestingProfile should call this.
243 // Otherwise, clearing the pending e-mail crashes because the code expects
244 // a real ResourceContext rather than the MockResourceContext a
245 // TestingProfile provides.
246 void SetDoNotClearPendingEmailForTesting();
248 // In unit tests, disable starting the actual sync.
249 void set_do_not_start_sync_for_testing();
251 // Called when password has been submitted.
252 void PasswordSubmitted(const autofill::PasswordForm& form);
254 // content::WebContentsObserver overrides.
255 virtual void DidStartNavigationToPendingEntry(
257 content::NavigationController::ReloadType reload_type) OVERRIDE;
258 virtual void DidNavigateMainFrame(
259 const content::LoadCommittedDetails& details,
260 const content::FrameNavigateParams& params) OVERRIDE;
261 virtual void DidStopLoading(
262 content::RenderViewHost* render_view_host) OVERRIDE;
263 virtual void WebContentsDestroyed(content::WebContents* contents) OVERRIDE;
265 // ProfileSyncServiceObserver.
266 virtual void OnStateChanged() OVERRIDE;
268 OneClickSigninSyncStarter::Callback CreateSyncStarterCallback();
270 // Callback invoked when OneClickSigninSyncStarter completes sync setup.
271 void SyncSetupCompletedCallback(
272 OneClickSigninSyncStarter::SyncSetupResult result);
274 // Tracks if we are in the process of showing the signin or one click
275 // interstitial page. It's set to true the first time we load one of those
276 // pages and set to false when transient state is cleaned.
277 // Note: This should only be used for logging purposes.
278 bool showing_signin_;
280 // Information about the account that has just logged in.
281 std::string session_index_;
283 std::string password_;
284 AutoAccept auto_accept_;
285 signin::Source source_;
286 bool switched_to_advanced_;
288 // The orignal continue URL after sync setup is complete.
289 GURL original_continue_url_;
290 std::string error_message_;
292 // Number of navigations since starting a sign in that is outside the
293 // the set of trusted Gaia URLs. Sign in attempts that include visits to
294 // one more untrusted will cause a modal dialog to appear asking the user
295 // to confirm, similar to the interstitial flow.
296 int untrusted_navigations_since_signin_visit_;
298 // Whether a Gaia URL during the sign in process was not handled by the
299 // dedicated sign in process (e.g. SAML login, which redirects to a
300 // non-google-controlled domain).
301 // This is set to true if at least one such URL is detected.
302 bool untrusted_confirmation_required_;
304 // Allows unittests to avoid accessing the ResourceContext for clearing a
306 bool do_not_clear_pending_email_;
308 // Allows unittest to avoid starting sync for real.
309 bool do_not_start_sync_for_testing_;
311 base::WeakPtrFactory<OneClickSigninHelper> weak_pointer_factory_;
313 DISALLOW_COPY_AND_ASSIGN(OneClickSigninHelper);
316 #endif // CHROME_BROWSER_UI_SYNC_ONE_CLICK_SIGNIN_HELPER_H_