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