// Copyright (c) 2012 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 "chrome/browser/autofill/autofill_profile.h" #include #include #include #include #include #include "base/basictypes.h" #include "base/logging.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" #include "chrome/browser/autofill/address.h" #include "chrome/browser/autofill/autofill_type.h" #include "chrome/browser/autofill/contact_info.h" #include "chrome/browser/autofill/phone_number.h" #include "chrome/browser/autofill/phone_number_i18n.h" #include "chrome/common/guid.h" #include "grit/generated_resources.h" #include "ui/base/l10n/l10n_util.h" 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 string16 MultiString(const AutofillProfile& p, AutofillFieldType type) { std::vector values; p.GetMultiInfo(type, &values); string16 accumulate; for (size_t i = 0; i < values.size(); ++i) { if (i > 0) accumulate += ASCIIToUTF16(" "); accumulate += values[i]; } return accumulate; } 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].SetInfo(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, bool canonicalize, std::vector* values) { values->resize(form_group_items.size()); for (size_t i = 0; i < values->size(); ++i) { if (canonicalize) (*values)[i] = form_group_items[i].GetCanonicalizedInfo(type); else (*values)[i] = form_group_items[i].GetInfo(type); } } // 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 string16& phone, const std::string& country_code) : phone_(phone), country_code_(country_code) { } bool operator()(const string16& phone) { return autofill_i18n::PhoneNumbersMatch(phone, phone_, country_code_); } bool operator()(const string16* phone) { return autofill_i18n::PhoneNumbersMatch(*phone, phone_, country_code_); } private: string16 phone_; std::string country_code_; }; // Functor used to check for case-insensitive equality of two strings. struct CaseInsensitiveStringEquals : public std::binary_function { bool operator()(const string16& x, const string16& y) const { return x.size() == y.size() && StringToLowerASCII(x) == StringToLowerASCII(y); } }; } // namespace AutofillProfile::AutofillProfile(const std::string& guid) : guid_(guid), name_(1), email_(1), home_number_(1, PhoneNumber(this)) { } AutofillProfile::AutofillProfile() : guid_(guid::GenerateGUID()), name_(1), email_(1), home_number_(1, PhoneNumber(this)) { } AutofillProfile::AutofillProfile(const AutofillProfile& profile) : FormGroup() { operator=(profile); } AutofillProfile::~AutofillProfile() { } AutofillProfile& AutofillProfile::operator=(const AutofillProfile& profile) { if (this == &profile) return *this; label_ = profile.label_; guid_ = profile.guid_; name_ = profile.name_; email_ = profile.email_; company_ = profile.company_; home_number_ = profile.home_number_; for (size_t i = 0; i < home_number_.size(); ++i) home_number_[i].set_profile(this); address_ = profile.address_; return *this; } 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); } void AutofillProfile::GetMatchingTypes(const string16& text, FieldTypeSet* matching_types) const { FormGroupList info = FormGroups(); for (FormGroupList::const_iterator it = info.begin(); it != info.end(); ++it) (*it)->GetMatchingTypes(text, matching_types); } string16 AutofillProfile::GetInfo(AutofillFieldType type) const { AutofillFieldType return_type = AutofillType::GetEquivalentFieldType(type); const FormGroup* form_group = FormGroupForType(return_type); if (!form_group) return string16(); return form_group->GetInfo(return_type); } void AutofillProfile::SetInfo(AutofillFieldType type, const string16& value) { FormGroup* form_group = MutableFormGroupForType(type); if (form_group) form_group->SetInfo(type, CollapseWhitespace(value, false)); } string16 AutofillProfile::GetCanonicalizedInfo(AutofillFieldType type) const { AutofillFieldType return_type = AutofillType::GetEquivalentFieldType(type); const FormGroup* form_group = FormGroupForType(return_type); if (!form_group) return string16(); return form_group->GetCanonicalizedInfo(return_type); } bool AutofillProfile::SetCanonicalizedInfo(AutofillFieldType type, const string16& value) { FormGroup* form_group = MutableFormGroupForType(type); if (form_group) { return form_group->SetCanonicalizedInfo(type, CollapseWhitespace(value, false)); } return false; } void AutofillProfile::SetMultiInfo(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: CopyValuesToItems(type, values, &home_number_, PhoneNumber(this)); break; default: if (values.size() == 1) { SetInfo(type, values[0]); } else if (values.size() == 0) { SetInfo(type, string16()); } else { NOTREACHED() << "Attempt to set multiple values on single-valued field."; } break; } } void AutofillProfile::GetMultiInfo(AutofillFieldType type, std::vector* values) const { GetMultiInfoImpl(type, false, values); } void AutofillProfile::GetCanonicalizedMultiInfo( AutofillFieldType type, std::vector* values) const { GetMultiInfoImpl(type, true, values); } void AutofillProfile::GetMultiInfoImpl(AutofillFieldType type, bool canonicalize, std::vector* values) const { switch (AutofillType(type).group()) { case AutofillType::NAME: CopyItemsToValues(type, name_, canonicalize, values); break; case AutofillType::EMAIL: CopyItemsToValues(type, email_, canonicalize, values); break; case AutofillType::PHONE: CopyItemsToValues(type, home_number_, canonicalize, values); break; default: values->resize(1); (*values)[0] = GetInfo(type); } } // static bool AutofillProfile::SupportsMultiValue(AutofillFieldType type) { AutofillType::FieldTypeGroup group = AutofillType(type).group(); return group == AutofillType::NAME || group == AutofillType::EMAIL || group == AutofillType::PHONE; } const string16 AutofillProfile::Label() const { return label_; } const std::string AutofillProfile::CountryCode() const { return address_.country_code(); } void AutofillProfile::SetCountryCode(const std::string& country_code) { address_.set_country_code(country_code); } // 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) { 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. 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); } } } bool AutofillProfile::IsEmpty() const { FieldTypeSet types; GetNonEmptyTypes(&types); return types.empty(); } 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 = GetInfo(single_value_types[i]).compare( profile.GetInfo(single_value_types[i])); if (comparison != 0) return comparison; } const AutofillFieldType multi_value_types[] = { NAME_FIRST, NAME_MIDDLE, NAME_LAST, EMAIL_ADDRESS, PHONE_HOME_NUMBER }; for (size_t i = 0; i < arraysize(multi_value_types); ++i) { std::vector values_a; std::vector values_b; GetMultiInfo(multi_value_types[i], &values_a); profile.GetMultiInfo(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_ && Compare(profile) == 0; } bool AutofillProfile::operator!=(const AutofillProfile& profile) const { return !operator==(profile); } const string16 AutofillProfile::PrimaryValue() const { return GetInfo(ADDRESS_HOME_LINE1) + GetInfo(ADDRESS_HOME_CITY); } bool AutofillProfile::IsSubsetOf(const AutofillProfile& profile) const { FieldTypeSet types; GetNonEmptyTypes(&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) { // Phone numbers should be canonicalized prior to being compared. if (*iter != PHONE_HOME_WHOLE_NUMBER) { continue; } else if (!autofill_i18n::PhoneNumbersMatch(GetInfo(*iter), profile.GetInfo(*iter), CountryCode())) { return false; } } else if (StringToLowerASCII(GetInfo(*iter)) != StringToLowerASCII(profile.GetInfo(*iter))) { return false; } } return true; } void AutofillProfile::OverwriteWithOrAddTo(const AutofillProfile& profile) { FieldTypeSet field_types; profile.GetNonEmptyTypes(&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.GetMultiInfo(*iter, &new_values); std::vector existing_values; GetMultiInfo(*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) { AddPhoneIfUnique(*value_iter, &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); } } SetMultiInfo(*iter, existing_values); } else { string16 new_value = profile.GetInfo(*iter); if (StringToLowerASCII(GetInfo(*iter)) != StringToLowerASCII(new_value)) SetInfo(*iter, new_value); } } } void AutofillProfile::AddPhoneIfUnique(const string16& phone, std::vector* existing_phones) { DCHECK(existing_phones); // Phones allow "fuzzy" matching, so "1-800-FLOWERS", "18003569377", // "(800)356-9377" and "356-9377" are considered the same. if (std::find_if(existing_phones->begin(), existing_phones->end(), FindByPhone(phone, CountryCode())) == existing_phones->end()) { existing_phones->push_back(phone); } } string16 AutofillProfile::ConstructInferredLabel( const std::vector& included_fields, size_t num_fields_to_use) const { const string16 separator = l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_ADDRESS_SUMMARY_SEPARATOR); string16 label; size_t num_fields_used = 0; for (std::vector::const_iterator it = included_fields.begin(); it != included_fields.end() && num_fields_used < num_fields_to_use; ++it) { string16 field = GetInfo(*it); if (field.empty()) continue; if (!label.empty()) label.append(separator); label.append(field); ++num_fields_used; } return label; } // static void AutofillProfile::CreateDifferentiatingLabels( const std::vector& profiles, const std::list& indices, const std::vector& fields, size_t num_fields_to_include, std::vector* created_labels) { // For efficiency, we first construct a map of fields to their text values and // each value's frequency. std::map > field_text_frequencies_by_field; for (std::vector::const_iterator field = fields.begin(); field != fields.end(); ++field) { std::map& field_text_frequencies = field_text_frequencies_by_field[*field]; for (std::list::const_iterator it = indices.begin(); it != indices.end(); ++it) { const AutofillProfile* profile = profiles[*it]; string16 field_text = profile->GetInfo(*field); // If this label is not already in the map, add it with frequency 0. if (!field_text_frequencies.count(field_text)) field_text_frequencies[field_text] = 0; // Now, increment the frequency for this label. ++field_text_frequencies[field_text]; } } // Now comes the meat of the algorithm. For each profile, we scan the list of // fields to use, looking for two things: // 1. A (non-empty) field that differentiates the profile from all others // 2. At least |num_fields_to_include| non-empty fields // Before we've satisfied condition (2), we include all fields, even ones that // are identical across all the profiles. Once we've satisfied condition (2), // we only include fields that that have at last two distinct values. for (std::list::const_iterator it = indices.begin(); it != indices.end(); ++it) { const AutofillProfile* profile = profiles[*it]; std::vector label_fields; bool found_differentiating_field = false; for (std::vector::const_iterator field = fields.begin(); field != fields.end(); ++field) { // Skip over empty fields. string16 field_text = profile->GetInfo(*field); if (field_text.empty()) continue; std::map& field_text_frequencies = field_text_frequencies_by_field[*field]; found_differentiating_field |= !field_text_frequencies.count(string16()) && (field_text_frequencies[field_text] == 1); // Once we've found enough non-empty fields, skip over any remaining // fields that are identical across all the profiles. if (label_fields.size() >= num_fields_to_include && (field_text_frequencies.size() == 1)) continue; label_fields.push_back(*field); // If we've (1) found a differentiating field and (2) found at least // |num_fields_to_include| non-empty fields, we're done! if (found_differentiating_field && label_fields.size() >= num_fields_to_include) break; } (*created_labels)[*it] = profile->ConstructInferredLabel(label_fields, label_fields.size()); } } AutofillProfile::FormGroupList AutofillProfile::FormGroups() const { FormGroupList v(5); v[0] = &name_[0]; v[1] = &email_[0]; v[2] = &company_; v[3] = &home_number_[0]; v[4] = &address_; return v; } const FormGroup* AutofillProfile::FormGroupForType( AutofillFieldType type) const { return const_cast(this)->MutableFormGroupForType(type); } FormGroup* AutofillProfile::MutableFormGroupForType(AutofillFieldType type) { FormGroup* form_group = NULL; switch (AutofillType(type).group()) { case AutofillType::NAME: form_group = &name_[0]; break; case AutofillType::EMAIL: form_group = &email_[0]; break; case AutofillType::COMPANY: form_group = &company_; break; case AutofillType::PHONE: form_group = &home_number_[0]; break; case AutofillType::ADDRESS_HOME: form_group = &address_; break; default: break; } return form_group; } // So we can compare AutofillProfiles with EXPECT_EQ(). std::ostream& operator<<(std::ostream& os, const AutofillProfile& profile) { return os << UTF16ToUTF8(profile.Label()) << " " << profile.guid() << " " << UTF16ToUTF8(MultiString(profile, NAME_FIRST)) << " " << UTF16ToUTF8(MultiString(profile, NAME_MIDDLE)) << " " << UTF16ToUTF8(MultiString(profile, NAME_LAST)) << " " << UTF16ToUTF8(MultiString(profile, EMAIL_ADDRESS)) << " " << UTF16ToUTF8(profile.GetInfo(COMPANY_NAME)) << " " << UTF16ToUTF8(profile.GetInfo(ADDRESS_HOME_LINE1)) << " " << UTF16ToUTF8(profile.GetInfo(ADDRESS_HOME_LINE2)) << " " << UTF16ToUTF8(profile.GetInfo(ADDRESS_HOME_CITY)) << " " << UTF16ToUTF8(profile.GetInfo(ADDRESS_HOME_STATE)) << " " << UTF16ToUTF8(profile.GetInfo(ADDRESS_HOME_ZIP)) << " " << UTF16ToUTF8(profile.GetInfo(ADDRESS_HOME_COUNTRY)) << " " << UTF16ToUTF8(MultiString(profile, PHONE_HOME_WHOLE_NUMBER)); }