Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / autofill / autofill_dialog_controller_impl.cc
index db0ef10..4f19b35 100644 (file)
@@ -8,11 +8,12 @@
 #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"
@@ -26,6 +27,7 @@
 #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"
@@ -58,6 +60,7 @@
 #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 {
@@ -141,6 +156,32 @@ class ScopedViewUpdates {
   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.
@@ -214,7 +255,7 @@ void GetBillingInfoFromOutputs(const FieldValueMap& output,
 }
 
 // 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) {
@@ -224,10 +265,10 @@ ui::BaseWindow* GetBaseWindowForWebContents(
 
   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
@@ -485,6 +526,37 @@ ServerFieldType CountryTypeForSection(DialogSection section) {
                                        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() {}
@@ -599,11 +671,11 @@ void AutofillDialogControllerImpl::Show() {
 
   // 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,
@@ -614,6 +686,17 @@ void AutofillDialogControllerImpl::Show() {
   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();
@@ -642,10 +725,6 @@ void AutofillDialogControllerImpl::TabActivated() {
   }
 }
 
-TestableAutofillDialogView* AutofillDialogControllerImpl::GetTestableView() {
-  return view_ ? view_->GetTestableView() : NULL;
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // AutofillDialogViewDelegate implementation.
 
@@ -774,9 +853,9 @@ DialogOverlayState AutofillDialogControllerImpl::GetDialogOverlay() {
     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);
@@ -787,8 +866,7 @@ DialogOverlayState AutofillDialogControllerImpl::GetDialogOverlay() {
     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 ") +
@@ -1076,6 +1154,7 @@ void AutofillDialogControllerImpl::ConstructLegalDocumentsText() {
 
 void AutofillDialogControllerImpl::ResetSectionInput(DialogSection section) {
   SetEditingExistingData(section, false);
+  needs_validation_.erase(section);
 
   if (i18ninput::Enabled()) {
     CountryComboboxModel* model = CountryComboboxModelForSection(section);
@@ -1086,8 +1165,10 @@ void AutofillDialogControllerImpl::ResetSectionInput(DialogSection 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);
   }
 }
 
@@ -1163,20 +1244,20 @@ void AutofillDialogControllerImpl::RestoreUserInputFromSnapshot(
   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);
     }
   }
 }
@@ -1649,7 +1730,16 @@ base::string16 AutofillDialogControllerImpl::InputValidityMessage(
     }
   }
 
-  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(
@@ -1669,14 +1759,14 @@ base::string16 AutofillDialogControllerImpl::InputValidityMessage(
     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;
 
@@ -1700,8 +1790,9 @@ base::string16 AutofillDialogControllerImpl::InputValidityMessage(
       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);
       }
@@ -1710,6 +1801,7 @@ base::string16 AutofillDialogControllerImpl::InputValidityMessage(
     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);
       }
@@ -1736,9 +1828,9 @@ base::string16 AutofillDialogControllerImpl::InputValidityMessage(
       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.
@@ -1746,35 +1838,59 @@ ValidityMessages AutofillDialogControllerImpl::InputsAreValid(
     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) &&
@@ -1855,11 +1971,17 @@ void AutofillDialogControllerImpl::UserEditedOrActivatedInput(
   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.
@@ -1888,12 +2010,8 @@ void AutofillDialogControllerImpl::UserEditedOrActivatedInput(
                                            &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,
@@ -2580,7 +2698,6 @@ AutofillDialogControllerImpl::AutofillDialogControllerImpl(
       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),
@@ -2588,7 +2705,8 @@ AutofillDialogControllerImpl::AutofillDialogControllerImpl(
       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());
 }
@@ -2601,6 +2719,10 @@ PersonalDataManager* AutofillDialogControllerImpl::GetManager() const {
   return PersonalDataManagerFactory::GetForProfile(profile_);
 }
 
+AddressValidator* AutofillDialogControllerImpl::GetValidator() {
+  return validator_.get();
+}
+
 const wallet::WalletClient* AutofillDialogControllerImpl::GetWalletClient()
     const {
   return const_cast<AutofillDialogControllerImpl*>(this)->GetWalletClient();
@@ -2883,12 +3005,12 @@ void AutofillDialogControllerImpl::SuggestionsUpdated() {
   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);
 
@@ -2906,18 +3028,19 @@ void AutofillDialogControllerImpl::SuggestionsUpdated() {
 
 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
@@ -2940,7 +3063,6 @@ void AutofillDialogControllerImpl::FillOutputForSectionWithComparator(
 
     if (section == SECTION_CC) {
       CreditCard card;
-      card.set_origin(kAutofillDialogOrigin);
       FillFormGroupFromOutputs(output, &card);
 
       // The card holder name comes from the billing address section.
@@ -2948,6 +3070,10 @@ void AutofillDialogControllerImpl::FillOutputForSectionWithComparator(
                       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());
@@ -2955,7 +3081,7 @@ void AutofillDialogControllerImpl::FillOutputForSectionWithComparator(
       }
 
       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(
@@ -2963,16 +3089,18 @@ void AutofillDialogControllerImpl::FillOutputForSectionWithComparator(
           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_);
     }
   }
 }
@@ -3056,7 +3184,7 @@ DialogSection AutofillDialogControllerImpl::SectionForSuggestionsMenuModel(
 
 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)
@@ -3070,17 +3198,26 @@ DetailInputs* AutofillDialogControllerImpl::MutableRequestedFieldsForSection(
   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(
@@ -3170,6 +3307,17 @@ bool AutofillDialogControllerImpl::SectionIsValid(
   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 {
@@ -3178,9 +3326,9 @@ bool AutofillDialogControllerImpl::IsCreditCardExpirationValid(
   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) &&
@@ -3322,6 +3470,7 @@ scoped_ptr<wallet::Instrument> AutofillDialogControllerImpl::
   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));
@@ -3335,6 +3484,7 @@ scoped_ptr<wallet::Address>AutofillDialogControllerImpl::
 
   AutofillProfile profile;
   FillFormGroupFromOutputs(output, &profile);
+  CanonicalizeState(validator_.get(), &profile);
 
   return scoped_ptr<wallet::Address>(new wallet::Address(profile));
 }
@@ -3397,6 +3547,23 @@ void AutofillDialogControllerImpl::AnimationEnded(
   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);