1// Copyright 2014 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. 4 5#include "chrome/browser/ui/auto_login_infobar_delegate.h" 6 7#include "base/bind.h" 8#include "base/logging.h" 9#include "base/message_loop/message_loop.h" 10#include "base/metrics/histogram.h" 11#include "base/prefs/pref_service.h" 12#include "base/strings/utf_string_conversions.h" 13#include "chrome/browser/browser_process.h" 14#include "chrome/browser/infobars/infobar_service.h" 15#include "chrome/browser/profiles/profile.h" 16#include "chrome/browser/signin/profile_oauth2_token_service_factory.h" 17#include "chrome/browser/signin/signin_manager_factory.h" 18#include "chrome/browser/ui/sync/sync_promo_ui.h" 19#include "chrome/common/chrome_switches.h" 20#include "chrome/common/pref_names.h" 21#include "chrome/common/url_constants.h" 22#include "chrome/grit/generated_resources.h" 23#include "components/google/core/browser/google_util.h" 24#include "components/infobars/core/infobar.h" 25#include "components/signin/core/browser/profile_oauth2_token_service.h" 26#include "content/public/browser/navigation_controller.h" 27#include "content/public/browser/page_navigator.h" 28#include "content/public/browser/web_contents.h" 29#include "content/public/browser/web_contents_observer.h" 30#include "content/public/common/referrer.h" 31#include "google_apis/gaia/gaia_constants.h" 32#include "google_apis/gaia/gaia_urls.h" 33#include "google_apis/gaia/ubertoken_fetcher.h" 34#include "grit/theme_resources.h" 35#include "net/base/escape.h" 36#include "net/url_request/url_request.h" 37#include "ui/base/l10n/l10n_util.h" 38 39#if defined(OS_ANDROID) 40#include "chrome/browser/ui/android/infobars/auto_login_infobar_delegate_android.h" 41#endif 42 43 44// AutoLoginRedirector -------------------------------------------------------- 45 46namespace { 47 48// This class is created by the AutoLoginInfoBarDelegate when the user wishes to 49// auto-login. It holds context information needed while re-issuing service 50// tokens using the OAuth2TokenService, gets the browser cookies with the 51// TokenAuth API, and finally redirects the user to the correct page. 52class AutoLoginRedirector : public UbertokenConsumer, 53 public content::WebContentsObserver { 54 public: 55 AutoLoginRedirector(content::WebContents* web_contents, 56 const std::string& args); 57 virtual ~AutoLoginRedirector(); 58 59 private: 60 // Overriden from UbertokenConsumer: 61 virtual void OnUbertokenSuccess(const std::string& token) OVERRIDE; 62 virtual void OnUbertokenFailure(const GoogleServiceAuthError& error) OVERRIDE; 63 64 // Implementation of content::WebContentsObserver 65 virtual void WebContentsDestroyed() OVERRIDE; 66 67 // Redirect tab to MergeSession URL, logging the user in and navigating 68 // to the desired page. 69 void RedirectToMergeSession(const std::string& token); 70 71 const std::string args_; 72 scoped_ptr<UbertokenFetcher> ubertoken_fetcher_; 73 74 DISALLOW_COPY_AND_ASSIGN(AutoLoginRedirector); 75}; 76 77AutoLoginRedirector::AutoLoginRedirector( 78 content::WebContents* web_contents, 79 const std::string& args) 80 : content::WebContentsObserver(web_contents), 81 args_(args) { 82 Profile* profile = 83 Profile::FromBrowserContext(web_contents->GetBrowserContext()); 84 ProfileOAuth2TokenService* token_service = 85 ProfileOAuth2TokenServiceFactory::GetForProfile(profile); 86 SigninManagerBase* signin_manager = 87 SigninManagerFactory::GetInstance()->GetForProfile(profile); 88 ubertoken_fetcher_.reset(new UbertokenFetcher(token_service, 89 this, 90 profile->GetRequestContext())); 91 ubertoken_fetcher_->StartFetchingToken( 92 signin_manager->GetAuthenticatedAccountId()); 93} 94 95AutoLoginRedirector::~AutoLoginRedirector() { 96} 97 98void AutoLoginRedirector::WebContentsDestroyed() { 99 // The WebContents that started this has been destroyed. The request must be 100 // cancelled and this object must be deleted. 101 ubertoken_fetcher_.reset(); 102 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); 103} 104 105void AutoLoginRedirector::OnUbertokenSuccess(const std::string& token) { 106 RedirectToMergeSession(token); 107 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); 108} 109 110void AutoLoginRedirector::OnUbertokenFailure( 111 const GoogleServiceAuthError& error) { 112 LOG(WARNING) << "AutoLoginRedirector: token request failed"; 113 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); 114} 115 116void AutoLoginRedirector::RedirectToMergeSession(const std::string& token) { 117 // TODO(rogerta): what is the correct page transition? 118 web_contents()->GetController().LoadURL( 119 GaiaUrls::GetInstance()->merge_session_url().Resolve( 120 "?source=chrome&uberauth=" + token + "&" + args_), 121 content::Referrer(), ui::PAGE_TRANSITION_AUTO_BOOKMARK, 122 std::string()); 123} 124 125} // namespace 126 127 128// AutoLoginInfoBarDelegate --------------------------------------------------- 129 130// static 131bool AutoLoginInfoBarDelegate::Create(content::WebContents* web_contents, 132 const Params& params) { 133 // If |web_contents| is hosted in a WebDialog, there may be no infobar 134 // service. 135 InfoBarService* infobar_service = 136 InfoBarService::FromWebContents(web_contents); 137 if (!infobar_service) 138 return false; 139 140 Profile* profile = 141 Profile::FromBrowserContext(web_contents->GetBrowserContext()); 142#if defined(OS_ANDROID) 143 typedef AutoLoginInfoBarDelegateAndroid Delegate; 144#else 145 typedef AutoLoginInfoBarDelegate Delegate; 146#endif 147 return !!infobar_service->AddInfoBar(ConfirmInfoBarDelegate::CreateInfoBar( 148 scoped_ptr<ConfirmInfoBarDelegate>(new Delegate(params, profile)))); 149} 150 151AutoLoginInfoBarDelegate::AutoLoginInfoBarDelegate(const Params& params, 152 Profile* profile) 153 : ConfirmInfoBarDelegate(), 154 params_(params), 155 profile_(profile), 156 button_pressed_(false) { 157 RecordHistogramAction(SHOWN); 158 159 // The AutoLogin infobar is shown in incognito mode on Android, so a 160 // SigninManager isn't guaranteed to exist for |profile_|. 161 SigninManagerBase* signin_manager = 162 SigninManagerFactory::GetInstance()->GetForProfile(profile_); 163 if (signin_manager) 164 signin_manager->AddObserver(this); 165} 166 167AutoLoginInfoBarDelegate::~AutoLoginInfoBarDelegate() { 168 // The AutoLogin infobar is shown in incognito mode on Android, so a 169 // SigninManager isn't guaranteed to exist for |profile_|. 170 SigninManagerBase* signin_manager = 171 SigninManagerFactory::GetInstance()->GetForProfile(profile_); 172 if (signin_manager) 173 signin_manager->RemoveObserver(this); 174 175 if (!button_pressed_) 176 RecordHistogramAction(IGNORED); 177} 178 179void AutoLoginInfoBarDelegate::RecordHistogramAction(Actions action) { 180 UMA_HISTOGRAM_ENUMERATION("AutoLogin.Regular", action, 181 HISTOGRAM_BOUNDING_VALUE); 182} 183 184void AutoLoginInfoBarDelegate::InfoBarDismissed() { 185 RecordHistogramAction(DISMISSED); 186 button_pressed_ = true; 187} 188 189int AutoLoginInfoBarDelegate::GetIconID() const { 190 return IDR_INFOBAR_AUTOLOGIN; 191} 192 193infobars::InfoBarDelegate::Type AutoLoginInfoBarDelegate::GetInfoBarType() 194 const { 195 return PAGE_ACTION_TYPE; 196} 197 198AutoLoginInfoBarDelegate* 199 AutoLoginInfoBarDelegate::AsAutoLoginInfoBarDelegate() { 200 return this; 201} 202 203base::string16 AutoLoginInfoBarDelegate::GetMessageText() const { 204 return l10n_util::GetStringFUTF16(IDS_AUTOLOGIN_INFOBAR_MESSAGE, 205 base::UTF8ToUTF16(params_.username)); 206} 207 208base::string16 AutoLoginInfoBarDelegate::GetButtonLabel( 209 InfoBarButton button) const { 210 return l10n_util::GetStringUTF16((button == BUTTON_OK) ? 211 IDS_AUTOLOGIN_INFOBAR_OK_BUTTON : IDS_AUTOLOGIN_INFOBAR_CANCEL_BUTTON); 212} 213 214bool AutoLoginInfoBarDelegate::Accept() { 215 // AutoLoginRedirector deletes itself. 216 content::WebContents* web_contents = 217 InfoBarService::WebContentsFromInfoBar(infobar()); 218 new AutoLoginRedirector(web_contents, params_.header.args); 219 RecordHistogramAction(ACCEPTED); 220 button_pressed_ = true; 221 return true; 222} 223 224bool AutoLoginInfoBarDelegate::Cancel() { 225 content::WebContents* web_contents = 226 InfoBarService::WebContentsFromInfoBar(infobar()); 227 PrefService* pref_service = Profile::FromBrowserContext( 228 web_contents->GetBrowserContext())->GetPrefs(); 229 pref_service->SetBoolean(prefs::kAutologinEnabled, false); 230 RecordHistogramAction(REJECTED); 231 button_pressed_ = true; 232 return true; 233} 234 235void AutoLoginInfoBarDelegate::GoogleSignedOut( 236 const std::string& account_id, 237 const std::string& username) { 238 infobar()->RemoveSelf(); 239} 240