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