Commit b908f689 authored by estade's avatar estade Committed by Commit bot

Add a regression test for the CardUnmaskPrompt.

BUG=none

Review URL: https://codereview.chromium.org/1020013003

Cr-Commit-Position: refs/heads/master@{#321634}
parent d60174a4
......@@ -7,6 +7,10 @@
#include "base/strings/string16.h"
namespace base {
class TimeDelta;
}
namespace content {
class WebContents;
}
......@@ -32,6 +36,7 @@ class CardUnmaskPromptController {
virtual bool InputCvcIsValid(const base::string16& input_text) const = 0;
virtual bool InputExpirationIsValid(const base::string16& month,
const base::string16& year) const = 0;
virtual base::TimeDelta GetSuccessMessageDuration() const = 0;
};
} // namespace autofill
......
......@@ -36,10 +36,6 @@ CardUnmaskPromptControllerImpl::~CardUnmaskPromptControllerImpl() {
card_unmask_view_->ControllerGone();
}
CardUnmaskPromptView* CardUnmaskPromptControllerImpl::CreateAndShowView() {
return CardUnmaskPromptView::CreateAndShow(this);
}
void CardUnmaskPromptControllerImpl::ShowPrompt(
const CreditCard& card,
base::WeakPtr<CardUnmaskDelegate> delegate) {
......@@ -261,6 +257,15 @@ bool CardUnmaskPromptControllerImpl::InputExpirationIsValid(
return month_value >= now.month;
}
base::TimeDelta CardUnmaskPromptControllerImpl::GetSuccessMessageDuration()
const {
return base::TimeDelta::FromMilliseconds(500);
}
CardUnmaskPromptView* CardUnmaskPromptControllerImpl::CreateAndShowView() {
return CardUnmaskPromptView::CreateAndShow(this);
}
void CardUnmaskPromptControllerImpl::LoadRiskFingerprint() {
LoadRiskData(
0, web_contents_,
......
......@@ -43,19 +43,20 @@ class CardUnmaskPromptControllerImpl : public CardUnmaskPromptController {
bool InputCvcIsValid(const base::string16& input_text) const override;
bool InputExpirationIsValid(const base::string16& month,
const base::string16& year) const override;
base::TimeDelta GetSuccessMessageDuration() const override;
protected:
// Virtual so tests can suppress it.
virtual CardUnmaskPromptView* CreateAndShowView();
virtual void LoadRiskFingerprint();
virtual void OnDidLoadRiskFingerprint(const std::string& risk_data);
// Protected so tests can call it.
void OnDidLoadRiskFingerprint(const std::string& risk_data);
// Exposed for testing.
CardUnmaskPromptView* view() { return card_unmask_view_; }
private:
bool AllowsRetry(AutofillClient::GetRealPanResult result);
void LogOnCloseEvents();
......
......@@ -5,6 +5,8 @@
#ifndef CHROME_BROWSER_UI_AUTOFILL_CARD_UNMASK_PROMPT_VIEW_H_
#define CHROME_BROWSER_UI_AUTOFILL_CARD_UNMASK_PROMPT_VIEW_H_
#include "base/strings/string16.h"
namespace autofill {
class CardUnmaskPromptController;
......
......@@ -4,7 +4,9 @@
#include "base/guid.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ui/autofill/card_unmask_prompt_controller_impl.h"
#include "chrome/browser/ui/autofill/card_unmask_prompt_view_tester.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/test/base/in_process_browser_test.h"
......@@ -24,14 +26,20 @@ class TestCardUnmaskDelegate : public CardUnmaskDelegate {
virtual ~TestCardUnmaskDelegate() {}
// CardUnmaskDelegate implementation.
void OnUnmaskResponse(const UnmaskResponse& response) override {}
void OnUnmaskResponse(const UnmaskResponse& response) override {
response_ = response;
}
void OnUnmaskPromptClosed() override {}
base::WeakPtr<TestCardUnmaskDelegate> GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
UnmaskResponse response() { return response_; }
private:
UnmaskResponse response_;
base::WeakPtrFactory<TestCardUnmaskDelegate> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(TestCardUnmaskDelegate);
......@@ -47,24 +55,20 @@ class TestCardUnmaskPromptController : public CardUnmaskPromptControllerImpl {
weak_factory_(this) {}
// CardUnmaskPromptControllerImpl implementation.
void OnDidLoadRiskFingerprint(const std::string& risk_data) override {
CardUnmaskPromptControllerImpl::OnDidLoadRiskFingerprint(risk_data);
// Call Quit() from here rather than from OnUnmaskDialogClosed().
// FingerprintDataLoader starts several async tasks that take a while to
// complete. If Quit() is called before FingerprintDataLoader is all done
// then LeakTracker will detect that some resources have not been freed
// and cause the browser test to fail. This is not a real leak though -
// normally the async tasks would complete and encounter weak callbacks.
runner_->Quit();
base::TimeDelta GetSuccessMessageDuration() const override {
return base::TimeDelta::FromMilliseconds(10);
}
void RunMessageLoop() { runner_->Run(); }
void LoadRiskFingerprint() override {
OnDidLoadRiskFingerprint(std::string("risk_data"));
}
base::WeakPtr<TestCardUnmaskPromptController> GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
using CardUnmaskPromptControllerImpl::view;
private:
scoped_refptr<content::MessageLoopRunner> runner_;
base::WeakPtrFactory<TestCardUnmaskPromptController> weak_factory_;
......@@ -88,10 +92,11 @@ class CardUnmaskPromptViewBrowserTest : public InProcessBrowserTest {
TestCardUnmaskPromptController* controller() { return controller_.get(); }
TestCardUnmaskDelegate* delegate() { return delegate_.get(); }
private:
protected:
// This member must outlive the controller.
scoped_refptr<content::MessageLoopRunner> runner_;
private:
scoped_ptr<TestCardUnmaskPromptController> controller_;
scoped_ptr<TestCardUnmaskDelegate> delegate_;
......@@ -99,14 +104,34 @@ class CardUnmaskPromptViewBrowserTest : public InProcessBrowserTest {
};
IN_PROC_BROWSER_TEST_F(CardUnmaskPromptViewBrowserTest, DisplayUI) {
CreditCard credit_card(CreditCard::MASKED_SERVER_CARD, "a123");
test::SetCreditCardInfo(&credit_card, "Bonnie Parker",
"2109" /* Mastercard */, "12", "2012");
credit_card.SetTypeForMaskedCard(kMasterCard);
controller()->ShowPrompt(test::GetMaskedServerCard(),
delegate()->GetWeakPtr());
}
controller()->ShowPrompt(credit_card, delegate()->GetWeakPtr());
controller()->RunMessageLoop();
// TODO(bondd): bring up on Mac.
#if !defined(OS_MACOSX)
// Makes sure the user can close the dialog while the verification success
// message is showing.
IN_PROC_BROWSER_TEST_F(CardUnmaskPromptViewBrowserTest,
EarlyCloseAfterSuccess) {
controller()->ShowPrompt(test::GetMaskedServerCard(),
delegate()->GetWeakPtr());
controller()->OnUnmaskResponse(base::ASCIIToUTF16("123"),
base::ASCIIToUTF16("10"),
base::ASCIIToUTF16("19"), false);
EXPECT_EQ(base::ASCIIToUTF16("123"), delegate()->response().cvc);
controller()->OnVerificationResult(AutofillClient::SUCCESS);
// Simulate the user clicking [x] before the "Success!" message disappears.
CardUnmaskPromptViewTester::For(controller()->view())->Close();
// Wait a little while; there should be no crash.
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE, base::Bind(&content::MessageLoopRunner::Quit,
base::Unretained(runner_.get())),
2 * controller()->GetSuccessMessageDuration());
runner_->Run();
}
#endif
} // namespace
......
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_AUTOFILL_CARD_UNMASK_PROMPT_VIEW_TESTER_H_
#define CHROME_BROWSER_UI_AUTOFILL_CARD_UNMASK_PROMPT_VIEW_TESTER_H_
#include "base/memory/scoped_ptr.h"
namespace content {
class WebContents;
}
namespace autofill {
class CardUnmaskPromptView;
// Functionality that helps to test an AutofillCardUnmaskPromptView.
class CardUnmaskPromptViewTester {
public:
// Gets a AutofillCardUnmaskPromptViewTester for |view|.
static scoped_ptr<CardUnmaskPromptViewTester> For(CardUnmaskPromptView* view);
virtual ~CardUnmaskPromptViewTester() {}
virtual void Close() = 0;
};
} // namespace autofill
#endif // CHROME_BROWSER_UI_AUTOFILL_CARD_UNMASK_PROMPT_VIEW_TESTER_H_
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/views/autofill/card_unmask_prompt_view_tester_views.h"
#include "chrome/browser/ui/views/autofill/card_unmask_prompt_views.h"
namespace autofill {
// static
scoped_ptr<CardUnmaskPromptViewTester> CardUnmaskPromptViewTester::For(
CardUnmaskPromptView* view) {
return scoped_ptr<CardUnmaskPromptViewTester>(
new CardUnmaskPromptViewTesterViews(
static_cast<CardUnmaskPromptViews*>(view)));
}
// Class that facilitates testing.
CardUnmaskPromptViewTesterViews::CardUnmaskPromptViewTesterViews(
CardUnmaskPromptViews* view)
: view_(view) {
}
CardUnmaskPromptViewTesterViews::~CardUnmaskPromptViewTesterViews() {
}
void CardUnmaskPromptViewTesterViews::Close() {
view_->ClosePrompt();
}
} // namespace autofill
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_VIEWS_AUTOFILL_CARD_UNMASK_PROMPT_VIEW_TESTER_VIEWS_H_
#define CHROME_BROWSER_UI_VIEWS_AUTOFILL_CARD_UNMASK_PROMPT_VIEW_TESTER_VIEWS_H_
#include "chrome/browser/ui/autofill/card_unmask_prompt_view_tester.h"
namespace autofill {
class CardUnmaskPromptViews;
// Class that facilitates testing a CardUnmaskPromptViews.
class CardUnmaskPromptViewTesterViews : public CardUnmaskPromptViewTester {
public:
explicit CardUnmaskPromptViewTesterViews(CardUnmaskPromptViews* view);
~CardUnmaskPromptViewTesterViews() override;
void Close() override;
private:
CardUnmaskPromptViews* view_;
DISALLOW_COPY_AND_ASSIGN(CardUnmaskPromptViewTesterViews);
};
} // namespace autofill
#endif // CHROME_BROWSER_UI_VIEWS_AUTOFILL_CARD_UNMASK_PROMPT_VIEW_TESTER_VIEWS_H_
......@@ -2,12 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/views/autofill/card_unmask_prompt_views.h"
#include "base/basictypes.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ui/autofill/autofill_dialog_models.h"
#include "chrome/browser/ui/autofill/autofill_dialog_types.h"
#include "chrome/browser/ui/autofill/card_unmask_prompt_controller.h"
#include "chrome/browser/ui/autofill/card_unmask_prompt_view.h"
#include "chrome/browser/ui/views/autofill/decorated_textfield.h"
#include "chrome/browser/ui/views/autofill/tooltip_icon.h"
#include "chrome/grit/generated_resources.h"
......@@ -22,19 +22,14 @@
#include "ui/views/background.h"
#include "ui/views/controls/button/checkbox.h"
#include "ui/views/controls/combobox/combobox.h"
#include "ui/views/controls/combobox/combobox_listener.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
#include "ui/views/controls/textfield/textfield_controller.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/widget/widget.h"
#include "ui/views/window/dialog_client_view.h"
#include "ui/views/window/dialog_delegate.h"
namespace autofill {
namespace {
// The number of pixels of blank space on the outer horizontal edges of the
// dialog.
const int kEdgePadding = 19;
......@@ -43,396 +38,366 @@ const int kEdgePadding = 19;
SkColor kShadingColor = SkColorSetARGB(7, 0, 0, 0);
SkColor kSubtleBorderColor = SkColorSetARGB(10, 0, 0, 0);
class CardUnmaskPromptViews : public CardUnmaskPromptView,
views::ComboboxListener,
views::DialogDelegateView,
views::TextfieldController {
public:
explicit CardUnmaskPromptViews(CardUnmaskPromptController* controller)
: controller_(controller),
main_contents_(nullptr),
permanent_error_label_(nullptr),
cvc_input_(nullptr),
month_input_(nullptr),
year_input_(nullptr),
error_label_(nullptr),
storage_checkbox_(nullptr),
progress_overlay_(nullptr),
progress_label_(nullptr),
weak_ptr_factory_(this) {}
~CardUnmaskPromptViews() override {
if (controller_)
controller_->OnUnmaskDialogClosed();
}
// static
CardUnmaskPromptView* CardUnmaskPromptView::CreateAndShow(
CardUnmaskPromptController* controller) {
CardUnmaskPromptViews* view = new CardUnmaskPromptViews(controller);
view->Show();
return view;
}
void Show() {
constrained_window::ShowWebModalDialogViews(this,
controller_->GetWebContents());
}
CardUnmaskPromptViews::CardUnmaskPromptViews(
CardUnmaskPromptController* controller)
: controller_(controller),
main_contents_(nullptr),
permanent_error_label_(nullptr),
cvc_input_(nullptr),
month_input_(nullptr),
year_input_(nullptr),
error_label_(nullptr),
storage_checkbox_(nullptr),
progress_overlay_(nullptr),
progress_label_(nullptr),
weak_ptr_factory_(this) {
}
// CardUnmaskPromptView
void ControllerGone() override {
controller_ = nullptr;
ClosePrompt();
}
CardUnmaskPromptViews::~CardUnmaskPromptViews() {
if (controller_)
controller_->OnUnmaskDialogClosed();
}
void DisableAndWaitForVerification() override {
SetInputsEnabled(false);
progress_overlay_->SetVisible(true);
GetDialogClientView()->UpdateDialogButtons();
Layout();
}
void CardUnmaskPromptViews::Show() {
constrained_window::ShowWebModalDialogViews(this,
controller_->GetWebContents());
}
void GotVerificationResult(const base::string16& error_message,
bool allow_retry) override {
if (error_message.empty()) {
progress_label_->SetText(l10n_util::GetStringUTF16(
IDS_AUTOFILL_CARD_UNMASK_VERIFICATION_SUCCESS));
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE, base::Bind(&CardUnmaskPromptViews::ClosePrompt,
weak_ptr_factory_.GetWeakPtr()),
base::TimeDelta::FromSeconds(1));
} else {
// TODO(estade): it's somewhat jarring when the error comes back too
// quickly.
progress_overlay_->SetVisible(false);
if (allow_retry) {
SetInputsEnabled(true);
// If there is more than one input showing, don't mark anything as
// invalid since we don't know the location of the problem.
if (!controller_->ShouldRequestExpirationDate())
cvc_input_->SetInvalid(true);
// TODO(estade): When do we hide |error_label_|?
SetRetriableErrorMessage(error_message);
} else {
permanent_error_label_->SetText(error_message);
permanent_error_label_->SetVisible(true);
SetRetriableErrorMessage(base::string16());
}
GetDialogClientView()->UpdateDialogButtons();
}
void CardUnmaskPromptViews::ControllerGone() {
controller_ = nullptr;
ClosePrompt();
}
Layout();
}
void CardUnmaskPromptViews::DisableAndWaitForVerification() {
SetInputsEnabled(false);
progress_overlay_->SetVisible(true);
GetDialogClientView()->UpdateDialogButtons();
Layout();
}
void SetRetriableErrorMessage(const base::string16& message) {
if (message.empty()) {
error_label_->SetMultiLine(false);
error_label_->SetText(base::ASCIIToUTF16(" "));
} else {
error_label_->SetMultiLine(true);
error_label_->SetText(message);
}
void CardUnmaskPromptViews::GotVerificationResult(
const base::string16& error_message,
bool allow_retry) {
if (error_message.empty()) {
progress_label_->SetText(l10n_util::GetStringUTF16(
IDS_AUTOFILL_CARD_UNMASK_VERIFICATION_SUCCESS));
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE, base::Bind(&CardUnmaskPromptViews::ClosePrompt,
weak_ptr_factory_.GetWeakPtr()),
controller_->GetSuccessMessageDuration());
} else {
// TODO(estade): it's somewhat jarring when the error comes back too
// quickly.
progress_overlay_->SetVisible(false);
// Update the dialog's size.
if (GetWidget() && controller_->GetWebContents()) {
constrained_window::UpdateWebContentsModalDialogPosition(
GetWidget(),
web_modal::WebContentsModalDialogManager::FromWebContents(
controller_->GetWebContents())
->delegate()
->GetWebContentsModalDialogHost());
}
}
if (allow_retry) {
SetInputsEnabled(true);
void SetInputsEnabled(bool enabled) {
cvc_input_->SetEnabled(enabled);
storage_checkbox_->SetEnabled(enabled);
// If there is more than one input showing, don't mark anything as
// invalid since we don't know the location of the problem.
if (!controller_->ShouldRequestExpirationDate())
cvc_input_->SetInvalid(true);
if (month_input_)
month_input_->SetEnabled(enabled);
if (year_input_)
year_input_->SetEnabled(enabled);
// TODO(estade): When do we hide |error_label_|?
SetRetriableErrorMessage(error_message);
} else {
permanent_error_label_->SetText(error_message);
permanent_error_label_->SetVisible(true);
SetRetriableErrorMessage(base::string16());
}
GetDialogClientView()->UpdateDialogButtons();
}
// views::DialogDelegateView
View* GetContentsView() override {
InitIfNecessary();
return this;
}
Layout();
}
views::View* CreateFootnoteView() override {
// Local storage checkbox and (?) tooltip.
views::View* storage_row = new views::View();
views::BoxLayout* storage_row_layout = new views::BoxLayout(
views::BoxLayout::kHorizontal, kEdgePadding, kEdgePadding, 0);
storage_row->SetLayoutManager(storage_row_layout);
storage_row->SetBorder(
views::Border::CreateSolidSidedBorder(1, 0, 0, 0, kSubtleBorderColor));
storage_row->set_background(
views::Background::CreateSolidBackground(kShadingColor));
storage_checkbox_ = new views::Checkbox(l10n_util::GetStringUTF16(
IDS_AUTOFILL_CARD_UNMASK_PROMPT_STORAGE_CHECKBOX));
storage_checkbox_->SetChecked(controller_->GetStoreLocallyStartState());
storage_row->AddChildView(storage_checkbox_);
storage_row_layout->SetFlexForView(storage_checkbox_, 1);
storage_row->AddChildView(new TooltipIcon(l10n_util::GetStringUTF16(
IDS_AUTOFILL_CARD_UNMASK_PROMPT_STORAGE_TOOLTIP)));
return storage_row;
void CardUnmaskPromptViews::SetRetriableErrorMessage(
const base::string16& message) {
if (message.empty()) {
error_label_->SetMultiLine(false);
error_label_->SetText(base::ASCIIToUTF16(" "));
} else {
error_label_->SetMultiLine(true);
error_label_->SetText(message);
}
// views::View
gfx::Size GetPreferredSize() const override {
// Must hardcode a width so the label knows where to wrap. TODO(estade):
// This can lead to a weird looking dialog if we end up getting allocated
// more width than we ask for, e.g. if the title is super long.
const int kWidth = 375;
return gfx::Size(kWidth, GetHeightForWidth(kWidth));
// Update the dialog's size.
if (GetWidget() && controller_->GetWebContents()) {
constrained_window::UpdateWebContentsModalDialogPosition(
GetWidget(), web_modal::WebContentsModalDialogManager::FromWebContents(
controller_->GetWebContents())
->delegate()
->GetWebContentsModalDialogHost());
}
}
void Layout() override {
for (int i = 0; i < child_count(); ++i) {
child_at(i)->SetBoundsRect(GetContentsBounds());
}
}
void CardUnmaskPromptViews::SetInputsEnabled(bool enabled) {
cvc_input_->SetEnabled(enabled);
storage_checkbox_->SetEnabled(enabled);
int GetHeightForWidth(int width) const override {
if (!has_children())
return 0;
const gfx::Insets insets = GetInsets();
return main_contents_->GetHeightForWidth(width - insets.width()) +
insets.height();
}
if (month_input_)
month_input_->SetEnabled(enabled);
if (year_input_)
year_input_->SetEnabled(enabled);
}
void OnNativeThemeChanged(const ui::NativeTheme* theme) override {
SkColor bg_color =
theme->GetSystemColor(ui::NativeTheme::kColorId_DialogBackground);
bg_color = SkColorSetA(bg_color, 0xDD);
progress_overlay_->set_background(
views::Background::CreateSolidBackground(bg_color));
}
views::View* CardUnmaskPromptViews::GetContentsView() {
InitIfNecessary();
return this;
}
ui::ModalType GetModalType() const override { return ui::MODAL_TYPE_CHILD; }
views::View* CardUnmaskPromptViews::CreateFootnoteView() {
// Local storage checkbox and (?) tooltip.
views::View* storage_row = new views::View();
views::BoxLayout* storage_row_layout = new views::BoxLayout(
views::BoxLayout::kHorizontal, kEdgePadding, kEdgePadding, 0);
storage_row->SetLayoutManager(storage_row_layout);
storage_row->SetBorder(
views::Border::CreateSolidSidedBorder(1, 0, 0, 0, kSubtleBorderColor));
storage_row->set_background(
views::Background::CreateSolidBackground(kShadingColor));
storage_checkbox_ = new views::Checkbox(l10n_util::GetStringUTF16(
IDS_AUTOFILL_CARD_UNMASK_PROMPT_STORAGE_CHECKBOX));
storage_checkbox_->SetChecked(controller_->GetStoreLocallyStartState());
storage_row->AddChildView(storage_checkbox_);
storage_row_layout->SetFlexForView(storage_checkbox_, 1);
storage_row->AddChildView(new TooltipIcon(l10n_util::GetStringUTF16(
IDS_AUTOFILL_CARD_UNMASK_PROMPT_STORAGE_TOOLTIP)));
return storage_row;
}
base::string16 GetWindowTitle() const override {
return controller_->GetWindowTitle();
gfx::Size CardUnmaskPromptViews::GetPreferredSize() const {
// Must hardcode a width so the label knows where to wrap. TODO(estade):
// This can lead to a weird looking dialog if we end up getting allocated
// more width than we ask for, e.g. if the title is super long.
const int kWidth = 375;
return gfx::Size(kWidth, GetHeightForWidth(kWidth));
}
void CardUnmaskPromptViews::Layout() {
for (int i = 0; i < child_count(); ++i) {
child_at(i)->SetBoundsRect(GetContentsBounds());
}
}
void DeleteDelegate() override { delete this; }
int CardUnmaskPromptViews::GetHeightForWidth(int width) const {
if (!has_children())
return 0;
const gfx::Insets insets = GetInsets();
return main_contents_->GetHeightForWidth(width - insets.width()) +
insets.height();
}
int GetDialogButtons() const override {
return ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL;
}
void CardUnmaskPromptViews::OnNativeThemeChanged(const ui::NativeTheme* theme) {
SkColor bg_color =
theme->GetSystemColor(ui::NativeTheme::kColorId_DialogBackground);
bg_color = SkColorSetA(bg_color, 0xDD);
progress_overlay_->set_background(
views::Background::CreateSolidBackground(bg_color));
}
base::string16 GetDialogButtonLabel(ui::DialogButton button) const override {
if (button == ui::DIALOG_BUTTON_OK)
return l10n_util::GetStringUTF16(IDS_AUTOFILL_CARD_UNMASK_CONFIRM_BUTTON);
ui::ModalType CardUnmaskPromptViews::GetModalType() const {
return ui::MODAL_TYPE_CHILD;
}
return DialogDelegateView::GetDialogButtonLabel(button);
}
base::string16 CardUnmaskPromptViews::GetWindowTitle() const {
return controller_->GetWindowTitle();
}
bool ShouldDefaultButtonBeBlue() const override { return true; }
void CardUnmaskPromptViews::DeleteDelegate() {
delete this;
}
bool IsDialogButtonEnabled(ui::DialogButton button) const override {
if (button == ui::DIALOG_BUTTON_CANCEL)
return true;
int CardUnmaskPromptViews::GetDialogButtons() const {
return ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL;
}
DCHECK_EQ(ui::DIALOG_BUTTON_OK, button);
base::string16 CardUnmaskPromptViews::GetDialogButtonLabel(
ui::DialogButton button) const {
if (button == ui::DIALOG_BUTTON_OK)
return l10n_util::GetStringUTF16(IDS_AUTOFILL_CARD_UNMASK_CONFIRM_BUTTON);
return cvc_input_->enabled() &&
controller_->InputCvcIsValid(cvc_input_->text()) &&
ExpirationDateIsValid();
}
return DialogDelegateView::GetDialogButtonLabel(button);
}
views::View* GetInitiallyFocusedView() override { return cvc_input_; }
bool CardUnmaskPromptViews::ShouldDefaultButtonBeBlue() const {
return true;
}
bool Cancel() override {
bool CardUnmaskPromptViews::IsDialogButtonEnabled(
ui::DialogButton button) const {
if (button == ui::DIALOG_BUTTON_CANCEL)
return true;
}
bool Accept() override {
if (!controller_)
return true;
controller_->OnUnmaskResponse(
cvc_input_->text(),
month_input_
? month_input_->GetTextForRow(month_input_->selected_index())
: base::string16(),
year_input_ ? year_input_->GetTextForRow(year_input_->selected_index())
: base::string16(),
storage_checkbox_ ? storage_checkbox_->checked() : false);
return false;
}
DCHECK_EQ(ui::DIALOG_BUTTON_OK, button);
// views::TextfieldController
void ContentsChanged(views::Textfield* sender,
const base::string16& new_contents) override {
if (controller_->InputCvcIsValid(new_contents))
cvc_input_->SetInvalid(false);
return cvc_input_->enabled() &&
controller_->InputCvcIsValid(cvc_input_->text()) &&
ExpirationDateIsValid();
}
GetDialogClientView()->UpdateDialogButtons();
}
views::View* CardUnmaskPromptViews::GetInitiallyFocusedView() {
return cvc_input_;
}
// views::ComboboxListener
void OnPerformAction(views::Combobox* combobox) override {
if (ExpirationDateIsValid()) {
if (month_input_->invalid()) {
month_input_->SetInvalid(false);
year_input_->SetInvalid(false);
error_label_->SetMultiLine(false);
SetRetriableErrorMessage(base::string16());
}
} else if (month_input_->selected_index() !=
month_combobox_model_.GetDefaultIndex() &&
year_input_->selected_index() !=
year_combobox_model_.GetDefaultIndex()) {
month_input_->SetInvalid(true);
year_input_->SetInvalid(true);
error_label_->SetMultiLine(true);
SetRetriableErrorMessage(l10n_util::GetStringUTF16(
IDS_AUTOFILL_CARD_UNMASK_INVALID_EXPIRATION_DATE));
}
bool CardUnmaskPromptViews::Cancel() {
return true;
}
GetDialogClientView()->UpdateDialogButtons();
}
bool CardUnmaskPromptViews::Accept() {
if (!controller_)
return true;
private:
void InitIfNecessary() {
if (has_children())
return;
main_contents_ = new views::View();
main_contents_->SetLayoutManager(
new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 10));
AddChildView(main_contents_);
permanent_error_label_ = new views::Label();
permanent_error_label_->set_background(
views::Background::CreateSolidBackground(
SkColorSetRGB(0xdb, 0x44, 0x37)));
permanent_error_label_->SetBorder(
views::Border::CreateEmptyBorder(10, kEdgePadding, 10, kEdgePadding));
permanent_error_label_->SetEnabledColor(SK_ColorWHITE);
permanent_error_label_->SetAutoColorReadabilityEnabled(false);
permanent_error_label_->SetVisible(false);
permanent_error_label_->SetMultiLine(true);
permanent_error_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
main_contents_->AddChildView(permanent_error_label_);
views::View* controls_container = new views::View();
controls_container->SetLayoutManager(
new views::BoxLayout(views::BoxLayout::kVertical, kEdgePadding, 0, 0));
main_contents_->AddChildView(controls_container);
views::Label* instructions =
new views::Label(controller_->GetInstructionsMessage());
instructions->SetMultiLine(true);
instructions->SetHorizontalAlignment(gfx::ALIGN_LEFT);
instructions->SetBorder(views::Border::CreateEmptyBorder(0, 0, 15, 0));
controls_container->AddChildView(instructions);
views::View* input_row = new views::View();
input_row->SetLayoutManager(
new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 5));
controls_container->AddChildView(input_row);
if (controller_->ShouldRequestExpirationDate()) {
month_input_ = new views::Combobox(&month_combobox_model_);
month_input_->set_listener(this);
input_row->AddChildView(month_input_);
input_row->AddChildView(new views::Label(l10n_util::GetStringUTF16(
IDS_AUTOFILL_CARD_UNMASK_EXPIRATION_DATE_SEPARATOR)));
year_input_ = new views::Combobox(&year_combobox_model_);
year_input_->set_listener(this);
input_row->AddChildView(year_input_);
input_row->AddChildView(new views::Label(base::ASCIIToUTF16(" ")));
}
controller_->OnUnmaskResponse(
cvc_input_->text(),
month_input_ ? month_input_->GetTextForRow(month_input_->selected_index())
: base::string16(),
year_input_ ? year_input_->GetTextForRow(year_input_->selected_index())
: base::string16(),
storage_checkbox_ ? storage_checkbox_->checked() : false);
return false;
}
cvc_input_ = new DecoratedTextfield(
base::string16(),
l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_CVC),
this);
cvc_input_->set_default_width_in_chars(8);
input_row->AddChildView(cvc_input_);
views::ImageView* cvc_image = new views::ImageView();
ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
cvc_image->SetImage(rb.GetImageSkiaNamed(controller_->GetCvcImageRid()));
input_row->AddChildView(cvc_image);
// Reserve vertical space for the error label, assuming it's one line.
error_label_ = new views::Label(base::ASCIIToUTF16(" "));
error_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
error_label_->SetEnabledColor(kWarningColor);
error_label_->SetBorder(views::Border::CreateEmptyBorder(3, 0, 5, 0));
controls_container->AddChildView(error_label_);
progress_overlay_ = new views::View();
views::BoxLayout* progress_layout =
new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0);
progress_layout->set_main_axis_alignment(
views::BoxLayout::MAIN_AXIS_ALIGNMENT_CENTER);
progress_layout->set_cross_axis_alignment(
views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER);
progress_overlay_->SetLayoutManager(progress_layout);
void CardUnmaskPromptViews::ContentsChanged(
views::Textfield* sender,
const base::string16& new_contents) {
if (controller_->InputCvcIsValid(new_contents))
cvc_input_->SetInvalid(false);
progress_overlay_->SetVisible(false);
AddChildView(progress_overlay_);
GetDialogClientView()->UpdateDialogButtons();
}
progress_label_ = new views::Label(l10n_util::GetStringUTF16(
IDS_AUTOFILL_CARD_UNMASK_VERIFICATION_IN_PROGRESS));
progress_overlay_->AddChildView(progress_label_);
void CardUnmaskPromptViews::OnPerformAction(views::Combobox* combobox) {
if (ExpirationDateIsValid()) {
if (month_input_->invalid()) {
month_input_->SetInvalid(false);
year_input_->SetInvalid(false);
error_label_->SetMultiLine(false);
SetRetriableErrorMessage(base::string16());
}
} else if (month_input_->selected_index() !=
month_combobox_model_.GetDefaultIndex() &&
year_input_->selected_index() !=
year_combobox_model_.GetDefaultIndex()) {
month_input_->SetInvalid(true);
year_input_->SetInvalid(true);
error_label_->SetMultiLine(true);
SetRetriableErrorMessage(l10n_util::GetStringUTF16(
IDS_AUTOFILL_CARD_UNMASK_INVALID_EXPIRATION_DATE));
}
void ClosePrompt() { GetWidget()->Close(); }
bool ExpirationDateIsValid() const {
if (!controller_->ShouldRequestExpirationDate())
return true;
GetDialogClientView()->UpdateDialogButtons();
}
return controller_->InputExpirationIsValid(
month_input_->GetTextForRow(month_input_->selected_index()),
year_input_->GetTextForRow(year_input_->selected_index()));
void CardUnmaskPromptViews::InitIfNecessary() {
if (has_children())
return;
main_contents_ = new views::View();
main_contents_->SetLayoutManager(
new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 10));
AddChildView(main_contents_);
permanent_error_label_ = new views::Label();
permanent_error_label_->set_background(
views::Background::CreateSolidBackground(
SkColorSetRGB(0xdb, 0x44, 0x37)));
permanent_error_label_->SetBorder(
views::Border::CreateEmptyBorder(10, kEdgePadding, 10, kEdgePadding));
permanent_error_label_->SetEnabledColor(SK_ColorWHITE);
permanent_error_label_->SetAutoColorReadabilityEnabled(false);
permanent_error_label_->SetVisible(false);
permanent_error_label_->SetMultiLine(true);
permanent_error_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
main_contents_->AddChildView(permanent_error_label_);
views::View* controls_container = new views::View();
controls_container->SetLayoutManager(
new views::BoxLayout(views::BoxLayout::kVertical, kEdgePadding, 0, 0));
main_contents_->AddChildView(controls_container);
views::Label* instructions =
new views::Label(controller_->GetInstructionsMessage());
instructions->SetMultiLine(true);
instructions->SetHorizontalAlignment(gfx::ALIGN_LEFT);
instructions->SetBorder(views::Border::CreateEmptyBorder(0, 0, 15, 0));
controls_container->AddChildView(instructions);
views::View* input_row = new views::View();
input_row->SetLayoutManager(
new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 5));
controls_container->AddChildView(input_row);
if (controller_->ShouldRequestExpirationDate()) {
month_input_ = new views::Combobox(&month_combobox_model_);
month_input_->set_listener(this);
input_row->AddChildView(month_input_);
input_row->AddChildView(new views::Label(l10n_util::GetStringUTF16(
IDS_AUTOFILL_CARD_UNMASK_EXPIRATION_DATE_SEPARATOR)));
year_input_ = new views::Combobox(&year_combobox_model_);
year_input_->set_listener(this);
input_row->AddChildView(year_input_);
input_row->AddChildView(new views::Label(base::ASCIIToUTF16(" ")));
}
CardUnmaskPromptController* controller_;
views::View* main_contents_;
// The error label for permanent errors (where the user can't retry).
views::Label* permanent_error_label_;
DecoratedTextfield* cvc_input_;
// These will be null when expiration date is not required.
views::Combobox* month_input_;
views::Combobox* year_input_;
MonthComboboxModel month_combobox_model_;
YearComboboxModel year_combobox_model_;
// The error label for most errors, which lives beneath the inputs.
views::Label* error_label_;
views::Checkbox* storage_checkbox_;
views::View* progress_overlay_;
views::Label* progress_label_;
base::WeakPtrFactory<CardUnmaskPromptViews> weak_ptr_factory_;
cvc_input_ = new DecoratedTextfield(
base::string16(),
l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_CVC), this);
cvc_input_->set_default_width_in_chars(8);
input_row->AddChildView(cvc_input_);
views::ImageView* cvc_image = new views::ImageView();
ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
cvc_image->SetImage(rb.GetImageSkiaNamed(controller_->GetCvcImageRid()));
input_row->AddChildView(cvc_image);
// Reserve vertical space for the error label, assuming it's one line.
error_label_ = new views::Label(base::ASCIIToUTF16(" "));
error_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
error_label_->SetEnabledColor(kWarningColor);
error_label_->SetBorder(views::Border::CreateEmptyBorder(3, 0, 5, 0));
controls_container->AddChildView(error_label_);
progress_overlay_ = new views::View();
views::BoxLayout* progress_layout =
new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0);
progress_layout->set_main_axis_alignment(
views::BoxLayout::MAIN_AXIS_ALIGNMENT_CENTER);
progress_layout->set_cross_axis_alignment(
views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER);
progress_overlay_->SetLayoutManager(progress_layout);
progress_overlay_->SetVisible(false);
AddChildView(progress_overlay_);
progress_label_ = new views::Label(l10n_util::GetStringUTF16(
IDS_AUTOFILL_CARD_UNMASK_VERIFICATION_IN_PROGRESS));
progress_overlay_->AddChildView(progress_label_);
}
DISALLOW_COPY_AND_ASSIGN(CardUnmaskPromptViews);
};
bool CardUnmaskPromptViews::ExpirationDateIsValid() const {
if (!controller_->ShouldRequestExpirationDate())
return true;
} // namespace
return controller_->InputExpirationIsValid(
month_input_->GetTextForRow(month_input_->selected_index()),
year_input_->GetTextForRow(year_input_->selected_index()));
}
// static
CardUnmaskPromptView* CardUnmaskPromptView::CreateAndShow(
CardUnmaskPromptController* controller) {
CardUnmaskPromptViews* view = new CardUnmaskPromptViews(controller);
view->Show();
return view;
void CardUnmaskPromptViews::ClosePrompt() {
GetWidget()->Close();
}
} // namespace autofill
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_VIEWS_AUTOFILL_CARD_UNMASK_PROMPT_VIEWS_H_
#define CHROME_BROWSER_UI_VIEWS_AUTOFILL_CARD_UNMASK_PROMPT_VIEWS_H_
#include "chrome/browser/ui/autofill/autofill_dialog_models.h"
#include "chrome/browser/ui/autofill/card_unmask_prompt_view.h"
#include "ui/views/controls/combobox/combobox_listener.h"
#include "ui/views/controls/textfield/textfield_controller.h"
#include "ui/views/window/dialog_delegate.h"
namespace views {
class Label;
class Checkbox;
}
namespace autofill {
class DecoratedTextfield;
class CardUnmaskPromptViews : public CardUnmaskPromptView,
views::ComboboxListener,
views::DialogDelegateView,
views::TextfieldController {
public:
explicit CardUnmaskPromptViews(CardUnmaskPromptController* controller);
~CardUnmaskPromptViews() override;
void Show();
// CardUnmaskPromptView
void ControllerGone() override;
void DisableAndWaitForVerification() override;
void GotVerificationResult(const base::string16& error_message,
bool allow_retry) override;
// views::DialogDelegateView
View* GetContentsView() override;
views::View* CreateFootnoteView() override;
// views::View
gfx::Size GetPreferredSize() const override;
void Layout() override;
int GetHeightForWidth(int width) const override;
void OnNativeThemeChanged(const ui::NativeTheme* theme) override;
ui::ModalType GetModalType() const override;
base::string16 GetWindowTitle() const override;
void DeleteDelegate() override;
int GetDialogButtons() const override;
base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
bool ShouldDefaultButtonBeBlue() const override;
bool IsDialogButtonEnabled(ui::DialogButton button) const override;
views::View* GetInitiallyFocusedView() override;
bool Cancel() override;
bool Accept() override;
// views::TextfieldController
void ContentsChanged(views::Textfield* sender,
const base::string16& new_contents) override;
// views::ComboboxListener
void OnPerformAction(views::Combobox* combobox) override;
private:
friend class CardUnmaskPromptViewTesterViews;
void InitIfNecessary();
void SetRetriableErrorMessage(const base::string16& message);
bool ExpirationDateIsValid() const;
void SetInputsEnabled(bool enabled);
void ClosePrompt();
CardUnmaskPromptController* controller_;
views::View* main_contents_;
// The error label for permanent errors (where the user can't retry).
views::Label* permanent_error_label_;
DecoratedTextfield* cvc_input_;
// These will be null when expiration date is not required.
views::Combobox* month_input_;
views::Combobox* year_input_;
MonthComboboxModel month_combobox_model_;
YearComboboxModel year_combobox_model_;
// The error label for most errors, which lives beneath the inputs.
views::Label* error_label_;
views::Checkbox* storage_checkbox_;
views::View* progress_overlay_;
views::Label* progress_label_;
base::WeakPtrFactory<CardUnmaskPromptViews> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(CardUnmaskPromptViews);
};
} // namespace autofill
#endif // CHROME_BROWSER_UI_VIEWS_AUTOFILL_CARD_UNMASK_PROMPT_VIEWS_H_
......@@ -1941,6 +1941,7 @@
'browser/ui/views/autofill/autofill_popup_view_views.cc',
'browser/ui/views/autofill/autofill_popup_view_views.h',
'browser/ui/views/autofill/card_unmask_prompt_views.cc',
'browser/ui/views/autofill/card_unmask_prompt_views.h',
'browser/ui/views/autofill/decorated_textfield.cc',
'browser/ui/views/autofill/decorated_textfield.h',
'browser/ui/views/autofill/expanding_textfield.cc',
......
......@@ -429,6 +429,8 @@
'browser/ui/ash/volume_controller_browsertest_chromeos.cc',
'browser/ui/autofill/autofill_dialog_controller_browsertest.cc',
'browser/ui/autofill/autofill_dialog_view_tester.h',
'browser/ui/autofill/card_unmask_prompt_view_browsertest.cc',
'browser/ui/autofill/card_unmask_view_tester.h',
'browser/ui/autofill/mock_address_validator.cc',
'browser/ui/autofill/mock_address_validator.h',
'browser/ui/autofill/password_generation_popup_view_browsertest.cc',
......@@ -596,6 +598,8 @@
'browser/ui/views/autofill/autofill_dialog_view_tester_views.cc',
'browser/ui/views/autofill/autofill_dialog_view_tester_views.h',
'browser/ui/views/autofill/autofill_popup_base_view_browsertest.cc',
'browser/ui/views/autofill/card_unmask_prompt_view_tester_views.cc',
'browser/ui/views/autofill/card_unmask_prompt_view_tester_views.h',
'browser/ui/views/autofill/password_generation_popup_view_tester_views.cc',
'browser/ui/views/autofill/password_generation_popup_view_tester_views.h',
'browser/ui/views/collected_cookies_views_browsertest.cc',
......@@ -2270,8 +2274,6 @@
'browser/media_galleries/fileapi/iphoto_data_provider_browsertest.cc',
'browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper_browsertest.mm',
'browser/spellchecker/spellcheck_message_filter_mac_browsertest.cc',
# TODO(bondd): Make this test work on other platforms.
'browser/ui/autofill/card_unmask_prompt_view_browsertest.cc',
],
'sources!': [
# TODO(groby): This test depends on hunspell and we cannot run it on
......
......@@ -209,6 +209,14 @@ CreditCard GetVerifiedCreditCard2() {
return credit_card;
}
CreditCard GetMaskedServerCard() {
CreditCard credit_card(CreditCard::MASKED_SERVER_CARD, "a123");
test::SetCreditCardInfo(&credit_card, "Bonnie Parker",
"2109" /* Mastercard */, "12", "2012");
credit_card.SetTypeForMaskedCard(kMasterCard);
return credit_card;
}
void SetProfileInfo(AutofillProfile* profile,
const char* first_name, const char* middle_name,
const char* last_name, const char* email, const char* company,
......
......@@ -69,6 +69,9 @@ CreditCard GetVerifiedCreditCard();
// Returns a verified credit card full of dummy info, different to the above.
CreditCard GetVerifiedCreditCard2();
// Returns a masked server card full of dummy info.
CreditCard GetMaskedServerCard();
// A unit testing utility that is common to a number of the Autofill unit
// tests. |SetProfileInfo| provides a quick way to populate a profile with
// c-strings.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment