Index: chrome/browser/ui/autofill/autofill_dialog_controller_impl.cc |
diff --git a/chrome/browser/ui/autofill/autofill_dialog_controller_impl.cc b/chrome/browser/ui/autofill/autofill_dialog_controller_impl.cc |
deleted file mode 100644 |
index 45dd681729d8a80983f7cb781e44400b41e2285f..0000000000000000000000000000000000000000 |
--- a/chrome/browser/ui/autofill/autofill_dialog_controller_impl.cc |
+++ /dev/null |
@@ -1,2271 +0,0 @@ |
-// 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 <algorithm> |
-#include <map> |
-#include <string> |
-#include <utility> |
- |
-#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/macros.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/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/country_names.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 "components/prefs/pref_registry_simple.h" |
-#include "components/prefs/pref_service.h" |
-#include "components/prefs/scoped_user_pref_update.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(CountryNames::GetInstance()->GetCountryCode(info)); |
- } |
- |
- 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 = |
- CountryNames::GetInstance()->GetCountryCode(country_name); |
- 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<base::string16>& 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<base::string16>& form_structure_values, |
- const std::set<std::string>& 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> |
-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> 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. |
- form_structure_.ParseFieldTypesFromAutocompleteAttributes(); |
- |
- // Fail if the author didn't specify autocomplete types. |
- if (!form_structure_.has_author_specified_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_UNSPECIFIED && |
- 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 <select> but none of the options are valid, bail. |
- if (billing_country_combobox_model_->GetItemCount() == 0 || |
- shipping_country_combobox_model_->GetItemCount() == 0) { |
- callback_.Run(AutofillClient::AutocompleteResultErrorDisabled, |
- base::ASCIIToUTF16("No valid/supported country options" |
- " found."), |
- NULL); |
- delete this; |
- return; |
- } |
- |
- // Log any relevant UI metrics and security exceptions. |
- AutofillMetrics::LogDialogUiEvent(AutofillMetrics::DIALOG_UI_SHOWN); |
- |
- AutofillMetrics::LogDialogSecurityMetric( |
- AutofillMetrics::SECURITY_METRIC_DIALOG_SHOWN); |
- |
- // The Autofill dialog is shown in response to a message from the renderer and |
- // as such, it can only be made in the context of the current document. A call |
- // to GetActiveEntry would return a pending entry, if there was one, which |
- // would be a security bug. Therefore, we use the last committed URL for the |
- // access checks. |
- const GURL& current_url = web_contents()->GetLastCommittedURL(); |
- invoked_from_same_origin_ = |
- current_url.GetOrigin() == source_url_.GetOrigin(); |
- |
- if (!invoked_from_same_origin_) { |
- AutofillMetrics::LogDialogSecurityMetric( |
- AutofillMetrics::SECURITY_METRIC_CROSS_ORIGIN_FRAME); |
- } |
- |
- for (size_t i = SECTION_MIN; i <= SECTION_MAX; ++i) { |
- DialogSection section = static_cast<DialogSection>(i); |
- |
- std::string country_code; |
- CountryComboboxModel* model = CountryComboboxModelForSection(section); |
- if (model) |
- country_code = model->GetDefaultCountryCode(); |
- |
- DetailInputs* inputs = MutableRequestedFieldsForSection(section); |
- BuildInputsForSection( |
- section, country_code, inputs, |
- MutableAddressLanguageCodeForSection(section)); |
- } |
- |
- // Test whether we need to show the shipping section. If filling that section |
- // would be a no-op, don't show it. |
- cares_about_shipping_ = form_structure_.FillFields( |
- RequestedTypesForSection(SECTION_SHIPPING), |
- base::Bind(ServerTypeMatchesField, SECTION_SHIPPING), |
- base::Bind(NullGetInfo), std::string(), |
- g_browser_process->GetApplicationLocale()); |
- |
- transaction_amount_ = form_structure_.GetUniqueValue( |
- HTML_TYPE_TRANSACTION_AMOUNT); |
- transaction_currency_ = form_structure_.GetUniqueValue( |
- HTML_TYPE_TRANSACTION_CURRENCY); |
- acceptable_cc_types_ = form_structure_.PossibleValues(CREDIT_CARD_TYPE); |
- |
- validator_.reset(new AddressValidator( |
- std::unique_ptr<::i18n::addressinput::Source>( |
- new autofill::ChromeMetadataSource(I18N_ADDRESS_VALIDATION_DATA_URL, |
- profile_->GetRequestContext())), |
- ValidationRulesStorageFactory::CreateStorage(), this)); |
- |
- SuggestionsUpdated(); |
- SubmitButtonDelayBegin(); |
- view_.reset(CreateView()); |
- view_->Show(); |
- GetManager()->AddObserver(this); |
- |
- LogDialogLatencyToShow(); |
-} |
- |
-void AutofillDialogControllerImpl::Hide() { |
- if (view_) |
- view_->Hide(); |
-} |
- |
-// TODO(estade): remove. |
-void AutofillDialogControllerImpl::TabActivated() { |
-} |
- |
-//////////////////////////////////////////////////////////////////////////////// |
-// AutofillDialogViewDelegate implementation. |
- |
-base::string16 AutofillDialogControllerImpl::DialogTitle() const { |
- return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_TITLE); |
-} |
- |
-base::string16 AutofillDialogControllerImpl::EditSuggestionText() const { |
- return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_EDIT); |
-} |
- |
-base::string16 AutofillDialogControllerImpl::CancelButtonText() const { |
- return l10n_util::GetStringUTF16(IDS_CANCEL); |
-} |
- |
-base::string16 AutofillDialogControllerImpl::ConfirmButtonText() const { |
- return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SUBMIT_BUTTON); |
-} |
- |
-base::string16 AutofillDialogControllerImpl::SaveLocallyText() const { |
- return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SAVE_LOCALLY_CHECKBOX); |
-} |
- |
-base::string16 AutofillDialogControllerImpl::SaveLocallyTooltip() const { |
- return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SAVE_LOCALLY_TOOLTIP); |
-} |
- |
-bool AutofillDialogControllerImpl::ShouldOfferToSaveInChrome() const { |
- return IsAutofillEnabled() && !profile_->IsOffTheRecord() && |
- IsManuallyEditingAnySection(); |
-} |
- |
-bool AutofillDialogControllerImpl::ShouldSaveInChrome() const { |
- return profile_->GetPrefs()->GetBoolean(::prefs::kAutofillDialogSaveData); |
-} |
- |
-int AutofillDialogControllerImpl::GetDialogButtons() const { |
- return ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL; |
-} |
- |
-bool AutofillDialogControllerImpl::IsDialogButtonEnabled( |
- ui::DialogButton button) const { |
- if (button == ui::DIALOG_BUTTON_OK) |
- return !submit_button_delay_timer_.IsRunning(); |
- |
- DCHECK_EQ(ui::DIALOG_BUTTON_CANCEL, button); |
- return true; |
-} |
- |
-bool AutofillDialogControllerImpl::SectionIsActive(DialogSection section) |
- const { |
- return FormStructureCaresAboutSection(section); |
-} |
- |
-void AutofillDialogControllerImpl::ResetSectionInput(DialogSection section) { |
- SetEditingExistingData(section, false); |
- needs_validation_.erase(section); |
- |
- CountryComboboxModel* model = CountryComboboxModelForSection(section); |
- if (model) { |
- base::string16 country = model->GetItemAt(model->GetDefaultIndex()); |
- RebuildInputsForCountry(section, country, false); |
- } |
- |
- DetailInputs* inputs = MutableRequestedFieldsForSection(section); |
- for (DetailInputs::iterator it = inputs->begin(); |
- it != inputs->end(); ++it) { |
- if (it->length != DetailInput::NONE) { |
- it->initial_value.clear(); |
- } else if (!it->initial_value.empty() && |
- (it->type == ADDRESS_BILLING_COUNTRY || |
- it->type == ADDRESS_HOME_COUNTRY)) { |
- GetValidator()->LoadRules( |
- CountryNames::GetInstance()->GetCountryCode(it->initial_value)); |
- } |
- } |
-} |
- |
-void AutofillDialogControllerImpl::ShowEditUiIfBadSuggestion( |
- DialogSection section) { |
- // |CreateWrapper()| returns an empty wrapper if |IsEditingExistingData()|, so |
- // get the wrapper before this potentially happens below. |
- std::unique_ptr<DataModelWrapper> wrapper = CreateWrapper(section); |
- |
- // If the chosen item in |model| yields an empty suggestion text, it is |
- // invalid. In this case, show the edit UI and highlight invalid fields. |
- SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section); |
- base::string16 unused, unused2; |
- if (IsASuggestionItemKey(model->GetItemKeyForCheckedItem()) && |
- !SuggestionTextForSection(section, &unused, &unused2)) { |
- SetEditingExistingData(section, true); |
- } |
- |
- if (wrapper && IsEditingExistingData(section)) { |
- base::string16 country = |
- wrapper->GetInfo(AutofillType(CountryTypeForSection(section))); |
- if (!country.empty()) { |
- // There's no user input to restore here as this is only called after |
- // resetting all section input. |
- if (RebuildInputsForCountry(section, country, false)) |
- UpdateSection(section); |
- } |
- wrapper->FillInputs(MutableRequestedFieldsForSection(section)); |
- } |
-} |
- |
-bool AutofillDialogControllerImpl::InputWasEdited(ServerFieldType type, |
- const base::string16& value) { |
- if (value.empty()) |
- return false; |
- |
- // If this is a combobox at the default value, don't preserve it. |
- ui::ComboboxModel* model = ComboboxModelForAutofillType(type); |
- if (model && model->GetItemAt(model->GetDefaultIndex()) == value) |
- return false; |
- |
- return true; |
-} |
- |
-FieldValueMap AutofillDialogControllerImpl::TakeUserInputSnapshot() { |
- FieldValueMap snapshot; |
- if (!view_) |
- return snapshot; |
- |
- for (size_t i = SECTION_MIN; i <= SECTION_MAX; ++i) { |
- DialogSection section = static_cast<DialogSection>(i); |
- SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section); |
- if (model->GetItemKeyForCheckedItem() != kAddNewItemKey) |
- continue; |
- |
- FieldValueMap outputs; |
- view_->GetUserInput(section, &outputs); |
- // Remove fields that are empty, at their default values, or invalid. |
- for (FieldValueMap::iterator it = outputs.begin(); it != outputs.end(); |
- ++it) { |
- if (InputWasEdited(it->first, it->second) && |
- InputValidityMessage(section, it->first, it->second).empty()) { |
- snapshot.insert(std::make_pair(it->first, it->second)); |
- } |
- } |
- } |
- |
- return snapshot; |
-} |
- |
-void AutofillDialogControllerImpl::RestoreUserInputFromSnapshot( |
- const FieldValueMap& snapshot) { |
- if (snapshot.empty()) |
- return; |
- |
- for (size_t i = SECTION_MIN; i <= SECTION_MAX; ++i) { |
- DialogSection section = static_cast<DialogSection>(i); |
- if (!SectionIsActive(section)) |
- continue; |
- |
- DetailInputs* inputs = MutableRequestedFieldsForSection(section); |
- for (size_t i = 0; i < inputs->size(); ++i) { |
- 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); |
- } |
- } |
-} |
- |
-void AutofillDialogControllerImpl::UpdateSection(DialogSection section) { |
- if (view_) |
- view_->UpdateSection(section); |
-} |
- |
-void AutofillDialogControllerImpl::UpdateForErrors() { |
- if (!view_) |
- return; |
- |
- // Currently, the view should only need to be updated if validating a |
- // suggestion that's based on existing data. |
- for (size_t i = SECTION_MIN; i <= SECTION_MAX; ++i) { |
- if (IsEditingExistingData(static_cast<DialogSection>(i))) { |
- view_->UpdateForErrors(); |
- break; |
- } |
- } |
-} |
- |
-const DetailInputs& AutofillDialogControllerImpl::RequestedFieldsForSection( |
- DialogSection section) const { |
- switch (section) { |
- case SECTION_CC: |
- return requested_cc_fields_; |
- case SECTION_BILLING: |
- return requested_billing_fields_; |
- case SECTION_SHIPPING: |
- return requested_shipping_fields_; |
- } |
- |
- NOTREACHED(); |
- return requested_billing_fields_; |
-} |
- |
-ui::ComboboxModel* AutofillDialogControllerImpl::ComboboxModelForAutofillType( |
- ServerFieldType type) { |
- switch (type) { |
- case CREDIT_CARD_EXP_MONTH: |
- return &cc_exp_month_combobox_model_; |
- |
- case CREDIT_CARD_EXP_4_DIGIT_YEAR: |
- return &cc_exp_year_combobox_model_; |
- |
- case ADDRESS_BILLING_COUNTRY: |
- return billing_country_combobox_model_.get(); |
- |
- case ADDRESS_HOME_COUNTRY: |
- return shipping_country_combobox_model_.get(); |
- |
- default: |
- return NULL; |
- } |
-} |
- |
-ui::MenuModel* AutofillDialogControllerImpl::MenuModelForSection( |
- DialogSection section) { |
- SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section); |
- // The shipping section menu is special. It will always show because there is |
- // a choice between "Use billing" and "enter new". |
- if (section == SECTION_SHIPPING) |
- return model; |
- |
- // For other sections, only show a menu if there's at least one suggestion. |
- for (int i = 0; i < model->GetItemCount(); ++i) { |
- if (IsASuggestionItemKey(model->GetItemKeyAt(i))) |
- return model; |
- } |
- |
- return NULL; |
-} |
- |
-base::string16 AutofillDialogControllerImpl::LabelForSection( |
- DialogSection section) const { |
- switch (section) { |
- case SECTION_CC: |
- return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SECTION_CC); |
- case SECTION_BILLING: |
- return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SECTION_BILLING); |
- case SECTION_SHIPPING: |
- return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SECTION_SHIPPING); |
- } |
- NOTREACHED(); |
- return base::string16(); |
-} |
- |
-SuggestionState AutofillDialogControllerImpl::SuggestionStateForSection( |
- DialogSection section) { |
- base::string16 vertically_compact, horizontally_compact; |
- bool show_suggestion = SuggestionTextForSection(section, |
- &vertically_compact, |
- &horizontally_compact); |
- return SuggestionState(show_suggestion, |
- vertically_compact, |
- horizontally_compact, |
- SuggestionIconForSection(section), |
- ExtraSuggestionTextForSection(section), |
- ExtraSuggestionIconForSection(section)); |
-} |
- |
-bool AutofillDialogControllerImpl::SuggestionTextForSection( |
- DialogSection section, |
- base::string16* vertically_compact, |
- base::string16* horizontally_compact) { |
- // When the user has clicked 'edit' or a suggestion is somehow invalid (e.g. a |
- // user selects a credit card that has expired), don't show a suggestion (even |
- // though there is a profile selected in the model). |
- if (IsEditingExistingData(section)) |
- return false; |
- |
- SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section); |
- std::string item_key = model->GetItemKeyForCheckedItem(); |
- if (item_key == kSameAsBillingKey) { |
- *vertically_compact = *horizontally_compact = l10n_util::GetStringUTF16( |
- IDS_AUTOFILL_DIALOG_USING_BILLING_FOR_SHIPPING); |
- return true; |
- } |
- |
- if (!IsASuggestionItemKey(item_key)) |
- return false; |
- |
- if (section == SECTION_BILLING || section == SECTION_SHIPPING) { |
- // Also check if the address is invalid (rules may have loaded since |
- // the dialog was shown). |
- if (HasInvalidAddress(*GetManager()->GetProfileByGUID(item_key))) |
- return false; |
- } |
- |
- std::unique_ptr<DataModelWrapper> wrapper = CreateWrapper(section); |
- return wrapper->GetDisplayText(vertically_compact, horizontally_compact); |
-} |
- |
-base::string16 AutofillDialogControllerImpl::ExtraSuggestionTextForSection( |
- DialogSection section) const { |
- if (section == SECTION_CC) |
- return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_CVC); |
- |
- return base::string16(); |
-} |
- |
-std::unique_ptr<DataModelWrapper> AutofillDialogControllerImpl::CreateWrapper( |
- DialogSection section) { |
- SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section); |
- std::string item_key = model->GetItemKeyForCheckedItem(); |
- if (!IsASuggestionItemKey(item_key) || IsManuallyEditingSection(section)) |
- return std::unique_ptr<DataModelWrapper>(); |
- |
- if (section == SECTION_CC) { |
- CreditCard* card = GetManager()->GetCreditCardByGUID(item_key); |
- DCHECK(card); |
- return std::unique_ptr<DataModelWrapper>( |
- new AutofillCreditCardWrapper(card)); |
- } |
- |
- AutofillProfile* profile = GetManager()->GetProfileByGUID(item_key); |
- DCHECK(profile); |
- if (section == SECTION_SHIPPING) { |
- return std::unique_ptr<DataModelWrapper>( |
- new AutofillShippingAddressWrapper(profile)); |
- } |
- DCHECK_EQ(SECTION_BILLING, section); |
- return std::unique_ptr<DataModelWrapper>(new AutofillProfileWrapper(profile)); |
-} |
- |
-gfx::Image AutofillDialogControllerImpl::SuggestionIconForSection( |
- DialogSection section) { |
- std::unique_ptr<DataModelWrapper> model = CreateWrapper(section); |
- if (!model.get()) |
- return gfx::Image(); |
- |
- return model->GetIcon(); |
-} |
- |
-gfx::Image AutofillDialogControllerImpl::ExtraSuggestionIconForSection( |
- DialogSection section) { |
- if (section != SECTION_CC) |
- return gfx::Image(); |
- |
- std::unique_ptr<DataModelWrapper> model = CreateWrapper(section); |
- if (!model.get()) |
- return gfx::Image(); |
- |
- return CvcIconForCreditCardType( |
- model->GetInfo(AutofillType(CREDIT_CARD_TYPE))); |
-} |
- |
-FieldIconMap AutofillDialogControllerImpl::IconsForFields( |
- const FieldValueMap& user_inputs) const { |
- FieldIconMap result; |
- base::string16 credit_card_type; |
- |
- FieldValueMap::const_iterator credit_card_iter = |
- user_inputs.find(CREDIT_CARD_NUMBER); |
- if (credit_card_iter != user_inputs.end()) { |
- const base::string16& number = credit_card_iter->second; |
- const std::string type = CreditCard::GetCreditCardType(number); |
- credit_card_type = CreditCard::TypeForDisplay(type); |
- result[CREDIT_CARD_NUMBER] = CreditCardIconForType(type); |
- } |
- |
- if (!user_inputs.count(CREDIT_CARD_VERIFICATION_CODE)) |
- return result; |
- |
- result[CREDIT_CARD_VERIFICATION_CODE] = |
- CvcIconForCreditCardType(credit_card_type); |
- |
- return result; |
-} |
- |
-bool AutofillDialogControllerImpl::FieldControlsIcons( |
- ServerFieldType type) const { |
- return type == CREDIT_CARD_NUMBER; |
-} |
- |
-base::string16 AutofillDialogControllerImpl::TooltipForField( |
- ServerFieldType type) const { |
- if (type == PHONE_HOME_WHOLE_NUMBER || type == PHONE_BILLING_WHOLE_NUMBER) |
- return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_TOOLTIP_PHONE_NUMBER); |
- |
- return base::string16(); |
-} |
- |
-// TODO(groby): Add more tests. |
-base::string16 AutofillDialogControllerImpl::InputValidityMessage( |
- DialogSection section, |
- ServerFieldType type, |
- const base::string16& value) { |
- AutofillType autofill_type(type); |
- if (autofill_type.group() == ADDRESS_HOME || |
- autofill_type.group() == ADDRESS_BILLING) { |
- return base::string16(); |
- } |
- |
- switch (autofill_type.GetStorableType()) { |
- case EMAIL_ADDRESS: |
- if (!value.empty() && !IsValidEmailAddress(value)) { |
- return l10n_util::GetStringUTF16( |
- IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_EMAIL_ADDRESS); |
- } |
- break; |
- |
- case CREDIT_CARD_NUMBER: { |
- if (!value.empty()) { |
- base::string16 message = CreditCardNumberValidityMessage(value); |
- if (!message.empty()) |
- return message; |
- } |
- break; |
- } |
- |
- case CREDIT_CARD_EXP_MONTH: |
- if (!InputWasEdited(CREDIT_CARD_EXP_MONTH, value)) { |
- return l10n_util::GetStringUTF16( |
- IDS_LIBADDRESSINPUT_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_LIBADDRESSINPUT_MISSING_REQUIRED_FIELD); |
- } |
- break; |
- |
- case CREDIT_CARD_VERIFICATION_CODE: |
- if (!value.empty() && !autofill::IsValidCreditCardSecurityCode(value)) { |
- return l10n_util::GetStringUTF16( |
- IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_CREDIT_CARD_SECURITY_CODE); |
- } |
- break; |
- |
- case NAME_FULL: |
- break; |
- |
- case PHONE_HOME_WHOLE_NUMBER: // Used in shipping section. |
- break; |
- |
- case PHONE_BILLING_WHOLE_NUMBER: // Used in billing section. |
- break; |
- |
- default: |
- NOTREACHED(); // Trying to validate unknown field. |
- break; |
- } |
- |
- return value.empty() ? l10n_util::GetStringUTF16( |
- IDS_LIBADDRESSINPUT_MISSING_REQUIRED_FIELD) : |
- base::string16(); |
-} |
- |
-// TODO(groby): Also add tests. |
-ValidityMessages AutofillDialogControllerImpl::InputsAreValid( |
- DialogSection section, |
- const FieldValueMap& inputs) { |
- ValidityMessages messages; |
- if (inputs.empty()) |
- return messages; |
- |
- AddressValidator::Status status = AddressValidator::SUCCESS; |
- if (section != SECTION_CC) { |
- AutofillProfile profile; |
- FillFormGroupFromOutputs(inputs, &profile); |
- std::unique_ptr<AddressData> address_data = |
- i18n::CreateAddressDataFromAutofillProfile( |
- profile, g_browser_process->GetApplicationLocale()); |
- address_data->language_code = AddressLanguageCodeForSection(section); |
- |
- Localization localization; |
- localization.SetGetter(l10n_util::GetStringUTF8); |
- FieldProblemMap problems; |
- status = GetValidator()->ValidateAddress(*address_data, NULL, &problems); |
- bool billing = section != SECTION_SHIPPING; |
- |
- for (FieldProblemMap::const_iterator iter = problems.begin(); |
- iter != problems.end(); ++iter) { |
- bool sure = iter->second != MISSING_REQUIRED_FIELD; |
- base::string16 text = base::UTF8ToUTF16(localization.GetErrorMessage( |
- *address_data, iter->first, iter->second, true, false)); |
- messages.Set(i18n::TypeForField(iter->first, billing), |
- 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. 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()); |
- 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) && |
- field_values.count(CREDIT_CARD_EXP_MONTH) && |
- InputWasEdited(CREDIT_CARD_EXP_4_DIGIT_YEAR, |
- field_values[CREDIT_CARD_EXP_4_DIGIT_YEAR]) && |
- InputWasEdited(CREDIT_CARD_EXP_MONTH, |
- field_values[CREDIT_CARD_EXP_MONTH])) { |
- ValidityMessage year_message(base::string16(), true); |
- ValidityMessage month_message(base::string16(), true); |
- if (!autofill::IsValidCreditCardExpirationDate( |
- field_values[CREDIT_CARD_EXP_4_DIGIT_YEAR], |
- field_values[CREDIT_CARD_EXP_MONTH], base::Time::Now())) { |
- // The dialog shows the same error message for the month and year fields. |
- year_message.text = l10n_util::GetStringUTF16( |
- IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_CREDIT_CARD_EXPIRATION_DATE); |
- month_message.text = l10n_util::GetStringUTF16( |
- IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_CREDIT_CARD_EXPIRATION_DATE); |
- } |
- messages.Set(CREDIT_CARD_EXP_4_DIGIT_YEAR, year_message); |
- messages.Set(CREDIT_CARD_EXP_MONTH, month_message); |
- } |
- |
- // If there is a credit card number and a CVC, validate them together. |
- if (field_values.count(CREDIT_CARD_NUMBER) && |
- field_values.count(CREDIT_CARD_VERIFICATION_CODE)) { |
- ValidityMessage ccv_message(base::string16(), true); |
- if (!autofill::IsValidCreditCardSecurityCode( |
- field_values[CREDIT_CARD_VERIFICATION_CODE], |
- field_values[CREDIT_CARD_NUMBER])) { |
- ccv_message.text = l10n_util::GetStringUTF16( |
- IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_CREDIT_CARD_SECURITY_CODE); |
- } |
- messages.Set(CREDIT_CARD_VERIFICATION_CODE, ccv_message); |
- } |
- |
- // Validate the shipping phone number against the country code of the address. |
- if (field_values.count(ADDRESS_HOME_COUNTRY) && |
- field_values.count(PHONE_HOME_WHOLE_NUMBER)) { |
- messages.Set( |
- PHONE_HOME_WHOLE_NUMBER, |
- GetPhoneValidityMessage(field_values[ADDRESS_HOME_COUNTRY], |
- field_values[PHONE_HOME_WHOLE_NUMBER])); |
- } |
- |
- // Validate the billing phone number against the country code of the address. |
- if (field_values.count(ADDRESS_BILLING_COUNTRY) && |
- field_values.count(PHONE_BILLING_WHOLE_NUMBER)) { |
- messages.Set( |
- PHONE_BILLING_WHOLE_NUMBER, |
- GetPhoneValidityMessage(field_values[ADDRESS_BILLING_COUNTRY], |
- field_values[PHONE_BILLING_WHOLE_NUMBER])); |
- } |
- |
- return messages; |
-} |
- |
-void AutofillDialogControllerImpl::UserEditedOrActivatedInput( |
- DialogSection section, |
- ServerFieldType type, |
- gfx::NativeView parent_view, |
- const gfx::Rect& content_bounds, |
- const base::string16& field_contents, |
- bool was_edit) { |
- ScopedViewUpdates updates(view_.get()); |
- |
- if (type == ADDRESS_BILLING_COUNTRY || type == ADDRESS_HOME_COUNTRY) { |
- const FieldValueMap& snapshot = TakeUserInputSnapshot(); |
- |
- // Clobber the inputs because the view's already been updated. |
- RebuildInputsForCountry(section, field_contents, true); |
- RestoreUserInputFromSnapshot(snapshot); |
- UpdateSection(section); |
- } |
- |
- // The rest of this method applies only to textfields while Autofill is |
- // enabled. If a combobox or Autofill is disabled, bail. |
- if (ComboboxModelForAutofillType(type) || !IsAutofillEnabled()) |
- return; |
- |
- // If the field is edited down to empty, don't show a popup. |
- if (was_edit && field_contents.empty()) { |
- HidePopup(); |
- return; |
- } |
- |
- // If the user clicks while the popup is already showing, be sure to hide |
- // it. |
- if (!was_edit && popup_controller_.get()) { |
- HidePopup(); |
- return; |
- } |
- |
- std::vector<autofill::Suggestion> popup_suggestions; |
- popup_suggestion_ids_.clear(); |
- if (IsCreditCardType(type)) { |
- popup_suggestions = GetManager()->GetCreditCardSuggestions( |
- AutofillType(type), field_contents); |
- for (const auto& suggestion : popup_suggestions) |
- popup_suggestion_ids_.push_back(suggestion.backend_id); |
- } else { |
- popup_suggestions = GetManager()->GetProfileSuggestions( |
- AutofillType(type), |
- field_contents, |
- false, |
- RequestedTypesForSection(section)); |
- // Filter out ones we don't want. |
- for (int i = 0; i < static_cast<int>(popup_suggestions.size()); i++) { |
- const autofill::AutofillProfile* profile = |
- GetManager()->GetProfileByGUID(popup_suggestions[i].backend_id); |
- if (!profile || !ShouldSuggestProfile(section, *profile)) { |
- popup_suggestions.erase(popup_suggestions.begin() + i); |
- i--; |
- } |
- } |
- |
- // Save the IDs. |
- for (const auto& suggestion : popup_suggestions) |
- popup_suggestion_ids_.push_back(suggestion.backend_id); |
- |
- // This will append to the popup_suggestions but not the IDs since there |
- // are no backend IDs for the I18N validator suggestions. |
- GetI18nValidatorSuggestions(section, type, &popup_suggestions); |
- } |
- |
- if (popup_suggestions.empty()) { |
- HidePopup(); |
- return; |
- } |
- |
- // |popup_input_type_| must be set before calling |Show()|. |
- popup_input_type_ = type; |
- popup_section_ = section; |
- |
- // Use our own 0-based IDs for the items. |
- // TODO(estade): do we need separators and control rows like 'Clear |
- // Form'? |
- for (size_t i = 0; i < popup_suggestions.size(); ++i) { |
- popup_suggestions[i].frontend_id = i; |
- } |
- |
- popup_controller_ = AutofillPopupControllerImpl::GetOrCreate( |
- popup_controller_, weak_ptr_factory_.GetWeakPtr(), NULL, parent_view, |
- gfx::RectF(content_bounds), |
- base::i18n::IsRTL() ? base::i18n::RIGHT_TO_LEFT |
- : base::i18n::LEFT_TO_RIGHT); |
- popup_controller_->Show(popup_suggestions); |
-} |
- |
-void AutofillDialogControllerImpl::FocusMoved() { |
- HidePopup(); |
-} |
- |
-bool AutofillDialogControllerImpl::ShouldShowErrorBubble() const { |
- return popup_input_type_ == UNKNOWN_TYPE; |
-} |
- |
-void AutofillDialogControllerImpl::ViewClosed() { |
- GetManager()->RemoveObserver(this); |
- |
- delete this; |
-} |
- |
-std::vector<DialogNotification> AutofillDialogControllerImpl:: |
- CurrentNotifications() { |
- std::vector<DialogNotification> notifications; |
- |
- if (!invoked_from_same_origin_) { |
- notifications.push_back(DialogNotification( |
- DialogNotification::SECURITY_WARNING, |
- l10n_util::GetStringFUTF16(IDS_AUTOFILL_DIALOG_SITE_WARNING, |
- base::UTF8ToUTF16(source_url_.host())))); |
- } |
- |
- return notifications; |
-} |
- |
-void AutofillDialogControllerImpl::LinkClicked(const GURL& url) { |
- OpenTabWithUrl(url); |
-} |
- |
-void AutofillDialogControllerImpl::OnCancel() { |
- HidePopup(); |
- if (!data_was_passed_back_) |
- LogOnCancelMetrics(); |
- callback_.Run( |
- AutofillClient::AutocompleteResultErrorCancel, base::string16(), NULL); |
-} |
- |
-void AutofillDialogControllerImpl::OnAccept() { |
- ScopedViewUpdates updates(view_.get()); |
- HidePopup(); |
- FinishSubmit(); |
-} |
- |
-Profile* AutofillDialogControllerImpl::profile() { |
- return profile_; |
-} |
- |
-content::WebContents* AutofillDialogControllerImpl::GetWebContents() { |
- return web_contents(); |
-} |
- |
-//////////////////////////////////////////////////////////////////////////////// |
-// AutofillPopupDelegate implementation. |
- |
-void AutofillDialogControllerImpl::OnPopupShown() { |
- ScopedViewUpdates update(view_.get()); |
- view_->UpdateErrorBubble(); |
- |
- AutofillMetrics::LogDialogPopupEvent(AutofillMetrics::DIALOG_POPUP_SHOWN); |
-} |
- |
-void AutofillDialogControllerImpl::OnPopupHidden() {} |
- |
-void AutofillDialogControllerImpl::DidSelectSuggestion( |
- const base::string16& value, |
- int identifier) { |
- // TODO(estade): implement. |
-} |
- |
-void AutofillDialogControllerImpl::DidAcceptSuggestion( |
- const base::string16& value, |
- int identifier, |
- int position) { |
- DCHECK_NE(UNKNOWN_TYPE, popup_input_type_); |
- // Because |HidePopup()| can be called from |UpdateSection()|, remember the |
- // type of the input for later here. |
- const ServerFieldType popup_input_type = popup_input_type_; |
- |
- ScopedViewUpdates updates(view_.get()); |
- std::unique_ptr<DataModelWrapper> wrapper; |
- |
- if (static_cast<size_t>(identifier) < popup_suggestion_ids_.size()) { |
- const std::string& guid = popup_suggestion_ids_[identifier]; |
- if (IsCreditCardType(popup_input_type)) { |
- wrapper.reset(new AutofillCreditCardWrapper( |
- GetManager()->GetCreditCardByGUID(guid))); |
- } else { |
- wrapper.reset(new AutofillProfileWrapper( |
- GetManager()->GetProfileByGUID(guid))); |
- } |
- } else { |
- wrapper.reset(new I18nAddressDataWrapper( |
- &i18n_validator_suggestions_[ |
- identifier - popup_suggestion_ids_.size()])); |
- } |
- |
- // If the user hasn't switched away from the default country and |wrapper|'s |
- // country differs from the |view_|'s, rebuild inputs and restore user data. |
- const FieldValueMap snapshot = TakeUserInputSnapshot(); |
- bool billing_rebuilt = false, shipping_rebuilt = false; |
- |
- base::string16 billing_country = |
- wrapper->GetInfo(AutofillType(ADDRESS_BILLING_COUNTRY)); |
- if (popup_section_ == ActiveBillingSection() && |
- !snapshot.count(ADDRESS_BILLING_COUNTRY) && |
- !billing_country.empty()) { |
- billing_rebuilt = RebuildInputsForCountry( |
- ActiveBillingSection(), billing_country, false); |
- } |
- |
- base::string16 shipping_country = |
- wrapper->GetInfo(AutofillType(ADDRESS_HOME_COUNTRY)); |
- if (popup_section_ == SECTION_SHIPPING && |
- !snapshot.count(ADDRESS_HOME_COUNTRY) && |
- !shipping_country.empty()) { |
- shipping_rebuilt = RebuildInputsForCountry( |
- SECTION_SHIPPING, shipping_country, false); |
- } |
- |
- if (billing_rebuilt || shipping_rebuilt) { |
- RestoreUserInputFromSnapshot(snapshot); |
- if (billing_rebuilt) |
- UpdateSection(ActiveBillingSection()); |
- if (shipping_rebuilt) |
- UpdateSection(SECTION_SHIPPING); |
- } |
- |
- DCHECK(SectionIsActive(popup_section_)); |
- wrapper->FillInputs(MutableRequestedFieldsForSection(popup_section_)); |
- view_->FillSection(popup_section_, popup_input_type); |
- |
- AutofillMetrics::LogDialogPopupEvent( |
- AutofillMetrics::DIALOG_POPUP_FORM_FILLED); |
- |
- // TODO(estade): not sure why it's necessary to do this explicitly. |
- HidePopup(); |
-} |
- |
-bool AutofillDialogControllerImpl::GetDeletionConfirmationText( |
- const base::string16& value, |
- int identifier, |
- base::string16* title, |
- base::string16* body) { |
- return false; |
-} |
- |
-bool AutofillDialogControllerImpl::RemoveSuggestion(const base::string16& value, |
- int identifier) { |
- // TODO(estade): implement. |
- return false; |
-} |
- |
-void AutofillDialogControllerImpl::ClearPreviewedForm() { |
- // TODO(estade): implement. |
-} |
- |
-//////////////////////////////////////////////////////////////////////////////// |
-// SuggestionsMenuModelDelegate implementation. |
- |
-void AutofillDialogControllerImpl::SuggestionItemSelected( |
- SuggestionsMenuModel* model, |
- size_t index) { |
- ScopedViewUpdates updates(view_.get()); |
- |
- if (model->GetItemKeyAt(index) == kManageItemsKey) { |
- GURL url; |
- DCHECK(IsAutofillEnabled()); |
- GURL settings_url(chrome::kChromeUISettingsURL); |
- url = settings_url.Resolve(chrome::kAutofillSubPage); |
- OpenTabWithUrl(url); |
- return; |
- } |
- |
- model->SetCheckedIndex(index); |
- DialogSection section = SectionForSuggestionsMenuModel(*model); |
- |
- ResetSectionInput(section); |
- ShowEditUiIfBadSuggestion(section); |
- UpdateSection(section); |
- view_->UpdateNotificationArea(); |
- UpdateForErrors(); |
- |
- LogSuggestionItemSelectedMetric(*model); |
-} |
- |
-//////////////////////////////////////////////////////////////////////////////// |
-// PersonalDataManagerObserver implementation. |
- |
-void AutofillDialogControllerImpl::OnPersonalDataChanged() { |
- SuggestionsUpdated(); |
-} |
- |
-//////////////////////////////////////////////////////////////////////////////// |
- |
-bool AutofillDialogControllerImpl::HandleKeyPressEventInInput( |
- const content::NativeWebKeyboardEvent& event) { |
- if (popup_controller_.get()) |
- return popup_controller_->HandleKeyPressEvent(event); |
- |
- return false; |
-} |
- |
-void AutofillDialogControllerImpl::SubmitButtonDelayBegin() { |
- submit_button_delay_timer_.Start( |
- FROM_HERE, |
- base::TimeDelta::FromMilliseconds(kSubmitButtonDelayMs), |
- this, |
- &AutofillDialogControllerImpl::OnSubmitButtonDelayEnd); |
-} |
- |
-void AutofillDialogControllerImpl::SubmitButtonDelayEndForTesting() { |
- DCHECK(submit_button_delay_timer_.IsRunning()); |
- submit_button_delay_timer_.user_task().Run(); |
- submit_button_delay_timer_.Stop(); |
-} |
- |
-AutofillDialogControllerImpl::AutofillDialogControllerImpl( |
- content::WebContents* contents, |
- const FormData& form_structure, |
- const GURL& source_url, |
- const AutofillClient::ResultCallback& callback) |
- : WebContentsObserver(contents), |
- profile_(Profile::FromBrowserContext(contents->GetBrowserContext())), |
- initial_user_state_(AutofillMetrics::DIALOG_USER_STATE_UNKNOWN), |
- form_structure_(form_structure), |
- invoked_from_same_origin_(true), |
- source_url_(source_url), |
- callback_(callback), |
- suggested_cc_(this), |
- suggested_billing_(this), |
- suggested_shipping_(this), |
- cares_about_shipping_(true), |
- popup_input_type_(UNKNOWN_TYPE), |
- popup_section_(SECTION_MIN), |
- data_was_passed_back_(false), |
- was_ui_latency_logged_(false), |
- weak_ptr_factory_(this) { |
- DCHECK(!callback_.is_null()); |
-} |
- |
-AutofillDialogView* AutofillDialogControllerImpl::CreateView() { |
- return AutofillDialogView::Create(this); |
-} |
- |
-PersonalDataManager* AutofillDialogControllerImpl::GetManager() const { |
- return PersonalDataManagerFactory::GetForProfile(profile_); |
-} |
- |
-AddressValidator* AutofillDialogControllerImpl::GetValidator() { |
- return validator_.get(); |
-} |
- |
-void AutofillDialogControllerImpl::OpenTabWithUrl(const GURL& url) { |
- chrome::NavigateParams params( |
- chrome::FindBrowserWithWebContents(web_contents()), |
- url, |
- ui::PAGE_TRANSITION_LINK); |
- params.disposition = NEW_FOREGROUND_TAB; |
- chrome::Navigate(¶ms); |
-} |
- |
-// TODO(estade): remove. |
-DialogSection AutofillDialogControllerImpl::ActiveBillingSection() const { |
- return SECTION_BILLING; |
-} |
- |
-bool AutofillDialogControllerImpl::IsEditingExistingData( |
- DialogSection section) const { |
- return section_editing_state_.count(section) > 0; |
-} |
- |
-bool AutofillDialogControllerImpl::IsManuallyEditingSection( |
- DialogSection section) const { |
- return IsEditingExistingData(section) || |
- SuggestionsMenuModelForSection(section)-> |
- GetItemKeyForCheckedItem() == kAddNewItemKey; |
-} |
- |
-void AutofillDialogControllerImpl::SuggestionsUpdated() { |
- ScopedViewUpdates updates(view_.get()); |
- |
- const FieldValueMap snapshot = TakeUserInputSnapshot(); |
- |
- suggested_cc_.Reset(); |
- suggested_billing_.Reset(); |
- suggested_shipping_.Reset(); |
- HidePopup(); |
- |
- suggested_shipping_.AddKeyedItem( |
- kSameAsBillingKey, |
- l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_USE_BILLING_FOR_SHIPPING)); |
- |
- shipping_country_combobox_model_->SetCountries( |
- *GetManager(), |
- base::Bind(AutofillCountryFilter, acceptable_shipping_countries_)); |
- |
- if (IsAutofillEnabled()) { |
- PersonalDataManager* manager = GetManager(); |
- const std::vector<CreditCard*>& cards = manager->GetCreditCards(); |
- ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |
- for (size_t i = 0; i < cards.size(); ++i) { |
- if (!i18ninput::CardHasCompleteAndVerifiedData(*cards[i])) |
- continue; |
- |
- suggested_cc_.AddKeyedItemWithIcon( |
- cards[i]->guid(), cards[i]->Label(), |
- rb.GetImageNamed(CreditCard::IconResourceId(cards[i]->type()))); |
- suggested_cc_.SetEnabled( |
- cards[i]->guid(), !ShouldDisallowCcType(cards[i]->TypeForDisplay())); |
- } |
- |
- const std::vector<AutofillProfile*>& profiles = manager->GetProfiles(); |
- std::vector<base::string16> labels; |
- AutofillProfile::CreateDifferentiatingLabels( |
- profiles, g_browser_process->GetApplicationLocale(), &labels); |
- DCHECK_EQ(labels.size(), profiles.size()); |
- for (size_t i = 0; i < profiles.size(); ++i) { |
- const AutofillProfile& profile = *profiles[i]; |
- if (!i18ninput::AddressHasCompleteAndVerifiedData( |
- profile, g_browser_process->GetApplicationLocale())) { |
- continue; |
- } |
- |
- suggested_shipping_.AddKeyedItem(profile.guid(), labels[i]); |
- suggested_shipping_.SetEnabled( |
- profile.guid(), |
- CanAcceptCountry( |
- SECTION_SHIPPING, |
- base::UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_COUNTRY)))); |
- if (!profile.GetRawInfo(EMAIL_ADDRESS).empty() && |
- !profile.IsPresentButInvalid(EMAIL_ADDRESS)) { |
- suggested_billing_.AddKeyedItem(profile.guid(), labels[i]); |
- suggested_billing_.SetEnabled( |
- profile.guid(), |
- CanAcceptCountry( |
- SECTION_BILLING, |
- base::UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_COUNTRY)))); |
- } |
- } |
- } |
- |
- suggested_cc_.AddKeyedItem( |
- kAddNewItemKey, |
- l10n_util::GetStringUTF16(IsAutofillEnabled() |
- ? IDS_AUTOFILL_DIALOG_ADD_CREDIT_CARD |
- : IDS_AUTOFILL_DIALOG_ENTER_CREDIT_CARD)); |
- suggested_cc_.AddKeyedItem( |
- kManageItemsKey, |
- l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_MANAGE_CREDIT_CARD)); |
- suggested_billing_.AddKeyedItem( |
- kAddNewItemKey, |
- l10n_util::GetStringUTF16( |
- IsAutofillEnabled() ? IDS_AUTOFILL_DIALOG_ADD_BILLING_ADDRESS |
- : IDS_AUTOFILL_DIALOG_ENTER_BILLING_DETAILS)); |
- suggested_billing_.AddKeyedItem( |
- kManageItemsKey, |
- l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_MANAGE_BILLING_ADDRESS)); |
- |
- suggested_shipping_.AddKeyedItem( |
- kAddNewItemKey, |
- l10n_util::GetStringUTF16( |
- IsAutofillEnabled() |
- ? IDS_AUTOFILL_DIALOG_ADD_SHIPPING_ADDRESS |
- : IDS_AUTOFILL_DIALOG_USE_DIFFERENT_SHIPPING_ADDRESS)); |
- |
- if (IsAutofillEnabled()) { |
- suggested_shipping_.AddKeyedItem( |
- kManageItemsKey, |
- l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_MANAGE_SHIPPING_ADDRESS)); |
- |
- for (size_t i = SECTION_MIN; i <= SECTION_MAX; ++i) { |
- DialogSection section = static_cast<DialogSection>(i); |
- if (!SectionIsActive(section)) |
- continue; |
- |
- // Set the starting choice for the menu. First set to the default in case |
- // the GUID saved in prefs refers to a profile that no longer exists. |
- std::string guid; |
- GetDefaultAutofillChoice(section, &guid); |
- SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section); |
- model->SetCheckedItem(guid); |
- if (GetAutofillChoice(section, &guid)) |
- model->SetCheckedItem(guid); |
- } |
- } |
- |
- if (view_) |
- view_->ModelChanged(); |
- |
- for (size_t i = SECTION_MIN; i <= SECTION_MAX; ++i) { |
- ResetSectionInput(static_cast<DialogSection>(i)); |
- } |
- |
- FieldValueMap::const_iterator billing_it = |
- snapshot.find(ADDRESS_BILLING_COUNTRY); |
- if (billing_it != snapshot.end()) |
- 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, true); |
- |
- RestoreUserInputFromSnapshot(snapshot); |
- |
- for (size_t i = SECTION_MIN; i <= SECTION_MAX; ++i) { |
- DialogSection section = static_cast<DialogSection>(i); |
- if (!SectionIsActive(section)) |
- continue; |
- |
- ShowEditUiIfBadSuggestion(section); |
- UpdateSection(section); |
- } |
- |
- UpdateForErrors(); |
-} |
- |
-void AutofillDialogControllerImpl::FillOutputForSectionWithComparator( |
- DialogSection section, |
- const FormStructure::InputFieldComparator& compare) { |
- if (!SectionIsActive(section)) |
- return; |
- |
- DetailInputs inputs; |
- std::string country_code = CountryCodeForSection(section); |
- BuildInputsForSection(section, country_code, &inputs, |
- MutableAddressLanguageCodeForSection(section)); |
- std::vector<ServerFieldType> types = TypesFromInputs(inputs); |
- |
- std::unique_ptr<DataModelWrapper> wrapper = CreateWrapper(section); |
- if (wrapper) { |
- // Only fill in data that is associated with this section. |
- 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 |
- // billing section as CVC comes from |full_wallet_| in this case. |
- if (section == SECTION_CC) |
- SetOutputForFieldsOfType(CREDIT_CARD_VERIFICATION_CODE, view_->GetCvc()); |
- |
- } else { |
- // The user manually input data. If using Autofill, save the info as new or |
- // edited data. Always fill local data into |form_structure_|. |
- FieldValueMap output; |
- view_->GetUserInput(section, &output); |
- |
- if (section == SECTION_CC) { |
- CreditCard card; |
- FillFormGroupFromOutputs(output, &card); |
- |
- // The card holder name comes from the billing address section. |
- card.SetRawInfo(CREDIT_CARD_NAME_FULL, |
- GetValueFromSection(SECTION_BILLING, NAME_BILLING_FULL)); |
- |
- AutofillCreditCardWrapper card_wrapper(&card); |
- card_wrapper.FillFormStructure(types, compare, &form_structure_); |
- |
- // Again, CVC needs special-casing. Fill it in directly from |output|. |
- SetOutputForFieldsOfType( |
- CREDIT_CARD_VERIFICATION_CODE, |
- output[CREDIT_CARD_VERIFICATION_CODE]); |
- } else { |
- AutofillProfile profile; |
- FillFormGroupFromOutputs(output, &profile); |
- profile.set_language_code(AddressLanguageCodeForSection(section)); |
- |
- 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(types, compare, &form_structure_); |
- } |
- } |
-} |
- |
-void AutofillDialogControllerImpl::FillOutputForSection(DialogSection section) { |
- FillOutputForSectionWithComparator( |
- section, base::Bind(ServerTypeMatchesField, section)); |
-} |
- |
-bool AutofillDialogControllerImpl::FormStructureCaresAboutSection( |
- DialogSection section) const { |
- // For now, only SECTION_SHIPPING may be omitted due to a site not asking for |
- // any of the fields. |
- if (section == SECTION_SHIPPING) |
- return cares_about_shipping_; |
- |
- return true; |
-} |
- |
-void AutofillDialogControllerImpl::SetOutputForFieldsOfType( |
- ServerFieldType type, |
- const base::string16& output) { |
- for (size_t i = 0; i < form_structure_.field_count(); ++i) { |
- AutofillField* field = form_structure_.field(i); |
- if (field->Type().GetStorableType() == type) |
- field->value = output; |
- } |
-} |
- |
-base::string16 AutofillDialogControllerImpl::GetValueFromSection( |
- DialogSection section, |
- ServerFieldType type) { |
- DCHECK(SectionIsActive(section)); |
- |
- std::unique_ptr<DataModelWrapper> wrapper = CreateWrapper(section); |
- if (wrapper) |
- return wrapper->GetInfo(AutofillType(type)); |
- |
- FieldValueMap output; |
- view_->GetUserInput(section, &output); |
- return output[type]; |
-} |
- |
-bool AutofillDialogControllerImpl::CanAcceptCountry( |
- DialogSection section, |
- const std::string& country_code) { |
- DCHECK_EQ(2U, country_code.size()); |
- CountryComboboxModel* model = CountryComboboxModelForSection(section); |
- const std::vector<AutofillCountry*>& countries = model->countries(); |
- for (size_t i = 0; i < countries.size(); ++i) { |
- if (countries[i] && countries[i]->country_code() == country_code) |
- return true; |
- } |
- |
- return false; |
-} |
- |
-bool AutofillDialogControllerImpl::ShouldSuggestProfile( |
- DialogSection section, |
- const AutofillProfile& profile) { |
- std::string country_code = |
- base::UTF16ToASCII(profile.GetRawInfo(ADDRESS_HOME_COUNTRY)); |
- return country_code.empty() || CanAcceptCountry(section, country_code); |
-} |
- |
-SuggestionsMenuModel* AutofillDialogControllerImpl:: |
- SuggestionsMenuModelForSection(DialogSection section) { |
- switch (section) { |
- case SECTION_CC: |
- return &suggested_cc_; |
- case SECTION_BILLING: |
- return &suggested_billing_; |
- case SECTION_SHIPPING: |
- return &suggested_shipping_; |
- } |
- |
- NOTREACHED(); |
- return NULL; |
-} |
- |
-const SuggestionsMenuModel* AutofillDialogControllerImpl:: |
- SuggestionsMenuModelForSection(DialogSection section) const { |
- return const_cast<AutofillDialogControllerImpl*>(this)-> |
- SuggestionsMenuModelForSection(section); |
-} |
- |
-DialogSection AutofillDialogControllerImpl::SectionForSuggestionsMenuModel( |
- const SuggestionsMenuModel& model) { |
- if (&model == &suggested_cc_) |
- return SECTION_CC; |
- |
- if (&model == &suggested_billing_) |
- return SECTION_BILLING; |
- |
- DCHECK_EQ(&model, &suggested_shipping_); |
- return SECTION_SHIPPING; |
-} |
- |
-CountryComboboxModel* AutofillDialogControllerImpl:: |
- CountryComboboxModelForSection(DialogSection section) { |
- if (section == SECTION_BILLING) |
- return billing_country_combobox_model_.get(); |
- |
- if (section == SECTION_SHIPPING) |
- return shipping_country_combobox_model_.get(); |
- |
- return NULL; |
-} |
- |
-void AutofillDialogControllerImpl::GetI18nValidatorSuggestions( |
- DialogSection section, |
- ServerFieldType type, |
- std::vector<autofill::Suggestion>* popup_suggestions) { |
- AddressField focused_field; |
- if (!i18n::FieldForType(type, &focused_field)) |
- return; |
- |
- FieldValueMap inputs; |
- view_->GetUserInput(section, &inputs); |
- |
- AutofillProfile profile; |
- FillFormGroupFromOutputs(inputs, &profile); |
- |
- std::unique_ptr<AddressData> user_input = |
- i18n::CreateAddressDataFromAutofillProfile( |
- profile, g_browser_process->GetApplicationLocale()); |
- user_input->language_code = AddressLanguageCodeForSection(section); |
- |
- static const size_t kSuggestionsLimit = 10; |
- AddressValidator::Status status = GetValidator()->GetSuggestions( |
- *user_input, focused_field, kSuggestionsLimit, |
- &i18n_validator_suggestions_); |
- |
- if (status != AddressValidator::SUCCESS) |
- return; |
- |
- for (size_t i = 0; i < i18n_validator_suggestions_.size(); ++i) { |
- popup_suggestions->push_back(autofill::Suggestion( |
- base::UTF8ToUTF16( |
- i18n_validator_suggestions_[i].GetFieldValue(focused_field)))); |
- |
- // Disambiguate the suggestion by showing the smallest administrative |
- // region of the suggested address: |
- // ADMIN_AREA > LOCALITY > DEPENDENT_LOCALITY |
- for (int field = DEPENDENT_LOCALITY; field >= ADMIN_AREA; --field) { |
- const std::string& field_value = |
- i18n_validator_suggestions_[i].GetFieldValue( |
- static_cast<AddressField>(field)); |
- if (focused_field != field && !field_value.empty()) { |
- popup_suggestions->back().label = base::UTF8ToUTF16(field_value); |
- break; |
- } |
- } |
- } |
-} |
- |
-DetailInputs* AutofillDialogControllerImpl::MutableRequestedFieldsForSection( |
- DialogSection section) { |
- return const_cast<DetailInputs*>(&RequestedFieldsForSection(section)); |
-} |
- |
-std::string* AutofillDialogControllerImpl::MutableAddressLanguageCodeForSection( |
- DialogSection section) { |
- switch (section) { |
- case SECTION_BILLING: |
- return &billing_address_language_code_; |
- case SECTION_SHIPPING: |
- return &shipping_address_language_code_; |
- case SECTION_CC: |
- return NULL; |
- } |
- NOTREACHED(); |
- return NULL; |
-} |
- |
-std::string AutofillDialogControllerImpl::AddressLanguageCodeForSection( |
- DialogSection section) { |
- std::string* language_code = MutableAddressLanguageCodeForSection(section); |
- return language_code != NULL ? *language_code : std::string(); |
-} |
- |
-std::vector<ServerFieldType> AutofillDialogControllerImpl:: |
- RequestedTypesForSection(DialogSection section) const { |
- return TypesFromInputs(RequestedFieldsForSection(section)); |
-} |
- |
-std::string AutofillDialogControllerImpl::CountryCodeForSection( |
- DialogSection section) { |
- base::string16 country; |
- |
- std::unique_ptr<DataModelWrapper> wrapper = CreateWrapper(section); |
- if (wrapper) { |
- country = wrapper->GetInfo(AutofillType(CountryTypeForSection(section))); |
- } else { |
- FieldValueMap outputs; |
- view_->GetUserInput(section, &outputs); |
- country = outputs[CountryTypeForSection(section)]; |
- } |
- |
- return CountryNames::GetInstance()->GetCountryCode(country); |
-} |
- |
-bool AutofillDialogControllerImpl::RebuildInputsForCountry( |
- DialogSection section, |
- const base::string16& country_name, |
- bool should_clobber) { |
- CountryComboboxModel* model = CountryComboboxModelForSection(section); |
- if (!model) |
- return false; |
- |
- std::string country_code = |
- CountryNames::GetInstance()->GetCountryCode(country_name); |
- DCHECK(CanAcceptCountry(section, country_code)); |
- |
- if (view_ && !should_clobber) { |
- FieldValueMap outputs; |
- view_->GetUserInput(section, &outputs); |
- |
- // If |country_name| is the same as the view, no-op and let the caller know. |
- if (outputs[CountryTypeForSection(section)] == country_name) |
- return false; |
- } |
- |
- DetailInputs* inputs = MutableRequestedFieldsForSection(section); |
- inputs->clear(); |
- BuildInputsForSection(section, country_code, inputs, |
- MutableAddressLanguageCodeForSection(section)); |
- |
- if (!country_code.empty()) { |
- GetValidator()->LoadRules( |
- CountryNames::GetInstance()->GetCountryCode(country_name)); |
- } |
- |
- return true; |
-} |
- |
-void AutofillDialogControllerImpl::HidePopup() { |
- if (popup_controller_) |
- popup_controller_->Hide(); |
- popup_input_type_ = UNKNOWN_TYPE; |
-} |
- |
-void AutofillDialogControllerImpl::SetEditingExistingData( |
- DialogSection section, bool editing) { |
- if (editing) |
- section_editing_state_.insert(section); |
- else |
- section_editing_state_.erase(section); |
-} |
- |
-bool AutofillDialogControllerImpl::IsASuggestionItemKey( |
- const std::string& key) const { |
- return !key.empty() && |
- key != kAddNewItemKey && |
- key != kManageItemsKey && |
- key != kSameAsBillingKey; |
-} |
- |
-bool AutofillDialogControllerImpl::IsAutofillEnabled() const { |
- return profile_->GetPrefs()->GetBoolean(prefs::kAutofillEnabled); |
-} |
- |
-bool AutofillDialogControllerImpl::IsManuallyEditingAnySection() const { |
- for (size_t section = SECTION_MIN; section <= SECTION_MAX; ++section) { |
- if (IsManuallyEditingSection(static_cast<DialogSection>(section))) |
- return true; |
- } |
- return false; |
-} |
- |
-base::string16 AutofillDialogControllerImpl::CreditCardNumberValidityMessage( |
- const base::string16& number) const { |
- if (!number.empty() && !autofill::IsValidCreditCardNumber(number)) { |
- return l10n_util::GetStringUTF16( |
- IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_CREDIT_CARD_NUMBER); |
- } |
- |
- if (ShouldDisallowCcType( |
- CreditCard::TypeForDisplay(CreditCard::GetCreditCardType(number)))) { |
- int ids = IDS_AUTOFILL_DIALOG_VALIDATION_UNACCEPTED_GENERIC_CARD; |
- const char* const type = CreditCard::GetCreditCardType(number); |
- if (type == kAmericanExpressCard) |
- ids = IDS_AUTOFILL_DIALOG_VALIDATION_UNACCEPTED_AMEX; |
- else if (type == kDiscoverCard) |
- ids = IDS_AUTOFILL_DIALOG_VALIDATION_UNACCEPTED_DISCOVER; |
- else if (type == kMasterCard) |
- ids = IDS_AUTOFILL_DIALOG_VALIDATION_UNACCEPTED_MASTERCARD; |
- else if (type == kVisaCard) |
- ids = IDS_AUTOFILL_DIALOG_VALIDATION_UNACCEPTED_VISA; |
- |
- return l10n_util::GetStringUTF16(ids); |
- } |
- |
- // Card number is good and supported. |
- return base::string16(); |
-} |
- |
-bool AutofillDialogControllerImpl::AllSectionsAreValid() { |
- for (size_t section = SECTION_MIN; section <= SECTION_MAX; ++section) { |
- if (!SectionIsValid(static_cast<DialogSection>(section))) |
- return false; |
- } |
- return true; |
-} |
- |
-bool AutofillDialogControllerImpl::SectionIsValid( |
- DialogSection section) { |
- if (!IsManuallyEditingSection(section)) |
- return true; |
- |
- FieldValueMap detail_outputs; |
- view_->GetUserInput(section, &detail_outputs); |
- return !InputsAreValid(section, detail_outputs).HasSureErrors(); |
-} |
- |
-bool AutofillDialogControllerImpl::RulesAreLoaded(DialogSection section) { |
- AddressData address_data; |
- address_data.region_code = CountryCodeForSection(section); |
- AddressValidator::Status status = GetValidator()->ValidateAddress( |
- address_data, NULL, NULL); |
- return status == AddressValidator::SUCCESS; |
-} |
- |
-bool AutofillDialogControllerImpl::ShouldDisallowCcType( |
- const base::string16& type) const { |
- if (acceptable_cc_types_.empty()) |
- return false; |
- |
- if (acceptable_cc_types_.find(base::i18n::ToUpper(type)) == |
- acceptable_cc_types_.end()) { |
- return true; |
- } |
- |
- return false; |
-} |
- |
-bool AutofillDialogControllerImpl::HasInvalidAddress( |
- const AutofillProfile& profile) { |
- std::unique_ptr<AddressData> address_data = |
- i18n::CreateAddressDataFromAutofillProfile( |
- profile, g_browser_process->GetApplicationLocale()); |
- |
- FieldProblemMap problems; |
- GetValidator()->ValidateAddress(*address_data, NULL, &problems); |
- return !problems.empty(); |
-} |
- |
-bool AutofillDialogControllerImpl::ShouldUseBillingForShipping() { |
- return SectionIsActive(SECTION_SHIPPING) && |
- suggested_shipping_.GetItemKeyForCheckedItem() == kSameAsBillingKey; |
-} |
- |
-bool AutofillDialogControllerImpl::ShouldSaveDetailsLocally() { |
- // It's possible that the user checked [X] Save details locally before |
- // switching payment methods, so only ask the view whether to save details |
- // locally if that checkbox is showing (currently if not paying with wallet). |
- // Also, if the user isn't editing any sections, there's no data to save |
- // locally. |
- return ShouldOfferToSaveInChrome() && view_->SaveDetailsLocally(); |
-} |
- |
-void AutofillDialogControllerImpl::FinishSubmit() { |
- FillOutputForSection(SECTION_CC); |
- FillOutputForSection(SECTION_BILLING); |
- |
- if (ShouldUseBillingForShipping()) { |
- FillOutputForSectionWithComparator( |
- SECTION_BILLING, |
- base::Bind(ServerTypeMatchesShippingField)); |
- FillOutputForSectionWithComparator( |
- SECTION_CC, |
- base::Bind(ServerTypeMatchesShippingField)); |
- } else { |
- FillOutputForSection(SECTION_SHIPPING); |
- } |
- |
- if (ShouldOfferToSaveInChrome()) { |
- for (size_t i = SECTION_MIN; i <= SECTION_MAX; ++i) { |
- DialogSection section = static_cast<DialogSection>(i); |
- if (!SectionIsActive(section) || section == SECTION_CC) |
- continue; |
- |
- SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section); |
- std::string item_key = model->GetItemKeyForCheckedItem(); |
- if (IsASuggestionItemKey(item_key) || item_key == kSameAsBillingKey) { |
- PersistAutofillChoice(section, item_key); |
- } else if (item_key == kAddNewItemKey && ShouldSaveDetailsLocally()) { |
- DCHECK(newly_saved_data_model_guids_.count(section)); |
- PersistAutofillChoice(section, newly_saved_data_model_guids_[section]); |
- } |
- } |
- |
- profile_->GetPrefs()->SetBoolean(::prefs::kAutofillDialogSaveData, |
- view_->SaveDetailsLocally()); |
- } |
- |
- LogOnFinishSubmitMetrics(); |
- |
- // Callback should be called as late as possible. |
- callback_.Run(AutofillClient::AutocompleteResultSuccess, |
- base::string16(), |
- &form_structure_); |
- data_was_passed_back_ = true; |
- |
- // This might delete us. |
- Hide(); |
-} |
- |
-void AutofillDialogControllerImpl::OnAddressValidationRulesLoaded( |
- const std::string& country_code, |
- bool success) { |
- // Rules may load instantly (during initialization, before the view is |
- // even ready). We'll validate when the view is created. |
- if (!view_) |
- return; |
- |
- ScopedViewUpdates updates(view_.get()); |
- |
- // 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) || |
- CountryCodeForSection(section) != country_code) { |
- continue; |
- } |
- |
- if (IsManuallyEditingSection(section) && needs_validation_.count(section)) { |
- view_->ValidateSection(section); |
- needs_validation_.erase(section); |
- } else if (success) { |
- ShowEditUiIfBadSuggestion(section); |
- UpdateSection(section); |
- } |
- } |
-} |
- |
-void AutofillDialogControllerImpl::PersistAutofillChoice( |
- DialogSection section, |
- const std::string& guid) { |
- DCHECK(ShouldOfferToSaveInChrome()); |
- std::unique_ptr<base::DictionaryValue> value(new base::DictionaryValue()); |
- value->SetString(kGuidPrefKey, guid); |
- |
- DictionaryPrefUpdate updater(profile()->GetPrefs(), |
- ::prefs::kAutofillDialogAutofillDefault); |
- base::DictionaryValue* autofill_choice = updater.Get(); |
- autofill_choice->Set(SectionToPrefString(section), value.release()); |
-} |
- |
-void AutofillDialogControllerImpl::GetDefaultAutofillChoice( |
- DialogSection section, |
- std::string* guid) { |
- DCHECK(IsAutofillEnabled()); |
- // The default choice is the first thing in the menu that is a suggestion |
- // item. |
- SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section); |
- for (int i = 0; i < model->GetItemCount(); ++i) { |
- // Try the first suggestion item that is enabled. |
- if (IsASuggestionItemKey(model->GetItemKeyAt(i)) && model->IsEnabledAt(i)) { |
- *guid = model->GetItemKeyAt(i); |
- return; |
- // Fall back to the first non-suggestion key. |
- } else if (!IsASuggestionItemKey(model->GetItemKeyAt(i)) && guid->empty()) { |
- *guid = model->GetItemKeyAt(i); |
- } |
- } |
-} |
- |
-bool AutofillDialogControllerImpl::GetAutofillChoice(DialogSection section, |
- std::string* guid) { |
- DCHECK(IsAutofillEnabled()); |
- const base::DictionaryValue* choices = profile()->GetPrefs()->GetDictionary( |
- ::prefs::kAutofillDialogAutofillDefault); |
- if (!choices) |
- return false; |
- |
- const base::DictionaryValue* choice = NULL; |
- if (!choices->GetDictionary(SectionToPrefString(section), &choice)) |
- return false; |
- |
- choice->GetString(kGuidPrefKey, guid); |
- return true; |
-} |
- |
-void AutofillDialogControllerImpl::LogOnFinishSubmitMetrics() { |
- AutofillMetrics::LogDialogUiDuration( |
- base::Time::Now() - dialog_shown_timestamp_, |
- AutofillMetrics::DIALOG_ACCEPTED); |
- |
- AutofillMetrics::LogDialogUiEvent(AutofillMetrics::DIALOG_UI_ACCEPTED); |
- |
- AutofillMetrics::DialogDismissalState dismissal_state; |
- if (!IsManuallyEditingAnySection()) { |
- dismissal_state = AutofillMetrics::DIALOG_ACCEPTED_EXISTING_AUTOFILL_DATA; |
- } else if (ShouldSaveDetailsLocally()) { |
- dismissal_state = AutofillMetrics::DIALOG_ACCEPTED_SAVE_TO_AUTOFILL; |
- } else { |
- dismissal_state = AutofillMetrics::DIALOG_ACCEPTED_NO_SAVE; |
- } |
- |
- AutofillMetrics::LogDialogDismissalState(dismissal_state); |
-} |
- |
-void AutofillDialogControllerImpl::LogOnCancelMetrics() { |
- AutofillMetrics::LogDialogUiEvent(AutofillMetrics::DIALOG_UI_CANCELED); |
- |
- AutofillMetrics::DialogDismissalState dismissal_state; |
- if (!IsManuallyEditingAnySection()) |
- dismissal_state = AutofillMetrics::DIALOG_CANCELED_NO_EDITS; |
- else if (AllSectionsAreValid()) |
- dismissal_state = AutofillMetrics::DIALOG_CANCELED_NO_INVALID_FIELDS; |
- else |
- dismissal_state = AutofillMetrics::DIALOG_CANCELED_WITH_INVALID_FIELDS; |
- |
- AutofillMetrics::LogDialogDismissalState(dismissal_state); |
- |
- AutofillMetrics::LogDialogUiDuration( |
- base::Time::Now() - dialog_shown_timestamp_, |
- AutofillMetrics::DIALOG_CANCELED); |
-} |
- |
-void AutofillDialogControllerImpl::LogSuggestionItemSelectedMetric( |
- const SuggestionsMenuModel& model) { |
- DialogSection section = SectionForSuggestionsMenuModel(model); |
- |
- AutofillMetrics::DialogUiEvent dialog_ui_event; |
- if (model.GetItemKeyForCheckedItem() == kAddNewItemKey) { |
- // Selected to add a new item. |
- dialog_ui_event = common::DialogSectionToUiItemAddedEvent(section); |
- } else if (IsASuggestionItemKey(model.GetItemKeyForCheckedItem())) { |
- // Selected an existing item. |
- dialog_ui_event = common::DialogSectionToUiSelectionChangedEvent(section); |
- } else { |
- // TODO(estade): add logging for "Manage items" or "Use billing for |
- // shipping"? |
- return; |
- } |
- |
- AutofillMetrics::LogDialogUiEvent(dialog_ui_event); |
-} |
- |
-void AutofillDialogControllerImpl::LogDialogLatencyToShow() { |
- if (was_ui_latency_logged_) |
- return; |
- |
- AutofillMetrics::LogDialogLatencyToShow(base::Time::Now() - |
- dialog_shown_timestamp_); |
- was_ui_latency_logged_ = true; |
-} |
- |
-AutofillMetrics::DialogInitialUserStateMetric |
- AutofillDialogControllerImpl::GetInitialUserState() const { |
- // Consider a user to be an Autofill user if the user has any credit cards |
- // or addresses saved. Check that the item count is greater than 2 because |
- // an "empty" menu still has the "add new" menu item and "manage" menu item. |
- const bool has_autofill_profiles = |
- suggested_cc_.GetItemCount() > 2 || |
- suggested_billing_.GetItemCount() > 2; |
- |
- return has_autofill_profiles |
- ? AutofillMetrics::DIALOG_USER_NOT_SIGNED_IN_HAS_AUTOFILL |
- : AutofillMetrics::DIALOG_USER_NOT_SIGNED_IN_NO_AUTOFILL; |
-} |
- |
-void AutofillDialogControllerImpl::OnSubmitButtonDelayEnd() { |
- if (!view_) |
- return; |
- ScopedViewUpdates updates(view_.get()); |
- view_->UpdateButtonStrip(); |
-} |
- |
-} // namespace autofill |