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 46 namespace { 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. 52 class 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 77 AutoLoginRedirector::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 95 AutoLoginRedirector::~AutoLoginRedirector() { 96 } 97 98 void 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 105 void AutoLoginRedirector::OnUbertokenSuccess(const std::string& token) { 106 RedirectToMergeSession(token); 107 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); 108 } 109 110 void AutoLoginRedirector::OnUbertokenFailure( 111 const GoogleServiceAuthError& error) { 112 LOG(WARNING) << "AutoLoginRedirector: token request failed"; 113 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); 114 } 115 116 void 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 131 bool 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 151 AutoLoginInfoBarDelegate::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 167 AutoLoginInfoBarDelegate::~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 179 void AutoLoginInfoBarDelegate::RecordHistogramAction(Actions action) { 180 UMA_HISTOGRAM_ENUMERATION("AutoLogin.Regular", action, 181 HISTOGRAM_BOUNDING_VALUE); 182 } 183 184 void AutoLoginInfoBarDelegate::InfoBarDismissed() { 185 RecordHistogramAction(DISMISSED); 186 button_pressed_ = true; 187 } 188 189 int AutoLoginInfoBarDelegate::GetIconID() const { 190 return IDR_INFOBAR_AUTOLOGIN; 191 } 192 193 infobars::InfoBarDelegate::Type AutoLoginInfoBarDelegate::GetInfoBarType() 194 const { 195 return PAGE_ACTION_TYPE; 196 } 197 198 AutoLoginInfoBarDelegate* 199 AutoLoginInfoBarDelegate::AsAutoLoginInfoBarDelegate() { 200 return this; 201 } 202 203 base::string16 AutoLoginInfoBarDelegate::GetMessageText() const { 204 return l10n_util::GetStringFUTF16(IDS_AUTOLOGIN_INFOBAR_MESSAGE, 205 base::UTF8ToUTF16(params_.username)); 206 } 207 208 base::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 214 bool 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 224 bool 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 235 void AutoLoginInfoBarDelegate::GoogleSignedOut( 236 const std::string& account_id, 237 const std::string& username) { 238 infobar()->RemoveSelf(); 239 } 240