Commit fbd4d8d0 authored by dbeam@chromium.org's avatar dbeam@chromium.org

Highlight fields we know to be invalid but have no way of locally checking.

In this specific case, expired Online Wallet instruments.

R=groby@chromium.org,estade@chromium.org
TBR=aruslan@chromium.org
BUG=168680
TEST=unit_tests and manual

Review URL: https://chromiumcodereview.appspot.com/15961007

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@204012 0039d316-1c4b-4281-b951-d872f2087c98
parent 3d0294ef
...@@ -349,7 +349,7 @@ jboolean AutofillDialogViewAndroid::EditingComplete(JNIEnv* env, ...@@ -349,7 +349,7 @@ jboolean AutofillDialogViewAndroid::EditingComplete(JNIEnv* env,
jint jsection) { jint jsection) {
// Unfortunately, edits are not sent to the models, http://crbug.com/223919. // Unfortunately, edits are not sent to the models, http://crbug.com/223919.
const DialogSection section = static_cast<DialogSection>(jsection); const DialogSection section = static_cast<DialogSection>(jsection);
if (ValidateSection(section, AutofillDialogController::VALIDATE_FINAL)) { if (ValidateSection(section, VALIDATE_FINAL)) {
UpdateOrFillSectionToJava(section, false, UNKNOWN_TYPE); UpdateOrFillSectionToJava(section, false, UNKNOWN_TYPE);
return true; return true;
} }
...@@ -396,8 +396,7 @@ ScopedJavaLocalRef<jstring> AutofillDialogViewAndroid::ValidateField( ...@@ -396,8 +396,7 @@ ScopedJavaLocalRef<jstring> AutofillDialogViewAndroid::ValidateField(
void AutofillDialogViewAndroid::ValidateSection(JNIEnv* env, void AutofillDialogViewAndroid::ValidateSection(JNIEnv* env,
jobject obj, jobject obj,
jint section) { jint section) {
ValidateSection(static_cast<DialogSection>(section), ValidateSection(static_cast<DialogSection>(section), VALIDATE_EDIT);
AutofillDialogController::VALIDATE_EDIT);
} }
void AutofillDialogViewAndroid::DialogSubmit(JNIEnv* env, jobject obj) { void AutofillDialogViewAndroid::DialogSubmit(JNIEnv* env, jobject obj) {
...@@ -458,8 +457,8 @@ bool AutofillDialogViewAndroid::RegisterAutofillDialogViewAndroid(JNIEnv* env) { ...@@ -458,8 +457,8 @@ bool AutofillDialogViewAndroid::RegisterAutofillDialogViewAndroid(JNIEnv* env) {
return RegisterNativesImpl(env); return RegisterNativesImpl(env);
} }
bool AutofillDialogViewAndroid::ValidateSection( bool AutofillDialogViewAndroid::ValidateSection(DialogSection section,
DialogSection section, AutofillDialogController::ValidationType type) { ValidationType type) {
DetailOutputMap detail_outputs; DetailOutputMap detail_outputs;
GetUserInput(section, &detail_outputs); GetUserInput(section, &detail_outputs);
ValidityData invalid_inputs = ValidityData invalid_inputs =
......
...@@ -113,8 +113,7 @@ class AutofillDialogViewAndroid : public AutofillDialogView { ...@@ -113,8 +113,7 @@ class AutofillDialogViewAndroid : public AutofillDialogView {
// Returns the list of available user accounts. // Returns the list of available user accounts.
std::vector<std::string> GetAvailableUserAccounts(); std::vector<std::string> GetAvailableUserAccounts();
bool ValidateSection(DialogSection section, bool ValidateSection(DialogSection section, ValidationType type);
AutofillDialogController::ValidationType type);
// Starts an automatic sign-in attempt for a given account. // Starts an automatic sign-in attempt for a given account.
bool StartAutomaticSignIn(const std::string& username); bool StartAutomaticSignIn(const std::string& username);
......
...@@ -37,11 +37,6 @@ namespace autofill { ...@@ -37,11 +37,6 @@ namespace autofill {
// This class defines the interface to the controller that the dialog view sees. // This class defines the interface to the controller that the dialog view sees.
class AutofillDialogController { class AutofillDialogController {
public: public:
enum ValidationType {
VALIDATE_EDIT, // validate user edits. Allow for empty fields.
VALIDATE_FINAL, // Full form validation. Mandatory fields can't be empty.
};
// Strings ------------------------------------------------------------------- // Strings -------------------------------------------------------------------
virtual string16 DialogTitle() const = 0; virtual string16 DialogTitle() const = 0;
......
...@@ -999,6 +999,48 @@ string16 AutofillDialogControllerImpl::ExtraSuggestionTextForSection( ...@@ -999,6 +999,48 @@ string16 AutofillDialogControllerImpl::ExtraSuggestionTextForSection(
return string16(); return string16();
} }
const wallet::WalletItems::MaskedInstrument* AutofillDialogControllerImpl::
ActiveInstrument() const {
if (!IsPayingWithWallet())
return NULL;
const SuggestionsMenuModel* model =
SuggestionsMenuModelForSection(SECTION_CC_BILLING);
const std::string item_key = model->GetItemKeyForCheckedItem();
if (!IsASuggestionItemKey(item_key))
return NULL;
int index;
if (!base::StringToInt(item_key, &index) || index < 0 ||
static_cast<size_t>(index) >= wallet_items_->instruments().size()) {
NOTREACHED();
return NULL;
}
return wallet_items_->instruments()[index];
}
const wallet::Address* AutofillDialogControllerImpl::
ActiveShippingAddress() const {
if (!IsPayingWithWallet())
return NULL;
const SuggestionsMenuModel* model =
SuggestionsMenuModelForSection(SECTION_SHIPPING);
const std::string item_key = model->GetItemKeyForCheckedItem();
if (!IsASuggestionItemKey(item_key))
return NULL;
int index;
if (!base::StringToInt(item_key, &index) || index < 0 ||
static_cast<size_t>(index) >= wallet_items_->addresses().size()) {
NOTREACHED();
return NULL;
}
return wallet_items_->addresses()[index];
}
scoped_ptr<DataModelWrapper> AutofillDialogControllerImpl::CreateWrapper( scoped_ptr<DataModelWrapper> AutofillDialogControllerImpl::CreateWrapper(
DialogSection section) { DialogSection section) {
if (IsPayingWithWallet() && full_wallet_ && if (IsPayingWithWallet() && full_wallet_ &&
...@@ -1019,18 +1061,14 @@ scoped_ptr<DataModelWrapper> AutofillDialogControllerImpl::CreateWrapper( ...@@ -1019,18 +1061,14 @@ scoped_ptr<DataModelWrapper> AutofillDialogControllerImpl::CreateWrapper(
return scoped_ptr<DataModelWrapper>(); return scoped_ptr<DataModelWrapper>();
if (IsPayingWithWallet()) { if (IsPayingWithWallet()) {
int index;
bool success = base::StringToInt(item_key, &index);
DCHECK(success);
if (section == SECTION_CC_BILLING) { if (section == SECTION_CC_BILLING) {
return scoped_ptr<DataModelWrapper>( return scoped_ptr<DataModelWrapper>(
new WalletInstrumentWrapper(wallet_items_->instruments()[index])); new WalletInstrumentWrapper(ActiveInstrument()));
} }
if (section == SECTION_SHIPPING) { if (section == SECTION_SHIPPING) {
return scoped_ptr<DataModelWrapper>( return scoped_ptr<DataModelWrapper>(
new WalletAddressWrapper(wallet_items_->addresses()[index])); new WalletAddressWrapper(ActiveShippingAddress()));
} }
return scoped_ptr<DataModelWrapper>(); return scoped_ptr<DataModelWrapper>();
...@@ -1232,30 +1270,26 @@ ValidityData AutofillDialogControllerImpl::InputsAreValid( ...@@ -1232,30 +1270,26 @@ ValidityData AutofillDialogControllerImpl::InputsAreValid(
// Validate the date formed by month and year field. (Autofill dialog is // Validate the date formed by month and year field. (Autofill dialog is
// never supposed to have 2-digit years, so not checked). // never supposed to have 2-digit years, so not checked).
if (field_values.count(CREDIT_CARD_EXP_MONTH) && if (field_values.count(CREDIT_CARD_EXP_4_DIGIT_YEAR) &&
field_values.count(CREDIT_CARD_EXP_4_DIGIT_YEAR)) { field_values.count(CREDIT_CARD_EXP_MONTH) &&
if (!autofill::IsValidCreditCardExpirationDate( !IsCreditCardExpirationValid(field_values[CREDIT_CARD_EXP_4_DIGIT_YEAR],
field_values[CREDIT_CARD_EXP_4_DIGIT_YEAR], field_values[CREDIT_CARD_EXP_MONTH])) {
field_values[CREDIT_CARD_EXP_MONTH], invalid_messages[CREDIT_CARD_EXP_4_DIGIT_YEAR] =
base::Time::Now())) { ASCIIToUTF16("more complicated message");
invalid_messages[CREDIT_CARD_EXP_MONTH] = invalid_messages[CREDIT_CARD_EXP_MONTH] =
ASCIIToUTF16("more complicated message"); ASCIIToUTF16("more complicated message");
invalid_messages[CREDIT_CARD_EXP_4_DIGIT_YEAR] =
ASCIIToUTF16("more complicated message");
}
} }
// If there is a credit card number and a CVC, validate them together. // If there is a credit card number and a CVC, validate them together.
if (field_values.count(CREDIT_CARD_NUMBER) && if (field_values.count(CREDIT_CARD_NUMBER) &&
field_values.count(CREDIT_CARD_VERIFICATION_CODE) && field_values.count(CREDIT_CARD_VERIFICATION_CODE) &&
InputValidityMessage(CREDIT_CARD_NUMBER, InputValidityMessage(CREDIT_CARD_NUMBER,
field_values[CREDIT_CARD_NUMBER]).empty()) { field_values[CREDIT_CARD_NUMBER]).empty() &&
if (!autofill::IsValidCreditCardSecurityCode( !autofill::IsValidCreditCardSecurityCode(
field_values[CREDIT_CARD_VERIFICATION_CODE], field_values[CREDIT_CARD_VERIFICATION_CODE],
field_values[CREDIT_CARD_NUMBER])) { field_values[CREDIT_CARD_NUMBER])) {
invalid_messages[CREDIT_CARD_VERIFICATION_CODE] = invalid_messages[CREDIT_CARD_VERIFICATION_CODE] =
ASCIIToUTF16("CVC doesn't match card type!"); ASCIIToUTF16("CVC doesn't match card type!");
}
} }
// Validate the phone number against the country code of the address. // Validate the phone number against the country code of the address.
...@@ -2393,7 +2427,7 @@ bool AutofillDialogControllerImpl::IsManuallyEditingSection( ...@@ -2393,7 +2427,7 @@ bool AutofillDialogControllerImpl::IsManuallyEditingSection(
} }
bool AutofillDialogControllerImpl::IsASuggestionItemKey( bool AutofillDialogControllerImpl::IsASuggestionItemKey(
const std::string& key) { const std::string& key) const {
return !key.empty() && return !key.empty() &&
key != kAddNewItemKey && key != kAddNewItemKey &&
key != kManageItemsKey && key != kManageItemsKey &&
...@@ -2438,6 +2472,33 @@ bool AutofillDialogControllerImpl::SectionIsValid( ...@@ -2438,6 +2472,33 @@ bool AutofillDialogControllerImpl::SectionIsValid(
return InputsAreValid(detail_outputs, VALIDATE_EDIT).empty(); return InputsAreValid(detail_outputs, VALIDATE_EDIT).empty();
} }
bool AutofillDialogControllerImpl::IsCreditCardExpirationValid(
const base::string16& year,
const base::string16& month) const {
// If the expiration is in the past as per the local clock, it's invalid.
base::Time now = base::Time::Now();
if (!autofill::IsValidCreditCardExpirationDate(year, month, now))
return false;
if (IsPayingWithWallet() && IsEditingExistingData(SECTION_CC_BILLING)) {
const wallet::WalletItems::MaskedInstrument* instrument =
ActiveInstrument();
const std::string& locale = g_browser_process->GetApplicationLocale();
int month_int;
if (base::StringToInt(month, &month_int) &&
instrument->status() ==
wallet::WalletItems::MaskedInstrument::EXPIRED &&
year == instrument->GetInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, locale) &&
month_int == instrument->expiration_month()) {
// Otherwise, if the user is editing an instrument that's deemed expired
// by the Online Wallet server, mark it invalid on selection.
return false;
}
}
return true;
}
bool AutofillDialogControllerImpl::ShouldUseBillingForShipping() { bool AutofillDialogControllerImpl::ShouldUseBillingForShipping() {
return SectionIsActive(SECTION_SHIPPING) && return SectionIsActive(SECTION_SHIPPING) &&
suggested_shipping_.GetItemKeyForCheckedItem() == kSameAsBillingKey; suggested_shipping_.GetItemKeyForCheckedItem() == kSameAsBillingKey;
...@@ -2489,26 +2550,17 @@ void AutofillDialogControllerImpl::SubmitWithWallet() { ...@@ -2489,26 +2550,17 @@ void AutofillDialogControllerImpl::SubmitWithWallet() {
if (AreLegalDocumentsCurrent()) if (AreLegalDocumentsCurrent())
LoadRiskFingerprintData(); LoadRiskFingerprintData();
SuggestionsMenuModel* billing = const wallet::WalletItems::MaskedInstrument* active_instrument =
SuggestionsMenuModelForSection(SECTION_CC_BILLING); ActiveInstrument();
int instrument_index = -1;
base::StringToInt(billing->GetItemKeyForCheckedItem(), &instrument_index);
if (!IsManuallyEditingSection(SECTION_CC_BILLING)) { if (!IsManuallyEditingSection(SECTION_CC_BILLING)) {
active_instrument_id_ = active_instrument_id_ = active_instrument->object_id();
wallet_items_->instruments()[instrument_index]->object_id();
DCHECK(!active_instrument_id_.empty()); DCHECK(!active_instrument_id_.empty());
} }
SuggestionsMenuModel* shipping = const wallet::Address* active_address = ActiveShippingAddress();
SuggestionsMenuModelForSection(SECTION_SHIPPING);
int address_index = -1;
base::StringToInt(shipping->GetItemKeyForCheckedItem(), &address_index);
if (!IsManuallyEditingSection(SECTION_SHIPPING) && if (!IsManuallyEditingSection(SECTION_SHIPPING) &&
!ShouldUseBillingForShipping()) { !ShouldUseBillingForShipping()) {
active_address_id_ = active_address_id_ = active_address->object_id();
wallet_items_->addresses()[address_index]->object_id();
DCHECK(!active_address_id_.empty()); DCHECK(!active_address_id_.empty());
} }
...@@ -2518,14 +2570,13 @@ void AutofillDialogControllerImpl::SubmitWithWallet() { ...@@ -2518,14 +2570,13 @@ void AutofillDialogControllerImpl::SubmitWithWallet() {
CreateUpdateInstrumentRequest( CreateUpdateInstrumentRequest(
inputted_instrument.get(), inputted_instrument.get(),
!IsEditingExistingData(SECTION_CC_BILLING) ? std::string() : !IsEditingExistingData(SECTION_CC_BILLING) ? std::string() :
wallet_items_->instruments()[instrument_index]->object_id()); active_instrument->object_id());
scoped_ptr<wallet::Address> inputted_address; scoped_ptr<wallet::Address> inputted_address;
if (active_address_id_.empty()) { if (active_address_id_.empty()) {
if (ShouldUseBillingForShipping()) { if (ShouldUseBillingForShipping()) {
const wallet::Address& address = inputted_instrument ? const wallet::Address& address = inputted_instrument ?
inputted_instrument->address() : inputted_instrument->address() : active_instrument->address();
wallet_items_->instruments()[instrument_index]->address();
// Try to find an exact matched shipping address and use it for shipping, // Try to find an exact matched shipping address and use it for shipping,
// otherwise save it as a new shipping address. http://crbug.com/225442 // otherwise save it as a new shipping address. http://crbug.com/225442
const wallet::Address* duplicated_address = const wallet::Address* duplicated_address =
...@@ -2540,8 +2591,7 @@ void AutofillDialogControllerImpl::SubmitWithWallet() { ...@@ -2540,8 +2591,7 @@ void AutofillDialogControllerImpl::SubmitWithWallet() {
} else { } else {
inputted_address = CreateTransientAddress(); inputted_address = CreateTransientAddress();
if (IsEditingExistingData(SECTION_SHIPPING)) { if (IsEditingExistingData(SECTION_SHIPPING)) {
inputted_address->set_object_id( inputted_address->set_object_id(active_address->object_id());
wallet_items_->addresses()[address_index]->object_id());
DCHECK(!inputted_address->object_id().empty()); DCHECK(!inputted_address->object_id().empty());
} }
} }
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include "components/autofill/browser/personal_data_manager_observer.h" #include "components/autofill/browser/personal_data_manager_observer.h"
#include "components/autofill/browser/wallet/wallet_client.h" #include "components/autofill/browser/wallet/wallet_client.h"
#include "components/autofill/browser/wallet/wallet_client_delegate.h" #include "components/autofill/browser/wallet/wallet_client_delegate.h"
#include "components/autofill/browser/wallet/wallet_items.h"
#include "components/autofill/browser/wallet/wallet_signin_helper_delegate.h" #include "components/autofill/browser/wallet/wallet_signin_helper_delegate.h"
#include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h" #include "content/public/browser/notification_registrar.h"
...@@ -321,6 +322,12 @@ class AutofillDialogControllerImpl : public AutofillDialogController, ...@@ -321,6 +322,12 @@ class AutofillDialogControllerImpl : public AutofillDialogController,
// data or Wallet data, depending on whether Wallet is currently enabled. // data or Wallet data, depending on whether Wallet is currently enabled.
scoped_ptr<DataModelWrapper> CreateWrapper(DialogSection section); scoped_ptr<DataModelWrapper> CreateWrapper(DialogSection section);
// Helper to return the current Wallet instrument or address. If the dialog
// isn't using Wallet or the user is adding a new instrument or address, NULL
// will be returned.
const wallet::WalletItems::MaskedInstrument* ActiveInstrument() const;
const wallet::Address* ActiveShippingAddress() const;
// Fills in |section|-related fields in |output_| according to the state of // Fills in |section|-related fields in |output_| according to the state of
// |view_|. // |view_|.
void FillOutputForSection(DialogSection section); void FillOutputForSection(DialogSection section);
...@@ -418,9 +425,13 @@ class AutofillDialogControllerImpl : public AutofillDialogController, ...@@ -418,9 +425,13 @@ class AutofillDialogControllerImpl : public AutofillDialogController,
// the dialog have valid contents. // the dialog have valid contents.
bool SectionIsValid(DialogSection section) const; bool SectionIsValid(DialogSection section) const;
// Whether the currently active credit card expiration date is valid.
bool IsCreditCardExpirationValid(const base::string16& year,
const base::string16& month) const;
// Returns true if |key| refers to a suggestion, as opposed to some control // Returns true if |key| refers to a suggestion, as opposed to some control
// menu item. // menu item.
bool IsASuggestionItemKey(const std::string& key); bool IsASuggestionItemKey(const std::string& key) const;
// Whether the billing section should be used to fill in the shipping details. // Whether the billing section should be used to fill in the shipping details.
bool ShouldUseBillingForShipping(); bool ShouldUseBillingForShipping();
......
...@@ -459,57 +459,49 @@ TEST_F(AutofillDialogControllerTest, PhoneNumberValidation) { ...@@ -459,57 +459,49 @@ TEST_F(AutofillDialogControllerTest, PhoneNumberValidation) {
// Existing data should have no errors. // Existing data should have no errors.
ValidityData validity_data = ValidityData validity_data =
controller()->InputsAreValid(outputs, controller()->InputsAreValid(outputs, VALIDATE_FINAL);
AutofillDialogController::VALIDATE_FINAL);
EXPECT_EQ(0U, validity_data.count(PHONE_HOME_WHOLE_NUMBER)); EXPECT_EQ(0U, validity_data.count(PHONE_HOME_WHOLE_NUMBER));
// Input an empty phone number with VALIDATE_FINAL. // Input an empty phone number with VALIDATE_FINAL.
SetOutputValue(inputs, &outputs, PHONE_HOME_WHOLE_NUMBER, ""); SetOutputValue(inputs, &outputs, PHONE_HOME_WHOLE_NUMBER, "");
validity_data = validity_data =
controller()->InputsAreValid(outputs, controller()->InputsAreValid(outputs, VALIDATE_FINAL);
AutofillDialogController::VALIDATE_FINAL);
EXPECT_EQ(1U, validity_data.count(PHONE_HOME_WHOLE_NUMBER)); EXPECT_EQ(1U, validity_data.count(PHONE_HOME_WHOLE_NUMBER));
// Input an empty phone number with VALIDATE_EDIT. // Input an empty phone number with VALIDATE_EDIT.
validity_data = validity_data =
controller()->InputsAreValid(outputs, controller()->InputsAreValid(outputs, VALIDATE_EDIT);
AutofillDialogController::VALIDATE_EDIT);
EXPECT_EQ(0U, validity_data.count(PHONE_HOME_WHOLE_NUMBER)); EXPECT_EQ(0U, validity_data.count(PHONE_HOME_WHOLE_NUMBER));
// Input an invalid phone number. // Input an invalid phone number.
SetOutputValue(inputs, &outputs, PHONE_HOME_WHOLE_NUMBER, "ABC"); SetOutputValue(inputs, &outputs, PHONE_HOME_WHOLE_NUMBER, "ABC");
validity_data = validity_data =
controller()->InputsAreValid(outputs, controller()->InputsAreValid(outputs, VALIDATE_EDIT);
AutofillDialogController::VALIDATE_EDIT);
EXPECT_EQ(1U, validity_data.count(PHONE_HOME_WHOLE_NUMBER)); EXPECT_EQ(1U, validity_data.count(PHONE_HOME_WHOLE_NUMBER));
// Input a local phone number. // Input a local phone number.
SetOutputValue(inputs, &outputs, PHONE_HOME_WHOLE_NUMBER, "2155546699"); SetOutputValue(inputs, &outputs, PHONE_HOME_WHOLE_NUMBER, "2155546699");
validity_data = validity_data =
controller()->InputsAreValid(outputs, controller()->InputsAreValid(outputs, VALIDATE_EDIT);
AutofillDialogController::VALIDATE_EDIT);
EXPECT_EQ(0U, validity_data.count(PHONE_HOME_WHOLE_NUMBER)); EXPECT_EQ(0U, validity_data.count(PHONE_HOME_WHOLE_NUMBER));
// Input an invalid local phone number. // Input an invalid local phone number.
SetOutputValue(inputs, &outputs, PHONE_HOME_WHOLE_NUMBER, "215554669"); SetOutputValue(inputs, &outputs, PHONE_HOME_WHOLE_NUMBER, "215554669");
validity_data = validity_data =
controller()->InputsAreValid(outputs, controller()->InputsAreValid(outputs, VALIDATE_EDIT);
AutofillDialogController::VALIDATE_EDIT);
EXPECT_EQ(1U, validity_data.count(PHONE_HOME_WHOLE_NUMBER)); EXPECT_EQ(1U, validity_data.count(PHONE_HOME_WHOLE_NUMBER));
// Input an international phone number. // Input an international phone number.
SetOutputValue(inputs, &outputs, PHONE_HOME_WHOLE_NUMBER, "+33 892 70 12 39"); SetOutputValue(inputs, &outputs, PHONE_HOME_WHOLE_NUMBER, "+33 892 70 12 39");
validity_data = validity_data =
controller()->InputsAreValid(outputs, controller()->InputsAreValid(outputs, VALIDATE_EDIT);
AutofillDialogController::VALIDATE_EDIT);
EXPECT_EQ(0U, validity_data.count(PHONE_HOME_WHOLE_NUMBER)); EXPECT_EQ(0U, validity_data.count(PHONE_HOME_WHOLE_NUMBER));
// Input an invalid international phone number. // Input an invalid international phone number.
SetOutputValue(inputs, &outputs, PHONE_HOME_WHOLE_NUMBER, SetOutputValue(inputs, &outputs, PHONE_HOME_WHOLE_NUMBER,
"+112333 892 70 12 39"); "+112333 892 70 12 39");
validity_data = validity_data =
controller()->InputsAreValid(outputs, controller()->InputsAreValid(outputs, VALIDATE_EDIT);
AutofillDialogController::VALIDATE_EDIT);
EXPECT_EQ(1U, validity_data.count(PHONE_HOME_WHOLE_NUMBER)); EXPECT_EQ(1U, validity_data.count(PHONE_HOME_WHOLE_NUMBER));
} }
...@@ -528,21 +520,18 @@ TEST_F(AutofillDialogControllerTest, CardHolderNameValidation) { ...@@ -528,21 +520,18 @@ TEST_F(AutofillDialogControllerTest, CardHolderNameValidation) {
// Input an empty card holder name with VALIDATE_FINAL. // Input an empty card holder name with VALIDATE_FINAL.
SetOutputValue(inputs, &outputs, CREDIT_CARD_NAME, ""); SetOutputValue(inputs, &outputs, CREDIT_CARD_NAME, "");
ValidityData validity_data = ValidityData validity_data =
controller()->InputsAreValid(outputs, controller()->InputsAreValid(outputs, VALIDATE_FINAL);
AutofillDialogController::VALIDATE_FINAL);
EXPECT_EQ(1U, validity_data.count(CREDIT_CARD_NAME)); EXPECT_EQ(1U, validity_data.count(CREDIT_CARD_NAME));
// Input an empty card holder name with VALIDATE_EDIT. // Input an empty card holder name with VALIDATE_EDIT.
validity_data = validity_data =
controller()->InputsAreValid(outputs, controller()->InputsAreValid(outputs, VALIDATE_EDIT);
AutofillDialogController::VALIDATE_EDIT);
EXPECT_EQ(0U, validity_data.count(CREDIT_CARD_NAME)); EXPECT_EQ(0U, validity_data.count(CREDIT_CARD_NAME));
// Input a non-empty card holder name. // Input a non-empty card holder name.
SetOutputValue(inputs, &outputs, CREDIT_CARD_NAME, "Bob"); SetOutputValue(inputs, &outputs, CREDIT_CARD_NAME, "Bob");
validity_data = validity_data =
controller()->InputsAreValid(outputs, controller()->InputsAreValid(outputs, VALIDATE_FINAL);
AutofillDialogController::VALIDATE_FINAL);
EXPECT_EQ(0U, validity_data.count(CREDIT_CARD_NAME)); EXPECT_EQ(0U, validity_data.count(CREDIT_CARD_NAME));
// Switch to Wallet which only considers names with with at least two names to // Switch to Wallet which only considers names with with at least two names to
...@@ -565,46 +554,40 @@ TEST_F(AutofillDialogControllerTest, CardHolderNameValidation) { ...@@ -565,46 +554,40 @@ TEST_F(AutofillDialogControllerTest, CardHolderNameValidation) {
// change this behavior. // change this behavior.
SetOutputValue(wallet_inputs, &wallet_outputs, CREDIT_CARD_NAME, ""); SetOutputValue(wallet_inputs, &wallet_outputs, CREDIT_CARD_NAME, "");
validity_data = validity_data =
controller()->InputsAreValid(wallet_outputs, controller()->InputsAreValid(wallet_outputs, VALIDATE_FINAL);
AutofillDialogController::VALIDATE_FINAL);
EXPECT_EQ(1U, validity_data.count(CREDIT_CARD_NAME)); EXPECT_EQ(1U, validity_data.count(CREDIT_CARD_NAME));
// Input an empty card holder name with VALIDATE_EDIT. Data source should not // Input an empty card holder name with VALIDATE_EDIT. Data source should not
// change this behavior. // change this behavior.
validity_data = validity_data =
controller()->InputsAreValid(wallet_outputs, controller()->InputsAreValid(wallet_outputs, VALIDATE_EDIT);
AutofillDialogController::VALIDATE_EDIT);
EXPECT_EQ(0U, validity_data.count(CREDIT_CARD_NAME)); EXPECT_EQ(0U, validity_data.count(CREDIT_CARD_NAME));
// Input a one name card holder name. Wallet does not currently support this. // Input a one name card holder name. Wallet does not currently support this.
SetOutputValue(wallet_inputs, &wallet_outputs, CREDIT_CARD_NAME, "Bob"); SetOutputValue(wallet_inputs, &wallet_outputs, CREDIT_CARD_NAME, "Bob");
validity_data = validity_data =
controller()->InputsAreValid(wallet_outputs, controller()->InputsAreValid(wallet_outputs, VALIDATE_FINAL);
AutofillDialogController::VALIDATE_FINAL);
EXPECT_EQ(1U, validity_data.count(CREDIT_CARD_NAME)); EXPECT_EQ(1U, validity_data.count(CREDIT_CARD_NAME));
// Input a two name card holder name. // Input a two name card holder name.
SetOutputValue(wallet_inputs, &wallet_outputs, CREDIT_CARD_NAME, SetOutputValue(wallet_inputs, &wallet_outputs, CREDIT_CARD_NAME,
"Bob Barker"); "Bob Barker");
validity_data = validity_data =
controller()->InputsAreValid(wallet_outputs, controller()->InputsAreValid(wallet_outputs, VALIDATE_FINAL);
AutofillDialogController::VALIDATE_FINAL);
EXPECT_EQ(0U, validity_data.count(CREDIT_CARD_NAME)); EXPECT_EQ(0U, validity_data.count(CREDIT_CARD_NAME));
// Input a more than two name card holder name. // Input a more than two name card holder name.
SetOutputValue(wallet_inputs, &wallet_outputs, CREDIT_CARD_NAME, SetOutputValue(wallet_inputs, &wallet_outputs, CREDIT_CARD_NAME,
"John Jacob Jingleheimer Schmidt"); "John Jacob Jingleheimer Schmidt");
validity_data = validity_data =
controller()->InputsAreValid(wallet_outputs, controller()->InputsAreValid(wallet_outputs, VALIDATE_FINAL);
AutofillDialogController::VALIDATE_FINAL);
EXPECT_EQ(0U, validity_data.count(CREDIT_CARD_NAME)); EXPECT_EQ(0U, validity_data.count(CREDIT_CARD_NAME));
// Input a card holder name with lots of crazy whitespace. // Input a card holder name with lots of crazy whitespace.
SetOutputValue(wallet_inputs, &wallet_outputs, CREDIT_CARD_NAME, SetOutputValue(wallet_inputs, &wallet_outputs, CREDIT_CARD_NAME,
" \\n\\r John \\n Jacob Jingleheimer \\t Schmidt "); " \\n\\r John \\n Jacob Jingleheimer \\t Schmidt ");
validity_data = validity_data =
controller()->InputsAreValid(wallet_outputs, controller()->InputsAreValid(wallet_outputs, VALIDATE_FINAL);
AutofillDialogController::VALIDATE_FINAL);
EXPECT_EQ(0U, validity_data.count(CREDIT_CARD_NAME)); EXPECT_EQ(0U, validity_data.count(CREDIT_CARD_NAME));
} }
...@@ -1907,4 +1890,38 @@ TEST_F(AutofillDialogControllerTest, NotProdNotification) { ...@@ -1907,4 +1890,38 @@ TEST_F(AutofillDialogControllerTest, NotProdNotification) {
NotificationsOfType(DialogNotification::DEVELOPER_WARNING).empty()); NotificationsOfType(DialogNotification::DEVELOPER_WARNING).empty());
} }
// Ensure Wallet instruments marked expired by the server are shown as invalid.
TEST_F(AutofillDialogControllerTest, WalletExpiredCard) {
scoped_ptr<wallet::WalletItems> wallet_items = wallet::GetTestWalletItems();
wallet_items->AddInstrument(wallet::GetTestMaskedInstrumentExpired());
controller()->OnDidGetWalletItems(wallet_items.Pass());
EXPECT_TRUE(controller()->IsEditingExistingData(SECTION_CC_BILLING));
// Use |SetOutputValue()| to put the right AutofillFieldTypes into the map.
const DetailInputs& inputs =
controller()->RequestedFieldsForSection(SECTION_CC_BILLING);
DetailOutputMap outputs;
SetOutputValue(inputs, &outputs, COMPANY_NAME, "Bluth Company");
ValidityData validity_data =
controller()->InputsAreValid(outputs, VALIDATE_EDIT);
EXPECT_EQ(1U, validity_data.count(CREDIT_CARD_EXP_MONTH));
EXPECT_EQ(1U, validity_data.count(CREDIT_CARD_EXP_4_DIGIT_YEAR));
// Make the local input year differ from the instrument.
SetOutputValue(inputs, &outputs, CREDIT_CARD_EXP_4_DIGIT_YEAR, "3002");
validity_data = controller()->InputsAreValid(outputs, VALIDATE_EDIT);
EXPECT_EQ(0U, validity_data.count(CREDIT_CARD_EXP_MONTH));
EXPECT_EQ(0U, validity_data.count(CREDIT_CARD_EXP_4_DIGIT_YEAR));
// Make the local input month differ from the instrument.
SetOutputValue(inputs, &outputs, CREDIT_CARD_EXP_MONTH, "06");
validity_data = controller()->InputsAreValid(outputs, VALIDATE_EDIT);
EXPECT_EQ(0U, validity_data.count(CREDIT_CARD_EXP_MONTH));
EXPECT_EQ(0U, validity_data.count(CREDIT_CARD_EXP_4_DIGIT_YEAR));
}
} // namespace autofill } // namespace autofill
...@@ -154,6 +154,11 @@ struct SuggestionState { ...@@ -154,6 +154,11 @@ struct SuggestionState {
bool editable; bool editable;
}; };
enum ValidationType {
VALIDATE_EDIT, // Validate user edits. Allow for empty fields.
VALIDATE_FINAL, // Full form validation. Required fields can't be empty.
};
typedef std::vector<DetailInput> DetailInputs; typedef std::vector<DetailInput> DetailInputs;
typedef std::map<const DetailInput*, string16> DetailOutputMap; typedef std::map<const DetailInput*, string16> DetailOutputMap;
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
#ifndef CHROME_BROWSER_UI_AUTOFILL_AUTOFILL_DIALOG_VIEW_H_ #ifndef CHROME_BROWSER_UI_AUTOFILL_AUTOFILL_DIALOG_VIEW_H_
#define CHROME_BROWSER_UI_AUTOFILL_AUTOFILL_DIALOG_VIEW_H_ #define CHROME_BROWSER_UI_AUTOFILL_AUTOFILL_DIALOG_VIEW_H_
#include "chrome/browser/ui/autofill/autofill_dialog_controller.h" #include "chrome/browser/ui/autofill/autofill_dialog_types.h"
namespace content { namespace content {
class NavigationController; class NavigationController;
...@@ -17,6 +17,7 @@ class Size; ...@@ -17,6 +17,7 @@ class Size;
namespace autofill { namespace autofill {
class AutofillDialogController;
class TestableAutofillDialogView; class TestableAutofillDialogView;
// An interface for the dialog that appears when a site initiates an Autofill // An interface for the dialog that appears when a site initiates an Autofill
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "base/mac/bundle_locations.h" #include "base/mac/bundle_locations.h"
#include "base/memory/scoped_nsobject.h" #include "base/memory/scoped_nsobject.h"
#include "chrome/browser/ui/autofill/autofill_dialog_controller.h"
#include "chrome/browser/ui/chrome_style.h" #include "chrome/browser/ui/chrome_style.h"
#import "chrome/browser/ui/cocoa/constrained_window/constrained_window_button.h" #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_button.h"
#include "chrome/browser/ui/chrome_style.h" #include "chrome/browser/ui/chrome_style.h"
......
...@@ -1249,7 +1249,7 @@ void AutofillDialogViews::OnDidChangeFocus( ...@@ -1249,7 +1249,7 @@ void AutofillDialogViews::OnDidChangeFocus(
if (focused_before) { if (focused_before) {
DetailsGroup* group = GroupForView(focused_before); DetailsGroup* group = GroupForView(focused_before);
if (group && group->container->visible()) if (group && group->container->visible())
ValidateGroup(*group, AutofillDialogController::VALIDATE_EDIT); ValidateGroup(*group, VALIDATE_EDIT);
} }
// Show an error bubble when the user focuses the input. // Show an error bubble when the user focuses the input.
...@@ -1270,7 +1270,7 @@ void AutofillDialogViews::LinkClicked(views::Link* source, int event_flags) { ...@@ -1270,7 +1270,7 @@ void AutofillDialogViews::LinkClicked(views::Link* source, int event_flags) {
void AutofillDialogViews::OnSelectedIndexChanged(views::Combobox* combobox) { void AutofillDialogViews::OnSelectedIndexChanged(views::Combobox* combobox) {
DetailsGroup* group = GroupForView(combobox); DetailsGroup* group = GroupForView(combobox);
ValidateGroup(*group, AutofillDialogController::VALIDATE_EDIT); ValidateGroup(*group, VALIDATE_EDIT);
} }
void AutofillDialogViews::StyledLabelLinkClicked(const ui::Range& range, void AutofillDialogViews::StyledLabelLinkClicked(const ui::Range& range,
...@@ -1564,7 +1564,7 @@ void AutofillDialogViews::UpdateDetailsGroupState(const DetailsGroup& group) { ...@@ -1564,7 +1564,7 @@ void AutofillDialogViews::UpdateDetailsGroupState(const DetailsGroup& group) {
group.container->SetForwardMouseEvents(has_menu && show_suggestions); group.container->SetForwardMouseEvents(has_menu && show_suggestions);
group.container->SetVisible(controller_->SectionIsActive(group.section)); group.container->SetVisible(controller_->SectionIsActive(group.section));
if (group.container->visible()) if (group.container->visible())
ValidateGroup(group, AutofillDialogController::VALIDATE_EDIT); ValidateGroup(group, VALIDATE_EDIT);
} }
ContentsPreferredSizeChanged(); ContentsPreferredSizeChanged();
...@@ -1607,9 +1607,8 @@ void AutofillDialogViews::ShowErrorBubbleForViewIfNecessary(views::View* view) { ...@@ -1607,9 +1607,8 @@ void AutofillDialogViews::ShowErrorBubbleForViewIfNecessary(views::View* view) {
error_bubble_.reset(new ErrorBubble(input, error_message->second)); error_bubble_.reset(new ErrorBubble(input, error_message->second));
} }
bool AutofillDialogViews::ValidateGroup( bool AutofillDialogViews::ValidateGroup(const DetailsGroup& group,
const DetailsGroup& group, ValidationType validation_type) {
AutofillDialogController::ValidationType validation_type) {
DCHECK(group.container->visible()); DCHECK(group.container->visible());
scoped_ptr<DetailInput> cvc_input; scoped_ptr<DetailInput> cvc_input;
...@@ -1683,7 +1682,7 @@ bool AutofillDialogViews::ValidateForm() { ...@@ -1683,7 +1682,7 @@ bool AutofillDialogViews::ValidateForm() {
if (!group.container->visible()) if (!group.container->visible())
continue; continue;
if (!ValidateGroup(group, AutofillDialogController::VALIDATE_FINAL)) if (!ValidateGroup(group, VALIDATE_FINAL))
all_valid = false; all_valid = false;
} }
...@@ -1737,7 +1736,7 @@ void AutofillDialogViews::TextfieldEditedOrActivated( ...@@ -1737,7 +1736,7 @@ void AutofillDialogViews::TextfieldEditedOrActivated(
// If the field transitioned from invalid to valid, re-validate the group, // If the field transitioned from invalid to valid, re-validate the group,
// since inter-field checks become meaningful with valid fields. // since inter-field checks become meaningful with valid fields.
if (!decorated->invalid()) if (!decorated->invalid())
ValidateGroup(*group, AutofillDialogController::VALIDATE_EDIT); ValidateGroup(*group, VALIDATE_EDIT);
} }
gfx::Image icon = controller_->IconForField(type, textfield->text()); gfx::Image icon = controller_->IconForField(type, textfield->text());
......
...@@ -498,8 +498,7 @@ class AutofillDialogViews : public AutofillDialogView, ...@@ -498,8 +498,7 @@ class AutofillDialogViews : public AutofillDialogView,
// Checks all manual inputs in |group| for validity. Decorates the invalid // Checks all manual inputs in |group| for validity. Decorates the invalid
// ones and returns true if all were valid. // ones and returns true if all were valid.
bool ValidateGroup(const DetailsGroup& group, bool ValidateGroup(const DetailsGroup& group, ValidationType type);
AutofillDialogController::ValidationType type);
// Checks all manual inputs in the form for validity. Decorates the invalid // Checks all manual inputs in the form for validity. Decorates the invalid
// ones and returns true if all were valid. // ones and returns true if all were valid.
......
...@@ -114,6 +114,14 @@ scoped_ptr<WalletItems::MaskedInstrument> GetTestMaskedInstrument() { ...@@ -114,6 +114,14 @@ scoped_ptr<WalletItems::MaskedInstrument> GetTestMaskedInstrument() {
return GetTestMaskedInstrumentWithId("default_instrument_id"); return GetTestMaskedInstrumentWithId("default_instrument_id");
} }
scoped_ptr<WalletItems::MaskedInstrument> GetTestMaskedInstrumentExpired() {
return GetTestMaskedInstrumentWithDetails(
"default_instrument_id",
GetTestAddress(),
WalletItems::MaskedInstrument::VISA,
WalletItems::MaskedInstrument::EXPIRED);
}
scoped_ptr<WalletItems::MaskedInstrument> GetTestMaskedInstrumentInvalid() { scoped_ptr<WalletItems::MaskedInstrument> GetTestMaskedInstrumentInvalid() {
return GetTestMaskedInstrumentWithDetails( return GetTestMaskedInstrumentWithDetails(
"default_instrument_id", "default_instrument_id",
......
...@@ -21,6 +21,7 @@ scoped_ptr<FullWallet> GetTestFullWallet(); ...@@ -21,6 +21,7 @@ scoped_ptr<FullWallet> GetTestFullWallet();
scoped_ptr<Instrument> GetTestInstrument(); scoped_ptr<Instrument> GetTestInstrument();
scoped_ptr<WalletItems::LegalDocument> GetTestLegalDocument(); scoped_ptr<WalletItems::LegalDocument> GetTestLegalDocument();
scoped_ptr<WalletItems::MaskedInstrument> GetTestMaskedInstrument(); scoped_ptr<WalletItems::MaskedInstrument> GetTestMaskedInstrument();
scoped_ptr<WalletItems::MaskedInstrument> GetTestMaskedInstrumentExpired();
scoped_ptr<WalletItems::MaskedInstrument> GetTestMaskedInstrumentInvalid(); scoped_ptr<WalletItems::MaskedInstrument> GetTestMaskedInstrumentInvalid();
scoped_ptr<WalletItems::MaskedInstrument> GetTestMaskedInstrumentAmex(); scoped_ptr<WalletItems::MaskedInstrument> GetTestMaskedInstrumentAmex();
scoped_ptr<WalletItems::MaskedInstrument> GetTestNonDefaultMaskedInstrument(); scoped_ptr<WalletItems::MaskedInstrument> GetTestNonDefaultMaskedInstrument();
......
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