blob: da9a138bd804339e389a35aff36a21c832241eec [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_SYNC_ONE_CLICK_SIGNIN_HELPER_H_
#define CHROME_BROWSER_UI_SYNC_ONE_CLICK_SIGNIN_HELPER_H_
#include <string>
#include "base/gtest_prod_util.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/signin/signin_promo.h"
#include "chrome/browser/sync/profile_sync_service_observer.h"
#include "chrome/browser/ui/sync/one_click_signin_sync_starter.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h"
#include "google_apis/gaia/google_service_auth_error.h"
class Browser;
class GURL;
class PasswordManager;
class ProfileIOData;
namespace content {
class WebContents;
struct FrameNavigateParams;
struct LoadCommittedDetails;
struct PasswordForm;
}
namespace net {
class URLRequest;
}
// Per-tab one-click signin helper. When a user signs in to a Google service
// and the profile is not yet connected to a Google account, will start the
// process of helping the user connect his profile with one click. The process
// begins with an infobar and is followed with a confirmation dialog explaining
// more about what this means.
class OneClickSigninHelper
: public content::WebContentsObserver,
public content::WebContentsUserData<OneClickSigninHelper>,
public ProfileSyncServiceObserver {
public:
// Represents user's decision about sign in process.
enum AutoAccept {
// User decision not yet known. Assume cancel.
AUTO_ACCEPT_NONE,
// User has explicitly accepted to sign in. A bubble is shown with the
// option to start sync, configure it first, or abort.
AUTO_ACCEPT_ACCEPTED,
// User has explicitly accepted to sign in, but wants to configure sync
// settings before turning it on.
AUTO_ACCEPT_CONFIGURE,
// User has explicitly rejected to sign in. Furthermore, the user does
// not want to be prompted to see the interstitial again in this profile.
AUTO_ACCEPT_REJECTED_FOR_PROFILE,
// This is an explicit sign in from either first run, NTP, wrench menu,
// or settings page. The user will be signed in automatically with sync
// enabled using default settings.
AUTO_ACCEPT_EXPLICIT
};
// Return value of CanOfferOnIOThread().
enum Offer {
CAN_OFFER,
DONT_OFFER,
IGNORE_REQUEST
};
// Argument to CanOffer().
enum CanOfferFor {
CAN_OFFER_FOR_ALL,
CAN_OFFER_FOR_INTERSTITAL_ONLY
};
static void CreateForWebContentsWithPasswordManager(
content::WebContents* contents,
PasswordManager* password_manager);
virtual ~OneClickSigninHelper();
// Returns true if the one-click signin feature can be offered at this time.
// If |email| is not empty, then the profile is checked to see if it's
// already connected to a google account or if the user has already rejected
// one-click sign-in with this email, in which cases a one click signin
// should not be offered.
//
// If |can_offer_for| is |CAN_OFFER_FOR_INTERSTITAL_ONLY|, then only do the
// checks that would affect the interstitial page. Otherwise, do the checks
// that would affect the interstitial and the explicit sign ins.
//
// Returns in |error_message_id| an explanation as a string resource ID for
// why one-clicked cannot be offered. |error_message_id| is valid only if
// the return value is false. If no explanation is needed, |error_message_id|
// may be null.
static bool CanOffer(content::WebContents* web_contents,
CanOfferFor can_offer_for,
const std::string& email,
std::string* error_message);
// Returns true if the one-click signin feature can be offered at this time.
// It can be offered if the io_data is not in an incognito window and if the
// origin of |url| is a valid Gaia sign in origin. This function is meant
// to called only from the IO thread.
static Offer CanOfferOnIOThread(net::URLRequest* request,
ProfileIOData* io_data);
// Looks for the Google-Accounts-SignIn response header, and if found,
// tries to display an infobar in the tab contents identified by the
// child/route id.
static void ShowInfoBarIfPossible(net::URLRequest* request,
ProfileIOData* io_data,
int child_id,
int route_id);
// Remove the item currently at the top of the history list if it's
// the Gaia redirect URL. Due to limitations of the NavigationController
// this cannot be done until a new page becomes "current".
static void RemoveSigninRedirectURLHistoryItem(
content::WebContents* web_contents);
static void LogConfirmHistogramValue(int action);
private:
friend class content::WebContentsUserData<OneClickSigninHelper>;
friend class OneClickSigninHelperTest;
FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIncognitoTest,
ShowInfoBarUIThreadIncognito);
FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperTest,
SigninFromWebstoreWithConfigSyncfirst);
FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperTest,
ShowSigninBubbleAfterSigninComplete);
FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperTest, SigninCancelled);
FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperTest, SigninFailed);
FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperTest,
CleanTransientStateOnNavigate);
FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest, CanOfferOnIOThread);
FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
CanOfferOnIOThreadIncognito);
FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
CanOfferOnIOThreadNoIOData);
FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
CanOfferOnIOThreadBadURL);
FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
CanOfferOnIOThreadReferrer);
FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
CanOfferOnIOThreadDisabled);
FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
CanOfferOnIOThreadSignedIn);
FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
CanOfferOnIOThreadEmailNotAllowed);
FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
CanOfferOnIOThreadEmailAlreadyUsed);
FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
CreateTestProfileIOData);
FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
CanOfferOnIOThreadWithRejectedEmail);
FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
CanOfferOnIOThreadNoSigninCookies);
FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
CanOfferOnIOThreadDisabledByPolicy);
// Maximum number of navigations away from the set of valid Gaia URLs before
// clearing the internal state of the helper. This is necessary to support
// SAML-based accounts, but causes bug crbug.com/181163.
static const int kMaxNavigationsSince;
OneClickSigninHelper(content::WebContents* web_contents,
PasswordManager* password_manager);
// Returns true if the one-click signin feature can be offered at this time.
// It can be offered if the io_data is not in an incognito window and if the
// origin of |url| is a valid Gaia sign in origin. This function is meant
// to called only from the IO thread.
static Offer CanOfferOnIOThreadImpl(const GURL& url,
const std::string& referrer,
base::SupportsUserData* request,
ProfileIOData* io_data);
// The portion of ShowInfoBarIfPossible() that needs to run on the UI thread.
// |session_index| and |email| are extracted from the Google-Accounts-SignIn
// header. |auto_accept| is extracted from the Google-Chrome-SignIn header.
// |source| is used to determine which of the explicit sign in mechanism is
// being used.
//
// |continue_url| is where Gaia will continue to when the sign in process is
// done. For explicit sign ins, this is a URL chrome controls. For one-click
// sign in, this could be any google property. This URL is used to know
// when the sign process is over and to collect infomation from the user
// entered on the Gaia sign in page (for explicit sign ins).
static void ShowInfoBarUIThread(const std::string& session_index,
const std::string& email,
AutoAccept auto_accept,
signin::Source source,
const GURL& continue_url,
int child_id,
int route_id);
void RedirectToSignin();
void ShowSigninErrorBubble(Browser* browser, const std::string& error);
// Clear all data member of the helper, except for the error.
void CleanTransientState();
// Unitests that use a TestingProfile should call this.
// Otherwise, clearing the pending e-mail crashes because the code expects
// a real ResourceContext rather than the MockResourceContext a
// TestingProfile provides.
void SetDoNotClearPendingEmailForTesting();
// Called when password has been submitted.
void PasswordSubmitted(const content::PasswordForm& form);
// content::WebContentsObserver overrides.
virtual void NavigateToPendingEntry(
const GURL& url,
content::NavigationController::ReloadType reload_type) OVERRIDE;
virtual void DidNavigateMainFrame(
const content::LoadCommittedDetails& details,
const content::FrameNavigateParams& params) OVERRIDE;
virtual void DidStopLoading(
content::RenderViewHost* render_view_host) OVERRIDE;
// ProfileSyncServiceObserver.
virtual void OnStateChanged() OVERRIDE;
OneClickSigninSyncStarter::Callback CreateSyncStarterCallback();
// Callback invoked when OneClickSigninSyncStarter completes sync setup.
void SyncSetupCompletedCallback(
OneClickSigninSyncStarter::SyncSetupResult result);
// Tracks if we are in the process of showing the signin or one click
// interstitial page. It's set to true the first time we load one of those
// pages and set to false when transient state is cleaned.
// Note: This should only be used for logging purposes.
bool showing_signin_;
// Information about the account that has just logged in.
std::string session_index_;
std::string email_;
std::string password_;
AutoAccept auto_accept_;
signin::Source source_;
bool switched_to_advanced_;
GURL continue_url_;
// The orignal continue URL after sync setup is complete.
GURL original_continue_url_;
std::string error_message_;
// Number of navigations since starting a sign in that is outside the
// the set of trusted Gaia URLs. Sign in attempts that include visits to
// one more untrusted will cause a modal dialog to appear asking the user
// to confirm, similar to the interstitial flow.
int untrusted_navigations_since_signin_visit_;
// Whether a Gaia URL during the sign in process was not handled by the
// dedicated sign in process (e.g. SAML login, which redirects to a
// non-google-controlled domain).
// This is set to true if at least one such URL is detected.
bool untrusted_confirmation_required_;
// Allows unittests to avoid accessing the ResourceContext for clearing a
// pending e-mail.
bool do_not_clear_pending_email_;
base::WeakPtrFactory<OneClickSigninHelper> weak_pointer_factory_;
DISALLOW_COPY_AND_ASSIGN(OneClickSigninHelper);
};
#endif // CHROME_BROWSER_UI_SYNC_ONE_CLICK_SIGNIN_HELPER_H_