[Extensions] Make extension message bubble factory platform-abstract
[chromium-blink-merge.git] / chrome / browser / ui / views / passwords / manage_passwords_bubble_view.cc
blobb1bede3a918aa8bfb5ec5176434f8cd8a9a3ce4b
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 --------------------------------------------------------------------
45 namespace {
47 const int kDesiredBubbleWidth = 370;
49 enum ColumnSetType {
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
62 // the corners.
63 LINK_BUTTON_COLUMN_SET,
65 // | | (TRAILING, CENTER) | |
66 // Used when there is only one button which should next at the bottom-right
67 // corner.
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
78 // to |layout|.
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);
83 switch (type) {
84 case SINGLE_VIEW_COLUMN_SET:
85 column_set->AddColumn(views::GridLayout::FILL,
86 views::GridLayout::FILL,
88 views::GridLayout::FIXED,
89 full_width,
90 0);
91 break;
93 case DOUBLE_BUTTON_COLUMN_SET:
94 column_set->AddColumn(views::GridLayout::TRAILING,
95 views::GridLayout::CENTER,
97 views::GridLayout::USE_PREF,
99 0);
100 column_set->AddPaddingColumn(0, views::kRelatedButtonHSpacing);
101 column_set->AddColumn(views::GridLayout::TRAILING,
102 views::GridLayout::CENTER,
104 views::GridLayout::USE_PREF,
107 break;
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,
122 break;
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,
151 break;
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);
172 } // namespace
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
179 // "Cancel" button.
180 class ManagePasswordsBubbleView::AccountChooserView
181 : public views::View,
182 public views::ButtonListener {
183 public:
184 explicit AccountChooserView(ManagePasswordsBubbleView* parent);
185 ~AccountChooserView() override;
187 private:
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)
205 : parent_(parent) {
206 views::GridLayout* layout = new views::GridLayout(this);
207 SetLayoutManager(layout);
209 cancel_button_ =
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));
216 // Title row.
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);
228 // Button row.
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,
254 request_context));
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());
266 } else {
267 parent_->model()->OnNopeClicked();
269 parent_->Close();
272 // ManagePasswordsBubbleView::AutoSigninView ----------------------------------
274 // A view containing just one credential that was used for for automatic signing
275 // in.
276 class ManagePasswordsBubbleView::AutoSigninView
277 : public views::View,
278 public views::ButtonListener,
279 public views::WidgetObserver {
280 public:
281 explicit AutoSigninView(ManagePasswordsBubbleView* parent);
283 private:
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;
292 void OnTimer();
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)
307 : parent_(parent),
308 observed_browser_(this) {
309 SetLayoutManager(new views::FillLayout);
310 CredentialsItemView* credential = new CredentialsItemView(
311 this,
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);
319 Browser* browser =
320 chrome::FindBrowserWithWebContents(parent_->web_contents());
321 DCHECK(browser);
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();
332 parent_->Close();
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();
348 parent_->Close();
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 {
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 // 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)
386 : parent_(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.
409 // Title row.
410 BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
411 AddTitleRow(layout, parent_->model());
413 // Credential row.
414 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
415 layout->AddView(item);
417 // Button row.
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();
438 parent_->Close();
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();
446 parent_->Close();
447 } else if (source->selected_index() == combobox_model_->index_never()) {
448 parent_->NotifyNeverForThisSiteClicked();
449 } else {
450 NOTREACHED();
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 {
462 public:
463 explicit SaveAccountView(ManagePasswordsBubbleView* parent);
465 private:
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)
487 : parent_(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(
494 this,
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);
510 // Title row.
511 BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
512 AddTitleRow(layout, parent_->model());
514 // Button row.
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();
533 else
534 NOTREACHED();
536 parent_->Close();
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:
544 break;
545 case SaveAccountMoreComboboxModel::INDEX_NEVER_FOR_THIS_SITE:
546 parent_->NotifyNeverForThisSiteClicked();
547 break;
548 case SaveAccountMoreComboboxModel::INDEX_SETTINGS:
549 parent_->model()->OnManageLinkClicked();
550 parent_->Close();
551 break;
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 {
562 public:
563 explicit ConfirmNeverView(ManagePasswordsBubbleView* parent);
564 ~ConfirmNeverView() override;
566 private:
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)
580 : parent_(parent) {
581 views::GridLayout* layout = new views::GridLayout(this);
582 layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
583 SetLayoutManager(layout);
585 // Title row.
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(
606 this,
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_);
615 undo_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();
637 else
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
645 // "Done" button.
646 class ManagePasswordsBubbleView::ManageView : public views::View,
647 public views::ButtonListener,
648 public views::LinkListener {
649 public:
650 explicit ManageView(ManagePasswordsBubbleView* parent);
651 ~ManageView() override;
653 private:
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)
670 : parent_(parent) {
671 views::GridLayout* layout = new views::GridLayout(this);
672 layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
673 SetLayoutManager(layout);
675 // Add the title.
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);
687 } else {
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);
709 done_button_ =
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();
735 parent_->Close();
738 void ManagePasswordsBubbleView::ManageView::LinkClicked(views::Link* source,
739 int event_flags) {
740 DCHECK_EQ(source, manage_link_);
741 parent_->model()->OnManageLinkClicked();
742 parent_->Close();
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 {
753 public:
754 explicit ManageAccountsView(ManagePasswordsBubbleView* parent);
756 private:
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)
773 : parent_(parent) {
774 views::GridLayout* layout = new views::GridLayout(this);
775 layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
776 SetLayoutManager(layout);
778 // Add the title.
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));
788 } else {
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);
809 done_button_ =
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();
831 parent_->Close();
834 void ManagePasswordsBubbleView::ManageAccountsView::LinkClicked(
835 views::Link* source, int event_flags) {
836 DCHECK_EQ(source, manage_link_);
837 parent_->model()->OnManageLinkClicked();
838 parent_->Close();
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 {
848 public:
849 explicit BlacklistedView(ManagePasswordsBubbleView* parent);
850 ~BlacklistedView() override;
852 private:
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)
866 : parent_(parent) {
867 views::GridLayout* layout = new views::GridLayout(this);
868 layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
869 SetLayoutManager(layout);
871 // Add the title.
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));
892 done_button_ =
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();
920 else
921 NOTREACHED();
922 parent_->Close();
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 {
933 public:
934 explicit SaveConfirmationView(ManagePasswordsBubbleView* parent);
935 ~SaveConfirmationView() override;
937 private:
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)
953 : parent_(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();
996 parent_->Close();
999 void ManagePasswordsBubbleView::SaveConfirmationView::ButtonPressed(
1000 views::Button* sender, const ui::Event& event) {
1001 DCHECK_EQ(sender, ok_button_);
1002 parent_->model()->OnOKClicked();
1003 parent_->Close();
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 {
1012 public:
1013 explicit WebContentMouseHandler(ManagePasswordsBubbleView* bubble);
1015 void OnKeyEvent(ui::KeyEvent* event) override;
1016 void OnMouseEvent(ui::MouseEvent* event) override;
1018 private:
1019 ManagePasswordsBubbleView* bubble_;
1020 scoped_ptr<views::EventMonitor> event_monitor_;
1022 DISALLOW_COPY_AND_ASSIGN(WebContentMouseHandler);
1025 ManagePasswordsBubbleView::WebContentMouseHandler::WebContentMouseHandler(
1026 ManagePasswordsBubbleView* bubble)
1027 : bubble_(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)
1040 bubble_->Close();
1043 void ManagePasswordsBubbleView::WebContentMouseHandler::OnMouseEvent(
1044 ui::MouseEvent* event) {
1045 if (event->type() == ui::ET_MOUSE_PRESSED)
1046 bubble_->Close();
1049 // ManagePasswordsBubbleView --------------------------------------------------
1051 // static
1052 ManagePasswordsBubbleView* ManagePasswordsBubbleView::manage_passwords_bubble_ =
1053 NULL;
1055 // static
1056 void ManagePasswordsBubbleView::ShowBubble(content::WebContents* web_contents,
1057 DisplayReason reason) {
1058 Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
1059 DCHECK(browser);
1060 DCHECK(browser->window());
1062 if (IsShowing())
1063 return;
1065 BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
1066 bool is_fullscreen = browser_view->IsFullscreen();
1067 ManagePasswordsIconView* anchor_view =
1068 is_fullscreen
1069 ? NULL
1070 : browser_view->GetLocationBarView()->manage_passwords_icon_view();
1071 manage_passwords_bubble_ = new ManagePasswordsBubbleView(
1072 web_contents, anchor_view, reason);
1074 if (is_fullscreen)
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();
1086 else
1087 manage_passwords_bubble_->GetWidget()->Show();
1090 // static
1091 void ManagePasswordsBubbleView::CloseBubble() {
1092 if (manage_passwords_bubble_)
1093 manage_passwords_bubble_->Close();
1096 // static
1097 void ManagePasswordsBubbleView::ActivateBubble() {
1098 if (!IsShowing())
1099 return;
1100 manage_passwords_bubble_->GetWidget()->Activate();
1103 // static
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));
1124 if (anchor_view)
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);
1142 Refresh();
1145 void ManagePasswordsBubbleView::Close() {
1146 mouse_handler_.reset();
1147 ManagedFullScreenBubbleDelegateView::Close();
1150 void ManagePasswordsBubbleView::OnWidgetClosing(views::Widget* /*widget*/) {
1151 if (anchor_view_)
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));
1161 } else {
1162 if (model()->IsNewUIActive())
1163 AddChildView(new SaveAccountView(this));
1164 else
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));
1176 } else {
1177 if (model()->IsNewUIActive())
1178 AddChildView(new ManageAccountsView(this));
1179 else
1180 AddChildView(new ManageView(this));
1182 GetLayoutManager()->Layout(this);
1185 void ManagePasswordsBubbleView::NotifyConfirmedNeverForThisSite() {
1186 model()->OnNeverForThisSiteClicked();
1187 Close();
1190 void ManagePasswordsBubbleView::NotifyUndoNeverForThisSite() {
1191 model()->OnUndoNeverForThisSite();
1192 Refresh();
1193 SizeToContents();
1196 void ManagePasswordsBubbleView::NotifyNeverForThisSiteClicked() {
1197 if (model()->local_credentials().empty()) {
1198 // Skip confirmation if there are no existing passwords for this site.
1199 NotifyConfirmedNeverForThisSite();
1200 } else {
1201 model()->OnConfirmationForNeverForThisSite();
1202 Refresh();
1203 SizeToContents();