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/views/passwords/manage_passwords_bubble_view.h"
7 #include "base/timer/timer.h"
8 #include "chrome/browser/profiles/profile.h"
9 #include "chrome/browser/ui/browser.h"
10 #include "chrome/browser/ui/browser_finder.h"
11 #include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
12 #include "chrome/browser/ui/passwords/manage_passwords_bubble_model.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/browser/ui/passwords/save_password_refusal_combobox_model.h"
16 #include "chrome/browser/ui/views/frame/browser_view.h"
17 #include "chrome/browser/ui/views/passwords/credentials_item_view.h"
18 #include "chrome/browser/ui/views/passwords/manage_credential_item_view.h"
19 #include "chrome/browser/ui/views/passwords/manage_password_items_view.h"
20 #include "chrome/browser/ui/views/passwords/manage_passwords_icon_view.h"
21 #include "chrome/browser/ui/views/passwords/save_account_more_combobox_model.h"
22 #include "chrome/grit/generated_resources.h"
23 #include "content/public/browser/render_view_host.h"
24 #include "ui/base/l10n/l10n_util.h"
25 #include "ui/base/resource/resource_bundle.h"
26 #include "ui/views/controls/button/blue_button.h"
27 #include "ui/views/controls/button/label_button.h"
28 #include "ui/views/controls/combobox/combobox.h"
29 #include "ui/views/controls/combobox/combobox_listener.h"
30 #include "ui/views/controls/link.h"
31 #include "ui/views/controls/link_listener.h"
32 #include "ui/views/controls/separator.h"
33 #include "ui/views/controls/styled_label.h"
34 #include "ui/views/controls/styled_label_listener.h"
35 #include "ui/views/event_monitor.h"
36 #include "ui/views/layout/fill_layout.h"
37 #include "ui/views/layout/grid_layout.h"
38 #include "ui/views/layout/layout_constants.h"
39 #include "ui/views/widget/widget.h"
41 int ManagePasswordsBubbleView::auto_signin_toast_timeout_
= 3;
43 // Helpers --------------------------------------------------------------------
47 const int kDesiredBubbleWidth
= 370;
50 // | | (FILL, FILL) | |
51 // Used for the bubble's header, the credentials list, and for simple
52 // messages like "No passwords".
53 SINGLE_VIEW_COLUMN_SET
,
55 // | | (TRAILING, CENTER) | | (TRAILING, CENTER) | |
56 // Used for buttons at the bottom of the bubble which should nest at the
57 // bottom-right corner.
58 DOUBLE_BUTTON_COLUMN_SET
,
60 // | | (LEADING, CENTER) | | (TRAILING, CENTER) | |
61 // Used for buttons at the bottom of the bubble which should occupy
63 LINK_BUTTON_COLUMN_SET
,
65 // | | (TRAILING, CENTER) | |
66 // Used when there is only one button which should next at the bottom-right
68 SINGLE_BUTTON_COLUMN_SET
,
70 // | | (LEADING, CENTER) | | (TRAILING, CENTER) | | (TRAILING, CENTER) | |
71 // Used when there are three buttons.
72 TRIPLE_BUTTON_COLUMN_SET
,
75 enum TextRowType
{ ROW_SINGLE
, ROW_MULTILINE
};
77 // Construct an appropriate ColumnSet for the given |type|, and add it
79 void BuildColumnSet(views::GridLayout
* layout
, ColumnSetType type
) {
80 views::ColumnSet
* column_set
= layout
->AddColumnSet(type
);
81 column_set
->AddPaddingColumn(0, views::kPanelHorizMargin
);
82 int full_width
= kDesiredBubbleWidth
- (2 * views::kPanelHorizMargin
);
84 case SINGLE_VIEW_COLUMN_SET
:
85 column_set
->AddColumn(views::GridLayout::FILL
,
86 views::GridLayout::FILL
,
88 views::GridLayout::FIXED
,
93 case DOUBLE_BUTTON_COLUMN_SET
:
94 column_set
->AddColumn(views::GridLayout::TRAILING
,
95 views::GridLayout::CENTER
,
97 views::GridLayout::USE_PREF
,
100 column_set
->AddPaddingColumn(0, views::kRelatedButtonHSpacing
);
101 column_set
->AddColumn(views::GridLayout::TRAILING
,
102 views::GridLayout::CENTER
,
104 views::GridLayout::USE_PREF
,
108 case LINK_BUTTON_COLUMN_SET
:
109 column_set
->AddColumn(views::GridLayout::LEADING
,
110 views::GridLayout::CENTER
,
112 views::GridLayout::USE_PREF
,
115 column_set
->AddPaddingColumn(0, views::kRelatedButtonHSpacing
);
116 column_set
->AddColumn(views::GridLayout::TRAILING
,
117 views::GridLayout::CENTER
,
119 views::GridLayout::USE_PREF
,
123 case SINGLE_BUTTON_COLUMN_SET
:
124 column_set
->AddColumn(views::GridLayout::TRAILING
,
125 views::GridLayout::CENTER
,
127 views::GridLayout::USE_PREF
,
130 case TRIPLE_BUTTON_COLUMN_SET
:
131 column_set
->AddColumn(views::GridLayout::LEADING
,
132 views::GridLayout::CENTER
,
134 views::GridLayout::USE_PREF
,
137 column_set
->AddPaddingColumn(0, views::kRelatedButtonHSpacing
);
138 column_set
->AddColumn(views::GridLayout::TRAILING
,
139 views::GridLayout::CENTER
,
141 views::GridLayout::USE_PREF
,
144 column_set
->AddPaddingColumn(0, views::kRelatedButtonHSpacing
);
145 column_set
->AddColumn(views::GridLayout::TRAILING
,
146 views::GridLayout::CENTER
,
148 views::GridLayout::USE_PREF
,
153 column_set
->AddPaddingColumn(0, views::kPanelHorizMargin
);
156 // Given a layout and a model, add an appropriate title using a
157 // SINGLE_VIEW_COLUMN_SET, followed by a spacer row.
158 void AddTitleRow(views::GridLayout
* layout
, ManagePasswordsBubbleModel
* model
) {
159 views::Label
* title_label
= new views::Label(model
->title());
160 title_label
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
161 title_label
->SetMultiLine(true);
162 title_label
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
163 ui::ResourceBundle::MediumFont
));
165 // Add the title to the layout with appropriate padding.
166 layout
->StartRowWithPadding(
167 0, SINGLE_VIEW_COLUMN_SET
, 0, views::kRelatedControlSmallVerticalSpacing
);
168 layout
->AddView(title_label
);
169 layout
->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing
);
175 // ManagePasswordsBubbleView::AccountChooserView ------------------------------
177 // A view offering the user the ability to choose credentials for
178 // authentication. Contains a list of CredentialsItemView, along with a
180 class ManagePasswordsBubbleView::AccountChooserView
181 : public views::View
,
182 public views::ButtonListener
{
184 explicit AccountChooserView(ManagePasswordsBubbleView
* parent
);
185 ~AccountChooserView() override
;
188 // views::ButtonListener:
189 void ButtonPressed(views::Button
* sender
, const ui::Event
& event
) override
;
191 // Adds |password_forms| to the layout remembering their |type|.
192 void AddCredentialItemsWithType(
193 views::GridLayout
* layout
,
194 const ScopedVector
<const autofill::PasswordForm
>& password_forms
,
195 password_manager::CredentialType type
);
197 ManagePasswordsBubbleView
* parent_
;
198 views::LabelButton
* cancel_button_
;
200 DISALLOW_COPY_AND_ASSIGN(AccountChooserView
);
203 ManagePasswordsBubbleView::AccountChooserView::AccountChooserView(
204 ManagePasswordsBubbleView
* parent
)
206 views::GridLayout
* layout
= new views::GridLayout(this);
207 SetLayoutManager(layout
);
210 new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_CANCEL
));
211 cancel_button_
->SetStyle(views::Button::STYLE_BUTTON
);
212 cancel_button_
->SetFontList(
213 ui::ResourceBundle::GetSharedInstance().GetFontList(
214 ui::ResourceBundle::SmallFont
));
217 BuildColumnSet(layout
, SINGLE_VIEW_COLUMN_SET
);
218 AddTitleRow(layout
, parent_
->model());
220 AddCredentialItemsWithType(
221 layout
, parent_
->model()->local_credentials(),
222 password_manager::CredentialType::CREDENTIAL_TYPE_LOCAL
);
224 AddCredentialItemsWithType(
225 layout
, parent_
->model()->federated_credentials(),
226 password_manager::CredentialType::CREDENTIAL_TYPE_FEDERATED
);
229 BuildColumnSet(layout
, SINGLE_BUTTON_COLUMN_SET
);
230 layout
->StartRowWithPadding(
231 0, SINGLE_BUTTON_COLUMN_SET
, 0, views::kRelatedControlVerticalSpacing
);
232 layout
->AddView(cancel_button_
);
234 // Extra padding for visual awesomeness.
235 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
237 parent_
->set_initially_focused_view(cancel_button_
);
240 ManagePasswordsBubbleView::AccountChooserView::~AccountChooserView() {
243 void ManagePasswordsBubbleView::AccountChooserView::AddCredentialItemsWithType(
244 views::GridLayout
* layout
,
245 const ScopedVector
<const autofill::PasswordForm
>& password_forms
,
246 password_manager::CredentialType type
) {
247 net::URLRequestContextGetter
* request_context
=
248 parent_
->model()->GetProfile()->GetRequestContext();
249 for (const autofill::PasswordForm
* form
: password_forms
) {
250 // Add the title to the layout with appropriate padding.
251 layout
->StartRow(0, SINGLE_VIEW_COLUMN_SET
);
252 layout
->AddView(new CredentialsItemView(
253 this, form
, type
, CredentialsItemView::ACCOUNT_CHOOSER
,
258 void ManagePasswordsBubbleView::AccountChooserView::ButtonPressed(
259 views::Button
* sender
, const ui::Event
& event
) {
260 if (sender
!= cancel_button_
) {
261 // ManagePasswordsBubbleModel should care about calling a callback in case
262 // the bubble is dismissed by any other means.
263 CredentialsItemView
* view
= static_cast<CredentialsItemView
*>(sender
);
264 parent_
->model()->OnChooseCredentials(*view
->form(),
265 view
->credential_type());
267 parent_
->model()->OnNopeClicked();
272 // ManagePasswordsBubbleView::AutoSigninView ----------------------------------
274 // A view containing just one credential that was used for for automatic signing
276 class ManagePasswordsBubbleView::AutoSigninView
277 : public views::View
,
278 public views::ButtonListener
,
279 public views::WidgetObserver
{
281 explicit AutoSigninView(ManagePasswordsBubbleView
* parent
);
284 // views::ButtonListener:
285 void ButtonPressed(views::Button
* sender
, const ui::Event
& event
) override
;
287 // views::WidgetObserver:
288 // Tracks the state of the browser window.
289 void OnWidgetActivationChanged(views::Widget
* widget
, bool active
) override
;
290 void OnWidgetClosing(views::Widget
* widget
) override
;
293 static base::TimeDelta
GetTimeout() {
294 return base::TimeDelta::FromSeconds(
295 ManagePasswordsBubbleView::auto_signin_toast_timeout_
);
298 base::OneShotTimer
<AutoSigninView
> timer_
;
299 ManagePasswordsBubbleView
* parent_
;
300 ScopedObserver
<views::Widget
, views::WidgetObserver
> observed_browser_
;
302 DISALLOW_COPY_AND_ASSIGN(AutoSigninView
);
305 ManagePasswordsBubbleView::AutoSigninView::AutoSigninView(
306 ManagePasswordsBubbleView
* parent
)
308 observed_browser_(this) {
309 SetLayoutManager(new views::FillLayout
);
310 CredentialsItemView
* credential
= new CredentialsItemView(
312 &parent_
->model()->pending_password(),
313 password_manager::CredentialType::CREDENTIAL_TYPE_LOCAL
,
314 CredentialsItemView::AUTO_SIGNIN
,
315 parent_
->model()->GetProfile()->GetRequestContext());
316 AddChildView(credential
);
317 parent_
->set_initially_focused_view(credential
);
320 chrome::FindBrowserWithWebContents(parent_
->web_contents());
322 BrowserView
* browser_view
= BrowserView::GetBrowserViewForBrowser(browser
);
323 observed_browser_
.Add(browser_view
->GetWidget());
325 if (browser_view
->IsActive())
326 timer_
.Start(FROM_HERE
, GetTimeout(), this, &AutoSigninView::OnTimer
);
329 void ManagePasswordsBubbleView::AutoSigninView::ButtonPressed(
330 views::Button
* sender
, const ui::Event
& event
) {
331 parent_
->model()->OnAutoSignInClicked();
335 void ManagePasswordsBubbleView::AutoSigninView::OnWidgetActivationChanged(
336 views::Widget
* widget
, bool active
) {
337 if (active
&& !timer_
.IsRunning())
338 timer_
.Start(FROM_HERE
, GetTimeout(), this, &AutoSigninView::OnTimer
);
341 void ManagePasswordsBubbleView::AutoSigninView::OnWidgetClosing(
342 views::Widget
* widget
) {
343 observed_browser_
.RemoveAll();
346 void ManagePasswordsBubbleView::AutoSigninView::OnTimer() {
347 parent_
->model()->OnAutoSignInToastTimeout();
351 // ManagePasswordsBubbleView::PendingView -------------------------------------
353 // A view offering the user the ability to save credentials. Contains a
354 // single ManagePasswordItemsView, along with a "Save Passwords" button
355 // and a rejection combobox.
356 class ManagePasswordsBubbleView::PendingView
: public views::View
,
357 public views::ButtonListener
,
358 public views::ComboboxListener
{
360 explicit PendingView(ManagePasswordsBubbleView
* parent
);
361 ~PendingView() override
;
364 // views::ButtonListener:
365 void ButtonPressed(views::Button
* sender
, const ui::Event
& event
) override
;
367 // Handles the event when the user changes an index of a combobox.
368 void OnPerformAction(views::Combobox
* source
) override
;
370 ManagePasswordsBubbleView
* parent_
;
372 views::BlueButton
* save_button_
;
374 // The combobox doesn't take ownership of its model. If we created a
375 // combobox we need to ensure that we delete the model here, and because the
376 // combobox uses the model in it's destructor, we need to make sure we
377 // delete the model _after_ the combobox itself is deleted.
378 scoped_ptr
<SavePasswordRefusalComboboxModel
> combobox_model_
;
379 scoped_ptr
<views::Combobox
> refuse_combobox_
;
381 DISALLOW_COPY_AND_ASSIGN(PendingView
);
384 ManagePasswordsBubbleView::PendingView::PendingView(
385 ManagePasswordsBubbleView
* parent
)
387 views::GridLayout
* layout
= new views::GridLayout(this);
388 layout
->set_minimum_size(gfx::Size(kDesiredBubbleWidth
, 0));
389 SetLayoutManager(layout
);
391 std::vector
<const autofill::PasswordForm
*> credentials(
392 1, &parent
->model()->pending_password());
393 // Create the pending credential item, save button and refusal combobox.
394 ManagePasswordItemsView
* item
=
395 new ManagePasswordItemsView(parent_
->model(), credentials
);
396 save_button_
= new views::BlueButton(
397 this, l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_SAVE_BUTTON
));
398 save_button_
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
399 ui::ResourceBundle::SmallFont
));
401 combobox_model_
.reset(new SavePasswordRefusalComboboxModel(
402 password_bubble_experiment::ShouldShowNeverForThisSiteDefault(
403 parent_
->model()->GetProfile()->GetPrefs())));
404 refuse_combobox_
.reset(new views::Combobox(combobox_model_
.get()));
405 refuse_combobox_
->set_listener(this);
406 refuse_combobox_
->SetStyle(views::Combobox::STYLE_ACTION
);
407 // TODO(mkwst): Need a mechanism to pipe a font list down into a combobox.
410 BuildColumnSet(layout
, SINGLE_VIEW_COLUMN_SET
);
411 AddTitleRow(layout
, parent_
->model());
414 layout
->StartRow(0, SINGLE_VIEW_COLUMN_SET
);
415 layout
->AddView(item
);
418 BuildColumnSet(layout
, DOUBLE_BUTTON_COLUMN_SET
);
419 layout
->StartRowWithPadding(
420 0, DOUBLE_BUTTON_COLUMN_SET
, 0, views::kRelatedControlVerticalSpacing
);
421 layout
->AddView(save_button_
);
422 layout
->AddView(refuse_combobox_
.get());
424 // Extra padding for visual awesomeness.
425 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
427 parent_
->set_initially_focused_view(save_button_
);
430 ManagePasswordsBubbleView::PendingView::~PendingView() {
433 void ManagePasswordsBubbleView::PendingView::ButtonPressed(
434 views::Button
* sender
,
435 const ui::Event
& event
) {
436 DCHECK(sender
== save_button_
);
437 parent_
->model()->OnSaveClicked();
441 void ManagePasswordsBubbleView::PendingView::OnPerformAction(
442 views::Combobox
* source
) {
443 DCHECK_EQ(source
, refuse_combobox_
);
444 if (source
->selected_index() == combobox_model_
->index_nope()) {
445 parent_
->model()->OnNopeClicked();
447 } else if (source
->selected_index() == combobox_model_
->index_never()) {
448 parent_
->NotifyNeverForThisSiteClicked();
454 // ManagePasswordsBubbleView::SaveAccountView ---------------------------------
456 // A view offering the user the ability to save credentials. Contains 2 buttons
457 // and a "More" combobox.
458 class ManagePasswordsBubbleView::SaveAccountView
459 : public views::View
,
460 public views::ButtonListener
,
461 public views::ComboboxListener
{
463 explicit SaveAccountView(ManagePasswordsBubbleView
* parent
);
466 // views::ButtonListener:
467 void ButtonPressed(views::Button
* sender
, const ui::Event
& event
) override
;
469 // Handles the event when the user changes an index of a combobox.
470 void OnPerformAction(views::Combobox
* source
) override
;
472 ManagePasswordsBubbleView
* parent_
;
474 views::BlueButton
* save_button_
;
475 views::LabelButton
* no_button_
;
477 // The combobox doesn't take ownership of its model. If we created a
478 // combobox we need to ensure that we delete the model here, and because the
479 // combobox uses the model in it's destructor, we need to make sure we
480 // delete the model _after_ the combobox itself is deleted.
481 SaveAccountMoreComboboxModel combobox_model_
;
482 scoped_ptr
<views::Combobox
> more_combobox_
;
485 ManagePasswordsBubbleView::SaveAccountView::SaveAccountView(
486 ManagePasswordsBubbleView
* parent
)
488 DCHECK(parent_
->model()->IsNewUIActive());
489 views::GridLayout
* layout
= new views::GridLayout(this);
490 layout
->set_minimum_size(gfx::Size(kDesiredBubbleWidth
, 0));
491 SetLayoutManager(layout
);
493 save_button_
= new views::BlueButton(
495 l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_SAVE_ACCOUNT_BUTTON
));
496 save_button_
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
497 ui::ResourceBundle::SmallFont
));
499 no_button_
= new views::LabelButton(this, l10n_util::GetStringUTF16(
500 IDS_PASSWORD_MANAGER_SAVE_PASSWORD_SMART_LOCK_NO_THANKS_BUTTON
));
501 no_button_
->SetStyle(views::Button::STYLE_BUTTON
);
502 no_button_
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
503 ui::ResourceBundle::SmallFont
));
505 more_combobox_
.reset(new views::Combobox(&combobox_model_
));
506 more_combobox_
->set_owned_by_client();
507 more_combobox_
->set_listener(this);
508 more_combobox_
->SetStyle(views::Combobox::STYLE_ACTION
);
511 BuildColumnSet(layout
, SINGLE_VIEW_COLUMN_SET
);
512 AddTitleRow(layout
, parent_
->model());
515 BuildColumnSet(layout
, TRIPLE_BUTTON_COLUMN_SET
);
516 layout
->StartRow(0, TRIPLE_BUTTON_COLUMN_SET
);
517 layout
->AddView(more_combobox_
.get());
518 layout
->AddView(save_button_
);
519 layout
->AddView(no_button_
);
520 // Extra padding for visual awesomeness.
521 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
523 parent_
->set_initially_focused_view(save_button_
);
526 void ManagePasswordsBubbleView::SaveAccountView::ButtonPressed(
527 views::Button
* sender
,
528 const ui::Event
& event
) {
529 if (sender
== save_button_
)
530 parent_
->model()->OnSaveClicked();
531 else if (sender
== no_button_
)
532 parent_
->model()->OnNopeClicked();
539 void ManagePasswordsBubbleView::SaveAccountView::OnPerformAction(
540 views::Combobox
* source
) {
541 DCHECK_EQ(source
, more_combobox_
);
542 switch (more_combobox_
->selected_index()) {
543 case SaveAccountMoreComboboxModel::INDEX_MORE
:
545 case SaveAccountMoreComboboxModel::INDEX_NEVER_FOR_THIS_SITE
:
546 parent_
->NotifyNeverForThisSiteClicked();
548 case SaveAccountMoreComboboxModel::INDEX_SETTINGS
:
549 parent_
->model()->OnManageLinkClicked();
555 // ManagePasswordsBubbleView::ConfirmNeverView --------------------------------
557 // A view offering the user the ability to undo her decision to never save
558 // passwords for a particular site.
559 class ManagePasswordsBubbleView::ConfirmNeverView
560 : public views::View
,
561 public views::ButtonListener
{
563 explicit ConfirmNeverView(ManagePasswordsBubbleView
* parent
);
564 ~ConfirmNeverView() override
;
567 // views::ButtonListener:
568 void ButtonPressed(views::Button
* sender
, const ui::Event
& event
) override
;
570 ManagePasswordsBubbleView
* parent_
;
572 views::LabelButton
* confirm_button_
;
573 views::LabelButton
* undo_button_
;
575 DISALLOW_COPY_AND_ASSIGN(ConfirmNeverView
);
578 ManagePasswordsBubbleView::ConfirmNeverView::ConfirmNeverView(
579 ManagePasswordsBubbleView
* parent
)
581 views::GridLayout
* layout
= new views::GridLayout(this);
582 layout
->set_minimum_size(gfx::Size(kDesiredBubbleWidth
, 0));
583 SetLayoutManager(layout
);
586 BuildColumnSet(layout
, SINGLE_VIEW_COLUMN_SET
);
587 AddTitleRow(layout
, parent_
->model());
589 // Confirmation text.
590 views::Label
* confirmation
= new views::Label(l10n_util::GetStringUTF16(
591 IDS_MANAGE_PASSWORDS_BLACKLIST_CONFIRMATION_TEXT
));
592 confirmation
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
593 confirmation
->SetMultiLine(true);
594 confirmation
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
595 ui::ResourceBundle::SmallFont
));
596 layout
->StartRow(0, SINGLE_VIEW_COLUMN_SET
);
597 layout
->AddView(confirmation
);
598 layout
->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing
);
600 // Confirm and undo buttons.
601 BuildColumnSet(layout
, DOUBLE_BUTTON_COLUMN_SET
);
602 layout
->StartRowWithPadding(
603 0, DOUBLE_BUTTON_COLUMN_SET
, 0, views::kRelatedControlVerticalSpacing
);
605 confirm_button_
= new views::LabelButton(
607 l10n_util::GetStringUTF16(
608 IDS_MANAGE_PASSWORDS_BLACKLIST_CONFIRMATION_BUTTON
));
609 confirm_button_
->SetStyle(views::Button::STYLE_BUTTON
);
610 confirm_button_
->SetFontList(
611 ui::ResourceBundle::GetSharedInstance().GetFontList(
612 ui::ResourceBundle::SmallFont
));
613 layout
->AddView(confirm_button_
);
616 new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_CANCEL
));
617 undo_button_
->SetStyle(views::Button::STYLE_BUTTON
);
618 undo_button_
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
619 ui::ResourceBundle::SmallFont
));
620 layout
->AddView(undo_button_
);
622 // Extra padding for visual awesomeness.
623 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
625 parent_
->set_initially_focused_view(confirm_button_
);
628 ManagePasswordsBubbleView::ConfirmNeverView::~ConfirmNeverView() {
631 void ManagePasswordsBubbleView::ConfirmNeverView::ButtonPressed(
632 views::Button
* sender
,
633 const ui::Event
& event
) {
634 DCHECK(sender
== confirm_button_
|| sender
== undo_button_
);
635 if (sender
== confirm_button_
)
636 parent_
->NotifyConfirmedNeverForThisSite();
638 parent_
->NotifyUndoNeverForThisSite();
641 // ManagePasswordsBubbleView::ManageView --------------------------------------
643 // A view offering the user a list of her currently saved credentials
644 // for the current page, along with a "Manage passwords" link and a
646 class ManagePasswordsBubbleView::ManageView
: public views::View
,
647 public views::ButtonListener
,
648 public views::LinkListener
{
650 explicit ManageView(ManagePasswordsBubbleView
* parent
);
651 ~ManageView() override
;
654 // views::ButtonListener:
655 void ButtonPressed(views::Button
* sender
, const ui::Event
& event
) override
;
657 // views::LinkListener:
658 void LinkClicked(views::Link
* source
, int event_flags
) override
;
660 ManagePasswordsBubbleView
* parent_
;
662 views::Link
* manage_link_
;
663 views::LabelButton
* done_button_
;
665 DISALLOW_COPY_AND_ASSIGN(ManageView
);
668 ManagePasswordsBubbleView::ManageView::ManageView(
669 ManagePasswordsBubbleView
* parent
)
671 views::GridLayout
* layout
= new views::GridLayout(this);
672 layout
->set_minimum_size(gfx::Size(kDesiredBubbleWidth
, 0));
673 SetLayoutManager(layout
);
676 BuildColumnSet(layout
, SINGLE_VIEW_COLUMN_SET
);
677 AddTitleRow(layout
, parent_
->model());
679 // If we have a list of passwords to store for the current site, display
680 // them to the user for management. Otherwise, render a "No passwords for
681 // this site" message.
682 if (!parent_
->model()->local_credentials().empty()) {
683 ManagePasswordItemsView
* item
= new ManagePasswordItemsView(
684 parent_
->model(), parent_
->model()->local_credentials().get());
685 layout
->StartRow(0, SINGLE_VIEW_COLUMN_SET
);
686 layout
->AddView(item
);
688 views::Label
* empty_label
= new views::Label(
689 l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_NO_PASSWORDS
));
690 empty_label
->SetMultiLine(true);
691 empty_label
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
692 empty_label
->SetFontList(
693 ui::ResourceBundle::GetSharedInstance().GetFontList(
694 ui::ResourceBundle::SmallFont
));
696 layout
->StartRow(0, SINGLE_VIEW_COLUMN_SET
);
697 layout
->AddView(empty_label
);
698 layout
->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing
);
701 // Then add the "manage passwords" link and "Done" button.
702 manage_link_
= new views::Link(parent_
->model()->manage_link());
703 manage_link_
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
704 manage_link_
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
705 ui::ResourceBundle::SmallFont
));
706 manage_link_
->SetUnderline(false);
707 manage_link_
->set_listener(this);
710 new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_DONE
));
711 done_button_
->SetStyle(views::Button::STYLE_BUTTON
);
712 done_button_
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
713 ui::ResourceBundle::SmallFont
));
715 BuildColumnSet(layout
, LINK_BUTTON_COLUMN_SET
);
716 layout
->StartRowWithPadding(
717 0, LINK_BUTTON_COLUMN_SET
, 0, views::kRelatedControlVerticalSpacing
);
718 layout
->AddView(manage_link_
);
719 layout
->AddView(done_button_
);
721 // Extra padding for visual awesomeness.
722 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
724 parent_
->set_initially_focused_view(done_button_
);
727 ManagePasswordsBubbleView::ManageView::~ManageView() {
730 void ManagePasswordsBubbleView::ManageView::ButtonPressed(
731 views::Button
* sender
,
732 const ui::Event
& event
) {
733 DCHECK(sender
== done_button_
);
734 parent_
->model()->OnDoneClicked();
738 void ManagePasswordsBubbleView::ManageView::LinkClicked(views::Link
* source
,
740 DCHECK_EQ(source
, manage_link_
);
741 parent_
->model()->OnManageLinkClicked();
745 // ManagePasswordsBubbleView::ManageAccountsView ------------------------------
747 // A view offering the user a list of his currently saved through the Credential
748 // Manager API accounts for the current page.
749 class ManagePasswordsBubbleView::ManageAccountsView
750 : public views::View
,
751 public views::ButtonListener
,
752 public views::LinkListener
{
754 explicit ManageAccountsView(ManagePasswordsBubbleView
* parent
);
757 // views::ButtonListener:
758 void ButtonPressed(views::Button
* sender
, const ui::Event
& event
) override
;
760 // views::LinkListener:
761 void LinkClicked(views::Link
* source
, int event_flags
) override
;
763 ManagePasswordsBubbleView
* parent_
;
765 views::Link
* manage_link_
;
766 views::LabelButton
* done_button_
;
768 DISALLOW_COPY_AND_ASSIGN(ManageAccountsView
);
771 ManagePasswordsBubbleView::ManageAccountsView::ManageAccountsView(
772 ManagePasswordsBubbleView
* parent
)
774 views::GridLayout
* layout
= new views::GridLayout(this);
775 layout
->set_minimum_size(gfx::Size(kDesiredBubbleWidth
, 0));
776 SetLayoutManager(layout
);
779 BuildColumnSet(layout
, SINGLE_VIEW_COLUMN_SET
);
780 AddTitleRow(layout
, parent_
->model());
782 if (!parent_
->model()->local_credentials().empty()) {
783 for (const autofill::PasswordForm
* form
:
784 parent_
->model()->local_credentials()) {
785 layout
->StartRow(0, SINGLE_VIEW_COLUMN_SET
);
786 layout
->AddView(new ManageCredentialItemView(parent_
->model(), form
));
789 views::Label
* empty_label
= new views::Label(
790 l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_NO_PASSWORDS
),
791 ui::ResourceBundle::GetSharedInstance().GetFontList(
792 ui::ResourceBundle::SmallFont
));
793 empty_label
->SetMultiLine(true);
794 empty_label
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
796 layout
->StartRow(0, SINGLE_VIEW_COLUMN_SET
);
797 layout
->AddView(empty_label
);
798 layout
->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing
);
801 // Then add the "manage passwords" link and "Done" button.
802 manage_link_
= new views::Link(parent_
->model()->manage_link());
803 manage_link_
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
804 manage_link_
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
805 ui::ResourceBundle::SmallFont
));
806 manage_link_
->SetUnderline(false);
807 manage_link_
->set_listener(this);
810 new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_DONE
));
811 done_button_
->SetStyle(views::Button::STYLE_BUTTON
);
812 done_button_
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
813 ui::ResourceBundle::SmallFont
));
815 BuildColumnSet(layout
, LINK_BUTTON_COLUMN_SET
);
816 layout
->StartRowWithPadding(
817 0, LINK_BUTTON_COLUMN_SET
, 0, views::kRelatedControlVerticalSpacing
);
818 layout
->AddView(manage_link_
);
819 layout
->AddView(done_button_
);
821 // Extra padding for visual awesomeness.
822 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
824 parent_
->set_initially_focused_view(done_button_
);
827 void ManagePasswordsBubbleView::ManageAccountsView::ButtonPressed(
828 views::Button
* sender
, const ui::Event
& event
) {
829 DCHECK(sender
== done_button_
);
830 parent_
->model()->OnDoneClicked();
834 void ManagePasswordsBubbleView::ManageAccountsView::LinkClicked(
835 views::Link
* source
, int event_flags
) {
836 DCHECK_EQ(source
, manage_link_
);
837 parent_
->model()->OnManageLinkClicked();
841 // ManagePasswordsBubbleView::BlacklistedView ---------------------------------
843 // A view offering the user the ability to re-enable the password manager for
844 // a specific site after she's decided to "never save passwords".
845 class ManagePasswordsBubbleView::BlacklistedView
846 : public views::View
,
847 public views::ButtonListener
{
849 explicit BlacklistedView(ManagePasswordsBubbleView
* parent
);
850 ~BlacklistedView() override
;
853 // views::ButtonListener:
854 void ButtonPressed(views::Button
* sender
, const ui::Event
& event
) override
;
856 ManagePasswordsBubbleView
* parent_
;
858 views::BlueButton
* unblacklist_button_
;
859 views::LabelButton
* done_button_
;
861 DISALLOW_COPY_AND_ASSIGN(BlacklistedView
);
864 ManagePasswordsBubbleView::BlacklistedView::BlacklistedView(
865 ManagePasswordsBubbleView
* parent
)
867 views::GridLayout
* layout
= new views::GridLayout(this);
868 layout
->set_minimum_size(gfx::Size(kDesiredBubbleWidth
, 0));
869 SetLayoutManager(layout
);
872 BuildColumnSet(layout
, SINGLE_VIEW_COLUMN_SET
);
873 AddTitleRow(layout
, parent_
->model());
875 // Add the "Hey! You blacklisted this site!" text.
876 views::Label
* blacklisted
= new views::Label(
877 l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_BLACKLISTED
));
878 blacklisted
->SetMultiLine(true);
879 blacklisted
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
880 blacklisted
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
881 ui::ResourceBundle::SmallFont
));
882 layout
->StartRow(0, SINGLE_VIEW_COLUMN_SET
);
883 layout
->AddView(blacklisted
);
884 layout
->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing
);
886 // Then add the "enable password manager" and "Done" buttons.
887 unblacklist_button_
= new views::BlueButton(
888 this, l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_UNBLACKLIST_BUTTON
));
889 unblacklist_button_
->SetFontList(
890 ui::ResourceBundle::GetSharedInstance().GetFontList(
891 ui::ResourceBundle::SmallFont
));
893 new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_DONE
));
894 done_button_
->SetStyle(views::Button::STYLE_BUTTON
);
895 done_button_
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
896 ui::ResourceBundle::SmallFont
));
898 BuildColumnSet(layout
, DOUBLE_BUTTON_COLUMN_SET
);
899 layout
->StartRowWithPadding(
900 0, DOUBLE_BUTTON_COLUMN_SET
, 0, views::kRelatedControlVerticalSpacing
);
901 layout
->AddView(unblacklist_button_
);
902 layout
->AddView(done_button_
);
904 // Extra padding for visual awesomeness.
905 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
907 parent_
->set_initially_focused_view(unblacklist_button_
);
910 ManagePasswordsBubbleView::BlacklistedView::~BlacklistedView() {
913 void ManagePasswordsBubbleView::BlacklistedView::ButtonPressed(
914 views::Button
* sender
,
915 const ui::Event
& event
) {
916 if (sender
== done_button_
)
917 parent_
->model()->OnDoneClicked();
918 else if (sender
== unblacklist_button_
)
919 parent_
->model()->OnUnblacklistClicked();
925 // ManagePasswordsBubbleView::SaveConfirmationView ----------------------------
927 // A view confirming to the user that a password was saved and offering a link
928 // to the Google account manager.
929 class ManagePasswordsBubbleView::SaveConfirmationView
930 : public views::View
,
931 public views::ButtonListener
,
932 public views::StyledLabelListener
{
934 explicit SaveConfirmationView(ManagePasswordsBubbleView
* parent
);
935 ~SaveConfirmationView() override
;
938 // views::ButtonListener:
939 void ButtonPressed(views::Button
* sender
, const ui::Event
& event
) override
;
941 // views::StyledLabelListener implementation
942 void StyledLabelLinkClicked(const gfx::Range
& range
,
943 int event_flags
) override
;
945 ManagePasswordsBubbleView
* parent_
;
946 views::LabelButton
* ok_button_
;
948 DISALLOW_COPY_AND_ASSIGN(SaveConfirmationView
);
951 ManagePasswordsBubbleView::SaveConfirmationView::SaveConfirmationView(
952 ManagePasswordsBubbleView
* parent
)
954 views::GridLayout
* layout
= new views::GridLayout(this);
955 layout
->set_minimum_size(gfx::Size(kDesiredBubbleWidth
, 0));
956 SetLayoutManager(layout
);
958 BuildColumnSet(layout
, SINGLE_VIEW_COLUMN_SET
);
959 AddTitleRow(layout
, parent_
->model());
961 views::StyledLabel
* confirmation
=
962 new views::StyledLabel(parent_
->model()->save_confirmation_text(), this);
963 confirmation
->SetBaseFontList(
964 ui::ResourceBundle::GetSharedInstance().GetFontList(
965 ui::ResourceBundle::SmallFont
));
966 confirmation
->AddStyleRange(
967 parent_
->model()->save_confirmation_link_range(),
968 views::StyledLabel::RangeStyleInfo::CreateForLink());
970 layout
->StartRow(0, SINGLE_VIEW_COLUMN_SET
);
971 layout
->AddView(confirmation
);
973 ok_button_
= new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_OK
));
974 ok_button_
->SetStyle(views::Button::STYLE_BUTTON
);
975 ok_button_
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
976 ui::ResourceBundle::SmallFont
));
978 BuildColumnSet(layout
, SINGLE_BUTTON_COLUMN_SET
);
979 layout
->StartRowWithPadding(
980 0, SINGLE_BUTTON_COLUMN_SET
, 0, views::kRelatedControlVerticalSpacing
);
981 layout
->AddView(ok_button_
);
983 // Extra padding for visual awesomeness.
984 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
986 parent_
->set_initially_focused_view(ok_button_
);
989 ManagePasswordsBubbleView::SaveConfirmationView::~SaveConfirmationView() {
992 void ManagePasswordsBubbleView::SaveConfirmationView::StyledLabelLinkClicked(
993 const gfx::Range
& range
, int event_flags
) {
994 DCHECK_EQ(range
, parent_
->model()->save_confirmation_link_range());
995 parent_
->model()->OnManageLinkClicked();
999 void ManagePasswordsBubbleView::SaveConfirmationView::ButtonPressed(
1000 views::Button
* sender
, const ui::Event
& event
) {
1001 DCHECK_EQ(sender
, ok_button_
);
1002 parent_
->model()->OnOKClicked();
1006 // ManagePasswordsBubbleView::WebContentMouseHandler --------------------------
1008 // The class listens for WebContentsView events and notifies the bubble if the
1009 // view was clicked on or received keystrokes.
1010 class ManagePasswordsBubbleView::WebContentMouseHandler
1011 : public ui::EventHandler
{
1013 explicit WebContentMouseHandler(ManagePasswordsBubbleView
* bubble
);
1015 void OnKeyEvent(ui::KeyEvent
* event
) override
;
1016 void OnMouseEvent(ui::MouseEvent
* event
) override
;
1019 ManagePasswordsBubbleView
* bubble_
;
1020 scoped_ptr
<views::EventMonitor
> event_monitor_
;
1022 DISALLOW_COPY_AND_ASSIGN(WebContentMouseHandler
);
1025 ManagePasswordsBubbleView::WebContentMouseHandler::WebContentMouseHandler(
1026 ManagePasswordsBubbleView
* bubble
)
1028 content::WebContents
* web_contents
= bubble_
->web_contents();
1029 DCHECK(web_contents
);
1030 event_monitor_
= views::EventMonitor::CreateWindowMonitor(
1031 this, web_contents
->GetTopLevelNativeWindow());
1034 void ManagePasswordsBubbleView::WebContentMouseHandler::OnKeyEvent(
1035 ui::KeyEvent
* event
) {
1036 content::WebContents
* web_contents
= bubble_
->web_contents();
1037 content::RenderViewHost
* rvh
= web_contents
->GetRenderViewHost();
1038 if (rvh
->IsFocusedElementEditable() &&
1039 event
->type() == ui::ET_KEY_PRESSED
)
1043 void ManagePasswordsBubbleView::WebContentMouseHandler::OnMouseEvent(
1044 ui::MouseEvent
* event
) {
1045 if (event
->type() == ui::ET_MOUSE_PRESSED
)
1049 // ManagePasswordsBubbleView --------------------------------------------------
1052 ManagePasswordsBubbleView
* ManagePasswordsBubbleView::manage_passwords_bubble_
=
1056 void ManagePasswordsBubbleView::ShowBubble(content::WebContents
* web_contents
,
1057 DisplayReason reason
) {
1058 Browser
* browser
= chrome::FindBrowserWithWebContents(web_contents
);
1060 DCHECK(browser
->window());
1065 BrowserView
* browser_view
= BrowserView::GetBrowserViewForBrowser(browser
);
1066 bool is_fullscreen
= browser_view
->IsFullscreen();
1067 ManagePasswordsIconView
* anchor_view
=
1070 : browser_view
->GetLocationBarView()->manage_passwords_icon_view();
1071 manage_passwords_bubble_
= new ManagePasswordsBubbleView(
1072 web_contents
, anchor_view
, reason
);
1075 manage_passwords_bubble_
->set_parent_window(web_contents
->GetNativeView());
1077 views::BubbleDelegateView::CreateBubble(manage_passwords_bubble_
);
1079 // Adjust for fullscreen after creation as it relies on the content size.
1080 if (is_fullscreen
) {
1081 manage_passwords_bubble_
->AdjustForFullscreen(
1082 browser_view
->GetBoundsInScreen());
1084 if (reason
== AUTOMATIC
)
1085 manage_passwords_bubble_
->GetWidget()->ShowInactive();
1087 manage_passwords_bubble_
->GetWidget()->Show();
1091 void ManagePasswordsBubbleView::CloseBubble() {
1092 if (manage_passwords_bubble_
)
1093 manage_passwords_bubble_
->Close();
1097 void ManagePasswordsBubbleView::ActivateBubble() {
1100 manage_passwords_bubble_
->GetWidget()->Activate();
1104 bool ManagePasswordsBubbleView::IsShowing() {
1105 // The bubble may be in the process of closing.
1106 return (manage_passwords_bubble_
!= NULL
) &&
1107 manage_passwords_bubble_
->GetWidget()->IsVisible();
1110 content::WebContents
* ManagePasswordsBubbleView::web_contents() const {
1111 return model()->web_contents();
1114 ManagePasswordsBubbleView::ManagePasswordsBubbleView(
1115 content::WebContents
* web_contents
,
1116 ManagePasswordsIconView
* anchor_view
,
1117 DisplayReason reason
)
1118 : ManagePasswordsBubble(web_contents
, reason
),
1119 ManagedFullScreenBubbleDelegateView(anchor_view
, web_contents
),
1120 anchor_view_(anchor_view
),
1121 initially_focused_view_(NULL
) {
1122 // Compensate for built-in vertical padding in the anchor view's image.
1123 set_anchor_view_insets(gfx::Insets(5, 0, 5, 0));
1125 anchor_view
->SetActive(true);
1126 mouse_handler_
.reset(new WebContentMouseHandler(this));
1129 ManagePasswordsBubbleView::~ManagePasswordsBubbleView() {
1130 if (manage_passwords_bubble_
== this)
1131 manage_passwords_bubble_
= NULL
;
1134 views::View
* ManagePasswordsBubbleView::GetInitiallyFocusedView() {
1135 return initially_focused_view_
;
1138 void ManagePasswordsBubbleView::Init() {
1139 views::FillLayout
* layout
= new views::FillLayout();
1140 SetLayoutManager(layout
);
1145 void ManagePasswordsBubbleView::Close() {
1146 mouse_handler_
.reset();
1147 ManagedFullScreenBubbleDelegateView::Close();
1150 void ManagePasswordsBubbleView::OnWidgetClosing(views::Widget
* /*widget*/) {
1152 anchor_view_
->SetActive(false);
1155 void ManagePasswordsBubbleView::Refresh() {
1156 RemoveAllChildViews(true);
1157 initially_focused_view_
= NULL
;
1158 if (model()->state() == password_manager::ui::PENDING_PASSWORD_STATE
) {
1159 if (model()->never_save_passwords()) {
1160 AddChildView(new ConfirmNeverView(this));
1162 if (model()->IsNewUIActive())
1163 AddChildView(new SaveAccountView(this));
1165 AddChildView(new PendingView(this));
1167 } else if (model()->state() == password_manager::ui::BLACKLIST_STATE
) {
1168 AddChildView(new BlacklistedView(this));
1169 } else if (model()->state() == password_manager::ui::CONFIRMATION_STATE
) {
1170 AddChildView(new SaveConfirmationView(this));
1171 } else if (model()->state() ==
1172 password_manager::ui::CREDENTIAL_REQUEST_STATE
) {
1173 AddChildView(new AccountChooserView(this));
1174 } else if (model()->state() == password_manager::ui::AUTO_SIGNIN_STATE
) {
1175 AddChildView(new AutoSigninView(this));
1177 if (model()->IsNewUIActive())
1178 AddChildView(new ManageAccountsView(this));
1180 AddChildView(new ManageView(this));
1182 GetLayoutManager()->Layout(this);
1185 void ManagePasswordsBubbleView::NotifyConfirmedNeverForThisSite() {
1186 model()->OnNeverForThisSiteClicked();
1190 void ManagePasswordsBubbleView::NotifyUndoNeverForThisSite() {
1191 model()->OnUndoNeverForThisSite();
1196 void ManagePasswordsBubbleView::NotifyNeverForThisSiteClicked() {
1197 if (model()->local_credentials().empty()) {
1198 // Skip confirmation if there are no existing passwords for this site.
1199 NotifyConfirmedNeverForThisSite();
1201 model()->OnConfirmationForNeverForThisSite();