MacViews: Use Mac's "Constrained Window Button" style for Button::STYLE_BUTTON LabelB...
[chromium-blink-merge.git] / chrome / browser / ui / views / passwords / manage_passwords_bubble_view.cc
blob3b07378d2b025fc7bdbe74aed730d1ee5c1c7622
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/save_account_more_combobox_model.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/grit/generated_resources.h"
22 #include "content/public/browser/render_view_host.h"
23 #include "ui/base/l10n/l10n_util.h"
24 #include "ui/base/resource/resource_bundle.h"
25 #include "ui/views/controls/button/blue_button.h"
26 #include "ui/views/controls/button/label_button.h"
27 #include "ui/views/controls/combobox/combobox.h"
28 #include "ui/views/controls/combobox/combobox_listener.h"
29 #include "ui/views/controls/link.h"
30 #include "ui/views/controls/link_listener.h"
31 #include "ui/views/controls/separator.h"
32 #include "ui/views/controls/styled_label.h"
33 #include "ui/views/controls/styled_label_listener.h"
34 #include "ui/views/event_monitor.h"
35 #include "ui/views/layout/fill_layout.h"
36 #include "ui/views/layout/grid_layout.h"
37 #include "ui/views/layout/layout_constants.h"
38 #include "ui/views/widget/widget.h"
40 int ManagePasswordsBubbleView::auto_signin_toast_timeout_ = 3;
42 // Helpers --------------------------------------------------------------------
44 namespace {
46 const int kDesiredBubbleWidth = 370;
48 enum ColumnSetType {
49 // | | (FILL, FILL) | |
50 // Used for the bubble's header, the credentials list, and for simple
51 // messages like "No passwords".
52 SINGLE_VIEW_COLUMN_SET,
54 // | | (TRAILING, CENTER) | | (TRAILING, CENTER) | |
55 // Used for buttons at the bottom of the bubble which should nest at the
56 // bottom-right corner.
57 DOUBLE_BUTTON_COLUMN_SET,
59 // | | (LEADING, CENTER) | | (TRAILING, CENTER) | |
60 // Used for buttons at the bottom of the bubble which should occupy
61 // the corners.
62 LINK_BUTTON_COLUMN_SET,
64 // | | (TRAILING, CENTER) | |
65 // Used when there is only one button which should next at the bottom-right
66 // corner.
67 SINGLE_BUTTON_COLUMN_SET,
69 // | | (LEADING, CENTER) | | (TRAILING, CENTER) | | (TRAILING, CENTER) | |
70 // Used when there are three buttons.
71 TRIPLE_BUTTON_COLUMN_SET,
74 enum TextRowType { ROW_SINGLE, ROW_MULTILINE };
76 // Construct an appropriate ColumnSet for the given |type|, and add it
77 // to |layout|.
78 void BuildColumnSet(views::GridLayout* layout, ColumnSetType type) {
79 views::ColumnSet* column_set = layout->AddColumnSet(type);
80 column_set->AddPaddingColumn(0, views::kPanelHorizMargin);
81 int full_width = kDesiredBubbleWidth - (2 * views::kPanelHorizMargin);
82 switch (type) {
83 case SINGLE_VIEW_COLUMN_SET:
84 column_set->AddColumn(views::GridLayout::FILL,
85 views::GridLayout::FILL,
87 views::GridLayout::FIXED,
88 full_width,
89 0);
90 break;
91 case DOUBLE_BUTTON_COLUMN_SET:
92 column_set->AddColumn(views::GridLayout::TRAILING,
93 views::GridLayout::CENTER,
95 views::GridLayout::USE_PREF,
97 0);
98 column_set->AddPaddingColumn(0, views::kRelatedButtonHSpacing);
99 column_set->AddColumn(views::GridLayout::TRAILING,
100 views::GridLayout::CENTER,
102 views::GridLayout::USE_PREF,
105 break;
106 case LINK_BUTTON_COLUMN_SET:
107 column_set->AddColumn(views::GridLayout::LEADING,
108 views::GridLayout::CENTER,
110 views::GridLayout::USE_PREF,
113 column_set->AddPaddingColumn(0, views::kRelatedButtonHSpacing);
114 column_set->AddColumn(views::GridLayout::TRAILING,
115 views::GridLayout::CENTER,
117 views::GridLayout::USE_PREF,
120 break;
121 case SINGLE_BUTTON_COLUMN_SET:
122 column_set->AddColumn(views::GridLayout::TRAILING,
123 views::GridLayout::CENTER,
125 views::GridLayout::USE_PREF,
128 break;
129 case TRIPLE_BUTTON_COLUMN_SET:
130 column_set->AddColumn(views::GridLayout::LEADING,
131 views::GridLayout::CENTER,
133 views::GridLayout::USE_PREF,
136 column_set->AddPaddingColumn(0, views::kRelatedButtonHSpacing);
137 column_set->AddColumn(views::GridLayout::TRAILING,
138 views::GridLayout::CENTER,
140 views::GridLayout::USE_PREF,
143 column_set->AddPaddingColumn(0, views::kRelatedButtonHSpacing);
144 column_set->AddColumn(views::GridLayout::TRAILING,
145 views::GridLayout::CENTER,
147 views::GridLayout::USE_PREF,
150 break;
152 column_set->AddPaddingColumn(0, views::kPanelHorizMargin);
155 // Given a layout and a model, add an appropriate title using a
156 // SINGLE_VIEW_COLUMN_SET, followed by a spacer row.
157 void AddTitleRow(views::GridLayout* layout, ManagePasswordsBubbleModel* model) {
158 views::Label* title_label = new views::Label(model->title());
159 title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
160 title_label->SetMultiLine(true);
161 title_label->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
162 ui::ResourceBundle::MediumFont));
164 // Add the title to the layout with appropriate padding.
165 layout->StartRowWithPadding(
166 0, SINGLE_VIEW_COLUMN_SET, 0, views::kRelatedControlSmallVerticalSpacing);
167 layout->AddView(title_label);
168 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
171 } // namespace
174 // ManagePasswordsBubbleView::AccountChooserView ------------------------------
176 // A view offering the user the ability to choose credentials for
177 // authentication. Contains a list of CredentialsItemView, along with a
178 // "Cancel" button.
179 class ManagePasswordsBubbleView::AccountChooserView
180 : public views::View,
181 public views::ButtonListener {
182 public:
183 explicit AccountChooserView(ManagePasswordsBubbleView* parent);
184 ~AccountChooserView() override;
186 private:
187 // views::ButtonListener:
188 void ButtonPressed(views::Button* sender, const ui::Event& event) override;
190 // Adds |password_forms| to the layout remembering their |type|.
191 void AddCredentialItemsWithType(
192 views::GridLayout* layout,
193 const ScopedVector<const autofill::PasswordForm>& password_forms,
194 password_manager::CredentialType type);
196 ManagePasswordsBubbleView* parent_;
197 views::LabelButton* cancel_button_;
199 DISALLOW_COPY_AND_ASSIGN(AccountChooserView);
202 ManagePasswordsBubbleView::AccountChooserView::AccountChooserView(
203 ManagePasswordsBubbleView* parent)
204 : parent_(parent) {
205 views::GridLayout* layout = new views::GridLayout(this);
206 SetLayoutManager(layout);
208 cancel_button_ =
209 new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_CANCEL));
210 cancel_button_->SetStyle(views::Button::STYLE_BUTTON);
211 cancel_button_->SetFontList(
212 ui::ResourceBundle::GetSharedInstance().GetFontList(
213 ui::ResourceBundle::SmallFont));
215 // Title row.
216 BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
217 AddTitleRow(layout, parent_->model());
219 AddCredentialItemsWithType(
220 layout, parent_->model()->local_credentials(),
221 password_manager::CredentialType::CREDENTIAL_TYPE_PASSWORD);
223 AddCredentialItemsWithType(
224 layout, parent_->model()->federated_credentials(),
225 password_manager::CredentialType::CREDENTIAL_TYPE_FEDERATED);
227 // Button row.
228 BuildColumnSet(layout, SINGLE_BUTTON_COLUMN_SET);
229 layout->StartRowWithPadding(
230 0, SINGLE_BUTTON_COLUMN_SET, 0, views::kRelatedControlVerticalSpacing);
231 layout->AddView(cancel_button_);
233 // Extra padding for visual awesomeness.
234 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
236 parent_->set_initially_focused_view(cancel_button_);
239 ManagePasswordsBubbleView::AccountChooserView::~AccountChooserView() {
242 void ManagePasswordsBubbleView::AccountChooserView::AddCredentialItemsWithType(
243 views::GridLayout* layout,
244 const ScopedVector<const autofill::PasswordForm>& password_forms,
245 password_manager::CredentialType type) {
246 net::URLRequestContextGetter* request_context =
247 parent_->model()->GetProfile()->GetRequestContext();
248 for (const autofill::PasswordForm* form : password_forms) {
249 // Add the title to the layout with appropriate padding.
250 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
251 layout->AddView(new CredentialsItemView(
252 this, form, type, CredentialsItemView::ACCOUNT_CHOOSER,
253 request_context));
257 void ManagePasswordsBubbleView::AccountChooserView::ButtonPressed(
258 views::Button* sender, const ui::Event& event) {
259 if (sender != cancel_button_) {
260 // ManagePasswordsBubbleModel should care about calling a callback in case
261 // the bubble is dismissed by any other means.
262 CredentialsItemView* view = static_cast<CredentialsItemView*>(sender);
263 parent_->model()->OnChooseCredentials(*view->form(),
264 view->credential_type());
265 } else {
266 parent_->model()->OnNopeClicked();
268 parent_->Close();
271 // ManagePasswordsBubbleView::AutoSigninView ----------------------------------
273 // A view containing just one credential that was used for for automatic signing
274 // in.
275 class ManagePasswordsBubbleView::AutoSigninView
276 : public views::View,
277 public views::ButtonListener,
278 public views::WidgetObserver {
279 public:
280 explicit AutoSigninView(ManagePasswordsBubbleView* parent);
282 private:
283 // views::ButtonListener:
284 void ButtonPressed(views::Button* sender, const ui::Event& event) override;
286 // views::WidgetObserver:
287 // Tracks the state of the browser window.
288 void OnWidgetActivationChanged(views::Widget* widget, bool active) override;
289 void OnWidgetClosing(views::Widget* widget) override;
291 void OnTimer();
292 static base::TimeDelta GetTimeout() {
293 return base::TimeDelta::FromSeconds(
294 ManagePasswordsBubbleView::auto_signin_toast_timeout_);
297 base::OneShotTimer<AutoSigninView> timer_;
298 ManagePasswordsBubbleView* parent_;
299 ScopedObserver<views::Widget, views::WidgetObserver> observed_browser_;
301 DISALLOW_COPY_AND_ASSIGN(AutoSigninView);
304 ManagePasswordsBubbleView::AutoSigninView::AutoSigninView(
305 ManagePasswordsBubbleView* parent)
306 : parent_(parent),
307 observed_browser_(this) {
308 SetLayoutManager(new views::FillLayout);
309 CredentialsItemView* credential = new CredentialsItemView(
310 this, &parent_->model()->pending_password(),
311 password_manager::CredentialType::CREDENTIAL_TYPE_PASSWORD,
312 CredentialsItemView::AUTO_SIGNIN,
313 parent_->model()->GetProfile()->GetRequestContext());
314 AddChildView(credential);
315 parent_->set_initially_focused_view(credential);
317 Browser* browser =
318 chrome::FindBrowserWithWebContents(parent_->web_contents());
319 DCHECK(browser);
320 BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
321 observed_browser_.Add(browser_view->GetWidget());
323 if (browser_view->IsActive())
324 timer_.Start(FROM_HERE, GetTimeout(), this, &AutoSigninView::OnTimer);
327 void ManagePasswordsBubbleView::AutoSigninView::ButtonPressed(
328 views::Button* sender, const ui::Event& event) {
329 parent_->model()->OnAutoSignInClicked();
330 parent_->Close();
333 void ManagePasswordsBubbleView::AutoSigninView::OnWidgetActivationChanged(
334 views::Widget* widget, bool active) {
335 if (active && !timer_.IsRunning())
336 timer_.Start(FROM_HERE, GetTimeout(), this, &AutoSigninView::OnTimer);
339 void ManagePasswordsBubbleView::AutoSigninView::OnWidgetClosing(
340 views::Widget* widget) {
341 observed_browser_.RemoveAll();
344 void ManagePasswordsBubbleView::AutoSigninView::OnTimer() {
345 parent_->model()->OnAutoSignInToastTimeout();
346 parent_->Close();
349 // ManagePasswordsBubbleView::PendingView -------------------------------------
351 // A view offering the user the ability to save credentials. Contains a
352 // single ManagePasswordItemsView, along with a "Save Passwords" button
353 // and a rejection combobox.
354 class ManagePasswordsBubbleView::PendingView
355 : public views::View,
356 public views::ButtonListener,
357 public views::ComboboxListener,
358 public views::StyledLabelListener {
359 public:
360 explicit PendingView(ManagePasswordsBubbleView* parent);
361 ~PendingView() override;
363 private:
364 // views::ButtonListener:
365 void ButtonPressed(views::Button* sender, const ui::Event& event) override;
367 // views::StyledLabelListener:
368 void StyledLabelLinkClicked(const gfx::Range& range,
369 int event_flags) override;
371 // Handles the event when the user changes an index of a combobox.
372 void OnPerformAction(views::Combobox* source) override;
374 ManagePasswordsBubbleView* parent_;
376 views::BlueButton* save_button_;
378 // The combobox doesn't take ownership of its model. If we created a
379 // combobox we need to ensure that we delete the model here, and because the
380 // combobox uses the model in it's destructor, we need to make sure we
381 // delete the model _after_ the combobox itself is deleted.
382 scoped_ptr<SavePasswordRefusalComboboxModel> combobox_model_;
383 scoped_ptr<views::Combobox> refuse_combobox_;
385 DISALLOW_COPY_AND_ASSIGN(PendingView);
388 ManagePasswordsBubbleView::PendingView::PendingView(
389 ManagePasswordsBubbleView* parent)
390 : parent_(parent) {
391 views::GridLayout* layout = new views::GridLayout(this);
392 layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
393 SetLayoutManager(layout);
395 std::vector<const autofill::PasswordForm*> credentials(
396 1, &parent->model()->pending_password());
397 // Create the pending credential item, save button and refusal combobox.
398 ManagePasswordItemsView* item =
399 new ManagePasswordItemsView(parent_->model(), credentials);
400 save_button_ = new views::BlueButton(
401 this, l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_SAVE_BUTTON));
402 save_button_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
403 ui::ResourceBundle::SmallFont));
405 combobox_model_.reset(new SavePasswordRefusalComboboxModel);
406 refuse_combobox_.reset(new views::Combobox(combobox_model_.get()));
407 refuse_combobox_->set_listener(this);
408 refuse_combobox_->SetStyle(views::Combobox::STYLE_ACTION);
409 // TODO(mkwst): Need a mechanism to pipe a font list down into a combobox.
411 // Title row.
412 views::StyledLabel* title_label =
413 new views::StyledLabel(parent_->model()->title(), this);
414 title_label->SetBaseFontList(
415 ui::ResourceBundle::GetSharedInstance().GetFontList(
416 ui::ResourceBundle::MediumFont));
417 if (!parent_->model()->title_brand_link_range().is_empty()) {
418 title_label->AddStyleRange(
419 parent_->model()->title_brand_link_range(),
420 views::StyledLabel::RangeStyleInfo::CreateForLink());
422 BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
423 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
424 layout->AddView(title_label);
425 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
427 // Credential row.
428 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
429 layout->AddView(item);
431 // Button row.
432 BuildColumnSet(layout, DOUBLE_BUTTON_COLUMN_SET);
433 layout->StartRowWithPadding(
434 0, DOUBLE_BUTTON_COLUMN_SET, 0, views::kRelatedControlVerticalSpacing);
435 layout->AddView(save_button_);
436 layout->AddView(refuse_combobox_.get());
438 // Extra padding for visual awesomeness.
439 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
441 parent_->set_initially_focused_view(save_button_);
444 ManagePasswordsBubbleView::PendingView::~PendingView() {
447 void ManagePasswordsBubbleView::PendingView::ButtonPressed(
448 views::Button* sender,
449 const ui::Event& event) {
450 DCHECK(sender == save_button_);
451 parent_->model()->OnSaveClicked();
452 parent_->Close();
455 void ManagePasswordsBubbleView::PendingView::StyledLabelLinkClicked(
456 const gfx::Range& range,
457 int event_flags) {
458 DCHECK_EQ(range, parent_->model()->title_brand_link_range());
459 parent_->model()->OnBrandLinkClicked();
462 void ManagePasswordsBubbleView::PendingView::OnPerformAction(
463 views::Combobox* source) {
464 DCHECK_EQ(source, refuse_combobox_);
465 switch (refuse_combobox_->selected_index()) {
466 case SavePasswordRefusalComboboxModel::INDEX_NOPE:
467 parent_->model()->OnNopeClicked();
468 parent_->Close();
469 break;
470 case SavePasswordRefusalComboboxModel::INDEX_NEVER_FOR_THIS_SITE:
471 parent_->NotifyNeverForThisSiteClicked();
472 break;
476 // ManagePasswordsBubbleView::ConfirmNeverView --------------------------------
478 // A view offering the user the ability to undo her decision to never save
479 // passwords for a particular site.
480 class ManagePasswordsBubbleView::ConfirmNeverView
481 : public views::View,
482 public views::ButtonListener {
483 public:
484 explicit ConfirmNeverView(ManagePasswordsBubbleView* parent);
485 ~ConfirmNeverView() override;
487 private:
488 // views::ButtonListener:
489 void ButtonPressed(views::Button* sender, const ui::Event& event) override;
491 ManagePasswordsBubbleView* parent_;
493 views::LabelButton* confirm_button_;
494 views::LabelButton* undo_button_;
496 DISALLOW_COPY_AND_ASSIGN(ConfirmNeverView);
499 ManagePasswordsBubbleView::ConfirmNeverView::ConfirmNeverView(
500 ManagePasswordsBubbleView* parent)
501 : parent_(parent) {
502 views::GridLayout* layout = new views::GridLayout(this);
503 layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
504 SetLayoutManager(layout);
506 // Title row.
507 BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
508 AddTitleRow(layout, parent_->model());
510 // Confirmation text.
511 views::Label* confirmation = new views::Label(l10n_util::GetStringUTF16(
512 IDS_MANAGE_PASSWORDS_BLACKLIST_CONFIRMATION_TEXT));
513 confirmation->SetHorizontalAlignment(gfx::ALIGN_LEFT);
514 confirmation->SetMultiLine(true);
515 confirmation->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
516 ui::ResourceBundle::SmallFont));
517 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
518 layout->AddView(confirmation);
519 layout->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing);
521 // Confirm and undo buttons.
522 BuildColumnSet(layout, DOUBLE_BUTTON_COLUMN_SET);
523 layout->StartRowWithPadding(
524 0, DOUBLE_BUTTON_COLUMN_SET, 0, views::kRelatedControlVerticalSpacing);
526 confirm_button_ = new views::LabelButton(
527 this,
528 l10n_util::GetStringUTF16(
529 IDS_MANAGE_PASSWORDS_BLACKLIST_CONFIRMATION_BUTTON));
530 confirm_button_->SetStyle(views::Button::STYLE_BUTTON);
531 confirm_button_->SetFontList(
532 ui::ResourceBundle::GetSharedInstance().GetFontList(
533 ui::ResourceBundle::SmallFont));
534 layout->AddView(confirm_button_);
536 undo_button_ =
537 new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_CANCEL));
538 undo_button_->SetStyle(views::Button::STYLE_BUTTON);
539 undo_button_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
540 ui::ResourceBundle::SmallFont));
541 layout->AddView(undo_button_);
543 // Extra padding for visual awesomeness.
544 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
546 parent_->set_initially_focused_view(confirm_button_);
549 ManagePasswordsBubbleView::ConfirmNeverView::~ConfirmNeverView() {
552 void ManagePasswordsBubbleView::ConfirmNeverView::ButtonPressed(
553 views::Button* sender,
554 const ui::Event& event) {
555 DCHECK(sender == confirm_button_ || sender == undo_button_);
556 if (sender == confirm_button_)
557 parent_->NotifyConfirmedNeverForThisSite();
558 else
559 parent_->NotifyUndoNeverForThisSite();
562 // ManagePasswordsBubbleView::ManageView --------------------------------------
564 // A view offering the user a list of her currently saved credentials
565 // for the current page, along with a "Manage passwords" link and a
566 // "Done" button.
567 class ManagePasswordsBubbleView::ManageView : public views::View,
568 public views::ButtonListener,
569 public views::LinkListener {
570 public:
571 explicit ManageView(ManagePasswordsBubbleView* parent);
572 ~ManageView() override;
574 private:
575 // views::ButtonListener:
576 void ButtonPressed(views::Button* sender, const ui::Event& event) override;
578 // views::LinkListener:
579 void LinkClicked(views::Link* source, int event_flags) override;
581 ManagePasswordsBubbleView* parent_;
583 views::Link* manage_link_;
584 views::LabelButton* done_button_;
586 DISALLOW_COPY_AND_ASSIGN(ManageView);
589 ManagePasswordsBubbleView::ManageView::ManageView(
590 ManagePasswordsBubbleView* parent)
591 : parent_(parent) {
592 views::GridLayout* layout = new views::GridLayout(this);
593 layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
594 SetLayoutManager(layout);
596 // Add the title.
597 BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
598 AddTitleRow(layout, parent_->model());
600 // If we have a list of passwords to store for the current site, display
601 // them to the user for management. Otherwise, render a "No passwords for
602 // this site" message.
603 if (!parent_->model()->local_credentials().empty()) {
604 ManagePasswordItemsView* item = new ManagePasswordItemsView(
605 parent_->model(), parent_->model()->local_credentials().get());
606 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
607 layout->AddView(item);
608 } else {
609 views::Label* empty_label = new views::Label(
610 l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_NO_PASSWORDS));
611 empty_label->SetMultiLine(true);
612 empty_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
613 empty_label->SetFontList(
614 ui::ResourceBundle::GetSharedInstance().GetFontList(
615 ui::ResourceBundle::SmallFont));
617 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
618 layout->AddView(empty_label);
619 layout->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing);
622 // Then add the "manage passwords" link and "Done" button.
623 manage_link_ = new views::Link(parent_->model()->manage_link());
624 manage_link_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
625 manage_link_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
626 ui::ResourceBundle::SmallFont));
627 manage_link_->SetUnderline(false);
628 manage_link_->set_listener(this);
630 done_button_ =
631 new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_DONE));
632 done_button_->SetStyle(views::Button::STYLE_BUTTON);
633 done_button_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
634 ui::ResourceBundle::SmallFont));
636 BuildColumnSet(layout, LINK_BUTTON_COLUMN_SET);
637 layout->StartRowWithPadding(
638 0, LINK_BUTTON_COLUMN_SET, 0, views::kRelatedControlVerticalSpacing);
639 layout->AddView(manage_link_);
640 layout->AddView(done_button_);
642 // Extra padding for visual awesomeness.
643 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
645 parent_->set_initially_focused_view(done_button_);
648 ManagePasswordsBubbleView::ManageView::~ManageView() {
651 void ManagePasswordsBubbleView::ManageView::ButtonPressed(
652 views::Button* sender,
653 const ui::Event& event) {
654 DCHECK(sender == done_button_);
655 parent_->model()->OnDoneClicked();
656 parent_->Close();
659 void ManagePasswordsBubbleView::ManageView::LinkClicked(views::Link* source,
660 int event_flags) {
661 DCHECK_EQ(source, manage_link_);
662 parent_->model()->OnManageLinkClicked();
663 parent_->Close();
666 // ManagePasswordsBubbleView::ManageAccountsView ------------------------------
668 // A view offering the user a list of his currently saved through the Credential
669 // Manager API accounts for the current page.
670 class ManagePasswordsBubbleView::ManageAccountsView
671 : public views::View,
672 public views::ButtonListener,
673 public views::LinkListener {
674 public:
675 explicit ManageAccountsView(ManagePasswordsBubbleView* parent);
677 private:
678 // views::ButtonListener:
679 void ButtonPressed(views::Button* sender, const ui::Event& event) override;
681 // views::LinkListener:
682 void LinkClicked(views::Link* source, int event_flags) override;
684 ManagePasswordsBubbleView* parent_;
686 views::Link* manage_link_;
687 views::LabelButton* done_button_;
689 DISALLOW_COPY_AND_ASSIGN(ManageAccountsView);
692 ManagePasswordsBubbleView::ManageAccountsView::ManageAccountsView(
693 ManagePasswordsBubbleView* parent)
694 : parent_(parent) {
695 views::GridLayout* layout = new views::GridLayout(this);
696 layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
697 SetLayoutManager(layout);
699 // Add the title.
700 BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
701 AddTitleRow(layout, parent_->model());
703 if (!parent_->model()->local_credentials().empty()) {
704 for (const autofill::PasswordForm* form :
705 parent_->model()->local_credentials()) {
706 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
707 layout->AddView(new ManageCredentialItemView(parent_->model(), form));
709 } else {
710 views::Label* empty_label = new views::Label(
711 l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_NO_PASSWORDS),
712 ui::ResourceBundle::GetSharedInstance().GetFontList(
713 ui::ResourceBundle::SmallFont));
714 empty_label->SetMultiLine(true);
715 empty_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
717 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
718 layout->AddView(empty_label);
719 layout->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing);
722 // Then add the "manage passwords" link and "Done" button.
723 manage_link_ = new views::Link(parent_->model()->manage_link());
724 manage_link_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
725 manage_link_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
726 ui::ResourceBundle::SmallFont));
727 manage_link_->SetUnderline(false);
728 manage_link_->set_listener(this);
730 done_button_ =
731 new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_DONE));
732 done_button_->SetStyle(views::Button::STYLE_BUTTON);
733 done_button_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
734 ui::ResourceBundle::SmallFont));
736 BuildColumnSet(layout, LINK_BUTTON_COLUMN_SET);
737 layout->StartRowWithPadding(
738 0, LINK_BUTTON_COLUMN_SET, 0, views::kRelatedControlVerticalSpacing);
739 layout->AddView(manage_link_);
740 layout->AddView(done_button_);
742 // Extra padding for visual awesomeness.
743 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
745 parent_->set_initially_focused_view(done_button_);
748 void ManagePasswordsBubbleView::ManageAccountsView::ButtonPressed(
749 views::Button* sender, const ui::Event& event) {
750 DCHECK(sender == done_button_);
751 parent_->model()->OnDoneClicked();
752 parent_->Close();
755 void ManagePasswordsBubbleView::ManageAccountsView::LinkClicked(
756 views::Link* source, int event_flags) {
757 DCHECK_EQ(source, manage_link_);
758 parent_->model()->OnManageLinkClicked();
759 parent_->Close();
762 // ManagePasswordsBubbleView::BlacklistedView ---------------------------------
764 // A view offering the user the ability to re-enable the password manager for
765 // a specific site after she's decided to "never save passwords".
766 class ManagePasswordsBubbleView::BlacklistedView
767 : public views::View,
768 public views::ButtonListener {
769 public:
770 explicit BlacklistedView(ManagePasswordsBubbleView* parent);
771 ~BlacklistedView() override;
773 private:
774 // views::ButtonListener:
775 void ButtonPressed(views::Button* sender, const ui::Event& event) override;
777 ManagePasswordsBubbleView* parent_;
779 views::BlueButton* unblacklist_button_;
780 views::LabelButton* done_button_;
782 DISALLOW_COPY_AND_ASSIGN(BlacklistedView);
785 ManagePasswordsBubbleView::BlacklistedView::BlacklistedView(
786 ManagePasswordsBubbleView* parent)
787 : parent_(parent) {
788 views::GridLayout* layout = new views::GridLayout(this);
789 layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
790 SetLayoutManager(layout);
792 // Add the title.
793 BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
794 AddTitleRow(layout, parent_->model());
796 // Add the "Hey! You blacklisted this site!" text.
797 views::Label* blacklisted = new views::Label(
798 l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_BLACKLISTED));
799 blacklisted->SetMultiLine(true);
800 blacklisted->SetHorizontalAlignment(gfx::ALIGN_LEFT);
801 blacklisted->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
802 ui::ResourceBundle::SmallFont));
803 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
804 layout->AddView(blacklisted);
805 layout->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing);
807 // Then add the "enable password manager" and "Done" buttons.
808 unblacklist_button_ = new views::BlueButton(
809 this, l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_UNBLACKLIST_BUTTON));
810 unblacklist_button_->SetFontList(
811 ui::ResourceBundle::GetSharedInstance().GetFontList(
812 ui::ResourceBundle::SmallFont));
813 done_button_ =
814 new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_DONE));
815 done_button_->SetStyle(views::Button::STYLE_BUTTON);
816 done_button_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
817 ui::ResourceBundle::SmallFont));
819 BuildColumnSet(layout, DOUBLE_BUTTON_COLUMN_SET);
820 layout->StartRowWithPadding(
821 0, DOUBLE_BUTTON_COLUMN_SET, 0, views::kRelatedControlVerticalSpacing);
822 layout->AddView(unblacklist_button_);
823 layout->AddView(done_button_);
825 // Extra padding for visual awesomeness.
826 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
828 parent_->set_initially_focused_view(unblacklist_button_);
831 ManagePasswordsBubbleView::BlacklistedView::~BlacklistedView() {
834 void ManagePasswordsBubbleView::BlacklistedView::ButtonPressed(
835 views::Button* sender,
836 const ui::Event& event) {
837 if (sender == done_button_)
838 parent_->model()->OnDoneClicked();
839 else if (sender == unblacklist_button_)
840 parent_->model()->OnUnblacklistClicked();
841 else
842 NOTREACHED();
843 parent_->Close();
846 // ManagePasswordsBubbleView::SaveConfirmationView ----------------------------
848 // A view confirming to the user that a password was saved and offering a link
849 // to the Google account manager.
850 class ManagePasswordsBubbleView::SaveConfirmationView
851 : public views::View,
852 public views::ButtonListener,
853 public views::StyledLabelListener {
854 public:
855 explicit SaveConfirmationView(ManagePasswordsBubbleView* parent);
856 ~SaveConfirmationView() override;
858 private:
859 // views::ButtonListener:
860 void ButtonPressed(views::Button* sender, const ui::Event& event) override;
862 // views::StyledLabelListener implementation
863 void StyledLabelLinkClicked(const gfx::Range& range,
864 int event_flags) override;
866 ManagePasswordsBubbleView* parent_;
867 views::LabelButton* ok_button_;
869 DISALLOW_COPY_AND_ASSIGN(SaveConfirmationView);
872 ManagePasswordsBubbleView::SaveConfirmationView::SaveConfirmationView(
873 ManagePasswordsBubbleView* parent)
874 : parent_(parent) {
875 views::GridLayout* layout = new views::GridLayout(this);
876 layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
877 SetLayoutManager(layout);
879 BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
880 AddTitleRow(layout, parent_->model());
882 views::StyledLabel* confirmation =
883 new views::StyledLabel(parent_->model()->save_confirmation_text(), this);
884 confirmation->SetBaseFontList(
885 ui::ResourceBundle::GetSharedInstance().GetFontList(
886 ui::ResourceBundle::SmallFont));
887 confirmation->AddStyleRange(
888 parent_->model()->save_confirmation_link_range(),
889 views::StyledLabel::RangeStyleInfo::CreateForLink());
891 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
892 layout->AddView(confirmation);
894 ok_button_ = new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_OK));
895 ok_button_->SetStyle(views::Button::STYLE_BUTTON);
896 ok_button_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
897 ui::ResourceBundle::SmallFont));
899 BuildColumnSet(layout, SINGLE_BUTTON_COLUMN_SET);
900 layout->StartRowWithPadding(
901 0, SINGLE_BUTTON_COLUMN_SET, 0, views::kRelatedControlVerticalSpacing);
902 layout->AddView(ok_button_);
904 // Extra padding for visual awesomeness.
905 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
907 parent_->set_initially_focused_view(ok_button_);
910 ManagePasswordsBubbleView::SaveConfirmationView::~SaveConfirmationView() {
913 void ManagePasswordsBubbleView::SaveConfirmationView::StyledLabelLinkClicked(
914 const gfx::Range& range, int event_flags) {
915 DCHECK_EQ(range, parent_->model()->save_confirmation_link_range());
916 parent_->model()->OnManageLinkClicked();
917 parent_->Close();
920 void ManagePasswordsBubbleView::SaveConfirmationView::ButtonPressed(
921 views::Button* sender, const ui::Event& event) {
922 DCHECK_EQ(sender, ok_button_);
923 parent_->model()->OnOKClicked();
924 parent_->Close();
927 // ManagePasswordsBubbleView::WebContentMouseHandler --------------------------
929 // The class listens for WebContentsView events and notifies the bubble if the
930 // view was clicked on or received keystrokes.
931 class ManagePasswordsBubbleView::WebContentMouseHandler
932 : public ui::EventHandler {
933 public:
934 explicit WebContentMouseHandler(ManagePasswordsBubbleView* bubble);
936 void OnKeyEvent(ui::KeyEvent* event) override;
937 void OnMouseEvent(ui::MouseEvent* event) override;
938 void OnTouchEvent(ui::TouchEvent* event) override;
940 private:
941 ManagePasswordsBubbleView* bubble_;
942 scoped_ptr<views::EventMonitor> event_monitor_;
944 DISALLOW_COPY_AND_ASSIGN(WebContentMouseHandler);
947 ManagePasswordsBubbleView::WebContentMouseHandler::WebContentMouseHandler(
948 ManagePasswordsBubbleView* bubble)
949 : bubble_(bubble) {
950 content::WebContents* web_contents = bubble_->web_contents();
951 DCHECK(web_contents);
952 event_monitor_ = views::EventMonitor::CreateWindowMonitor(
953 this, web_contents->GetTopLevelNativeWindow());
956 void ManagePasswordsBubbleView::WebContentMouseHandler::OnKeyEvent(
957 ui::KeyEvent* event) {
958 content::WebContents* web_contents = bubble_->web_contents();
959 content::RenderViewHost* rvh = web_contents->GetRenderViewHost();
960 if ((event->key_code() == ui::VKEY_ESCAPE ||
961 rvh->IsFocusedElementEditable()) && event->type() == ui::ET_KEY_PRESSED)
962 bubble_->Close();
965 void ManagePasswordsBubbleView::WebContentMouseHandler::OnMouseEvent(
966 ui::MouseEvent* event) {
967 if (event->type() == ui::ET_MOUSE_PRESSED)
968 bubble_->Close();
971 void ManagePasswordsBubbleView::WebContentMouseHandler::OnTouchEvent(
972 ui::TouchEvent* event) {
973 if (event->type() == ui::ET_TOUCH_PRESSED)
974 bubble_->Close();
977 // ManagePasswordsBubbleView --------------------------------------------------
979 // static
980 ManagePasswordsBubbleView* ManagePasswordsBubbleView::manage_passwords_bubble_ =
981 NULL;
983 // static
984 void ManagePasswordsBubbleView::ShowBubble(content::WebContents* web_contents,
985 DisplayReason reason) {
986 Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
987 DCHECK(browser);
988 DCHECK(browser->window());
989 DCHECK(!manage_passwords_bubble_ ||
990 !manage_passwords_bubble_->GetWidget()->IsVisible());
992 BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
993 bool is_fullscreen = browser_view->IsFullscreen();
994 ManagePasswordsIconView* anchor_view =
995 is_fullscreen
996 ? NULL
997 : browser_view->GetLocationBarView()->manage_passwords_icon_view();
998 manage_passwords_bubble_ = new ManagePasswordsBubbleView(
999 web_contents, anchor_view, reason);
1001 if (is_fullscreen)
1002 manage_passwords_bubble_->set_parent_window(web_contents->GetNativeView());
1004 views::BubbleDelegateView::CreateBubble(manage_passwords_bubble_);
1006 // Adjust for fullscreen after creation as it relies on the content size.
1007 if (is_fullscreen) {
1008 manage_passwords_bubble_->AdjustForFullscreen(
1009 browser_view->GetBoundsInScreen());
1011 if (reason == AUTOMATIC)
1012 manage_passwords_bubble_->GetWidget()->ShowInactive();
1013 else
1014 manage_passwords_bubble_->GetWidget()->Show();
1017 // static
1018 void ManagePasswordsBubbleView::CloseBubble() {
1019 if (manage_passwords_bubble_)
1020 manage_passwords_bubble_->Close();
1023 // static
1024 void ManagePasswordsBubbleView::ActivateBubble() {
1025 DCHECK(manage_passwords_bubble_);
1026 DCHECK(manage_passwords_bubble_->GetWidget()->IsVisible());
1027 manage_passwords_bubble_->GetWidget()->Activate();
1030 content::WebContents* ManagePasswordsBubbleView::web_contents() const {
1031 return model()->web_contents();
1034 ManagePasswordsBubbleView::ManagePasswordsBubbleView(
1035 content::WebContents* web_contents,
1036 ManagePasswordsIconView* anchor_view,
1037 DisplayReason reason)
1038 : ManagePasswordsBubble(web_contents, reason),
1039 ManagedFullScreenBubbleDelegateView(anchor_view, web_contents),
1040 anchor_view_(anchor_view),
1041 initially_focused_view_(NULL) {
1042 // Compensate for built-in vertical padding in the anchor view's image.
1043 set_anchor_view_insets(gfx::Insets(5, 0, 5, 0));
1044 if (anchor_view)
1045 anchor_view->SetActive(true);
1046 mouse_handler_.reset(new WebContentMouseHandler(this));
1049 ManagePasswordsBubbleView::~ManagePasswordsBubbleView() {
1050 if (manage_passwords_bubble_ == this)
1051 manage_passwords_bubble_ = NULL;
1054 views::View* ManagePasswordsBubbleView::GetInitiallyFocusedView() {
1055 return initially_focused_view_;
1058 void ManagePasswordsBubbleView::Init() {
1059 views::FillLayout* layout = new views::FillLayout();
1060 SetLayoutManager(layout);
1062 Refresh();
1065 void ManagePasswordsBubbleView::Close() {
1066 mouse_handler_.reset();
1067 ManagedFullScreenBubbleDelegateView::Close();
1070 void ManagePasswordsBubbleView::OnWidgetClosing(views::Widget* /*widget*/) {
1071 if (anchor_view_)
1072 anchor_view_->SetActive(false);
1075 void ManagePasswordsBubbleView::Refresh() {
1076 RemoveAllChildViews(true);
1077 initially_focused_view_ = NULL;
1078 if (model()->state() == password_manager::ui::PENDING_PASSWORD_STATE) {
1079 if (model()->never_save_passwords())
1080 AddChildView(new ConfirmNeverView(this));
1081 else
1082 AddChildView(new PendingView(this));
1083 } else if (model()->state() == password_manager::ui::BLACKLIST_STATE) {
1084 AddChildView(new BlacklistedView(this));
1085 } else if (model()->state() == password_manager::ui::CONFIRMATION_STATE) {
1086 AddChildView(new SaveConfirmationView(this));
1087 } else if (model()->state() ==
1088 password_manager::ui::CREDENTIAL_REQUEST_STATE) {
1089 AddChildView(new AccountChooserView(this));
1090 } else if (model()->state() == password_manager::ui::AUTO_SIGNIN_STATE) {
1091 AddChildView(new AutoSigninView(this));
1092 } else {
1093 if (model()->IsNewUIActive())
1094 AddChildView(new ManageAccountsView(this));
1095 else
1096 AddChildView(new ManageView(this));
1098 GetLayoutManager()->Layout(this);
1101 void ManagePasswordsBubbleView::NotifyConfirmedNeverForThisSite() {
1102 model()->OnNeverForThisSiteClicked();
1103 Close();
1106 void ManagePasswordsBubbleView::NotifyUndoNeverForThisSite() {
1107 model()->OnUndoNeverForThisSite();
1108 Refresh();
1109 SizeToContents();
1112 void ManagePasswordsBubbleView::NotifyNeverForThisSiteClicked() {
1113 if (model()->local_credentials().empty()) {
1114 // Skip confirmation if there are no existing passwords for this site.
1115 NotifyConfirmedNeverForThisSite();
1116 } else {
1117 model()->OnConfirmationForNeverForThisSite();
1118 Refresh();
1119 SizeToContents();