Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / views / passwords / manage_passwords_bubble_view.cc
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 "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"
31
32
33 // Helpers --------------------------------------------------------------------
34
35 namespace {
36
37 // The number of seconds the inactive bubble should stay alive.
38 const int kBubbleCloseDelay = 15;
39
40 const int kDesiredBubbleWidth = 370;
41
42 enum 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|.
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);
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.
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));
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
142 namespace chrome {
143
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();
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
164 ManagePasswordsBubbleView::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                                  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));
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
208 ManagePasswordsBubbleView::PendingView::~PendingView() {
209 }
210
211 void 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
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();
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
235 ManagePasswordsBubbleView::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
294 ManagePasswordsBubbleView::ConfirmNeverView::~ConfirmNeverView() {
295 }
296
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();
303   else
304     parent_->NotifyUndoNeverForThisSite();
305 }
306
307 // ManagePasswordsBubbleView::ManageView --------------------------------------
308
309 ManagePasswordsBubbleView::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               ? password_manager::ui::FIRST_ITEM
333               : password_manager::ui::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
378 ManagePasswordsBubbleView::ManageView::~ManageView() {
379 }
380
381 void 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
389 void 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
398 ManagePasswordsBubbleView::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
443 ManagePasswordsBubbleView::BlacklistedView::~BlacklistedView() {
444 }
445
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();
453   else
454     NOTREACHED();
455   parent_->Close();
456 }
457
458 // ManagePasswordsBubbleView::SaveConfirmationView ----------------------------
459
460 ManagePasswordsBubbleView::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
498 ManagePasswordsBubbleView::SaveConfirmationView::~SaveConfirmationView() {
499 }
500
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();
505   parent_->Close();
506 }
507
508 void 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
518 ManagePasswordsBubbleView* ManagePasswordsBubbleView::manage_passwords_bubble_ =
519     NULL;
520
521 // static
522 void 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
561 void ManagePasswordsBubbleView::CloseBubble() {
562   if (manage_passwords_bubble_)
563     manage_passwords_bubble_->Close();
564 }
565
566 // static
567 void ManagePasswordsBubbleView::ActivateBubble() {
568   if (!IsShowing())
569     return;
570   manage_passwords_bubble_->GetWidget()->Activate();
571 }
572
573 // static
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();
578 }
579
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);
594   if (anchor_view)
595     anchor_view->SetActive(true);
596 }
597
598 ManagePasswordsBubbleView::~ManagePasswordsBubbleView() {
599   if (anchor_view_)
600     anchor_view_->SetActive(false);
601 }
602
603 void 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
617 void ManagePasswordsBubbleView::Close() {
618   GetWidget()->Close();
619 }
620
621 void ManagePasswordsBubbleView::Init() {
622   views::FillLayout* layout = new views::FillLayout();
623   SetLayoutManager(layout);
624
625   Refresh();
626 }
627
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;
633 }
634
635 void ManagePasswordsBubbleView::OnWidgetActivationChanged(views::Widget* widget,
636                                                           bool active) {
637   if (active && widget == GetWidget())
638     timer_.Stop();
639   BubbleDelegateView::OnWidgetActivationChanged(widget, active);
640 }
641
642 views::View* ManagePasswordsBubbleView::GetInitiallyFocusedView() {
643   return initially_focused_view_;
644 }
645
646 void ManagePasswordsBubbleView::OnMouseEntered(const ui::MouseEvent& event) {
647   timer_.Stop();
648 }
649
650 void ManagePasswordsBubbleView::OnMouseExited(const ui::MouseEvent& event) {
651   StartTimerIfNecessary();
652 }
653
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));
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
675 void 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
685 void ManagePasswordsBubbleView::NotifyConfirmedNeverForThisSite() {
686   model()->OnNeverForThisSiteClicked();
687   Close();
688 }
689
690 void ManagePasswordsBubbleView::NotifyUndoNeverForThisSite() {
691   never_save_passwords_ = false;
692   Refresh();
693 }
694
695 void 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 }