1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/ui/autofill/autofill_dialog_controller_impl.h"
11 #include "base/bind.h"
12 #include "base/bind_helpers.h"
13 #include "base/i18n/case_conversion.h"
14 #include "base/i18n/rtl.h"
15 #include "base/location.h"
16 #include "base/logging.h"
17 #include "base/prefs/pref_registry_simple.h"
18 #include "base/prefs/pref_service.h"
19 #include "base/prefs/scoped_user_pref_update.h"
20 #include "base/rand_util.h"
21 #include "base/single_thread_task_runner.h"
22 #include "base/strings/string_number_conversions.h"
23 #include "base/strings/string_split.h"
24 #include "base/strings/stringprintf.h"
25 #include "base/strings/utf_string_conversions.h"
26 #include "base/thread_task_runner_handle.h"
27 #include "base/time/time.h"
28 #include "chrome/browser/autofill/personal_data_manager_factory.h"
29 #include "chrome/browser/autofill/risk_util.h"
30 #include "chrome/browser/autofill/validation_rules_storage_factory.h"
31 #include "chrome/browser/browser_process.h"
32 #include "chrome/browser/profiles/profile.h"
33 #include "chrome/browser/ui/autofill/autofill_dialog_common.h"
34 #include "chrome/browser/ui/autofill/autofill_dialog_i18n_input.h"
35 #include "chrome/browser/ui/autofill/autofill_dialog_view.h"
36 #include "chrome/browser/ui/autofill/data_model_wrapper.h"
37 #include "chrome/browser/ui/autofill/new_credit_card_bubble_controller.h"
38 #include "chrome/browser/ui/browser.h"
39 #include "chrome/browser/ui/browser_finder.h"
40 #include "chrome/browser/ui/browser_navigator.h"
41 #include "chrome/browser/ui/browser_window.h"
42 #include "chrome/common/pref_names.h"
43 #include "chrome/common/url_constants.h"
44 #include "chrome/grit/chromium_strings.h"
45 #include "chrome/grit/generated_resources.h"
46 #include "components/autofill/core/browser/address_i18n.h"
47 #include "components/autofill/core/browser/autofill_country.h"
48 #include "components/autofill/core/browser/autofill_data_model.h"
49 #include "components/autofill/core/browser/autofill_manager.h"
50 #include "components/autofill/core/browser/autofill_type.h"
51 #include "components/autofill/core/browser/detail_input.h"
52 #include "components/autofill/core/browser/field_types.h"
53 #include "components/autofill/core/browser/personal_data_manager.h"
54 #include "components/autofill/core/browser/phone_number_i18n.h"
55 #include "components/autofill/core/browser/server_field_types_util.h"
56 #include "components/autofill/core/browser/validation.h"
57 #include "components/autofill/core/common/autofill_pref_names.h"
58 #include "components/autofill/core/common/form_data.h"
59 #include "components/pref_registry/pref_registry_syncable.h"
60 #include "content/public/browser/browser_thread.h"
61 #include "content/public/browser/geolocation_provider.h"
62 #include "content/public/browser/navigation_controller.h"
63 #include "content/public/browser/navigation_details.h"
64 #include "content/public/browser/navigation_entry.h"
65 #include "content/public/browser/notification_service.h"
66 #include "content/public/browser/notification_types.h"
67 #include "content/public/browser/render_view_host.h"
68 #include "content/public/browser/web_contents.h"
69 #include "content/public/common/url_constants.h"
70 #include "grit/components_scaled_resources.h"
71 #include "grit/components_strings.h"
72 #include "grit/platform_locale_settings.h"
73 #include "grit/theme_resources.h"
74 #include "net/cert/cert_status_flags.h"
75 #include "third_party/libaddressinput/chromium/chrome_metadata_source.h"
76 #include "third_party/libaddressinput/chromium/chrome_storage_impl.h"
77 #include "third_party/libaddressinput/messages.h"
78 #include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_data.h"
79 #include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_field.h"
80 #include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_problem.h"
81 #include "third_party/libaddressinput/src/cpp/include/libaddressinput/localization.h"
82 #include "ui/base/l10n/l10n_util.h"
83 #include "ui/base/models/combobox_model.h"
84 #include "ui/base/resource/resource_bundle.h"
85 #include "ui/gfx/canvas.h"
86 #include "ui/gfx/geometry/vector2d.h"
87 #include "ui/gfx/image/image_skia_operations.h"
88 #include "ui/gfx/skia_util.h"
90 using ::i18n::addressinput::AddressData
;
91 using ::i18n::addressinput::AddressField
;
92 using ::i18n::addressinput::AddressProblem
;
93 using ::i18n::addressinput::ADMIN_AREA
;
94 using ::i18n::addressinput::DEPENDENT_LOCALITY
;
95 using ::i18n::addressinput::FieldProblemMap
;
96 using ::i18n::addressinput::Localization
;
97 using ::i18n::addressinput::MISSING_REQUIRED_FIELD
;
103 const char kAddNewItemKey
[] = "add-new-item";
104 const char kManageItemsKey
[] = "manage-items";
105 const char kSameAsBillingKey
[] = "same-as-billing";
107 // Keys for the kAutofillDialogAutofillDefault pref dictionary (do not change
109 const char kGuidPrefKey
[] = "guid";
111 // This string is stored along with saved addresses and credit cards in the
112 // WebDB, and hence should not be modified, so that it remains consistent over
114 const char kAutofillDialogOrigin
[] = "Chrome Autofill dialog";
116 // The number of milliseconds to delay enabling the submit button after showing
117 // the dialog. This delay prevents users from accidentally clicking the submit
118 // button on startup.
119 const int kSubmitButtonDelayMs
= 1000;
121 // A helper class to make sure an AutofillDialogView knows when a series of
122 // updates is incoming.
123 class ScopedViewUpdates
{
125 explicit ScopedViewUpdates(AutofillDialogView
* view
) : view_(view
) {
127 view_
->UpdatesStarted();
130 ~ScopedViewUpdates() {
132 view_
->UpdatesFinished();
136 AutofillDialogView
* view_
;
138 DISALLOW_COPY_AND_ASSIGN(ScopedViewUpdates
);
141 base::string16
NullGetInfo(const AutofillType
& type
) {
142 return base::string16();
145 // Extract |type| from |inputs| using |section| to determine whether the info
146 // should be billing or shipping specific (for sections with address info).
147 base::string16
GetInfoFromInputs(const FieldValueMap
& inputs
,
148 DialogSection section
,
149 const AutofillType
& type
) {
150 ServerFieldType field_type
= type
.GetStorableType();
151 if (section
!= SECTION_SHIPPING
)
152 field_type
= AutofillType::GetEquivalentBillingFieldType(field_type
);
155 FieldValueMap::const_iterator it
= inputs
.find(field_type
);
156 if (it
!= inputs
.end())
159 if (!info
.empty() && type
.html_type() == HTML_TYPE_COUNTRY_CODE
) {
160 info
= base::ASCIIToUTF16(AutofillCountry::GetCountryCode(
161 info
, g_browser_process
->GetApplicationLocale()));
167 // Returns true if |input| should be used to fill a site-requested |field| which
168 // is notated with a "shipping" tag, for use when the user has decided to use
169 // the billing address as the shipping address.
170 bool ServerTypeMatchesShippingField(ServerFieldType type
,
171 const AutofillField
& field
) {
172 // Equivalent billing field type is used to support UseBillingAsShipping
174 return ServerTypeEncompassesFieldType(
175 type
, AutofillType(AutofillType::GetEquivalentBillingFieldType(
176 field
.Type().GetStorableType())));
179 // Initializes |form_group| from user-entered data.
180 void FillFormGroupFromOutputs(const FieldValueMap
& detail_outputs
,
181 FormGroup
* form_group
) {
182 for (FieldValueMap::const_iterator iter
= detail_outputs
.begin();
183 iter
!= detail_outputs
.end(); ++iter
) {
184 ServerFieldType type
= iter
->first
;
185 if (!iter
->second
.empty()) {
186 form_group
->SetInfo(AutofillType(type
),
188 g_browser_process
->GetApplicationLocale());
193 // Returns a string descriptor for a DialogSection, for use with prefs (do not
194 // change these values).
195 std::string
SectionToPrefString(DialogSection section
) {
200 case SECTION_BILLING
:
203 case SECTION_SHIPPING
:
208 return std::string();
211 gfx::Image
CreditCardIconForType(const std::string
& credit_card_type
) {
212 const int input_card_idr
= CreditCard::IconResourceId(credit_card_type
);
213 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
214 if (input_card_idr
== IDR_AUTOFILL_CC_GENERIC
) {
215 // When the credit card type is unknown, no image should be shown. However,
216 // to simplify the view code on Mac, save space for the credit card image by
217 // returning a transparent image of the appropriate size. All credit card
218 // icons are the same size.
219 return gfx::Image(gfx::ImageSkiaOperations::CreateTransparentImage(
220 rb
.GetImageNamed(IDR_AUTOFILL_CC_GENERIC
).AsImageSkia(), 0));
222 return rb
.GetImageNamed(input_card_idr
);
225 gfx::Image
CvcIconForCreditCardType(const base::string16
& credit_card_type
) {
226 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
227 if (credit_card_type
== l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_AMEX
))
228 return rb
.GetImageNamed(IDR_CREDIT_CARD_CVC_HINT_AMEX
);
230 return rb
.GetImageNamed(IDR_CREDIT_CARD_CVC_HINT
);
233 ServerFieldType
CountryTypeForSection(DialogSection section
) {
234 return section
== SECTION_SHIPPING
? ADDRESS_HOME_COUNTRY
:
235 ADDRESS_BILLING_COUNTRY
;
238 ValidityMessage
GetPhoneValidityMessage(const base::string16
& country_name
,
239 const base::string16
& number
) {
240 std::string region
= AutofillCountry::GetCountryCode(
242 g_browser_process
->GetApplicationLocale());
243 i18n::PhoneObject
phone_object(number
, region
);
244 ValidityMessage
phone_message(base::string16(), true);
246 // Check if the phone number is invalid. Allow valid international
247 // numbers that don't match the address's country only if they have an
248 // international calling code.
249 if (!phone_object
.IsValidNumber() ||
250 (phone_object
.country_code().empty() &&
251 phone_object
.region() != region
)) {
252 phone_message
.text
= l10n_util::GetStringUTF16(
253 IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_PHONE_NUMBER
);
256 return phone_message
;
259 // Constructs |inputs| from template data for a given |dialog_section|.
260 // |country_country| specifies the country code that the inputs should be built
261 // for. Sets the |language_code| to be used for address formatting, if
262 // internationalized address input is enabled. The |language_code| parameter can
264 void BuildInputsForSection(DialogSection dialog_section
,
265 const std::string
& country_code
,
266 DetailInputs
* inputs
,
267 std::string
* language_code
) {
268 using l10n_util::GetStringUTF16
;
270 const DetailInput kCCInputs
[] = {
273 GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_CARD_NUMBER
) },
274 { DetailInput::SHORT
,
275 CREDIT_CARD_EXP_MONTH
,
276 GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_EXPIRY_MONTH
) },
277 { DetailInput::SHORT
,
278 CREDIT_CARD_EXP_4_DIGIT_YEAR
,
279 GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_EXPIRY_YEAR
) },
280 { DetailInput::SHORT_EOL
,
281 CREDIT_CARD_VERIFICATION_CODE
,
282 GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_CVC
),
286 const DetailInput kBillingPhoneInputs
[] = {
288 PHONE_BILLING_WHOLE_NUMBER
,
289 GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_PHONE_NUMBER
) },
292 const DetailInput kEmailInputs
[] = {
295 GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_EMAIL
) },
298 const DetailInput kShippingPhoneInputs
[] = {
300 PHONE_HOME_WHOLE_NUMBER
,
301 GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_PHONE_NUMBER
) },
304 switch (dialog_section
) {
306 BuildInputs(kCCInputs
, arraysize(kCCInputs
), inputs
);
310 case SECTION_BILLING
: {
311 i18ninput::BuildAddressInputs(common::ADDRESS_TYPE_BILLING
,
312 country_code
, inputs
, language_code
);
313 BuildInputs(kBillingPhoneInputs
, arraysize(kBillingPhoneInputs
), inputs
);
314 BuildInputs(kEmailInputs
, arraysize(kEmailInputs
), inputs
);
318 case SECTION_SHIPPING
: {
319 i18ninput::BuildAddressInputs(common::ADDRESS_TYPE_SHIPPING
,
320 country_code
, inputs
, language_code
);
321 BuildInputs(kShippingPhoneInputs
, arraysize(kShippingPhoneInputs
),
330 AutofillDialogViewDelegate::~AutofillDialogViewDelegate() {}
332 AutofillDialogControllerImpl::~AutofillDialogControllerImpl() {
333 if (popup_controller_
)
334 popup_controller_
->Hide();
336 AutofillMetrics::LogDialogInitialUserState(initial_user_state_
);
339 // Checks the country code against the values the form structure enumerates.
340 bool AutofillCountryFilter(
341 const std::set
<base::string16
>& form_structure_values
,
342 const std::string
& country_code
) {
343 if (!form_structure_values
.empty() &&
344 !form_structure_values
.count(base::ASCIIToUTF16(country_code
))) {
351 // Checks the country code against the values the form structure enumerates and
352 // against the ones Wallet allows.
353 bool WalletCountryFilter(
354 const std::set
<base::string16
>& form_structure_values
,
355 const std::set
<std::string
>& wallet_allowed_values
,
356 const std::string
& country_code
) {
357 if ((!form_structure_values
.empty() &&
358 !form_structure_values
.count(base::ASCIIToUTF16(country_code
))) ||
359 (!wallet_allowed_values
.empty() &&
360 !wallet_allowed_values
.count(country_code
))) {
368 base::WeakPtr
<AutofillDialogControllerImpl
>
369 AutofillDialogControllerImpl::Create(
370 content::WebContents
* contents
,
371 const FormData
& form_structure
,
372 const GURL
& source_url
,
373 const AutofillClient::ResultCallback
& callback
) {
374 // AutofillDialogControllerImpl owns itself.
375 AutofillDialogControllerImpl
* autofill_dialog_controller
=
376 new AutofillDialogControllerImpl(contents
,
380 return autofill_dialog_controller
->weak_ptr_factory_
.GetWeakPtr();
384 void AutofillDialogController::RegisterPrefs(PrefRegistrySimple
* registry
) {
385 registry
->RegisterListPref(::prefs::kAutofillDialogWalletLocationAcceptance
);
389 void AutofillDialogController::RegisterProfilePrefs(
390 user_prefs::PrefRegistrySyncable
* registry
) {
391 registry
->RegisterBooleanPref(
392 ::prefs::kAutofillDialogPayWithoutWallet
,
394 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF
);
395 registry
->RegisterDictionaryPref(
396 ::prefs::kAutofillDialogAutofillDefault
,
397 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF
);
398 registry
->RegisterBooleanPref(
399 ::prefs::kAutofillDialogSaveData
,
401 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF
);
402 registry
->RegisterBooleanPref(
403 ::prefs::kAutofillDialogWalletShippingSameAsBilling
,
405 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF
);
409 base::WeakPtr
<AutofillDialogController
> AutofillDialogController::Create(
410 content::WebContents
* contents
,
411 const FormData
& form_structure
,
412 const GURL
& source_url
,
413 const AutofillClient::ResultCallback
& callback
) {
414 return AutofillDialogControllerImpl::Create(contents
,
420 void AutofillDialogControllerImpl::Show() {
421 dialog_shown_timestamp_
= base::Time::Now();
423 // Determine what field types should be included in the dialog.
424 bool has_types
= false;
425 bool has_sections
= false;
426 form_structure_
.ParseFieldTypesFromAutocompleteAttributes(
427 &has_types
, &has_sections
);
429 // Fail if the author didn't specify autocomplete types.
432 AutofillClient::AutocompleteResultErrorDisabled
,
433 base::ASCIIToUTF16("Form is missing autocomplete attributes."),
439 // Fail if the author didn't ask for at least some kind of credit card
441 bool has_credit_card_field
= false;
442 for (size_t i
= 0; i
< form_structure_
.field_count(); ++i
) {
443 AutofillType type
= form_structure_
.field(i
)->Type();
444 if (type
.html_type() != HTML_TYPE_UNKNOWN
&& type
.group() == CREDIT_CARD
) {
445 has_credit_card_field
= true;
450 if (!has_credit_card_field
) {
451 callback_
.Run(AutofillClient::AutocompleteResultErrorDisabled
,
453 "Form is not a payment form (must contain "
454 "some autocomplete=\"cc-*\" fields). "),
460 billing_country_combobox_model_
.reset(new CountryComboboxModel());
461 billing_country_combobox_model_
->SetCountries(
463 base::Bind(AutofillCountryFilter
,
464 form_structure_
.PossibleValues(ADDRESS_BILLING_COUNTRY
)));
465 shipping_country_combobox_model_
.reset(new CountryComboboxModel());
466 acceptable_shipping_countries_
=
467 form_structure_
.PossibleValues(ADDRESS_HOME_COUNTRY
);
468 shipping_country_combobox_model_
->SetCountries(
470 base::Bind(AutofillCountryFilter
,
471 base::ConstRef(acceptable_shipping_countries_
)));
473 // If the form has a country <select> but none of the options are valid, bail.
474 if (billing_country_combobox_model_
->GetItemCount() == 0 ||
475 shipping_country_combobox_model_
->GetItemCount() == 0) {
476 callback_
.Run(AutofillClient::AutocompleteResultErrorDisabled
,
477 base::ASCIIToUTF16("No valid/supported country options"
484 // Log any relevant UI metrics and security exceptions.
485 AutofillMetrics::LogDialogUiEvent(AutofillMetrics::DIALOG_UI_SHOWN
);
487 AutofillMetrics::LogDialogSecurityMetric(
488 AutofillMetrics::SECURITY_METRIC_DIALOG_SHOWN
);
490 // The Autofill dialog is shown in response to a message from the renderer and
491 // as such, it can only be made in the context of the current document. A call
492 // to GetActiveEntry would return a pending entry, if there was one, which
493 // would be a security bug. Therefore, we use the last committed URL for the
495 const GURL
& current_url
= web_contents()->GetLastCommittedURL();
496 invoked_from_same_origin_
=
497 current_url
.GetOrigin() == source_url_
.GetOrigin();
499 if (!invoked_from_same_origin_
) {
500 AutofillMetrics::LogDialogSecurityMetric(
501 AutofillMetrics::SECURITY_METRIC_CROSS_ORIGIN_FRAME
);
504 for (size_t i
= SECTION_MIN
; i
<= SECTION_MAX
; ++i
) {
505 DialogSection section
= static_cast<DialogSection
>(i
);
507 std::string country_code
;
508 CountryComboboxModel
* model
= CountryComboboxModelForSection(section
);
510 country_code
= model
->GetDefaultCountryCode();
512 DetailInputs
* inputs
= MutableRequestedFieldsForSection(section
);
513 BuildInputsForSection(
514 section
, country_code
, inputs
,
515 MutableAddressLanguageCodeForSection(section
));
518 // Test whether we need to show the shipping section. If filling that section
519 // would be a no-op, don't show it.
520 cares_about_shipping_
= form_structure_
.FillFields(
521 RequestedTypesForSection(SECTION_SHIPPING
),
522 base::Bind(ServerTypeMatchesField
, SECTION_SHIPPING
),
523 base::Bind(NullGetInfo
), std::string(),
524 g_browser_process
->GetApplicationLocale());
526 transaction_amount_
= form_structure_
.GetUniqueValue(
527 HTML_TYPE_TRANSACTION_AMOUNT
);
528 transaction_currency_
= form_structure_
.GetUniqueValue(
529 HTML_TYPE_TRANSACTION_CURRENCY
);
530 acceptable_cc_types_
= form_structure_
.PossibleValues(CREDIT_CARD_TYPE
);
532 validator_
.reset(new AddressValidator(
533 scoped_ptr
< ::i18n::addressinput::Source
>(
534 new autofill::ChromeMetadataSource(I18N_ADDRESS_VALIDATION_DATA_URL
,
535 profile_
->GetRequestContext())),
536 ValidationRulesStorageFactory::CreateStorage(),
539 SuggestionsUpdated();
540 SubmitButtonDelayBegin();
541 view_
.reset(CreateView());
543 GetManager()->AddObserver(this);
545 LogDialogLatencyToShow();
548 void AutofillDialogControllerImpl::Hide() {
553 // TODO(estade): remove.
554 void AutofillDialogControllerImpl::TabActivated() {
557 ////////////////////////////////////////////////////////////////////////////////
558 // AutofillDialogViewDelegate implementation.
560 base::string16
AutofillDialogControllerImpl::DialogTitle() const {
561 return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_TITLE
);
564 base::string16
AutofillDialogControllerImpl::EditSuggestionText() const {
565 return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_EDIT
);
568 base::string16
AutofillDialogControllerImpl::CancelButtonText() const {
569 return l10n_util::GetStringUTF16(IDS_CANCEL
);
572 base::string16
AutofillDialogControllerImpl::ConfirmButtonText() const {
573 return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SUBMIT_BUTTON
);
576 base::string16
AutofillDialogControllerImpl::SaveLocallyText() const {
577 return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SAVE_LOCALLY_CHECKBOX
);
580 base::string16
AutofillDialogControllerImpl::SaveLocallyTooltip() const {
581 return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SAVE_LOCALLY_TOOLTIP
);
584 bool AutofillDialogControllerImpl::ShouldOfferToSaveInChrome() const {
585 return IsAutofillEnabled() && !profile_
->IsOffTheRecord() &&
586 IsManuallyEditingAnySection();
589 bool AutofillDialogControllerImpl::ShouldSaveInChrome() const {
590 return profile_
->GetPrefs()->GetBoolean(::prefs::kAutofillDialogSaveData
);
593 int AutofillDialogControllerImpl::GetDialogButtons() const {
594 return ui::DIALOG_BUTTON_OK
| ui::DIALOG_BUTTON_CANCEL
;
597 bool AutofillDialogControllerImpl::IsDialogButtonEnabled(
598 ui::DialogButton button
) const {
599 if (button
== ui::DIALOG_BUTTON_OK
)
600 return !submit_button_delay_timer_
.IsRunning();
602 DCHECK_EQ(ui::DIALOG_BUTTON_CANCEL
, button
);
606 bool AutofillDialogControllerImpl::SectionIsActive(DialogSection section
)
608 return FormStructureCaresAboutSection(section
);
611 void AutofillDialogControllerImpl::ResetSectionInput(DialogSection section
) {
612 SetEditingExistingData(section
, false);
613 needs_validation_
.erase(section
);
615 CountryComboboxModel
* model
= CountryComboboxModelForSection(section
);
617 base::string16 country
= model
->GetItemAt(model
->GetDefaultIndex());
618 RebuildInputsForCountry(section
, country
, false);
621 DetailInputs
* inputs
= MutableRequestedFieldsForSection(section
);
622 for (DetailInputs::iterator it
= inputs
->begin();
623 it
!= inputs
->end(); ++it
) {
624 if (it
->length
!= DetailInput::NONE
) {
625 it
->initial_value
.clear();
626 } else if (!it
->initial_value
.empty() &&
627 (it
->type
== ADDRESS_BILLING_COUNTRY
||
628 it
->type
== ADDRESS_HOME_COUNTRY
)) {
629 GetValidator()->LoadRules(AutofillCountry::GetCountryCode(
630 it
->initial_value
, g_browser_process
->GetApplicationLocale()));
635 void AutofillDialogControllerImpl::ShowEditUiIfBadSuggestion(
636 DialogSection section
) {
637 // |CreateWrapper()| returns an empty wrapper if |IsEditingExistingData()|, so
638 // get the wrapper before this potentially happens below.
639 scoped_ptr
<DataModelWrapper
> wrapper
= CreateWrapper(section
);
641 // If the chosen item in |model| yields an empty suggestion text, it is
642 // invalid. In this case, show the edit UI and highlight invalid fields.
643 SuggestionsMenuModel
* model
= SuggestionsMenuModelForSection(section
);
644 base::string16 unused
, unused2
;
645 if (IsASuggestionItemKey(model
->GetItemKeyForCheckedItem()) &&
646 !SuggestionTextForSection(section
, &unused
, &unused2
)) {
647 SetEditingExistingData(section
, true);
650 if (wrapper
&& IsEditingExistingData(section
)) {
651 base::string16 country
=
652 wrapper
->GetInfo(AutofillType(CountryTypeForSection(section
)));
653 if (!country
.empty()) {
654 // There's no user input to restore here as this is only called after
655 // resetting all section input.
656 if (RebuildInputsForCountry(section
, country
, false))
657 UpdateSection(section
);
659 wrapper
->FillInputs(MutableRequestedFieldsForSection(section
));
663 bool AutofillDialogControllerImpl::InputWasEdited(ServerFieldType type
,
664 const base::string16
& value
) {
668 // If this is a combobox at the default value, don't preserve it.
669 ui::ComboboxModel
* model
= ComboboxModelForAutofillType(type
);
670 if (model
&& model
->GetItemAt(model
->GetDefaultIndex()) == value
)
676 FieldValueMap
AutofillDialogControllerImpl::TakeUserInputSnapshot() {
677 FieldValueMap snapshot
;
681 for (size_t i
= SECTION_MIN
; i
<= SECTION_MAX
; ++i
) {
682 DialogSection section
= static_cast<DialogSection
>(i
);
683 SuggestionsMenuModel
* model
= SuggestionsMenuModelForSection(section
);
684 if (model
->GetItemKeyForCheckedItem() != kAddNewItemKey
)
687 FieldValueMap outputs
;
688 view_
->GetUserInput(section
, &outputs
);
689 // Remove fields that are empty, at their default values, or invalid.
690 for (FieldValueMap::iterator it
= outputs
.begin(); it
!= outputs
.end();
692 if (InputWasEdited(it
->first
, it
->second
) &&
693 InputValidityMessage(section
, it
->first
, it
->second
).empty()) {
694 snapshot
.insert(std::make_pair(it
->first
, it
->second
));
702 void AutofillDialogControllerImpl::RestoreUserInputFromSnapshot(
703 const FieldValueMap
& snapshot
) {
704 if (snapshot
.empty())
707 for (size_t i
= SECTION_MIN
; i
<= SECTION_MAX
; ++i
) {
708 DialogSection section
= static_cast<DialogSection
>(i
);
709 if (!SectionIsActive(section
))
712 DetailInputs
* inputs
= MutableRequestedFieldsForSection(section
);
713 for (size_t i
= 0; i
< inputs
->size(); ++i
) {
714 DetailInput
* input
= &(*inputs
)[i
];
715 if (input
->length
!= DetailInput::NONE
) {
716 input
->initial_value
=
717 GetInfoFromInputs(snapshot
, section
, AutofillType(input
->type
));
719 if (InputWasEdited(input
->type
, input
->initial_value
))
720 SuggestionsMenuModelForSection(section
)->SetCheckedItem(kAddNewItemKey
);
725 void AutofillDialogControllerImpl::UpdateSection(DialogSection section
) {
727 view_
->UpdateSection(section
);
730 void AutofillDialogControllerImpl::UpdateForErrors() {
734 // Currently, the view should only need to be updated if validating a
735 // suggestion that's based on existing data.
736 for (size_t i
= SECTION_MIN
; i
<= SECTION_MAX
; ++i
) {
737 if (IsEditingExistingData(static_cast<DialogSection
>(i
))) {
738 view_
->UpdateForErrors();
744 const DetailInputs
& AutofillDialogControllerImpl::RequestedFieldsForSection(
745 DialogSection section
) const {
748 return requested_cc_fields_
;
749 case SECTION_BILLING
:
750 return requested_billing_fields_
;
751 case SECTION_SHIPPING
:
752 return requested_shipping_fields_
;
756 return requested_billing_fields_
;
759 ui::ComboboxModel
* AutofillDialogControllerImpl::ComboboxModelForAutofillType(
760 ServerFieldType type
) {
762 case CREDIT_CARD_EXP_MONTH
:
763 return &cc_exp_month_combobox_model_
;
765 case CREDIT_CARD_EXP_4_DIGIT_YEAR
:
766 return &cc_exp_year_combobox_model_
;
768 case ADDRESS_BILLING_COUNTRY
:
769 return billing_country_combobox_model_
.get();
771 case ADDRESS_HOME_COUNTRY
:
772 return shipping_country_combobox_model_
.get();
779 ui::MenuModel
* AutofillDialogControllerImpl::MenuModelForSection(
780 DialogSection section
) {
781 SuggestionsMenuModel
* model
= SuggestionsMenuModelForSection(section
);
782 // The shipping section menu is special. It will always show because there is
783 // a choice between "Use billing" and "enter new".
784 if (section
== SECTION_SHIPPING
)
787 // For other sections, only show a menu if there's at least one suggestion.
788 for (int i
= 0; i
< model
->GetItemCount(); ++i
) {
789 if (IsASuggestionItemKey(model
->GetItemKeyAt(i
)))
796 base::string16
AutofillDialogControllerImpl::LabelForSection(
797 DialogSection section
) const {
800 return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SECTION_CC
);
801 case SECTION_BILLING
:
802 return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SECTION_BILLING
);
803 case SECTION_SHIPPING
:
804 return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SECTION_SHIPPING
);
807 return base::string16();
810 SuggestionState
AutofillDialogControllerImpl::SuggestionStateForSection(
811 DialogSection section
) {
812 base::string16 vertically_compact
, horizontally_compact
;
813 bool show_suggestion
= SuggestionTextForSection(section
,
815 &horizontally_compact
);
816 return SuggestionState(show_suggestion
,
818 horizontally_compact
,
819 SuggestionIconForSection(section
),
820 ExtraSuggestionTextForSection(section
),
821 ExtraSuggestionIconForSection(section
));
824 bool AutofillDialogControllerImpl::SuggestionTextForSection(
825 DialogSection section
,
826 base::string16
* vertically_compact
,
827 base::string16
* horizontally_compact
) {
828 // When the user has clicked 'edit' or a suggestion is somehow invalid (e.g. a
829 // user selects a credit card that has expired), don't show a suggestion (even
830 // though there is a profile selected in the model).
831 if (IsEditingExistingData(section
))
834 SuggestionsMenuModel
* model
= SuggestionsMenuModelForSection(section
);
835 std::string item_key
= model
->GetItemKeyForCheckedItem();
836 if (item_key
== kSameAsBillingKey
) {
837 *vertically_compact
= *horizontally_compact
= l10n_util::GetStringUTF16(
838 IDS_AUTOFILL_DIALOG_USING_BILLING_FOR_SHIPPING
);
842 if (!IsASuggestionItemKey(item_key
))
845 if (section
== SECTION_BILLING
|| section
== SECTION_SHIPPING
) {
846 // Also check if the address is invalid (rules may have loaded since
847 // the dialog was shown).
848 if (HasInvalidAddress(*GetManager()->GetProfileByGUID(item_key
)))
852 scoped_ptr
<DataModelWrapper
> wrapper
= CreateWrapper(section
);
853 return wrapper
->GetDisplayText(vertically_compact
, horizontally_compact
);
856 base::string16
AutofillDialogControllerImpl::ExtraSuggestionTextForSection(
857 DialogSection section
) const {
858 if (section
== SECTION_CC
)
859 return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_CVC
);
861 return base::string16();
864 scoped_ptr
<DataModelWrapper
> AutofillDialogControllerImpl::CreateWrapper(
865 DialogSection section
) {
866 SuggestionsMenuModel
* model
= SuggestionsMenuModelForSection(section
);
867 std::string item_key
= model
->GetItemKeyForCheckedItem();
868 if (!IsASuggestionItemKey(item_key
) || IsManuallyEditingSection(section
))
869 return scoped_ptr
<DataModelWrapper
>();
871 if (section
== SECTION_CC
) {
872 CreditCard
* card
= GetManager()->GetCreditCardByGUID(item_key
);
874 return scoped_ptr
<DataModelWrapper
>(new AutofillCreditCardWrapper(card
));
877 AutofillProfile
* profile
= GetManager()->GetProfileByGUID(item_key
);
879 if (section
== SECTION_SHIPPING
) {
880 return scoped_ptr
<DataModelWrapper
>(
881 new AutofillShippingAddressWrapper(profile
));
883 DCHECK_EQ(SECTION_BILLING
, section
);
884 return scoped_ptr
<DataModelWrapper
>(
885 new AutofillProfileWrapper(profile
));
888 gfx::Image
AutofillDialogControllerImpl::SuggestionIconForSection(
889 DialogSection section
) {
890 scoped_ptr
<DataModelWrapper
> model
= CreateWrapper(section
);
894 return model
->GetIcon();
897 gfx::Image
AutofillDialogControllerImpl::ExtraSuggestionIconForSection(
898 DialogSection section
) {
899 if (section
!= SECTION_CC
)
902 scoped_ptr
<DataModelWrapper
> model
= CreateWrapper(section
);
906 return CvcIconForCreditCardType(
907 model
->GetInfo(AutofillType(CREDIT_CARD_TYPE
)));
910 FieldIconMap
AutofillDialogControllerImpl::IconsForFields(
911 const FieldValueMap
& user_inputs
) const {
913 base::string16 credit_card_type
;
915 FieldValueMap::const_iterator credit_card_iter
=
916 user_inputs
.find(CREDIT_CARD_NUMBER
);
917 if (credit_card_iter
!= user_inputs
.end()) {
918 const base::string16
& number
= credit_card_iter
->second
;
919 const std::string type
= CreditCard::GetCreditCardType(number
);
920 credit_card_type
= CreditCard::TypeForDisplay(type
);
921 result
[CREDIT_CARD_NUMBER
] = CreditCardIconForType(type
);
924 if (!user_inputs
.count(CREDIT_CARD_VERIFICATION_CODE
))
927 result
[CREDIT_CARD_VERIFICATION_CODE
] =
928 CvcIconForCreditCardType(credit_card_type
);
933 bool AutofillDialogControllerImpl::FieldControlsIcons(
934 ServerFieldType type
) const {
935 return type
== CREDIT_CARD_NUMBER
;
938 base::string16
AutofillDialogControllerImpl::TooltipForField(
939 ServerFieldType type
) const {
940 if (type
== PHONE_HOME_WHOLE_NUMBER
|| type
== PHONE_BILLING_WHOLE_NUMBER
)
941 return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_TOOLTIP_PHONE_NUMBER
);
943 return base::string16();
946 // TODO(groby): Add more tests.
947 base::string16
AutofillDialogControllerImpl::InputValidityMessage(
948 DialogSection section
,
949 ServerFieldType type
,
950 const base::string16
& value
) {
951 AutofillType
autofill_type(type
);
952 if (autofill_type
.group() == ADDRESS_HOME
||
953 autofill_type
.group() == ADDRESS_BILLING
) {
954 return base::string16();
957 switch (autofill_type
.GetStorableType()) {
959 if (!value
.empty() && !IsValidEmailAddress(value
)) {
960 return l10n_util::GetStringUTF16(
961 IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_EMAIL_ADDRESS
);
965 case CREDIT_CARD_NUMBER
: {
966 if (!value
.empty()) {
967 base::string16 message
= CreditCardNumberValidityMessage(value
);
968 if (!message
.empty())
974 case CREDIT_CARD_EXP_MONTH
:
975 if (!InputWasEdited(CREDIT_CARD_EXP_MONTH
, value
)) {
976 return l10n_util::GetStringUTF16(
977 IDS_LIBADDRESSINPUT_MISSING_REQUIRED_FIELD
);
981 case CREDIT_CARD_EXP_4_DIGIT_YEAR
:
982 if (!InputWasEdited(CREDIT_CARD_EXP_4_DIGIT_YEAR
, value
)) {
983 return l10n_util::GetStringUTF16(
984 IDS_LIBADDRESSINPUT_MISSING_REQUIRED_FIELD
);
988 case CREDIT_CARD_VERIFICATION_CODE
:
989 if (!value
.empty() && !autofill::IsValidCreditCardSecurityCode(value
)) {
990 return l10n_util::GetStringUTF16(
991 IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_CREDIT_CARD_SECURITY_CODE
);
998 case PHONE_HOME_WHOLE_NUMBER
: // Used in shipping section.
1001 case PHONE_BILLING_WHOLE_NUMBER
: // Used in billing section.
1005 NOTREACHED(); // Trying to validate unknown field.
1009 return value
.empty() ? l10n_util::GetStringUTF16(
1010 IDS_LIBADDRESSINPUT_MISSING_REQUIRED_FIELD
) :
1014 // TODO(groby): Also add tests.
1015 ValidityMessages
AutofillDialogControllerImpl::InputsAreValid(
1016 DialogSection section
,
1017 const FieldValueMap
& inputs
) {
1018 ValidityMessages messages
;
1022 AddressValidator::Status status
= AddressValidator::SUCCESS
;
1023 if (section
!= SECTION_CC
) {
1024 AutofillProfile profile
;
1025 FillFormGroupFromOutputs(inputs
, &profile
);
1026 scoped_ptr
<AddressData
> address_data
=
1027 i18n::CreateAddressDataFromAutofillProfile(
1028 profile
, g_browser_process
->GetApplicationLocale());
1029 address_data
->language_code
= AddressLanguageCodeForSection(section
);
1031 Localization localization
;
1032 localization
.SetGetter(l10n_util::GetStringUTF8
);
1033 FieldProblemMap problems
;
1034 status
= GetValidator()->ValidateAddress(*address_data
, NULL
, &problems
);
1035 bool billing
= section
!= SECTION_SHIPPING
;
1037 for (FieldProblemMap::const_iterator iter
= problems
.begin();
1038 iter
!= problems
.end(); ++iter
) {
1039 bool sure
= iter
->second
!= MISSING_REQUIRED_FIELD
;
1040 base::string16 text
= base::UTF8ToUTF16(localization
.GetErrorMessage(
1041 *address_data
, iter
->first
, iter
->second
, true, false));
1042 messages
.Set(i18n::TypeForField(iter
->first
, billing
),
1043 ValidityMessage(text
, sure
));
1047 for (FieldValueMap::const_iterator iter
= inputs
.begin();
1048 iter
!= inputs
.end(); ++iter
) {
1049 const ServerFieldType type
= iter
->first
;
1050 base::string16 text
= InputValidityMessage(section
, type
, iter
->second
);
1052 // Skip empty/unchanged fields in edit mode. If the individual field does
1053 // not have validation errors, assume it to be valid unless later proven
1055 bool sure
= InputWasEdited(type
, iter
->second
);
1057 if (sure
&& status
== AddressValidator::RULES_NOT_READY
&&
1058 !ComboboxModelForAutofillType(type
) &&
1059 (AutofillType(type
).group() == ADDRESS_HOME
||
1060 AutofillType(type
).group() == ADDRESS_BILLING
)) {
1061 DCHECK(text
.empty());
1062 text
= l10n_util::GetStringUTF16(
1063 IDS_AUTOFILL_DIALOG_VALIDATION_WAITING_FOR_RULES
);
1065 needs_validation_
.insert(section
);
1068 messages
.Set(type
, ValidityMessage(text
, sure
));
1071 // For the convenience of using operator[].
1072 FieldValueMap
& field_values
= const_cast<FieldValueMap
&>(inputs
);
1073 // Validate the date formed by month and year field. (Autofill dialog is
1074 // never supposed to have 2-digit years, so not checked).
1075 if (field_values
.count(CREDIT_CARD_EXP_4_DIGIT_YEAR
) &&
1076 field_values
.count(CREDIT_CARD_EXP_MONTH
) &&
1077 InputWasEdited(CREDIT_CARD_EXP_4_DIGIT_YEAR
,
1078 field_values
[CREDIT_CARD_EXP_4_DIGIT_YEAR
]) &&
1079 InputWasEdited(CREDIT_CARD_EXP_MONTH
,
1080 field_values
[CREDIT_CARD_EXP_MONTH
])) {
1081 ValidityMessage
year_message(base::string16(), true);
1082 ValidityMessage
month_message(base::string16(), true);
1083 if (!autofill::IsValidCreditCardExpirationDate(
1084 field_values
[CREDIT_CARD_EXP_4_DIGIT_YEAR
],
1085 field_values
[CREDIT_CARD_EXP_MONTH
], base::Time::Now())) {
1086 // The dialog shows the same error message for the month and year fields.
1087 year_message
.text
= l10n_util::GetStringUTF16(
1088 IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_CREDIT_CARD_EXPIRATION_DATE
);
1089 month_message
.text
= l10n_util::GetStringUTF16(
1090 IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_CREDIT_CARD_EXPIRATION_DATE
);
1092 messages
.Set(CREDIT_CARD_EXP_4_DIGIT_YEAR
, year_message
);
1093 messages
.Set(CREDIT_CARD_EXP_MONTH
, month_message
);
1096 // If there is a credit card number and a CVC, validate them together.
1097 if (field_values
.count(CREDIT_CARD_NUMBER
) &&
1098 field_values
.count(CREDIT_CARD_VERIFICATION_CODE
)) {
1099 ValidityMessage
ccv_message(base::string16(), true);
1100 if (!autofill::IsValidCreditCardSecurityCode(
1101 field_values
[CREDIT_CARD_VERIFICATION_CODE
],
1102 field_values
[CREDIT_CARD_NUMBER
])) {
1103 ccv_message
.text
= l10n_util::GetStringUTF16(
1104 IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_CREDIT_CARD_SECURITY_CODE
);
1106 messages
.Set(CREDIT_CARD_VERIFICATION_CODE
, ccv_message
);
1109 // Validate the shipping phone number against the country code of the address.
1110 if (field_values
.count(ADDRESS_HOME_COUNTRY
) &&
1111 field_values
.count(PHONE_HOME_WHOLE_NUMBER
)) {
1113 PHONE_HOME_WHOLE_NUMBER
,
1114 GetPhoneValidityMessage(field_values
[ADDRESS_HOME_COUNTRY
],
1115 field_values
[PHONE_HOME_WHOLE_NUMBER
]));
1118 // Validate the billing phone number against the country code of the address.
1119 if (field_values
.count(ADDRESS_BILLING_COUNTRY
) &&
1120 field_values
.count(PHONE_BILLING_WHOLE_NUMBER
)) {
1122 PHONE_BILLING_WHOLE_NUMBER
,
1123 GetPhoneValidityMessage(field_values
[ADDRESS_BILLING_COUNTRY
],
1124 field_values
[PHONE_BILLING_WHOLE_NUMBER
]));
1130 void AutofillDialogControllerImpl::UserEditedOrActivatedInput(
1131 DialogSection section
,
1132 ServerFieldType type
,
1133 gfx::NativeView parent_view
,
1134 const gfx::Rect
& content_bounds
,
1135 const base::string16
& field_contents
,
1137 ScopedViewUpdates
updates(view_
.get());
1139 if (type
== ADDRESS_BILLING_COUNTRY
|| type
== ADDRESS_HOME_COUNTRY
) {
1140 const FieldValueMap
& snapshot
= TakeUserInputSnapshot();
1142 // Clobber the inputs because the view's already been updated.
1143 RebuildInputsForCountry(section
, field_contents
, true);
1144 RestoreUserInputFromSnapshot(snapshot
);
1145 UpdateSection(section
);
1148 // The rest of this method applies only to textfields while Autofill is
1149 // enabled. If a combobox or Autofill is disabled, bail.
1150 if (ComboboxModelForAutofillType(type
) || !IsAutofillEnabled())
1153 // If the field is edited down to empty, don't show a popup.
1154 if (was_edit
&& field_contents
.empty()) {
1159 // If the user clicks while the popup is already showing, be sure to hide
1161 if (!was_edit
&& popup_controller_
.get()) {
1166 std::vector
<autofill::Suggestion
> popup_suggestions
;
1167 popup_suggestion_ids_
.clear();
1168 if (IsCreditCardType(type
)) {
1169 popup_suggestions
= GetManager()->GetCreditCardSuggestions(
1170 AutofillType(type
), field_contents
);
1171 for (const auto& suggestion
: popup_suggestions
)
1172 popup_suggestion_ids_
.push_back(suggestion
.backend_id
);
1174 popup_suggestions
= GetManager()->GetProfileSuggestions(
1178 RequestedTypesForSection(section
));
1179 // Filter out ones we don't want.
1180 for (int i
= 0; i
< static_cast<int>(popup_suggestions
.size()); i
++) {
1181 const autofill::AutofillProfile
* profile
=
1182 GetManager()->GetProfileByGUID(popup_suggestions
[i
].backend_id
);
1183 if (!profile
|| !ShouldSuggestProfile(section
, *profile
)) {
1184 popup_suggestions
.erase(popup_suggestions
.begin() + i
);
1190 for (const auto& suggestion
: popup_suggestions
)
1191 popup_suggestion_ids_
.push_back(suggestion
.backend_id
);
1193 // This will append to the popup_suggestions but not the IDs since there
1194 // are no backend IDs for the I18N validator suggestions.
1195 GetI18nValidatorSuggestions(section
, type
, &popup_suggestions
);
1198 if (popup_suggestions
.empty()) {
1203 // |popup_input_type_| must be set before calling |Show()|.
1204 popup_input_type_
= type
;
1205 popup_section_
= section
;
1207 // Use our own 0-based IDs for the items.
1208 // TODO(estade): do we need separators and control rows like 'Clear
1210 for (size_t i
= 0; i
< popup_suggestions
.size(); ++i
) {
1211 popup_suggestions
[i
].frontend_id
= i
;
1214 popup_controller_
= AutofillPopupControllerImpl::GetOrCreate(
1215 popup_controller_
, weak_ptr_factory_
.GetWeakPtr(), NULL
, parent_view
,
1216 gfx::RectF(content_bounds
),
1217 base::i18n::IsRTL() ? base::i18n::RIGHT_TO_LEFT
1218 : base::i18n::LEFT_TO_RIGHT
);
1219 popup_controller_
->Show(popup_suggestions
);
1222 void AutofillDialogControllerImpl::FocusMoved() {
1226 bool AutofillDialogControllerImpl::ShouldShowErrorBubble() const {
1227 return popup_input_type_
== UNKNOWN_TYPE
;
1230 void AutofillDialogControllerImpl::ViewClosed() {
1231 GetManager()->RemoveObserver(this);
1233 // Called from here rather than in ~AutofillDialogControllerImpl as this
1234 // relies on virtual methods that change to their base class in the dtor.
1235 MaybeShowCreditCardBubble();
1240 std::vector
<DialogNotification
> AutofillDialogControllerImpl::
1241 CurrentNotifications() {
1242 std::vector
<DialogNotification
> notifications
;
1244 if (!invoked_from_same_origin_
) {
1245 notifications
.push_back(DialogNotification(
1246 DialogNotification::SECURITY_WARNING
,
1247 l10n_util::GetStringFUTF16(IDS_AUTOFILL_DIALOG_SITE_WARNING
,
1248 base::UTF8ToUTF16(source_url_
.host()))));
1251 return notifications
;
1254 void AutofillDialogControllerImpl::LinkClicked(const GURL
& url
) {
1255 OpenTabWithUrl(url
);
1258 void AutofillDialogControllerImpl::OnCancel() {
1260 if (!data_was_passed_back_
)
1261 LogOnCancelMetrics();
1263 AutofillClient::AutocompleteResultErrorCancel
, base::string16(), NULL
);
1266 void AutofillDialogControllerImpl::OnAccept() {
1267 ScopedViewUpdates
updates(view_
.get());
1272 Profile
* AutofillDialogControllerImpl::profile() {
1276 content::WebContents
* AutofillDialogControllerImpl::GetWebContents() {
1277 return web_contents();
1280 ////////////////////////////////////////////////////////////////////////////////
1281 // AutofillPopupDelegate implementation.
1283 void AutofillDialogControllerImpl::OnPopupShown() {
1284 ScopedViewUpdates
update(view_
.get());
1285 view_
->UpdateErrorBubble();
1287 AutofillMetrics::LogDialogPopupEvent(AutofillMetrics::DIALOG_POPUP_SHOWN
);
1290 void AutofillDialogControllerImpl::OnPopupHidden() {}
1292 void AutofillDialogControllerImpl::DidSelectSuggestion(
1293 const base::string16
& value
,
1295 // TODO(estade): implement.
1298 void AutofillDialogControllerImpl::DidAcceptSuggestion(
1299 const base::string16
& value
,
1302 DCHECK_NE(UNKNOWN_TYPE
, popup_input_type_
);
1303 // Because |HidePopup()| can be called from |UpdateSection()|, remember the
1304 // type of the input for later here.
1305 const ServerFieldType popup_input_type
= popup_input_type_
;
1307 ScopedViewUpdates
updates(view_
.get());
1308 scoped_ptr
<DataModelWrapper
> wrapper
;
1310 if (static_cast<size_t>(identifier
) < popup_suggestion_ids_
.size()) {
1311 const std::string
& guid
= popup_suggestion_ids_
[identifier
];
1312 if (IsCreditCardType(popup_input_type
)) {
1313 wrapper
.reset(new AutofillCreditCardWrapper(
1314 GetManager()->GetCreditCardByGUID(guid
)));
1316 wrapper
.reset(new AutofillProfileWrapper(
1317 GetManager()->GetProfileByGUID(guid
)));
1320 wrapper
.reset(new I18nAddressDataWrapper(
1321 &i18n_validator_suggestions_
[
1322 identifier
- popup_suggestion_ids_
.size()]));
1325 // If the user hasn't switched away from the default country and |wrapper|'s
1326 // country differs from the |view_|'s, rebuild inputs and restore user data.
1327 const FieldValueMap snapshot
= TakeUserInputSnapshot();
1328 bool billing_rebuilt
= false, shipping_rebuilt
= false;
1330 base::string16 billing_country
=
1331 wrapper
->GetInfo(AutofillType(ADDRESS_BILLING_COUNTRY
));
1332 if (popup_section_
== ActiveBillingSection() &&
1333 !snapshot
.count(ADDRESS_BILLING_COUNTRY
) &&
1334 !billing_country
.empty()) {
1335 billing_rebuilt
= RebuildInputsForCountry(
1336 ActiveBillingSection(), billing_country
, false);
1339 base::string16 shipping_country
=
1340 wrapper
->GetInfo(AutofillType(ADDRESS_HOME_COUNTRY
));
1341 if (popup_section_
== SECTION_SHIPPING
&&
1342 !snapshot
.count(ADDRESS_HOME_COUNTRY
) &&
1343 !shipping_country
.empty()) {
1344 shipping_rebuilt
= RebuildInputsForCountry(
1345 SECTION_SHIPPING
, shipping_country
, false);
1348 if (billing_rebuilt
|| shipping_rebuilt
) {
1349 RestoreUserInputFromSnapshot(snapshot
);
1350 if (billing_rebuilt
)
1351 UpdateSection(ActiveBillingSection());
1352 if (shipping_rebuilt
)
1353 UpdateSection(SECTION_SHIPPING
);
1356 DCHECK(SectionIsActive(popup_section_
));
1357 wrapper
->FillInputs(MutableRequestedFieldsForSection(popup_section_
));
1358 view_
->FillSection(popup_section_
, popup_input_type
);
1360 AutofillMetrics::LogDialogPopupEvent(
1361 AutofillMetrics::DIALOG_POPUP_FORM_FILLED
);
1363 // TODO(estade): not sure why it's necessary to do this explicitly.
1367 bool AutofillDialogControllerImpl::GetDeletionConfirmationText(
1368 const base::string16
& value
,
1370 base::string16
* title
,
1371 base::string16
* body
) {
1375 bool AutofillDialogControllerImpl::RemoveSuggestion(const base::string16
& value
,
1377 // TODO(estade): implement.
1381 void AutofillDialogControllerImpl::ClearPreviewedForm() {
1382 // TODO(estade): implement.
1385 ////////////////////////////////////////////////////////////////////////////////
1386 // SuggestionsMenuModelDelegate implementation.
1388 void AutofillDialogControllerImpl::SuggestionItemSelected(
1389 SuggestionsMenuModel
* model
,
1391 ScopedViewUpdates
updates(view_
.get());
1393 if (model
->GetItemKeyAt(index
) == kManageItemsKey
) {
1395 DCHECK(IsAutofillEnabled());
1396 GURL
settings_url(chrome::kChromeUISettingsURL
);
1397 url
= settings_url
.Resolve(chrome::kAutofillSubPage
);
1398 OpenTabWithUrl(url
);
1402 model
->SetCheckedIndex(index
);
1403 DialogSection section
= SectionForSuggestionsMenuModel(*model
);
1405 ResetSectionInput(section
);
1406 ShowEditUiIfBadSuggestion(section
);
1407 UpdateSection(section
);
1408 view_
->UpdateNotificationArea();
1411 LogSuggestionItemSelectedMetric(*model
);
1414 ////////////////////////////////////////////////////////////////////////////////
1415 // PersonalDataManagerObserver implementation.
1417 void AutofillDialogControllerImpl::OnPersonalDataChanged() {
1418 SuggestionsUpdated();
1421 ////////////////////////////////////////////////////////////////////////////////
1423 bool AutofillDialogControllerImpl::HandleKeyPressEventInInput(
1424 const content::NativeWebKeyboardEvent
& event
) {
1425 if (popup_controller_
.get())
1426 return popup_controller_
->HandleKeyPressEvent(event
);
1431 void AutofillDialogControllerImpl::ShowNewCreditCardBubble(
1432 scoped_ptr
<CreditCard
> new_card
,
1433 scoped_ptr
<AutofillProfile
> billing_profile
) {
1434 NewCreditCardBubbleController::Show(web_contents(),
1436 billing_profile
.Pass());
1439 void AutofillDialogControllerImpl::SubmitButtonDelayBegin() {
1440 submit_button_delay_timer_
.Start(
1442 base::TimeDelta::FromMilliseconds(kSubmitButtonDelayMs
),
1444 &AutofillDialogControllerImpl::OnSubmitButtonDelayEnd
);
1447 void AutofillDialogControllerImpl::SubmitButtonDelayEndForTesting() {
1448 DCHECK(submit_button_delay_timer_
.IsRunning());
1449 submit_button_delay_timer_
.user_task().Run();
1450 submit_button_delay_timer_
.Stop();
1453 AutofillDialogControllerImpl::AutofillDialogControllerImpl(
1454 content::WebContents
* contents
,
1455 const FormData
& form_structure
,
1456 const GURL
& source_url
,
1457 const AutofillClient::ResultCallback
& callback
)
1458 : WebContentsObserver(contents
),
1459 profile_(Profile::FromBrowserContext(contents
->GetBrowserContext())),
1460 initial_user_state_(AutofillMetrics::DIALOG_USER_STATE_UNKNOWN
),
1461 form_structure_(form_structure
),
1462 invoked_from_same_origin_(true),
1463 source_url_(source_url
),
1464 callback_(callback
),
1465 suggested_cc_(this),
1466 suggested_billing_(this),
1467 suggested_shipping_(this),
1468 cares_about_shipping_(true),
1469 popup_input_type_(UNKNOWN_TYPE
),
1470 popup_section_(SECTION_MIN
),
1471 data_was_passed_back_(false),
1472 was_ui_latency_logged_(false),
1473 weak_ptr_factory_(this) {
1474 DCHECK(!callback_
.is_null());
1477 AutofillDialogView
* AutofillDialogControllerImpl::CreateView() {
1478 return AutofillDialogView::Create(this);
1481 PersonalDataManager
* AutofillDialogControllerImpl::GetManager() const {
1482 return PersonalDataManagerFactory::GetForProfile(profile_
);
1485 AddressValidator
* AutofillDialogControllerImpl::GetValidator() {
1486 return validator_
.get();
1489 void AutofillDialogControllerImpl::OpenTabWithUrl(const GURL
& url
) {
1490 chrome::NavigateParams
params(
1491 chrome::FindBrowserWithWebContents(web_contents()),
1493 ui::PAGE_TRANSITION_LINK
);
1494 params
.disposition
= NEW_FOREGROUND_TAB
;
1495 chrome::Navigate(¶ms
);
1498 // TODO(estade): remove.
1499 DialogSection
AutofillDialogControllerImpl::ActiveBillingSection() const {
1500 return SECTION_BILLING
;
1503 bool AutofillDialogControllerImpl::IsEditingExistingData(
1504 DialogSection section
) const {
1505 return section_editing_state_
.count(section
) > 0;
1508 bool AutofillDialogControllerImpl::IsManuallyEditingSection(
1509 DialogSection section
) const {
1510 return IsEditingExistingData(section
) ||
1511 SuggestionsMenuModelForSection(section
)->
1512 GetItemKeyForCheckedItem() == kAddNewItemKey
;
1515 void AutofillDialogControllerImpl::SuggestionsUpdated() {
1516 ScopedViewUpdates
updates(view_
.get());
1518 const FieldValueMap snapshot
= TakeUserInputSnapshot();
1520 suggested_cc_
.Reset();
1521 suggested_billing_
.Reset();
1522 suggested_shipping_
.Reset();
1525 suggested_shipping_
.AddKeyedItem(
1527 l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_USE_BILLING_FOR_SHIPPING
));
1529 shipping_country_combobox_model_
->SetCountries(
1531 base::Bind(AutofillCountryFilter
, acceptable_shipping_countries_
));
1533 if (IsAutofillEnabled()) {
1534 PersonalDataManager
* manager
= GetManager();
1535 const std::vector
<CreditCard
*>& cards
= manager
->GetCreditCards();
1536 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
1537 for (size_t i
= 0; i
< cards
.size(); ++i
) {
1538 if (!i18ninput::CardHasCompleteAndVerifiedData(*cards
[i
]))
1541 suggested_cc_
.AddKeyedItemWithIcon(
1542 cards
[i
]->guid(), cards
[i
]->Label(),
1543 rb
.GetImageNamed(CreditCard::IconResourceId(cards
[i
]->type())));
1544 suggested_cc_
.SetEnabled(
1545 cards
[i
]->guid(), !ShouldDisallowCcType(cards
[i
]->TypeForDisplay()));
1548 const std::vector
<AutofillProfile
*>& profiles
= manager
->GetProfiles();
1549 std::vector
<base::string16
> labels
;
1550 AutofillProfile::CreateDifferentiatingLabels(
1551 profiles
, g_browser_process
->GetApplicationLocale(), &labels
);
1552 DCHECK_EQ(labels
.size(), profiles
.size());
1553 for (size_t i
= 0; i
< profiles
.size(); ++i
) {
1554 const AutofillProfile
& profile
= *profiles
[i
];
1555 if (!i18ninput::AddressHasCompleteAndVerifiedData(
1556 profile
, g_browser_process
->GetApplicationLocale())) {
1560 suggested_shipping_
.AddKeyedItem(profile
.guid(), labels
[i
]);
1561 suggested_shipping_
.SetEnabled(
1565 base::UTF16ToUTF8(profile
.GetRawInfo(ADDRESS_HOME_COUNTRY
))));
1566 if (!profile
.GetRawInfo(EMAIL_ADDRESS
).empty() &&
1567 !profile
.IsPresentButInvalid(EMAIL_ADDRESS
)) {
1568 suggested_billing_
.AddKeyedItem(profile
.guid(), labels
[i
]);
1569 suggested_billing_
.SetEnabled(
1573 base::UTF16ToUTF8(profile
.GetRawInfo(ADDRESS_HOME_COUNTRY
))));
1578 suggested_cc_
.AddKeyedItem(
1580 l10n_util::GetStringUTF16(IsAutofillEnabled()
1581 ? IDS_AUTOFILL_DIALOG_ADD_CREDIT_CARD
1582 : IDS_AUTOFILL_DIALOG_ENTER_CREDIT_CARD
));
1583 suggested_cc_
.AddKeyedItem(
1585 l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_MANAGE_CREDIT_CARD
));
1586 suggested_billing_
.AddKeyedItem(
1588 l10n_util::GetStringUTF16(
1589 IsAutofillEnabled() ? IDS_AUTOFILL_DIALOG_ADD_BILLING_ADDRESS
1590 : IDS_AUTOFILL_DIALOG_ENTER_BILLING_DETAILS
));
1591 suggested_billing_
.AddKeyedItem(
1593 l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_MANAGE_BILLING_ADDRESS
));
1595 suggested_shipping_
.AddKeyedItem(
1597 l10n_util::GetStringUTF16(
1599 ? IDS_AUTOFILL_DIALOG_ADD_SHIPPING_ADDRESS
1600 : IDS_AUTOFILL_DIALOG_USE_DIFFERENT_SHIPPING_ADDRESS
));
1602 if (IsAutofillEnabled()) {
1603 suggested_shipping_
.AddKeyedItem(
1605 l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_MANAGE_SHIPPING_ADDRESS
));
1607 for (size_t i
= SECTION_MIN
; i
<= SECTION_MAX
; ++i
) {
1608 DialogSection section
= static_cast<DialogSection
>(i
);
1609 if (!SectionIsActive(section
))
1612 // Set the starting choice for the menu. First set to the default in case
1613 // the GUID saved in prefs refers to a profile that no longer exists.
1615 GetDefaultAutofillChoice(section
, &guid
);
1616 SuggestionsMenuModel
* model
= SuggestionsMenuModelForSection(section
);
1617 model
->SetCheckedItem(guid
);
1618 if (GetAutofillChoice(section
, &guid
))
1619 model
->SetCheckedItem(guid
);
1624 view_
->ModelChanged();
1626 for (size_t i
= SECTION_MIN
; i
<= SECTION_MAX
; ++i
) {
1627 ResetSectionInput(static_cast<DialogSection
>(i
));
1630 FieldValueMap::const_iterator billing_it
=
1631 snapshot
.find(ADDRESS_BILLING_COUNTRY
);
1632 if (billing_it
!= snapshot
.end())
1633 RebuildInputsForCountry(ActiveBillingSection(), billing_it
->second
, true);
1635 FieldValueMap::const_iterator shipping_it
=
1636 snapshot
.find(ADDRESS_HOME_COUNTRY
);
1637 if (shipping_it
!= snapshot
.end())
1638 RebuildInputsForCountry(SECTION_SHIPPING
, shipping_it
->second
, true);
1640 RestoreUserInputFromSnapshot(snapshot
);
1642 for (size_t i
= SECTION_MIN
; i
<= SECTION_MAX
; ++i
) {
1643 DialogSection section
= static_cast<DialogSection
>(i
);
1644 if (!SectionIsActive(section
))
1647 ShowEditUiIfBadSuggestion(section
);
1648 UpdateSection(section
);
1654 void AutofillDialogControllerImpl::FillOutputForSectionWithComparator(
1655 DialogSection section
,
1656 const FormStructure::InputFieldComparator
& compare
) {
1657 if (!SectionIsActive(section
))
1660 DetailInputs inputs
;
1661 std::string country_code
= CountryCodeForSection(section
);
1662 BuildInputsForSection(section
, country_code
, &inputs
,
1663 MutableAddressLanguageCodeForSection(section
));
1664 std::vector
<ServerFieldType
> types
= TypesFromInputs(inputs
);
1666 scoped_ptr
<DataModelWrapper
> wrapper
= CreateWrapper(section
);
1668 // Only fill in data that is associated with this section.
1669 wrapper
->FillFormStructure(types
, compare
, &form_structure_
);
1671 // CVC needs special-casing because the CreditCard class doesn't store or
1672 // handle them. This isn't necessary when filling the combined CC and
1673 // billing section as CVC comes from |full_wallet_| in this case.
1674 if (section
== SECTION_CC
)
1675 SetOutputForFieldsOfType(CREDIT_CARD_VERIFICATION_CODE
, view_
->GetCvc());
1678 // The user manually input data. If using Autofill, save the info as new or
1679 // edited data. Always fill local data into |form_structure_|.
1680 FieldValueMap output
;
1681 view_
->GetUserInput(section
, &output
);
1683 if (section
== SECTION_CC
) {
1685 FillFormGroupFromOutputs(output
, &card
);
1687 // The card holder name comes from the billing address section.
1688 card
.SetRawInfo(CREDIT_CARD_NAME
,
1689 GetValueFromSection(SECTION_BILLING
, NAME_BILLING_FULL
));
1691 if (ShouldSaveDetailsLocally()) {
1692 card
.set_origin(kAutofillDialogOrigin
);
1694 std::string guid
= GetManager()->SaveImportedCreditCard(card
);
1695 newly_saved_data_model_guids_
[section
] = guid
;
1696 DCHECK(!profile()->IsOffTheRecord());
1697 newly_saved_card_
.reset(new CreditCard(card
));
1700 AutofillCreditCardWrapper
card_wrapper(&card
);
1701 card_wrapper
.FillFormStructure(types
, compare
, &form_structure_
);
1703 // Again, CVC needs special-casing. Fill it in directly from |output|.
1704 SetOutputForFieldsOfType(
1705 CREDIT_CARD_VERIFICATION_CODE
,
1706 output
[CREDIT_CARD_VERIFICATION_CODE
]);
1708 AutofillProfile profile
;
1709 FillFormGroupFromOutputs(output
, &profile
);
1710 profile
.set_language_code(AddressLanguageCodeForSection(section
));
1712 if (ShouldSaveDetailsLocally()) {
1713 profile
.set_origin(RulesAreLoaded(section
) ?
1714 kAutofillDialogOrigin
: source_url_
.GetOrigin().spec());
1716 std::string guid
= GetManager()->SaveImportedProfile(profile
);
1717 newly_saved_data_model_guids_
[section
] = guid
;
1720 AutofillProfileWrapper
profile_wrapper(&profile
);
1721 profile_wrapper
.FillFormStructure(types
, compare
, &form_structure_
);
1726 void AutofillDialogControllerImpl::FillOutputForSection(DialogSection section
) {
1727 FillOutputForSectionWithComparator(
1728 section
, base::Bind(ServerTypeMatchesField
, section
));
1731 bool AutofillDialogControllerImpl::FormStructureCaresAboutSection(
1732 DialogSection section
) const {
1733 // For now, only SECTION_SHIPPING may be omitted due to a site not asking for
1734 // any of the fields.
1735 if (section
== SECTION_SHIPPING
)
1736 return cares_about_shipping_
;
1741 void AutofillDialogControllerImpl::SetOutputForFieldsOfType(
1742 ServerFieldType type
,
1743 const base::string16
& output
) {
1744 for (size_t i
= 0; i
< form_structure_
.field_count(); ++i
) {
1745 AutofillField
* field
= form_structure_
.field(i
);
1746 if (field
->Type().GetStorableType() == type
)
1747 field
->value
= output
;
1751 base::string16
AutofillDialogControllerImpl::GetValueFromSection(
1752 DialogSection section
,
1753 ServerFieldType type
) {
1754 DCHECK(SectionIsActive(section
));
1756 scoped_ptr
<DataModelWrapper
> wrapper
= CreateWrapper(section
);
1758 return wrapper
->GetInfo(AutofillType(type
));
1760 FieldValueMap output
;
1761 view_
->GetUserInput(section
, &output
);
1762 return output
[type
];
1765 bool AutofillDialogControllerImpl::CanAcceptCountry(
1766 DialogSection section
,
1767 const std::string
& country_code
) {
1768 DCHECK_EQ(2U, country_code
.size());
1769 CountryComboboxModel
* model
= CountryComboboxModelForSection(section
);
1770 const std::vector
<AutofillCountry
*>& countries
= model
->countries();
1771 for (size_t i
= 0; i
< countries
.size(); ++i
) {
1772 if (countries
[i
] && countries
[i
]->country_code() == country_code
)
1779 bool AutofillDialogControllerImpl::ShouldSuggestProfile(
1780 DialogSection section
,
1781 const AutofillProfile
& profile
) {
1782 std::string country_code
=
1783 base::UTF16ToASCII(profile
.GetRawInfo(ADDRESS_HOME_COUNTRY
));
1784 return country_code
.empty() || CanAcceptCountry(section
, country_code
);
1787 SuggestionsMenuModel
* AutofillDialogControllerImpl::
1788 SuggestionsMenuModelForSection(DialogSection section
) {
1791 return &suggested_cc_
;
1792 case SECTION_BILLING
:
1793 return &suggested_billing_
;
1794 case SECTION_SHIPPING
:
1795 return &suggested_shipping_
;
1802 const SuggestionsMenuModel
* AutofillDialogControllerImpl::
1803 SuggestionsMenuModelForSection(DialogSection section
) const {
1804 return const_cast<AutofillDialogControllerImpl
*>(this)->
1805 SuggestionsMenuModelForSection(section
);
1808 DialogSection
AutofillDialogControllerImpl::SectionForSuggestionsMenuModel(
1809 const SuggestionsMenuModel
& model
) {
1810 if (&model
== &suggested_cc_
)
1813 if (&model
== &suggested_billing_
)
1814 return SECTION_BILLING
;
1816 DCHECK_EQ(&model
, &suggested_shipping_
);
1817 return SECTION_SHIPPING
;
1820 CountryComboboxModel
* AutofillDialogControllerImpl::
1821 CountryComboboxModelForSection(DialogSection section
) {
1822 if (section
== SECTION_BILLING
)
1823 return billing_country_combobox_model_
.get();
1825 if (section
== SECTION_SHIPPING
)
1826 return shipping_country_combobox_model_
.get();
1831 void AutofillDialogControllerImpl::GetI18nValidatorSuggestions(
1832 DialogSection section
,
1833 ServerFieldType type
,
1834 std::vector
<autofill::Suggestion
>* popup_suggestions
) {
1835 AddressField focused_field
;
1836 if (!i18n::FieldForType(type
, &focused_field
))
1839 FieldValueMap inputs
;
1840 view_
->GetUserInput(section
, &inputs
);
1842 AutofillProfile profile
;
1843 FillFormGroupFromOutputs(inputs
, &profile
);
1845 scoped_ptr
<AddressData
> user_input
=
1846 i18n::CreateAddressDataFromAutofillProfile(
1847 profile
, g_browser_process
->GetApplicationLocale());
1848 user_input
->language_code
= AddressLanguageCodeForSection(section
);
1850 static const size_t kSuggestionsLimit
= 10;
1851 AddressValidator::Status status
= GetValidator()->GetSuggestions(
1852 *user_input
, focused_field
, kSuggestionsLimit
,
1853 &i18n_validator_suggestions_
);
1855 if (status
!= AddressValidator::SUCCESS
)
1858 for (size_t i
= 0; i
< i18n_validator_suggestions_
.size(); ++i
) {
1859 popup_suggestions
->push_back(autofill::Suggestion(
1861 i18n_validator_suggestions_
[i
].GetFieldValue(focused_field
))));
1863 // Disambiguate the suggestion by showing the smallest administrative
1864 // region of the suggested address:
1865 // ADMIN_AREA > LOCALITY > DEPENDENT_LOCALITY
1866 for (int field
= DEPENDENT_LOCALITY
; field
>= ADMIN_AREA
; --field
) {
1867 const std::string
& field_value
=
1868 i18n_validator_suggestions_
[i
].GetFieldValue(
1869 static_cast<AddressField
>(field
));
1870 if (focused_field
!= field
&& !field_value
.empty()) {
1871 popup_suggestions
->back().label
= base::UTF8ToUTF16(field_value
);
1878 DetailInputs
* AutofillDialogControllerImpl::MutableRequestedFieldsForSection(
1879 DialogSection section
) {
1880 return const_cast<DetailInputs
*>(&RequestedFieldsForSection(section
));
1883 std::string
* AutofillDialogControllerImpl::MutableAddressLanguageCodeForSection(
1884 DialogSection section
) {
1886 case SECTION_BILLING
:
1887 return &billing_address_language_code_
;
1888 case SECTION_SHIPPING
:
1889 return &shipping_address_language_code_
;
1897 std::string
AutofillDialogControllerImpl::AddressLanguageCodeForSection(
1898 DialogSection section
) {
1899 std::string
* language_code
= MutableAddressLanguageCodeForSection(section
);
1900 return language_code
!= NULL
? *language_code
: std::string();
1903 std::vector
<ServerFieldType
> AutofillDialogControllerImpl::
1904 RequestedTypesForSection(DialogSection section
) const {
1905 return TypesFromInputs(RequestedFieldsForSection(section
));
1908 std::string
AutofillDialogControllerImpl::CountryCodeForSection(
1909 DialogSection section
) {
1910 base::string16 country
;
1912 scoped_ptr
<DataModelWrapper
> wrapper
= CreateWrapper(section
);
1914 country
= wrapper
->GetInfo(AutofillType(CountryTypeForSection(section
)));
1916 FieldValueMap outputs
;
1917 view_
->GetUserInput(section
, &outputs
);
1918 country
= outputs
[CountryTypeForSection(section
)];
1921 return AutofillCountry::GetCountryCode(
1922 country
, g_browser_process
->GetApplicationLocale());
1925 bool AutofillDialogControllerImpl::RebuildInputsForCountry(
1926 DialogSection section
,
1927 const base::string16
& country_name
,
1928 bool should_clobber
) {
1929 CountryComboboxModel
* model
= CountryComboboxModelForSection(section
);
1933 std::string country_code
= AutofillCountry::GetCountryCode(
1934 country_name
, g_browser_process
->GetApplicationLocale());
1935 DCHECK(CanAcceptCountry(section
, country_code
));
1937 if (view_
&& !should_clobber
) {
1938 FieldValueMap outputs
;
1939 view_
->GetUserInput(section
, &outputs
);
1941 // If |country_name| is the same as the view, no-op and let the caller know.
1942 if (outputs
[CountryTypeForSection(section
)] == country_name
)
1946 DetailInputs
* inputs
= MutableRequestedFieldsForSection(section
);
1948 BuildInputsForSection(section
, country_code
, inputs
,
1949 MutableAddressLanguageCodeForSection(section
));
1951 if (!country_code
.empty()) {
1952 GetValidator()->LoadRules(AutofillCountry::GetCountryCode(
1953 country_name
, g_browser_process
->GetApplicationLocale()));
1959 void AutofillDialogControllerImpl::HidePopup() {
1960 if (popup_controller_
)
1961 popup_controller_
->Hide();
1962 popup_input_type_
= UNKNOWN_TYPE
;
1965 void AutofillDialogControllerImpl::SetEditingExistingData(
1966 DialogSection section
, bool editing
) {
1968 section_editing_state_
.insert(section
);
1970 section_editing_state_
.erase(section
);
1973 bool AutofillDialogControllerImpl::IsASuggestionItemKey(
1974 const std::string
& key
) const {
1975 return !key
.empty() &&
1976 key
!= kAddNewItemKey
&&
1977 key
!= kManageItemsKey
&&
1978 key
!= kSameAsBillingKey
;
1981 bool AutofillDialogControllerImpl::IsAutofillEnabled() const {
1982 return profile_
->GetPrefs()->GetBoolean(prefs::kAutofillEnabled
);
1985 bool AutofillDialogControllerImpl::IsManuallyEditingAnySection() const {
1986 for (size_t section
= SECTION_MIN
; section
<= SECTION_MAX
; ++section
) {
1987 if (IsManuallyEditingSection(static_cast<DialogSection
>(section
)))
1993 base::string16
AutofillDialogControllerImpl::CreditCardNumberValidityMessage(
1994 const base::string16
& number
) const {
1995 if (!number
.empty() && !autofill::IsValidCreditCardNumber(number
)) {
1996 return l10n_util::GetStringUTF16(
1997 IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_CREDIT_CARD_NUMBER
);
2000 if (ShouldDisallowCcType(
2001 CreditCard::TypeForDisplay(CreditCard::GetCreditCardType(number
)))) {
2002 int ids
= IDS_AUTOFILL_DIALOG_VALIDATION_UNACCEPTED_GENERIC_CARD
;
2003 const char* const type
= CreditCard::GetCreditCardType(number
);
2004 if (type
== kAmericanExpressCard
)
2005 ids
= IDS_AUTOFILL_DIALOG_VALIDATION_UNACCEPTED_AMEX
;
2006 else if (type
== kDiscoverCard
)
2007 ids
= IDS_AUTOFILL_DIALOG_VALIDATION_UNACCEPTED_DISCOVER
;
2008 else if (type
== kMasterCard
)
2009 ids
= IDS_AUTOFILL_DIALOG_VALIDATION_UNACCEPTED_MASTERCARD
;
2010 else if (type
== kVisaCard
)
2011 ids
= IDS_AUTOFILL_DIALOG_VALIDATION_UNACCEPTED_VISA
;
2013 return l10n_util::GetStringUTF16(ids
);
2016 // Card number is good and supported.
2017 return base::string16();
2020 bool AutofillDialogControllerImpl::AllSectionsAreValid() {
2021 for (size_t section
= SECTION_MIN
; section
<= SECTION_MAX
; ++section
) {
2022 if (!SectionIsValid(static_cast<DialogSection
>(section
)))
2028 bool AutofillDialogControllerImpl::SectionIsValid(
2029 DialogSection section
) {
2030 if (!IsManuallyEditingSection(section
))
2033 FieldValueMap detail_outputs
;
2034 view_
->GetUserInput(section
, &detail_outputs
);
2035 return !InputsAreValid(section
, detail_outputs
).HasSureErrors();
2038 bool AutofillDialogControllerImpl::RulesAreLoaded(DialogSection section
) {
2039 AddressData address_data
;
2040 address_data
.region_code
= CountryCodeForSection(section
);
2041 AddressValidator::Status status
= GetValidator()->ValidateAddress(
2042 address_data
, NULL
, NULL
);
2043 return status
== AddressValidator::SUCCESS
;
2046 bool AutofillDialogControllerImpl::ShouldDisallowCcType(
2047 const base::string16
& type
) const {
2048 if (acceptable_cc_types_
.empty())
2051 if (acceptable_cc_types_
.find(base::i18n::ToUpper(type
)) ==
2052 acceptable_cc_types_
.end()) {
2059 bool AutofillDialogControllerImpl::HasInvalidAddress(
2060 const AutofillProfile
& profile
) {
2061 scoped_ptr
<AddressData
> address_data
=
2062 i18n::CreateAddressDataFromAutofillProfile(
2063 profile
, g_browser_process
->GetApplicationLocale());
2065 FieldProblemMap problems
;
2066 GetValidator()->ValidateAddress(*address_data
, NULL
, &problems
);
2067 return !problems
.empty();
2070 bool AutofillDialogControllerImpl::ShouldUseBillingForShipping() {
2071 return SectionIsActive(SECTION_SHIPPING
) &&
2072 suggested_shipping_
.GetItemKeyForCheckedItem() == kSameAsBillingKey
;
2075 bool AutofillDialogControllerImpl::ShouldSaveDetailsLocally() {
2076 // It's possible that the user checked [X] Save details locally before
2077 // switching payment methods, so only ask the view whether to save details
2078 // locally if that checkbox is showing (currently if not paying with wallet).
2079 // Also, if the user isn't editing any sections, there's no data to save
2081 return ShouldOfferToSaveInChrome() && view_
->SaveDetailsLocally();
2084 void AutofillDialogControllerImpl::FinishSubmit() {
2085 FillOutputForSection(SECTION_CC
);
2086 FillOutputForSection(SECTION_BILLING
);
2088 if (ShouldUseBillingForShipping()) {
2089 FillOutputForSectionWithComparator(
2091 base::Bind(ServerTypeMatchesShippingField
));
2092 FillOutputForSectionWithComparator(
2094 base::Bind(ServerTypeMatchesShippingField
));
2096 FillOutputForSection(SECTION_SHIPPING
);
2099 if (ShouldOfferToSaveInChrome()) {
2100 for (size_t i
= SECTION_MIN
; i
<= SECTION_MAX
; ++i
) {
2101 DialogSection section
= static_cast<DialogSection
>(i
);
2102 if (!SectionIsActive(section
))
2105 SuggestionsMenuModel
* model
= SuggestionsMenuModelForSection(section
);
2106 std::string item_key
= model
->GetItemKeyForCheckedItem();
2107 if (IsASuggestionItemKey(item_key
) || item_key
== kSameAsBillingKey
) {
2108 PersistAutofillChoice(section
, item_key
);
2109 } else if (item_key
== kAddNewItemKey
&& ShouldSaveDetailsLocally()) {
2110 DCHECK(newly_saved_data_model_guids_
.count(section
));
2111 PersistAutofillChoice(section
, newly_saved_data_model_guids_
[section
]);
2115 profile_
->GetPrefs()->SetBoolean(::prefs::kAutofillDialogSaveData
,
2116 view_
->SaveDetailsLocally());
2119 LogOnFinishSubmitMetrics();
2121 // Callback should be called as late as possible.
2122 callback_
.Run(AutofillClient::AutocompleteResultSuccess
,
2125 data_was_passed_back_
= true;
2127 // This might delete us.
2131 void AutofillDialogControllerImpl::OnAddressValidationRulesLoaded(
2132 const std::string
& country_code
,
2134 // Rules may load instantly (during initialization, before the view is
2135 // even ready). We'll validate when the view is created.
2139 ScopedViewUpdates
updates(view_
.get());
2141 // TODO(dbeam): should we retry on failure?
2142 for (size_t i
= SECTION_MIN
; i
<= SECTION_MAX
; ++i
) {
2143 DialogSection section
= static_cast<DialogSection
>(i
);
2144 if (!SectionIsActive(section
) ||
2145 CountryCodeForSection(section
) != country_code
) {
2149 if (IsManuallyEditingSection(section
) && needs_validation_
.count(section
)) {
2150 view_
->ValidateSection(section
);
2151 needs_validation_
.erase(section
);
2152 } else if (success
) {
2153 ShowEditUiIfBadSuggestion(section
);
2154 UpdateSection(section
);
2159 void AutofillDialogControllerImpl::PersistAutofillChoice(
2160 DialogSection section
,
2161 const std::string
& guid
) {
2162 DCHECK(ShouldOfferToSaveInChrome());
2163 scoped_ptr
<base::DictionaryValue
> value(new base::DictionaryValue());
2164 value
->SetString(kGuidPrefKey
, guid
);
2166 DictionaryPrefUpdate
updater(profile()->GetPrefs(),
2167 ::prefs::kAutofillDialogAutofillDefault
);
2168 base::DictionaryValue
* autofill_choice
= updater
.Get();
2169 autofill_choice
->Set(SectionToPrefString(section
), value
.release());
2172 void AutofillDialogControllerImpl::GetDefaultAutofillChoice(
2173 DialogSection section
,
2174 std::string
* guid
) {
2175 DCHECK(IsAutofillEnabled());
2176 // The default choice is the first thing in the menu that is a suggestion
2178 SuggestionsMenuModel
* model
= SuggestionsMenuModelForSection(section
);
2179 for (int i
= 0; i
< model
->GetItemCount(); ++i
) {
2180 // Try the first suggestion item that is enabled.
2181 if (IsASuggestionItemKey(model
->GetItemKeyAt(i
)) && model
->IsEnabledAt(i
)) {
2182 *guid
= model
->GetItemKeyAt(i
);
2184 // Fall back to the first non-suggestion key.
2185 } else if (!IsASuggestionItemKey(model
->GetItemKeyAt(i
)) && guid
->empty()) {
2186 *guid
= model
->GetItemKeyAt(i
);
2191 bool AutofillDialogControllerImpl::GetAutofillChoice(DialogSection section
,
2192 std::string
* guid
) {
2193 DCHECK(IsAutofillEnabled());
2194 const base::DictionaryValue
* choices
= profile()->GetPrefs()->GetDictionary(
2195 ::prefs::kAutofillDialogAutofillDefault
);
2199 const base::DictionaryValue
* choice
= NULL
;
2200 if (!choices
->GetDictionary(SectionToPrefString(section
), &choice
))
2203 choice
->GetString(kGuidPrefKey
, guid
);
2207 void AutofillDialogControllerImpl::LogOnFinishSubmitMetrics() {
2208 AutofillMetrics::LogDialogUiDuration(
2209 base::Time::Now() - dialog_shown_timestamp_
,
2210 AutofillMetrics::DIALOG_ACCEPTED
);
2212 AutofillMetrics::LogDialogUiEvent(AutofillMetrics::DIALOG_UI_ACCEPTED
);
2214 AutofillMetrics::DialogDismissalState dismissal_state
;
2215 if (!IsManuallyEditingAnySection()) {
2216 dismissal_state
= AutofillMetrics::DIALOG_ACCEPTED_EXISTING_AUTOFILL_DATA
;
2217 } else if (ShouldSaveDetailsLocally()) {
2218 dismissal_state
= AutofillMetrics::DIALOG_ACCEPTED_SAVE_TO_AUTOFILL
;
2220 dismissal_state
= AutofillMetrics::DIALOG_ACCEPTED_NO_SAVE
;
2223 AutofillMetrics::LogDialogDismissalState(dismissal_state
);
2226 void AutofillDialogControllerImpl::LogOnCancelMetrics() {
2227 AutofillMetrics::LogDialogUiEvent(AutofillMetrics::DIALOG_UI_CANCELED
);
2229 AutofillMetrics::DialogDismissalState dismissal_state
;
2230 if (!IsManuallyEditingAnySection())
2231 dismissal_state
= AutofillMetrics::DIALOG_CANCELED_NO_EDITS
;
2232 else if (AllSectionsAreValid())
2233 dismissal_state
= AutofillMetrics::DIALOG_CANCELED_NO_INVALID_FIELDS
;
2235 dismissal_state
= AutofillMetrics::DIALOG_CANCELED_WITH_INVALID_FIELDS
;
2237 AutofillMetrics::LogDialogDismissalState(dismissal_state
);
2239 AutofillMetrics::LogDialogUiDuration(
2240 base::Time::Now() - dialog_shown_timestamp_
,
2241 AutofillMetrics::DIALOG_CANCELED
);
2244 void AutofillDialogControllerImpl::LogSuggestionItemSelectedMetric(
2245 const SuggestionsMenuModel
& model
) {
2246 DialogSection section
= SectionForSuggestionsMenuModel(model
);
2248 AutofillMetrics::DialogUiEvent dialog_ui_event
;
2249 if (model
.GetItemKeyForCheckedItem() == kAddNewItemKey
) {
2250 // Selected to add a new item.
2251 dialog_ui_event
= common::DialogSectionToUiItemAddedEvent(section
);
2252 } else if (IsASuggestionItemKey(model
.GetItemKeyForCheckedItem())) {
2253 // Selected an existing item.
2254 dialog_ui_event
= common::DialogSectionToUiSelectionChangedEvent(section
);
2256 // TODO(estade): add logging for "Manage items" or "Use billing for
2261 AutofillMetrics::LogDialogUiEvent(dialog_ui_event
);
2264 void AutofillDialogControllerImpl::LogDialogLatencyToShow() {
2265 if (was_ui_latency_logged_
)
2268 AutofillMetrics::LogDialogLatencyToShow(base::Time::Now() -
2269 dialog_shown_timestamp_
);
2270 was_ui_latency_logged_
= true;
2273 AutofillMetrics::DialogInitialUserStateMetric
2274 AutofillDialogControllerImpl::GetInitialUserState() const {
2275 // Consider a user to be an Autofill user if the user has any credit cards
2276 // or addresses saved. Check that the item count is greater than 2 because
2277 // an "empty" menu still has the "add new" menu item and "manage" menu item.
2278 const bool has_autofill_profiles
=
2279 suggested_cc_
.GetItemCount() > 2 ||
2280 suggested_billing_
.GetItemCount() > 2;
2282 return has_autofill_profiles
2283 ? AutofillMetrics::DIALOG_USER_NOT_SIGNED_IN_HAS_AUTOFILL
2284 : AutofillMetrics::DIALOG_USER_NOT_SIGNED_IN_NO_AUTOFILL
;
2287 void AutofillDialogControllerImpl::MaybeShowCreditCardBubble() {
2288 if (!data_was_passed_back_
|| !newly_saved_card_
)
2291 scoped_ptr
<AutofillProfile
> billing_profile
;
2292 if (IsManuallyEditingSection(SECTION_BILLING
)) {
2293 // Scrape the view as the user's entering or updating information.
2294 FieldValueMap outputs
;
2295 view_
->GetUserInput(SECTION_BILLING
, &outputs
);
2296 billing_profile
.reset(new AutofillProfile
);
2297 FillFormGroupFromOutputs(outputs
, billing_profile
.get());
2298 billing_profile
->set_language_code(billing_address_language_code_
);
2300 // Just snag the currently suggested profile.
2301 std::string item_key
= SuggestionsMenuModelForSection(SECTION_BILLING
)
2302 ->GetItemKeyForCheckedItem();
2303 AutofillProfile
* profile
= GetManager()->GetProfileByGUID(item_key
);
2304 billing_profile
.reset(new AutofillProfile(*profile
));
2307 ShowNewCreditCardBubble(newly_saved_card_
.Pass(), billing_profile
.Pass());
2310 void AutofillDialogControllerImpl::OnSubmitButtonDelayEnd() {
2313 ScopedViewUpdates
updates(view_
.get());
2314 view_
->UpdateButtonStrip();
2317 } // namespace autofill