// 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));
}