Credential Manager API: pop up the new "Manage accounts" bubble.
[chromium-blink-merge.git] / chrome / browser / ui / passwords / manage_passwords_bubble_model.cc
blobfdc12e466aed3e884b6e0f2d84dbd86321d672dd
1 // Copyright 2013 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.
5 #include "chrome/browser/ui/passwords/manage_passwords_bubble_model.h"
7 #include "base/command_line.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "chrome/browser/password_manager/password_store_factory.h"
10 #include "chrome/browser/profiles/profile.h"
11 #include "chrome/browser/ui/browser.h"
12 #include "chrome/browser/ui/browser_finder.h"
13 #include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h"
14 #include "chrome/browser/ui/passwords/password_bubble_experiment.h"
15 #include "chrome/grit/generated_resources.h"
16 #include "components/feedback/feedback_data.h"
17 #include "components/feedback/feedback_util.h"
18 #include "components/password_manager/content/common/credential_manager_types.h"
19 #include "components/password_manager/core/browser/password_store.h"
20 #include "components/password_manager/core/common/password_manager_ui.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "content/public/common/content_switches.h"
23 #include "ui/base/l10n/l10n_util.h"
24 #include "ui/base/resource/resource_bundle.h"
26 using autofill::PasswordFormMap;
27 using feedback::FeedbackData;
28 using content::WebContents;
29 namespace metrics_util = password_manager::metrics_util;
31 namespace {
33 enum FieldType { USERNAME_FIELD, PASSWORD_FIELD };
35 const int kUsernameFieldSize = 30;
36 const int kPasswordFieldSize = 22;
38 // Returns the width of |type| field.
39 int GetFieldWidth(FieldType type) {
40 return ui::ResourceBundle::GetSharedInstance()
41 .GetFontList(ui::ResourceBundle::SmallFont)
42 .GetExpectedTextWidth(type == USERNAME_FIELD ? kUsernameFieldSize
43 : kPasswordFieldSize);
46 Profile* GetProfileFromWebContents(content::WebContents* web_contents) {
47 if (!web_contents)
48 return nullptr;
49 return Profile::FromBrowserContext(web_contents->GetBrowserContext());
52 void RecordExperimentStatistics(content::WebContents* web_contents,
53 metrics_util::UIDismissalReason reason) {
54 Profile* profile = GetProfileFromWebContents(web_contents);
55 if (!profile)
56 return;
57 password_bubble_experiment::RecordBubbleClosed(profile->GetPrefs(), reason);
60 base::string16 PendingStateTitleBasedOnSavePasswordPref(
61 bool never_save_passwords) {
62 return l10n_util::GetStringUTF16(
63 never_save_passwords ? IDS_MANAGE_PASSWORDS_BLACKLIST_CONFIRMATION_TITLE
64 : IDS_SAVE_PASSWORD);
67 ScopedVector<const autofill::PasswordForm> DeepCopyForms(
68 const std::vector<const autofill::PasswordForm*>& forms) {
69 ScopedVector<const autofill::PasswordForm> result;
70 result.reserve(forms.size());
71 std::transform(forms.begin(), forms.end(), std::back_inserter(result),
72 [](const autofill::PasswordForm* form) {
73 return new autofill::PasswordForm(*form);
74 });
75 return result.Pass();
78 } // namespace
80 ManagePasswordsBubbleModel::ManagePasswordsBubbleModel(
81 content::WebContents* web_contents)
82 : content::WebContentsObserver(web_contents),
83 never_save_passwords_(false),
84 display_disposition_(metrics_util::AUTOMATIC_WITH_PASSWORD_PENDING),
85 dismissal_reason_(metrics_util::NOT_DISPLAYED) {
86 ManagePasswordsUIController* controller =
87 ManagePasswordsUIController::FromWebContents(web_contents);
89 origin_ = controller->origin();
90 state_ = controller->state();
91 if (state_ == password_manager::ui::PENDING_PASSWORD_STATE) {
92 pending_password_ = controller->PendingPassword();
93 local_credentials_ = DeepCopyForms(controller->GetCurrentForms());
94 } else if (state_ == password_manager::ui::CONFIRMATION_STATE) {
95 // We don't need anything.
96 } else if (state_ == password_manager::ui::CREDENTIAL_REQUEST_STATE) {
97 local_credentials_ = DeepCopyForms(controller->GetCurrentForms());
98 federated_credentials_ = DeepCopyForms(controller->GetFederatedForms());
99 } else if (state_ == password_manager::ui::AUTO_SIGNIN_STATE) {
100 pending_password_ = *controller->GetCurrentForms()[0];
101 } else {
102 local_credentials_ = DeepCopyForms(controller->GetCurrentForms());
105 if (state_ == password_manager::ui::PENDING_PASSWORD_STATE) {
106 title_ = PendingStateTitleBasedOnSavePasswordPref(never_save_passwords_);
107 } else if (state_ == password_manager::ui::BLACKLIST_STATE) {
108 title_ = l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_BLACKLISTED_TITLE);
109 } else if (state_ == password_manager::ui::CONFIRMATION_STATE) {
110 title_ =
111 l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_CONFIRM_GENERATED_TITLE);
112 } else if (state_ == password_manager::ui::CREDENTIAL_REQUEST_STATE) {
113 title_ = l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_CHOOSE_TITLE);
114 } else if (state_ == password_manager::ui::AUTO_SIGNIN_STATE) {
115 // There is no title.
116 } else {
117 title_ = IsNewUIActive() ?
118 l10n_util::GetStringFUTF16(IDS_MANAGE_ACCOUNTS_TITLE,
119 base::UTF8ToUTF16(origin_.spec())) :
120 l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_TITLE);
123 if (state_ == password_manager::ui::CONFIRMATION_STATE) {
124 base::string16 save_confirmation_link =
125 l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_CONFIRM_GENERATED_LINK);
126 size_t offset;
127 save_confirmation_text_ =
128 l10n_util::GetStringFUTF16(IDS_MANAGE_PASSWORDS_CONFIRM_GENERATED_TEXT,
129 save_confirmation_link, &offset);
130 save_confirmation_link_range_ =
131 gfx::Range(offset, offset + save_confirmation_link.length());
134 manage_link_ =
135 l10n_util::GetStringUTF16(IDS_OPTIONS_PASSWORDS_MANAGE_PASSWORDS_LINK);
138 ManagePasswordsBubbleModel::~ManagePasswordsBubbleModel() {}
140 void ManagePasswordsBubbleModel::OnBubbleShown(
141 ManagePasswordsBubble::DisplayReason reason) {
142 if (reason == ManagePasswordsBubble::USER_ACTION) {
143 if (state_ == password_manager::ui::PENDING_PASSWORD_STATE) {
144 display_disposition_ = metrics_util::MANUAL_WITH_PASSWORD_PENDING;
145 } else if (state_ == password_manager::ui::BLACKLIST_STATE) {
146 display_disposition_ = metrics_util::MANUAL_BLACKLISTED;
147 } else {
148 display_disposition_ = metrics_util::MANUAL_MANAGE_PASSWORDS;
150 } else {
151 if (state_ == password_manager::ui::CONFIRMATION_STATE) {
152 display_disposition_ =
153 metrics_util::AUTOMATIC_GENERATED_PASSWORD_CONFIRMATION;
154 } else if (state_ == password_manager::ui::CREDENTIAL_REQUEST_STATE) {
155 display_disposition_ = metrics_util::AUTOMATIC_CREDENTIAL_REQUEST;
156 } else if (state_ == password_manager::ui::AUTO_SIGNIN_STATE) {
157 display_disposition_ = metrics_util::AUTOMATIC_SIGNIN_TOAST;
158 } else {
159 display_disposition_ = metrics_util::AUTOMATIC_WITH_PASSWORD_PENDING;
162 metrics_util::LogUIDisplayDisposition(display_disposition_);
164 // Default to a dismissal reason of "no interaction". If the user interacts
165 // with the button in such a way that it closes, we'll reset this value
166 // accordingly.
167 dismissal_reason_ = metrics_util::NO_DIRECT_INTERACTION;
169 ManagePasswordsUIController* controller =
170 ManagePasswordsUIController::FromWebContents(web_contents());
171 controller->OnBubbleShown();
174 void ManagePasswordsBubbleModel::OnBubbleHidden() {
175 ManagePasswordsUIController* manage_passwords_ui_controller =
176 web_contents() ?
177 ManagePasswordsUIController::FromWebContents(web_contents())
178 : nullptr;
179 if (manage_passwords_ui_controller)
180 manage_passwords_ui_controller->OnBubbleHidden();
181 if (dismissal_reason_ == metrics_util::NOT_DISPLAYED)
182 return;
184 metrics_util::LogUIDismissalReason(dismissal_reason_);
185 // Other use cases have been reported in the callbacks like OnSaveClicked().
186 if (state_ == password_manager::ui::PENDING_PASSWORD_STATE &&
187 dismissal_reason_ == metrics_util::NO_DIRECT_INTERACTION)
188 RecordExperimentStatistics(web_contents(), dismissal_reason_);
191 void ManagePasswordsBubbleModel::OnNopeClicked() {
192 dismissal_reason_ = metrics_util::CLICKED_NOPE;
193 RecordExperimentStatistics(web_contents(), dismissal_reason_);
194 if (state_ != password_manager::ui::CREDENTIAL_REQUEST_STATE)
195 state_ = password_manager::ui::PENDING_PASSWORD_STATE;
198 void ManagePasswordsBubbleModel::OnConfirmationForNeverForThisSite() {
199 never_save_passwords_ = true;
200 title_ = PendingStateTitleBasedOnSavePasswordPref(never_save_passwords_);
203 void ManagePasswordsBubbleModel::OnUndoNeverForThisSite() {
204 never_save_passwords_ = false;
205 title_ = PendingStateTitleBasedOnSavePasswordPref(never_save_passwords_);
208 void ManagePasswordsBubbleModel::OnNeverForThisSiteClicked() {
209 dismissal_reason_ = metrics_util::CLICKED_NEVER;
210 RecordExperimentStatistics(web_contents(), dismissal_reason_);
211 ManagePasswordsUIController* manage_passwords_ui_controller =
212 ManagePasswordsUIController::FromWebContents(web_contents());
213 manage_passwords_ui_controller->NeverSavePassword();
214 state_ = password_manager::ui::BLACKLIST_STATE;
217 void ManagePasswordsBubbleModel::OnUnblacklistClicked() {
218 dismissal_reason_ = metrics_util::CLICKED_UNBLACKLIST;
219 ManagePasswordsUIController* manage_passwords_ui_controller =
220 ManagePasswordsUIController::FromWebContents(web_contents());
221 manage_passwords_ui_controller->UnblacklistSite();
222 state_ = password_manager::ui::MANAGE_STATE;
225 void ManagePasswordsBubbleModel::OnSaveClicked() {
226 dismissal_reason_ = metrics_util::CLICKED_SAVE;
227 RecordExperimentStatistics(web_contents(), dismissal_reason_);
228 ManagePasswordsUIController* manage_passwords_ui_controller =
229 ManagePasswordsUIController::FromWebContents(web_contents());
230 manage_passwords_ui_controller->SavePassword();
231 state_ = password_manager::ui::MANAGE_STATE;
234 void ManagePasswordsBubbleModel::OnDoneClicked() {
235 dismissal_reason_ = metrics_util::CLICKED_DONE;
238 // TODO(gcasto): Is it worth having this be separate from OnDoneClicked()?
239 // User intent is pretty similar in both cases.
240 void ManagePasswordsBubbleModel::OnOKClicked() {
241 dismissal_reason_ = metrics_util::CLICKED_OK;
244 void ManagePasswordsBubbleModel::OnManageLinkClicked() {
245 dismissal_reason_ = metrics_util::CLICKED_MANAGE;
246 ManagePasswordsUIController::FromWebContents(web_contents())
247 ->NavigateToPasswordManagerSettingsPage();
250 void ManagePasswordsBubbleModel::OnAutoSignInToastTimeout() {
251 dismissal_reason_ = metrics_util::AUTO_SIGNIN_TOAST_TIMEOUT;
254 void ManagePasswordsBubbleModel::OnAutoSignInClicked() {
255 dismissal_reason_ = metrics_util::AUTO_SIGNIN_TOAST_CLICKED;
256 ManagePasswordsUIController* manage_passwords_ui_controller =
257 ManagePasswordsUIController::FromWebContents(web_contents());
258 manage_passwords_ui_controller->ManageAccounts();
259 state_ = password_manager::ui::MANAGE_STATE;
262 void ManagePasswordsBubbleModel::OnPasswordAction(
263 const autofill::PasswordForm& password_form,
264 PasswordAction action) {
265 if (!web_contents())
266 return;
267 Profile* profile =
268 Profile::FromBrowserContext(web_contents()->GetBrowserContext());
269 password_manager::PasswordStore* password_store =
270 PasswordStoreFactory::GetForProfile(
271 profile, ServiceAccessType::EXPLICIT_ACCESS).get();
272 DCHECK(password_store);
273 if (action == REMOVE_PASSWORD)
274 password_store->RemoveLogin(password_form);
275 else
276 password_store->AddLogin(password_form);
279 void ManagePasswordsBubbleModel::OnChooseCredentials(
280 const autofill::PasswordForm& password_form,
281 password_manager::CredentialType credential_type) {
282 dismissal_reason_ = metrics_util::CLICKED_CREDENTIAL;
283 RecordExperimentStatistics(web_contents(), dismissal_reason_);
284 ManagePasswordsUIController* manage_passwords_ui_controller =
285 ManagePasswordsUIController::FromWebContents(web_contents());
286 manage_passwords_ui_controller->ChooseCredential(password_form,
287 credential_type);
288 state_ = password_manager::ui::INACTIVE_STATE;
291 Profile* ManagePasswordsBubbleModel::GetProfile() const {
292 return GetProfileFromWebContents(web_contents());
295 bool ManagePasswordsBubbleModel::IsNewUIActive() const {
296 return base::CommandLine::ForCurrentProcess()->HasSwitch(
297 switches::kEnableCredentialManagerAPI);
300 // static
301 int ManagePasswordsBubbleModel::UsernameFieldWidth() {
302 return GetFieldWidth(USERNAME_FIELD);
305 // static
306 int ManagePasswordsBubbleModel::PasswordFieldWidth() {
307 return GetFieldWidth(PASSWORD_FIELD);