BookmarkManager: Fix 'new folder text field size changes on clicking it' issue.
[chromium-blink-merge.git] / chrome / browser / ui / passwords / manage_passwords_bubble_model.cc
bloba753a17e92887c0276e26825e02d11d780b1b6e9
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/prefs/pref_service.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "chrome/browser/password_manager/password_store_factory.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/browser/signin/signin_manager_factory.h"
13 #include "chrome/browser/sync/profile_sync_service.h"
14 #include "chrome/browser/sync/profile_sync_service_factory.h"
15 #include "chrome/browser/ui/browser.h"
16 #include "chrome/browser/ui/browser_finder.h"
17 #include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h"
18 #include "chrome/browser/ui/passwords/manage_passwords_view_utils.h"
19 #include "chrome/common/url_constants.h"
20 #include "chrome/grit/chromium_strings.h"
21 #include "chrome/grit/generated_resources.h"
22 #include "components/feedback/feedback_data.h"
23 #include "components/feedback/feedback_util.h"
24 #include "components/password_manager/core/browser/password_bubble_experiment.h"
25 #include "components/password_manager/core/browser/password_store.h"
26 #include "components/password_manager/core/common/credential_manager_types.h"
27 #include "components/password_manager/core/common/password_manager_pref_names.h"
28 #include "components/password_manager/core/common/password_manager_ui.h"
29 #include "components/signin/core/browser/signin_manager.h"
30 #include "content/public/browser/browser_thread.h"
31 #include "content/public/common/content_switches.h"
32 #include "ui/base/l10n/l10n_util.h"
33 #include "ui/base/resource/resource_bundle.h"
35 using autofill::PasswordFormMap;
36 using feedback::FeedbackData;
37 using content::WebContents;
38 namespace metrics_util = password_manager::metrics_util;
40 namespace {
42 enum FieldType { USERNAME_FIELD, PASSWORD_FIELD };
44 const int kUsernameFieldSize = 30;
45 const int kPasswordFieldSize = 22;
47 // Returns the width of |type| field.
48 int GetFieldWidth(FieldType type) {
49 return ui::ResourceBundle::GetSharedInstance()
50 .GetFontList(ui::ResourceBundle::SmallFont)
51 .GetExpectedTextWidth(type == USERNAME_FIELD ? kUsernameFieldSize
52 : kPasswordFieldSize);
55 Profile* GetProfileFromWebContents(content::WebContents* web_contents) {
56 if (!web_contents)
57 return nullptr;
58 return Profile::FromBrowserContext(web_contents->GetBrowserContext());
61 void RecordExperimentStatistics(content::WebContents* web_contents,
62 metrics_util::UIDismissalReason reason) {
63 // TODO(vasilii): revive the function while implementing the smart bubble.
66 ScopedVector<const autofill::PasswordForm> DeepCopyForms(
67 const std::vector<const autofill::PasswordForm*>& forms) {
68 ScopedVector<const autofill::PasswordForm> result;
69 result.reserve(forms.size());
70 std::transform(forms.begin(), forms.end(), std::back_inserter(result),
71 [](const autofill::PasswordForm* form) {
72 return new autofill::PasswordForm(*form);
73 });
74 return result.Pass();
77 // A wrapper around password_bubble_experiment::IsSmartLockBrandingEnabled
78 // extracting the sync_service from the profile.
79 bool IsSmartLockBrandingEnabled(Profile* profile) {
80 const ProfileSyncService* sync_service =
81 ProfileSyncServiceFactory::GetForProfile(profile);
82 return password_bubble_experiment::IsSmartLockBrandingEnabled(sync_service);
85 } // namespace
87 ManagePasswordsBubbleModel::ManagePasswordsBubbleModel(
88 content::WebContents* web_contents)
89 : content::WebContentsObserver(web_contents),
90 display_disposition_(metrics_util::AUTOMATIC_WITH_PASSWORD_PENDING),
91 dismissal_reason_(metrics_util::NOT_DISPLAYED),
92 update_password_submission_event_(metrics_util::NO_UPDATE_SUBMISSION) {
93 ManagePasswordsUIController* controller =
94 ManagePasswordsUIController::FromWebContents(web_contents);
96 origin_ = controller->origin();
97 state_ = controller->state();
98 password_overridden_ = controller->PasswordOverridden();
99 if (state_ == password_manager::ui::PENDING_PASSWORD_STATE ||
100 state_ == password_manager::ui::PENDING_PASSWORD_UPDATE_STATE) {
101 pending_password_ = controller->PendingPassword();
102 local_credentials_ = DeepCopyForms(controller->GetCurrentForms());
103 } else if (state_ == password_manager::ui::CONFIRMATION_STATE) {
104 // We don't need anything.
105 } else if (state_ == password_manager::ui::CREDENTIAL_REQUEST_STATE) {
106 local_credentials_ = DeepCopyForms(controller->GetCurrentForms());
107 federated_credentials_ = DeepCopyForms(controller->GetFederatedForms());
108 } else if (state_ == password_manager::ui::AUTO_SIGNIN_STATE) {
109 pending_password_ = *controller->GetCurrentForms()[0];
110 } else {
111 local_credentials_ = DeepCopyForms(controller->GetCurrentForms());
114 if (state_ == password_manager::ui::PENDING_PASSWORD_STATE ||
115 state_ == password_manager::ui::PENDING_PASSWORD_UPDATE_STATE) {
116 UpdatePendingStateTitle();
117 } else if (state_ == password_manager::ui::CONFIRMATION_STATE) {
118 title_ =
119 l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_CONFIRM_GENERATED_TITLE);
120 } else if (state_ == password_manager::ui::CREDENTIAL_REQUEST_STATE) {
121 title_ = l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_CHOOSE_TITLE);
122 } else if (state_ == password_manager::ui::AUTO_SIGNIN_STATE) {
123 // There is no title.
124 } else if (state_ == password_manager::ui::MANAGE_STATE) {
125 UpdateManageStateTitle();
128 if (state_ == password_manager::ui::CONFIRMATION_STATE) {
129 base::string16 save_confirmation_link =
130 l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_LINK);
131 int confirmation_text_id = IDS_MANAGE_PASSWORDS_CONFIRM_GENERATED_TEXT;
132 if (IsSmartLockBrandingEnabled(GetProfile())) {
133 std::string management_hostname =
134 GURL(chrome::kPasswordManagerAccountDashboardURL).host();
135 save_confirmation_link = base::UTF8ToUTF16(management_hostname);
136 confirmation_text_id =
137 IDS_MANAGE_PASSWORDS_CONFIRM_GENERATED_SMART_LOCK_TEXT;
140 size_t offset;
141 save_confirmation_text_ =
142 l10n_util::GetStringFUTF16(
143 confirmation_text_id, save_confirmation_link, &offset);
144 save_confirmation_link_range_ =
145 gfx::Range(offset, offset + save_confirmation_link.length());
148 manage_link_ =
149 l10n_util::GetStringUTF16(IDS_OPTIONS_PASSWORDS_MANAGE_PASSWORDS_LINK);
152 ManagePasswordsBubbleModel::~ManagePasswordsBubbleModel() {}
154 void ManagePasswordsBubbleModel::OnBubbleShown(
155 ManagePasswordsBubble::DisplayReason reason) {
156 if (reason == ManagePasswordsBubble::USER_ACTION) {
157 switch (state_) {
158 case password_manager::ui::PENDING_PASSWORD_STATE:
159 display_disposition_ = metrics_util::MANUAL_WITH_PASSWORD_PENDING;
160 break;
161 case password_manager::ui::PENDING_PASSWORD_UPDATE_STATE:
162 display_disposition_ =
163 metrics_util::MANUAL_WITH_PASSWORD_PENDING_UPDATE;
164 break;
165 case password_manager::ui::MANAGE_STATE:
166 display_disposition_ = metrics_util::MANUAL_MANAGE_PASSWORDS;
167 break;
168 default:
169 break;
171 } else {
172 switch (state_) {
173 case password_manager::ui::PENDING_PASSWORD_STATE:
174 display_disposition_ = metrics_util::AUTOMATIC_WITH_PASSWORD_PENDING;
175 break;
176 case password_manager::ui::PENDING_PASSWORD_UPDATE_STATE:
177 display_disposition_ =
178 metrics_util::AUTOMATIC_WITH_PASSWORD_PENDING_UPDATE;
179 break;
180 case password_manager::ui::CONFIRMATION_STATE:
181 display_disposition_ =
182 metrics_util::AUTOMATIC_GENERATED_PASSWORD_CONFIRMATION;
183 break;
184 case password_manager::ui::CREDENTIAL_REQUEST_STATE:
185 display_disposition_ = metrics_util::AUTOMATIC_CREDENTIAL_REQUEST;
186 break;
187 case password_manager::ui::AUTO_SIGNIN_STATE:
188 display_disposition_ = metrics_util::AUTOMATIC_SIGNIN_TOAST;
189 break;
190 default:
191 break;
194 metrics_util::LogUIDisplayDisposition(display_disposition_);
196 // Default to a dismissal reason of "no interaction". If the user interacts
197 // with the button in such a way that it closes, we'll reset this value
198 // accordingly.
199 dismissal_reason_ = metrics_util::NO_DIRECT_INTERACTION;
201 ManagePasswordsUIController* controller =
202 ManagePasswordsUIController::FromWebContents(web_contents());
203 controller->OnBubbleShown();
206 void ManagePasswordsBubbleModel::OnBubbleHidden() {
207 if (state_ == password_manager::ui::PENDING_PASSWORD_STATE) {
208 Profile* profile = GetProfile();
209 if (profile && IsSmartLockBrandingEnabled(profile)) {
210 profile->GetPrefs()->SetBoolean(
211 password_manager::prefs::kWasSavePrompFirstRunExperienceShown, true);
214 ManagePasswordsUIController* manage_passwords_ui_controller =
215 web_contents() ?
216 ManagePasswordsUIController::FromWebContents(web_contents())
217 : nullptr;
218 if (manage_passwords_ui_controller)
219 manage_passwords_ui_controller->OnBubbleHidden();
220 if (dismissal_reason_ == metrics_util::NOT_DISPLAYED)
221 return;
223 if (state_ != password_manager::ui::PENDING_PASSWORD_UPDATE_STATE) {
224 // We have separate metrics for the Update bubble so do not record dismissal
225 // reason for it.
226 metrics_util::LogUIDismissalReason(dismissal_reason_);
228 // Other use cases have been reported in the callbacks like OnSaveClicked().
229 if (state_ == password_manager::ui::PENDING_PASSWORD_STATE &&
230 dismissal_reason_ == metrics_util::NO_DIRECT_INTERACTION)
231 RecordExperimentStatistics(web_contents(), dismissal_reason_);
232 // Check if this was update password and record update statistics.
233 if (update_password_submission_event_ == metrics_util::NO_UPDATE_SUBMISSION &&
234 (state_ == password_manager::ui::PENDING_PASSWORD_UPDATE_STATE ||
235 state_ == password_manager::ui::PENDING_PASSWORD_STATE)) {
236 update_password_submission_event_ =
237 GetUpdateDismissalReason(NO_INTERACTION);
239 if (update_password_submission_event_ != metrics_util::NO_UPDATE_SUBMISSION)
240 LogUpdatePasswordSubmissionEvent(update_password_submission_event_);
243 void ManagePasswordsBubbleModel::OnCancelClicked() {
244 DCHECK_EQ(password_manager::ui::CREDENTIAL_REQUEST_STATE, state_);
245 dismissal_reason_ = metrics_util::CLICKED_CANCEL;
248 void ManagePasswordsBubbleModel::OnNeverForThisSiteClicked() {
249 dismissal_reason_ = metrics_util::CLICKED_NEVER;
250 update_password_submission_event_ = GetUpdateDismissalReason(NOPE_CLICKED);
251 RecordExperimentStatistics(web_contents(), dismissal_reason_);
252 ManagePasswordsUIController* manage_passwords_ui_controller =
253 ManagePasswordsUIController::FromWebContents(web_contents());
254 manage_passwords_ui_controller->NeverSavePassword();
257 void ManagePasswordsBubbleModel::OnSaveClicked() {
258 dismissal_reason_ = metrics_util::CLICKED_SAVE;
259 RecordExperimentStatistics(web_contents(), dismissal_reason_);
260 update_password_submission_event_ = GetUpdateDismissalReason(UPDATE_CLICKED);
261 ManagePasswordsUIController* manage_passwords_ui_controller =
262 ManagePasswordsUIController::FromWebContents(web_contents());
263 manage_passwords_ui_controller->SavePassword();
266 void ManagePasswordsBubbleModel::OnNopeUpdateClicked() {
267 update_password_submission_event_ = GetUpdateDismissalReason(NOPE_CLICKED);
270 void ManagePasswordsBubbleModel::OnUpdateClicked(
271 const autofill::PasswordForm& password_form) {
272 update_password_submission_event_ = GetUpdateDismissalReason(UPDATE_CLICKED);
273 ManagePasswordsUIController* manage_passwords_ui_controller =
274 ManagePasswordsUIController::FromWebContents(web_contents());
275 manage_passwords_ui_controller->UpdatePassword(password_form);
278 void ManagePasswordsBubbleModel::OnDoneClicked() {
279 dismissal_reason_ = metrics_util::CLICKED_DONE;
282 // TODO(gcasto): Is it worth having this be separate from OnDoneClicked()?
283 // User intent is pretty similar in both cases.
284 void ManagePasswordsBubbleModel::OnOKClicked() {
285 dismissal_reason_ = metrics_util::CLICKED_OK;
288 void ManagePasswordsBubbleModel::OnManageLinkClicked() {
289 dismissal_reason_ = metrics_util::CLICKED_MANAGE;
290 if (IsSmartLockBrandingEnabled(GetProfile())) {
291 ManagePasswordsUIController::FromWebContents(web_contents())
292 ->NavigateToExternalPasswordManager();
293 } else {
294 ManagePasswordsUIController::FromWebContents(web_contents())
295 ->NavigateToPasswordManagerSettingsPage();
299 void ManagePasswordsBubbleModel::OnBrandLinkClicked() {
300 dismissal_reason_ = metrics_util::CLICKED_BRAND_NAME;
301 ManagePasswordsUIController::FromWebContents(web_contents())
302 ->NavigateToSmartLockPage();
305 void ManagePasswordsBubbleModel::OnAutoSignInToastTimeout() {
306 dismissal_reason_ = metrics_util::AUTO_SIGNIN_TOAST_TIMEOUT;
309 void ManagePasswordsBubbleModel::OnAutoSignInClicked() {
310 dismissal_reason_ = metrics_util::AUTO_SIGNIN_TOAST_CLICKED;
311 ManagePasswordsUIController* manage_passwords_ui_controller =
312 ManagePasswordsUIController::FromWebContents(web_contents());
313 manage_passwords_ui_controller->ManageAccounts();
316 void ManagePasswordsBubbleModel::OnPasswordAction(
317 const autofill::PasswordForm& password_form,
318 PasswordAction action) {
319 if (!web_contents())
320 return;
321 Profile* profile =
322 Profile::FromBrowserContext(web_contents()->GetBrowserContext());
323 password_manager::PasswordStore* password_store =
324 PasswordStoreFactory::GetForProfile(
325 profile, ServiceAccessType::EXPLICIT_ACCESS).get();
326 DCHECK(password_store);
327 if (action == REMOVE_PASSWORD)
328 password_store->RemoveLogin(password_form);
329 else
330 password_store->AddLogin(password_form);
333 void ManagePasswordsBubbleModel::OnChooseCredentials(
334 const autofill::PasswordForm& password_form,
335 password_manager::CredentialType credential_type) {
336 dismissal_reason_ = metrics_util::CLICKED_CREDENTIAL;
337 ManagePasswordsUIController* manage_passwords_ui_controller =
338 ManagePasswordsUIController::FromWebContents(web_contents());
339 manage_passwords_ui_controller->ChooseCredential(password_form,
340 credential_type);
343 Profile* ManagePasswordsBubbleModel::GetProfile() const {
344 return GetProfileFromWebContents(web_contents());
347 bool ManagePasswordsBubbleModel::ShouldShowMultipleAccountUpdateUI() const {
348 ManagePasswordsUIController* controller =
349 ManagePasswordsUIController::FromWebContents(web_contents());
350 return controller->ShouldShowMultipleAccountUpdateUI();
353 bool ManagePasswordsBubbleModel::ShouldShowGoogleSmartLockWelcome() const {
354 Profile* profile = GetProfile();
355 if (IsSmartLockBrandingEnabled(profile)) {
356 PrefService* prefs = profile->GetPrefs();
357 return !prefs->GetBoolean(
358 password_manager::prefs::kWasSavePrompFirstRunExperienceShown);
360 return false;
363 // static
364 int ManagePasswordsBubbleModel::UsernameFieldWidth() {
365 return GetFieldWidth(USERNAME_FIELD);
368 // static
369 int ManagePasswordsBubbleModel::PasswordFieldWidth() {
370 return GetFieldWidth(PASSWORD_FIELD);
373 void ManagePasswordsBubbleModel::UpdatePendingStateTitle() {
374 title_brand_link_range_ = gfx::Range();
375 GetSavePasswordDialogTitleTextAndLinkRange(
376 web_contents()->GetVisibleURL(), origin(),
377 IsSmartLockBrandingEnabled(GetProfile()),
378 state_ == password_manager::ui::PENDING_PASSWORD_UPDATE_STATE, &title_,
379 &title_brand_link_range_);
382 void ManagePasswordsBubbleModel::UpdateManageStateTitle() {
383 GetManagePasswordsDialogTitleText(web_contents()->GetVisibleURL(), origin(),
384 &title_);
387 password_manager::metrics_util::UpdatePasswordSubmissionEvent
388 ManagePasswordsBubbleModel::GetUpdateDismissalReason(
389 UserBehaviorOnUpdateBubble behavior) const {
390 using namespace password_manager::metrics_util;
391 static const password_manager::metrics_util::UpdatePasswordSubmissionEvent
392 update_events[4][3] = {
393 {NO_ACCOUNTS_CLICKED_UPDATE, NO_ACCOUNTS_CLICKED_NOPE,
394 NO_ACCOUNTS_NO_INTERACTION},
395 {ONE_ACCOUNT_CLICKED_UPDATE, ONE_ACCOUNT_CLICKED_NOPE,
396 ONE_ACCOUNT_NO_INTERACTION},
397 {MULTIPLE_ACCOUNTS_CLICKED_UPDATE, MULTIPLE_ACCOUNTS_CLICKED_NOPE,
398 MULTIPLE_ACCOUNTS_NO_INTERACTION},
399 {PASSWORD_OVERRIDDEN_CLICKED_UPDATE, PASSWORD_OVERRIDDEN_CLICKED_NOPE,
400 PASSWORD_OVERRIDDEN_NO_INTERACTION}};
402 if (state_ == password_manager::ui::PENDING_PASSWORD_STATE) {
403 if (pending_password_.IsPossibleChangePasswordFormWithoutUsername())
404 return update_events[0][behavior];
405 return NO_UPDATE_SUBMISSION;
407 if (state_ != password_manager::ui::PENDING_PASSWORD_UPDATE_STATE)
408 return NO_UPDATE_SUBMISSION;
409 if (password_overridden_)
410 return update_events[3][behavior];
411 if (ShouldShowMultipleAccountUpdateUI())
412 return update_events[2][behavior];
413 return update_events[1][behavior];