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_password_items_view.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "chrome/browser/ui/passwords/manage_passwords_bubble_model.h"
11 #include "chrome/grit/generated_resources.h"
12 #include "grit/components_strings.h"
13 #include "ui/base/l10n/l10n_util.h"
14 #include "ui/base/resource/resource_bundle.h"
15 #include "ui/resources/grit/ui_resources.h"
16 #include "ui/views/controls/button/button.h"
17 #include "ui/views/controls/button/image_button.h"
18 #include "ui/views/controls/label.h"
19 #include "ui/views/controls/link.h"
20 #include "ui/views/controls/link_listener.h"
21 #include "ui/views/layout/fill_layout.h"
22 #include "ui/views/layout/grid_layout.h"
23 #include "ui/views/layout/layout_constants.h"
33 void BuildColumnSetIfNeeded(views::GridLayout
* layout
, int column_set_id
) {
34 if (layout
->GetColumnSet(column_set_id
))
36 views::ColumnSet
* column_set
= layout
->AddColumnSet(column_set_id
);
38 // The username/"Deleted!"/Border field.
39 column_set
->AddColumn(views::GridLayout::FILL
,
40 views::GridLayout::FILL
,
42 views::GridLayout::USE_PREF
,
45 if (column_set_id
>= TWO_COLUMN_SET
) {
46 // The password/"Undo!" field.
47 column_set
->AddPaddingColumn(0, views::kItemLabelSpacing
);
48 column_set
->AddColumn(views::GridLayout::FILL
,
49 views::GridLayout::FILL
,
51 views::GridLayout::USE_PREF
,
55 // If we're in manage-mode, we need another column for the delete button.
56 if (column_set_id
== THREE_COLUMN_SET
) {
57 column_set
->AddPaddingColumn(0, views::kItemLabelSpacing
);
58 column_set
->AddColumn(views::GridLayout::TRAILING
,
59 views::GridLayout::FILL
,
61 views::GridLayout::USE_PREF
,
67 scoped_ptr
<views::Label
> GenerateUsernameLabel(
68 const autofill::PasswordForm
& form
) {
69 scoped_ptr
<views::Label
> label(new views::Label(
70 form
.username_value
.empty()
71 ? l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_EMPTY_LOGIN
)
72 : form
.username_value
));
73 label
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
74 ui::ResourceBundle::SmallFont
));
75 label
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
79 scoped_ptr
<views::Label
> GeneratePasswordLabel(
80 const autofill::PasswordForm
& form
) {
81 base::string16 text
= form
.federation_url
.is_empty()
83 : l10n_util::GetStringFUTF16(
84 IDS_PASSWORDS_VIA_FEDERATION
,
85 base::UTF8ToUTF16(form
.federation_url
.host()));
86 scoped_ptr
<views::Label
> label(new views::Label(text
));
87 label
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
88 ui::ResourceBundle::SmallFont
));
89 label
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
90 if (form
.federation_url
.is_empty())
91 label
->SetObscured(true);
95 scoped_ptr
<views::ImageButton
> GenerateDeleteButton(
96 views::ButtonListener
* listener
) {
97 ui::ResourceBundle
* rb
= &ui::ResourceBundle::GetSharedInstance();
98 scoped_ptr
<views::ImageButton
> button(new views::ImageButton(listener
));
99 button
->SetImage(views::ImageButton::STATE_NORMAL
,
100 rb
->GetImageNamed(IDR_CLOSE_2
).ToImageSkia());
101 button
->SetImage(views::ImageButton::STATE_HOVERED
,
102 rb
->GetImageNamed(IDR_CLOSE_2_H
).ToImageSkia());
103 button
->SetImage(views::ImageButton::STATE_PRESSED
,
104 rb
->GetImageNamed(IDR_CLOSE_2_P
).ToImageSkia());
105 return button
.Pass();
108 scoped_ptr
<views::Label
> GenerateDeletedPasswordLabel() {
109 scoped_ptr
<views::Label
> text(new views::Label(
110 l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_DELETED
)));
111 text
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
112 text
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
113 ui::ResourceBundle::SmallFont
));
117 scoped_ptr
<views::Link
> GenerateUndoLink(views::LinkListener
* listener
) {
118 scoped_ptr
<views::Link
> undo_link(new views::Link(
119 l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_UNDO
)));
120 undo_link
->SetHorizontalAlignment(gfx::ALIGN_RIGHT
);
121 undo_link
->set_listener(listener
);
122 undo_link
->SetUnderline(false);
123 undo_link
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
124 ui::ResourceBundle::SmallFont
));
125 return undo_link
.Pass();
130 // Manage credentials: stores credentials state and adds proper row to layout
131 // based on credential state.
132 class ManagePasswordItemsView::PasswordFormRow
: public views::ButtonListener
,
133 public views::LinkListener
{
135 PasswordFormRow(ManagePasswordItemsView
* host
,
136 const autofill::PasswordForm
* password_form
,
138 ~PasswordFormRow() override
= default;
140 void AddRow(views::GridLayout
* layout
);
142 // Returns the fixed height for a row excluding padding. 0 means no fixed
144 // In MANAGE_STATE a row may represent a credential or a deleted credential.
145 // To avoid repositioning all the rows should have a fixed height.
146 static int GetFixedHeight(password_manager::ui::State state
);
149 void AddCredentialsRow(views::GridLayout
* layout
);
150 void AddUndoRow(views::GridLayout
* layout
);
152 // views::ButtonListener:
153 void ButtonPressed(views::Button
* sender
, const ui::Event
& event
) override
;
154 // views::LinkListener:
155 void LinkClicked(views::Link
* source
, int event_flags
) override
;
157 void ResetControls();
159 ManagePasswordItemsView
* host_
;
160 const autofill::PasswordForm
* password_form_
;
161 // The UI elements pointers are weak and owned by their parent.
162 views::Link
* undo_link_
;
163 views::ImageButton
* delete_button_
;
164 const int fixed_height_
;
167 DISALLOW_COPY_AND_ASSIGN(PasswordFormRow
);
170 ManagePasswordItemsView::PasswordFormRow::PasswordFormRow(
171 ManagePasswordItemsView
* host
,
172 const autofill::PasswordForm
* password_form
,
175 password_form_(password_form
),
177 delete_button_(nullptr),
178 fixed_height_(fixed_height
),
181 void ManagePasswordItemsView::PasswordFormRow::AddRow(
182 views::GridLayout
* layout
) {
186 AddCredentialsRow(layout
);
190 int ManagePasswordItemsView::PasswordFormRow::GetFixedHeight(
191 password_manager::ui::State state
) {
192 if (state
!= password_manager::ui::MANAGE_STATE
)
194 scoped_ptr
<views::ImageButton
> delete_button(GenerateDeleteButton(nullptr));
195 scoped_ptr
<views::Link
> link(GenerateUndoLink(nullptr));
196 scoped_ptr
<views::Label
> label(GenerateDeletedPasswordLabel());
197 views::View
* row_views
[] = {delete_button
.get(), link
.get(), label
.get()};
198 return std::accumulate(row_views
, row_views
+ arraysize(row_views
), 0,
199 [](int max_height
, const views::View
* view
) {
200 return std::max(max_height
, view
->GetPreferredSize().height());
204 void ManagePasswordItemsView::PasswordFormRow::AddCredentialsRow(
205 views::GridLayout
* layout
) {
208 host_
->model_
->state() == password_manager::ui::MANAGE_STATE
211 BuildColumnSetIfNeeded(layout
, column_set_id
);
212 layout
->StartRow(0, column_set_id
);
213 layout
->AddView(GenerateUsernameLabel(*password_form_
).release(), 1, 1,
214 views::GridLayout::FILL
, views::GridLayout::FILL
,
216 layout
->AddView(GeneratePasswordLabel(*password_form_
).release(), 1, 1,
217 views::GridLayout::FILL
, views::GridLayout::FILL
,
219 if (column_set_id
== THREE_COLUMN_SET
) {
220 delete_button_
= GenerateDeleteButton(this).release();
221 layout
->AddView(delete_button_
, 1, 1,
222 views::GridLayout::TRAILING
, views::GridLayout::FILL
,
227 void ManagePasswordItemsView::PasswordFormRow::AddUndoRow(
228 views::GridLayout
* layout
) {
230 scoped_ptr
<views::Label
> text
= GenerateDeletedPasswordLabel();
231 scoped_ptr
<views::Link
> undo_link
= GenerateUndoLink(this);
232 undo_link_
= undo_link
.get();
233 BuildColumnSetIfNeeded(layout
, TWO_COLUMN_SET
);
234 layout
->StartRow(0, TWO_COLUMN_SET
);
235 layout
->AddView(text
.release(), 1, 1,
236 views::GridLayout::FILL
, views::GridLayout::FILL
,
238 layout
->AddView(undo_link
.release(), 1, 1,
239 views::GridLayout::FILL
, views::GridLayout::FILL
,
243 void ManagePasswordItemsView::PasswordFormRow::ButtonPressed(
244 views::Button
* sender
, const ui::Event
& event
) {
245 DCHECK_EQ(delete_button_
, sender
);
247 host_
->NotifyPasswordFormStatusChanged(*password_form_
, deleted_
);
250 void ManagePasswordItemsView::PasswordFormRow::LinkClicked(views::Link
* sender
,
252 DCHECK_EQ(undo_link_
, sender
);
254 host_
->NotifyPasswordFormStatusChanged(*password_form_
, deleted_
);
257 void ManagePasswordItemsView::PasswordFormRow::ResetControls() {
258 delete_button_
= nullptr;
259 undo_link_
= nullptr;
262 // ManagePasswordItemsView
263 ManagePasswordItemsView::ManagePasswordItemsView(
264 ManagePasswordsBubbleModel
* manage_passwords_bubble_model
,
265 const std::vector
<const autofill::PasswordForm
*>& password_forms
)
266 : model_(manage_passwords_bubble_model
) {
267 int fixed_height
= PasswordFormRow::GetFixedHeight(model_
->state());
268 for (const autofill::PasswordForm
* password_form
: password_forms
) {
269 password_forms_rows_
.push_back(
270 new PasswordFormRow(this, password_form
, fixed_height
));
275 ManagePasswordItemsView::~ManagePasswordItemsView() = default;
277 void ManagePasswordItemsView::AddRows() {
278 views::GridLayout
* layout
= new views::GridLayout(this);
279 SetLayoutManager(layout
);
280 for (auto* row
: password_forms_rows_
) {
281 if (row
!= password_forms_rows_
[0])
282 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
285 GetLayoutManager()->Layout(this);
288 void ManagePasswordItemsView::NotifyPasswordFormStatusChanged(
289 const autofill::PasswordForm
& password_form
, bool deleted
) {
291 // After the view is consistent, notify the model that the password needs to
292 // be updated (either removed or put back into the store, as appropriate.
293 model_
->OnPasswordAction(password_form
,
295 ? ManagePasswordsBubbleModel::REMOVE_PASSWORD
296 : ManagePasswordsBubbleModel::ADD_PASSWORD
);
299 void ManagePasswordItemsView::Refresh() {
300 DCHECK_NE(password_manager::ui::PENDING_PASSWORD_STATE
, model_
->state());
301 RemoveAllChildViews(true);