1 // Copyright (c) 2012 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/password_generation_bubble_view.h" 6 7 #include "base/strings/utf_string_conversions.h" 8 #include "chrome/browser/ui/browser.h" 9 #include "chrome/browser/ui/browser_list.h" 10 #include "chrome/common/url_constants.h" 11 #include "components/autofill/content/common/autofill_messages.h" 12 #include "components/autofill/core/browser/password_generator.h" 13 #include "components/autofill/core/common/password_generation_util.h" 14 #include "components/password_manager/core/browser/password_manager.h" 15 #include "content/public/browser/render_view_host.h" 16 #include "grit/generated_resources.h" 17 #include "grit/theme_resources.h" 18 #include "third_party/skia/include/core/SkPaint.h" 19 #include "ui/base/l10n/l10n_util.h" 20 #include "ui/base/resource/resource_bundle.h" 21 #include "ui/base/theme_provider.h" 22 #include "ui/gfx/canvas.h" 23 #include "ui/views/border.h" 24 #include "ui/views/controls/button/image_button.h" 25 #include "ui/views/controls/button/label_button.h" 26 #include "ui/views/controls/label.h" 27 #include "ui/views/controls/link.h" 28 #include "ui/views/controls/textfield/textfield.h" 29 #include "ui/views/layout/layout_constants.h" 30 #include "url/gurl.h" 31 32 namespace { 33 // Constants for PasswordGenerationBubbleView. 34 const int kBubbleMargin = 9; 35 const int kButtonHorizontalSpacing = 4; 36 const int kButtonWidth = 65; 37 const int kDefaultTextFieldChars = 18; 38 const int kTitleLabelVerticalOffset = -1; 39 const int kVerticalPadding = 8; 40 41 // Constants for Text fieldWrapper. 42 const int kTextfieldHorizontalPadding = 2; 43 const int kTextfieldVerticalPadding = 3; 44 const int kWrapperBorderSize = 1; 45 46 // This class handles layout so that it looks like a Textfield and ImageButton 47 // are part of one logical textfield with the button on the right side of the 48 // field. It also assumes that the textfield is already sized appropriately 49 // and will alter the image size to fit. 50 class TextfieldWrapper : public views::View { 51 public: 52 TextfieldWrapper(views::Textfield* textfield, 53 views::ImageButton* image_button); 54 virtual ~TextfieldWrapper(); 55 56 virtual void Layout() OVERRIDE; 57 virtual gfx::Size GetPreferredSize() const OVERRIDE; 58 59 private: 60 gfx::Size GetImageSize() const; 61 62 views::Textfield* textfield_; 63 views::ImageButton* image_button_; 64 }; 65 66 TextfieldWrapper::TextfieldWrapper(views::Textfield* textfield, 67 views::ImageButton* image_button) 68 : textfield_(textfield), 69 image_button_(image_button) { 70 SetBorder(views::Border::CreateSolidBorder(kWrapperBorderSize, SK_ColorGRAY)); 71 72 AddChildView(textfield_); 73 AddChildView(image_button); 74 } 75 76 TextfieldWrapper::~TextfieldWrapper() {} 77 78 void TextfieldWrapper::Layout() { 79 // Add some spacing between the textfield and the border. 80 textfield_->SetPosition(gfx::Point(kTextfieldHorizontalPadding, 81 kTextfieldVerticalPadding)); 82 textfield_->SizeToPreferredSize(); 83 84 // Button should be offset one pixel from the end of the textfield so that 85 // there is no overlap. It is also displaced down by the size of the border 86 // so it doesn't overlap with it either. 87 int button_x = (textfield_->GetPreferredSize().width() + 88 kTextfieldHorizontalPadding + 1); 89 image_button_->SetPosition(gfx::Point(button_x, 90 kWrapperBorderSize)); 91 92 // Make sure that the image is centered after cropping. 93 image_button_->SetImageAlignment(views::ImageButton::ALIGN_CENTER, 94 views::ImageButton::ALIGN_MIDDLE); 95 96 image_button_->SetSize(GetImageSize()); 97 } 98 99 gfx::Size TextfieldWrapper::GetPreferredSize() const { 100 int width = (textfield_->GetPreferredSize().width() + 101 GetImageSize().width() + 102 kTextfieldHorizontalPadding * 3); 103 int height = (textfield_->GetPreferredSize().height() + 104 kTextfieldVerticalPadding * 2); 105 106 return gfx::Size(width, height); 107 } 108 109 gfx::Size TextfieldWrapper::GetImageSize() const { 110 // The image is sized so that it fills the space between the borders 111 // completely. 112 int size = (textfield_->GetPreferredSize().height() + 113 (kTextfieldVerticalPadding - kWrapperBorderSize) * 2); 114 return gfx::Size(size, size); 115 } 116 } // namespace 117 118 PasswordGenerationBubbleView::PasswordGenerationBubbleView( 119 const autofill::PasswordForm& form, 120 const gfx::Rect& anchor_rect, 121 views::View* anchor_view, 122 content::RenderViewHost* render_view_host, 123 password_manager::PasswordManager* password_manager, 124 autofill::PasswordGenerator* password_generator, 125 ui::ThemeProvider* theme_provider) 126 : BubbleDelegateView(anchor_view, views::BubbleBorder::TOP_LEFT), 127 title_label_(NULL), 128 accept_button_(NULL), 129 textfield_(NULL), 130 regenerate_button_(NULL), 131 textfield_wrapper_(NULL), 132 form_(form), 133 anchor_rect_(anchor_rect), 134 render_view_host_(render_view_host), 135 password_manager_(password_manager), 136 password_generator_(password_generator), 137 theme_provider_(theme_provider) {} 138 139 PasswordGenerationBubbleView::~PasswordGenerationBubbleView() {} 140 141 void PasswordGenerationBubbleView::Init() { 142 set_margins(gfx::Insets(kBubbleMargin, kBubbleMargin, 143 kBubbleMargin, kBubbleMargin)); 144 145 // TODO(gcasto): Localize text after we have finalized the UI. 146 // crbug.com/118062. 147 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 148 title_label_ = new views::Label( 149 l10n_util::GetStringUTF16(IDS_PASSWORD_GENERATION_BUBBLE_TITLE), 150 rb.GetFontList(ui::ResourceBundle::MediumFont)); 151 AddChildView(title_label_); 152 153 regenerate_button_ = new views::ImageButton(this); 154 regenerate_button_->SetImage( 155 views::CustomButton::STATE_NORMAL, 156 theme_provider_->GetImageSkiaNamed(IDR_RELOAD_DIMMED)); 157 regenerate_button_->SetImage( 158 views::CustomButton::STATE_HOVERED, 159 theme_provider_->GetImageSkiaNamed(IDR_RELOAD)); 160 regenerate_button_->SetImage( 161 views::CustomButton::STATE_PRESSED, 162 theme_provider_->GetImageSkiaNamed(IDR_RELOAD)); 163 164 textfield_ = new views::Textfield(); 165 textfield_->set_default_width_in_chars(kDefaultTextFieldChars); 166 textfield_->SetText(base::ASCIIToUTF16(password_generator_->Generate())); 167 textfield_->set_controller(this); 168 169 textfield_wrapper_ = new TextfieldWrapper(textfield_, 170 regenerate_button_); 171 AddChildView(textfield_wrapper_); 172 173 accept_button_ = new views::LabelButton( 174 this, 175 l10n_util::GetStringUTF16(IDS_PASSWORD_GENERATION_BUTTON_TEXT)); 176 accept_button_->SetStyle(views::Button::STYLE_BUTTON); 177 AddChildView(accept_button_); 178 } 179 180 void PasswordGenerationBubbleView::Layout() { 181 // We have the title label shifted up to make the borders look more uniform. 182 title_label_->SetPosition(gfx::Point(0, kTitleLabelVerticalOffset)); 183 title_label_->SizeToPreferredSize(); 184 185 int y = title_label_->GetPreferredSize().height() + kVerticalPadding; 186 187 textfield_wrapper_->SetPosition(gfx::Point(0, y)); 188 textfield_wrapper_->SizeToPreferredSize(); 189 190 int button_x = (textfield_wrapper_->GetPreferredSize().width() + 191 kButtonHorizontalSpacing); 192 accept_button_->SetBounds( 193 button_x, 194 y - kWrapperBorderSize, 195 kButtonWidth, 196 textfield_wrapper_->GetPreferredSize().height() + kWrapperBorderSize * 2); 197 } 198 199 gfx::Size PasswordGenerationBubbleView::GetPreferredSize() const { 200 int width = (textfield_wrapper_->GetPreferredSize().width() + 201 kButtonHorizontalSpacing + 202 kButtonWidth - 1); 203 int height = (title_label_->GetPreferredSize().height() + 204 textfield_wrapper_->GetPreferredSize().height() + 205 kVerticalPadding); 206 return gfx::Size(width, height); 207 } 208 209 gfx::Rect PasswordGenerationBubbleView::GetAnchorRect() const { 210 return anchor_rect_; 211 } 212 213 void PasswordGenerationBubbleView::ButtonPressed(views::Button* sender, 214 const ui::Event& event) { 215 if (sender == accept_button_) { 216 render_view_host_->Send(new AutofillMsg_GeneratedPasswordAccepted( 217 render_view_host_->GetRoutingID(), textfield_->text())); 218 password_manager_->SetFormHasGeneratedPassword(form_); 219 actions_.password_accepted = true; 220 GetWidget()->Close(); 221 } 222 if (sender == regenerate_button_) { 223 textfield_->SetText( 224 base::ASCIIToUTF16(password_generator_->Generate())); 225 actions_.password_regenerated = true; 226 } 227 } 228 229 void PasswordGenerationBubbleView::ContentsChanged( 230 views::Textfield* sender, 231 const base::string16& contents) { 232 actions_.password_edited = true; 233 } 234 235 bool PasswordGenerationBubbleView::HandleKeyEvent( 236 views::Textfield* sender, 237 const ui::KeyEvent& key_event) { 238 return false; 239 } 240 241 views::View* PasswordGenerationBubbleView::GetInitiallyFocusedView() { 242 return textfield_; 243 } 244 245 void PasswordGenerationBubbleView::WindowClosing() { 246 autofill::password_generation::LogUserActions(actions_); 247 } 248