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 CreateForWebContentsWithPasswordManager(
89 content::WebContents
* contents
,
90 PasswordManager
* password_manager
);
92 // Returns true if the one-click signin feature can be offered at this time.
93 // If |email| is not empty, then the profile is checked to see if it's
94 // already connected to a google account or if the user has already rejected
95 // one-click sign-in with this email, in which cases a one click signin
96 // should not be offered.
98 // If |can_offer_for| is |CAN_OFFER_FOR_INTERSTITAL_ONLY|, then only do the
99 // checks that would affect the interstitial page. Otherwise, do the checks
100 // that would affect the interstitial and the explicit sign ins.
102 // Returns in |error_message_id| an explanation as a string resource ID for
103 // why one-clicked cannot be offered. |error_message_id| is valid only if
104 // the return value is false. If no explanation is needed, |error_message_id|
106 static bool CanOffer(content::WebContents
* web_contents
,
107 CanOfferFor can_offer_for
,
108 const std::string
& email
,
109 std::string
* error_message
);
111 // Returns true if the one-click signin feature can be offered at this time.
112 // It can be offered if the io_data is not in an incognito window and if the
113 // origin of |url| is a valid Gaia sign in origin. This function is meant
114 // to called only from the IO thread.
115 static Offer
CanOfferOnIOThread(net::URLRequest
* request
,
116 ProfileIOData
* io_data
);
118 // Looks for the Google-Accounts-SignIn response header, and if found,
119 // tries to display an infobar in the tab contents identified by the
121 static void ShowInfoBarIfPossible(net::URLRequest
* request
,
122 ProfileIOData
* io_data
,
126 // Handles cross account sign in error. If the supplied |email| does not match
127 // the last signed in email of the current profile, then Chrome will show a
128 // confirmation dialog before starting sync. It returns true if there is a
129 // cross ccount error, and false otherwise.
130 static bool HandleCrossAccountError(
131 content::WebContents
* contents
,
132 const std::string
& session_index
,
133 const std::string
& email
,
134 const std::string
& password
,
135 const std::string
& oauth_code
,
136 OneClickSigninHelper::AutoAccept auto_accept
,
137 signin::Source source
,
138 OneClickSigninSyncStarter::StartSyncMode start_mode
,
139 OneClickSigninSyncStarter::Callback sync_callback
);
141 // If the |source| is not settings page/webstore, redirects to
142 // the NTP/Apps page.
143 static void RedirectToNtpOrAppsPageIfNecessary(
144 content::WebContents
* contents
, signin::Source source
);
146 static void ShowSigninErrorBubble(Browser
* browser
, const std::string
& error
);
148 // Remove the item currently at the top of the history list if it's
149 // the Gaia redirect URL. Due to limitations of the NavigationController
150 // this cannot be done until a new page becomes "current".
151 static void RemoveSigninRedirectURLHistoryItem(
152 content::WebContents
* web_contents
);
154 static void LogConfirmHistogramValue(int action
);
157 friend class content::WebContentsUserData
<OneClickSigninHelper
>;
158 friend class OneClickSigninHelperTest
;
159 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIncognitoTest
,
160 ShowInfoBarUIThreadIncognito
);
161 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperTest
,
162 SigninFromWebstoreWithConfigSyncfirst
);
163 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperTest
,
164 ShowSigninBubbleAfterSigninComplete
);
165 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperTest
, SigninCancelled
);
166 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperTest
, SigninFailed
);
167 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperTest
,
168 CleanTransientStateOnNavigate
);
169 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperTest
,
170 RemoveObserverFromProfileSyncService
);
171 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest
, CanOfferOnIOThread
);
172 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest
,
173 CanOfferOnIOThreadIncognito
);
174 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest
,
175 CanOfferOnIOThreadNoIOData
);
176 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest
,
177 CanOfferOnIOThreadBadURL
);
178 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest
,
179 CanOfferOnIOThreadDisabled
);
180 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest
,
181 CanOfferOnIOThreadSignedIn
);
182 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest
,
183 CanOfferOnIOThreadEmailNotAllowed
);
184 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest
,
185 CanOfferOnIOThreadEmailAlreadyUsed
);
186 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest
,
187 CreateTestProfileIOData
);
188 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest
,
189 CanOfferOnIOThreadWithRejectedEmail
);
190 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest
,
191 CanOfferOnIOThreadNoSigninCookies
);
192 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest
,
193 CanOfferOnIOThreadDisabledByPolicy
);
195 // Maximum number of navigations away from the set of valid Gaia URLs before
196 // clearing the internal state of the helper. This is necessary to support
197 // SAML-based accounts, but causes bug crbug.com/181163.
198 static const int kMaxNavigationsSince
;
200 OneClickSigninHelper(content::WebContents
* web_contents
,
201 PasswordManager
* password_manager
);
203 virtual ~OneClickSigninHelper();
205 // Returns true if the one-click signin feature can be offered at this time.
206 // It can be offered if the io_data is not in an incognito window and if the
207 // origin of |url| is a valid Gaia sign in origin. This function is meant
208 // to called only from the IO thread.
209 static Offer
CanOfferOnIOThreadImpl(const GURL
& url
,
210 base::SupportsUserData
* request
,
211 ProfileIOData
* io_data
);
213 // The portion of ShowInfoBarIfPossible() that needs to run on the UI thread.
214 // |session_index| and |email| are extracted from the Google-Accounts-SignIn
215 // header. |auto_accept| is extracted from the Google-Chrome-SignIn header.
216 // |source| is used to determine which of the explicit sign in mechanism is
219 // |continue_url| is where Gaia will continue to when the sign in process is
220 // done. For explicit sign ins, this is a URL chrome controls. For one-click
221 // sign in, this could be any google property. This URL is used to know
222 // when the sign process is over and to collect infomation from the user
223 // entered on the Gaia sign in page (for explicit sign ins).
224 static void ShowInfoBarUIThread(const std::string
& session_index
,
225 const std::string
& email
,
226 AutoAccept auto_accept
,
227 signin::Source source
,
228 const GURL
& continue_url
,
232 void RedirectToSignin();
234 // Clear all data member of the helper, except for the error.
235 void CleanTransientState();
237 // Unitests that use a TestingProfile should call this.
238 // Otherwise, clearing the pending e-mail crashes because the code expects
239 // a real ResourceContext rather than the MockResourceContext a
240 // TestingProfile provides.
241 void SetDoNotClearPendingEmailForTesting();
243 // In unit tests, disable starting the actual sync.
244 void set_do_not_start_sync_for_testing();
246 // Called when password has been submitted.
247 void PasswordSubmitted(const autofill::PasswordForm
& form
);
249 // content::WebContentsObserver overrides.
250 virtual void DidStartNavigationToPendingEntry(
252 content::NavigationController::ReloadType reload_type
) OVERRIDE
;
253 virtual void DidNavigateMainFrame(
254 const content::LoadCommittedDetails
& details
,
255 const content::FrameNavigateParams
& params
) OVERRIDE
;
256 virtual void DidStopLoading(
257 content::RenderViewHost
* render_view_host
) OVERRIDE
;
258 virtual void WebContentsDestroyed(content::WebContents
* contents
) OVERRIDE
;
260 // ProfileSyncServiceObserver.
261 virtual void OnStateChanged() OVERRIDE
;
263 OneClickSigninSyncStarter::Callback
CreateSyncStarterCallback();
265 // Callback invoked when OneClickSigninSyncStarter completes sync setup.
266 void SyncSetupCompletedCallback(
267 OneClickSigninSyncStarter::SyncSetupResult result
);
269 // Tracks if we are in the process of showing the signin or one click
270 // interstitial page. It's set to true the first time we load one of those
271 // pages and set to false when transient state is cleaned.
272 // Note: This should only be used for logging purposes.
273 bool showing_signin_
;
275 // Information about the account that has just logged in.
276 std::string session_index_
;
278 std::string password_
;
279 AutoAccept auto_accept_
;
280 signin::Source source_
;
281 bool switched_to_advanced_
;
283 // The orignal continue URL after sync setup is complete.
284 GURL original_continue_url_
;
285 std::string error_message_
;
287 // Number of navigations since starting a sign in that is outside the
288 // the set of trusted Gaia URLs. Sign in attempts that include visits to
289 // one more untrusted will cause a modal dialog to appear asking the user
290 // to confirm, similar to the interstitial flow.
291 int untrusted_navigations_since_signin_visit_
;
293 // Whether a Gaia URL during the sign in process was not handled by the
294 // dedicated sign in process (e.g. SAML login, which redirects to a
295 // non-google-controlled domain).
296 // This is set to true if at least one such URL is detected.
297 bool untrusted_confirmation_required_
;
299 // Allows unittests to avoid accessing the ResourceContext for clearing a
301 bool do_not_clear_pending_email_
;
303 // Allows unittest to avoid starting sync for real.
304 bool do_not_start_sync_for_testing_
;
306 base::WeakPtrFactory
<OneClickSigninHelper
> weak_pointer_factory_
;
308 DISALLOW_COPY_AND_ASSIGN(OneClickSigninHelper
);
311 #endif // CHROME_BROWSER_UI_SYNC_ONE_CLICK_SIGNIN_HELPER_H_