// Copyright (c) 2011 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 <algorithm> #include <map> #include <set> #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/fax_number.h" #include "chrome/browser/autofill/home_phone_number.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) 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<AutofillFieldType>* suggested_fields, AutofillFieldType excluded_field, std::vector<AutofillFieldType>* distinguishing_fields) { static const AutofillFieldType kDefaultDistinguishingFields[] = { NAME_FULL, ADDRESS_HOME_LINE1, ADDRESS_HOME_CITY, ADDRESS_HOME_STATE, ADDRESS_HOME_ZIP, ADDRESS_HOME_COUNTRY, EMAIL_ADDRESS, PHONE_HOME_WHOLE_NUMBER, PHONE_FAX_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<AutofillFieldType> seen_fields; seen_fields.insert(UNKNOWN_TYPE); seen_fields.insert(GetEquivalentFieldTypeCollapsingNames(excluded_field)); distinguishing_fields->clear(); for (std::vector<AutofillFieldType>::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<AutofillFieldType>::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<string16> 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 <class T> void CopyValuesToItems(AutofillFieldType type, const std::vector<string16>& values, std::vector<T>* form_group_items) { form_group_items->resize(values.size()); 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); } template <class T> void CopyItemsToValues(AutofillFieldType type, const std::vector<T>& form_group_items, std::vector<string16>* values) { values->resize(form_group_items.size()); for (size_t i = 0; i < values->size(); ++i) (*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; case PHONE_FAX_NUMBER: case PHONE_FAX_CITY_CODE: case PHONE_FAX_COUNTRY_CODE: case PHONE_FAX_CITY_AND_NUMBER: case PHONE_FAX_WHOLE_NUMBER: collapsed_set.insert(PHONE_FAX_WHOLE_NUMBER); break; default: collapsed_set.insert(*iter); } } std::swap(*type_set, collapsed_set); } } // namespace AutofillProfile::AutofillProfile(const std::string& guid) : guid_(guid), name_(1), email_(1), home_number_(1), fax_number_(1) { } AutofillProfile::AutofillProfile() : guid_(guid::GenerateGUID()), name_(1), email_(1), home_number_(1), fax_number_(1) { } 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_; fax_number_ = profile.fax_number_; address_ = profile.address_; return *this; } void AutofillProfile::GetPossibleFieldTypes( const string16& text, FieldTypeSet* possible_types) const { FormGroupList info = FormGroups(); for (FormGroupList::const_iterator it = info.begin(); it != info.end(); ++it) (*it)->GetPossibleFieldTypes(text, possible_types); } void AutofillProfile::GetAvailableFieldTypes( FieldTypeSet* available_types) const { FormGroupList info = FormGroups(); for (FormGroupList::const_iterator it = info.begin(); it != info.end(); ++it) (*it)->GetAvailableFieldTypes(available_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)); } void AutofillProfile::SetMultiInfo(AutofillFieldType type, const std::vector<string16>& values) { switch (AutofillType(type).group()) { case AutofillType::NAME: CopyValuesToItems(type, values, &name_); break; case AutofillType::EMAIL: CopyValuesToItems(type, values, &email_); break; case AutofillType::PHONE_HOME: CopyValuesToItems(type, values, &home_number_); break; case AutofillType::PHONE_FAX: CopyValuesToItems(type, values, &fax_number_); 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<string16>* values) const { switch (AutofillType(type).group()) { case AutofillType::NAME: CopyItemsToValues(type, name_, values); break; case AutofillType::EMAIL: CopyItemsToValues(type, email_, values); break; case AutofillType::PHONE_HOME: CopyItemsToValues(type, home_number_, values); break; case AutofillType::PHONE_FAX: CopyItemsToValues(type, fax_number_, 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_HOME || group == AutofillType::PHONE_FAX; } 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<AutofillProfile*>* profiles) { const size_t kMinimalFieldsShown = 2; std::vector<string16> 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<AutofillProfile*>* profiles, const std::vector<AutofillFieldType>* suggested_fields, AutofillFieldType excluded_field, size_t minimal_fields_shown, std::vector<string16>* created_labels) { DCHECK(profiles); DCHECK(created_labels); std::vector<AutofillFieldType> 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<string16, std::list<size_t> > 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<string16, std::list<size_t> >::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; GetAvailableFieldTypes(&types); return types.empty(); } int AutofillProfile::Compare(const AutofillProfile& profile) const { // The following Autofill field types are the only types we store in the WebDB // so far, so we're only concerned with matching these types in the profile. const AutofillFieldType types[] = { NAME_FIRST, NAME_MIDDLE, NAME_LAST, EMAIL_ADDRESS, COMPANY_NAME, ADDRESS_HOME_LINE1, ADDRESS_HOME_LINE2, ADDRESS_HOME_CITY, ADDRESS_HOME_STATE, ADDRESS_HOME_ZIP, ADDRESS_HOME_COUNTRY, PHONE_HOME_NUMBER, PHONE_FAX_NUMBER }; for (size_t index = 0; index < arraysize(types); ++index) { int comparison = GetInfo(types[index]).compare( profile.GetInfo(types[index])); if (comparison != 0) return comparison; } return 0; } int AutofillProfile::CompareMulti(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, PHONE_FAX_NUMBER }; for (size_t i = 0; i < arraysize(multi_value_types); ++i) { std::vector<string16> values_a; std::vector<string16> 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); } void AutofillProfile::OverwriteWithOrAddTo(const AutofillProfile& profile) { FieldTypeSet field_types; profile.GetAvailableFieldTypes(&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<string16> new_values; profile.GetMultiInfo(*iter, &new_values); std::vector<string16> existing_values; GetMultiInfo(*iter, &existing_values); for (std::vector<string16>::iterator value_iter = new_values.begin(); value_iter != new_values.end(); ++value_iter) { // Don't add duplicates. if (std::find(existing_values.begin(), existing_values.end(), *value_iter) == existing_values.end()) { existing_values.insert(existing_values.end(), *value_iter); } } SetMultiInfo(*iter, existing_values); } else { SetInfo(*iter, profile.GetInfo(*iter)); } } } string16 AutofillProfile::ConstructInferredLabel( const std::vector<AutofillFieldType>& 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<AutofillFieldType>::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); #ifndef ANDROID // Fax number has special format, to indicate that this is a fax number. if (*it == PHONE_FAX_WHOLE_NUMBER) { field = l10n_util::GetStringFUTF16( IDS_AUTOFILL_DIALOG_ADDRESS_SUMMARY_FAX_FORMAT, field); } #endif label.append(field); ++num_fields_used; } return label; } // static void AutofillProfile::CreateDifferentiatingLabels( const std::vector<AutofillProfile*>& profiles, const std::list<size_t>& indices, const std::vector<AutofillFieldType>& fields, size_t num_fields_to_include, std::vector<string16>* created_labels) { // For efficiency, we first construct a map of fields to their text values and // each value's frequency. std::map<AutofillFieldType, std::map<string16, size_t> > field_text_frequencies_by_field; for (std::vector<AutofillFieldType>::const_iterator field = fields.begin(); field != fields.end(); ++field) { std::map<string16, size_t>& field_text_frequencies = field_text_frequencies_by_field[*field]; for (std::list<size_t>::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<size_t>::const_iterator it = indices.begin(); it != indices.end(); ++it) { const AutofillProfile* profile = profiles[*it]; std::vector<AutofillFieldType> label_fields; bool found_differentiating_field = false; for (std::vector<AutofillFieldType>::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<string16, size_t>& 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(6); v[0] = &name_[0]; v[1] = &email_[0]; v[2] = &company_; v[3] = &home_number_[0]; v[4] = &fax_number_[0]; v[5] = &address_; return v; } const FormGroup* AutofillProfile::FormGroupForType( AutofillFieldType type) const { return const_cast<AutofillProfile*>(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_HOME: form_group = &home_number_[0]; break; case AutofillType::PHONE_FAX: form_group = &fax_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)) << " " << UTF16ToUTF8(MultiString(profile, PHONE_FAX_WHOLE_NUMBER)); }