Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / ui / views / passwords / manage_passwords_bubble_view.cc
blobb1391a21ac24147306ee84097d9a95a992f16221
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/views/frame/browser_view.h"
15 #include "chrome/browser/ui/views/passwords/credentials_item_view.h"
16 #include "chrome/browser/ui/views/passwords/credentials_selection_view.h"
17 #include "chrome/browser/ui/views/passwords/manage_password_items_view.h"
18 #include "chrome/browser/ui/views/passwords/manage_passwords_icon_view.h"
19 #include "chrome/grit/generated_resources.h"
20 #include "content/public/browser/render_view_host.h"
21 #include "ui/base/l10n/l10n_util.h"
22 #include "ui/base/resource/resource_bundle.h"
23 #include "ui/views/controls/button/blue_button.h"
24 #include "ui/views/controls/button/label_button.h"
25 #include "ui/views/controls/link.h"
26 #include "ui/views/controls/link_listener.h"
27 #include "ui/views/controls/separator.h"
28 #include "ui/views/controls/styled_label.h"
29 #include "ui/views/controls/styled_label_listener.h"
30 #include "ui/views/event_monitor.h"
31 #include "ui/views/layout/fill_layout.h"
32 #include "ui/views/layout/grid_layout.h"
33 #include "ui/views/layout/layout_constants.h"
34 #include "ui/views/widget/widget.h"
36 int ManagePasswordsBubbleView::auto_signin_toast_timeout_ = 3;
38 // Helpers --------------------------------------------------------------------
40 namespace {
42 const int kDesiredBubbleWidth = 370;
43 const SkColor kWarmWelcomeColor = SkColorSetARGB(0xFF, 0x64, 0x64, 0x64);
45 enum ColumnSetType {
46 // | | (FILL, FILL) | |
47 // Used for the bubble's header, the credentials list, and for simple
48 // messages like "No passwords".
49 SINGLE_VIEW_COLUMN_SET,
51 // | | (TRAILING, CENTER) | | (TRAILING, CENTER) | |
52 // Used for buttons at the bottom of the bubble which should nest at the
53 // bottom-right corner.
54 DOUBLE_BUTTON_COLUMN_SET,
56 // | | (LEADING, CENTER) | | (TRAILING, CENTER) | |
57 // Used for buttons at the bottom of the bubble which should occupy
58 // the corners.
59 LINK_BUTTON_COLUMN_SET,
61 // | | (TRAILING, CENTER) | |
62 // Used when there is only one button which should next at the bottom-right
63 // corner.
64 SINGLE_BUTTON_COLUMN_SET,
66 // | | (LEADING, CENTER) | | (TRAILING, CENTER) | | (TRAILING, CENTER) | |
67 // Used when there are three buttons.
68 TRIPLE_BUTTON_COLUMN_SET,
71 enum TextRowType { ROW_SINGLE, ROW_MULTILINE };
73 // Construct an appropriate ColumnSet for the given |type|, and add it
74 // to |layout|.
75 void BuildColumnSet(views::GridLayout* layout, ColumnSetType type) {
76 views::ColumnSet* column_set = layout->AddColumnSet(type);
77 column_set->AddPaddingColumn(0, views::kPanelHorizMargin);
78 int full_width = kDesiredBubbleWidth - (2 * views::kPanelHorizMargin);
79 switch (type) {
80 case SINGLE_VIEW_COLUMN_SET:
81 column_set->AddColumn(views::GridLayout::FILL,
82 views::GridLayout::FILL,
84 views::GridLayout::FIXED,
85 full_width,
86 0);
87 break;
88 case DOUBLE_BUTTON_COLUMN_SET:
89 column_set->AddColumn(views::GridLayout::TRAILING,
90 views::GridLayout::CENTER,
92 views::GridLayout::USE_PREF,
94 0);
95 column_set->AddPaddingColumn(0, views::kRelatedButtonHSpacing);
96 column_set->AddColumn(views::GridLayout::TRAILING,
97 views::GridLayout::CENTER,
99 views::GridLayout::USE_PREF,
102 break;
103 case LINK_BUTTON_COLUMN_SET:
104 column_set->AddColumn(views::GridLayout::LEADING,
105 views::GridLayout::CENTER,
107 views::GridLayout::USE_PREF,
110 column_set->AddPaddingColumn(0, views::kRelatedButtonHSpacing);
111 column_set->AddColumn(views::GridLayout::TRAILING,
112 views::GridLayout::CENTER,
114 views::GridLayout::USE_PREF,
117 break;
118 case SINGLE_BUTTON_COLUMN_SET:
119 column_set->AddColumn(views::GridLayout::TRAILING,
120 views::GridLayout::CENTER,
122 views::GridLayout::USE_PREF,
125 break;
126 case TRIPLE_BUTTON_COLUMN_SET:
127 column_set->AddColumn(views::GridLayout::LEADING,
128 views::GridLayout::CENTER,
130 views::GridLayout::USE_PREF,
133 column_set->AddPaddingColumn(0, views::kRelatedButtonHSpacing);
134 column_set->AddColumn(views::GridLayout::TRAILING,
135 views::GridLayout::CENTER,
137 views::GridLayout::USE_PREF,
140 column_set->AddPaddingColumn(0, views::kRelatedButtonHSpacing);
141 column_set->AddColumn(views::GridLayout::TRAILING,
142 views::GridLayout::CENTER,
144 views::GridLayout::USE_PREF,
147 break;
149 column_set->AddPaddingColumn(0, views::kPanelHorizMargin);
152 // Given a layout and a model, add an appropriate title using a
153 // SINGLE_VIEW_COLUMN_SET, followed by a spacer row.
154 void AddTitleRow(views::GridLayout* layout, ManagePasswordsBubbleModel* model) {
155 views::Label* title_label = new views::Label(model->title());
156 title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
157 title_label->SetMultiLine(true);
158 title_label->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
159 ui::ResourceBundle::MediumFont));
161 // Add the title to the layout with appropriate padding.
162 layout->StartRowWithPadding(
163 0, SINGLE_VIEW_COLUMN_SET, 0, views::kRelatedControlSmallVerticalSpacing);
164 layout->AddView(title_label);
165 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
168 } // namespace
171 // ManagePasswordsBubbleView::AccountChooserView ------------------------------
173 // A view offering the user the ability to choose credentials for
174 // authentication. Contains a list of CredentialsItemView, along with a
175 // "Cancel" button.
176 class ManagePasswordsBubbleView::AccountChooserView
177 : public views::View,
178 public views::ButtonListener {
179 public:
180 explicit AccountChooserView(ManagePasswordsBubbleView* parent);
181 ~AccountChooserView() override;
183 private:
184 // views::ButtonListener:
185 void ButtonPressed(views::Button* sender, const ui::Event& event) override;
187 // Adds |password_forms| to the layout remembering their |type|.
188 void AddCredentialItemsWithType(
189 views::GridLayout* layout,
190 const ScopedVector<const autofill::PasswordForm>& password_forms,
191 password_manager::CredentialType type);
193 ManagePasswordsBubbleView* parent_;
194 views::LabelButton* cancel_button_;
196 DISALLOW_COPY_AND_ASSIGN(AccountChooserView);
199 ManagePasswordsBubbleView::AccountChooserView::AccountChooserView(
200 ManagePasswordsBubbleView* parent)
201 : parent_(parent) {
202 views::GridLayout* layout = new views::GridLayout(this);
203 SetLayoutManager(layout);
205 cancel_button_ =
206 new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_CANCEL));
207 cancel_button_->SetStyle(views::Button::STYLE_BUTTON);
208 cancel_button_->SetFontList(
209 ui::ResourceBundle::GetSharedInstance().GetFontList(
210 ui::ResourceBundle::SmallFont));
212 // Title row.
213 BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
214 AddTitleRow(layout, parent_->model());
216 AddCredentialItemsWithType(
217 layout, parent_->model()->local_credentials(),
218 password_manager::CredentialType::CREDENTIAL_TYPE_PASSWORD);
220 AddCredentialItemsWithType(
221 layout, parent_->model()->federated_credentials(),
222 password_manager::CredentialType::CREDENTIAL_TYPE_FEDERATED);
224 // Button row.
225 BuildColumnSet(layout, SINGLE_BUTTON_COLUMN_SET);
226 layout->StartRowWithPadding(
227 0, SINGLE_BUTTON_COLUMN_SET, 0, views::kRelatedControlVerticalSpacing);
228 layout->AddView(cancel_button_);
230 // Extra padding for visual awesomeness.
231 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
233 parent_->set_initially_focused_view(cancel_button_);
236 ManagePasswordsBubbleView::AccountChooserView::~AccountChooserView() {
239 void ManagePasswordsBubbleView::AccountChooserView::AddCredentialItemsWithType(
240 views::GridLayout* layout,
241 const ScopedVector<const autofill::PasswordForm>& password_forms,
242 password_manager::CredentialType type) {
243 net::URLRequestContextGetter* request_context =
244 parent_->model()->GetProfile()->GetRequestContext();
245 for (const autofill::PasswordForm* form : password_forms) {
246 const base::string16& upper_string =
247 form->display_name.empty() ? form->username_value : form->display_name;
248 base::string16 lower_string;
249 if (form->federation_url.is_empty()) {
250 if (!form->display_name.empty())
251 lower_string = form->username_value;
252 } else {
253 lower_string = l10n_util::GetStringFUTF16(
254 IDS_PASSWORDS_VIA_FEDERATION,
255 base::UTF8ToUTF16(form->federation_url.host()));
257 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
258 layout->AddView(new CredentialsItemView(this, form, type, upper_string,
259 lower_string, request_context));
263 void ManagePasswordsBubbleView::AccountChooserView::ButtonPressed(
264 views::Button* sender, const ui::Event& event) {
265 if (sender != cancel_button_) {
266 // ManagePasswordsBubbleModel should care about calling a callback in case
267 // the bubble is dismissed by any other means.
268 CredentialsItemView* view = static_cast<CredentialsItemView*>(sender);
269 parent_->model()->OnChooseCredentials(*view->form(),
270 view->credential_type());
271 } else {
272 parent_->model()->OnCancelClicked();
274 parent_->Close();
277 // ManagePasswordsBubbleView::AutoSigninView ----------------------------------
279 // A view containing just one credential that was used for for automatic signing
280 // in.
281 class ManagePasswordsBubbleView::AutoSigninView
282 : public views::View,
283 public views::ButtonListener,
284 public views::WidgetObserver {
285 public:
286 explicit AutoSigninView(ManagePasswordsBubbleView* parent);
288 private:
289 // views::ButtonListener:
290 void ButtonPressed(views::Button* sender, const ui::Event& event) override;
292 // views::WidgetObserver:
293 // Tracks the state of the browser window.
294 void OnWidgetActivationChanged(views::Widget* widget, bool active) override;
295 void OnWidgetClosing(views::Widget* widget) override;
297 void OnTimer();
298 static base::TimeDelta GetTimeout() {
299 return base::TimeDelta::FromSeconds(
300 ManagePasswordsBubbleView::auto_signin_toast_timeout_);
303 base::OneShotTimer<AutoSigninView> timer_;
304 ManagePasswordsBubbleView* parent_;
305 ScopedObserver<views::Widget, views::WidgetObserver> observed_browser_;
307 DISALLOW_COPY_AND_ASSIGN(AutoSigninView);
310 ManagePasswordsBubbleView::AutoSigninView::AutoSigninView(
311 ManagePasswordsBubbleView* parent)
312 : parent_(parent),
313 observed_browser_(this) {
314 SetLayoutManager(new views::FillLayout);
315 const autofill::PasswordForm& form = parent_->model()->pending_password();
316 CredentialsItemView* credential = new CredentialsItemView(
317 this, &form,
318 password_manager::CredentialType::CREDENTIAL_TYPE_PASSWORD,
319 base::string16(),
320 l10n_util::GetStringFUTF16(IDS_MANAGE_PASSWORDS_AUTO_SIGNIN_TITLE,
321 form.username_value),
322 parent_->model()->GetProfile()->GetRequestContext());
323 AddChildView(credential);
324 credential->SetEnabled(false);
326 Browser* browser =
327 chrome::FindBrowserWithWebContents(parent_->web_contents());
328 DCHECK(browser);
329 BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
330 observed_browser_.Add(browser_view->GetWidget());
332 if (browser_view->IsActive())
333 timer_.Start(FROM_HERE, GetTimeout(), this, &AutoSigninView::OnTimer);
336 void ManagePasswordsBubbleView::AutoSigninView::ButtonPressed(
337 views::Button* sender, const ui::Event& event) {
338 parent_->model()->OnAutoSignInClicked();
339 parent_->Close();
342 void ManagePasswordsBubbleView::AutoSigninView::OnWidgetActivationChanged(
343 views::Widget* widget, bool active) {
344 if (active && !timer_.IsRunning())
345 timer_.Start(FROM_HERE, GetTimeout(), this, &AutoSigninView::OnTimer);
348 void ManagePasswordsBubbleView::AutoSigninView::OnWidgetClosing(
349 views::Widget* widget) {
350 observed_browser_.RemoveAll();
353 void ManagePasswordsBubbleView::AutoSigninView::OnTimer() {
354 parent_->model()->OnAutoSignInToastTimeout();
355 parent_->Close();
358 // ManagePasswordsBubbleView::PendingView -------------------------------------
360 // A view offering the user the ability to save credentials. Contains a
361 // single ManagePasswordItemsView, along with a "Save Passwords" button
362 // and a "Never" button.
363 class ManagePasswordsBubbleView::PendingView
364 : public views::View,
365 public views::ButtonListener,
366 public views::StyledLabelListener {
367 public:
368 explicit PendingView(ManagePasswordsBubbleView* parent);
369 ~PendingView() override;
371 private:
372 // views::ButtonListener:
373 void ButtonPressed(views::Button* sender, const ui::Event& event) override;
375 // views::StyledLabelListener:
376 void StyledLabelLinkClicked(const gfx::Range& range,
377 int event_flags) override;
379 ManagePasswordsBubbleView* parent_;
381 views::BlueButton* save_button_;
382 views::LabelButton* never_button_;
384 DISALLOW_COPY_AND_ASSIGN(PendingView);
387 ManagePasswordsBubbleView::PendingView::PendingView(
388 ManagePasswordsBubbleView* parent)
389 : parent_(parent) {
390 views::GridLayout* layout = new views::GridLayout(this);
391 layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
392 SetLayoutManager(layout);
394 // Create the pending credential item, save button and refusal combobox.
395 ManagePasswordItemsView* item = nullptr;
396 if (!parent->model()->pending_password().username_value.empty()) {
397 std::vector<const autofill::PasswordForm*> credentials(
398 1, &parent->model()->pending_password());
399 item = new ManagePasswordItemsView(parent_->model(), credentials);
401 save_button_ = new views::BlueButton(
402 this, l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_SAVE_BUTTON));
403 save_button_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
404 ui::ResourceBundle::SmallFont));
405 never_button_ = new views::LabelButton(
406 this,
407 l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_BUBBLE_BLACKLIST_BUTTON));
408 never_button_->SetStyle(views::Button::STYLE_BUTTON);
409 never_button_->SetFontList(
410 ui::ResourceBundle::GetSharedInstance().GetFontList(
411 ui::ResourceBundle::SmallFont));
413 // Title row.
414 views::StyledLabel* title_label =
415 new views::StyledLabel(parent_->model()->title(), this);
416 title_label->SetBaseFontList(
417 ui::ResourceBundle::GetSharedInstance().GetFontList(
418 ui::ResourceBundle::MediumFont));
419 if (!parent_->model()->title_brand_link_range().is_empty()) {
420 title_label->AddStyleRange(
421 parent_->model()->title_brand_link_range(),
422 views::StyledLabel::RangeStyleInfo::CreateForLink());
424 BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
425 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
426 layout->AddView(title_label);
428 // Credential row.
429 if (item) {
430 layout->StartRowWithPadding(0, SINGLE_VIEW_COLUMN_SET, 0,
431 views::kUnrelatedControlVerticalSpacing);
432 layout->AddView(item);
435 // Smart Lock warm welcome.
436 if (parent_->model()->ShouldShowGoogleSmartLockWelcome()) {
437 views::Label* smart_lock_label = new views::Label(
438 l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_SMART_LOCK_WELCOME));
439 smart_lock_label->SetMultiLine(true);
440 smart_lock_label->SetFontList(
441 ui::ResourceBundle::GetSharedInstance().GetFontList(
442 ui::ResourceBundle::SmallFont));
443 smart_lock_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
444 smart_lock_label->SetEnabledColor(kWarmWelcomeColor);
445 layout->StartRowWithPadding(0, SINGLE_VIEW_COLUMN_SET, 0,
446 views::kUnrelatedControlVerticalSpacing);
447 layout->AddView(smart_lock_label);
450 // Button row.
451 BuildColumnSet(layout, DOUBLE_BUTTON_COLUMN_SET);
452 layout->StartRowWithPadding(0, DOUBLE_BUTTON_COLUMN_SET, 0,
453 views::kUnrelatedControlVerticalSpacing);
454 layout->AddView(save_button_);
455 layout->AddView(never_button_);
457 // Extra padding for visual awesomeness.
458 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
460 parent_->set_initially_focused_view(save_button_);
463 ManagePasswordsBubbleView::PendingView::~PendingView() {
466 void ManagePasswordsBubbleView::PendingView::ButtonPressed(
467 views::Button* sender,
468 const ui::Event& event) {
469 if (sender == save_button_)
470 parent_->model()->OnSaveClicked();
471 else if (sender == never_button_)
472 parent_->model()->OnNeverForThisSiteClicked();
473 else
474 NOTREACHED();
476 parent_->Close();
479 void ManagePasswordsBubbleView::PendingView::StyledLabelLinkClicked(
480 const gfx::Range& range,
481 int event_flags) {
482 DCHECK_EQ(range, parent_->model()->title_brand_link_range());
483 parent_->model()->OnBrandLinkClicked();
486 // ManagePasswordsBubbleView::ManageView --------------------------------------
488 // A view offering the user a list of her currently saved credentials
489 // for the current page, along with a "Manage passwords" link and a
490 // "Done" button.
491 class ManagePasswordsBubbleView::ManageView : public views::View,
492 public views::ButtonListener,
493 public views::LinkListener {
494 public:
495 explicit ManageView(ManagePasswordsBubbleView* parent);
496 ~ManageView() override;
498 private:
499 // views::ButtonListener:
500 void ButtonPressed(views::Button* sender, const ui::Event& event) override;
502 // views::LinkListener:
503 void LinkClicked(views::Link* source, int event_flags) override;
505 ManagePasswordsBubbleView* parent_;
507 views::Link* manage_link_;
508 views::LabelButton* done_button_;
510 DISALLOW_COPY_AND_ASSIGN(ManageView);
513 ManagePasswordsBubbleView::ManageView::ManageView(
514 ManagePasswordsBubbleView* parent)
515 : parent_(parent) {
516 views::GridLayout* layout = new views::GridLayout(this);
517 layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
518 SetLayoutManager(layout);
520 // Add the title.
521 BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
522 AddTitleRow(layout, parent_->model());
524 // If we have a list of passwords to store for the current site, display
525 // them to the user for management. Otherwise, render a "No passwords for
526 // this site" message.
527 if (!parent_->model()->local_credentials().empty()) {
528 ManagePasswordItemsView* item = new ManagePasswordItemsView(
529 parent_->model(), parent_->model()->local_credentials().get());
530 layout->StartRowWithPadding(0, SINGLE_VIEW_COLUMN_SET, 0,
531 views::kUnrelatedControlVerticalSpacing);
532 layout->AddView(item);
533 } else {
534 views::Label* empty_label = new views::Label(
535 l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_NO_PASSWORDS));
536 empty_label->SetMultiLine(true);
537 empty_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
538 empty_label->SetFontList(
539 ui::ResourceBundle::GetSharedInstance().GetFontList(
540 ui::ResourceBundle::SmallFont));
542 layout->StartRowWithPadding(0, SINGLE_VIEW_COLUMN_SET, 0,
543 views::kUnrelatedControlVerticalSpacing);
544 layout->AddView(empty_label);
547 // Then add the "manage passwords" link and "Done" button.
548 manage_link_ = new views::Link(parent_->model()->manage_link());
549 manage_link_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
550 manage_link_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
551 ui::ResourceBundle::SmallFont));
552 manage_link_->SetUnderline(false);
553 manage_link_->set_listener(this);
555 done_button_ =
556 new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_DONE));
557 done_button_->SetStyle(views::Button::STYLE_BUTTON);
558 done_button_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
559 ui::ResourceBundle::SmallFont));
561 BuildColumnSet(layout, LINK_BUTTON_COLUMN_SET);
562 layout->StartRowWithPadding(0, LINK_BUTTON_COLUMN_SET, 0,
563 views::kUnrelatedControlVerticalSpacing);
564 layout->AddView(manage_link_);
565 layout->AddView(done_button_);
567 // Extra padding for visual awesomeness.
568 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
570 parent_->set_initially_focused_view(done_button_);
573 ManagePasswordsBubbleView::ManageView::~ManageView() {
576 void ManagePasswordsBubbleView::ManageView::ButtonPressed(
577 views::Button* sender,
578 const ui::Event& event) {
579 DCHECK(sender == done_button_);
580 parent_->model()->OnDoneClicked();
581 parent_->Close();
584 void ManagePasswordsBubbleView::ManageView::LinkClicked(views::Link* source,
585 int event_flags) {
586 DCHECK_EQ(source, manage_link_);
587 parent_->model()->OnManageLinkClicked();
588 parent_->Close();
591 // ManagePasswordsBubbleView::SaveConfirmationView ----------------------------
593 // A view confirming to the user that a password was saved and offering a link
594 // to the Google account manager.
595 class ManagePasswordsBubbleView::SaveConfirmationView
596 : public views::View,
597 public views::ButtonListener,
598 public views::StyledLabelListener {
599 public:
600 explicit SaveConfirmationView(ManagePasswordsBubbleView* parent);
601 ~SaveConfirmationView() override;
603 private:
604 // views::ButtonListener:
605 void ButtonPressed(views::Button* sender, const ui::Event& event) override;
607 // views::StyledLabelListener implementation
608 void StyledLabelLinkClicked(const gfx::Range& range,
609 int event_flags) override;
611 ManagePasswordsBubbleView* parent_;
612 views::LabelButton* ok_button_;
614 DISALLOW_COPY_AND_ASSIGN(SaveConfirmationView);
617 ManagePasswordsBubbleView::SaveConfirmationView::SaveConfirmationView(
618 ManagePasswordsBubbleView* parent)
619 : parent_(parent) {
620 views::GridLayout* layout = new views::GridLayout(this);
621 layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
622 SetLayoutManager(layout);
624 BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
625 AddTitleRow(layout, parent_->model());
627 views::StyledLabel* confirmation =
628 new views::StyledLabel(parent_->model()->save_confirmation_text(), this);
629 confirmation->SetBaseFontList(
630 ui::ResourceBundle::GetSharedInstance().GetFontList(
631 ui::ResourceBundle::SmallFont));
632 confirmation->AddStyleRange(
633 parent_->model()->save_confirmation_link_range(),
634 views::StyledLabel::RangeStyleInfo::CreateForLink());
636 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
637 layout->AddView(confirmation);
639 ok_button_ = new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_OK));
640 ok_button_->SetStyle(views::Button::STYLE_BUTTON);
641 ok_button_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
642 ui::ResourceBundle::SmallFont));
644 BuildColumnSet(layout, SINGLE_BUTTON_COLUMN_SET);
645 layout->StartRowWithPadding(
646 0, SINGLE_BUTTON_COLUMN_SET, 0, views::kRelatedControlVerticalSpacing);
647 layout->AddView(ok_button_);
649 // Extra padding for visual awesomeness.
650 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
652 parent_->set_initially_focused_view(ok_button_);
655 ManagePasswordsBubbleView::SaveConfirmationView::~SaveConfirmationView() {
658 void ManagePasswordsBubbleView::SaveConfirmationView::StyledLabelLinkClicked(
659 const gfx::Range& range, int event_flags) {
660 DCHECK_EQ(range, parent_->model()->save_confirmation_link_range());
661 parent_->model()->OnManageLinkClicked();
662 parent_->Close();
665 void ManagePasswordsBubbleView::SaveConfirmationView::ButtonPressed(
666 views::Button* sender, const ui::Event& event) {
667 DCHECK_EQ(sender, ok_button_);
668 parent_->model()->OnOKClicked();
669 parent_->Close();
672 // ManagePasswordsBubbleView::WebContentMouseHandler --------------------------
674 // The class listens for WebContentsView events and notifies the bubble if the
675 // view was clicked on or received keystrokes.
676 class ManagePasswordsBubbleView::WebContentMouseHandler
677 : public ui::EventHandler {
678 public:
679 explicit WebContentMouseHandler(ManagePasswordsBubbleView* bubble);
681 void OnKeyEvent(ui::KeyEvent* event) override;
682 void OnMouseEvent(ui::MouseEvent* event) override;
683 void OnTouchEvent(ui::TouchEvent* event) override;
685 private:
686 ManagePasswordsBubbleView* bubble_;
687 scoped_ptr<views::EventMonitor> event_monitor_;
689 DISALLOW_COPY_AND_ASSIGN(WebContentMouseHandler);
692 ManagePasswordsBubbleView::WebContentMouseHandler::WebContentMouseHandler(
693 ManagePasswordsBubbleView* bubble)
694 : bubble_(bubble) {
695 content::WebContents* web_contents = bubble_->web_contents();
696 DCHECK(web_contents);
697 event_monitor_ = views::EventMonitor::CreateWindowMonitor(
698 this, web_contents->GetTopLevelNativeWindow());
701 void ManagePasswordsBubbleView::WebContentMouseHandler::OnKeyEvent(
702 ui::KeyEvent* event) {
703 content::WebContents* web_contents = bubble_->web_contents();
704 content::RenderViewHost* rvh = web_contents->GetRenderViewHost();
705 if ((event->key_code() == ui::VKEY_ESCAPE ||
706 rvh->IsFocusedElementEditable()) && event->type() == ui::ET_KEY_PRESSED)
707 bubble_->Close();
710 void ManagePasswordsBubbleView::WebContentMouseHandler::OnMouseEvent(
711 ui::MouseEvent* event) {
712 if (event->type() == ui::ET_MOUSE_PRESSED)
713 bubble_->Close();
716 void ManagePasswordsBubbleView::WebContentMouseHandler::OnTouchEvent(
717 ui::TouchEvent* event) {
718 if (event->type() == ui::ET_TOUCH_PRESSED)
719 bubble_->Close();
722 // ManagePasswordsBubbleView::UpdatePendingView -------------------------------
724 // A view offering the user the ability to update credentials. Contains a
725 // single ManagePasswordItemsView (in case of one credentials) or
726 // CredentialsSelectionView otherwise, along with a "Update Passwords" button
727 // and a rejection button.
728 class ManagePasswordsBubbleView::UpdatePendingView
729 : public views::View,
730 public views::ButtonListener,
731 public views::StyledLabelListener {
732 public:
733 explicit UpdatePendingView(ManagePasswordsBubbleView* parent);
734 ~UpdatePendingView() override;
736 private:
737 // views::ButtonListener:
738 void ButtonPressed(views::Button* sender, const ui::Event& event) override;
740 // views::StyledLabelListener:
741 void StyledLabelLinkClicked(const gfx::Range& range,
742 int event_flags) override;
744 ManagePasswordsBubbleView* parent_;
746 CredentialsSelectionView* selection_view_;
748 views::BlueButton* update_button_;
750 views::LabelButton* nope_button_;
752 DISALLOW_COPY_AND_ASSIGN(UpdatePendingView);
755 ManagePasswordsBubbleView::UpdatePendingView::UpdatePendingView(
756 ManagePasswordsBubbleView* parent)
757 : parent_(parent), selection_view_(nullptr) {
758 views::GridLayout* layout = new views::GridLayout(this);
759 layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
760 SetLayoutManager(layout);
762 // Create the pending credential item, update button.
763 View* item = nullptr;
764 if (parent->model()->ShouldShowMultipleAccountUpdateUI()) {
765 selection_view_ = new CredentialsSelectionView(
766 parent->model(), parent->model()->local_credentials().get(),
767 parent->model()->pending_password().username_value);
768 item = selection_view_;
769 } else {
770 std::vector<const autofill::PasswordForm*> forms;
771 forms.push_back(&parent->model()->pending_password());
772 item = new ManagePasswordItemsView(parent_->model(), forms);
774 nope_button_ = new views::LabelButton(
775 this, l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_CANCEL_BUTTON));
776 nope_button_->SetStyle(views::Button::STYLE_BUTTON);
777 nope_button_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
778 ui::ResourceBundle::SmallFont));
780 update_button_ = new views::BlueButton(
781 this, l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_UPDATE_BUTTON));
782 update_button_->SetFontList(
783 ui::ResourceBundle::GetSharedInstance().GetFontList(
784 ui::ResourceBundle::SmallFont));
786 // Title row.
787 views::StyledLabel* title_label =
788 new views::StyledLabel(parent_->model()->title(), this);
789 title_label->SetBaseFontList(
790 ui::ResourceBundle::GetSharedInstance().GetFontList(
791 ui::ResourceBundle::MediumFont));
792 if (!parent_->model()->title_brand_link_range().is_empty()) {
793 title_label->AddStyleRange(
794 parent_->model()->title_brand_link_range(),
795 views::StyledLabel::RangeStyleInfo::CreateForLink());
797 BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
798 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
799 layout->AddView(title_label);
800 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
802 // Credential row.
803 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
804 layout->AddView(item);
806 // Button row.
807 BuildColumnSet(layout, DOUBLE_BUTTON_COLUMN_SET);
808 layout->StartRowWithPadding(0, DOUBLE_BUTTON_COLUMN_SET, 0,
809 views::kUnrelatedControlVerticalSpacing);
810 layout->AddView(update_button_);
811 layout->AddView(nope_button_);
813 // Extra padding for visual awesomeness.
814 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
816 parent_->set_initially_focused_view(update_button_);
819 ManagePasswordsBubbleView::UpdatePendingView::~UpdatePendingView() {}
821 void ManagePasswordsBubbleView::UpdatePendingView::ButtonPressed(
822 views::Button* sender,
823 const ui::Event& event) {
824 DCHECK(sender == update_button_ || sender == nope_button_);
825 if (sender == update_button_) {
826 if (selection_view_) {
827 // Multi account case.
828 parent_->model()->OnUpdateClicked(
829 *selection_view_->GetSelectedCredentials());
830 } else {
831 parent_->model()->OnUpdateClicked(parent_->model()->pending_password());
833 } else {
834 parent_->model()->OnNopeUpdateClicked();
836 parent_->Close();
839 void ManagePasswordsBubbleView::UpdatePendingView::StyledLabelLinkClicked(
840 const gfx::Range& range,
841 int event_flags) {
842 DCHECK_EQ(range, parent_->model()->title_brand_link_range());
843 parent_->model()->OnBrandLinkClicked();
846 // ManagePasswordsBubbleView --------------------------------------------------
848 // static
849 ManagePasswordsBubbleView* ManagePasswordsBubbleView::manage_passwords_bubble_ =
850 NULL;
852 // static
853 void ManagePasswordsBubbleView::ShowBubble(content::WebContents* web_contents,
854 DisplayReason reason) {
855 Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
856 DCHECK(browser);
857 DCHECK(browser->window());
858 DCHECK(!manage_passwords_bubble_ ||
859 !manage_passwords_bubble_->GetWidget()->IsVisible());
861 BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
862 bool is_fullscreen = browser_view->IsFullscreen();
863 ManagePasswordsIconView* anchor_view =
864 is_fullscreen
865 ? NULL
866 : browser_view->GetLocationBarView()->manage_passwords_icon_view();
867 manage_passwords_bubble_ = new ManagePasswordsBubbleView(
868 web_contents, anchor_view, reason);
870 if (is_fullscreen)
871 manage_passwords_bubble_->set_parent_window(web_contents->GetNativeView());
873 views::BubbleDelegateView::CreateBubble(manage_passwords_bubble_);
875 // Adjust for fullscreen after creation as it relies on the content size.
876 if (is_fullscreen) {
877 manage_passwords_bubble_->AdjustForFullscreen(
878 browser_view->GetBoundsInScreen());
880 if (reason == AUTOMATIC)
881 manage_passwords_bubble_->GetWidget()->ShowInactive();
882 else
883 manage_passwords_bubble_->GetWidget()->Show();
886 // static
887 void ManagePasswordsBubbleView::CloseBubble() {
888 if (manage_passwords_bubble_)
889 manage_passwords_bubble_->Close();
892 // static
893 void ManagePasswordsBubbleView::ActivateBubble() {
894 DCHECK(manage_passwords_bubble_);
895 DCHECK(manage_passwords_bubble_->GetWidget()->IsVisible());
896 manage_passwords_bubble_->GetWidget()->Activate();
899 content::WebContents* ManagePasswordsBubbleView::web_contents() const {
900 return model()->web_contents();
903 ManagePasswordsBubbleView::ManagePasswordsBubbleView(
904 content::WebContents* web_contents,
905 ManagePasswordsIconView* anchor_view,
906 DisplayReason reason)
907 : ManagePasswordsBubble(web_contents, reason),
908 ManagedFullScreenBubbleDelegateView(anchor_view, web_contents),
909 anchor_view_(anchor_view),
910 initially_focused_view_(NULL) {
911 // Compensate for built-in vertical padding in the anchor view's image.
912 set_anchor_view_insets(gfx::Insets(5, 0, 5, 0));
913 if (anchor_view)
914 anchor_view->SetActive(true);
915 mouse_handler_.reset(new WebContentMouseHandler(this));
918 ManagePasswordsBubbleView::~ManagePasswordsBubbleView() {
919 if (manage_passwords_bubble_ == this)
920 manage_passwords_bubble_ = NULL;
923 views::View* ManagePasswordsBubbleView::GetInitiallyFocusedView() {
924 return initially_focused_view_;
927 void ManagePasswordsBubbleView::Init() {
928 views::FillLayout* layout = new views::FillLayout();
929 SetLayoutManager(layout);
931 Refresh();
934 void ManagePasswordsBubbleView::Close() {
935 mouse_handler_.reset();
936 ManagedFullScreenBubbleDelegateView::Close();
939 void ManagePasswordsBubbleView::OnWidgetClosing(views::Widget* /*widget*/) {
940 if (anchor_view_)
941 anchor_view_->SetActive(false);
944 bool ManagePasswordsBubbleView::ShouldShowCloseButton() const {
945 return model()->state() == password_manager::ui::PENDING_PASSWORD_STATE;
948 void ManagePasswordsBubbleView::Refresh() {
949 RemoveAllChildViews(true);
950 initially_focused_view_ = NULL;
951 if (model()->state() == password_manager::ui::PENDING_PASSWORD_STATE) {
952 AddChildView(new PendingView(this));
953 } else if (model()->state() ==
954 password_manager::ui::PENDING_PASSWORD_UPDATE_STATE) {
955 AddChildView(new UpdatePendingView(this));
956 } else if (model()->state() == password_manager::ui::CONFIRMATION_STATE) {
957 AddChildView(new SaveConfirmationView(this));
958 } else if (model()->state() ==
959 password_manager::ui::CREDENTIAL_REQUEST_STATE) {
960 AddChildView(new AccountChooserView(this));
961 } else if (model()->state() == password_manager::ui::AUTO_SIGNIN_STATE) {
962 AddChildView(new AutoSigninView(this));
963 } else {
964 AddChildView(new ManageView(this));
966 GetLayoutManager()->Layout(this);