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/chrome_notification_types.h"
8 #include "chrome/browser/ui/browser.h"
9 #include "chrome/browser/ui/browser_finder.h"
10 #include "chrome/browser/ui/browser_window.h"
11 #include "chrome/browser/ui/passwords/manage_passwords_bubble_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 "content/public/browser/notification_source.h"
17 #include "content/public/browser/web_contents_view.h"
18 #include "grit/generated_resources.h"
19 #include "ui/base/l10n/l10n_util.h"
20 #include "ui/gfx/text_utils.h"
21 #include "ui/views/controls/button/blue_button.h"
22 #include "ui/views/controls/button/label_button.h"
23 #include "ui/views/layout/grid_layout.h"
24 #include "ui/views/layout/layout_constants.h"
27 // Helpers --------------------------------------------------------------------
31 // Updates either the biggest possible width for the username field in the
32 // manage passwords bubble or the biggest possible width for the password field.
33 void UpdateBiggestWidth(const autofill::PasswordForm
& password_form
,
36 const gfx::FontList font_list
;
37 base::string16
display_string(username
?
38 password_form
.username_value
:
39 ManagePasswordItemView::GetPasswordDisplayString(
40 password_form
.password_value
));
41 *biggest_width
= std::max(gfx::GetStringWidth(display_string
, font_list
),
48 // ManagePasswordsBubbleView --------------------------------------------------
51 ManagePasswordsBubbleView
* ManagePasswordsBubbleView::manage_passwords_bubble_
=
55 void ManagePasswordsBubbleView::ShowBubble(content::WebContents
* web_contents
,
56 ManagePasswordsIconView
* icon_view
) {
57 Browser
* browser
= chrome::FindBrowserWithWebContents(web_contents
);
59 DCHECK(browser
->window());
60 DCHECK(browser
->fullscreen_controller());
63 BrowserView
* browser_view
= BrowserView::GetBrowserViewForBrowser(browser
);
64 bool is_fullscreen
= browser_view
->IsFullscreen();
65 views::View
* anchor_view
= is_fullscreen
?
66 NULL
: browser_view
->GetLocationBarView()->manage_passwords_icon_view();
67 manage_passwords_bubble_
=
68 new ManagePasswordsBubbleView(web_contents
, anchor_view
, icon_view
);
71 manage_passwords_bubble_
->set_parent_window(
72 web_contents
->GetView()->GetTopLevelNativeWindow());
75 views::BubbleDelegateView::CreateBubble(manage_passwords_bubble_
);
77 // Adjust for fullscreen after creation as it relies on the content size.
79 manage_passwords_bubble_
->AdjustForFullscreen(
80 browser_view
->GetBoundsInScreen());
83 manage_passwords_bubble_
->GetWidget()->Show();
87 void ManagePasswordsBubbleView::CloseBubble() {
88 if (manage_passwords_bubble_
)
89 manage_passwords_bubble_
->Close();
93 bool ManagePasswordsBubbleView::IsShowing() {
94 // The bubble may be in the process of closing.
95 return (manage_passwords_bubble_
!= NULL
) &&
96 manage_passwords_bubble_
->GetWidget()->IsVisible();
99 ManagePasswordsBubbleView::ManagePasswordsBubbleView(
100 content::WebContents
* web_contents
,
101 views::View
* anchor_view
,
102 ManagePasswordsIconView
* icon_view
)
103 : BubbleDelegateView(
106 views::BubbleBorder::TOP_RIGHT
: views::BubbleBorder::NONE
),
107 manage_passwords_bubble_model_(
108 new ManagePasswordsBubbleModel(web_contents
)),
109 icon_view_(icon_view
) {
110 // Compensate for built-in vertical padding in the anchor view's image.
111 set_anchor_view_insets(gfx::Insets(5, 0, 5, 0));
112 set_notify_enter_exit_on_child(true);
115 ManagePasswordsBubbleView::~ManagePasswordsBubbleView() {}
117 int ManagePasswordsBubbleView::GetMaximumUsernameOrPasswordWidth(
119 int biggest_width
= 0;
120 if (manage_passwords_bubble_model_
->manage_passwords_bubble_state() !=
121 ManagePasswordsBubbleModel::PASSWORD_TO_BE_SAVED
) {
122 // If we are in the PASSWORD_TO_BE_SAVED state we only display the
123 // password that was just submitted and should not take these into account.
124 for (autofill::PasswordFormMap::const_iterator
i(
125 manage_passwords_bubble_model_
->best_matches().begin());
126 i
!= manage_passwords_bubble_model_
->best_matches().end(); ++i
) {
127 UpdateBiggestWidth((*i
->second
), username
, &biggest_width
);
130 if (manage_passwords_bubble_model_
->password_submitted()) {
131 UpdateBiggestWidth(manage_passwords_bubble_model_
->pending_credentials(),
132 username
, &biggest_width
);
134 return biggest_width
;
137 void ManagePasswordsBubbleView::AdjustForFullscreen(
138 const gfx::Rect
& screen_bounds
) {
142 // The bubble's padding from the screen edge, used in fullscreen.
143 const int kFullscreenPaddingEnd
= 20;
144 const size_t bubble_half_width
= width() / 2;
145 const int x_pos
= base::i18n::IsRTL() ?
146 screen_bounds
.x() + bubble_half_width
+ kFullscreenPaddingEnd
:
147 screen_bounds
.right() - bubble_half_width
- kFullscreenPaddingEnd
;
148 SetAnchorRect(gfx::Rect(x_pos
, screen_bounds
.y(), 0, 0));
151 void ManagePasswordsBubbleView::Close() {
152 GetWidget()->Close();
155 void ManagePasswordsBubbleView::Init() {
156 using views::GridLayout
;
158 GridLayout
* layout
= new GridLayout(this);
159 SetLayoutManager(layout
);
161 // This calculates the necessary widths for the list of credentials in the
162 // bubble. We do not need to clamp the password field width because
163 // ManagePasswordItemView::GetPasswordFisplayString() does this.
165 const int predefined_username_field_max_width
=
166 gfx::FontList().GetExpectedTextWidth(22);
167 const int max_username_or_password_width
=
168 std::min(GetMaximumUsernameOrPasswordWidth(true),
169 predefined_username_field_max_width
);
170 const int first_field_width
= std::max(max_username_or_password_width
,
171 views::Label(l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_DELETED
)).
172 GetPreferredSize().width());
174 const int second_field_width
= std::max(
175 GetMaximumUsernameOrPasswordWidth(false),
176 views::Label(l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_UNDO
)).
177 GetPreferredSize().width());
179 const int kSingleColumnSetId
= 0;
180 views::ColumnSet
* column_set
= layout
->AddColumnSet(kSingleColumnSetId
);
181 column_set
->AddPaddingColumn(0, views::kPanelHorizMargin
);
182 column_set
->AddColumn(GridLayout::LEADING
, GridLayout::FILL
, 0,
183 GridLayout::USE_PREF
, 0, 0);
184 column_set
->AddPaddingColumn(0, views::kPanelHorizMargin
);
186 ui::ResourceBundle
* rb
= &ui::ResourceBundle::GetSharedInstance();
187 views::Label
* title_label
=
188 new views::Label(manage_passwords_bubble_model_
->title());
189 title_label
->SetMultiLine(true);
190 title_label
->SetFontList(rb
->GetFontList(ui::ResourceBundle::MediumFont
));
192 layout
->StartRowWithPadding(0, kSingleColumnSetId
,
193 0, views::kRelatedControlSmallVerticalSpacing
);
194 layout
->AddView(title_label
);
195 layout
->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing
);
197 if (manage_passwords_bubble_model_
->manage_passwords_bubble_state() ==
198 ManagePasswordsBubbleModel::PASSWORD_TO_BE_SAVED
) {
199 const int kSingleColumnCredentialsId
= 1;
200 views::ColumnSet
* single_column
=
201 layout
->AddColumnSet(kSingleColumnCredentialsId
);
202 single_column
->AddPaddingColumn(0, views::kPanelHorizMargin
);
203 single_column
->AddColumn(GridLayout::FILL
, GridLayout::FILL
, 1,
204 GridLayout::USE_PREF
, 0, 0);
205 single_column
->AddPaddingColumn(0, views::kPanelHorizMargin
);
207 layout
->StartRow(0, kSingleColumnCredentialsId
);
208 ManagePasswordItemView
* item
= new ManagePasswordItemView(
209 manage_passwords_bubble_model_
,
210 manage_passwords_bubble_model_
->pending_credentials(),
211 first_field_width
, second_field_width
);
212 item
->SetBorder(views::Border::CreateSolidSidedBorder(
217 GetNativeTheme()->GetSystemColor(
218 ui::NativeTheme::kColorId_EnabledMenuButtonBorderColor
)));
219 layout
->AddView(item
);
221 const int kDoubleColumnSetId
= 2;
222 views::ColumnSet
* double_column_set
=
223 layout
->AddColumnSet(kDoubleColumnSetId
);
224 double_column_set
->AddPaddingColumn(0, views::kPanelHorizMargin
);
225 double_column_set
->AddColumn(GridLayout::TRAILING
, GridLayout::CENTER
, 1,
226 GridLayout::USE_PREF
, 0, 0);
227 double_column_set
->AddPaddingColumn(0, views::kRelatedButtonHSpacing
);
228 double_column_set
->AddColumn(GridLayout::TRAILING
, GridLayout::CENTER
, 0,
229 GridLayout::USE_PREF
, 0, 0);
230 double_column_set
->AddPaddingColumn(0, views::kPanelHorizMargin
);
232 cancel_button_
= new views::LabelButton(
233 this, l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_CANCEL_BUTTON
));
234 cancel_button_
->SetStyle(views::Button::STYLE_BUTTON
);
235 save_button_
= new views::BlueButton(
236 this, l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_SAVE_BUTTON
));
238 layout
->StartRowWithPadding(0, kDoubleColumnSetId
,
239 0, views::kRelatedControlVerticalSpacing
);
240 layout
->AddView(save_button_
);
241 layout
->AddView(cancel_button_
);
242 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
244 const int kSingleButtonSetId
= 3;
245 views::ColumnSet
* single_column_set
=
246 layout
->AddColumnSet(kSingleButtonSetId
);
247 single_column_set
->AddPaddingColumn(0, views::kPanelHorizMargin
);
248 single_column_set
->AddColumn(GridLayout::LEADING
, GridLayout::CENTER
, 1,
249 GridLayout::USE_PREF
, 0, 0);
250 single_column_set
->AddPaddingColumn(0,
251 views::kUnrelatedControlHorizontalSpacing
);
252 single_column_set
->AddColumn(GridLayout::TRAILING
, GridLayout::CENTER
, 0,
253 GridLayout::USE_PREF
, 0, 0);
254 single_column_set
->AddPaddingColumn(0, views::kPanelHorizMargin
);
256 const int kSingleColumnCredentialsId
= 1;
257 views::ColumnSet
* single_column
=
258 layout
->AddColumnSet(kSingleColumnCredentialsId
);
259 single_column
->AddPaddingColumn(0, views::kPanelHorizMargin
);
260 single_column
->AddColumn(GridLayout::FILL
, GridLayout::FILL
, 1,
261 GridLayout::USE_PREF
, 0, 0);
262 single_column
->AddPaddingColumn(0, views::kPanelHorizMargin
);
264 if (!manage_passwords_bubble_model_
->best_matches().empty()) {
265 for (autofill::PasswordFormMap::const_iterator
i(
266 manage_passwords_bubble_model_
->best_matches().begin());
267 i
!= manage_passwords_bubble_model_
->best_matches().end(); ++i
) {
268 layout
->StartRow(0, kSingleColumnCredentialsId
);
269 ManagePasswordItemView
* item
= new ManagePasswordItemView(
270 manage_passwords_bubble_model_
, *i
->second
, first_field_width
,
272 if (i
== manage_passwords_bubble_model_
->best_matches().begin()) {
273 item
->SetBorder(views::Border::CreateSolidSidedBorder(
278 GetNativeTheme()->GetSystemColor(
279 ui::NativeTheme::kColorId_EnabledMenuButtonBorderColor
)));
281 item
->SetBorder(views::Border::CreateSolidSidedBorder(
286 GetNativeTheme()->GetSystemColor(
287 ui::NativeTheme::kColorId_EnabledMenuButtonBorderColor
)));
289 layout
->AddView(item
);
291 } else if (!manage_passwords_bubble_model_
->password_submitted()) {
292 views::Label
* empty_label
= new views::Label(
293 l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_NO_PASSWORDS
));
294 empty_label
->SetMultiLine(true);
295 layout
->StartRow(0, kSingleColumnSetId
);
296 layout
->AddView(empty_label
);
299 if (manage_passwords_bubble_model_
->password_submitted()) {
300 layout
->StartRow(0, kSingleColumnCredentialsId
);
301 ManagePasswordItemView
* item
= new ManagePasswordItemView(
302 manage_passwords_bubble_model_
,
303 manage_passwords_bubble_model_
->pending_credentials(),
304 first_field_width
, second_field_width
);
305 if (manage_passwords_bubble_model_
->best_matches().empty()) {
306 item
->SetBorder(views::Border::CreateSolidSidedBorder(
311 GetNativeTheme()->GetSystemColor(
312 ui::NativeTheme::kColorId_EnabledMenuButtonBorderColor
)));
314 item
->SetBorder(views::Border::CreateSolidSidedBorder(
319 GetNativeTheme()->GetSystemColor(
320 ui::NativeTheme::kColorId_EnabledMenuButtonBorderColor
)));
322 layout
->AddView(item
);
326 new views::Link(manage_passwords_bubble_model_
->manage_link());
327 manage_link_
->set_listener(this);
328 layout
->StartRowWithPadding(0, kSingleButtonSetId
,
329 0, views::kRelatedControlVerticalSpacing
);
330 layout
->AddView(manage_link_
);
333 new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_DONE
));
334 done_button_
->SetStyle(views::Button::STYLE_BUTTON
);
335 layout
->AddView(done_button_
);
339 void ManagePasswordsBubbleView::WindowClosing() {
340 // Close() closes the window asynchronously, so by the time we reach here,
341 // |manage_passwords_bubble_| may have already been reset.
342 if (manage_passwords_bubble_
== this)
343 manage_passwords_bubble_
= NULL
;
346 void ManagePasswordsBubbleView::ButtonPressed(views::Button
* sender
,
347 const ui::Event
& event
) {
348 if (sender
== save_button_
)
349 manage_passwords_bubble_model_
->OnSaveClicked();
350 else if (sender
== cancel_button_
)
351 manage_passwords_bubble_model_
->OnCancelClicked();
353 DCHECK_EQ(done_button_
, sender
);
354 icon_view_
->SetTooltip(
355 manage_passwords_bubble_model_
->manage_passwords_bubble_state() ==
356 ManagePasswordsBubbleModel::PASSWORD_TO_BE_SAVED
);
360 void ManagePasswordsBubbleView::LinkClicked(views::Link
* source
,
362 DCHECK_EQ(source
, manage_link_
);
363 manage_passwords_bubble_model_
->OnManageLinkClicked();