aura: Stop converting Rect to RectF implicitly.
[chromium-blink-merge.git] / chrome / browser / ui / autofill / autofill_dialog_controller_impl.cc
blob9553ad55f97d8bd8bf0e8aa38507381a3d4b30cd
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"
7 #include <algorithm>
8 #include <map>
9 #include <string>
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;
99 namespace autofill {
101 namespace {
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
108 // these values).
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
113 // time.
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 {
124 public:
125 explicit ScopedViewUpdates(AutofillDialogView* view) : view_(view) {
126 if (view_)
127 view_->UpdatesStarted();
130 ~ScopedViewUpdates() {
131 if (view_)
132 view_->UpdatesFinished();
135 private:
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);
154 base::string16 info;
155 FieldValueMap::const_iterator it = inputs.find(field_type);
156 if (it != inputs.end())
157 info = it->second;
159 if (!info.empty() && type.html_type() == HTML_TYPE_COUNTRY_CODE) {
160 info = base::ASCIIToUTF16(AutofillCountry::GetCountryCode(
161 info, g_browser_process->GetApplicationLocale()));
164 return info;
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
173 // usecase.
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),
187 iter->second,
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) {
196 switch (section) {
197 case SECTION_CC:
198 return "cc";
200 case SECTION_BILLING:
201 return "billing";
203 case SECTION_SHIPPING:
204 return "shipping";
207 NOTREACHED();
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(
241 country_name,
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
263 // be NULL.
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[] = {
271 { DetailInput::LONG,
272 CREDIT_CARD_NUMBER,
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),
283 1.5 },
286 const DetailInput kBillingPhoneInputs[] = {
287 { DetailInput::LONG,
288 PHONE_BILLING_WHOLE_NUMBER,
289 GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_PHONE_NUMBER) },
292 const DetailInput kEmailInputs[] = {
293 { DetailInput::LONG,
294 EMAIL_ADDRESS,
295 GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_EMAIL) },
298 const DetailInput kShippingPhoneInputs[] = {
299 { DetailInput::LONG,
300 PHONE_HOME_WHOLE_NUMBER,
301 GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_PHONE_NUMBER) },
304 switch (dialog_section) {
305 case SECTION_CC: {
306 BuildInputs(kCCInputs, arraysize(kCCInputs), inputs);
307 break;
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);
315 break;
318 case SECTION_SHIPPING: {
319 i18ninput::BuildAddressInputs(common::ADDRESS_TYPE_SHIPPING,
320 country_code, inputs, language_code);
321 BuildInputs(kShippingPhoneInputs, arraysize(kShippingPhoneInputs),
322 inputs);
323 break;
328 } // namespace
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))) {
345 return false;
348 return true;
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))) {
361 return false;
364 return true;
367 // static
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,
377 form_structure,
378 source_url,
379 callback);
380 return autofill_dialog_controller->weak_ptr_factory_.GetWeakPtr();
383 // static
384 void AutofillDialogController::RegisterPrefs(PrefRegistrySimple* registry) {
385 registry->RegisterListPref(::prefs::kAutofillDialogWalletLocationAcceptance);
388 // static
389 void AutofillDialogController::RegisterProfilePrefs(
390 user_prefs::PrefRegistrySyncable* registry) {
391 registry->RegisterBooleanPref(
392 ::prefs::kAutofillDialogPayWithoutWallet,
393 false,
394 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
395 registry->RegisterDictionaryPref(
396 ::prefs::kAutofillDialogAutofillDefault,
397 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
398 registry->RegisterBooleanPref(
399 ::prefs::kAutofillDialogSaveData,
400 true,
401 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
402 registry->RegisterBooleanPref(
403 ::prefs::kAutofillDialogWalletShippingSameAsBilling,
404 false,
405 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
408 // static
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,
415 form_structure,
416 source_url,
417 callback);
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.
430 if (!has_types) {
431 callback_.Run(
432 AutofillClient::AutocompleteResultErrorDisabled,
433 base::ASCIIToUTF16("Form is missing autocomplete attributes."),
434 NULL);
435 delete this;
436 return;
439 // Fail if the author didn't ask for at least some kind of credit card
440 // information.
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;
446 break;
450 if (!has_credit_card_field) {
451 callback_.Run(AutofillClient::AutocompleteResultErrorDisabled,
452 base::ASCIIToUTF16(
453 "Form is not a payment form (must contain "
454 "some autocomplete=\"cc-*\" fields). "),
455 NULL);
456 delete this;
457 return;
460 billing_country_combobox_model_.reset(new CountryComboboxModel());
461 billing_country_combobox_model_->SetCountries(
462 *GetManager(),
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(
469 *GetManager(),
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"
478 " found."),
479 NULL);
480 delete this;
481 return;
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
494 // access checks.
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);
509 if (model)
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(),
537 this));
539 SuggestionsUpdated();
540 SubmitButtonDelayBegin();
541 view_.reset(CreateView());
542 view_->Show();
543 GetManager()->AddObserver(this);
545 LogDialogLatencyToShow();
548 void AutofillDialogControllerImpl::Hide() {
549 if (view_)
550 view_->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);
603 return true;
606 bool AutofillDialogControllerImpl::SectionIsActive(DialogSection section)
607 const {
608 return FormStructureCaresAboutSection(section);
611 void AutofillDialogControllerImpl::ResetSectionInput(DialogSection section) {
612 SetEditingExistingData(section, false);
613 needs_validation_.erase(section);
615 CountryComboboxModel* model = CountryComboboxModelForSection(section);
616 if (model) {
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) {
665 if (value.empty())
666 return false;
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)
671 return false;
673 return true;
676 FieldValueMap AutofillDialogControllerImpl::TakeUserInputSnapshot() {
677 FieldValueMap snapshot;
678 if (!view_)
679 return 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)
685 continue;
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();
691 ++it) {
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));
699 return snapshot;
702 void AutofillDialogControllerImpl::RestoreUserInputFromSnapshot(
703 const FieldValueMap& snapshot) {
704 if (snapshot.empty())
705 return;
707 for (size_t i = SECTION_MIN; i <= SECTION_MAX; ++i) {
708 DialogSection section = static_cast<DialogSection>(i);
709 if (!SectionIsActive(section))
710 continue;
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) {
726 if (view_)
727 view_->UpdateSection(section);
730 void AutofillDialogControllerImpl::UpdateForErrors() {
731 if (!view_)
732 return;
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();
739 break;
744 const DetailInputs& AutofillDialogControllerImpl::RequestedFieldsForSection(
745 DialogSection section) const {
746 switch (section) {
747 case SECTION_CC:
748 return requested_cc_fields_;
749 case SECTION_BILLING:
750 return requested_billing_fields_;
751 case SECTION_SHIPPING:
752 return requested_shipping_fields_;
755 NOTREACHED();
756 return requested_billing_fields_;
759 ui::ComboboxModel* AutofillDialogControllerImpl::ComboboxModelForAutofillType(
760 ServerFieldType type) {
761 switch (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();
774 default:
775 return NULL;
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)
785 return model;
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)))
790 return model;
793 return NULL;
796 base::string16 AutofillDialogControllerImpl::LabelForSection(
797 DialogSection section) const {
798 switch (section) {
799 case SECTION_CC:
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);
806 NOTREACHED();
807 return base::string16();
810 SuggestionState AutofillDialogControllerImpl::SuggestionStateForSection(
811 DialogSection section) {
812 base::string16 vertically_compact, horizontally_compact;
813 bool show_suggestion = SuggestionTextForSection(section,
814 &vertically_compact,
815 &horizontally_compact);
816 return SuggestionState(show_suggestion,
817 vertically_compact,
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))
832 return false;
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);
839 return true;
842 if (!IsASuggestionItemKey(item_key))
843 return false;
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)))
849 return false;
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);
873 DCHECK(card);
874 return scoped_ptr<DataModelWrapper>(new AutofillCreditCardWrapper(card));
877 AutofillProfile* profile = GetManager()->GetProfileByGUID(item_key);
878 DCHECK(profile);
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);
891 if (!model.get())
892 return gfx::Image();
894 return model->GetIcon();
897 gfx::Image AutofillDialogControllerImpl::ExtraSuggestionIconForSection(
898 DialogSection section) {
899 if (section != SECTION_CC)
900 return gfx::Image();
902 scoped_ptr<DataModelWrapper> model = CreateWrapper(section);
903 if (!model.get())
904 return gfx::Image();
906 return CvcIconForCreditCardType(
907 model->GetInfo(AutofillType(CREDIT_CARD_TYPE)));
910 FieldIconMap AutofillDialogControllerImpl::IconsForFields(
911 const FieldValueMap& user_inputs) const {
912 FieldIconMap result;
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))
925 return result;
927 result[CREDIT_CARD_VERIFICATION_CODE] =
928 CvcIconForCreditCardType(credit_card_type);
930 return result;
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()) {
958 case EMAIL_ADDRESS:
959 if (!value.empty() && !IsValidEmailAddress(value)) {
960 return l10n_util::GetStringUTF16(
961 IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_EMAIL_ADDRESS);
963 break;
965 case CREDIT_CARD_NUMBER: {
966 if (!value.empty()) {
967 base::string16 message = CreditCardNumberValidityMessage(value);
968 if (!message.empty())
969 return message;
971 break;
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);
979 break;
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);
986 break;
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);
993 break;
995 case NAME_FULL:
996 break;
998 case PHONE_HOME_WHOLE_NUMBER: // Used in shipping section.
999 break;
1001 case PHONE_BILLING_WHOLE_NUMBER: // Used in billing section.
1002 break;
1004 default:
1005 NOTREACHED(); // Trying to validate unknown field.
1006 break;
1009 return value.empty() ? l10n_util::GetStringUTF16(
1010 IDS_LIBADDRESSINPUT_MISSING_REQUIRED_FIELD) :
1011 base::string16();
1014 // TODO(groby): Also add tests.
1015 ValidityMessages AutofillDialogControllerImpl::InputsAreValid(
1016 DialogSection section,
1017 const FieldValueMap& inputs) {
1018 ValidityMessages messages;
1019 if (inputs.empty())
1020 return 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
1054 // otherwise.
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);
1064 sure = false;
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)) {
1112 messages.Set(
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)) {
1121 messages.Set(
1122 PHONE_BILLING_WHOLE_NUMBER,
1123 GetPhoneValidityMessage(field_values[ADDRESS_BILLING_COUNTRY],
1124 field_values[PHONE_BILLING_WHOLE_NUMBER]));
1127 return messages;
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,
1136 bool was_edit) {
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())
1151 return;
1153 // If the field is edited down to empty, don't show a popup.
1154 if (was_edit && field_contents.empty()) {
1155 HidePopup();
1156 return;
1159 // If the user clicks while the popup is already showing, be sure to hide
1160 // it.
1161 if (!was_edit && popup_controller_.get()) {
1162 HidePopup();
1163 return;
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);
1173 } else {
1174 popup_suggestions = GetManager()->GetProfileSuggestions(
1175 AutofillType(type),
1176 field_contents,
1177 false,
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);
1185 i--;
1189 // Save the IDs.
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()) {
1199 HidePopup();
1200 return;
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
1209 // Form'?
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() {
1223 HidePopup();
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();
1237 delete this;
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() {
1259 HidePopup();
1260 if (!data_was_passed_back_)
1261 LogOnCancelMetrics();
1262 callback_.Run(
1263 AutofillClient::AutocompleteResultErrorCancel, base::string16(), NULL);
1266 void AutofillDialogControllerImpl::OnAccept() {
1267 ScopedViewUpdates updates(view_.get());
1268 HidePopup();
1269 FinishSubmit();
1272 Profile* AutofillDialogControllerImpl::profile() {
1273 return 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,
1294 int identifier) {
1295 // TODO(estade): implement.
1298 void AutofillDialogControllerImpl::DidAcceptSuggestion(
1299 const base::string16& value,
1300 int identifier,
1301 int position) {
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)));
1315 } else {
1316 wrapper.reset(new AutofillProfileWrapper(
1317 GetManager()->GetProfileByGUID(guid)));
1319 } else {
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.
1364 HidePopup();
1367 bool AutofillDialogControllerImpl::GetDeletionConfirmationText(
1368 const base::string16& value,
1369 int identifier,
1370 base::string16* title,
1371 base::string16* body) {
1372 return false;
1375 bool AutofillDialogControllerImpl::RemoveSuggestion(const base::string16& value,
1376 int identifier) {
1377 // TODO(estade): implement.
1378 return false;
1381 void AutofillDialogControllerImpl::ClearPreviewedForm() {
1382 // TODO(estade): implement.
1385 ////////////////////////////////////////////////////////////////////////////////
1386 // SuggestionsMenuModelDelegate implementation.
1388 void AutofillDialogControllerImpl::SuggestionItemSelected(
1389 SuggestionsMenuModel* model,
1390 size_t index) {
1391 ScopedViewUpdates updates(view_.get());
1393 if (model->GetItemKeyAt(index) == kManageItemsKey) {
1394 GURL url;
1395 DCHECK(IsAutofillEnabled());
1396 GURL settings_url(chrome::kChromeUISettingsURL);
1397 url = settings_url.Resolve(chrome::kAutofillSubPage);
1398 OpenTabWithUrl(url);
1399 return;
1402 model->SetCheckedIndex(index);
1403 DialogSection section = SectionForSuggestionsMenuModel(*model);
1405 ResetSectionInput(section);
1406 ShowEditUiIfBadSuggestion(section);
1407 UpdateSection(section);
1408 view_->UpdateNotificationArea();
1409 UpdateForErrors();
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);
1428 return false;
1431 void AutofillDialogControllerImpl::ShowNewCreditCardBubble(
1432 scoped_ptr<CreditCard> new_card,
1433 scoped_ptr<AutofillProfile> billing_profile) {
1434 NewCreditCardBubbleController::Show(web_contents(),
1435 new_card.Pass(),
1436 billing_profile.Pass());
1439 void AutofillDialogControllerImpl::SubmitButtonDelayBegin() {
1440 submit_button_delay_timer_.Start(
1441 FROM_HERE,
1442 base::TimeDelta::FromMilliseconds(kSubmitButtonDelayMs),
1443 this,
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()),
1492 url,
1493 ui::PAGE_TRANSITION_LINK);
1494 params.disposition = NEW_FOREGROUND_TAB;
1495 chrome::Navigate(&params);
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();
1523 HidePopup();
1525 suggested_shipping_.AddKeyedItem(
1526 kSameAsBillingKey,
1527 l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_USE_BILLING_FOR_SHIPPING));
1529 shipping_country_combobox_model_->SetCountries(
1530 *GetManager(),
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]))
1539 continue;
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())) {
1557 continue;
1560 suggested_shipping_.AddKeyedItem(profile.guid(), labels[i]);
1561 suggested_shipping_.SetEnabled(
1562 profile.guid(),
1563 CanAcceptCountry(
1564 SECTION_SHIPPING,
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(
1570 profile.guid(),
1571 CanAcceptCountry(
1572 SECTION_BILLING,
1573 base::UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_COUNTRY))));
1578 suggested_cc_.AddKeyedItem(
1579 kAddNewItemKey,
1580 l10n_util::GetStringUTF16(IsAutofillEnabled()
1581 ? IDS_AUTOFILL_DIALOG_ADD_CREDIT_CARD
1582 : IDS_AUTOFILL_DIALOG_ENTER_CREDIT_CARD));
1583 suggested_cc_.AddKeyedItem(
1584 kManageItemsKey,
1585 l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_MANAGE_CREDIT_CARD));
1586 suggested_billing_.AddKeyedItem(
1587 kAddNewItemKey,
1588 l10n_util::GetStringUTF16(
1589 IsAutofillEnabled() ? IDS_AUTOFILL_DIALOG_ADD_BILLING_ADDRESS
1590 : IDS_AUTOFILL_DIALOG_ENTER_BILLING_DETAILS));
1591 suggested_billing_.AddKeyedItem(
1592 kManageItemsKey,
1593 l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_MANAGE_BILLING_ADDRESS));
1595 suggested_shipping_.AddKeyedItem(
1596 kAddNewItemKey,
1597 l10n_util::GetStringUTF16(
1598 IsAutofillEnabled()
1599 ? IDS_AUTOFILL_DIALOG_ADD_SHIPPING_ADDRESS
1600 : IDS_AUTOFILL_DIALOG_USE_DIFFERENT_SHIPPING_ADDRESS));
1602 if (IsAutofillEnabled()) {
1603 suggested_shipping_.AddKeyedItem(
1604 kManageItemsKey,
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))
1610 continue;
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.
1614 std::string guid;
1615 GetDefaultAutofillChoice(section, &guid);
1616 SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section);
1617 model->SetCheckedItem(guid);
1618 if (GetAutofillChoice(section, &guid))
1619 model->SetCheckedItem(guid);
1623 if (view_)
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))
1645 continue;
1647 ShowEditUiIfBadSuggestion(section);
1648 UpdateSection(section);
1651 UpdateForErrors();
1654 void AutofillDialogControllerImpl::FillOutputForSectionWithComparator(
1655 DialogSection section,
1656 const FormStructure::InputFieldComparator& compare) {
1657 if (!SectionIsActive(section))
1658 return;
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);
1667 if (wrapper) {
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());
1677 } else {
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) {
1684 CreditCard card;
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]);
1707 } else {
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_;
1738 return true;
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);
1757 if (wrapper)
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)
1773 return true;
1776 return false;
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) {
1789 switch (section) {
1790 case SECTION_CC:
1791 return &suggested_cc_;
1792 case SECTION_BILLING:
1793 return &suggested_billing_;
1794 case SECTION_SHIPPING:
1795 return &suggested_shipping_;
1798 NOTREACHED();
1799 return NULL;
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_)
1811 return SECTION_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();
1828 return NULL;
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))
1837 return;
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)
1856 return;
1858 for (size_t i = 0; i < i18n_validator_suggestions_.size(); ++i) {
1859 popup_suggestions->push_back(autofill::Suggestion(
1860 base::UTF8ToUTF16(
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);
1872 break;
1878 DetailInputs* AutofillDialogControllerImpl::MutableRequestedFieldsForSection(
1879 DialogSection section) {
1880 return const_cast<DetailInputs*>(&RequestedFieldsForSection(section));
1883 std::string* AutofillDialogControllerImpl::MutableAddressLanguageCodeForSection(
1884 DialogSection section) {
1885 switch (section) {
1886 case SECTION_BILLING:
1887 return &billing_address_language_code_;
1888 case SECTION_SHIPPING:
1889 return &shipping_address_language_code_;
1890 case SECTION_CC:
1891 return NULL;
1893 NOTREACHED();
1894 return NULL;
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);
1913 if (wrapper) {
1914 country = wrapper->GetInfo(AutofillType(CountryTypeForSection(section)));
1915 } else {
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);
1930 if (!model)
1931 return false;
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)
1943 return false;
1946 DetailInputs* inputs = MutableRequestedFieldsForSection(section);
1947 inputs->clear();
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()));
1956 return true;
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) {
1967 if (editing)
1968 section_editing_state_.insert(section);
1969 else
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)))
1988 return true;
1990 return false;
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)))
2023 return false;
2025 return true;
2028 bool AutofillDialogControllerImpl::SectionIsValid(
2029 DialogSection section) {
2030 if (!IsManuallyEditingSection(section))
2031 return true;
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())
2049 return false;
2051 if (acceptable_cc_types_.find(base::i18n::ToUpper(type)) ==
2052 acceptable_cc_types_.end()) {
2053 return true;
2056 return false;
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
2080 // locally.
2081 return ShouldOfferToSaveInChrome() && view_->SaveDetailsLocally();
2084 void AutofillDialogControllerImpl::FinishSubmit() {
2085 FillOutputForSection(SECTION_CC);
2086 FillOutputForSection(SECTION_BILLING);
2088 if (ShouldUseBillingForShipping()) {
2089 FillOutputForSectionWithComparator(
2090 SECTION_BILLING,
2091 base::Bind(ServerTypeMatchesShippingField));
2092 FillOutputForSectionWithComparator(
2093 SECTION_CC,
2094 base::Bind(ServerTypeMatchesShippingField));
2095 } else {
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))
2103 continue;
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,
2123 base::string16(),
2124 &form_structure_);
2125 data_was_passed_back_ = true;
2127 // This might delete us.
2128 Hide();
2131 void AutofillDialogControllerImpl::OnAddressValidationRulesLoaded(
2132 const std::string& country_code,
2133 bool success) {
2134 // Rules may load instantly (during initialization, before the view is
2135 // even ready). We'll validate when the view is created.
2136 if (!view_)
2137 return;
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) {
2146 continue;
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
2177 // item.
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);
2183 return;
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);
2196 if (!choices)
2197 return false;
2199 const base::DictionaryValue* choice = NULL;
2200 if (!choices->GetDictionary(SectionToPrefString(section), &choice))
2201 return false;
2203 choice->GetString(kGuidPrefKey, guid);
2204 return true;
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;
2219 } else {
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;
2234 else
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);
2255 } else {
2256 // TODO(estade): add logging for "Manage items" or "Use billing for
2257 // shipping"?
2258 return;
2261 AutofillMetrics::LogDialogUiEvent(dialog_ui_event);
2264 void AutofillDialogControllerImpl::LogDialogLatencyToShow() {
2265 if (was_ui_latency_logged_)
2266 return;
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_)
2289 return;
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_);
2299 } else {
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() {
2311 if (!view_)
2312 return;
2313 ScopedViewUpdates updates(view_.get());
2314 view_->UpdateButtonStrip();
2317 } // namespace autofill