diff options
Diffstat (limited to 'chrome/renderer')
-rw-r--r-- | chrome/renderer/form_manager.cc | 117 | ||||
-rw-r--r-- | chrome/renderer/form_manager.h | 9 | ||||
-rw-r--r-- | chrome/renderer/form_manager_unittest.cc | 50 | ||||
-rw-r--r-- | chrome/renderer/render_view.cc | 22 |
4 files changed, 146 insertions, 52 deletions
diff --git a/chrome/renderer/form_manager.cc b/chrome/renderer/form_manager.cc index 3d313b6..5bd22e5 100644 --- a/chrome/renderer/form_manager.cc +++ b/chrome/renderer/form_manager.cc @@ -5,6 +5,7 @@ #include "chrome/renderer/form_manager.h" #include "base/logging.h" +#include "base/scoped_vector.h" #include "base/string_util.h" #include "base/stl_util-inl.h" #include "third_party/WebKit/WebKit/chromium/public/WebDocument.h" @@ -58,9 +59,9 @@ void FormManager::WebFormControlElementToFormField( const WebFormControlElement& element, FormField* field) { DCHECK(field); - // TODO(jhawkins): LabelForElement. Returning an empty label temporarily to - // diagnose a perf issue. - field->set_label(string16()); + // The label is not officially part of a WebFormControlElement; however, the + // labels for all form control elements are scraped from the DOM and set in + // WebFormElementToFormData. field->set_name(element.nameForAutofill()); field->set_form_control_type(element.formControlType()); @@ -104,6 +105,13 @@ bool FormManager::WebFormElementToFormData(const WebFormElement& element, if (!form->action.is_valid()) form->action = GURL(element.action()); + // A map from a FormField's name to the FormField itself. + std::map<string16, FormField*> name_map; + + // The extracted FormFields. We use pointers so we can store them in + // |name_map|. + ScopedVector<FormField> form_fields; + WebVector<WebFormControlElement> control_elements; element.getFormControlElements(control_elements); for (size_t i = 0; i < control_elements.size(); ++i) { @@ -120,12 +128,46 @@ bool FormManager::WebFormElementToFormData(const WebFormElement& element, if (requirements & REQUIRE_ELEMENTS_ENABLED && !control_element.isEnabled()) continue; - FormField field; - WebFormControlElementToFormField(control_element, &field); - form->fields.push_back(field); + // Create a new FormField, fill it out and map it to the field's name. + FormField* field = new FormField; + WebFormControlElementToFormField(control_element, field); + form_fields.push_back(field); + // TODO(jhawkins): A label element is mapped to a form control element's id. + // field->name() will contain the id only if the name does not exist. Add + // an id() method to WebFormControlElement and use that here. + name_map[field->name()] = field; + } + + // Don't extract field labels if we have no fields. + if (form_fields.empty()) + return false; + + // Loop through the label elements inside the form element. For each label + // element, get the corresponding form control element, use the form control + // element's name as a key into the <name, FormField> map to find the + // previously created FormField and set the FormField's label to the + // innerText() of the label element. + WebNodeList labels = element.getElementsByTagName("label"); + for (unsigned i = 0; i < labels.length(); ++i) { + WebLabelElement label = labels.item(i).toElement<WebLabelElement>(); + WebFormControlElement field_element = + label.correspondingControl().toElement<WebFormControlElement>(); + if (field_element.isNull() || !field_element.isFormControlElement()) + continue; + + std::map<string16, FormField*>::iterator iter = + name_map.find(field_element.nameForAutofill()); + if (iter != name_map.end()) + iter->second->set_label(label.innerText()); } - return !form->fields.empty(); + // Copy the created FormFields into the resulting FormData object. + for (ScopedVector<FormField>::const_iterator iter = form_fields.begin(); + iter != form_fields.end(); ++iter) { + form->fields.push_back(**iter); + } + + return true; } void FormManager::ExtractForms(const WebFrame* frame) { @@ -145,9 +187,7 @@ void FormManager::ExtractForms(const WebFrame* frame) { form_elements->form_element.getFormControlElements(control_elements); for (size_t j = 0; j < control_elements.size(); ++j) { WebFormControlElement element = control_elements[j]; - // TODO(jhawkins): Remove this check when we have labels. - if (!element.nameForAutofill().isEmpty()) - form_elements->control_elements[element.nameForAutofill()] = element; + form_elements->control_elements.push_back(element); } form_elements_map_[frame].push_back(form_elements); @@ -160,19 +200,13 @@ void FormManager::GetForms(RequirementsMask requirements, for (WebFrameFormElementMap::iterator iter = form_elements_map_.begin(); iter != form_elements_map_.end(); ++iter) { - const WebFrame* frame = iter->first; - for (std::vector<FormElement*>::iterator form_iter = iter->second.begin(); form_iter != iter->second.end(); ++form_iter) { - FormElement* form_element = *form_iter; - - if (requirements & REQUIRE_AUTOCOMPLETE && - !form_element->form_element.autoComplete()) - continue; - FormData form; - FormElementToFormData(frame, form_element, requirements, &form); - forms->push_back(form); + if (WebFormElementToFormData((*form_iter)->form_element, + requirements, + &form)) + forms->push_back(form); } } } @@ -254,10 +288,13 @@ bool FormManager::FindFormWithFormControlElement( iter != forms.end(); ++iter) { const FormElement* form_element = *iter; - if (form_element->control_elements.find(element.nameForAutofill()) != - form_element->control_elements.end()) { - FormElementToFormData(frame, form_element, requirements, form); - return true; + for (std::vector<WebFormControlElement>::const_iterator iter = + form_element->control_elements.begin(); + iter != form_element->control_elements.end(); ++iter) { + if (iter->nameForAutofill() == element.nameForAutofill()) { + WebFormElementToFormData(form_element->form_element, requirements, form); + return true; + } } } @@ -267,10 +304,8 @@ bool FormManager::FindFormWithFormControlElement( bool FormManager::FillForm(const FormData& form) { FormElement* form_element = NULL; - // Frame loop. for (WebFrameFormElementMap::iterator iter = form_elements_map_.begin(); iter != form_elements_map_.end(); ++iter) { - // Form loop. for (std::vector<FormElement*>::iterator form_iter = iter->second.begin(); form_iter != iter->second.end(); ++form_iter) { // TODO(dhollowa): matching on form name here which is not guaranteed to @@ -293,23 +328,27 @@ bool FormManager::FillForm(const FormData& form) { DCHECK(form_element->control_elements.size() == form.fields.size()); - size_t i = 0; - for (FormControlElementMap::iterator iter = - form_element->control_elements.begin(); - iter != form_element->control_elements.end(); ++iter, ++i) { - DCHECK_EQ(form.fields[i].name(), iter->second.nameForAutofill()); + for (size_t i = 0; i < form.fields.size(); ++i) { + 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[i].name().empty()) + DCHECK(element->nameForAutofill().isEmpty()); + else + DCHECK_EQ(form.fields[i].name(), element->nameForAutofill()); if (!form.fields[i].value().empty() && - iter->second.formControlType() != WebString::fromUTF8("submit")) { - if (iter->second.formControlType() == WebString::fromUTF8("text")) { - WebInputElement input_element = - iter->second.toElement<WebInputElement>(); + element->formControlType() != WebString::fromUTF8("submit")) { + if (element->formControlType() == WebString::fromUTF8("text")) { + WebInputElement input_element = element->toElement<WebInputElement>(); input_element.setValue(form.fields[i].value()); input_element.setAutofilled(true); - } else if (iter->second.formControlType() == + } else if (element->formControlType() == WebString::fromUTF8("select-one")) { WebSelectElement select_element = - iter->second.toElement<WebSelectElement>(); + element->toElement<WebSelectElement>(); select_element.setValue(form.fields[i].value()); } } @@ -346,10 +385,10 @@ bool FormManager::FormElementToFormData(const WebFrame* frame, form->action = GURL(form_element->form_element.action()); // Form elements loop. - for (FormControlElementMap::const_iterator element_iter = + for (std::vector<WebFormControlElement>::const_iterator element_iter = form_element->control_elements.begin(); element_iter != form_element->control_elements.end(); ++element_iter) { - WebFormControlElement control_element = element_iter->second; + const WebFormControlElement& control_element = *element_iter; if (requirements & REQUIRE_AUTOCOMPLETE && control_element.formControlType() == WebString::fromUTF8("text")) { diff --git a/chrome/renderer/form_manager.h b/chrome/renderer/form_manager.h index a410294..3842450 100644 --- a/chrome/renderer/form_manager.h +++ b/chrome/renderer/form_manager.h @@ -85,15 +85,10 @@ class FormManager { void Reset(); private: - // A map of WebFormControlElements keyed by each element's name. - typedef std::map<string16, WebKit::WebFormControlElement> - FormControlElementMap; - - // Stores the WebFormElement and the map of form control elements for each - // form. + // Stores the WebFormElement and the form control elements for a form. struct FormElement { WebKit::WebFormElement form_element; - FormControlElementMap control_elements; + std::vector<WebKit::WebFormControlElement> control_elements; }; // A map of vectors of FormElements keyed by the WebFrame containing each diff --git a/chrome/renderer/form_manager_unittest.cc b/chrome/renderer/form_manager_unittest.cc index 4b6b05a..f6fb5a0 100644 --- a/chrome/renderer/form_manager_unittest.cc +++ b/chrome/renderer/form_manager_unittest.cc @@ -422,8 +422,7 @@ TEST_F(FormManagerTest, Reset) { ASSERT_EQ(0U, forms.size()); } -// http://crbug.com/40306 -TEST_F(FormManagerTest, DISABLED_Labels) { +TEST_F(FormManagerTest, Labels) { LoadHTML("<FORM name=\"TestForm\" action=\"http://cnn.com\" method=\"post\">" " <LABEL for=\"firstname\"> First name: </LABEL>" " <INPUT type=\"text\" id=\"firstname\" value=\"John\"/>" @@ -466,6 +465,53 @@ TEST_F(FormManagerTest, DISABLED_Labels) { fields[2]); } +// This test is different from FormManagerTest.Labels in that the label elements +// for= attribute is set to the name of the form control element it is a label +// for instead of the id of the form control element. This is invalid because +// the for= attribute must be set to the id of the form control element. +TEST_F(FormManagerTest, InvalidLabels) { + LoadHTML("<FORM name=\"TestForm\" action=\"http://cnn.com\" method=\"post\">" + " <LABEL for=\"firstname\"> First name: </LABEL>" + " <INPUT type=\"text\" name=\"firstname\" value=\"John\"/>" + " <LABEL for=\"lastname\"> Last name: </LABEL>" + " <INPUT type=\"text\" name=\"lastname\" value=\"Smith\"/>" + " <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); + + 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(string16(), + ASCIIToUTF16("firstname"), + ASCIIToUTF16("John"), + ASCIIToUTF16("text")), + fields[0]); + EXPECT_EQ(FormField(string16(), + ASCIIToUTF16("lastname"), + ASCIIToUTF16("Smith"), + ASCIIToUTF16("text")), + fields[1]); + EXPECT_EQ(FormField(string16(), + ASCIIToUTF16("reply-send"), + ASCIIToUTF16("Send"), + ASCIIToUTF16("submit")), + fields[2]); +} + // http://crbug.com/40306 TEST_F(FormManagerTest, DISABLED_LabelsInferredFromText) { LoadHTML("<FORM name=\"TestForm\" action=\"http://cnn.com\" method=\"post\">" diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc index 0527889..cd649e7 100644 --- a/chrome/renderer/render_view.cc +++ b/chrome/renderer/render_view.cc @@ -1952,10 +1952,24 @@ void RenderView::queryAutofillSuggestions(const WebNode& node, static int query_counter = 0; autofill_query_id_ = query_counter++; autofill_query_node_ = node; - const WebKit::WebInputElement input_element = - node.toConstElement<WebInputElement>(); - Send(new ViewHostMsg_QueryFormFieldAutofill( - routing_id_, autofill_query_id_, FormField(input_element))); + + const WebFormControlElement& element = + node.toConstElement<WebFormControlElement>(); + + FormData form; + if (!form_manager_.FindFormWithFormControlElement(element, + FormManager::REQUIRE_NONE, + &form)) + return; + + // TODO(jhawkins): This is very slow. Add a label cache to FormManager. + for (std::vector<FormField>::const_iterator iter = form.fields.begin(); + iter != form.fields.end(); ++iter) { + if (iter->name() == element.nameForAutofill()) { + Send(new ViewHostMsg_QueryFormFieldAutofill( + routing_id_, autofill_query_id_, *iter)); + } + } } void RenderView::removeAutofillSuggestions(const WebString& name, |