Upstream version 10.39.225.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/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"
33
34
35 // Helpers --------------------------------------------------------------------
36
37 namespace {
38
39 const int kDesiredBubbleWidth = 370;
40
41 enum ColumnSetType {
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,
46
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,
51
52   // | | (LEADING, CENTER) | | (TRAILING, CENTER) | |
53   // Used for buttons at the bottom of the bubble which should occupy
54   // the corners.
55   LINK_BUTTON_COLUMN_SET = 2,
56
57   // | | (TRAILING, CENTER) | |
58   // Used when there is only one button which should next at the bottom-right
59   // corner.
60   SINGLE_BUTTON_COLUMN_SET = 3,
61 };
62
63 // Construct an appropriate ColumnSet for the given |type|, and add it
64 // to |layout|.
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);
69   switch (type) {
70     case SINGLE_VIEW_COLUMN_SET:
71       column_set->AddColumn(views::GridLayout::FILL,
72                             views::GridLayout::FILL,
73                             0,
74                             views::GridLayout::FIXED,
75                             full_width,
76                             0);
77       break;
78
79     case DOUBLE_BUTTON_COLUMN_SET:
80       column_set->AddColumn(views::GridLayout::TRAILING,
81                             views::GridLayout::CENTER,
82                             1,
83                             views::GridLayout::USE_PREF,
84                             0,
85                             0);
86       column_set->AddPaddingColumn(0, views::kRelatedButtonHSpacing);
87       column_set->AddColumn(views::GridLayout::TRAILING,
88                             views::GridLayout::CENTER,
89                             0,
90                             views::GridLayout::USE_PREF,
91                             0,
92                             0);
93       break;
94     case LINK_BUTTON_COLUMN_SET:
95       column_set->AddColumn(views::GridLayout::LEADING,
96                             views::GridLayout::CENTER,
97                             1,
98                             views::GridLayout::USE_PREF,
99                             0,
100                             0);
101       column_set->AddPaddingColumn(0, views::kRelatedButtonHSpacing);
102       column_set->AddColumn(views::GridLayout::TRAILING,
103                             views::GridLayout::CENTER,
104                             0,
105                             views::GridLayout::USE_PREF,
106                             0,
107                             0);
108       break;
109     case SINGLE_BUTTON_COLUMN_SET:
110       column_set->AddColumn(views::GridLayout::TRAILING,
111                             views::GridLayout::CENTER,
112                             1,
113                             views::GridLayout::USE_PREF,
114                             0,
115                             0);
116   }
117   column_set->AddPaddingColumn(0, views::kPanelHorizMargin);
118 }
119
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));
128
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);
134 }
135
136 }  // namespace
137
138
139 // Globals --------------------------------------------------------------------
140
141 namespace chrome {
142
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();
148   }
149   ManagePasswordsUIController* controller =
150       ManagePasswordsUIController::FromWebContents(web_contents);
151   ManagePasswordsBubbleView::ShowBubble(
152       web_contents,
153       password_manager::ui::IsAutomaticDisplayState(controller->state())
154           ? ManagePasswordsBubbleView::AUTOMATIC
155           : ManagePasswordsBubbleView::USER_ACTION);
156 }
157
158 void CloseManagePasswordsBubble(content::WebContents* web_contents) {
159   if (!ManagePasswordsBubbleView::IsShowing())
160     return;
161   content::WebContents* bubble_web_contents =
162       ManagePasswordsBubbleView::manage_password_bubble()->web_contents();
163   if (web_contents == bubble_web_contents)
164     ManagePasswordsBubbleView::CloseBubble();
165 }
166
167 }  // namespace chrome
168
169
170 // ManagePasswordsBubbleView::PendingView -------------------------------------
171
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 {
178  public:
179   explicit PendingView(ManagePasswordsBubbleView* parent);
180   virtual ~PendingView();
181
182  private:
183   // views::ButtonListener:
184   virtual void ButtonPressed(views::Button* sender,
185                              const ui::Event& event) OVERRIDE;
186
187   // Handles the event when the user changes an index of a combobox.
188   virtual void OnPerformAction(views::Combobox* source) OVERRIDE;
189
190   ManagePasswordsBubbleView* parent_;
191
192   views::BlueButton* save_button_;
193
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_;
200 };
201
202 ManagePasswordsBubbleView::PendingView::PendingView(
203     ManagePasswordsBubbleView* parent)
204     : parent_(parent) {
205   views::GridLayout* layout = new views::GridLayout(this);
206   layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
207   SetLayoutManager(layout);
208
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));
218
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.
224
225   // Title row.
226   BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
227   AddTitleRow(layout, parent_->model());
228
229   // Credential row.
230   layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
231   layout->AddView(item);
232
233   // Button row.
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());
239
240   // Extra padding for visual awesomeness.
241   layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
242
243   parent_->set_initially_focused_view(save_button_);
244 }
245
246 ManagePasswordsBubbleView::PendingView::~PendingView() {
247 }
248
249 void ManagePasswordsBubbleView::PendingView::ButtonPressed(
250     views::Button* sender,
251     const ui::Event& event) {
252   DCHECK(sender == save_button_);
253   parent_->model()->OnSaveClicked();
254   parent_->Close();
255 }
256
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();
263       parent_->Close();
264       break;
265     case SavePasswordRefusalComboboxModel::INDEX_NEVER_FOR_THIS_SITE:
266       parent_->NotifyNeverForThisSiteClicked();
267       break;
268   }
269 }
270
271 // ManagePasswordsBubbleView::ConfirmNeverView ---------------------------------
272
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 {
278  public:
279   explicit ConfirmNeverView(ManagePasswordsBubbleView* parent);
280   virtual ~ConfirmNeverView();
281
282  private:
283   // views::ButtonListener:
284   virtual void ButtonPressed(views::Button* sender,
285                              const ui::Event& event) OVERRIDE;
286
287   ManagePasswordsBubbleView* parent_;
288
289   views::LabelButton* confirm_button_;
290   views::LabelButton* undo_button_;
291 };
292
293 ManagePasswordsBubbleView::ConfirmNeverView::ConfirmNeverView(
294     ManagePasswordsBubbleView* parent)
295     : parent_(parent) {
296   views::GridLayout* layout = new views::GridLayout(this);
297   layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
298   SetLayoutManager(layout);
299
300   // Title row.
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);
312
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);
323
324   // Confirm and undo buttons.
325   BuildColumnSet(layout, DOUBLE_BUTTON_COLUMN_SET);
326   layout->StartRowWithPadding(
327       0, DOUBLE_BUTTON_COLUMN_SET, 0, views::kRelatedControlVerticalSpacing);
328
329   confirm_button_ = new views::LabelButton(
330       this,
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_);
338
339   undo_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_);
345
346   // Extra padding for visual awesomeness.
347   layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
348
349   parent_->set_initially_focused_view(confirm_button_);
350 }
351
352 ManagePasswordsBubbleView::ConfirmNeverView::~ConfirmNeverView() {
353 }
354
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();
361   else
362     parent_->NotifyUndoNeverForThisSite();
363 }
364
365 // ManagePasswordsBubbleView::ManageView --------------------------------------
366
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
369 // "Done" button.
370 class ManagePasswordsBubbleView::ManageView : public views::View,
371                                               public views::ButtonListener,
372                                               public views::LinkListener {
373  public:
374   explicit ManageView(ManagePasswordsBubbleView* parent);
375   virtual ~ManageView();
376
377  private:
378   // views::ButtonListener:
379   virtual void ButtonPressed(views::Button* sender,
380                              const ui::Event& event) OVERRIDE;
381
382   // views::LinkListener:
383   virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE;
384
385   ManagePasswordsBubbleView* parent_;
386
387   views::Link* manage_link_;
388   views::LabelButton* done_button_;
389 };
390
391 ManagePasswordsBubbleView::ManageView::ManageView(
392     ManagePasswordsBubbleView* parent)
393     : parent_(parent) {
394   views::GridLayout* layout = new views::GridLayout(this);
395   layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
396   SetLayoutManager(layout);
397
398   // Add the title.
399   BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
400   AddTitleRow(layout, parent_->model());
401
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();
409          ++i) {
410       ManagePasswordItemView* item = new ManagePasswordItemView(
411           parent_->model(),
412           *i->second,
413           i == parent_->model()->best_matches().begin()
414               ? password_manager::ui::FIRST_ITEM
415               : password_manager::ui::SUBSEQUENT_ITEM);
416
417       layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
418       layout->AddView(item);
419     }
420   } else {
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));
428
429     layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
430     layout->AddView(empty_label);
431     layout->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing);
432   }
433
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);
441
442   done_button_ =
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));
447
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_);
453
454   // Extra padding for visual awesomeness.
455   layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
456
457   parent_->set_initially_focused_view(done_button_);
458 }
459
460 ManagePasswordsBubbleView::ManageView::~ManageView() {
461 }
462
463 void ManagePasswordsBubbleView::ManageView::ButtonPressed(
464     views::Button* sender,
465     const ui::Event& event) {
466   DCHECK(sender == done_button_);
467   parent_->model()->OnDoneClicked();
468   parent_->Close();
469 }
470
471 void ManagePasswordsBubbleView::ManageView::LinkClicked(views::Link* source,
472                                                         int event_flags) {
473   DCHECK_EQ(source, manage_link_);
474   parent_->model()->OnManageLinkClicked();
475   parent_->Close();
476 }
477
478 // ManagePasswordsBubbleView::BlacklistedView ---------------------------------
479
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 {
485  public:
486   explicit BlacklistedView(ManagePasswordsBubbleView* parent);
487   virtual ~BlacklistedView();
488
489  private:
490   // views::ButtonListener:
491   virtual void ButtonPressed(views::Button* sender,
492                              const ui::Event& event) OVERRIDE;
493
494   ManagePasswordsBubbleView* parent_;
495
496   views::BlueButton* unblacklist_button_;
497   views::LabelButton* done_button_;
498 };
499
500 ManagePasswordsBubbleView::BlacklistedView::BlacklistedView(
501     ManagePasswordsBubbleView* parent)
502     : parent_(parent) {
503   views::GridLayout* layout = new views::GridLayout(this);
504   layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
505   SetLayoutManager(layout);
506
507   // Add the title.
508   BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
509   AddTitleRow(layout, parent_->model());
510
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);
520
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));
527   done_button_ =
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));
532
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_);
538
539   // Extra padding for visual awesomeness.
540   layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
541
542   parent_->set_initially_focused_view(unblacklist_button_);
543 }
544
545 ManagePasswordsBubbleView::BlacklistedView::~BlacklistedView() {
546 }
547
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();
555   else
556     NOTREACHED();
557   parent_->Close();
558 }
559
560 // ManagePasswordsBubbleView::SaveConfirmationView ----------------------------
561
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 {
568  public:
569   explicit SaveConfirmationView(ManagePasswordsBubbleView* parent);
570   virtual ~SaveConfirmationView();
571
572  private:
573   // views::ButtonListener:
574   virtual void ButtonPressed(views::Button* sender,
575                              const ui::Event& event) OVERRIDE;
576
577   // views::StyledLabelListener implementation
578   virtual void StyledLabelLinkClicked(const gfx::Range& range,
579                                       int event_flags) OVERRIDE;
580
581   ManagePasswordsBubbleView* parent_;
582
583   views::LabelButton* ok_button_;
584 };
585
586 ManagePasswordsBubbleView::SaveConfirmationView::SaveConfirmationView(
587     ManagePasswordsBubbleView* parent)
588     : parent_(parent) {
589   views::GridLayout* layout = new views::GridLayout(this);
590   layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
591   SetLayoutManager(layout);
592
593   BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
594   AddTitleRow(layout, parent_->model());
595
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());
604
605   layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
606   layout->AddView(confirmation);
607
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));
612
613   BuildColumnSet(layout, SINGLE_BUTTON_COLUMN_SET);
614   layout->StartRowWithPadding(
615       0, SINGLE_BUTTON_COLUMN_SET, 0, views::kRelatedControlVerticalSpacing);
616   layout->AddView(ok_button_);
617
618   // Extra padding for visual awesomeness.
619   layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
620
621   parent_->set_initially_focused_view(ok_button_);
622 }
623
624 ManagePasswordsBubbleView::SaveConfirmationView::~SaveConfirmationView() {
625 }
626
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();
631   parent_->Close();
632 }
633
634 void ManagePasswordsBubbleView::SaveConfirmationView::ButtonPressed(
635     views::Button* sender, const ui::Event& event) {
636   DCHECK_EQ(sender, ok_button_);
637   parent_->model()->OnOKClicked();
638   parent_->Close();
639 }
640
641 // ManagePasswordsBubbleView::WebContentMouseHandler --------------------------
642
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 {
647  public:
648   explicit WebContentMouseHandler(ManagePasswordsBubbleView* bubble);
649   virtual ~WebContentMouseHandler();
650
651   virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE;
652   virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE;
653
654  private:
655   aura::Window* GetWebContentsWindow();
656
657   ManagePasswordsBubbleView* bubble_;
658
659   DISALLOW_COPY_AND_ASSIGN(WebContentMouseHandler);
660 };
661
662 ManagePasswordsBubbleView::WebContentMouseHandler::WebContentMouseHandler(
663     ManagePasswordsBubbleView* bubble)
664     : bubble_(bubble) {
665   GetWebContentsWindow()->AddPreTargetHandler(this);
666 }
667
668 ManagePasswordsBubbleView::WebContentMouseHandler::~WebContentMouseHandler() {
669   if (aura::Window* window = GetWebContentsWindow())
670     window->RemovePreTargetHandler(this);
671 }
672
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)
679     bubble_->Close();
680 }
681
682 void ManagePasswordsBubbleView::WebContentMouseHandler::OnMouseEvent(
683     ui::MouseEvent* event) {
684   if (event->type() == ui::ET_MOUSE_PRESSED)
685     bubble_->Close();
686 }
687
688 aura::Window*
689 ManagePasswordsBubbleView::WebContentMouseHandler::GetWebContentsWindow() {
690   content::WebContents* web_contents = bubble_->model()->web_contents();
691   return web_contents ? web_contents->GetNativeView() : NULL;
692 }
693
694 // ManagePasswordsBubbleView --------------------------------------------------
695
696 // static
697 ManagePasswordsBubbleView* ManagePasswordsBubbleView::manage_passwords_bubble_ =
698     NULL;
699
700 // static
701 void ManagePasswordsBubbleView::ShowBubble(content::WebContents* web_contents,
702                                            DisplayReason reason) {
703   Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
704   DCHECK(browser);
705   DCHECK(browser->window());
706   DCHECK(browser->fullscreen_controller());
707
708   if (IsShowing())
709     return;
710
711   BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
712   bool is_fullscreen = browser_view->IsFullscreen();
713   ManagePasswordsIconView* anchor_view =
714       is_fullscreen
715           ? NULL
716           : browser_view->GetLocationBarView()->manage_passwords_icon_view();
717   manage_passwords_bubble_ = new ManagePasswordsBubbleView(
718       web_contents, anchor_view, reason);
719
720   if (is_fullscreen) {
721     manage_passwords_bubble_->set_parent_window(
722         web_contents->GetTopLevelNativeWindow());
723   }
724
725   views::BubbleDelegateView::CreateBubble(manage_passwords_bubble_);
726
727   // Adjust for fullscreen after creation as it relies on the content size.
728   if (is_fullscreen) {
729     manage_passwords_bubble_->AdjustForFullscreen(
730         browser_view->GetBoundsInScreen());
731   }
732   if (reason == AUTOMATIC)
733     manage_passwords_bubble_->GetWidget()->ShowInactive();
734   else
735     manage_passwords_bubble_->GetWidget()->Show();
736 }
737
738 // static
739 void ManagePasswordsBubbleView::CloseBubble() {
740   if (manage_passwords_bubble_)
741     manage_passwords_bubble_->Close();
742 }
743
744 // static
745 void ManagePasswordsBubbleView::ActivateBubble() {
746   if (!IsShowing())
747     return;
748   manage_passwords_bubble_->GetWidget()->Activate();
749 }
750
751 // static
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();
756 }
757
758 content::WebContents* ManagePasswordsBubbleView::web_contents() const {
759   return model()->web_contents();
760 }
761
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));
775   if (anchor_view)
776     anchor_view->SetActive(true);
777   mouse_handler_.reset(new WebContentMouseHandler(this));
778 }
779
780 ManagePasswordsBubbleView::~ManagePasswordsBubbleView() {
781   if (anchor_view_)
782     anchor_view_->SetActive(false);
783 }
784
785 void ManagePasswordsBubbleView::AdjustForFullscreen(
786     const gfx::Rect& screen_bounds) {
787   if (GetAnchorView())
788     return;
789
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));
797 }
798
799 void ManagePasswordsBubbleView::Close() {
800   mouse_handler_.reset();
801   GetWidget()->Close();
802 }
803
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));
810     else
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));
816   } else {
817     AddChildView(new ManageView(this));
818   }
819   GetLayoutManager()->Layout(this);
820 }
821
822 void ManagePasswordsBubbleView::NotifyNeverForThisSiteClicked() {
823   if (model()->best_matches().empty()) {
824     // Skip confirmation if there are no existing passwords for this site.
825     NotifyConfirmedNeverForThisSite();
826   } else {
827     never_save_passwords_ = true;
828     Refresh();
829   }
830 }
831
832 void ManagePasswordsBubbleView::NotifyConfirmedNeverForThisSite() {
833   model()->OnNeverForThisSiteClicked();
834   Close();
835 }
836
837 void ManagePasswordsBubbleView::NotifyUndoNeverForThisSite() {
838   never_save_passwords_ = false;
839   Refresh();
840 }
841
842 void ManagePasswordsBubbleView::Init() {
843   views::FillLayout* layout = new views::FillLayout();
844   SetLayoutManager(layout);
845
846   Refresh();
847 }
848
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;
854 }
855
856 views::View* ManagePasswordsBubbleView::GetInitiallyFocusedView() {
857   return initially_focused_view_;
858 }