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/chrome_notification_types.h"
8 #include "chrome/browser/ui/browser.h"
9 #include "chrome/browser/ui/browser_finder.h"
10 #include "chrome/browser/ui/browser_window.h"
11 #include "chrome/browser/ui/passwords/manage_passwords_bubble_model.h"
12 #include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h"
13 #include "chrome/browser/ui/views/frame/browser_view.h"
14 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
15 #include "chrome/browser/ui/views/passwords/manage_password_item_view.h"
16 #include "chrome/browser/ui/views/passwords/manage_passwords_icon_view.h"
17 #include "chrome/grit/generated_resources.h"
18 #include "components/password_manager/core/common/password_manager_ui.h"
19 #include "content/public/browser/notification_source.h"
20 #include "ui/base/l10n/l10n_util.h"
21 #include "ui/base/models/combobox_model.h"
22 #include "ui/base/resource/resource_bundle.h"
23 #include "ui/gfx/text_utils.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/styled_label.h"
28 #include "ui/views/layout/fill_layout.h"
29 #include "ui/views/layout/grid_layout.h"
30 #include "ui/views/layout/layout_constants.h"
33 // Helpers --------------------------------------------------------------------
37 // The number of seconds the inactive bubble should stay alive.
38 const int kBubbleCloseDelay = 15;
40 const int kDesiredBubbleWidth = 370;
43 // | | (FILL, FILL) | |
44 // Used for the bubble's header, the credentials list, and for simple
45 // messages like "No passwords".
46 SINGLE_VIEW_COLUMN_SET = 0,
48 // | | (TRAILING, CENTER) | | (TRAILING, CENTER) | |
49 // Used for buttons at the bottom of the bubble which should nest at the
50 // bottom-right corner.
51 DOUBLE_BUTTON_COLUMN_SET = 1,
53 // | | (LEADING, CENTER) | | (TRAILING, CENTER) | |
54 // Used for buttons at the bottom of the bubble which should occupy
56 LINK_BUTTON_COLUMN_SET = 2,
58 // | | (TRAILING, CENTER) | |
59 // Used when there is only one button which should next at the bottom-right
61 SINGLE_BUTTON_COLUMN_SET = 3,
64 // Construct an appropriate ColumnSet for the given |type|, and add it
66 void BuildColumnSet(views::GridLayout* layout, ColumnSetType type) {
67 views::ColumnSet* column_set = layout->AddColumnSet(type);
68 column_set->AddPaddingColumn(0, views::kPanelHorizMargin);
69 int full_width = kDesiredBubbleWidth - (2 * views::kPanelHorizMargin);
71 case SINGLE_VIEW_COLUMN_SET:
72 column_set->AddColumn(views::GridLayout::FILL,
73 views::GridLayout::FILL,
75 views::GridLayout::FIXED,
80 case DOUBLE_BUTTON_COLUMN_SET:
81 column_set->AddColumn(views::GridLayout::TRAILING,
82 views::GridLayout::CENTER,
84 views::GridLayout::USE_PREF,
87 column_set->AddPaddingColumn(0, views::kRelatedButtonHSpacing);
88 column_set->AddColumn(views::GridLayout::TRAILING,
89 views::GridLayout::CENTER,
91 views::GridLayout::USE_PREF,
95 case LINK_BUTTON_COLUMN_SET:
96 column_set->AddColumn(views::GridLayout::LEADING,
97 views::GridLayout::CENTER,
99 views::GridLayout::USE_PREF,
102 column_set->AddPaddingColumn(0, views::kRelatedButtonHSpacing);
103 column_set->AddColumn(views::GridLayout::TRAILING,
104 views::GridLayout::CENTER,
106 views::GridLayout::USE_PREF,
110 case SINGLE_BUTTON_COLUMN_SET:
111 column_set->AddColumn(views::GridLayout::TRAILING,
112 views::GridLayout::CENTER,
114 views::GridLayout::USE_PREF,
118 column_set->AddPaddingColumn(0, views::kPanelHorizMargin);
121 // Given a layout and a model, add an appropriate title using a
122 // SINGLE_VIEW_COLUMN_SET, followed by a spacer row.
123 void AddTitleRow(views::GridLayout* layout, ManagePasswordsBubbleModel* model) {
124 views::Label* title_label = new views::Label(model->title());
125 title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
126 title_label->SetMultiLine(true);
127 title_label->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
128 ui::ResourceBundle::MediumFont));
130 // Add the title to the layout with appropriate padding.
131 layout->StartRowWithPadding(
132 0, SINGLE_VIEW_COLUMN_SET, 0, views::kRelatedControlSmallVerticalSpacing);
133 layout->AddView(title_label);
134 layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing);
140 // Globals --------------------------------------------------------------------
144 void ShowManagePasswordsBubble(content::WebContents* web_contents) {
145 if (ManagePasswordsBubbleView::IsShowing()) {
146 // The bubble is currently shown for some other tab. We should close it now
147 // and open for |web_contents|.
148 ManagePasswordsBubbleView::CloseBubble();
150 ManagePasswordsUIController* controller =
151 ManagePasswordsUIController::FromWebContents(web_contents);
152 ManagePasswordsBubbleView::ShowBubble(
154 password_manager::ui::IsAutomaticDisplayState(controller->state())
155 ? ManagePasswordsBubbleView::AUTOMATIC
156 : ManagePasswordsBubbleView::USER_ACTION);
159 } // namespace chrome
162 // ManagePasswordsBubbleView::PendingView -------------------------------------
164 ManagePasswordsBubbleView::PendingView::PendingView(
165 ManagePasswordsBubbleView* parent)
167 views::GridLayout* layout = new views::GridLayout(this);
168 layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
169 SetLayoutManager(layout);
171 // Create the pending credential item, save button and refusal combobox.
172 ManagePasswordItemView* item =
173 new ManagePasswordItemView(parent->model(),
174 parent->model()->pending_credentials(),
175 password_manager::ui::FIRST_ITEM);
176 save_button_ = new views::BlueButton(
177 this, l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_SAVE_BUTTON));
178 save_button_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
179 ui::ResourceBundle::SmallFont));
181 combobox_model_.reset(new SavePasswordRefusalComboboxModel());
182 refuse_combobox_.reset(new views::Combobox(combobox_model_.get()));
183 refuse_combobox_->set_listener(this);
184 refuse_combobox_->SetStyle(views::Combobox::STYLE_ACTION);
185 // TODO(mkwst): Need a mechanism to pipe a font list down into a combobox.
188 BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
189 AddTitleRow(layout, parent_->model());
192 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
193 layout->AddView(item);
196 BuildColumnSet(layout, DOUBLE_BUTTON_COLUMN_SET);
197 layout->StartRowWithPadding(
198 0, DOUBLE_BUTTON_COLUMN_SET, 0, views::kRelatedControlVerticalSpacing);
199 layout->AddView(save_button_);
200 layout->AddView(refuse_combobox_.get());
202 // Extra padding for visual awesomeness.
203 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
205 parent_->set_initially_focused_view(save_button_);
208 ManagePasswordsBubbleView::PendingView::~PendingView() {
211 void ManagePasswordsBubbleView::PendingView::ButtonPressed(
212 views::Button* sender,
213 const ui::Event& event) {
214 DCHECK(sender == save_button_);
215 parent_->model()->OnSaveClicked();
219 void ManagePasswordsBubbleView::PendingView::OnPerformAction(
220 views::Combobox* source) {
221 DCHECK_EQ(source, refuse_combobox_);
222 switch (refuse_combobox_->selected_index()) {
223 case SavePasswordRefusalComboboxModel::INDEX_NOPE:
224 parent_->model()->OnNopeClicked();
227 case SavePasswordRefusalComboboxModel::INDEX_NEVER_FOR_THIS_SITE:
228 parent_->NotifyNeverForThisSiteClicked();
233 // ManagePasswordsBubbleView::ConfirmNeverView ---------------------------------
235 ManagePasswordsBubbleView::ConfirmNeverView::ConfirmNeverView(
236 ManagePasswordsBubbleView* parent)
238 views::GridLayout* layout = new views::GridLayout(this);
239 layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
240 SetLayoutManager(layout);
243 BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
244 views::Label* title_label = new views::Label(l10n_util::GetStringUTF16(
245 IDS_MANAGE_PASSWORDS_BLACKLIST_CONFIRMATION_TITLE));
246 title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
247 title_label->SetMultiLine(true);
248 title_label->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
249 ui::ResourceBundle::MediumFont));
250 layout->StartRowWithPadding(
251 0, SINGLE_VIEW_COLUMN_SET, 0, views::kRelatedControlSmallVerticalSpacing);
252 layout->AddView(title_label);
253 layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing);
255 // Confirmation text.
256 views::Label* confirmation = new views::Label(l10n_util::GetStringUTF16(
257 IDS_MANAGE_PASSWORDS_BLACKLIST_CONFIRMATION_TEXT));
258 confirmation->SetHorizontalAlignment(gfx::ALIGN_LEFT);
259 confirmation->SetMultiLine(true);
260 confirmation->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
261 ui::ResourceBundle::SmallFont));
262 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
263 layout->AddView(confirmation);
264 layout->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing);
266 // Confirm and undo buttons.
267 BuildColumnSet(layout, DOUBLE_BUTTON_COLUMN_SET);
268 layout->StartRowWithPadding(
269 0, DOUBLE_BUTTON_COLUMN_SET, 0, views::kRelatedControlVerticalSpacing);
271 confirm_button_ = new views::LabelButton(
273 l10n_util::GetStringUTF16(
274 IDS_MANAGE_PASSWORDS_BLACKLIST_CONFIRMATION_BUTTON));
275 confirm_button_->SetStyle(views::Button::STYLE_BUTTON);
276 confirm_button_->SetFontList(
277 ui::ResourceBundle::GetSharedInstance().GetFontList(
278 ui::ResourceBundle::SmallFont));
279 layout->AddView(confirm_button_);
282 new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_CANCEL));
283 undo_button_->SetStyle(views::Button::STYLE_BUTTON);
284 undo_button_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
285 ui::ResourceBundle::SmallFont));
286 layout->AddView(undo_button_);
288 // Extra padding for visual awesomeness.
289 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
291 parent_->set_initially_focused_view(confirm_button_);
294 ManagePasswordsBubbleView::ConfirmNeverView::~ConfirmNeverView() {
297 void ManagePasswordsBubbleView::ConfirmNeverView::ButtonPressed(
298 views::Button* sender,
299 const ui::Event& event) {
300 DCHECK(sender == confirm_button_ || sender == undo_button_);
301 if (sender == confirm_button_)
302 parent_->NotifyConfirmedNeverForThisSite();
304 parent_->NotifyUndoNeverForThisSite();
307 // ManagePasswordsBubbleView::ManageView --------------------------------------
309 ManagePasswordsBubbleView::ManageView::ManageView(
310 ManagePasswordsBubbleView* parent)
312 views::GridLayout* layout = new views::GridLayout(this);
313 layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
314 SetLayoutManager(layout);
317 BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
318 AddTitleRow(layout, parent_->model());
320 // If we have a list of passwords to store for the current site, display
321 // them to the user for management. Otherwise, render a "No passwords for
322 // this site" message.
323 if (!parent_->model()->best_matches().empty()) {
324 for (autofill::ConstPasswordFormMap::const_iterator i(
325 parent_->model()->best_matches().begin());
326 i != parent_->model()->best_matches().end();
328 ManagePasswordItemView* item = new ManagePasswordItemView(
331 i == parent_->model()->best_matches().begin()
332 ? password_manager::ui::FIRST_ITEM
333 : password_manager::ui::SUBSEQUENT_ITEM);
335 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
336 layout->AddView(item);
339 views::Label* empty_label = new views::Label(
340 l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_NO_PASSWORDS));
341 empty_label->SetMultiLine(true);
342 empty_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
343 empty_label->SetFontList(
344 ui::ResourceBundle::GetSharedInstance().GetFontList(
345 ui::ResourceBundle::SmallFont));
347 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
348 layout->AddView(empty_label);
349 layout->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing);
352 // Then add the "manage passwords" link and "Done" button.
353 manage_link_ = new views::Link(parent_->model()->manage_link());
354 manage_link_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
355 manage_link_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
356 ui::ResourceBundle::SmallFont));
357 manage_link_->SetUnderline(false);
358 manage_link_->set_listener(this);
361 new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_DONE));
362 done_button_->SetStyle(views::Button::STYLE_BUTTON);
363 done_button_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
364 ui::ResourceBundle::SmallFont));
366 BuildColumnSet(layout, LINK_BUTTON_COLUMN_SET);
367 layout->StartRowWithPadding(
368 0, LINK_BUTTON_COLUMN_SET, 0, views::kRelatedControlVerticalSpacing);
369 layout->AddView(manage_link_);
370 layout->AddView(done_button_);
372 // Extra padding for visual awesomeness.
373 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
375 parent_->set_initially_focused_view(done_button_);
378 ManagePasswordsBubbleView::ManageView::~ManageView() {
381 void ManagePasswordsBubbleView::ManageView::ButtonPressed(
382 views::Button* sender,
383 const ui::Event& event) {
384 DCHECK(sender == done_button_);
385 parent_->model()->OnDoneClicked();
389 void ManagePasswordsBubbleView::ManageView::LinkClicked(views::Link* source,
391 DCHECK_EQ(source, manage_link_);
392 parent_->model()->OnManageLinkClicked();
396 // ManagePasswordsBubbleView::BlacklistedView ---------------------------------
398 ManagePasswordsBubbleView::BlacklistedView::BlacklistedView(
399 ManagePasswordsBubbleView* parent)
401 views::GridLayout* layout = new views::GridLayout(this);
402 layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
403 SetLayoutManager(layout);
406 BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
407 AddTitleRow(layout, parent_->model());
409 // Add the "Hey! You blacklisted this site!" text.
410 views::Label* blacklisted = new views::Label(
411 l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_BLACKLISTED));
412 blacklisted->SetMultiLine(true);
413 blacklisted->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
414 ui::ResourceBundle::SmallFont));
415 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
416 layout->AddView(blacklisted);
417 layout->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing);
419 // Then add the "enable password manager" and "Done" buttons.
420 unblacklist_button_ = new views::BlueButton(
421 this, l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_UNBLACKLIST_BUTTON));
422 unblacklist_button_->SetFontList(
423 ui::ResourceBundle::GetSharedInstance().GetFontList(
424 ui::ResourceBundle::SmallFont));
426 new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_DONE));
427 done_button_->SetStyle(views::Button::STYLE_BUTTON);
428 done_button_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
429 ui::ResourceBundle::SmallFont));
431 BuildColumnSet(layout, DOUBLE_BUTTON_COLUMN_SET);
432 layout->StartRowWithPadding(
433 0, DOUBLE_BUTTON_COLUMN_SET, 0, views::kRelatedControlVerticalSpacing);
434 layout->AddView(unblacklist_button_);
435 layout->AddView(done_button_);
437 // Extra padding for visual awesomeness.
438 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
440 parent_->set_initially_focused_view(unblacklist_button_);
443 ManagePasswordsBubbleView::BlacklistedView::~BlacklistedView() {
446 void ManagePasswordsBubbleView::BlacklistedView::ButtonPressed(
447 views::Button* sender,
448 const ui::Event& event) {
449 if (sender == done_button_)
450 parent_->model()->OnDoneClicked();
451 else if (sender == unblacklist_button_)
452 parent_->model()->OnUnblacklistClicked();
458 // ManagePasswordsBubbleView::SaveConfirmationView ----------------------------
460 ManagePasswordsBubbleView::SaveConfirmationView::SaveConfirmationView(
461 ManagePasswordsBubbleView* parent)
463 views::GridLayout* layout = new views::GridLayout(this);
464 layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
465 SetLayoutManager(layout);
467 BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
468 AddTitleRow(layout, parent_->model());
470 views::StyledLabel* confirmation =
471 new views::StyledLabel(parent_->model()->save_confirmation_text(), this);
472 confirmation->SetBaseFontList(
473 ui::ResourceBundle::GetSharedInstance().GetFontList(
474 ui::ResourceBundle::SmallFont));
475 confirmation->AddStyleRange(
476 parent_->model()->save_confirmation_link_range(),
477 views::StyledLabel::RangeStyleInfo::CreateForLink());
479 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
480 layout->AddView(confirmation);
482 ok_button_ = new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_OK));
483 ok_button_->SetStyle(views::Button::STYLE_BUTTON);
484 ok_button_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
485 ui::ResourceBundle::SmallFont));
487 BuildColumnSet(layout, SINGLE_BUTTON_COLUMN_SET);
488 layout->StartRowWithPadding(
489 0, SINGLE_BUTTON_COLUMN_SET, 0, views::kRelatedControlVerticalSpacing);
490 layout->AddView(ok_button_);
492 // Extra padding for visual awesomeness.
493 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
495 parent_->set_initially_focused_view(ok_button_);
498 ManagePasswordsBubbleView::SaveConfirmationView::~SaveConfirmationView() {
501 void ManagePasswordsBubbleView::SaveConfirmationView::StyledLabelLinkClicked(
502 const gfx::Range& range, int event_flags) {
503 DCHECK_EQ(range, parent_->model()->save_confirmation_link_range());
504 parent_->model()->OnRemoteManageLinkClicked();
508 void ManagePasswordsBubbleView::SaveConfirmationView::ButtonPressed(
509 views::Button* sender, const ui::Event& event) {
510 DCHECK_EQ(sender, ok_button_);
511 parent_->model()->OnOKClicked();
515 // ManagePasswordsBubbleView --------------------------------------------------
518 ManagePasswordsBubbleView* ManagePasswordsBubbleView::manage_passwords_bubble_ =
522 void ManagePasswordsBubbleView::ShowBubble(content::WebContents* web_contents,
523 DisplayReason reason) {
524 Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
526 DCHECK(browser->window());
527 DCHECK(browser->fullscreen_controller());
532 BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
533 bool is_fullscreen = browser_view->IsFullscreen();
534 ManagePasswordsIconView* anchor_view =
537 : browser_view->GetLocationBarView()->manage_passwords_icon_view();
538 manage_passwords_bubble_ = new ManagePasswordsBubbleView(
539 web_contents, anchor_view, reason);
542 manage_passwords_bubble_->set_parent_window(
543 web_contents->GetTopLevelNativeWindow());
546 views::BubbleDelegateView::CreateBubble(manage_passwords_bubble_);
548 // Adjust for fullscreen after creation as it relies on the content size.
550 manage_passwords_bubble_->AdjustForFullscreen(
551 browser_view->GetBoundsInScreen());
553 if (reason == AUTOMATIC)
554 manage_passwords_bubble_->GetWidget()->ShowInactive();
556 manage_passwords_bubble_->GetWidget()->Show();
557 manage_passwords_bubble_->StartTimerIfNecessary();
561 void ManagePasswordsBubbleView::CloseBubble() {
562 if (manage_passwords_bubble_)
563 manage_passwords_bubble_->Close();
567 void ManagePasswordsBubbleView::ActivateBubble() {
570 manage_passwords_bubble_->GetWidget()->Activate();
574 bool ManagePasswordsBubbleView::IsShowing() {
575 // The bubble may be in the process of closing.
576 return (manage_passwords_bubble_ != NULL) &&
577 manage_passwords_bubble_->GetWidget()->IsVisible();
580 ManagePasswordsBubbleView::ManagePasswordsBubbleView(
581 content::WebContents* web_contents,
582 ManagePasswordsIconView* anchor_view,
583 DisplayReason reason)
584 : ManagePasswordsBubble(web_contents, reason),
585 BubbleDelegateView(anchor_view,
586 anchor_view ? views::BubbleBorder::TOP_RIGHT
587 : views::BubbleBorder::NONE),
588 anchor_view_(anchor_view),
589 never_save_passwords_(false),
590 initially_focused_view_(NULL) {
591 // Compensate for built-in vertical padding in the anchor view's image.
592 set_anchor_view_insets(gfx::Insets(5, 0, 5, 0));
593 set_notify_enter_exit_on_child(true);
595 anchor_view->SetActive(true);
598 ManagePasswordsBubbleView::~ManagePasswordsBubbleView() {
600 anchor_view_->SetActive(false);
603 void ManagePasswordsBubbleView::AdjustForFullscreen(
604 const gfx::Rect& screen_bounds) {
608 // The bubble's padding from the screen edge, used in fullscreen.
609 const int kFullscreenPaddingEnd = 20;
610 const size_t bubble_half_width = width() / 2;
611 const int x_pos = base::i18n::IsRTL() ?
612 screen_bounds.x() + bubble_half_width + kFullscreenPaddingEnd :
613 screen_bounds.right() - bubble_half_width - kFullscreenPaddingEnd;
614 SetAnchorRect(gfx::Rect(x_pos, screen_bounds.y(), 0, 0));
617 void ManagePasswordsBubbleView::Close() {
618 GetWidget()->Close();
621 void ManagePasswordsBubbleView::Init() {
622 views::FillLayout* layout = new views::FillLayout();
623 SetLayoutManager(layout);
628 void ManagePasswordsBubbleView::WindowClosing() {
629 // Close() closes the window asynchronously, so by the time we reach here,
630 // |manage_passwords_bubble_| may have already been reset.
631 if (manage_passwords_bubble_ == this)
632 manage_passwords_bubble_ = NULL;
635 void ManagePasswordsBubbleView::OnWidgetActivationChanged(views::Widget* widget,
637 if (active && widget == GetWidget())
639 BubbleDelegateView::OnWidgetActivationChanged(widget, active);
642 views::View* ManagePasswordsBubbleView::GetInitiallyFocusedView() {
643 return initially_focused_view_;
646 void ManagePasswordsBubbleView::OnMouseEntered(const ui::MouseEvent& event) {
650 void ManagePasswordsBubbleView::OnMouseExited(const ui::MouseEvent& event) {
651 StartTimerIfNecessary();
654 void ManagePasswordsBubbleView::Refresh() {
655 RemoveAllChildViews(true);
656 initially_focused_view_ = NULL;
657 if (password_manager::ui::IsPendingState(model()->state())) {
658 if (never_save_passwords_)
659 AddChildView(new ConfirmNeverView(this));
661 AddChildView(new PendingView(this));
662 } else if (model()->state() == password_manager::ui::BLACKLIST_STATE) {
663 AddChildView(new BlacklistedView(this));
664 } else if (model()->state() == password_manager::ui::CONFIRMATION_STATE) {
665 AddChildView(new SaveConfirmationView(this));
667 AddChildView(new ManageView(this));
669 GetLayoutManager()->Layout(this);
670 // If we refresh the existing bubble we may want to restart the timer.
672 StartTimerIfNecessary();
675 void ManagePasswordsBubbleView::NotifyNeverForThisSiteClicked() {
676 if (model()->best_matches().empty()) {
677 // Skip confirmation if there are no existing passwords for this site.
678 NotifyConfirmedNeverForThisSite();
680 never_save_passwords_ = true;
685 void ManagePasswordsBubbleView::NotifyConfirmedNeverForThisSite() {
686 model()->OnNeverForThisSiteClicked();
690 void ManagePasswordsBubbleView::NotifyUndoNeverForThisSite() {
691 never_save_passwords_ = false;
695 void ManagePasswordsBubbleView::StartTimerIfNecessary() {
696 // Active bubble will stay visible until it loses focus.
697 if (GetWidget()->IsActive())
699 timer_.Start(FROM_HERE,
700 base::TimeDelta::FromSeconds(kBubbleCloseDelay),
702 &ManagePasswordsBubbleView::Close);