// Copyright 2013 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/autofill/autofill_dialog_controller_impl.h" #include #include #include #include "base/bind.h" #include "base/bind_helpers.h" #include "base/i18n/case_conversion.h" #include "base/i18n/rtl.h" #include "base/location.h" #include "base/logging.h" #include "base/prefs/pref_registry_simple.h" #include "base/prefs/pref_service.h" #include "base/prefs/scoped_user_pref_update.h" #include "base/rand_util.h" #include "base/single_thread_task_runner.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/thread_task_runner_handle.h" #include "base/time/time.h" #include "chrome/browser/autofill/personal_data_manager_factory.h" #include "chrome/browser/autofill/risk_util.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 "chrome/browser/ui/autofill/autofill_dialog_i18n_input.h" #include "chrome/browser/ui/autofill/autofill_dialog_view.h" #include "chrome/browser/ui/autofill/data_model_wrapper.h" #include "chrome/browser/ui/autofill/new_credit_card_bubble_controller.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_navigator.h" #include "chrome/browser/ui/browser_navigator_params.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/common/pref_names.h" #include "chrome/common/url_constants.h" #include "chrome/grit/chromium_strings.h" #include "chrome/grit/generated_resources.h" #include "components/autofill/core/browser/address_i18n.h" #include "components/autofill/core/browser/autofill_country.h" #include "components/autofill/core/browser/autofill_data_model.h" #include "components/autofill/core/browser/autofill_manager.h" #include "components/autofill/core/browser/autofill_type.h" #include "components/autofill/core/browser/detail_input.h" #include "components/autofill/core/browser/field_types.h" #include "components/autofill/core/browser/personal_data_manager.h" #include "components/autofill/core/browser/phone_number_i18n.h" #include "components/autofill/core/browser/server_field_types_util.h" #include "components/autofill/core/browser/validation.h" #include "components/autofill/core/common/autofill_pref_names.h" #include "components/autofill/core/common/form_data.h" #include "components/pref_registry/pref_registry_syncable.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/geolocation_provider.h" #include "content/public/browser/navigation_controller.h" #include "content/public/browser/navigation_details.h" #include "content/public/browser/navigation_entry.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_types.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" #include "content/public/common/url_constants.h" #include "grit/components_scaled_resources.h" #include "grit/components_strings.h" #include "grit/platform_locale_settings.h" #include "grit/theme_resources.h" #include "net/cert/cert_status_flags.h" #include "third_party/libaddressinput/chromium/chrome_metadata_source.h" #include "third_party/libaddressinput/chromium/chrome_storage_impl.h" #include "third_party/libaddressinput/messages.h" #include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_data.h" #include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_field.h" #include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_problem.h" #include "third_party/libaddressinput/src/cpp/include/libaddressinput/localization.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/models/combobox_model.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/canvas.h" #include "ui/gfx/geometry/vector2d.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::ADMIN_AREA; using ::i18n::addressinput::DEPENDENT_LOCALITY; using ::i18n::addressinput::FieldProblemMap; using ::i18n::addressinput::Localization; using ::i18n::addressinput::MISSING_REQUIRED_FIELD; namespace autofill { namespace { const char kAddNewItemKey[] = "add-new-item"; const char kManageItemsKey[] = "manage-items"; const char kSameAsBillingKey[] = "same-as-billing"; // Keys for the kAutofillDialogAutofillDefault pref dictionary (do not change // these values). const char kGuidPrefKey[] = "guid"; // This string is stored along with saved addresses and credit cards in the // WebDB, and hence should not be modified, so that it remains consistent over // time. const char kAutofillDialogOrigin[] = "Chrome Autofill dialog"; // The number of milliseconds to delay enabling the submit button after showing // the dialog. This delay prevents users from accidentally clicking the submit // button on startup. const int kSubmitButtonDelayMs = 1000; // A helper class to make sure an AutofillDialogView knows when a series of // updates is incoming. class ScopedViewUpdates { public: explicit ScopedViewUpdates(AutofillDialogView* view) : view_(view) { if (view_) view_->UpdatesStarted(); } ~ScopedViewUpdates() { if (view_) view_->UpdatesFinished(); } private: AutofillDialogView* view_; 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. bool ServerTypeMatchesShippingField(ServerFieldType type, const AutofillField& field) { // Equivalent billing field type is used to support UseBillingAsShipping // usecase. return ServerTypeEncompassesFieldType( type, AutofillType(AutofillType::GetEquivalentBillingFieldType( field.Type().GetStorableType()))); } // Initializes |form_group| from user-entered data. void FillFormGroupFromOutputs(const FieldValueMap& detail_outputs, FormGroup* form_group) { for (FieldValueMap::const_iterator iter = detail_outputs.begin(); iter != detail_outputs.end(); ++iter) { ServerFieldType type = iter->first; if (!iter->second.empty()) { form_group->SetInfo(AutofillType(type), iter->second, g_browser_process->GetApplicationLocale()); } } } // Returns a string descriptor for a DialogSection, for use with prefs (do not // change these values). std::string SectionToPrefString(DialogSection section) { switch (section) { case SECTION_CC: return "cc"; case SECTION_BILLING: return "billing"; case SECTION_SHIPPING: return "shipping"; } NOTREACHED(); return std::string(); } gfx::Image CreditCardIconForType(const std::string& credit_card_type) { const int input_card_idr = CreditCard::IconResourceId(credit_card_type); ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); if (input_card_idr == IDR_AUTOFILL_CC_GENERIC) { // When the credit card type is unknown, no image should be shown. However, // to simplify the view code on Mac, save space for the credit card image by // returning a transparent image of the appropriate size. All credit card // icons are the same size. return gfx::Image(gfx::ImageSkiaOperations::CreateTransparentImage( rb.GetImageNamed(IDR_AUTOFILL_CC_GENERIC).AsImageSkia(), 0)); } return rb.GetImageNamed(input_card_idr); } gfx::Image CvcIconForCreditCardType(const base::string16& credit_card_type) { ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); if (credit_card_type == l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_AMEX)) return rb.GetImageNamed(IDR_CREDIT_CARD_CVC_HINT_AMEX); return rb.GetImageNamed(IDR_CREDIT_CARD_CVC_HINT); } ServerFieldType CountryTypeForSection(DialogSection section) { return section == SECTION_SHIPPING ? ADDRESS_HOME_COUNTRY : ADDRESS_BILLING_COUNTRY; } ValidityMessage GetPhoneValidityMessage(const base::string16& country_name, const base::string16& number) { std::string region = AutofillCountry::GetCountryCode( country_name, g_browser_process->GetApplicationLocale()); i18n::PhoneObject phone_object(number, region); ValidityMessage phone_message(base::string16(), true); // Check if the phone number is invalid. Allow valid international // numbers that don't match the address's country only if they have an // international calling code. if (!phone_object.IsValidNumber() || (phone_object.country_code().empty() && phone_object.region() != region)) { phone_message.text = l10n_util::GetStringUTF16( IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_PHONE_NUMBER); } return phone_message; } // Constructs |inputs| from template data for a given |dialog_section|. // |country_country| specifies the country code that the inputs should be built // for. Sets the |language_code| to be used for address formatting, if // internationalized address input is enabled. The |language_code| parameter can // be NULL. void BuildInputsForSection(DialogSection dialog_section, const std::string& country_code, DetailInputs* inputs, std::string* language_code) { using l10n_util::GetStringUTF16; const DetailInput kCCInputs[] = { { DetailInput::LONG, CREDIT_CARD_NUMBER, GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_CARD_NUMBER) }, { DetailInput::SHORT, CREDIT_CARD_EXP_MONTH, GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_EXPIRY_MONTH) }, { DetailInput::SHORT, CREDIT_CARD_EXP_4_DIGIT_YEAR, GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_EXPIRY_YEAR) }, { DetailInput::SHORT_EOL, CREDIT_CARD_VERIFICATION_CODE, GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_CVC), 1.5 }, }; const DetailInput kBillingPhoneInputs[] = { { DetailInput::LONG, PHONE_BILLING_WHOLE_NUMBER, GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_PHONE_NUMBER) }, }; const DetailInput kEmailInputs[] = { { DetailInput::LONG, EMAIL_ADDRESS, GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_EMAIL) }, }; const DetailInput kShippingPhoneInputs[] = { { DetailInput::LONG, PHONE_HOME_WHOLE_NUMBER, GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_PHONE_NUMBER) }, }; switch (dialog_section) { case SECTION_CC: { BuildInputs(kCCInputs, arraysize(kCCInputs), inputs); break; } case SECTION_BILLING: { i18ninput::BuildAddressInputs(common::ADDRESS_TYPE_BILLING, country_code, inputs, language_code); BuildInputs(kBillingPhoneInputs, arraysize(kBillingPhoneInputs), inputs); BuildInputs(kEmailInputs, arraysize(kEmailInputs), inputs); break; } case SECTION_SHIPPING: { i18ninput::BuildAddressInputs(common::ADDRESS_TYPE_SHIPPING, country_code, inputs, language_code); BuildInputs(kShippingPhoneInputs, arraysize(kShippingPhoneInputs), inputs); break; } } } } // namespace AutofillDialogViewDelegate::~AutofillDialogViewDelegate() {} AutofillDialogControllerImpl::~AutofillDialogControllerImpl() { if (popup_controller_) popup_controller_->Hide(); AutofillMetrics::LogDialogInitialUserState(initial_user_state_); } // Checks the country code against the values the form structure enumerates. bool AutofillCountryFilter( const std::set& form_structure_values, const std::string& country_code) { if (!form_structure_values.empty() && !form_structure_values.count(base::ASCIIToUTF16(country_code))) { return false; } return true; } // Checks the country code against the values the form structure enumerates and // against the ones Wallet allows. bool WalletCountryFilter( const std::set& form_structure_values, const std::set& wallet_allowed_values, const std::string& country_code) { if ((!form_structure_values.empty() && !form_structure_values.count(base::ASCIIToUTF16(country_code))) || (!wallet_allowed_values.empty() && !wallet_allowed_values.count(country_code))) { return false; } return true; } // static base::WeakPtr AutofillDialogControllerImpl::Create( content::WebContents* contents, const FormData& form_structure, const GURL& source_url, const AutofillClient::ResultCallback& callback) { // AutofillDialogControllerImpl owns itself. AutofillDialogControllerImpl* autofill_dialog_controller = new AutofillDialogControllerImpl(contents, form_structure, source_url, callback); return autofill_dialog_controller->weak_ptr_factory_.GetWeakPtr(); } // static void AutofillDialogController::RegisterPrefs(PrefRegistrySimple* registry) { registry->RegisterListPref(::prefs::kAutofillDialogWalletLocationAcceptance); } // static void AutofillDialogController::RegisterProfilePrefs( user_prefs::PrefRegistrySyncable* registry) { registry->RegisterBooleanPref( ::prefs::kAutofillDialogPayWithoutWallet, false, user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); registry->RegisterDictionaryPref( ::prefs::kAutofillDialogAutofillDefault, user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); registry->RegisterBooleanPref( ::prefs::kAutofillDialogSaveData, true, user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); registry->RegisterBooleanPref( ::prefs::kAutofillDialogWalletShippingSameAsBilling, false, user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); } // static base::WeakPtr AutofillDialogController::Create( content::WebContents* contents, const FormData& form_structure, const GURL& source_url, const AutofillClient::ResultCallback& callback) { return AutofillDialogControllerImpl::Create(contents, form_structure, source_url, callback); } void AutofillDialogControllerImpl::Show() { dialog_shown_timestamp_ = base::Time::Now(); // Determine what field types should be included in the dialog. bool has_types = false; bool has_sections = false; form_structure_.ParseFieldTypesFromAutocompleteAttributes( &has_types, &has_sections); // Fail if the author didn't specify autocomplete types. if (!has_types) { callback_.Run( AutofillClient::AutocompleteResultErrorDisabled, base::ASCIIToUTF16("Form is missing autocomplete attributes."), NULL); delete this; return; } // Fail if the author didn't ask for at least some kind of credit card // information. bool has_credit_card_field = false; for (size_t i = 0; i < form_structure_.field_count(); ++i) { AutofillType type = form_structure_.field(i)->Type(); if (type.html_type() != HTML_TYPE_UNKNOWN && type.group() == CREDIT_CARD) { has_credit_card_field = true; break; } } if (!has_credit_card_field) { callback_.Run(AutofillClient::AutocompleteResultErrorDisabled, base::ASCIIToUTF16( "Form is not a payment form (must contain " "some autocomplete=\"cc-*\" fields). "), NULL); delete this; return; } billing_country_combobox_model_.reset(new CountryComboboxModel()); billing_country_combobox_model_->SetCountries( *GetManager(), base::Bind(AutofillCountryFilter, form_structure_.PossibleValues(ADDRESS_BILLING_COUNTRY))); shipping_country_combobox_model_.reset(new CountryComboboxModel()); acceptable_shipping_countries_ = form_structure_.PossibleValues(ADDRESS_HOME_COUNTRY); shipping_country_combobox_model_->SetCountries( *GetManager(), base::Bind(AutofillCountryFilter, base::ConstRef(acceptable_shipping_countries_))); // If the form has a country