manage_passwords_bubble_view.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
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.
4
5#include "chrome/browser/ui/views/passwords/manage_passwords_bubble_view.h"
6
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/canvas.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"
25
26
27// Helpers --------------------------------------------------------------------
28
29namespace {
30
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.
33void UpdateBiggestWidth(const autofill::PasswordForm& password_form,
34                        bool username,
35                        int* biggest_width) {
36  ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
37  gfx::FontList font_list(rb->GetFontList(ui::ResourceBundle::BaseFont));
38  base::string16 display_string(username ?
39      password_form.username_value :
40      ManagePasswordItemView::GetPasswordDisplayString(
41          password_form.password_value));
42  *biggest_width = std::max(
43      gfx::Canvas::GetStringWidth(display_string, font_list), *biggest_width);
44}
45
46}  // namespace
47
48
49// ManagePasswordsBubbleView --------------------------------------------------
50
51// static
52ManagePasswordsBubbleView* ManagePasswordsBubbleView::manage_passwords_bubble_ =
53    NULL;
54
55// static
56void ManagePasswordsBubbleView::ShowBubble(content::WebContents* web_contents,
57                                           ManagePasswordsIconView* icon_view) {
58  Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
59  DCHECK(browser);
60  DCHECK(browser->window());
61  DCHECK(browser->fullscreen_controller());
62  DCHECK(!IsShowing());
63
64  BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
65  bool is_fullscreen = browser_view->IsFullscreen();
66  views::View* anchor_view = is_fullscreen ?
67      NULL : browser_view->GetLocationBarView()->manage_passwords_icon_view();
68  manage_passwords_bubble_ =
69      new ManagePasswordsBubbleView(web_contents, anchor_view, icon_view);
70
71  if (is_fullscreen) {
72    manage_passwords_bubble_->set_parent_window(
73        web_contents->GetView()->GetTopLevelNativeWindow());
74  }
75
76  views::BubbleDelegateView::CreateBubble(manage_passwords_bubble_);
77
78  // Adjust for fullscreen after creation as it relies on the content size.
79  if (is_fullscreen) {
80    manage_passwords_bubble_->AdjustForFullscreen(
81        browser_view->GetBoundsInScreen());
82  }
83
84  manage_passwords_bubble_->GetWidget()->Show();
85}
86
87// static
88void ManagePasswordsBubbleView::CloseBubble() {
89  if (manage_passwords_bubble_)
90   manage_passwords_bubble_->Close();
91}
92
93// static
94bool ManagePasswordsBubbleView::IsShowing() {
95  // The bubble may be in the process of closing.
96  return (manage_passwords_bubble_ != NULL) &&
97      manage_passwords_bubble_->GetWidget()->IsVisible();
98}
99
100ManagePasswordsBubbleView::ManagePasswordsBubbleView(
101    content::WebContents* web_contents,
102    views::View* anchor_view,
103    ManagePasswordsIconView* icon_view)
104    : BubbleDelegateView(
105          anchor_view,
106          anchor_view ?
107              views::BubbleBorder::TOP_RIGHT : views::BubbleBorder::NONE),
108      manage_passwords_bubble_model_(
109          new ManagePasswordsBubbleModel(web_contents)),
110      icon_view_(icon_view) {
111  // Compensate for built-in vertical padding in the anchor view's image.
112  set_anchor_view_insets(gfx::Insets(5, 0, 5, 0));
113  set_notify_enter_exit_on_child(true);
114}
115
116ManagePasswordsBubbleView::~ManagePasswordsBubbleView() {}
117
118int ManagePasswordsBubbleView::GetMaximumUsernameOrPasswordWidth(
119    bool username) {
120  int biggest_width = 0;
121  if (manage_passwords_bubble_model_->manage_passwords_bubble_state() !=
122      ManagePasswordsBubbleModel::PASSWORD_TO_BE_SAVED) {
123    // If we are in the PASSWORD_TO_BE_SAVED state we only display the
124    // password that was just submitted and should not take these into account.
125    for (autofill::PasswordFormMap::const_iterator i(
126             manage_passwords_bubble_model_->best_matches().begin());
127         i != manage_passwords_bubble_model_->best_matches().end(); ++i) {
128      UpdateBiggestWidth((*i->second), username, &biggest_width);
129    }
130  }
131  if (manage_passwords_bubble_model_->password_submitted()) {
132    UpdateBiggestWidth(manage_passwords_bubble_model_->pending_credentials(),
133                       username, &biggest_width);
134  }
135  return biggest_width;
136}
137
138void ManagePasswordsBubbleView::AdjustForFullscreen(
139    const gfx::Rect& screen_bounds) {
140  if (GetAnchorView())
141    return;
142
143  // The bubble's padding from the screen edge, used in fullscreen.
144  const int kFullscreenPaddingEnd = 20;
145  const size_t bubble_half_width = width() / 2;
146  const int x_pos = base::i18n::IsRTL() ?
147      screen_bounds.x() + bubble_half_width + kFullscreenPaddingEnd :
148      screen_bounds.right() - bubble_half_width - kFullscreenPaddingEnd;
149  SetAnchorRect(gfx::Rect(x_pos, screen_bounds.y(), 0, 0));
150}
151
152void ManagePasswordsBubbleView::Close() {
153  GetWidget()->Close();
154}
155
156void ManagePasswordsBubbleView::Init() {
157  using views::GridLayout;
158
159  GridLayout* layout = new GridLayout(this);
160  SetLayoutManager(layout);
161
162  // This calculates the necessary widths for the list of credentials in the
163  // bubble. We do not need to clamp the password field width because
164  // ManagePasswordItemView::GetPasswordFisplayString() does this.
165
166  ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
167  const int predefined_username_field_max_width =
168      rb->GetFont(ui::ResourceBundle::BaseFont).GetAverageCharacterWidth() * 22;
169  const int max_username_or_password_width =
170      std::min(GetMaximumUsernameOrPasswordWidth(true),
171               predefined_username_field_max_width);
172  const int first_field_width = std::max(max_username_or_password_width,
173      views::Label(l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_DELETED)).
174          GetPreferredSize().width());
175
176  const int second_field_width = std::max(
177      GetMaximumUsernameOrPasswordWidth(false),
178      views::Label(l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_UNDO)).
179          GetPreferredSize().width());
180
181  const int kSingleColumnSetId = 0;
182  views::ColumnSet* column_set = layout->AddColumnSet(kSingleColumnSetId);
183  column_set->AddPaddingColumn(0, views::kPanelHorizMargin);
184  column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, 0,
185                        GridLayout::USE_PREF, 0, 0);
186  column_set->AddPaddingColumn(0, views::kPanelHorizMargin);
187
188  views::Label* title_label =
189      new views::Label(manage_passwords_bubble_model_->title());
190  title_label->SetMultiLine(true);
191  title_label->SetFontList(rb->GetFontList(ui::ResourceBundle::MediumFont));
192
193  layout->StartRowWithPadding(0, kSingleColumnSetId,
194                              0, views::kRelatedControlSmallVerticalSpacing);
195  layout->AddView(title_label);
196  layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing);
197
198  if (manage_passwords_bubble_model_->manage_passwords_bubble_state() ==
199      ManagePasswordsBubbleModel::PASSWORD_TO_BE_SAVED) {
200    const int kSingleColumnCredentialsId = 1;
201    views::ColumnSet* single_column =
202        layout->AddColumnSet(kSingleColumnCredentialsId);
203    single_column->AddPaddingColumn(0, views::kPanelHorizMargin);
204    single_column->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
205                             GridLayout::USE_PREF, 0, 0);
206    single_column->AddPaddingColumn(0, views::kPanelHorizMargin);
207
208    layout->StartRow(0, kSingleColumnCredentialsId);
209    ManagePasswordItemView* item = new ManagePasswordItemView(
210        manage_passwords_bubble_model_,
211        manage_passwords_bubble_model_->pending_credentials(),
212        first_field_width, second_field_width);
213    item->set_border(views::Border::CreateSolidSidedBorder(
214        1, 0, 1, 0, GetNativeTheme()->GetSystemColor(
215            ui::NativeTheme::kColorId_EnabledMenuButtonBorderColor)));
216    layout->AddView(item);
217
218    const int kDoubleColumnSetId = 2;
219    views::ColumnSet* double_column_set =
220        layout->AddColumnSet(kDoubleColumnSetId);
221    double_column_set->AddPaddingColumn(0, views::kPanelHorizMargin);
222    double_column_set->AddColumn(GridLayout::TRAILING, GridLayout::CENTER, 1,
223                                 GridLayout::USE_PREF, 0, 0);
224    double_column_set->AddPaddingColumn(0, views::kRelatedButtonHSpacing);
225    double_column_set->AddColumn(GridLayout::TRAILING, GridLayout::CENTER, 0,
226                                 GridLayout::USE_PREF, 0, 0);
227    double_column_set->AddPaddingColumn(0, views::kPanelHorizMargin);
228
229    cancel_button_ = new views::LabelButton(
230        this, l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_CANCEL_BUTTON));
231    cancel_button_->SetStyle(views::Button::STYLE_NATIVE_TEXTBUTTON);
232    save_button_ = new views::BlueButton(
233        this, l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_SAVE_BUTTON));
234
235    layout->StartRowWithPadding(0, kDoubleColumnSetId,
236                                0, views::kRelatedControlVerticalSpacing);
237    layout->AddView(save_button_);
238    layout->AddView(cancel_button_);
239    layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
240  } else {
241    const int kSingleButtonSetId = 3;
242    views::ColumnSet* single_column_set =
243        layout->AddColumnSet(kSingleButtonSetId);
244    single_column_set->AddPaddingColumn(0, views::kPanelHorizMargin);
245    single_column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 1,
246                                 GridLayout::USE_PREF, 0, 0);
247    single_column_set->AddPaddingColumn(0,
248        views::kUnrelatedControlHorizontalSpacing);
249    single_column_set->AddColumn(GridLayout::TRAILING, GridLayout::CENTER, 0,
250                                 GridLayout::USE_PREF, 0, 0);
251    single_column_set->AddPaddingColumn(0, views::kPanelHorizMargin);
252
253    const int kSingleColumnCredentialsId = 1;
254    views::ColumnSet* single_column =
255        layout->AddColumnSet(kSingleColumnCredentialsId);
256    single_column->AddPaddingColumn(0, views::kPanelHorizMargin);
257    single_column->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
258                             GridLayout::USE_PREF, 0, 0);
259    single_column->AddPaddingColumn(0, views::kPanelHorizMargin);
260
261    if (!manage_passwords_bubble_model_->best_matches().empty()) {
262      for (autofill::PasswordFormMap::const_iterator i(
263               manage_passwords_bubble_model_->best_matches().begin());
264           i != manage_passwords_bubble_model_->best_matches().end(); ++i) {
265        layout->StartRow(0, kSingleColumnCredentialsId);
266        ManagePasswordItemView* item = new ManagePasswordItemView(
267            manage_passwords_bubble_model_, *i->second, first_field_width,
268            second_field_width);
269        if (i == manage_passwords_bubble_model_->best_matches().begin()) {
270          item->set_border(views::Border::CreateSolidSidedBorder(
271              1, 0, 1, 0, GetNativeTheme()->GetSystemColor(
272                  ui::NativeTheme::kColorId_EnabledMenuButtonBorderColor)));
273        } else {
274          item->set_border(views::Border::CreateSolidSidedBorder(
275              0, 0, 1, 0, GetNativeTheme()->GetSystemColor(
276                  ui::NativeTheme::kColorId_EnabledMenuButtonBorderColor)));
277        }
278        layout->AddView(item);
279      }
280    } else if (!manage_passwords_bubble_model_->password_submitted()) {
281        views::Label* empty_label = new views::Label(
282            l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_NO_PASSWORDS));
283        empty_label->SetMultiLine(true);
284        layout->StartRow(0, kSingleColumnSetId);
285        layout->AddView(empty_label);
286    }
287
288    if (manage_passwords_bubble_model_->password_submitted()) {
289      layout->StartRow(0, kSingleColumnCredentialsId);
290      ManagePasswordItemView* item = new ManagePasswordItemView(
291          manage_passwords_bubble_model_,
292          manage_passwords_bubble_model_->pending_credentials(),
293          first_field_width, second_field_width);
294      if (manage_passwords_bubble_model_->best_matches().empty()) {
295        item->set_border(views::Border::CreateSolidSidedBorder(1, 0, 1, 0,
296            GetNativeTheme()->GetSystemColor(
297                ui::NativeTheme::kColorId_EnabledMenuButtonBorderColor)));
298      } else {
299        item->set_border(views::Border::CreateSolidSidedBorder(0, 0, 1, 0,
300            GetNativeTheme()->GetSystemColor(
301                ui::NativeTheme::kColorId_EnabledMenuButtonBorderColor)));
302      }
303      layout->AddView(item);
304    }
305
306    manage_link_ =
307        new views::Link(manage_passwords_bubble_model_->manage_link());
308    manage_link_->set_listener(this);
309    layout->StartRowWithPadding(0, kSingleButtonSetId,
310                                0, views::kRelatedControlVerticalSpacing);
311    layout->AddView(manage_link_);
312
313    done_button_ =
314        new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_DONE));
315    done_button_->SetStyle(views::Button::STYLE_NATIVE_TEXTBUTTON);
316    layout->AddView(done_button_);
317  }
318}
319
320void ManagePasswordsBubbleView::WindowClosing() {
321  // Close() closes the window asynchronously, so by the time we reach here,
322  // |manage_passwords_bubble_| may have already been reset.
323  if (manage_passwords_bubble_ == this)
324    manage_passwords_bubble_ = NULL;
325}
326
327void ManagePasswordsBubbleView::ButtonPressed(views::Button* sender,
328                                              const ui::Event& event) {
329  if (sender == save_button_)
330    manage_passwords_bubble_model_->OnSaveClicked();
331  else if (sender == cancel_button_)
332    manage_passwords_bubble_model_->OnCancelClicked();
333  else
334    DCHECK_EQ(done_button_, sender);
335  icon_view_->SetTooltip(
336      manage_passwords_bubble_model_->manage_passwords_bubble_state() ==
337          ManagePasswordsBubbleModel::PASSWORD_TO_BE_SAVED);
338  Close();
339}
340
341void ManagePasswordsBubbleView::LinkClicked(views::Link* source,
342                                            int event_flags) {
343  DCHECK_EQ(source, manage_link_);
344  manage_passwords_bubble_model_->OnManageLinkClicked();
345}
346