OLD | NEW |
| (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(¶ms); | |
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 | |
OLD | NEW |