Popular sites on the NTP: check that experiment group StartsWith (rather than IS...
[chromium-blink-merge.git] / chrome / browser / ui / views / passwords / manage_password_items_view.cc
blob82703802b3a1c2cae1f18a8720428c607194da37
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"
7 #include <numeric>
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"
25 namespace {
27 enum ColumnSets {
28 ONE_COLUMN_SET,
29 TWO_COLUMN_SET,
30 THREE_COLUMN_SET
33 void BuildColumnSetIfNeeded(views::GridLayout* layout, int column_set_id) {
34 if (layout->GetColumnSet(column_set_id))
35 return;
36 views::ColumnSet* column_set = layout->AddColumnSet(column_set_id);
38 // The username/"Deleted!"/Border field.
39 column_set->AddPaddingColumn(0, views::kItemLabelSpacing);
40 column_set->AddColumn(views::GridLayout::FILL,
41 views::GridLayout::FILL,
43 views::GridLayout::USE_PREF,
45 0);
46 if (column_set_id >= TWO_COLUMN_SET) {
47 // The password/"Undo!" field.
48 column_set->AddPaddingColumn(0, views::kItemLabelSpacing);
49 column_set->AddColumn(views::GridLayout::FILL,
50 views::GridLayout::FILL,
52 views::GridLayout::USE_PREF,
54 0);
56 // If we're in manage-mode, we need another column for the delete button.
57 if (column_set_id == THREE_COLUMN_SET) {
58 column_set->AddPaddingColumn(0, views::kItemLabelSpacing);
59 column_set->AddColumn(views::GridLayout::TRAILING,
60 views::GridLayout::FILL,
62 views::GridLayout::USE_PREF,
64 0);
66 column_set->AddPaddingColumn(0, views::kItemLabelSpacing);
69 scoped_ptr<views::Label> GenerateUsernameLabel(
70 const autofill::PasswordForm& form) {
71 scoped_ptr<views::Label> label(new views::Label(
72 form.username_value.empty()
73 ? l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_EMPTY_LOGIN)
74 : form.username_value));
75 label->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
76 ui::ResourceBundle::SmallFont));
77 label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
78 return label.Pass();
81 scoped_ptr<views::Label> GeneratePasswordLabel(
82 const autofill::PasswordForm& form) {
83 base::string16 text = form.federation_url.is_empty()
84 ? form.password_value
85 : l10n_util::GetStringFUTF16(
86 IDS_PASSWORDS_VIA_FEDERATION,
87 base::UTF8ToUTF16(form.federation_url.host()));
88 scoped_ptr<views::Label> label(new views::Label(text));
89 label->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
90 ui::ResourceBundle::SmallFont));
91 label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
92 if (form.federation_url.is_empty())
93 label->SetObscured(true);
94 return label.Pass();
97 scoped_ptr<views::ImageButton> GenerateDeleteButton(
98 views::ButtonListener* listener) {
99 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
100 scoped_ptr<views::ImageButton> button(new views::ImageButton(listener));
101 button->SetImage(views::ImageButton::STATE_NORMAL,
102 rb->GetImageNamed(IDR_CLOSE_2).ToImageSkia());
103 button->SetImage(views::ImageButton::STATE_HOVERED,
104 rb->GetImageNamed(IDR_CLOSE_2_H).ToImageSkia());
105 button->SetImage(views::ImageButton::STATE_PRESSED,
106 rb->GetImageNamed(IDR_CLOSE_2_P).ToImageSkia());
107 return button.Pass();
110 scoped_ptr<views::Label> GenerateDeletedPasswordLabel() {
111 scoped_ptr<views::Label> text(new views::Label(
112 l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_DELETED)));
113 text->SetHorizontalAlignment(gfx::ALIGN_LEFT);
114 text->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
115 ui::ResourceBundle::SmallFont));
116 return text.Pass();
119 scoped_ptr<views::Link> GenerateUndoLink(views::LinkListener* listener) {
120 scoped_ptr<views::Link> undo_link(new views::Link(
121 l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_UNDO)));
122 undo_link->SetHorizontalAlignment(gfx::ALIGN_RIGHT);
123 undo_link->set_listener(listener);
124 undo_link->SetUnderline(false);
125 undo_link->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
126 ui::ResourceBundle::SmallFont));
127 return undo_link.Pass();
130 } // namespace
132 // Manage credentials: stores credentials state and adds proper row to layout
133 // based on credential state.
134 class ManagePasswordItemsView::PasswordFormRow : public views::ButtonListener,
135 public views::LinkListener {
136 public:
137 PasswordFormRow(ManagePasswordItemsView* host,
138 const autofill::PasswordForm* password_form,
139 int fixed_height);
140 ~PasswordFormRow() override = default;
142 void AddRow(views::GridLayout* layout);
144 // Returns the fixed height for a row excluding padding. 0 means no fixed
145 // height required.
146 // In MANAGE_STATE a row may represent a credential or a deleted credential.
147 // To avoid repositioning all the rows should have a fixed height.
148 static int GetFixedHeight(password_manager::ui::State state);
150 private:
151 void AddCredentialsRow(views::GridLayout* layout);
152 void AddUndoRow(views::GridLayout* layout);
154 // views::ButtonListener:
155 void ButtonPressed(views::Button* sender, const ui::Event& event) override;
156 // views::LinkListener:
157 void LinkClicked(views::Link* source, int event_flags) override;
159 void ResetControls();
161 ManagePasswordItemsView* host_;
162 const autofill::PasswordForm* password_form_;
163 // The UI elements pointers are weak and owned by their parent.
164 views::Link* undo_link_;
165 views::ImageButton* delete_button_;
166 const int fixed_height_;
167 bool deleted_;
169 DISALLOW_COPY_AND_ASSIGN(PasswordFormRow);
172 ManagePasswordItemsView::PasswordFormRow::PasswordFormRow(
173 ManagePasswordItemsView* host,
174 const autofill::PasswordForm* password_form,
175 int fixed_height)
176 : host_(host),
177 password_form_(password_form),
178 undo_link_(nullptr),
179 delete_button_(nullptr),
180 fixed_height_(fixed_height),
181 deleted_(false) {}
183 void ManagePasswordItemsView::PasswordFormRow::AddRow(
184 views::GridLayout* layout) {
185 if (deleted_) {
186 AddUndoRow(layout);
187 } else {
188 AddCredentialsRow(layout);
192 int ManagePasswordItemsView::PasswordFormRow::GetFixedHeight(
193 password_manager::ui::State state) {
194 if (state != password_manager::ui::MANAGE_STATE)
195 return 0;
196 scoped_ptr<views::ImageButton> delete_button(GenerateDeleteButton(nullptr));
197 scoped_ptr<views::Link> link(GenerateUndoLink(nullptr));
198 scoped_ptr<views::Label> label(GenerateDeletedPasswordLabel());
199 views::View* row_views[] = {delete_button.get(), link.get(), label.get()};
200 return std::accumulate(row_views, row_views + arraysize(row_views), 0,
201 [](int max_height, const views::View* view) {
202 return std::max(max_height, view->GetPreferredSize().height());
206 void ManagePasswordItemsView::PasswordFormRow::AddCredentialsRow(
207 views::GridLayout* layout) {
208 ResetControls();
209 int column_set_id =
210 host_->model_->state() == password_manager::ui::MANAGE_STATE
211 ? THREE_COLUMN_SET
212 : TWO_COLUMN_SET;
213 BuildColumnSetIfNeeded(layout, column_set_id);
214 layout->StartRowWithPadding(0, column_set_id, 0,
215 views::kRelatedControlVerticalSpacing);
216 layout->AddView(GenerateUsernameLabel(*password_form_).release(), 1, 1,
217 views::GridLayout::FILL, views::GridLayout::FILL,
218 0, fixed_height_);
219 layout->AddView(GeneratePasswordLabel(*password_form_).release(), 1, 1,
220 views::GridLayout::FILL, views::GridLayout::FILL,
221 0, fixed_height_);
222 if (column_set_id == THREE_COLUMN_SET) {
223 delete_button_ = GenerateDeleteButton(this).release();
224 layout->AddView(delete_button_, 1, 1,
225 views::GridLayout::TRAILING, views::GridLayout::FILL,
226 0, fixed_height_);
230 void ManagePasswordItemsView::PasswordFormRow::AddUndoRow(
231 views::GridLayout* layout) {
232 ResetControls();
233 scoped_ptr<views::Label> text = GenerateDeletedPasswordLabel();
234 scoped_ptr<views::Link> undo_link = GenerateUndoLink(this);
235 undo_link_ = undo_link.get();
236 BuildColumnSetIfNeeded(layout, TWO_COLUMN_SET);
237 layout->StartRowWithPadding(0, TWO_COLUMN_SET, 0,
238 views::kRelatedControlVerticalSpacing);
239 layout->AddView(text.release(), 1, 1,
240 views::GridLayout::FILL, views::GridLayout::FILL,
241 0, fixed_height_);
242 layout->AddView(undo_link.release(), 1, 1,
243 views::GridLayout::FILL, views::GridLayout::FILL,
244 0, fixed_height_);
247 void ManagePasswordItemsView::PasswordFormRow::ButtonPressed(
248 views::Button* sender, const ui::Event& event) {
249 DCHECK_EQ(delete_button_, sender);
250 deleted_ = true;
251 host_->NotifyPasswordFormStatusChanged(*password_form_, deleted_);
254 void ManagePasswordItemsView::PasswordFormRow::LinkClicked(views::Link* sender,
255 int event_flags) {
256 DCHECK_EQ(undo_link_, sender);
257 deleted_ = false;
258 host_->NotifyPasswordFormStatusChanged(*password_form_, deleted_);
261 void ManagePasswordItemsView::PasswordFormRow::ResetControls() {
262 delete_button_ = nullptr;
263 undo_link_ = nullptr;
266 // ManagePasswordItemsView
267 ManagePasswordItemsView::ManagePasswordItemsView(
268 ManagePasswordsBubbleModel* manage_passwords_bubble_model,
269 const std::vector<const autofill::PasswordForm*>& password_forms)
270 : model_(manage_passwords_bubble_model) {
271 int fixed_height = PasswordFormRow::GetFixedHeight(model_->state());
272 for (const autofill::PasswordForm* password_form : password_forms) {
273 password_forms_rows_.push_back(
274 new PasswordFormRow(this, password_form, fixed_height));
276 AddRows();
279 ManagePasswordItemsView::~ManagePasswordItemsView() = default;
281 void ManagePasswordItemsView::AddRows() {
282 views::GridLayout* layout = new views::GridLayout(this);
283 SetLayoutManager(layout);
284 for (auto* row : password_forms_rows_) {
285 row->AddRow(layout);
287 GetLayoutManager()->Layout(this);
290 void ManagePasswordItemsView::NotifyPasswordFormStatusChanged(
291 const autofill::PasswordForm& password_form, bool deleted) {
292 Refresh();
293 // After the view is consistent, notify the model that the password needs to
294 // be updated (either removed or put back into the store, as appropriate.
295 model_->OnPasswordAction(password_form,
296 deleted
297 ? ManagePasswordsBubbleModel::REMOVE_PASSWORD
298 : ManagePasswordsBubbleModel::ADD_PASSWORD);
301 void ManagePasswordItemsView::Refresh() {
302 DCHECK_NE(password_manager::ui::PENDING_PASSWORD_STATE, model_->state());
303 RemoveAllChildViews(true);
304 AddRows();