// 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 #include #include #include #include #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 "chrome/browser/guid.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), guid_(guid::GenerateGUID()) { InitPersonalInfo(&personal_info_); } AutoFillProfile::AutoFillProfile(const std::string& guid) : unique_id_(0), guid_(guid) { 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* 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(); profile->guid_ = guid(); 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; } const string16& AutoFillProfile::Label() const { return label_; } 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* profiles) { std::vector 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* profiles, std::vector* 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 > labels; for (size_t it = 0; it < profiles->size(); ++it) { labels[ profiles->at(it)->GetFieldText(AutoFillType(NAME_FULL))].push_back(it); } std::map >::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::iterator similar_profiles; std::map 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::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 fields; std::vector 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 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 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_; guid_ = source.guid_; 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(); } } 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 = GetFieldText(AutoFillType(types[index])).compare( profile.GetFieldText(AutoFillType(types[index]))); if (comparison != 0) return comparison; } return 0; } 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(personal_info_[AutoFillType::ADDRESS_HOME]); } string16 AutoFillProfile::ConstructInferredLabel( const std::vector* included_fields) const { DCHECK(included_fields); string16 label; string16 separator = l10n_util::GetStringUTF16( IDS_AUTOFILL_DIALOG_ADDRESS_SUMMARY_SEPARATOR); for (std::vector::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() << " " << profile.guid() << " " << 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))); }