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 "base/timer/timer.h"
8 #include "chrome/browser/profiles/profile.h"
9 #include "chrome/browser/ui/browser.h"
10 #include "chrome/browser/ui/browser_finder.h"
11 #include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
12 #include "chrome/browser/ui/passwords/manage_passwords_bubble_model.h"
13 #include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h"
14 #include "chrome/browser/ui/views/frame/browser_view.h"
15 #include "chrome/browser/ui/views/passwords/credentials_item_view.h"
16 #include "chrome/browser/ui/views/passwords/credentials_selection_view.h"
17 #include "chrome/browser/ui/views/passwords/manage_password_items_view.h"
18 #include "chrome/browser/ui/views/passwords/manage_passwords_icon_view.h"
19 #include "chrome/grit/generated_resources.h"
20 #include "content/public/browser/render_view_host.h"
21 #include "ui/base/l10n/l10n_util.h"
22 #include "ui/base/resource/resource_bundle.h"
23 #include "ui/views/controls/button/blue_button.h"
24 #include "ui/views/controls/button/label_button.h"
25 #include "ui/views/controls/link.h"
26 #include "ui/views/controls/link_listener.h"
27 #include "ui/views/controls/separator.h"
28 #include "ui/views/controls/styled_label.h"
29 #include "ui/views/controls/styled_label_listener.h"
30 #include "ui/views/event_monitor.h"
31 #include "ui/views/layout/fill_layout.h"
32 #include "ui/views/layout/grid_layout.h"
33 #include "ui/views/layout/layout_constants.h"
34 #include "ui/views/widget/widget.h"
36 int ManagePasswordsBubbleView::auto_signin_toast_timeout_
= 3;
38 // Helpers --------------------------------------------------------------------
42 const int kDesiredBubbleWidth
= 370;
45 // | | (FILL, FILL) | |
46 // Used for the bubble's header, the credentials list, and for simple
47 // messages like "No passwords".
48 SINGLE_VIEW_COLUMN_SET
,
50 // | | (TRAILING, CENTER) | | (TRAILING, CENTER) | |
51 // Used for buttons at the bottom of the bubble which should nest at the
52 // bottom-right corner.
53 DOUBLE_BUTTON_COLUMN_SET
,
55 // | | (LEADING, CENTER) | | (TRAILING, CENTER) | |
56 // Used for buttons at the bottom of the bubble which should occupy
58 LINK_BUTTON_COLUMN_SET
,
60 // | | (TRAILING, CENTER) | |
61 // Used when there is only one button which should next at the bottom-right
63 SINGLE_BUTTON_COLUMN_SET
,
65 // | | (LEADING, CENTER) | | (TRAILING, CENTER) | | (TRAILING, CENTER) | |
66 // Used when there are three buttons.
67 TRIPLE_BUTTON_COLUMN_SET
,
70 enum TextRowType
{ ROW_SINGLE
, ROW_MULTILINE
};
72 // Construct an appropriate ColumnSet for the given |type|, and add it
74 void BuildColumnSet(views::GridLayout
* layout
, ColumnSetType type
) {
75 views::ColumnSet
* column_set
= layout
->AddColumnSet(type
);
76 column_set
->AddPaddingColumn(0, views::kPanelHorizMargin
);
77 int full_width
= kDesiredBubbleWidth
- (2 * views::kPanelHorizMargin
);
79 case SINGLE_VIEW_COLUMN_SET
:
80 column_set
->AddColumn(views::GridLayout::FILL
,
81 views::GridLayout::FILL
,
83 views::GridLayout::FIXED
,
87 case DOUBLE_BUTTON_COLUMN_SET
:
88 column_set
->AddColumn(views::GridLayout::TRAILING
,
89 views::GridLayout::CENTER
,
91 views::GridLayout::USE_PREF
,
94 column_set
->AddPaddingColumn(0, views::kRelatedButtonHSpacing
);
95 column_set
->AddColumn(views::GridLayout::TRAILING
,
96 views::GridLayout::CENTER
,
98 views::GridLayout::USE_PREF
,
102 case LINK_BUTTON_COLUMN_SET
:
103 column_set
->AddColumn(views::GridLayout::LEADING
,
104 views::GridLayout::CENTER
,
106 views::GridLayout::USE_PREF
,
109 column_set
->AddPaddingColumn(0, views::kRelatedButtonHSpacing
);
110 column_set
->AddColumn(views::GridLayout::TRAILING
,
111 views::GridLayout::CENTER
,
113 views::GridLayout::USE_PREF
,
117 case SINGLE_BUTTON_COLUMN_SET
:
118 column_set
->AddColumn(views::GridLayout::TRAILING
,
119 views::GridLayout::CENTER
,
121 views::GridLayout::USE_PREF
,
125 case TRIPLE_BUTTON_COLUMN_SET
:
126 column_set
->AddColumn(views::GridLayout::LEADING
,
127 views::GridLayout::CENTER
,
129 views::GridLayout::USE_PREF
,
132 column_set
->AddPaddingColumn(0, views::kRelatedButtonHSpacing
);
133 column_set
->AddColumn(views::GridLayout::TRAILING
,
134 views::GridLayout::CENTER
,
136 views::GridLayout::USE_PREF
,
139 column_set
->AddPaddingColumn(0, views::kRelatedButtonHSpacing
);
140 column_set
->AddColumn(views::GridLayout::TRAILING
,
141 views::GridLayout::CENTER
,
143 views::GridLayout::USE_PREF
,
148 column_set
->AddPaddingColumn(0, views::kPanelHorizMargin
);
151 // Given a layout and a model, add an appropriate title using a
152 // SINGLE_VIEW_COLUMN_SET, followed by a spacer row.
153 void AddTitleRow(views::GridLayout
* layout
, ManagePasswordsBubbleModel
* model
) {
154 views::Label
* title_label
= new views::Label(model
->title());
155 title_label
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
156 title_label
->SetMultiLine(true);
157 title_label
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
158 ui::ResourceBundle::MediumFont
));
160 // Add the title to the layout with appropriate padding.
161 layout
->StartRowWithPadding(
162 0, SINGLE_VIEW_COLUMN_SET
, 0, views::kRelatedControlSmallVerticalSpacing
);
163 layout
->AddView(title_label
);
164 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
170 // ManagePasswordsBubbleView::AccountChooserView ------------------------------
172 // A view offering the user the ability to choose credentials for
173 // authentication. Contains a list of CredentialsItemView, along with a
175 class ManagePasswordsBubbleView::AccountChooserView
176 : public views::View
,
177 public views::ButtonListener
{
179 explicit AccountChooserView(ManagePasswordsBubbleView
* parent
);
180 ~AccountChooserView() override
;
183 // views::ButtonListener:
184 void ButtonPressed(views::Button
* sender
, const ui::Event
& event
) override
;
186 // Adds |password_forms| to the layout remembering their |type|.
187 void AddCredentialItemsWithType(
188 views::GridLayout
* layout
,
189 const ScopedVector
<const autofill::PasswordForm
>& password_forms
,
190 password_manager::CredentialType type
);
192 ManagePasswordsBubbleView
* parent_
;
193 views::LabelButton
* cancel_button_
;
195 DISALLOW_COPY_AND_ASSIGN(AccountChooserView
);
198 ManagePasswordsBubbleView::AccountChooserView::AccountChooserView(
199 ManagePasswordsBubbleView
* parent
)
201 views::GridLayout
* layout
= new views::GridLayout(this);
202 SetLayoutManager(layout
);
205 new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_CANCEL
));
206 cancel_button_
->SetStyle(views::Button::STYLE_BUTTON
);
207 cancel_button_
->SetFontList(
208 ui::ResourceBundle::GetSharedInstance().GetFontList(
209 ui::ResourceBundle::SmallFont
));
212 BuildColumnSet(layout
, SINGLE_VIEW_COLUMN_SET
);
213 AddTitleRow(layout
, parent_
->model());
215 AddCredentialItemsWithType(
216 layout
, parent_
->model()->local_credentials(),
217 password_manager::CredentialType::CREDENTIAL_TYPE_PASSWORD
);
219 AddCredentialItemsWithType(
220 layout
, parent_
->model()->federated_credentials(),
221 password_manager::CredentialType::CREDENTIAL_TYPE_FEDERATED
);
224 BuildColumnSet(layout
, SINGLE_BUTTON_COLUMN_SET
);
225 layout
->StartRowWithPadding(
226 0, SINGLE_BUTTON_COLUMN_SET
, 0, views::kRelatedControlVerticalSpacing
);
227 layout
->AddView(cancel_button_
);
229 // Extra padding for visual awesomeness.
230 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
232 parent_
->set_initially_focused_view(cancel_button_
);
235 ManagePasswordsBubbleView::AccountChooserView::~AccountChooserView() {
238 void ManagePasswordsBubbleView::AccountChooserView::AddCredentialItemsWithType(
239 views::GridLayout
* layout
,
240 const ScopedVector
<const autofill::PasswordForm
>& password_forms
,
241 password_manager::CredentialType type
) {
242 net::URLRequestContextGetter
* request_context
=
243 parent_
->model()->GetProfile()->GetRequestContext();
244 for (const autofill::PasswordForm
* form
: password_forms
) {
245 const base::string16
& upper_string
=
246 form
->display_name
.empty() ? form
->username_value
: form
->display_name
;
247 base::string16 lower_string
;
248 if (form
->federation_url
.is_empty()) {
249 if (!form
->display_name
.empty())
250 lower_string
= form
->username_value
;
252 lower_string
= l10n_util::GetStringFUTF16(
253 IDS_PASSWORDS_VIA_FEDERATION
,
254 base::UTF8ToUTF16(form
->federation_url
.host()));
256 layout
->StartRow(0, SINGLE_VIEW_COLUMN_SET
);
257 layout
->AddView(new CredentialsItemView(this, form
, type
, upper_string
,
258 lower_string
, request_context
));
262 void ManagePasswordsBubbleView::AccountChooserView::ButtonPressed(
263 views::Button
* sender
, const ui::Event
& event
) {
264 if (sender
!= cancel_button_
) {
265 // ManagePasswordsBubbleModel should care about calling a callback in case
266 // the bubble is dismissed by any other means.
267 CredentialsItemView
* view
= static_cast<CredentialsItemView
*>(sender
);
268 parent_
->model()->OnChooseCredentials(*view
->form(),
269 view
->credential_type());
271 parent_
->model()->OnCancelClicked();
276 // ManagePasswordsBubbleView::AutoSigninView ----------------------------------
278 // A view containing just one credential that was used for for automatic signing
280 class ManagePasswordsBubbleView::AutoSigninView
281 : public views::View
,
282 public views::ButtonListener
,
283 public views::WidgetObserver
{
285 explicit AutoSigninView(ManagePasswordsBubbleView
* parent
);
288 // views::ButtonListener:
289 void ButtonPressed(views::Button
* sender
, const ui::Event
& event
) override
;
291 // views::WidgetObserver:
292 // Tracks the state of the browser window.
293 void OnWidgetActivationChanged(views::Widget
* widget
, bool active
) override
;
294 void OnWidgetClosing(views::Widget
* widget
) override
;
297 static base::TimeDelta
GetTimeout() {
298 return base::TimeDelta::FromSeconds(
299 ManagePasswordsBubbleView::auto_signin_toast_timeout_
);
302 base::OneShotTimer
<AutoSigninView
> timer_
;
303 ManagePasswordsBubbleView
* parent_
;
304 ScopedObserver
<views::Widget
, views::WidgetObserver
> observed_browser_
;
306 DISALLOW_COPY_AND_ASSIGN(AutoSigninView
);
309 ManagePasswordsBubbleView::AutoSigninView::AutoSigninView(
310 ManagePasswordsBubbleView
* parent
)
312 observed_browser_(this) {
313 SetLayoutManager(new views::FillLayout
);
314 const autofill::PasswordForm
& form
= parent_
->model()->pending_password();
315 CredentialsItemView
* credential
= new CredentialsItemView(
317 password_manager::CredentialType::CREDENTIAL_TYPE_PASSWORD
,
319 l10n_util::GetStringFUTF16(IDS_MANAGE_PASSWORDS_AUTO_SIGNIN_TITLE
,
320 form
.username_value
),
321 parent_
->model()->GetProfile()->GetRequestContext());
322 AddChildView(credential
);
323 credential
->SetEnabled(false);
326 chrome::FindBrowserWithWebContents(parent_
->web_contents());
328 BrowserView
* browser_view
= BrowserView::GetBrowserViewForBrowser(browser
);
329 observed_browser_
.Add(browser_view
->GetWidget());
331 if (browser_view
->IsActive())
332 timer_
.Start(FROM_HERE
, GetTimeout(), this, &AutoSigninView::OnTimer
);
335 void ManagePasswordsBubbleView::AutoSigninView::ButtonPressed(
336 views::Button
* sender
, const ui::Event
& event
) {
337 parent_
->model()->OnAutoSignInClicked();
341 void ManagePasswordsBubbleView::AutoSigninView::OnWidgetActivationChanged(
342 views::Widget
* widget
, bool active
) {
343 if (active
&& !timer_
.IsRunning())
344 timer_
.Start(FROM_HERE
, GetTimeout(), this, &AutoSigninView::OnTimer
);
347 void ManagePasswordsBubbleView::AutoSigninView::OnWidgetClosing(
348 views::Widget
* widget
) {
349 observed_browser_
.RemoveAll();
352 void ManagePasswordsBubbleView::AutoSigninView::OnTimer() {
353 parent_
->model()->OnAutoSignInToastTimeout();
357 // ManagePasswordsBubbleView::PendingView -------------------------------------
359 // A view offering the user the ability to save credentials. Contains a
360 // single ManagePasswordItemsView, along with a "Save Passwords" button
361 // and a "Never" button.
362 class ManagePasswordsBubbleView::PendingView
363 : public views::View
,
364 public views::ButtonListener
,
365 public views::StyledLabelListener
{
367 explicit PendingView(ManagePasswordsBubbleView
* parent
);
368 ~PendingView() override
;
371 // views::ButtonListener:
372 void ButtonPressed(views::Button
* sender
, const ui::Event
& event
) override
;
374 // views::StyledLabelListener:
375 void StyledLabelLinkClicked(const gfx::Range
& range
,
376 int event_flags
) override
;
378 ManagePasswordsBubbleView
* parent_
;
380 views::BlueButton
* save_button_
;
381 views::LabelButton
* never_button_
;
383 DISALLOW_COPY_AND_ASSIGN(PendingView
);
386 ManagePasswordsBubbleView::PendingView::PendingView(
387 ManagePasswordsBubbleView
* parent
)
389 views::GridLayout
* layout
= new views::GridLayout(this);
390 layout
->set_minimum_size(gfx::Size(kDesiredBubbleWidth
, 0));
391 SetLayoutManager(layout
);
393 // Create the pending credential item, save button and refusal combobox.
394 ManagePasswordItemsView
* item
= nullptr;
395 if (!parent
->model()->pending_password()
396 .IsPossibleChangePasswordFormWithoutUsername()) {
397 std::vector
<const autofill::PasswordForm
*> credentials(
398 1, &parent
->model()->pending_password());
399 item
= new ManagePasswordItemsView(parent_
->model(), credentials
);
401 save_button_
= new views::BlueButton(
402 this, l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_SAVE_BUTTON
));
403 save_button_
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
404 ui::ResourceBundle::SmallFont
));
405 never_button_
= new views::LabelButton(
407 l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_BUBBLE_BLACKLIST_BUTTON
));
408 never_button_
->SetStyle(views::Button::STYLE_BUTTON
);
409 never_button_
->SetFontList(
410 ui::ResourceBundle::GetSharedInstance().GetFontList(
411 ui::ResourceBundle::SmallFont
));
414 views::StyledLabel
* title_label
=
415 new views::StyledLabel(parent_
->model()->title(), this);
416 title_label
->SetBaseFontList(
417 ui::ResourceBundle::GetSharedInstance().GetFontList(
418 ui::ResourceBundle::MediumFont
));
419 if (!parent_
->model()->title_brand_link_range().is_empty()) {
420 title_label
->AddStyleRange(
421 parent_
->model()->title_brand_link_range(),
422 views::StyledLabel::RangeStyleInfo::CreateForLink());
424 BuildColumnSet(layout
, SINGLE_VIEW_COLUMN_SET
);
425 layout
->StartRow(0, SINGLE_VIEW_COLUMN_SET
);
426 layout
->AddView(title_label
);
427 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
431 layout
->StartRow(0, SINGLE_VIEW_COLUMN_SET
);
432 layout
->AddView(item
);
436 BuildColumnSet(layout
, DOUBLE_BUTTON_COLUMN_SET
);
437 layout
->StartRowWithPadding(
438 0, DOUBLE_BUTTON_COLUMN_SET
, 0, views::kRelatedControlVerticalSpacing
);
439 layout
->AddView(save_button_
);
440 layout
->AddView(never_button_
);
442 // Extra padding for visual awesomeness.
443 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
445 parent_
->set_initially_focused_view(save_button_
);
448 ManagePasswordsBubbleView::PendingView::~PendingView() {
451 void ManagePasswordsBubbleView::PendingView::ButtonPressed(
452 views::Button
* sender
,
453 const ui::Event
& event
) {
454 if (sender
== save_button_
)
455 parent_
->model()->OnSaveClicked();
456 else if (sender
== never_button_
)
457 parent_
->model()->OnNeverForThisSiteClicked();
464 void ManagePasswordsBubbleView::PendingView::StyledLabelLinkClicked(
465 const gfx::Range
& range
,
467 DCHECK_EQ(range
, parent_
->model()->title_brand_link_range());
468 parent_
->model()->OnBrandLinkClicked();
471 // ManagePasswordsBubbleView::ManageView --------------------------------------
473 // A view offering the user a list of her currently saved credentials
474 // for the current page, along with a "Manage passwords" link and a
476 class ManagePasswordsBubbleView::ManageView
: public views::View
,
477 public views::ButtonListener
,
478 public views::LinkListener
{
480 explicit ManageView(ManagePasswordsBubbleView
* parent
);
481 ~ManageView() override
;
484 // views::ButtonListener:
485 void ButtonPressed(views::Button
* sender
, const ui::Event
& event
) override
;
487 // views::LinkListener:
488 void LinkClicked(views::Link
* source
, int event_flags
) override
;
490 ManagePasswordsBubbleView
* parent_
;
492 views::Link
* manage_link_
;
493 views::LabelButton
* done_button_
;
495 DISALLOW_COPY_AND_ASSIGN(ManageView
);
498 ManagePasswordsBubbleView::ManageView::ManageView(
499 ManagePasswordsBubbleView
* parent
)
501 views::GridLayout
* layout
= new views::GridLayout(this);
502 layout
->set_minimum_size(gfx::Size(kDesiredBubbleWidth
, 0));
503 SetLayoutManager(layout
);
506 BuildColumnSet(layout
, SINGLE_VIEW_COLUMN_SET
);
507 AddTitleRow(layout
, parent_
->model());
509 // If we have a list of passwords to store for the current site, display
510 // them to the user for management. Otherwise, render a "No passwords for
511 // this site" message.
512 if (!parent_
->model()->local_credentials().empty()) {
513 ManagePasswordItemsView
* item
= new ManagePasswordItemsView(
514 parent_
->model(), parent_
->model()->local_credentials().get());
515 layout
->StartRow(0, SINGLE_VIEW_COLUMN_SET
);
516 layout
->AddView(item
);
518 views::Label
* empty_label
= new views::Label(
519 l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_NO_PASSWORDS
));
520 empty_label
->SetMultiLine(true);
521 empty_label
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
522 empty_label
->SetFontList(
523 ui::ResourceBundle::GetSharedInstance().GetFontList(
524 ui::ResourceBundle::SmallFont
));
526 layout
->StartRow(0, SINGLE_VIEW_COLUMN_SET
);
527 layout
->AddView(empty_label
);
528 layout
->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing
);
531 // Then add the "manage passwords" link and "Done" button.
532 manage_link_
= new views::Link(parent_
->model()->manage_link());
533 manage_link_
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
534 manage_link_
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
535 ui::ResourceBundle::SmallFont
));
536 manage_link_
->SetUnderline(false);
537 manage_link_
->set_listener(this);
540 new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_DONE
));
541 done_button_
->SetStyle(views::Button::STYLE_BUTTON
);
542 done_button_
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
543 ui::ResourceBundle::SmallFont
));
545 BuildColumnSet(layout
, LINK_BUTTON_COLUMN_SET
);
546 layout
->StartRowWithPadding(
547 0, LINK_BUTTON_COLUMN_SET
, 0, views::kRelatedControlVerticalSpacing
);
548 layout
->AddView(manage_link_
);
549 layout
->AddView(done_button_
);
551 // Extra padding for visual awesomeness.
552 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
554 parent_
->set_initially_focused_view(done_button_
);
557 ManagePasswordsBubbleView::ManageView::~ManageView() {
560 void ManagePasswordsBubbleView::ManageView::ButtonPressed(
561 views::Button
* sender
,
562 const ui::Event
& event
) {
563 DCHECK(sender
== done_button_
);
564 parent_
->model()->OnDoneClicked();
568 void ManagePasswordsBubbleView::ManageView::LinkClicked(views::Link
* source
,
570 DCHECK_EQ(source
, manage_link_
);
571 parent_
->model()->OnManageLinkClicked();
575 // ManagePasswordsBubbleView::SaveConfirmationView ----------------------------
577 // A view confirming to the user that a password was saved and offering a link
578 // to the Google account manager.
579 class ManagePasswordsBubbleView::SaveConfirmationView
580 : public views::View
,
581 public views::ButtonListener
,
582 public views::StyledLabelListener
{
584 explicit SaveConfirmationView(ManagePasswordsBubbleView
* parent
);
585 ~SaveConfirmationView() override
;
588 // views::ButtonListener:
589 void ButtonPressed(views::Button
* sender
, const ui::Event
& event
) override
;
591 // views::StyledLabelListener implementation
592 void StyledLabelLinkClicked(const gfx::Range
& range
,
593 int event_flags
) override
;
595 ManagePasswordsBubbleView
* parent_
;
596 views::LabelButton
* ok_button_
;
598 DISALLOW_COPY_AND_ASSIGN(SaveConfirmationView
);
601 ManagePasswordsBubbleView::SaveConfirmationView::SaveConfirmationView(
602 ManagePasswordsBubbleView
* parent
)
604 views::GridLayout
* layout
= new views::GridLayout(this);
605 layout
->set_minimum_size(gfx::Size(kDesiredBubbleWidth
, 0));
606 SetLayoutManager(layout
);
608 BuildColumnSet(layout
, SINGLE_VIEW_COLUMN_SET
);
609 AddTitleRow(layout
, parent_
->model());
611 views::StyledLabel
* confirmation
=
612 new views::StyledLabel(parent_
->model()->save_confirmation_text(), this);
613 confirmation
->SetBaseFontList(
614 ui::ResourceBundle::GetSharedInstance().GetFontList(
615 ui::ResourceBundle::SmallFont
));
616 confirmation
->AddStyleRange(
617 parent_
->model()->save_confirmation_link_range(),
618 views::StyledLabel::RangeStyleInfo::CreateForLink());
620 layout
->StartRow(0, SINGLE_VIEW_COLUMN_SET
);
621 layout
->AddView(confirmation
);
623 ok_button_
= new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_OK
));
624 ok_button_
->SetStyle(views::Button::STYLE_BUTTON
);
625 ok_button_
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
626 ui::ResourceBundle::SmallFont
));
628 BuildColumnSet(layout
, SINGLE_BUTTON_COLUMN_SET
);
629 layout
->StartRowWithPadding(
630 0, SINGLE_BUTTON_COLUMN_SET
, 0, views::kRelatedControlVerticalSpacing
);
631 layout
->AddView(ok_button_
);
633 // Extra padding for visual awesomeness.
634 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
636 parent_
->set_initially_focused_view(ok_button_
);
639 ManagePasswordsBubbleView::SaveConfirmationView::~SaveConfirmationView() {
642 void ManagePasswordsBubbleView::SaveConfirmationView::StyledLabelLinkClicked(
643 const gfx::Range
& range
, int event_flags
) {
644 DCHECK_EQ(range
, parent_
->model()->save_confirmation_link_range());
645 parent_
->model()->OnManageLinkClicked();
649 void ManagePasswordsBubbleView::SaveConfirmationView::ButtonPressed(
650 views::Button
* sender
, const ui::Event
& event
) {
651 DCHECK_EQ(sender
, ok_button_
);
652 parent_
->model()->OnOKClicked();
656 // ManagePasswordsBubbleView::WebContentMouseHandler --------------------------
658 // The class listens for WebContentsView events and notifies the bubble if the
659 // view was clicked on or received keystrokes.
660 class ManagePasswordsBubbleView::WebContentMouseHandler
661 : public ui::EventHandler
{
663 explicit WebContentMouseHandler(ManagePasswordsBubbleView
* bubble
);
665 void OnKeyEvent(ui::KeyEvent
* event
) override
;
666 void OnMouseEvent(ui::MouseEvent
* event
) override
;
667 void OnTouchEvent(ui::TouchEvent
* event
) override
;
670 ManagePasswordsBubbleView
* bubble_
;
671 scoped_ptr
<views::EventMonitor
> event_monitor_
;
673 DISALLOW_COPY_AND_ASSIGN(WebContentMouseHandler
);
676 ManagePasswordsBubbleView::WebContentMouseHandler::WebContentMouseHandler(
677 ManagePasswordsBubbleView
* bubble
)
679 content::WebContents
* web_contents
= bubble_
->web_contents();
680 DCHECK(web_contents
);
681 event_monitor_
= views::EventMonitor::CreateWindowMonitor(
682 this, web_contents
->GetTopLevelNativeWindow());
685 void ManagePasswordsBubbleView::WebContentMouseHandler::OnKeyEvent(
686 ui::KeyEvent
* event
) {
687 content::WebContents
* web_contents
= bubble_
->web_contents();
688 content::RenderViewHost
* rvh
= web_contents
->GetRenderViewHost();
689 if ((event
->key_code() == ui::VKEY_ESCAPE
||
690 rvh
->IsFocusedElementEditable()) && event
->type() == ui::ET_KEY_PRESSED
)
694 void ManagePasswordsBubbleView::WebContentMouseHandler::OnMouseEvent(
695 ui::MouseEvent
* event
) {
696 if (event
->type() == ui::ET_MOUSE_PRESSED
)
700 void ManagePasswordsBubbleView::WebContentMouseHandler::OnTouchEvent(
701 ui::TouchEvent
* event
) {
702 if (event
->type() == ui::ET_TOUCH_PRESSED
)
706 // ManagePasswordsBubbleView::UpdatePendingView -------------------------------
708 // A view offering the user the ability to update credentials. Contains a
709 // single ManagePasswordItemsView (in case of one credentials) or
710 // CredentialsSelectionView otherwise, along with a "Update Passwords" button
711 // and a rejection button.
712 class ManagePasswordsBubbleView::UpdatePendingView
713 : public views::View
,
714 public views::ButtonListener
,
715 public views::StyledLabelListener
{
717 explicit UpdatePendingView(ManagePasswordsBubbleView
* parent
);
718 ~UpdatePendingView() override
;
721 // views::ButtonListener:
722 void ButtonPressed(views::Button
* sender
, const ui::Event
& event
) override
;
724 // views::StyledLabelListener:
725 void StyledLabelLinkClicked(const gfx::Range
& range
,
726 int event_flags
) override
;
728 ManagePasswordsBubbleView
* parent_
;
730 CredentialsSelectionView
* selection_view_
;
732 views::BlueButton
* update_button_
;
734 views::LabelButton
* nope_button_
;
736 DISALLOW_COPY_AND_ASSIGN(UpdatePendingView
);
739 ManagePasswordsBubbleView::UpdatePendingView::UpdatePendingView(
740 ManagePasswordsBubbleView
* parent
)
741 : parent_(parent
), selection_view_(nullptr) {
742 views::GridLayout
* layout
= new views::GridLayout(this);
743 layout
->set_minimum_size(gfx::Size(kDesiredBubbleWidth
, 0));
744 SetLayoutManager(layout
);
746 // Create the pending credential item, update button.
747 View
* item
= nullptr;
748 if (parent
->model()->ShouldShowMultipleAccountUpdateUI()) {
749 selection_view_
= new CredentialsSelectionView(
750 parent
->model(), parent
->model()->local_credentials().get(),
751 parent
->model()->pending_password().username_value
);
752 item
= selection_view_
;
754 std::vector
<const autofill::PasswordForm
*> forms
;
755 forms
.push_back(&parent
->model()->pending_password());
756 item
= new ManagePasswordItemsView(parent_
->model(), forms
);
758 nope_button_
= new views::LabelButton(
759 this, l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_CANCEL_BUTTON
));
760 nope_button_
->SetStyle(views::Button::STYLE_BUTTON
);
761 nope_button_
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
762 ui::ResourceBundle::SmallFont
));
764 update_button_
= new views::BlueButton(
765 this, l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_UPDATE_BUTTON
));
766 update_button_
->SetFontList(
767 ui::ResourceBundle::GetSharedInstance().GetFontList(
768 ui::ResourceBundle::SmallFont
));
771 views::StyledLabel
* title_label
=
772 new views::StyledLabel(parent_
->model()->title(), this);
773 title_label
->SetBaseFontList(
774 ui::ResourceBundle::GetSharedInstance().GetFontList(
775 ui::ResourceBundle::MediumFont
));
776 if (!parent_
->model()->title_brand_link_range().is_empty()) {
777 title_label
->AddStyleRange(
778 parent_
->model()->title_brand_link_range(),
779 views::StyledLabel::RangeStyleInfo::CreateForLink());
781 BuildColumnSet(layout
, SINGLE_VIEW_COLUMN_SET
);
782 layout
->StartRow(0, SINGLE_VIEW_COLUMN_SET
);
783 layout
->AddView(title_label
);
784 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
787 layout
->StartRow(0, SINGLE_VIEW_COLUMN_SET
);
788 layout
->AddView(item
);
791 BuildColumnSet(layout
, DOUBLE_BUTTON_COLUMN_SET
);
792 layout
->StartRowWithPadding(0, DOUBLE_BUTTON_COLUMN_SET
, 0,
793 views::kUnrelatedControlVerticalSpacing
);
794 layout
->AddView(update_button_
);
795 layout
->AddView(nope_button_
);
797 // Extra padding for visual awesomeness.
798 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
800 parent_
->set_initially_focused_view(update_button_
);
803 ManagePasswordsBubbleView::UpdatePendingView::~UpdatePendingView() {}
805 void ManagePasswordsBubbleView::UpdatePendingView::ButtonPressed(
806 views::Button
* sender
,
807 const ui::Event
& event
) {
808 DCHECK(sender
== update_button_
|| sender
== nope_button_
);
809 if (sender
== update_button_
) {
810 if (selection_view_
) {
811 // Multi account case.
812 parent_
->model()->OnUpdateClicked(
813 *selection_view_
->GetSelectedCredentials());
815 parent_
->model()->OnUpdateClicked(parent_
->model()->pending_password());
818 parent_
->model()->OnNopeUpdateClicked();
823 void ManagePasswordsBubbleView::UpdatePendingView::StyledLabelLinkClicked(
824 const gfx::Range
& range
,
826 DCHECK_EQ(range
, parent_
->model()->title_brand_link_range());
827 parent_
->model()->OnBrandLinkClicked();
830 // ManagePasswordsBubbleView --------------------------------------------------
833 ManagePasswordsBubbleView
* ManagePasswordsBubbleView::manage_passwords_bubble_
=
837 void ManagePasswordsBubbleView::ShowBubble(content::WebContents
* web_contents
,
838 DisplayReason reason
) {
839 Browser
* browser
= chrome::FindBrowserWithWebContents(web_contents
);
841 DCHECK(browser
->window());
842 DCHECK(!manage_passwords_bubble_
||
843 !manage_passwords_bubble_
->GetWidget()->IsVisible());
845 BrowserView
* browser_view
= BrowserView::GetBrowserViewForBrowser(browser
);
846 bool is_fullscreen
= browser_view
->IsFullscreen();
847 ManagePasswordsIconView
* anchor_view
=
850 : browser_view
->GetLocationBarView()->manage_passwords_icon_view();
851 manage_passwords_bubble_
= new ManagePasswordsBubbleView(
852 web_contents
, anchor_view
, reason
);
855 manage_passwords_bubble_
->set_parent_window(web_contents
->GetNativeView());
857 views::BubbleDelegateView::CreateBubble(manage_passwords_bubble_
);
859 // Adjust for fullscreen after creation as it relies on the content size.
861 manage_passwords_bubble_
->AdjustForFullscreen(
862 browser_view
->GetBoundsInScreen());
864 if (reason
== AUTOMATIC
)
865 manage_passwords_bubble_
->GetWidget()->ShowInactive();
867 manage_passwords_bubble_
->GetWidget()->Show();
871 void ManagePasswordsBubbleView::CloseBubble() {
872 if (manage_passwords_bubble_
)
873 manage_passwords_bubble_
->Close();
877 void ManagePasswordsBubbleView::ActivateBubble() {
878 DCHECK(manage_passwords_bubble_
);
879 DCHECK(manage_passwords_bubble_
->GetWidget()->IsVisible());
880 manage_passwords_bubble_
->GetWidget()->Activate();
883 content::WebContents
* ManagePasswordsBubbleView::web_contents() const {
884 return model()->web_contents();
887 ManagePasswordsBubbleView::ManagePasswordsBubbleView(
888 content::WebContents
* web_contents
,
889 ManagePasswordsIconView
* anchor_view
,
890 DisplayReason reason
)
891 : ManagePasswordsBubble(web_contents
, reason
),
892 ManagedFullScreenBubbleDelegateView(anchor_view
, web_contents
),
893 anchor_view_(anchor_view
),
894 initially_focused_view_(NULL
) {
895 // Compensate for built-in vertical padding in the anchor view's image.
896 set_anchor_view_insets(gfx::Insets(5, 0, 5, 0));
898 anchor_view
->SetActive(true);
899 mouse_handler_
.reset(new WebContentMouseHandler(this));
902 ManagePasswordsBubbleView::~ManagePasswordsBubbleView() {
903 if (manage_passwords_bubble_
== this)
904 manage_passwords_bubble_
= NULL
;
907 views::View
* ManagePasswordsBubbleView::GetInitiallyFocusedView() {
908 return initially_focused_view_
;
911 void ManagePasswordsBubbleView::Init() {
912 views::FillLayout
* layout
= new views::FillLayout();
913 SetLayoutManager(layout
);
918 void ManagePasswordsBubbleView::Close() {
919 mouse_handler_
.reset();
920 ManagedFullScreenBubbleDelegateView::Close();
923 void ManagePasswordsBubbleView::OnWidgetClosing(views::Widget
* /*widget*/) {
925 anchor_view_
->SetActive(false);
928 bool ManagePasswordsBubbleView::ShouldShowCloseButton() const {
929 return model()->state() == password_manager::ui::PENDING_PASSWORD_STATE
;
932 void ManagePasswordsBubbleView::Refresh() {
933 RemoveAllChildViews(true);
934 initially_focused_view_
= NULL
;
935 if (model()->state() == password_manager::ui::PENDING_PASSWORD_STATE
) {
936 AddChildView(new PendingView(this));
937 } else if (model()->state() ==
938 password_manager::ui::PENDING_PASSWORD_UPDATE_STATE
) {
939 AddChildView(new UpdatePendingView(this));
940 } else if (model()->state() == password_manager::ui::CONFIRMATION_STATE
) {
941 AddChildView(new SaveConfirmationView(this));
942 } else if (model()->state() ==
943 password_manager::ui::CREDENTIAL_REQUEST_STATE
) {
944 AddChildView(new AccountChooserView(this));
945 } else if (model()->state() == password_manager::ui::AUTO_SIGNIN_STATE
) {
946 AddChildView(new AutoSigninView(this));
948 AddChildView(new ManageView(this));
950 GetLayoutManager()->Layout(this);