#include <map>
#include <string>
-#include "apps/shell_window.h"
-#include "apps/shell_window_registry.h"
+#include "apps/app_window.h"
+#include "apps/app_window_registry.h"
#include "apps/ui/native_app_window.h"
#include "base/base64.h"
#include "base/bind.h"
+#include "base/bind_helpers.h"
#include "base/i18n/case_conversion.h"
#include "base/i18n/rtl.h"
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "chrome/browser/autofill/personal_data_manager_factory.h"
+#include "chrome/browser/autofill/validation_rules_storage_factory.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/autofill/autofill_dialog_common.h"
#include "components/autofill/core/browser/autofill_type.h"
#include "components/autofill/core/browser/personal_data_manager.h"
#include "components/autofill/core/browser/phone_number_i18n.h"
+#include "components/autofill/core/browser/state_names.h"
#include "components/autofill/core/browser/validation.h"
#include "components/autofill/core/common/form_data.h"
#include "components/user_prefs/pref_registry_syncable.h"
#include "grit/chromium_strings.h"
#include "grit/component_strings.h"
#include "grit/generated_resources.h"
+#include "grit/libaddressinput_strings.h"
#include "grit/platform_locale_settings.h"
#include "grit/theme_resources.h"
#include "grit/webkit_resources.h"
#include "net/cert/cert_status_flags.h"
+#include "third_party/libaddressinput/chromium/chrome_downloader_impl.h"
+#include "third_party/libaddressinput/chromium/chrome_storage_impl.h"
+#include "third_party/libaddressinput/chromium/cpp/include/libaddressinput/address_data.h"
+#include "third_party/libaddressinput/chromium/cpp/include/libaddressinput/address_problem.h"
#include "ui/base/base_window.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/models/combobox_model.h"
#include "ui/gfx/image/image_skia_operations.h"
#include "ui/gfx/skia_util.h"
+using ::i18n::addressinput::AddressData;
+using ::i18n::addressinput::AddressField;
+using ::i18n::addressinput::AddressProblem;
+using ::i18n::addressinput::AddressProblemFilter;
+using ::i18n::addressinput::AddressProblems;
+using ::i18n::addressinput::AddressValidator;
+
namespace autofill {
namespace {
DISALLOW_COPY_AND_ASSIGN(ScopedViewUpdates);
};
+base::string16 NullGetInfo(const AutofillType& type) {
+ return base::string16();
+}
+
+// Extract |type| from |inputs| using |section| to determine whether the info
+// should be billing or shipping specific (for sections with address info).
+base::string16 GetInfoFromInputs(const FieldValueMap& inputs,
+ DialogSection section,
+ const AutofillType& type) {
+ ServerFieldType field_type = type.GetStorableType();
+ if (section != SECTION_SHIPPING)
+ field_type = AutofillType::GetEquivalentBillingFieldType(field_type);
+
+ base::string16 info;
+ FieldValueMap::const_iterator it = inputs.find(field_type);
+ if (it != inputs.end())
+ info = it->second;
+
+ if (!info.empty() && type.html_type() == HTML_TYPE_COUNTRY_CODE) {
+ info = base::ASCIIToUTF16(AutofillCountry::GetCountryCode(
+ info, g_browser_process->GetApplicationLocale()));
+ }
+
+ return info;
+}
+
// Returns true if |input| should be used to fill a site-requested |field| which
// is notated with a "shipping" tag, for use when the user has decided to use
// the billing address as the shipping address.
}
// Returns the containing window for the given |web_contents|. The containing
-// window might be a browser window for a Chrome tab, or it might be a shell
+// window might be a browser window for a Chrome tab, or it might be an app
// window for a platform app.
ui::BaseWindow* GetBaseWindowForWebContents(
const content::WebContents* web_contents) {
gfx::NativeWindow native_window =
web_contents->GetView()->GetTopLevelNativeWindow();
- apps::ShellWindow* shell_window =
- apps::ShellWindowRegistry::
- GetShellWindowForNativeWindowAnyProfile(native_window);
- return shell_window->GetBaseWindow();
+ apps::AppWindow* app_window =
+ apps::AppWindowRegistry::GetAppWindowForNativeWindowAnyProfile(
+ native_window);
+ return app_window->GetBaseWindow();
}
// Returns a string descriptor for a DialogSection, for use with prefs (do not
ADDRESS_BILLING_COUNTRY;
}
+// profile.GetInfo() thunk.
+base::string16 GetInfoFromProfile(const AutofillProfile& profile,
+ const AutofillType& type) {
+ return profile.GetInfo(type, g_browser_process->GetApplicationLocale());
+}
+
+// Attempts to canonicalize the administrative area name in |profile| using the
+// rules in |validator|.
+void CanonicalizeState(const AddressValidator* validator,
+ AutofillProfile* profile) {
+ base::string16 administrative_area;
+ DCHECK_EQ(!!validator, !!i18ninput::Enabled());
+ if (validator) {
+ AddressData address_data;
+ i18ninput::CreateAddressData(base::Bind(&GetInfoFromProfile, *profile),
+ &address_data);
+ validator->CanonicalizeAdministrativeArea(&address_data);
+ administrative_area = base::UTF8ToUTF16(address_data.administrative_area);
+ } else {
+ // Temporary crutch for i18n-not-enabled case: works for US only.
+ state_names::GetNameAndAbbreviation(profile->GetRawInfo(ADDRESS_HOME_STATE),
+ NULL,
+ &administrative_area);
+ StringToUpperASCII(&administrative_area);
+ }
+
+ profile->SetInfo(AutofillType(ADDRESS_HOME_STATE),
+ administrative_area,
+ g_browser_process->GetApplicationLocale());
+}
+
} // namespace
AutofillDialogViewDelegate::~AutofillDialogViewDelegate() {}
// Test whether we need to show the shipping section. If filling that section
// would be a no-op, don't show it.
- const DetailInputs& inputs = RequestedFieldsForSection(SECTION_SHIPPING);
- cares_about_shipping_ = EmptyDataModelWrapper().FillFormStructure(
- inputs,
+ cares_about_shipping_ = form_structure_.FillFields(
+ RequestedTypesForSection(SECTION_SHIPPING),
base::Bind(common::ServerTypeMatchesField, SECTION_SHIPPING),
- &form_structure_);
+ base::Bind(NullGetInfo),
+ g_browser_process->GetApplicationLocale());
account_chooser_model_.reset(
new AccountChooserModel(this,
if (account_chooser_model_->WalletIsSelected())
FetchWalletCookie();
+ if (i18ninput::Enabled()) {
+ scoped_ptr< ::i18n::addressinput::Downloader> downloader(
+ new autofill::ChromeDownloaderImpl(profile_->GetRequestContext()));
+ validator_ = AddressValidator::Build(
+ downloader.Pass(),
+ ValidationRulesStorageFactory::CreateStorage(),
+ this);
+ GetValidator()->LoadRules(
+ GetManager()->GetDefaultCountryCodeForNewAddress());
+ }
+
// TODO(estade): don't show the dialog if the site didn't specify the right
// fields. First we must figure out what the "right" fields are.
SuggestionsUpdated();
}
}
-TestableAutofillDialogView* AutofillDialogControllerImpl::GetTestableView() {
- return view_ ? view_->GetTestableView() : NULL;
-}
-
////////////////////////////////////////////////////////////////////////////////
// AutofillDialogViewDelegate implementation.
return DialogOverlayState();
}
- ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
+ ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
DialogOverlayState state;
- state.string.font = rb.GetFont(ui::ResourceBundle::BaseFont).DeriveFont(3);
+ state.string.font_list = rb->GetFontList(ui::ResourceBundle::MediumFont);
const SkColor start_top_color = SkColorSetRGB(0xD6, 0xD6, 0xD6);
const SkColor start_bottom_color = SkColorSetRGB(0x98, 0x98, 0x98);
card_scrambling_delay_.Stop();
card_scrambling_refresher_.Stop();
- base::string16 cc_number =
- full_wallet_->GetInfo(AutofillType(CREDIT_CARD_NUMBER));
+ base::string16 cc_number = base::ASCIIToUTF16(full_wallet_->GetPan());
DCHECK_GE(cc_number.size(), 4U);
state.image = GetGeneratedCardImage(
base::ASCIIToUTF16("XXXX XXXX XXXX ") +
void AutofillDialogControllerImpl::ResetSectionInput(DialogSection section) {
SetEditingExistingData(section, false);
+ needs_validation_.erase(section);
if (i18ninput::Enabled()) {
CountryComboboxModel* model = CountryComboboxModelForSection(section);
}
DetailInputs* inputs = MutableRequestedFieldsForSection(section);
- for (DetailInputs::iterator it = inputs->begin(); it != inputs->end(); ++it) {
- it->initial_value = common::GetHardcodedValueForType(it->type);
+ for (DetailInputs::iterator it = inputs->begin();
+ it != inputs->end(); ++it) {
+ if (it->length != DetailInput::NONE)
+ it->initial_value = common::GetHardcodedValueForType(it->type);
}
}
if (snapshot.empty())
return;
- FieldMapWrapper wrapper(snapshot);
for (size_t i = SECTION_MIN; i <= SECTION_MAX; ++i) {
DialogSection section = static_cast<DialogSection>(i);
if (!SectionIsActive(section))
continue;
DetailInputs* inputs = MutableRequestedFieldsForSection(section);
- wrapper.FillInputs(inputs);
-
for (size_t i = 0; i < inputs->size(); ++i) {
- if (InputWasEdited((*inputs)[i].type, (*inputs)[i].initial_value)) {
- SuggestionsMenuModelForSection(section)->SetCheckedItem(kAddNewItemKey);
- break;
+ DetailInput* input = &(*inputs)[i];
+ if (input->length != DetailInput::NONE) {
+ input->initial_value =
+ GetInfoFromInputs(snapshot, section, AutofillType(input->type));
}
+ if (InputWasEdited(input->type, input->initial_value))
+ SuggestionsMenuModelForSection(section)->SetCheckedItem(kAddNewItemKey);
}
}
}
}
}
- switch (AutofillType(type).GetStorableType()) {
+ AutofillType autofill_type(type);
+ if (i18ninput::Enabled() &&
+ (autofill_type.group() == ADDRESS_HOME ||
+ autofill_type.group() == ADDRESS_BILLING)) {
+ // TODO(dbeam): delete all US-specific address validation when
+ // --enable-autofill-address-i18n is removed.
+ return base::string16();
+ }
+
+ switch (autofill_type.GetStorableType()) {
case EMAIL_ADDRESS:
if (!value.empty() && !IsValidEmailAddress(value)) {
return l10n_util::GetStringUTF16(
case CREDIT_CARD_EXP_MONTH:
if (!InputWasEdited(CREDIT_CARD_EXP_MONTH, value)) {
return l10n_util::GetStringUTF16(
- IDS_AUTOFILL_DIALOG_VALIDATION_MISSING_VALUE);
+ IDS_LIBADDRESSINPUT_I18N_MISSING_REQUIRED_FIELD);
}
break;
case CREDIT_CARD_EXP_4_DIGIT_YEAR:
if (!InputWasEdited(CREDIT_CARD_EXP_4_DIGIT_YEAR, value)) {
return l10n_util::GetStringUTF16(
- IDS_AUTOFILL_DIALOG_VALIDATION_MISSING_VALUE);
+ IDS_LIBADDRESSINPUT_I18N_MISSING_REQUIRED_FIELD);
}
break;
break;
case ADDRESS_HOME_STATE:
- if (!value.empty() && !autofill::IsValidState(value) &&
+ if (!value.empty() &&!autofill::IsValidState(value) &&
CountryCodeForSection(section) == "US") {
+ DCHECK(!i18ninput::Enabled());
return l10n_util::GetStringUTF16(
IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_REGION);
}
case ADDRESS_HOME_ZIP:
if (!value.empty() && !autofill::IsValidZip(value) &&
CountryCodeForSection(section) == "US") {
+ DCHECK(!i18ninput::Enabled());
return l10n_util::GetStringUTF16(
IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_ZIP_CODE);
}
break;
}
- return value.empty() ?
- l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_VALIDATION_MISSING_VALUE) :
- base::string16();
+ return value.empty() ? l10n_util::GetStringUTF16(
+ IDS_LIBADDRESSINPUT_I18N_MISSING_REQUIRED_FIELD) :
+ base::string16();
}
// TODO(groby): Also add tests.
DialogSection section,
const FieldValueMap& inputs) {
ValidityMessages messages;
- FieldValueMap field_values;
+ if (inputs.empty())
+ return messages;
+
+ AddressValidator::Status status = AddressValidator::SUCCESS;
+ if (i18ninput::Enabled() && section != SECTION_CC) {
+ AutofillProfile profile;
+ FillFormGroupFromOutputs(inputs, &profile);
+ AddressData address_data;
+ i18ninput::CreateAddressData(base::Bind(&GetInfoFromProfile, profile),
+ &address_data);
+
+ AddressProblems problems;
+ status = GetValidator()->ValidateAddress(address_data,
+ AddressProblemFilter(),
+ &problems);
+ common::AddressType address_type = section == SECTION_SHIPPING ?
+ common::ADDRESS_TYPE_SHIPPING : common::ADDRESS_TYPE_BILLING;
+ for (size_t i = 0; i < problems.size(); ++i) {
+ const AddressProblem& problem = problems[i];
+ bool sure = problem.type != AddressProblem::MISSING_REQUIRED_FIELD;
+ base::string16 text = l10n_util::GetStringUTF16(problem.description_id);
+ messages.Set(i18ninput::TypeForField(problem.field, address_type),
+ ValidityMessage(text, sure));
+ }
+ }
+
for (FieldValueMap::const_iterator iter = inputs.begin();
iter != inputs.end(); ++iter) {
const ServerFieldType type = iter->first;
-
base::string16 text = InputValidityMessage(section, type, iter->second);
- // Skip empty/unchanged fields in edit mode. Ignore country as it always has
- // a value. If the individual field does not have validation errors, assume
- // it to be valid unless later proven otherwise.
- bool sure = InputWasEdited(type, iter->second) ||
- AutofillType(type).GetStorableType() == ADDRESS_HOME_COUNTRY;
-
- // Consider only individually valid fields for inter-field validation.
- if (text.empty()) {
- field_values[type] = iter->second;
- // If the field is valid but can be invalidated by inter-field validation,
- // assume it to be unsure.
- if (type == CREDIT_CARD_EXP_4_DIGIT_YEAR ||
- type == CREDIT_CARD_EXP_MONTH ||
- type == CREDIT_CARD_VERIFICATION_CODE ||
- type == PHONE_HOME_WHOLE_NUMBER ||
- type == PHONE_BILLING_WHOLE_NUMBER) {
- sure = false;
- }
+ // Skip empty/unchanged fields in edit mode. If the individual field does
+ // not have validation errors, assume it to be valid unless later proven
+ // otherwise.
+ bool sure = InputWasEdited(type, iter->second);
+
+ if (sure && status == AddressValidator::RULES_NOT_READY &&
+ !ComboboxModelForAutofillType(type) &&
+ (AutofillType(type).group() == ADDRESS_HOME ||
+ AutofillType(type).group() == ADDRESS_BILLING)) {
+ DCHECK(text.empty());
+ // TODO(estade): string translation or remove this (sweet) hack.
+ text = l10n_util::GetStringUTF16(
+ IDS_AUTOFILL_DIALOG_VALIDATION_WAITING_FOR_RULES);
+ sure = false;
+ needs_validation_.insert(section);
}
+
messages.Set(type, ValidityMessage(text, sure));
}
+ // For the convenience of using operator[].
+ FieldValueMap& field_values = const_cast<FieldValueMap&>(inputs);
// Validate the date formed by month and year field. (Autofill dialog is
// never supposed to have 2-digit years, so not checked).
if (field_values.count(CREDIT_CARD_EXP_4_DIGIT_YEAR) &&
ScopedViewUpdates updates(view_.get());
if (type == ADDRESS_BILLING_COUNTRY || type == ADDRESS_HOME_COUNTRY) {
+ DCHECK(i18ninput::Enabled());
+
const FieldValueMap snapshot = TakeUserInputSnapshot();
+
// Clobber the inputs because the view's already been updated.
RebuildInputsForCountry(section, field_contents, true);
RestoreUserInputFromSnapshot(snapshot);
UpdateSection(section);
+
+ GetValidator()->LoadRules(AutofillCountry::GetCountryCode(
+ field_contents, g_browser_process->GetApplicationLocale()));
}
// The rest of this method applies only to textfields. If a combobox, bail.
&popup_icons,
&popup_guids_);
} else {
- std::vector<ServerFieldType> field_types;
- const DetailInputs& inputs = RequestedFieldsForSection(section);
- for (DetailInputs::const_iterator iter = inputs.begin();
- iter != inputs.end(); ++iter) {
- field_types.push_back(iter->type);
- }
+ std::vector<ServerFieldType> field_types =
+ RequestedTypesForSection(section);
GetManager()->GetProfileSuggestions(AutofillType(type),
field_contents,
false,
suggested_shipping_(this),
cares_about_shipping_(true),
popup_input_type_(UNKNOWN_TYPE),
- weak_ptr_factory_(this),
waiting_for_explicit_sign_in_response_(false),
has_accepted_legal_documents_(false),
is_submitting_(false),
wallet_server_validation_recoverable_(true),
data_was_passed_back_(false),
was_ui_latency_logged_(false),
- card_generated_animation_(2000, 60, this) {
+ card_generated_animation_(2000, 60, this),
+ weak_ptr_factory_(this) {
// TODO(estade): remove duplicates from |form_structure|?
DCHECK(!callback_.is_null());
}
return PersonalDataManagerFactory::GetForProfile(profile_);
}
+AddressValidator* AutofillDialogControllerImpl::GetValidator() {
+ return validator_.get();
+}
+
const wallet::WalletClient* AutofillDialogControllerImpl::GetWalletClient()
const {
return const_cast<AutofillDialogControllerImpl*>(this)->GetWalletClient();
FieldValueMap::const_iterator billing_it =
snapshot.find(ADDRESS_BILLING_COUNTRY);
if (billing_it != snapshot.end())
- RebuildInputsForCountry(ActiveBillingSection(), billing_it->second, false);
+ RebuildInputsForCountry(ActiveBillingSection(), billing_it->second, true);
FieldValueMap::const_iterator shipping_it =
snapshot.find(ADDRESS_HOME_COUNTRY);
if (shipping_it != snapshot.end())
- RebuildInputsForCountry(SECTION_SHIPPING, shipping_it->second, false);
+ RebuildInputsForCountry(SECTION_SHIPPING, shipping_it->second, true);
RestoreUserInputFromSnapshot(snapshot);
void AutofillDialogControllerImpl::FillOutputForSectionWithComparator(
DialogSection section,
- const InputFieldComparator& compare) {
+ const FormStructure::InputFieldComparator& compare) {
if (!SectionIsActive(section))
return;
DetailInputs inputs;
std::string country_code = CountryCodeForSection(section);
common::BuildInputsForSection(section, country_code, &inputs);
+ std::vector<ServerFieldType> types = common::TypesFromInputs(inputs);
scoped_ptr<DataModelWrapper> wrapper = CreateWrapper(section);
if (wrapper) {
// Only fill in data that is associated with this section.
- wrapper->FillFormStructure(inputs, compare, &form_structure_);
+ wrapper->FillFormStructure(types, compare, &form_structure_);
// CVC needs special-casing because the CreditCard class doesn't store or
// handle them. This isn't necessary when filling the combined CC and
if (section == SECTION_CC) {
CreditCard card;
- card.set_origin(kAutofillDialogOrigin);
FillFormGroupFromOutputs(output, &card);
// The card holder name comes from the billing address section.
GetValueFromSection(SECTION_BILLING, NAME_BILLING_FULL));
if (ShouldSaveDetailsLocally()) {
+ // Only save new profiles as verified if validation rules are loaded.
+ card.set_origin(RulesAreLoaded(section) ?
+ kAutofillDialogOrigin : source_url_.GetOrigin().spec());
+
std::string guid = GetManager()->SaveImportedCreditCard(card);
newly_saved_data_model_guids_[section] = guid;
DCHECK(!profile()->IsOffTheRecord());
}
AutofillCreditCardWrapper card_wrapper(&card);
- card_wrapper.FillFormStructure(inputs, compare, &form_structure_);
+ card_wrapper.FillFormStructure(types, compare, &form_structure_);
// Again, CVC needs special-casing. Fill it in directly from |output|.
SetOutputForFieldsOfType(
output[CREDIT_CARD_VERIFICATION_CODE]);
} else {
AutofillProfile profile;
- profile.set_origin(kAutofillDialogOrigin);
FillFormGroupFromOutputs(output, &profile);
if (ShouldSaveDetailsLocally()) {
+ profile.set_origin(RulesAreLoaded(section) ?
+ kAutofillDialogOrigin : source_url_.GetOrigin().spec());
+
std::string guid = GetManager()->SaveImportedProfile(profile);
newly_saved_data_model_guids_[section] = guid;
}
AutofillProfileWrapper profile_wrapper(&profile);
- profile_wrapper.FillFormStructure(inputs, compare, &form_structure_);
+ profile_wrapper.FillFormStructure(types, compare, &form_structure_);
}
}
}
CountryComboboxModel* AutofillDialogControllerImpl::
CountryComboboxModelForSection(DialogSection section) {
- if (section == SECTION_BILLING || section == SECTION_CC_BILLING)
+ if (section == SECTION_BILLING)
return &billing_country_combobox_model_;
if (section == SECTION_SHIPPING)
return const_cast<DetailInputs*>(&RequestedFieldsForSection(section));
}
+std::vector<ServerFieldType> AutofillDialogControllerImpl::
+ RequestedTypesForSection(DialogSection section) const {
+ return common::TypesFromInputs(RequestedFieldsForSection(section));
+}
+
std::string AutofillDialogControllerImpl::CountryCodeForSection(
DialogSection section) {
+ base::string16 country;
+
scoped_ptr<DataModelWrapper> wrapper = CreateWrapper(section);
if (wrapper) {
- return AutofillCountry::GetCountryCode(
- wrapper->GetInfo(AutofillType(CountryTypeForSection(section))),
- g_browser_process->GetApplicationLocale());
+ country = wrapper->GetInfo(AutofillType(CountryTypeForSection(section)));
+ } else {
+ FieldValueMap outputs;
+ view_->GetUserInput(section, &outputs);
+ country = outputs[CountryTypeForSection(section)];
}
- CountryComboboxModel* model = CountryComboboxModelForSection(section);
- return model ? model->GetDefaultCountryCode() : std::string();
+ return AutofillCountry::GetCountryCode(
+ country, g_browser_process->GetApplicationLocale());
}
bool AutofillDialogControllerImpl::RebuildInputsForCountry(
return !InputsAreValid(section, detail_outputs).HasSureErrors();
}
+bool AutofillDialogControllerImpl::RulesAreLoaded(DialogSection section) {
+ if (!i18ninput::Enabled())
+ return true;
+
+ AddressData address_data;
+ address_data.country_code = CountryCodeForSection(section);
+ AddressValidator::Status status = GetValidator()->ValidateAddress(
+ address_data, AddressProblemFilter(), NULL);
+ return status == AddressValidator::SUCCESS;
+}
+
bool AutofillDialogControllerImpl::IsCreditCardExpirationValid(
const base::string16& year,
const base::string16& month) const {
if (!autofill::IsValidCreditCardExpirationDate(year, month, now))
return false;
- if (IsPayingWithWallet() && IsEditingExistingData(SECTION_CC_BILLING)) {
- const wallet::WalletItems::MaskedInstrument* instrument =
- ActiveInstrument();
+ const wallet::WalletItems::MaskedInstrument* instrument =
+ ActiveInstrument();
+ if (instrument) {
const std::string& locale = g_browser_process->GetApplicationLocale();
int month_int;
if (base::StringToInt(month, &month_int) &&
AutofillProfile profile;
base::string16 cvc;
GetBillingInfoFromOutputs(output, &card, &cvc, &profile);
+ CanonicalizeState(validator_.get(), &profile);
return scoped_ptr<wallet::Instrument>(
new wallet::Instrument(card, cvc, profile));
AutofillProfile profile;
FillFormGroupFromOutputs(output, &profile);
+ CanonicalizeState(validator_.get(), &profile);
return scoped_ptr<wallet::Address>(new wallet::Address(profile));
}
DoFinishSubmit();
}
+void AutofillDialogControllerImpl::OnAddressValidationRulesLoaded(
+ const std::string& country_code,
+ bool success) {
+ // TODO(dbeam): should we retry on failure?
+ for (size_t i = SECTION_MIN; i <= SECTION_MAX; ++i) {
+ DialogSection section = static_cast<DialogSection>(i);
+ if (!SectionIsActive(section) || !IsManuallyEditingSection(section))
+ continue;
+
+ if (needs_validation_.count(section) &&
+ CountryCodeForSection(section) == country_code) {
+ view_->ValidateSection(section);
+ needs_validation_.erase(section);
+ }
+ }
+}
+
void AutofillDialogControllerImpl::DoFinishSubmit() {
FillOutputForSection(SECTION_CC);
FillOutputForSection(SECTION_BILLING);