1// Copyright (c) 2011 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 <map> 9#include <set> 10 11#include "base/basictypes.h" 12#include "base/logging.h" 13#include "base/string_util.h" 14#include "base/utf_string_conversions.h" 15#include "chrome/browser/autofill/address.h" 16#include "chrome/browser/autofill/autofill_type.h" 17#include "chrome/browser/autofill/contact_info.h" 18#include "chrome/browser/autofill/fax_number.h" 19#include "chrome/browser/autofill/home_phone_number.h" 20#include "chrome/common/guid.h" 21#include "grit/generated_resources.h" 22#include "ui/base/l10n/l10n_util.h" 23 24namespace { 25 26// Like |AutofillType::GetEquivalentFieldType()|, but also returns |NAME_FULL| 27// for first, middle, and last name field types. 28AutofillFieldType GetEquivalentFieldTypeCollapsingNames( 29 AutofillFieldType field_type) { 30 if (field_type == NAME_FIRST || field_type == NAME_MIDDLE || 31 field_type == NAME_LAST) 32 return NAME_FULL; 33 34 return AutofillType::GetEquivalentFieldType(field_type); 35} 36 37// Fills |distinguishing_fields| with a list of fields to use when creating 38// labels that can help to distinguish between two profiles. Draws fields from 39// |suggested_fields| if it is non-NULL; otherwise returns a default list. 40// If |suggested_fields| is non-NULL, does not include |excluded_field| in the 41// list. Otherwise, |excluded_field| is ignored, and should be set to 42// |UNKNOWN_TYPE| by convention. The resulting list of fields is sorted in 43// decreasing order of importance. 44void GetFieldsForDistinguishingProfiles( 45 const std::vector<AutofillFieldType>* suggested_fields, 46 AutofillFieldType excluded_field, 47 std::vector<AutofillFieldType>* distinguishing_fields) { 48 static const AutofillFieldType kDefaultDistinguishingFields[] = { 49 NAME_FULL, 50 ADDRESS_HOME_LINE1, 51 ADDRESS_HOME_CITY, 52 ADDRESS_HOME_STATE, 53 ADDRESS_HOME_ZIP, 54 ADDRESS_HOME_COUNTRY, 55 EMAIL_ADDRESS, 56 PHONE_HOME_WHOLE_NUMBER, 57 PHONE_FAX_WHOLE_NUMBER, 58 COMPANY_NAME, 59 }; 60 61 if (!suggested_fields) { 62 DCHECK_EQ(excluded_field, UNKNOWN_TYPE); 63 distinguishing_fields->assign( 64 kDefaultDistinguishingFields, 65 kDefaultDistinguishingFields + arraysize(kDefaultDistinguishingFields)); 66 return; 67 } 68 69 // Keep track of which fields we've seen so that we avoid duplicate entries. 70 // Always ignore fields of unknown type and the excluded field. 71 std::set<AutofillFieldType> seen_fields; 72 seen_fields.insert(UNKNOWN_TYPE); 73 seen_fields.insert(GetEquivalentFieldTypeCollapsingNames(excluded_field)); 74 75 distinguishing_fields->clear(); 76 for (std::vector<AutofillFieldType>::const_iterator it = 77 suggested_fields->begin(); 78 it != suggested_fields->end(); ++it) { 79 AutofillFieldType suggested_type = 80 GetEquivalentFieldTypeCollapsingNames(*it); 81 if (seen_fields.insert(suggested_type).second) 82 distinguishing_fields->push_back(suggested_type); 83 } 84 85 // Special case: If the excluded field is a partial name (e.g. first name) and 86 // the suggested fields include other name fields, include |NAME_FULL| in the 87 // list of distinguishing fields as a last-ditch fallback. This allows us to 88 // distinguish between profiles that are identical except for the name. 89 if (excluded_field != NAME_FULL && 90 GetEquivalentFieldTypeCollapsingNames(excluded_field) == NAME_FULL) { 91 for (std::vector<AutofillFieldType>::const_iterator it = 92 suggested_fields->begin(); 93 it != suggested_fields->end(); ++it) { 94 if (*it != excluded_field && 95 GetEquivalentFieldTypeCollapsingNames(*it) == NAME_FULL) { 96 distinguishing_fields->push_back(NAME_FULL); 97 break; 98 } 99 } 100 } 101} 102 103// A helper function for string streaming. Concatenates multi-valued entries 104// stored for a given |type| into a single string. This string is returned. 105const string16 MultiString(const AutofillProfile& p, AutofillFieldType type) { 106 std::vector<string16> values; 107 p.GetMultiInfo(type, &values); 108 string16 accumulate; 109 for (size_t i = 0; i < values.size(); ++i) { 110 if (i > 0) 111 accumulate += ASCIIToUTF16(" "); 112 accumulate += values[i]; 113 } 114 return accumulate; 115} 116 117template <class T> 118void CopyValuesToItems(AutofillFieldType type, 119 const std::vector<string16>& values, 120 std::vector<T>* form_group_items) { 121 form_group_items->resize(values.size()); 122 for (size_t i = 0; i < form_group_items->size(); ++i) 123 (*form_group_items)[i].SetInfo(type, CollapseWhitespace(values[i], false)); 124 // Must have at least one (possibly empty) element. 125 if (form_group_items->empty()) 126 form_group_items->resize(1); 127} 128 129template <class T> 130void CopyItemsToValues(AutofillFieldType type, 131 const std::vector<T>& form_group_items, 132 std::vector<string16>* values) { 133 values->resize(form_group_items.size()); 134 for (size_t i = 0; i < values->size(); ++i) 135 (*values)[i] = form_group_items[i].GetInfo(type); 136} 137 138// Collapse compound field types to their "full" type. I.e. First name 139// collapses to full name, area code collapses to full phone, etc. 140void CollapseCompoundFieldTypes(FieldTypeSet* type_set) { 141 FieldTypeSet collapsed_set; 142 for (FieldTypeSet::iterator iter = type_set->begin(); iter != type_set->end(); 143 ++iter) { 144 switch (*iter) { 145 case NAME_FIRST: 146 case NAME_MIDDLE: 147 case NAME_LAST: 148 case NAME_MIDDLE_INITIAL: 149 case NAME_FULL: 150 case NAME_SUFFIX: 151 collapsed_set.insert(NAME_FULL); 152 break; 153 154 case PHONE_HOME_NUMBER: 155 case PHONE_HOME_CITY_CODE: 156 case PHONE_HOME_COUNTRY_CODE: 157 case PHONE_HOME_CITY_AND_NUMBER: 158 case PHONE_HOME_WHOLE_NUMBER: 159 collapsed_set.insert(PHONE_HOME_WHOLE_NUMBER); 160 break; 161 162 case PHONE_FAX_NUMBER: 163 case PHONE_FAX_CITY_CODE: 164 case PHONE_FAX_COUNTRY_CODE: 165 case PHONE_FAX_CITY_AND_NUMBER: 166 case PHONE_FAX_WHOLE_NUMBER: 167 collapsed_set.insert(PHONE_FAX_WHOLE_NUMBER); 168 break; 169 170 default: 171 collapsed_set.insert(*iter); 172 } 173 } 174 std::swap(*type_set, collapsed_set); 175} 176 177} // namespace 178 179AutofillProfile::AutofillProfile(const std::string& guid) 180 : guid_(guid), name_(1), email_(1), home_number_(1), fax_number_(1) { 181} 182 183AutofillProfile::AutofillProfile() 184 : guid_(guid::GenerateGUID()), 185 name_(1), 186 email_(1), 187 home_number_(1), 188 fax_number_(1) { 189} 190 191AutofillProfile::AutofillProfile(const AutofillProfile& profile) 192 : FormGroup() { 193 operator=(profile); 194} 195 196AutofillProfile::~AutofillProfile() { 197} 198 199AutofillProfile& AutofillProfile::operator=(const AutofillProfile& profile) { 200 if (this == &profile) 201 return *this; 202 203 label_ = profile.label_; 204 guid_ = profile.guid_; 205 206 name_ = profile.name_; 207 email_ = profile.email_; 208 company_ = profile.company_; 209 home_number_ = profile.home_number_; 210 fax_number_ = profile.fax_number_; 211 address_ = profile.address_; 212 213 return *this; 214} 215 216void AutofillProfile::GetPossibleFieldTypes( 217 const string16& text, 218 FieldTypeSet* possible_types) const { 219 FormGroupList info = FormGroups(); 220 for (FormGroupList::const_iterator it = info.begin(); it != info.end(); ++it) 221 (*it)->GetPossibleFieldTypes(text, possible_types); 222} 223 224void AutofillProfile::GetAvailableFieldTypes( 225 FieldTypeSet* available_types) const { 226 FormGroupList info = FormGroups(); 227 for (FormGroupList::const_iterator it = info.begin(); it != info.end(); ++it) 228 (*it)->GetAvailableFieldTypes(available_types); 229} 230 231string16 AutofillProfile::GetInfo(AutofillFieldType type) const { 232 AutofillFieldType return_type = AutofillType::GetEquivalentFieldType(type); 233 const FormGroup* form_group = FormGroupForType(return_type); 234 if (!form_group) 235 return string16(); 236 237 return form_group->GetInfo(return_type); 238} 239 240void AutofillProfile::SetInfo(AutofillFieldType type, const string16& value) { 241 FormGroup* form_group = MutableFormGroupForType(type); 242 if (form_group) 243 form_group->SetInfo(type, CollapseWhitespace(value, false)); 244} 245 246void AutofillProfile::SetMultiInfo(AutofillFieldType type, 247 const std::vector<string16>& values) { 248 switch (AutofillType(type).group()) { 249 case AutofillType::NAME: 250 CopyValuesToItems(type, values, &name_); 251 break; 252 case AutofillType::EMAIL: 253 CopyValuesToItems(type, values, &email_); 254 break; 255 case AutofillType::PHONE_HOME: 256 CopyValuesToItems(type, values, &home_number_); 257 break; 258 case AutofillType::PHONE_FAX: 259 CopyValuesToItems(type, values, &fax_number_); 260 break; 261 default: 262 if (values.size() == 1) { 263 SetInfo(type, values[0]); 264 } else if (values.size() == 0) { 265 SetInfo(type, string16()); 266 } else { 267 NOTREACHED() 268 << "Attempt to set multiple values on single-valued field."; 269 } 270 break; 271 } 272} 273 274void AutofillProfile::GetMultiInfo(AutofillFieldType type, 275 std::vector<string16>* values) const { 276 switch (AutofillType(type).group()) { 277 case AutofillType::NAME: 278 CopyItemsToValues(type, name_, values); 279 break; 280 case AutofillType::EMAIL: 281 CopyItemsToValues(type, email_, values); 282 break; 283 case AutofillType::PHONE_HOME: 284 CopyItemsToValues(type, home_number_, values); 285 break; 286 case AutofillType::PHONE_FAX: 287 CopyItemsToValues(type, fax_number_, values); 288 break; 289 default: 290 values->resize(1); 291 (*values)[0] = GetInfo(type); 292 } 293} 294 295// static 296bool AutofillProfile::SupportsMultiValue(AutofillFieldType type) { 297 AutofillType::FieldTypeGroup group = AutofillType(type).group(); 298 return group == AutofillType::NAME || 299 group == AutofillType::EMAIL || 300 group == AutofillType::PHONE_HOME || 301 group == AutofillType::PHONE_FAX; 302} 303 304const string16 AutofillProfile::Label() const { 305 return label_; 306} 307 308const std::string AutofillProfile::CountryCode() const { 309 return address_.country_code(); 310} 311 312void AutofillProfile::SetCountryCode(const std::string& country_code) { 313 address_.set_country_code(country_code); 314} 315 316// static 317bool AutofillProfile::AdjustInferredLabels( 318 std::vector<AutofillProfile*>* profiles) { 319 const size_t kMinimalFieldsShown = 2; 320 321 std::vector<string16> created_labels; 322 CreateInferredLabels(profiles, NULL, UNKNOWN_TYPE, kMinimalFieldsShown, 323 &created_labels); 324 DCHECK_EQ(profiles->size(), created_labels.size()); 325 326 bool updated_labels = false; 327 for (size_t i = 0; i < profiles->size(); ++i) { 328 if ((*profiles)[i]->Label() != created_labels[i]) { 329 updated_labels = true; 330 (*profiles)[i]->label_ = created_labels[i]; 331 } 332 } 333 return updated_labels; 334} 335 336// static 337void AutofillProfile::CreateInferredLabels( 338 const std::vector<AutofillProfile*>* profiles, 339 const std::vector<AutofillFieldType>* suggested_fields, 340 AutofillFieldType excluded_field, 341 size_t minimal_fields_shown, 342 std::vector<string16>* created_labels) { 343 DCHECK(profiles); 344 DCHECK(created_labels); 345 346 std::vector<AutofillFieldType> fields_to_use; 347 GetFieldsForDistinguishingProfiles(suggested_fields, excluded_field, 348 &fields_to_use); 349 350 // Construct the default label for each profile. Also construct a map that 351 // associates each label with the profiles that have this label. This map is 352 // then used to detect which labels need further differentiating fields. 353 std::map<string16, std::list<size_t> > labels; 354 for (size_t i = 0; i < profiles->size(); ++i) { 355 string16 label = 356 (*profiles)[i]->ConstructInferredLabel(fields_to_use, 357 minimal_fields_shown); 358 labels[label].push_back(i); 359 } 360 361 created_labels->resize(profiles->size()); 362 for (std::map<string16, std::list<size_t> >::const_iterator it = 363 labels.begin(); 364 it != labels.end(); ++it) { 365 if (it->second.size() == 1) { 366 // This label is unique, so use it without any further ado. 367 string16 label = it->first; 368 size_t profile_index = it->second.front(); 369 (*created_labels)[profile_index] = label; 370 } else { 371 // We have more than one profile with the same label, so add 372 // differentiating fields. 373 CreateDifferentiatingLabels(*profiles, it->second, fields_to_use, 374 minimal_fields_shown, created_labels); 375 } 376 } 377} 378 379bool AutofillProfile::IsEmpty() const { 380 FieldTypeSet types; 381 GetAvailableFieldTypes(&types); 382 return types.empty(); 383} 384 385int AutofillProfile::Compare(const AutofillProfile& profile) const { 386 // The following Autofill field types are the only types we store in the WebDB 387 // so far, so we're only concerned with matching these types in the profile. 388 const AutofillFieldType types[] = { NAME_FIRST, 389 NAME_MIDDLE, 390 NAME_LAST, 391 EMAIL_ADDRESS, 392 COMPANY_NAME, 393 ADDRESS_HOME_LINE1, 394 ADDRESS_HOME_LINE2, 395 ADDRESS_HOME_CITY, 396 ADDRESS_HOME_STATE, 397 ADDRESS_HOME_ZIP, 398 ADDRESS_HOME_COUNTRY, 399 PHONE_HOME_NUMBER, 400 PHONE_FAX_NUMBER }; 401 402 for (size_t index = 0; index < arraysize(types); ++index) { 403 int comparison = GetInfo(types[index]).compare( 404 profile.GetInfo(types[index])); 405 if (comparison != 0) 406 return comparison; 407 } 408 409 return 0; 410} 411 412int AutofillProfile::CompareMulti(const AutofillProfile& profile) const { 413 const AutofillFieldType single_value_types[] = { COMPANY_NAME, 414 ADDRESS_HOME_LINE1, 415 ADDRESS_HOME_LINE2, 416 ADDRESS_HOME_CITY, 417 ADDRESS_HOME_STATE, 418 ADDRESS_HOME_ZIP, 419 ADDRESS_HOME_COUNTRY }; 420 421 for (size_t i = 0; i < arraysize(single_value_types); ++i) { 422 int comparison = GetInfo(single_value_types[i]).compare( 423 profile.GetInfo(single_value_types[i])); 424 if (comparison != 0) 425 return comparison; 426 } 427 428 const AutofillFieldType multi_value_types[] = { NAME_FIRST, 429 NAME_MIDDLE, 430 NAME_LAST, 431 EMAIL_ADDRESS, 432 PHONE_HOME_NUMBER, 433 PHONE_FAX_NUMBER }; 434 435 for (size_t i = 0; i < arraysize(multi_value_types); ++i) { 436 std::vector<string16> values_a; 437 std::vector<string16> values_b; 438 GetMultiInfo(multi_value_types[i], &values_a); 439 profile.GetMultiInfo(multi_value_types[i], &values_b); 440 if (values_a.size() < values_b.size()) 441 return -1; 442 if (values_a.size() > values_b.size()) 443 return 1; 444 for (size_t j = 0; j < values_a.size(); ++j) { 445 int comparison = values_a[j].compare(values_b[j]); 446 if (comparison != 0) 447 return comparison; 448 } 449 } 450 451 return 0; 452} 453 454bool AutofillProfile::operator==(const AutofillProfile& profile) const { 455 return guid_ == profile.guid_ && Compare(profile) == 0; 456} 457 458bool AutofillProfile::operator!=(const AutofillProfile& profile) const { 459 return !operator==(profile); 460} 461 462const string16 AutofillProfile::PrimaryValue() const { 463 return GetInfo(ADDRESS_HOME_LINE1) + 464 GetInfo(ADDRESS_HOME_CITY); 465} 466 467void AutofillProfile::OverwriteWithOrAddTo(const AutofillProfile& profile) { 468 FieldTypeSet field_types; 469 profile.GetAvailableFieldTypes(&field_types); 470 471 // Only transfer "full" types (e.g. full name) and not fragments (e.g. 472 // first name, last name). 473 CollapseCompoundFieldTypes(&field_types); 474 475 for (FieldTypeSet::const_iterator iter = field_types.begin(); 476 iter != field_types.end(); ++iter) { 477 if (AutofillProfile::SupportsMultiValue(*iter)) { 478 std::vector<string16> new_values; 479 profile.GetMultiInfo(*iter, &new_values); 480 std::vector<string16> existing_values; 481 GetMultiInfo(*iter, &existing_values); 482 for (std::vector<string16>::iterator value_iter = new_values.begin(); 483 value_iter != new_values.end(); ++value_iter) { 484 // Don't add duplicates. 485 if (std::find(existing_values.begin(), existing_values.end(), 486 *value_iter) == existing_values.end()) { 487 existing_values.insert(existing_values.end(), *value_iter); 488 } 489 } 490 SetMultiInfo(*iter, existing_values); 491 } else { 492 SetInfo(*iter, profile.GetInfo(*iter)); 493 } 494 } 495} 496 497string16 AutofillProfile::ConstructInferredLabel( 498 const std::vector<AutofillFieldType>& included_fields, 499 size_t num_fields_to_use) const { 500 const string16 separator = 501 l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_ADDRESS_SUMMARY_SEPARATOR); 502 503 string16 label; 504 size_t num_fields_used = 0; 505 for (std::vector<AutofillFieldType>::const_iterator it = 506 included_fields.begin(); 507 it != included_fields.end() && num_fields_used < num_fields_to_use; 508 ++it) { 509 string16 field = GetInfo(*it); 510 if (field.empty()) 511 continue; 512 513 if (!label.empty()) 514 label.append(separator); 515 516#ifndef ANDROID 517 // Fax number has special format, to indicate that this is a fax number. 518 if (*it == PHONE_FAX_WHOLE_NUMBER) { 519 field = l10n_util::GetStringFUTF16( 520 IDS_AUTOFILL_DIALOG_ADDRESS_SUMMARY_FAX_FORMAT, field); 521 } 522#endif 523 label.append(field); 524 ++num_fields_used; 525 } 526 return label; 527} 528 529// static 530void AutofillProfile::CreateDifferentiatingLabels( 531 const std::vector<AutofillProfile*>& profiles, 532 const std::list<size_t>& indices, 533 const std::vector<AutofillFieldType>& fields, 534 size_t num_fields_to_include, 535 std::vector<string16>* created_labels) { 536 // For efficiency, we first construct a map of fields to their text values and 537 // each value's frequency. 538 std::map<AutofillFieldType, 539 std::map<string16, size_t> > field_text_frequencies_by_field; 540 for (std::vector<AutofillFieldType>::const_iterator field = fields.begin(); 541 field != fields.end(); ++field) { 542 std::map<string16, size_t>& field_text_frequencies = 543 field_text_frequencies_by_field[*field]; 544 545 for (std::list<size_t>::const_iterator it = indices.begin(); 546 it != indices.end(); ++it) { 547 const AutofillProfile* profile = profiles[*it]; 548 string16 field_text = profile->GetInfo(*field); 549 550 // If this label is not already in the map, add it with frequency 0. 551 if (!field_text_frequencies.count(field_text)) 552 field_text_frequencies[field_text] = 0; 553 554 // Now, increment the frequency for this label. 555 ++field_text_frequencies[field_text]; 556 } 557 } 558 559 // Now comes the meat of the algorithm. For each profile, we scan the list of 560 // fields to use, looking for two things: 561 // 1. A (non-empty) field that differentiates the profile from all others 562 // 2. At least |num_fields_to_include| non-empty fields 563 // Before we've satisfied condition (2), we include all fields, even ones that 564 // are identical across all the profiles. Once we've satisfied condition (2), 565 // we only include fields that that have at last two distinct values. 566 for (std::list<size_t>::const_iterator it = indices.begin(); 567 it != indices.end(); ++it) { 568 const AutofillProfile* profile = profiles[*it]; 569 570 std::vector<AutofillFieldType> label_fields; 571 bool found_differentiating_field = false; 572 for (std::vector<AutofillFieldType>::const_iterator field = fields.begin(); 573 field != fields.end(); ++field) { 574 // Skip over empty fields. 575 string16 field_text = profile->GetInfo(*field); 576 if (field_text.empty()) 577 continue; 578 579 std::map<string16, size_t>& field_text_frequencies = 580 field_text_frequencies_by_field[*field]; 581 found_differentiating_field |= 582 !field_text_frequencies.count(string16()) && 583 (field_text_frequencies[field_text] == 1); 584 585 // Once we've found enough non-empty fields, skip over any remaining 586 // fields that are identical across all the profiles. 587 if (label_fields.size() >= num_fields_to_include && 588 (field_text_frequencies.size() == 1)) 589 continue; 590 591 label_fields.push_back(*field); 592 593 // If we've (1) found a differentiating field and (2) found at least 594 // |num_fields_to_include| non-empty fields, we're done! 595 if (found_differentiating_field && 596 label_fields.size() >= num_fields_to_include) 597 break; 598 } 599 600 (*created_labels)[*it] = 601 profile->ConstructInferredLabel(label_fields, 602 label_fields.size()); 603 } 604} 605 606AutofillProfile::FormGroupList AutofillProfile::FormGroups() const { 607 FormGroupList v(6); 608 v[0] = &name_[0]; 609 v[1] = &email_[0]; 610 v[2] = &company_; 611 v[3] = &home_number_[0]; 612 v[4] = &fax_number_[0]; 613 v[5] = &address_; 614 return v; 615} 616 617const FormGroup* AutofillProfile::FormGroupForType( 618 AutofillFieldType type) const { 619 return const_cast<AutofillProfile*>(this)->MutableFormGroupForType(type); 620} 621 622FormGroup* AutofillProfile::MutableFormGroupForType(AutofillFieldType type) { 623 FormGroup* form_group = NULL; 624 switch (AutofillType(type).group()) { 625 case AutofillType::NAME: 626 form_group = &name_[0]; 627 break; 628 case AutofillType::EMAIL: 629 form_group = &email_[0]; 630 break; 631 case AutofillType::COMPANY: 632 form_group = &company_; 633 break; 634 case AutofillType::PHONE_HOME: 635 form_group = &home_number_[0]; 636 break; 637 case AutofillType::PHONE_FAX: 638 form_group = &fax_number_[0]; 639 break; 640 case AutofillType::ADDRESS_HOME: 641 form_group = &address_; 642 break; 643 default: 644 break; 645 } 646 return form_group; 647} 648 649// So we can compare AutofillProfiles with EXPECT_EQ(). 650std::ostream& operator<<(std::ostream& os, const AutofillProfile& profile) { 651 return os 652 << UTF16ToUTF8(profile.Label()) 653 << " " 654 << profile.guid() 655 << " " 656 << UTF16ToUTF8(MultiString(profile, NAME_FIRST)) 657 << " " 658 << UTF16ToUTF8(MultiString(profile, NAME_MIDDLE)) 659 << " " 660 << UTF16ToUTF8(MultiString(profile, NAME_LAST)) 661 << " " 662 << UTF16ToUTF8(MultiString(profile, EMAIL_ADDRESS)) 663 << " " 664 << UTF16ToUTF8(profile.GetInfo(COMPANY_NAME)) 665 << " " 666 << UTF16ToUTF8(profile.GetInfo(ADDRESS_HOME_LINE1)) 667 << " " 668 << UTF16ToUTF8(profile.GetInfo(ADDRESS_HOME_LINE2)) 669 << " " 670 << UTF16ToUTF8(profile.GetInfo(ADDRESS_HOME_CITY)) 671 << " " 672 << UTF16ToUTF8(profile.GetInfo(ADDRESS_HOME_STATE)) 673 << " " 674 << UTF16ToUTF8(profile.GetInfo(ADDRESS_HOME_ZIP)) 675 << " " 676 << UTF16ToUTF8(profile.GetInfo(ADDRESS_HOME_COUNTRY)) 677 << " " 678 << UTF16ToUTF8(MultiString(profile, PHONE_HOME_WHOLE_NUMBER)) 679 << " " 680 << UTF16ToUTF8(MultiString(profile, PHONE_FAX_WHOLE_NUMBER)); 681} 682