diff options
-rw-r--r-- | chrome/browser/autofill/autofill_manager.cc | 294 | ||||
-rw-r--r-- | chrome/browser/autofill/autofill_manager.h | 24 | ||||
-rw-r--r-- | chrome/browser/autofill/autofill_manager_unittest.cc | 347 | ||||
-rw-r--r-- | chrome/browser/autofill/credit_card.cc | 9 | ||||
-rw-r--r-- | chrome/browser/autofill/credit_card.h | 2 | ||||
-rw-r--r-- | chrome/browser/autofill/form_structure.cc | 41 | ||||
-rw-r--r-- | chrome/browser/autofill/form_structure.h | 8 |
7 files changed, 519 insertions, 206 deletions
diff --git a/chrome/browser/autofill/autofill_manager.cc b/chrome/browser/autofill/autofill_manager.cc index 6754c2a..15b5ba8 100644 --- a/chrome/browser/autofill/autofill_manager.cc +++ b/chrome/browser/autofill/autofill_manager.cc @@ -6,7 +6,8 @@ #include <string> -#include "base/command_line.h" +#include "base/basictypes.h" +#include "base/string16.h" #include "chrome/browser/autofill/autofill_dialog.h" #include "chrome/browser/autofill/autofill_infobar_delegate.h" #include "chrome/browser/autofill/form_structure.h" @@ -35,6 +36,8 @@ const int kAutoFillPhoneNumberPrefixCount = 3; const int kAutoFillPhoneNumberSuffixOffset = 3; const int kAutoFillPhoneNumberSuffixCount = 4; +const string16::value_type kLabelSeparator[] = {';',' ',0}; + } // namespace // TODO(jhawkins): Maybe this should be in a grd file? @@ -130,37 +133,48 @@ bool AutoFillManager::GetAutoFillSuggestions(int query_id, personal_data_->credit_cards().empty()) return false; - AutoFillField* form_field = NULL; - for (std::vector<FormStructure*>::iterator form = form_structures_.begin(); - form != form_structures_.end(); ++form) { + // Loops through the cached FormStructures looking for the FormStructure that + // contains |field| and the associated AutoFillFieldType. + FormStructure* form = NULL; + AutoFillField* autofill_field = NULL; + for (std::vector<FormStructure*>::iterator form_iter = + form_structures_.begin(); + form_iter != form_structures_.end(); ++form_iter) { + form = *form_iter; + // Don't send suggestions for forms that aren't auto-fillable. - if (!(*form)->IsAutoFillable()) + if (!form->IsAutoFillable()) continue; - for (std::vector<AutoFillField*>::const_iterator iter = (*form)->begin(); - iter != (*form)->end(); ++iter) { + for (std::vector<AutoFillField*>::const_iterator iter = form->begin(); + iter != form->end(); ++iter) { // The field list is terminated with a NULL AutoFillField, so don't try to // dereference it. if (!*iter) break; if ((**iter) == field) { - form_field = *iter; + autofill_field = *iter; break; } } } - if (form_field == NULL) + if (autofill_field == NULL) return false; std::vector<string16> values; std::vector<string16> labels; + AutoFillType type(autofill_field->type()); - if (AutoFillType(form_field->type()).group() != AutoFillType::CREDIT_CARD) - GetProfileSuggestions(field, form_field->type(), &values, &labels); + if (type.group() == AutoFillType::CREDIT_CARD) + GetCreditCardSuggestions(form, field, type, &values, &labels); + else if (type.group() == AutoFillType::ADDRESS_BILLING) + GetBillingProfileSuggestions(field, type, &values, &labels); else - GetCreditCardSuggestions(field, form_field->type(), &values, &labels); + GetProfileSuggestions(form, field, type, &values, &labels); + + DCHECK_EQ(values.size(), labels.size()); // No suggestions. if (values.empty()) @@ -170,6 +184,7 @@ bool AutoFillManager::GetAutoFillSuggestions(int query_id, return true; } +// TODO(jhawkins): Remove the |value| parameter. bool AutoFillManager::FillAutoFillFormData(int query_id, const FormData& form, const string16& value, @@ -183,35 +198,66 @@ bool AutoFillManager::FillAutoFillFormData(int query_id, const std::vector<AutoFillProfile*>& profiles = personal_data_->profiles(); const std::vector<CreditCard*>& credit_cards = personal_data_->credit_cards(); + + // No data to return if the profiles are empty. if (profiles.empty() && credit_cards.empty()) return false; - // Find profile that matches the |value| and |label| in question. + // Find the FormStructure that corresponds to |form|. + FormData result = form; + FormStructure* form_structure = NULL; + for (std::vector<FormStructure*>::const_iterator iter = + form_structures_.begin(); + iter != form_structures_.end(); ++iter) { + if (**iter == form) { + form_structure = *iter; + break; + } + } + + if (!form_structure) + return false; + + // No data to return if there are no auto-fillable fields. + if (!form_structure->autofill_count()) + return false; + + // |cc_digits| will contain the last four digits of a credit card number only + // if the form has billing fields. + string16 profile_label = label; + string16 cc_digits; + + // If the form has billing fields, |label| will contain at least one "; " + // followed by the last four digits of a credit card number. + if (form_structure->HasBillingFields()) { + // We must search for the last "; " as it's possible the profile label + // proper can contain this sequence of characters. + size_t index = label.find_last_of(kLabelSeparator); + if (index != string16::npos) { + profile_label = label.substr(0, index - 1); + + size_t cc_index = index + 1; + cc_digits = label.substr(cc_index); + } + } + + // Find the profile that matches the |profile_label|. const AutoFillProfile* profile = NULL; for (std::vector<AutoFillProfile*>::const_iterator iter = profiles.begin(); iter != profiles.end(); ++iter) { - if ((*iter)->Label() != label) - continue; - - FieldTypeSet field_types; - (*iter)->GetPossibleFieldTypes(value, &field_types); - for (FieldTypeSet::const_iterator type = field_types.begin(); - type != field_types.end(); ++type) { - if ((*iter)->GetFieldText(AutoFillType(*type)) == value) { - profile = *iter; - break; - } + if ((*iter)->Label() == profile_label) { + profile = *iter; } } - // Only look for credit card info if we're not filling profile. + // Don't look for a matching credit card if we fully-matched the profile using + // the entire label. const CreditCard* credit_card = NULL; - if (!profile) { + if (!cc_digits.empty()) { + // Find the credit card that matches the |cc_label|. for (std::vector<CreditCard*>::const_iterator iter = credit_cards.begin(); iter != credit_cards.end(); ++iter) { - // Labels are unique, so we only need to verify the label for the credit - // card. - if ((*iter)->Label() == label) { + if ((*iter)->LastFourDigits() == cc_digits) { credit_card = *iter; break; } @@ -221,56 +267,44 @@ bool AutoFillManager::FillAutoFillFormData(int query_id, if (!profile && !credit_card) return false; - // We fill either the profile or the credit card, not both. - DCHECK((profile && !credit_card) || (!profile && credit_card)); + // The list of fields in |form_structure| and |result.fields| often match + // directly and we can fill these corresponding fields; however, when the + // |form_structure| and |result.fields| do not match directly we search + // ahead in the |form_structure| for the matching field. + // See unit tests: AutoFillManagerTest.FormChangesRemoveField and + // AutoFillManagerTest.FormChangesAddField for usage. + for (size_t i = 0, j = 0; + i < form_structure->field_count() && j < result.fields.size(); + j++) { + size_t k = i; + + // Search forward in the |form_structure| for a corresponding field. + while (k < form_structure->field_count() && + *form_structure->field(k) != result.fields[j]) { + k++; + } - FormData result = form; - for (std::vector<FormStructure*>::const_iterator iter = - form_structures_.begin(); - iter != form_structures_.end(); ++iter) { - const FormStructure* form_structure = *iter; - if (*form_structure != form) + // If we've found a match then fill the |result| field with the found + // field in the |form_structure|. + if (k >= form_structure->field_count()) continue; - // The list of fields in |form_structure| and |result.fields| often match - // directly and we can fill these corresponding fields; however, when the - // |form_structure| and |result.fields| do not match directly we search - // ahead in the |form_structure| for the matching field. - // See unit tests: AutoFillManagerTest.FormChangesRemoveField and - // AutoFillManagerTest.FormChangesAddField for usage. - for (size_t i = 0, j = 0; - i < form_structure->field_count() && j < result.fields.size(); - j++) { - size_t k = i; - - // Search forward in the |form_structure| for a corresponding field. - while (k < form_structure->field_count() && - *form_structure->field(k) != result.fields[j]) { - k++; - } - - // If we've found a match then fill the |result| field with the found - // field in the |form_structure|. - if (k >= form_structure->field_count()) - continue; - - const AutoFillField* field = form_structure->field(k); - AutoFillType autofill_type(field->type()); - if (credit_card && - autofill_type.group() == AutoFillType::CREDIT_CARD) { - result.fields[i].set_value( - credit_card->GetFieldText(autofill_type)); - } else if (credit_card && - autofill_type.group() == AutoFillType::ADDRESS_BILLING) { - FillBillingFormField(credit_card, autofill_type, &result.fields[j]); - } else if (profile) { - FillFormField(profile, autofill_type, &result.fields[j]); - } - - // We found a matching field in the |form_structure| so we - // proceed to the next |result| field, and the next |form_structure|. - ++i; + const AutoFillField* field = form_structure->field(k); + AutoFillType autofill_type(field->type()); + if (credit_card && + autofill_type.group() == AutoFillType::CREDIT_CARD) { + result.fields[i].set_value( + credit_card->GetFieldText(autofill_type)); + } else if (credit_card && + autofill_type.group() == AutoFillType::ADDRESS_BILLING) { + FillBillingFormField(credit_card, autofill_type, &result.fields[j]); + } else if (profile) { + FillFormField(profile, autofill_type, &result.fields[j]); } + + // We found a matching field in the |form_structure| so we + // proceed to the next |result| field, and the next |form_structure|. + ++i; } host->AutoFillFormDataFilled(query_id, result); @@ -400,8 +434,9 @@ AutoFillManager::AutoFillManager(TabContents* tab_contents, DCHECK(tab_contents); } -void AutoFillManager::GetProfileSuggestions(const FormField& field, - AutoFillFieldType type, +void AutoFillManager::GetProfileSuggestions(FormStructure* form, + const FormField& field, + AutoFillType type, std::vector<string16>* values, std::vector<string16>* labels) { const std::vector<AutoFillProfile*>& profiles = personal_data_->profiles(); @@ -410,37 +445,110 @@ void AutoFillManager::GetProfileSuggestions(const FormField& field, AutoFillProfile* profile = *iter; // The value of the stored data for this field type in the |profile|. - string16 profile_field_value = profile->GetFieldText(AutoFillType(type)); + string16 profile_field_value = profile->GetFieldText(type); if (!profile_field_value.empty() && StartsWith(profile_field_value, field.value(), false)) { - values->push_back(profile_field_value); - labels->push_back(profile->Label()); + if (!form->HasBillingFields()) { + values->push_back(profile_field_value); + labels->push_back(profile->Label()); + } else { + for (std::vector<CreditCard*>::const_iterator cc = + personal_data_->credit_cards().begin(); + cc != personal_data_->credit_cards().end(); ++cc) { + values->push_back(profile_field_value); + + string16 label = profile->Label() + kLabelSeparator + + (*cc)->LastFourDigits(); + labels->push_back(label); + } + } + } + } +} + +void AutoFillManager::GetBillingProfileSuggestions( + const FormField& field, + AutoFillType type, + std::vector<string16>* values, + std::vector<string16>* labels) { + std::vector<CreditCard*> matching_creditcards; + std::vector<AutoFillProfile*> matching_profiles; + std::vector<string16> cc_values; + std::vector<string16> cc_labels; + + for (std::vector<CreditCard*>::const_iterator cc = + personal_data_->credit_cards().begin(); + cc != personal_data_->credit_cards().end(); ++cc) { + string16 label = (*cc)->billing_address(); + AutoFillProfile* billing_profile = NULL; + + // The value of the stored data for this field type in the |profile|. + string16 profile_field_value; + + for (std::vector<AutoFillProfile*>::const_iterator iter = + personal_data_->profiles().begin(); + iter != personal_data_->profiles().end(); ++iter) { + AutoFillProfile* profile = *iter; + + // This assumes that labels are unique. + if (profile->Label() == label && + !profile->GetFieldText(type).empty() && + StartsWith(profile->GetFieldText(type), field.value(), false)) { + billing_profile = profile; + break; + } + } + + if (!billing_profile) + continue; + + for (std::vector<AutoFillProfile*>::const_iterator iter = + personal_data_->profiles().begin(); + iter != personal_data_->profiles().end(); ++iter) { + values->push_back(billing_profile->GetFieldText(type)); + + string16 label = (*iter)->Label() + + ASCIIToUTF16("; ") + + (*cc)->LastFourDigits(); + labels->push_back(label); } } } -void AutoFillManager::GetCreditCardSuggestions(const FormField& field, - AutoFillFieldType type, +void AutoFillManager::GetCreditCardSuggestions(FormStructure* form, + const FormField& field, + AutoFillType type, std::vector<string16>* values, std::vector<string16>* labels) { - // TODO(jhawkins): Only return suggestions for the credit card number until - // the AutoFill dropdown is redesigned to show a credit card icon. - if (type != CREDIT_CARD_NUMBER) - return; - - const std::vector<CreditCard*>& credit_cards = personal_data_->credit_cards(); - for (std::vector<CreditCard*>::const_iterator iter = credit_cards.begin(); - iter != credit_cards.end(); ++iter) { + for (std::vector<CreditCard*>::const_iterator iter = + personal_data_->credit_cards().begin(); + iter != personal_data_->credit_cards().end(); ++iter) { CreditCard* credit_card = *iter; // The value of the stored data for this field type in the |credit_card|. string16 creditcard_field_value = - credit_card->GetFieldText(AutoFillType(type)); + credit_card->GetFieldText(type); if (!creditcard_field_value.empty() && StartsWith(creditcard_field_value, field.value(), false)) { - values->push_back(credit_card->ObfuscatedNumber()); - labels->push_back(credit_card->Label()); + if (type.field_type() == CREDIT_CARD_NUMBER) + creditcard_field_value = credit_card->ObfuscatedNumber(); + + if (!form->HasNonBillingFields()) { + values->push_back(creditcard_field_value); + labels->push_back(credit_card->Label()); + } else { + for (std::vector<AutoFillProfile*>::const_iterator iter = + personal_data_->profiles().begin(); + iter != personal_data_->profiles().end(); ++iter) { + values->push_back(creditcard_field_value); + + string16 label = (*iter)->Label() + + ASCIIToUTF16("; ") + + credit_card->LastFourDigits(); + labels->push_back(label); + } + } } } } diff --git a/chrome/browser/autofill/autofill_manager.h b/chrome/browser/autofill/autofill_manager.h index 31c03d2..46ee0f5 100644 --- a/chrome/browser/autofill/autofill_manager.h +++ b/chrome/browser/autofill/autofill_manager.h @@ -100,23 +100,35 @@ class AutoFillManager : public RenderViewHostDelegate::AutoFill, private: // Returns a list of values from the stored profiles that match |type| and the - // value of |field| and returns the labels of the matching profiles. - void GetProfileSuggestions(const webkit_glue::FormField& field, - AutoFillFieldType type, + // value of |field| and returns the labels of the matching profiles. |labels| + // is filled with the Profile label and possibly the last four digits of a + // corresponding credit card: 'Home; 1258' - Home is the Profile label and + // 1258 is the last four digits of the credit card. + void GetProfileSuggestions(FormStructure* form, + const webkit_glue::FormField& field, + AutoFillType type, std::vector<string16>* values, std::vector<string16>* labels); + // Same as GetProfileSuggestions, but the list of stored profiles is limited + // to the linked billing addresses from the list of credit cards. + void GetBillingProfileSuggestions(const webkit_glue::FormField& field, + AutoFillType type, + std::vector<string16>* values, + std::vector<string16>* labels); + // Returns a list of values from the stored credit cards that match |type| and // the value of |field| and returns the labels of the matching credit cards. - void GetCreditCardSuggestions(const webkit_glue::FormField& field, - AutoFillFieldType type, + void GetCreditCardSuggestions(FormStructure* form, + const webkit_glue::FormField& field, + AutoFillType type, std::vector<string16>* values, std::vector<string16>* labels); // Set |field| argument's value based on |type| and contents of the // |credit_card|. The |type| field is expected to have main group type of // ADDRESS_BILLING. The address information is retrieved from the billing - // profile asscociated with the |credit_card|, if there is one set. + // profile associated with the |credit_card|, if there is one set. void FillBillingFormField(const CreditCard* credit_card, AutoFillType type, webkit_glue::FormField* field); diff --git a/chrome/browser/autofill/autofill_manager_unittest.cc b/chrome/browser/autofill/autofill_manager_unittest.cc index 33ac34c..2435b93 100644 --- a/chrome/browser/autofill/autofill_manager_unittest.cc +++ b/chrome/browser/autofill/autofill_manager_unittest.cc @@ -49,6 +49,16 @@ class TestPersonalDataManager : public PersonalDataManager { return NULL; } + void AddSemicolonAutoFillProfile() { + AutoFillProfile* profile = new AutoFillProfile; + autofill_unittest::SetProfileInfo(profile, "Home; 8765", "Joe", "", "Ely", + "flatlander@gmail.com", "MCA", + "916 16th St.", "Apt. 6", "Lubbock", + "Texas", "79401", "USA", + "12345678901", ""); + web_profiles_->push_back(profile); + } + private: void CreateTestAutoFillProfiles(ScopedVector<AutoFillProfile>* profiles) { AutoFillProfile* profile = new AutoFillProfile; @@ -105,6 +115,10 @@ class TestAutoFillManager : public AutoFillManager { return test_personal_data_->GetLabeledProfile(label); } + void AddSemicolonAutoFillProfile() { + test_personal_data_->AddSemicolonAutoFillProfile(); + } + private: scoped_refptr<TestPersonalDataManager> test_personal_data_; @@ -149,14 +163,6 @@ void CreateTestFormData(FormData* form) { form->fields.push_back(field); CreateTestFormField("Email", "email", "", "text", &field); form->fields.push_back(field); - CreateTestFormField("Name on Card", "nameoncard", "", "text", &field); - form->fields.push_back(field); - CreateTestFormField("Card Number", "cardnumber", "", "text", &field); - form->fields.push_back(field); - CreateTestFormField("Expiration Date", "ccmonth", "", "text", &field); - form->fields.push_back(field); - CreateTestFormField("", "ccyear", "", "text", &field); - form->fields.push_back(field); } void CreateTestFormDataBilling(FormData* form) { @@ -243,6 +249,10 @@ class AutoFillManagerTest : public RenderViewHostTestHarness { return true; } + void AddSemicolonAutoFillProfile() { + autofill_manager_->AddSemicolonAutoFillProfile(); + } + protected: scoped_ptr<TestAutoFillManager> autofill_manager_; @@ -312,7 +322,7 @@ TEST_F(AutoFillManagerTest, GetProfileSuggestionsMatchCharacter) { TEST_F(AutoFillManagerTest, GetCreditCardSuggestionsEmptyValue) { FormData form; - CreateTestFormData(&form); + CreateTestFormDataBilling(&form); // Set up our FormStructures. std::vector<FormData> forms; @@ -333,17 +343,25 @@ TEST_F(AutoFillManagerTest, GetCreditCardSuggestionsEmptyValue) { std::vector<string16> labels; EXPECT_TRUE(GetAutoFillSuggestionsMessage(&page_id, &values, &labels)); EXPECT_EQ(kPageID, page_id); - ASSERT_EQ(2U, values.size()); + ASSERT_EQ(6U, values.size()); EXPECT_EQ(ASCIIToUTF16("************3456"), values[0]); - EXPECT_EQ(ASCIIToUTF16("************8765"), values[1]); - ASSERT_EQ(2U, labels.size()); - EXPECT_EQ(ASCIIToUTF16("First"), labels[0]); - EXPECT_EQ(ASCIIToUTF16("Second"), labels[1]); + EXPECT_EQ(ASCIIToUTF16("************3456"), values[1]); + EXPECT_EQ(ASCIIToUTF16("************3456"), values[2]); + EXPECT_EQ(ASCIIToUTF16("************8765"), values[3]); + EXPECT_EQ(ASCIIToUTF16("************8765"), values[4]); + EXPECT_EQ(ASCIIToUTF16("************8765"), values[5]); + ASSERT_EQ(6U, labels.size()); + EXPECT_EQ(ASCIIToUTF16("Home; 3456"), labels[0]); + EXPECT_EQ(ASCIIToUTF16("Work; 3456"), labels[1]); + EXPECT_EQ(ASCIIToUTF16("Empty; 3456"), labels[2]); + EXPECT_EQ(ASCIIToUTF16("Home; 8765"), labels[3]); + EXPECT_EQ(ASCIIToUTF16("Work; 8765"), labels[4]); + EXPECT_EQ(ASCIIToUTF16("Empty; 8765"), labels[5]); } TEST_F(AutoFillManagerTest, GetCreditCardSuggestionsMatchCharacter) { FormData form; - CreateTestFormData(&form); + CreateTestFormDataBilling(&form); // Set up our FormStructures. std::vector<FormData> forms; @@ -364,15 +382,19 @@ TEST_F(AutoFillManagerTest, GetCreditCardSuggestionsMatchCharacter) { std::vector<string16> labels; EXPECT_TRUE(GetAutoFillSuggestionsMessage(&page_id, &values, &labels)); EXPECT_EQ(kPageID, page_id); - ASSERT_EQ(1U, values.size()); + ASSERT_EQ(3U, values.size()); EXPECT_EQ(ASCIIToUTF16("************3456"), values[0]); - ASSERT_EQ(1U, labels.size()); - EXPECT_EQ(ASCIIToUTF16("First"), labels[0]); + EXPECT_EQ(ASCIIToUTF16("************3456"), values[1]); + EXPECT_EQ(ASCIIToUTF16("************3456"), values[2]); + ASSERT_EQ(3U, labels.size()); + EXPECT_EQ(ASCIIToUTF16("Home; 3456"), labels[0]); + EXPECT_EQ(ASCIIToUTF16("Work; 3456"), labels[1]); + EXPECT_EQ(ASCIIToUTF16("Empty; 3456"), labels[2]); } TEST_F(AutoFillManagerTest, GetCreditCardSuggestionsNonCCNumber) { FormData form; - CreateTestFormData(&form); + CreateTestFormDataBilling(&form); // Set up our FormStructures. std::vector<FormData> forms; @@ -384,24 +406,145 @@ TEST_F(AutoFillManagerTest, GetCreditCardSuggestionsNonCCNumber) { const int kPageID = 1; webkit_glue::FormField field; + CreateTestFormField("Name on Card", "nameoncard", "", "text", &field); + EXPECT_TRUE(autofill_manager_->GetAutoFillSuggestions(kPageID, field)); + + // Test that we sent the right message to the renderer. int page_id = 0; std::vector<string16> values; std::vector<string16> labels; - CreateTestFormField("Name on Card", "nameoncard", "", "text", &field); - EXPECT_FALSE(autofill_manager_->GetAutoFillSuggestions(kPageID, field)); - EXPECT_FALSE(GetAutoFillSuggestionsMessage(&page_id, &values, &labels)); + EXPECT_TRUE(GetAutoFillSuggestionsMessage(&page_id, &values, &labels)); + EXPECT_EQ(kPageID, page_id); + ASSERT_EQ(6U, values.size()); + EXPECT_EQ(ASCIIToUTF16("Elvis Presley"), values[0]); + EXPECT_EQ(ASCIIToUTF16("Elvis Presley"), values[1]); + EXPECT_EQ(ASCIIToUTF16("Elvis Presley"), values[2]); + EXPECT_EQ(ASCIIToUTF16("Buddy Holly"), values[3]); + EXPECT_EQ(ASCIIToUTF16("Buddy Holly"), values[4]); + EXPECT_EQ(ASCIIToUTF16("Buddy Holly"), values[5]); + ASSERT_EQ(6U, labels.size()); + EXPECT_EQ(ASCIIToUTF16("Home; 3456"), labels[0]); + EXPECT_EQ(ASCIIToUTF16("Work; 3456"), labels[1]); + EXPECT_EQ(ASCIIToUTF16("Empty; 3456"), labels[2]); + EXPECT_EQ(ASCIIToUTF16("Home; 8765"), labels[3]); + EXPECT_EQ(ASCIIToUTF16("Work; 8765"), labels[4]); + EXPECT_EQ(ASCIIToUTF16("Empty; 8765"), labels[5]); +} - CreateTestFormField("Expiration Date", "ccmonth", "", "text", &field); - EXPECT_FALSE(autofill_manager_->GetAutoFillSuggestions(kPageID, field)); - EXPECT_FALSE(GetAutoFillSuggestionsMessage(&page_id, &values, &labels)); +TEST_F(AutoFillManagerTest, GetCreditCardSuggestionsSemicolon) { + AddSemicolonAutoFillProfile(); - CreateTestFormField("", "ccyear", "", "text", &field); - EXPECT_FALSE(autofill_manager_->GetAutoFillSuggestions(kPageID, field)); - EXPECT_FALSE(GetAutoFillSuggestionsMessage(&page_id, &values, &labels)); + FormData form; + CreateTestFormDataBilling(&form); + + // Set up our FormStructures. + std::vector<FormData> forms; + forms.push_back(form); + autofill_manager_->FormsSeen(forms); + + // The page ID sent to the AutoFillManager from the RenderView, used to send + // an IPC message back to the renderer. + const int kPageID = 1; + + webkit_glue::FormField field; + CreateTestFormField("Name on Card", "nameoncard", "", "text", &field); + EXPECT_TRUE(autofill_manager_->GetAutoFillSuggestions(kPageID, field)); + + // Test that we sent the right message to the renderer. + int page_id = 0; + std::vector<string16> values; + std::vector<string16> labels; + EXPECT_TRUE(GetAutoFillSuggestionsMessage(&page_id, &values, &labels)); + EXPECT_EQ(kPageID, page_id); + ASSERT_EQ(8U, values.size()); + EXPECT_EQ(ASCIIToUTF16("Elvis Presley"), values[0]); + EXPECT_EQ(ASCIIToUTF16("Elvis Presley"), values[1]); + EXPECT_EQ(ASCIIToUTF16("Elvis Presley"), values[2]); + EXPECT_EQ(ASCIIToUTF16("Elvis Presley"), values[3]); + EXPECT_EQ(ASCIIToUTF16("Buddy Holly"), values[4]); + EXPECT_EQ(ASCIIToUTF16("Buddy Holly"), values[5]); + EXPECT_EQ(ASCIIToUTF16("Buddy Holly"), values[6]); + EXPECT_EQ(ASCIIToUTF16("Buddy Holly"), values[7]); + ASSERT_EQ(8U, labels.size()); + EXPECT_EQ(ASCIIToUTF16("Home; 3456"), labels[0]); + EXPECT_EQ(ASCIIToUTF16("Work; 3456"), labels[1]); + EXPECT_EQ(ASCIIToUTF16("Empty; 3456"), labels[2]); + EXPECT_EQ(ASCIIToUTF16("Home; 8765; 3456"), labels[3]); + EXPECT_EQ(ASCIIToUTF16("Home; 8765"), labels[4]); + EXPECT_EQ(ASCIIToUTF16("Work; 8765"), labels[5]); + EXPECT_EQ(ASCIIToUTF16("Empty; 8765"), labels[6]); + EXPECT_EQ(ASCIIToUTF16("Home; 8765; 8765"), labels[7]); } TEST_F(AutoFillManagerTest, FillCreditCardForm) { FormData form; + CreateTestFormDataBilling(&form); + + // Set up our FormStructures. + std::vector<FormData> forms; + forms.push_back(form); + autofill_manager_->FormsSeen(forms); + + // The page ID sent to the AutoFillManager from the RenderView, used to send + // an IPC message back to the renderer. + const int kPageID = 1; + EXPECT_TRUE( + autofill_manager_->FillAutoFillFormData(kPageID, + form, + string16(), + ASCIIToUTF16("Home; 3456"))); + + int page_id = 0; + FormData results; + EXPECT_TRUE(GetAutoFillFormDataFilledMessage(&page_id, &results)); + EXPECT_EQ(ASCIIToUTF16("MyForm"), results.name); + EXPECT_EQ(ASCIIToUTF16("POST"), results.method); + EXPECT_EQ(GURL("http://myform.com/form.html"), results.origin); + EXPECT_EQ(GURL("http://myform.com/submit.html"), results.action); + ASSERT_EQ(15U, results.fields.size()); + + webkit_glue::FormField field; + CreateTestFormField("First Name", "firstname", "Elvis", "text", &field); + EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[0])); + CreateTestFormField("Middle Name", "middlename", "Aaron", "text", &field); + EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[1])); + CreateTestFormField("Last Name", "lastname", "Presley", "text", &field); + EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[2])); + CreateTestFormField("Address Line 1", "billingAddr1", + "3734 Elvis Presley Blvd.", "text", &field); + EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[3])); + CreateTestFormField( + "Address Line 2", "billingAddr2", "Apt. 10", "text", &field); + EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[4])); + CreateTestFormField("City", "billingCity", "Memphis", "text", &field); + EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[5])); + CreateTestFormField("State", "billingState", "Tennessee", "text", &field); + EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[6])); + CreateTestFormField("Postal Code", "billingZipcode", "38116", "text", &field); + EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[7])); + CreateTestFormField("Country", "billingCountry", "USA", "text", &field); + EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[8])); + CreateTestFormField( + "Phone Number", "phonenumber", "12345678901", "text", &field); + EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[9])); + CreateTestFormField("Email", "email", "theking@gmail.com", "text", &field); + EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[10])); + CreateTestFormField( + "Name on Card", "nameoncard", "Elvis Presley", "text", &field); + EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[11])); + CreateTestFormField( + "Card Number", "cardnumber", "1234567890123456", "text", &field); + EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[12])); + CreateTestFormField("Expiration Date", "ccmonth", "04", "text", &field); + EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[13])); + CreateTestFormField("", "ccyear", "2012", "text", &field); + EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[14])); +} + +TEST_F(AutoFillManagerTest, FillNonBillingFormSemicolon) { + AddSemicolonAutoFillProfile(); + + FormData form; CreateTestFormData(&form); // Set up our FormStructures. @@ -415,8 +558,62 @@ TEST_F(AutoFillManagerTest, FillCreditCardForm) { EXPECT_TRUE( autofill_manager_->FillAutoFillFormData(kPageID, form, - ASCIIToUTF16("cardnumber"), - ASCIIToUTF16("First"))); + string16(), + ASCIIToUTF16("Home; 8765"))); + + int page_id = 0; + FormData results; + EXPECT_TRUE(GetAutoFillFormDataFilledMessage(&page_id, &results)); + EXPECT_EQ(ASCIIToUTF16("MyForm"), results.name); + EXPECT_EQ(ASCIIToUTF16("POST"), results.method); + EXPECT_EQ(GURL("http://myform.com/form.html"), results.origin); + EXPECT_EQ(GURL("http://myform.com/submit.html"), results.action); + ASSERT_EQ(11U, results.fields.size()); + + webkit_glue::FormField field; + CreateTestFormField("First Name", "firstname", "Joe", "text", &field); + EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[0])); + CreateTestFormField("Middle Name", "middlename", "", "text", &field); + EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[1])); + CreateTestFormField("Last Name", "lastname", "Ely", "text", &field); + EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[2])); + CreateTestFormField( + "Address Line 1", "addr1", "916 16th St.", "text", &field); + EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[3])); + CreateTestFormField( + "Address Line 2", "addr2", "Apt. 6", "text", &field); + EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[4])); + CreateTestFormField("City", "city", "Lubbock", "text", &field); + EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[5])); + CreateTestFormField("State", "state", "Texas", "text", &field); + EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[6])); + CreateTestFormField("Postal Code", "zipcode", "79401", "text", &field); + EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[7])); + CreateTestFormField("Country", "country", "USA", "text", &field); + EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[8])); + CreateTestFormField( + "Phone Number", "phonenumber", "12345678901", "text", &field); + EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[9])); + CreateTestFormField("Email", "email", "flatlander@gmail.com", "text", &field); + EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[10])); +} + +TEST_F(AutoFillManagerTest, FillBillFormSemicolon) { + AddSemicolonAutoFillProfile(); + + FormData form; + CreateTestFormDataBilling(&form); + + // Set up our FormStructures. + std::vector<FormData> forms; + forms.push_back(form); + autofill_manager_->FormsSeen(forms); + + // The page ID sent to the AutoFillManager from the RenderView, used to send + // an IPC message back to the renderer. + const int kPageID = 1; + EXPECT_TRUE(autofill_manager_->FillAutoFillFormData( + kPageID, form, string16(), ASCIIToUTF16("Home; 8765; 3456"))); int page_id = 0; FormData results; @@ -428,27 +625,30 @@ TEST_F(AutoFillManagerTest, FillCreditCardForm) { ASSERT_EQ(15U, results.fields.size()); webkit_glue::FormField field; - CreateTestFormField("First Name", "firstname", "", "text", &field); + CreateTestFormField("First Name", "firstname", "Joe", "text", &field); EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[0])); CreateTestFormField("Middle Name", "middlename", "", "text", &field); EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[1])); - CreateTestFormField("Last Name", "lastname", "", "text", &field); + CreateTestFormField("Last Name", "lastname", "Ely", "text", &field); EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[2])); - CreateTestFormField("Address Line 1", "addr1", "", "text", &field); + CreateTestFormField("Address Line 1", "billingAddr1", + "3734 Elvis Presley Blvd.", "text", &field); EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[3])); - CreateTestFormField("Address Line 2", "addr2", "", "text", &field); + CreateTestFormField( + "Address Line 2", "billingAddr2", "Apt. 10", "text", &field); EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[4])); - CreateTestFormField("City", "city", "", "text", &field); + CreateTestFormField("City", "billingCity", "Memphis", "text", &field); EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[5])); - CreateTestFormField("State", "state", "", "text", &field); + CreateTestFormField("State", "billingState", "Tennessee", "text", &field); EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[6])); - CreateTestFormField("Postal Code", "zipcode", "", "text", &field); + CreateTestFormField("Postal Code", "billingZipcode", "38116", "text", &field); EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[7])); - CreateTestFormField("Country", "country", "", "text", &field); + CreateTestFormField("Country", "billingCountry", "USA", "text", &field); EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[8])); - CreateTestFormField("Phone Number", "phonenumber", "", "text", &field); + CreateTestFormField( + "Phone Number", "phonenumber", "12345678901", "text", &field); EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[9])); - CreateTestFormField("Email", "email", "", "text", &field); + CreateTestFormField("Email", "email", "flatlander@gmail.com", "text", &field); EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[10])); CreateTestFormField( "Name on Card", "nameoncard", "Elvis Presley", "text", &field); @@ -527,73 +727,6 @@ TEST_F(AutoFillManagerTest, FillPhoneNumberTest) { work_profile->SetInfo(phone_type, saved_phone); } -TEST_F(AutoFillManagerTest, FillCreditCardFormWithBilling) { - FormData form; - CreateTestFormDataBilling(&form); - - // Set up our FormStructures. - std::vector<FormData> forms; - forms.push_back(form); - autofill_manager_->FormsSeen(forms); - - // The page ID sent to the AutoFillManager from the RenderView, used to send - // an IPC message back to the renderer. - const int kPageID = 1; - EXPECT_TRUE( - autofill_manager_->FillAutoFillFormData(kPageID, - form, - ASCIIToUTF16("cardnumber"), - ASCIIToUTF16("First"))); - - int page_id = 0; - FormData results; - EXPECT_TRUE(GetAutoFillFormDataFilledMessage(&page_id, &results)); - EXPECT_EQ(ASCIIToUTF16("MyForm"), results.name); - EXPECT_EQ(ASCIIToUTF16("POST"), results.method); - EXPECT_EQ(GURL("http://myform.com/form.html"), results.origin); - EXPECT_EQ(GURL("http://myform.com/submit.html"), results.action); - ASSERT_EQ(15U, results.fields.size()); - - webkit_glue::FormField field; - CreateTestFormField("First Name", "firstname", "", "text", &field); - EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[0])); - CreateTestFormField("Middle Name", "middlename", "", "text", &field); - EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[1])); - CreateTestFormField("Last Name", "lastname", "", "text", &field); - EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[2])); - CreateTestFormField( - "Address Line 1", "billingAddr1", "3734 Elvis Presley Blvd.", "text", - &field); - EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[3])); - CreateTestFormField( - "Address Line 2", "billingAddr2", "Apt. 10", "text", &field); - EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[4])); - CreateTestFormField("City", "billingCity", "Memphis", "text", &field); - EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[5])); - CreateTestFormField("State", "billingState", "Tennessee", "text", &field); - EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[6])); - CreateTestFormField("Postal Code", "billingZipcode", "38116", "text", &field); - EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[7])); - CreateTestFormField("Country", "billingCountry", "USA", "text", &field); - EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[8])); - CreateTestFormField( - "Phone Number", "phonenumber", "", "text", &field); - EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[9])); - CreateTestFormField( - "Email", "email", "", "text", &field); - EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[10])); - CreateTestFormField( - "Name on Card", "nameoncard", "Elvis Presley", "text", &field); - EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[11])); - CreateTestFormField( - "Card Number", "cardnumber", "1234567890123456", "text", &field); - EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[12])); - CreateTestFormField("Expiration Date", "ccmonth", "04", "text", &field); - EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[13])); - CreateTestFormField("", "ccyear", "2012", "text", &field); - EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[14])); -} - TEST_F(AutoFillManagerTest, FormChangesRemoveField) { FormData form; form.name = ASCIIToUTF16("MyForm"); diff --git a/chrome/browser/autofill/credit_card.cc b/chrome/browser/autofill/credit_card.cc index 7f41b5e..f694cda 100644 --- a/chrome/browser/autofill/credit_card.cc +++ b/chrome/browser/autofill/credit_card.cc @@ -236,6 +236,15 @@ string16 CreditCard::PreviewSummary() const { return preview; } +string16 CreditCard::LastFourDigits() const { + static const size_t kNumLastDigits = 4; + + if (number().size() < kNumLastDigits) + return string16(); + + return number().substr(number().size() - kNumLastDigits, kNumLastDigits); +} + void CreditCard::operator=(const CreditCard& source) { number_ = source.number_; name_on_card_ = source.name_on_card_; diff --git a/chrome/browser/autofill/credit_card.h b/chrome/browser/autofill/credit_card.h index 50bfa3a..00e5f39 100644 --- a/chrome/browser/autofill/credit_card.h +++ b/chrome/browser/autofill/credit_card.h @@ -34,6 +34,8 @@ class CreditCard : public FormGroup { string16 ObfuscatedNumber() const; // Credit card preview summary, for example: ******1234, Exp: 01/2020 string16 PreviewSummary() const; + // The last four digits of the credit card number. + string16 LastFourDigits() const; const string16& billing_address() const { return billing_address_; } const string16& shipping_address() const { return shipping_address_; } diff --git a/chrome/browser/autofill/form_structure.cc b/chrome/browser/autofill/form_structure.cc index 75ea94b..1a6a973 100644 --- a/chrome/browser/autofill/form_structure.cc +++ b/chrome/browser/autofill/form_structure.cc @@ -245,6 +245,47 @@ bool FormStructure::HasAutoFillableValues() const { if (field && !field->IsEmpty() && field->IsFieldFillable()) return true; } + + return false; +} + +// TODO(jhawkins): Cache this result. +bool FormStructure::HasBillingFields() const { + for (std::vector<AutoFillField*>::const_iterator iter = begin(); + iter != end(); ++iter) { + if (!*iter) + return false; + + AutoFillField* field = *iter; + if (!field) + continue; + + AutoFillType type(field->type()); + if (type.group() == AutoFillType::ADDRESS_BILLING || + type.group() == AutoFillType::CREDIT_CARD) + return true; + } + + return false; +} + +// TODO(jhawkins): Cache this result. +bool FormStructure::HasNonBillingFields() const { + for (std::vector<AutoFillField*>::const_iterator iter = begin(); + iter != end(); ++iter) { + if (!*iter) + return false; + + AutoFillField* field = *iter; + if (!field) + continue; + + AutoFillType type(field->type()); + if (type.group() != AutoFillType::ADDRESS_BILLING && + type.group() != AutoFillType::CREDIT_CARD) + return true; + } + return false; } diff --git a/chrome/browser/autofill/form_structure.h b/chrome/browser/autofill/form_structure.h index a0740a0..6537bd4 100644 --- a/chrome/browser/autofill/form_structure.h +++ b/chrome/browser/autofill/form_structure.h @@ -66,6 +66,14 @@ class FormStructure { // is not empty. bool HasAutoFillableValues() const; + // Returns true if at least one of the form fields is a billing field, which + // includes billing address fields and credit card fields. + bool HasBillingFields() const; + + // Returns true if at least one of the form fields is a non-billing field, + // which includes billing address fields and credit card fields. + bool HasNonBillingFields() const; + // Resets |autofill_count_| and counts the number of auto-fillable fields. // This is used when we receive server data for form fields. At that time, // we may have more known fields than just the number of fields we matched |