blob: 4150e69ea957c08abc6911229b37ac78d0535cda [file] [log] [blame]
// Copyright (c) 2010 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 <list>
#include <map>
#include <set>
#include <vector>
#include "app/l10n_util.h"
#include "base/stl_util-inl.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/autofill/address.h"
#include "chrome/browser/autofill/autofill_manager.h"
#include "chrome/browser/autofill/contact_info.h"
#include "chrome/browser/autofill/fax_number.h"
#include "chrome/browser/autofill/home_address.h"
#include "chrome/browser/autofill/home_phone_number.h"
#include "grit/generated_resources.h"
namespace {
void InitPersonalInfo(FormGroupMap* personal_info) {
(*personal_info)[AutoFillType::CONTACT_INFO] = new ContactInfo();
(*personal_info)[AutoFillType::PHONE_HOME] = new HomePhoneNumber();
(*personal_info)[AutoFillType::PHONE_FAX] = new FaxNumber();
(*personal_info)[AutoFillType::ADDRESS_HOME] = new HomeAddress();
}
} // namespace
AutoFillProfile::AutoFillProfile(const string16& label, int unique_id)
: label_(label),
unique_id_(unique_id) {
InitPersonalInfo(&personal_info_);
}
AutoFillProfile::AutoFillProfile()
: unique_id_(0) {
InitPersonalInfo(&personal_info_);
}
AutoFillProfile::AutoFillProfile(const AutoFillProfile& source)
: FormGroup() {
operator=(source);
}
AutoFillProfile::~AutoFillProfile() {
STLDeleteContainerPairSecondPointers(personal_info_.begin(),
personal_info_.end());
}
void AutoFillProfile::GetPossibleFieldTypes(
const string16& text,
FieldTypeSet* possible_types) const {
for (FormGroupMap::const_iterator iter = personal_info_.begin();
iter != personal_info_.end(); ++iter) {
FormGroup* data = iter->second;
DCHECK(data != NULL);
data->GetPossibleFieldTypes(text, possible_types);
}
}
void AutoFillProfile::GetAvailableFieldTypes(
FieldTypeSet* available_types) const {
for (FormGroupMap::const_iterator iter = personal_info_.begin();
iter != personal_info_.end(); ++iter) {
FormGroup* data = iter->second;
DCHECK(data != NULL);
data->GetAvailableFieldTypes(available_types);
}
}
string16 AutoFillProfile::GetFieldText(const AutoFillType& type) const {
AutoFillType return_type = type;
// When billing information is requested from the profile we map to the
// home address equivalents. This indicates the address information within
// this profile is being used to fill billing fields in the form.
switch (type.field_type()) {
case ADDRESS_BILLING_LINE1:
return_type = AutoFillType(ADDRESS_HOME_LINE1);
break;
case ADDRESS_BILLING_LINE2:
return_type = AutoFillType(ADDRESS_HOME_LINE2);
break;
case ADDRESS_BILLING_APT_NUM:
return_type = AutoFillType(ADDRESS_HOME_APT_NUM);
break;
case ADDRESS_BILLING_CITY:
return_type = AutoFillType(ADDRESS_HOME_CITY);
break;
case ADDRESS_BILLING_STATE:
return_type = AutoFillType(ADDRESS_HOME_STATE);
break;
case ADDRESS_BILLING_ZIP:
return_type = AutoFillType(ADDRESS_HOME_ZIP);
break;
case ADDRESS_BILLING_COUNTRY:
return_type = AutoFillType(ADDRESS_HOME_COUNTRY);
break;
default:
break;
}
FormGroupMap::const_iterator iter = personal_info_.find(return_type.group());
if (iter == personal_info_.end() || iter->second == NULL)
return string16();
return iter->second->GetFieldText(return_type);
}
void AutoFillProfile::FindInfoMatches(
const AutoFillType& type,
const string16& info,
std::vector<string16>* matched_text) const {
if (matched_text == NULL) {
DLOG(ERROR) << "NULL matched text passed in";
return;
}
string16 clean_info = StringToLowerASCII(CollapseWhitespace(info, false));
// If the field_type is unknown, then match against all field types.
if (type.field_type() == UNKNOWN_TYPE) {
FormGroupMap::const_iterator iter;
for (iter = personal_info_.begin(); iter != personal_info_.end(); ++iter) {
iter->second->FindInfoMatches(type, clean_info, matched_text);
}
} else {
FormGroupMap::const_iterator iter = personal_info_.find(type.group());
DCHECK(iter != personal_info_.end() && iter->second != NULL);
if (iter != personal_info_.end() && iter->second != NULL)
iter->second->FindInfoMatches(type, clean_info, matched_text);
}
}
void AutoFillProfile::SetInfo(const AutoFillType& type, const string16& value) {
FormGroupMap::const_iterator iter = personal_info_.find(type.group());
if (iter == personal_info_.end() || iter->second == NULL)
return;
iter->second->SetInfo(type, CollapseWhitespace(value, false));
}
FormGroup* AutoFillProfile::Clone() const {
AutoFillProfile* profile = new AutoFillProfile();
profile->label_ = label_;
profile->unique_id_ = unique_id();
FormGroupMap::const_iterator iter;
for (iter = personal_info_.begin(); iter != personal_info_.end(); ++iter) {
if (profile->personal_info_.count(iter->first))
delete profile->personal_info_[iter->first];
profile->personal_info_[iter->first] = iter->second->Clone();
}
return profile;
}
string16 AutoFillProfile::PreviewSummary() const {
// Fetch the components of the summary string. Any or all of these
// may be an empty string.
string16 first_name = GetFieldText(AutoFillType(NAME_FIRST));
string16 last_name = GetFieldText(AutoFillType(NAME_LAST));
string16 address = GetFieldText(AutoFillType(ADDRESS_HOME_LINE1));
// String separators depend (below) on the existence of the various fields.
bool have_first_name = first_name.length() > 0;
bool have_last_name = last_name.length() > 0;
bool have_address = address.length() > 0;
// Name separator defaults to "". Space if we have first and last name.
string16 name_separator;
if (have_first_name && have_last_name) {
name_separator = l10n_util::GetStringUTF16(
IDS_AUTOFILL_DIALOG_ADDRESS_NAME_SEPARATOR);
}
// E.g. "John Smith", or "John", or "Smith", or "".
string16 name_format = l10n_util::GetStringFUTF16(
IDS_AUTOFILL_DIALOG_ADDRESS_SUMMARY_NAME_FORMAT,
first_name,
name_separator,
last_name);
// Summary separator defaults to "". ", " if we have name and address.
string16 summary_separator;
if ((have_first_name || have_last_name) && have_address) {
summary_separator = l10n_util::GetStringUTF16(
IDS_AUTOFILL_DIALOG_ADDRESS_SUMMARY_SEPARATOR);
}
// E.g. "John Smith, 123 Main Street".
string16 summary_format = l10n_util::GetStringFUTF16(
IDS_AUTOFILL_DIALOG_ADDRESS_SUMMARY_FORMAT,
name_format,
summary_separator,
address);
return summary_format;
}
// static
bool AutoFillProfile::AdjustInferredLabels(
std::vector<AutoFillProfile*>* profiles) {
std::vector<string16> created_labels;
const size_t kMinimalFieldsShown = 2;
CreateInferredLabels(profiles, &created_labels, kMinimalFieldsShown,
UNKNOWN_TYPE);
DCHECK(profiles->size() == created_labels.size());
bool updated_labels = false;
for (size_t i = 0; i < profiles->size(); ++i) {
if (profiles->at(i)->Label() != created_labels[i]) {
updated_labels = true;
profiles->at(i)->set_label(created_labels[i]);
}
}
return updated_labels;
}
// static
void AutoFillProfile::CreateInferredLabels(
const std::vector<AutoFillProfile*>* profiles,
std::vector<string16>* created_labels,
size_t minimal_fields_shown,
AutoFillFieldType exclude_field) {
// These fields are use to distinguish between two profiles in the order of
// importance, e. g. if both EMAIL_ADDRESS and COMPANY_NAME are different,
// EMAIL_ADDRESS will be used to distinguish them.
const AutoFillFieldType distinguishing_fields[] = {
// First non empty data are always present in the label, even if it matches.
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 (exclude_field == NAME_FIRST || exclude_field == NAME_LAST)
exclude_field = NAME_FULL;
DCHECK(profiles);
DCHECK(created_labels);
created_labels->resize(profiles->size());
std::map<string16, std::list<size_t> > labels;
for (size_t it = 0; it < profiles->size(); ++it) {
labels[
profiles->at(it)->GetFieldText(AutoFillType(NAME_FULL))].push_back(it);
}
std::map<string16, std::list<size_t> >::iterator label_iterator;
for (label_iterator = labels.begin(); label_iterator != labels.end();
++label_iterator) {
if (label_iterator->second.size() > 1) {
// We have more than one item with the same preview, add differentiating
// data.
std::list<size_t>::iterator similar_profiles;
std::map<string16, int> tested_fields[arraysize(distinguishing_fields)];
for (similar_profiles = label_iterator->second.begin();
similar_profiles != label_iterator->second.end();
++similar_profiles) {
for (size_t i = 0; i < arraysize(distinguishing_fields); ++i) {
string16 key = profiles->at(*similar_profiles)->GetFieldText(
AutoFillType(distinguishing_fields[i]));
std::map<string16, int>::iterator tested_field =
tested_fields[i].find(key);
if (tested_field == tested_fields[i].end())
(tested_fields[i])[key] = 1;
else
++(tested_field->second);
}
}
std::vector<AutoFillFieldType> fields;
std::vector<AutoFillFieldType> first_non_empty_fields;
size_t added_fields = 0;
bool matched_necessary = false;
// Leave it as a candidate if it is not the same for everybody.
for (size_t i = 0; i < arraysize(distinguishing_fields); ++i) {
if (tested_fields[i].size() == label_iterator->second.size()) {
// This field is different for everybody.
if (!matched_necessary) {
matched_necessary = true;
fields.clear();
added_fields = 1;
if (first_non_empty_fields.size()) {
added_fields += first_non_empty_fields.size();
fields.resize(added_fields - 1);
std::copy(first_non_empty_fields.begin(),
first_non_empty_fields.end(),
fields.begin());
}
} else {
++added_fields;
}
fields.push_back(distinguishing_fields[i]);
if (added_fields >= minimal_fields_shown)
break;
} else if (tested_fields[i].size() != 1) {
// this field is different for some.
if (added_fields < minimal_fields_shown) {
first_non_empty_fields.push_back(distinguishing_fields[i]);
++added_fields;
if (added_fields == minimal_fields_shown && matched_necessary)
break;
}
fields.push_back(distinguishing_fields[i]);
} else if (added_fields < minimal_fields_shown &&
exclude_field != distinguishing_fields[i] &&
!label_iterator->first.empty()) {
fields.push_back(distinguishing_fields[i]);
first_non_empty_fields.push_back(distinguishing_fields[i]);
++added_fields;
if (added_fields == minimal_fields_shown && matched_necessary)
break;
}
}
// Update labels if needed.
for (similar_profiles = label_iterator->second.begin();
similar_profiles != label_iterator->second.end();
++similar_profiles) {
size_t field_it = 0;
for (size_t field_id = 0;
field_id < arraysize(distinguishing_fields) &&
field_it < fields.size(); ++field_id) {
if (fields[field_it] == distinguishing_fields[field_id]) {
if ((tested_fields[field_id])[
profiles->at(*similar_profiles)->GetFieldText(
AutoFillType(distinguishing_fields[field_id]))] == 1) {
// this field is unique among the subset.
break;
}
++field_it;
}
}
string16 new_label;
if (field_it < fields.size() && fields.size() > minimal_fields_shown) {
std::vector<AutoFillFieldType> unique_fields;
unique_fields.resize(fields.size());
std::copy(fields.begin(), fields.end(), unique_fields.begin());
unique_fields.resize(std::max(field_it + 1, minimal_fields_shown));
new_label =
profiles->at(*similar_profiles)->ConstructInferredLabel(
&unique_fields);
} else {
new_label =
profiles->at(*similar_profiles)->ConstructInferredLabel(&fields);
}
(*created_labels)[*similar_profiles] = new_label;
}
} else {
std::vector<AutoFillFieldType> non_empty_fields;
size_t include_fields = minimal_fields_shown ? minimal_fields_shown : 1;
non_empty_fields.reserve(include_fields);
for (size_t i = 0; i < arraysize(distinguishing_fields); ++i) {
if (exclude_field == distinguishing_fields[i])
continue;
if (!profiles->at(label_iterator->second.front())->GetFieldText(
AutoFillType(distinguishing_fields[i])).empty()) {
non_empty_fields.push_back(distinguishing_fields[i]);
if (non_empty_fields.size() >= include_fields)
break;
}
}
(*created_labels)[label_iterator->second.front()] =
profiles->at(label_iterator->second.front())->ConstructInferredLabel(
&non_empty_fields);
}
}
}
bool AutoFillProfile::IsEmpty() const {
FieldTypeSet types;
GetAvailableFieldTypes(&types);
return types.empty();
}
void AutoFillProfile::operator=(const AutoFillProfile& source) {
label_ = source.label_;
unique_id_ = source.unique_id_;
STLDeleteContainerPairSecondPointers(personal_info_.begin(),
personal_info_.end());
personal_info_.clear();
FormGroupMap::const_iterator iter;
for (iter = source.personal_info_.begin();
iter != source.personal_info_.end();
++iter) {
personal_info_[iter->first] = iter->second->Clone();
}
}
bool AutoFillProfile::operator==(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 };
if (label_ != profile.label_ ||
unique_id_ != profile.unique_id_) {
return false;
}
for (size_t index = 0; index < arraysize(types); ++index) {
if (GetFieldText(AutoFillType(types[index])) !=
profile.GetFieldText(AutoFillType(types[index])))
return false;
}
return true;
}
bool AutoFillProfile::operator!=(const AutoFillProfile& profile) const {
return !operator==(profile);
}
Address* AutoFillProfile::GetHomeAddress() {
return static_cast<Address*>(personal_info_[AutoFillType::ADDRESS_HOME]);
}
string16 AutoFillProfile::ConstructInferredLabel(
const std::vector<AutoFillFieldType>* included_fields) const {
DCHECK(included_fields);
string16 label;
string16 separator = l10n_util::GetStringUTF16(
IDS_AUTOFILL_DIALOG_ADDRESS_SUMMARY_SEPARATOR);
for (std::vector<AutoFillFieldType>::const_iterator it =
included_fields->begin(); it != included_fields->end(); ++it) {
string16 field = GetFieldText(AutoFillType(*it));
if (!field.empty()) {
if (!label.empty()) {
label.append(separator);
}
// 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);
}
label.append(field);
}
}
return label;
}
// So we can compare AutoFillProfiles with EXPECT_EQ().
std::ostream& operator<<(std::ostream& os, const AutoFillProfile& profile) {
return os
<< UTF16ToUTF8(profile.Label())
<< " "
<< profile.unique_id()
<< " "
<< UTF16ToUTF8(profile.GetFieldText(AutoFillType(NAME_FIRST)))
<< " "
<< UTF16ToUTF8(profile.GetFieldText(AutoFillType(NAME_MIDDLE)))
<< " "
<< UTF16ToUTF8(profile.GetFieldText(AutoFillType(NAME_LAST)))
<< " "
<< UTF16ToUTF8(profile.GetFieldText(AutoFillType(EMAIL_ADDRESS)))
<< " "
<< UTF16ToUTF8(profile.GetFieldText(AutoFillType(COMPANY_NAME)))
<< " "
<< UTF16ToUTF8(profile.GetFieldText(AutoFillType(ADDRESS_HOME_LINE1)))
<< " "
<< UTF16ToUTF8(profile.GetFieldText(AutoFillType(ADDRESS_HOME_LINE2)))
<< " "
<< UTF16ToUTF8(profile.GetFieldText(AutoFillType(ADDRESS_HOME_CITY)))
<< " "
<< UTF16ToUTF8(profile.GetFieldText(AutoFillType(ADDRESS_HOME_STATE)))
<< " "
<< UTF16ToUTF8(profile.GetFieldText(AutoFillType(ADDRESS_HOME_ZIP)))
<< " "
<< UTF16ToUTF8(profile.GetFieldText(AutoFillType(ADDRESS_HOME_COUNTRY)))
<< " "
<< UTF16ToUTF8(profile.GetFieldText(AutoFillType(
PHONE_HOME_WHOLE_NUMBER)))
<< " "
<< UTF16ToUTF8(profile.GetFieldText(AutoFillType(
PHONE_FAX_WHOLE_NUMBER)));
}