1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/ui/views/passwords/manage_passwords_bubble_view.h"
7 #include "chrome/browser/ui/browser.h"
8 #include "chrome/browser/ui/browser_finder.h"
9 #include "chrome/browser/ui/passwords/manage_passwords_bubble_model.h"
10 #include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h"
11 #include "chrome/browser/ui/passwords/save_password_refusal_combobox_model.h"
12 #include "chrome/browser/ui/views/frame/browser_view.h"
13 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
14 #include "chrome/browser/ui/views/passwords/manage_password_item_view.h"
15 #include "chrome/browser/ui/views/passwords/manage_passwords_icon_view.h"
16 #include "chrome/grit/generated_resources.h"
17 #include "content/public/browser/render_view_host.h"
18 #include "content/public/browser/web_contents.h"
19 #include "ui/aura/window.h"
20 #include "ui/base/l10n/l10n_util.h"
21 #include "ui/base/resource/resource_bundle.h"
22 #include "ui/compositor/layer_animation_observer.h"
23 #include "ui/compositor/scoped_layer_animation_settings.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/combobox/combobox_listener.h"
28 #include "ui/views/controls/link.h"
29 #include "ui/views/controls/link_listener.h"
30 #include "ui/views/controls/styled_label.h"
31 #include "ui/views/controls/styled_label_listener.h"
32 #include "ui/views/layout/fill_layout.h"
33 #include "ui/views/layout/grid_layout.h"
34 #include "ui/views/layout/layout_constants.h"
37 // Helpers --------------------------------------------------------------------
41 // The number of seconds the bubble needs to fade out.
42 const int kBubbleFadeDelay
= 2;
44 const int kDesiredBubbleWidth
= 370;
47 // | | (FILL, FILL) | |
48 // Used for the bubble's header, the credentials list, and for simple
49 // messages like "No passwords".
50 SINGLE_VIEW_COLUMN_SET
= 0,
52 // | | (TRAILING, CENTER) | | (TRAILING, CENTER) | |
53 // Used for buttons at the bottom of the bubble which should nest at the
54 // bottom-right corner.
55 DOUBLE_BUTTON_COLUMN_SET
= 1,
57 // | | (LEADING, CENTER) | | (TRAILING, CENTER) | |
58 // Used for buttons at the bottom of the bubble which should occupy
60 LINK_BUTTON_COLUMN_SET
= 2,
62 // | | (TRAILING, CENTER) | |
63 // Used when there is only one button which should next at the bottom-right
65 SINGLE_BUTTON_COLUMN_SET
= 3,
68 // Construct an appropriate ColumnSet for the given |type|, and add it
70 void BuildColumnSet(views::GridLayout
* layout
, ColumnSetType type
) {
71 views::ColumnSet
* column_set
= layout
->AddColumnSet(type
);
72 column_set
->AddPaddingColumn(0, views::kPanelHorizMargin
);
73 int full_width
= kDesiredBubbleWidth
- (2 * views::kPanelHorizMargin
);
75 case SINGLE_VIEW_COLUMN_SET
:
76 column_set
->AddColumn(views::GridLayout::FILL
,
77 views::GridLayout::FILL
,
79 views::GridLayout::FIXED
,
84 case DOUBLE_BUTTON_COLUMN_SET
:
85 column_set
->AddColumn(views::GridLayout::TRAILING
,
86 views::GridLayout::CENTER
,
88 views::GridLayout::USE_PREF
,
91 column_set
->AddPaddingColumn(0, views::kRelatedButtonHSpacing
);
92 column_set
->AddColumn(views::GridLayout::TRAILING
,
93 views::GridLayout::CENTER
,
95 views::GridLayout::USE_PREF
,
99 case LINK_BUTTON_COLUMN_SET
:
100 column_set
->AddColumn(views::GridLayout::LEADING
,
101 views::GridLayout::CENTER
,
103 views::GridLayout::USE_PREF
,
106 column_set
->AddPaddingColumn(0, views::kRelatedButtonHSpacing
);
107 column_set
->AddColumn(views::GridLayout::TRAILING
,
108 views::GridLayout::CENTER
,
110 views::GridLayout::USE_PREF
,
114 case SINGLE_BUTTON_COLUMN_SET
:
115 column_set
->AddColumn(views::GridLayout::TRAILING
,
116 views::GridLayout::CENTER
,
118 views::GridLayout::USE_PREF
,
122 column_set
->AddPaddingColumn(0, views::kPanelHorizMargin
);
125 // Given a layout and a model, add an appropriate title using a
126 // SINGLE_VIEW_COLUMN_SET, followed by a spacer row.
127 void AddTitleRow(views::GridLayout
* layout
, ManagePasswordsBubbleModel
* model
) {
128 views::Label
* title_label
= new views::Label(model
->title());
129 title_label
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
130 title_label
->SetMultiLine(true);
131 title_label
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
132 ui::ResourceBundle::MediumFont
));
134 // Add the title to the layout with appropriate padding.
135 layout
->StartRowWithPadding(
136 0, SINGLE_VIEW_COLUMN_SET
, 0, views::kRelatedControlSmallVerticalSpacing
);
137 layout
->AddView(title_label
);
138 layout
->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing
);
144 // Globals --------------------------------------------------------------------
148 void ShowManagePasswordsBubble(content::WebContents
* web_contents
) {
149 if (ManagePasswordsBubbleView::IsShowing()) {
150 // The bubble is currently shown for some other tab. We should close it now
151 // and open for |web_contents|.
152 ManagePasswordsBubbleView::CloseBubble();
154 ManagePasswordsUIController
* controller
=
155 ManagePasswordsUIController::FromWebContents(web_contents
);
156 ManagePasswordsBubbleView::ShowBubble(
158 password_manager::ui::IsAutomaticDisplayState(controller
->state())
159 ? ManagePasswordsBubbleView::AUTOMATIC
160 : ManagePasswordsBubbleView::USER_ACTION
);
163 } // namespace chrome
166 // ManagePasswordsBubbleView::PendingView -------------------------------------
168 // A view offering the user the ability to save credentials. Contains a
169 // single ManagePasswordItemView, along with a "Save Passwords" button
170 // and a rejection combobox.
171 class ManagePasswordsBubbleView::PendingView
: public views::View
,
172 public views::ButtonListener
,
173 public views::ComboboxListener
{
175 explicit PendingView(ManagePasswordsBubbleView
* parent
);
176 virtual ~PendingView();
179 // views::ButtonListener:
180 virtual void ButtonPressed(views::Button
* sender
,
181 const ui::Event
& event
) OVERRIDE
;
183 // Handles the event when the user changes an index of a combobox.
184 virtual void OnPerformAction(views::Combobox
* source
) OVERRIDE
;
186 ManagePasswordsBubbleView
* parent_
;
188 views::BlueButton
* save_button_
;
190 // The combobox doesn't take ownership of its model. If we created a
191 // combobox we need to ensure that we delete the model here, and because the
192 // combobox uses the model in it's destructor, we need to make sure we
193 // delete the model _after_ the combobox itself is deleted.
194 scoped_ptr
<SavePasswordRefusalComboboxModel
> combobox_model_
;
195 scoped_ptr
<views::Combobox
> refuse_combobox_
;
198 ManagePasswordsBubbleView::PendingView::PendingView(
199 ManagePasswordsBubbleView
* parent
)
201 views::GridLayout
* layout
= new views::GridLayout(this);
202 layout
->set_minimum_size(gfx::Size(kDesiredBubbleWidth
, 0));
203 SetLayoutManager(layout
);
205 // Create the pending credential item, save button and refusal combobox.
206 ManagePasswordItemView
* item
=
207 new ManagePasswordItemView(parent
->model(),
208 parent
->model()->pending_credentials(),
209 password_manager::ui::FIRST_ITEM
);
210 save_button_
= new views::BlueButton(
211 this, l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_SAVE_BUTTON
));
212 save_button_
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
213 ui::ResourceBundle::SmallFont
));
215 combobox_model_
.reset(new SavePasswordRefusalComboboxModel());
216 refuse_combobox_
.reset(new views::Combobox(combobox_model_
.get()));
217 refuse_combobox_
->set_listener(this);
218 refuse_combobox_
->SetStyle(views::Combobox::STYLE_ACTION
);
219 // TODO(mkwst): Need a mechanism to pipe a font list down into a combobox.
222 BuildColumnSet(layout
, SINGLE_VIEW_COLUMN_SET
);
223 AddTitleRow(layout
, parent_
->model());
226 layout
->StartRow(0, SINGLE_VIEW_COLUMN_SET
);
227 layout
->AddView(item
);
230 BuildColumnSet(layout
, DOUBLE_BUTTON_COLUMN_SET
);
231 layout
->StartRowWithPadding(
232 0, DOUBLE_BUTTON_COLUMN_SET
, 0, views::kRelatedControlVerticalSpacing
);
233 layout
->AddView(save_button_
);
234 layout
->AddView(refuse_combobox_
.get());
236 // Extra padding for visual awesomeness.
237 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
239 parent_
->set_initially_focused_view(save_button_
);
242 ManagePasswordsBubbleView::PendingView::~PendingView() {
245 void ManagePasswordsBubbleView::PendingView::ButtonPressed(
246 views::Button
* sender
,
247 const ui::Event
& event
) {
248 DCHECK(sender
== save_button_
);
249 parent_
->model()->OnSaveClicked();
253 void ManagePasswordsBubbleView::PendingView::OnPerformAction(
254 views::Combobox
* source
) {
255 DCHECK_EQ(source
, refuse_combobox_
);
256 switch (refuse_combobox_
->selected_index()) {
257 case SavePasswordRefusalComboboxModel::INDEX_NOPE
:
258 parent_
->model()->OnNopeClicked();
261 case SavePasswordRefusalComboboxModel::INDEX_NEVER_FOR_THIS_SITE
:
262 parent_
->NotifyNeverForThisSiteClicked();
267 // ManagePasswordsBubbleView::ConfirmNeverView ---------------------------------
269 // A view offering the user the ability to undo her decision to never save
270 // passwords for a particular site.
271 class ManagePasswordsBubbleView::ConfirmNeverView
272 : public views::View
,
273 public views::ButtonListener
{
275 explicit ConfirmNeverView(ManagePasswordsBubbleView
* parent
);
276 virtual ~ConfirmNeverView();
279 // views::ButtonListener:
280 virtual void ButtonPressed(views::Button
* sender
,
281 const ui::Event
& event
) OVERRIDE
;
283 ManagePasswordsBubbleView
* parent_
;
285 views::LabelButton
* confirm_button_
;
286 views::LabelButton
* undo_button_
;
289 ManagePasswordsBubbleView::ConfirmNeverView::ConfirmNeverView(
290 ManagePasswordsBubbleView
* parent
)
292 views::GridLayout
* layout
= new views::GridLayout(this);
293 layout
->set_minimum_size(gfx::Size(kDesiredBubbleWidth
, 0));
294 SetLayoutManager(layout
);
297 BuildColumnSet(layout
, SINGLE_VIEW_COLUMN_SET
);
298 views::Label
* title_label
= new views::Label(l10n_util::GetStringUTF16(
299 IDS_MANAGE_PASSWORDS_BLACKLIST_CONFIRMATION_TITLE
));
300 title_label
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
301 title_label
->SetMultiLine(true);
302 title_label
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
303 ui::ResourceBundle::MediumFont
));
304 layout
->StartRowWithPadding(
305 0, SINGLE_VIEW_COLUMN_SET
, 0, views::kRelatedControlSmallVerticalSpacing
);
306 layout
->AddView(title_label
);
307 layout
->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing
);
309 // Confirmation text.
310 views::Label
* confirmation
= new views::Label(l10n_util::GetStringUTF16(
311 IDS_MANAGE_PASSWORDS_BLACKLIST_CONFIRMATION_TEXT
));
312 confirmation
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
313 confirmation
->SetMultiLine(true);
314 confirmation
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
315 ui::ResourceBundle::SmallFont
));
316 layout
->StartRow(0, SINGLE_VIEW_COLUMN_SET
);
317 layout
->AddView(confirmation
);
318 layout
->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing
);
320 // Confirm and undo buttons.
321 BuildColumnSet(layout
, DOUBLE_BUTTON_COLUMN_SET
);
322 layout
->StartRowWithPadding(
323 0, DOUBLE_BUTTON_COLUMN_SET
, 0, views::kRelatedControlVerticalSpacing
);
325 confirm_button_
= new views::LabelButton(
327 l10n_util::GetStringUTF16(
328 IDS_MANAGE_PASSWORDS_BLACKLIST_CONFIRMATION_BUTTON
));
329 confirm_button_
->SetStyle(views::Button::STYLE_BUTTON
);
330 confirm_button_
->SetFontList(
331 ui::ResourceBundle::GetSharedInstance().GetFontList(
332 ui::ResourceBundle::SmallFont
));
333 layout
->AddView(confirm_button_
);
336 new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_CANCEL
));
337 undo_button_
->SetStyle(views::Button::STYLE_BUTTON
);
338 undo_button_
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
339 ui::ResourceBundle::SmallFont
));
340 layout
->AddView(undo_button_
);
342 // Extra padding for visual awesomeness.
343 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
345 parent_
->set_initially_focused_view(confirm_button_
);
348 ManagePasswordsBubbleView::ConfirmNeverView::~ConfirmNeverView() {
351 void ManagePasswordsBubbleView::ConfirmNeverView::ButtonPressed(
352 views::Button
* sender
,
353 const ui::Event
& event
) {
354 DCHECK(sender
== confirm_button_
|| sender
== undo_button_
);
355 if (sender
== confirm_button_
)
356 parent_
->NotifyConfirmedNeverForThisSite();
358 parent_
->NotifyUndoNeverForThisSite();
361 // ManagePasswordsBubbleView::ManageView --------------------------------------
363 // A view offering the user a list of her currently saved credentials
364 // for the current page, along with a "Manage passwords" link and a
366 class ManagePasswordsBubbleView::ManageView
: public views::View
,
367 public views::ButtonListener
,
368 public views::LinkListener
{
370 explicit ManageView(ManagePasswordsBubbleView
* parent
);
371 virtual ~ManageView();
374 // views::ButtonListener:
375 virtual void ButtonPressed(views::Button
* sender
,
376 const ui::Event
& event
) OVERRIDE
;
378 // views::LinkListener:
379 virtual void LinkClicked(views::Link
* source
, int event_flags
) OVERRIDE
;
381 ManagePasswordsBubbleView
* parent_
;
383 views::Link
* manage_link_
;
384 views::LabelButton
* done_button_
;
387 ManagePasswordsBubbleView::ManageView::ManageView(
388 ManagePasswordsBubbleView
* parent
)
390 views::GridLayout
* layout
= new views::GridLayout(this);
391 layout
->set_minimum_size(gfx::Size(kDesiredBubbleWidth
, 0));
392 SetLayoutManager(layout
);
395 BuildColumnSet(layout
, SINGLE_VIEW_COLUMN_SET
);
396 AddTitleRow(layout
, parent_
->model());
398 // If we have a list of passwords to store for the current site, display
399 // them to the user for management. Otherwise, render a "No passwords for
400 // this site" message.
401 if (!parent_
->model()->best_matches().empty()) {
402 for (autofill::ConstPasswordFormMap::const_iterator
i(
403 parent_
->model()->best_matches().begin());
404 i
!= parent_
->model()->best_matches().end();
406 ManagePasswordItemView
* item
= new ManagePasswordItemView(
409 i
== parent_
->model()->best_matches().begin()
410 ? password_manager::ui::FIRST_ITEM
411 : password_manager::ui::SUBSEQUENT_ITEM
);
413 layout
->StartRow(0, SINGLE_VIEW_COLUMN_SET
);
414 layout
->AddView(item
);
417 views::Label
* empty_label
= new views::Label(
418 l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_NO_PASSWORDS
));
419 empty_label
->SetMultiLine(true);
420 empty_label
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
421 empty_label
->SetFontList(
422 ui::ResourceBundle::GetSharedInstance().GetFontList(
423 ui::ResourceBundle::SmallFont
));
425 layout
->StartRow(0, SINGLE_VIEW_COLUMN_SET
);
426 layout
->AddView(empty_label
);
427 layout
->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing
);
430 // Then add the "manage passwords" link and "Done" button.
431 manage_link_
= new views::Link(parent_
->model()->manage_link());
432 manage_link_
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
433 manage_link_
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
434 ui::ResourceBundle::SmallFont
));
435 manage_link_
->SetUnderline(false);
436 manage_link_
->set_listener(this);
439 new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_DONE
));
440 done_button_
->SetStyle(views::Button::STYLE_BUTTON
);
441 done_button_
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
442 ui::ResourceBundle::SmallFont
));
444 BuildColumnSet(layout
, LINK_BUTTON_COLUMN_SET
);
445 layout
->StartRowWithPadding(
446 0, LINK_BUTTON_COLUMN_SET
, 0, views::kRelatedControlVerticalSpacing
);
447 layout
->AddView(manage_link_
);
448 layout
->AddView(done_button_
);
450 // Extra padding for visual awesomeness.
451 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
453 parent_
->set_initially_focused_view(done_button_
);
456 ManagePasswordsBubbleView::ManageView::~ManageView() {
459 void ManagePasswordsBubbleView::ManageView::ButtonPressed(
460 views::Button
* sender
,
461 const ui::Event
& event
) {
462 DCHECK(sender
== done_button_
);
463 parent_
->model()->OnDoneClicked();
467 void ManagePasswordsBubbleView::ManageView::LinkClicked(views::Link
* source
,
469 DCHECK_EQ(source
, manage_link_
);
470 parent_
->model()->OnManageLinkClicked();
474 // ManagePasswordsBubbleView::BlacklistedView ---------------------------------
476 // A view offering the user the ability to re-enable the password manager for
477 // a specific site after she's decided to "never save passwords".
478 class ManagePasswordsBubbleView::BlacklistedView
479 : public views::View
,
480 public views::ButtonListener
{
482 explicit BlacklistedView(ManagePasswordsBubbleView
* parent
);
483 virtual ~BlacklistedView();
486 // views::ButtonListener:
487 virtual void ButtonPressed(views::Button
* sender
,
488 const ui::Event
& event
) OVERRIDE
;
490 ManagePasswordsBubbleView
* parent_
;
492 views::BlueButton
* unblacklist_button_
;
493 views::LabelButton
* done_button_
;
496 ManagePasswordsBubbleView::BlacklistedView::BlacklistedView(
497 ManagePasswordsBubbleView
* parent
)
499 views::GridLayout
* layout
= new views::GridLayout(this);
500 layout
->set_minimum_size(gfx::Size(kDesiredBubbleWidth
, 0));
501 SetLayoutManager(layout
);
504 BuildColumnSet(layout
, SINGLE_VIEW_COLUMN_SET
);
505 AddTitleRow(layout
, parent_
->model());
507 // Add the "Hey! You blacklisted this site!" text.
508 views::Label
* blacklisted
= new views::Label(
509 l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_BLACKLISTED
));
510 blacklisted
->SetMultiLine(true);
511 blacklisted
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
512 ui::ResourceBundle::SmallFont
));
513 layout
->StartRow(0, SINGLE_VIEW_COLUMN_SET
);
514 layout
->AddView(blacklisted
);
515 layout
->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing
);
517 // Then add the "enable password manager" and "Done" buttons.
518 unblacklist_button_
= new views::BlueButton(
519 this, l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_UNBLACKLIST_BUTTON
));
520 unblacklist_button_
->SetFontList(
521 ui::ResourceBundle::GetSharedInstance().GetFontList(
522 ui::ResourceBundle::SmallFont
));
524 new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_DONE
));
525 done_button_
->SetStyle(views::Button::STYLE_BUTTON
);
526 done_button_
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
527 ui::ResourceBundle::SmallFont
));
529 BuildColumnSet(layout
, DOUBLE_BUTTON_COLUMN_SET
);
530 layout
->StartRowWithPadding(
531 0, DOUBLE_BUTTON_COLUMN_SET
, 0, views::kRelatedControlVerticalSpacing
);
532 layout
->AddView(unblacklist_button_
);
533 layout
->AddView(done_button_
);
535 // Extra padding for visual awesomeness.
536 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
538 parent_
->set_initially_focused_view(unblacklist_button_
);
541 ManagePasswordsBubbleView::BlacklistedView::~BlacklistedView() {
544 void ManagePasswordsBubbleView::BlacklistedView::ButtonPressed(
545 views::Button
* sender
,
546 const ui::Event
& event
) {
547 if (sender
== done_button_
)
548 parent_
->model()->OnDoneClicked();
549 else if (sender
== unblacklist_button_
)
550 parent_
->model()->OnUnblacklistClicked();
556 // ManagePasswordsBubbleView::SaveConfirmationView ----------------------------
558 // A view confirming to the user that a password was saved and offering a link
559 // to the Google account manager.
560 class ManagePasswordsBubbleView::SaveConfirmationView
561 : public views::View
,
562 public views::ButtonListener
,
563 public views::StyledLabelListener
{
565 explicit SaveConfirmationView(ManagePasswordsBubbleView
* parent
);
566 virtual ~SaveConfirmationView();
569 // views::ButtonListener:
570 virtual void ButtonPressed(views::Button
* sender
,
571 const ui::Event
& event
) OVERRIDE
;
573 // views::StyledLabelListener implementation
574 virtual void StyledLabelLinkClicked(const gfx::Range
& range
,
575 int event_flags
) OVERRIDE
;
577 ManagePasswordsBubbleView
* parent_
;
579 views::LabelButton
* ok_button_
;
582 ManagePasswordsBubbleView::SaveConfirmationView::SaveConfirmationView(
583 ManagePasswordsBubbleView
* parent
)
585 views::GridLayout
* layout
= new views::GridLayout(this);
586 layout
->set_minimum_size(gfx::Size(kDesiredBubbleWidth
, 0));
587 SetLayoutManager(layout
);
589 BuildColumnSet(layout
, SINGLE_VIEW_COLUMN_SET
);
590 AddTitleRow(layout
, parent_
->model());
592 views::StyledLabel
* confirmation
=
593 new views::StyledLabel(parent_
->model()->save_confirmation_text(), this);
594 confirmation
->SetBaseFontList(
595 ui::ResourceBundle::GetSharedInstance().GetFontList(
596 ui::ResourceBundle::SmallFont
));
597 confirmation
->AddStyleRange(
598 parent_
->model()->save_confirmation_link_range(),
599 views::StyledLabel::RangeStyleInfo::CreateForLink());
601 layout
->StartRow(0, SINGLE_VIEW_COLUMN_SET
);
602 layout
->AddView(confirmation
);
604 ok_button_
= new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_OK
));
605 ok_button_
->SetStyle(views::Button::STYLE_BUTTON
);
606 ok_button_
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
607 ui::ResourceBundle::SmallFont
));
609 BuildColumnSet(layout
, SINGLE_BUTTON_COLUMN_SET
);
610 layout
->StartRowWithPadding(
611 0, SINGLE_BUTTON_COLUMN_SET
, 0, views::kRelatedControlVerticalSpacing
);
612 layout
->AddView(ok_button_
);
614 // Extra padding for visual awesomeness.
615 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
617 parent_
->set_initially_focused_view(ok_button_
);
620 ManagePasswordsBubbleView::SaveConfirmationView::~SaveConfirmationView() {
623 void ManagePasswordsBubbleView::SaveConfirmationView::StyledLabelLinkClicked(
624 const gfx::Range
& range
, int event_flags
) {
625 DCHECK_EQ(range
, parent_
->model()->save_confirmation_link_range());
626 parent_
->model()->OnRemoteManageLinkClicked();
630 void ManagePasswordsBubbleView::SaveConfirmationView::ButtonPressed(
631 views::Button
* sender
, const ui::Event
& event
) {
632 DCHECK_EQ(sender
, ok_button_
);
633 parent_
->model()->OnOKClicked();
637 // ManagePasswordsBubbleView::WebContentMouseHandler --------------------------
639 // The class listens for WebContentsView events and notifies the bubble if the
640 // view was clicked on or received keystrokes.
641 class ManagePasswordsBubbleView::WebContentMouseHandler
642 : public ui::EventHandler
{
644 explicit WebContentMouseHandler(ManagePasswordsBubbleView
* bubble
);
645 virtual ~WebContentMouseHandler();
647 virtual void OnKeyEvent(ui::KeyEvent
* event
) OVERRIDE
;
648 virtual void OnMouseEvent(ui::MouseEvent
* event
) OVERRIDE
;
651 aura::Window
* GetWebContentsWindow();
653 ManagePasswordsBubbleView
* bubble_
;
655 DISALLOW_COPY_AND_ASSIGN(WebContentMouseHandler
);
658 ManagePasswordsBubbleView::WebContentMouseHandler::WebContentMouseHandler(
659 ManagePasswordsBubbleView
* bubble
)
661 GetWebContentsWindow()->AddPreTargetHandler(this);
664 ManagePasswordsBubbleView::WebContentMouseHandler::~WebContentMouseHandler() {
665 if (aura::Window
* window
= GetWebContentsWindow())
666 window
->RemovePreTargetHandler(this);
669 void ManagePasswordsBubbleView::WebContentMouseHandler::OnKeyEvent(
670 ui::KeyEvent
* event
) {
671 content::WebContents
* web_contents
= bubble_
->model()->web_contents();
672 content::RenderViewHost
* rvh
= web_contents
->GetRenderViewHost();
673 if (rvh
->IsFocusedElementEditable() &&
674 event
->type() == ui::ET_KEY_PRESSED
)
675 bubble_
->StartFadingOut();
678 void ManagePasswordsBubbleView::WebContentMouseHandler::OnMouseEvent(
679 ui::MouseEvent
* event
) {
680 if (event
->type() == ui::ET_MOUSE_PRESSED
)
681 bubble_
->StartFadingOut();
685 ManagePasswordsBubbleView::WebContentMouseHandler::GetWebContentsWindow() {
686 content::WebContents
* web_contents
= bubble_
->model()->web_contents();
687 return web_contents
? web_contents
->GetNativeView() : NULL
;
690 // ManagePasswordsBubbleView::FadeOutObserver ---------------------------------
692 // The class notifies the bubble when it faded out completely.
693 class ManagePasswordsBubbleView::FadeOutObserver
694 : public ui::ImplicitAnimationObserver
{
696 explicit FadeOutObserver(ManagePasswordsBubbleView
* bubble
)
700 virtual void OnImplicitAnimationsCompleted() OVERRIDE
{
701 bubble_
->OnBubbleDisappeared();
705 ManagePasswordsBubbleView
* bubble_
;
707 DISALLOW_COPY_AND_ASSIGN(FadeOutObserver
);
710 // ManagePasswordsBubbleView --------------------------------------------------
713 ManagePasswordsBubbleView
* ManagePasswordsBubbleView::manage_passwords_bubble_
=
717 void ManagePasswordsBubbleView::ShowBubble(content::WebContents
* web_contents
,
718 DisplayReason reason
) {
719 Browser
* browser
= chrome::FindBrowserWithWebContents(web_contents
);
721 DCHECK(browser
->window());
722 DCHECK(browser
->fullscreen_controller());
727 BrowserView
* browser_view
= BrowserView::GetBrowserViewForBrowser(browser
);
728 bool is_fullscreen
= browser_view
->IsFullscreen();
729 ManagePasswordsIconView
* anchor_view
=
732 : browser_view
->GetLocationBarView()->manage_passwords_icon_view();
733 manage_passwords_bubble_
= new ManagePasswordsBubbleView(
734 web_contents
, anchor_view
, reason
);
737 manage_passwords_bubble_
->set_parent_window(
738 web_contents
->GetTopLevelNativeWindow());
741 views::BubbleDelegateView::CreateBubble(manage_passwords_bubble_
);
743 // Adjust for fullscreen after creation as it relies on the content size.
745 manage_passwords_bubble_
->AdjustForFullscreen(
746 browser_view
->GetBoundsInScreen());
748 if (reason
== AUTOMATIC
)
749 manage_passwords_bubble_
->GetWidget()->ShowInactive();
751 manage_passwords_bubble_
->GetWidget()->Show();
755 void ManagePasswordsBubbleView::CloseBubble() {
756 if (manage_passwords_bubble_
)
757 manage_passwords_bubble_
->Close();
761 void ManagePasswordsBubbleView::ActivateBubble() {
764 manage_passwords_bubble_
->GetWidget()->Activate();
768 bool ManagePasswordsBubbleView::IsShowing() {
769 // The bubble may be in the process of closing.
770 return (manage_passwords_bubble_
!= NULL
) &&
771 manage_passwords_bubble_
->GetWidget()->IsVisible();
774 ManagePasswordsBubbleView::ManagePasswordsBubbleView(
775 content::WebContents
* web_contents
,
776 ManagePasswordsIconView
* anchor_view
,
777 DisplayReason reason
)
778 : ManagePasswordsBubble(web_contents
, reason
),
779 BubbleDelegateView(anchor_view
,
780 anchor_view
? views::BubbleBorder::TOP_RIGHT
781 : views::BubbleBorder::NONE
),
782 anchor_view_(anchor_view
),
783 never_save_passwords_(false),
784 initially_focused_view_(NULL
) {
785 // Compensate for built-in vertical padding in the anchor view's image.
786 set_anchor_view_insets(gfx::Insets(5, 0, 5, 0));
787 set_notify_enter_exit_on_child(true);
789 anchor_view
->SetActive(true);
790 mouse_handler_
.reset(new WebContentMouseHandler(this));
793 ManagePasswordsBubbleView::~ManagePasswordsBubbleView() {
795 anchor_view_
->SetActive(false);
798 void ManagePasswordsBubbleView::AdjustForFullscreen(
799 const gfx::Rect
& screen_bounds
) {
803 // The bubble's padding from the screen edge, used in fullscreen.
804 const int kFullscreenPaddingEnd
= 20;
805 const size_t bubble_half_width
= width() / 2;
806 const int x_pos
= base::i18n::IsRTL() ?
807 screen_bounds
.x() + bubble_half_width
+ kFullscreenPaddingEnd
:
808 screen_bounds
.right() - bubble_half_width
- kFullscreenPaddingEnd
;
809 SetAnchorRect(gfx::Rect(x_pos
, screen_bounds
.y(), 0, 0));
812 void ManagePasswordsBubbleView::Close() {
813 fadeout_observer_
.reset();
814 mouse_handler_
.reset();
815 GetWidget()->Close();
818 void ManagePasswordsBubbleView::Refresh() {
819 RemoveAllChildViews(true);
820 initially_focused_view_
= NULL
;
821 if (password_manager::ui::IsPendingState(model()->state())) {
822 if (never_save_passwords_
)
823 AddChildView(new ConfirmNeverView(this));
825 AddChildView(new PendingView(this));
826 } else if (model()->state() == password_manager::ui::BLACKLIST_STATE
) {
827 AddChildView(new BlacklistedView(this));
828 } else if (model()->state() == password_manager::ui::CONFIRMATION_STATE
) {
829 AddChildView(new SaveConfirmationView(this));
831 AddChildView(new ManageView(this));
833 GetLayoutManager()->Layout(this);
837 void ManagePasswordsBubbleView::NotifyNeverForThisSiteClicked() {
838 if (model()->best_matches().empty()) {
839 // Skip confirmation if there are no existing passwords for this site.
840 NotifyConfirmedNeverForThisSite();
842 never_save_passwords_
= true;
847 void ManagePasswordsBubbleView::NotifyConfirmedNeverForThisSite() {
848 model()->OnNeverForThisSiteClicked();
852 void ManagePasswordsBubbleView::NotifyUndoNeverForThisSite() {
853 never_save_passwords_
= false;
857 void ManagePasswordsBubbleView::Init() {
858 views::FillLayout
* layout
= new views::FillLayout();
859 SetLayoutManager(layout
);
864 void ManagePasswordsBubbleView::WindowClosing() {
865 // Close() closes the window asynchronously, so by the time we reach here,
866 // |manage_passwords_bubble_| may have already been reset.
867 if (manage_passwords_bubble_
== this)
868 manage_passwords_bubble_
= NULL
;
871 void ManagePasswordsBubbleView::OnWidgetActivationChanged(views::Widget
* widget
,
873 if (active
&& widget
== GetWidget())
875 BubbleDelegateView::OnWidgetActivationChanged(widget
, active
);
878 views::View
* ManagePasswordsBubbleView::GetInitiallyFocusedView() {
879 return initially_focused_view_
;
882 void ManagePasswordsBubbleView::OnMouseEntered(const ui::MouseEvent
& event
) {
886 void ManagePasswordsBubbleView::StartFadingOut() {
887 if (fadeout_observer_
)
889 aura::Window
* window
= GetWidget()->GetNativeView();
890 ui::ScopedLayerAnimationSettings
animator(window
->layer()->GetAnimator());
891 fadeout_observer_
.reset(new FadeOutObserver(this));
892 animator
.AddObserver(fadeout_observer_
.get());
893 animator
.SetTransitionDuration(
894 base::TimeDelta::FromSeconds(kBubbleFadeDelay
));
895 window
->layer()->SetOpacity(0);
898 void ManagePasswordsBubbleView::CancelFadingOut() {
899 if (!fadeout_observer_
)
901 fadeout_observer_
.reset();
902 aura::Window
* window
= GetWidget()->GetNativeView();
903 window
->layer()->SetOpacity(1);
906 void ManagePasswordsBubbleView::OnBubbleDisappeared() {