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/views/controls/button/blue_button.h"
23 #include "ui/views/controls/button/label_button.h"
24 #include "ui/views/controls/combobox/combobox.h"
25 #include "ui/views/controls/combobox/combobox_listener.h"
26 #include "ui/views/controls/link.h"
27 #include "ui/views/controls/link_listener.h"
28 #include "ui/views/controls/styled_label.h"
29 #include "ui/views/controls/styled_label_listener.h"
30 #include "ui/views/layout/fill_layout.h"
31 #include "ui/views/layout/grid_layout.h"
32 #include "ui/views/layout/layout_constants.h"
35 // Helpers --------------------------------------------------------------------
39 const int kDesiredBubbleWidth = 370;
42 // | | (FILL, FILL) | |
43 // Used for the bubble's header, the credentials list, and for simple
44 // messages like "No passwords".
45 SINGLE_VIEW_COLUMN_SET = 0,
47 // | | (TRAILING, CENTER) | | (TRAILING, CENTER) | |
48 // Used for buttons at the bottom of the bubble which should nest at the
49 // bottom-right corner.
50 DOUBLE_BUTTON_COLUMN_SET = 1,
52 // | | (LEADING, CENTER) | | (TRAILING, CENTER) | |
53 // Used for buttons at the bottom of the bubble which should occupy
55 LINK_BUTTON_COLUMN_SET = 2,
57 // | | (TRAILING, CENTER) | |
58 // Used when there is only one button which should next at the bottom-right
60 SINGLE_BUTTON_COLUMN_SET = 3,
63 // Construct an appropriate ColumnSet for the given |type|, and add it
65 void BuildColumnSet(views::GridLayout* layout, ColumnSetType type) {
66 views::ColumnSet* column_set = layout->AddColumnSet(type);
67 column_set->AddPaddingColumn(0, views::kPanelHorizMargin);
68 int full_width = kDesiredBubbleWidth - (2 * views::kPanelHorizMargin);
70 case SINGLE_VIEW_COLUMN_SET:
71 column_set->AddColumn(views::GridLayout::FILL,
72 views::GridLayout::FILL,
74 views::GridLayout::FIXED,
79 case DOUBLE_BUTTON_COLUMN_SET:
80 column_set->AddColumn(views::GridLayout::TRAILING,
81 views::GridLayout::CENTER,
83 views::GridLayout::USE_PREF,
86 column_set->AddPaddingColumn(0, views::kRelatedButtonHSpacing);
87 column_set->AddColumn(views::GridLayout::TRAILING,
88 views::GridLayout::CENTER,
90 views::GridLayout::USE_PREF,
94 case LINK_BUTTON_COLUMN_SET:
95 column_set->AddColumn(views::GridLayout::LEADING,
96 views::GridLayout::CENTER,
98 views::GridLayout::USE_PREF,
101 column_set->AddPaddingColumn(0, views::kRelatedButtonHSpacing);
102 column_set->AddColumn(views::GridLayout::TRAILING,
103 views::GridLayout::CENTER,
105 views::GridLayout::USE_PREF,
109 case SINGLE_BUTTON_COLUMN_SET:
110 column_set->AddColumn(views::GridLayout::TRAILING,
111 views::GridLayout::CENTER,
113 views::GridLayout::USE_PREF,
117 column_set->AddPaddingColumn(0, views::kPanelHorizMargin);
120 // Given a layout and a model, add an appropriate title using a
121 // SINGLE_VIEW_COLUMN_SET, followed by a spacer row.
122 void AddTitleRow(views::GridLayout* layout, ManagePasswordsBubbleModel* model) {
123 views::Label* title_label = new views::Label(model->title());
124 title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
125 title_label->SetMultiLine(true);
126 title_label->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
127 ui::ResourceBundle::MediumFont));
129 // Add the title to the layout with appropriate padding.
130 layout->StartRowWithPadding(
131 0, SINGLE_VIEW_COLUMN_SET, 0, views::kRelatedControlSmallVerticalSpacing);
132 layout->AddView(title_label);
133 layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing);
139 // Globals --------------------------------------------------------------------
143 void ShowManagePasswordsBubble(content::WebContents* web_contents) {
144 if (ManagePasswordsBubbleView::IsShowing()) {
145 // The bubble is currently shown for some other tab. We should close it now
146 // and open for |web_contents|.
147 ManagePasswordsBubbleView::CloseBubble();
149 ManagePasswordsUIController* controller =
150 ManagePasswordsUIController::FromWebContents(web_contents);
151 ManagePasswordsBubbleView::ShowBubble(
153 password_manager::ui::IsAutomaticDisplayState(controller->state())
154 ? ManagePasswordsBubbleView::AUTOMATIC
155 : ManagePasswordsBubbleView::USER_ACTION);
158 void CloseManagePasswordsBubble(content::WebContents* web_contents) {
159 if (!ManagePasswordsBubbleView::IsShowing())
161 content::WebContents* bubble_web_contents =
162 ManagePasswordsBubbleView::manage_password_bubble()->web_contents();
163 if (web_contents == bubble_web_contents)
164 ManagePasswordsBubbleView::CloseBubble();
167 } // namespace chrome
170 // ManagePasswordsBubbleView::PendingView -------------------------------------
172 // A view offering the user the ability to save credentials. Contains a
173 // single ManagePasswordItemView, along with a "Save Passwords" button
174 // and a rejection combobox.
175 class ManagePasswordsBubbleView::PendingView : public views::View,
176 public views::ButtonListener,
177 public views::ComboboxListener {
179 explicit PendingView(ManagePasswordsBubbleView* parent);
180 virtual ~PendingView();
183 // views::ButtonListener:
184 virtual void ButtonPressed(views::Button* sender,
185 const ui::Event& event) OVERRIDE;
187 // Handles the event when the user changes an index of a combobox.
188 virtual void OnPerformAction(views::Combobox* source) OVERRIDE;
190 ManagePasswordsBubbleView* parent_;
192 views::BlueButton* save_button_;
194 // The combobox doesn't take ownership of its model. If we created a
195 // combobox we need to ensure that we delete the model here, and because the
196 // combobox uses the model in it's destructor, we need to make sure we
197 // delete the model _after_ the combobox itself is deleted.
198 scoped_ptr<SavePasswordRefusalComboboxModel> combobox_model_;
199 scoped_ptr<views::Combobox> refuse_combobox_;
202 ManagePasswordsBubbleView::PendingView::PendingView(
203 ManagePasswordsBubbleView* parent)
205 views::GridLayout* layout = new views::GridLayout(this);
206 layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
207 SetLayoutManager(layout);
209 // Create the pending credential item, save button and refusal combobox.
210 ManagePasswordItemView* item =
211 new ManagePasswordItemView(parent->model(),
212 parent->model()->pending_credentials(),
213 password_manager::ui::FIRST_ITEM);
214 save_button_ = new views::BlueButton(
215 this, l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_SAVE_BUTTON));
216 save_button_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
217 ui::ResourceBundle::SmallFont));
219 combobox_model_.reset(new SavePasswordRefusalComboboxModel());
220 refuse_combobox_.reset(new views::Combobox(combobox_model_.get()));
221 refuse_combobox_->set_listener(this);
222 refuse_combobox_->SetStyle(views::Combobox::STYLE_ACTION);
223 // TODO(mkwst): Need a mechanism to pipe a font list down into a combobox.
226 BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
227 AddTitleRow(layout, parent_->model());
230 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
231 layout->AddView(item);
234 BuildColumnSet(layout, DOUBLE_BUTTON_COLUMN_SET);
235 layout->StartRowWithPadding(
236 0, DOUBLE_BUTTON_COLUMN_SET, 0, views::kRelatedControlVerticalSpacing);
237 layout->AddView(save_button_);
238 layout->AddView(refuse_combobox_.get());
240 // Extra padding for visual awesomeness.
241 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
243 parent_->set_initially_focused_view(save_button_);
246 ManagePasswordsBubbleView::PendingView::~PendingView() {
249 void ManagePasswordsBubbleView::PendingView::ButtonPressed(
250 views::Button* sender,
251 const ui::Event& event) {
252 DCHECK(sender == save_button_);
253 parent_->model()->OnSaveClicked();
257 void ManagePasswordsBubbleView::PendingView::OnPerformAction(
258 views::Combobox* source) {
259 DCHECK_EQ(source, refuse_combobox_);
260 switch (refuse_combobox_->selected_index()) {
261 case SavePasswordRefusalComboboxModel::INDEX_NOPE:
262 parent_->model()->OnNopeClicked();
265 case SavePasswordRefusalComboboxModel::INDEX_NEVER_FOR_THIS_SITE:
266 parent_->NotifyNeverForThisSiteClicked();
271 // ManagePasswordsBubbleView::ConfirmNeverView ---------------------------------
273 // A view offering the user the ability to undo her decision to never save
274 // passwords for a particular site.
275 class ManagePasswordsBubbleView::ConfirmNeverView
276 : public views::View,
277 public views::ButtonListener {
279 explicit ConfirmNeverView(ManagePasswordsBubbleView* parent);
280 virtual ~ConfirmNeverView();
283 // views::ButtonListener:
284 virtual void ButtonPressed(views::Button* sender,
285 const ui::Event& event) OVERRIDE;
287 ManagePasswordsBubbleView* parent_;
289 views::LabelButton* confirm_button_;
290 views::LabelButton* undo_button_;
293 ManagePasswordsBubbleView::ConfirmNeverView::ConfirmNeverView(
294 ManagePasswordsBubbleView* parent)
296 views::GridLayout* layout = new views::GridLayout(this);
297 layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
298 SetLayoutManager(layout);
301 BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
302 views::Label* title_label = new views::Label(l10n_util::GetStringUTF16(
303 IDS_MANAGE_PASSWORDS_BLACKLIST_CONFIRMATION_TITLE));
304 title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
305 title_label->SetMultiLine(true);
306 title_label->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
307 ui::ResourceBundle::MediumFont));
308 layout->StartRowWithPadding(
309 0, SINGLE_VIEW_COLUMN_SET, 0, views::kRelatedControlSmallVerticalSpacing);
310 layout->AddView(title_label);
311 layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing);
313 // Confirmation text.
314 views::Label* confirmation = new views::Label(l10n_util::GetStringUTF16(
315 IDS_MANAGE_PASSWORDS_BLACKLIST_CONFIRMATION_TEXT));
316 confirmation->SetHorizontalAlignment(gfx::ALIGN_LEFT);
317 confirmation->SetMultiLine(true);
318 confirmation->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
319 ui::ResourceBundle::SmallFont));
320 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
321 layout->AddView(confirmation);
322 layout->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing);
324 // Confirm and undo buttons.
325 BuildColumnSet(layout, DOUBLE_BUTTON_COLUMN_SET);
326 layout->StartRowWithPadding(
327 0, DOUBLE_BUTTON_COLUMN_SET, 0, views::kRelatedControlVerticalSpacing);
329 confirm_button_ = new views::LabelButton(
331 l10n_util::GetStringUTF16(
332 IDS_MANAGE_PASSWORDS_BLACKLIST_CONFIRMATION_BUTTON));
333 confirm_button_->SetStyle(views::Button::STYLE_BUTTON);
334 confirm_button_->SetFontList(
335 ui::ResourceBundle::GetSharedInstance().GetFontList(
336 ui::ResourceBundle::SmallFont));
337 layout->AddView(confirm_button_);
340 new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_CANCEL));
341 undo_button_->SetStyle(views::Button::STYLE_BUTTON);
342 undo_button_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
343 ui::ResourceBundle::SmallFont));
344 layout->AddView(undo_button_);
346 // Extra padding for visual awesomeness.
347 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
349 parent_->set_initially_focused_view(confirm_button_);
352 ManagePasswordsBubbleView::ConfirmNeverView::~ConfirmNeverView() {
355 void ManagePasswordsBubbleView::ConfirmNeverView::ButtonPressed(
356 views::Button* sender,
357 const ui::Event& event) {
358 DCHECK(sender == confirm_button_ || sender == undo_button_);
359 if (sender == confirm_button_)
360 parent_->NotifyConfirmedNeverForThisSite();
362 parent_->NotifyUndoNeverForThisSite();
365 // ManagePasswordsBubbleView::ManageView --------------------------------------
367 // A view offering the user a list of her currently saved credentials
368 // for the current page, along with a "Manage passwords" link and a
370 class ManagePasswordsBubbleView::ManageView : public views::View,
371 public views::ButtonListener,
372 public views::LinkListener {
374 explicit ManageView(ManagePasswordsBubbleView* parent);
375 virtual ~ManageView();
378 // views::ButtonListener:
379 virtual void ButtonPressed(views::Button* sender,
380 const ui::Event& event) OVERRIDE;
382 // views::LinkListener:
383 virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE;
385 ManagePasswordsBubbleView* parent_;
387 views::Link* manage_link_;
388 views::LabelButton* done_button_;
391 ManagePasswordsBubbleView::ManageView::ManageView(
392 ManagePasswordsBubbleView* parent)
394 views::GridLayout* layout = new views::GridLayout(this);
395 layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
396 SetLayoutManager(layout);
399 BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
400 AddTitleRow(layout, parent_->model());
402 // If we have a list of passwords to store for the current site, display
403 // them to the user for management. Otherwise, render a "No passwords for
404 // this site" message.
405 if (!parent_->model()->best_matches().empty()) {
406 for (autofill::ConstPasswordFormMap::const_iterator i(
407 parent_->model()->best_matches().begin());
408 i != parent_->model()->best_matches().end();
410 ManagePasswordItemView* item = new ManagePasswordItemView(
413 i == parent_->model()->best_matches().begin()
414 ? password_manager::ui::FIRST_ITEM
415 : password_manager::ui::SUBSEQUENT_ITEM);
417 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
418 layout->AddView(item);
421 views::Label* empty_label = new views::Label(
422 l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_NO_PASSWORDS));
423 empty_label->SetMultiLine(true);
424 empty_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
425 empty_label->SetFontList(
426 ui::ResourceBundle::GetSharedInstance().GetFontList(
427 ui::ResourceBundle::SmallFont));
429 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
430 layout->AddView(empty_label);
431 layout->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing);
434 // Then add the "manage passwords" link and "Done" button.
435 manage_link_ = new views::Link(parent_->model()->manage_link());
436 manage_link_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
437 manage_link_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
438 ui::ResourceBundle::SmallFont));
439 manage_link_->SetUnderline(false);
440 manage_link_->set_listener(this);
443 new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_DONE));
444 done_button_->SetStyle(views::Button::STYLE_BUTTON);
445 done_button_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
446 ui::ResourceBundle::SmallFont));
448 BuildColumnSet(layout, LINK_BUTTON_COLUMN_SET);
449 layout->StartRowWithPadding(
450 0, LINK_BUTTON_COLUMN_SET, 0, views::kRelatedControlVerticalSpacing);
451 layout->AddView(manage_link_);
452 layout->AddView(done_button_);
454 // Extra padding for visual awesomeness.
455 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
457 parent_->set_initially_focused_view(done_button_);
460 ManagePasswordsBubbleView::ManageView::~ManageView() {
463 void ManagePasswordsBubbleView::ManageView::ButtonPressed(
464 views::Button* sender,
465 const ui::Event& event) {
466 DCHECK(sender == done_button_);
467 parent_->model()->OnDoneClicked();
471 void ManagePasswordsBubbleView::ManageView::LinkClicked(views::Link* source,
473 DCHECK_EQ(source, manage_link_);
474 parent_->model()->OnManageLinkClicked();
478 // ManagePasswordsBubbleView::BlacklistedView ---------------------------------
480 // A view offering the user the ability to re-enable the password manager for
481 // a specific site after she's decided to "never save passwords".
482 class ManagePasswordsBubbleView::BlacklistedView
483 : public views::View,
484 public views::ButtonListener {
486 explicit BlacklistedView(ManagePasswordsBubbleView* parent);
487 virtual ~BlacklistedView();
490 // views::ButtonListener:
491 virtual void ButtonPressed(views::Button* sender,
492 const ui::Event& event) OVERRIDE;
494 ManagePasswordsBubbleView* parent_;
496 views::BlueButton* unblacklist_button_;
497 views::LabelButton* done_button_;
500 ManagePasswordsBubbleView::BlacklistedView::BlacklistedView(
501 ManagePasswordsBubbleView* parent)
503 views::GridLayout* layout = new views::GridLayout(this);
504 layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
505 SetLayoutManager(layout);
508 BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
509 AddTitleRow(layout, parent_->model());
511 // Add the "Hey! You blacklisted this site!" text.
512 views::Label* blacklisted = new views::Label(
513 l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_BLACKLISTED));
514 blacklisted->SetMultiLine(true);
515 blacklisted->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
516 ui::ResourceBundle::SmallFont));
517 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
518 layout->AddView(blacklisted);
519 layout->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing);
521 // Then add the "enable password manager" and "Done" buttons.
522 unblacklist_button_ = new views::BlueButton(
523 this, l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_UNBLACKLIST_BUTTON));
524 unblacklist_button_->SetFontList(
525 ui::ResourceBundle::GetSharedInstance().GetFontList(
526 ui::ResourceBundle::SmallFont));
528 new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_DONE));
529 done_button_->SetStyle(views::Button::STYLE_BUTTON);
530 done_button_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
531 ui::ResourceBundle::SmallFont));
533 BuildColumnSet(layout, DOUBLE_BUTTON_COLUMN_SET);
534 layout->StartRowWithPadding(
535 0, DOUBLE_BUTTON_COLUMN_SET, 0, views::kRelatedControlVerticalSpacing);
536 layout->AddView(unblacklist_button_);
537 layout->AddView(done_button_);
539 // Extra padding for visual awesomeness.
540 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
542 parent_->set_initially_focused_view(unblacklist_button_);
545 ManagePasswordsBubbleView::BlacklistedView::~BlacklistedView() {
548 void ManagePasswordsBubbleView::BlacklistedView::ButtonPressed(
549 views::Button* sender,
550 const ui::Event& event) {
551 if (sender == done_button_)
552 parent_->model()->OnDoneClicked();
553 else if (sender == unblacklist_button_)
554 parent_->model()->OnUnblacklistClicked();
560 // ManagePasswordsBubbleView::SaveConfirmationView ----------------------------
562 // A view confirming to the user that a password was saved and offering a link
563 // to the Google account manager.
564 class ManagePasswordsBubbleView::SaveConfirmationView
565 : public views::View,
566 public views::ButtonListener,
567 public views::StyledLabelListener {
569 explicit SaveConfirmationView(ManagePasswordsBubbleView* parent);
570 virtual ~SaveConfirmationView();
573 // views::ButtonListener:
574 virtual void ButtonPressed(views::Button* sender,
575 const ui::Event& event) OVERRIDE;
577 // views::StyledLabelListener implementation
578 virtual void StyledLabelLinkClicked(const gfx::Range& range,
579 int event_flags) OVERRIDE;
581 ManagePasswordsBubbleView* parent_;
583 views::LabelButton* ok_button_;
586 ManagePasswordsBubbleView::SaveConfirmationView::SaveConfirmationView(
587 ManagePasswordsBubbleView* parent)
589 views::GridLayout* layout = new views::GridLayout(this);
590 layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
591 SetLayoutManager(layout);
593 BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
594 AddTitleRow(layout, parent_->model());
596 views::StyledLabel* confirmation =
597 new views::StyledLabel(parent_->model()->save_confirmation_text(), this);
598 confirmation->SetBaseFontList(
599 ui::ResourceBundle::GetSharedInstance().GetFontList(
600 ui::ResourceBundle::SmallFont));
601 confirmation->AddStyleRange(
602 parent_->model()->save_confirmation_link_range(),
603 views::StyledLabel::RangeStyleInfo::CreateForLink());
605 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
606 layout->AddView(confirmation);
608 ok_button_ = new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_OK));
609 ok_button_->SetStyle(views::Button::STYLE_BUTTON);
610 ok_button_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
611 ui::ResourceBundle::SmallFont));
613 BuildColumnSet(layout, SINGLE_BUTTON_COLUMN_SET);
614 layout->StartRowWithPadding(
615 0, SINGLE_BUTTON_COLUMN_SET, 0, views::kRelatedControlVerticalSpacing);
616 layout->AddView(ok_button_);
618 // Extra padding for visual awesomeness.
619 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
621 parent_->set_initially_focused_view(ok_button_);
624 ManagePasswordsBubbleView::SaveConfirmationView::~SaveConfirmationView() {
627 void ManagePasswordsBubbleView::SaveConfirmationView::StyledLabelLinkClicked(
628 const gfx::Range& range, int event_flags) {
629 DCHECK_EQ(range, parent_->model()->save_confirmation_link_range());
630 parent_->model()->OnManageLinkClicked();
634 void ManagePasswordsBubbleView::SaveConfirmationView::ButtonPressed(
635 views::Button* sender, const ui::Event& event) {
636 DCHECK_EQ(sender, ok_button_);
637 parent_->model()->OnOKClicked();
641 // ManagePasswordsBubbleView::WebContentMouseHandler --------------------------
643 // The class listens for WebContentsView events and notifies the bubble if the
644 // view was clicked on or received keystrokes.
645 class ManagePasswordsBubbleView::WebContentMouseHandler
646 : public ui::EventHandler {
648 explicit WebContentMouseHandler(ManagePasswordsBubbleView* bubble);
649 virtual ~WebContentMouseHandler();
651 virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE;
652 virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE;
655 aura::Window* GetWebContentsWindow();
657 ManagePasswordsBubbleView* bubble_;
659 DISALLOW_COPY_AND_ASSIGN(WebContentMouseHandler);
662 ManagePasswordsBubbleView::WebContentMouseHandler::WebContentMouseHandler(
663 ManagePasswordsBubbleView* bubble)
665 GetWebContentsWindow()->AddPreTargetHandler(this);
668 ManagePasswordsBubbleView::WebContentMouseHandler::~WebContentMouseHandler() {
669 if (aura::Window* window = GetWebContentsWindow())
670 window->RemovePreTargetHandler(this);
673 void ManagePasswordsBubbleView::WebContentMouseHandler::OnKeyEvent(
674 ui::KeyEvent* event) {
675 content::WebContents* web_contents = bubble_->model()->web_contents();
676 content::RenderViewHost* rvh = web_contents->GetRenderViewHost();
677 if (rvh->IsFocusedElementEditable() &&
678 event->type() == ui::ET_KEY_PRESSED)
682 void ManagePasswordsBubbleView::WebContentMouseHandler::OnMouseEvent(
683 ui::MouseEvent* event) {
684 if (event->type() == ui::ET_MOUSE_PRESSED)
689 ManagePasswordsBubbleView::WebContentMouseHandler::GetWebContentsWindow() {
690 content::WebContents* web_contents = bubble_->model()->web_contents();
691 return web_contents ? web_contents->GetNativeView() : NULL;
694 // ManagePasswordsBubbleView --------------------------------------------------
697 ManagePasswordsBubbleView* ManagePasswordsBubbleView::manage_passwords_bubble_ =
701 void ManagePasswordsBubbleView::ShowBubble(content::WebContents* web_contents,
702 DisplayReason reason) {
703 Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
705 DCHECK(browser->window());
706 DCHECK(browser->fullscreen_controller());
711 BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
712 bool is_fullscreen = browser_view->IsFullscreen();
713 ManagePasswordsIconView* anchor_view =
716 : browser_view->GetLocationBarView()->manage_passwords_icon_view();
717 manage_passwords_bubble_ = new ManagePasswordsBubbleView(
718 web_contents, anchor_view, reason);
721 manage_passwords_bubble_->set_parent_window(
722 web_contents->GetTopLevelNativeWindow());
725 views::BubbleDelegateView::CreateBubble(manage_passwords_bubble_);
727 // Adjust for fullscreen after creation as it relies on the content size.
729 manage_passwords_bubble_->AdjustForFullscreen(
730 browser_view->GetBoundsInScreen());
732 if (reason == AUTOMATIC)
733 manage_passwords_bubble_->GetWidget()->ShowInactive();
735 manage_passwords_bubble_->GetWidget()->Show();
739 void ManagePasswordsBubbleView::CloseBubble() {
740 if (manage_passwords_bubble_)
741 manage_passwords_bubble_->Close();
745 void ManagePasswordsBubbleView::ActivateBubble() {
748 manage_passwords_bubble_->GetWidget()->Activate();
752 bool ManagePasswordsBubbleView::IsShowing() {
753 // The bubble may be in the process of closing.
754 return (manage_passwords_bubble_ != NULL) &&
755 manage_passwords_bubble_->GetWidget()->IsVisible();
758 content::WebContents* ManagePasswordsBubbleView::web_contents() const {
759 return model()->web_contents();
762 ManagePasswordsBubbleView::ManagePasswordsBubbleView(
763 content::WebContents* web_contents,
764 ManagePasswordsIconView* anchor_view,
765 DisplayReason reason)
766 : ManagePasswordsBubble(web_contents, reason),
767 BubbleDelegateView(anchor_view,
768 anchor_view ? views::BubbleBorder::TOP_RIGHT
769 : views::BubbleBorder::NONE),
770 anchor_view_(anchor_view),
771 never_save_passwords_(false),
772 initially_focused_view_(NULL) {
773 // Compensate for built-in vertical padding in the anchor view's image.
774 set_anchor_view_insets(gfx::Insets(5, 0, 5, 0));
776 anchor_view->SetActive(true);
777 mouse_handler_.reset(new WebContentMouseHandler(this));
780 ManagePasswordsBubbleView::~ManagePasswordsBubbleView() {
782 anchor_view_->SetActive(false);
785 void ManagePasswordsBubbleView::AdjustForFullscreen(
786 const gfx::Rect& screen_bounds) {
790 // The bubble's padding from the screen edge, used in fullscreen.
791 const int kFullscreenPaddingEnd = 20;
792 const size_t bubble_half_width = width() / 2;
793 const int x_pos = base::i18n::IsRTL() ?
794 screen_bounds.x() + bubble_half_width + kFullscreenPaddingEnd :
795 screen_bounds.right() - bubble_half_width - kFullscreenPaddingEnd;
796 SetAnchorRect(gfx::Rect(x_pos, screen_bounds.y(), 0, 0));
799 void ManagePasswordsBubbleView::Close() {
800 mouse_handler_.reset();
801 GetWidget()->Close();
804 void ManagePasswordsBubbleView::Refresh() {
805 RemoveAllChildViews(true);
806 initially_focused_view_ = NULL;
807 if (password_manager::ui::IsPendingState(model()->state())) {
808 if (never_save_passwords_)
809 AddChildView(new ConfirmNeverView(this));
811 AddChildView(new PendingView(this));
812 } else if (model()->state() == password_manager::ui::BLACKLIST_STATE) {
813 AddChildView(new BlacklistedView(this));
814 } else if (model()->state() == password_manager::ui::CONFIRMATION_STATE) {
815 AddChildView(new SaveConfirmationView(this));
817 AddChildView(new ManageView(this));
819 GetLayoutManager()->Layout(this);
822 void ManagePasswordsBubbleView::NotifyNeverForThisSiteClicked() {
823 if (model()->best_matches().empty()) {
824 // Skip confirmation if there are no existing passwords for this site.
825 NotifyConfirmedNeverForThisSite();
827 never_save_passwords_ = true;
832 void ManagePasswordsBubbleView::NotifyConfirmedNeverForThisSite() {
833 model()->OnNeverForThisSiteClicked();
837 void ManagePasswordsBubbleView::NotifyUndoNeverForThisSite() {
838 never_save_passwords_ = false;
842 void ManagePasswordsBubbleView::Init() {
843 views::FillLayout* layout = new views::FillLayout();
844 SetLayoutManager(layout);
849 void ManagePasswordsBubbleView::WindowClosing() {
850 // Close() closes the window asynchronously, so by the time we reach here,
851 // |manage_passwords_bubble_| may have already been reset.
852 if (manage_passwords_bubble_ == this)
853 manage_passwords_bubble_ = NULL;
856 views::View* ManagePasswordsBubbleView::GetInitiallyFocusedView() {
857 return initially_focused_view_;