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/password_manager/password_manager_delegate_impl.h"
6
7#include "base/memory/singleton.h"
8#include "base/metrics/histogram.h"
9#include "base/strings/utf_string_conversions.h"
10#include "base/timer/elapsed_timer.h"
11#include "chrome/browser/infobars/confirm_infobar_delegate.h"
12#include "chrome/browser/infobars/infobar.h"
13#include "chrome/browser/infobars/infobar_service.h"
14#include "chrome/browser/password_manager/password_form_manager.h"
15#include "chrome/browser/password_manager/password_manager.h"
16#include "chrome/browser/password_manager/password_manager_metrics_util.h"
17#include "chrome/browser/profiles/profile.h"
18#include "chrome/browser/ui/sync/one_click_signin_helper.h"
19#include "components/autofill/content/browser/autofill_driver_impl.h"
20#include "components/autofill/content/common/autofill_messages.h"
21#include "components/autofill/core/browser/autofill_manager.h"
22#include "components/autofill/core/common/password_form.h"
23#include "content/public/browser/navigation_entry.h"
24#include "content/public/browser/render_view_host.h"
25#include "content/public/browser/web_contents.h"
26#include "content/public/common/ssl_status.h"
27#include "google_apis/gaia/gaia_urls.h"
28#include "grit/chromium_strings.h"
29#include "grit/generated_resources.h"
30#include "grit/theme_resources.h"
31#include "net/cert/cert_status_flags.h"
32#include "ui/base/l10n/l10n_util.h"
33
34
35// SavePasswordInfoBarDelegate ------------------------------------------------
36
37// After a successful *new* login attempt, we take the PasswordFormManager in
38// provisional_save_manager_ and move it to a SavePasswordInfoBarDelegate while
39// the user makes up their mind with the "save password" infobar. Note if the
40// login is one we already know about, the end of the line is
41// provisional_save_manager_ because we just update it on success and so such
42// forms never end up in an infobar.
43class SavePasswordInfoBarDelegate : public ConfirmInfoBarDelegate {
44 public:
45  // If we won't be showing the one-click signin infobar, creates a save
46  // password infobar and delegate and adds the infobar to the InfoBarService
47  // for |web_contents|.  |uma_histogram_suffix| is empty, or one of the
48  // "group_X" suffixes used in the histogram names for infobar usage reporting;
49  // if empty, the usage is not reported, otherwise the suffix is used to choose
50  // the right histogram.
51  static void Create(content::WebContents* web_contents,
52                     PasswordFormManager* form_to_save,
53                     const std::string& uma_histogram_suffix);
54
55 private:
56  enum ResponseType {
57    NO_RESPONSE = 0,
58    REMEMBER_PASSWORD,
59    NEVER_REMEMBER_PASSWORD,
60    INFOBAR_DISMISSED,
61    NUM_RESPONSE_TYPES,
62  };
63
64  SavePasswordInfoBarDelegate(PasswordFormManager* form_to_save,
65                              const std::string& uma_histogram_suffix);
66  virtual ~SavePasswordInfoBarDelegate();
67
68  // ConfirmInfoBarDelegate
69  virtual int GetIconID() const OVERRIDE;
70  virtual Type GetInfoBarType() const OVERRIDE;
71  virtual base::string16 GetMessageText() const OVERRIDE;
72  virtual base::string16 GetButtonLabel(InfoBarButton button) const OVERRIDE;
73  virtual bool Accept() OVERRIDE;
74  virtual bool Cancel() OVERRIDE;
75  virtual void InfoBarDismissed() OVERRIDE;
76
77  virtual InfoBarAutomationType GetInfoBarAutomationType() const OVERRIDE;
78
79  // The PasswordFormManager managing the form we're asking the user about,
80  // and should update as per her decision.
81  scoped_ptr<PasswordFormManager> form_to_save_;
82
83  // Used to track the results we get from the info bar.
84  ResponseType infobar_response_;
85
86  // Measures the "Save password?" prompt lifetime. Used to report an UMA
87  // signal.
88  base::ElapsedTimer timer_;
89
90  // The group name corresponding to the domain name of |form_to_save_| if the
91  // form is on a monitored domain. Otherwise, an empty string.
92  const std::string uma_histogram_suffix_;
93
94  DISALLOW_COPY_AND_ASSIGN(SavePasswordInfoBarDelegate);
95};
96
97// static
98void SavePasswordInfoBarDelegate::Create(
99    content::WebContents* web_contents,
100    PasswordFormManager* form_to_save,
101    const std::string& uma_histogram_suffix) {
102#if defined(ENABLE_ONE_CLICK_SIGNIN)
103  // Don't show the password manager infobar if this form is for a google
104  // account and we are going to show the one-click signin infobar.
105  GURL realm(form_to_save->realm());
106  // TODO(mathp): Checking only against associated_username() causes a bug
107  // referenced here: crbug.com/133275
108  if (((realm == GaiaUrls::GetInstance()->gaia_login_form_realm()) ||
109       (realm == GURL("https://www.google.com/"))) &&
110      OneClickSigninHelper::CanOffer(
111          web_contents, OneClickSigninHelper::CAN_OFFER_FOR_INTERSTITAL_ONLY,
112          UTF16ToUTF8(form_to_save->associated_username()), NULL))
113    return;
114#endif
115
116  InfoBarService::FromWebContents(web_contents)->AddInfoBar(
117      ConfirmInfoBarDelegate::CreateInfoBar(scoped_ptr<ConfirmInfoBarDelegate>(
118          new SavePasswordInfoBarDelegate(form_to_save,
119                                          uma_histogram_suffix))));
120}
121
122SavePasswordInfoBarDelegate::SavePasswordInfoBarDelegate(
123    PasswordFormManager* form_to_save,
124    const std::string& uma_histogram_suffix)
125    : ConfirmInfoBarDelegate(),
126      form_to_save_(form_to_save),
127      infobar_response_(NO_RESPONSE),
128      uma_histogram_suffix_(uma_histogram_suffix) {
129  if (!uma_histogram_suffix_.empty()) {
130    password_manager_metrics_util::LogUMAHistogramBoolean(
131        "PasswordManager.SavePasswordPromptDisplayed_" + uma_histogram_suffix_,
132        true);
133  }
134}
135
136SavePasswordInfoBarDelegate::~SavePasswordInfoBarDelegate() {
137  UMA_HISTOGRAM_ENUMERATION("PasswordManager.InfoBarResponse",
138                            infobar_response_, NUM_RESPONSE_TYPES);
139
140  // The shortest period for which the prompt needs to live, so that we don't
141  // consider it killed prematurely, as might happen, e.g., if a pre-rendered
142  // page gets swapped in (and the current WebContents is destroyed).
143  const base::TimeDelta kMinimumPromptDisplayTime =
144      base::TimeDelta::FromSeconds(1);
145
146  if (!uma_histogram_suffix_.empty()) {
147    password_manager_metrics_util::LogUMAHistogramEnumeration(
148        "PasswordManager.SavePasswordPromptResponse_" + uma_histogram_suffix_,
149        infobar_response_, NUM_RESPONSE_TYPES);
150    password_manager_metrics_util::LogUMAHistogramBoolean(
151        "PasswordManager.SavePasswordPromptDisappearedQuickly_" +
152            uma_histogram_suffix_,
153        timer_.Elapsed() < kMinimumPromptDisplayTime);
154  }
155}
156
157int SavePasswordInfoBarDelegate::GetIconID() const {
158  return IDR_INFOBAR_SAVE_PASSWORD;
159}
160
161InfoBarDelegate::Type SavePasswordInfoBarDelegate::GetInfoBarType() const {
162  return PAGE_ACTION_TYPE;
163}
164
165base::string16 SavePasswordInfoBarDelegate::GetMessageText() const {
166  return l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_SAVE_PASSWORD_PROMPT);
167}
168
169base::string16 SavePasswordInfoBarDelegate::GetButtonLabel(
170    InfoBarButton button) const {
171  return l10n_util::GetStringUTF16((button == BUTTON_OK) ?
172      IDS_PASSWORD_MANAGER_SAVE_BUTTON : IDS_PASSWORD_MANAGER_BLACKLIST_BUTTON);
173}
174
175bool SavePasswordInfoBarDelegate::Accept() {
176  DCHECK(form_to_save_.get());
177  form_to_save_->Save();
178  infobar_response_ = REMEMBER_PASSWORD;
179  return true;
180}
181
182bool SavePasswordInfoBarDelegate::Cancel() {
183  DCHECK(form_to_save_.get());
184  form_to_save_->PermanentlyBlacklist();
185  infobar_response_ = NEVER_REMEMBER_PASSWORD;
186  return true;
187}
188
189void SavePasswordInfoBarDelegate::InfoBarDismissed() {
190  DCHECK(form_to_save_.get());
191  infobar_response_ = INFOBAR_DISMISSED;
192}
193
194InfoBarDelegate::InfoBarAutomationType
195    SavePasswordInfoBarDelegate::GetInfoBarAutomationType() const {
196  return PASSWORD_INFOBAR;
197}
198
199
200// PasswordManagerDelegateImpl ------------------------------------------------
201
202DEFINE_WEB_CONTENTS_USER_DATA_KEY(PasswordManagerDelegateImpl);
203
204PasswordManagerDelegateImpl::PasswordManagerDelegateImpl(
205    content::WebContents* web_contents)
206    : web_contents_(web_contents) {
207}
208
209PasswordManagerDelegateImpl::~PasswordManagerDelegateImpl() {
210}
211
212void PasswordManagerDelegateImpl::FillPasswordForm(
213    const autofill::PasswordFormFillData& form_data) {
214  web_contents_->GetRenderViewHost()->Send(
215      new AutofillMsg_FillPasswordForm(
216          web_contents_->GetRenderViewHost()->GetRoutingID(),
217          form_data));
218}
219
220void PasswordManagerDelegateImpl::AddSavePasswordInfoBarIfPermitted(
221    PasswordFormManager* form_to_save) {
222  std::string uma_histogram_suffix(
223      password_manager_metrics_util::GroupIdToString(
224          password_manager_metrics_util::MonitoredDomainGroupId(
225              form_to_save->realm(), GetProfile()->GetPrefs())));
226  SavePasswordInfoBarDelegate::Create(
227      web_contents_, form_to_save, uma_histogram_suffix);
228}
229
230Profile* PasswordManagerDelegateImpl::GetProfile() {
231  return Profile::FromBrowserContext(web_contents_->GetBrowserContext());
232}
233
234bool PasswordManagerDelegateImpl::DidLastPageLoadEncounterSSLErrors() {
235  content::NavigationEntry* entry =
236      web_contents_->GetController().GetActiveEntry();
237  if (!entry) {
238    NOTREACHED();
239    return false;
240  }
241
242  return net::IsCertStatusError(entry->GetSSL().cert_status);
243}
244