summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/autofill/autofill_manager.cc32
-rw-r--r--chrome/browser/autofill/autofill_manager_unittest.cc116
-rw-r--r--chrome/renderer/form_manager.cc286
-rw-r--r--chrome/renderer/form_manager_unittest.cc159
4 files changed, 467 insertions, 126 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
diff --git a/chrome/renderer/form_manager.cc b/chrome/renderer/form_manager.cc
index 12b6353..83f6ed9 100644
--- a/chrome/renderer/form_manager.cc
+++ b/chrome/renderer/form_manager.cc
@@ -46,30 +46,28 @@ namespace {
const size_t kRequiredAutoFillFields = 3;
// This is a helper function for the FindChildText() function.
-// Returns the node value of the descendant or sibling of |node| that is a
-// non-empty text node. This is a faster alternative to |innerText()| for
+// Returns the aggregated values of the descendants or siblings of |node| that
+// are non-empty text nodes. This is a faster alternative to |innerText()| for
// performance critical operations. It does a full depth-first search so
-// can be used when the structure is not directly known. It does not aggregate
-// the text of multiple nodes, it just returns the value of the first found.
-// "Non-empty" in this case means non-empty after the whitespace has been
-// stripped.
+// can be used when the structure is not directly known. The text is
+// accumulated after the whitespace has been stripped.
string16 FindChildTextInner(const WebNode& node) {
string16 element_text;
if (node.isNull())
return element_text;
- element_text = node.nodeValue();
- TrimWhitespace(element_text, TRIM_ALL, &element_text);
- if (!element_text.empty())
- return element_text;
+ string16 node_text = node.nodeValue();
+ TrimWhitespace(node_text, TRIM_ALL, &node_text);
+ if (!node_text.empty())
+ element_text = node_text;
- element_text = FindChildTextInner(node.firstChild());
- if (!element_text.empty())
- return element_text;
+ string16 child_text = FindChildTextInner(node.firstChild());
+ if (!child_text.empty())
+ element_text = element_text + child_text;
- element_text = FindChildTextInner(node.nextSibling());
- if (!element_text.empty())
- return element_text;
+ string16 sibling_text = FindChildTextInner(node.nextSibling());
+ if (!sibling_text.empty())
+ element_text = element_text + sibling_text;
return element_text;
}
@@ -82,6 +80,132 @@ string16 FindChildText(const WebElement& element) {
return FindChildTextInner(child);
}
+// Helper for |InferLabelForElement()| that infers a label, if possible, from
+// a previous node of |element|.
+string16 InferLabelFromPrevious(
+ const WebFormControlElement& element) {
+ string16 inferred_label;
+ WebNode previous = element.previousSibling();
+ if (!previous.isNull()) {
+ // Eg. Some Text<input ...>
+ if (previous.isTextNode()) {
+ inferred_label = previous.nodeValue();
+ TrimWhitespace(inferred_label, TRIM_ALL, &inferred_label);
+ }
+
+ // If we didn't find text, check for previous paragraph.
+ // Eg. <p>Some Text</p><input ...>
+ // Note the lack of whitespace between <p> and <input> elements.
+ if (inferred_label.empty()) {
+ if (previous.isElementNode()) {
+ WebElement element = previous.to<WebElement>();
+ if (element.hasTagName("p")) {
+ inferred_label = FindChildText(element);
+ }
+ }
+ }
+
+ // If we didn't find paragraph, check for previous paragraph to this.
+ // Eg. <p>Some Text</p> <input ...>
+ // Note the whitespace between <p> and <input> elements.
+ if (inferred_label.empty()) {
+ previous = previous.previousSibling();
+ if (!previous.isNull() && previous.isElementNode()) {
+ WebElement element = previous.to<WebElement>();
+ if (element.hasTagName("p")) {
+ inferred_label = FindChildText(element);
+ }
+ }
+ }
+
+ // Look for text node prior to <img> tag.
+ // Eg. Some Text<img/><input ...>
+ if (inferred_label.empty()) {
+ while (inferred_label.empty() && !previous.isNull()) {
+ if (previous.isTextNode()) {
+ inferred_label = previous.nodeValue();
+ TrimWhitespace(inferred_label, TRIM_ALL, &inferred_label);
+ } else if (previous.isElementNode()) {
+ WebElement element = previous.to<WebElement>();
+ if (!element.hasTagName("img"))
+ break;
+ } else {
+ break;
+ }
+ previous = previous.previousSibling();
+ }
+ }
+ }
+
+ return inferred_label;
+}
+
+// Helper for |InferLabelForElement()| that infers a label, if possible, from
+// surrounding table structure.
+// Eg. <tr><td>Some Text</td><td><input ...></td></tr>
+// Eg. <tr><td><b>Some Text</b></td><td><b><input ...></b></td></tr>
+string16 InferLabelFromTable(
+ const WebFormControlElement& element) {
+ string16 inferred_label;
+ WebNode parent = element.parentNode();
+ while (!parent.isNull() && parent.isElementNode() &&
+ !parent.to<WebElement>().hasTagName("td"))
+ parent = parent.parentNode();
+
+ if (!parent.isNull() && parent.isElementNode()) {
+ WebElement element = parent.to<WebElement>();
+ if (element.hasTagName("td")) {
+ WebNode previous = parent.previousSibling();
+
+ // Skip by any intervening text nodes.
+ while (!previous.isNull() && previous.isTextNode())
+ previous = previous.previousSibling();
+
+ if (!previous.isNull() && previous.isElementNode()) {
+ element = previous.to<WebElement>();
+ if (element.hasTagName("td")) {
+ inferred_label = FindChildText(element);
+ }
+ }
+ }
+ }
+
+ return inferred_label;
+}
+
+// Helper for |InferLabelForElement()| that infers a label, if possible, from
+// a surrounding definition list.
+// Eg. <dl><dt>Some Text</dt><dd><input ...></dd></dl>
+// Eg. <dl><dt><b>Some Text</b></dt><dd><b><input ...></b></dd></dl>
+string16 InferLabelFromDefinitionList(
+ const WebFormControlElement& element) {
+ string16 inferred_label;
+ WebNode parent = element.parentNode();
+ while (!parent.isNull() && parent.isElementNode() &&
+ !parent.to<WebElement>().hasTagName("dd"))
+ parent = parent.parentNode();
+
+ if (!parent.isNull() && parent.isElementNode()) {
+ WebElement element = parent.to<WebElement>();
+ if (element.hasTagName("dd")) {
+ WebNode previous = parent.previousSibling();
+
+ // Skip by any intervening text nodes.
+ while (!previous.isNull() && previous.isTextNode())
+ previous = previous.previousSibling();
+
+ if (!previous.isNull() && previous.isElementNode()) {
+ element = previous.to<WebElement>();
+ if (element.hasTagName("dt")) {
+ inferred_label = FindChildText(element);
+ }
+ }
+ }
+ }
+
+ return inferred_label;
+}
+
} // namespace
FormManager::FormManager() {
@@ -427,62 +551,51 @@ bool FormManager::FillForm(const FormData& form) {
for (size_t i = 0, j = 0;
i < form_element->control_elements.size() && j < form.fields.size();
- ++i, ++j) {
+ ++i) {
// Once again, empty WebString != empty string16, so we have to explicitly
// check for this case.
if (form_element->control_elements[i].nameForAutofill().length() == 0 &&
form.fields[j].name().empty())
continue;
- // We assume that the intersection of the fields in
- // |form_element->control_elements| and |form.fields| is ordered, but it's
- // possible that one or the other sets may have more fields than the other,
- // so loop past non-matching fields in the set with more elements.
- while (form_element->control_elements[i].nameForAutofill() !=
- form.fields[j].name()) {
- if (form_element->control_elements.size() > form.fields.size()) {
- // We're at the end of the elements already.
- if (i + 1 == form_element->control_elements.size())
- break;
- ++i;
- } else if (form.fields.size() > form_element->control_elements.size()) {
- // We're at the end of the elements already.
- if (j + 1 == form.fields.size())
- break;
- ++j;
- } else {
- NOTREACHED();
- }
-
- continue;
+ size_t k = j;
+ while (k < form.fields.size() &&
+ form_element->control_elements[i].nameForAutofill() !=
+ form.fields[k].name()) {
+ k++;
}
+ if (k >= form.fields.size())
+ continue;
WebFormControlElement* element = &form_element->control_elements[i];
- // It's possible that nameForAutofill() is empty if the form control element
- // has no name or ID. In that case, iter->nameForAutofill() must also be
- // empty.
- if (form.fields[j].name().empty())
+ // It's possible that nameForAutofill() is empty if the form control
+ // element has no name or ID. In that case, iter->nameForAutofill() must
+ // also be empty.
+ if (form.fields[k].name().empty())
DCHECK(element->nameForAutofill().isEmpty());
else
- DCHECK_EQ(form.fields[j].name(), element->nameForAutofill());
+ DCHECK_EQ(form.fields[k].name(), element->nameForAutofill());
- if (!form.fields[j].value().empty() &&
+ if (!form.fields[k].value().empty() &&
element->formControlType() != WebString::fromUTF8("submit")) {
if (element->formControlType() == WebString::fromUTF8("text")) {
WebInputElement input_element = element->to<WebInputElement>();
// If the maxlength attribute contains a negative value, maxLength()
// returns the default maxlength value.
input_element.setValue(
- form.fields[j].value().substr(0, input_element.maxLength()));
+ form.fields[k].value().substr(0, input_element.maxLength()));
input_element.setAutofilled(true);
} else if (element->formControlType() ==
WebString::fromUTF8("select-one")) {
WebSelectElement select_element =
element->to<WebSelectElement>();
- select_element.setValue(form.fields[j].value());
+ select_element.setValue(form.fields[k].value());
}
}
+
+ // We found a matching form field so move on to the next.
+ ++j;
}
return true;
@@ -559,85 +672,18 @@ bool FormManager::FormElementToFormData(const WebFrame* frame,
// static
string16 FormManager::InferLabelForElement(
const WebFormControlElement& element) {
- string16 inferred_label;
- WebNode previous = element.previousSibling();
- if (!previous.isNull()) {
- if (previous.isTextNode()) {
- inferred_label = previous.nodeValue();
- TrimWhitespace(inferred_label, TRIM_ALL, &inferred_label);
- }
-
- // If we didn't find text, check for previous paragraph.
- // Eg. <p>Some Text</p><input ...>
- // Note the lack of whitespace between <p> and <input> elements.
- if (inferred_label.empty()) {
- if (previous.isElementNode()) {
- WebElement element = previous.to<WebElement>();
- if (element.hasTagName("p")) {
- inferred_label = FindChildText(element);
- }
- }
- }
+ string16 inferred_label = InferLabelFromPrevious(element);
- // If we didn't find paragraph, check for previous paragraph to this.
- // Eg. <p>Some Text</p> <input ...>
- // Note the whitespace between <p> and <input> elements.
- if (inferred_label.empty()) {
- previous = previous.previousSibling();
- if (!previous.isNull() && previous.isElementNode()) {
- WebElement element = previous.to<WebElement>();
- if (element.hasTagName("p")) {
- inferred_label = FindChildText(element);
- }
- }
- }
-
- // Look for text node prior to <img> tag.
- // Eg. Some Text<img/><input ...>
- if (inferred_label.empty()) {
- while (inferred_label.empty() && !previous.isNull()) {
- if (previous.isTextNode()) {
- inferred_label = previous.nodeValue();
- TrimWhitespace(inferred_label, TRIM_ALL, &inferred_label);
- } else if (previous.isElementNode()) {
- WebElement element = previous.to<WebElement>();
- if (!element.hasTagName("img"))
- break;
- } else {
- break;
- }
- previous = previous.previousSibling();
- }
- }
+ // If we didn't find a label, check for table cell case.
+ if (inferred_label.empty()) {
+ inferred_label = InferLabelFromTable(element);
}
- // If we didn't find paragraph, check for table cell case.
- // Eg. <tr><td>Some Text</td><td><input ...></td></tr>
- // Eg. <tr><td><b>Some Text</b></td><td><b><input ...></b></td></tr>
+ // If we didn't find a label, check for definition list case.
if (inferred_label.empty()) {
- WebNode parent = element.parentNode();
- while (!parent.isNull() && parent.isElementNode() &&
- !parent.to<WebElement>().hasTagName("td"))
- parent = parent.parentNode();
-
- if (!parent.isNull() && parent.isElementNode()) {
- WebElement element = parent.to<WebElement>();
- if (element.hasTagName("td")) {
- previous = parent.previousSibling();
-
- // Skip by any intervening text nodes.
- while (!previous.isNull() && previous.isTextNode())
- previous = previous.previousSibling();
-
- if (!previous.isNull() && previous.isElementNode()) {
- element = previous.to<WebElement>();
- if (element.hasTagName("td")) {
- inferred_label = FindChildText(element);
- }
- }
- }
- }
+ inferred_label = InferLabelFromDefinitionList(element);
}
return inferred_label;
}
+
diff --git a/chrome/renderer/form_manager_unittest.cc b/chrome/renderer/form_manager_unittest.cc
index 01dc52a..d5a8c39 100644
--- a/chrome/renderer/form_manager_unittest.cc
+++ b/chrome/renderer/form_manager_unittest.cc
@@ -842,7 +842,80 @@ TEST_F(FormManagerTest, LabelsInferredFromTableCellNested) {
const std::vector<FormField>& fields = form.fields;
ASSERT_EQ(3U, fields.size());
- EXPECT_EQ(FormField(ASCIIToUTF16("First name:"),
+ EXPECT_EQ(FormField(ASCIIToUTF16("First name:Bogus"),
+ ASCIIToUTF16("firstname"),
+ ASCIIToUTF16("John"),
+ ASCIIToUTF16("text"),
+ 20),
+ fields[0]);
+ EXPECT_EQ(FormField(ASCIIToUTF16("Last name:"),
+ ASCIIToUTF16("lastname"),
+ ASCIIToUTF16("Smith"),
+ ASCIIToUTF16("text"),
+ 20),
+ fields[1]);
+ EXPECT_EQ(FormField(string16(),
+ ASCIIToUTF16("reply-send"),
+ ASCIIToUTF16("Send"),
+ ASCIIToUTF16("submit"),
+ 0),
+ fields[2]);
+}
+
+TEST_F(FormManagerTest, LabelsInferredFromDefinitionList) {
+ LoadHTML("<FORM name=\"TestForm\" action=\"http://cnn.com\" method=\"post\">"
+ "<DL>"
+ " <DT>"
+ " <SPAN>"
+ " *"
+ " </SPAN>"
+ " <SPAN>"
+ " First name:"
+ " </SPAN>"
+ " <SPAN>"
+ " Bogus"
+ " </SPAN>"
+ " </DT>"
+ " <DD>"
+ " <FONT>"
+ " <INPUT type=\"text\" id=\"firstname\" value=\"John\"/>"
+ " </FONT>"
+ " </DD>"
+ " <DT>"
+ " <SPAN>"
+ " Last name:"
+ " </SPAN>"
+ " </DT>"
+ " <DD>"
+ " <FONT>"
+ " <INPUT type=\"text\" id=\"lastname\" value=\"Smith\"/>"
+ " </FONT>"
+ " </DD>"
+ " <DT></DT>"
+ " <DD>"
+ " <INPUT type=\"submit\" name=\"reply-send\" value=\"Send\"/>"
+ " </DD>"
+ "</DL>"
+ "</FORM>");
+
+ WebFrame* web_frame = GetMainFrame();
+ ASSERT_NE(static_cast<WebFrame*>(NULL), web_frame);
+
+ FormManager form_manager;
+ form_manager.ExtractForms(web_frame);
+
+ std::vector<FormData> forms;
+ form_manager.GetForms(FormManager::REQUIRE_NONE, &forms);
+ ASSERT_EQ(1U, forms.size());
+
+ const FormData& form = forms[0];
+ EXPECT_EQ(ASCIIToUTF16("TestForm"), form.name);
+ EXPECT_EQ(GURL(web_frame->url()), form.origin);
+ EXPECT_EQ(GURL("http://cnn.com"), form.action);
+
+ const std::vector<FormField>& fields = form.fields;
+ ASSERT_EQ(3U, fields.size());
+ EXPECT_EQ(FormField(ASCIIToUTF16("*First name:Bogus"),
ASCIIToUTF16("firstname"),
ASCIIToUTF16("John"),
ASCIIToUTF16("text"),
@@ -1394,6 +1467,90 @@ TEST_F(FormManagerTest, FillFormFewerFormDataFields) {
fields[7]);
}
+// This test sends a FormData object to FillForm with a field changed from
+// those in the cached WebFormElement. In this case, we only fill out the
+// fields that match between the FormData object and the WebFormElement.
+TEST_F(FormManagerTest, FillFormChangedFormDataFields) {
+ LoadHTML("<FORM name=\"TestForm\" action=\"http://buh.com\" method=\"post\">"
+ " <INPUT type=\"text\" id=\"firstname\"/>"
+ " <INPUT type=\"text\" id=\"middlename\"/>"
+ " <INPUT type=\"text\" id=\"lastname\"/>"
+ " <INPUT type=\"submit\" name=\"reply-send\" value=\"Send\"/>"
+ "</FORM>");
+
+ WebFrame* web_frame = GetMainFrame();
+ ASSERT_NE(static_cast<WebFrame*>(NULL), web_frame);
+
+ FormManager form_manager;
+ form_manager.ExtractForms(web_frame);
+
+ // Verify that we have the form.
+ std::vector<FormData> forms;
+ form_manager.GetForms(FormManager::REQUIRE_NONE, &forms);
+ ASSERT_EQ(1U, forms.size());
+
+ // After the field modification, the fields in |form| will look like:
+ // firstname
+ // middlename
+ // lastname
+ FormData* form = &forms[0];
+
+ // Fill the form.
+ form->fields[0].set_value(ASCIIToUTF16("Brother"));
+ form->fields[1].set_value(ASCIIToUTF16("Joseph"));
+ form->fields[2].set_value(ASCIIToUTF16("Jonathan"));
+
+ // Alter the label and name used for matching.
+ form->fields[1].set_label(ASCIIToUTF16("bogus"));
+ form->fields[1].set_name(ASCIIToUTF16("bogus"));
+
+ EXPECT_TRUE(form_manager.FillForm(*form));
+
+ // Get the input element we want to find.
+ WebElement element = web_frame->document().getElementById("firstname");
+ WebInputElement input_element = element.to<WebInputElement>();
+
+ // Find the newly-filled form that contains the input element.
+ FormData form2;
+ EXPECT_TRUE(form_manager.FindFormWithFormControlElement(
+ input_element, FormManager::REQUIRE_NONE, &form2));
+ EXPECT_EQ(ASCIIToUTF16("TestForm"), form2.name);
+ EXPECT_EQ(GURL(web_frame->url()), form2.origin);
+ EXPECT_EQ(GURL("http://buh.com"), form2.action);
+
+ // TODO(jhawkins): We don't actually compare the value of the field in
+ // FormField::operator==()!
+ const std::vector<FormField>& fields = form2.fields;
+ ASSERT_EQ(4U, fields.size());
+ EXPECT_EQ(FormField(string16(),
+ ASCIIToUTF16("firstname"),
+ ASCIIToUTF16("Brother"),
+ ASCIIToUTF16("text"),
+ 20),
+ fields[0]);
+ EXPECT_EQ(ASCIIToUTF16("Brother"), fields[0].value());
+ EXPECT_EQ(FormField(string16(),
+ ASCIIToUTF16("middlename"),
+ ASCIIToUTF16("Joseph"),
+ ASCIIToUTF16("text"),
+ 20),
+ fields[1]);
+ EXPECT_EQ(string16(), fields[1].value());
+ EXPECT_EQ(FormField(string16(),
+ ASCIIToUTF16("lastname"),
+ ASCIIToUTF16("Jonathan"),
+ ASCIIToUTF16("text"),
+ 20),
+ fields[2]);
+ EXPECT_EQ(ASCIIToUTF16("Jonathan"), fields[2].value());
+ EXPECT_EQ(FormField(string16(),
+ ASCIIToUTF16("reply-send"),
+ ASCIIToUTF16("Send"),
+ ASCIIToUTF16("submit"),
+ 0),
+ fields[3]);
+}
+
// This test sends a FormData object to FillForm with fewer fields than are in
// the cached WebFormElement. In this case, we only fill out the fields that
// match between the FormData object and the WebFormElement.