Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(25)

Side by Side Diff: chrome/browser/ui/autofill/autofill_dialog_controller_impl.cc

Issue 1931043002: Remove requestAutocomplete (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: rebase Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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.
4
5 #include "chrome/browser/ui/autofill/autofill_dialog_controller_impl.h"
6
7 #include <algorithm>
8 #include <map>
9 #include <string>
10 #include <utility>
11
12 #include "base/bind.h"
13 #include "base/bind_helpers.h"
14 #include "base/i18n/case_conversion.h"
15 #include "base/i18n/rtl.h"
16 #include "base/location.h"
17 #include "base/logging.h"
18 #include "base/macros.h"
19 #include "base/rand_util.h"
20 #include "base/single_thread_task_runner.h"
21 #include "base/strings/string_number_conversions.h"
22 #include "base/strings/string_split.h"
23 #include "base/strings/stringprintf.h"
24 #include "base/strings/utf_string_conversions.h"
25 #include "base/thread_task_runner_handle.h"
26 #include "base/time/time.h"
27 #include "chrome/browser/autofill/personal_data_manager_factory.h"
28 #include "chrome/browser/autofill/risk_util.h"
29 #include "chrome/browser/autofill/validation_rules_storage_factory.h"
30 #include "chrome/browser/browser_process.h"
31 #include "chrome/browser/profiles/profile.h"
32 #include "chrome/browser/ui/autofill/autofill_dialog_common.h"
33 #include "chrome/browser/ui/autofill/autofill_dialog_i18n_input.h"
34 #include "chrome/browser/ui/autofill/autofill_dialog_view.h"
35 #include "chrome/browser/ui/autofill/data_model_wrapper.h"
36 #include "chrome/browser/ui/browser.h"
37 #include "chrome/browser/ui/browser_finder.h"
38 #include "chrome/browser/ui/browser_navigator.h"
39 #include "chrome/browser/ui/browser_navigator_params.h"
40 #include "chrome/browser/ui/browser_window.h"
41 #include "chrome/common/pref_names.h"
42 #include "chrome/common/url_constants.h"
43 #include "chrome/grit/chromium_strings.h"
44 #include "chrome/grit/generated_resources.h"
45 #include "components/autofill/core/browser/address_i18n.h"
46 #include "components/autofill/core/browser/autofill_country.h"
47 #include "components/autofill/core/browser/autofill_data_model.h"
48 #include "components/autofill/core/browser/autofill_manager.h"
49 #include "components/autofill/core/browser/autofill_type.h"
50 #include "components/autofill/core/browser/country_names.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 "components/prefs/pref_registry_simple.h"
61 #include "components/prefs/pref_service.h"
62 #include "components/prefs/scoped_user_pref_update.h"
63 #include "content/public/browser/browser_thread.h"
64 #include "content/public/browser/geolocation_provider.h"
65 #include "content/public/browser/navigation_controller.h"
66 #include "content/public/browser/navigation_details.h"
67 #include "content/public/browser/navigation_entry.h"
68 #include "content/public/browser/notification_service.h"
69 #include "content/public/browser/notification_types.h"
70 #include "content/public/browser/render_view_host.h"
71 #include "content/public/browser/web_contents.h"
72 #include "content/public/common/url_constants.h"
73 #include "grit/components_scaled_resources.h"
74 #include "grit/components_strings.h"
75 #include "grit/platform_locale_settings.h"
76 #include "grit/theme_resources.h"
77 #include "net/cert/cert_status_flags.h"
78 #include "third_party/libaddressinput/chromium/chrome_metadata_source.h"
79 #include "third_party/libaddressinput/chromium/chrome_storage_impl.h"
80 #include "third_party/libaddressinput/messages.h"
81 #include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_da ta.h"
82 #include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_fi eld.h"
83 #include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_pr oblem.h"
84 #include "third_party/libaddressinput/src/cpp/include/libaddressinput/localizati on.h"
85 #include "ui/base/l10n/l10n_util.h"
86 #include "ui/base/models/combobox_model.h"
87 #include "ui/base/resource/resource_bundle.h"
88 #include "ui/gfx/canvas.h"
89 #include "ui/gfx/geometry/vector2d.h"
90 #include "ui/gfx/image/image_skia_operations.h"
91 #include "ui/gfx/skia_util.h"
92
93 using ::i18n::addressinput::AddressData;
94 using ::i18n::addressinput::AddressField;
95 using ::i18n::addressinput::AddressProblem;
96 using ::i18n::addressinput::ADMIN_AREA;
97 using ::i18n::addressinput::DEPENDENT_LOCALITY;
98 using ::i18n::addressinput::FieldProblemMap;
99 using ::i18n::addressinput::Localization;
100 using ::i18n::addressinput::MISSING_REQUIRED_FIELD;
101
102 namespace autofill {
103
104 namespace {
105
106 const char kAddNewItemKey[] = "add-new-item";
107 const char kManageItemsKey[] = "manage-items";
108 const char kSameAsBillingKey[] = "same-as-billing";
109
110 // Keys for the kAutofillDialogAutofillDefault pref dictionary (do not change
111 // these values).
112 const char kGuidPrefKey[] = "guid";
113
114 // This string is stored along with saved addresses and credit cards in the
115 // WebDB, and hence should not be modified, so that it remains consistent over
116 // time.
117 const char kAutofillDialogOrigin[] = "Chrome Autofill dialog";
118
119 // The number of milliseconds to delay enabling the submit button after showing
120 // the dialog. This delay prevents users from accidentally clicking the submit
121 // button on startup.
122 const int kSubmitButtonDelayMs = 1000;
123
124 // A helper class to make sure an AutofillDialogView knows when a series of
125 // updates is incoming.
126 class ScopedViewUpdates {
127 public:
128 explicit ScopedViewUpdates(AutofillDialogView* view) : view_(view) {
129 if (view_)
130 view_->UpdatesStarted();
131 }
132
133 ~ScopedViewUpdates() {
134 if (view_)
135 view_->UpdatesFinished();
136 }
137
138 private:
139 AutofillDialogView* view_;
140
141 DISALLOW_COPY_AND_ASSIGN(ScopedViewUpdates);
142 };
143
144 base::string16 NullGetInfo(const AutofillType& type) {
145 return base::string16();
146 }
147
148 // Extract |type| from |inputs| using |section| to determine whether the info
149 // should be billing or shipping specific (for sections with address info).
150 base::string16 GetInfoFromInputs(const FieldValueMap& inputs,
151 DialogSection section,
152 const AutofillType& type) {
153 ServerFieldType field_type = type.GetStorableType();
154 if (section != SECTION_SHIPPING)
155 field_type = AutofillType::GetEquivalentBillingFieldType(field_type);
156
157 base::string16 info;
158 FieldValueMap::const_iterator it = inputs.find(field_type);
159 if (it != inputs.end())
160 info = it->second;
161
162 if (!info.empty() && type.html_type() == HTML_TYPE_COUNTRY_CODE) {
163 info =
164 base::ASCIIToUTF16(CountryNames::GetInstance()->GetCountryCode(info));
165 }
166
167 return info;
168 }
169
170 // Returns true if |input| should be used to fill a site-requested |field| which
171 // is notated with a "shipping" tag, for use when the user has decided to use
172 // the billing address as the shipping address.
173 bool ServerTypeMatchesShippingField(ServerFieldType type,
174 const AutofillField& field) {
175 // Equivalent billing field type is used to support UseBillingAsShipping
176 // usecase.
177 return ServerTypeEncompassesFieldType(
178 type, AutofillType(AutofillType::GetEquivalentBillingFieldType(
179 field.Type().GetStorableType())));
180 }
181
182 // Initializes |form_group| from user-entered data.
183 void FillFormGroupFromOutputs(const FieldValueMap& detail_outputs,
184 FormGroup* form_group) {
185 for (FieldValueMap::const_iterator iter = detail_outputs.begin();
186 iter != detail_outputs.end(); ++iter) {
187 ServerFieldType type = iter->first;
188 if (!iter->second.empty()) {
189 form_group->SetInfo(AutofillType(type),
190 iter->second,
191 g_browser_process->GetApplicationLocale());
192 }
193 }
194 }
195
196 // Returns a string descriptor for a DialogSection, for use with prefs (do not
197 // change these values).
198 std::string SectionToPrefString(DialogSection section) {
199 switch (section) {
200 case SECTION_CC:
201 return "cc";
202
203 case SECTION_BILLING:
204 return "billing";
205
206 case SECTION_SHIPPING:
207 return "shipping";
208 }
209
210 NOTREACHED();
211 return std::string();
212 }
213
214 gfx::Image CreditCardIconForType(const std::string& credit_card_type) {
215 const int input_card_idr = CreditCard::IconResourceId(credit_card_type);
216 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
217 if (input_card_idr == IDR_AUTOFILL_CC_GENERIC) {
218 // When the credit card type is unknown, no image should be shown. However,
219 // to simplify the view code on Mac, save space for the credit card image by
220 // returning a transparent image of the appropriate size. All credit card
221 // icons are the same size.
222 return gfx::Image(gfx::ImageSkiaOperations::CreateTransparentImage(
223 rb.GetImageNamed(IDR_AUTOFILL_CC_GENERIC).AsImageSkia(), 0));
224 }
225 return rb.GetImageNamed(input_card_idr);
226 }
227
228 gfx::Image CvcIconForCreditCardType(const base::string16& credit_card_type) {
229 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
230 if (credit_card_type == l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_AMEX))
231 return rb.GetImageNamed(IDR_CREDIT_CARD_CVC_HINT_AMEX);
232
233 return rb.GetImageNamed(IDR_CREDIT_CARD_CVC_HINT);
234 }
235
236 ServerFieldType CountryTypeForSection(DialogSection section) {
237 return section == SECTION_SHIPPING ? ADDRESS_HOME_COUNTRY :
238 ADDRESS_BILLING_COUNTRY;
239 }
240
241 ValidityMessage GetPhoneValidityMessage(const base::string16& country_name,
242 const base::string16& number) {
243 std::string region =
244 CountryNames::GetInstance()->GetCountryCode(country_name);
245 i18n::PhoneObject phone_object(number, region);
246 ValidityMessage phone_message(base::string16(), true);
247
248 // Check if the phone number is invalid. Allow valid international
249 // numbers that don't match the address's country only if they have an
250 // international calling code.
251 if (!phone_object.IsValidNumber() ||
252 (phone_object.country_code().empty() &&
253 phone_object.region() != region)) {
254 phone_message.text = l10n_util::GetStringUTF16(
255 IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_PHONE_NUMBER);
256 }
257
258 return phone_message;
259 }
260
261 // Constructs |inputs| from template data for a given |dialog_section|.
262 // |country_country| specifies the country code that the inputs should be built
263 // for. Sets the |language_code| to be used for address formatting, if
264 // internationalized address input is enabled. The |language_code| parameter can
265 // be NULL.
266 void BuildInputsForSection(DialogSection dialog_section,
267 const std::string& country_code,
268 DetailInputs* inputs,
269 std::string* language_code) {
270 using l10n_util::GetStringUTF16;
271
272 const DetailInput kCCInputs[] = {
273 { DetailInput::LONG,
274 CREDIT_CARD_NUMBER,
275 GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_CARD_NUMBER) },
276 { DetailInput::SHORT,
277 CREDIT_CARD_EXP_MONTH,
278 GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_EXPIRY_MONTH) },
279 { DetailInput::SHORT,
280 CREDIT_CARD_EXP_4_DIGIT_YEAR,
281 GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_EXPIRY_YEAR) },
282 { DetailInput::SHORT_EOL,
283 CREDIT_CARD_VERIFICATION_CODE,
284 GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_CVC),
285 1.5 },
286 };
287
288 const DetailInput kBillingPhoneInputs[] = {
289 { DetailInput::LONG,
290 PHONE_BILLING_WHOLE_NUMBER,
291 GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_PHONE_NUMBER) },
292 };
293
294 const DetailInput kEmailInputs[] = {
295 { DetailInput::LONG,
296 EMAIL_ADDRESS,
297 GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_EMAIL) },
298 };
299
300 const DetailInput kShippingPhoneInputs[] = {
301 { DetailInput::LONG,
302 PHONE_HOME_WHOLE_NUMBER,
303 GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_PHONE_NUMBER) },
304 };
305
306 switch (dialog_section) {
307 case SECTION_CC: {
308 BuildInputs(kCCInputs, arraysize(kCCInputs), inputs);
309 break;
310 }
311
312 case SECTION_BILLING: {
313 i18ninput::BuildAddressInputs(common::ADDRESS_TYPE_BILLING,
314 country_code, inputs, language_code);
315 BuildInputs(kBillingPhoneInputs, arraysize(kBillingPhoneInputs), inputs);
316 BuildInputs(kEmailInputs, arraysize(kEmailInputs), inputs);
317 break;
318 }
319
320 case SECTION_SHIPPING: {
321 i18ninput::BuildAddressInputs(common::ADDRESS_TYPE_SHIPPING,
322 country_code, inputs, language_code);
323 BuildInputs(kShippingPhoneInputs, arraysize(kShippingPhoneInputs),
324 inputs);
325 break;
326 }
327 }
328 }
329
330 } // namespace
331
332 AutofillDialogViewDelegate::~AutofillDialogViewDelegate() {}
333
334 AutofillDialogControllerImpl::~AutofillDialogControllerImpl() {
335 if (popup_controller_)
336 popup_controller_->Hide();
337
338 AutofillMetrics::LogDialogInitialUserState(initial_user_state_);
339 }
340
341 // Checks the country code against the values the form structure enumerates.
342 bool AutofillCountryFilter(
343 const std::set<base::string16>& form_structure_values,
344 const std::string& country_code) {
345 if (!form_structure_values.empty() &&
346 !form_structure_values.count(base::ASCIIToUTF16(country_code))) {
347 return false;
348 }
349
350 return true;
351 }
352
353 // Checks the country code against the values the form structure enumerates and
354 // against the ones Wallet allows.
355 bool WalletCountryFilter(
356 const std::set<base::string16>& form_structure_values,
357 const std::set<std::string>& wallet_allowed_values,
358 const std::string& country_code) {
359 if ((!form_structure_values.empty() &&
360 !form_structure_values.count(base::ASCIIToUTF16(country_code))) ||
361 (!wallet_allowed_values.empty() &&
362 !wallet_allowed_values.count(country_code))) {
363 return false;
364 }
365
366 return true;
367 }
368
369 // static
370 base::WeakPtr<AutofillDialogControllerImpl>
371 AutofillDialogControllerImpl::Create(
372 content::WebContents* contents,
373 const FormData& form_structure,
374 const GURL& source_url,
375 const AutofillClient::ResultCallback& callback) {
376 // AutofillDialogControllerImpl owns itself.
377 AutofillDialogControllerImpl* autofill_dialog_controller =
378 new AutofillDialogControllerImpl(contents,
379 form_structure,
380 source_url,
381 callback);
382 return autofill_dialog_controller->weak_ptr_factory_.GetWeakPtr();
383 }
384
385 // static
386 void AutofillDialogController::RegisterPrefs(PrefRegistrySimple* registry) {
387 registry->RegisterListPref(::prefs::kAutofillDialogWalletLocationAcceptance);
388 }
389
390 // static
391 void AutofillDialogController::RegisterProfilePrefs(
392 user_prefs::PrefRegistrySyncable* registry) {
393 registry->RegisterBooleanPref(
394 ::prefs::kAutofillDialogPayWithoutWallet,
395 false,
396 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
397 registry->RegisterDictionaryPref(
398 ::prefs::kAutofillDialogAutofillDefault,
399 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
400 registry->RegisterBooleanPref(
401 ::prefs::kAutofillDialogSaveData,
402 true,
403 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
404 registry->RegisterBooleanPref(
405 ::prefs::kAutofillDialogWalletShippingSameAsBilling,
406 false,
407 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
408 }
409
410 // static
411 base::WeakPtr<AutofillDialogController> AutofillDialogController::Create(
412 content::WebContents* contents,
413 const FormData& form_structure,
414 const GURL& source_url,
415 const AutofillClient::ResultCallback& callback) {
416 return AutofillDialogControllerImpl::Create(contents,
417 form_structure,
418 source_url,
419 callback);
420 }
421
422 void AutofillDialogControllerImpl::Show() {
423 dialog_shown_timestamp_ = base::Time::Now();
424
425 // Determine what field types should be included in the dialog.
426 form_structure_.ParseFieldTypesFromAutocompleteAttributes();
427
428 // Fail if the author didn't specify autocomplete types.
429 if (!form_structure_.has_author_specified_types()) {
430 callback_.Run(
431 AutofillClient::AutocompleteResultErrorDisabled,
432 base::ASCIIToUTF16("Form is missing autocomplete attributes."),
433 NULL);
434 delete this;
435 return;
436 }
437
438 // Fail if the author didn't ask for at least some kind of credit card
439 // information.
440 bool has_credit_card_field = false;
441 for (size_t i = 0; i < form_structure_.field_count(); ++i) {
442 AutofillType type = form_structure_.field(i)->Type();
443 if (type.html_type() != HTML_TYPE_UNSPECIFIED &&
444 type.group() == CREDIT_CARD) {
445 has_credit_card_field = true;
446 break;
447 }
448 }
449
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;
458 }
459
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_)));
472
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;
482 }
483
484 // Log any relevant UI metrics and security exceptions.
485 AutofillMetrics::LogDialogUiEvent(AutofillMetrics::DIALOG_UI_SHOWN);
486
487 AutofillMetrics::LogDialogSecurityMetric(
488 AutofillMetrics::SECURITY_METRIC_DIALOG_SHOWN);
489
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();
498
499 if (!invoked_from_same_origin_) {
500 AutofillMetrics::LogDialogSecurityMetric(
501 AutofillMetrics::SECURITY_METRIC_CROSS_ORIGIN_FRAME);
502 }
503
504 for (size_t i = SECTION_MIN; i <= SECTION_MAX; ++i) {
505 DialogSection section = static_cast<DialogSection>(i);
506
507 std::string country_code;
508 CountryComboboxModel* model = CountryComboboxModelForSection(section);
509 if (model)
510 country_code = model->GetDefaultCountryCode();
511
512 DetailInputs* inputs = MutableRequestedFieldsForSection(section);
513 BuildInputsForSection(
514 section, country_code, inputs,
515 MutableAddressLanguageCodeForSection(section));
516 }
517
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());
525
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);
531
532 validator_.reset(new AddressValidator(
533 std::unique_ptr<::i18n::addressinput::Source>(
534 new autofill::ChromeMetadataSource(I18N_ADDRESS_VALIDATION_DATA_URL,
535 profile_->GetRequestContext())),
536 ValidationRulesStorageFactory::CreateStorage(), this));
537
538 SuggestionsUpdated();
539 SubmitButtonDelayBegin();
540 view_.reset(CreateView());
541 view_->Show();
542 GetManager()->AddObserver(this);
543
544 LogDialogLatencyToShow();
545 }
546
547 void AutofillDialogControllerImpl::Hide() {
548 if (view_)
549 view_->Hide();
550 }
551
552 // TODO(estade): remove.
553 void AutofillDialogControllerImpl::TabActivated() {
554 }
555
556 ////////////////////////////////////////////////////////////////////////////////
557 // AutofillDialogViewDelegate implementation.
558
559 base::string16 AutofillDialogControllerImpl::DialogTitle() const {
560 return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_TITLE);
561 }
562
563 base::string16 AutofillDialogControllerImpl::EditSuggestionText() const {
564 return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_EDIT);
565 }
566
567 base::string16 AutofillDialogControllerImpl::CancelButtonText() const {
568 return l10n_util::GetStringUTF16(IDS_CANCEL);
569 }
570
571 base::string16 AutofillDialogControllerImpl::ConfirmButtonText() const {
572 return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SUBMIT_BUTTON);
573 }
574
575 base::string16 AutofillDialogControllerImpl::SaveLocallyText() const {
576 return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SAVE_LOCALLY_CHECKBOX);
577 }
578
579 base::string16 AutofillDialogControllerImpl::SaveLocallyTooltip() const {
580 return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SAVE_LOCALLY_TOOLTIP);
581 }
582
583 bool AutofillDialogControllerImpl::ShouldOfferToSaveInChrome() const {
584 return IsAutofillEnabled() && !profile_->IsOffTheRecord() &&
585 IsManuallyEditingAnySection();
586 }
587
588 bool AutofillDialogControllerImpl::ShouldSaveInChrome() const {
589 return profile_->GetPrefs()->GetBoolean(::prefs::kAutofillDialogSaveData);
590 }
591
592 int AutofillDialogControllerImpl::GetDialogButtons() const {
593 return ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL;
594 }
595
596 bool AutofillDialogControllerImpl::IsDialogButtonEnabled(
597 ui::DialogButton button) const {
598 if (button == ui::DIALOG_BUTTON_OK)
599 return !submit_button_delay_timer_.IsRunning();
600
601 DCHECK_EQ(ui::DIALOG_BUTTON_CANCEL, button);
602 return true;
603 }
604
605 bool AutofillDialogControllerImpl::SectionIsActive(DialogSection section)
606 const {
607 return FormStructureCaresAboutSection(section);
608 }
609
610 void AutofillDialogControllerImpl::ResetSectionInput(DialogSection section) {
611 SetEditingExistingData(section, false);
612 needs_validation_.erase(section);
613
614 CountryComboboxModel* model = CountryComboboxModelForSection(section);
615 if (model) {
616 base::string16 country = model->GetItemAt(model->GetDefaultIndex());
617 RebuildInputsForCountry(section, country, false);
618 }
619
620 DetailInputs* inputs = MutableRequestedFieldsForSection(section);
621 for (DetailInputs::iterator it = inputs->begin();
622 it != inputs->end(); ++it) {
623 if (it->length != DetailInput::NONE) {
624 it->initial_value.clear();
625 } else if (!it->initial_value.empty() &&
626 (it->type == ADDRESS_BILLING_COUNTRY ||
627 it->type == ADDRESS_HOME_COUNTRY)) {
628 GetValidator()->LoadRules(
629 CountryNames::GetInstance()->GetCountryCode(it->initial_value));
630 }
631 }
632 }
633
634 void AutofillDialogControllerImpl::ShowEditUiIfBadSuggestion(
635 DialogSection section) {
636 // |CreateWrapper()| returns an empty wrapper if |IsEditingExistingData()|, so
637 // get the wrapper before this potentially happens below.
638 std::unique_ptr<DataModelWrapper> wrapper = CreateWrapper(section);
639
640 // If the chosen item in |model| yields an empty suggestion text, it is
641 // invalid. In this case, show the edit UI and highlight invalid fields.
642 SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section);
643 base::string16 unused, unused2;
644 if (IsASuggestionItemKey(model->GetItemKeyForCheckedItem()) &&
645 !SuggestionTextForSection(section, &unused, &unused2)) {
646 SetEditingExistingData(section, true);
647 }
648
649 if (wrapper && IsEditingExistingData(section)) {
650 base::string16 country =
651 wrapper->GetInfo(AutofillType(CountryTypeForSection(section)));
652 if (!country.empty()) {
653 // There's no user input to restore here as this is only called after
654 // resetting all section input.
655 if (RebuildInputsForCountry(section, country, false))
656 UpdateSection(section);
657 }
658 wrapper->FillInputs(MutableRequestedFieldsForSection(section));
659 }
660 }
661
662 bool AutofillDialogControllerImpl::InputWasEdited(ServerFieldType type,
663 const base::string16& value) {
664 if (value.empty())
665 return false;
666
667 // If this is a combobox at the default value, don't preserve it.
668 ui::ComboboxModel* model = ComboboxModelForAutofillType(type);
669 if (model && model->GetItemAt(model->GetDefaultIndex()) == value)
670 return false;
671
672 return true;
673 }
674
675 FieldValueMap AutofillDialogControllerImpl::TakeUserInputSnapshot() {
676 FieldValueMap snapshot;
677 if (!view_)
678 return snapshot;
679
680 for (size_t i = SECTION_MIN; i <= SECTION_MAX; ++i) {
681 DialogSection section = static_cast<DialogSection>(i);
682 SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section);
683 if (model->GetItemKeyForCheckedItem() != kAddNewItemKey)
684 continue;
685
686 FieldValueMap outputs;
687 view_->GetUserInput(section, &outputs);
688 // Remove fields that are empty, at their default values, or invalid.
689 for (FieldValueMap::iterator it = outputs.begin(); it != outputs.end();
690 ++it) {
691 if (InputWasEdited(it->first, it->second) &&
692 InputValidityMessage(section, it->first, it->second).empty()) {
693 snapshot.insert(std::make_pair(it->first, it->second));
694 }
695 }
696 }
697
698 return snapshot;
699 }
700
701 void AutofillDialogControllerImpl::RestoreUserInputFromSnapshot(
702 const FieldValueMap& snapshot) {
703 if (snapshot.empty())
704 return;
705
706 for (size_t i = SECTION_MIN; i <= SECTION_MAX; ++i) {
707 DialogSection section = static_cast<DialogSection>(i);
708 if (!SectionIsActive(section))
709 continue;
710
711 DetailInputs* inputs = MutableRequestedFieldsForSection(section);
712 for (size_t i = 0; i < inputs->size(); ++i) {
713 DetailInput* input = &(*inputs)[i];
714 if (input->length != DetailInput::NONE) {
715 input->initial_value =
716 GetInfoFromInputs(snapshot, section, AutofillType(input->type));
717 }
718 if (InputWasEdited(input->type, input->initial_value))
719 SuggestionsMenuModelForSection(section)->SetCheckedItem(kAddNewItemKey);
720 }
721 }
722 }
723
724 void AutofillDialogControllerImpl::UpdateSection(DialogSection section) {
725 if (view_)
726 view_->UpdateSection(section);
727 }
728
729 void AutofillDialogControllerImpl::UpdateForErrors() {
730 if (!view_)
731 return;
732
733 // Currently, the view should only need to be updated if validating a
734 // suggestion that's based on existing data.
735 for (size_t i = SECTION_MIN; i <= SECTION_MAX; ++i) {
736 if (IsEditingExistingData(static_cast<DialogSection>(i))) {
737 view_->UpdateForErrors();
738 break;
739 }
740 }
741 }
742
743 const DetailInputs& AutofillDialogControllerImpl::RequestedFieldsForSection(
744 DialogSection section) const {
745 switch (section) {
746 case SECTION_CC:
747 return requested_cc_fields_;
748 case SECTION_BILLING:
749 return requested_billing_fields_;
750 case SECTION_SHIPPING:
751 return requested_shipping_fields_;
752 }
753
754 NOTREACHED();
755 return requested_billing_fields_;
756 }
757
758 ui::ComboboxModel* AutofillDialogControllerImpl::ComboboxModelForAutofillType(
759 ServerFieldType type) {
760 switch (type) {
761 case CREDIT_CARD_EXP_MONTH:
762 return &cc_exp_month_combobox_model_;
763
764 case CREDIT_CARD_EXP_4_DIGIT_YEAR:
765 return &cc_exp_year_combobox_model_;
766
767 case ADDRESS_BILLING_COUNTRY:
768 return billing_country_combobox_model_.get();
769
770 case ADDRESS_HOME_COUNTRY:
771 return shipping_country_combobox_model_.get();
772
773 default:
774 return NULL;
775 }
776 }
777
778 ui::MenuModel* AutofillDialogControllerImpl::MenuModelForSection(
779 DialogSection section) {
780 SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section);
781 // The shipping section menu is special. It will always show because there is
782 // a choice between "Use billing" and "enter new".
783 if (section == SECTION_SHIPPING)
784 return model;
785
786 // For other sections, only show a menu if there's at least one suggestion.
787 for (int i = 0; i < model->GetItemCount(); ++i) {
788 if (IsASuggestionItemKey(model->GetItemKeyAt(i)))
789 return model;
790 }
791
792 return NULL;
793 }
794
795 base::string16 AutofillDialogControllerImpl::LabelForSection(
796 DialogSection section) const {
797 switch (section) {
798 case SECTION_CC:
799 return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SECTION_CC);
800 case SECTION_BILLING:
801 return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SECTION_BILLING);
802 case SECTION_SHIPPING:
803 return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_SECTION_SHIPPING);
804 }
805 NOTREACHED();
806 return base::string16();
807 }
808
809 SuggestionState AutofillDialogControllerImpl::SuggestionStateForSection(
810 DialogSection section) {
811 base::string16 vertically_compact, horizontally_compact;
812 bool show_suggestion = SuggestionTextForSection(section,
813 &vertically_compact,
814 &horizontally_compact);
815 return SuggestionState(show_suggestion,
816 vertically_compact,
817 horizontally_compact,
818 SuggestionIconForSection(section),
819 ExtraSuggestionTextForSection(section),
820 ExtraSuggestionIconForSection(section));
821 }
822
823 bool AutofillDialogControllerImpl::SuggestionTextForSection(
824 DialogSection section,
825 base::string16* vertically_compact,
826 base::string16* horizontally_compact) {
827 // When the user has clicked 'edit' or a suggestion is somehow invalid (e.g. a
828 // user selects a credit card that has expired), don't show a suggestion (even
829 // though there is a profile selected in the model).
830 if (IsEditingExistingData(section))
831 return false;
832
833 SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section);
834 std::string item_key = model->GetItemKeyForCheckedItem();
835 if (item_key == kSameAsBillingKey) {
836 *vertically_compact = *horizontally_compact = l10n_util::GetStringUTF16(
837 IDS_AUTOFILL_DIALOG_USING_BILLING_FOR_SHIPPING);
838 return true;
839 }
840
841 if (!IsASuggestionItemKey(item_key))
842 return false;
843
844 if (section == SECTION_BILLING || section == SECTION_SHIPPING) {
845 // Also check if the address is invalid (rules may have loaded since
846 // the dialog was shown).
847 if (HasInvalidAddress(*GetManager()->GetProfileByGUID(item_key)))
848 return false;
849 }
850
851 std::unique_ptr<DataModelWrapper> wrapper = CreateWrapper(section);
852 return wrapper->GetDisplayText(vertically_compact, horizontally_compact);
853 }
854
855 base::string16 AutofillDialogControllerImpl::ExtraSuggestionTextForSection(
856 DialogSection section) const {
857 if (section == SECTION_CC)
858 return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_CVC);
859
860 return base::string16();
861 }
862
863 std::unique_ptr<DataModelWrapper> AutofillDialogControllerImpl::CreateWrapper(
864 DialogSection section) {
865 SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section);
866 std::string item_key = model->GetItemKeyForCheckedItem();
867 if (!IsASuggestionItemKey(item_key) || IsManuallyEditingSection(section))
868 return std::unique_ptr<DataModelWrapper>();
869
870 if (section == SECTION_CC) {
871 CreditCard* card = GetManager()->GetCreditCardByGUID(item_key);
872 DCHECK(card);
873 return std::unique_ptr<DataModelWrapper>(
874 new AutofillCreditCardWrapper(card));
875 }
876
877 AutofillProfile* profile = GetManager()->GetProfileByGUID(item_key);
878 DCHECK(profile);
879 if (section == SECTION_SHIPPING) {
880 return std::unique_ptr<DataModelWrapper>(
881 new AutofillShippingAddressWrapper(profile));
882 }
883 DCHECK_EQ(SECTION_BILLING, section);
884 return std::unique_ptr<DataModelWrapper>(new AutofillProfileWrapper(profile));
885 }
886
887 gfx::Image AutofillDialogControllerImpl::SuggestionIconForSection(
888 DialogSection section) {
889 std::unique_ptr<DataModelWrapper> model = CreateWrapper(section);
890 if (!model.get())
891 return gfx::Image();
892
893 return model->GetIcon();
894 }
895
896 gfx::Image AutofillDialogControllerImpl::ExtraSuggestionIconForSection(
897 DialogSection section) {
898 if (section != SECTION_CC)
899 return gfx::Image();
900
901 std::unique_ptr<DataModelWrapper> model = CreateWrapper(section);
902 if (!model.get())
903 return gfx::Image();
904
905 return CvcIconForCreditCardType(
906 model->GetInfo(AutofillType(CREDIT_CARD_TYPE)));
907 }
908
909 FieldIconMap AutofillDialogControllerImpl::IconsForFields(
910 const FieldValueMap& user_inputs) const {
911 FieldIconMap result;
912 base::string16 credit_card_type;
913
914 FieldValueMap::const_iterator credit_card_iter =
915 user_inputs.find(CREDIT_CARD_NUMBER);
916 if (credit_card_iter != user_inputs.end()) {
917 const base::string16& number = credit_card_iter->second;
918 const std::string type = CreditCard::GetCreditCardType(number);
919 credit_card_type = CreditCard::TypeForDisplay(type);
920 result[CREDIT_CARD_NUMBER] = CreditCardIconForType(type);
921 }
922
923 if (!user_inputs.count(CREDIT_CARD_VERIFICATION_CODE))
924 return result;
925
926 result[CREDIT_CARD_VERIFICATION_CODE] =
927 CvcIconForCreditCardType(credit_card_type);
928
929 return result;
930 }
931
932 bool AutofillDialogControllerImpl::FieldControlsIcons(
933 ServerFieldType type) const {
934 return type == CREDIT_CARD_NUMBER;
935 }
936
937 base::string16 AutofillDialogControllerImpl::TooltipForField(
938 ServerFieldType type) const {
939 if (type == PHONE_HOME_WHOLE_NUMBER || type == PHONE_BILLING_WHOLE_NUMBER)
940 return l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_TOOLTIP_PHONE_NUMBER);
941
942 return base::string16();
943 }
944
945 // TODO(groby): Add more tests.
946 base::string16 AutofillDialogControllerImpl::InputValidityMessage(
947 DialogSection section,
948 ServerFieldType type,
949 const base::string16& value) {
950 AutofillType autofill_type(type);
951 if (autofill_type.group() == ADDRESS_HOME ||
952 autofill_type.group() == ADDRESS_BILLING) {
953 return base::string16();
954 }
955
956 switch (autofill_type.GetStorableType()) {
957 case EMAIL_ADDRESS:
958 if (!value.empty() && !IsValidEmailAddress(value)) {
959 return l10n_util::GetStringUTF16(
960 IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_EMAIL_ADDRESS);
961 }
962 break;
963
964 case CREDIT_CARD_NUMBER: {
965 if (!value.empty()) {
966 base::string16 message = CreditCardNumberValidityMessage(value);
967 if (!message.empty())
968 return message;
969 }
970 break;
971 }
972
973 case CREDIT_CARD_EXP_MONTH:
974 if (!InputWasEdited(CREDIT_CARD_EXP_MONTH, value)) {
975 return l10n_util::GetStringUTF16(
976 IDS_LIBADDRESSINPUT_MISSING_REQUIRED_FIELD);
977 }
978 break;
979
980 case CREDIT_CARD_EXP_4_DIGIT_YEAR:
981 if (!InputWasEdited(CREDIT_CARD_EXP_4_DIGIT_YEAR, value)) {
982 return l10n_util::GetStringUTF16(
983 IDS_LIBADDRESSINPUT_MISSING_REQUIRED_FIELD);
984 }
985 break;
986
987 case CREDIT_CARD_VERIFICATION_CODE:
988 if (!value.empty() && !autofill::IsValidCreditCardSecurityCode(value)) {
989 return l10n_util::GetStringUTF16(
990 IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_CREDIT_CARD_SECURITY_CODE);
991 }
992 break;
993
994 case NAME_FULL:
995 break;
996
997 case PHONE_HOME_WHOLE_NUMBER: // Used in shipping section.
998 break;
999
1000 case PHONE_BILLING_WHOLE_NUMBER: // Used in billing section.
1001 break;
1002
1003 default:
1004 NOTREACHED(); // Trying to validate unknown field.
1005 break;
1006 }
1007
1008 return value.empty() ? l10n_util::GetStringUTF16(
1009 IDS_LIBADDRESSINPUT_MISSING_REQUIRED_FIELD) :
1010 base::string16();
1011 }
1012
1013 // TODO(groby): Also add tests.
1014 ValidityMessages AutofillDialogControllerImpl::InputsAreValid(
1015 DialogSection section,
1016 const FieldValueMap& inputs) {
1017 ValidityMessages messages;
1018 if (inputs.empty())
1019 return messages;
1020
1021 AddressValidator::Status status = AddressValidator::SUCCESS;
1022 if (section != SECTION_CC) {
1023 AutofillProfile profile;
1024 FillFormGroupFromOutputs(inputs, &profile);
1025 std::unique_ptr<AddressData> address_data =
1026 i18n::CreateAddressDataFromAutofillProfile(
1027 profile, g_browser_process->GetApplicationLocale());
1028 address_data->language_code = AddressLanguageCodeForSection(section);
1029
1030 Localization localization;
1031 localization.SetGetter(l10n_util::GetStringUTF8);
1032 FieldProblemMap problems;
1033 status = GetValidator()->ValidateAddress(*address_data, NULL, &problems);
1034 bool billing = section != SECTION_SHIPPING;
1035
1036 for (FieldProblemMap::const_iterator iter = problems.begin();
1037 iter != problems.end(); ++iter) {
1038 bool sure = iter->second != MISSING_REQUIRED_FIELD;
1039 base::string16 text = base::UTF8ToUTF16(localization.GetErrorMessage(
1040 *address_data, iter->first, iter->second, true, false));
1041 messages.Set(i18n::TypeForField(iter->first, billing),
1042 ValidityMessage(text, sure));
1043 }
1044 }
1045
1046 for (FieldValueMap::const_iterator iter = inputs.begin();
1047 iter != inputs.end(); ++iter) {
1048 const ServerFieldType type = iter->first;
1049 base::string16 text = InputValidityMessage(section, type, iter->second);
1050
1051 // Skip empty/unchanged fields in edit mode. If the individual field does
1052 // not have validation errors, assume it to be valid unless later proven
1053 // otherwise.
1054 bool sure = InputWasEdited(type, iter->second);
1055
1056 if (sure && status == AddressValidator::RULES_NOT_READY &&
1057 !ComboboxModelForAutofillType(type) &&
1058 (AutofillType(type).group() == ADDRESS_HOME ||
1059 AutofillType(type).group() == ADDRESS_BILLING)) {
1060 DCHECK(text.empty());
1061 text = l10n_util::GetStringUTF16(
1062 IDS_AUTOFILL_DIALOG_VALIDATION_WAITING_FOR_RULES);
1063 sure = false;
1064 needs_validation_.insert(section);
1065 }
1066
1067 messages.Set(type, ValidityMessage(text, sure));
1068 }
1069
1070 // For the convenience of using operator[].
1071 FieldValueMap& field_values = const_cast<FieldValueMap&>(inputs);
1072 // Validate the date formed by month and year field. (Autofill dialog is
1073 // never supposed to have 2-digit years, so not checked).
1074 if (field_values.count(CREDIT_CARD_EXP_4_DIGIT_YEAR) &&
1075 field_values.count(CREDIT_CARD_EXP_MONTH) &&
1076 InputWasEdited(CREDIT_CARD_EXP_4_DIGIT_YEAR,
1077 field_values[CREDIT_CARD_EXP_4_DIGIT_YEAR]) &&
1078 InputWasEdited(CREDIT_CARD_EXP_MONTH,
1079 field_values[CREDIT_CARD_EXP_MONTH])) {
1080 ValidityMessage year_message(base::string16(), true);
1081 ValidityMessage month_message(base::string16(), true);
1082 if (!autofill::IsValidCreditCardExpirationDate(
1083 field_values[CREDIT_CARD_EXP_4_DIGIT_YEAR],
1084 field_values[CREDIT_CARD_EXP_MONTH], base::Time::Now())) {
1085 // The dialog shows the same error message for the month and year fields.
1086 year_message.text = l10n_util::GetStringUTF16(
1087 IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_CREDIT_CARD_EXPIRATION_DATE);
1088 month_message.text = l10n_util::GetStringUTF16(
1089 IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_CREDIT_CARD_EXPIRATION_DATE);
1090 }
1091 messages.Set(CREDIT_CARD_EXP_4_DIGIT_YEAR, year_message);
1092 messages.Set(CREDIT_CARD_EXP_MONTH, month_message);
1093 }
1094
1095 // If there is a credit card number and a CVC, validate them together.
1096 if (field_values.count(CREDIT_CARD_NUMBER) &&
1097 field_values.count(CREDIT_CARD_VERIFICATION_CODE)) {
1098 ValidityMessage ccv_message(base::string16(), true);
1099 if (!autofill::IsValidCreditCardSecurityCode(
1100 field_values[CREDIT_CARD_VERIFICATION_CODE],
1101 field_values[CREDIT_CARD_NUMBER])) {
1102 ccv_message.text = l10n_util::GetStringUTF16(
1103 IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_CREDIT_CARD_SECURITY_CODE);
1104 }
1105 messages.Set(CREDIT_CARD_VERIFICATION_CODE, ccv_message);
1106 }
1107
1108 // Validate the shipping phone number against the country code of the address.
1109 if (field_values.count(ADDRESS_HOME_COUNTRY) &&
1110 field_values.count(PHONE_HOME_WHOLE_NUMBER)) {
1111 messages.Set(
1112 PHONE_HOME_WHOLE_NUMBER,
1113 GetPhoneValidityMessage(field_values[ADDRESS_HOME_COUNTRY],
1114 field_values[PHONE_HOME_WHOLE_NUMBER]));
1115 }
1116
1117 // Validate the billing phone number against the country code of the address.
1118 if (field_values.count(ADDRESS_BILLING_COUNTRY) &&
1119 field_values.count(PHONE_BILLING_WHOLE_NUMBER)) {
1120 messages.Set(
1121 PHONE_BILLING_WHOLE_NUMBER,
1122 GetPhoneValidityMessage(field_values[ADDRESS_BILLING_COUNTRY],
1123 field_values[PHONE_BILLING_WHOLE_NUMBER]));
1124 }
1125
1126 return messages;
1127 }
1128
1129 void AutofillDialogControllerImpl::UserEditedOrActivatedInput(
1130 DialogSection section,
1131 ServerFieldType type,
1132 gfx::NativeView parent_view,
1133 const gfx::Rect& content_bounds,
1134 const base::string16& field_contents,
1135 bool was_edit) {
1136 ScopedViewUpdates updates(view_.get());
1137
1138 if (type == ADDRESS_BILLING_COUNTRY || type == ADDRESS_HOME_COUNTRY) {
1139 const FieldValueMap& snapshot = TakeUserInputSnapshot();
1140
1141 // Clobber the inputs because the view's already been updated.
1142 RebuildInputsForCountry(section, field_contents, true);
1143 RestoreUserInputFromSnapshot(snapshot);
1144 UpdateSection(section);
1145 }
1146
1147 // The rest of this method applies only to textfields while Autofill is
1148 // enabled. If a combobox or Autofill is disabled, bail.
1149 if (ComboboxModelForAutofillType(type) || !IsAutofillEnabled())
1150 return;
1151
1152 // If the field is edited down to empty, don't show a popup.
1153 if (was_edit && field_contents.empty()) {
1154 HidePopup();
1155 return;
1156 }
1157
1158 // If the user clicks while the popup is already showing, be sure to hide
1159 // it.
1160 if (!was_edit && popup_controller_.get()) {
1161 HidePopup();
1162 return;
1163 }
1164
1165 std::vector<autofill::Suggestion> popup_suggestions;
1166 popup_suggestion_ids_.clear();
1167 if (IsCreditCardType(type)) {
1168 popup_suggestions = GetManager()->GetCreditCardSuggestions(
1169 AutofillType(type), field_contents);
1170 for (const auto& suggestion : popup_suggestions)
1171 popup_suggestion_ids_.push_back(suggestion.backend_id);
1172 } else {
1173 popup_suggestions = GetManager()->GetProfileSuggestions(
1174 AutofillType(type),
1175 field_contents,
1176 false,
1177 RequestedTypesForSection(section));
1178 // Filter out ones we don't want.
1179 for (int i = 0; i < static_cast<int>(popup_suggestions.size()); i++) {
1180 const autofill::AutofillProfile* profile =
1181 GetManager()->GetProfileByGUID(popup_suggestions[i].backend_id);
1182 if (!profile || !ShouldSuggestProfile(section, *profile)) {
1183 popup_suggestions.erase(popup_suggestions.begin() + i);
1184 i--;
1185 }
1186 }
1187
1188 // Save the IDs.
1189 for (const auto& suggestion : popup_suggestions)
1190 popup_suggestion_ids_.push_back(suggestion.backend_id);
1191
1192 // This will append to the popup_suggestions but not the IDs since there
1193 // are no backend IDs for the I18N validator suggestions.
1194 GetI18nValidatorSuggestions(section, type, &popup_suggestions);
1195 }
1196
1197 if (popup_suggestions.empty()) {
1198 HidePopup();
1199 return;
1200 }
1201
1202 // |popup_input_type_| must be set before calling |Show()|.
1203 popup_input_type_ = type;
1204 popup_section_ = section;
1205
1206 // Use our own 0-based IDs for the items.
1207 // TODO(estade): do we need separators and control rows like 'Clear
1208 // Form'?
1209 for (size_t i = 0; i < popup_suggestions.size(); ++i) {
1210 popup_suggestions[i].frontend_id = i;
1211 }
1212
1213 popup_controller_ = AutofillPopupControllerImpl::GetOrCreate(
1214 popup_controller_, weak_ptr_factory_.GetWeakPtr(), NULL, parent_view,
1215 gfx::RectF(content_bounds),
1216 base::i18n::IsRTL() ? base::i18n::RIGHT_TO_LEFT
1217 : base::i18n::LEFT_TO_RIGHT);
1218 popup_controller_->Show(popup_suggestions);
1219 }
1220
1221 void AutofillDialogControllerImpl::FocusMoved() {
1222 HidePopup();
1223 }
1224
1225 bool AutofillDialogControllerImpl::ShouldShowErrorBubble() const {
1226 return popup_input_type_ == UNKNOWN_TYPE;
1227 }
1228
1229 void AutofillDialogControllerImpl::ViewClosed() {
1230 GetManager()->RemoveObserver(this);
1231
1232 delete this;
1233 }
1234
1235 std::vector<DialogNotification> AutofillDialogControllerImpl::
1236 CurrentNotifications() {
1237 std::vector<DialogNotification> notifications;
1238
1239 if (!invoked_from_same_origin_) {
1240 notifications.push_back(DialogNotification(
1241 DialogNotification::SECURITY_WARNING,
1242 l10n_util::GetStringFUTF16(IDS_AUTOFILL_DIALOG_SITE_WARNING,
1243 base::UTF8ToUTF16(source_url_.host()))));
1244 }
1245
1246 return notifications;
1247 }
1248
1249 void AutofillDialogControllerImpl::LinkClicked(const GURL& url) {
1250 OpenTabWithUrl(url);
1251 }
1252
1253 void AutofillDialogControllerImpl::OnCancel() {
1254 HidePopup();
1255 if (!data_was_passed_back_)
1256 LogOnCancelMetrics();
1257 callback_.Run(
1258 AutofillClient::AutocompleteResultErrorCancel, base::string16(), NULL);
1259 }
1260
1261 void AutofillDialogControllerImpl::OnAccept() {
1262 ScopedViewUpdates updates(view_.get());
1263 HidePopup();
1264 FinishSubmit();
1265 }
1266
1267 Profile* AutofillDialogControllerImpl::profile() {
1268 return profile_;
1269 }
1270
1271 content::WebContents* AutofillDialogControllerImpl::GetWebContents() {
1272 return web_contents();
1273 }
1274
1275 ////////////////////////////////////////////////////////////////////////////////
1276 // AutofillPopupDelegate implementation.
1277
1278 void AutofillDialogControllerImpl::OnPopupShown() {
1279 ScopedViewUpdates update(view_.get());
1280 view_->UpdateErrorBubble();
1281
1282 AutofillMetrics::LogDialogPopupEvent(AutofillMetrics::DIALOG_POPUP_SHOWN);
1283 }
1284
1285 void AutofillDialogControllerImpl::OnPopupHidden() {}
1286
1287 void AutofillDialogControllerImpl::DidSelectSuggestion(
1288 const base::string16& value,
1289 int identifier) {
1290 // TODO(estade): implement.
1291 }
1292
1293 void AutofillDialogControllerImpl::DidAcceptSuggestion(
1294 const base::string16& value,
1295 int identifier,
1296 int position) {
1297 DCHECK_NE(UNKNOWN_TYPE, popup_input_type_);
1298 // Because |HidePopup()| can be called from |UpdateSection()|, remember the
1299 // type of the input for later here.
1300 const ServerFieldType popup_input_type = popup_input_type_;
1301
1302 ScopedViewUpdates updates(view_.get());
1303 std::unique_ptr<DataModelWrapper> wrapper;
1304
1305 if (static_cast<size_t>(identifier) < popup_suggestion_ids_.size()) {
1306 const std::string& guid = popup_suggestion_ids_[identifier];
1307 if (IsCreditCardType(popup_input_type)) {
1308 wrapper.reset(new AutofillCreditCardWrapper(
1309 GetManager()->GetCreditCardByGUID(guid)));
1310 } else {
1311 wrapper.reset(new AutofillProfileWrapper(
1312 GetManager()->GetProfileByGUID(guid)));
1313 }
1314 } else {
1315 wrapper.reset(new I18nAddressDataWrapper(
1316 &i18n_validator_suggestions_[
1317 identifier - popup_suggestion_ids_.size()]));
1318 }
1319
1320 // If the user hasn't switched away from the default country and |wrapper|'s
1321 // country differs from the |view_|'s, rebuild inputs and restore user data.
1322 const FieldValueMap snapshot = TakeUserInputSnapshot();
1323 bool billing_rebuilt = false, shipping_rebuilt = false;
1324
1325 base::string16 billing_country =
1326 wrapper->GetInfo(AutofillType(ADDRESS_BILLING_COUNTRY));
1327 if (popup_section_ == ActiveBillingSection() &&
1328 !snapshot.count(ADDRESS_BILLING_COUNTRY) &&
1329 !billing_country.empty()) {
1330 billing_rebuilt = RebuildInputsForCountry(
1331 ActiveBillingSection(), billing_country, false);
1332 }
1333
1334 base::string16 shipping_country =
1335 wrapper->GetInfo(AutofillType(ADDRESS_HOME_COUNTRY));
1336 if (popup_section_ == SECTION_SHIPPING &&
1337 !snapshot.count(ADDRESS_HOME_COUNTRY) &&
1338 !shipping_country.empty()) {
1339 shipping_rebuilt = RebuildInputsForCountry(
1340 SECTION_SHIPPING, shipping_country, false);
1341 }
1342
1343 if (billing_rebuilt || shipping_rebuilt) {
1344 RestoreUserInputFromSnapshot(snapshot);
1345 if (billing_rebuilt)
1346 UpdateSection(ActiveBillingSection());
1347 if (shipping_rebuilt)
1348 UpdateSection(SECTION_SHIPPING);
1349 }
1350
1351 DCHECK(SectionIsActive(popup_section_));
1352 wrapper->FillInputs(MutableRequestedFieldsForSection(popup_section_));
1353 view_->FillSection(popup_section_, popup_input_type);
1354
1355 AutofillMetrics::LogDialogPopupEvent(
1356 AutofillMetrics::DIALOG_POPUP_FORM_FILLED);
1357
1358 // TODO(estade): not sure why it's necessary to do this explicitly.
1359 HidePopup();
1360 }
1361
1362 bool AutofillDialogControllerImpl::GetDeletionConfirmationText(
1363 const base::string16& value,
1364 int identifier,
1365 base::string16* title,
1366 base::string16* body) {
1367 return false;
1368 }
1369
1370 bool AutofillDialogControllerImpl::RemoveSuggestion(const base::string16& value,
1371 int identifier) {
1372 // TODO(estade): implement.
1373 return false;
1374 }
1375
1376 void AutofillDialogControllerImpl::ClearPreviewedForm() {
1377 // TODO(estade): implement.
1378 }
1379
1380 ////////////////////////////////////////////////////////////////////////////////
1381 // SuggestionsMenuModelDelegate implementation.
1382
1383 void AutofillDialogControllerImpl::SuggestionItemSelected(
1384 SuggestionsMenuModel* model,
1385 size_t index) {
1386 ScopedViewUpdates updates(view_.get());
1387
1388 if (model->GetItemKeyAt(index) == kManageItemsKey) {
1389 GURL url;
1390 DCHECK(IsAutofillEnabled());
1391 GURL settings_url(chrome::kChromeUISettingsURL);
1392 url = settings_url.Resolve(chrome::kAutofillSubPage);
1393 OpenTabWithUrl(url);
1394 return;
1395 }
1396
1397 model->SetCheckedIndex(index);
1398 DialogSection section = SectionForSuggestionsMenuModel(*model);
1399
1400 ResetSectionInput(section);
1401 ShowEditUiIfBadSuggestion(section);
1402 UpdateSection(section);
1403 view_->UpdateNotificationArea();
1404 UpdateForErrors();
1405
1406 LogSuggestionItemSelectedMetric(*model);
1407 }
1408
1409 ////////////////////////////////////////////////////////////////////////////////
1410 // PersonalDataManagerObserver implementation.
1411
1412 void AutofillDialogControllerImpl::OnPersonalDataChanged() {
1413 SuggestionsUpdated();
1414 }
1415
1416 ////////////////////////////////////////////////////////////////////////////////
1417
1418 bool AutofillDialogControllerImpl::HandleKeyPressEventInInput(
1419 const content::NativeWebKeyboardEvent& event) {
1420 if (popup_controller_.get())
1421 return popup_controller_->HandleKeyPressEvent(event);
1422
1423 return false;
1424 }
1425
1426 void AutofillDialogControllerImpl::SubmitButtonDelayBegin() {
1427 submit_button_delay_timer_.Start(
1428 FROM_HERE,
1429 base::TimeDelta::FromMilliseconds(kSubmitButtonDelayMs),
1430 this,
1431 &AutofillDialogControllerImpl::OnSubmitButtonDelayEnd);
1432 }
1433
1434 void AutofillDialogControllerImpl::SubmitButtonDelayEndForTesting() {
1435 DCHECK(submit_button_delay_timer_.IsRunning());
1436 submit_button_delay_timer_.user_task().Run();
1437 submit_button_delay_timer_.Stop();
1438 }
1439
1440 AutofillDialogControllerImpl::AutofillDialogControllerImpl(
1441 content::WebContents* contents,
1442 const FormData& form_structure,
1443 const GURL& source_url,
1444 const AutofillClient::ResultCallback& callback)
1445 : WebContentsObserver(contents),
1446 profile_(Profile::FromBrowserContext(contents->GetBrowserContext())),
1447 initial_user_state_(AutofillMetrics::DIALOG_USER_STATE_UNKNOWN),
1448 form_structure_(form_structure),
1449 invoked_from_same_origin_(true),
1450 source_url_(source_url),
1451 callback_(callback),
1452 suggested_cc_(this),
1453 suggested_billing_(this),
1454 suggested_shipping_(this),
1455 cares_about_shipping_(true),
1456 popup_input_type_(UNKNOWN_TYPE),
1457 popup_section_(SECTION_MIN),
1458 data_was_passed_back_(false),
1459 was_ui_latency_logged_(false),
1460 weak_ptr_factory_(this) {
1461 DCHECK(!callback_.is_null());
1462 }
1463
1464 AutofillDialogView* AutofillDialogControllerImpl::CreateView() {
1465 return AutofillDialogView::Create(this);
1466 }
1467
1468 PersonalDataManager* AutofillDialogControllerImpl::GetManager() const {
1469 return PersonalDataManagerFactory::GetForProfile(profile_);
1470 }
1471
1472 AddressValidator* AutofillDialogControllerImpl::GetValidator() {
1473 return validator_.get();
1474 }
1475
1476 void AutofillDialogControllerImpl::OpenTabWithUrl(const GURL& url) {
1477 chrome::NavigateParams params(
1478 chrome::FindBrowserWithWebContents(web_contents()),
1479 url,
1480 ui::PAGE_TRANSITION_LINK);
1481 params.disposition = NEW_FOREGROUND_TAB;
1482 chrome::Navigate(&params);
1483 }
1484
1485 // TODO(estade): remove.
1486 DialogSection AutofillDialogControllerImpl::ActiveBillingSection() const {
1487 return SECTION_BILLING;
1488 }
1489
1490 bool AutofillDialogControllerImpl::IsEditingExistingData(
1491 DialogSection section) const {
1492 return section_editing_state_.count(section) > 0;
1493 }
1494
1495 bool AutofillDialogControllerImpl::IsManuallyEditingSection(
1496 DialogSection section) const {
1497 return IsEditingExistingData(section) ||
1498 SuggestionsMenuModelForSection(section)->
1499 GetItemKeyForCheckedItem() == kAddNewItemKey;
1500 }
1501
1502 void AutofillDialogControllerImpl::SuggestionsUpdated() {
1503 ScopedViewUpdates updates(view_.get());
1504
1505 const FieldValueMap snapshot = TakeUserInputSnapshot();
1506
1507 suggested_cc_.Reset();
1508 suggested_billing_.Reset();
1509 suggested_shipping_.Reset();
1510 HidePopup();
1511
1512 suggested_shipping_.AddKeyedItem(
1513 kSameAsBillingKey,
1514 l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_USE_BILLING_FOR_SHIPPING));
1515
1516 shipping_country_combobox_model_->SetCountries(
1517 *GetManager(),
1518 base::Bind(AutofillCountryFilter, acceptable_shipping_countries_));
1519
1520 if (IsAutofillEnabled()) {
1521 PersonalDataManager* manager = GetManager();
1522 const std::vector<CreditCard*>& cards = manager->GetCreditCards();
1523 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
1524 for (size_t i = 0; i < cards.size(); ++i) {
1525 if (!i18ninput::CardHasCompleteAndVerifiedData(*cards[i]))
1526 continue;
1527
1528 suggested_cc_.AddKeyedItemWithIcon(
1529 cards[i]->guid(), cards[i]->Label(),
1530 rb.GetImageNamed(CreditCard::IconResourceId(cards[i]->type())));
1531 suggested_cc_.SetEnabled(
1532 cards[i]->guid(), !ShouldDisallowCcType(cards[i]->TypeForDisplay()));
1533 }
1534
1535 const std::vector<AutofillProfile*>& profiles = manager->GetProfiles();
1536 std::vector<base::string16> labels;
1537 AutofillProfile::CreateDifferentiatingLabels(
1538 profiles, g_browser_process->GetApplicationLocale(), &labels);
1539 DCHECK_EQ(labels.size(), profiles.size());
1540 for (size_t i = 0; i < profiles.size(); ++i) {
1541 const AutofillProfile& profile = *profiles[i];
1542 if (!i18ninput::AddressHasCompleteAndVerifiedData(
1543 profile, g_browser_process->GetApplicationLocale())) {
1544 continue;
1545 }
1546
1547 suggested_shipping_.AddKeyedItem(profile.guid(), labels[i]);
1548 suggested_shipping_.SetEnabled(
1549 profile.guid(),
1550 CanAcceptCountry(
1551 SECTION_SHIPPING,
1552 base::UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_COUNTRY))));
1553 if (!profile.GetRawInfo(EMAIL_ADDRESS).empty() &&
1554 !profile.IsPresentButInvalid(EMAIL_ADDRESS)) {
1555 suggested_billing_.AddKeyedItem(profile.guid(), labels[i]);
1556 suggested_billing_.SetEnabled(
1557 profile.guid(),
1558 CanAcceptCountry(
1559 SECTION_BILLING,
1560 base::UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_COUNTRY))));
1561 }
1562 }
1563 }
1564
1565 suggested_cc_.AddKeyedItem(
1566 kAddNewItemKey,
1567 l10n_util::GetStringUTF16(IsAutofillEnabled()
1568 ? IDS_AUTOFILL_DIALOG_ADD_CREDIT_CARD
1569 : IDS_AUTOFILL_DIALOG_ENTER_CREDIT_CARD));
1570 suggested_cc_.AddKeyedItem(
1571 kManageItemsKey,
1572 l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_MANAGE_CREDIT_CARD));
1573 suggested_billing_.AddKeyedItem(
1574 kAddNewItemKey,
1575 l10n_util::GetStringUTF16(
1576 IsAutofillEnabled() ? IDS_AUTOFILL_DIALOG_ADD_BILLING_ADDRESS
1577 : IDS_AUTOFILL_DIALOG_ENTER_BILLING_DETAILS));
1578 suggested_billing_.AddKeyedItem(
1579 kManageItemsKey,
1580 l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_MANAGE_BILLING_ADDRESS));
1581
1582 suggested_shipping_.AddKeyedItem(
1583 kAddNewItemKey,
1584 l10n_util::GetStringUTF16(
1585 IsAutofillEnabled()
1586 ? IDS_AUTOFILL_DIALOG_ADD_SHIPPING_ADDRESS
1587 : IDS_AUTOFILL_DIALOG_USE_DIFFERENT_SHIPPING_ADDRESS));
1588
1589 if (IsAutofillEnabled()) {
1590 suggested_shipping_.AddKeyedItem(
1591 kManageItemsKey,
1592 l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_MANAGE_SHIPPING_ADDRESS));
1593
1594 for (size_t i = SECTION_MIN; i <= SECTION_MAX; ++i) {
1595 DialogSection section = static_cast<DialogSection>(i);
1596 if (!SectionIsActive(section))
1597 continue;
1598
1599 // Set the starting choice for the menu. First set to the default in case
1600 // the GUID saved in prefs refers to a profile that no longer exists.
1601 std::string guid;
1602 GetDefaultAutofillChoice(section, &guid);
1603 SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section);
1604 model->SetCheckedItem(guid);
1605 if (GetAutofillChoice(section, &guid))
1606 model->SetCheckedItem(guid);
1607 }
1608 }
1609
1610 if (view_)
1611 view_->ModelChanged();
1612
1613 for (size_t i = SECTION_MIN; i <= SECTION_MAX; ++i) {
1614 ResetSectionInput(static_cast<DialogSection>(i));
1615 }
1616
1617 FieldValueMap::const_iterator billing_it =
1618 snapshot.find(ADDRESS_BILLING_COUNTRY);
1619 if (billing_it != snapshot.end())
1620 RebuildInputsForCountry(ActiveBillingSection(), billing_it->second, true);
1621
1622 FieldValueMap::const_iterator shipping_it =
1623 snapshot.find(ADDRESS_HOME_COUNTRY);
1624 if (shipping_it != snapshot.end())
1625 RebuildInputsForCountry(SECTION_SHIPPING, shipping_it->second, true);
1626
1627 RestoreUserInputFromSnapshot(snapshot);
1628
1629 for (size_t i = SECTION_MIN; i <= SECTION_MAX; ++i) {
1630 DialogSection section = static_cast<DialogSection>(i);
1631 if (!SectionIsActive(section))
1632 continue;
1633
1634 ShowEditUiIfBadSuggestion(section);
1635 UpdateSection(section);
1636 }
1637
1638 UpdateForErrors();
1639 }
1640
1641 void AutofillDialogControllerImpl::FillOutputForSectionWithComparator(
1642 DialogSection section,
1643 const FormStructure::InputFieldComparator& compare) {
1644 if (!SectionIsActive(section))
1645 return;
1646
1647 DetailInputs inputs;
1648 std::string country_code = CountryCodeForSection(section);
1649 BuildInputsForSection(section, country_code, &inputs,
1650 MutableAddressLanguageCodeForSection(section));
1651 std::vector<ServerFieldType> types = TypesFromInputs(inputs);
1652
1653 std::unique_ptr<DataModelWrapper> wrapper = CreateWrapper(section);
1654 if (wrapper) {
1655 // Only fill in data that is associated with this section.
1656 wrapper->FillFormStructure(types, compare, &form_structure_);
1657
1658 // CVC needs special-casing because the CreditCard class doesn't store or
1659 // handle them. This isn't necessary when filling the combined CC and
1660 // billing section as CVC comes from |full_wallet_| in this case.
1661 if (section == SECTION_CC)
1662 SetOutputForFieldsOfType(CREDIT_CARD_VERIFICATION_CODE, view_->GetCvc());
1663
1664 } else {
1665 // The user manually input data. If using Autofill, save the info as new or
1666 // edited data. Always fill local data into |form_structure_|.
1667 FieldValueMap output;
1668 view_->GetUserInput(section, &output);
1669
1670 if (section == SECTION_CC) {
1671 CreditCard card;
1672 FillFormGroupFromOutputs(output, &card);
1673
1674 // The card holder name comes from the billing address section.
1675 card.SetRawInfo(CREDIT_CARD_NAME_FULL,
1676 GetValueFromSection(SECTION_BILLING, NAME_BILLING_FULL));
1677
1678 AutofillCreditCardWrapper card_wrapper(&card);
1679 card_wrapper.FillFormStructure(types, compare, &form_structure_);
1680
1681 // Again, CVC needs special-casing. Fill it in directly from |output|.
1682 SetOutputForFieldsOfType(
1683 CREDIT_CARD_VERIFICATION_CODE,
1684 output[CREDIT_CARD_VERIFICATION_CODE]);
1685 } else {
1686 AutofillProfile profile;
1687 FillFormGroupFromOutputs(output, &profile);
1688 profile.set_language_code(AddressLanguageCodeForSection(section));
1689
1690 if (ShouldSaveDetailsLocally()) {
1691 profile.set_origin(RulesAreLoaded(section) ?
1692 kAutofillDialogOrigin : source_url_.GetOrigin().spec());
1693
1694 std::string guid = GetManager()->SaveImportedProfile(profile);
1695 newly_saved_data_model_guids_[section] = guid;
1696 }
1697
1698 AutofillProfileWrapper profile_wrapper(&profile);
1699 profile_wrapper.FillFormStructure(types, compare, &form_structure_);
1700 }
1701 }
1702 }
1703
1704 void AutofillDialogControllerImpl::FillOutputForSection(DialogSection section) {
1705 FillOutputForSectionWithComparator(
1706 section, base::Bind(ServerTypeMatchesField, section));
1707 }
1708
1709 bool AutofillDialogControllerImpl::FormStructureCaresAboutSection(
1710 DialogSection section) const {
1711 // For now, only SECTION_SHIPPING may be omitted due to a site not asking for
1712 // any of the fields.
1713 if (section == SECTION_SHIPPING)
1714 return cares_about_shipping_;
1715
1716 return true;
1717 }
1718
1719 void AutofillDialogControllerImpl::SetOutputForFieldsOfType(
1720 ServerFieldType type,
1721 const base::string16& output) {
1722 for (size_t i = 0; i < form_structure_.field_count(); ++i) {
1723 AutofillField* field = form_structure_.field(i);
1724 if (field->Type().GetStorableType() == type)
1725 field->value = output;
1726 }
1727 }
1728
1729 base::string16 AutofillDialogControllerImpl::GetValueFromSection(
1730 DialogSection section,
1731 ServerFieldType type) {
1732 DCHECK(SectionIsActive(section));
1733
1734 std::unique_ptr<DataModelWrapper> wrapper = CreateWrapper(section);
1735 if (wrapper)
1736 return wrapper->GetInfo(AutofillType(type));
1737
1738 FieldValueMap output;
1739 view_->GetUserInput(section, &output);
1740 return output[type];
1741 }
1742
1743 bool AutofillDialogControllerImpl::CanAcceptCountry(
1744 DialogSection section,
1745 const std::string& country_code) {
1746 DCHECK_EQ(2U, country_code.size());
1747 CountryComboboxModel* model = CountryComboboxModelForSection(section);
1748 const std::vector<AutofillCountry*>& countries = model->countries();
1749 for (size_t i = 0; i < countries.size(); ++i) {
1750 if (countries[i] && countries[i]->country_code() == country_code)
1751 return true;
1752 }
1753
1754 return false;
1755 }
1756
1757 bool AutofillDialogControllerImpl::ShouldSuggestProfile(
1758 DialogSection section,
1759 const AutofillProfile& profile) {
1760 std::string country_code =
1761 base::UTF16ToASCII(profile.GetRawInfo(ADDRESS_HOME_COUNTRY));
1762 return country_code.empty() || CanAcceptCountry(section, country_code);
1763 }
1764
1765 SuggestionsMenuModel* AutofillDialogControllerImpl::
1766 SuggestionsMenuModelForSection(DialogSection section) {
1767 switch (section) {
1768 case SECTION_CC:
1769 return &suggested_cc_;
1770 case SECTION_BILLING:
1771 return &suggested_billing_;
1772 case SECTION_SHIPPING:
1773 return &suggested_shipping_;
1774 }
1775
1776 NOTREACHED();
1777 return NULL;
1778 }
1779
1780 const SuggestionsMenuModel* AutofillDialogControllerImpl::
1781 SuggestionsMenuModelForSection(DialogSection section) const {
1782 return const_cast<AutofillDialogControllerImpl*>(this)->
1783 SuggestionsMenuModelForSection(section);
1784 }
1785
1786 DialogSection AutofillDialogControllerImpl::SectionForSuggestionsMenuModel(
1787 const SuggestionsMenuModel& model) {
1788 if (&model == &suggested_cc_)
1789 return SECTION_CC;
1790
1791 if (&model == &suggested_billing_)
1792 return SECTION_BILLING;
1793
1794 DCHECK_EQ(&model, &suggested_shipping_);
1795 return SECTION_SHIPPING;
1796 }
1797
1798 CountryComboboxModel* AutofillDialogControllerImpl::
1799 CountryComboboxModelForSection(DialogSection section) {
1800 if (section == SECTION_BILLING)
1801 return billing_country_combobox_model_.get();
1802
1803 if (section == SECTION_SHIPPING)
1804 return shipping_country_combobox_model_.get();
1805
1806 return NULL;
1807 }
1808
1809 void AutofillDialogControllerImpl::GetI18nValidatorSuggestions(
1810 DialogSection section,
1811 ServerFieldType type,
1812 std::vector<autofill::Suggestion>* popup_suggestions) {
1813 AddressField focused_field;
1814 if (!i18n::FieldForType(type, &focused_field))
1815 return;
1816
1817 FieldValueMap inputs;
1818 view_->GetUserInput(section, &inputs);
1819
1820 AutofillProfile profile;
1821 FillFormGroupFromOutputs(inputs, &profile);
1822
1823 std::unique_ptr<AddressData> user_input =
1824 i18n::CreateAddressDataFromAutofillProfile(
1825 profile, g_browser_process->GetApplicationLocale());
1826 user_input->language_code = AddressLanguageCodeForSection(section);
1827
1828 static const size_t kSuggestionsLimit = 10;
1829 AddressValidator::Status status = GetValidator()->GetSuggestions(
1830 *user_input, focused_field, kSuggestionsLimit,
1831 &i18n_validator_suggestions_);
1832
1833 if (status != AddressValidator::SUCCESS)
1834 return;
1835
1836 for (size_t i = 0; i < i18n_validator_suggestions_.size(); ++i) {
1837 popup_suggestions->push_back(autofill::Suggestion(
1838 base::UTF8ToUTF16(
1839 i18n_validator_suggestions_[i].GetFieldValue(focused_field))));
1840
1841 // Disambiguate the suggestion by showing the smallest administrative
1842 // region of the suggested address:
1843 // ADMIN_AREA > LOCALITY > DEPENDENT_LOCALITY
1844 for (int field = DEPENDENT_LOCALITY; field >= ADMIN_AREA; --field) {
1845 const std::string& field_value =
1846 i18n_validator_suggestions_[i].GetFieldValue(
1847 static_cast<AddressField>(field));
1848 if (focused_field != field && !field_value.empty()) {
1849 popup_suggestions->back().label = base::UTF8ToUTF16(field_value);
1850 break;
1851 }
1852 }
1853 }
1854 }
1855
1856 DetailInputs* AutofillDialogControllerImpl::MutableRequestedFieldsForSection(
1857 DialogSection section) {
1858 return const_cast<DetailInputs*>(&RequestedFieldsForSection(section));
1859 }
1860
1861 std::string* AutofillDialogControllerImpl::MutableAddressLanguageCodeForSection(
1862 DialogSection section) {
1863 switch (section) {
1864 case SECTION_BILLING:
1865 return &billing_address_language_code_;
1866 case SECTION_SHIPPING:
1867 return &shipping_address_language_code_;
1868 case SECTION_CC:
1869 return NULL;
1870 }
1871 NOTREACHED();
1872 return NULL;
1873 }
1874
1875 std::string AutofillDialogControllerImpl::AddressLanguageCodeForSection(
1876 DialogSection section) {
1877 std::string* language_code = MutableAddressLanguageCodeForSection(section);
1878 return language_code != NULL ? *language_code : std::string();
1879 }
1880
1881 std::vector<ServerFieldType> AutofillDialogControllerImpl::
1882 RequestedTypesForSection(DialogSection section) const {
1883 return TypesFromInputs(RequestedFieldsForSection(section));
1884 }
1885
1886 std::string AutofillDialogControllerImpl::CountryCodeForSection(
1887 DialogSection section) {
1888 base::string16 country;
1889
1890 std::unique_ptr<DataModelWrapper> wrapper = CreateWrapper(section);
1891 if (wrapper) {
1892 country = wrapper->GetInfo(AutofillType(CountryTypeForSection(section)));
1893 } else {
1894 FieldValueMap outputs;
1895 view_->GetUserInput(section, &outputs);
1896 country = outputs[CountryTypeForSection(section)];
1897 }
1898
1899 return CountryNames::GetInstance()->GetCountryCode(country);
1900 }
1901
1902 bool AutofillDialogControllerImpl::RebuildInputsForCountry(
1903 DialogSection section,
1904 const base::string16& country_name,
1905 bool should_clobber) {
1906 CountryComboboxModel* model = CountryComboboxModelForSection(section);
1907 if (!model)
1908 return false;
1909
1910 std::string country_code =
1911 CountryNames::GetInstance()->GetCountryCode(country_name);
1912 DCHECK(CanAcceptCountry(section, country_code));
1913
1914 if (view_ && !should_clobber) {
1915 FieldValueMap outputs;
1916 view_->GetUserInput(section, &outputs);
1917
1918 // If |country_name| is the same as the view, no-op and let the caller know.
1919 if (outputs[CountryTypeForSection(section)] == country_name)
1920 return false;
1921 }
1922
1923 DetailInputs* inputs = MutableRequestedFieldsForSection(section);
1924 inputs->clear();
1925 BuildInputsForSection(section, country_code, inputs,
1926 MutableAddressLanguageCodeForSection(section));
1927
1928 if (!country_code.empty()) {
1929 GetValidator()->LoadRules(
1930 CountryNames::GetInstance()->GetCountryCode(country_name));
1931 }
1932
1933 return true;
1934 }
1935
1936 void AutofillDialogControllerImpl::HidePopup() {
1937 if (popup_controller_)
1938 popup_controller_->Hide();
1939 popup_input_type_ = UNKNOWN_TYPE;
1940 }
1941
1942 void AutofillDialogControllerImpl::SetEditingExistingData(
1943 DialogSection section, bool editing) {
1944 if (editing)
1945 section_editing_state_.insert(section);
1946 else
1947 section_editing_state_.erase(section);
1948 }
1949
1950 bool AutofillDialogControllerImpl::IsASuggestionItemKey(
1951 const std::string& key) const {
1952 return !key.empty() &&
1953 key != kAddNewItemKey &&
1954 key != kManageItemsKey &&
1955 key != kSameAsBillingKey;
1956 }
1957
1958 bool AutofillDialogControllerImpl::IsAutofillEnabled() const {
1959 return profile_->GetPrefs()->GetBoolean(prefs::kAutofillEnabled);
1960 }
1961
1962 bool AutofillDialogControllerImpl::IsManuallyEditingAnySection() const {
1963 for (size_t section = SECTION_MIN; section <= SECTION_MAX; ++section) {
1964 if (IsManuallyEditingSection(static_cast<DialogSection>(section)))
1965 return true;
1966 }
1967 return false;
1968 }
1969
1970 base::string16 AutofillDialogControllerImpl::CreditCardNumberValidityMessage(
1971 const base::string16& number) const {
1972 if (!number.empty() && !autofill::IsValidCreditCardNumber(number)) {
1973 return l10n_util::GetStringUTF16(
1974 IDS_AUTOFILL_DIALOG_VALIDATION_INVALID_CREDIT_CARD_NUMBER);
1975 }
1976
1977 if (ShouldDisallowCcType(
1978 CreditCard::TypeForDisplay(CreditCard::GetCreditCardType(number)))) {
1979 int ids = IDS_AUTOFILL_DIALOG_VALIDATION_UNACCEPTED_GENERIC_CARD;
1980 const char* const type = CreditCard::GetCreditCardType(number);
1981 if (type == kAmericanExpressCard)
1982 ids = IDS_AUTOFILL_DIALOG_VALIDATION_UNACCEPTED_AMEX;
1983 else if (type == kDiscoverCard)
1984 ids = IDS_AUTOFILL_DIALOG_VALIDATION_UNACCEPTED_DISCOVER;
1985 else if (type == kMasterCard)
1986 ids = IDS_AUTOFILL_DIALOG_VALIDATION_UNACCEPTED_MASTERCARD;
1987 else if (type == kVisaCard)
1988 ids = IDS_AUTOFILL_DIALOG_VALIDATION_UNACCEPTED_VISA;
1989
1990 return l10n_util::GetStringUTF16(ids);
1991 }
1992
1993 // Card number is good and supported.
1994 return base::string16();
1995 }
1996
1997 bool AutofillDialogControllerImpl::AllSectionsAreValid() {
1998 for (size_t section = SECTION_MIN; section <= SECTION_MAX; ++section) {
1999 if (!SectionIsValid(static_cast<DialogSection>(section)))
2000 return false;
2001 }
2002 return true;
2003 }
2004
2005 bool AutofillDialogControllerImpl::SectionIsValid(
2006 DialogSection section) {
2007 if (!IsManuallyEditingSection(section))
2008 return true;
2009
2010 FieldValueMap detail_outputs;
2011 view_->GetUserInput(section, &detail_outputs);
2012 return !InputsAreValid(section, detail_outputs).HasSureErrors();
2013 }
2014
2015 bool AutofillDialogControllerImpl::RulesAreLoaded(DialogSection section) {
2016 AddressData address_data;
2017 address_data.region_code = CountryCodeForSection(section);
2018 AddressValidator::Status status = GetValidator()->ValidateAddress(
2019 address_data, NULL, NULL);
2020 return status == AddressValidator::SUCCESS;
2021 }
2022
2023 bool AutofillDialogControllerImpl::ShouldDisallowCcType(
2024 const base::string16& type) const {
2025 if (acceptable_cc_types_.empty())
2026 return false;
2027
2028 if (acceptable_cc_types_.find(base::i18n::ToUpper(type)) ==
2029 acceptable_cc_types_.end()) {
2030 return true;
2031 }
2032
2033 return false;
2034 }
2035
2036 bool AutofillDialogControllerImpl::HasInvalidAddress(
2037 const AutofillProfile& profile) {
2038 std::unique_ptr<AddressData> address_data =
2039 i18n::CreateAddressDataFromAutofillProfile(
2040 profile, g_browser_process->GetApplicationLocale());
2041
2042 FieldProblemMap problems;
2043 GetValidator()->ValidateAddress(*address_data, NULL, &problems);
2044 return !problems.empty();
2045 }
2046
2047 bool AutofillDialogControllerImpl::ShouldUseBillingForShipping() {
2048 return SectionIsActive(SECTION_SHIPPING) &&
2049 suggested_shipping_.GetItemKeyForCheckedItem() == kSameAsBillingKey;
2050 }
2051
2052 bool AutofillDialogControllerImpl::ShouldSaveDetailsLocally() {
2053 // It's possible that the user checked [X] Save details locally before
2054 // switching payment methods, so only ask the view whether to save details
2055 // locally if that checkbox is showing (currently if not paying with wallet).
2056 // Also, if the user isn't editing any sections, there's no data to save
2057 // locally.
2058 return ShouldOfferToSaveInChrome() && view_->SaveDetailsLocally();
2059 }
2060
2061 void AutofillDialogControllerImpl::FinishSubmit() {
2062 FillOutputForSection(SECTION_CC);
2063 FillOutputForSection(SECTION_BILLING);
2064
2065 if (ShouldUseBillingForShipping()) {
2066 FillOutputForSectionWithComparator(
2067 SECTION_BILLING,
2068 base::Bind(ServerTypeMatchesShippingField));
2069 FillOutputForSectionWithComparator(
2070 SECTION_CC,
2071 base::Bind(ServerTypeMatchesShippingField));
2072 } else {
2073 FillOutputForSection(SECTION_SHIPPING);
2074 }
2075
2076 if (ShouldOfferToSaveInChrome()) {
2077 for (size_t i = SECTION_MIN; i <= SECTION_MAX; ++i) {
2078 DialogSection section = static_cast<DialogSection>(i);
2079 if (!SectionIsActive(section) || section == SECTION_CC)
2080 continue;
2081
2082 SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section);
2083 std::string item_key = model->GetItemKeyForCheckedItem();
2084 if (IsASuggestionItemKey(item_key) || item_key == kSameAsBillingKey) {
2085 PersistAutofillChoice(section, item_key);
2086 } else if (item_key == kAddNewItemKey && ShouldSaveDetailsLocally()) {
2087 DCHECK(newly_saved_data_model_guids_.count(section));
2088 PersistAutofillChoice(section, newly_saved_data_model_guids_[section]);
2089 }
2090 }
2091
2092 profile_->GetPrefs()->SetBoolean(::prefs::kAutofillDialogSaveData,
2093 view_->SaveDetailsLocally());
2094 }
2095
2096 LogOnFinishSubmitMetrics();
2097
2098 // Callback should be called as late as possible.
2099 callback_.Run(AutofillClient::AutocompleteResultSuccess,
2100 base::string16(),
2101 &form_structure_);
2102 data_was_passed_back_ = true;
2103
2104 // This might delete us.
2105 Hide();
2106 }
2107
2108 void AutofillDialogControllerImpl::OnAddressValidationRulesLoaded(
2109 const std::string& country_code,
2110 bool success) {
2111 // Rules may load instantly (during initialization, before the view is
2112 // even ready). We'll validate when the view is created.
2113 if (!view_)
2114 return;
2115
2116 ScopedViewUpdates updates(view_.get());
2117
2118 // TODO(dbeam): should we retry on failure?
2119 for (size_t i = SECTION_MIN; i <= SECTION_MAX; ++i) {
2120 DialogSection section = static_cast<DialogSection>(i);
2121 if (!SectionIsActive(section) ||
2122 CountryCodeForSection(section) != country_code) {
2123 continue;
2124 }
2125
2126 if (IsManuallyEditingSection(section) && needs_validation_.count(section)) {
2127 view_->ValidateSection(section);
2128 needs_validation_.erase(section);
2129 } else if (success) {
2130 ShowEditUiIfBadSuggestion(section);
2131 UpdateSection(section);
2132 }
2133 }
2134 }
2135
2136 void AutofillDialogControllerImpl::PersistAutofillChoice(
2137 DialogSection section,
2138 const std::string& guid) {
2139 DCHECK(ShouldOfferToSaveInChrome());
2140 std::unique_ptr<base::DictionaryValue> value(new base::DictionaryValue());
2141 value->SetString(kGuidPrefKey, guid);
2142
2143 DictionaryPrefUpdate updater(profile()->GetPrefs(),
2144 ::prefs::kAutofillDialogAutofillDefault);
2145 base::DictionaryValue* autofill_choice = updater.Get();
2146 autofill_choice->Set(SectionToPrefString(section), value.release());
2147 }
2148
2149 void AutofillDialogControllerImpl::GetDefaultAutofillChoice(
2150 DialogSection section,
2151 std::string* guid) {
2152 DCHECK(IsAutofillEnabled());
2153 // The default choice is the first thing in the menu that is a suggestion
2154 // item.
2155 SuggestionsMenuModel* model = SuggestionsMenuModelForSection(section);
2156 for (int i = 0; i < model->GetItemCount(); ++i) {
2157 // Try the first suggestion item that is enabled.
2158 if (IsASuggestionItemKey(model->GetItemKeyAt(i)) && model->IsEnabledAt(i)) {
2159 *guid = model->GetItemKeyAt(i);
2160 return;
2161 // Fall back to the first non-suggestion key.
2162 } else if (!IsASuggestionItemKey(model->GetItemKeyAt(i)) && guid->empty()) {
2163 *guid = model->GetItemKeyAt(i);
2164 }
2165 }
2166 }
2167
2168 bool AutofillDialogControllerImpl::GetAutofillChoice(DialogSection section,
2169 std::string* guid) {
2170 DCHECK(IsAutofillEnabled());
2171 const base::DictionaryValue* choices = profile()->GetPrefs()->GetDictionary(
2172 ::prefs::kAutofillDialogAutofillDefault);
2173 if (!choices)
2174 return false;
2175
2176 const base::DictionaryValue* choice = NULL;
2177 if (!choices->GetDictionary(SectionToPrefString(section), &choice))
2178 return false;
2179
2180 choice->GetString(kGuidPrefKey, guid);
2181 return true;
2182 }
2183
2184 void AutofillDialogControllerImpl::LogOnFinishSubmitMetrics() {
2185 AutofillMetrics::LogDialogUiDuration(
2186 base::Time::Now() - dialog_shown_timestamp_,
2187 AutofillMetrics::DIALOG_ACCEPTED);
2188
2189 AutofillMetrics::LogDialogUiEvent(AutofillMetrics::DIALOG_UI_ACCEPTED);
2190
2191 AutofillMetrics::DialogDismissalState dismissal_state;
2192 if (!IsManuallyEditingAnySection()) {
2193 dismissal_state = AutofillMetrics::DIALOG_ACCEPTED_EXISTING_AUTOFILL_DATA;
2194 } else if (ShouldSaveDetailsLocally()) {
2195 dismissal_state = AutofillMetrics::DIALOG_ACCEPTED_SAVE_TO_AUTOFILL;
2196 } else {
2197 dismissal_state = AutofillMetrics::DIALOG_ACCEPTED_NO_SAVE;
2198 }
2199
2200 AutofillMetrics::LogDialogDismissalState(dismissal_state);
2201 }
2202
2203 void AutofillDialogControllerImpl::LogOnCancelMetrics() {
2204 AutofillMetrics::LogDialogUiEvent(AutofillMetrics::DIALOG_UI_CANCELED);
2205
2206 AutofillMetrics::DialogDismissalState dismissal_state;
2207 if (!IsManuallyEditingAnySection())
2208 dismissal_state = AutofillMetrics::DIALOG_CANCELED_NO_EDITS;
2209 else if (AllSectionsAreValid())
2210 dismissal_state = AutofillMetrics::DIALOG_CANCELED_NO_INVALID_FIELDS;
2211 else
2212 dismissal_state = AutofillMetrics::DIALOG_CANCELED_WITH_INVALID_FIELDS;
2213
2214 AutofillMetrics::LogDialogDismissalState(dismissal_state);
2215
2216 AutofillMetrics::LogDialogUiDuration(
2217 base::Time::Now() - dialog_shown_timestamp_,
2218 AutofillMetrics::DIALOG_CANCELED);
2219 }
2220
2221 void AutofillDialogControllerImpl::LogSuggestionItemSelectedMetric(
2222 const SuggestionsMenuModel& model) {
2223 DialogSection section = SectionForSuggestionsMenuModel(model);
2224
2225 AutofillMetrics::DialogUiEvent dialog_ui_event;
2226 if (model.GetItemKeyForCheckedItem() == kAddNewItemKey) {
2227 // Selected to add a new item.
2228 dialog_ui_event = common::DialogSectionToUiItemAddedEvent(section);
2229 } else if (IsASuggestionItemKey(model.GetItemKeyForCheckedItem())) {
2230 // Selected an existing item.
2231 dialog_ui_event = common::DialogSectionToUiSelectionChangedEvent(section);
2232 } else {
2233 // TODO(estade): add logging for "Manage items" or "Use billing for
2234 // shipping"?
2235 return;
2236 }
2237
2238 AutofillMetrics::LogDialogUiEvent(dialog_ui_event);
2239 }
2240
2241 void AutofillDialogControllerImpl::LogDialogLatencyToShow() {
2242 if (was_ui_latency_logged_)
2243 return;
2244
2245 AutofillMetrics::LogDialogLatencyToShow(base::Time::Now() -
2246 dialog_shown_timestamp_);
2247 was_ui_latency_logged_ = true;
2248 }
2249
2250 AutofillMetrics::DialogInitialUserStateMetric
2251 AutofillDialogControllerImpl::GetInitialUserState() const {
2252 // Consider a user to be an Autofill user if the user has any credit cards
2253 // or addresses saved. Check that the item count is greater than 2 because
2254 // an "empty" menu still has the "add new" menu item and "manage" menu item.
2255 const bool has_autofill_profiles =
2256 suggested_cc_.GetItemCount() > 2 ||
2257 suggested_billing_.GetItemCount() > 2;
2258
2259 return has_autofill_profiles
2260 ? AutofillMetrics::DIALOG_USER_NOT_SIGNED_IN_HAS_AUTOFILL
2261 : AutofillMetrics::DIALOG_USER_NOT_SIGNED_IN_NO_AUTOFILL;
2262 }
2263
2264 void AutofillDialogControllerImpl::OnSubmitButtonDelayEnd() {
2265 if (!view_)
2266 return;
2267 ScopedViewUpdates updates(view_.get());
2268 view_->UpdateButtonStrip();
2269 }
2270
2271 } // namespace autofill
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698