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;
43 const SkColor kWarmWelcomeColor
= SkColorSetARGB(0xFF, 0x64, 0x64, 0x64);
46 // | | (FILL, FILL) | |
47 // Used for the bubble's header, the credentials list, and for simple
48 // messages like "No passwords".
49 SINGLE_VIEW_COLUMN_SET
,
51 // | | (TRAILING, CENTER) | | (TRAILING, CENTER) | |
52 // Used for buttons at the bottom of the bubble which should nest at the
53 // bottom-right corner.
54 DOUBLE_BUTTON_COLUMN_SET
,
56 // | | (LEADING, CENTER) | | (TRAILING, CENTER) | |
57 // Used for buttons at the bottom of the bubble which should occupy
59 LINK_BUTTON_COLUMN_SET
,
61 // | | (TRAILING, CENTER) | |
62 // Used when there is only one button which should next at the bottom-right
64 SINGLE_BUTTON_COLUMN_SET
,
66 // | | (LEADING, CENTER) | | (TRAILING, CENTER) | | (TRAILING, CENTER) | |
67 // Used when there are three buttons.
68 TRIPLE_BUTTON_COLUMN_SET
,
71 enum TextRowType
{ ROW_SINGLE
, ROW_MULTILINE
};
73 // Construct an appropriate ColumnSet for the given |type|, and add it
75 void BuildColumnSet(views::GridLayout
* layout
, ColumnSetType type
) {
76 views::ColumnSet
* column_set
= layout
->AddColumnSet(type
);
77 column_set
->AddPaddingColumn(0, views::kPanelHorizMargin
);
78 int full_width
= kDesiredBubbleWidth
- (2 * views::kPanelHorizMargin
);
80 case SINGLE_VIEW_COLUMN_SET
:
81 column_set
->AddColumn(views::GridLayout::FILL
,
82 views::GridLayout::FILL
,
84 views::GridLayout::FIXED
,
88 case DOUBLE_BUTTON_COLUMN_SET
:
89 column_set
->AddColumn(views::GridLayout::TRAILING
,
90 views::GridLayout::CENTER
,
92 views::GridLayout::USE_PREF
,
95 column_set
->AddPaddingColumn(0, views::kRelatedButtonHSpacing
);
96 column_set
->AddColumn(views::GridLayout::TRAILING
,
97 views::GridLayout::CENTER
,
99 views::GridLayout::USE_PREF
,
103 case LINK_BUTTON_COLUMN_SET
:
104 column_set
->AddColumn(views::GridLayout::LEADING
,
105 views::GridLayout::CENTER
,
107 views::GridLayout::USE_PREF
,
110 column_set
->AddPaddingColumn(0, views::kRelatedButtonHSpacing
);
111 column_set
->AddColumn(views::GridLayout::TRAILING
,
112 views::GridLayout::CENTER
,
114 views::GridLayout::USE_PREF
,
118 case SINGLE_BUTTON_COLUMN_SET
:
119 column_set
->AddColumn(views::GridLayout::TRAILING
,
120 views::GridLayout::CENTER
,
122 views::GridLayout::USE_PREF
,
126 case TRIPLE_BUTTON_COLUMN_SET
:
127 column_set
->AddColumn(views::GridLayout::LEADING
,
128 views::GridLayout::CENTER
,
130 views::GridLayout::USE_PREF
,
133 column_set
->AddPaddingColumn(0, views::kRelatedButtonHSpacing
);
134 column_set
->AddColumn(views::GridLayout::TRAILING
,
135 views::GridLayout::CENTER
,
137 views::GridLayout::USE_PREF
,
140 column_set
->AddPaddingColumn(0, views::kRelatedButtonHSpacing
);
141 column_set
->AddColumn(views::GridLayout::TRAILING
,
142 views::GridLayout::CENTER
,
144 views::GridLayout::USE_PREF
,
149 column_set
->AddPaddingColumn(0, views::kPanelHorizMargin
);
152 // Given a layout and a model, add an appropriate title using a
153 // SINGLE_VIEW_COLUMN_SET, followed by a spacer row.
154 void AddTitleRow(views::GridLayout
* layout
, ManagePasswordsBubbleModel
* model
) {
155 views::Label
* title_label
= new views::Label(model
->title());
156 title_label
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
157 title_label
->SetMultiLine(true);
158 title_label
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
159 ui::ResourceBundle::MediumFont
));
161 // Add the title to the layout with appropriate padding.
162 layout
->StartRowWithPadding(
163 0, SINGLE_VIEW_COLUMN_SET
, 0, views::kRelatedControlSmallVerticalSpacing
);
164 layout
->AddView(title_label
);
165 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
171 // ManagePasswordsBubbleView::AccountChooserView ------------------------------
173 // A view offering the user the ability to choose credentials for
174 // authentication. Contains a list of CredentialsItemView, along with a
176 class ManagePasswordsBubbleView::AccountChooserView
177 : public views::View
,
178 public views::ButtonListener
{
180 explicit AccountChooserView(ManagePasswordsBubbleView
* parent
);
181 ~AccountChooserView() override
;
184 // views::ButtonListener:
185 void ButtonPressed(views::Button
* sender
, const ui::Event
& event
) override
;
187 // Adds |password_forms| to the layout remembering their |type|.
188 void AddCredentialItemsWithType(
189 views::GridLayout
* layout
,
190 const ScopedVector
<const autofill::PasswordForm
>& password_forms
,
191 password_manager::CredentialType type
);
193 ManagePasswordsBubbleView
* parent_
;
194 views::LabelButton
* cancel_button_
;
196 DISALLOW_COPY_AND_ASSIGN(AccountChooserView
);
199 ManagePasswordsBubbleView::AccountChooserView::AccountChooserView(
200 ManagePasswordsBubbleView
* parent
)
202 views::GridLayout
* layout
= new views::GridLayout(this);
203 SetLayoutManager(layout
);
206 new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_CANCEL
));
207 cancel_button_
->SetStyle(views::Button::STYLE_BUTTON
);
208 cancel_button_
->SetFontList(
209 ui::ResourceBundle::GetSharedInstance().GetFontList(
210 ui::ResourceBundle::SmallFont
));
213 BuildColumnSet(layout
, SINGLE_VIEW_COLUMN_SET
);
214 AddTitleRow(layout
, parent_
->model());
216 AddCredentialItemsWithType(
217 layout
, parent_
->model()->local_credentials(),
218 password_manager::CredentialType::CREDENTIAL_TYPE_PASSWORD
);
220 AddCredentialItemsWithType(
221 layout
, parent_
->model()->federated_credentials(),
222 password_manager::CredentialType::CREDENTIAL_TYPE_FEDERATED
);
225 BuildColumnSet(layout
, SINGLE_BUTTON_COLUMN_SET
);
226 layout
->StartRowWithPadding(
227 0, SINGLE_BUTTON_COLUMN_SET
, 0, views::kRelatedControlVerticalSpacing
);
228 layout
->AddView(cancel_button_
);
230 // Extra padding for visual awesomeness.
231 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
233 parent_
->set_initially_focused_view(cancel_button_
);
236 ManagePasswordsBubbleView::AccountChooserView::~AccountChooserView() {
239 void ManagePasswordsBubbleView::AccountChooserView::AddCredentialItemsWithType(
240 views::GridLayout
* layout
,
241 const ScopedVector
<const autofill::PasswordForm
>& password_forms
,
242 password_manager::CredentialType type
) {
243 net::URLRequestContextGetter
* request_context
=
244 parent_
->model()->GetProfile()->GetRequestContext();
245 for (const autofill::PasswordForm
* form
: password_forms
) {
246 const base::string16
& upper_string
=
247 form
->display_name
.empty() ? form
->username_value
: form
->display_name
;
248 base::string16 lower_string
;
249 if (form
->federation_url
.is_empty()) {
250 if (!form
->display_name
.empty())
251 lower_string
= form
->username_value
;
253 lower_string
= l10n_util::GetStringFUTF16(
254 IDS_PASSWORDS_VIA_FEDERATION
,
255 base::UTF8ToUTF16(form
->federation_url
.host()));
257 layout
->StartRow(0, SINGLE_VIEW_COLUMN_SET
);
258 layout
->AddView(new CredentialsItemView(this, form
, type
, upper_string
,
259 lower_string
, request_context
));
263 void ManagePasswordsBubbleView::AccountChooserView::ButtonPressed(
264 views::Button
* sender
, const ui::Event
& event
) {
265 if (sender
!= cancel_button_
) {
266 // ManagePasswordsBubbleModel should care about calling a callback in case
267 // the bubble is dismissed by any other means.
268 CredentialsItemView
* view
= static_cast<CredentialsItemView
*>(sender
);
269 parent_
->model()->OnChooseCredentials(*view
->form(),
270 view
->credential_type());
272 parent_
->model()->OnCancelClicked();
277 // ManagePasswordsBubbleView::AutoSigninView ----------------------------------
279 // A view containing just one credential that was used for for automatic signing
281 class ManagePasswordsBubbleView::AutoSigninView
282 : public views::View
,
283 public views::ButtonListener
,
284 public views::WidgetObserver
{
286 explicit AutoSigninView(ManagePasswordsBubbleView
* parent
);
289 // views::ButtonListener:
290 void ButtonPressed(views::Button
* sender
, const ui::Event
& event
) override
;
292 // views::WidgetObserver:
293 // Tracks the state of the browser window.
294 void OnWidgetActivationChanged(views::Widget
* widget
, bool active
) override
;
295 void OnWidgetClosing(views::Widget
* widget
) override
;
298 static base::TimeDelta
GetTimeout() {
299 return base::TimeDelta::FromSeconds(
300 ManagePasswordsBubbleView::auto_signin_toast_timeout_
);
303 base::OneShotTimer
<AutoSigninView
> timer_
;
304 ManagePasswordsBubbleView
* parent_
;
305 ScopedObserver
<views::Widget
, views::WidgetObserver
> observed_browser_
;
307 DISALLOW_COPY_AND_ASSIGN(AutoSigninView
);
310 ManagePasswordsBubbleView::AutoSigninView::AutoSigninView(
311 ManagePasswordsBubbleView
* parent
)
313 observed_browser_(this) {
314 SetLayoutManager(new views::FillLayout
);
315 const autofill::PasswordForm
& form
= parent_
->model()->pending_password();
316 CredentialsItemView
* credential
= new CredentialsItemView(
318 password_manager::CredentialType::CREDENTIAL_TYPE_PASSWORD
,
320 l10n_util::GetStringFUTF16(IDS_MANAGE_PASSWORDS_AUTO_SIGNIN_TITLE
,
321 form
.username_value
),
322 parent_
->model()->GetProfile()->GetRequestContext());
323 AddChildView(credential
);
324 credential
->SetEnabled(false);
327 chrome::FindBrowserWithWebContents(parent_
->web_contents());
329 BrowserView
* browser_view
= BrowserView::GetBrowserViewForBrowser(browser
);
330 observed_browser_
.Add(browser_view
->GetWidget());
332 if (browser_view
->IsActive())
333 timer_
.Start(FROM_HERE
, GetTimeout(), this, &AutoSigninView::OnTimer
);
336 void ManagePasswordsBubbleView::AutoSigninView::ButtonPressed(
337 views::Button
* sender
, const ui::Event
& event
) {
338 parent_
->model()->OnAutoSignInClicked();
342 void ManagePasswordsBubbleView::AutoSigninView::OnWidgetActivationChanged(
343 views::Widget
* widget
, bool active
) {
344 if (active
&& !timer_
.IsRunning())
345 timer_
.Start(FROM_HERE
, GetTimeout(), this, &AutoSigninView::OnTimer
);
348 void ManagePasswordsBubbleView::AutoSigninView::OnWidgetClosing(
349 views::Widget
* widget
) {
350 observed_browser_
.RemoveAll();
353 void ManagePasswordsBubbleView::AutoSigninView::OnTimer() {
354 parent_
->model()->OnAutoSignInToastTimeout();
358 // ManagePasswordsBubbleView::PendingView -------------------------------------
360 // A view offering the user the ability to save credentials. Contains a
361 // single ManagePasswordItemsView, along with a "Save Passwords" button
362 // and a "Never" button.
363 class ManagePasswordsBubbleView::PendingView
364 : public views::View
,
365 public views::ButtonListener
,
366 public views::StyledLabelListener
{
368 explicit PendingView(ManagePasswordsBubbleView
* parent
);
369 ~PendingView() override
;
372 // views::ButtonListener:
373 void ButtonPressed(views::Button
* sender
, const ui::Event
& event
) override
;
375 // views::StyledLabelListener:
376 void StyledLabelLinkClicked(const gfx::Range
& range
,
377 int event_flags
) override
;
379 ManagePasswordsBubbleView
* parent_
;
381 views::BlueButton
* save_button_
;
382 views::LabelButton
* never_button_
;
384 DISALLOW_COPY_AND_ASSIGN(PendingView
);
387 ManagePasswordsBubbleView::PendingView::PendingView(
388 ManagePasswordsBubbleView
* parent
)
390 views::GridLayout
* layout
= new views::GridLayout(this);
391 layout
->set_minimum_size(gfx::Size(kDesiredBubbleWidth
, 0));
392 SetLayoutManager(layout
);
394 // Create the pending credential item, save button and refusal combobox.
395 ManagePasswordItemsView
* item
= nullptr;
396 if (!parent
->model()->pending_password()
397 .IsPossibleChangePasswordFormWithoutUsername()) {
398 std::vector
<const autofill::PasswordForm
*> credentials(
399 1, &parent
->model()->pending_password());
400 item
= new ManagePasswordItemsView(parent_
->model(), credentials
);
402 save_button_
= new views::BlueButton(
403 this, l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_SAVE_BUTTON
));
404 save_button_
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
405 ui::ResourceBundle::SmallFont
));
406 never_button_
= new views::LabelButton(
408 l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_BUBBLE_BLACKLIST_BUTTON
));
409 never_button_
->SetStyle(views::Button::STYLE_BUTTON
);
410 never_button_
->SetFontList(
411 ui::ResourceBundle::GetSharedInstance().GetFontList(
412 ui::ResourceBundle::SmallFont
));
415 views::StyledLabel
* title_label
=
416 new views::StyledLabel(parent_
->model()->title(), this);
417 title_label
->SetBaseFontList(
418 ui::ResourceBundle::GetSharedInstance().GetFontList(
419 ui::ResourceBundle::MediumFont
));
420 if (!parent_
->model()->title_brand_link_range().is_empty()) {
421 title_label
->AddStyleRange(
422 parent_
->model()->title_brand_link_range(),
423 views::StyledLabel::RangeStyleInfo::CreateForLink());
425 BuildColumnSet(layout
, SINGLE_VIEW_COLUMN_SET
);
426 layout
->StartRow(0, SINGLE_VIEW_COLUMN_SET
);
427 layout
->AddView(title_label
);
431 layout
->StartRowWithPadding(0, SINGLE_VIEW_COLUMN_SET
, 0,
432 views::kUnrelatedControlVerticalSpacing
);
433 layout
->AddView(item
);
436 // Smart Lock warm welcome.
437 if (parent_
->model()->ShouldShowGoogleSmartLockWelcome()) {
438 views::Label
* smart_lock_label
= new views::Label(
439 l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_SMART_LOCK_WELCOME
));
440 smart_lock_label
->SetMultiLine(true);
441 smart_lock_label
->SetFontList(
442 ui::ResourceBundle::GetSharedInstance().GetFontList(
443 ui::ResourceBundle::SmallFont
));
444 smart_lock_label
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
445 smart_lock_label
->SetEnabledColor(kWarmWelcomeColor
);
446 layout
->StartRowWithPadding(0, SINGLE_VIEW_COLUMN_SET
, 0,
447 views::kUnrelatedControlVerticalSpacing
);
448 layout
->AddView(smart_lock_label
);
452 BuildColumnSet(layout
, DOUBLE_BUTTON_COLUMN_SET
);
453 layout
->StartRowWithPadding(0, DOUBLE_BUTTON_COLUMN_SET
, 0,
454 views::kUnrelatedControlVerticalSpacing
);
455 layout
->AddView(save_button_
);
456 layout
->AddView(never_button_
);
458 // Extra padding for visual awesomeness.
459 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
461 parent_
->set_initially_focused_view(save_button_
);
464 ManagePasswordsBubbleView::PendingView::~PendingView() {
467 void ManagePasswordsBubbleView::PendingView::ButtonPressed(
468 views::Button
* sender
,
469 const ui::Event
& event
) {
470 if (sender
== save_button_
)
471 parent_
->model()->OnSaveClicked();
472 else if (sender
== never_button_
)
473 parent_
->model()->OnNeverForThisSiteClicked();
480 void ManagePasswordsBubbleView::PendingView::StyledLabelLinkClicked(
481 const gfx::Range
& range
,
483 DCHECK_EQ(range
, parent_
->model()->title_brand_link_range());
484 parent_
->model()->OnBrandLinkClicked();
487 // ManagePasswordsBubbleView::ManageView --------------------------------------
489 // A view offering the user a list of her currently saved credentials
490 // for the current page, along with a "Manage passwords" link and a
492 class ManagePasswordsBubbleView::ManageView
: public views::View
,
493 public views::ButtonListener
,
494 public views::LinkListener
{
496 explicit ManageView(ManagePasswordsBubbleView
* parent
);
497 ~ManageView() override
;
500 // views::ButtonListener:
501 void ButtonPressed(views::Button
* sender
, const ui::Event
& event
) override
;
503 // views::LinkListener:
504 void LinkClicked(views::Link
* source
, int event_flags
) override
;
506 ManagePasswordsBubbleView
* parent_
;
508 views::Link
* manage_link_
;
509 views::LabelButton
* done_button_
;
511 DISALLOW_COPY_AND_ASSIGN(ManageView
);
514 ManagePasswordsBubbleView::ManageView::ManageView(
515 ManagePasswordsBubbleView
* parent
)
517 views::GridLayout
* layout
= new views::GridLayout(this);
518 layout
->set_minimum_size(gfx::Size(kDesiredBubbleWidth
, 0));
519 SetLayoutManager(layout
);
522 BuildColumnSet(layout
, SINGLE_VIEW_COLUMN_SET
);
523 AddTitleRow(layout
, parent_
->model());
525 // If we have a list of passwords to store for the current site, display
526 // them to the user for management. Otherwise, render a "No passwords for
527 // this site" message.
528 if (!parent_
->model()->local_credentials().empty()) {
529 ManagePasswordItemsView
* item
= new ManagePasswordItemsView(
530 parent_
->model(), parent_
->model()->local_credentials().get());
531 layout
->StartRowWithPadding(0, SINGLE_VIEW_COLUMN_SET
, 0,
532 views::kUnrelatedControlVerticalSpacing
);
533 layout
->AddView(item
);
535 views::Label
* empty_label
= new views::Label(
536 l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_NO_PASSWORDS
));
537 empty_label
->SetMultiLine(true);
538 empty_label
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
539 empty_label
->SetFontList(
540 ui::ResourceBundle::GetSharedInstance().GetFontList(
541 ui::ResourceBundle::SmallFont
));
543 layout
->StartRowWithPadding(0, SINGLE_VIEW_COLUMN_SET
, 0,
544 views::kUnrelatedControlVerticalSpacing
);
545 layout
->AddView(empty_label
);
548 // Then add the "manage passwords" link and "Done" button.
549 manage_link_
= new views::Link(parent_
->model()->manage_link());
550 manage_link_
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
551 manage_link_
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
552 ui::ResourceBundle::SmallFont
));
553 manage_link_
->SetUnderline(false);
554 manage_link_
->set_listener(this);
557 new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_DONE
));
558 done_button_
->SetStyle(views::Button::STYLE_BUTTON
);
559 done_button_
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
560 ui::ResourceBundle::SmallFont
));
562 BuildColumnSet(layout
, LINK_BUTTON_COLUMN_SET
);
563 layout
->StartRowWithPadding(0, LINK_BUTTON_COLUMN_SET
, 0,
564 views::kUnrelatedControlVerticalSpacing
);
565 layout
->AddView(manage_link_
);
566 layout
->AddView(done_button_
);
568 // Extra padding for visual awesomeness.
569 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
571 parent_
->set_initially_focused_view(done_button_
);
574 ManagePasswordsBubbleView::ManageView::~ManageView() {
577 void ManagePasswordsBubbleView::ManageView::ButtonPressed(
578 views::Button
* sender
,
579 const ui::Event
& event
) {
580 DCHECK(sender
== done_button_
);
581 parent_
->model()->OnDoneClicked();
585 void ManagePasswordsBubbleView::ManageView::LinkClicked(views::Link
* source
,
587 DCHECK_EQ(source
, manage_link_
);
588 parent_
->model()->OnManageLinkClicked();
592 // ManagePasswordsBubbleView::SaveConfirmationView ----------------------------
594 // A view confirming to the user that a password was saved and offering a link
595 // to the Google account manager.
596 class ManagePasswordsBubbleView::SaveConfirmationView
597 : public views::View
,
598 public views::ButtonListener
,
599 public views::StyledLabelListener
{
601 explicit SaveConfirmationView(ManagePasswordsBubbleView
* parent
);
602 ~SaveConfirmationView() override
;
605 // views::ButtonListener:
606 void ButtonPressed(views::Button
* sender
, const ui::Event
& event
) override
;
608 // views::StyledLabelListener implementation
609 void StyledLabelLinkClicked(const gfx::Range
& range
,
610 int event_flags
) override
;
612 ManagePasswordsBubbleView
* parent_
;
613 views::LabelButton
* ok_button_
;
615 DISALLOW_COPY_AND_ASSIGN(SaveConfirmationView
);
618 ManagePasswordsBubbleView::SaveConfirmationView::SaveConfirmationView(
619 ManagePasswordsBubbleView
* parent
)
621 views::GridLayout
* layout
= new views::GridLayout(this);
622 layout
->set_minimum_size(gfx::Size(kDesiredBubbleWidth
, 0));
623 SetLayoutManager(layout
);
625 BuildColumnSet(layout
, SINGLE_VIEW_COLUMN_SET
);
626 AddTitleRow(layout
, parent_
->model());
628 views::StyledLabel
* confirmation
=
629 new views::StyledLabel(parent_
->model()->save_confirmation_text(), this);
630 confirmation
->SetBaseFontList(
631 ui::ResourceBundle::GetSharedInstance().GetFontList(
632 ui::ResourceBundle::SmallFont
));
633 confirmation
->AddStyleRange(
634 parent_
->model()->save_confirmation_link_range(),
635 views::StyledLabel::RangeStyleInfo::CreateForLink());
637 layout
->StartRow(0, SINGLE_VIEW_COLUMN_SET
);
638 layout
->AddView(confirmation
);
640 ok_button_
= new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_OK
));
641 ok_button_
->SetStyle(views::Button::STYLE_BUTTON
);
642 ok_button_
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
643 ui::ResourceBundle::SmallFont
));
645 BuildColumnSet(layout
, SINGLE_BUTTON_COLUMN_SET
);
646 layout
->StartRowWithPadding(
647 0, SINGLE_BUTTON_COLUMN_SET
, 0, views::kRelatedControlVerticalSpacing
);
648 layout
->AddView(ok_button_
);
650 // Extra padding for visual awesomeness.
651 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
653 parent_
->set_initially_focused_view(ok_button_
);
656 ManagePasswordsBubbleView::SaveConfirmationView::~SaveConfirmationView() {
659 void ManagePasswordsBubbleView::SaveConfirmationView::StyledLabelLinkClicked(
660 const gfx::Range
& range
, int event_flags
) {
661 DCHECK_EQ(range
, parent_
->model()->save_confirmation_link_range());
662 parent_
->model()->OnManageLinkClicked();
666 void ManagePasswordsBubbleView::SaveConfirmationView::ButtonPressed(
667 views::Button
* sender
, const ui::Event
& event
) {
668 DCHECK_EQ(sender
, ok_button_
);
669 parent_
->model()->OnOKClicked();
673 // ManagePasswordsBubbleView::WebContentMouseHandler --------------------------
675 // The class listens for WebContentsView events and notifies the bubble if the
676 // view was clicked on or received keystrokes.
677 class ManagePasswordsBubbleView::WebContentMouseHandler
678 : public ui::EventHandler
{
680 explicit WebContentMouseHandler(ManagePasswordsBubbleView
* bubble
);
682 void OnKeyEvent(ui::KeyEvent
* event
) override
;
683 void OnMouseEvent(ui::MouseEvent
* event
) override
;
684 void OnTouchEvent(ui::TouchEvent
* event
) override
;
687 ManagePasswordsBubbleView
* bubble_
;
688 scoped_ptr
<views::EventMonitor
> event_monitor_
;
690 DISALLOW_COPY_AND_ASSIGN(WebContentMouseHandler
);
693 ManagePasswordsBubbleView::WebContentMouseHandler::WebContentMouseHandler(
694 ManagePasswordsBubbleView
* bubble
)
696 content::WebContents
* web_contents
= bubble_
->web_contents();
697 DCHECK(web_contents
);
698 event_monitor_
= views::EventMonitor::CreateWindowMonitor(
699 this, web_contents
->GetTopLevelNativeWindow());
702 void ManagePasswordsBubbleView::WebContentMouseHandler::OnKeyEvent(
703 ui::KeyEvent
* event
) {
704 content::WebContents
* web_contents
= bubble_
->web_contents();
705 content::RenderViewHost
* rvh
= web_contents
->GetRenderViewHost();
706 if ((event
->key_code() == ui::VKEY_ESCAPE
||
707 rvh
->IsFocusedElementEditable()) && event
->type() == ui::ET_KEY_PRESSED
)
711 void ManagePasswordsBubbleView::WebContentMouseHandler::OnMouseEvent(
712 ui::MouseEvent
* event
) {
713 if (event
->type() == ui::ET_MOUSE_PRESSED
)
717 void ManagePasswordsBubbleView::WebContentMouseHandler::OnTouchEvent(
718 ui::TouchEvent
* event
) {
719 if (event
->type() == ui::ET_TOUCH_PRESSED
)
723 // ManagePasswordsBubbleView::UpdatePendingView -------------------------------
725 // A view offering the user the ability to update credentials. Contains a
726 // single ManagePasswordItemsView (in case of one credentials) or
727 // CredentialsSelectionView otherwise, along with a "Update Passwords" button
728 // and a rejection button.
729 class ManagePasswordsBubbleView::UpdatePendingView
730 : public views::View
,
731 public views::ButtonListener
,
732 public views::StyledLabelListener
{
734 explicit UpdatePendingView(ManagePasswordsBubbleView
* parent
);
735 ~UpdatePendingView() override
;
738 // views::ButtonListener:
739 void ButtonPressed(views::Button
* sender
, const ui::Event
& event
) override
;
741 // views::StyledLabelListener:
742 void StyledLabelLinkClicked(const gfx::Range
& range
,
743 int event_flags
) override
;
745 ManagePasswordsBubbleView
* parent_
;
747 CredentialsSelectionView
* selection_view_
;
749 views::BlueButton
* update_button_
;
751 views::LabelButton
* nope_button_
;
753 DISALLOW_COPY_AND_ASSIGN(UpdatePendingView
);
756 ManagePasswordsBubbleView::UpdatePendingView::UpdatePendingView(
757 ManagePasswordsBubbleView
* parent
)
758 : parent_(parent
), selection_view_(nullptr) {
759 views::GridLayout
* layout
= new views::GridLayout(this);
760 layout
->set_minimum_size(gfx::Size(kDesiredBubbleWidth
, 0));
761 SetLayoutManager(layout
);
763 // Create the pending credential item, update button.
764 View
* item
= nullptr;
765 if (parent
->model()->ShouldShowMultipleAccountUpdateUI()) {
766 selection_view_
= new CredentialsSelectionView(
767 parent
->model(), parent
->model()->local_credentials().get(),
768 parent
->model()->pending_password().username_value
);
769 item
= selection_view_
;
771 std::vector
<const autofill::PasswordForm
*> forms
;
772 forms
.push_back(&parent
->model()->pending_password());
773 item
= new ManagePasswordItemsView(parent_
->model(), forms
);
775 nope_button_
= new views::LabelButton(
776 this, l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_CANCEL_BUTTON
));
777 nope_button_
->SetStyle(views::Button::STYLE_BUTTON
);
778 nope_button_
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
779 ui::ResourceBundle::SmallFont
));
781 update_button_
= new views::BlueButton(
782 this, l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_UPDATE_BUTTON
));
783 update_button_
->SetFontList(
784 ui::ResourceBundle::GetSharedInstance().GetFontList(
785 ui::ResourceBundle::SmallFont
));
788 views::StyledLabel
* title_label
=
789 new views::StyledLabel(parent_
->model()->title(), this);
790 title_label
->SetBaseFontList(
791 ui::ResourceBundle::GetSharedInstance().GetFontList(
792 ui::ResourceBundle::MediumFont
));
793 if (!parent_
->model()->title_brand_link_range().is_empty()) {
794 title_label
->AddStyleRange(
795 parent_
->model()->title_brand_link_range(),
796 views::StyledLabel::RangeStyleInfo::CreateForLink());
798 BuildColumnSet(layout
, SINGLE_VIEW_COLUMN_SET
);
799 layout
->StartRow(0, SINGLE_VIEW_COLUMN_SET
);
800 layout
->AddView(title_label
);
801 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
804 layout
->StartRow(0, SINGLE_VIEW_COLUMN_SET
);
805 layout
->AddView(item
);
808 BuildColumnSet(layout
, DOUBLE_BUTTON_COLUMN_SET
);
809 layout
->StartRowWithPadding(0, DOUBLE_BUTTON_COLUMN_SET
, 0,
810 views::kUnrelatedControlVerticalSpacing
);
811 layout
->AddView(update_button_
);
812 layout
->AddView(nope_button_
);
814 // Extra padding for visual awesomeness.
815 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
817 parent_
->set_initially_focused_view(update_button_
);
820 ManagePasswordsBubbleView::UpdatePendingView::~UpdatePendingView() {}
822 void ManagePasswordsBubbleView::UpdatePendingView::ButtonPressed(
823 views::Button
* sender
,
824 const ui::Event
& event
) {
825 DCHECK(sender
== update_button_
|| sender
== nope_button_
);
826 if (sender
== update_button_
) {
827 if (selection_view_
) {
828 // Multi account case.
829 parent_
->model()->OnUpdateClicked(
830 *selection_view_
->GetSelectedCredentials());
832 parent_
->model()->OnUpdateClicked(parent_
->model()->pending_password());
835 parent_
->model()->OnNopeUpdateClicked();
840 void ManagePasswordsBubbleView::UpdatePendingView::StyledLabelLinkClicked(
841 const gfx::Range
& range
,
843 DCHECK_EQ(range
, parent_
->model()->title_brand_link_range());
844 parent_
->model()->OnBrandLinkClicked();
847 // ManagePasswordsBubbleView --------------------------------------------------
850 ManagePasswordsBubbleView
* ManagePasswordsBubbleView::manage_passwords_bubble_
=
854 void ManagePasswordsBubbleView::ShowBubble(content::WebContents
* web_contents
,
855 DisplayReason reason
) {
856 Browser
* browser
= chrome::FindBrowserWithWebContents(web_contents
);
858 DCHECK(browser
->window());
859 DCHECK(!manage_passwords_bubble_
||
860 !manage_passwords_bubble_
->GetWidget()->IsVisible());
862 BrowserView
* browser_view
= BrowserView::GetBrowserViewForBrowser(browser
);
863 bool is_fullscreen
= browser_view
->IsFullscreen();
864 ManagePasswordsIconView
* anchor_view
=
867 : browser_view
->GetLocationBarView()->manage_passwords_icon_view();
868 manage_passwords_bubble_
= new ManagePasswordsBubbleView(
869 web_contents
, anchor_view
, reason
);
872 manage_passwords_bubble_
->set_parent_window(web_contents
->GetNativeView());
874 views::BubbleDelegateView::CreateBubble(manage_passwords_bubble_
);
876 // Adjust for fullscreen after creation as it relies on the content size.
878 manage_passwords_bubble_
->AdjustForFullscreen(
879 browser_view
->GetBoundsInScreen());
881 if (reason
== AUTOMATIC
)
882 manage_passwords_bubble_
->GetWidget()->ShowInactive();
884 manage_passwords_bubble_
->GetWidget()->Show();
888 void ManagePasswordsBubbleView::CloseBubble() {
889 if (manage_passwords_bubble_
)
890 manage_passwords_bubble_
->Close();
894 void ManagePasswordsBubbleView::ActivateBubble() {
895 DCHECK(manage_passwords_bubble_
);
896 DCHECK(manage_passwords_bubble_
->GetWidget()->IsVisible());
897 manage_passwords_bubble_
->GetWidget()->Activate();
900 content::WebContents
* ManagePasswordsBubbleView::web_contents() const {
901 return model()->web_contents();
904 ManagePasswordsBubbleView::ManagePasswordsBubbleView(
905 content::WebContents
* web_contents
,
906 ManagePasswordsIconView
* anchor_view
,
907 DisplayReason reason
)
908 : ManagePasswordsBubble(web_contents
, reason
),
909 ManagedFullScreenBubbleDelegateView(anchor_view
, web_contents
),
910 anchor_view_(anchor_view
),
911 initially_focused_view_(NULL
) {
912 // Compensate for built-in vertical padding in the anchor view's image.
913 set_anchor_view_insets(gfx::Insets(5, 0, 5, 0));
915 anchor_view
->SetActive(true);
916 mouse_handler_
.reset(new WebContentMouseHandler(this));
919 ManagePasswordsBubbleView::~ManagePasswordsBubbleView() {
920 if (manage_passwords_bubble_
== this)
921 manage_passwords_bubble_
= NULL
;
924 views::View
* ManagePasswordsBubbleView::GetInitiallyFocusedView() {
925 return initially_focused_view_
;
928 void ManagePasswordsBubbleView::Init() {
929 views::FillLayout
* layout
= new views::FillLayout();
930 SetLayoutManager(layout
);
935 void ManagePasswordsBubbleView::Close() {
936 mouse_handler_
.reset();
937 ManagedFullScreenBubbleDelegateView::Close();
940 void ManagePasswordsBubbleView::OnWidgetClosing(views::Widget
* /*widget*/) {
942 anchor_view_
->SetActive(false);
945 bool ManagePasswordsBubbleView::ShouldShowCloseButton() const {
946 return model()->state() == password_manager::ui::PENDING_PASSWORD_STATE
;
949 void ManagePasswordsBubbleView::Refresh() {
950 RemoveAllChildViews(true);
951 initially_focused_view_
= NULL
;
952 if (model()->state() == password_manager::ui::PENDING_PASSWORD_STATE
) {
953 AddChildView(new PendingView(this));
954 } else if (model()->state() ==
955 password_manager::ui::PENDING_PASSWORD_UPDATE_STATE
) {
956 AddChildView(new UpdatePendingView(this));
957 } else if (model()->state() == password_manager::ui::CONFIRMATION_STATE
) {
958 AddChildView(new SaveConfirmationView(this));
959 } else if (model()->state() ==
960 password_manager::ui::CREDENTIAL_REQUEST_STATE
) {
961 AddChildView(new AccountChooserView(this));
962 } else if (model()->state() == password_manager::ui::AUTO_SIGNIN_STATE
) {
963 AddChildView(new AutoSigninView(this));
965 AddChildView(new ManageView(this));
967 GetLayoutManager()->Layout(this);