| // Copyright 2017 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/autofill/core/browser/field_filler.h" |
| |
| #include <stdint.h> |
| #include <array> |
| #include <vector> |
| |
| #include "base/feature_list.h" |
| #include "base/i18n/case_conversion.h" |
| #include "base/i18n/string_search.h" |
| #include "base/ranges/algorithm.h" |
| #include "base/strings/strcat.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_split.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "components/autofill/core/browser/address_normalizer.h" |
| #include "components/autofill/core/browser/autofill_data_util.h" |
| #include "components/autofill/core/browser/autofill_field.h" |
| #include "components/autofill/core/browser/autofill_type.h" |
| #include "components/autofill/core/browser/data_model/autofill_data_model.h" |
| #include "components/autofill/core/browser/data_model/credit_card.h" |
| #include "components/autofill/core/browser/data_model/data_model_utils.h" |
| #include "components/autofill/core/browser/data_model/phone_number.h" |
| #include "components/autofill/core/browser/field_type_utils.h" |
| #include "components/autofill/core/browser/field_types.h" |
| #include "components/autofill/core/browser/form_parsing/credit_card_field.h" |
| #include "components/autofill/core/browser/geo/alternative_state_name_map.h" |
| #include "components/autofill/core/browser/geo/autofill_country.h" |
| #include "components/autofill/core/browser/geo/country_names.h" |
| #include "components/autofill/core/browser/geo/state_names.h" |
| #include "components/autofill/core/browser/proto/states.pb.h" |
| #include "components/autofill/core/common/autofill_features.h" |
| #include "components/autofill/core/common/autofill_l10n_util.h" |
| #include "components/autofill/core/common/autofill_regexes.h" |
| #include "components/autofill/core/common/autofill_util.h" |
| #include "components/strings/grit/components_strings.h" |
| #include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_data.h" |
| #include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_formatter.h" |
| #include "ui/base/l10n/l10n_util.h" |
| |
| using base::ASCIIToUTF16; |
| using base::StringToInt; |
| |
| namespace autofill { |
| |
| namespace { |
| |
| // Returns true if the value was successfully set, meaning |value| was found in |
| // the list of select options in |field|. Optionally, the caller may pass |
| // |best_match_index| which will be set to the index of the best match. |
| bool SetSelectControlValue(const std::u16string& value, |
| FormFieldData* field, |
| size_t* best_match_index, |
| std::string* failure_to_fill) { |
| l10n::CaseInsensitiveCompare compare; |
| |
| std::u16string best_match; |
| for (size_t i = 0; i < field->options.size(); ++i) { |
| const SelectOption& option = field->options[i]; |
| if (value == option.value || value == option.content) { |
| // An exact match, use it. |
| best_match = option.value; |
| if (best_match_index) |
| *best_match_index = i; |
| break; |
| } |
| |
| if (compare.StringsEqual(value, option.value) || |
| compare.StringsEqual(value, option.content)) { |
| // A match, but not in the same case. Save it in case an exact match is |
| // not found. |
| best_match = option.value; |
| if (best_match_index) |
| *best_match_index = i; |
| } |
| } |
| |
| if (best_match.empty()) { |
| if (failure_to_fill) { |
| *failure_to_fill += |
| "Did not find value to fill in select control element. "; |
| } |
| return false; |
| } |
| |
| field->value = best_match; |
| return true; |
| } |
| |
| // Like SetSelectControlValue, but searches within the field values and options |
| // for |value|. For example, "NC - North Carolina" would match "north carolina". |
| bool SetSelectControlValueSubstringMatch(const std::u16string& value, |
| bool ignore_whitespace, |
| FormFieldData* field, |
| std::string* failure_to_fill) { |
| int best_match = FieldFiller::FindShortestSubstringMatchInSelect( |
| value, ignore_whitespace, field); |
| |
| if (best_match >= 0) { |
| field->value = field->options[best_match].value; |
| return true; |
| } |
| |
| if (failure_to_fill) { |
| *failure_to_fill += |
| "Did not find substring match for filling select control element. "; |
| } |
| |
| return false; |
| } |
| |
| // Like SetSelectControlValue, but searches within the field values and options |
| // for |value|. First it tokenizes the options, then tries to match against |
| // tokens. For example, "NC - North Carolina" would match "nc" but not "ca". |
| bool SetSelectControlValueTokenMatch(const std::u16string& value, |
| FormFieldData* field, |
| std::string* failure_to_fill) { |
| const auto tokenize = [](const std::u16string& str) { |
| return base::SplitString(str, base::kWhitespaceASCIIAs16, |
| base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); |
| }; |
| l10n::CaseInsensitiveCompare compare; |
| const auto equals_value = [&](const std::u16string& rhs) { |
| return compare.StringsEqual(value, rhs); |
| }; |
| for (const SelectOption& option : field->options) { |
| if (base::ranges::any_of(tokenize(option.value), equals_value) || |
| base::ranges::any_of(tokenize(option.content), equals_value)) { |
| field->value = option.value; |
| return true; |
| } |
| } |
| |
| if (failure_to_fill) { |
| *failure_to_fill += |
| "Did not find token match for filling select control element. "; |
| } |
| |
| return false; |
| } |
| |
| // Helper method to normalize the |admin_area| for the given |country_code|. |
| // The value in |admin_area| will be overwritten. |
| bool NormalizeAdminAreaForCountryCode(std::u16string* admin_area, |
| const std::string& country_code, |
| AddressNormalizer* address_normalizer) { |
| DCHECK(address_normalizer); |
| DCHECK(admin_area); |
| if (admin_area->empty() || country_code.empty()) |
| return false; |
| |
| AutofillProfile tmp_profile; |
| tmp_profile.SetRawInfo(ADDRESS_HOME_COUNTRY, base::UTF8ToUTF16(country_code)); |
| tmp_profile.SetRawInfo(ADDRESS_HOME_STATE, *admin_area); |
| if (!address_normalizer->NormalizeAddressSync(&tmp_profile)) |
| return false; |
| |
| *admin_area = tmp_profile.GetRawInfo(ADDRESS_HOME_STATE); |
| return true; |
| } |
| |
| // Will normalize |value| and the options in |field| with |address_normalizer| |
| // (which should not be null), and return whether the fill was successful. |
| bool SetNormalizedStateSelectControlValue(const std::u16string& value, |
| FormFieldData* field, |
| const std::string& country_code, |
| AddressNormalizer* address_normalizer, |
| std::string* failure_to_fill) { |
| DCHECK(address_normalizer); |
| // We attempt to normalize a copy of the field value. If normalization was not |
| // successful, it means the rules were probably not loaded. Give up. Note that |
| // the normalizer will fetch the rule for next time it's called. |
| // TODO(crbug.com/788417): We should probably sanitize |value| before |
| // normalizing. |
| std::u16string field_value = value; |
| if (!NormalizeAdminAreaForCountryCode(&field_value, country_code, |
| address_normalizer)) { |
| if (failure_to_fill) |
| *failure_to_fill += "Could not normalize admin area for country code. "; |
| return false; |
| } |
| |
| // If successful, try filling the normalized value with the existing field |
| // |options|. |
| if (SetSelectControlValue(field_value, field, /*best_match_index=*/nullptr, |
| failure_to_fill)) |
| return true; |
| |
| // Normalize the |field| options in place, using a copy. |
| // TODO(crbug.com/788417): We should probably sanitize the values in |
| // |field_copy| before normalizing. |
| FormFieldData field_copy(*field); |
| bool normalized = false; |
| for (SelectOption& option : field_copy.options) { |
| normalized |= NormalizeAdminAreaForCountryCode(&option.value, country_code, |
| address_normalizer); |
| normalized |= NormalizeAdminAreaForCountryCode( |
| &option.content, country_code, address_normalizer); |
| } |
| |
| // If at least some normalization happened on the field options, try filling |
| // them with |field_value|. |
| size_t best_match_index = 0; |
| if (normalized && SetSelectControlValue(field_value, &field_copy, |
| &best_match_index, failure_to_fill)) { |
| // |best_match_index| now points to the option in |field->options| |
| // that corresponds to our best match. Update |field| with the answer. |
| field->value = field->options[best_match_index].value; |
| return true; |
| } |
| |
| if (failure_to_fill) |
| *failure_to_fill += "Could not set normalized state in control element. "; |
| |
| return false; |
| } |
| |
| // Try to fill a numeric |value| into the given |field|. |
| bool FillNumericSelectControl(int value, |
| FormFieldData* field, |
| std::string* failure_to_fill) { |
| for (const SelectOption& option : field->options) { |
| int num; |
| if ((StringToInt(option.value, &num) && num == value) || |
| (StringToInt(option.content, &num) && num == value)) { |
| field->value = option.value; |
| return true; |
| } |
| } |
| |
| if (failure_to_fill) { |
| *failure_to_fill += |
| "Did not find numeric value to fill in select control element. "; |
| } |
| return false; |
| } |
| |
| bool FillStateSelectControl(const std::u16string& value, |
| FormFieldData* field, |
| const std::string& country_code, |
| AddressNormalizer* address_normalizer, |
| std::string* failure_to_fill) { |
| std::vector<std::u16string> abbreviations; |
| std::vector<std::u16string> full_names; |
| |
| // Fetch the corresponding entry from AlternativeStateNameMap. |
| absl::optional<StateEntry> state_entry = |
| AlternativeStateNameMap::GetInstance()->GetEntry( |
| AlternativeStateNameMap::CountryCode(country_code), |
| AlternativeStateNameMap::StateName(value)); |
| if (state_entry) { |
| for (const auto& abbr : state_entry->abbreviations()) { |
| abbreviations.push_back(base::UTF8ToUTF16(abbr)); |
| } |
| if (state_entry->has_canonical_name()) { |
| full_names.push_back(base::UTF8ToUTF16(state_entry->canonical_name())); |
| } |
| for (const auto& alternative_name : state_entry->alternative_names()) { |
| full_names.push_back(base::UTF8ToUTF16(alternative_name)); |
| } |
| } else { |
| if (value.size() > 2) { |
| full_names.push_back(value); |
| } else { |
| abbreviations.push_back(value); |
| } |
| } |
| |
| std::u16string state_name; |
| std::u16string state_abbreviation; |
| // |abbreviation| will be empty for non-US countries. |
| state_names::GetNameAndAbbreviation(value, &state_name, &state_abbreviation); |
| |
| if (!state_name.empty()) |
| full_names.push_back(std::move(state_name)); |
| |
| if (!state_abbreviation.empty()) |
| abbreviations.push_back(std::move(state_abbreviation)); |
| |
| // Remove `abbreviations` from the `full_names` as a precautionary measure in |
| // case the `AlternativeStateNameMap` contains bad data. |
| base::ranges::sort(abbreviations); |
| full_names.erase( |
| base::ranges::remove_if(full_names, |
| [&](const std::u16string& full_name) { |
| return base::ranges::binary_search( |
| abbreviations, full_name); |
| }), |
| full_names.end()); |
| |
| // Try an exact match of the abbreviation first. |
| for (const auto& abbreviation : abbreviations) { |
| if (!abbreviation.empty() && |
| SetSelectControlValue(abbreviation, field, /*best_match_index=*/nullptr, |
| failure_to_fill)) { |
| return true; |
| } |
| } |
| |
| // Try an exact match of the full name. |
| for (const auto& full : full_names) { |
| if (!full.empty() && |
| SetSelectControlValue(full, field, /*best_match_index=*/nullptr, |
| failure_to_fill)) { |
| return true; |
| } |
| } |
| |
| // Try an inexact match of the full name. |
| for (const auto& full : full_names) { |
| if (!full.empty() && SetSelectControlValueSubstringMatch(full, false, field, |
| failure_to_fill)) { |
| return true; |
| } |
| } |
| |
| // Try an inexact match of the abbreviation name. |
| for (const auto& abbreviation : abbreviations) { |
| if (!abbreviation.empty() && |
| SetSelectControlValueTokenMatch(abbreviation, field, failure_to_fill)) { |
| return true; |
| } |
| } |
| |
| // Try to match a normalized |value| of the state and the |field| options. |
| if (address_normalizer && |
| SetNormalizedStateSelectControlValue( |
| value, field, country_code, address_normalizer, failure_to_fill)) { |
| return true; |
| } |
| |
| if (failure_to_fill) |
| *failure_to_fill += "Could not fill state in select control element. "; |
| |
| return false; |
| } |
| |
| bool FillCountrySelectControl(const std::u16string& value, |
| FormFieldData* field_data, |
| std::string* failure_to_fill) { |
| std::string country_code = CountryNames::GetInstance()->GetCountryCode(value); |
| if (country_code.empty()) { |
| if (failure_to_fill) |
| *failure_to_fill += "Cannot fill empty country code. "; |
| return false; |
| } |
| |
| for (const SelectOption& option : field_data->options) { |
| // Canonicalize each <option> value to a country code, and compare to the |
| // target country code. |
| if (country_code == |
| CountryNames::GetInstance()->GetCountryCode(option.value) || |
| country_code == |
| CountryNames::GetInstance()->GetCountryCode(option.content)) { |
| field_data->value = option.value; |
| return true; |
| } |
| } |
| |
| if (failure_to_fill) { |
| *failure_to_fill += |
| "Did not find country to fill in select control element. "; |
| } |
| return false; |
| } |
| |
| // Attempt to fill the user's expiration month |value| inside the <select> |
| // or <selectlist> |field|. Since |value| is well defined but the website's |
| // |field| option values may not be, some heuristics are run to cover all |
| // observed cases. |
| bool FillExpirationMonthSelectControl(const std::u16string& value, |
| const std::string& app_locale, |
| FormFieldData* field, |
| std::string* failure_to_fill) { |
| // |value| is defined to be between 1 and 12, inclusively. |
| int month = 0; |
| if (!StringToInt(value, &month) || month <= 0 || month > 12) { |
| if (failure_to_fill) |
| *failure_to_fill += "Cannot parse month, or value is <=0 or >12. "; |
| return false; |
| } |
| |
| // Trim the whitespace and specific prefixes used in AngularJS from the |
| // select values before attempting to convert them to months. |
| std::vector<std::u16string> trimmed_values(field->options.size()); |
| const std::u16string kNumberPrefix = u"number:"; |
| const std::u16string kStringPrefix = u"string:"; |
| for (size_t i = 0; i < field->options.size(); ++i) { |
| base::TrimWhitespace(field->options[i].value, base::TRIM_ALL, |
| &trimmed_values[i]); |
| base::ReplaceFirstSubstringAfterOffset(&trimmed_values[i], 0, kNumberPrefix, |
| u""); |
| base::ReplaceFirstSubstringAfterOffset(&trimmed_values[i], 0, kStringPrefix, |
| u""); |
| } |
| |
| if (trimmed_values.size() == 12) { |
| // The select presumable only contains the year's months. |
| // If the first value of the select is 0, decrement the value of |month| so |
| // January is associated with 0 instead of 1. |
| int first_value; |
| if (StringToInt(trimmed_values[0], &first_value) && first_value == 0) |
| --month; |
| } else if (trimmed_values.size() == 13) { |
| // The select presumably uses the first value as a placeholder. |
| int first_value; |
| // If the first value is not a number or is a negative one, check the second |
| // value and apply the same logic as if there was no placeholder. |
| if (!StringToInt(trimmed_values[0], &first_value) || first_value < 0) { |
| int second_value; |
| if (StringToInt(trimmed_values[1], &second_value) && second_value == 0) |
| --month; |
| } else if (first_value == 1) { |
| // If the first value of the select is 1, increment the value of |month| |
| // to skip the placeholder value (January = 2). |
| ++month; |
| } |
| } |
| |
| // Attempt to match the user's |month| with the field's value attributes. |
| DCHECK_EQ(trimmed_values.size(), field->options.size()); |
| for (size_t i = 0; i < trimmed_values.size(); ++i) { |
| int converted_value = 0; |
| // We use the trimmed value to match with |month|, but the original select |
| // value to fill the field (otherwise filling wouldn't work). |
| if (data_util::ParseExpirationMonth(trimmed_values[i], app_locale, |
| &converted_value) && |
| month == converted_value) { |
| field->value = field->options[i].value; |
| return true; |
| } |
| } |
| |
| // Attempt to match with each of the options' content. |
| for (const SelectOption& option : field->options) { |
| int converted_contents = 0; |
| if (data_util::ParseExpirationMonth(option.content, app_locale, |
| &converted_contents) && |
| month == converted_contents) { |
| field->value = option.value; |
| return true; |
| } |
| } |
| |
| return FillNumericSelectControl(month, field, failure_to_fill); |
| } |
| |
| // Returns true if the last two digits in |year| match those in |str|. |
| bool LastTwoDigitsMatch(const std::u16string& year, const std::u16string& str) { |
| int year_int; |
| int str_int; |
| if (!StringToInt(year, &year_int) || !StringToInt(str, &str_int)) |
| return false; |
| |
| return (year_int % 100) == (str_int % 100); |
| } |
| |
| // Try to fill a year |value| into the given |field| by comparing the last two |
| // digits of the year to the field's options. |
| bool FillYearSelectControl(const std::u16string& value, |
| FormFieldData* field, |
| std::string* failure_to_fill) { |
| if (value.size() != 2U && value.size() != 4U) { |
| if (failure_to_fill) |
| *failure_to_fill += "Year to fill does not have length 2 or 4. "; |
| return false; |
| } |
| |
| for (const SelectOption& option : field->options) { |
| if (LastTwoDigitsMatch(value, option.value) || |
| LastTwoDigitsMatch(value, option.content)) { |
| field->value = option.value; |
| return true; |
| } |
| } |
| |
| if (failure_to_fill) { |
| *failure_to_fill += |
| "Year to fill was not found in select control element. "; |
| } |
| |
| return false; |
| } |
| |
| // Try to fill a credit card type |value| (Visa, Mastercard, etc.) into the |
| // given |field|. We ignore whitespace when filling credit card types to |
| // allow for cases such as "Master card". |
| |
| bool FillCreditCardTypeSelectControl(const std::u16string& value, |
| FormFieldData* field, |
| std::string* failure_to_fill) { |
| if (SetSelectControlValueSubstringMatch(value, /* ignore_whitespace= */ true, |
| field, failure_to_fill)) { |
| return true; |
| } |
| if (value == l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_AMEX)) { |
| // For American Express, also try filling as "AmEx". |
| return SetSelectControlValueSubstringMatch(u"AmEx", |
| /* ignore_whitespace= */ true, |
| field, failure_to_fill); |
| } |
| |
| if (failure_to_fill) |
| *failure_to_fill += "Failed to fill credit card type. "; |
| return false; |
| } |
| |
| std::u16string TruncateCardNumberIfNecessary(const AutofillField& field, |
| const std::u16string& value) { |
| // Take the substring of the credit card number starting from the offset |
| // and ending at the field's max_length (or the entire string if |
| // max_length is 0). |
| // If the offset is greater than the length of the string, then the entire |
| // number should be returned; |
| if (field.credit_card_number_offset() < value.length()) { |
| return value.substr( |
| field.credit_card_number_offset(), |
| field.max_length > 0 ? field.max_length : std::u16string::npos); |
| } |
| return value; |
| } |
| |
| // Returns the appropriate credit card number from |credit_card|. Truncates the |
| // credit card number to be split across HTML form input fields depending on if |
| // 'field.credit_card_number_offset()' is less than the length of the credit |
| // card number. |
| std::u16string GetCreditCardNumberForInput( |
| const CreditCard& credit_card, |
| const AutofillField& field, |
| const std::string& app_locale, |
| mojom::AutofillActionPersistence action_persistence) { |
| std::u16string value; |
| |
| if (action_persistence == mojom::AutofillActionPersistence::kPreview) { |
| // A single field is detected when the offset begins at 0 and the field's |
| // max_length can hold the entire obfuscated credit card number. |
| bool is_single_field = |
| (field.credit_card_number_offset() == 0 && |
| (field.max_length == 0 || |
| field.max_length >= |
| credit_card.ObfuscatedNumberWithVisibleLastFourDigits() |
| .length())); |
| |
| // If previewing a credit card number that needs to be split, pad the number |
| // to 16 digits rather than displaying a fancy string with RTL support. This |
| // also returns 16 digits if there is only one field and it cannot fit the |
| // longer version CC number. |
| value = |
| is_single_field |
| ? credit_card.ObfuscatedNumberWithVisibleLastFourDigits() |
| : credit_card |
| .ObfuscatedNumberWithVisibleLastFourDigitsForSplitFields(); |
| } else { |
| value = credit_card.GetInfo(CREDIT_CARD_NUMBER, app_locale); |
| } |
| |
| // Check to truncate card number based on the field's credit card number |
| // offset and length of the credit card number. |
| return TruncateCardNumberIfNecessary(field, value); |
| } |
| |
| // Returns the appropriate credit card number from |virtual_card|. Truncates the |
| // credit card number to be split across HTML form input fields depending on if |
| // 'field.credit_card_number_offset()' is less than the length of the credit |
| // card number. |
| std::u16string GetVirtualCardNumberForPreviewInput( |
| const CreditCard& virtual_card, |
| const AutofillField& field) { |
| std::u16string value = |
| l10n_util::GetStringUTF16( |
| IDS_AUTOFILL_VIRTUAL_CARD_SUGGESTION_OPTION_VALUE) + |
| u" " + virtual_card.CardNameAndLastFourDigits(); |
| |
| // |field|'s max_length truncates the credit card number to fit within. |
| if (field.credit_card_number_offset() < value.length()) { |
| // A single field is detected when the offset begins at 0 and the field's |
| // max_length can hold the entire obfuscated credit card number. |
| bool is_single_field = |
| (field.credit_card_number_offset() == 0 && |
| (field.max_length == 0 || field.max_length >= value.length())); |
| |
| if (!is_single_field) { |
| value = virtual_card |
| .ObfuscatedNumberWithVisibleLastFourDigitsForSplitFields(); |
| } |
| |
| // Check to truncate card number based on the field's credit card number |
| // offset and length of the credit card number. |
| return TruncateCardNumberIfNecessary(field, value); |
| } |
| |
| return value; |
| } |
| |
| // Returns the credit card CVC for Preview or Fill. |
| std::u16string GetCreditCardVerificationCodeForInput( |
| const CreditCard& credit_card, |
| mojom::AutofillActionPersistence action_persistence, |
| const std::u16string& cvc) { |
| const std::u16string cvc_candidate = |
| credit_card.cvc().empty() ? cvc : credit_card.cvc(); |
| // If CVC is empty we will not return anything. |
| if (cvc_candidate.empty()) { |
| return u""; |
| } |
| switch (action_persistence) { |
| case mojom::AutofillActionPersistence::kFill: |
| return cvc_candidate; |
| // For preview, we will mask CVC with dots. |
| case mojom::AutofillActionPersistence::kPreview: |
| return CreditCard::GetMidlineEllipsisDots(cvc_candidate.length()); |
| } |
| } |
| |
| // Fills in the select or selectlist control |field| with |value|. If an exact |
| // match is not found, falls back to alternate filling strategies based on the |
| // |type|. |
| bool FillSelectOrSelectListControl( |
| const AutofillType& type, |
| const std::u16string& value, |
| absl::variant<const AutofillProfile*, const CreditCard*> |
| profile_or_credit_card, |
| const std::string& app_locale, |
| FormFieldData* field, |
| AddressNormalizer* address_normalizer, |
| std::string* failure_to_fill) { |
| DCHECK(field->IsSelectOrSelectListElement()); |
| |
| ServerFieldType storable_type = type.GetStorableType(); |
| |
| // Credit card expiration month is checked first since an exact match on value |
| // may not be correct. |
| if (storable_type == CREDIT_CARD_EXP_MONTH) |
| return FillExpirationMonthSelectControl(value, app_locale, field, |
| failure_to_fill); |
| |
| // Search for exact matches. |
| if (SetSelectControlValue(value, field, /*best_match_index=*/nullptr, |
| failure_to_fill)) |
| return true; |
| |
| // If that fails, try specific fallbacks based on the field type. |
| if (storable_type == ADDRESS_HOME_STATE) { |
| DCHECK(absl::holds_alternative<const AutofillProfile*>( |
| profile_or_credit_card)); |
| const std::string country_code = data_util::GetCountryCodeWithFallback( |
| *absl::get<const AutofillProfile*>(profile_or_credit_card), app_locale); |
| return FillStateSelectControl(value, field, country_code, |
| address_normalizer, failure_to_fill); |
| } |
| if (storable_type == ADDRESS_HOME_COUNTRY) |
| return FillCountrySelectControl(value, field, failure_to_fill); |
| if (storable_type == CREDIT_CARD_EXP_2_DIGIT_YEAR || |
| storable_type == CREDIT_CARD_EXP_4_DIGIT_YEAR) { |
| return FillYearSelectControl(value, field, failure_to_fill); |
| } |
| if (storable_type == CREDIT_CARD_TYPE) |
| return FillCreditCardTypeSelectControl(value, field, failure_to_fill); |
| |
| return false; |
| } |
| |
| // Gets the appropriate expiration date from the |card| for a month control |
| // field. (i.e. a <input type="month">) |
| std::u16string GetExpirationForMonthControl(const CreditCard& card) { |
| return card.Expiration4DigitYearAsString() + u"-" + |
| card.Expiration2DigitMonthAsString(); |
| } |
| |
| // Returns appropriate street address for |field|. Translates newlines into |
| // equivalent separators when necessary, i.e. when filling a single-line field. |
| // The separators depend on |address_language_code|. |
| std::u16string GetStreetAddressForInput( |
| const std::u16string& address_value, |
| const std::string& address_language_code, |
| FormFieldData* field) { |
| if (field->form_control_type == StringToFormControlType("textarea")) { |
| return address_value; |
| } |
| |
| ::i18n::addressinput::AddressData address_data; |
| address_data.language_code = address_language_code; |
| address_data.address_line = |
| base::SplitString(base::UTF16ToUTF8(address_value), "\n", |
| base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); |
| std::string line; |
| ::i18n::addressinput::GetStreetAddressLinesAsSingleLine(address_data, &line); |
| return base::UTF8ToUTF16(line); |
| } |
| |
| // Returns appropriate state value that matches |field|. |
| // The canonical state is checked if it fits in the field and at last the |
| // abbreviations are tried. Does not return a state if neither |state_value| nor |
| // the canonical state name nor its abbreviation fit into the field. |
| std::u16string GetStateTextForInput(const std::u16string& state_value, |
| const std::string& country_code, |
| FormFieldData* field, |
| std::string* failure_to_fill) { |
| if (field->max_length == 0 || field->max_length >= state_value.size()) |
| // Return the state value directly. |
| return state_value; |
| |
| absl::optional<StateEntry> state = |
| AlternativeStateNameMap::GetInstance()->GetEntry( |
| AlternativeStateNameMap::CountryCode(country_code), |
| AlternativeStateNameMap::StateName(state_value)); |
| if (state) { |
| // Return the canonical state name if possible. |
| if (state->has_canonical_name() && !state->canonical_name().empty() && |
| field->max_length >= state->canonical_name().size()) { |
| return base::UTF8ToUTF16(state->canonical_name()); |
| } |
| |
| // Return the abbreviation if possible. |
| for (const auto& abbr : state->abbreviations()) { |
| if (!abbr.empty() && field->max_length >= abbr.size()) { |
| return base::i18n::ToUpper(base::UTF8ToUTF16(abbr)); |
| } |
| } |
| } |
| |
| // Return with the state abbreviation. |
| std::u16string abbreviation; |
| state_names::GetNameAndAbbreviation(state_value, nullptr, &abbreviation); |
| if (!abbreviation.empty() && field->max_length >= abbreviation.size()) |
| return base::i18n::ToUpper(abbreviation); |
| |
| if (failure_to_fill) |
| *failure_to_fill += "Could not fit raw state nor abbreviation. "; |
| return std::u16string(); |
| } |
| |
| // Returns the appropriate expiration year from |credit_card| for the field. |
| // Uses the |field|'s type and the |field|'s max_length attribute to |
| // determine if the year needs to be truncated. |
| std::u16string GetExpirationYearForInput(const CreditCard& credit_card, |
| const AutofillField& field) { |
| const size_t year_length = |
| DetermineExpirationYearLength(field, field.Type().GetStorableType()); |
| std::u16string value = year_length == 2 |
| ? credit_card.Expiration2DigitYearAsString() |
| : credit_card.Expiration4DigitYearAsString(); |
| |
| // In case the field's max_length is less than the length of the year, shorten |
| // the year to field.max_length. |
| if (field.max_length != 0 && field.max_length < value.length()) { |
| value = value.substr(value.length() - field.max_length, field.max_length); |
| } |
| |
| return value; |
| } |
| |
| // Returns the appropriate virtual card expiration year for the field. Uses the |
| // |field_type| and the |field|'s max_length attribute to determine if the year |
| // needs to be truncated. |
| std::u16string GetExpirationYearForVirtualCardPreviewInput( |
| ServerFieldType storable_type, |
| const AutofillField& field) { |
| if (storable_type == CREDIT_CARD_EXP_2_DIGIT_YEAR && |
| (field.max_length == 2 || field.max_length == 0)) { |
| return CreditCard::GetMidlineEllipsisDots(2); |
| } else if (storable_type == CREDIT_CARD_EXP_4_DIGIT_YEAR && |
| (field.max_length == 4 || field.max_length == 0)) { |
| return CreditCard::GetMidlineEllipsisDots(4); |
| } |
| |
| if (field.max_length > 4) { |
| return CreditCard::GetMidlineEllipsisDots(4); |
| } else { |
| return CreditCard::GetMidlineEllipsisDots(field.max_length); |
| } |
| } |
| |
| // Returns the appropriate expiration date from |credit_card| for the field |
| // based on the |field_type|. If the field contains a recognized date format |
| // string, the function follows that format. Otherwise, it uses the |field|'s |
| // max_length attribute to determine if the |value| needs to be truncated or |
| // padded. Returns an empty string in case of a failure. |
| std::u16string GetExpirationDateForInput(const CreditCard& credit_card, |
| const AutofillField& field, |
| std::string* failure_to_fill) { |
| std::u16string mm = credit_card.Expiration2DigitMonthAsString(); |
| std::u16string yy = credit_card.Expiration2DigitYearAsString(); |
| std::u16string yyyy = credit_card.Expiration4DigitYearAsString(); |
| |
| ServerFieldType field_type = field.Type().GetStorableType(); |
| // At this point the field type is determined, so we pass it even as |
| // `forced_field_type`. |
| CreditCardField::ExpirationDateFormat format; |
| if (base::FeatureList::IsEnabled( |
| features::kAutofillEnableExpirationDateImprovements)) { |
| format = CreditCardField::DetermineExpirationDateFormat( |
| field, /*fallback_type=*/field_type, |
| /*server_hint=*/field_type, /*forced_field_type=*/field_type); |
| } else { |
| // Before the experiment, the type was not fully determined yet. That |
| // happened at field filling time like in this else-branch. |
| ServerFieldType server_hint = field.server_type(); |
| ServerFieldType forced_field_type = |
| field.server_type_prediction_is_override() ? server_hint |
| : NO_SERVER_DATA; |
| ServerFieldType fallback_type = field.Type().GetStorableType(); |
| format = CreditCardField::DetermineExpirationDateFormat( |
| field, /*fallback_type=*/fallback_type, |
| /*server_hint=*/server_hint, /*forced_field_type=*/forced_field_type); |
| } |
| |
| std::u16string expiration_candidate = |
| base::StrCat({mm, format.separator, |
| format.digits_in_expiration_year == 4 ? yyyy : yy}); |
| if (field.max_length != 0 && |
| expiration_candidate.length() > field.max_length) { |
| if (failure_to_fill) { |
| *failure_to_fill += |
| "Field to fill must have a max length of at least 4. "; |
| } |
| return std::u16string(); |
| } |
| return expiration_candidate; |
| } |
| |
| // Returns the appropriate virtual_card expiration date from for the field based |
| // on the |field|'s max_length. Returns an empty string in case of a failure. |
| std::u16string GetExpirationDateForVirtualCardPreviewInput( |
| const AutofillField& field, |
| std::string* failure_to_fill) { |
| switch (field.max_length) { |
| case 1: |
| case 2: |
| case 3: |
| if (failure_to_fill) { |
| *failure_to_fill += |
| "Field to fill must have a max length of at least 4. "; |
| } |
| return std::u16string(); |
| case 4: |
| // Expects MMYY |
| return CreditCard::GetMidlineEllipsisDots(4); |
| case 5: |
| // Expects MM/YY |
| return CreditCard::GetMidlineEllipsisDots(2) + u"/" + |
| CreditCard::GetMidlineEllipsisDots(2); |
| case 6: |
| // Expects MMYYYY |
| return CreditCard::GetMidlineEllipsisDots(2) + |
| CreditCard::GetMidlineEllipsisDots(4); |
| default: |
| // Return MM/YYYY for default case |
| return CreditCard::GetMidlineEllipsisDots(2) + u"/" + |
| CreditCard::GetMidlineEllipsisDots(4); |
| } |
| } |
| |
| std::u16string RemoveWhitespace(const std::u16string& value) { |
| std::u16string stripped_value; |
| base::RemoveChars(value, base::kWhitespaceUTF16, &stripped_value); |
| return stripped_value; |
| } |
| |
| // Finds the best suitable option in the |field| that corresponds to the |
| // |country_code|. |
| // If the exact match is not found, extracts the digits (ignoring leading '00' |
| // or '+') from each option and compares them with the |country_code|. |
| std::u16string GetPhoneCountryCodeSelectControlForInput( |
| const std::u16string& country_code, |
| FormFieldData* field, |
| std::string* failure_to_fill) { |
| if (country_code.empty()) |
| return std::u16string(); |
| |
| // Find the option that exactly matches the |country_code|. |
| if (SetSelectControlValue(country_code, field, /*best_match_index=*/nullptr, |
| failure_to_fill)) { |
| return country_code; |
| } |
| |
| for (const SelectOption& option : field->options) { |
| std::u16string cc_candidate_in_value = |
| data_util::FindPossiblePhoneCountryCode(RemoveWhitespace(option.value)); |
| std::u16string cc_candidate_in_content = |
| data_util::FindPossiblePhoneCountryCode( |
| RemoveWhitespace(option.content)); |
| if (cc_candidate_in_value == country_code || |
| cc_candidate_in_content == country_code) { |
| return option.value; |
| } |
| } |
| return std::u16string(); |
| } |
| |
| // Returns the appropriate |credit_card| value based on |storable_type| to fill |
| // into |field|. |
| std::u16string GetValueForCreditCard( |
| const CreditCard& credit_card, |
| const std::u16string& cvc, |
| const std::string& app_locale, |
| mojom::AutofillActionPersistence action_persistence, |
| const AutofillField& field, |
| std::string* failure_to_fill) { |
| ServerFieldType storable_type = field.Type().GetStorableType(); |
| |
| if (field.form_control_type == StringToFormControlType("month")) { |
| return GetExpirationForMonthControl(credit_card); |
| } else { |
| switch (storable_type) { |
| case CREDIT_CARD_VERIFICATION_CODE: |
| case CREDIT_CARD_STANDALONE_VERIFICATION_CODE: |
| return GetCreditCardVerificationCodeForInput(credit_card, |
| action_persistence, cvc); |
| case CREDIT_CARD_NUMBER: |
| return GetCreditCardNumberForInput(credit_card, field, app_locale, |
| action_persistence); |
| case CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR: |
| case CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR: |
| return GetExpirationDateForInput(credit_card, field, failure_to_fill); |
| case CREDIT_CARD_EXP_2_DIGIT_YEAR: |
| case CREDIT_CARD_EXP_4_DIGIT_YEAR: |
| return GetExpirationYearForInput(credit_card, field); |
| default: |
| // All other cases handled here. |
| return credit_card.GetInfo(storable_type, app_locale); |
| } |
| } |
| } |
| |
| // Returns the appropriate |profile| value based on |type| to fill |
| // into |field|. |
| std::u16string GetValueForProfile(const AutofillProfile& profile, |
| const std::string& app_locale, |
| const AutofillField& field, |
| FormFieldData* field_data, |
| std::string* failure_to_fill) { |
| const AutofillType type = field.Type(); |
| std::u16string value = profile.GetInfo(type, app_locale); |
| |
| if (type.group() == FieldTypeGroup::kPhone) { |
| // If the `field_data` is a selection box and having the type |
| // `PHONE_HOME_COUNTRY_CODE`, call |
| // `GetPhoneCountryCodeSelectControlForInput`. |
| if (field_data->IsSelectOrSelectListElement() && |
| type.GetStorableType() == PHONE_HOME_COUNTRY_CODE) { |
| value = GetPhoneCountryCodeSelectControlForInput(value, field_data, |
| failure_to_fill); |
| } else { |
| value = FieldFiller::GetPhoneNumberValueForInput( |
| field_data->max_length, value, |
| profile.GetInfo(PHONE_HOME_CITY_AND_NUMBER, app_locale)); |
| } |
| } else if (type.GetStorableType() == ADDRESS_HOME_STREET_ADDRESS) { |
| const std::string& profile_language_code = profile.language_code(); |
| value = GetStreetAddressForInput(value, profile_language_code, field_data); |
| } else if (type.GetStorableType() == ADDRESS_HOME_STATE) { |
| const std::string& country_code = |
| data_util::GetCountryCodeWithFallback(profile, app_locale); |
| value = |
| GetStateTextForInput(value, country_code, field_data, failure_to_fill); |
| } |
| return value; |
| } |
| |
| // Returns the appropriate |virtual_card| value based on |storable_type| to |
| // preview into |field|. |
| std::u16string GetValueForVirtualCardPreview(const CreditCard& virtual_card, |
| const std::string& app_locale, |
| const AutofillField& field, |
| std::string* failure_to_fill) { |
| DCHECK_EQ(virtual_card.record_type(), CreditCard::RecordType::kVirtualCard); |
| |
| ServerFieldType storable_type = field.Type().GetStorableType(); |
| |
| switch (storable_type) { |
| case CREDIT_CARD_VERIFICATION_CODE: |
| case CREDIT_CARD_STANDALONE_VERIFICATION_CODE: |
| // For preview virtual card CVC, return three dots unless for American |
| // Express, which uses 4-digit CVCs. |
| return virtual_card.network() == kAmericanExpressCard |
| ? CreditCard::GetMidlineEllipsisDots(4) |
| : CreditCard::GetMidlineEllipsisDots(3); |
| case CREDIT_CARD_NUMBER: |
| return GetVirtualCardNumberForPreviewInput(virtual_card, field); |
| case CREDIT_CARD_EXP_MONTH: |
| // Expects MM |
| return CreditCard::GetMidlineEllipsisDots(2); |
| case CREDIT_CARD_EXP_2_DIGIT_YEAR: |
| case CREDIT_CARD_EXP_4_DIGIT_YEAR: |
| return GetExpirationYearForVirtualCardPreviewInput(storable_type, field); |
| case CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR: |
| case CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR: |
| return GetExpirationDateForVirtualCardPreviewInput(field, |
| failure_to_fill); |
| default: |
| // All other cases handled here. |
| return virtual_card.GetInfo(storable_type, app_locale); |
| } |
| } |
| |
| } // namespace |
| |
| FieldFiller::FieldFiller(const std::string& app_locale, |
| AddressNormalizer* address_normalizer) |
| : app_locale_(app_locale), address_normalizer_(address_normalizer) {} |
| |
| FieldFiller::~FieldFiller() {} |
| |
| std::u16string FieldFiller::GetValueForFilling( |
| const AutofillField& field, |
| absl::variant<const AutofillProfile*, const CreditCard*> |
| profile_or_credit_card, |
| FormFieldData* field_data, |
| const std::u16string& cvc, |
| mojom::AutofillActionPersistence action_persistence, |
| std::string* failure_to_fill) { |
| std::u16string value; |
| DCHECK(field_data); |
| |
| if (absl::holds_alternative<const CreditCard*>(profile_or_credit_card)) { |
| const CreditCard* credit_card = |
| absl::get<const CreditCard*>(profile_or_credit_card); |
| |
| if (credit_card->record_type() == CreditCard::RecordType::kVirtualCard && |
| action_persistence == mojom::AutofillActionPersistence::kPreview) { |
| value = GetValueForVirtualCardPreview(*credit_card, app_locale_, field, |
| failure_to_fill); |
| } else { |
| value = GetValueForCreditCard(*credit_card, cvc, app_locale_, |
| action_persistence, field, failure_to_fill); |
| } |
| } |
| |
| // Grab AutofillProfile data. |
| else { |
| DCHECK(absl::holds_alternative<const AutofillProfile*>( |
| profile_or_credit_card)); |
| const AutofillProfile* profile = |
| absl::get<const AutofillProfile*>(profile_or_credit_card); |
| |
| value = GetValueForProfile(*profile, app_locale_, field, field_data, |
| failure_to_fill); |
| } |
| |
| return value; |
| } |
| |
| bool FieldFiller::FillFormField( |
| const AutofillField& field, |
| absl::variant<const AutofillProfile*, const CreditCard*> |
| profile_or_credit_card, |
| const std::map<FieldGlobalId, std::u16string>& forced_fill_values, |
| FormFieldData* field_data, |
| const std::u16string& cvc, |
| mojom::AutofillActionPersistence action_persistence, |
| std::string* failure_to_fill) { |
| const AutofillType type = field.Type(); |
| |
| auto it = forced_fill_values.find(field.global_id()); |
| bool value_is_an_override = it != forced_fill_values.end(); |
| std::u16string value = |
| value_is_an_override |
| ? it->second |
| : GetValueForFilling(field, profile_or_credit_card, field_data, cvc, |
| action_persistence, failure_to_fill); |
| |
| // Do not attempt to fill empty values as it would skew the metrics. |
| if (value.empty()) { |
| if (failure_to_fill) |
| *failure_to_fill += "No value to fill available. "; |
| return false; |
| } |
| if (field.IsSelectOrSelectListElement()) { |
| return FillSelectOrSelectListControl(type, value, profile_or_credit_card, |
| app_locale_, field_data, |
| address_normalizer_, failure_to_fill); |
| } |
| field_data->value = value; |
| if (value_is_an_override) |
| field_data->force_override = true; |
| return true; |
| } |
| |
| // static |
| std::u16string FieldFiller::GetPhoneNumberValueForInput( |
| uint64_t field_max_length, |
| const std::u16string& number, |
| const std::u16string& city_and_number) { |
| // If no max length was specified, return the complete number. |
| if (field_max_length == 0) { |
| return number; |
| } |
| |
| if (number.length() > field_max_length) { |
| // Try after removing the country code, if |number| exceeds the maximum size |
| // of the field. |
| if (city_and_number.length() <= field_max_length) { |
| return city_and_number; |
| } |
| |
| // If |number| exceeds the maximum size of the field, cut the first part to |
| // provide a valid number for the field. For example, the number 15142365264 |
| // with a field with a max length of 10 would return 5142365264, thus |
| // filling in the last |field_data.max_length| characters from the |number|. |
| return number.substr(number.length() - field_max_length, field_max_length); |
| } |
| |
| return number; |
| } |
| |
| // static |
| int FieldFiller::FindShortestSubstringMatchInSelect( |
| const std::u16string& value, |
| bool ignore_whitespace, |
| const FormFieldData* field) { |
| int best_match = -1; |
| |
| std::u16string value_stripped = |
| ignore_whitespace ? RemoveWhitespace(value) : value; |
| base::i18n::FixedPatternStringSearchIgnoringCaseAndAccents searcher( |
| value_stripped); |
| for (size_t i = 0; i < field->options.size(); ++i) { |
| const SelectOption& option = field->options[i]; |
| std::u16string option_value = |
| ignore_whitespace ? RemoveWhitespace(option.value) : option.value; |
| std::u16string option_content = |
| ignore_whitespace ? RemoveWhitespace(option.content) : option.content; |
| if (searcher.Search(option_value, nullptr, nullptr) || |
| searcher.Search(option_content, nullptr, nullptr)) { |
| if (best_match == -1 || |
| field->options[best_match].value.size() > option.value.size()) { |
| best_match = i; |
| } |
| } |
| } |
| return best_match; |
| } |
| |
| } // namespace autofill |