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