普通文本  |  682行  |  22.93 KB

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