manage_passwords_bubble_model.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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. 4 5#include "chrome/browser/ui/passwords/manage_passwords_bubble_model.h" 6 7#include "base/strings/string_split.h" 8#include "base/strings/string_util.h" 9#include "chrome/browser/password_manager/password_store_factory.h" 10#include "chrome/browser/ui/browser.h" 11#include "chrome/browser/ui/browser_finder.h" 12#include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h" 13#include "chrome/grit/generated_resources.h" 14#include "components/password_manager/core/browser/password_store.h" 15#include "components/password_manager/core/common/password_manager_ui.h" 16#include "ui/base/l10n/l10n_util.h" 17#include "ui/base/resource/resource_bundle.h" 18 19using autofill::PasswordFormMap; 20using content::WebContents; 21namespace metrics_util = password_manager::metrics_util; 22 23namespace { 24 25enum FieldType { USERNAME_FIELD, PASSWORD_FIELD }; 26 27const int kUsernameFieldSize = 30; 28const int kPasswordFieldSize = 22; 29 30// Returns the width of |type| field. 31int GetFieldWidth(FieldType type) { 32 return ui::ResourceBundle::GetSharedInstance() 33 .GetFontList(ui::ResourceBundle::SmallFont) 34 .GetExpectedTextWidth(type == USERNAME_FIELD ? kUsernameFieldSize 35 : kPasswordFieldSize); 36} 37 38void SetupLinkifiedText(const base::string16& string_with_separator, 39 base::string16* text, 40 gfx::Range* link_range) { 41 std::vector<base::string16> pieces; 42 base::SplitStringDontTrim(string_with_separator, 43 '|', // separator 44 &pieces); 45 DCHECK_EQ(3u, pieces.size()); 46 *link_range = gfx::Range(pieces[0].size(), 47 pieces[0].size() + pieces[1].size()); 48 *text = JoinString(pieces, base::string16()); 49} 50 51} // namespace 52 53ManagePasswordsBubbleModel::ManagePasswordsBubbleModel( 54 content::WebContents* web_contents) 55 : content::WebContentsObserver(web_contents), 56 display_disposition_( 57 metrics_util::AUTOMATIC_WITH_PASSWORD_PENDING), 58 dismissal_reason_(metrics_util::NOT_DISPLAYED) { 59 ManagePasswordsUIController* controller = 60 ManagePasswordsUIController::FromWebContents(web_contents); 61 62 // TODO(mkwst): Reverse this logic. The controller should populate the model 63 // directly rather than the model pulling from the controller. Perhaps like 64 // `controller->PopulateModel(this)`. 65 state_ = controller->state(); 66 if (password_manager::ui::IsPendingState(state_)) 67 pending_credentials_ = controller->PendingCredentials(); 68 best_matches_ = controller->best_matches(); 69 70 if (password_manager::ui::IsPendingState(state_)) { 71 title_ = l10n_util::GetStringUTF16(IDS_SAVE_PASSWORD); 72 } else if (state_ == password_manager::ui::BLACKLIST_STATE) { 73 title_ = l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_BLACKLISTED_TITLE); 74 } else if (state_ == password_manager::ui::CONFIRMATION_STATE) { 75 title_ = 76 l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_CONFIRM_GENERATED_TITLE); 77 } else { 78 title_ = l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_TITLE); 79 } 80 81 SetupLinkifiedText( 82 l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_CONFIRM_GENERATED_TEXT), 83 &save_confirmation_text_, 84 &save_confirmation_link_range_); 85 86 manage_link_ = 87 l10n_util::GetStringUTF16(IDS_OPTIONS_PASSWORDS_MANAGE_PASSWORDS_LINK); 88} 89 90ManagePasswordsBubbleModel::~ManagePasswordsBubbleModel() {} 91 92void ManagePasswordsBubbleModel::OnBubbleShown( 93 ManagePasswordsBubble::DisplayReason reason) { 94 if (reason == ManagePasswordsBubble::USER_ACTION) { 95 if (password_manager::ui::IsPendingState(state_)) { 96 display_disposition_ = metrics_util::MANUAL_WITH_PASSWORD_PENDING; 97 } else if (state_ == password_manager::ui::BLACKLIST_STATE) { 98 display_disposition_ = metrics_util::MANUAL_BLACKLISTED; 99 } else { 100 display_disposition_ = metrics_util::MANUAL_MANAGE_PASSWORDS; 101 } 102 } else { 103 if (state_ == password_manager::ui::CONFIRMATION_STATE) { 104 display_disposition_ = 105 metrics_util::AUTOMATIC_GENERATED_PASSWORD_CONFIRMATION; 106 } else { 107 display_disposition_ = metrics_util::AUTOMATIC_WITH_PASSWORD_PENDING; 108 } 109 } 110 metrics_util::LogUIDisplayDisposition(display_disposition_); 111 112 // Default to a dismissal reason of "no interaction". If the user interacts 113 // with the button in such a way that it closes, we'll reset this value 114 // accordingly. 115 dismissal_reason_ = metrics_util::NO_DIRECT_INTERACTION; 116} 117 118void ManagePasswordsBubbleModel::OnBubbleHidden() { 119 if (dismissal_reason_ == metrics_util::NOT_DISPLAYED) 120 return; 121 122 metrics_util::LogUIDismissalReason(dismissal_reason_); 123} 124 125void ManagePasswordsBubbleModel::OnNopeClicked() { 126 dismissal_reason_ = metrics_util::CLICKED_NOPE; 127 state_ = password_manager::ui::PENDING_PASSWORD_STATE; 128} 129 130void ManagePasswordsBubbleModel::OnNeverForThisSiteClicked() { 131 dismissal_reason_ = metrics_util::CLICKED_NEVER; 132 ManagePasswordsUIController* manage_passwords_ui_controller = 133 ManagePasswordsUIController::FromWebContents(web_contents()); 134 manage_passwords_ui_controller->NeverSavePassword(); 135 state_ = password_manager::ui::BLACKLIST_STATE; 136} 137 138void ManagePasswordsBubbleModel::OnUnblacklistClicked() { 139 dismissal_reason_ = metrics_util::CLICKED_UNBLACKLIST; 140 ManagePasswordsUIController* manage_passwords_ui_controller = 141 ManagePasswordsUIController::FromWebContents(web_contents()); 142 manage_passwords_ui_controller->UnblacklistSite(); 143 state_ = password_manager::ui::MANAGE_STATE; 144} 145 146void ManagePasswordsBubbleModel::OnSaveClicked() { 147 dismissal_reason_ = metrics_util::CLICKED_SAVE; 148 ManagePasswordsUIController* manage_passwords_ui_controller = 149 ManagePasswordsUIController::FromWebContents(web_contents()); 150 manage_passwords_ui_controller->SavePassword(); 151 state_ = password_manager::ui::MANAGE_STATE; 152} 153 154void ManagePasswordsBubbleModel::OnDoneClicked() { 155 dismissal_reason_ = metrics_util::CLICKED_DONE; 156} 157 158// TODO(gcasto): Is it worth having this be separate from OnDoneClicked()? 159// User intent is pretty similar in both cases. 160void ManagePasswordsBubbleModel::OnOKClicked() { 161 dismissal_reason_ = metrics_util::CLICKED_OK; 162} 163 164void ManagePasswordsBubbleModel::OnManageLinkClicked() { 165 dismissal_reason_ = metrics_util::CLICKED_MANAGE; 166 ManagePasswordsUIController::FromWebContents(web_contents()) 167 ->NavigateToPasswordManagerSettingsPage(); 168} 169 170void ManagePasswordsBubbleModel::OnPasswordAction( 171 const autofill::PasswordForm& password_form, 172 PasswordAction action) { 173 if (!web_contents()) 174 return; 175 Profile* profile = 176 Profile::FromBrowserContext(web_contents()->GetBrowserContext()); 177 password_manager::PasswordStore* password_store = 178 PasswordStoreFactory::GetForProfile(profile, Profile::EXPLICIT_ACCESS) 179 .get(); 180 DCHECK(password_store); 181 if (action == REMOVE_PASSWORD) 182 password_store->RemoveLogin(password_form); 183 else 184 password_store->AddLogin(password_form); 185} 186 187// static 188int ManagePasswordsBubbleModel::UsernameFieldWidth() { 189 return GetFieldWidth(USERNAME_FIELD); 190} 191 192// static 193int ManagePasswordsBubbleModel::PasswordFieldWidth() { 194 return GetFieldWidth(PASSWORD_FIELD); 195} 196