Revert of Add button to add new FSP services to Files app. (patchset #8 id:140001...
[chromium-blink-merge.git] / chrome / browser / ui / views / autofill / card_unmask_prompt_views.cc
blob4df074f726531b7076b9ca9683d68c5d70427f33
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
3 // found in the LICENSE file.
5 #include "chrome/browser/ui/views/autofill/card_unmask_prompt_views.h"
7 #include "base/basictypes.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "chrome/browser/ui/autofill/autofill_dialog_types.h"
10 #include "chrome/browser/ui/autofill/card_unmask_prompt_controller.h"
11 #include "chrome/browser/ui/views/autofill/decorated_textfield.h"
12 #include "chrome/browser/ui/views/autofill/tooltip_icon.h"
13 #include "chrome/grit/generated_resources.h"
14 #include "components/constrained_window/constrained_window_views.h"
15 #include "components/web_modal/web_contents_modal_dialog_host.h"
16 #include "components/web_modal/web_contents_modal_dialog_manager.h"
17 #include "components/web_modal/web_contents_modal_dialog_manager_delegate.h"
18 #include "grit/theme_resources.h"
19 #include "third_party/skia/include/core/SkColor.h"
20 #include "ui/base/l10n/l10n_util.h"
21 #include "ui/base/resource/resource_bundle.h"
22 #include "ui/gfx/canvas.h"
23 #include "ui/views/background.h"
24 #include "ui/views/controls/button/checkbox.h"
25 #include "ui/views/controls/combobox/combobox.h"
26 #include "ui/views/controls/image_view.h"
27 #include "ui/views/controls/label.h"
28 #include "ui/views/controls/throbber.h"
29 #include "ui/views/layout/box_layout.h"
30 #include "ui/views/widget/widget.h"
31 #include "ui/views/window/dialog_client_view.h"
33 namespace autofill {
35 // The number of pixels of blank space on the outer horizontal edges of the
36 // dialog.
37 const int kEdgePadding = 19;
39 SkColor kGreyTextColor = SkColorSetRGB(0x64, 0x64, 0x64);
41 // static
42 CardUnmaskPromptView* CardUnmaskPromptView::CreateAndShow(
43 CardUnmaskPromptController* controller) {
44 CardUnmaskPromptViews* view = new CardUnmaskPromptViews(controller);
45 view->Show();
46 return view;
49 CardUnmaskPromptViews::CardUnmaskPromptViews(
50 CardUnmaskPromptController* controller)
51 : controller_(controller),
52 main_contents_(nullptr),
53 permanent_error_label_(nullptr),
54 input_row_(nullptr),
55 cvc_input_(nullptr),
56 month_input_(nullptr),
57 year_input_(nullptr),
58 error_icon_(nullptr),
59 error_label_(nullptr),
60 storage_row_(nullptr),
61 storage_checkbox_(nullptr),
62 progress_overlay_(nullptr),
63 progress_throbber_(nullptr),
64 progress_label_(nullptr),
65 overlay_animation_(this),
66 weak_ptr_factory_(this) {
69 CardUnmaskPromptViews::~CardUnmaskPromptViews() {
70 if (controller_)
71 controller_->OnUnmaskDialogClosed();
74 void CardUnmaskPromptViews::Show() {
75 constrained_window::ShowWebModalDialogViews(this,
76 controller_->GetWebContents());
79 void CardUnmaskPromptViews::ControllerGone() {
80 controller_ = nullptr;
81 ClosePrompt();
84 void CardUnmaskPromptViews::DisableAndWaitForVerification() {
85 SetInputsEnabled(false);
86 progress_overlay_->SetOpacity(0.0);
87 progress_overlay_->SetVisible(true);
88 progress_throbber_->Start();
89 overlay_animation_.Show();
90 GetDialogClientView()->UpdateDialogButtons();
91 Layout();
94 void CardUnmaskPromptViews::GotVerificationResult(
95 const base::string16& error_message,
96 bool allow_retry) {
97 progress_throbber_->Stop();
98 if (error_message.empty()) {
99 progress_label_->SetText(l10n_util::GetStringUTF16(
100 IDS_AUTOFILL_CARD_UNMASK_VERIFICATION_SUCCESS));
101 progress_throbber_->SetChecked(true);
102 base::MessageLoop::current()->PostDelayedTask(
103 FROM_HERE, base::Bind(&CardUnmaskPromptViews::ClosePrompt,
104 weak_ptr_factory_.GetWeakPtr()),
105 controller_->GetSuccessMessageDuration());
106 } else {
107 // TODO(estade): it's somewhat jarring when the error comes back too
108 // quickly.
109 overlay_animation_.Reset();
110 storage_row_->SetOpacity(1.0);
111 progress_overlay_->SetVisible(false);
113 if (allow_retry) {
114 SetInputsEnabled(true);
116 // If there is more than one input showing, don't mark anything as
117 // invalid since we don't know the location of the problem.
118 if (!controller_->ShouldRequestExpirationDate())
119 cvc_input_->SetInvalid(true);
121 // TODO(estade): When do we hide |error_label_|?
122 SetRetriableErrorMessage(error_message);
123 } else {
124 permanent_error_label_->SetText(error_message);
125 permanent_error_label_->SetVisible(true);
126 SetRetriableErrorMessage(base::string16());
128 GetDialogClientView()->UpdateDialogButtons();
131 Layout();
134 void CardUnmaskPromptViews::SetRetriableErrorMessage(
135 const base::string16& message) {
136 if (message.empty()) {
137 error_label_->SetMultiLine(false);
138 error_label_->SetText(base::ASCIIToUTF16(" "));
139 error_icon_->SetVisible(false);
140 } else {
141 error_label_->SetMultiLine(true);
142 error_label_->SetText(message);
143 error_icon_->SetVisible(true);
146 // Update the dialog's size.
147 if (GetWidget() && controller_->GetWebContents()) {
148 constrained_window::UpdateWebContentsModalDialogPosition(
149 GetWidget(), web_modal::WebContentsModalDialogManager::FromWebContents(
150 controller_->GetWebContents())
151 ->delegate()
152 ->GetWebContentsModalDialogHost());
155 Layout();
158 void CardUnmaskPromptViews::SetInputsEnabled(bool enabled) {
159 cvc_input_->SetEnabled(enabled);
160 if (storage_checkbox_)
161 storage_checkbox_->SetEnabled(enabled);
162 if (month_input_)
163 month_input_->SetEnabled(enabled);
164 if (year_input_)
165 year_input_->SetEnabled(enabled);
168 views::View* CardUnmaskPromptViews::GetContentsView() {
169 InitIfNecessary();
170 return this;
173 views::View* CardUnmaskPromptViews::CreateFootnoteView() {
174 if (!controller_->CanStoreLocally())
175 return nullptr;
177 // Local storage checkbox and (?) tooltip.
178 storage_row_ = new FadeOutView();
179 views::BoxLayout* storage_row_layout = new views::BoxLayout(
180 views::BoxLayout::kHorizontal, kEdgePadding, kEdgePadding, 0);
181 storage_row_->SetLayoutManager(storage_row_layout);
182 storage_row_->SetBorder(
183 views::Border::CreateSolidSidedBorder(1, 0, 0, 0, kSubtleBorderColor));
184 storage_row_->set_background(
185 views::Background::CreateSolidBackground(kLightShadingColor));
187 storage_checkbox_ = new views::Checkbox(l10n_util::GetStringUTF16(
188 IDS_AUTOFILL_CARD_UNMASK_PROMPT_STORAGE_CHECKBOX));
189 storage_checkbox_->SetChecked(controller_->GetStoreLocallyStartState());
190 storage_row_->AddChildView(storage_checkbox_);
191 storage_row_layout->SetFlexForView(storage_checkbox_, 1);
193 storage_row_->AddChildView(new TooltipIcon(l10n_util::GetStringUTF16(
194 IDS_AUTOFILL_CARD_UNMASK_PROMPT_STORAGE_TOOLTIP)));
196 return storage_row_;
199 gfx::Size CardUnmaskPromptViews::GetPreferredSize() const {
200 // Must hardcode a width so the label knows where to wrap.
201 const int kWidth = 375;
202 return gfx::Size(kWidth, GetHeightForWidth(kWidth));
205 void CardUnmaskPromptViews::Layout() {
206 gfx::Rect contents_bounds = GetContentsBounds();
207 main_contents_->SetBoundsRect(contents_bounds);
209 // The progress overlay extends from the top of the input row
210 // to the bottom of the content area.
211 gfx::RectF input_rect = input_row_->GetContentsBounds();
212 View::ConvertRectToTarget(input_row_, this, &input_rect);
213 input_rect.set_height(contents_bounds.height());
214 contents_bounds.Intersect(gfx::ToNearestRect(input_rect));
215 progress_overlay_->SetBoundsRect(contents_bounds);
218 int CardUnmaskPromptViews::GetHeightForWidth(int width) const {
219 if (!has_children())
220 return 0;
221 const gfx::Insets insets = GetInsets();
222 return main_contents_->GetHeightForWidth(width - insets.width()) +
223 insets.height();
226 void CardUnmaskPromptViews::OnNativeThemeChanged(const ui::NativeTheme* theme) {
227 SkColor bg_color =
228 theme->GetSystemColor(ui::NativeTheme::kColorId_DialogBackground);
229 progress_overlay_->set_background(
230 views::Background::CreateSolidBackground(bg_color));
231 progress_label_->SetBackgroundColor(bg_color);
234 ui::ModalType CardUnmaskPromptViews::GetModalType() const {
235 return ui::MODAL_TYPE_CHILD;
238 base::string16 CardUnmaskPromptViews::GetWindowTitle() const {
239 return controller_->GetWindowTitle();
242 void CardUnmaskPromptViews::DeleteDelegate() {
243 delete this;
246 int CardUnmaskPromptViews::GetDialogButtons() const {
247 return ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL;
250 base::string16 CardUnmaskPromptViews::GetDialogButtonLabel(
251 ui::DialogButton button) const {
252 if (button == ui::DIALOG_BUTTON_OK)
253 return l10n_util::GetStringUTF16(IDS_AUTOFILL_CARD_UNMASK_CONFIRM_BUTTON);
255 return DialogDelegateView::GetDialogButtonLabel(button);
258 bool CardUnmaskPromptViews::ShouldDefaultButtonBeBlue() const {
259 return true;
262 bool CardUnmaskPromptViews::IsDialogButtonEnabled(
263 ui::DialogButton button) const {
264 if (button == ui::DIALOG_BUTTON_CANCEL)
265 return true;
267 DCHECK_EQ(ui::DIALOG_BUTTON_OK, button);
269 return cvc_input_->enabled() &&
270 controller_->InputCvcIsValid(cvc_input_->text()) &&
271 ExpirationDateIsValid();
274 views::View* CardUnmaskPromptViews::GetInitiallyFocusedView() {
275 return cvc_input_;
278 bool CardUnmaskPromptViews::Cancel() {
279 return true;
282 bool CardUnmaskPromptViews::Accept() {
283 if (!controller_)
284 return true;
286 controller_->OnUnmaskResponse(
287 cvc_input_->text(),
288 month_input_ ? month_input_->GetTextForRow(month_input_->selected_index())
289 : base::string16(),
290 year_input_ ? year_input_->GetTextForRow(year_input_->selected_index())
291 : base::string16(),
292 storage_checkbox_ ? storage_checkbox_->checked() : false);
293 return false;
296 void CardUnmaskPromptViews::ContentsChanged(
297 views::Textfield* sender,
298 const base::string16& new_contents) {
299 if (controller_->InputCvcIsValid(new_contents))
300 cvc_input_->SetInvalid(false);
302 GetDialogClientView()->UpdateDialogButtons();
305 void CardUnmaskPromptViews::OnPerformAction(views::Combobox* combobox) {
306 if (ExpirationDateIsValid()) {
307 if (month_input_->invalid()) {
308 month_input_->SetInvalid(false);
309 year_input_->SetInvalid(false);
310 SetRetriableErrorMessage(base::string16());
312 } else if (month_input_->selected_index() !=
313 month_combobox_model_.GetDefaultIndex() &&
314 year_input_->selected_index() !=
315 year_combobox_model_.GetDefaultIndex()) {
316 month_input_->SetInvalid(true);
317 year_input_->SetInvalid(true);
318 SetRetriableErrorMessage(l10n_util::GetStringUTF16(
319 IDS_AUTOFILL_CARD_UNMASK_INVALID_EXPIRATION_DATE));
322 GetDialogClientView()->UpdateDialogButtons();
325 void CardUnmaskPromptViews::AnimationProgressed(
326 const gfx::Animation* animation) {
327 progress_overlay_->SetOpacity(animation->GetCurrentValue());
328 storage_row_->SetOpacity(1.0 - animation->GetCurrentValue());
331 void CardUnmaskPromptViews::InitIfNecessary() {
332 if (has_children())
333 return;
335 main_contents_ = new views::View();
336 main_contents_->SetLayoutManager(
337 new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 12));
338 AddChildView(main_contents_);
340 permanent_error_label_ = new views::Label();
341 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
342 permanent_error_label_->SetFontList(
343 rb.GetFontList(ui::ResourceBundle::BoldFont));
344 permanent_error_label_->set_background(
345 views::Background::CreateSolidBackground(kWarningColor));
346 permanent_error_label_->SetBorder(
347 views::Border::CreateEmptyBorder(12, kEdgePadding, 12, kEdgePadding));
348 permanent_error_label_->SetEnabledColor(SK_ColorWHITE);
349 permanent_error_label_->SetAutoColorReadabilityEnabled(false);
350 permanent_error_label_->SetVisible(false);
351 permanent_error_label_->SetMultiLine(true);
352 permanent_error_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
353 main_contents_->AddChildView(permanent_error_label_);
355 views::View* controls_container = new views::View();
356 controls_container->SetLayoutManager(
357 new views::BoxLayout(views::BoxLayout::kVertical, kEdgePadding, 0, 0));
358 main_contents_->AddChildView(controls_container);
360 views::Label* instructions =
361 new views::Label(controller_->GetInstructionsMessage());
362 instructions->SetEnabledColor(kGreyTextColor);
363 instructions->SetMultiLine(true);
364 instructions->SetHorizontalAlignment(gfx::ALIGN_LEFT);
365 instructions->SetBorder(views::Border::CreateEmptyBorder(0, 0, 16, 0));
366 controls_container->AddChildView(instructions);
368 input_row_ = new views::View();
369 input_row_->SetLayoutManager(
370 new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 5));
371 controls_container->AddChildView(input_row_);
373 if (controller_->ShouldRequestExpirationDate()) {
374 month_input_ = new views::Combobox(&month_combobox_model_);
375 month_input_->set_listener(this);
376 input_row_->AddChildView(month_input_);
377 views::Label* separator = new views::Label(l10n_util::GetStringUTF16(
378 IDS_AUTOFILL_CARD_UNMASK_EXPIRATION_DATE_SEPARATOR));
379 separator->SetEnabledColor(kGreyTextColor);
380 input_row_->AddChildView(separator);
381 year_input_ = new views::Combobox(&year_combobox_model_);
382 year_input_->set_listener(this);
383 input_row_->AddChildView(year_input_);
384 input_row_->AddChildView(new views::Label(base::ASCIIToUTF16(" ")));
387 cvc_input_ = new DecoratedTextfield(
388 base::string16(),
389 l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_CVC), this);
390 cvc_input_->set_default_width_in_chars(8);
391 input_row_->AddChildView(cvc_input_);
393 views::ImageView* cvc_image = new views::ImageView();
394 cvc_image->SetImage(rb.GetImageSkiaNamed(controller_->GetCvcImageRid()));
395 input_row_->AddChildView(cvc_image);
397 views::View* temporary_error = new views::View();
398 views::BoxLayout* temporary_error_layout =
399 new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 4);
400 temporary_error->SetLayoutManager(temporary_error_layout);
401 temporary_error_layout->set_cross_axis_alignment(
402 views::BoxLayout::CROSS_AXIS_ALIGNMENT_START);
403 temporary_error->SetBorder(views::Border::CreateEmptyBorder(8, 0, 0, 0));
404 controls_container->AddChildView(temporary_error);
406 error_icon_ = new views::ImageView();
407 error_icon_->SetVisible(false);
408 error_icon_->SetImage(ui::ResourceBundle::GetSharedInstance()
409 .GetImageNamed(IDR_ALERT_RED)
410 .ToImageSkia());
411 temporary_error->AddChildView(error_icon_);
413 // Reserve vertical space for the error label, assuming it's one line.
414 error_label_ = new views::Label(base::ASCIIToUTF16(" "));
415 error_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
416 error_label_->SetEnabledColor(kWarningColor);
417 temporary_error->AddChildView(error_label_);
418 temporary_error_layout->SetFlexForView(error_label_, 1);
420 progress_overlay_ = new FadeOutView();
421 progress_overlay_->set_fade_everything(true);
422 views::BoxLayout* progress_layout =
423 new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 5);
424 progress_layout->set_cross_axis_alignment(
425 views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER);
426 progress_layout->set_main_axis_alignment(
427 views::BoxLayout::MAIN_AXIS_ALIGNMENT_CENTER);
428 progress_overlay_->SetLayoutManager(progress_layout);
430 progress_overlay_->SetVisible(false);
431 AddChildView(progress_overlay_);
433 progress_throbber_ = new views::CheckmarkThrobber();
434 progress_overlay_->AddChildView(progress_throbber_);
436 progress_label_ = new views::Label(l10n_util::GetStringUTF16(
437 IDS_AUTOFILL_CARD_UNMASK_VERIFICATION_IN_PROGRESS));
438 // Material blue. TODO(estade): find an appropriate place for this color.
439 progress_label_->SetEnabledColor(SkColorSetRGB(0x42, 0x85, 0xF4));
440 progress_overlay_->AddChildView(progress_label_);
443 bool CardUnmaskPromptViews::ExpirationDateIsValid() const {
444 if (!controller_->ShouldRequestExpirationDate())
445 return true;
447 return controller_->InputExpirationIsValid(
448 month_input_->GetTextForRow(month_input_->selected_index()),
449 year_input_->GetTextForRow(year_input_->selected_index()));
452 void CardUnmaskPromptViews::ClosePrompt() {
453 GetWidget()->Close();
456 CardUnmaskPromptViews::FadeOutView::FadeOutView()
457 : fade_everything_(false), opacity_(1.0) {
459 CardUnmaskPromptViews::FadeOutView::~FadeOutView() {
462 void CardUnmaskPromptViews::FadeOutView::PaintChildren(
463 const PaintContext& context) {
464 if (opacity_ > 0.99)
465 return views::View::PaintChildren(context);
467 gfx::Canvas* canvas = context.canvas();
468 canvas->SaveLayerAlpha(0xff * opacity_);
469 views::View::PaintChildren(context);
470 canvas->Restore();
473 void CardUnmaskPromptViews::FadeOutView::OnPaint(gfx::Canvas* canvas) {
474 if (!fade_everything_ || opacity_ > 0.99)
475 return views::View::OnPaint(canvas);
477 canvas->SaveLayerAlpha(0xff * opacity_);
478 views::View::OnPaint(canvas);
479 canvas->Restore();
482 void CardUnmaskPromptViews::FadeOutView::SetOpacity(double opacity) {
483 opacity_ = opacity;
484 SchedulePaint();
487 } // namespace autofill