blob: 8e4270d096dee3f99af0b49db8ab1b97293621e1 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// 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 "base/feature_list.h"
#include "base/i18n/case_conversion.h"
#include "base/i18n/string_search.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_country.h"
#include "components/autofill/core/browser/autofill_data_model.h"
#include "components/autofill/core/browser/autofill_data_util.h"
#include "components/autofill/core/browser/autofill_field.h"
#include "components/autofill/core/browser/autofill_profile.h"
#include "components/autofill/core/browser/autofill_type.h"
#include "components/autofill/core/browser/country_names.h"
#include "components/autofill/core/browser/credit_card.h"
#include "components/autofill/core/browser/field_types.h"
#include "components/autofill/core/browser/phone_number.h"
#include "components/autofill/core/browser/state_names.h"
#include "components/autofill/core/common/autofill_features.h"
#include "components/autofill/core/common/autofill_l10n_util.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 base::string16& value,
FormFieldData* field,
size_t* best_match_index = nullptr) {
l10n::CaseInsensitiveCompare compare;
DCHECK_EQ(field->option_values.size(), field->option_contents.size());
base::string16 best_match;
for (size_t i = 0; i < field->option_values.size(); ++i) {
if (value == field->option_values[i] ||
value == field->option_contents[i]) {
// An exact match, use it.
best_match = field->option_values[i];
if (best_match_index)
*best_match_index = i;
break;
}
if (compare.StringsEqual(value, field->option_values[i]) ||
compare.StringsEqual(value, field->option_contents[i])) {
// A match, but not in the same case. Save it in case an exact match is
// not found.
best_match = field->option_values[i];
if (best_match_index)
*best_match_index = i;
}
}
if (best_match.empty())
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 base::string16& value,
bool ignore_whitespace,
FormFieldData* field) {
int best_match = FieldFiller::FindShortestSubstringMatchInSelect(
value, ignore_whitespace, field);
if (best_match >= 0) {
field->value = field->option_values[best_match];
return true;
}
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 base::string16& value,
FormFieldData* field) {
std::vector<base::string16> tokenized;
DCHECK_EQ(field->option_values.size(), field->option_contents.size());
l10n::CaseInsensitiveCompare compare;
for (size_t i = 0; i < field->option_values.size(); ++i) {
tokenized =
base::SplitString(field->option_values[i], base::kWhitespaceASCIIAs16,
base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
if (std::find_if(tokenized.begin(), tokenized.end(),
[&compare, value](base::string16& rhs) {
return compare.StringsEqual(value, rhs);
}) != tokenized.end()) {
field->value = field->option_values[i];
return true;
}
tokenized =
base::SplitString(field->option_contents[i], base::kWhitespaceASCIIAs16,
base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
if (std::find_if(tokenized.begin(), tokenized.end(),
[&compare, value](base::string16& rhs) {
return compare.StringsEqual(value, rhs);
}) != tokenized.end()) {
field->value = field->option_values[i];
return true;
}
}
return false;
}
// Helper method to normalize the |admin_area| for the given |country_code|.
// The value in |admin_area| will be overwritten.
bool NormalizeAdminAreaForCountryCode(base::string16* 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 base::string16& value,
FormFieldData* field,
const std::string& country_code,
AddressNormalizer* address_normalizer) {
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.
base::string16 field_value = value;
if (!NormalizeAdminAreaForCountryCode(&field_value, country_code,
address_normalizer)) {
return false;
}
// If successful, try filling the normalized value with the existing field
// |options|.
if (SetSelectControlValue(field_value, field))
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 (size_t i = 0; i < field_copy.option_values.size(); ++i) {
normalized |= NormalizeAdminAreaForCountryCode(
&field_copy.option_values[i], country_code, address_normalizer);
normalized |= NormalizeAdminAreaForCountryCode(
&field_copy.option_contents[i], 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)) {
// |best_match_index| now points to the option in |field->option_values|
// that corresponds to our best match. Update |field| with the answer.
field->value = field->option_values[best_match_index];
return true;
}
return false;
}
// Try to fill a numeric |value| into the given |field|.
bool FillNumericSelectControl(int value, FormFieldData* field) {
DCHECK_EQ(field->option_values.size(), field->option_contents.size());
for (size_t i = 0; i < field->option_values.size(); ++i) {
int option;
if ((StringToInt(field->option_values[i], &option) && option == value) ||
(StringToInt(field->option_contents[i], &option) && option == value)) {
field->value = field->option_values[i];
return true;
}
}
return false;
}
bool FillStateSelectControl(const base::string16& value,
FormFieldData* field,
const std::string& country_code,
AddressNormalizer* address_normalizer) {
base::string16 full;
base::string16 abbreviation;
// |abbreviation| will be empty for non-US countries.
state_names::GetNameAndAbbreviation(value, &full, &abbreviation);
// Try an exact match of the abbreviation first.
if (!abbreviation.empty() && SetSelectControlValue(abbreviation, field)) {
return true;
}
// Try an exact match of the full name.
if (!full.empty() && SetSelectControlValue(full, field)) {
return true;
}
// Try an inexact match of the full name.
if (!full.empty() &&
SetSelectControlValueSubstringMatch(full, false, field)) {
return true;
}
// Try an inexact match of the abbreviation name.
if (!abbreviation.empty() &&
SetSelectControlValueTokenMatch(abbreviation, field)) {
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)) {
return true;
}
return false;
}
bool FillCountrySelectControl(const base::string16& value,
FormFieldData* field_data) {
std::string country_code = CountryNames::GetInstance()->GetCountryCode(value);
if (country_code.empty())
return false;
DCHECK_EQ(field_data->option_values.size(),
field_data->option_contents.size());
for (size_t i = 0; i < field_data->option_values.size(); ++i) {
// Canonicalize each <option> value to a country code, and compare to the
// target country code.
base::string16 value = field_data->option_values[i];
base::string16 contents = field_data->option_contents[i];
if (country_code == CountryNames::GetInstance()->GetCountryCode(value) ||
country_code == CountryNames::GetInstance()->GetCountryCode(contents)) {
field_data->value = value;
return true;
}
}
return false;
}
// Attempt to fill the user's expiration month |value| inside the <select>
// |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 base::string16& value,
const std::string& app_locale,
FormFieldData* field) {
// |value| is defined to be between 1 and 12, inclusively.
int month = 0;
if (!StringToInt(value, &month) || month <= 0 || month > 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<base::string16> trimmed_values(field->option_values.size());
const base::string16 kNumberPrefix = ASCIIToUTF16("number:");
const base::string16 kStringPrefix = ASCIIToUTF16("string:");
for (size_t i = 0; i < field->option_values.size(); ++i) {
base::TrimWhitespace(field->option_values[i], base::TRIM_ALL,
&trimmed_values[i]);
base::ReplaceFirstSubstringAfterOffset(&trimmed_values[i], 0, kNumberPrefix,
ASCIIToUTF16(""));
base::ReplaceFirstSubstringAfterOffset(&trimmed_values[i], 0, kStringPrefix,
ASCIIToUTF16(""));
}
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.
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 (CreditCard::ConvertMonth(trimmed_values[i], app_locale,
&converted_value) &&
month == converted_value) {
field->value = field->option_values[i];
return true;
}
}
// Attempt to match with each of the options' content.
for (size_t i = 0; i < field->option_contents.size(); ++i) {
int converted_contents = 0;
if (CreditCard::ConvertMonth(field->option_contents[i], app_locale,
&converted_contents) &&
month == converted_contents) {
field->value = field->option_values[i];
return true;
}
}
return FillNumericSelectControl(month, field);
}
// Returns true if the last two digits in |year| match those in |str|.
bool LastTwoDigitsMatch(const base::string16& year, const base::string16& 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 base::string16& value, FormFieldData* field) {
if (value.size() != 2U && value.size() != 4U)
return false;
DCHECK_EQ(field->option_values.size(), field->option_contents.size());
for (size_t i = 0; i < field->option_values.size(); ++i) {
if (LastTwoDigitsMatch(value, field->option_values[i]) ||
LastTwoDigitsMatch(value, field->option_contents[i])) {
field->value = field->option_values[i];
return true;
}
}
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 base::string16& value,
FormFieldData* field) {
if (SetSelectControlValueSubstringMatch(value, /* ignore_whitespace= */ true,
field)) {
return true;
}
if (value == l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_AMEX)) {
// For American Express, also try filling as "AmEx".
return SetSelectControlValueSubstringMatch(
ASCIIToUTF16("AmEx"), /* ignore_whitespace= */ true, field);
}
return false;
}
// Set |field_data|'s value to |number|, or possibly an appropriate substring of
// |number|. The |field| specifies the type of the phone and whether this is a
// phone prefix or suffix.
void FillPhoneNumberField(const AutofillField& field,
const base::string16& number,
FormFieldData* field_data) {
field_data->value =
FieldFiller::GetPhoneNumberValue(field, number, *field_data);
}
// Set |field_data|'s value to |number|, or possibly an appropriate substring
// of |number| for cases where credit card number splits across multiple HTML
// form input fields.
// The |field| specifies the |credit_card_number_offset_| to the substring
// within credit card number.
void FillCreditCardNumberField(const AutofillField& field,
const base::string16& number,
FormFieldData* field_data) {
base::string16 value = number;
// |field|'s max_length truncates credit card number to fit within.
if (field.credit_card_number_offset() < value.length())
value = value.substr(field.credit_card_number_offset());
field_data->value = value;
}
// Fills in the select control |field| with |value|. If an exact match is not
// found, falls back to alternate filling strategies based on the |type|.
bool FillSelectControl(const AutofillType& type,
const base::string16& value,
FormFieldData* field,
const AutofillDataModel& data_model,
const std::string& app_locale,
AddressNormalizer* address_normalizer) {
DCHECK_EQ("select-one", field->form_control_type);
// Guard against corrupted values passed over IPC.
if (field->option_values.size() != field->option_contents.size())
return false;
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);
// Search for exact matches.
if (SetSelectControlValue(value, field))
return true;
// If that fails, try specific fallbacks based on the field type.
if (storable_type == ADDRESS_HOME_STATE) {
// Safe to cast the data model to AutofillProfile here.
const std::string country_code = data_util::GetCountryCodeWithFallback(
static_cast<const AutofillProfile&>(data_model), app_locale);
return FillStateSelectControl(value, field, country_code,
address_normalizer);
}
if (storable_type == ADDRESS_HOME_COUNTRY)
return FillCountrySelectControl(value, field);
if (storable_type == CREDIT_CARD_EXP_2_DIGIT_YEAR ||
storable_type == CREDIT_CARD_EXP_4_DIGIT_YEAR) {
return FillYearSelectControl(value, field);
}
if (storable_type == CREDIT_CARD_TYPE)
return FillCreditCardTypeSelectControl(value, field);
return false;
}
// Fills in the month control |field| with the expiration date in |card|.
void FillMonthControl(const CreditCard& card, FormFieldData* field) {
field->value = card.Expiration4DigitYearAsString() + ASCIIToUTF16("-") +
card.ExpirationMonthAsString();
}
// Fills |field| with the street address in |value|. Translates newlines into
// equivalent separators when necessary, i.e. when filling a single-line field.
// The separators depend on |address_language_code|.
void FillStreetAddress(const base::string16& value,
const std::string& address_language_code,
FormFieldData* field) {
if (field->form_control_type == "textarea") {
field->value = value;
return;
}
::i18n::addressinput::AddressData address_data;
address_data.language_code = address_language_code;
address_data.address_line =
base::SplitString(base::UTF16ToUTF8(value), "\n", base::TRIM_WHITESPACE,
base::SPLIT_WANT_ALL);
std::string line;
::i18n::addressinput::GetStreetAddressLinesAsSingleLine(address_data, &line);
field->value = base::UTF8ToUTF16(line);
}
// Returns whether the |field| was filled with the state in |value| or its
// abbreviation. First looks if |value| fits directly in the field, then looks
// if the abbreviation of |value| fits. Does not fill if neither |value| or its
// abbreviation are too long for the field.
bool FillStateText(const base::string16& value, FormFieldData* field) {
if (field->max_length == 0 || field->max_length >= value.size()) {
// Fill the state value directly.
field->value = value;
return true;
}
// Fill with the state abbreviation.
base::string16 abbreviation;
state_names::GetNameAndAbbreviation(value, nullptr, &abbreviation);
if (!abbreviation.empty() && field->max_length >= abbreviation.size()) {
field->value = base::i18n::ToUpper(abbreviation);
return true;
}
return false;
}
// Fills the expiration year |value| into the |field|. Uses the |field_type|
// and the |field|'s max_length attribute to determine if the |value| needs to
// be truncated.
void FillExpirationYearInput(base::string16 value,
ServerFieldType field_type,
FormFieldData* field) {
// If the |field_type| requires only 2 digits, keep only the last 2 digits of
// |value|.
if (field_type == CREDIT_CARD_EXP_2_DIGIT_YEAR && value.length() > 2)
value = value.substr(value.length() - 2, 2);
if (field->max_length == 0 || field->max_length >= value.size()) {
// No length restrictions, fill the year value directly.
field->value = value;
} else {
// Truncate the front of |value| to keep only the number of characters equal
// to the |field|'s max length.
field->value =
value.substr(value.length() - field->max_length, field->max_length);
}
}
// Returns whether the expiration date |value| was filled into the |field|.
// Uses the |field|'s max_length attribute to determine if the |value| needs to
// be truncated. |value| should be a date formatted as either MM/YY or MM/YYYY.
// If it isn't, the field doesn't get filled.
bool FillExpirationDateInput(const base::string16& value,
FormFieldData* field) {
const base::string16 kSeparator = ASCIIToUTF16("/");
// Autofill formats a combined date as month/year.
std::vector<base::string16> pieces = base::SplitString(
value, kSeparator, base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
if (pieces.size() != 2)
return false;
base::string16 month = pieces[0];
base::string16 year = pieces[1];
if (month.length() != 2 || (year.length() != 2 && year.length() != 4))
return false;
switch (field->max_length) {
case 1:
case 2:
case 3:
return false;
case 4:
// Field likely expects MMYY
if (year.length() != 2) {
// Shorten year to 2 characters from 4
year = year.substr(2);
}
field->value = month + year;
break;
case 5:
// Field likely expects MM/YY
if (year.length() != 2) {
// Shorten year to 2 characters
year = year.substr(2);
field->value = month + kSeparator + year;
} else {
field->value = value;
}
break;
case 6:
case 7:
if (year.length() != 4) {
// Will normalize 2-digit years to the 4-digit version.
year = ASCIIToUTF16("20") + year;
}
if (field->max_length == 6) {
// Field likely expects MMYYYY
field->value = month + year;
} else {
// Field likely expects MM/YYYY
field->value = month + kSeparator + year;
}
break;
default:
// Includes the case where max_length is not specified (0).
field->value = month + kSeparator + year;
}
return true;
}
base::string16 RemoveWhitespace(const base::string16& value) {
base::string16 stripped_value;
base::RemoveChars(value, base::kWhitespaceUTF16, &stripped_value);
return stripped_value;
}
} // namespace
FieldFiller::FieldFiller(const std::string& app_locale,
AddressNormalizer* address_normalizer)
: app_locale_(app_locale), address_normalizer_(address_normalizer) {}
FieldFiller::~FieldFiller() {}
bool FieldFiller::FillFormField(const AutofillField& field,
const AutofillDataModel& data_model,
FormFieldData* field_data,
const base::string16& cvc) {
const AutofillType type = field.Type();
// Don't fill if autocomplete=off is set on |field| on desktop for non credit
// card related fields.
if (!base::FeatureList::IsEnabled(features::kAutofillAlwaysFillAddresses) &&
!field.should_autocomplete && IsDesktopPlatform() &&
(type.group() != CREDIT_CARD)) {
return false;
}
// Check for the validity of the data. Leave the field empty if the data is
// invalid and the relevant feature is enabled.
bool use_server_validation = base::FeatureList::IsEnabled(
autofill::features::kAutofillProfileServerValidation);
bool use_client_validation = base::FeatureList::IsEnabled(
autofill::features::kAutofillProfileClientValidation);
ServerFieldType server_field_type = type.GetStorableType();
if ((use_client_validation &&
data_model.GetValidityState(server_field_type,
AutofillProfile::CLIENT) ==
AutofillProfile::INVALID) ||
(use_server_validation &&
data_model.GetValidityState(server_field_type,
AutofillProfile::SERVER) ==
AutofillProfile::INVALID)) {
return false;
}
base::string16 value = data_model.GetInfo(type, app_locale_);
if (type.GetStorableType() == CREDIT_CARD_VERIFICATION_CODE)
value = cvc;
// Do not attempt to fill empty values as it would skew the metrics.
if (value.empty())
return false;
if (type.group() == PHONE_HOME) {
FillPhoneNumberField(field, value, field_data);
return true;
}
if (field_data->form_control_type == "select-one") {
return FillSelectControl(type, value, field_data, data_model, app_locale_,
address_normalizer_);
}
if (field_data->form_control_type == "month") {
// Safe to cast to CreditCard here because month control type only applying
// to credit card expirations.
FillMonthControl(static_cast<const CreditCard&>(data_model), field_data);
return true;
}
if (type.GetStorableType() == ADDRESS_HOME_STREET_ADDRESS) {
// Safe to cast to AutofillProfile here because of the address |type|.
const std::string profile_language_code =
static_cast<const AutofillProfile&>(data_model).language_code();
FillStreetAddress(value, profile_language_code, field_data);
return true;
}
if (type.GetStorableType() == CREDIT_CARD_NUMBER) {
FillCreditCardNumberField(field, value, field_data);
return true;
}
if (type.GetStorableType() == ADDRESS_HOME_STATE)
return FillStateText(value, field_data);
if (field_data->form_control_type == "text" &&
(type.GetStorableType() == CREDIT_CARD_EXP_2_DIGIT_YEAR ||
type.GetStorableType() == CREDIT_CARD_EXP_4_DIGIT_YEAR)) {
FillExpirationYearInput(value, type.GetStorableType(), field_data);
return true;
}
if (field_data->form_control_type == "text" &&
(type.GetStorableType() == CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR ||
type.GetStorableType() == CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR)) {
return FillExpirationDateInput(value, field_data);
}
field_data->value = value;
return true;
}
// TODO(crbug.com/581514): Add support for filling only the prefix/suffix for
// phone numbers with 10 or 11 digits.
// static
base::string16 FieldFiller::GetPhoneNumberValue(
const AutofillField& field,
const base::string16& number,
const FormFieldData& field_data) {
// TODO(crbug.com/581485): Investigate the use of libphonenumber here.
// Check to see if the |field| size matches the "prefix" or "suffix" size or
// if the field was labeled as such. If so, return the appropriate substring.
if (number.length() ==
PhoneNumber::kPrefixLength + PhoneNumber::kSuffixLength) {
if (field.phone_part() == AutofillField::PHONE_PREFIX ||
field_data.max_length == PhoneNumber::kPrefixLength) {
return number.substr(PhoneNumber::kPrefixOffset,
PhoneNumber::kPrefixLength);
}
if (field.phone_part() == AutofillField::PHONE_SUFFIX ||
field_data.max_length == PhoneNumber::kSuffixLength) {
return number.substr(PhoneNumber::kSuffixOffset,
PhoneNumber::kSuffixLength);
}
}
// If no max length was specified, return the complete number.
if (field_data.max_length == 0)
return 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 removing
// the country code and remaining valid.
if (number.length() > field_data.max_length) {
return number.substr(number.length() - field_data.max_length,
field_data.max_length);
}
return number;
}
// static
int FieldFiller::FindShortestSubstringMatchInSelect(
const base::string16& value,
bool ignore_whitespace,
const FormFieldData* field) {
DCHECK_EQ(field->option_values.size(), field->option_contents.size());
int best_match = -1;
base::string16 value_stripped =
ignore_whitespace ? RemoveWhitespace(value) : value;
base::i18n::FixedPatternStringSearchIgnoringCaseAndAccents searcher(
value_stripped);
for (size_t i = 0; i < field->option_values.size(); ++i) {
base::string16 option_value =
ignore_whitespace ? RemoveWhitespace(field->option_values[i])
: field->option_values[i];
base::string16 option_content =
ignore_whitespace ? RemoveWhitespace(field->option_contents[i])
: field->option_contents[i];
if (searcher.Search(option_value, nullptr, nullptr) ||
searcher.Search(option_content, nullptr, nullptr)) {
if (best_match == -1 || field->option_values[best_match].size() >
field->option_values[i].size()) {
best_match = i;
}
}
}
return best_match;
}
} // namespace autofill