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