Add a minor text member to ui::MenuModel.
[chromium-blink-merge.git] / chrome / browser / ui / sync / one_click_signin_helper.cc
blobe6b6adb7db4c6207efc86dd80197acd4d8faa30f
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 #include "chrome/browser/ui/sync/one_click_signin_helper.h"
7 #include <algorithm>
8 #include <functional>
9 #include <utility>
10 #include <vector>
12 #include "base/bind.h"
13 #include "base/callback_forward.h"
14 #include "base/callback_helpers.h"
15 #include "base/compiler_specific.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "base/metrics/field_trial.h"
18 #include "base/metrics/histogram.h"
19 #include "base/prefs/pref_service.h"
20 #include "base/strings/string_split.h"
21 #include "base/strings/string_util.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/supports_user_data.h"
24 #include "base/values.h"
25 #include "chrome/browser/browser_process.h"
26 #include "chrome/browser/chrome_notification_types.h"
27 #include "chrome/browser/defaults.h"
28 #include "chrome/browser/google/google_util.h"
29 #include "chrome/browser/history/history_service.h"
30 #include "chrome/browser/history/history_service_factory.h"
31 #include "chrome/browser/password_manager/password_manager.h"
32 #include "chrome/browser/prefs/scoped_user_pref_update.h"
33 #include "chrome/browser/profiles/profile.h"
34 #include "chrome/browser/profiles/profile_info_cache.h"
35 #include "chrome/browser/profiles/profile_io_data.h"
36 #include "chrome/browser/profiles/profile_manager.h"
37 #include "chrome/browser/search/search.h"
38 #include "chrome/browser/signin/chrome_signin_manager_delegate.h"
39 #include "chrome/browser/signin/signin_global_error.h"
40 #include "chrome/browser/signin/signin_manager.h"
41 #include "chrome/browser/signin/signin_manager_delegate.h"
42 #include "chrome/browser/signin/signin_manager_factory.h"
43 #include "chrome/browser/signin/signin_names_io_thread.h"
44 #include "chrome/browser/sync/profile_sync_service.h"
45 #include "chrome/browser/sync/profile_sync_service_factory.h"
46 #include "chrome/browser/sync/sync_prefs.h"
47 #include "chrome/browser/tab_contents/tab_util.h"
48 #include "chrome/browser/ui/browser_finder.h"
49 #include "chrome/browser/ui/browser_window.h"
50 #include "chrome/browser/ui/chrome_pages.h"
51 #include "chrome/browser/ui/sync/one_click_signin_histogram.h"
52 #include "chrome/browser/ui/sync/one_click_signin_sync_starter.h"
53 #include "chrome/browser/ui/sync/signin_histogram.h"
54 #include "chrome/browser/ui/tab_modal_confirm_dialog.h"
55 #include "chrome/browser/ui/tab_modal_confirm_dialog_delegate.h"
56 #include "chrome/browser/ui/tabs/tab_strip_model.h"
57 #include "chrome/common/chrome_switches.h"
58 #include "chrome/common/chrome_version_info.h"
59 #include "chrome/common/net/url_util.h"
60 #include "chrome/common/pref_names.h"
61 #include "chrome/common/url_constants.h"
62 #include "content/public/browser/browser_thread.h"
63 #include "content/public/browser/navigation_entry.h"
64 #include "content/public/browser/page_navigator.h"
65 #include "content/public/browser/render_process_host.h"
66 #include "content/public/browser/web_contents.h"
67 #include "content/public/browser/web_contents_view.h"
68 #include "content/public/common/frame_navigate_params.h"
69 #include "content/public/common/page_transition_types.h"
70 #include "content/public/common/password_form.h"
71 #include "google_apis/gaia/gaia_auth_util.h"
72 #include "google_apis/gaia/gaia_urls.h"
73 #include "grit/chromium_strings.h"
74 #include "grit/generated_resources.h"
75 #include "grit/theme_resources.h"
76 #include "ipc/ipc_message_macros.h"
77 #include "net/base/url_util.h"
78 #include "net/cookies/cookie_monster.h"
79 #include "net/url_request/url_request.h"
80 #include "ui/base/l10n/l10n_util.h"
81 #include "ui/base/resource/resource_bundle.h"
82 #include "url/gurl.h"
85 namespace {
87 // StartSyncArgs --------------------------------------------------------------
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 StartSyncArgs(Profile* profile,
93 Browser* browser,
94 OneClickSigninHelper::AutoAccept auto_accept,
95 const std::string& session_index,
96 const std::string& email,
97 const std::string& password,
98 content::WebContents* web_contents,
99 bool untrusted_confirmation_required,
100 signin::Source source,
101 OneClickSigninSyncStarter::Callback callback);
103 Profile* profile;
104 Browser* browser;
105 OneClickSigninHelper::AutoAccept auto_accept;
106 std::string session_index;
107 std::string email;
108 std::string password;
110 // Web contents in which the sync setup page should be displayed,
111 // if necessary. Can be NULL.
112 content::WebContents* web_contents;
114 OneClickSigninSyncStarter::ConfirmationRequired confirmation_required;
115 signin::Source source;
116 OneClickSigninSyncStarter::Callback callback;
119 StartSyncArgs::StartSyncArgs(Profile* profile,
120 Browser* browser,
121 OneClickSigninHelper::AutoAccept auto_accept,
122 const std::string& session_index,
123 const std::string& email,
124 const std::string& password,
125 content::WebContents* web_contents,
126 bool untrusted_confirmation_required,
127 signin::Source source,
128 OneClickSigninSyncStarter::Callback callback)
129 : profile(profile),
130 browser(browser),
131 auto_accept(auto_accept),
132 session_index(session_index),
133 email(email),
134 password(password),
135 web_contents(web_contents),
136 source(source),
137 callback(callback) {
138 if (untrusted_confirmation_required) {
139 confirmation_required = OneClickSigninSyncStarter::CONFIRM_UNTRUSTED_SIGNIN;
140 } else if (source == signin::SOURCE_SETTINGS ||
141 source == signin::SOURCE_WEBSTORE_INSTALL) {
142 // Do not display a status confirmation for webstore installs or re-auth.
143 confirmation_required = OneClickSigninSyncStarter::NO_CONFIRMATION;
144 } else {
145 confirmation_required = OneClickSigninSyncStarter::CONFIRM_AFTER_SIGNIN;
150 // ConfirmEmailDialogDelegate -------------------------------------------------
152 class ConfirmEmailDialogDelegate : public TabModalConfirmDialogDelegate {
153 public:
154 enum Action {
155 CREATE_NEW_USER,
156 START_SYNC,
157 CLOSE
160 // Callback indicating action performed by the user.
161 typedef base::Callback<void(Action)> Callback;
163 // Ask the user for confirmation before starting to sync.
164 static void AskForConfirmation(content::WebContents* contents,
165 const std::string& last_email,
166 const std::string& email,
167 Callback callback);
169 private:
170 ConfirmEmailDialogDelegate(content::WebContents* contents,
171 const std::string& last_email,
172 const std::string& email,
173 Callback callback);
174 virtual ~ConfirmEmailDialogDelegate();
176 // TabModalConfirmDialogDelegate:
177 virtual string16 GetTitle() OVERRIDE;
178 virtual string16 GetMessage() OVERRIDE;
179 virtual string16 GetAcceptButtonTitle() OVERRIDE;
180 virtual string16 GetCancelButtonTitle() OVERRIDE;
181 virtual void OnAccepted() OVERRIDE;
182 virtual void OnCanceled() OVERRIDE;
183 virtual void OnClosed() OVERRIDE;
185 std::string last_email_;
186 std::string email_;
187 Callback callback_;
189 DISALLOW_COPY_AND_ASSIGN(ConfirmEmailDialogDelegate);
192 // static
193 void ConfirmEmailDialogDelegate::AskForConfirmation(
194 content::WebContents* contents,
195 const std::string& last_email,
196 const std::string& email,
197 Callback callback) {
198 TabModalConfirmDialog::Create(
199 new ConfirmEmailDialogDelegate(contents, last_email, email,
200 callback), contents);
203 ConfirmEmailDialogDelegate::ConfirmEmailDialogDelegate(
204 content::WebContents* contents,
205 const std::string& last_email,
206 const std::string& email,
207 Callback callback)
208 : TabModalConfirmDialogDelegate(contents),
209 last_email_(last_email),
210 email_(email),
211 callback_(callback) {
214 ConfirmEmailDialogDelegate::~ConfirmEmailDialogDelegate() {
217 string16 ConfirmEmailDialogDelegate::GetTitle() {
218 return l10n_util::GetStringUTF16(
219 IDS_ONE_CLICK_SIGNIN_CONFIRM_EMAIL_DIALOG_TITLE);
222 string16 ConfirmEmailDialogDelegate::GetMessage() {
223 return l10n_util::GetStringFUTF16(
224 IDS_ONE_CLICK_SIGNIN_CONFIRM_EMAIL_DIALOG_MESSAGE,
225 UTF8ToUTF16(last_email_), UTF8ToUTF16(email_));
228 string16 ConfirmEmailDialogDelegate::GetAcceptButtonTitle() {
229 return l10n_util::GetStringUTF16(
230 IDS_ONE_CLICK_SIGNIN_CONFIRM_EMAIL_DIALOG_OK_BUTTON);
233 string16 ConfirmEmailDialogDelegate::GetCancelButtonTitle() {
234 return l10n_util::GetStringUTF16(
235 IDS_ONE_CLICK_SIGNIN_CONFIRM_EMAIL_DIALOG_CANCEL_BUTTON);
238 void ConfirmEmailDialogDelegate::OnAccepted() {
239 base::ResetAndReturn(&callback_).Run(CREATE_NEW_USER);
242 void ConfirmEmailDialogDelegate::OnCanceled() {
243 base::ResetAndReturn(&callback_).Run(START_SYNC);
246 void ConfirmEmailDialogDelegate::OnClosed() {
247 base::ResetAndReturn(&callback_).Run(CLOSE);
251 // Helpers --------------------------------------------------------------------
253 // Add a specific email to the list of emails rejected for one-click
254 // sign-in, for this profile.
255 void AddEmailToOneClickRejectedList(Profile* profile,
256 const std::string& email) {
257 ListPrefUpdate updater(profile->GetPrefs(),
258 prefs::kReverseAutologinRejectedEmailList);
259 updater->AppendIfNotPresent(new base::StringValue(email));
262 void LogHistogramValue(signin::Source source, int action) {
263 switch (source) {
264 case signin::SOURCE_START_PAGE:
265 UMA_HISTOGRAM_ENUMERATION("Signin.StartPageActions", action,
266 one_click_signin::HISTOGRAM_MAX);
267 break;
268 case signin::SOURCE_NTP_LINK:
269 UMA_HISTOGRAM_ENUMERATION("Signin.NTPLinkActions", action,
270 one_click_signin::HISTOGRAM_MAX);
271 break;
272 case signin::SOURCE_MENU:
273 UMA_HISTOGRAM_ENUMERATION("Signin.MenuActions", action,
274 one_click_signin::HISTOGRAM_MAX);
275 break;
276 case signin::SOURCE_SETTINGS:
277 UMA_HISTOGRAM_ENUMERATION("Signin.SettingsActions", action,
278 one_click_signin::HISTOGRAM_MAX);
279 break;
280 case signin::SOURCE_EXTENSION_INSTALL_BUBBLE:
281 UMA_HISTOGRAM_ENUMERATION("Signin.ExtensionInstallBubbleActions", action,
282 one_click_signin::HISTOGRAM_MAX);
283 break;
284 case signin::SOURCE_WEBSTORE_INSTALL:
285 UMA_HISTOGRAM_ENUMERATION("Signin.WebstoreInstallActions", action,
286 one_click_signin::HISTOGRAM_MAX);
287 break;
288 case signin::SOURCE_APP_LAUNCHER:
289 UMA_HISTOGRAM_ENUMERATION("Signin.AppLauncherActions", action,
290 one_click_signin::HISTOGRAM_MAX);
291 break;
292 case signin::SOURCE_APPS_PAGE_LINK:
293 UMA_HISTOGRAM_ENUMERATION("Signin.AppsPageLinkActions", action,
294 one_click_signin::HISTOGRAM_MAX);
295 break;
296 case signin::SOURCE_BOOKMARK_BUBBLE:
297 UMA_HISTOGRAM_ENUMERATION("Signin.BookmarkBubbleActions", action,
298 one_click_signin::HISTOGRAM_MAX);
299 break;
300 default:
301 // This switch statement needs to be updated when the enum Source changes.
302 COMPILE_ASSERT(signin::SOURCE_UNKNOWN == 9,
303 kSourceEnumHasChangedButNotThisSwitchStatement);
304 NOTREACHED();
305 return;
307 UMA_HISTOGRAM_ENUMERATION("Signin.AllAccessPointActions", action,
308 one_click_signin::HISTOGRAM_MAX);
311 void LogOneClickHistogramValue(int action) {
312 UMA_HISTOGRAM_ENUMERATION("Signin.OneClickActions", action,
313 one_click_signin::HISTOGRAM_MAX);
314 UMA_HISTOGRAM_ENUMERATION("Signin.AllAccessPointActions", action,
315 one_click_signin::HISTOGRAM_MAX);
318 void RedirectToNtpOrAppsPage(content::WebContents* contents,
319 signin::Source source) {
320 VLOG(1) << "RedirectToNtpOrAppsPage";
321 // Redirect to NTP/Apps page and display a confirmation bubble
322 GURL url(source == signin::SOURCE_APPS_PAGE_LINK ?
323 chrome::kChromeUIAppsURL : chrome::kChromeUINewTabURL);
324 content::OpenURLParams params(url,
325 content::Referrer(),
326 CURRENT_TAB,
327 content::PAGE_TRANSITION_AUTO_TOPLEVEL,
328 false);
329 contents->OpenURL(params);
332 // If the |source| is not settings page/webstore, redirects to
333 // the NTP/Apps page.
334 void RedirectToNtpOrAppsPageIfNecessary(content::WebContents* contents,
335 signin::Source source) {
336 if (source != signin::SOURCE_SETTINGS &&
337 source != signin::SOURCE_WEBSTORE_INSTALL) {
338 RedirectToNtpOrAppsPage(contents, source);
342 // Start syncing with the given user information.
343 void StartSync(const StartSyncArgs& args,
344 OneClickSigninSyncStarter::StartSyncMode start_mode) {
345 if (start_mode == OneClickSigninSyncStarter::UNDO_SYNC) {
346 LogOneClickHistogramValue(one_click_signin::HISTOGRAM_UNDO);
347 return;
350 // The starter deletes itself once its done.
351 new OneClickSigninSyncStarter(args.profile, args.browser, args.session_index,
352 args.email, args.password, start_mode,
353 args.web_contents,
354 args.confirmation_required,
355 args.source,
356 args.callback);
358 int action = one_click_signin::HISTOGRAM_MAX;
359 switch (args.auto_accept) {
360 case OneClickSigninHelper::AUTO_ACCEPT_EXPLICIT:
361 break;
362 case OneClickSigninHelper::AUTO_ACCEPT_ACCEPTED:
363 action =
364 start_mode == OneClickSigninSyncStarter::SYNC_WITH_DEFAULT_SETTINGS ?
365 one_click_signin::HISTOGRAM_AUTO_WITH_DEFAULTS :
366 one_click_signin::HISTOGRAM_AUTO_WITH_ADVANCED;
367 break;
368 case OneClickSigninHelper::AUTO_ACCEPT_CONFIGURE:
369 DCHECK(start_mode == OneClickSigninSyncStarter::CONFIGURE_SYNC_FIRST);
370 action = one_click_signin::HISTOGRAM_AUTO_WITH_ADVANCED;
371 break;
372 default:
373 NOTREACHED() << "Invalid auto_accept: " << args.auto_accept;
374 break;
376 if (action != one_click_signin::HISTOGRAM_MAX)
377 LogOneClickHistogramValue(action);
380 void StartExplicitSync(const StartSyncArgs& args,
381 content::WebContents* contents,
382 OneClickSigninSyncStarter::StartSyncMode start_mode,
383 ConfirmEmailDialogDelegate::Action action) {
384 if (action == ConfirmEmailDialogDelegate::START_SYNC) {
385 StartSync(args, start_mode);
386 RedirectToNtpOrAppsPageIfNecessary(contents, args.source);
387 } else {
388 if (signin::IsContinueUrlForWebBasedSigninFlow(contents->GetVisibleURL()))
389 RedirectToNtpOrAppsPage(contents, args.source);
390 if (action == ConfirmEmailDialogDelegate::CREATE_NEW_USER) {
391 chrome::ShowSettingsSubPage(args.browser,
392 std::string(chrome::kSearchUsersSubPage));
397 void ClearPendingEmailOnIOThread(content::ResourceContext* context) {
398 ProfileIOData* io_data = ProfileIOData::FromResourceContext(context);
399 DCHECK(io_data);
400 io_data->set_reverse_autologin_pending_email(std::string());
403 // Determines the source of the sign in and the continue URL. Its either one
404 // of the known sign in access point (first run, NTP, Apps page, menu, settings)
405 // or its an implicit sign in via another Google property. In the former case,
406 // "service" is also checked to make sure its "chromiumsync".
407 signin::Source GetSigninSource(const GURL& url, GURL* continue_url) {
408 DCHECK(url.is_valid());
409 std::string value;
410 net::GetValueForKeyInQuery(url, "service", &value);
411 bool possibly_an_explicit_signin = value == "chromiumsync";
413 // Find the final continue URL for this sign in. In some cases, Gaia can
414 // continue to itself, with the original continue URL buried under a couple
415 // of layers of indirection. Peel those layers away. The final destination
416 // can also be "IsGaiaSignonRealm" so stop if we get to the end (but be sure
417 // we always extract at least one "continue" value).
418 GURL local_continue_url = signin::GetNextPageURLForPromoURL(url);
419 while (gaia::IsGaiaSignonRealm(local_continue_url.GetOrigin())) {
420 GURL next_continue_url =
421 signin::GetNextPageURLForPromoURL(local_continue_url);
422 if (!next_continue_url.is_valid())
423 break;
424 local_continue_url = next_continue_url;
427 if (continue_url && local_continue_url.is_valid()) {
428 DCHECK(!continue_url->is_valid() || *continue_url == local_continue_url);
429 *continue_url = local_continue_url;
432 return possibly_an_explicit_signin ?
433 signin::GetSourceForPromoURL(local_continue_url) :
434 signin::SOURCE_UNKNOWN;
437 // Returns true if |url| is a valid URL that can occur during the sign in
438 // process. Valid URLs are of the form:
440 // https://accounts.google.{TLD}/...
441 // https://accounts.youtube.com/...
442 // https://accounts.blogger.com/...
444 // All special headers used by one click sign in occur on
445 // https://accounts.google.com URLs. However, the sign in process may redirect
446 // to intermediate Gaia URLs that do not end with .com. For example, an account
447 // that uses SMS 2-factor outside the US may redirect to country specific URLs.
449 // The sign in process may also redirect to youtube and blogger account URLs
450 // so that Gaia acts as a single signon service.
451 bool IsValidGaiaSigninRedirectOrResponseURL(const GURL& url) {
452 std::string hostname = url.host();
453 if (google_util::IsGoogleHostname(hostname, google_util::ALLOW_SUBDOMAIN)) {
454 // Also using IsGaiaSignonRealm() to handle overriding with command line.
455 return gaia::IsGaiaSignonRealm(url.GetOrigin()) ||
456 StartsWithASCII(hostname, "accounts.", false);
459 GURL origin = url.GetOrigin();
460 if (origin == GURL("https://accounts.youtube.com") ||
461 origin == GURL("https://accounts.blogger.com"))
462 return true;
464 return false;
467 // Tells when we are in the process of showing either the signin to chrome page
468 // or the one click sign in to chrome page.
469 // NOTE: This should only be used for logging purposes since it relies on hard
470 // coded URLs that could change.
471 bool AreWeShowingSignin(GURL url, signin::Source source, std::string email) {
472 GURL::Replacements replacements;
473 replacements.ClearQuery();
474 GURL clean_login_url =
475 GURL(GaiaUrls::GetInstance()->service_login_url()).ReplaceComponents(
476 replacements);
478 return (url.ReplaceComponents(replacements) == clean_login_url &&
479 source != signin::SOURCE_UNKNOWN) ||
480 (IsValidGaiaSigninRedirectOrResponseURL(url) &&
481 url.spec().find("ChromeLoginPrompt") != std::string::npos &&
482 !email.empty());
485 // CurrentHistoryCleaner ------------------------------------------------------
487 // Watch a webcontents and remove URL from the history once loading is complete.
488 // We have to delay the cleaning until the new URL has finished loading because
489 // we're not allowed to remove the last-loaded URL from the history. Objects
490 // of this type automatically self-destruct once they're finished their work.
491 class CurrentHistoryCleaner : public content::WebContentsObserver {
492 public:
493 explicit CurrentHistoryCleaner(content::WebContents* contents);
494 virtual ~CurrentHistoryCleaner();
496 // content::WebContentsObserver:
497 virtual void WebContentsDestroyed(content::WebContents* contents) OVERRIDE;
498 virtual void DidCommitProvisionalLoadForFrame(
499 int64 frame_id,
500 bool is_main_frame,
501 const GURL& url,
502 content::PageTransition transition_type,
503 content::RenderViewHost* render_view_host) OVERRIDE;
505 private:
506 scoped_ptr<content::WebContents> contents_;
507 int history_index_to_remove_;
509 DISALLOW_COPY_AND_ASSIGN(CurrentHistoryCleaner);
512 CurrentHistoryCleaner::CurrentHistoryCleaner(content::WebContents* contents)
513 : WebContentsObserver(contents) {
514 history_index_to_remove_ =
515 web_contents()->GetController().GetLastCommittedEntryIndex();
518 CurrentHistoryCleaner::~CurrentHistoryCleaner() {
521 void CurrentHistoryCleaner::DidCommitProvisionalLoadForFrame(
522 int64 frame_id,
523 bool is_main_frame,
524 const GURL& url,
525 content::PageTransition transition_type,
526 content::RenderViewHost* render_view_host) {
527 // Return early if this is not top-level navigation.
528 if (!is_main_frame)
529 return;
531 content::NavigationController* nc = &web_contents()->GetController();
532 HistoryService* hs = HistoryServiceFactory::GetForProfile(
533 Profile::FromBrowserContext(web_contents()->GetBrowserContext()),
534 Profile::IMPLICIT_ACCESS);
536 // Have to wait until something else gets added to history before removal.
537 if (history_index_to_remove_ < nc->GetLastCommittedEntryIndex()) {
538 content::NavigationEntry* entry =
539 nc->GetEntryAtIndex(history_index_to_remove_);
540 if (signin::IsContinueUrlForWebBasedSigninFlow(entry->GetURL())) {
541 hs->DeleteURL(entry->GetURL());
542 nc->RemoveEntryAtIndex(history_index_to_remove_);
543 delete this; // Success.
548 void CurrentHistoryCleaner::WebContentsDestroyed(
549 content::WebContents* contents) {
550 delete this; // Failure.
553 void CloseTab(content::WebContents* tab) {
554 Browser* browser = chrome::FindBrowserWithWebContents(tab);
555 if (browser) {
556 TabStripModel* tab_strip_model = browser->tab_strip_model();
557 if (tab_strip_model) {
558 int index = tab_strip_model->GetIndexOfWebContents(tab);
559 if (index != TabStripModel::kNoTab) {
560 tab_strip_model->ExecuteContextMenuCommand(
561 index, TabStripModel::CommandCloseTab);
567 } // namespace
570 // OneClickSigninHelper -------------------------------------------------------
572 DEFINE_WEB_CONTENTS_USER_DATA_KEY(OneClickSigninHelper);
574 // static
575 const int OneClickSigninHelper::kMaxNavigationsSince = 10;
577 OneClickSigninHelper::OneClickSigninHelper(content::WebContents* web_contents,
578 PasswordManager* password_manager)
579 : content::WebContentsObserver(web_contents),
580 showing_signin_(false),
581 auto_accept_(AUTO_ACCEPT_NONE),
582 source_(signin::SOURCE_UNKNOWN),
583 switched_to_advanced_(false),
584 untrusted_navigations_since_signin_visit_(0),
585 untrusted_confirmation_required_(false),
586 do_not_clear_pending_email_(false),
587 weak_pointer_factory_(this) {
588 // May be NULL during testing.
589 if (password_manager) {
590 password_manager->AddSubmissionCallback(
591 base::Bind(&OneClickSigninHelper::PasswordSubmitted,
592 weak_pointer_factory_.GetWeakPtr()));
596 OneClickSigninHelper::~OneClickSigninHelper() {
597 // WebContentsDestroyed() should always be called before the object is
598 // deleted.
599 DCHECK(!web_contents());
602 // static
603 void OneClickSigninHelper::CreateForWebContentsWithPasswordManager(
604 content::WebContents* contents,
605 PasswordManager* password_manager) {
606 if (!FromWebContents(contents)) {
607 contents->SetUserData(UserDataKey(),
608 new OneClickSigninHelper(contents, password_manager));
612 // static
613 bool OneClickSigninHelper::CanOffer(content::WebContents* web_contents,
614 CanOfferFor can_offer_for,
615 const std::string& email,
616 std::string* error_message) {
617 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
618 VLOG(1) << "OneClickSigninHelper::CanOffer";
620 if (error_message)
621 error_message->clear();
623 if (!web_contents)
624 return false;
626 if (web_contents->GetBrowserContext()->IsOffTheRecord())
627 return false;
629 Profile* profile =
630 Profile::FromBrowserContext(web_contents->GetBrowserContext());
631 if (!profile)
632 return false;
634 SigninManager* manager =
635 SigninManagerFactory::GetForProfile(profile);
636 if (manager && !manager->IsSigninAllowed())
637 return false;
639 if (can_offer_for == CAN_OFFER_FOR_INTERSTITAL_ONLY &&
640 !profile->GetPrefs()->GetBoolean(prefs::kReverseAutologinEnabled))
641 return false;
643 if (!ChromeSigninManagerDelegate::ProfileAllowsSigninCookies(profile))
644 return false;
646 if (!email.empty()) {
647 if (!manager)
648 return false;
650 // If the signin manager already has an authenticated name, then this is a
651 // re-auth scenario. Make sure the email just signed in corresponds to the
652 // the one sign in manager expects.
653 std::string current_email = manager->GetAuthenticatedUsername();
654 const bool same_email = gaia::AreEmailsSame(current_email, email);
655 if (!current_email.empty() && !same_email) {
656 UMA_HISTOGRAM_ENUMERATION("Signin.Reauth",
657 signin::HISTOGRAM_ACCOUNT_MISSMATCH,
658 signin::HISTOGRAM_MAX);
659 if (error_message) {
660 error_message->assign(
661 l10n_util::GetStringFUTF8(IDS_SYNC_WRONG_EMAIL,
662 UTF8ToUTF16(current_email)));
664 return false;
667 // Make sure this username is not prohibited by policy.
668 if (!manager->IsAllowedUsername(email)) {
669 if (error_message) {
670 error_message->assign(
671 l10n_util::GetStringUTF8(IDS_SYNC_LOGIN_NAME_PROHIBITED));
673 return false;
676 // If some profile, not just the current one, is already connected to this
677 // account, don't show the infobar.
678 if (g_browser_process && !same_email) {
679 ProfileManager* manager = g_browser_process->profile_manager();
680 if (manager) {
681 string16 email16 = UTF8ToUTF16(email);
682 ProfileInfoCache& cache = manager->GetProfileInfoCache();
684 for (size_t i = 0; i < cache.GetNumberOfProfiles(); ++i) {
685 if (email16 == cache.GetUserNameOfProfileAtIndex(i)) {
686 if (error_message) {
687 error_message->assign(
688 l10n_util::GetStringUTF8(IDS_SYNC_USER_NAME_IN_USE_ERROR));
690 return false;
696 // If email was already rejected by this profile for one-click sign-in.
697 if (can_offer_for == CAN_OFFER_FOR_INTERSTITAL_ONLY) {
698 const ListValue* rejected_emails = profile->GetPrefs()->GetList(
699 prefs::kReverseAutologinRejectedEmailList);
700 if (!rejected_emails->empty()) {
701 base::ListValue::const_iterator iter = rejected_emails->Find(
702 base::StringValue(email));
703 if (iter != rejected_emails->end())
704 return false;
709 VLOG(1) << "OneClickSigninHelper::CanOffer: yes we can";
710 return true;
713 // static
714 OneClickSigninHelper::Offer OneClickSigninHelper::CanOfferOnIOThread(
715 net::URLRequest* request,
716 ProfileIOData* io_data) {
717 return CanOfferOnIOThreadImpl(request->url(), request->referrer(),
718 request, io_data);
721 // static
722 OneClickSigninHelper::Offer OneClickSigninHelper::CanOfferOnIOThreadImpl(
723 const GURL& url,
724 const std::string& referrer,
725 base::SupportsUserData* request,
726 ProfileIOData* io_data) {
727 if (!gaia::IsGaiaSignonRealm(url.GetOrigin()))
728 return IGNORE_REQUEST;
730 if (!io_data)
731 return DONT_OFFER;
733 // Check for incognito before other parts of the io_data, since those
734 // members may not be initalized.
735 if (io_data->is_incognito())
736 return DONT_OFFER;
738 if (!SigninManager::IsSigninAllowedOnIOThread(io_data))
739 return DONT_OFFER;
741 if (!io_data->reverse_autologin_enabled()->GetValue())
742 return DONT_OFFER;
744 if (!io_data->google_services_username()->GetValue().empty())
745 return DONT_OFFER;
747 if (!ChromeSigninManagerDelegate::SettingsAllowSigninCookies(
748 io_data->GetCookieSettings()))
749 return DONT_OFFER;
751 // The checks below depend on chrome already knowing what account the user
752 // signed in with. This happens only after receiving the response containing
753 // the Google-Accounts-SignIn header. Until then, if there is even a chance
754 // that we want to connect the profile, chrome needs to tell Gaia that
755 // it should offer the interstitial. Therefore missing one click data on
756 // the request means can offer is true.
757 const std::string& pending_email = io_data->reverse_autologin_pending_email();
758 if (!pending_email.empty()) {
759 if (!SigninManager::IsUsernameAllowedByPolicy(pending_email,
760 io_data->google_services_username_pattern()->GetValue())) {
761 return DONT_OFFER;
764 std::vector<std::string> rejected_emails =
765 io_data->one_click_signin_rejected_email_list()->GetValue();
766 if (std::count_if(rejected_emails.begin(), rejected_emails.end(),
767 std::bind2nd(std::equal_to<std::string>(),
768 pending_email)) > 0) {
769 return DONT_OFFER;
772 if (io_data->signin_names()->GetEmails().count(
773 UTF8ToUTF16(pending_email)) > 0) {
774 return DONT_OFFER;
778 return CAN_OFFER;
781 // static
782 void OneClickSigninHelper::ShowInfoBarIfPossible(net::URLRequest* request,
783 ProfileIOData* io_data,
784 int child_id,
785 int route_id) {
786 std::string google_chrome_signin_value;
787 std::string google_accounts_signin_value;
788 request->GetResponseHeaderByName("Google-Chrome-SignIn",
789 &google_chrome_signin_value);
790 request->GetResponseHeaderByName("Google-Accounts-SignIn",
791 &google_accounts_signin_value);
793 if (!google_accounts_signin_value.empty() ||
794 !google_chrome_signin_value.empty()) {
795 VLOG(1) << "OneClickSigninHelper::ShowInfoBarIfPossible:"
796 << " g-a-s='" << google_accounts_signin_value << "'"
797 << " g-c-s='" << google_chrome_signin_value << "'";
800 if (!gaia::IsGaiaSignonRealm(request->original_url().GetOrigin()))
801 return;
803 // Parse Google-Accounts-SignIn.
804 std::vector<std::pair<std::string, std::string> > pairs;
805 base::SplitStringIntoKeyValuePairs(google_accounts_signin_value, '=', ',',
806 &pairs);
807 std::string session_index;
808 std::string email;
809 for (size_t i = 0; i < pairs.size(); ++i) {
810 const std::pair<std::string, std::string>& pair = pairs[i];
811 const std::string& key = pair.first;
812 const std::string& value = pair.second;
813 if (key == "email") {
814 TrimString(value, "\"", &email);
815 } else if (key == "sessionindex") {
816 session_index = value;
820 // Later in the chain of this request, we'll need to check the email address
821 // in the IO thread (see CanOfferOnIOThread). So save the email address as
822 // user data on the request (only for web-based flow).
823 if (!email.empty())
824 io_data->set_reverse_autologin_pending_email(email);
826 if (!email.empty() || !session_index.empty()) {
827 VLOG(1) << "OneClickSigninHelper::ShowInfoBarIfPossible:"
828 << " email=" << email
829 << " sessionindex=" << session_index;
832 // Parse Google-Chrome-SignIn.
833 AutoAccept auto_accept = AUTO_ACCEPT_NONE;
834 signin::Source source = signin::SOURCE_UNKNOWN;
835 GURL continue_url;
836 std::vector<std::string> tokens;
837 base::SplitString(google_chrome_signin_value, ',', &tokens);
838 for (size_t i = 0; i < tokens.size(); ++i) {
839 const std::string& token = tokens[i];
840 if (token == "accepted") {
841 auto_accept = AUTO_ACCEPT_ACCEPTED;
842 } else if (token == "configure") {
843 auto_accept = AUTO_ACCEPT_CONFIGURE;
844 } else if (token == "rejected-for-profile") {
845 auto_accept = AUTO_ACCEPT_REJECTED_FOR_PROFILE;
849 // If this is an explicit sign in (i.e., first run, NTP, Apps page, menu,
850 // settings) then force the auto accept type to explicit.
851 source = GetSigninSource(request->url(), &continue_url);
852 if (source != signin::SOURCE_UNKNOWN)
853 auto_accept = AUTO_ACCEPT_EXPLICIT;
855 if (auto_accept != AUTO_ACCEPT_NONE) {
856 VLOG(1) << "OneClickSigninHelper::ShowInfoBarIfPossible:"
857 << " auto_accept=" << auto_accept;
860 // If |session_index|, |email|, |auto_accept|, and |continue_url| all have
861 // their default value, don't bother posting a task to the UI thread.
862 // It will be a noop anyway.
864 // The two headers above may (but not always) come in different http requests
865 // so a post to the UI thread is still needed if |auto_accept| is not its
866 // default value, but |email| and |session_index| are.
867 if (session_index.empty() && email.empty() &&
868 auto_accept == AUTO_ACCEPT_NONE && !continue_url.is_valid()) {
869 return;
872 content::BrowserThread::PostTask(
873 content::BrowserThread::UI, FROM_HERE,
874 base::Bind(&OneClickSigninHelper::ShowInfoBarUIThread, session_index,
875 email, auto_accept, source, continue_url, child_id, route_id));
878 // static
879 void OneClickSigninHelper::LogConfirmHistogramValue(int action) {
880 UMA_HISTOGRAM_ENUMERATION("Signin.OneClickConfirmation", action,
881 one_click_signin::HISTOGRAM_CONFIRM_MAX);
883 // static
884 void OneClickSigninHelper::ShowInfoBarUIThread(
885 const std::string& session_index,
886 const std::string& email,
887 AutoAccept auto_accept,
888 signin::Source source,
889 const GURL& continue_url,
890 int child_id,
891 int route_id) {
892 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
894 content::WebContents* web_contents = tab_util::GetWebContentsByID(child_id,
895 route_id);
896 if (!web_contents)
897 return;
899 // TODO(mathp): The appearance of this infobar should be tested using a
900 // browser_test.
901 OneClickSigninHelper* helper =
902 OneClickSigninHelper::FromWebContents(web_contents);
903 if (!helper)
904 return;
906 if (auto_accept != AUTO_ACCEPT_NONE)
907 helper->auto_accept_ = auto_accept;
909 if (source != signin::SOURCE_UNKNOWN &&
910 helper->source_ == signin::SOURCE_UNKNOWN) {
911 helper->source_ = source;
914 // Save the email in the one-click signin manager. The manager may
915 // not exist if the contents is incognito or if the profile is already
916 // connected to a Google account.
917 if (!session_index.empty())
918 helper->session_index_ = session_index;
920 if (!email.empty())
921 helper->email_ = email;
923 CanOfferFor can_offer_for =
924 (auto_accept != AUTO_ACCEPT_EXPLICIT &&
925 helper->auto_accept_ != AUTO_ACCEPT_EXPLICIT) ?
926 CAN_OFFER_FOR_INTERSTITAL_ONLY : CAN_OFFER_FOR_ALL;
928 std::string error_message;
930 if (!web_contents || !CanOffer(web_contents, can_offer_for, email,
931 &error_message)) {
932 VLOG(1) << "OneClickSigninHelper::ShowInfoBarUIThread: not offering";
933 // TODO(rogerta): Can we just display our error now instead of keeping it
934 // around and doing it later?
935 if (helper && helper->error_message_.empty() && !error_message.empty())
936 helper->error_message_ = error_message;
938 return;
941 // Only allow the dedicated signin process to sign the user into
942 // Chrome without intervention, because it doesn't load any untrusted
943 // pages. If at any point an untrusted page is detected, chrome will
944 // show a modal dialog asking the user to confirm.
945 Profile* profile =
946 Profile::FromBrowserContext(web_contents->GetBrowserContext());
947 SigninManager* manager = profile ?
948 SigninManagerFactory::GetForProfile(profile) : NULL;
949 helper->untrusted_confirmation_required_ |=
950 (manager && !manager->IsSigninProcess(child_id));
952 if (continue_url.is_valid()) {
953 // Set |original_continue_url_| if it is currently empty. |continue_url|
954 // could be modified by gaia pages, thus we need to record the original
955 // continue url to navigate back to the right page when sync setup is
956 // complete.
957 if (helper->original_continue_url_.is_empty())
958 helper->original_continue_url_ = continue_url;
959 helper->continue_url_ = continue_url;
963 // static
964 void OneClickSigninHelper::RemoveSigninRedirectURLHistoryItem(
965 content::WebContents* web_contents) {
966 // Only actually remove the item if it's the blank.html continue url.
967 if (signin::IsContinueUrlForWebBasedSigninFlow(
968 web_contents->GetLastCommittedURL())) {
969 new CurrentHistoryCleaner(web_contents); // will self-destruct when done
973 void OneClickSigninHelper::ShowSigninErrorBubble(Browser* browser,
974 const std::string& error) {
975 DCHECK(!error.empty());
977 browser->window()->ShowOneClickSigninBubble(
978 BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_BUBBLE,
979 string16(), /* no SAML email */
980 UTF8ToUTF16(error),
981 // This callback is never invoked.
982 // TODO(rogerta): Separate out the bubble API so we don't have to pass
983 // ignored |email| and |callback| params.
984 BrowserWindow::StartSyncCallback());
987 void OneClickSigninHelper::RedirectToSignin() {
988 VLOG(1) << "OneClickSigninHelper::RedirectToSignin";
990 // Extract the existing sounce=X value. Default to "2" if missing.
991 signin::Source source = signin::GetSourceForPromoURL(continue_url_);
992 if (source == signin::SOURCE_UNKNOWN)
993 source = signin::SOURCE_MENU;
994 GURL page = signin::GetPromoURL(source, false);
996 content::WebContents* contents = web_contents();
997 contents->GetController().LoadURL(page,
998 content::Referrer(),
999 content::PAGE_TRANSITION_AUTO_TOPLEVEL,
1000 std::string());
1003 void OneClickSigninHelper::CleanTransientState() {
1004 VLOG(1) << "OneClickSigninHelper::CleanTransientState";
1005 showing_signin_ = false;
1006 email_.clear();
1007 password_.clear();
1008 auto_accept_ = AUTO_ACCEPT_NONE;
1009 source_ = signin::SOURCE_UNKNOWN;
1010 switched_to_advanced_ = false;
1011 continue_url_ = GURL();
1012 untrusted_navigations_since_signin_visit_ = 0;
1013 untrusted_confirmation_required_ = false;
1014 error_message_.clear();
1016 // Post to IO thread to clear pending email.
1017 if (!do_not_clear_pending_email_) {
1018 Profile* profile =
1019 Profile::FromBrowserContext(web_contents()->GetBrowserContext());
1020 content::BrowserThread::PostTask(
1021 content::BrowserThread::IO, FROM_HERE,
1022 base::Bind(&ClearPendingEmailOnIOThread,
1023 base::Unretained(profile->GetResourceContext())));
1027 void OneClickSigninHelper::PasswordSubmitted(
1028 const content::PasswordForm& form) {
1029 // We only need to scrape the password for Gaia logins.
1030 if (gaia::IsGaiaSignonRealm(GURL(form.signon_realm))) {
1031 VLOG(1) << "OneClickSigninHelper::DidNavigateAnyFrame: got password";
1032 password_ = UTF16ToUTF8(form.password_value);
1036 void OneClickSigninHelper::SetDoNotClearPendingEmailForTesting() {
1037 do_not_clear_pending_email_ = true;
1040 void OneClickSigninHelper::NavigateToPendingEntry(
1041 const GURL& url,
1042 content::NavigationController::ReloadType reload_type) {
1043 VLOG(1) << "OneClickSigninHelper::NavigateToPendingEntry: url=" << url.spec();
1044 // If the tab navigates to a new page, and this page is not a valid Gaia
1045 // sign in redirect or reponse, or the expected continue URL, make sure to
1046 // clear the internal state. This is needed to detect navigations in the
1047 // middle of the sign in process that may redirect back to the sign in
1048 // process (see crbug.com/181163 for details).
1049 const GURL continue_url = signin::GetNextPageURLForPromoURL(
1050 signin::GetPromoURL(signin::SOURCE_START_PAGE, false));
1051 GURL::Replacements replacements;
1052 replacements.ClearQuery();
1054 if (!IsValidGaiaSigninRedirectOrResponseURL(url) &&
1055 continue_url_.is_valid() &&
1056 url.ReplaceComponents(replacements) !=
1057 continue_url_.ReplaceComponents(replacements)) {
1058 if (++untrusted_navigations_since_signin_visit_ > kMaxNavigationsSince)
1059 CleanTransientState();
1063 void OneClickSigninHelper::DidNavigateMainFrame(
1064 const content::LoadCommittedDetails& details,
1065 const content::FrameNavigateParams& params) {
1066 if (!SigninManager::IsWebBasedSigninFlowURL(params.url)) {
1067 // Make sure the renderer process is no longer considered the trusted
1068 // sign-in process when a navigation to a non-sign-in URL occurs.
1069 Profile* profile =
1070 Profile::FromBrowserContext(web_contents()->GetBrowserContext());
1071 SigninManager* manager = profile ?
1072 SigninManagerFactory::GetForProfile(profile) : NULL;
1073 int process_id = web_contents()->GetRenderProcessHost()->GetID();
1074 if (manager && manager->IsSigninProcess(process_id))
1075 manager->ClearSigninProcess();
1077 // If the navigation to a non-sign-in URL hasn't been triggered by the web
1078 // contents, the sign in flow has been aborted and the state must be
1079 // cleaned (crbug.com/269421).
1080 if (!content::PageTransitionIsWebTriggerable(params.transition) &&
1081 auto_accept_ != AUTO_ACCEPT_NONE) {
1082 CleanTransientState();
1087 void OneClickSigninHelper::DidStopLoading(
1088 content::RenderViewHost* render_view_host) {
1089 // If the user left the sign in process, clear all members.
1090 // TODO(rogerta): might need to allow some youtube URLs.
1091 content::WebContents* contents = web_contents();
1092 const GURL url = contents->GetLastCommittedURL();
1093 Profile* profile =
1094 Profile::FromBrowserContext(contents->GetBrowserContext());
1095 VLOG(1) << "OneClickSigninHelper::DidStopLoading: url=" << url.spec();
1097 // If an error has already occured during the sign in flow, make sure to
1098 // display it to the user and abort the process. Do this only for
1099 // explicit sign ins.
1100 // TODO(rogerta): Could we move this code back up to ShowInfoBarUIThread()?
1101 if (!error_message_.empty() && auto_accept_ == AUTO_ACCEPT_EXPLICIT) {
1102 VLOG(1) << "OneClickSigninHelper::DidStopLoading: error=" << error_message_;
1103 RemoveSigninRedirectURLHistoryItem(contents);
1104 // After we redirect to NTP, our browser pointer gets corrupted because the
1105 // WebContents have changed, so grab the browser pointer
1106 // before the navigation.
1107 Browser* browser = chrome::FindBrowserWithWebContents(contents);
1109 // Redirect to the landing page and display an error popup.
1110 RedirectToNtpOrAppsPage(web_contents(), source_);
1111 ShowSigninErrorBubble(browser, error_message_);
1112 CleanTransientState();
1113 return;
1116 if (AreWeShowingSignin(url, source_, email_)) {
1117 if (!showing_signin_) {
1118 if (source_ == signin::SOURCE_UNKNOWN)
1119 LogOneClickHistogramValue(one_click_signin::HISTOGRAM_SHOWN);
1120 else
1121 LogHistogramValue(source_, one_click_signin::HISTOGRAM_SHOWN);
1123 showing_signin_ = true;
1126 // When Gaia finally redirects to the continue URL, Gaia will add some
1127 // extra query parameters. So ignore the parameters when checking to see
1128 // if the user has continued.
1129 GURL::Replacements replacements;
1130 replacements.ClearQuery();
1131 const bool continue_url_match = (
1132 continue_url_.is_valid() &&
1133 url.ReplaceComponents(replacements) ==
1134 continue_url_.ReplaceComponents(replacements));
1136 if (continue_url_match)
1137 RemoveSigninRedirectURLHistoryItem(contents);
1139 // If there is no valid email yet, there is nothing to do. As of M26, the
1140 // password is allowed to be empty, since its no longer required to setup
1141 // sync.
1142 if (email_.empty()) {
1143 VLOG(1) << "OneClickSigninHelper::DidStopLoading: nothing to do";
1144 if (continue_url_match && auto_accept_ == AUTO_ACCEPT_EXPLICIT)
1145 RedirectToSignin();
1146 std::string unused_value;
1147 if (net::GetValueForKeyInQuery(url, "ntp", &unused_value)) {
1148 signin::SetUserSkippedPromo(profile);
1149 RedirectToNtpOrAppsPage(web_contents(), source_);
1152 if (!continue_url_match && !IsValidGaiaSigninRedirectOrResponseURL(url) &&
1153 ++untrusted_navigations_since_signin_visit_ > kMaxNavigationsSince) {
1154 CleanTransientState();
1157 return;
1160 if (!continue_url_match && IsValidGaiaSigninRedirectOrResponseURL(url))
1161 return;
1163 // During an explicit sign in, if the user has not yet reached the final
1164 // continue URL, wait for it to arrive. Note that Gaia will add some extra
1165 // query parameters to the continue URL. Ignore them when checking to
1166 // see if the user has continued.
1168 // If this is not an explicit sign in, we don't need to check if we landed
1169 // on the right continue URL. This is important because the continue URL
1170 // may itself lead to a redirect, which means this function will never see
1171 // the continue URL go by.
1172 if (auto_accept_ == AUTO_ACCEPT_EXPLICIT) {
1173 DCHECK(source_ != signin::SOURCE_UNKNOWN);
1174 if (!continue_url_match) {
1175 VLOG(1) << "OneClickSigninHelper::DidStopLoading: invalid url='"
1176 << url.spec()
1177 << "' expected continue url=" << continue_url_;
1178 CleanTransientState();
1179 return;
1182 // In explicit sign ins, the user may have changed the box
1183 // "Let me choose what to sync". This is reflected as a change in the
1184 // source of the continue URL. Make one last check of the current URL
1185 // to see if there is a valid source. If so, it overrides the
1186 // current source.
1188 // If the source was changed to SOURCE_SETTINGS, we want
1189 // OneClickSigninSyncStarter to reuse the current tab to display the
1190 // advanced configuration.
1191 signin::Source source = signin::GetSourceForPromoURL(url);
1192 if (source != source_) {
1193 source_ = source;
1194 switched_to_advanced_ = source == signin::SOURCE_SETTINGS;
1198 Browser* browser = chrome::FindBrowserWithWebContents(contents);
1200 VLOG(1) << "OneClickSigninHelper::DidStopLoading: signin is go."
1201 << " auto_accept=" << auto_accept_
1202 << " source=" << source_;
1204 switch (auto_accept_) {
1205 case AUTO_ACCEPT_NONE:
1206 if (showing_signin_)
1207 LogOneClickHistogramValue(one_click_signin::HISTOGRAM_DISMISSED);
1208 break;
1209 case AUTO_ACCEPT_ACCEPTED:
1210 LogOneClickHistogramValue(one_click_signin::HISTOGRAM_ACCEPTED);
1211 LogOneClickHistogramValue(one_click_signin::HISTOGRAM_WITH_DEFAULTS);
1212 SigninManager::DisableOneClickSignIn(profile);
1213 // Start syncing with the default settings - prompt the user to sign in
1214 // first.
1215 StartSync(
1216 StartSyncArgs(profile, browser, auto_accept_,
1217 session_index_, email_, password_,
1218 NULL /* don't force to show sync setup in same tab */,
1219 true /* confirmation_required */, source_,
1220 CreateSyncStarterCallback()),
1221 OneClickSigninSyncStarter::SYNC_WITH_DEFAULT_SETTINGS);
1222 break;
1223 case AUTO_ACCEPT_CONFIGURE:
1224 LogOneClickHistogramValue(one_click_signin::HISTOGRAM_ACCEPTED);
1225 LogOneClickHistogramValue(one_click_signin::HISTOGRAM_WITH_ADVANCED);
1226 SigninManager::DisableOneClickSignIn(profile);
1227 // Display the extra confirmation (even in the SAML case) in case this
1228 // was an untrusted renderer.
1229 StartSync(
1230 StartSyncArgs(profile, browser, auto_accept_,
1231 session_index_, email_, password_,
1232 NULL /* don't force to show sync setup in same tab */,
1233 true /* confirmation_required */, source_,
1234 CreateSyncStarterCallback()),
1235 OneClickSigninSyncStarter::CONFIGURE_SYNC_FIRST);
1236 break;
1237 case AUTO_ACCEPT_EXPLICIT: {
1238 signin::Source original_source =
1239 signin::GetSourceForPromoURL(original_continue_url_);
1240 if (switched_to_advanced_) {
1241 LogHistogramValue(original_source,
1242 one_click_signin::HISTOGRAM_WITH_ADVANCED);
1243 LogHistogramValue(original_source,
1244 one_click_signin::HISTOGRAM_ACCEPTED);
1245 } else {
1246 LogHistogramValue(source_, one_click_signin::HISTOGRAM_ACCEPTED);
1247 LogHistogramValue(source_, one_click_signin::HISTOGRAM_WITH_DEFAULTS);
1250 // - If sign in was initiated from the NTP or the hotdog menu, sync with
1251 // default settings.
1252 // - If sign in was initiated from the settings page for first time sync
1253 // set up, show the advanced sync settings dialog.
1254 // - If sign in was initiated from the settings page due to a re-auth,
1255 // simply navigate back to the settings page.
1256 OneClickSigninSyncStarter::StartSyncMode start_mode =
1257 source_ == signin::SOURCE_SETTINGS ?
1258 SigninGlobalError::GetForProfile(profile)->HasMenuItem() ?
1259 OneClickSigninSyncStarter::SHOW_SETTINGS_WITHOUT_CONFIGURE :
1260 OneClickSigninSyncStarter::CONFIGURE_SYNC_FIRST :
1261 OneClickSigninSyncStarter::SYNC_WITH_DEFAULT_SETTINGS;
1263 std::string last_email =
1264 profile->GetPrefs()->GetString(prefs::kGoogleServicesLastUsername);
1266 if (!last_email.empty() && !gaia::AreEmailsSame(last_email, email_)) {
1267 // If the new email address is different from the email address that
1268 // just signed in, show a confirmation dialog.
1270 // No need to display a second confirmation so pass false below.
1271 // TODO(atwilson): Move this into OneClickSigninSyncStarter.
1272 // The tab modal dialog always executes its callback before |contents|
1273 // is deleted.
1274 ConfirmEmailDialogDelegate::AskForConfirmation(
1275 contents,
1276 last_email,
1277 email_,
1278 base::Bind(
1279 &StartExplicitSync,
1280 StartSyncArgs(profile, browser, auto_accept_,
1281 session_index_, email_, password_, contents,
1282 false /* confirmation_required */, source_,
1283 CreateSyncStarterCallback()),
1284 contents,
1285 start_mode));
1286 } else {
1287 StartSync(
1288 StartSyncArgs(profile, browser, auto_accept_,
1289 session_index_, email_, password_, contents,
1290 untrusted_confirmation_required_, source_,
1291 CreateSyncStarterCallback()),
1292 start_mode);
1294 // If this explicit sign in is not from settings page/webstore, show
1295 // the NTP/Apps page after sign in completes. In the case of the
1296 // settings page, it will get auto-closed after sync setup. In the case
1297 // of webstore, it will redirect back to webstore.
1298 RedirectToNtpOrAppsPageIfNecessary(web_contents(), source_);
1301 // Observe the sync service if the Webstore tab or the settings tab
1302 // requested a gaia sign in, so that when sign in and sync setup are
1303 // successful, we can redirect to the correct URL, or auto-close the gaia
1304 // sign in tab.
1305 if (original_source == signin::SOURCE_SETTINGS ||
1306 (original_source == signin::SOURCE_WEBSTORE_INSTALL &&
1307 source_ == signin::SOURCE_SETTINGS)) {
1308 ProfileSyncService* sync_service =
1309 ProfileSyncServiceFactory::GetForProfile(profile);
1310 if (sync_service)
1311 sync_service->AddObserver(this);
1313 break;
1315 case AUTO_ACCEPT_REJECTED_FOR_PROFILE:
1316 AddEmailToOneClickRejectedList(profile, email_);
1317 LogOneClickHistogramValue(one_click_signin::HISTOGRAM_REJECTED);
1318 break;
1319 default:
1320 NOTREACHED() << "Invalid auto_accept=" << auto_accept_;
1321 break;
1324 CleanTransientState();
1327 // It is guaranteed that this method is called before the object is deleted.
1328 void OneClickSigninHelper::WebContentsDestroyed(
1329 content::WebContents* contents) {
1330 Profile* profile =
1331 Profile::FromBrowserContext(contents->GetBrowserContext());
1332 ProfileSyncService* sync_service =
1333 ProfileSyncServiceFactory::GetForProfile(profile);
1334 if (sync_service)
1335 sync_service->RemoveObserver(this);
1338 void OneClickSigninHelper::OnStateChanged() {
1339 // We only add observer for ProfileSyncService when original_continue_url_ is
1340 // not empty.
1341 DCHECK(!original_continue_url_.is_empty());
1343 content::WebContents* contents = web_contents();
1344 Profile* profile =
1345 Profile::FromBrowserContext(contents->GetBrowserContext());
1346 ProfileSyncService* sync_service =
1347 ProfileSyncServiceFactory::GetForProfile(profile);
1349 // At this point, the sign in process is complete, and control has been handed
1350 // back to the sync engine. Close the gaia sign in tab if
1351 // |original_continue_url_| contains the |auto_close| parameter. Otherwise,
1352 // wait for sync setup to complete and then navigate to
1353 // |original_continue_url_|.
1354 if (signin::IsAutoCloseEnabledInURL(original_continue_url_)) {
1355 // Close the gaia sign in tab via a task to make sure we aren't in the
1356 // middle of any webui handler code.
1357 base::MessageLoop::current()->PostTask(
1358 FROM_HERE,
1359 base::Bind(&CloseTab, base::Unretained(contents)));
1360 } else {
1361 // Sync setup not completed yet.
1362 if (sync_service->FirstSetupInProgress())
1363 return;
1365 if (sync_service->sync_initialized()) {
1366 contents->GetController().LoadURL(original_continue_url_,
1367 content::Referrer(),
1368 content::PAGE_TRANSITION_AUTO_TOPLEVEL,
1369 std::string());
1373 // Clears |original_continue_url_| here instead of in CleanTransientState,
1374 // because it is used in OnStateChanged which occurs later.
1375 original_continue_url_ = GURL();
1376 sync_service->RemoveObserver(this);
1379 OneClickSigninSyncStarter::Callback
1380 OneClickSigninHelper::CreateSyncStarterCallback() {
1381 // The callback will only be invoked if this object is still alive when sync
1382 // setup is completed. This is correct because this object is only deleted
1383 // when the web contents that potentially shows a blank page is deleted.
1384 return base::Bind(&OneClickSigninHelper::SyncSetupCompletedCallback,
1385 weak_pointer_factory_.GetWeakPtr());
1388 void OneClickSigninHelper::SyncSetupCompletedCallback(
1389 OneClickSigninSyncStarter::SyncSetupResult result) {
1390 if (result == OneClickSigninSyncStarter::SYNC_SETUP_FAILURE &&
1391 web_contents()) {
1392 GURL current_url = web_contents()->GetVisibleURL();
1394 // If the web contents is showing a blank page and not about to be closed,
1395 // redirect to the NTP or apps page.
1396 if (signin::IsContinueUrlForWebBasedSigninFlow(current_url) &&
1397 !signin::IsAutoCloseEnabledInURL(original_continue_url_)) {
1398 RedirectToNtpOrAppsPage(
1399 web_contents(),
1400 signin::GetSourceForPromoURL(original_continue_url_));