// Copyright 2013 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/autofill_profile.h" #include #include #include #include #include #include "base/basictypes.h" #include "base/guid.h" #include "base/logging.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/address.h" #include "components/autofill/core/browser/autofill_country.h" #include "components/autofill/core/browser/autofill_field.h" #include "components/autofill/core/browser/autofill_type.h" #include "components/autofill/core/browser/contact_info.h" #include "components/autofill/core/browser/phone_number.h" #include "components/autofill/core/browser/phone_number_i18n.h" #include "components/autofill/core/browser/validation.h" #include "components/autofill/core/common/form_field_data.h" #include "grit/component_strings.h" #include "ui/base/l10n/l10n_util.h" namespace autofill { namespace { // Like |AutofillType::GetEquivalentFieldType()|, but also returns |NAME_FULL| // for first, middle, and last name field types. AutofillFieldType GetEquivalentFieldTypeCollapsingNames( AutofillFieldType field_type) { if (field_type == NAME_FIRST || field_type == NAME_MIDDLE || field_type == NAME_LAST || field_type == NAME_MIDDLE_INITIAL) return NAME_FULL; return AutofillType::GetEquivalentFieldType(field_type); } // Fills |distinguishing_fields| with a list of fields to use when creating // labels that can help to distinguish between two profiles. Draws fields from // |suggested_fields| if it is non-NULL; otherwise returns a default list. // If |suggested_fields| is non-NULL, does not include |excluded_field| in the // list. Otherwise, |excluded_field| is ignored, and should be set to // |UNKNOWN_TYPE| by convention. The resulting list of fields is sorted in // decreasing order of importance. void GetFieldsForDistinguishingProfiles( const std::vector* suggested_fields, AutofillFieldType excluded_field, std::vector* distinguishing_fields) { static const AutofillFieldType kDefaultDistinguishingFields[] = { NAME_FULL, ADDRESS_HOME_LINE1, ADDRESS_HOME_LINE2, ADDRESS_HOME_CITY, ADDRESS_HOME_STATE, ADDRESS_HOME_ZIP, ADDRESS_HOME_COUNTRY, EMAIL_ADDRESS, PHONE_HOME_WHOLE_NUMBER, COMPANY_NAME, }; if (!suggested_fields) { DCHECK_EQ(excluded_field, UNKNOWN_TYPE); distinguishing_fields->assign( kDefaultDistinguishingFields, kDefaultDistinguishingFields + arraysize(kDefaultDistinguishingFields)); return; } // Keep track of which fields we've seen so that we avoid duplicate entries. // Always ignore fields of unknown type and the excluded field. std::set seen_fields; seen_fields.insert(UNKNOWN_TYPE); seen_fields.insert(GetEquivalentFieldTypeCollapsingNames(excluded_field)); distinguishing_fields->clear(); for (std::vector::const_iterator it = suggested_fields->begin(); it != suggested_fields->end(); ++it) { AutofillFieldType suggested_type = GetEquivalentFieldTypeCollapsingNames(*it); if (seen_fields.insert(suggested_type).second) distinguishing_fields->push_back(suggested_type); } // Special case: If the excluded field is a partial name (e.g. first name) and // the suggested fields include other name fields, include |NAME_FULL| in the // list of distinguishing fields as a last-ditch fallback. This allows us to // distinguish between profiles that are identical except for the name. if (excluded_field != NAME_FULL && GetEquivalentFieldTypeCollapsingNames(excluded_field) == NAME_FULL) { for (std::vector::const_iterator it = suggested_fields->begin(); it != suggested_fields->end(); ++it) { if (*it != excluded_field && GetEquivalentFieldTypeCollapsingNames(*it) == NAME_FULL) { distinguishing_fields->push_back(NAME_FULL); break; } } } } // A helper function for string streaming. Concatenates multi-valued entries // stored for a given |type| into a single string. This string is returned. const base::string16 MultiString(const AutofillProfile& p, AutofillFieldType type) { std::vector values; p.GetRawMultiInfo(type, &values); base::string16 accumulate; for (size_t i = 0; i < values.size(); ++i) { if (i > 0) accumulate += ASCIIToUTF16(" "); accumulate += values[i]; } return accumulate; } base::string16 GetFormGroupInfo(const FormGroup& form_group, AutofillFieldType type, const std::string& app_locale) { return app_locale.empty() ? form_group.GetRawInfo(type) : form_group.GetInfo(type, app_locale); } template void CopyValuesToItems(AutofillFieldType type, const std::vector& values, std::vector* form_group_items, const T& prototype) { form_group_items->resize(values.size(), prototype); for (size_t i = 0; i < form_group_items->size(); ++i) { (*form_group_items)[i].SetRawInfo(type, CollapseWhitespace(values[i], false)); } // Must have at least one (possibly empty) element. if (form_group_items->empty()) form_group_items->resize(1, prototype); } template void CopyItemsToValues(AutofillFieldType type, const std::vector& form_group_items, const std::string& app_locale, std::vector* values) { values->resize(form_group_items.size()); for (size_t i = 0; i < values->size(); ++i) { (*values)[i] = GetFormGroupInfo(form_group_items[i], type, app_locale); } } // Collapse compound field types to their "full" type. I.e. First name // collapses to full name, area code collapses to full phone, etc. void CollapseCompoundFieldTypes(FieldTypeSet* type_set) { FieldTypeSet collapsed_set; for (FieldTypeSet::iterator iter = type_set->begin(); iter != type_set->end(); ++iter) { switch (*iter) { case NAME_FIRST: case NAME_MIDDLE: case NAME_LAST: case NAME_MIDDLE_INITIAL: case NAME_FULL: case NAME_SUFFIX: collapsed_set.insert(NAME_FULL); break; case PHONE_HOME_NUMBER: case PHONE_HOME_CITY_CODE: case PHONE_HOME_COUNTRY_CODE: case PHONE_HOME_CITY_AND_NUMBER: case PHONE_HOME_WHOLE_NUMBER: collapsed_set.insert(PHONE_HOME_WHOLE_NUMBER); break; default: collapsed_set.insert(*iter); } } std::swap(*type_set, collapsed_set); } class FindByPhone { public: FindByPhone(const base::string16& phone, const std::string& country_code, const std::string& app_locale) : phone_(phone), country_code_(country_code), app_locale_(app_locale) { } bool operator()(const base::string16& phone) { return i18n::PhoneNumbersMatch(phone, phone_, country_code_, app_locale_); } bool operator()(const base::string16* phone) { return i18n::PhoneNumbersMatch(*phone, phone_, country_code_, app_locale_); } private: base::string16 phone_; std::string country_code_; std::string app_locale_; }; // Functor used to check for case-insensitive equality of two strings. struct CaseInsensitiveStringEquals : public std::binary_function { bool operator()(const base::string16& x, const base::string16& y) const { return x.size() == y.size() && StringToLowerASCII(x) == StringToLowerASCII(y); } }; } // namespace AutofillProfile::AutofillProfile(const std::string& guid, const std::string& origin) : AutofillDataModel(guid, origin), name_(1), email_(1), phone_number_(1, PhoneNumber(this)) { } AutofillProfile::AutofillProfile() : AutofillDataModel(base::GenerateGUID(), std::string()), name_(1), email_(1), phone_number_(1, PhoneNumber(this)) { } AutofillProfile::AutofillProfile(const AutofillProfile& profile) : AutofillDataModel(std::string(), std::string()) { operator=(profile); } AutofillProfile::~AutofillProfile() { } AutofillProfile& AutofillProfile::operator=(const AutofillProfile& profile) { if (this == &profile) return *this; set_guid(profile.guid()); set_origin(profile.origin()); label_ = profile.label_; name_ = profile.name_; email_ = profile.email_; company_ = profile.company_; phone_number_ = profile.phone_number_; for (size_t i = 0; i < phone_number_.size(); ++i) phone_number_[i].set_profile(this); address_ = profile.address_; return *this; } void AutofillProfile::GetMatchingTypes(const base::string16& text, const std::string& app_locale, FieldTypeSet* matching_types) const { FormGroupList info = FormGroups(); for (FormGroupList::const_iterator it = info.begin(); it != info.end(); ++it) (*it)->GetMatchingTypes(text, app_locale, matching_types); } base::string16 AutofillProfile::GetRawInfo(AutofillFieldType type) const { AutofillFieldType return_type = AutofillType::GetEquivalentFieldType(type); const FormGroup* form_group = FormGroupForType(return_type); if (!form_group) return base::string16(); return form_group->GetRawInfo(return_type); } void AutofillProfile::SetRawInfo(AutofillFieldType type, const base::string16& value) { FormGroup* form_group = MutableFormGroupForType(type); if (form_group) form_group->SetRawInfo(type, CollapseWhitespace(value, false)); } base::string16 AutofillProfile::GetInfo(AutofillFieldType type, const std::string& app_locale) const { AutofillFieldType return_type = AutofillType::GetEquivalentFieldType(type); const FormGroup* form_group = FormGroupForType(return_type); if (!form_group) return base::string16(); return form_group->GetInfo(return_type, app_locale); } bool AutofillProfile::SetInfo(AutofillFieldType type, const base::string16& value, const std::string& app_locale) { FormGroup* form_group = MutableFormGroupForType(type); if (!form_group) return false; return form_group->SetInfo(type, CollapseWhitespace(value, false), app_locale); } void AutofillProfile::SetRawMultiInfo( AutofillFieldType type, const std::vector& values) { switch (AutofillType(type).group()) { case AutofillType::NAME: CopyValuesToItems(type, values, &name_, NameInfo()); break; case AutofillType::EMAIL: CopyValuesToItems(type, values, &email_, EmailInfo()); break; case AutofillType::PHONE_HOME: case AutofillType::PHONE_BILLING: CopyValuesToItems(type, values, &phone_number_, PhoneNumber(this)); break; default: if (values.size() == 1) { SetRawInfo(type, values[0]); } else if (values.size() == 0) { SetRawInfo(type, base::string16()); } else { // Shouldn't attempt to set multiple values on single-valued field. NOTREACHED(); } break; } } void AutofillProfile::GetRawMultiInfo( AutofillFieldType type, std::vector* values) const { GetMultiInfoImpl(type, std::string(), values); } void AutofillProfile::GetMultiInfo(AutofillFieldType type, const std::string& app_locale, std::vector* values) const { GetMultiInfoImpl(type, app_locale, values); } void AutofillProfile::FillFormField(const AutofillField& field, size_t variant, const std::string& app_locale, FormFieldData* field_data) const { AutofillFieldType type = field.type(); DCHECK_NE(AutofillType::CREDIT_CARD, AutofillType(type).group()); DCHECK(field_data); if (type == PHONE_HOME_NUMBER || type == PHONE_BILLING_NUMBER) { FillPhoneNumberField(field, variant, app_locale, field_data); } else if (field_data->form_control_type == "select-one") { FillSelectControl(type, app_locale, field_data); } else { std::vector values; GetMultiInfo(type, app_locale, &values); if (variant >= values.size()) { // If the variant is unavailable, bail. This case is reachable, for // example if Sync updates a profile during the filling process. return; } field_data->value = values[variant]; } } void AutofillProfile::FillPhoneNumberField(const AutofillField& field, size_t variant, const std::string& app_locale, FormFieldData* field_data) const { std::vector values; GetMultiInfo(field.type(), app_locale, &values); DCHECK(variant < values.size()); // If we are filling a phone number, check to see if the size field // matches the "prefix" or "suffix" sizes and fill accordingly. base::string16 number = values[variant]; if (number.length() == PhoneNumber::kPrefixLength + PhoneNumber::kSuffixLength) { if (field.phone_part() == AutofillField::PHONE_PREFIX || field_data->max_length == PhoneNumber::kPrefixLength) { number = number.substr(PhoneNumber::kPrefixOffset, PhoneNumber::kPrefixLength); } else if (field.phone_part() == AutofillField::PHONE_SUFFIX || field_data->max_length == PhoneNumber::kSuffixLength) { number = number.substr(PhoneNumber::kSuffixOffset, PhoneNumber::kSuffixLength); } } field_data->value = number; } const base::string16 AutofillProfile::Label() const { return label_; } bool AutofillProfile::IsEmpty(const std::string& app_locale) const { FieldTypeSet types; GetNonEmptyTypes(app_locale, &types); return types.empty(); } bool AutofillProfile::IsPresentButInvalid(AutofillFieldType type) const { std::string country = UTF16ToUTF8(GetRawInfo(ADDRESS_HOME_COUNTRY)); base::string16 data = GetRawInfo(type); switch (type) { case ADDRESS_HOME_STATE: if (!data.empty() && country == "US" && !autofill::IsValidState(data)) return true; break; case ADDRESS_HOME_ZIP: if (!data.empty() && country == "US" && !autofill::IsValidZip(data)) return true; break; case PHONE_HOME_WHOLE_NUMBER: { if (!data.empty() && !i18n::PhoneObject(data, country).IsValidNumber()) return true; break; } default: NOTREACHED(); break; } return false; } int AutofillProfile::Compare(const AutofillProfile& profile) const { const AutofillFieldType single_value_types[] = { COMPANY_NAME, ADDRESS_HOME_LINE1, ADDRESS_HOME_LINE2, ADDRESS_HOME_CITY, ADDRESS_HOME_STATE, ADDRESS_HOME_ZIP, ADDRESS_HOME_COUNTRY }; for (size_t i = 0; i < arraysize(single_value_types); ++i) { int comparison = GetRawInfo(single_value_types[i]).compare( profile.GetRawInfo(single_value_types[i])); if (comparison != 0) return comparison; } const AutofillFieldType multi_value_types[] = { NAME_FIRST, NAME_MIDDLE, NAME_LAST, EMAIL_ADDRESS, PHONE_HOME_WHOLE_NUMBER }; for (size_t i = 0; i < arraysize(multi_value_types); ++i) { std::vector values_a; std::vector values_b; GetRawMultiInfo(multi_value_types[i], &values_a); profile.GetRawMultiInfo(multi_value_types[i], &values_b); if (values_a.size() < values_b.size()) return -1; if (values_a.size() > values_b.size()) return 1; for (size_t j = 0; j < values_a.size(); ++j) { int comparison = values_a[j].compare(values_b[j]); if (comparison != 0) return comparison; } } return 0; } bool AutofillProfile::operator==(const AutofillProfile& profile) const { return guid() == profile.guid() && origin() == profile.origin() && Compare(profile) == 0; } bool AutofillProfile::operator!=(const AutofillProfile& profile) const { return !operator==(profile); } const base::string16 AutofillProfile::PrimaryValue() const { return GetRawInfo(ADDRESS_HOME_LINE1) + GetRawInfo(ADDRESS_HOME_CITY); } bool AutofillProfile::IsSubsetOf(const AutofillProfile& profile, const std::string& app_locale) const { FieldTypeSet types; GetNonEmptyTypes(app_locale, &types); for (FieldTypeSet::const_iterator iter = types.begin(); iter != types.end(); ++iter) { if (*iter == NAME_FULL) { // Ignore the compound "full name" field type. We are only interested in // comparing the constituent parts. For example, if |this| has a middle // name saved, but |profile| lacks one, |profile| could still be a subset // of |this|. continue; } else if (AutofillType(*iter).group() == AutofillType::PHONE_HOME) { // Phone numbers should be canonicalized prior to being compared. if (*iter != PHONE_HOME_WHOLE_NUMBER) { continue; } else if (!i18n::PhoneNumbersMatch( GetRawInfo(*iter), profile.GetRawInfo(*iter), UTF16ToASCII(GetRawInfo(ADDRESS_HOME_COUNTRY)), app_locale)) { return false; } } else if (StringToLowerASCII(GetRawInfo(*iter)) != StringToLowerASCII(profile.GetRawInfo(*iter))) { return false; } } return true; } void AutofillProfile::OverwriteWithOrAddTo(const AutofillProfile& profile, const std::string& app_locale) { // Verified profiles should never be overwritten with unverified data. DCHECK(!IsVerified() || profile.IsVerified()); set_origin(profile.origin()); FieldTypeSet field_types; profile.GetNonEmptyTypes(app_locale, &field_types); // Only transfer "full" types (e.g. full name) and not fragments (e.g. // first name, last name). CollapseCompoundFieldTypes(&field_types); for (FieldTypeSet::const_iterator iter = field_types.begin(); iter != field_types.end(); ++iter) { if (AutofillProfile::SupportsMultiValue(*iter)) { std::vector new_values; profile.GetRawMultiInfo(*iter, &new_values); std::vector existing_values; GetRawMultiInfo(*iter, &existing_values); // GetMultiInfo always returns at least one element, even if the profile // has no data stored for this field type. if (existing_values.size() == 1 && existing_values.front().empty()) existing_values.clear(); FieldTypeGroup group = AutofillType(*iter).group(); for (std::vector::iterator value_iter = new_values.begin(); value_iter != new_values.end(); ++value_iter) { // Don't add duplicates. if (group == AutofillType::PHONE_HOME) { AddPhoneIfUnique(*value_iter, app_locale, &existing_values); } else { std::vector::const_iterator existing_iter = std::find_if( existing_values.begin(), existing_values.end(), std::bind1st(CaseInsensitiveStringEquals(), *value_iter)); if (existing_iter == existing_values.end()) existing_values.insert(existing_values.end(), *value_iter); } } SetRawMultiInfo(*iter, existing_values); } else { base::string16 new_value = profile.GetRawInfo(*iter); if (StringToLowerASCII(GetRawInfo(*iter)) != StringToLowerASCII(new_value)) { SetRawInfo(*iter, new_value); } } } } // static bool AutofillProfile::SupportsMultiValue(AutofillFieldType type) { AutofillType::FieldTypeGroup group = AutofillType(type).group(); return group == AutofillType::NAME || group == AutofillType::EMAIL || group == AutofillType::PHONE_HOME || group == AutofillType::PHONE_BILLING; } // static bool AutofillProfile::AdjustInferredLabels( std::vector* profiles) { const size_t kMinimalFieldsShown = 2; std::vector created_labels; CreateInferredLabels(profiles, NULL, UNKNOWN_TYPE, kMinimalFieldsShown, &created_labels); DCHECK_EQ(profiles->size(), created_labels.size()); bool updated_labels = false; for (size_t i = 0; i < profiles->size(); ++i) { if ((*profiles)[i]->Label() != created_labels[i]) { updated_labels = true; (*profiles)[i]->label_ = created_labels[i]; } } return updated_labels; } // static void AutofillProfile::CreateInferredLabels( const std::vector* profiles, const std::vector* suggested_fields, AutofillFieldType excluded_field, size_t minimal_fields_shown, std::vector* created_labels) { DCHECK(profiles); DCHECK(created_labels); std::vector fields_to_use; GetFieldsForDistinguishingProfiles(suggested_fields, excluded_field, &fields_to_use); // Construct the default label for each profile. Also construct a map that // associates each label with the profiles that have this label. This map is // then used to detect which labels need further differentiating fields. std::map > labels; for (size_t i = 0; i < profiles->size(); ++i) { base::string16 label = (*profiles)[i]->ConstructInferredLabel(fields_to_use, minimal_fields_shown); labels[label].push_back(i); } created_labels->resize(profiles->size()); for (std::map >::const_iterator it = labels.begin(); it != labels.end(); ++it) { if (it->second.size() == 1) { // This label is unique, so use it without any further ado. base::string16 label = it->first; size_t profile_index = it->second.front(); (*created_labels)[profile_index] = label; } else { // We have more than one profile with the same label, so add // differentiating fields. CreateDifferentiatingLabels(*profiles, it->second, fields_to_use, minimal_fields_shown, created_labels); } } } void AutofillProfile::GetSupportedTypes(FieldTypeSet* supported_types) const { FormGroupList info = FormGroups(); for (FormGroupList::const_iterator it = info.begin(); it != info.end(); ++it) (*it)->GetSupportedTypes(supported_types); } bool AutofillProfile::FillCountrySelectControl( const std::string& app_locale, FormFieldData* field_data) const { std::string country_code = UTF16ToASCII(GetRawInfo(ADDRESS_HOME_COUNTRY)); 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