Add ICU message format support
[chromium-blink-merge.git] / chrome / browser / ui / passwords / manage_passwords_ui_controller.cc
blobeaaf1c8cf4158f04ce2507f1f14e1eb255a966ef
1 // Copyright 2014 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_ui_controller.h"
7 #include "base/auto_reset.h"
8 #include "chrome/app/chrome_command_ids.h"
9 #include "chrome/browser/browsing_data/browsing_data_helper.h"
10 #include "chrome/browser/password_manager/chrome_password_manager_client.h"
11 #include "chrome/browser/password_manager/password_store_factory.h"
12 #include "chrome/browser/ui/browser_command_controller.h"
13 #include "chrome/browser/ui/browser_finder.h"
14 #include "chrome/browser/ui/browser_window.h"
15 #include "chrome/browser/ui/chrome_pages.h"
16 #include "chrome/browser/ui/location_bar/location_bar.h"
17 #include "chrome/browser/ui/passwords/manage_passwords_icon.h"
18 #include "chrome/browser/ui/tab_dialogs.h"
19 #include "chrome/common/url_constants.h"
20 #include "chrome/grit/generated_resources.h"
21 #include "components/password_manager/core/browser/browser_save_password_progress_logger.h"
22 #include "components/password_manager/core/browser/password_bubble_experiment.h"
23 #include "components/password_manager/core/browser/password_form_manager.h"
24 #include "components/password_manager/core/common/credential_manager_types.h"
25 #include "content/public/browser/navigation_details.h"
26 #include "ui/base/l10n/l10n_util.h"
28 #if defined(OS_ANDROID)
29 #include "chrome/browser/android/chrome_application.h"
30 #include "chrome/browser/infobars/infobar_service.h"
31 #include "chrome/browser/password_manager/account_chooser_infobar_delegate_android.h"
32 #endif
34 using autofill::PasswordFormMap;
35 using password_manager::PasswordFormManager;
37 namespace {
39 // Minimal time span the bubble should survive implicit navigations.
40 const int kBubbleMinTime = 5;
42 password_manager::PasswordStore* GetPasswordStore(
43 content::WebContents* web_contents) {
44 return PasswordStoreFactory::GetForProfile(
45 Profile::FromBrowserContext(web_contents->GetBrowserContext()),
46 ServiceAccessType::EXPLICIT_ACCESS).get();
49 } // namespace
51 DEFINE_WEB_CONTENTS_USER_DATA_KEY(ManagePasswordsUIController);
53 ManagePasswordsUIController::ManagePasswordsUIController(
54 content::WebContents* web_contents)
55 : content::WebContentsObserver(web_contents),
56 should_pop_up_bubble_(false) {
57 passwords_data_.set_client(
58 ChromePasswordManagerClient::FromWebContents(web_contents));
59 password_manager::PasswordStore* password_store =
60 GetPasswordStore(web_contents);
61 if (password_store)
62 password_store->AddObserver(this);
65 ManagePasswordsUIController::~ManagePasswordsUIController() {}
67 void ManagePasswordsUIController::UpdateBubbleAndIconVisibility() {
68 // If we're not on a "webby" URL (e.g. "chrome://sign-in"), we shouldn't
69 // display either the bubble or the icon.
70 if (!BrowsingDataHelper::IsWebScheme(
71 web_contents()->GetLastCommittedURL().scheme())) {
72 passwords_data_.OnInactive();
75 #if !defined(OS_ANDROID)
76 Browser* browser = chrome::FindBrowserWithWebContents(web_contents());
77 if (!browser)
78 return;
79 LocationBar* location_bar = browser->window()->GetLocationBar();
80 DCHECK(location_bar);
81 location_bar->UpdateManagePasswordsIconAndBubble();
82 #endif
85 void ManagePasswordsUIController::
86 UpdateAndroidAccountChooserInfoBarVisibility() {
87 #if defined(OS_ANDROID)
88 AccountChooserInfoBarDelegateAndroid::Create(
89 InfoBarService::FromWebContents(web_contents()), this);
90 should_pop_up_bubble_ = false;
91 #endif
94 base::TimeDelta ManagePasswordsUIController::Elapsed() const {
95 return timer_ ? timer_->Elapsed() : base::TimeDelta::Max();
98 void ManagePasswordsUIController::OnPasswordSubmitted(
99 scoped_ptr<PasswordFormManager> form_manager) {
100 passwords_data_.OnPendingPassword(form_manager.Pass());
101 timer_.reset(new base::ElapsedTimer);
102 base::AutoReset<bool> resetter(&should_pop_up_bubble_, true);
103 UpdateBubbleAndIconVisibility();
106 void ManagePasswordsUIController::OnUpdatePasswordSubmitted(
107 scoped_ptr<PasswordFormManager> form_manager) {
108 passwords_data_.OnUpdatePassword(form_manager.Pass());
109 timer_.reset(new base::ElapsedTimer);
110 base::AutoReset<bool> resetter(&should_pop_up_bubble_, true);
111 UpdateBubbleAndIconVisibility();
114 bool ManagePasswordsUIController::OnChooseCredentials(
115 ScopedVector<autofill::PasswordForm> local_credentials,
116 ScopedVector<autofill::PasswordForm> federated_credentials,
117 const GURL& origin,
118 base::Callback<void(const password_manager::CredentialInfo&)> callback) {
119 DCHECK_IMPLIES(local_credentials.empty(), !federated_credentials.empty());
120 passwords_data_.OnRequestCredentials(local_credentials.Pass(),
121 federated_credentials.Pass(),
122 origin);
123 base::AutoReset<bool> resetter(&should_pop_up_bubble_, true);
124 #if defined(OS_ANDROID)
125 UpdateAndroidAccountChooserInfoBarVisibility();
126 #else
127 UpdateBubbleAndIconVisibility();
128 #endif
129 if (!should_pop_up_bubble_) {
130 passwords_data_.set_credentials_callback(callback);
131 return true;
133 passwords_data_.TransitionToState(password_manager::ui::MANAGE_STATE);
134 return false;
137 void ManagePasswordsUIController::OnAutoSignin(
138 ScopedVector<autofill::PasswordForm> local_forms) {
139 DCHECK(!local_forms.empty());
140 passwords_data_.OnAutoSignin(local_forms.Pass());
141 timer_.reset(new base::ElapsedTimer);
142 base::AutoReset<bool> resetter(&should_pop_up_bubble_, true);
143 UpdateBubbleAndIconVisibility();
146 void ManagePasswordsUIController::OnAutomaticPasswordSave(
147 scoped_ptr<PasswordFormManager> form_manager) {
148 passwords_data_.OnAutomaticPasswordSave(form_manager.Pass());
149 base::AutoReset<bool> resetter(&should_pop_up_bubble_, true);
150 UpdateBubbleAndIconVisibility();
153 void ManagePasswordsUIController::OnPasswordAutofilled(
154 const PasswordFormMap& password_form_map) {
155 passwords_data_.OnPasswordAutofilled(password_form_map);
156 UpdateBubbleAndIconVisibility();
159 void ManagePasswordsUIController::OnBlacklistBlockedAutofill(
160 const PasswordFormMap& password_form_map) {
161 passwords_data_.OnInactive();
162 UpdateBubbleAndIconVisibility();
165 void ManagePasswordsUIController::OnLoginsChanged(
166 const password_manager::PasswordStoreChangeList& changes) {
167 password_manager::ui::State current_state = state();
168 passwords_data_.ProcessLoginsChanged(changes);
169 if (current_state != state())
170 UpdateBubbleAndIconVisibility();
173 void ManagePasswordsUIController::NavigateToPasswordManagerSettingsPage() {
174 #if defined(OS_ANDROID)
175 chrome::android::ChromeApplication::ShowPasswordSettings();
176 #else
177 chrome::ShowSettingsSubPage(
178 chrome::FindBrowserWithWebContents(web_contents()),
179 chrome::kPasswordManagerSubPage);
180 #endif
183 void ManagePasswordsUIController::NavigateToExternalPasswordManager() {
184 #if defined(OS_ANDROID)
185 NOTREACHED();
186 #else
187 chrome::NavigateParams params(
188 chrome::FindBrowserWithWebContents(web_contents()),
189 GURL(chrome::kPasswordManagerAccountDashboardURL),
190 ui::PAGE_TRANSITION_LINK);
191 params.disposition = NEW_FOREGROUND_TAB;
192 chrome::Navigate(&params);
193 #endif
196 void ManagePasswordsUIController::NavigateToSmartLockPage() {
197 #if defined(OS_ANDROID)
198 NOTREACHED();
199 #else
200 chrome::NavigateParams params(
201 chrome::FindBrowserWithWebContents(web_contents()),
202 GURL(l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_SMART_LOCK_PAGE)),
203 ui::PAGE_TRANSITION_LINK);
204 params.disposition = NEW_FOREGROUND_TAB;
205 chrome::Navigate(&params);
206 #endif
209 void ManagePasswordsUIController::SavePassword() {
210 DCHECK(PasswordPendingUserDecision());
211 SavePasswordInternal();
212 passwords_data_.TransitionToState(password_manager::ui::MANAGE_STATE);
213 UpdateBubbleAndIconVisibility();
216 void ManagePasswordsUIController::UpdatePassword(
217 const autofill::PasswordForm& password_form) {
218 DCHECK_EQ(password_manager::ui::PENDING_PASSWORD_UPDATE_STATE, state());
219 UpdatePasswordInternal(password_form);
220 passwords_data_.TransitionToState(password_manager::ui::MANAGE_STATE);
221 UpdateBubbleAndIconVisibility();
224 void ManagePasswordsUIController::ChooseCredential(
225 const autofill::PasswordForm& form,
226 password_manager::CredentialType credential_type) {
227 DCHECK_EQ(password_manager::ui::CREDENTIAL_REQUEST_STATE, state());
228 DCHECK(!passwords_data_.credentials_callback().is_null());
230 // Here, |credential_type| refers to whether the credential was originally
231 // passed into ::OnChooseCredentials as part of the |local_credentials| or
232 // |federated_credentials| lists (e.g. whether it is an existing credential
233 // saved for this origin, or whether we should synthesize a new
234 // FederatedCredential).
236 // If |credential_type| is federated, the credential MUST be returned as
237 // a FederatedCredential in order to prevent password information leaking
238 // cross-origin.
240 // If |credential_type| is local, the credential MIGHT be a PasswordCredential
241 // or it MIGHT be a FederatedCredential. We inspect the |federation_url|
242 // field to determine which we should return.
244 // TODO(mkwst): Clean this up. It is confusing.
245 password_manager::CredentialType type_to_return;
246 if (credential_type ==
247 password_manager::CredentialType::CREDENTIAL_TYPE_PASSWORD &&
248 form.federation_url.is_empty()) {
249 type_to_return = password_manager::CredentialType::CREDENTIAL_TYPE_PASSWORD;
250 } else if (credential_type ==
251 password_manager::CredentialType::CREDENTIAL_TYPE_EMPTY) {
252 type_to_return = password_manager::CredentialType::CREDENTIAL_TYPE_EMPTY;
253 } else {
254 type_to_return =
255 password_manager::CredentialType::CREDENTIAL_TYPE_FEDERATED;
257 password_manager::CredentialInfo info =
258 password_manager::CredentialInfo(form, type_to_return);
259 passwords_data_.credentials_callback().Run(info);
260 passwords_data_.set_credentials_callback(
261 ManagePasswordsState::CredentialsCallback());
264 void ManagePasswordsUIController::SavePasswordInternal() {
265 password_manager::PasswordFormManager* form_manager =
266 passwords_data_.form_manager();
267 form_manager->Save();
270 void ManagePasswordsUIController::UpdatePasswordInternal(
271 const autofill::PasswordForm& password_form) {
272 password_manager::PasswordFormManager* form_manager =
273 passwords_data_.form_manager();
274 form_manager->Update(password_form);
277 void ManagePasswordsUIController::NeverSavePassword() {
278 DCHECK(PasswordPendingUserDecision());
279 NeverSavePasswordInternal();
280 passwords_data_.TransitionToState(password_manager::ui::BLACKLIST_STATE);
281 UpdateBubbleAndIconVisibility();
284 void ManagePasswordsUIController::NeverSavePasswordInternal() {
285 password_manager::PasswordFormManager* form_manager =
286 passwords_data_.form_manager();
287 DCHECK(form_manager);
288 form_manager->PermanentlyBlacklist();
291 void ManagePasswordsUIController::UnblacklistSite() {
292 // We're in one of two states: either the user _just_ blacklisted the site
293 // by clicking "Never save" in the pending bubble, or the user is visiting
294 // a blacklisted site.
296 // Either way, |passwords_data_| has been populated with the relevant form. We
297 // can safely pull it out, send it over to the password store for removal, and
298 // update our internal state.
299 DCHECK(!passwords_data_.GetCurrentForms().empty());
300 DCHECK_EQ(password_manager::ui::BLACKLIST_STATE, state());
301 password_manager::PasswordStore* password_store =
302 GetPasswordStore(web_contents());
303 DCHECK(GetCurrentForms().front()->blacklisted_by_user);
304 if (password_store)
305 password_store->RemoveLogin(*GetCurrentForms().front());
306 passwords_data_.TransitionToState(password_manager::ui::MANAGE_STATE);
307 UpdateBubbleAndIconVisibility();
310 void ManagePasswordsUIController::ManageAccounts() {
311 DCHECK_EQ(password_manager::ui::AUTO_SIGNIN_STATE, state());
312 passwords_data_.TransitionToState(password_manager::ui::MANAGE_STATE);
313 base::AutoReset<bool> resetter(&should_pop_up_bubble_, true);
314 UpdateBubbleAndIconVisibility();
317 void ManagePasswordsUIController::DidNavigateMainFrame(
318 const content::LoadCommittedDetails& details,
319 const content::FrameNavigateParams& params) {
320 // Don't react to in-page (fragment) navigations.
321 if (details.is_in_page)
322 return;
324 // Don't do anything if a navigation occurs before a user could reasonably
325 // interact with the password bubble.
326 if (Elapsed() < base::TimeDelta::FromSeconds(kBubbleMinTime))
327 return;
329 // Otherwise, reset the password manager and the timer.
330 passwords_data_.OnInactive();
331 UpdateBubbleAndIconVisibility();
332 // This allows the bubble to survive several redirects in case the whole
333 // process of navigating to the landing page is longer than 1 second.
334 timer_.reset(new base::ElapsedTimer());
337 void ManagePasswordsUIController::WasHidden() {
338 #if !defined(OS_ANDROID)
339 TabDialogs::FromWebContents(web_contents())->HideManagePasswordsBubble();
340 #endif
343 const autofill::PasswordForm& ManagePasswordsUIController::
344 PendingPassword() const {
345 DCHECK(state() == password_manager::ui::PENDING_PASSWORD_STATE ||
346 state() == password_manager::ui::PENDING_PASSWORD_UPDATE_STATE ||
347 state() == password_manager::ui::CONFIRMATION_STATE)
348 << state();
349 password_manager::PasswordFormManager* form_manager =
350 passwords_data_.form_manager();
351 DCHECK(form_manager);
352 return form_manager->pending_credentials();
355 void ManagePasswordsUIController::UpdateIconAndBubbleState(
356 ManagePasswordsIcon* icon) {
357 if (should_pop_up_bubble_) {
358 // We must display the icon before showing the bubble, as the bubble would
359 // be otherwise unanchored.
360 icon->SetState(state());
361 ShowBubbleWithoutUserInteraction();
362 } else {
363 icon->SetState(state());
367 void ManagePasswordsUIController::OnBubbleShown() {
368 should_pop_up_bubble_ = false;
371 void ManagePasswordsUIController::OnBubbleHidden() {
372 if (state() == password_manager::ui::CREDENTIAL_REQUEST_STATE ||
373 state() == password_manager::ui::CONFIRMATION_STATE ||
374 state() == password_manager::ui::AUTO_SIGNIN_STATE) {
375 passwords_data_.TransitionToState(password_manager::ui::MANAGE_STATE);
376 UpdateBubbleAndIconVisibility();
380 void ManagePasswordsUIController::ShowBubbleWithoutUserInteraction() {
381 DCHECK(should_pop_up_bubble_);
382 #if !defined(OS_ANDROID)
383 Browser* browser = chrome::FindBrowserWithWebContents(web_contents());
384 if (!browser || browser->toolbar_model()->input_in_progress())
385 return;
387 CommandUpdater* updater = browser->command_controller()->command_updater();
388 updater->ExecuteCommand(IDC_MANAGE_PASSWORDS_FOR_PAGE);
389 #endif
392 void ManagePasswordsUIController::WebContentsDestroyed() {
393 password_manager::PasswordStore* password_store =
394 GetPasswordStore(web_contents());
395 if (password_store)
396 password_store->RemoveObserver(this);