Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(72)

Side by Side Diff: chrome/browser/ui/views/autofill/card_unmask_prompt_views.cc

Issue 1020013003: Add a regression test for the CardUnmaskPrompt. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: EXPECT Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/ui/views/autofill/card_unmask_prompt_views.h"
6
5 #include "base/basictypes.h" 7 #include "base/basictypes.h"
6 #include "base/strings/utf_string_conversions.h" 8 #include "base/strings/utf_string_conversions.h"
7 #include "chrome/browser/ui/autofill/autofill_dialog_models.h"
8 #include "chrome/browser/ui/autofill/autofill_dialog_types.h" 9 #include "chrome/browser/ui/autofill/autofill_dialog_types.h"
9 #include "chrome/browser/ui/autofill/card_unmask_prompt_controller.h" 10 #include "chrome/browser/ui/autofill/card_unmask_prompt_controller.h"
10 #include "chrome/browser/ui/autofill/card_unmask_prompt_view.h"
11 #include "chrome/browser/ui/views/autofill/decorated_textfield.h" 11 #include "chrome/browser/ui/views/autofill/decorated_textfield.h"
12 #include "chrome/browser/ui/views/autofill/tooltip_icon.h" 12 #include "chrome/browser/ui/views/autofill/tooltip_icon.h"
13 #include "chrome/grit/generated_resources.h" 13 #include "chrome/grit/generated_resources.h"
14 #include "components/constrained_window/constrained_window_views.h" 14 #include "components/constrained_window/constrained_window_views.h"
15 #include "components/web_modal/web_contents_modal_dialog_host.h" 15 #include "components/web_modal/web_contents_modal_dialog_host.h"
16 #include "components/web_modal/web_contents_modal_dialog_manager.h" 16 #include "components/web_modal/web_contents_modal_dialog_manager.h"
17 #include "components/web_modal/web_contents_modal_dialog_manager_delegate.h" 17 #include "components/web_modal/web_contents_modal_dialog_manager_delegate.h"
18 #include "grit/theme_resources.h" 18 #include "grit/theme_resources.h"
19 #include "third_party/skia/include/core/SkColor.h" 19 #include "third_party/skia/include/core/SkColor.h"
20 #include "ui/base/l10n/l10n_util.h" 20 #include "ui/base/l10n/l10n_util.h"
21 #include "ui/base/resource/resource_bundle.h" 21 #include "ui/base/resource/resource_bundle.h"
22 #include "ui/views/background.h" 22 #include "ui/views/background.h"
23 #include "ui/views/controls/button/checkbox.h" 23 #include "ui/views/controls/button/checkbox.h"
24 #include "ui/views/controls/combobox/combobox.h" 24 #include "ui/views/controls/combobox/combobox.h"
25 #include "ui/views/controls/combobox/combobox_listener.h"
26 #include "ui/views/controls/image_view.h" 25 #include "ui/views/controls/image_view.h"
27 #include "ui/views/controls/label.h" 26 #include "ui/views/controls/label.h"
28 #include "ui/views/controls/textfield/textfield_controller.h"
29 #include "ui/views/layout/box_layout.h" 27 #include "ui/views/layout/box_layout.h"
30 #include "ui/views/widget/widget.h" 28 #include "ui/views/widget/widget.h"
31 #include "ui/views/window/dialog_client_view.h" 29 #include "ui/views/window/dialog_client_view.h"
32 #include "ui/views/window/dialog_delegate.h"
33 30
34 namespace autofill { 31 namespace autofill {
35 32
36 namespace {
37
38 // The number of pixels of blank space on the outer horizontal edges of the 33 // The number of pixels of blank space on the outer horizontal edges of the
39 // dialog. 34 // dialog.
40 const int kEdgePadding = 19; 35 const int kEdgePadding = 19;
41 36
42 // From AutofillDialogViews. TODO(estade): share. 37 // From AutofillDialogViews. TODO(estade): share.
43 SkColor kShadingColor = SkColorSetARGB(7, 0, 0, 0); 38 SkColor kShadingColor = SkColorSetARGB(7, 0, 0, 0);
44 SkColor kSubtleBorderColor = SkColorSetARGB(10, 0, 0, 0); 39 SkColor kSubtleBorderColor = SkColorSetARGB(10, 0, 0, 0);
45 40
46 class CardUnmaskPromptViews : public CardUnmaskPromptView,
47 views::ComboboxListener,
48 views::DialogDelegateView,
49 views::TextfieldController {
50 public:
51 explicit CardUnmaskPromptViews(CardUnmaskPromptController* controller)
52 : controller_(controller),
53 main_contents_(nullptr),
54 permanent_error_label_(nullptr),
55 cvc_input_(nullptr),
56 month_input_(nullptr),
57 year_input_(nullptr),
58 error_label_(nullptr),
59 storage_checkbox_(nullptr),
60 progress_overlay_(nullptr),
61 progress_label_(nullptr),
62 weak_ptr_factory_(this) {}
63
64 ~CardUnmaskPromptViews() override {
65 if (controller_)
66 controller_->OnUnmaskDialogClosed();
67 }
68
69 void Show() {
70 constrained_window::ShowWebModalDialogViews(this,
71 controller_->GetWebContents());
72 }
73
74 // CardUnmaskPromptView
75 void ControllerGone() override {
76 controller_ = nullptr;
77 ClosePrompt();
78 }
79
80 void DisableAndWaitForVerification() override {
81 SetInputsEnabled(false);
82 progress_overlay_->SetVisible(true);
83 GetDialogClientView()->UpdateDialogButtons();
84 Layout();
85 }
86
87 void GotVerificationResult(const base::string16& error_message,
88 bool allow_retry) override {
89 if (error_message.empty()) {
90 progress_label_->SetText(l10n_util::GetStringUTF16(
91 IDS_AUTOFILL_CARD_UNMASK_VERIFICATION_SUCCESS));
92 base::MessageLoop::current()->PostDelayedTask(
93 FROM_HERE, base::Bind(&CardUnmaskPromptViews::ClosePrompt,
94 weak_ptr_factory_.GetWeakPtr()),
95 base::TimeDelta::FromSeconds(1));
96 } else {
97 // TODO(estade): it's somewhat jarring when the error comes back too
98 // quickly.
99 progress_overlay_->SetVisible(false);
100
101 if (allow_retry) {
102 SetInputsEnabled(true);
103
104 // If there is more than one input showing, don't mark anything as
105 // invalid since we don't know the location of the problem.
106 if (!controller_->ShouldRequestExpirationDate())
107 cvc_input_->SetInvalid(true);
108
109 // TODO(estade): When do we hide |error_label_|?
110 SetRetriableErrorMessage(error_message);
111 } else {
112 permanent_error_label_->SetText(error_message);
113 permanent_error_label_->SetVisible(true);
114 SetRetriableErrorMessage(base::string16());
115 }
116
117 GetDialogClientView()->UpdateDialogButtons();
118 }
119
120 Layout();
121 }
122
123 void SetRetriableErrorMessage(const base::string16& message) {
124 if (message.empty()) {
125 error_label_->SetMultiLine(false);
126 error_label_->SetText(base::ASCIIToUTF16(" "));
127 } else {
128 error_label_->SetMultiLine(true);
129 error_label_->SetText(message);
130 }
131
132 // Update the dialog's size.
133 if (GetWidget() && controller_->GetWebContents()) {
134 constrained_window::UpdateWebContentsModalDialogPosition(
135 GetWidget(),
136 web_modal::WebContentsModalDialogManager::FromWebContents(
137 controller_->GetWebContents())
138 ->delegate()
139 ->GetWebContentsModalDialogHost());
140 }
141 }
142
143 void SetInputsEnabled(bool enabled) {
144 cvc_input_->SetEnabled(enabled);
145 storage_checkbox_->SetEnabled(enabled);
146
147 if (month_input_)
148 month_input_->SetEnabled(enabled);
149 if (year_input_)
150 year_input_->SetEnabled(enabled);
151 }
152
153 // views::DialogDelegateView
154 View* GetContentsView() override {
155 InitIfNecessary();
156 return this;
157 }
158
159 views::View* CreateFootnoteView() override {
160 // Local storage checkbox and (?) tooltip.
161 views::View* storage_row = new views::View();
162 views::BoxLayout* storage_row_layout = new views::BoxLayout(
163 views::BoxLayout::kHorizontal, kEdgePadding, kEdgePadding, 0);
164 storage_row->SetLayoutManager(storage_row_layout);
165 storage_row->SetBorder(
166 views::Border::CreateSolidSidedBorder(1, 0, 0, 0, kSubtleBorderColor));
167 storage_row->set_background(
168 views::Background::CreateSolidBackground(kShadingColor));
169
170 storage_checkbox_ = new views::Checkbox(l10n_util::GetStringUTF16(
171 IDS_AUTOFILL_CARD_UNMASK_PROMPT_STORAGE_CHECKBOX));
172 storage_checkbox_->SetChecked(controller_->GetStoreLocallyStartState());
173 storage_row->AddChildView(storage_checkbox_);
174 storage_row_layout->SetFlexForView(storage_checkbox_, 1);
175
176 storage_row->AddChildView(new TooltipIcon(l10n_util::GetStringUTF16(
177 IDS_AUTOFILL_CARD_UNMASK_PROMPT_STORAGE_TOOLTIP)));
178
179 return storage_row;
180 }
181
182 // views::View
183 gfx::Size GetPreferredSize() const override {
184 // Must hardcode a width so the label knows where to wrap. TODO(estade):
185 // This can lead to a weird looking dialog if we end up getting allocated
186 // more width than we ask for, e.g. if the title is super long.
187 const int kWidth = 375;
188 return gfx::Size(kWidth, GetHeightForWidth(kWidth));
189 }
190
191 void Layout() override {
192 for (int i = 0; i < child_count(); ++i) {
193 child_at(i)->SetBoundsRect(GetContentsBounds());
194 }
195 }
196
197 int GetHeightForWidth(int width) const override {
198 if (!has_children())
199 return 0;
200 const gfx::Insets insets = GetInsets();
201 return main_contents_->GetHeightForWidth(width - insets.width()) +
202 insets.height();
203 }
204
205 void OnNativeThemeChanged(const ui::NativeTheme* theme) override {
206 SkColor bg_color =
207 theme->GetSystemColor(ui::NativeTheme::kColorId_DialogBackground);
208 bg_color = SkColorSetA(bg_color, 0xDD);
209 progress_overlay_->set_background(
210 views::Background::CreateSolidBackground(bg_color));
211 }
212
213 ui::ModalType GetModalType() const override { return ui::MODAL_TYPE_CHILD; }
214
215 base::string16 GetWindowTitle() const override {
216 return controller_->GetWindowTitle();
217 }
218
219 void DeleteDelegate() override { delete this; }
220
221 int GetDialogButtons() const override {
222 return ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL;
223 }
224
225 base::string16 GetDialogButtonLabel(ui::DialogButton button) const override {
226 if (button == ui::DIALOG_BUTTON_OK)
227 return l10n_util::GetStringUTF16(IDS_AUTOFILL_CARD_UNMASK_CONFIRM_BUTTON);
228
229 return DialogDelegateView::GetDialogButtonLabel(button);
230 }
231
232 bool ShouldDefaultButtonBeBlue() const override { return true; }
233
234 bool IsDialogButtonEnabled(ui::DialogButton button) const override {
235 if (button == ui::DIALOG_BUTTON_CANCEL)
236 return true;
237
238 DCHECK_EQ(ui::DIALOG_BUTTON_OK, button);
239
240 return cvc_input_->enabled() &&
241 controller_->InputCvcIsValid(cvc_input_->text()) &&
242 ExpirationDateIsValid();
243 }
244
245 views::View* GetInitiallyFocusedView() override { return cvc_input_; }
246
247 bool Cancel() override {
248 return true;
249 }
250
251 bool Accept() override {
252 if (!controller_)
253 return true;
254
255 controller_->OnUnmaskResponse(
256 cvc_input_->text(),
257 month_input_
258 ? month_input_->GetTextForRow(month_input_->selected_index())
259 : base::string16(),
260 year_input_ ? year_input_->GetTextForRow(year_input_->selected_index())
261 : base::string16(),
262 storage_checkbox_ ? storage_checkbox_->checked() : false);
263 return false;
264 }
265
266 // views::TextfieldController
267 void ContentsChanged(views::Textfield* sender,
268 const base::string16& new_contents) override {
269 if (controller_->InputCvcIsValid(new_contents))
270 cvc_input_->SetInvalid(false);
271
272 GetDialogClientView()->UpdateDialogButtons();
273 }
274
275 // views::ComboboxListener
276 void OnPerformAction(views::Combobox* combobox) override {
277 if (ExpirationDateIsValid()) {
278 if (month_input_->invalid()) {
279 month_input_->SetInvalid(false);
280 year_input_->SetInvalid(false);
281 error_label_->SetMultiLine(false);
282 SetRetriableErrorMessage(base::string16());
283 }
284 } else if (month_input_->selected_index() !=
285 month_combobox_model_.GetDefaultIndex() &&
286 year_input_->selected_index() !=
287 year_combobox_model_.GetDefaultIndex()) {
288 month_input_->SetInvalid(true);
289 year_input_->SetInvalid(true);
290 error_label_->SetMultiLine(true);
291 SetRetriableErrorMessage(l10n_util::GetStringUTF16(
292 IDS_AUTOFILL_CARD_UNMASK_INVALID_EXPIRATION_DATE));
293 }
294
295 GetDialogClientView()->UpdateDialogButtons();
296 }
297
298 private:
299 void InitIfNecessary() {
300 if (has_children())
301 return;
302
303 main_contents_ = new views::View();
304 main_contents_->SetLayoutManager(
305 new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 10));
306 AddChildView(main_contents_);
307
308 permanent_error_label_ = new views::Label();
309 permanent_error_label_->set_background(
310 views::Background::CreateSolidBackground(
311 SkColorSetRGB(0xdb, 0x44, 0x37)));
312 permanent_error_label_->SetBorder(
313 views::Border::CreateEmptyBorder(10, kEdgePadding, 10, kEdgePadding));
314 permanent_error_label_->SetEnabledColor(SK_ColorWHITE);
315 permanent_error_label_->SetAutoColorReadabilityEnabled(false);
316 permanent_error_label_->SetVisible(false);
317 permanent_error_label_->SetMultiLine(true);
318 permanent_error_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
319 main_contents_->AddChildView(permanent_error_label_);
320
321 views::View* controls_container = new views::View();
322 controls_container->SetLayoutManager(
323 new views::BoxLayout(views::BoxLayout::kVertical, kEdgePadding, 0, 0));
324 main_contents_->AddChildView(controls_container);
325
326 views::Label* instructions =
327 new views::Label(controller_->GetInstructionsMessage());
328
329 instructions->SetMultiLine(true);
330 instructions->SetHorizontalAlignment(gfx::ALIGN_LEFT);
331 instructions->SetBorder(views::Border::CreateEmptyBorder(0, 0, 15, 0));
332 controls_container->AddChildView(instructions);
333
334 views::View* input_row = new views::View();
335 input_row->SetLayoutManager(
336 new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 5));
337 controls_container->AddChildView(input_row);
338
339 if (controller_->ShouldRequestExpirationDate()) {
340 month_input_ = new views::Combobox(&month_combobox_model_);
341 month_input_->set_listener(this);
342 input_row->AddChildView(month_input_);
343 input_row->AddChildView(new views::Label(l10n_util::GetStringUTF16(
344 IDS_AUTOFILL_CARD_UNMASK_EXPIRATION_DATE_SEPARATOR)));
345 year_input_ = new views::Combobox(&year_combobox_model_);
346 year_input_->set_listener(this);
347 input_row->AddChildView(year_input_);
348
349 input_row->AddChildView(new views::Label(base::ASCIIToUTF16(" ")));
350 }
351
352 cvc_input_ = new DecoratedTextfield(
353 base::string16(),
354 l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_CVC),
355 this);
356 cvc_input_->set_default_width_in_chars(8);
357 input_row->AddChildView(cvc_input_);
358
359 views::ImageView* cvc_image = new views::ImageView();
360 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
361 cvc_image->SetImage(rb.GetImageSkiaNamed(controller_->GetCvcImageRid()));
362 input_row->AddChildView(cvc_image);
363
364 // Reserve vertical space for the error label, assuming it's one line.
365 error_label_ = new views::Label(base::ASCIIToUTF16(" "));
366 error_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
367 error_label_->SetEnabledColor(kWarningColor);
368 error_label_->SetBorder(views::Border::CreateEmptyBorder(3, 0, 5, 0));
369 controls_container->AddChildView(error_label_);
370
371 progress_overlay_ = new views::View();
372 views::BoxLayout* progress_layout =
373 new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0);
374 progress_layout->set_main_axis_alignment(
375 views::BoxLayout::MAIN_AXIS_ALIGNMENT_CENTER);
376 progress_layout->set_cross_axis_alignment(
377 views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER);
378 progress_overlay_->SetLayoutManager(progress_layout);
379
380 progress_overlay_->SetVisible(false);
381 AddChildView(progress_overlay_);
382
383 progress_label_ = new views::Label(l10n_util::GetStringUTF16(
384 IDS_AUTOFILL_CARD_UNMASK_VERIFICATION_IN_PROGRESS));
385 progress_overlay_->AddChildView(progress_label_);
386 }
387
388 void ClosePrompt() { GetWidget()->Close(); }
389
390 bool ExpirationDateIsValid() const {
391 if (!controller_->ShouldRequestExpirationDate())
392 return true;
393
394 return controller_->InputExpirationIsValid(
395 month_input_->GetTextForRow(month_input_->selected_index()),
396 year_input_->GetTextForRow(year_input_->selected_index()));
397 }
398
399 CardUnmaskPromptController* controller_;
400
401 views::View* main_contents_;
402
403 // The error label for permanent errors (where the user can't retry).
404 views::Label* permanent_error_label_;
405
406 DecoratedTextfield* cvc_input_;
407
408 // These will be null when expiration date is not required.
409 views::Combobox* month_input_;
410 views::Combobox* year_input_;
411
412 MonthComboboxModel month_combobox_model_;
413 YearComboboxModel year_combobox_model_;
414
415 // The error label for most errors, which lives beneath the inputs.
416 views::Label* error_label_;
417
418 views::Checkbox* storage_checkbox_;
419
420 views::View* progress_overlay_;
421 views::Label* progress_label_;
422
423 base::WeakPtrFactory<CardUnmaskPromptViews> weak_ptr_factory_;
424
425 DISALLOW_COPY_AND_ASSIGN(CardUnmaskPromptViews);
426 };
427
428 } // namespace
429
430 // static 41 // static
431 CardUnmaskPromptView* CardUnmaskPromptView::CreateAndShow( 42 CardUnmaskPromptView* CardUnmaskPromptView::CreateAndShow(
432 CardUnmaskPromptController* controller) { 43 CardUnmaskPromptController* controller) {
433 CardUnmaskPromptViews* view = new CardUnmaskPromptViews(controller); 44 CardUnmaskPromptViews* view = new CardUnmaskPromptViews(controller);
434 view->Show(); 45 view->Show();
435 return view; 46 return view;
436 } 47 }
437 48
49 CardUnmaskPromptViews::CardUnmaskPromptViews(
50 CardUnmaskPromptController* controller)
51 : controller_(controller),
52 main_contents_(nullptr),
53 permanent_error_label_(nullptr),
54 cvc_input_(nullptr),
55 month_input_(nullptr),
56 year_input_(nullptr),
57 error_label_(nullptr),
58 storage_checkbox_(nullptr),
59 progress_overlay_(nullptr),
60 progress_label_(nullptr),
61 weak_ptr_factory_(this) {
62 }
63
64 CardUnmaskPromptViews::~CardUnmaskPromptViews() {
65 if (controller_)
66 controller_->OnUnmaskDialogClosed();
67 }
68
69 void CardUnmaskPromptViews::Show() {
70 constrained_window::ShowWebModalDialogViews(this,
71 controller_->GetWebContents());
72 }
73
74 void CardUnmaskPromptViews::ControllerGone() {
75 controller_ = nullptr;
76 ClosePrompt();
77 }
78
79 void CardUnmaskPromptViews::DisableAndWaitForVerification() {
80 SetInputsEnabled(false);
81 progress_overlay_->SetVisible(true);
82 GetDialogClientView()->UpdateDialogButtons();
83 Layout();
84 }
85
86 void CardUnmaskPromptViews::GotVerificationResult(
87 const base::string16& error_message,
88 bool allow_retry) {
89 if (error_message.empty()) {
90 progress_label_->SetText(l10n_util::GetStringUTF16(
91 IDS_AUTOFILL_CARD_UNMASK_VERIFICATION_SUCCESS));
92 base::MessageLoop::current()->PostDelayedTask(
93 FROM_HERE, base::Bind(&CardUnmaskPromptViews::ClosePrompt,
94 weak_ptr_factory_.GetWeakPtr()),
95 controller_->GetSuccessMessageDuration());
96 } else {
97 // TODO(estade): it's somewhat jarring when the error comes back too
98 // quickly.
99 progress_overlay_->SetVisible(false);
100
101 if (allow_retry) {
102 SetInputsEnabled(true);
103
104 // If there is more than one input showing, don't mark anything as
105 // invalid since we don't know the location of the problem.
106 if (!controller_->ShouldRequestExpirationDate())
107 cvc_input_->SetInvalid(true);
108
109 // TODO(estade): When do we hide |error_label_|?
110 SetRetriableErrorMessage(error_message);
111 } else {
112 permanent_error_label_->SetText(error_message);
113 permanent_error_label_->SetVisible(true);
114 SetRetriableErrorMessage(base::string16());
115 }
116 GetDialogClientView()->UpdateDialogButtons();
117 }
118
119 Layout();
120 }
121
122 void CardUnmaskPromptViews::SetRetriableErrorMessage(
123 const base::string16& message) {
124 if (message.empty()) {
125 error_label_->SetMultiLine(false);
126 error_label_->SetText(base::ASCIIToUTF16(" "));
127 } else {
128 error_label_->SetMultiLine(true);
129 error_label_->SetText(message);
130 }
131
132 // Update the dialog's size.
133 if (GetWidget() && controller_->GetWebContents()) {
134 constrained_window::UpdateWebContentsModalDialogPosition(
135 GetWidget(), web_modal::WebContentsModalDialogManager::FromWebContents(
136 controller_->GetWebContents())
137 ->delegate()
138 ->GetWebContentsModalDialogHost());
139 }
140 }
141
142 void CardUnmaskPromptViews::SetInputsEnabled(bool enabled) {
143 cvc_input_->SetEnabled(enabled);
144 storage_checkbox_->SetEnabled(enabled);
145
146 if (month_input_)
147 month_input_->SetEnabled(enabled);
148 if (year_input_)
149 year_input_->SetEnabled(enabled);
150 }
151
152 views::View* CardUnmaskPromptViews::GetContentsView() {
153 InitIfNecessary();
154 return this;
155 }
156
157 views::View* CardUnmaskPromptViews::CreateFootnoteView() {
158 // Local storage checkbox and (?) tooltip.
159 views::View* storage_row = new views::View();
160 views::BoxLayout* storage_row_layout = new views::BoxLayout(
161 views::BoxLayout::kHorizontal, kEdgePadding, kEdgePadding, 0);
162 storage_row->SetLayoutManager(storage_row_layout);
163 storage_row->SetBorder(
164 views::Border::CreateSolidSidedBorder(1, 0, 0, 0, kSubtleBorderColor));
165 storage_row->set_background(
166 views::Background::CreateSolidBackground(kShadingColor));
167
168 storage_checkbox_ = new views::Checkbox(l10n_util::GetStringUTF16(
169 IDS_AUTOFILL_CARD_UNMASK_PROMPT_STORAGE_CHECKBOX));
170 storage_checkbox_->SetChecked(controller_->GetStoreLocallyStartState());
171 storage_row->AddChildView(storage_checkbox_);
172 storage_row_layout->SetFlexForView(storage_checkbox_, 1);
173
174 storage_row->AddChildView(new TooltipIcon(l10n_util::GetStringUTF16(
175 IDS_AUTOFILL_CARD_UNMASK_PROMPT_STORAGE_TOOLTIP)));
176
177 return storage_row;
178 }
179
180 gfx::Size CardUnmaskPromptViews::GetPreferredSize() const {
181 // Must hardcode a width so the label knows where to wrap. TODO(estade):
182 // This can lead to a weird looking dialog if we end up getting allocated
183 // more width than we ask for, e.g. if the title is super long.
184 const int kWidth = 375;
185 return gfx::Size(kWidth, GetHeightForWidth(kWidth));
186 }
187
188 void CardUnmaskPromptViews::Layout() {
189 for (int i = 0; i < child_count(); ++i) {
190 child_at(i)->SetBoundsRect(GetContentsBounds());
191 }
192 }
193
194 int CardUnmaskPromptViews::GetHeightForWidth(int width) const {
195 if (!has_children())
196 return 0;
197 const gfx::Insets insets = GetInsets();
198 return main_contents_->GetHeightForWidth(width - insets.width()) +
199 insets.height();
200 }
201
202 void CardUnmaskPromptViews::OnNativeThemeChanged(const ui::NativeTheme* theme) {
203 SkColor bg_color =
204 theme->GetSystemColor(ui::NativeTheme::kColorId_DialogBackground);
205 bg_color = SkColorSetA(bg_color, 0xDD);
206 progress_overlay_->set_background(
207 views::Background::CreateSolidBackground(bg_color));
208 }
209
210 ui::ModalType CardUnmaskPromptViews::GetModalType() const {
211 return ui::MODAL_TYPE_CHILD;
212 }
213
214 base::string16 CardUnmaskPromptViews::GetWindowTitle() const {
215 return controller_->GetWindowTitle();
216 }
217
218 void CardUnmaskPromptViews::DeleteDelegate() {
219 delete this;
220 }
221
222 int CardUnmaskPromptViews::GetDialogButtons() const {
223 return ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL;
224 }
225
226 base::string16 CardUnmaskPromptViews::GetDialogButtonLabel(
227 ui::DialogButton button) const {
228 if (button == ui::DIALOG_BUTTON_OK)
229 return l10n_util::GetStringUTF16(IDS_AUTOFILL_CARD_UNMASK_CONFIRM_BUTTON);
230
231 return DialogDelegateView::GetDialogButtonLabel(button);
232 }
233
234 bool CardUnmaskPromptViews::ShouldDefaultButtonBeBlue() const {
235 return true;
236 }
237
238 bool CardUnmaskPromptViews::IsDialogButtonEnabled(
239 ui::DialogButton button) const {
240 if (button == ui::DIALOG_BUTTON_CANCEL)
241 return true;
242
243 DCHECK_EQ(ui::DIALOG_BUTTON_OK, button);
244
245 return cvc_input_->enabled() &&
246 controller_->InputCvcIsValid(cvc_input_->text()) &&
247 ExpirationDateIsValid();
248 }
249
250 views::View* CardUnmaskPromptViews::GetInitiallyFocusedView() {
251 return cvc_input_;
252 }
253
254 bool CardUnmaskPromptViews::Cancel() {
255 return true;
256 }
257
258 bool CardUnmaskPromptViews::Accept() {
259 if (!controller_)
260 return true;
261
262 controller_->OnUnmaskResponse(
263 cvc_input_->text(),
264 month_input_ ? month_input_->GetTextForRow(month_input_->selected_index())
265 : base::string16(),
266 year_input_ ? year_input_->GetTextForRow(year_input_->selected_index())
267 : base::string16(),
268 storage_checkbox_ ? storage_checkbox_->checked() : false);
269 return false;
270 }
271
272 void CardUnmaskPromptViews::ContentsChanged(
273 views::Textfield* sender,
274 const base::string16& new_contents) {
275 if (controller_->InputCvcIsValid(new_contents))
276 cvc_input_->SetInvalid(false);
277
278 GetDialogClientView()->UpdateDialogButtons();
279 }
280
281 void CardUnmaskPromptViews::OnPerformAction(views::Combobox* combobox) {
282 if (ExpirationDateIsValid()) {
283 if (month_input_->invalid()) {
284 month_input_->SetInvalid(false);
285 year_input_->SetInvalid(false);
286 error_label_->SetMultiLine(false);
287 SetRetriableErrorMessage(base::string16());
288 }
289 } else if (month_input_->selected_index() !=
290 month_combobox_model_.GetDefaultIndex() &&
291 year_input_->selected_index() !=
292 year_combobox_model_.GetDefaultIndex()) {
293 month_input_->SetInvalid(true);
294 year_input_->SetInvalid(true);
295 error_label_->SetMultiLine(true);
296 SetRetriableErrorMessage(l10n_util::GetStringUTF16(
297 IDS_AUTOFILL_CARD_UNMASK_INVALID_EXPIRATION_DATE));
298 }
299
300 GetDialogClientView()->UpdateDialogButtons();
301 }
302
303 void CardUnmaskPromptViews::InitIfNecessary() {
304 if (has_children())
305 return;
306
307 main_contents_ = new views::View();
308 main_contents_->SetLayoutManager(
309 new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 10));
310 AddChildView(main_contents_);
311
312 permanent_error_label_ = new views::Label();
313 permanent_error_label_->set_background(
314 views::Background::CreateSolidBackground(
315 SkColorSetRGB(0xdb, 0x44, 0x37)));
316 permanent_error_label_->SetBorder(
317 views::Border::CreateEmptyBorder(10, kEdgePadding, 10, kEdgePadding));
318 permanent_error_label_->SetEnabledColor(SK_ColorWHITE);
319 permanent_error_label_->SetAutoColorReadabilityEnabled(false);
320 permanent_error_label_->SetVisible(false);
321 permanent_error_label_->SetMultiLine(true);
322 permanent_error_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
323 main_contents_->AddChildView(permanent_error_label_);
324
325 views::View* controls_container = new views::View();
326 controls_container->SetLayoutManager(
327 new views::BoxLayout(views::BoxLayout::kVertical, kEdgePadding, 0, 0));
328 main_contents_->AddChildView(controls_container);
329
330 views::Label* instructions =
331 new views::Label(controller_->GetInstructionsMessage());
332
333 instructions->SetMultiLine(true);
334 instructions->SetHorizontalAlignment(gfx::ALIGN_LEFT);
335 instructions->SetBorder(views::Border::CreateEmptyBorder(0, 0, 15, 0));
336 controls_container->AddChildView(instructions);
337
338 views::View* input_row = new views::View();
339 input_row->SetLayoutManager(
340 new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 5));
341 controls_container->AddChildView(input_row);
342
343 if (controller_->ShouldRequestExpirationDate()) {
344 month_input_ = new views::Combobox(&month_combobox_model_);
345 month_input_->set_listener(this);
346 input_row->AddChildView(month_input_);
347 input_row->AddChildView(new views::Label(l10n_util::GetStringUTF16(
348 IDS_AUTOFILL_CARD_UNMASK_EXPIRATION_DATE_SEPARATOR)));
349 year_input_ = new views::Combobox(&year_combobox_model_);
350 year_input_->set_listener(this);
351 input_row->AddChildView(year_input_);
352 input_row->AddChildView(new views::Label(base::ASCIIToUTF16(" ")));
353 }
354
355 cvc_input_ = new DecoratedTextfield(
356 base::string16(),
357 l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_CVC), this);
358 cvc_input_->set_default_width_in_chars(8);
359 input_row->AddChildView(cvc_input_);
360
361 views::ImageView* cvc_image = new views::ImageView();
362 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
363 cvc_image->SetImage(rb.GetImageSkiaNamed(controller_->GetCvcImageRid()));
364 input_row->AddChildView(cvc_image);
365
366 // Reserve vertical space for the error label, assuming it's one line.
367 error_label_ = new views::Label(base::ASCIIToUTF16(" "));
368 error_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
369 error_label_->SetEnabledColor(kWarningColor);
370 error_label_->SetBorder(views::Border::CreateEmptyBorder(3, 0, 5, 0));
371 controls_container->AddChildView(error_label_);
372
373 progress_overlay_ = new views::View();
374 views::BoxLayout* progress_layout =
375 new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0);
376 progress_layout->set_main_axis_alignment(
377 views::BoxLayout::MAIN_AXIS_ALIGNMENT_CENTER);
378 progress_layout->set_cross_axis_alignment(
379 views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER);
380 progress_overlay_->SetLayoutManager(progress_layout);
381
382 progress_overlay_->SetVisible(false);
383 AddChildView(progress_overlay_);
384
385 progress_label_ = new views::Label(l10n_util::GetStringUTF16(
386 IDS_AUTOFILL_CARD_UNMASK_VERIFICATION_IN_PROGRESS));
387 progress_overlay_->AddChildView(progress_label_);
388 }
389
390 bool CardUnmaskPromptViews::ExpirationDateIsValid() const {
391 if (!controller_->ShouldRequestExpirationDate())
392 return true;
393
394 return controller_->InputExpirationIsValid(
395 month_input_->GetTextForRow(month_input_->selected_index()),
396 year_input_->GetTextForRow(year_input_->selected_index()));
397 }
398
399 void CardUnmaskPromptViews::ClosePrompt() {
400 GetWidget()->Close();
401 }
402
438 } // namespace autofill 403 } // namespace autofill
OLDNEW
« no previous file with comments | « chrome/browser/ui/views/autofill/card_unmask_prompt_views.h ('k') | chrome/chrome_browser_ui.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698