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_oauth_helper.h"
13 #include "chrome/browser/signin/signin_promo.h"
14 #include "chrome/browser/sync/profile_sync_service_observer.h"
15 #include "chrome/browser/ui/sync/one_click_signin_sync_starter.h"
16 #include "content/public/browser/navigation_controller.h"
17 #include "content/public/browser/web_contents_observer.h"
18 #include "content/public/browser/web_contents_user_data.h"
19 #include "google_apis/gaia/google_service_auth_error.h"
23 class PasswordManager;
32 struct FrameNavigateParams;
33 struct LoadCommittedDetails;
40 // Per-tab one-click signin helper. When a user signs in to a Google service
41 // and the profile is not yet connected to a Google account, will start the
42 // process of helping the user connect his profile with one click. The process
43 // begins with an infobar and is followed with a confirmation dialog explaining
44 // more about what this means.
45 class OneClickSigninHelper
46 : public content::WebContentsObserver,
47 public content::WebContentsUserData<OneClickSigninHelper>,
48 public ProfileSyncServiceObserver {
50 // Represents user's decision about sign in process.
52 // User decision not yet known. Assume cancel.
55 // User has explicitly accepted to sign in. A bubble is shown with the
56 // option to start sync, configure it first, or abort.
59 // User has explicitly accepted to sign in, but wants to configure sync
60 // settings before turning it on.
61 AUTO_ACCEPT_CONFIGURE,
63 // User has explicitly rejected to sign in. Furthermore, the user does
64 // not want to be prompted to see the interstitial again in this profile.
65 AUTO_ACCEPT_REJECTED_FOR_PROFILE,
67 // This is an explicit sign in from either first run, NTP, wrench menu,
68 // or settings page. The user will be signed in automatically with sync
69 // enabled using default settings.
73 // Return value of CanOfferOnIOThread().
80 // Argument to CanOffer().
83 CAN_OFFER_FOR_INTERSTITAL_ONLY,
84 CAN_OFFER_FOR_SECONDARY_ACCOUNT
85 // TODO(guohui): needs to handle adding secondary account through
89 // Arguments used with StartSync function. base::Bind() cannot support too
90 // many args for performance reasons, so they are packaged up into a struct.
91 struct StartSyncArgs {
92 // Default contructor for testing only.
94 StartSyncArgs(Profile* profile,
96 OneClickSigninHelper::AutoAccept auto_accept,
97 const std::string& session_index,
98 const std::string& email,
99 const std::string& password,
100 const std::string& refresh_token,
101 content::WebContents* web_contents,
102 bool untrusted_confirmation_required,
103 signin::Source source,
104 OneClickSigninSyncStarter::Callback callback);
109 OneClickSigninHelper::AutoAccept auto_accept;
110 std::string session_index;
112 std::string password;
113 std::string refresh_token;
115 // Web contents in which the sync setup page should be displayed,
116 // if necessary. Can be NULL.
117 content::WebContents* web_contents;
119 OneClickSigninSyncStarter::ConfirmationRequired confirmation_required;
120 signin::Source source;
121 OneClickSigninSyncStarter::Callback callback;
124 // Wrapper to call OneClickSigninSyncStarter after fetching the refresh token
125 // if needed. Also verifies that the cookies are correct if no password is
126 // specified, and checks that the email from the cookies match the expected
128 class SyncStarterWrapper : public SigninOAuthHelper::Consumer,
129 public chrome::BrowserListObserver {
132 const OneClickSigninHelper::StartSyncArgs& args,
133 OneClickSigninSyncStarter::StartSyncMode start_mode);
134 virtual ~SyncStarterWrapper();
139 void VerifyGaiaCookiesBeforeSignIn();
140 void OnGaiaCookiesFetched(const std::string session_index,
141 const net::CookieList& cookie_list);
143 // Virtual to be overridden in tests.
144 virtual void DisplayErrorBubble(const std::string& error_message);
145 virtual void StartSigninOAuthHelper();
146 virtual void StartOneClickSigninSyncStarter(
147 const std::string& email,
148 const std::string& refresh_token);
150 // Overriden from SigninOAuthHelper::Consumer.
151 virtual void OnSigninOAuthInformationAvailable(
152 const std::string& email,
153 const std::string& display_email,
154 const std::string& refresh_token) OVERRIDE;
155 virtual void OnSigninOAuthInformationFailure(
156 const GoogleServiceAuthError& error) OVERRIDE;
158 // Overriden from chrome::BrowserListObserver.
159 virtual void OnBrowserRemoved(Browser* browser) OVERRIDE;
161 OneClickSigninHelper::StartSyncArgs args_;
162 chrome::HostDesktopType desktop_type_;
163 OneClickSigninSyncStarter::StartSyncMode start_mode_;
164 scoped_ptr<SigninOAuthHelper> signin_oauth_helper_;
165 base::WeakPtrFactory<SyncStarterWrapper> weak_pointer_factory_;
167 DISALLOW_COPY_AND_ASSIGN(SyncStarterWrapper);
170 static void LogHistogramValue(signin::Source source, int action);
172 static void CreateForWebContentsWithPasswordManager(
173 content::WebContents* contents,
174 PasswordManager* password_manager);
176 // Returns true if the one-click signin feature can be offered at this time.
177 // If |email| is not empty, then the profile is checked to see if it's
178 // already connected to a google account or if the user has already rejected
179 // one-click sign-in with this email, in which cases a one click signin
180 // should not be offered.
182 // If |can_offer_for| is |CAN_OFFER_FOR_INTERSTITAL_ONLY|, then only do the
183 // checks that would affect the interstitial page. Otherwise, do the checks
184 // that would affect the interstitial and the explicit sign ins.
186 // Returns in |error_message_id| an explanation as a string resource ID for
187 // why one-clicked cannot be offered. |error_message_id| is valid only if
188 // the return value is false. If no explanation is needed, |error_message_id|
190 static bool CanOffer(content::WebContents* web_contents,
191 CanOfferFor can_offer_for,
192 const std::string& email,
193 std::string* error_message);
195 // Returns true if the one-click signin feature can be offered at this time.
196 // It can be offered if the io_data is not in an incognito window and if the
197 // origin of |url| is a valid Gaia sign in origin. This function is meant
198 // to called only from the IO thread.
199 static Offer CanOfferOnIOThread(net::URLRequest* request,
200 ProfileIOData* io_data);
202 // Looks for the Google-Accounts-SignIn response header, and if found,
203 // tries to display an infobar in the tab contents identified by the
205 static void ShowInfoBarIfPossible(net::URLRequest* request,
206 ProfileIOData* io_data,
210 // Handles cross account sign in error. If the supplied |email| does not match
211 // the last signed in email of the current profile, then Chrome will show a
212 // confirmation dialog before starting sync. It returns true if there is a
213 // cross ccount error, and false otherwise.
214 static bool HandleCrossAccountError(
215 content::WebContents* contents,
216 const std::string& session_index,
217 const std::string& email,
218 const std::string& password,
219 const std::string& refresh_token,
220 OneClickSigninHelper::AutoAccept auto_accept,
221 signin::Source source,
222 OneClickSigninSyncStarter::StartSyncMode start_mode,
223 OneClickSigninSyncStarter::Callback sync_callback);
225 static void RedirectToNtpOrAppsPage(
226 content::WebContents* contents, signin::Source source);
228 // If the |source| is not settings page/webstore, redirects to
229 // the NTP/Apps page.
230 static void RedirectToNtpOrAppsPageIfNecessary(
231 content::WebContents* contents, signin::Source source);
233 static void ShowSigninErrorBubble(Browser* browser, const std::string& error);
235 // Remove the item currently at the top of the history list if it's
236 // the Gaia redirect URL. Due to limitations of the NavigationController
237 // this cannot be done until a new page becomes "current".
238 static void RemoveSigninRedirectURLHistoryItem(
239 content::WebContents* web_contents);
241 static void LogConfirmHistogramValue(int action);
244 friend class content::WebContentsUserData<OneClickSigninHelper>;
245 friend class OneClickSigninHelperTest;
246 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIncognitoTest,
247 ShowInfoBarUIThreadIncognito);
248 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperTest,
249 SigninFromWebstoreWithConfigSyncfirst);
250 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperTest,
251 ShowSigninBubbleAfterSigninComplete);
252 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperTest, SigninCancelled);
253 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperTest, SigninFailed);
254 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperTest,
255 CleanTransientStateOnNavigate);
256 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperTest,
257 RemoveObserverFromProfileSyncService);
258 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest, CanOfferOnIOThread);
259 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
260 CanOfferOnIOThreadIncognito);
261 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
262 CanOfferOnIOThreadNoIOData);
263 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
264 CanOfferOnIOThreadBadURL);
265 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
266 CanOfferOnIOThreadDisabled);
267 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
268 CanOfferOnIOThreadSignedIn);
269 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
270 CanOfferOnIOThreadEmailNotAllowed);
271 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
272 CanOfferOnIOThreadEmailAlreadyUsed);
273 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
274 CreateTestProfileIOData);
275 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
276 CanOfferOnIOThreadWithRejectedEmail);
277 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
278 CanOfferOnIOThreadNoSigninCookies);
279 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
280 CanOfferOnIOThreadDisabledByPolicy);
282 // Maximum number of navigations away from the set of valid Gaia URLs before
283 // clearing the internal state of the helper. This is necessary to support
284 // SAML-based accounts, but causes bug crbug.com/181163.
285 static const int kMaxNavigationsSince;
287 OneClickSigninHelper(content::WebContents* web_contents,
288 PasswordManager* password_manager);
290 virtual ~OneClickSigninHelper();
292 // Returns true if the one-click signin feature can be offered at this time.
293 // It can be offered if the io_data is not in an incognito window and if the
294 // origin of |url| is a valid Gaia sign in origin. This function is meant
295 // to called only from the IO thread.
296 static Offer CanOfferOnIOThreadImpl(const GURL& url,
297 base::SupportsUserData* request,
298 ProfileIOData* io_data);
300 // The portion of ShowInfoBarIfPossible() that needs to run on the UI thread.
301 // |session_index| and |email| are extracted from the Google-Accounts-SignIn
302 // header. |auto_accept| is extracted from the Google-Chrome-SignIn header.
303 // |source| is used to determine which of the explicit sign in mechanism is
306 // |continue_url| is where Gaia will continue to when the sign in process is
307 // done. For explicit sign ins, this is a URL chrome controls. For one-click
308 // sign in, this could be any google property. This URL is used to know
309 // when the sign process is over and to collect infomation from the user
310 // entered on the Gaia sign in page (for explicit sign ins).
311 static void ShowInfoBarUIThread(const std::string& session_index,
312 const std::string& email,
313 AutoAccept auto_accept,
314 signin::Source source,
315 const GURL& continue_url,
319 void RedirectToSignin();
321 // Clear all data member of the helper, except for the error.
322 void CleanTransientState();
324 // Unitests that use a TestingProfile should call this.
325 // Otherwise, clearing the pending e-mail crashes because the code expects
326 // a real ResourceContext rather than the MockResourceContext a
327 // TestingProfile provides.
328 void SetDoNotClearPendingEmailForTesting();
330 // In unit tests, disable starting the actual sync.
331 void set_do_not_start_sync_for_testing();
333 // Called when password has been submitted.
334 void PasswordSubmitted(const autofill::PasswordForm& form);
336 // content::WebContentsObserver overrides.
337 virtual void DidStartNavigationToPendingEntry(
339 content::NavigationController::ReloadType reload_type) OVERRIDE;
340 virtual void DidNavigateMainFrame(
341 const content::LoadCommittedDetails& details,
342 const content::FrameNavigateParams& params) OVERRIDE;
343 virtual void DidStopLoading(
344 content::RenderViewHost* render_view_host) OVERRIDE;
345 virtual void WebContentsDestroyed(content::WebContents* contents) OVERRIDE;
347 // ProfileSyncServiceObserver.
348 virtual void OnStateChanged() OVERRIDE;
350 OneClickSigninSyncStarter::Callback CreateSyncStarterCallback();
352 // Callback invoked when OneClickSigninSyncStarter completes sync setup.
353 void SyncSetupCompletedCallback(
354 OneClickSigninSyncStarter::SyncSetupResult result);
356 // Tracks if we are in the process of showing the signin or one click
357 // interstitial page. It's set to true the first time we load one of those
358 // pages and set to false when transient state is cleaned.
359 // Note: This should only be used for logging purposes.
360 bool showing_signin_;
362 // Information about the account that has just logged in.
363 std::string session_index_;
365 std::string password_;
366 AutoAccept auto_accept_;
367 signin::Source source_;
368 bool switched_to_advanced_;
370 // The orignal continue URL after sync setup is complete.
371 GURL original_continue_url_;
372 std::string error_message_;
374 // Number of navigations since starting a sign in that is outside the
375 // the set of trusted Gaia URLs. Sign in attempts that include visits to
376 // one more untrusted will cause a modal dialog to appear asking the user
377 // to confirm, similar to the interstitial flow.
378 int untrusted_navigations_since_signin_visit_;
380 // Whether a Gaia URL during the sign in process was not handled by the
381 // dedicated sign in process (e.g. SAML login, which redirects to a
382 // non-google-controlled domain).
383 // This is set to true if at least one such URL is detected.
384 bool untrusted_confirmation_required_;
386 // Allows unittests to avoid accessing the ResourceContext for clearing a
388 bool do_not_clear_pending_email_;
390 // Allows unittest to avoid starting sync for real.
391 bool do_not_start_sync_for_testing_;
393 base::WeakPtrFactory<OneClickSigninHelper> weak_pointer_factory_;
395 DISALLOW_COPY_AND_ASSIGN(OneClickSigninHelper);
398 #endif // CHROME_BROWSER_UI_SYNC_ONE_CLICK_SIGNIN_HELPER_H_