manage_passwords_bubble_view.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
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.
4
5#include "chrome/browser/ui/views/passwords/manage_passwords_bubble_view.h"
6
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 "components/password_manager/core/common/password_manager_ui.h"
18#include "content/public/browser/notification_source.h"
19#include "grit/generated_resources.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"
31
32
33// Helpers --------------------------------------------------------------------
34
35namespace {
36
37// The number of seconds the inactive bubble should stay alive.
38const int kBubbleCloseDelay = 15;
39
40const int kDesiredBubbleWidth = 370;
41
42enum ColumnSetType {
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,
47
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,
52
53  // | | (LEADING, CENTER) | | (TRAILING, CENTER) | |
54  // Used for buttons at the bottom of the bubble which should occupy
55  // the corners.
56  LINK_BUTTON_COLUMN_SET = 2,
57
58  // | | (TRAILING, CENTER) | |
59  // Used when there is only one button which should next at the bottom-right
60  // corner.
61  SINGLE_BUTTON_COLUMN_SET = 3,
62};
63
64// Construct an appropriate ColumnSet for the given |type|, and add it
65// to |layout|.
66void 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);
70  switch (type) {
71    case SINGLE_VIEW_COLUMN_SET:
72      column_set->AddColumn(views::GridLayout::FILL,
73                            views::GridLayout::FILL,
74                            0,
75                            views::GridLayout::FIXED,
76                            full_width,
77                            0);
78      break;
79
80    case DOUBLE_BUTTON_COLUMN_SET:
81      column_set->AddColumn(views::GridLayout::TRAILING,
82                            views::GridLayout::CENTER,
83                            1,
84                            views::GridLayout::USE_PREF,
85                            0,
86                            0);
87      column_set->AddPaddingColumn(0, views::kRelatedButtonHSpacing);
88      column_set->AddColumn(views::GridLayout::TRAILING,
89                            views::GridLayout::CENTER,
90                            0,
91                            views::GridLayout::USE_PREF,
92                            0,
93                            0);
94      break;
95    case LINK_BUTTON_COLUMN_SET:
96      column_set->AddColumn(views::GridLayout::LEADING,
97                            views::GridLayout::CENTER,
98                            1,
99                            views::GridLayout::USE_PREF,
100                            0,
101                            0);
102      column_set->AddPaddingColumn(0, views::kRelatedButtonHSpacing);
103      column_set->AddColumn(views::GridLayout::TRAILING,
104                            views::GridLayout::CENTER,
105                            0,
106                            views::GridLayout::USE_PREF,
107                            0,
108                            0);
109      break;
110    case SINGLE_BUTTON_COLUMN_SET:
111      column_set->AddColumn(views::GridLayout::TRAILING,
112                            views::GridLayout::CENTER,
113                            1,
114                            views::GridLayout::USE_PREF,
115                            0,
116                            0);
117  }
118  column_set->AddPaddingColumn(0, views::kPanelHorizMargin);
119}
120
121// Given a layout and a model, add an appropriate title using a
122// SINGLE_VIEW_COLUMN_SET, followed by a spacer row.
123void 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));
129
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);
135}
136
137}  // namespace
138
139
140// Globals --------------------------------------------------------------------
141
142namespace chrome {
143
144void 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();
149  }
150  ManagePasswordsUIController* controller =
151      ManagePasswordsUIController::FromWebContents(web_contents);
152  ManagePasswordsBubbleView::ShowBubble(
153      web_contents,
154      password_manager::ui::IsAutomaticDisplayState(controller->state())
155          ? ManagePasswordsBubbleView::AUTOMATIC
156          : ManagePasswordsBubbleView::USER_ACTION);
157}
158
159}  // namespace chrome
160
161
162// ManagePasswordsBubbleView::PendingView -------------------------------------
163
164ManagePasswordsBubbleView::PendingView::PendingView(
165    ManagePasswordsBubbleView* parent)
166    : parent_(parent) {
167  views::GridLayout* layout = new views::GridLayout(this);
168  layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
169  SetLayoutManager(layout);
170
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                                 ManagePasswordItemView::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));
180
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.
186
187  // Title row.
188  BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
189  AddTitleRow(layout, parent_->model());
190
191  // Credential row.
192  layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
193  layout->AddView(item);
194
195  // Button row.
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());
201
202  // Extra padding for visual awesomeness.
203  layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
204
205  parent_->set_initially_focused_view(save_button_);
206}
207
208ManagePasswordsBubbleView::PendingView::~PendingView() {
209}
210
211void ManagePasswordsBubbleView::PendingView::ButtonPressed(
212    views::Button* sender,
213    const ui::Event& event) {
214  DCHECK(sender == save_button_);
215  parent_->model()->OnSaveClicked();
216  parent_->Close();
217}
218
219void 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();
225      parent_->Close();
226      break;
227    case SavePasswordRefusalComboboxModel::INDEX_NEVER_FOR_THIS_SITE:
228      parent_->NotifyNeverForThisSiteClicked();
229      break;
230  }
231}
232
233// ManagePasswordsBubbleView::ConfirmNeverView ---------------------------------
234
235ManagePasswordsBubbleView::ConfirmNeverView::ConfirmNeverView(
236    ManagePasswordsBubbleView* parent)
237    : parent_(parent) {
238  views::GridLayout* layout = new views::GridLayout(this);
239  layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
240  SetLayoutManager(layout);
241
242  // Title row.
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);
254
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);
265
266  // Confirm and undo buttons.
267  BuildColumnSet(layout, DOUBLE_BUTTON_COLUMN_SET);
268  layout->StartRowWithPadding(
269      0, DOUBLE_BUTTON_COLUMN_SET, 0, views::kRelatedControlVerticalSpacing);
270
271  confirm_button_ = new views::LabelButton(
272      this,
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_);
280
281  undo_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_);
287
288  // Extra padding for visual awesomeness.
289  layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
290
291  parent_->set_initially_focused_view(confirm_button_);
292}
293
294ManagePasswordsBubbleView::ConfirmNeverView::~ConfirmNeverView() {
295}
296
297void 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();
303  else
304    parent_->NotifyUndoNeverForThisSite();
305}
306
307// ManagePasswordsBubbleView::ManageView --------------------------------------
308
309ManagePasswordsBubbleView::ManageView::ManageView(
310    ManagePasswordsBubbleView* parent)
311    : parent_(parent) {
312  views::GridLayout* layout = new views::GridLayout(this);
313  layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
314  SetLayoutManager(layout);
315
316  // Add the title.
317  BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
318  AddTitleRow(layout, parent_->model());
319
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();
327         ++i) {
328      ManagePasswordItemView* item = new ManagePasswordItemView(
329          parent_->model(),
330          *i->second,
331          i == parent_->model()->best_matches().begin()
332              ? ManagePasswordItemView::FIRST_ITEM
333              : ManagePasswordItemView::SUBSEQUENT_ITEM);
334
335      layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
336      layout->AddView(item);
337    }
338  } else {
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));
346
347    layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
348    layout->AddView(empty_label);
349    layout->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing);
350  }
351
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);
359
360  done_button_ =
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));
365
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_);
371
372  // Extra padding for visual awesomeness.
373  layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
374
375  parent_->set_initially_focused_view(done_button_);
376}
377
378ManagePasswordsBubbleView::ManageView::~ManageView() {
379}
380
381void ManagePasswordsBubbleView::ManageView::ButtonPressed(
382    views::Button* sender,
383    const ui::Event& event) {
384  DCHECK(sender == done_button_);
385  parent_->model()->OnDoneClicked();
386  parent_->Close();
387}
388
389void ManagePasswordsBubbleView::ManageView::LinkClicked(views::Link* source,
390                                                        int event_flags) {
391  DCHECK_EQ(source, manage_link_);
392  parent_->model()->OnManageLinkClicked();
393  parent_->Close();
394}
395
396// ManagePasswordsBubbleView::BlacklistedView ---------------------------------
397
398ManagePasswordsBubbleView::BlacklistedView::BlacklistedView(
399    ManagePasswordsBubbleView* parent)
400    : parent_(parent) {
401  views::GridLayout* layout = new views::GridLayout(this);
402  layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
403  SetLayoutManager(layout);
404
405  // Add the title.
406  BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
407  AddTitleRow(layout, parent_->model());
408
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);
418
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));
425  done_button_ =
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));
430
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_);
436
437  // Extra padding for visual awesomeness.
438  layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
439
440  parent_->set_initially_focused_view(unblacklist_button_);
441}
442
443ManagePasswordsBubbleView::BlacklistedView::~BlacklistedView() {
444}
445
446void 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();
453  else
454    NOTREACHED();
455  parent_->Close();
456}
457
458// ManagePasswordsBubbleView::SaveConfirmationView ----------------------------
459
460ManagePasswordsBubbleView::SaveConfirmationView::SaveConfirmationView(
461    ManagePasswordsBubbleView* parent)
462    : parent_(parent) {
463  views::GridLayout* layout = new views::GridLayout(this);
464  layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
465  SetLayoutManager(layout);
466
467  BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
468  AddTitleRow(layout, parent_->model());
469
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());
478
479  layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
480  layout->AddView(confirmation);
481
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));
486
487  BuildColumnSet(layout, SINGLE_BUTTON_COLUMN_SET);
488  layout->StartRowWithPadding(
489      0, SINGLE_BUTTON_COLUMN_SET, 0, views::kRelatedControlVerticalSpacing);
490  layout->AddView(ok_button_);
491
492  // Extra padding for visual awesomeness.
493  layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
494
495  parent_->set_initially_focused_view(ok_button_);
496}
497
498ManagePasswordsBubbleView::SaveConfirmationView::~SaveConfirmationView() {
499}
500
501void 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();
505  parent_->Close();
506}
507
508void ManagePasswordsBubbleView::SaveConfirmationView::ButtonPressed(
509    views::Button* sender, const ui::Event& event) {
510  DCHECK_EQ(sender, ok_button_);
511  parent_->model()->OnOKClicked();
512  parent_->Close();
513}
514
515// ManagePasswordsBubbleView --------------------------------------------------
516
517// static
518ManagePasswordsBubbleView* ManagePasswordsBubbleView::manage_passwords_bubble_ =
519    NULL;
520
521// static
522void ManagePasswordsBubbleView::ShowBubble(content::WebContents* web_contents,
523                                           DisplayReason reason) {
524  Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
525  DCHECK(browser);
526  DCHECK(browser->window());
527  DCHECK(browser->fullscreen_controller());
528
529  if (IsShowing())
530    return;
531
532  BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
533  bool is_fullscreen = browser_view->IsFullscreen();
534  ManagePasswordsIconView* anchor_view =
535      is_fullscreen
536          ? NULL
537          : browser_view->GetLocationBarView()->manage_passwords_icon_view();
538  manage_passwords_bubble_ = new ManagePasswordsBubbleView(
539      web_contents, anchor_view, reason);
540
541  if (is_fullscreen) {
542    manage_passwords_bubble_->set_parent_window(
543        web_contents->GetTopLevelNativeWindow());
544  }
545
546  views::BubbleDelegateView::CreateBubble(manage_passwords_bubble_);
547
548  // Adjust for fullscreen after creation as it relies on the content size.
549  if (is_fullscreen) {
550    manage_passwords_bubble_->AdjustForFullscreen(
551        browser_view->GetBoundsInScreen());
552  }
553  if (reason == AUTOMATIC)
554    manage_passwords_bubble_->GetWidget()->ShowInactive();
555  else
556    manage_passwords_bubble_->GetWidget()->Show();
557  manage_passwords_bubble_->StartTimerIfNecessary();
558}
559
560// static
561void ManagePasswordsBubbleView::CloseBubble() {
562  if (manage_passwords_bubble_)
563    manage_passwords_bubble_->Close();
564}
565
566// static
567void ManagePasswordsBubbleView::ActivateBubble() {
568  if (!IsShowing())
569    return;
570  manage_passwords_bubble_->GetWidget()->Activate();
571}
572
573// static
574bool ManagePasswordsBubbleView::IsShowing() {
575  // The bubble may be in the process of closing.
576  return (manage_passwords_bubble_ != NULL) &&
577      manage_passwords_bubble_->GetWidget()->IsVisible();
578}
579
580ManagePasswordsBubbleView::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);
594  if (anchor_view)
595    anchor_view->SetActive(true);
596}
597
598ManagePasswordsBubbleView::~ManagePasswordsBubbleView() {
599  if (anchor_view_)
600    anchor_view_->SetActive(false);
601}
602
603void ManagePasswordsBubbleView::AdjustForFullscreen(
604    const gfx::Rect& screen_bounds) {
605  if (GetAnchorView())
606    return;
607
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));
615}
616
617void ManagePasswordsBubbleView::Close() {
618  GetWidget()->Close();
619}
620
621void ManagePasswordsBubbleView::Init() {
622  views::FillLayout* layout = new views::FillLayout();
623  SetLayoutManager(layout);
624
625  Refresh();
626}
627
628void 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;
633}
634
635void ManagePasswordsBubbleView::OnWidgetActivationChanged(views::Widget* widget,
636                                                          bool active) {
637  if (active && widget == GetWidget())
638    timer_.Stop();
639  BubbleDelegateView::OnWidgetActivationChanged(widget, active);
640}
641
642views::View* ManagePasswordsBubbleView::GetInitiallyFocusedView() {
643  return initially_focused_view_;
644}
645
646void ManagePasswordsBubbleView::OnMouseEntered(const ui::MouseEvent& event) {
647  timer_.Stop();
648}
649
650void ManagePasswordsBubbleView::OnMouseExited(const ui::MouseEvent& event) {
651  StartTimerIfNecessary();
652}
653
654void 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));
660    else
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));
666  } else {
667    AddChildView(new ManageView(this));
668  }
669  GetLayoutManager()->Layout(this);
670  // If we refresh the existing bubble we may want to restart the timer.
671  if (GetWidget())
672    StartTimerIfNecessary();
673}
674
675void ManagePasswordsBubbleView::NotifyNeverForThisSiteClicked() {
676  if (model()->best_matches().empty()) {
677    // Skip confirmation if there are no existing passwords for this site.
678    NotifyConfirmedNeverForThisSite();
679  } else {
680    never_save_passwords_ = true;
681    Refresh();
682  }
683}
684
685void ManagePasswordsBubbleView::NotifyConfirmedNeverForThisSite() {
686  model()->OnNeverForThisSiteClicked();
687  Close();
688}
689
690void ManagePasswordsBubbleView::NotifyUndoNeverForThisSite() {
691  never_save_passwords_ = false;
692  Refresh();
693}
694
695void ManagePasswordsBubbleView::StartTimerIfNecessary() {
696  // Active bubble will stay visible until it loses focus.
697  if (GetWidget()->IsActive())
698    return;
699  timer_.Start(FROM_HERE,
700               base::TimeDelta::FromSeconds(kBubbleCloseDelay),
701               this,
702               &ManagePasswordsBubbleView::Close);
703}
704