autofill_profile.cc revision 72a454cd3513ac24fbdd0e0cb9ad70b86a99b801
1// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/autofill/autofill_profile.h"
6
7#include <algorithm>
8#include <set>
9
10#include "base/stl_util-inl.h"
11#include "base/utf_string_conversions.h"
12#include "chrome/browser/autofill/address.h"
13#include "chrome/browser/autofill/autofill_manager.h"
14#include "chrome/browser/autofill/autofill_type.h"
15#include "chrome/browser/autofill/contact_info.h"
16#include "chrome/browser/autofill/fax_number.h"
17#include "chrome/browser/autofill/home_address.h"
18#include "chrome/browser/autofill/home_phone_number.h"
19#include "chrome/common/guid.h"
20#include "grit/generated_resources.h"
21#include "ui/base/l10n/l10n_util.h"
22
23namespace {
24
25void InitPersonalInfo(FormGroupMap* personal_info) {
26  (*personal_info)[AutoFillType::CONTACT_INFO] = new ContactInfo();
27  (*personal_info)[AutoFillType::PHONE_HOME] = new HomePhoneNumber();
28  (*personal_info)[AutoFillType::PHONE_FAX] = new FaxNumber();
29  (*personal_info)[AutoFillType::ADDRESS_HOME] = new HomeAddress();
30}
31
32// Like |AutoFillType::GetEquivalentFieldType()|, but also returns |NAME_FULL|
33// for first, middle, and last name field types.
34AutoFillFieldType GetEquivalentFieldTypeCollapsingNames(
35    AutoFillFieldType field_type) {
36  if (field_type == NAME_FIRST || field_type == NAME_MIDDLE ||
37      field_type == NAME_LAST)
38    return NAME_FULL;
39
40  return AutoFillType::GetEquivalentFieldType(field_type);
41}
42
43// Fills |distinguishing_fields| with a list of fields to use when creating
44// labels that can help to distinguish between two profiles. Draws fields from
45// |suggested_fields| if it is non-NULL; otherwise returns a default list.
46// If |suggested_fields| is non-NULL, does not include |excluded_field| in the
47// list. Otherwise, |excluded_field| is ignored, and should be set to
48// |UNKNOWN_TYPE| by convention. The resulting list of fields is sorted in
49// decreasing order of importance.
50void GetFieldsForDistinguishingProfiles(
51    const std::vector<AutoFillFieldType>* suggested_fields,
52    AutoFillFieldType excluded_field,
53    std::vector<AutoFillFieldType>* distinguishing_fields) {
54  static const AutoFillFieldType kDefaultDistinguishingFields[] = {
55    NAME_FULL,
56    ADDRESS_HOME_LINE1,
57    ADDRESS_HOME_CITY,
58    ADDRESS_HOME_STATE,
59    ADDRESS_HOME_ZIP,
60    ADDRESS_HOME_COUNTRY,
61    EMAIL_ADDRESS,
62    PHONE_HOME_WHOLE_NUMBER,
63    PHONE_FAX_WHOLE_NUMBER,
64    COMPANY_NAME,
65  };
66
67  if (!suggested_fields) {
68    DCHECK_EQ(excluded_field, UNKNOWN_TYPE);
69    distinguishing_fields->assign(
70        kDefaultDistinguishingFields,
71        kDefaultDistinguishingFields + arraysize(kDefaultDistinguishingFields));
72    return;
73  }
74
75  // Keep track of which fields we've seen so that we avoid duplicate entries.
76  // Always ignore fields of unknown type and the excluded field.
77  std::set<AutoFillFieldType> seen_fields;
78  seen_fields.insert(UNKNOWN_TYPE);
79  seen_fields.insert(GetEquivalentFieldTypeCollapsingNames(excluded_field));
80
81  distinguishing_fields->clear();
82  for (std::vector<AutoFillFieldType>::const_iterator it =
83           suggested_fields->begin();
84       it != suggested_fields->end(); ++it) {
85    AutoFillFieldType suggested_type =
86        GetEquivalentFieldTypeCollapsingNames(*it);
87    if (seen_fields.insert(suggested_type).second)
88      distinguishing_fields->push_back(suggested_type);
89  }
90
91  // Special case: If the excluded field is a partial name (e.g. first name) and
92  // the suggested fields include other name fields, include |NAME_FULL| in the
93  // list of distinguishing fields as a last-ditch fallback. This allows us to
94  // distinguish between profiles that are identical except for the name.
95  if (excluded_field != NAME_FULL &&
96      GetEquivalentFieldTypeCollapsingNames(excluded_field) == NAME_FULL) {
97    for (std::vector<AutoFillFieldType>::const_iterator it =
98             suggested_fields->begin();
99         it != suggested_fields->end(); ++it) {
100      if (*it != excluded_field &&
101          GetEquivalentFieldTypeCollapsingNames(*it) == NAME_FULL) {
102        distinguishing_fields->push_back(NAME_FULL);
103        break;
104      }
105    }
106  }
107}
108
109}  // namespace
110
111AutoFillProfile::AutoFillProfile(const std::string& guid)
112    : guid_(guid) {
113  InitPersonalInfo(&personal_info_);
114}
115
116AutoFillProfile::AutoFillProfile()
117    : guid_(guid::GenerateGUID()) {
118  InitPersonalInfo(&personal_info_);
119}
120
121AutoFillProfile::AutoFillProfile(const AutoFillProfile& source)
122    : FormGroup() {
123  operator=(source);
124}
125
126AutoFillProfile::~AutoFillProfile() {
127  STLDeleteContainerPairSecondPointers(personal_info_.begin(),
128                                       personal_info_.end());
129}
130
131void AutoFillProfile::GetPossibleFieldTypes(
132    const string16& text,
133    FieldTypeSet* possible_types) const {
134  for (FormGroupMap::const_iterator iter = personal_info_.begin();
135       iter != personal_info_.end(); ++iter) {
136    FormGroup* data = iter->second;
137    DCHECK(data != NULL);
138    data->GetPossibleFieldTypes(text, possible_types);
139  }
140}
141
142void AutoFillProfile::GetAvailableFieldTypes(
143    FieldTypeSet* available_types) const {
144  for (FormGroupMap::const_iterator iter = personal_info_.begin();
145       iter != personal_info_.end(); ++iter) {
146    FormGroup* data = iter->second;
147    DCHECK(data != NULL);
148    data->GetAvailableFieldTypes(available_types);
149  }
150}
151
152string16 AutoFillProfile::GetFieldText(const AutoFillType& type) const {
153  AutoFillType return_type(
154      AutoFillType::GetEquivalentFieldType(type.field_type()));
155
156  FormGroupMap::const_iterator iter = personal_info_.find(return_type.group());
157  if (iter == personal_info_.end() || iter->second == NULL)
158    return string16();
159
160  return iter->second->GetFieldText(return_type);
161}
162
163void AutoFillProfile::FindInfoMatches(
164    const AutoFillType& type,
165    const string16& info,
166    std::vector<string16>* matched_text) const {
167  if (matched_text == NULL) {
168    DLOG(ERROR) << "NULL matched text passed in";
169    return;
170  }
171
172  string16 clean_info = StringToLowerASCII(CollapseWhitespace(info, false));
173
174  // If the field_type is unknown, then match against all field types.
175  if (type.field_type() == UNKNOWN_TYPE) {
176    FormGroupMap::const_iterator iter;
177    for (iter = personal_info_.begin(); iter != personal_info_.end(); ++iter) {
178      iter->second->FindInfoMatches(type, clean_info, matched_text);
179    }
180  } else {
181    FormGroupMap::const_iterator iter = personal_info_.find(type.group());
182    DCHECK(iter != personal_info_.end() && iter->second != NULL);
183    if (iter != personal_info_.end() && iter->second != NULL)
184      iter->second->FindInfoMatches(type, clean_info, matched_text);
185  }
186}
187
188void AutoFillProfile::SetInfo(const AutoFillType& type, const string16& value) {
189  FormGroupMap::const_iterator iter = personal_info_.find(type.group());
190  if (iter == personal_info_.end() || iter->second == NULL)
191    return;
192
193  iter->second->SetInfo(type, CollapseWhitespace(value, false));
194}
195
196FormGroup* AutoFillProfile::Clone() const {
197  return new AutoFillProfile(*this);
198}
199
200const string16 AutoFillProfile::Label() const {
201  return label_;
202}
203
204// static
205bool AutoFillProfile::AdjustInferredLabels(
206    std::vector<AutoFillProfile*>* profiles) {
207  const size_t kMinimalFieldsShown = 2;
208
209  std::vector<string16> created_labels;
210  CreateInferredLabels(profiles, NULL, UNKNOWN_TYPE, kMinimalFieldsShown,
211                       &created_labels);
212  DCHECK_EQ(profiles->size(), created_labels.size());
213
214  bool updated_labels = false;
215  for (size_t i = 0; i < profiles->size(); ++i) {
216    if ((*profiles)[i]->Label() != created_labels[i]) {
217      updated_labels = true;
218      (*profiles)[i]->set_label(created_labels[i]);
219    }
220  }
221  return updated_labels;
222}
223
224// static
225void AutoFillProfile::CreateInferredLabels(
226    const std::vector<AutoFillProfile*>* profiles,
227    const std::vector<AutoFillFieldType>* suggested_fields,
228    AutoFillFieldType excluded_field,
229    size_t minimal_fields_shown,
230    std::vector<string16>* created_labels) {
231  DCHECK(profiles);
232  DCHECK(created_labels);
233
234  std::vector<AutoFillFieldType> fields_to_use;
235  GetFieldsForDistinguishingProfiles(suggested_fields, excluded_field,
236                                     &fields_to_use);
237
238  // Construct the default label for each profile. Also construct a map that
239  // associates each label with the profiles that have this label. This map is
240  // then used to detect which labels need further differentiating fields.
241  std::map<string16, std::list<size_t> > labels;
242  for (size_t i = 0; i < profiles->size(); ++i) {
243    string16 label =
244        (*profiles)[i]->ConstructInferredLabel(fields_to_use,
245                                               minimal_fields_shown);
246    labels[label].push_back(i);
247  }
248
249  created_labels->resize(profiles->size());
250  for (std::map<string16, std::list<size_t> >::const_iterator it =
251           labels.begin();
252       it != labels.end(); ++it) {
253    if (it->second.size() == 1) {
254      // This label is unique, so use it without any further ado.
255      string16 label = it->first;
256      size_t profile_index = it->second.front();
257      (*created_labels)[profile_index] = label;
258    } else {
259      // We have more than one profile with the same label, so add
260      // differentiating fields.
261      CreateDifferentiatingLabels(*profiles, it->second, fields_to_use,
262                                  minimal_fields_shown, created_labels);
263    }
264  }
265}
266
267bool AutoFillProfile::IsEmpty() const {
268  FieldTypeSet types;
269  GetAvailableFieldTypes(&types);
270  return types.empty();
271}
272
273void AutoFillProfile::operator=(const AutoFillProfile& source) {
274  if (this == &source)
275    return;
276
277  label_ = source.label_;
278  guid_ = source.guid_;
279
280  STLDeleteContainerPairSecondPointers(personal_info_.begin(),
281                                       personal_info_.end());
282  personal_info_.clear();
283
284  FormGroupMap::const_iterator iter;
285  for (iter = source.personal_info_.begin();
286       iter != source.personal_info_.end();
287       ++iter) {
288    personal_info_[iter->first] = iter->second->Clone();
289  }
290}
291
292int AutoFillProfile::Compare(const AutoFillProfile& profile) const {
293  // The following AutoFill field types are the only types we store in the WebDB
294  // so far, so we're only concerned with matching these types in the profile.
295  const AutoFillFieldType types[] = { NAME_FIRST,
296                                      NAME_MIDDLE,
297                                      NAME_LAST,
298                                      EMAIL_ADDRESS,
299                                      COMPANY_NAME,
300                                      ADDRESS_HOME_LINE1,
301                                      ADDRESS_HOME_LINE2,
302                                      ADDRESS_HOME_CITY,
303                                      ADDRESS_HOME_STATE,
304                                      ADDRESS_HOME_ZIP,
305                                      ADDRESS_HOME_COUNTRY,
306                                      PHONE_HOME_NUMBER,
307                                      PHONE_FAX_NUMBER };
308
309  for (size_t index = 0; index < arraysize(types); ++index) {
310    int comparison = GetFieldText(AutoFillType(types[index])).compare(
311        profile.GetFieldText(AutoFillType(types[index])));
312    if (comparison != 0)
313      return comparison;
314  }
315
316  return 0;
317}
318
319bool AutoFillProfile::operator==(const AutoFillProfile& profile) const {
320  if (label_ != profile.label_ || guid_ != profile.guid_)
321    return false;
322
323  return Compare(profile) == 0;
324}
325
326bool AutoFillProfile::operator!=(const AutoFillProfile& profile) const {
327  return !operator==(profile);
328}
329
330const string16 AutoFillProfile::PrimaryValue() const {
331  return GetFieldText(AutoFillType(NAME_FULL)) +
332         GetFieldText(AutoFillType(ADDRESS_HOME_LINE1)) +
333         GetFieldText(AutoFillType(ADDRESS_HOME_LINE2)) +
334         GetFieldText(AutoFillType(EMAIL_ADDRESS));
335}
336
337string16 AutoFillProfile::ConstructInferredLabel(
338    const std::vector<AutoFillFieldType>& included_fields,
339    size_t num_fields_to_use) const {
340  const string16 separator =
341      l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_ADDRESS_SUMMARY_SEPARATOR);
342
343  string16 label;
344  size_t num_fields_used = 0;
345  for (std::vector<AutoFillFieldType>::const_iterator it =
346           included_fields.begin();
347       it != included_fields.end() && num_fields_used < num_fields_to_use;
348       ++it) {
349    string16 field = GetFieldText(AutoFillType(*it));
350    if (field.empty())
351      continue;
352
353    if (!label.empty())
354      label.append(separator);
355
356#ifndef ANDROID
357    // Fax number has special format, to indicate that this is a fax number.
358    if (*it == PHONE_FAX_WHOLE_NUMBER) {
359      field = l10n_util::GetStringFUTF16(
360          IDS_AUTOFILL_DIALOG_ADDRESS_SUMMARY_FAX_FORMAT, field);
361    }
362#endif
363    label.append(field);
364    ++num_fields_used;
365  }
366  return label;
367}
368
369// static
370void AutoFillProfile::CreateDifferentiatingLabels(
371    const std::vector<AutoFillProfile*>& profiles,
372    const std::list<size_t>& indices,
373    const std::vector<AutoFillFieldType>& fields,
374    size_t num_fields_to_include,
375    std::vector<string16>* created_labels) {
376  // For efficiency, we first construct a map of fields to their text values and
377  // each value's frequency.
378  std::map<AutoFillFieldType,
379           std::map<string16, size_t> > field_text_frequencies_by_field;
380  for (std::vector<AutoFillFieldType>::const_iterator field = fields.begin();
381       field != fields.end(); ++field) {
382    std::map<string16, size_t>& field_text_frequencies =
383        field_text_frequencies_by_field[*field];
384
385    for (std::list<size_t>::const_iterator it = indices.begin();
386         it != indices.end(); ++it) {
387      const AutoFillProfile* profile = profiles[*it];
388      string16 field_text = profile->GetFieldText(AutoFillType(*field));
389
390      // If this label is not already in the map, add it with frequency 0.
391      if (!field_text_frequencies.count(field_text))
392        field_text_frequencies[field_text] = 0;
393
394      // Now, increment the frequency for this label.
395      ++field_text_frequencies[field_text];
396    }
397  }
398
399  // Now comes the meat of the algorithm. For each profile, we scan the list of
400  // fields to use, looking for two things:
401  //  1. A (non-empty) field that differentiates the profile from all others
402  //  2. At least |num_fields_to_include| non-empty fields
403  // Before we've satisfied condition (2), we include all fields, even ones that
404  // are identical across all the profiles. Once we've satisfied condition (2),
405  // we only include fields that that have at last two distinct values.
406  for (std::list<size_t>::const_iterator it = indices.begin();
407       it != indices.end(); ++it) {
408    const AutoFillProfile* profile = profiles[*it];
409
410    std::vector<AutoFillFieldType> label_fields;
411    bool found_differentiating_field = false;
412    for (std::vector<AutoFillFieldType>::const_iterator field = fields.begin();
413         field != fields.end(); ++field) {
414      // Skip over empty fields.
415      string16 field_text = profile->GetFieldText(AutoFillType(*field));
416      if (field_text.empty())
417        continue;
418
419      std::map<string16, size_t>& field_text_frequencies =
420          field_text_frequencies_by_field[*field];
421      found_differentiating_field |=
422          !field_text_frequencies.count(string16()) &&
423          (field_text_frequencies[field_text] == 1);
424
425      // Once we've found enough non-empty fields, skip over any remaining
426      // fields that are identical across all the profiles.
427      if (label_fields.size() >= num_fields_to_include &&
428          (field_text_frequencies.size() == 1))
429        continue;
430
431      label_fields.push_back(*field);
432
433      // If we've (1) found a differentiating field and (2) found at least
434      // |num_fields_to_include| non-empty fields, we're done!
435      if (found_differentiating_field &&
436          label_fields.size() >= num_fields_to_include)
437        break;
438    }
439
440    (*created_labels)[*it] =
441        profile->ConstructInferredLabel(label_fields,
442                                        label_fields.size());
443  }
444}
445
446// So we can compare AutoFillProfiles with EXPECT_EQ().
447std::ostream& operator<<(std::ostream& os, const AutoFillProfile& profile) {
448  return os
449      << UTF16ToUTF8(profile.Label())
450      << " "
451      << profile.guid()
452      << " "
453      << UTF16ToUTF8(profile.GetFieldText(AutoFillType(NAME_FIRST)))
454      << " "
455      << UTF16ToUTF8(profile.GetFieldText(AutoFillType(NAME_MIDDLE)))
456      << " "
457      << UTF16ToUTF8(profile.GetFieldText(AutoFillType(NAME_LAST)))
458      << " "
459      << UTF16ToUTF8(profile.GetFieldText(AutoFillType(EMAIL_ADDRESS)))
460      << " "
461      << UTF16ToUTF8(profile.GetFieldText(AutoFillType(COMPANY_NAME)))
462      << " "
463      << UTF16ToUTF8(profile.GetFieldText(AutoFillType(ADDRESS_HOME_LINE1)))
464      << " "
465      << UTF16ToUTF8(profile.GetFieldText(AutoFillType(ADDRESS_HOME_LINE2)))
466      << " "
467      << UTF16ToUTF8(profile.GetFieldText(AutoFillType(ADDRESS_HOME_CITY)))
468      << " "
469      << UTF16ToUTF8(profile.GetFieldText(AutoFillType(ADDRESS_HOME_STATE)))
470      << " "
471      << UTF16ToUTF8(profile.GetFieldText(AutoFillType(ADDRESS_HOME_ZIP)))
472      << " "
473      << UTF16ToUTF8(profile.GetFieldText(AutoFillType(ADDRESS_HOME_COUNTRY)))
474      << " "
475      << UTF16ToUTF8(profile.GetFieldText(AutoFillType(
476             PHONE_HOME_WHOLE_NUMBER)))
477      << " "
478      << UTF16ToUTF8(profile.GetFieldText(AutoFillType(
479             PHONE_FAX_WHOLE_NUMBER)));
480}
481