summaryrefslogtreecommitdiffstats
path: root/chrome/browser/autofill
diff options
context:
space:
mode:
authordhollowa@chromium.org <dhollowa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-27 20:24:46 +0000
committerdhollowa@chromium.org <dhollowa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-27 20:24:46 +0000
commitef8a5fd2185944a7ee04d5e3e05d0fe78800912c (patch)
tree8d9aba1f357b29f030ba5c2d213afeff360c047e /chrome/browser/autofill
parent9d5804f68add5c1ab7bb48be7989d798f0988375 (diff)
downloadchromium_src-ef8a5fd2185944a7ee04d5e3e05d0fe78800912c.zip
chromium_src-ef8a5fd2185944a7ee04d5e3e05d0fe78800912c.tar.gz
chromium_src-ef8a5fd2185944a7ee04d5e3e05d0fe78800912c.tar.bz2
AutoFill <dl> label scraping and dynamic html in forms.
The mcphee.com site demonstrates two issues. One is label scraping for forms that use <dl> <dt> and <dd> elements to partition labels and input elements. The other is dynamic html where JavaScript is used to manipulate the form elements dynamically as the user proceeds through checkout. These changes address both. Label scraping is implemented for <dl> <dt> and <dd> elements. Form filling, both at the FormManager and AutoFillManager levels are now more robust in the face of changing form structure between the time of page load and form fill. This does not "fix" the mcphee page though, as we still do not fill the elements that are added to the page after the initial page load. BUG=44323 TEST=AutoFillManagerTest.FormChangesRemoveField, AutoFillManagerTest.FormChangesAddField, FormManagerTest.LabelsInferredFromTableCellNested, FormManagerTest.LabelsInferredFromDefinitionList, FormManagerTest.FillFormChangedFormDataFields, manual tests using mphee.html and mcphee2.html sample files attached to bug report and original bug steps using live mcphee checkout. Review URL: http://codereview.chromium.org/2240004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@48429 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/autofill')
-rw-r--r--chrome/browser/autofill/autofill_manager.cc32
-rw-r--r--chrome/browser/autofill/autofill_manager_unittest.cc116
2 files changed, 143 insertions, 5 deletions
diff --git a/chrome/browser/autofill/autofill_manager.cc b/chrome/browser/autofill/autofill_manager.cc
index f0b3988..33d6e6e 100644
--- a/chrome/browser/autofill/autofill_manager.cc
+++ b/chrome/browser/autofill/autofill_manager.cc
@@ -234,11 +234,29 @@ bool AutoFillManager::FillAutoFillFormData(int query_id,
if (*form_structure != form)
continue;
- DCHECK_EQ(form_structure->field_count(), result.fields.size());
+ // 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++;
+ }
- for (size_t i = 0; i < form_structure->field_count(); ++i) {
- const AutoFillField* field = form_structure->field(i);
+ // 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) {
@@ -246,10 +264,14 @@ bool AutoFillManager::FillAutoFillFormData(int query_id,
credit_card->GetFieldText(autofill_type));
} else if (credit_card &&
autofill_type.group() == AutoFillType::ADDRESS_BILLING) {
- FillBillingFormField(credit_card, autofill_type, &result.fields[i]);
+ FillBillingFormField(credit_card, autofill_type, &result.fields[j]);
} else if (profile) {
- FillFormField(profile, autofill_type, &result.fields[i]);
+ 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;
}
}
diff --git a/chrome/browser/autofill/autofill_manager_unittest.cc b/chrome/browser/autofill/autofill_manager_unittest.cc
index 9d018d5..a54b04d 100644
--- a/chrome/browser/autofill/autofill_manager_unittest.cc
+++ b/chrome/browser/autofill/autofill_manager_unittest.cc
@@ -596,4 +596,120 @@ TEST_F(AutoFillManagerTest, FillCreditCardFormWithBilling) {
EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[14]));
}
+TEST_F(AutoFillManagerTest, FormChangesRemoveField) {
+ 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");
+
+ webkit_glue::FormField field;
+ CreateTestFormField("First Name", "firstname", "", "text", &field);
+ form.fields.push_back(field);
+ CreateTestFormField("Middle Name", "middlename", "", "text", &field);
+ form.fields.push_back(field);
+ CreateTestFormField("Last Name", "lastname", "", "text", &field);
+ form.fields.push_back(field);
+ CreateTestFormField("Phone Number", "phonenumber", "", "text", &field);
+ form.fields.push_back(field);
+ CreateTestFormField("Email", "email", "", "text", &field);
+ form.fields.push_back(field);
+
+ // Set up our FormStructures.
+ std::vector<FormData> forms;
+ forms.push_back(form);
+ autofill_manager_->FormsSeen(forms);
+
+ // Now, after the call to |FormsSeen| we remove the phone number field before
+ // filling.
+ form.fields.erase(form.fields.begin() + 3);
+
+ // 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("Elvis"),
+ ASCIIToUTF16("Home")));
+
+ 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(4U, results.fields.size());
+
+ 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(
+ "Email", "email", "theking@gmail.com", "text", &field);
+ EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[3]));
+}
+
+TEST_F(AutoFillManagerTest, FormChangesAddField) {
+ 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");
+
+ webkit_glue::FormField field;
+ CreateTestFormField("First Name", "firstname", "", "text", &field);
+ form.fields.push_back(field);
+ CreateTestFormField("Middle Name", "middlename", "", "text", &field);
+ form.fields.push_back(field);
+ CreateTestFormField("Last Name", "lastname", "", "text", &field);
+ // Note: absent phone number. Adding this below.
+ form.fields.push_back(field);
+ CreateTestFormField("Email", "email", "", "text", &field);
+ form.fields.push_back(field);
+
+ // Set up our FormStructures.
+ std::vector<FormData> forms;
+ forms.push_back(form);
+ autofill_manager_->FormsSeen(forms);
+
+ // Now, after the call to |FormsSeen| we add the phone number field before
+ // filling.
+ CreateTestFormField("Phone Number", "phonenumber", "", "text", &field);
+ form.fields.insert(form.fields.begin() + 3, field);
+
+ // 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("Elvis"),
+ ASCIIToUTF16("Home")));
+
+ 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(5U, results.fields.size());
+
+ 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("Phone Number", "phonenumber", "", "text", &field);
+ EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[3]));
+ CreateTestFormField(
+ "Email", "email", "theking@gmail.com", "text", &field);
+ EXPECT_TRUE(field.StrictlyEqualsHack(results.fields[4]));
+}
+
} // namespace