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