diff options
author | isherman@chromium.org <isherman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-22 08:29:32 +0000 |
---|---|---|
committer | isherman@chromium.org <isherman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-22 08:29:32 +0000 |
commit | fcfece45185268c1a55260a1d5c0abfbceaffe1f (patch) | |
tree | bab09f97a07ef8931e297f3801d792f0067ae407 /chrome | |
parent | b4291aefaf817107e62694d540c427a7d115bdea (diff) | |
download | chromium_src-fcfece45185268c1a55260a1d5c0abfbceaffe1f.zip chromium_src-fcfece45185268c1a55260a1d5c0abfbceaffe1f.tar.gz chromium_src-fcfece45185268c1a55260a1d5c0abfbceaffe1f.tar.bz2 |
Consolidate Autofill possible type detection code, and enforce greater match precision.
* Only consider submitted field values to match locally stored data if the strings match exactly
+ This means we will send fewer false positives to the server, at the cost of some false negatives. Most importantly, we should stop identifying ADDRESS_LINE_2 fields as ADDRESS_LINE_1.
+ There are a few excpetions for structured fields with canonicalized values, like countries (which are canonicalized to country codes)
* Eliminate FormGroup::GetMatchingTypes() -- all of this work is now done in a consistent way within AutofillManager
* Refactor FormGroup::GetNonEmptyTypes() to be shared code.
BUG=81867, 76992
TEST=unit_tests --gtest_filter=AutofillManagerTest.DeterminePossibleFieldTypesForUpload
Review URL: http://codereview.chromium.org/7398019
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@93582 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
37 files changed, 870 insertions, 982 deletions
diff --git a/chrome/browser/autofill/address.cc b/chrome/browser/autofill/address.cc index 0a687a1..6b2d345 100644 --- a/chrome/browser/autofill/address.cc +++ b/chrome/browser/autofill/address.cc @@ -28,6 +28,13 @@ const AutofillType::FieldTypeSubGroup kAutofillAddressTypes[] = { const int kAutofillAddressLength = arraysize(kAutofillAddressTypes); +// Returns the country code corresponding to |country|, which should be a +// localized country name. +std::string ToCountryCode(const string16& country) { + std::string app_locale = AutofillCountry::ApplicationLocale(); + return AutofillCountry::GetCountryCode(country, app_locale); +} + } // namespace Address::Address() {} @@ -42,8 +49,6 @@ Address& Address::operator=(const Address& address) { if (this == &address) return *this; - line1_tokens_ = address.line1_tokens_; - line2_tokens_= address.line2_tokens_; line1_ = address.line1_; line2_ = address.line2_; city_ = address.city_; @@ -53,54 +58,13 @@ Address& Address::operator=(const Address& address) { return *this; } -void Address::GetMatchingTypes(const string16& text, - FieldTypeSet* matching_types) const { - DCHECK(matching_types); - - // If the text to match against the field types is empty, then no results will - // match. - if (text.empty()) - return; - - if (IsLine1(text)) - matching_types->insert(ADDRESS_HOME_LINE1); - - if (IsLine2(text)) - matching_types->insert(ADDRESS_HOME_LINE2); - - if (IsCity(text)) - matching_types->insert(ADDRESS_HOME_CITY); - - if (IsState(text)) - matching_types->insert(ADDRESS_HOME_STATE); - - if (IsZipCode(text)) - matching_types->insert(ADDRESS_HOME_ZIP); - - if (IsCountry(text)) - matching_types->insert(ADDRESS_HOME_COUNTRY); -} - -void Address::GetNonEmptyTypes(FieldTypeSet* non_empty_types) const { - DCHECK(non_empty_types); - - if (!line1_.empty()) - non_empty_types->insert(ADDRESS_HOME_LINE1); - - if (!line2_.empty()) - non_empty_types->insert(ADDRESS_HOME_LINE2); - - if (!city_.empty()) - non_empty_types->insert(ADDRESS_HOME_CITY); - - if (!state_.empty()) - non_empty_types->insert(ADDRESS_HOME_STATE); - - if (!zip_code_.empty()) - non_empty_types->insert(ADDRESS_HOME_ZIP); - - if (!country_code_.empty()) - non_empty_types->insert(ADDRESS_HOME_COUNTRY); +void Address::GetSupportedTypes(FieldTypeSet* supported_types) const { + supported_types->insert(ADDRESS_HOME_LINE1); + supported_types->insert(ADDRESS_HOME_LINE2); + supported_types->insert(ADDRESS_HOME_CITY); + supported_types->insert(ADDRESS_HOME_STATE); + supported_types->insert(ADDRESS_HOME_ZIP); + supported_types->insert(ADDRESS_HOME_COUNTRY); } string16 Address::GetInfo(AutofillFieldType type) const { @@ -128,30 +92,29 @@ string16 Address::GetInfo(AutofillFieldType type) const { void Address::SetInfo(AutofillFieldType type, const string16& value) { FieldTypeSubGroup subgroup = AutofillType(type).subgroup(); if (subgroup == AutofillType::ADDRESS_LINE1) - set_line1(value); + line1_ = value; else if (subgroup == AutofillType::ADDRESS_LINE2) - set_line2(value); + line2_ = value; else if (subgroup == AutofillType::ADDRESS_CITY) city_ = value; else if (subgroup == AutofillType::ADDRESS_STATE) state_ = value; else if (subgroup == AutofillType::ADDRESS_COUNTRY) - SetCountry(value); + country_code_ = ToCountryCode(value); else if (subgroup == AutofillType::ADDRESS_ZIP) zip_code_ = value; else NOTREACHED(); } -void Address::Clear() { - line1_tokens_.clear(); - line1_.clear(); - line2_tokens_.clear(); - line2_.clear(); - city_.clear(); - state_.clear(); - country_code_.clear(); - zip_code_.clear(); +void Address::GetMatchingTypes(const string16& text, + FieldTypeSet* matching_types) const { + FormGroup::GetMatchingTypes(text, matching_types); + + // Check to see if the |text| canonicalized as a country name is a match. + std::string country_code = ToCountryCode(text); + if (!country_code.empty() && country_code_ == country_code) + matching_types->insert(ADDRESS_HOME_COUNTRY); } string16 Address::Country() const { @@ -161,89 +124,3 @@ string16 Address::Country() const { std::string app_locale = AutofillCountry::ApplicationLocale(); return AutofillCountry(country_code(), app_locale).name(); } - -void Address::set_line1(const string16& line1) { - line1_ = line1; - line1_tokens_.clear(); - Tokenize(line1, kAddressSplitChars, &line1_tokens_); - LineTokens::iterator iter; - for (iter = line1_tokens_.begin(); iter != line1_tokens_.end(); ++iter) - *iter = StringToLowerASCII(*iter); -} - -void Address::set_line2(const string16& line2) { - line2_ = line2; - line2_tokens_.clear(); - Tokenize(line2, kAddressSplitChars, &line2_tokens_); - LineTokens::iterator iter; - for (iter = line2_tokens_.begin(); iter != line2_tokens_.end(); ++iter) - *iter = StringToLowerASCII(*iter); -} - -void Address::SetCountry(const string16& country) { - std::string app_locale = AutofillCountry::ApplicationLocale(); - country_code_ = AutofillCountry::GetCountryCode(country, app_locale); -} - -bool Address::IsLine1(const string16& text) const { - return IsLineMatch(text, line1_tokens_); -} - -bool Address::IsLine2(const string16& text) const { - return IsLineMatch(text, line2_tokens_); -} - -bool Address::IsCity(const string16& text) const { - return (StringToLowerASCII(city_) == StringToLowerASCII(text)); -} - -bool Address::IsState(const string16& text) const { - return (StringToLowerASCII(state_) == StringToLowerASCII(text)); -} - -bool Address::IsCountry(const string16& text) const { - std::string app_locale = AutofillCountry::ApplicationLocale(); - std::string country_code = AutofillCountry::GetCountryCode(text, app_locale); - return (!country_code.empty() && country_code_ == country_code); -} - -bool Address::IsZipCode(const string16& text) const { - return zip_code_ == text; -} - -bool Address::IsLineMatch(const string16& text, - const LineTokens& line_tokens) const { - size_t line_tokens_size = line_tokens.size(); - if (line_tokens_size == 0) - return false; - - LineTokens text_tokens; - Tokenize(text, kAddressSplitChars, &text_tokens); - size_t text_tokens_size = text_tokens.size(); - if (text_tokens_size == 0) - return false; - - if (text_tokens_size > line_tokens_size) - return false; - - // If each of the 'words' contained in the text are also present in the line, - // then we will consider the text to match the line. - LineTokens::iterator iter; - for (iter = text_tokens.begin(); iter != text_tokens.end(); ++iter) { - if (!IsWordInLine(*iter, line_tokens)) - return false; - } - - return true; -} - -bool Address::IsWordInLine(const string16& word, - const LineTokens& line_tokens) const { - LineTokens::const_iterator iter; - for (iter = line_tokens.begin(); iter != line_tokens.end(); ++iter) { - if (StringToLowerASCII(word) == *iter) - return true; - } - - return false; -} diff --git a/chrome/browser/autofill/address.h b/chrome/browser/autofill/address.h index 1a4cf04..f03bbf1 100644 --- a/chrome/browser/autofill/address.h +++ b/chrome/browser/autofill/address.h @@ -24,54 +24,23 @@ class Address : public FormGroup { Address& operator=(const Address& address); // FormGroup: + virtual string16 GetInfo(AutofillFieldType type) const OVERRIDE; + virtual void SetInfo(AutofillFieldType type, const string16& value) OVERRIDE; virtual void GetMatchingTypes(const string16& text, - FieldTypeSet* matching_types) const; - virtual void GetNonEmptyTypes(FieldTypeSet* non_empty_types) const; - virtual string16 GetInfo(AutofillFieldType type) const; - virtual void SetInfo(AutofillFieldType type, const string16& value); + FieldTypeSet* matching_types) const OVERRIDE; const std::string& country_code() const { return country_code_; } void set_country_code(const std::string& country_code) { country_code_ = country_code; } - // Sets all of the fields to the empty string. - void Clear(); - private: - // Vector of tokens in an address line. - typedef std::vector<string16> LineTokens; + // FormGroup: + virtual void GetSupportedTypes(FieldTypeSet* supported_types) const OVERRIDE; // Returns the localized country name corresponding to |country_code_|. string16 Country() const; - void set_line1(const string16& line1); - void set_line2(const string16& line2); - - // Sets the |country_code_| based on |country|, which should be a localized - // country name. - void SetCountry(const string16& country); - - // The following functions match |text| against the various values of the - // address, returning true on match. - virtual bool IsLine1(const string16& text) const; - virtual bool IsLine2(const string16& text) const; - virtual bool IsCity(const string16& text) const; - virtual bool IsState(const string16& text) const; - virtual bool IsCountry(const string16& text) const; - virtual bool IsZipCode(const string16& text) const; - - // Returns true if all of the tokens in |text| match the tokens in - // |line_tokens|. - bool IsLineMatch(const string16& text, const LineTokens& line_tokens) const; - - // Returns true if |word| is one of the tokens in |line_tokens|. - bool IsWordInLine(const string16& word, const LineTokens& line_tokens) const; - - // List of tokens in each part of |line1_| and |line2_|. - LineTokens line1_tokens_; - LineTokens line2_tokens_; - // The address. string16 line1_; string16 line2_; diff --git a/chrome/browser/autofill/autofill_ie_toolbar_import_win.cc b/chrome/browser/autofill/autofill_ie_toolbar_import_win.cc index 96a4ba1..eacbacb 100644 --- a/chrome/browser/autofill/autofill_ie_toolbar_import_win.cc +++ b/chrome/browser/autofill/autofill_ie_toolbar_import_win.cc @@ -160,10 +160,14 @@ bool ImportSingleProfile(FormGroup* profile, } // Now re-construct the phones if needed. string16 constructed_number; - if (!home.empty() && home.ParseNumber(std::string("US"), &constructed_number)) - profile->SetInfo(PHONE_HOME_WHOLE_NUMBER, constructed_number); - if (!fax.empty() && fax.ParseNumber(std::string("US"), &constructed_number)) - profile->SetInfo(PHONE_FAX_WHOLE_NUMBER, constructed_number); + if (!home.IsEmpty() && + home.ParseNumber(std::string("US"), &constructed_number)) { + profile->SetCanonicalizedInfo(PHONE_HOME_WHOLE_NUMBER, constructed_number); + } + if (!fax.IsEmpty() && + fax.ParseNumber(std::string("US"), &constructed_number)) { + profile->SetCanonicalizedInfo(PHONE_FAX_WHOLE_NUMBER, constructed_number); + } return has_non_empty_fields; } diff --git a/chrome/browser/autofill/autofill_ie_toolbar_import_win_unittest.cc b/chrome/browser/autofill/autofill_ie_toolbar_import_win_unittest.cc index 240055f..f98e52a 100644 --- a/chrome/browser/autofill/autofill_ie_toolbar_import_win_unittest.cc +++ b/chrome/browser/autofill/autofill_ie_toolbar_import_win_unittest.cc @@ -161,34 +161,34 @@ TEST_F(AutofillIeToolbarImportTest, TestAutofillImport) { std::vector<AutofillProfile> profiles; std::vector<CreditCard> credit_cards; EXPECT_TRUE(ImportCurrentUserProfiles(&profiles, &credit_cards)); - ASSERT_EQ(profiles.size(), 2); + ASSERT_EQ(2U, profiles.size()); // The profiles are read in reverse order. - EXPECT_EQ(profiles[1].GetInfo(NAME_FIRST), profile1[0].value); - EXPECT_EQ(profiles[1].GetInfo(NAME_MIDDLE), profile1[1].value); - EXPECT_EQ(profiles[1].GetInfo(NAME_LAST), profile1[2].value); - EXPECT_EQ(profiles[1].GetInfo(EMAIL_ADDRESS), profile1[3].value); - EXPECT_EQ(profiles[1].GetInfo(COMPANY_NAME), profile1[4].value); - EXPECT_EQ(profiles[1].GetInfo(PHONE_HOME_COUNTRY_CODE), profile1[7].value); - EXPECT_EQ(profiles[1].GetInfo(PHONE_HOME_CITY_CODE), profile1[6].value); - EXPECT_EQ(profiles[1].GetInfo(PHONE_HOME_NUMBER), L"5555555"); - EXPECT_EQ(profiles[1].GetInfo(PHONE_HOME_WHOLE_NUMBER), L"+16505555555"); - - EXPECT_EQ(profiles[0].GetInfo(NAME_FIRST), profile2[0].value); - EXPECT_EQ(profiles[0].GetInfo(NAME_LAST), profile2[1].value); - EXPECT_EQ(profiles[0].GetInfo(EMAIL_ADDRESS), profile2[2].value); - EXPECT_EQ(profiles[0].GetInfo(COMPANY_NAME), profile2[3].value); - EXPECT_EQ(profiles[0].GetInfo(PHONE_FAX_COUNTRY_CODE), profile2[6].value); - EXPECT_EQ(profiles[0].GetInfo(PHONE_FAX_CITY_CODE), profile2[5].value); - EXPECT_EQ(profiles[0].GetInfo(PHONE_FAX_NUMBER), L"5556666"); - EXPECT_EQ(profiles[0].GetInfo(PHONE_FAX_WHOLE_NUMBER), L"+78125556666"); - - ASSERT_EQ(credit_cards.size(), 1); - EXPECT_EQ(credit_cards[0].GetInfo(CREDIT_CARD_NAME), credit_card[0].value); - EXPECT_EQ(credit_cards[0].GetInfo(CREDIT_CARD_NUMBER), L"4111111111111111"); - EXPECT_EQ(credit_cards[0].GetInfo(CREDIT_CARD_EXP_MONTH), - credit_card[2].value); - EXPECT_EQ(credit_cards[0].GetInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR), - credit_card[3].value); + EXPECT_EQ(profile1[0].value, profiles[1].GetInfo(NAME_FIRST)); + EXPECT_EQ(profile1[1].value, profiles[1].GetInfo(NAME_MIDDLE)); + EXPECT_EQ(profile1[2].value, profiles[1].GetInfo(NAME_LAST)); + EXPECT_EQ(profile1[3].value, profiles[1].GetInfo(EMAIL_ADDRESS)); + EXPECT_EQ(profile1[4].value, profiles[1].GetInfo(COMPANY_NAME)); + EXPECT_EQ(profile1[7].value, profiles[1].GetInfo(PHONE_HOME_COUNTRY_CODE)); + EXPECT_EQ(profile1[6].value, profiles[1].GetInfo(PHONE_HOME_CITY_CODE)); + EXPECT_EQ(L"5555555", profiles[1].GetInfo(PHONE_HOME_NUMBER)); + EXPECT_EQ(L"+1 650-555-5555", profiles[1].GetInfo(PHONE_HOME_WHOLE_NUMBER)); + + EXPECT_EQ(profile2[0].value, profiles[0].GetInfo(NAME_FIRST)); + EXPECT_EQ(profile2[1].value, profiles[0].GetInfo(NAME_LAST)); + EXPECT_EQ(profile2[2].value, profiles[0].GetInfo(EMAIL_ADDRESS)); + EXPECT_EQ(profile2[3].value, profiles[0].GetInfo(COMPANY_NAME)); + EXPECT_EQ(profile2[6].value, profiles[0].GetInfo(PHONE_FAX_COUNTRY_CODE)); + EXPECT_EQ(profile2[5].value, profiles[0].GetInfo(PHONE_FAX_CITY_CODE)); + EXPECT_EQ(L"5556666", profiles[0].GetInfo(PHONE_FAX_NUMBER)); + EXPECT_EQ(L"+7 812 555-66-66", profiles[0].GetInfo(PHONE_FAX_WHOLE_NUMBER)); + + ASSERT_EQ(1U, credit_cards.size()); + EXPECT_EQ(credit_card[0].value, credit_cards[0].GetInfo(CREDIT_CARD_NAME)); + EXPECT_EQ(L"4111111111111111", credit_cards[0].GetInfo(CREDIT_CARD_NUMBER)); + EXPECT_EQ(credit_card[2].value, + credit_cards[0].GetInfo(CREDIT_CARD_EXP_MONTH)); + EXPECT_EQ(credit_card[3].value, + credit_cards[0].GetInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR)); // Mock password encrypted cc. cc_key.Open(HKEY_CURRENT_USER, kCreditCardKey, KEY_ALL_ACCESS); @@ -201,8 +201,8 @@ TEST_F(AutofillIeToolbarImportTest, TestAutofillImport) { credit_cards.clear(); EXPECT_TRUE(ImportCurrentUserProfiles(&profiles, &credit_cards)); // Profiles are not protected. - EXPECT_EQ(profiles.size(), 2); + EXPECT_EQ(2U, profiles.size()); // Credit cards are. - EXPECT_EQ(credit_cards.size(), 0); + EXPECT_EQ(0U, credit_cards.size()); } diff --git a/chrome/browser/autofill/autofill_manager.cc b/chrome/browser/autofill/autofill_manager.cc index b9d133e..7e339e9 100644 --- a/chrome/browser/autofill/autofill_manager.cc +++ b/chrome/browser/autofill/autofill_manager.cc @@ -227,30 +227,6 @@ bool FormIsHTTPS(FormStructure* form) { return form->source_url().SchemeIs(chrome::kHttpsScheme); } -// Normalizes phones in multi-info. If |type| is anything but -// PHONE_HOME_WHOLE_NUMBER or PHONE_FAX_WHOLE_NUMBER does nothing, as it is -// either not a phone, or already normalized (parts of the phone parsed and -// normalized from the PHONE_*_WHOLE_NUMBER). |locale| is a profile locale. -// For whole number does normalization: -// (650)2345678 -> 6502345678 -// 1-800-FLOWERS -> 18003569377 -// If phone cannot be normalized, leaves it as it is. -void NormalizePhoneMultiInfo(AutofillFieldType type, - std::string const& locale, - std::vector<string16>* values) { - DCHECK(values); - if (type != PHONE_HOME_WHOLE_NUMBER && type != PHONE_FAX_WHOLE_NUMBER) - return; - for (std::vector<string16>::iterator it = values->begin(); - it != values->end(); - ++it) { - string16 normalized_phone = autofill_i18n::NormalizePhoneNumber(*it, - locale); - if (!normalized_phone.empty()) - *it = normalized_phone; - } -} - // Check for unidentified forms among those with the most query or upload // requests. If found, present an infobar prompting the user to send Google // Feedback identifying these forms. Only executes if the appropriate flag is @@ -751,13 +727,30 @@ bool AutofillManager::IsAutofillEnabled() const { void AutofillManager::DeterminePossibleFieldTypesForUpload( FormStructure* submitted_form) { + // Combine all the profiles and credit cards stored in |personal_data_| into + // one vector for ease of iteration. + const std::vector<AutofillProfile*>& profiles = personal_data_->profiles(); + const std::vector<CreditCard*>& credit_cards = personal_data_->credit_cards(); + std::vector<FormGroup*> stored_data; + stored_data.insert(stored_data.end(), profiles.begin(), profiles.end()); + stored_data.insert(stored_data.end(), credit_cards.begin(), + credit_cards.end()); + + // For each field in the |submitted_form|, extract the value. Then for each + // profile or credit card, identify any stored types that match the value. for (size_t i = 0; i < submitted_form->field_count(); i++) { const AutofillField* field = submitted_form->field(i); - FieldTypeSet field_types; - personal_data_->GetMatchingTypes(field->value, &field_types); + string16 value = CollapseWhitespace(field->value, false); + FieldTypeSet matching_types; + for (std::vector<FormGroup*>::const_iterator it = stored_data.begin(); + it != stored_data.end(); ++it) { + (*it)->GetMatchingTypes(value, &matching_types); + } + + if (matching_types.empty()) + matching_types.insert(UNKNOWN_TYPE); - DCHECK(!field_types.empty()); - submitted_form->set_possible_types(i, field_types); + submitted_form->set_possible_types(i, matching_types); } } @@ -904,8 +897,7 @@ void AutofillManager::GetProfileSuggestions(FormStructure* form, // The value of the stored data for this field type in the |profile|. std::vector<string16> multi_values; - profile->GetMultiInfo(type, &multi_values); - NormalizePhoneMultiInfo(type, profile->CountryCode(), &multi_values); + profile->GetCanonicalizedMultiInfo(type, &multi_values); for (size_t i = 0; i < multi_values.size(); ++i) { if (!multi_values[i].empty() && @@ -938,8 +930,7 @@ void AutofillManager::GetProfileSuggestions(FormStructure* form, // The value of the stored data for this field type in the |profile|. std::vector<string16> multi_values; - profile->GetMultiInfo(type, &multi_values); - NormalizePhoneMultiInfo(type, profile->CountryCode(), &multi_values); + profile->GetCanonicalizedMultiInfo(type, &multi_values); for (size_t i = 0; i < multi_values.size(); ++i) { if (multi_values[i].empty()) @@ -991,7 +982,8 @@ void AutofillManager::GetCreditCardSuggestions(FormStructure* form, CreditCard* credit_card = *iter; // The value of the stored data for this field type in the |credit_card|. - string16 creditcard_field_value = credit_card->GetInfo(type); + string16 creditcard_field_value = + credit_card->GetCanonicalizedInfo(type); if (!creditcard_field_value.empty() && StartsWith(creditcard_field_value, field.value, false)) { if (type == CREDIT_CARD_NUMBER) @@ -1000,7 +992,7 @@ void AutofillManager::GetCreditCardSuggestions(FormStructure* form, string16 label; if (credit_card->number().empty()) { // If there is no CC number, return name to show something. - label = credit_card->GetInfo(CREDIT_CARD_NAME); + label = credit_card->GetCanonicalizedInfo(CREDIT_CARD_NAME); } else { label = kCreditCardPrefix; label.append(credit_card->LastFourDigits()); @@ -1026,18 +1018,16 @@ void AutofillManager::FillCreditCardFormField(const CreditCard* credit_card, autofill::FillSelectControl(*credit_card, type, field); } else if (field->form_control_type == ASCIIToUTF16("month")) { // HTML5 input="month" consists of year-month. - string16 year = credit_card->GetInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR); - string16 month = credit_card->GetInfo(CREDIT_CARD_EXP_MONTH); + string16 year = + credit_card->GetCanonicalizedInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR); + string16 month = credit_card->GetCanonicalizedInfo(CREDIT_CARD_EXP_MONTH); if (!year.empty() && !month.empty()) { // Fill the value only if |credit_card| includes both year and month // information. field->value = year + ASCIIToUTF16("-") + month; } } else { - string16 value = credit_card->GetInfo(type); - if (type == CREDIT_CARD_NUMBER) - value = CreditCard::StripSeparators(value); - field->value = value; + field->value = credit_card->GetCanonicalizedInfo(type); } } @@ -1056,8 +1046,7 @@ void AutofillManager::FillFormField(const AutofillProfile* profile, autofill::FillSelectControl(*profile, type, field); } else { std::vector<string16> values; - profile->GetMultiInfo(type, &values); - NormalizePhoneMultiInfo(type, profile->CountryCode(), &values); + profile->GetCanonicalizedMultiInfo(type, &values); DCHECK(variant < values.size()); field->value = values[variant]; } @@ -1071,8 +1060,7 @@ void AutofillManager::FillPhoneNumberField(const AutofillProfile* profile, // If we are filling a phone number, check to see if the size field // matches the "prefix" or "suffix" sizes and fill accordingly. std::vector<string16> values; - profile->GetMultiInfo(type, &values); - NormalizePhoneMultiInfo(type, profile->CountryCode(), &values); + profile->GetCanonicalizedMultiInfo(type, &values); DCHECK(variant < values.size()); string16 number = values[variant]; bool has_valid_suffix_and_prefix = (number.length() == diff --git a/chrome/browser/autofill/autofill_manager.h b/chrome/browser/autofill/autofill_manager.h index 91d819c..ce2d133 100644 --- a/chrome/browser/autofill/autofill_manager.h +++ b/chrome/browser/autofill/autofill_manager.h @@ -251,6 +251,8 @@ class AutofillManager : public TabContentsObserver, FRIEND_TEST_ALL_PREFIXES(AutofillManagerTest, FormSubmittedWithDefaultValues); FRIEND_TEST_ALL_PREFIXES(AutofillManagerTest, DeterminePossibleFieldTypesForUpload); + FRIEND_TEST_ALL_PREFIXES(AutofillManagerTest, + DeterminePossibleFieldTypesForUploadStressTest); FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, AddressSuggestionsCount); FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, AutofillIsEnabledAtPageLoad); FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, diff --git a/chrome/browser/autofill/autofill_manager_unittest.cc b/chrome/browser/autofill/autofill_manager_unittest.cc index 21d2281..74df40c 100644 --- a/chrome/browser/autofill/autofill_manager_unittest.cc +++ b/chrome/browser/autofill/autofill_manager_unittest.cc @@ -2190,6 +2190,300 @@ TEST_F(AutofillManagerTest, AuxiliaryProfilesReset) { } TEST_F(AutofillManagerTest, DeterminePossibleFieldTypesForUpload) { + FormData form; + form.name = ASCIIToUTF16("MyForm"); + form.method = ASCIIToUTF16("POST"); + form.origin = GURL("http://myform.com/form.html"); + form.action = GURL("http://myform.com/submit.html"); + form.user_submitted = true; + + std::vector<FieldTypeSet> expected_types; + + // These fields should all match. + FormField field; + FieldTypeSet types; + autofill_test::CreateTestFormField("", "1", "Elvis", "text", &field); + types.clear(); + types.insert(NAME_FIRST); + form.fields.push_back(field); + expected_types.push_back(types); + + autofill_test::CreateTestFormField("", "2", "Aaron", "text", &field); + types.clear(); + types.insert(NAME_MIDDLE); + form.fields.push_back(field); + expected_types.push_back(types); + + autofill_test::CreateTestFormField("", "3", "A", "text", &field); + types.clear(); + types.insert(NAME_MIDDLE_INITIAL); + form.fields.push_back(field); + expected_types.push_back(types); + + autofill_test::CreateTestFormField("", "4", "Presley", "text", &field); + types.clear(); + types.insert(NAME_LAST); + form.fields.push_back(field); + expected_types.push_back(types); + + autofill_test::CreateTestFormField("", "5", "Elvis Presley", "text", &field); + types.clear(); + types.insert(CREDIT_CARD_NAME); + form.fields.push_back(field); + expected_types.push_back(types); + + autofill_test::CreateTestFormField("", "6", "Elvis Aaron Presley", "text", + &field); + types.clear(); + types.insert(NAME_FULL); + form.fields.push_back(field); + expected_types.push_back(types); + + autofill_test::CreateTestFormField("", "7", "theking@gmail.com", "email", + &field); + types.clear(); + types.insert(EMAIL_ADDRESS); + form.fields.push_back(field); + expected_types.push_back(types); + + autofill_test::CreateTestFormField("", "8", "RCA", "text", &field); + types.clear(); + types.insert(COMPANY_NAME); + form.fields.push_back(field); + expected_types.push_back(types); + + autofill_test::CreateTestFormField("", "9", "3734 Elvis Presley Blvd.", + "text", &field); + types.clear(); + types.insert(ADDRESS_HOME_LINE1); + form.fields.push_back(field); + expected_types.push_back(types); + + autofill_test::CreateTestFormField("", "10", "Apt. 10", "text", &field); + types.clear(); + types.insert(ADDRESS_HOME_LINE2); + form.fields.push_back(field); + expected_types.push_back(types); + + autofill_test::CreateTestFormField("", "11", "Memphis", "text", &field); + types.clear(); + types.insert(ADDRESS_HOME_CITY); + form.fields.push_back(field); + expected_types.push_back(types); + + autofill_test::CreateTestFormField("", "12", "Tennessee", "text", &field); + types.clear(); + types.insert(ADDRESS_HOME_STATE); + form.fields.push_back(field); + expected_types.push_back(types); + + autofill_test::CreateTestFormField("", "13", "38116", "text", &field); + types.clear(); + types.insert(ADDRESS_HOME_ZIP); + form.fields.push_back(field); + expected_types.push_back(types); + + autofill_test::CreateTestFormField("", "14", "USA", "text", &field); + types.clear(); + types.insert(ADDRESS_HOME_COUNTRY); + form.fields.push_back(field); + expected_types.push_back(types); + + autofill_test::CreateTestFormField("", "15", "United States", "text", &field); + types.clear(); + types.insert(ADDRESS_HOME_COUNTRY); + form.fields.push_back(field); + expected_types.push_back(types); + + autofill_test::CreateTestFormField("", "16", "+1 (234) 567-8901", "text", + &field); + types.clear(); + types.insert(PHONE_HOME_WHOLE_NUMBER); + form.fields.push_back(field); + expected_types.push_back(types); + + autofill_test::CreateTestFormField("", "17", "2345678901", "text", &field); + types.clear(); + types.insert(PHONE_HOME_CITY_AND_NUMBER); + form.fields.push_back(field); + expected_types.push_back(types); + + autofill_test::CreateTestFormField("", "18", "1", "text", &field); + types.clear(); + types.insert(PHONE_HOME_COUNTRY_CODE); + form.fields.push_back(field); + expected_types.push_back(types); + + autofill_test::CreateTestFormField("", "19", "234", "text", &field); + types.clear(); + types.insert(PHONE_HOME_CITY_CODE); + form.fields.push_back(field); + expected_types.push_back(types); + + autofill_test::CreateTestFormField("", "20", "5678901", "text", &field); + types.clear(); + types.insert(PHONE_HOME_NUMBER); + form.fields.push_back(field); + expected_types.push_back(types); + + autofill_test::CreateTestFormField("", "21", "567", "text", &field); + types.clear(); + types.insert(PHONE_HOME_NUMBER); + form.fields.push_back(field); + expected_types.push_back(types); + + autofill_test::CreateTestFormField("", "22", "8901", "text", &field); + types.clear(); + types.insert(PHONE_HOME_NUMBER); + form.fields.push_back(field); + expected_types.push_back(types); + + autofill_test::CreateTestFormField("", "23", "4234-5678-9012-3456", "text", + &field); + types.clear(); + types.insert(CREDIT_CARD_NUMBER); + form.fields.push_back(field); + expected_types.push_back(types); + + autofill_test::CreateTestFormField("", "24", "04", "text", &field); + types.clear(); + types.insert(CREDIT_CARD_EXP_MONTH); + form.fields.push_back(field); + expected_types.push_back(types); + + autofill_test::CreateTestFormField("", "25", "April", "text", &field); + types.clear(); + types.insert(CREDIT_CARD_EXP_MONTH); + form.fields.push_back(field); + expected_types.push_back(types); + + autofill_test::CreateTestFormField("", "26", "2012", "text", &field); + types.clear(); + types.insert(CREDIT_CARD_EXP_4_DIGIT_YEAR); + form.fields.push_back(field); + expected_types.push_back(types); + + autofill_test::CreateTestFormField("", "27", "12", "text", &field); + types.clear(); + types.insert(CREDIT_CARD_EXP_2_DIGIT_YEAR); + form.fields.push_back(field); + expected_types.push_back(types); + + autofill_test::CreateTestFormField("", "28", "04/2012", "text", &field); + types.clear(); + types.insert(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR); + form.fields.push_back(field); + expected_types.push_back(types); + + // Make sure that we trim whitespace properly. + autofill_test::CreateTestFormField("", "29", "", "text", &field); + types.clear(); + types.insert(EMPTY_TYPE); + form.fields.push_back(field); + expected_types.push_back(types); + + autofill_test::CreateTestFormField("", "30", " ", "text", &field); + types.clear(); + types.insert(EMPTY_TYPE); + form.fields.push_back(field); + expected_types.push_back(types); + + autofill_test::CreateTestFormField("", "31", " Elvis", "text", &field); + types.clear(); + types.insert(NAME_FIRST); + form.fields.push_back(field); + expected_types.push_back(types); + + autofill_test::CreateTestFormField("", "32", "Elvis ", "text", &field); + types.clear(); + types.insert(NAME_FIRST); + form.fields.push_back(field); + expected_types.push_back(types); + + // These fields should not match, as they differ by case. + autofill_test::CreateTestFormField("", "33", "elvis", "text", &field); + types.clear(); + types.insert(UNKNOWN_TYPE); + form.fields.push_back(field); + expected_types.push_back(types); + + autofill_test::CreateTestFormField("", "34", "3734 Elvis Presley BLVD", + "text", &field); + types.clear(); + types.insert(UNKNOWN_TYPE); + form.fields.push_back(field); + expected_types.push_back(types); + + // These fields should not match, as they are unsupported variants. + autofill_test::CreateTestFormField("", "35", "Elvis Aaron", "text", &field); + types.clear(); + types.insert(UNKNOWN_TYPE); + form.fields.push_back(field); + expected_types.push_back(types); + + autofill_test::CreateTestFormField("", "36", "Mr. Presley", "text", &field); + types.clear(); + types.insert(UNKNOWN_TYPE); + form.fields.push_back(field); + expected_types.push_back(types); + + autofill_test::CreateTestFormField("", "37", "3734 Elvis Presley", "text", + &field); + types.clear(); + types.insert(UNKNOWN_TYPE); + form.fields.push_back(field); + expected_types.push_back(types); + + autofill_test::CreateTestFormField("", "38", "TN", "text", &field); + types.clear(); + types.insert(UNKNOWN_TYPE); + form.fields.push_back(field); + expected_types.push_back(types); + + autofill_test::CreateTestFormField("", "39", "38116-1023", "text", &field); + types.clear(); + types.insert(UNKNOWN_TYPE); + form.fields.push_back(field); + expected_types.push_back(types); + + autofill_test::CreateTestFormField("", "20", "5", "text", &field); + types.clear(); + types.insert(UNKNOWN_TYPE); + form.fields.push_back(field); + expected_types.push_back(types); + + autofill_test::CreateTestFormField("", "20", "56", "text", &field); + types.clear(); + types.insert(UNKNOWN_TYPE); + form.fields.push_back(field); + expected_types.push_back(types); + + autofill_test::CreateTestFormField("", "20", "901", "text", &field); + types.clear(); + types.insert(UNKNOWN_TYPE); + form.fields.push_back(field); + expected_types.push_back(types); + + FormStructure form_structure(form); + autofill_manager_->DeterminePossibleFieldTypesForUpload(&form_structure); + + ASSERT_EQ(expected_types.size(), form_structure.field_count()); + for (size_t i = 0; i < expected_types.size(); ++i) { + SCOPED_TRACE( + StringPrintf("Field %d with value %s", static_cast<int>(i), + UTF16ToUTF8(form_structure.field(i)->value).c_str())); + const FieldTypeSet& possible_types = + form_structure.field(i)->possible_types(); + EXPECT_EQ(expected_types[i].size(), possible_types.size()); + for (FieldTypeSet::const_iterator it = expected_types[i].begin(); + it != expected_types[i].end(); ++it) { + EXPECT_TRUE(possible_types.count(*it)) + << "Expected type: " << AutofillType::FieldTypeToString(*it); + } + } +} + +TEST_F(AutofillManagerTest, DeterminePossibleFieldTypesForUploadStressTest) { test_personal_data_->ClearAutofillProfiles(); const int kNumProfiles = 5; for (int i = 0; i < kNumProfiles; ++i) { diff --git a/chrome/browser/autofill/autofill_metrics_unittest.cc b/chrome/browser/autofill/autofill_metrics_unittest.cc index e5c53c8..f0c4a33 100644 --- a/chrome/browser/autofill/autofill_metrics_unittest.cc +++ b/chrome/browser/autofill/autofill_metrics_unittest.cc @@ -263,7 +263,7 @@ TEST_F(AutofillMetricsTest, QualityMetrics) { FormField field; autofill_test::CreateTestFormField( - "Autofilled", "autofilled", "Elvis Presley", "text", &field); + "Autofilled", "autofilled", "Elvis Aaron Presley", "text", &field); field.is_autofilled = true; form.fields.push_back(field); heuristic_types.push_back(NAME_FULL); @@ -314,13 +314,13 @@ TEST_F(AutofillMetricsTest, QualityMetrics) { std::string())); EXPECT_CALL(*autofill_manager_->metric_logger(), LogHeuristicTypePrediction(AutofillMetrics::TYPE_MATCH, - UNKNOWN_TYPE, std::string())); + NAME_FULL, std::string())); EXPECT_CALL(*autofill_manager_->metric_logger(), LogServerTypePrediction(AutofillMetrics::TYPE_MISMATCH, - UNKNOWN_TYPE, std::string())); + NAME_FULL, std::string())); EXPECT_CALL(*autofill_manager_->metric_logger(), LogOverallTypePrediction(AutofillMetrics::TYPE_MISMATCH, - UNKNOWN_TYPE, std::string())); + NAME_FULL, std::string())); EXPECT_CALL(*autofill_manager_->metric_logger(), LogQualityMetric(AutofillMetrics::FIELD_AUTOFILLED, std::string())); @@ -522,7 +522,7 @@ TEST_F(AutofillMetricsTest, SaneMetricsWithCacheMismatch) { FormField field; autofill_test::CreateTestFormField( - "Both match", "match", "Elvis Presley", "text", &field); + "Both match", "match", "Elvis Aaron Presley", "text", &field); field.is_autofilled = true; form.fields.push_back(field); heuristic_types.push_back(NAME_FULL); @@ -646,13 +646,13 @@ TEST_F(AutofillMetricsTest, SaneMetricsWithCacheMismatch) { std::string())); EXPECT_CALL(*autofill_manager_->metric_logger(), LogHeuristicTypePrediction(AutofillMetrics::TYPE_MATCH, - UNKNOWN_TYPE, std::string())); + NAME_FULL, std::string())); EXPECT_CALL(*autofill_manager_->metric_logger(), LogServerTypePrediction(AutofillMetrics::TYPE_MATCH, - UNKNOWN_TYPE, std::string())); + NAME_FULL, std::string())); EXPECT_CALL(*autofill_manager_->metric_logger(), LogOverallTypePrediction(AutofillMetrics::TYPE_MATCH, - UNKNOWN_TYPE, std::string())); + NAME_FULL, std::string())); EXPECT_CALL(*autofill_manager_->metric_logger(), LogQualityMetric(AutofillMetrics::FIELD_AUTOFILLED, std::string())); @@ -713,7 +713,7 @@ TEST_F(AutofillMetricsTest, QualityMetricsWithExperimentId) { FormField field; autofill_test::CreateTestFormField( - "Autofilled", "autofilled", "Elvis Presley", "text", &field); + "Autofilled", "autofilled", "Elvis Aaron Presley", "text", &field); field.is_autofilled = true; form.fields.push_back(field); heuristic_types.push_back(NAME_FULL); @@ -760,13 +760,13 @@ TEST_F(AutofillMetricsTest, QualityMetricsWithExperimentId) { experiment_id)); EXPECT_CALL(*autofill_manager_->metric_logger(), LogHeuristicTypePrediction(AutofillMetrics::TYPE_MATCH, - UNKNOWN_TYPE, experiment_id)); + NAME_FULL, experiment_id)); EXPECT_CALL(*autofill_manager_->metric_logger(), LogServerTypePrediction(AutofillMetrics::TYPE_MISMATCH, - UNKNOWN_TYPE, experiment_id)); + NAME_FULL, experiment_id)); EXPECT_CALL(*autofill_manager_->metric_logger(), LogOverallTypePrediction(AutofillMetrics::TYPE_MISMATCH, - UNKNOWN_TYPE, experiment_id)); + NAME_FULL, experiment_id)); EXPECT_CALL(*autofill_manager_->metric_logger(), LogQualityMetric(AutofillMetrics::FIELD_AUTOFILLED, experiment_id)); diff --git a/chrome/browser/autofill/autofill_profile.cc b/chrome/browser/autofill/autofill_profile.cc index 4df95bd..482cef8 100644 --- a/chrome/browser/autofill/autofill_profile.cc +++ b/chrome/browser/autofill/autofill_profile.cc @@ -119,7 +119,7 @@ void CopyValuesToItems(AutofillFieldType type, const std::vector<string16>& values, std::vector<T>* form_group_items, const T& prototype) { - form_group_items->resize(values.size()); + form_group_items->resize(values.size(), prototype); 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. @@ -130,10 +130,15 @@ void CopyValuesToItems(AutofillFieldType type, template <class T> void CopyItemsToValues(AutofillFieldType type, const std::vector<T>& form_group_items, + bool canonicalize, 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); + for (size_t i = 0; i < values->size(); ++i) { + if (canonicalize) + (*values)[i] = form_group_items[i].GetCanonicalizedInfo(type); + else + (*values)[i] = form_group_items[i].GetInfo(type); + } } // Collapse compound field types to their "full" type. I.e. First name @@ -201,16 +206,16 @@ AutofillProfile::AutofillProfile(const std::string& guid) : guid_(guid), name_(1), email_(1), - home_number_(1, PhoneNumber(AutofillType::PHONE_HOME)), - fax_number_(1, PhoneNumber(AutofillType::PHONE_FAX)) { + home_number_(1, PhoneNumber(AutofillType::PHONE_HOME, this)), + fax_number_(1, PhoneNumber(AutofillType::PHONE_FAX, this)) { } AutofillProfile::AutofillProfile() : guid_(guid::GenerateGUID()), name_(1), email_(1), - home_number_(1, PhoneNumber(AutofillType::PHONE_HOME)), - fax_number_(1, PhoneNumber(AutofillType::PHONE_FAX)) { + home_number_(1, PhoneNumber(AutofillType::PHONE_HOME, this)), + fax_number_(1, PhoneNumber(AutofillType::PHONE_FAX, this)) { } AutofillProfile::AutofillProfile(const AutofillProfile& profile) @@ -232,26 +237,32 @@ AutofillProfile& AutofillProfile::operator=(const AutofillProfile& profile) { email_ = profile.email_; company_ = profile.company_; home_number_ = profile.home_number_; + for (size_t i = 0; i < home_number_.size(); ++i) { + home_number_[i].set_profile(this); + } fax_number_ = profile.fax_number_; + for (size_t i = 0; i < fax_number_.size(); ++i) { + fax_number_[i].set_profile(this); + } address_ = profile.address_; return *this; } -void AutofillProfile::GetMatchingTypes(const string16& text, - FieldTypeSet* matching_types) const { +void AutofillProfile::GetSupportedTypes(FieldTypeSet* supported_types) const { FormGroupList info = FormGroups(); for (FormGroupList::const_iterator it = info.begin(); it != info.end(); ++it) - (*it)->GetMatchingTypes(text, matching_types); + (*it)->GetSupportedTypes(supported_types); } -void AutofillProfile::GetNonEmptyTypes( - FieldTypeSet* non_empty_types) const { +void AutofillProfile::GetMatchingTypes(const string16& text, + FieldTypeSet* matching_types) const { FormGroupList info = FormGroups(); for (FormGroupList::const_iterator it = info.begin(); it != info.end(); ++it) - (*it)->GetNonEmptyTypes(non_empty_types); + (*it)->GetMatchingTypes(text, matching_types); } + string16 AutofillProfile::GetInfo(AutofillFieldType type) const { AutofillFieldType return_type = AutofillType::GetEquivalentFieldType(type); const FormGroup* form_group = FormGroupForType(return_type); @@ -267,6 +278,26 @@ void AutofillProfile::SetInfo(AutofillFieldType type, const string16& value) { form_group->SetInfo(type, CollapseWhitespace(value, false)); } +string16 AutofillProfile::GetCanonicalizedInfo(AutofillFieldType type) const { + AutofillFieldType return_type = AutofillType::GetEquivalentFieldType(type); + const FormGroup* form_group = FormGroupForType(return_type); + if (!form_group) + return string16(); + + return form_group->GetCanonicalizedInfo(return_type); +} + +bool AutofillProfile::SetCanonicalizedInfo(AutofillFieldType type, + const string16& value) { + FormGroup* form_group = MutableFormGroupForType(type); + if (form_group) { + return form_group->SetCanonicalizedInfo(type, + CollapseWhitespace(value, false)); + } + + return false; +} + void AutofillProfile::SetMultiInfo(AutofillFieldType type, const std::vector<string16>& values) { switch (AutofillType(type).group()) { @@ -280,13 +311,13 @@ void AutofillProfile::SetMultiInfo(AutofillFieldType type, CopyValuesToItems(type, values, &home_number_, - PhoneNumber(AutofillType::PHONE_HOME)); + PhoneNumber(AutofillType::PHONE_HOME, this)); break; case AutofillType::PHONE_FAX: CopyValuesToItems(type, values, &fax_number_, - PhoneNumber(AutofillType::PHONE_FAX)); + PhoneNumber(AutofillType::PHONE_FAX, this)); break; default: if (values.size() == 1) { @@ -303,18 +334,29 @@ void AutofillProfile::SetMultiInfo(AutofillFieldType type, void AutofillProfile::GetMultiInfo(AutofillFieldType type, std::vector<string16>* values) const { + GetMultiInfoImpl(type, false, values); +} + +void AutofillProfile::GetCanonicalizedMultiInfo( + AutofillFieldType type, std::vector<string16>* values) const { + GetMultiInfoImpl(type, true, values); +} + +void AutofillProfile::GetMultiInfoImpl(AutofillFieldType type, + bool canonicalize, + std::vector<string16>* values) const { switch (AutofillType(type).group()) { case AutofillType::NAME: - CopyItemsToValues(type, name_, values); + CopyItemsToValues(type, name_, canonicalize, values); break; case AutofillType::EMAIL: - CopyItemsToValues(type, email_, values); + CopyItemsToValues(type, email_, canonicalize, values); break; case AutofillType::PHONE_HOME: - CopyItemsToValues(type, home_number_, values); + CopyItemsToValues(type, home_number_, canonicalize, values); break; case AutofillType::PHONE_FAX: - CopyItemsToValues(type, fax_number_, values); + CopyItemsToValues(type, fax_number_, canonicalize, values); break; default: values->resize(1); @@ -494,22 +536,6 @@ const string16 AutofillProfile::PrimaryValue() const { GetInfo(ADDRESS_HOME_CITY); } -bool AutofillProfile::NormalizePhones() { - // Successful either if nothing to parse, or everything is parsed correctly. - bool success = true; - for (size_t i = 0; i < home_number_.size(); ++i) { - home_number_[i].set_locale(CountryCode()); - if (!home_number_[i].NormalizePhone()) - success = false; - } - for (size_t i = 0; i < fax_number_.size(); ++i) { - fax_number_[i].set_locale(CountryCode()); - if (!fax_number_[i].NormalizePhone()) - success = false; - } - return success; -} - void AutofillProfile::OverwriteWithOrAddTo(const AutofillProfile& profile) { FieldTypeSet field_types; profile.GetNonEmptyTypes(&field_types); diff --git a/chrome/browser/autofill/autofill_profile.h b/chrome/browser/autofill/autofill_profile.h index 1ff9d8d..4dbb94a 100644 --- a/chrome/browser/autofill/autofill_profile.h +++ b/chrome/browser/autofill/autofill_profile.h @@ -38,16 +38,20 @@ class AutofillProfile : public FormGroup { // FormGroup: virtual void GetMatchingTypes(const string16& text, - FieldTypeSet* matching_types) const; - virtual void GetNonEmptyTypes(FieldTypeSet* non_empty_types) const; - virtual string16 GetInfo(AutofillFieldType type) const; - virtual void SetInfo(AutofillFieldType type, const string16& value); + FieldTypeSet* matching_types) const OVERRIDE; + virtual string16 GetInfo(AutofillFieldType type) const OVERRIDE; + virtual void SetInfo(AutofillFieldType type, const string16& value) OVERRIDE; + virtual string16 GetCanonicalizedInfo(AutofillFieldType type) const OVERRIDE; + virtual bool SetCanonicalizedInfo(AutofillFieldType type, + const string16& value) OVERRIDE; // Multi-value equivalents to |GetInfo| and |SetInfo|. void SetMultiInfo(AutofillFieldType type, const std::vector<string16>& values); void GetMultiInfo(AutofillFieldType type, std::vector<string16>* values) const; + void GetCanonicalizedMultiInfo(AutofillFieldType type, + std::vector<string16>* values) const; // Returns |true| if |type| accepts multi-values. static bool SupportsMultiValue(AutofillFieldType type); @@ -119,13 +123,6 @@ class AutofillProfile : public FormGroup { // aid with correct aggregation of new data. const string16 PrimaryValue() const; - // Normalizes the home phone and fax numbers. - // Should be called after all of the form data is imported into profile. - // Drops unparsable numbers, so the numbers that are incomplete or wrong - // are not saved. Returns true if all numbers were successfully parsed, - // false otherwise. - bool NormalizePhones(); - // Overwrites the single-valued field data in |profile| with this // Profile. Or, for multi-valued fields append the new values. void OverwriteWithOrAddTo(const AutofillProfile& profile); @@ -133,6 +130,14 @@ class AutofillProfile : public FormGroup { private: typedef std::vector<const FormGroup*> FormGroupList; + // FormGroup: + virtual void GetSupportedTypes(FieldTypeSet* supported_types) const OVERRIDE; + + // Shared implementation for GetMultiInfo() and GetCanonicalizedMultiInfo(). + void GetMultiInfoImpl(AutofillFieldType type, + bool canonicalize, + std::vector<string16>* values) const; + // Checks if the |phone| is in the |existing_phones| using fuzzy matching: // for example, "1-800-FLOWERS", "18003569377", "(800)356-9377" and "356-9377" // are considered the same. diff --git a/chrome/browser/autofill/autofill_profile_unittest.cc b/chrome/browser/autofill/autofill_profile_unittest.cc index 28bf838..a5fa207 100644 --- a/chrome/browser/autofill/autofill_profile_unittest.cc +++ b/chrome/browser/autofill/autofill_profile_unittest.cc @@ -481,8 +481,7 @@ TEST(AutofillProfileTest, CreateInferredLabelsSkipsEmptyFields) { // A field must have a non-empty value for each profile to be considered a // distinguishing field. - profiles[1]->SetInfo(ADDRESS_HOME_LINE1, - ASCIIToUTF16("88 Nowhere Ave.")); + profiles[1]->SetInfo(ADDRESS_HOME_LINE1, ASCIIToUTF16("88 Nowhere Ave.")); AutofillProfile::CreateInferredLabels(&profiles.get(), NULL, UNKNOWN_TYPE, 1, &labels); ASSERT_EQ(3U, labels.size()); diff --git a/chrome/browser/autofill/contact_info.cc b/chrome/browser/autofill/contact_info.cc index 7010d2a..57b08fc 100644 --- a/chrome/browser/autofill/contact_info.cc +++ b/chrome/browser/autofill/contact_info.cc @@ -38,52 +38,18 @@ NameInfo& NameInfo::operator=(const NameInfo& info) { if (this == &info) return *this; - first_tokens_ = info.first_tokens_; - middle_tokens_ = info.middle_tokens_; - last_tokens_ = info.last_tokens_; first_ = info.first_; middle_ = info.middle_; last_ = info.last_; return *this; } -void NameInfo::GetMatchingTypes(const string16& text, - FieldTypeSet* matching_types) const { - DCHECK(matching_types); - - if (IsFirstName(text)) - matching_types->insert(NAME_FIRST); - - if (IsMiddleName(text)) - matching_types->insert(NAME_MIDDLE); - - if (IsLastName(text)) - matching_types->insert(NAME_LAST); - - if (IsMiddleInitial(text)) - matching_types->insert(NAME_MIDDLE_INITIAL); - - if (IsFullName(text)) - matching_types->insert(NAME_FULL); -} - -void NameInfo::GetNonEmptyTypes(FieldTypeSet* non_empty_types) const { - DCHECK(non_empty_types); - - if (!first().empty()) - non_empty_types->insert(NAME_FIRST); - - if (!middle().empty()) - non_empty_types->insert(NAME_MIDDLE); - - if (!last().empty()) - non_empty_types->insert(NAME_LAST); - - if (!MiddleInitial().empty()) - non_empty_types->insert(NAME_MIDDLE_INITIAL); - - if (!FullName().empty()) - non_empty_types->insert(NAME_FULL); +void NameInfo::GetSupportedTypes(FieldTypeSet* supported_types) const { + supported_types->insert(NAME_FIRST); + supported_types->insert(NAME_MIDDLE); + supported_types->insert(NAME_LAST); + supported_types->insert(NAME_MIDDLE_INITIAL); + supported_types->insert(NAME_FULL); } string16 NameInfo::GetInfo(AutofillFieldType type) const { @@ -108,11 +74,11 @@ string16 NameInfo::GetInfo(AutofillFieldType type) const { void NameInfo::SetInfo(AutofillFieldType type, const string16& value) { DCHECK_EQ(AutofillType::NAME, AutofillType(type).group()); if (type == NAME_FIRST) - SetFirst(value); + first_ = value; else if (type == NAME_MIDDLE || type == NAME_MIDDLE_INITIAL) - SetMiddle(value); + middle_ = value; else if (type == NAME_LAST) - SetLast(value); + last_ = value; else if (type == NAME_FULL) SetFullName(value); else @@ -145,181 +111,25 @@ string16 NameInfo::MiddleInitial() const { return initial; } -// If each of the 'words' contained in the text are also present in the first -// name then we will consider the text to be of type kFirstName. This means -// that people with multiple first names will be able to enter any one of -// their first names and have it correctly recognized. -bool NameInfo::IsFirstName(const string16& text) const { - return IsNameMatch(text, first_tokens_); -} - -// If each of the 'words' contained in the text are also present in the middle -// name then we will consider the text to be of type kMiddleName. -bool NameInfo::IsMiddleName(const string16& text) const { - return IsNameMatch(text, middle_tokens_); -} - -// If each of the 'words' contained in the text are also present in the last -// name then we will consider the text to be of type kLastName. -bool NameInfo::IsLastName(const string16& text) const { - return IsNameMatch(text, last_tokens_); -} - -bool NameInfo::IsMiddleInitial(const string16& text) const { - if (text.length() != 1) - return false; - - string16 lower_case = StringToLowerASCII(text); - // If the text entered was a single character and it matches the first letter - // of any of the given middle names then we consider it to be a middle - // initial field. - size_t middle_tokens_size = middle_tokens_.size(); - for (size_t i = 0; i < middle_tokens_size; ++i) { - if (middle_tokens_[i][0] == lower_case[0]) - return true; - } - - return false; -} - -// A field will be considered to be of type NAME_FULL if: -// 1) it contains at least one word from the first name. -// 2) it contains at least one word from the last name. -// 3) all of the words in the field match a word in either the first, -// middle, or last name. -bool NameInfo::IsFullName(const string16& text) const { - size_t first_tokens_size = first_tokens_.size(); - if (first_tokens_size == 0) - return false; - - size_t middle_tokens_size = middle_tokens_.size(); - - size_t last_tokens_size = last_tokens_.size(); - if (last_tokens_size == 0) - return false; - - std::vector<string16> text_tokens; - Tokenize(text, kNameSplitChars, &text_tokens); - size_t text_tokens_size = text_tokens.size(); - if (text_tokens_size == 0 || text_tokens_size < 2) - return false; - - size_t name_tokens_size = - first_tokens_size + middle_tokens_size + last_tokens_size; - if (text_tokens_size > name_tokens_size) - return false; - - bool first_name_match = false; - bool last_name_match = false; - for (std::vector<string16>::iterator iter = text_tokens.begin(); - iter != text_tokens.end(); ++iter) { - bool match = false; - if (IsWordInName(*iter, first_tokens_)) { - match = true; - first_name_match = true; - } - - if (IsWordInName(*iter, last_tokens_)) { - match = true; - last_name_match = true; - } - - if (IsWordInName(*iter, middle_tokens_)) - match = true; - - if (!match) - return false; - } - - return (first_name_match && last_name_match); -} - -bool NameInfo::IsNameMatch(const string16& text, - const std::vector<string16>& name_tokens) const { - size_t name_tokens_size = name_tokens.size(); - if (name_tokens_size == 0) - return false; - - std::vector<string16> text_tokens; - Tokenize(text, kNameSplitChars, &text_tokens); - size_t text_tokens_size = text_tokens.size(); - if (text_tokens_size == 0) - return false; - - if (text_tokens_size > name_tokens_size) - return false; - - // If each of the 'words' contained in the text are also present in the name, - // then we will consider the text to match the name. - for (std::vector<string16>::iterator iter = text_tokens.begin(); - iter != text_tokens.end(); ++iter) { - if (!IsWordInName(*iter, name_tokens)) - return false; - } - - return true; -} - -bool NameInfo::IsWordInName(const string16& word, - const std::vector<string16>& name_tokens) const { - for (std::vector<string16>::const_iterator iter = name_tokens.begin(); - iter != name_tokens.end(); ++iter) { - // |*iter| is already lower-cased. - if (StringToLowerASCII(word) == *iter) - return true; - } - - return false; -} - -void NameInfo::SetFirst(const string16& first) { - first_ = first; - first_tokens_.clear(); - Tokenize(first, kNameSplitChars, &first_tokens_); - for (std::vector<string16>::iterator iter = first_tokens_.begin(); - iter != first_tokens_.end(); ++iter) { - *iter = StringToLowerASCII(*iter); - } -} - -void NameInfo::SetMiddle(const string16& middle) { - middle_ = middle; - middle_tokens_.clear(); - Tokenize(middle, kNameSplitChars, &middle_tokens_); - for (std::vector<string16>::iterator iter = middle_tokens_.begin(); - iter != middle_tokens_.end(); ++iter) { - *iter = StringToLowerASCII(*iter); - } -} - -void NameInfo::SetLast(const string16& last) { - last_ = last; - last_tokens_.clear(); - Tokenize(last, kNameSplitChars, &last_tokens_); - for (std::vector<string16>::iterator iter = last_tokens_.begin(); - iter != last_tokens_.end(); ++iter) { - *iter = StringToLowerASCII(*iter); - } -} - void NameInfo::SetFullName(const string16& full) { + // Clear the names. + first_ = string16(); + middle_ = string16(); + last_ = string16(); + std::vector<string16> full_name_tokens; Tokenize(full, ASCIIToUTF16(" "), &full_name_tokens); - // Clear the names. - SetFirst(string16()); - SetMiddle(string16()); - SetLast(string16()); // There are four possibilities: empty; first name; first and last names; // first, middle (possibly multiple strings) and then the last name. if (full_name_tokens.size() > 0) { - SetFirst(full_name_tokens[0]); + first_ = full_name_tokens[0]; if (full_name_tokens.size() > 1) { - SetLast(full_name_tokens.back()); + last_ = full_name_tokens.back(); if (full_name_tokens.size() > 2) { full_name_tokens.erase(full_name_tokens.begin()); full_name_tokens.pop_back(); - SetMiddle(JoinString(full_name_tokens, ' ')); + middle_ = JoinString(full_name_tokens, ' '); } } } @@ -341,18 +151,8 @@ EmailInfo& EmailInfo::operator=(const EmailInfo& info) { return *this; } -void EmailInfo::GetMatchingTypes(const string16& text, - FieldTypeSet* matching_types) const { - DCHECK(matching_types); - // TODO(isherman): Investigate case-insensitive comparison. - if (email_ == text) - matching_types->insert(EMAIL_ADDRESS); -} - -void EmailInfo::GetNonEmptyTypes(FieldTypeSet* non_empty_types) const { - DCHECK(non_empty_types); - if (!email_.empty()) - non_empty_types->insert(EMAIL_ADDRESS); +void EmailInfo::GetSupportedTypes(FieldTypeSet* supported_types) const { + supported_types->insert(EMAIL_ADDRESS); } string16 EmailInfo::GetInfo(AutofillFieldType type) const { @@ -383,19 +183,8 @@ CompanyInfo& CompanyInfo::operator=(const CompanyInfo& info) { return *this; } -void CompanyInfo::GetMatchingTypes(const string16& text, - FieldTypeSet* matching_types) const { - DCHECK(matching_types); - - if (company_name_ == text) - matching_types->insert(COMPANY_NAME); -} - -void CompanyInfo::GetNonEmptyTypes(FieldTypeSet* non_empty_types) const { - DCHECK(non_empty_types); - - if (!company_name_.empty()) - non_empty_types->insert(COMPANY_NAME); +void CompanyInfo::GetSupportedTypes(FieldTypeSet* supported_types) const { + supported_types->insert(COMPANY_NAME); } string16 CompanyInfo::GetInfo(AutofillFieldType type) const { diff --git a/chrome/browser/autofill/contact_info.h b/chrome/browser/autofill/contact_info.h index 2d9d043..ac6ed2b 100644 --- a/chrome/browser/autofill/contact_info.h +++ b/chrome/browser/autofill/contact_info.h @@ -23,15 +23,15 @@ class NameInfo : public FormGroup { NameInfo& operator=(const NameInfo& info); // FormGroup: - virtual void GetMatchingTypes(const string16& text, - FieldTypeSet* matching_types) const; - virtual void GetNonEmptyTypes(FieldTypeSet* non_empty_types) const; - virtual string16 GetInfo(AutofillFieldType type) const; - virtual void SetInfo(AutofillFieldType type, const string16& value); + virtual string16 GetInfo(AutofillFieldType type) const OVERRIDE; + virtual void SetInfo(AutofillFieldType type, const string16& value) OVERRIDE; private: FRIEND_TEST_ALL_PREFIXES(NameInfoTest, TestSetFullName); + // FormGroup: + virtual void GetSupportedTypes(FieldTypeSet* supported_types) const OVERRIDE; + // Returns the full name, which can include up to the first, middle, and last // name. string16 FullName() const; @@ -44,51 +44,10 @@ class NameInfo : public FormGroup { const string16& middle() const { return middle_; } const string16& last() const { return last_; } - // Returns true if |text| is the first name. - bool IsFirstName(const string16& text) const; - - // Returns true if |text| is the middle name. - bool IsMiddleName(const string16& text) const; - - // Returns true if |text| is the last name. - bool IsLastName(const string16& text) const; - - // Returns true if |text| is the middle initial. - bool IsMiddleInitial(const string16& text) const; - - // Returns true if |text| is the last name. - bool IsFullName(const string16& text) const; - - // Returns true if all of the tokens in |text| match the tokens in - // |name_tokens|. - bool IsNameMatch(const string16& text, - const std::vector<string16>& name_tokens) const; - - // Returns true if |word| is one of the tokens in |name_tokens|. - bool IsWordInName(const string16& word, - const std::vector<string16>& name_tokens) const; - - // Sets |first_| to |first| and |first_tokens_| to the set of tokens in - // |first|, made lowercase. - void SetFirst(const string16& first); - - // Sets |middle_| to |middle| and |middle_tokens_| to the set of tokens in - // |middle|, made lowercase. - void SetMiddle(const string16& middle); - - // Sets |last_| to |last| and |last_tokens_| to the set of tokens in |last|, - // made lowercase. - void SetLast(const string16& last); - - // Sets |first_|, |middle_|, |last_| and |*_tokens_| to the tokenized - // |full|. It is tokenized on a space only. + // Sets |first_|, |middle_|, and |last_| to the tokenized |full|. + // It is tokenized on a space only. void SetFullName(const string16& full); - // List of tokens in each part of the name. - std::vector<string16> first_tokens_; - std::vector<string16> middle_tokens_; - std::vector<string16> last_tokens_; - string16 first_; string16 middle_; string16 last_; @@ -103,13 +62,13 @@ class EmailInfo : public FormGroup { EmailInfo& operator=(const EmailInfo& info); // FormGroup: - virtual void GetMatchingTypes(const string16& text, - FieldTypeSet* matching_types) const; - virtual void GetNonEmptyTypes(FieldTypeSet* non_empty_types) const; - virtual string16 GetInfo(AutofillFieldType type) const; - virtual void SetInfo(AutofillFieldType type, const string16& value); + virtual string16 GetInfo(AutofillFieldType type) const OVERRIDE; + virtual void SetInfo(AutofillFieldType type, const string16& value) OVERRIDE; private: + // FormGroup: + virtual void GetSupportedTypes(FieldTypeSet* supported_types) const OVERRIDE; + string16 email_; }; @@ -122,13 +81,13 @@ class CompanyInfo : public FormGroup { CompanyInfo& operator=(const CompanyInfo& info); // FormGroup: - virtual void GetMatchingTypes(const string16& text, - FieldTypeSet* matching_types) const; - virtual void GetNonEmptyTypes(FieldTypeSet* non_empty_types) const; - virtual string16 GetInfo(AutofillFieldType type) const; - virtual void SetInfo(AutofillFieldType type, const string16& value); + virtual string16 GetInfo(AutofillFieldType type) const OVERRIDE; + virtual void SetInfo(AutofillFieldType type, const string16& value) OVERRIDE; private: + // FormGroup: + virtual void GetSupportedTypes(FieldTypeSet* supported_types) const OVERRIDE; + string16 company_name_; }; diff --git a/chrome/browser/autofill/credit_card.cc b/chrome/browser/autofill/credit_card.cc index b6803bc..81f8e9d 100644 --- a/chrome/browser/autofill/credit_card.cc +++ b/chrome/browser/autofill/credit_card.cc @@ -38,6 +38,14 @@ const AutofillFieldType kAutofillCreditCardTypes[] = { const int kAutofillCreditCardLength = arraysize(kAutofillCreditCardTypes); +// Returns a version of |number| that has any separator characters removed. +const string16 StripSeparators(const string16& number) { + const char16 kSeparators[] = {'-', ' ', '\0'}; + string16 stripped; + RemoveChars(number, kSeparators, &stripped); + return stripped; +} + std::string GetCreditCardType(const string16& number) { // Don't check for a specific type if this is not a credit card number. if (!CreditCard::IsValidCreditCardNumber(number)) @@ -133,7 +141,6 @@ bool ConvertYear(const string16& year, int* num) { if (base::StringToInt(year, num)) return true; - NOTREACHED(); *num = 0; return false; } @@ -179,7 +186,6 @@ bool ConvertMonth(const string16& month, int* num) { } } - NOTREACHED(); *num = 0; return false; } @@ -206,53 +212,14 @@ CreditCard::CreditCard(const CreditCard& credit_card) : FormGroup() { CreditCard::~CreditCard() {} -void CreditCard::GetMatchingTypes(const string16& text, - FieldTypeSet* matching_types) const { - if (IsNameOnCard(text)) - matching_types->insert(CREDIT_CARD_NAME); - - if (IsNumber(text)) - matching_types->insert(CREDIT_CARD_NUMBER); - - if (IsExpirationMonth(text)) - matching_types->insert(CREDIT_CARD_EXP_MONTH); - - if (Is2DigitExpirationYear(text)) - matching_types->insert(CREDIT_CARD_EXP_2_DIGIT_YEAR); - - if (Is4DigitExpirationYear(text)) - matching_types->insert(CREDIT_CARD_EXP_4_DIGIT_YEAR); - - if (text == GetInfo(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR)) - matching_types->insert(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR); - - if (text == GetInfo(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR)) - matching_types->insert(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR); -} - -void CreditCard::GetNonEmptyTypes(FieldTypeSet* non_empty_types) const { - DCHECK(non_empty_types); - - if (!name_on_card_.empty()) - non_empty_types->insert(CREDIT_CARD_NAME); - - if (!number_.empty()) - non_empty_types->insert(CREDIT_CARD_NUMBER); - - if (!ExpirationMonthAsString().empty()) - non_empty_types->insert(CREDIT_CARD_EXP_MONTH); - - if (!Expiration2DigitYearAsString().empty()) - non_empty_types->insert(CREDIT_CARD_EXP_2_DIGIT_YEAR); - - if (!Expiration4DigitYearAsString().empty()) - non_empty_types->insert(CREDIT_CARD_EXP_4_DIGIT_YEAR); - - if (!GetInfo(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR).empty()) - non_empty_types->insert(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR); - - if (!GetInfo(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR).empty()) - non_empty_types->insert(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR); +void CreditCard::GetSupportedTypes(FieldTypeSet* supported_types) const { + supported_types->insert(CREDIT_CARD_NAME); + supported_types->insert(CREDIT_CARD_NUMBER); + supported_types->insert(CREDIT_CARD_EXP_MONTH); + supported_types->insert(CREDIT_CARD_EXP_2_DIGIT_YEAR); + supported_types->insert(CREDIT_CARD_EXP_4_DIGIT_YEAR); + supported_types->insert(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR); + supported_types->insert(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR); } string16 CreditCard::GetInfo(AutofillFieldType type) const { @@ -290,7 +257,7 @@ string16 CreditCard::GetInfo(AutofillFieldType type) const { return string16(); case CREDIT_CARD_NUMBER: - return number(); + return number_; case CREDIT_CARD_VERIFICATION_CODE: NOTREACHED(); @@ -349,6 +316,36 @@ void CreditCard::SetInfo(AutofillFieldType type, const string16& value) { } } +string16 CreditCard::GetCanonicalizedInfo(AutofillFieldType type) const { + if (type == CREDIT_CARD_NUMBER) + return StripSeparators(number_); + + return GetInfo(type); +} + +bool CreditCard::SetCanonicalizedInfo(AutofillFieldType type, + const string16& value) { + if (type == CREDIT_CARD_NUMBER) + SetInfo(type, StripSeparators(value)); + else + SetInfo(type, value); + + return true; +} + +void CreditCard::GetMatchingTypes(const string16& text, + FieldTypeSet* matching_types) const { + FormGroup::GetMatchingTypes(text, matching_types); + + string16 card_number = GetCanonicalizedInfo(CREDIT_CARD_NUMBER); + if (!card_number.empty() && StripSeparators(text) == card_number) + matching_types->insert(CREDIT_CARD_NUMBER); + + int month; + if (ConvertMonth(text, &month) && month != 0 && month == expiration_month_) + matching_types->insert(CREDIT_CARD_EXP_MONTH); +} + const string16 CreditCard::Label() const { string16 label; if (number().empty()) @@ -455,14 +452,6 @@ bool CreditCard::operator!=(const CreditCard& credit_card) const { } // static -const string16 CreditCard::StripSeparators(const string16& number) { - const char16 kSeparators[] = {'-', ' ', '\0'}; - string16 stripped; - RemoveChars(number, kSeparators, &stripped); - return stripped; -} - -// static bool CreditCard::IsValidCreditCardNumber(const string16& text) { string16 number = StripSeparators(text); @@ -576,38 +565,6 @@ void CreditCard::SetExpirationYear(int expiration_year) { expiration_year_ = expiration_year; } -bool CreditCard::IsNumber(const string16& text) const { - return StripSeparators(text) == StripSeparators(number_); -} - -bool CreditCard::IsNameOnCard(const string16& text) const { - return StringToLowerASCII(text) == StringToLowerASCII(name_on_card_); -} - -bool CreditCard::IsExpirationMonth(const string16& text) const { - int month; - if (!base::StringToInt(text, &month)) - return false; - - return expiration_month_ == month; -} - -bool CreditCard::Is2DigitExpirationYear(const string16& text) const { - int year; - if (!base::StringToInt(text, &year)) - return false; - - return year < 100 && (expiration_year_ % 100) == year; -} - -bool CreditCard::Is4DigitExpirationYear(const string16& text) const { - int year; - if (!base::StringToInt(text, &year)) - return false; - - return expiration_year_ == year; -} - // So we can compare CreditCards with EXPECT_EQ(). std::ostream& operator<<(std::ostream& os, const CreditCard& credit_card) { return os diff --git a/chrome/browser/autofill/credit_card.h b/chrome/browser/autofill/credit_card.h index 124ae79..8ec8969 100644 --- a/chrome/browser/autofill/credit_card.h +++ b/chrome/browser/autofill/credit_card.h @@ -25,11 +25,13 @@ class CreditCard : public FormGroup { virtual ~CreditCard(); // FormGroup implementation: + virtual string16 GetInfo(AutofillFieldType type) const OVERRIDE; + virtual void SetInfo(AutofillFieldType type, const string16& value) OVERRIDE; + virtual string16 GetCanonicalizedInfo(AutofillFieldType type) const OVERRIDE; + virtual bool SetCanonicalizedInfo(AutofillFieldType type, + const string16& value) OVERRIDE; virtual void GetMatchingTypes(const string16& text, - FieldTypeSet* matching_types) const; - virtual void GetNonEmptyTypes(FieldTypeSet* non_empty_types) const; - virtual string16 GetInfo(AutofillFieldType type) const; - virtual void SetInfo(AutofillFieldType type, const string16& value); + FieldTypeSet* matching_types) const OVERRIDE; // Credit card preview summary, for example: ******1234, Exp: 01/2020 virtual const string16 Label() const; @@ -66,9 +68,6 @@ class CreditCard : public FormGroup { bool operator==(const CreditCard& credit_card) const; bool operator!=(const CreditCard& credit_card) const; - // Return a version of |number| that has any separator characters removed. - static const string16 StripSeparators(const string16& number); - // Returns true if |text| looks like a valid credit card number. // Uses the Luhn formula to validate the number. static bool IsValidCreditCardNumber(const string16& text); @@ -83,6 +82,9 @@ class CreditCard : public FormGroup { const string16& number() const { return number_; } private: + // FormGroup: + virtual void GetSupportedTypes(FieldTypeSet* supported_types) const OVERRIDE; + // The month and year are zero if not present. int Expiration4DigitYear() const { return expiration_year_; } int Expiration2DigitYear() const { return expiration_year_ % 100; } @@ -96,7 +98,7 @@ class CreditCard : public FormGroup { // Sets |expiration_year_| to the integer conversion of |text|. void SetExpirationYearFromString(const string16& text); - // Sets |number_| to the stripped version of |number|, containing only digits. + // Sets |number_| to |number| and computes the appropriate card |type_|. void SetNumber(const string16& number); // These setters verify that the month and year are within appropriate @@ -104,24 +106,6 @@ class CreditCard : public FormGroup { void SetExpirationMonth(int expiration_month); void SetExpirationYear(int expiration_year); - // Returns true if |text| matches the name on the card. The comparison is - // case-insensitive. - bool IsNameOnCard(const string16& text) const; - - // Returns true if |text| matches the card number. - bool IsNumber(const string16& text) const; - - // Returns true if |text| matches the expiration month of the card. - bool IsExpirationMonth(const string16& text) const; - - // Returns true if the integer value of |text| matches the 2-digit expiration - // year. - bool Is2DigitExpirationYear(const string16& text) const; - - // Returns true if the integer value of |text| matches the 4-digit expiration - // year. - bool Is4DigitExpirationYear(const string16& text) const; - string16 number_; // The credit card number. string16 name_on_card_; // The cardholder's name. std::string type_; // The type of the card. diff --git a/chrome/browser/autofill/fax_number.cc b/chrome/browser/autofill/fax_number.cc index 3d1a651..baef644 100644 --- a/chrome/browser/autofill/fax_number.cc +++ b/chrome/browser/autofill/fax_number.cc @@ -4,11 +4,14 @@ #include "chrome/browser/autofill/fax_number.h" -FaxNumber::FaxNumber() {} +FaxNumber::FaxNumber(AutofillProfile* profile) : PhoneNumber(profile) { +} -FaxNumber::FaxNumber(const FaxNumber& fax) : PhoneNumber(fax) {} +FaxNumber::FaxNumber(const FaxNumber& fax) : PhoneNumber(fax) { +} -FaxNumber::~FaxNumber() {} +FaxNumber::~FaxNumber() { +} FaxNumber& FaxNumber::operator=(const FaxNumber& fax) { PhoneNumber::operator=(fax); diff --git a/chrome/browser/autofill/fax_number.h b/chrome/browser/autofill/fax_number.h index 292a169a5..c65e7ae 100644 --- a/chrome/browser/autofill/fax_number.h +++ b/chrome/browser/autofill/fax_number.h @@ -11,7 +11,7 @@ class FaxNumber : public PhoneNumber { public: - FaxNumber(); + explicit FaxNumber(AutofillProfile* profile); FaxNumber(const FaxNumber& fax); virtual ~FaxNumber(); diff --git a/chrome/browser/autofill/form_group.cc b/chrome/browser/autofill/form_group.cc index ace91f4..ae7f316 100644 --- a/chrome/browser/autofill/form_group.cc +++ b/chrome/browser/autofill/form_group.cc @@ -8,6 +8,45 @@ #include <iterator> +void FormGroup::GetMatchingTypes(const string16& text, + FieldTypeSet* matching_types) const { + if (text.empty()) { + matching_types->insert(EMPTY_TYPE); + return; + } + + FieldTypeSet types; + GetSupportedTypes(&types); + for (FieldTypeSet::const_iterator type = types.begin(); + type != types.end(); ++type) { + // TODO(isherman): Matches are case-sensitive for now. Let's keep an eye on + // this and decide whether there are compelling reasons to add case- + // insensitivity. + if (GetInfo(*type) == text) + matching_types->insert(*type); + } +} + +void FormGroup::GetNonEmptyTypes(FieldTypeSet* non_empty_types) const { + FieldTypeSet types; + GetSupportedTypes(&types); + for (FieldTypeSet::const_iterator type = types.begin(); + type != types.end(); ++type) { + if (!GetInfo(*type).empty()) + non_empty_types->insert(*type); + } +} + +string16 FormGroup::GetCanonicalizedInfo(AutofillFieldType type) const { + return GetInfo(type); +} + +bool FormGroup::SetCanonicalizedInfo(AutofillFieldType type, + const string16& value) { + SetInfo(type, value); + return true; +} + const string16 FormGroup::Label() const { return string16(); } bool FormGroup::operator!=(const FormGroup& form_group) const { diff --git a/chrome/browser/autofill/form_group.h b/chrome/browser/autofill/form_group.h index c976dc3..d940682 100644 --- a/chrome/browser/autofill/form_group.h +++ b/chrome/browser/autofill/form_group.h @@ -23,19 +23,27 @@ class FormGroup { // into the field. The field types can then be reported back to the server. // This method is additive on |matching_types|. virtual void GetMatchingTypes(const string16& text, - FieldTypeSet* matching_types) const = 0; + FieldTypeSet* matching_types) const; // Returns a set of AutofillFieldTypes for which this FormGroup has non-empty - // data. - virtual void GetNonEmptyTypes(FieldTypeSet* non_empty_types) const = 0; + // data. This method is additive on |non_empty_types|. + virtual void GetNonEmptyTypes(FieldTypeSet* non_empty_types) const; - // Returns the string that should be auto-filled into a text field given the - // type of that field. + // Returns the literal string associated with |type|. virtual string16 GetInfo(AutofillFieldType type) const = 0; // Used to populate this FormGroup object with data. virtual void SetInfo(AutofillFieldType type, const string16& value) = 0; + // Returns the string that should be auto-filled into a text field given the + // type of that field. + virtual string16 GetCanonicalizedInfo(AutofillFieldType type) const; + + // Used to populate this FormGroup object with data. Canonicalizes the data + // prior to storing, if appropriate. + virtual bool SetCanonicalizedInfo(AutofillFieldType type, + const string16& value); + // Returns the label for this FormGroup item. This should be overridden for // form group items that implement a label. virtual const string16 Label() const; @@ -57,6 +65,15 @@ class FormGroup { // Overwrites the field data in |form_group| with this FormGroup. void OverwriteWith(const FormGroup& form_group); + + protected: + // AutofillProfile needs to call into GetSupportedTypes() for objects of + // non-AutofillProfile type, for which mere inheritance is insufficient. + friend class AutofillProfile; + + // Returns a set of AutofillFieldTypes for which this FormGroup can store + // data. This method is additive on |supported_types|. + virtual void GetSupportedTypes(FieldTypeSet* supported_types) const = 0; }; #endif // CHROME_BROWSER_AUTOFILL_FORM_GROUP_H_ diff --git a/chrome/browser/autofill/home_phone_number.cc b/chrome/browser/autofill/home_phone_number.cc index 9425df5..6f4ecea 100644 --- a/chrome/browser/autofill/home_phone_number.cc +++ b/chrome/browser/autofill/home_phone_number.cc @@ -4,13 +4,16 @@ #include "chrome/browser/autofill/home_phone_number.h" -HomePhoneNumber::HomePhoneNumber() {} +HomePhoneNumber::HomePhoneNumber(AutofillProfile* profile) + : PhoneNumber(profile) { +} HomePhoneNumber::HomePhoneNumber(const HomePhoneNumber& phone) : PhoneNumber(phone) { } -HomePhoneNumber::~HomePhoneNumber() {} +HomePhoneNumber::~HomePhoneNumber() { +} HomePhoneNumber& HomePhoneNumber::operator=(const HomePhoneNumber& phone) { PhoneNumber::operator=(phone); diff --git a/chrome/browser/autofill/home_phone_number.h b/chrome/browser/autofill/home_phone_number.h index 8c3a10b..eee1162 100644 --- a/chrome/browser/autofill/home_phone_number.h +++ b/chrome/browser/autofill/home_phone_number.h @@ -12,7 +12,7 @@ class FormGroup; class HomePhoneNumber : public PhoneNumber { public: - HomePhoneNumber(); + explicit HomePhoneNumber(AutofillProfile* profile); HomePhoneNumber(const HomePhoneNumber& phone); virtual ~HomePhoneNumber(); diff --git a/chrome/browser/autofill/personal_data_manager.cc b/chrome/browser/autofill/personal_data_manager.cc index 91c44f3..65b0e9a 100644 --- a/chrome/browser/autofill/personal_data_manager.cc +++ b/chrome/browser/autofill/personal_data_manager.cc @@ -166,8 +166,8 @@ void PersonalDataManager::OnWebDataServiceRequestDone( // PersonalDataManager, // views::ButtonListener implementations void PersonalDataManager::SetObserver(PersonalDataManager::Observer* observer) { - // TODO: RemoveObserver is for compatibility with old code, it should be - // nuked. + // TODO(dhollowa): RemoveObserver is for compatibility with old code, it + // should be nuked. observers_.RemoveObserver(observer); observers_.AddObserver(observer); } @@ -244,17 +244,11 @@ bool PersonalDataManager::ImportFormData( types_seen.insert(field_type); if (group == AutofillType::CREDIT_CARD) { - // If the user has a password set, we have no way of setting credit - // card numbers. if (LowerCaseEqualsASCII(field->form_control_type, "month")) { DCHECK_EQ(CREDIT_CARD_EXP_MONTH, field_type); local_imported_credit_card->SetInfoForMonthInputType(value); } else { - if (field_type == CREDIT_CARD_NUMBER) { - // Clean up any imported credit card numbers. - value = CreditCard::StripSeparators(value); - } - local_imported_credit_card->SetInfo(field_type, value); + local_imported_credit_card->SetCanonicalizedInfo(field_type, value); } ++importable_credit_card_fields; } else { @@ -263,7 +257,7 @@ bool PersonalDataManager::ImportFormData( // If the fields are not the phone fields in question both home.SetInfo() // and fax.SetInfo() are going to return false. if (!home.SetInfo(field_type, value) && !fax.SetInfo(field_type, value)) - imported_profile->SetInfo(field_type, value); + imported_profile->SetCanonicalizedInfo(field_type, value); // Reject profiles with invalid country information. if (field_type == ADDRESS_HOME_COUNTRY && @@ -274,31 +268,25 @@ bool PersonalDataManager::ImportFormData( } } - // Build phone numbers if they are from parts. - if (imported_profile.get()) { + // Construct the phone and fax numbers. Reject the profile if either number + // is invalid. + if (imported_profile.get() && !home.IsEmpty()) { string16 constructed_number; - if (!home.empty()) { - if (!home.ParseNumber(imported_profile->CountryCode(), - &constructed_number)) { - imported_profile.reset(); - } else { - imported_profile->SetInfo(PHONE_HOME_WHOLE_NUMBER, constructed_number); - } - } - if (!fax.empty()) { - if (!fax.ParseNumber(imported_profile->CountryCode(), - &constructed_number)) { - imported_profile.reset(); - } else { - imported_profile->SetInfo(PHONE_FAX_WHOLE_NUMBER, constructed_number); - } + if (!home.ParseNumber(imported_profile->CountryCode(), + &constructed_number) || + !imported_profile->SetCanonicalizedInfo(PHONE_HOME_WHOLE_NUMBER, + constructed_number)) { + imported_profile.reset(); } } - // Normalize phone numbers. - if (imported_profile.get()) { - // Reject profile if even one of the phones is invalid. - if (!imported_profile->NormalizePhones()) + if (imported_profile.get() && !fax.IsEmpty()) { + string16 constructed_number; + if (!fax.ParseNumber(imported_profile->CountryCode(), + &constructed_number) || + !imported_profile->SetCanonicalizedInfo(PHONE_FAX_WHOLE_NUMBER, + constructed_number)) { imported_profile.reset(); + } } // Reject the profile if minimum address and validation requirements are not @@ -486,41 +474,6 @@ CreditCard* PersonalDataManager::GetCreditCardByGUID(const std::string& guid) { return NULL; } -void PersonalDataManager::GetMatchingTypes(const string16& text, - FieldTypeSet* matching_types) const { - string16 clean_info = StringToLowerASCII(CollapseWhitespace(text, false)); - if (clean_info.empty()) { - matching_types->insert(EMPTY_TYPE); - return; - } - - const std::vector<AutofillProfile*>& profiles = this->profiles(); - for (std::vector<AutofillProfile*>::const_iterator iter = profiles.begin(); - iter != profiles.end(); ++iter) { - const FormGroup* profile = *iter; - if (!profile) { - DLOG(ERROR) << "NULL information in profiles list"; - continue; - } - - profile->GetMatchingTypes(clean_info, matching_types); - } - - for (ScopedVector<CreditCard>::const_iterator iter = credit_cards_.begin(); - iter != credit_cards_.end(); ++iter) { - const FormGroup* credit_card = *iter; - if (!credit_card) { - DLOG(ERROR) << "NULL information in credit cards list"; - continue; - } - - credit_card->GetMatchingTypes(clean_info, matching_types); - } - - if (matching_types->empty()) - matching_types->insert(UNKNOWN_TYPE); -} - void PersonalDataManager::GetNonEmptyTypes( FieldTypeSet* non_empty_types) const { const std::vector<AutofillProfile*>& profiles = this->profiles(); diff --git a/chrome/browser/autofill/personal_data_manager.h b/chrome/browser/autofill/personal_data_manager.h index dce2acf..da7b0b8 100644 --- a/chrome/browser/autofill/personal_data_manager.h +++ b/chrome/browser/autofill/personal_data_manager.h @@ -96,11 +96,6 @@ class PersonalDataManager // no credit card with the specified |guid|. CreditCard* GetCreditCardByGUID(const std::string& guid); - // Gets the possible field types for the given text, determined by matching - // the text with all known personal information and returning matching types. - void GetMatchingTypes(const string16& text, - FieldTypeSet* matching_types) const; - // Gets the field types availabe in the stored address and credit card data. void GetNonEmptyTypes(FieldTypeSet* non_empty_types) const; diff --git a/chrome/browser/autofill/personal_data_manager_mac.mm b/chrome/browser/autofill/personal_data_manager_mac.mm index 5d02166..253fcdf 100644 --- a/chrome/browser/autofill/personal_data_manager_mac.mm +++ b/chrome/browser/autofill/personal_data_manager_mac.mm @@ -216,7 +216,6 @@ void AuxiliaryProfilesImpl::GetAddressBookPhoneNumbers( profile->SetInfo(PHONE_FAX_WHOLE_NUMBER, workFax); } } - profile->NormalizePhones(); } } // namespace diff --git a/chrome/browser/autofill/phone_field.cc b/chrome/browser/autofill/phone_field.cc index f9fc036..f310073 100644 --- a/chrome/browser/autofill/phone_field.cc +++ b/chrome/browser/autofill/phone_field.cc @@ -348,9 +348,9 @@ void PhoneField::SetPhoneType(PhoneType phone_type) { // Field types are different as well, so we create a temporary phone number, // to get relevant field types. if (phone_type == HOME_PHONE) - number_.reset(new PhoneNumber(AutofillType::PHONE_HOME)); + number_.reset(new PhoneNumber(AutofillType::PHONE_HOME, NULL)); else - number_.reset(new PhoneNumber(AutofillType::PHONE_FAX)); + number_.reset(new PhoneNumber(AutofillType::PHONE_FAX, NULL)); phone_type_ = phone_type; } diff --git a/chrome/browser/autofill/phone_number.cc b/chrome/browser/autofill/phone_number.cc index 57d44a1..72b5517 100644 --- a/chrome/browser/autofill/phone_number.cc +++ b/chrome/browser/autofill/phone_number.cc @@ -33,14 +33,21 @@ const AutofillType::FieldTypeSubGroup kAutofillPhoneTypes[] = { const int kAutofillPhoneLength = arraysize(kAutofillPhoneTypes); +void StripPunctuation(string16* number) { + RemoveChars(*number, kPhoneNumberSeparators, number); +} + } // namespace -PhoneNumber::PhoneNumber() - : phone_group_(AutofillType::NO_GROUP) { +PhoneNumber::PhoneNumber(AutofillProfile* profile) + : phone_group_(AutofillType::NO_GROUP), + profile_(profile) { } -PhoneNumber::PhoneNumber(AutofillType::FieldTypeGroup phone_group) - : phone_group_(phone_group) { +PhoneNumber::PhoneNumber(AutofillType::FieldTypeGroup phone_group, + AutofillProfile* profile) + : phone_group_(phone_group), + profile_(profile) { } PhoneNumber::PhoneNumber(const PhoneNumber& number) : FormGroup() { @@ -52,66 +59,26 @@ PhoneNumber::~PhoneNumber() {} PhoneNumber& PhoneNumber::operator=(const PhoneNumber& number) { if (this == &number) return *this; - number_ = number.number_; phone_group_ = number.phone_group_; + number_ = number.number_; + profile_ = number.profile_; cached_parsed_phone_ = number.cached_parsed_phone_; return *this; } -void PhoneNumber::GetMatchingTypes(const string16& text, - FieldTypeSet* matching_types) const { - string16 stripped_text(text); - StripPunctuation(&stripped_text); - - if (!cached_parsed_phone_.IsValidNumber()) - return; - - if (IsNumber(stripped_text, cached_parsed_phone_.GetNumber())) - matching_types->insert(GetNumberType()); - - if (stripped_text == cached_parsed_phone_.GetCityCode()) - matching_types->insert(GetCityCodeType()); - - if (stripped_text == cached_parsed_phone_.GetCountryCode()) - matching_types->insert(GetCountryCodeType()); - - string16 city_and_local(cached_parsed_phone_.GetCityCode()); - city_and_local.append(cached_parsed_phone_.GetNumber()); - if (stripped_text == city_and_local) - matching_types->insert(GetCityAndNumberType()); - - // Whole number is compared to unfiltered text - it would be parsed for phone - // comparision (e.g. 1-800-FLOWERS and 18003569377 are the same) - if (IsWholeNumber(text)) - matching_types->insert(GetWholeNumberType()); -} - -void PhoneNumber::GetNonEmptyTypes(FieldTypeSet* non_empty_types) const { - DCHECK(non_empty_types); - - if (number_.empty()) - return; - - non_empty_types->insert(GetWholeNumberType()); - - if (!cached_parsed_phone_.IsValidNumber()) - return; - - non_empty_types->insert(GetNumberType()); - - if (!cached_parsed_phone_.GetCityCode().empty()) { - non_empty_types->insert(GetCityCodeType()); - non_empty_types->insert(GetCityAndNumberType()); - } - - if (!cached_parsed_phone_.GetCountryCode().empty()) - non_empty_types->insert(GetCountryCodeType()); +void PhoneNumber::GetSupportedTypes(FieldTypeSet* supported_types) const { + supported_types->insert(GetWholeNumberType()); + supported_types->insert(GetNumberType()); + supported_types->insert(GetCityCodeType()); + supported_types->insert(GetCityAndNumberType()); + supported_types->insert(GetCountryCodeType()); } string16 PhoneNumber::GetInfo(AutofillFieldType type) const { if (type == GetWholeNumberType()) return number_; + UpdateCacheIfNeeded(); if (!cached_parsed_phone_.IsValidNumber()) return string16(); @@ -124,35 +91,78 @@ string16 PhoneNumber::GetInfo(AutofillFieldType type) const { if (type == GetCountryCodeType()) return cached_parsed_phone_.GetCountryCode(); - string16 city_and_local(cached_parsed_phone_.GetCityCode()); - city_and_local.append(cached_parsed_phone_.GetNumber()); - if (type == GetCityAndNumberType()) + if (type == GetCityAndNumberType()) { + string16 city_and_local(cached_parsed_phone_.GetCityCode()); + city_and_local.append(cached_parsed_phone_.GetNumber()); return city_and_local; + } return string16(); } void PhoneNumber::SetInfo(AutofillFieldType type, const string16& value) { - FieldTypeSubGroup subgroup = AutofillType(type).subgroup(); - FieldTypeGroup group = AutofillType(type).group(); + // Store the group the first time we set some info. if (phone_group_ == AutofillType::NO_GROUP) - phone_group_ = group; // First call on empty phone - set the group. - if (subgroup == AutofillType::PHONE_NUMBER) { - // Should not be set directly. - NOTREACHED(); - } else if (subgroup == AutofillType::PHONE_CITY_CODE) { - // Should not be set directly. - NOTREACHED(); - } else if (subgroup == AutofillType::PHONE_COUNTRY_CODE) { - // Should not be set directly. - NOTREACHED(); - } else if (subgroup == AutofillType::PHONE_CITY_AND_NUMBER || - subgroup == AutofillType::PHONE_WHOLE_NUMBER) { - number_ = value; - cached_parsed_phone_ = autofill_i18n::PhoneObject(number_, locale_); - StripPunctuation(&number_); - } else { + phone_group_ = AutofillType(type).group(); + + FieldTypeSubGroup subgroup = AutofillType(type).subgroup(); + if (subgroup != AutofillType::PHONE_CITY_AND_NUMBER && + subgroup != AutofillType::PHONE_WHOLE_NUMBER) { + // Only full phone numbers should be set directly. NOTREACHED(); + return; + } + + number_ = value; + cached_parsed_phone_ = autofill_i18n::PhoneObject(number_, locale()); +} + +// Normalize phones if |type| is a whole number: +// (650)2345678 -> 6502345678 +// 1-800-FLOWERS -> 18003569377 +// If the phone cannot be normalized, returns the stored value verbatim. +string16 PhoneNumber::GetCanonicalizedInfo(AutofillFieldType type) const { + string16 phone = GetInfo(type); + if (type != GetWholeNumberType()) + return phone; + + string16 normalized_phone = autofill_i18n::NormalizePhoneNumber(phone, + locale()); + if (!normalized_phone.empty()) + return normalized_phone; + + return phone; +} + +bool PhoneNumber::SetCanonicalizedInfo(AutofillFieldType type, + const string16& value) { + string16 number = value; + StripPunctuation(&number); + SetInfo(type, number); + + return NormalizePhone(); +} + +void PhoneNumber::GetMatchingTypes(const string16& text, + FieldTypeSet* matching_types) const { + string16 stripped_text = text; + StripPunctuation(&stripped_text); + FormGroup::GetMatchingTypes(stripped_text, matching_types); + + // For US numbers, also compare to the three-digit prefix and the four-digit + // suffix, since websites often split numbers into these two fields. + string16 number = GetCanonicalizedInfo(GetNumberType()); + if (locale() == "US" && number.size() == (kPrefixLength + kSuffixLength)) { + string16 prefix = number.substr(kPrefixOffset, kPrefixLength); + string16 suffix = number.substr(kSuffixOffset, kSuffixLength); + if (text == prefix || text == suffix) + matching_types->insert(GetNumberType()); + } + + string16 whole_number = GetCanonicalizedInfo(GetWholeNumberType()); + if (!whole_number.empty() && + autofill_i18n::NormalizePhoneNumber(text, locale()) == whole_number) { + matching_types->insert(GetWholeNumberType()); } } @@ -161,14 +171,23 @@ bool PhoneNumber::NormalizePhone() { if (number_.empty()) return true; + UpdateCacheIfNeeded(); number_ = cached_parsed_phone_.GetWholeNumber(); return !number_.empty(); } -void PhoneNumber::set_locale(const std::string& locale) { - locale_ = locale; - if (!number_.empty() && cached_parsed_phone_.GetLocale() != locale_) - cached_parsed_phone_ = autofill_i18n::PhoneObject(number_, locale_); +std::string PhoneNumber::locale() const { + if (!profile_) { + NOTREACHED(); + return "US"; + } + + return profile_->CountryCode(); +} + +void PhoneNumber::UpdateCacheIfNeeded() const { + if (!number_.empty() && cached_parsed_phone_.GetLocale() != locale()) + cached_parsed_phone_ = autofill_i18n::PhoneObject(number_, locale()); } AutofillFieldType PhoneNumber::GetNumberType() const { @@ -221,30 +240,53 @@ AutofillFieldType PhoneNumber::GetWholeNumberType() const { return UNKNOWN_TYPE; } +PhoneNumber::PhoneCombineHelper::PhoneCombineHelper( + AutofillType::FieldTypeGroup phone_group) + : phone_group_(phone_group) { +} + +PhoneNumber::PhoneCombineHelper::~PhoneCombineHelper() { +} + bool PhoneNumber::PhoneCombineHelper::SetInfo(AutofillFieldType field_type, const string16& value) { - PhoneNumber temp(phone_group_); + PhoneNumber temp(phone_group_, NULL); if (field_type == temp.GetCountryCodeType()) { country_ = value; return true; - } else if (field_type == temp.GetCityCodeType()) { + } + + if (field_type == temp.GetCityCodeType()) { city_ = value; return true; - } else if (field_type == temp.GetCityAndNumberType()) { + } + + if (field_type == temp.GetCityAndNumberType()) { phone_ = value; return true; - } else if (field_type == temp.GetNumberType()) { + } + + if (field_type == temp.GetWholeNumberType()) { + whole_number_ = value; + return true; + } + + if (field_type == temp.GetNumberType()) { phone_.append(value); return true; - } else { - return false; } + + return false; } bool PhoneNumber::PhoneCombineHelper::ParseNumber(const std::string& locale, string16* value) { - DCHECK(value); + if (!whole_number_.empty()) { + *value = whole_number_; + return true; + } + return autofill_i18n::ConstructPhoneNumber( country_, city_, phone_, locale, @@ -253,28 +295,6 @@ bool PhoneNumber::PhoneCombineHelper::ParseNumber(const std::string& locale, value); } -bool PhoneNumber::IsNumber(const string16& text, const string16& number) const { - // TODO(georgey): This will need to be updated once we add support for - // international phone numbers. - const size_t kPrefixLength = 3; - const size_t kSuffixLength = 4; - - if (text == number) - return true; - if (text.length() == kPrefixLength && StartsWith(number, text, true)) - return true; - if (text.length() == kSuffixLength && EndsWith(number, text, true)) - return true; - - return false; -} - -bool PhoneNumber::IsWholeNumber(const string16& text) const { - return cached_parsed_phone_.ComparePhones(text) == - autofill_i18n::PHONES_EQUAL; -} - -// Static. -void PhoneNumber::StripPunctuation(string16* number) { - RemoveChars(*number, kPhoneNumberSeparators, number); +bool PhoneNumber::PhoneCombineHelper::IsEmpty() const { + return phone_.empty() && whole_number_.empty(); } diff --git a/chrome/browser/autofill/phone_number.h b/chrome/browser/autofill/phone_number.h index cd168c9..13261d01 100644 --- a/chrome/browser/autofill/phone_number.h +++ b/chrome/browser/autofill/phone_number.h @@ -15,26 +15,32 @@ #include "chrome/browser/autofill/form_group.h" #include "chrome/browser/autofill/phone_number_i18n.h" +class AutofillProfile; + // A form group that stores phone number information. class PhoneNumber : public FormGroup { public: - PhoneNumber(); - explicit PhoneNumber(AutofillType::FieldTypeGroup phone_group); + explicit PhoneNumber(AutofillProfile* profile); + // The |profile| can be NULL; but if so, clients should avoid calling + // |GetInfo()| or |SetInfo()|. This is typically only useful if you want to + // call into the |GetNumberType()| (etc.) functions. + PhoneNumber(AutofillType::FieldTypeGroup phone_group, + AutofillProfile* profile); PhoneNumber(const PhoneNumber& number); virtual ~PhoneNumber(); PhoneNumber& operator=(const PhoneNumber& number); + void set_profile(AutofillProfile* profile) { profile_ = profile; } + // FormGroup implementation: virtual void GetMatchingTypes(const string16& text, - FieldTypeSet* matching_types) const; - virtual void GetNonEmptyTypes(FieldTypeSet* non_empty_typess) const; - virtual string16 GetInfo(AutofillFieldType type) const; - virtual void SetInfo(AutofillFieldType type, const string16& value); - - // Validates |number_| and translates it into digits-only format. - // Locale must be set. - bool NormalizePhone(); + FieldTypeSet* matching_types) const OVERRIDE; + virtual string16 GetInfo(AutofillFieldType type) const OVERRIDE; + virtual void SetInfo(AutofillFieldType type, const string16& value) OVERRIDE; + virtual string16 GetCanonicalizedInfo(AutofillFieldType type) const OVERRIDE; + virtual bool SetCanonicalizedInfo(AutofillFieldType type, + const string16& value) OVERRIDE; // Size and offset of the prefix and suffix portions of phone numbers. static const int kPrefixOffset = 0; @@ -42,11 +48,6 @@ class PhoneNumber : public FormGroup { static const int kSuffixOffset = 3; static const int kSuffixLength = 4; - // Sets locale for normalizing phone numbers. Must be called if you get - // normalized number or use NormalizePhone() function; - // Setting it to "", actually sets it to default locale - "US". - void set_locale(const std::string& locale); - // The following functions should return the field type for each part of the // phone number. Currently, these are either fax or home phone number types. AutofillFieldType GetNumberType() const; @@ -58,42 +59,52 @@ class PhoneNumber : public FormGroup { // The class used to combine home phone or fax parts into a whole number. class PhoneCombineHelper { public: - explicit PhoneCombineHelper(AutofillType::FieldTypeGroup phone_group) - : phone_group_(phone_group) { } - // Sets PHONE_HOME/FAX_CITY_CODE, PHONE_HOME/FAX_COUNTRY_CODE, - // PHONE_HOME/FAX_CITY_AND_NUMBER, PHONE_HOME/FAX_NUMBER and returns true. - // For all other field types returs false. + explicit PhoneCombineHelper(AutofillType::FieldTypeGroup phone_group); + ~PhoneCombineHelper(); + + // If |type| is a phone field type, saves the |value| accordingly and + // returns true. For all other field types returs false. bool SetInfo(AutofillFieldType type, const string16& value); + // Returns true if parsing was successful, false otherwise. bool ParseNumber(const std::string& locale, string16* value); - bool empty() const { return phone_.empty(); } + // Returns true if both |phone_| and |whole_number_| are empty. + bool IsEmpty() const; + private: string16 country_; string16 city_; string16 phone_; + string16 whole_number_; AutofillType::FieldTypeGroup phone_group_; }; private: FRIEND_TEST_ALL_PREFIXES(PhoneNumberTest, Matcher); - // The numbers will be digits only (no punctuation), so any call to the IsX() - // functions should first call StripPunctuation on the text. - bool IsNumber(const string16& text, const string16& number) const; - bool IsWholeNumber(const string16& text) const; + // FormGroup: + virtual void GetSupportedTypes(FieldTypeSet* supported_types) const OVERRIDE; + + // Validates |number_| and translates it into digits-only format. + bool NormalizePhone(); + + // Returns the locale for this phone number, based on the |profile_|. + std::string locale() const; - static void StripPunctuation(string16* number); + // Updates the cached parsed number if the profile's locale has changed + // since the last time the cache was updated. + void UpdateCacheIfNeeded() const; // Phone group - currently it is PHONE_HOME and PHONE_FAX. AutofillType::FieldTypeGroup phone_group_; - // Locale for phone normalizing. - std::string locale_; // The phone number. string16 number_; + // Profile which stores the locale used as hint when normalizing the number. + AutofillProfile* profile_; // WEAK // Cached number. - autofill_i18n::PhoneObject cached_parsed_phone_; + mutable autofill_i18n::PhoneObject cached_parsed_phone_; }; #endif // CHROME_BROWSER_AUTOFILL_PHONE_NUMBER_H_ diff --git a/chrome/browser/autofill/phone_number_i18n.cc b/chrome/browser/autofill/phone_number_i18n.cc index c26f7c5..1ad2508 100644 --- a/chrome/browser/autofill/phone_number_i18n.cc +++ b/chrome/browser/autofill/phone_number_i18n.cc @@ -315,7 +315,6 @@ string16 PhoneObject::GetWholeNumber() const { i18n::phonenumbers::PhoneNumberUtil* phone_util = i18n::phonenumbers::PhoneNumberUtil::GetInstance(); phone_util->Format(*i18n_number_, format, &formatted_number); - i18n::phonenumbers::PhoneNumberUtil::NormalizeDigitsOnly(&formatted_number); whole_number_ = UTF8ToUTF16(formatted_number); } return whole_number_; diff --git a/chrome/browser/autofill/phone_number_unittest.cc b/chrome/browser/autofill/phone_number_unittest.cc index 65b5952..9a06418 100644 --- a/chrome/browser/autofill/phone_number_unittest.cc +++ b/chrome/browser/autofill/phone_number_unittest.cc @@ -3,22 +3,25 @@ // found in the LICENSE file. #include "base/utf_string_conversions.h" +#include "chrome/browser/autofill/autofill_profile.h" #include "chrome/browser/autofill/field_types.h" #include "chrome/browser/autofill/phone_number.h" #include "chrome/browser/autofill/phone_number_i18n.h" #include "testing/gtest/include/gtest/gtest.h" TEST(PhoneNumberTest, Matcher) { + AutofillProfile profile; + profile.SetCountryCode("US"); // Set phone number so country_code == 1, city_code = 650, number = 2345678. string16 phone(ASCIIToUTF16("1 [650] 234-5678")); - PhoneNumber phone_number(AutofillType::PHONE_HOME); + PhoneNumber phone_number(AutofillType::PHONE_HOME, &profile); phone_number.SetInfo(PHONE_HOME_WHOLE_NUMBER, phone); - phone_number.set_locale(std::string("US")); phone_number.NormalizePhone(); FieldTypeSet matching_types; phone_number.GetMatchingTypes(ASCIIToUTF16(""), &matching_types); - EXPECT_EQ(0U, matching_types.size()); + EXPECT_EQ(1U, matching_types.size()); + EXPECT_TRUE(matching_types.find(EMPTY_TYPE) != matching_types.end()); matching_types.clear(); phone_number.GetMatchingTypes(ASCIIToUTF16("1"), &matching_types); EXPECT_EQ(1U, matching_types.size()); @@ -64,22 +67,17 @@ TEST(PhoneNumberTest, Matcher) { EXPECT_EQ(0U, matching_types.size()); matching_types.clear(); phone_number.GetMatchingTypes(ASCIIToUTF16("6502345678"), &matching_types); - EXPECT_EQ(2U, matching_types.size()); + EXPECT_EQ(1U, matching_types.size()); EXPECT_TRUE(matching_types.find(PHONE_HOME_CITY_AND_NUMBER) != matching_types.end()); - EXPECT_TRUE(matching_types.find(PHONE_HOME_WHOLE_NUMBER) != - matching_types.end()); matching_types.clear(); phone_number.GetMatchingTypes(ASCIIToUTF16("(650)2345678"), &matching_types); - EXPECT_EQ(2U, matching_types.size()); + EXPECT_EQ(1U, matching_types.size()); EXPECT_TRUE(matching_types.find(PHONE_HOME_CITY_AND_NUMBER) != matching_types.end()); - EXPECT_TRUE(matching_types.find(PHONE_HOME_WHOLE_NUMBER) != - matching_types.end()); string16 fax(ASCIIToUTF16("+1(650)650-5678")); - PhoneNumber fax_number; - fax_number.set_locale(std::string("US")); + PhoneNumber fax_number(&profile); fax_number.SetInfo(PHONE_FAX_WHOLE_NUMBER, fax); matching_types.clear(); diff --git a/chrome/browser/autofill/select_control_handler.cc b/chrome/browser/autofill/select_control_handler.cc index fe81b88..f2cf81d 100644 --- a/chrome/browser/autofill/select_control_handler.cc +++ b/chrome/browser/autofill/select_control_handler.cc @@ -205,7 +205,7 @@ void FillSelectControl(const FormGroup& form_group, DCHECK_EQ(ASCIIToUTF16("select-one"), field->form_control_type); DCHECK_EQ(field->option_values.size(), field->option_contents.size()); - string16 field_text = form_group.GetInfo(type); + string16 field_text = form_group.GetCanonicalizedInfo(type); string16 field_text_lower = StringToLowerASCII(field_text); if (field_text.empty()) return; diff --git a/chrome/browser/webdata/autofill_table.cc b/chrome/browser/webdata/autofill_table.cc index ff687c7..c387d86 100644 --- a/chrome/browser/webdata/autofill_table.cc +++ b/chrome/browser/webdata/autofill_table.cc @@ -126,10 +126,8 @@ CreditCard* CreditCardFromStatement(const sql::Statement& s) { DCHECK(guid::IsValidGUID(credit_card->guid())); credit_card->SetInfo(CREDIT_CARD_NAME, s.ColumnString16(1)); - credit_card->SetInfo(CREDIT_CARD_EXP_MONTH, - s.ColumnString16(2)); - credit_card->SetInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, - s.ColumnString16(3)); + credit_card->SetInfo(CREDIT_CARD_EXP_MONTH, s.ColumnString16(2)); + credit_card->SetInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, s.ColumnString16(3)); int encrypted_number_len = s.ColumnByteLength(4); string16 credit_card_number; if (encrypted_number_len) { diff --git a/chrome/test/data/autofill/merge/output/case.out b/chrome/test/data/autofill/merge/output/case.out index 8c6fb81..3ed4808 100644 --- a/chrome/test/data/autofill/merge/output/case.out +++ b/chrome/test/data/autofill/merge/output/case.out @@ -10,5 +10,5 @@ ADDRESS_HOME_CITY: Mountain View ADDRESS_HOME_STATE: CA ADDRESS_HOME_ZIP: 94043 ADDRESS_HOME_COUNTRY: United States -PHONE_HOME_WHOLE_NUMBER: 6505558888 -PHONE_FAX_WHOLE_NUMBER: 6505556789 +PHONE_HOME_WHOLE_NUMBER: (650) 555-8888 +PHONE_FAX_WHOLE_NUMBER: (650) 555-6789 diff --git a/chrome/test/data/autofill/merge/output/identical.out b/chrome/test/data/autofill/merge/output/identical.out index 8c6fb81..3ed4808 100644 --- a/chrome/test/data/autofill/merge/output/identical.out +++ b/chrome/test/data/autofill/merge/output/identical.out @@ -10,5 +10,5 @@ ADDRESS_HOME_CITY: Mountain View ADDRESS_HOME_STATE: CA ADDRESS_HOME_ZIP: 94043 ADDRESS_HOME_COUNTRY: United States -PHONE_HOME_WHOLE_NUMBER: 6505558888 -PHONE_FAX_WHOLE_NUMBER: 6505556789 +PHONE_HOME_WHOLE_NUMBER: (650) 555-8888 +PHONE_FAX_WHOLE_NUMBER: (650) 555-6789 diff --git a/chrome/test/data/autofill/merge/output/multimerge.out b/chrome/test/data/autofill/merge/output/multimerge.out index c0ca01d..f14e5e2 100644 --- a/chrome/test/data/autofill/merge/output/multimerge.out +++ b/chrome/test/data/autofill/merge/output/multimerge.out @@ -14,6 +14,6 @@ ADDRESS_HOME_CITY: San Francisco ADDRESS_HOME_STATE: CA ADDRESS_HOME_ZIP: 94102 ADDRESS_HOME_COUNTRY: United States -PHONE_HOME_WHOLE_NUMBER: 16502101111 -PHONE_HOME_WHOLE_NUMBER: 6502343333 -PHONE_FAX_WHOLE_NUMBER: 7812008888 +PHONE_HOME_WHOLE_NUMBER: +1 650-210-1111 +PHONE_HOME_WHOLE_NUMBER: (650) 234-3333 +PHONE_FAX_WHOLE_NUMBER: (781) 200-8888 diff --git a/chrome/test/data/autofill/merge/output/primarycase.out b/chrome/test/data/autofill/merge/output/primarycase.out index c0ca01d..f14e5e2 100644 --- a/chrome/test/data/autofill/merge/output/primarycase.out +++ b/chrome/test/data/autofill/merge/output/primarycase.out @@ -14,6 +14,6 @@ ADDRESS_HOME_CITY: San Francisco ADDRESS_HOME_STATE: CA ADDRESS_HOME_ZIP: 94102 ADDRESS_HOME_COUNTRY: United States -PHONE_HOME_WHOLE_NUMBER: 16502101111 -PHONE_HOME_WHOLE_NUMBER: 6502343333 -PHONE_FAX_WHOLE_NUMBER: 7812008888 +PHONE_HOME_WHOLE_NUMBER: +1 650-210-1111 +PHONE_HOME_WHOLE_NUMBER: (650) 234-3333 +PHONE_FAX_WHOLE_NUMBER: (781) 200-8888 diff --git a/chrome/test/data/autofill/merge/output/singlemerge.out b/chrome/test/data/autofill/merge/output/singlemerge.out index c0a25903..62843bc 100644 --- a/chrome/test/data/autofill/merge/output/singlemerge.out +++ b/chrome/test/data/autofill/merge/output/singlemerge.out @@ -10,5 +10,5 @@ ADDRESS_HOME_CITY: San Francisco ADDRESS_HOME_STATE: NY ADDRESS_HOME_ZIP: 11001 ADDRESS_HOME_COUNTRY: Canada -PHONE_HOME_WHOLE_NUMBER: 16502101111 -PHONE_FAX_WHOLE_NUMBER: 7812008888 +PHONE_HOME_WHOLE_NUMBER: +1 650-210-1111 +PHONE_FAX_WHOLE_NUMBER: (781) 200-8888 diff --git a/chrome/test/data/autofill/merge/output/validation.out b/chrome/test/data/autofill/merge/output/validation.out index 2925154..1462b59 100644 --- a/chrome/test/data/autofill/merge/output/validation.out +++ b/chrome/test/data/autofill/merge/output/validation.out @@ -10,5 +10,5 @@ ADDRESS_HOME_CITY: Mountain View ADDRESS_HOME_STATE: CA ADDRESS_HOME_ZIP: 94043 ADDRESS_HOME_COUNTRY: United States -PHONE_HOME_WHOLE_NUMBER: 6505558888 -PHONE_FAX_WHOLE_NUMBER: 6505556789 +PHONE_HOME_WHOLE_NUMBER: (650) 555-8888 +PHONE_FAX_WHOLE_NUMBER: (650) 555-6789 |