The Password bubble should fade out when user types in the input fields on the web...
[chromium-blink-merge.git] / chrome / browser / ui / views / passwords / manage_passwords_bubble_view.cc
blobb6e70f74144b786d8f2a5632a7153aea9a18c8fd
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 "chrome/browser/ui/browser.h"
8 #include "chrome/browser/ui/browser_finder.h"
9 #include "chrome/browser/ui/passwords/manage_passwords_bubble_model.h"
10 #include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h"
11 #include "chrome/browser/ui/passwords/save_password_refusal_combobox_model.h"
12 #include "chrome/browser/ui/views/frame/browser_view.h"
13 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
14 #include "chrome/browser/ui/views/passwords/manage_password_item_view.h"
15 #include "chrome/browser/ui/views/passwords/manage_passwords_icon_view.h"
16 #include "chrome/grit/generated_resources.h"
17 #include "content/public/browser/render_view_host.h"
18 #include "content/public/browser/web_contents.h"
19 #include "ui/aura/window.h"
20 #include "ui/base/l10n/l10n_util.h"
21 #include "ui/base/resource/resource_bundle.h"
22 #include "ui/compositor/layer_animation_observer.h"
23 #include "ui/compositor/scoped_layer_animation_settings.h"
24 #include "ui/views/controls/button/blue_button.h"
25 #include "ui/views/controls/button/label_button.h"
26 #include "ui/views/controls/combobox/combobox.h"
27 #include "ui/views/controls/combobox/combobox_listener.h"
28 #include "ui/views/controls/link.h"
29 #include "ui/views/controls/link_listener.h"
30 #include "ui/views/controls/styled_label.h"
31 #include "ui/views/controls/styled_label_listener.h"
32 #include "ui/views/layout/fill_layout.h"
33 #include "ui/views/layout/grid_layout.h"
34 #include "ui/views/layout/layout_constants.h"
37 // Helpers --------------------------------------------------------------------
39 namespace {
41 // The number of seconds the bubble needs to fade out.
42 const int kBubbleFadeDelay = 2;
44 const int kDesiredBubbleWidth = 370;
46 enum ColumnSetType {
47 // | | (FILL, FILL) | |
48 // Used for the bubble's header, the credentials list, and for simple
49 // messages like "No passwords".
50 SINGLE_VIEW_COLUMN_SET = 0,
52 // | | (TRAILING, CENTER) | | (TRAILING, CENTER) | |
53 // Used for buttons at the bottom of the bubble which should nest at the
54 // bottom-right corner.
55 DOUBLE_BUTTON_COLUMN_SET = 1,
57 // | | (LEADING, CENTER) | | (TRAILING, CENTER) | |
58 // Used for buttons at the bottom of the bubble which should occupy
59 // the corners.
60 LINK_BUTTON_COLUMN_SET = 2,
62 // | | (TRAILING, CENTER) | |
63 // Used when there is only one button which should next at the bottom-right
64 // corner.
65 SINGLE_BUTTON_COLUMN_SET = 3,
68 // Construct an appropriate ColumnSet for the given |type|, and add it
69 // to |layout|.
70 void BuildColumnSet(views::GridLayout* layout, ColumnSetType type) {
71 views::ColumnSet* column_set = layout->AddColumnSet(type);
72 column_set->AddPaddingColumn(0, views::kPanelHorizMargin);
73 int full_width = kDesiredBubbleWidth - (2 * views::kPanelHorizMargin);
74 switch (type) {
75 case SINGLE_VIEW_COLUMN_SET:
76 column_set->AddColumn(views::GridLayout::FILL,
77 views::GridLayout::FILL,
79 views::GridLayout::FIXED,
80 full_width,
81 0);
82 break;
84 case DOUBLE_BUTTON_COLUMN_SET:
85 column_set->AddColumn(views::GridLayout::TRAILING,
86 views::GridLayout::CENTER,
88 views::GridLayout::USE_PREF,
90 0);
91 column_set->AddPaddingColumn(0, views::kRelatedButtonHSpacing);
92 column_set->AddColumn(views::GridLayout::TRAILING,
93 views::GridLayout::CENTER,
95 views::GridLayout::USE_PREF,
97 0);
98 break;
99 case LINK_BUTTON_COLUMN_SET:
100 column_set->AddColumn(views::GridLayout::LEADING,
101 views::GridLayout::CENTER,
103 views::GridLayout::USE_PREF,
106 column_set->AddPaddingColumn(0, views::kRelatedButtonHSpacing);
107 column_set->AddColumn(views::GridLayout::TRAILING,
108 views::GridLayout::CENTER,
110 views::GridLayout::USE_PREF,
113 break;
114 case SINGLE_BUTTON_COLUMN_SET:
115 column_set->AddColumn(views::GridLayout::TRAILING,
116 views::GridLayout::CENTER,
118 views::GridLayout::USE_PREF,
122 column_set->AddPaddingColumn(0, views::kPanelHorizMargin);
125 // Given a layout and a model, add an appropriate title using a
126 // SINGLE_VIEW_COLUMN_SET, followed by a spacer row.
127 void AddTitleRow(views::GridLayout* layout, ManagePasswordsBubbleModel* model) {
128 views::Label* title_label = new views::Label(model->title());
129 title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
130 title_label->SetMultiLine(true);
131 title_label->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
132 ui::ResourceBundle::MediumFont));
134 // Add the title to the layout with appropriate padding.
135 layout->StartRowWithPadding(
136 0, SINGLE_VIEW_COLUMN_SET, 0, views::kRelatedControlSmallVerticalSpacing);
137 layout->AddView(title_label);
138 layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing);
141 } // namespace
144 // Globals --------------------------------------------------------------------
146 namespace chrome {
148 void ShowManagePasswordsBubble(content::WebContents* web_contents) {
149 if (ManagePasswordsBubbleView::IsShowing()) {
150 // The bubble is currently shown for some other tab. We should close it now
151 // and open for |web_contents|.
152 ManagePasswordsBubbleView::CloseBubble();
154 ManagePasswordsUIController* controller =
155 ManagePasswordsUIController::FromWebContents(web_contents);
156 ManagePasswordsBubbleView::ShowBubble(
157 web_contents,
158 password_manager::ui::IsAutomaticDisplayState(controller->state())
159 ? ManagePasswordsBubbleView::AUTOMATIC
160 : ManagePasswordsBubbleView::USER_ACTION);
163 } // namespace chrome
166 // ManagePasswordsBubbleView::PendingView -------------------------------------
168 // A view offering the user the ability to save credentials. Contains a
169 // single ManagePasswordItemView, along with a "Save Passwords" button
170 // and a rejection combobox.
171 class ManagePasswordsBubbleView::PendingView : public views::View,
172 public views::ButtonListener,
173 public views::ComboboxListener {
174 public:
175 explicit PendingView(ManagePasswordsBubbleView* parent);
176 virtual ~PendingView();
178 private:
179 // views::ButtonListener:
180 virtual void ButtonPressed(views::Button* sender,
181 const ui::Event& event) OVERRIDE;
183 // Handles the event when the user changes an index of a combobox.
184 virtual void OnPerformAction(views::Combobox* source) OVERRIDE;
186 ManagePasswordsBubbleView* parent_;
188 views::BlueButton* save_button_;
190 // The combobox doesn't take ownership of its model. If we created a
191 // combobox we need to ensure that we delete the model here, and because the
192 // combobox uses the model in it's destructor, we need to make sure we
193 // delete the model _after_ the combobox itself is deleted.
194 scoped_ptr<SavePasswordRefusalComboboxModel> combobox_model_;
195 scoped_ptr<views::Combobox> refuse_combobox_;
198 ManagePasswordsBubbleView::PendingView::PendingView(
199 ManagePasswordsBubbleView* parent)
200 : parent_(parent) {
201 views::GridLayout* layout = new views::GridLayout(this);
202 layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
203 SetLayoutManager(layout);
205 // Create the pending credential item, save button and refusal combobox.
206 ManagePasswordItemView* item =
207 new ManagePasswordItemView(parent->model(),
208 parent->model()->pending_credentials(),
209 password_manager::ui::FIRST_ITEM);
210 save_button_ = new views::BlueButton(
211 this, l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_SAVE_BUTTON));
212 save_button_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
213 ui::ResourceBundle::SmallFont));
215 combobox_model_.reset(new SavePasswordRefusalComboboxModel());
216 refuse_combobox_.reset(new views::Combobox(combobox_model_.get()));
217 refuse_combobox_->set_listener(this);
218 refuse_combobox_->SetStyle(views::Combobox::STYLE_ACTION);
219 // TODO(mkwst): Need a mechanism to pipe a font list down into a combobox.
221 // Title row.
222 BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
223 AddTitleRow(layout, parent_->model());
225 // Credential row.
226 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
227 layout->AddView(item);
229 // Button row.
230 BuildColumnSet(layout, DOUBLE_BUTTON_COLUMN_SET);
231 layout->StartRowWithPadding(
232 0, DOUBLE_BUTTON_COLUMN_SET, 0, views::kRelatedControlVerticalSpacing);
233 layout->AddView(save_button_);
234 layout->AddView(refuse_combobox_.get());
236 // Extra padding for visual awesomeness.
237 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
239 parent_->set_initially_focused_view(save_button_);
242 ManagePasswordsBubbleView::PendingView::~PendingView() {
245 void ManagePasswordsBubbleView::PendingView::ButtonPressed(
246 views::Button* sender,
247 const ui::Event& event) {
248 DCHECK(sender == save_button_);
249 parent_->model()->OnSaveClicked();
250 parent_->Close();
253 void ManagePasswordsBubbleView::PendingView::OnPerformAction(
254 views::Combobox* source) {
255 DCHECK_EQ(source, refuse_combobox_);
256 switch (refuse_combobox_->selected_index()) {
257 case SavePasswordRefusalComboboxModel::INDEX_NOPE:
258 parent_->model()->OnNopeClicked();
259 parent_->Close();
260 break;
261 case SavePasswordRefusalComboboxModel::INDEX_NEVER_FOR_THIS_SITE:
262 parent_->NotifyNeverForThisSiteClicked();
263 break;
267 // ManagePasswordsBubbleView::ConfirmNeverView ---------------------------------
269 // A view offering the user the ability to undo her decision to never save
270 // passwords for a particular site.
271 class ManagePasswordsBubbleView::ConfirmNeverView
272 : public views::View,
273 public views::ButtonListener {
274 public:
275 explicit ConfirmNeverView(ManagePasswordsBubbleView* parent);
276 virtual ~ConfirmNeverView();
278 private:
279 // views::ButtonListener:
280 virtual void ButtonPressed(views::Button* sender,
281 const ui::Event& event) OVERRIDE;
283 ManagePasswordsBubbleView* parent_;
285 views::LabelButton* confirm_button_;
286 views::LabelButton* undo_button_;
289 ManagePasswordsBubbleView::ConfirmNeverView::ConfirmNeverView(
290 ManagePasswordsBubbleView* parent)
291 : parent_(parent) {
292 views::GridLayout* layout = new views::GridLayout(this);
293 layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
294 SetLayoutManager(layout);
296 // Title row.
297 BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
298 views::Label* title_label = new views::Label(l10n_util::GetStringUTF16(
299 IDS_MANAGE_PASSWORDS_BLACKLIST_CONFIRMATION_TITLE));
300 title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
301 title_label->SetMultiLine(true);
302 title_label->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
303 ui::ResourceBundle::MediumFont));
304 layout->StartRowWithPadding(
305 0, SINGLE_VIEW_COLUMN_SET, 0, views::kRelatedControlSmallVerticalSpacing);
306 layout->AddView(title_label);
307 layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing);
309 // Confirmation text.
310 views::Label* confirmation = new views::Label(l10n_util::GetStringUTF16(
311 IDS_MANAGE_PASSWORDS_BLACKLIST_CONFIRMATION_TEXT));
312 confirmation->SetHorizontalAlignment(gfx::ALIGN_LEFT);
313 confirmation->SetMultiLine(true);
314 confirmation->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
315 ui::ResourceBundle::SmallFont));
316 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
317 layout->AddView(confirmation);
318 layout->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing);
320 // Confirm and undo buttons.
321 BuildColumnSet(layout, DOUBLE_BUTTON_COLUMN_SET);
322 layout->StartRowWithPadding(
323 0, DOUBLE_BUTTON_COLUMN_SET, 0, views::kRelatedControlVerticalSpacing);
325 confirm_button_ = new views::LabelButton(
326 this,
327 l10n_util::GetStringUTF16(
328 IDS_MANAGE_PASSWORDS_BLACKLIST_CONFIRMATION_BUTTON));
329 confirm_button_->SetStyle(views::Button::STYLE_BUTTON);
330 confirm_button_->SetFontList(
331 ui::ResourceBundle::GetSharedInstance().GetFontList(
332 ui::ResourceBundle::SmallFont));
333 layout->AddView(confirm_button_);
335 undo_button_ =
336 new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_CANCEL));
337 undo_button_->SetStyle(views::Button::STYLE_BUTTON);
338 undo_button_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
339 ui::ResourceBundle::SmallFont));
340 layout->AddView(undo_button_);
342 // Extra padding for visual awesomeness.
343 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
345 parent_->set_initially_focused_view(confirm_button_);
348 ManagePasswordsBubbleView::ConfirmNeverView::~ConfirmNeverView() {
351 void ManagePasswordsBubbleView::ConfirmNeverView::ButtonPressed(
352 views::Button* sender,
353 const ui::Event& event) {
354 DCHECK(sender == confirm_button_ || sender == undo_button_);
355 if (sender == confirm_button_)
356 parent_->NotifyConfirmedNeverForThisSite();
357 else
358 parent_->NotifyUndoNeverForThisSite();
361 // ManagePasswordsBubbleView::ManageView --------------------------------------
363 // A view offering the user a list of her currently saved credentials
364 // for the current page, along with a "Manage passwords" link and a
365 // "Done" button.
366 class ManagePasswordsBubbleView::ManageView : public views::View,
367 public views::ButtonListener,
368 public views::LinkListener {
369 public:
370 explicit ManageView(ManagePasswordsBubbleView* parent);
371 virtual ~ManageView();
373 private:
374 // views::ButtonListener:
375 virtual void ButtonPressed(views::Button* sender,
376 const ui::Event& event) OVERRIDE;
378 // views::LinkListener:
379 virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE;
381 ManagePasswordsBubbleView* parent_;
383 views::Link* manage_link_;
384 views::LabelButton* done_button_;
387 ManagePasswordsBubbleView::ManageView::ManageView(
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 // Add the title.
395 BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
396 AddTitleRow(layout, parent_->model());
398 // If we have a list of passwords to store for the current site, display
399 // them to the user for management. Otherwise, render a "No passwords for
400 // this site" message.
401 if (!parent_->model()->best_matches().empty()) {
402 for (autofill::ConstPasswordFormMap::const_iterator i(
403 parent_->model()->best_matches().begin());
404 i != parent_->model()->best_matches().end();
405 ++i) {
406 ManagePasswordItemView* item = new ManagePasswordItemView(
407 parent_->model(),
408 *i->second,
409 i == parent_->model()->best_matches().begin()
410 ? password_manager::ui::FIRST_ITEM
411 : password_manager::ui::SUBSEQUENT_ITEM);
413 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
414 layout->AddView(item);
416 } else {
417 views::Label* empty_label = new views::Label(
418 l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_NO_PASSWORDS));
419 empty_label->SetMultiLine(true);
420 empty_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
421 empty_label->SetFontList(
422 ui::ResourceBundle::GetSharedInstance().GetFontList(
423 ui::ResourceBundle::SmallFont));
425 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
426 layout->AddView(empty_label);
427 layout->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing);
430 // Then add the "manage passwords" link and "Done" button.
431 manage_link_ = new views::Link(parent_->model()->manage_link());
432 manage_link_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
433 manage_link_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
434 ui::ResourceBundle::SmallFont));
435 manage_link_->SetUnderline(false);
436 manage_link_->set_listener(this);
438 done_button_ =
439 new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_DONE));
440 done_button_->SetStyle(views::Button::STYLE_BUTTON);
441 done_button_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
442 ui::ResourceBundle::SmallFont));
444 BuildColumnSet(layout, LINK_BUTTON_COLUMN_SET);
445 layout->StartRowWithPadding(
446 0, LINK_BUTTON_COLUMN_SET, 0, views::kRelatedControlVerticalSpacing);
447 layout->AddView(manage_link_);
448 layout->AddView(done_button_);
450 // Extra padding for visual awesomeness.
451 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
453 parent_->set_initially_focused_view(done_button_);
456 ManagePasswordsBubbleView::ManageView::~ManageView() {
459 void ManagePasswordsBubbleView::ManageView::ButtonPressed(
460 views::Button* sender,
461 const ui::Event& event) {
462 DCHECK(sender == done_button_);
463 parent_->model()->OnDoneClicked();
464 parent_->Close();
467 void ManagePasswordsBubbleView::ManageView::LinkClicked(views::Link* source,
468 int event_flags) {
469 DCHECK_EQ(source, manage_link_);
470 parent_->model()->OnManageLinkClicked();
471 parent_->Close();
474 // ManagePasswordsBubbleView::BlacklistedView ---------------------------------
476 // A view offering the user the ability to re-enable the password manager for
477 // a specific site after she's decided to "never save passwords".
478 class ManagePasswordsBubbleView::BlacklistedView
479 : public views::View,
480 public views::ButtonListener {
481 public:
482 explicit BlacklistedView(ManagePasswordsBubbleView* parent);
483 virtual ~BlacklistedView();
485 private:
486 // views::ButtonListener:
487 virtual void ButtonPressed(views::Button* sender,
488 const ui::Event& event) OVERRIDE;
490 ManagePasswordsBubbleView* parent_;
492 views::BlueButton* unblacklist_button_;
493 views::LabelButton* done_button_;
496 ManagePasswordsBubbleView::BlacklistedView::BlacklistedView(
497 ManagePasswordsBubbleView* parent)
498 : parent_(parent) {
499 views::GridLayout* layout = new views::GridLayout(this);
500 layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
501 SetLayoutManager(layout);
503 // Add the title.
504 BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
505 AddTitleRow(layout, parent_->model());
507 // Add the "Hey! You blacklisted this site!" text.
508 views::Label* blacklisted = new views::Label(
509 l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_BLACKLISTED));
510 blacklisted->SetMultiLine(true);
511 blacklisted->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
512 ui::ResourceBundle::SmallFont));
513 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
514 layout->AddView(blacklisted);
515 layout->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing);
517 // Then add the "enable password manager" and "Done" buttons.
518 unblacklist_button_ = new views::BlueButton(
519 this, l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_UNBLACKLIST_BUTTON));
520 unblacklist_button_->SetFontList(
521 ui::ResourceBundle::GetSharedInstance().GetFontList(
522 ui::ResourceBundle::SmallFont));
523 done_button_ =
524 new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_DONE));
525 done_button_->SetStyle(views::Button::STYLE_BUTTON);
526 done_button_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
527 ui::ResourceBundle::SmallFont));
529 BuildColumnSet(layout, DOUBLE_BUTTON_COLUMN_SET);
530 layout->StartRowWithPadding(
531 0, DOUBLE_BUTTON_COLUMN_SET, 0, views::kRelatedControlVerticalSpacing);
532 layout->AddView(unblacklist_button_);
533 layout->AddView(done_button_);
535 // Extra padding for visual awesomeness.
536 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
538 parent_->set_initially_focused_view(unblacklist_button_);
541 ManagePasswordsBubbleView::BlacklistedView::~BlacklistedView() {
544 void ManagePasswordsBubbleView::BlacklistedView::ButtonPressed(
545 views::Button* sender,
546 const ui::Event& event) {
547 if (sender == done_button_)
548 parent_->model()->OnDoneClicked();
549 else if (sender == unblacklist_button_)
550 parent_->model()->OnUnblacklistClicked();
551 else
552 NOTREACHED();
553 parent_->Close();
556 // ManagePasswordsBubbleView::SaveConfirmationView ----------------------------
558 // A view confirming to the user that a password was saved and offering a link
559 // to the Google account manager.
560 class ManagePasswordsBubbleView::SaveConfirmationView
561 : public views::View,
562 public views::ButtonListener,
563 public views::StyledLabelListener {
564 public:
565 explicit SaveConfirmationView(ManagePasswordsBubbleView* parent);
566 virtual ~SaveConfirmationView();
568 private:
569 // views::ButtonListener:
570 virtual void ButtonPressed(views::Button* sender,
571 const ui::Event& event) OVERRIDE;
573 // views::StyledLabelListener implementation
574 virtual void StyledLabelLinkClicked(const gfx::Range& range,
575 int event_flags) OVERRIDE;
577 ManagePasswordsBubbleView* parent_;
579 views::LabelButton* ok_button_;
582 ManagePasswordsBubbleView::SaveConfirmationView::SaveConfirmationView(
583 ManagePasswordsBubbleView* parent)
584 : parent_(parent) {
585 views::GridLayout* layout = new views::GridLayout(this);
586 layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
587 SetLayoutManager(layout);
589 BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
590 AddTitleRow(layout, parent_->model());
592 views::StyledLabel* confirmation =
593 new views::StyledLabel(parent_->model()->save_confirmation_text(), this);
594 confirmation->SetBaseFontList(
595 ui::ResourceBundle::GetSharedInstance().GetFontList(
596 ui::ResourceBundle::SmallFont));
597 confirmation->AddStyleRange(
598 parent_->model()->save_confirmation_link_range(),
599 views::StyledLabel::RangeStyleInfo::CreateForLink());
601 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
602 layout->AddView(confirmation);
604 ok_button_ = new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_OK));
605 ok_button_->SetStyle(views::Button::STYLE_BUTTON);
606 ok_button_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
607 ui::ResourceBundle::SmallFont));
609 BuildColumnSet(layout, SINGLE_BUTTON_COLUMN_SET);
610 layout->StartRowWithPadding(
611 0, SINGLE_BUTTON_COLUMN_SET, 0, views::kRelatedControlVerticalSpacing);
612 layout->AddView(ok_button_);
614 // Extra padding for visual awesomeness.
615 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
617 parent_->set_initially_focused_view(ok_button_);
620 ManagePasswordsBubbleView::SaveConfirmationView::~SaveConfirmationView() {
623 void ManagePasswordsBubbleView::SaveConfirmationView::StyledLabelLinkClicked(
624 const gfx::Range& range, int event_flags) {
625 DCHECK_EQ(range, parent_->model()->save_confirmation_link_range());
626 parent_->model()->OnRemoteManageLinkClicked();
627 parent_->Close();
630 void ManagePasswordsBubbleView::SaveConfirmationView::ButtonPressed(
631 views::Button* sender, const ui::Event& event) {
632 DCHECK_EQ(sender, ok_button_);
633 parent_->model()->OnOKClicked();
634 parent_->Close();
637 // ManagePasswordsBubbleView::WebContentMouseHandler --------------------------
639 // The class listens for WebContentsView events and notifies the bubble if the
640 // view was clicked on or received keystrokes.
641 class ManagePasswordsBubbleView::WebContentMouseHandler
642 : public ui::EventHandler {
643 public:
644 explicit WebContentMouseHandler(ManagePasswordsBubbleView* bubble);
645 virtual ~WebContentMouseHandler();
647 virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE;
648 virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE;
650 private:
651 aura::Window* GetWebContentsWindow();
653 ManagePasswordsBubbleView* bubble_;
655 DISALLOW_COPY_AND_ASSIGN(WebContentMouseHandler);
658 ManagePasswordsBubbleView::WebContentMouseHandler::WebContentMouseHandler(
659 ManagePasswordsBubbleView* bubble)
660 : bubble_(bubble) {
661 GetWebContentsWindow()->AddPreTargetHandler(this);
664 ManagePasswordsBubbleView::WebContentMouseHandler::~WebContentMouseHandler() {
665 if (aura::Window* window = GetWebContentsWindow())
666 window->RemovePreTargetHandler(this);
669 void ManagePasswordsBubbleView::WebContentMouseHandler::OnKeyEvent(
670 ui::KeyEvent* event) {
671 content::WebContents* web_contents = bubble_->model()->web_contents();
672 content::RenderViewHost* rvh = web_contents->GetRenderViewHost();
673 if (rvh->IsFocusedElementEditable() &&
674 event->type() == ui::ET_KEY_PRESSED)
675 bubble_->StartFadingOut();
678 void ManagePasswordsBubbleView::WebContentMouseHandler::OnMouseEvent(
679 ui::MouseEvent* event) {
680 if (event->type() == ui::ET_MOUSE_PRESSED)
681 bubble_->StartFadingOut();
684 aura::Window*
685 ManagePasswordsBubbleView::WebContentMouseHandler::GetWebContentsWindow() {
686 content::WebContents* web_contents = bubble_->model()->web_contents();
687 return web_contents ? web_contents->GetNativeView() : NULL;
690 // ManagePasswordsBubbleView::FadeOutObserver ---------------------------------
692 // The class notifies the bubble when it faded out completely.
693 class ManagePasswordsBubbleView::FadeOutObserver
694 : public ui::ImplicitAnimationObserver {
695 public:
696 explicit FadeOutObserver(ManagePasswordsBubbleView* bubble)
697 : bubble_(bubble) {
700 virtual void OnImplicitAnimationsCompleted() OVERRIDE {
701 bubble_->OnBubbleDisappeared();
704 private:
705 ManagePasswordsBubbleView* bubble_;
707 DISALLOW_COPY_AND_ASSIGN(FadeOutObserver);
710 // ManagePasswordsBubbleView --------------------------------------------------
712 // static
713 ManagePasswordsBubbleView* ManagePasswordsBubbleView::manage_passwords_bubble_ =
714 NULL;
716 // static
717 void ManagePasswordsBubbleView::ShowBubble(content::WebContents* web_contents,
718 DisplayReason reason) {
719 Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
720 DCHECK(browser);
721 DCHECK(browser->window());
722 DCHECK(browser->fullscreen_controller());
724 if (IsShowing())
725 return;
727 BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
728 bool is_fullscreen = browser_view->IsFullscreen();
729 ManagePasswordsIconView* anchor_view =
730 is_fullscreen
731 ? NULL
732 : browser_view->GetLocationBarView()->manage_passwords_icon_view();
733 manage_passwords_bubble_ = new ManagePasswordsBubbleView(
734 web_contents, anchor_view, reason);
736 if (is_fullscreen) {
737 manage_passwords_bubble_->set_parent_window(
738 web_contents->GetTopLevelNativeWindow());
741 views::BubbleDelegateView::CreateBubble(manage_passwords_bubble_);
743 // Adjust for fullscreen after creation as it relies on the content size.
744 if (is_fullscreen) {
745 manage_passwords_bubble_->AdjustForFullscreen(
746 browser_view->GetBoundsInScreen());
748 if (reason == AUTOMATIC)
749 manage_passwords_bubble_->GetWidget()->ShowInactive();
750 else
751 manage_passwords_bubble_->GetWidget()->Show();
754 // static
755 void ManagePasswordsBubbleView::CloseBubble() {
756 if (manage_passwords_bubble_)
757 manage_passwords_bubble_->Close();
760 // static
761 void ManagePasswordsBubbleView::ActivateBubble() {
762 if (!IsShowing())
763 return;
764 manage_passwords_bubble_->GetWidget()->Activate();
767 // static
768 bool ManagePasswordsBubbleView::IsShowing() {
769 // The bubble may be in the process of closing.
770 return (manage_passwords_bubble_ != NULL) &&
771 manage_passwords_bubble_->GetWidget()->IsVisible();
774 ManagePasswordsBubbleView::ManagePasswordsBubbleView(
775 content::WebContents* web_contents,
776 ManagePasswordsIconView* anchor_view,
777 DisplayReason reason)
778 : ManagePasswordsBubble(web_contents, reason),
779 BubbleDelegateView(anchor_view,
780 anchor_view ? views::BubbleBorder::TOP_RIGHT
781 : views::BubbleBorder::NONE),
782 anchor_view_(anchor_view),
783 never_save_passwords_(false),
784 initially_focused_view_(NULL) {
785 // Compensate for built-in vertical padding in the anchor view's image.
786 set_anchor_view_insets(gfx::Insets(5, 0, 5, 0));
787 set_notify_enter_exit_on_child(true);
788 if (anchor_view)
789 anchor_view->SetActive(true);
790 mouse_handler_.reset(new WebContentMouseHandler(this));
793 ManagePasswordsBubbleView::~ManagePasswordsBubbleView() {
794 if (anchor_view_)
795 anchor_view_->SetActive(false);
798 void ManagePasswordsBubbleView::AdjustForFullscreen(
799 const gfx::Rect& screen_bounds) {
800 if (GetAnchorView())
801 return;
803 // The bubble's padding from the screen edge, used in fullscreen.
804 const int kFullscreenPaddingEnd = 20;
805 const size_t bubble_half_width = width() / 2;
806 const int x_pos = base::i18n::IsRTL() ?
807 screen_bounds.x() + bubble_half_width + kFullscreenPaddingEnd :
808 screen_bounds.right() - bubble_half_width - kFullscreenPaddingEnd;
809 SetAnchorRect(gfx::Rect(x_pos, screen_bounds.y(), 0, 0));
812 void ManagePasswordsBubbleView::Close() {
813 fadeout_observer_.reset();
814 mouse_handler_.reset();
815 GetWidget()->Close();
818 void ManagePasswordsBubbleView::Refresh() {
819 RemoveAllChildViews(true);
820 initially_focused_view_ = NULL;
821 if (password_manager::ui::IsPendingState(model()->state())) {
822 if (never_save_passwords_)
823 AddChildView(new ConfirmNeverView(this));
824 else
825 AddChildView(new PendingView(this));
826 } else if (model()->state() == password_manager::ui::BLACKLIST_STATE) {
827 AddChildView(new BlacklistedView(this));
828 } else if (model()->state() == password_manager::ui::CONFIRMATION_STATE) {
829 AddChildView(new SaveConfirmationView(this));
830 } else {
831 AddChildView(new ManageView(this));
833 GetLayoutManager()->Layout(this);
834 CancelFadingOut();
837 void ManagePasswordsBubbleView::NotifyNeverForThisSiteClicked() {
838 if (model()->best_matches().empty()) {
839 // Skip confirmation if there are no existing passwords for this site.
840 NotifyConfirmedNeverForThisSite();
841 } else {
842 never_save_passwords_ = true;
843 Refresh();
847 void ManagePasswordsBubbleView::NotifyConfirmedNeverForThisSite() {
848 model()->OnNeverForThisSiteClicked();
849 Close();
852 void ManagePasswordsBubbleView::NotifyUndoNeverForThisSite() {
853 never_save_passwords_ = false;
854 Refresh();
857 void ManagePasswordsBubbleView::Init() {
858 views::FillLayout* layout = new views::FillLayout();
859 SetLayoutManager(layout);
861 Refresh();
864 void ManagePasswordsBubbleView::WindowClosing() {
865 // Close() closes the window asynchronously, so by the time we reach here,
866 // |manage_passwords_bubble_| may have already been reset.
867 if (manage_passwords_bubble_ == this)
868 manage_passwords_bubble_ = NULL;
871 void ManagePasswordsBubbleView::OnWidgetActivationChanged(views::Widget* widget,
872 bool active) {
873 if (active && widget == GetWidget())
874 CancelFadingOut();
875 BubbleDelegateView::OnWidgetActivationChanged(widget, active);
878 views::View* ManagePasswordsBubbleView::GetInitiallyFocusedView() {
879 return initially_focused_view_;
882 void ManagePasswordsBubbleView::OnMouseEntered(const ui::MouseEvent& event) {
883 CancelFadingOut();
886 void ManagePasswordsBubbleView::StartFadingOut() {
887 if (fadeout_observer_)
888 return;
889 aura::Window* window = GetWidget()->GetNativeView();
890 ui::ScopedLayerAnimationSettings animator(window->layer()->GetAnimator());
891 fadeout_observer_.reset(new FadeOutObserver(this));
892 animator.AddObserver(fadeout_observer_.get());
893 animator.SetTransitionDuration(
894 base::TimeDelta::FromSeconds(kBubbleFadeDelay));
895 window->layer()->SetOpacity(0);
898 void ManagePasswordsBubbleView::CancelFadingOut() {
899 if (!fadeout_observer_)
900 return;
901 fadeout_observer_.reset();
902 aura::Window* window = GetWidget()->GetNativeView();
903 window->layer()->SetOpacity(1);
906 void ManagePasswordsBubbleView::OnBubbleDisappeared() {
907 Close();