| // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #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 "base/utf_string_conversions.h" |
| #include "third_party/WebKit/WebKit/chromium/public/WebDocument.h" |
| #include "third_party/WebKit/WebKit/chromium/public/WebElement.h" |
| #include "third_party/WebKit/WebKit/chromium/public/WebFormControlElement.h" |
| #include "third_party/WebKit/WebKit/chromium/public/WebFrame.h" |
| #include "third_party/WebKit/WebKit/chromium/public/WebInputElement.h" |
| #include "third_party/WebKit/WebKit/chromium/public/WebLabelElement.h" |
| #include "third_party/WebKit/WebKit/chromium/public/WebNode.h" |
| #include "third_party/WebKit/WebKit/chromium/public/WebNodeList.h" |
| #include "third_party/WebKit/WebKit/chromium/public/WebOptionElement.h" |
| #include "third_party/WebKit/WebKit/chromium/public/WebSelectElement.h" |
| #include "third_party/WebKit/WebKit/chromium/public/WebString.h" |
| #include "third_party/WebKit/WebKit/chromium/public/WebVector.h" |
| #include "webkit/glue/form_data.h" |
| #include "webkit/glue/form_field.h" |
| |
| using webkit_glue::FormData; |
| using webkit_glue::FormField; |
| using WebKit::WebDocument; |
| using WebKit::WebElement; |
| using WebKit::WebFormControlElement; |
| using WebKit::WebFormElement; |
| using WebKit::WebFrame; |
| using WebKit::WebInputElement; |
| using WebKit::WebLabelElement; |
| using WebKit::WebNode; |
| using WebKit::WebNodeList; |
| using WebKit::WebOptionElement; |
| using WebKit::WebSelectElement; |
| using WebKit::WebString; |
| using WebKit::WebVector; |
| |
| namespace { |
| |
| // The number of fields required by AutoFill. Ideally we could send the forms |
| // to AutoFill no matter how many fields are in the forms; however, finding the |
| // label for each field is a costly operation and we can't spare the cycles if |
| // it's not necessary. |
| const size_t kRequiredAutoFillFields = 3; |
| |
| // The maximum length allowed for form data. |
| const size_t kMaxDataLength = 1024; |
| |
| // This is a helper function for the FindChildText() function. |
| // 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. The text is |
| // accumulated after the whitespace has been stripped. Search depth is limited |
| // with the |depth| parameter. |
| string16 FindChildTextInner(const WebNode& node, int depth) { |
| string16 element_text; |
| if (depth <= 0 || node.isNull()) |
| return element_text; |
| |
| string16 node_text = node.nodeValue(); |
| TrimWhitespace(node_text, TRIM_ALL, &node_text); |
| if (!node_text.empty()) |
| element_text = node_text; |
| |
| string16 child_text = FindChildTextInner(node.firstChild(), depth-1); |
| if (!child_text.empty()) |
| element_text = element_text + child_text; |
| |
| string16 sibling_text = FindChildTextInner(node.nextSibling(), depth-1); |
| if (!sibling_text.empty()) |
| element_text = element_text + sibling_text; |
| |
| return element_text; |
| } |
| |
| // Returns the node value of the first descendant of |element| that is a |
| // non-empty text node. "Non-empty" in this case means non-empty after the |
| // whitespace has been stripped. Search is limited to within 10 siblings and/or |
| // descendants. |
| string16 FindChildText(const WebElement& element) { |
| WebNode child = element.firstChild(); |
| |
| const int kChildSearchDepth = 10; |
| return FindChildTextInner(child, kChildSearchDepth); |
| } |
| |
| // 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; |
| } |
| |
| void GetOptionStringsFromElement(WebKit::WebFormControlElement element, |
| std::vector<string16>* option_strings) { |
| DCHECK(!element.isNull()); |
| DCHECK(option_strings); |
| option_strings->clear(); |
| if (element.formControlType() == ASCIIToUTF16("select-one")) { |
| WebSelectElement select_element = element.to<WebSelectElement>(); |
| |
| // For select-one elements copy option strings. |
| WebVector<WebElement> list_items = select_element.listItems(); |
| option_strings->reserve(list_items.size()); |
| for (size_t i = 0; i < list_items.size(); ++i) { |
| if (list_items[i].hasTagName("option")) |
| option_strings->push_back(list_items[i].to<WebOptionElement>().value()); |
| } |
| } |
| } |
| |
| } // namespace |
| |
| FormManager::FormManager() { |
| } |
| |
| FormManager::~FormManager() { |
| Reset(); |
| } |
| |
| // static |
| void FormManager::WebFormControlElementToFormField( |
| const WebFormControlElement& element, bool get_value, FormField* field) { |
| DCHECK(field); |
| |
| // 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()); |
| |
| // Set option strings on the field if available. |
| std::vector<string16> option_strings; |
| GetOptionStringsFromElement(element, &option_strings); |
| field->set_option_strings(option_strings); |
| |
| if (element.formControlType() == WebString::fromUTF8("text")) { |
| const WebInputElement& input_element = element.toConst<WebInputElement>(); |
| field->set_size(input_element.size()); |
| } |
| |
| if (!get_value) |
| return; |
| |
| // TODO(jhawkins): In WebKit, move value() and setValue() to |
| // WebFormControlElement. |
| string16 value; |
| if (element.formControlType() == WebString::fromUTF8("text") || |
| element.formControlType() == WebString::fromUTF8("hidden")) { |
| const WebInputElement& input_element = |
| element.toConst<WebInputElement>(); |
| value = input_element.value(); |
| } else if (element.formControlType() == WebString::fromUTF8("select-one")) { |
| // TODO(jhawkins): This is ugly. WebSelectElement::value() is a non-const |
| // method. Look into fixing this on the WebKit side. |
| WebFormControlElement& e = const_cast<WebFormControlElement&>(element); |
| WebSelectElement select_element = e.to<WebSelectElement>(); |
| value = select_element.value(); |
| } |
| |
| // TODO(jhawkins): This is a temporary stop-gap measure designed to prevent |
| // a malicious site from DOS'ing the browser with extremely large profile |
| // data. The correct solution is to parse this data asynchronously. |
| // See http://crbug.com/49332. |
| if (value.size() > kMaxDataLength) |
| value = value.substr(0, kMaxDataLength); |
| |
| field->set_value(value); |
| } |
| |
| // static |
| string16 FormManager::LabelForElement(const WebFormControlElement& element) { |
| // Don't scrape labels for hidden elements. |
| if (element.formControlType() == WebString::fromUTF8("hidden")) |
| return string16(); |
| |
| WebNodeList labels = element.document().getElementsByTagName("label"); |
| for (unsigned i = 0; i < labels.length(); ++i) { |
| WebElement e = labels.item(i).to<WebElement>(); |
| if (e.hasTagName("label")) { |
| WebLabelElement label = e.to<WebLabelElement>(); |
| if (label.correspondingControl() == element) |
| return FindChildText(label); |
| } |
| } |
| |
| // Infer the label from context if not found in label element. |
| return FormManager::InferLabelForElement(element); |
| } |
| |
| // static |
| bool FormManager::WebFormElementToFormData(const WebFormElement& element, |
| RequirementsMask requirements, |
| bool get_values, |
| FormData* form) { |
| DCHECK(form); |
| |
| const WebFrame* frame = element.document().frame(); |
| if (!frame) |
| return false; |
| |
| if (requirements & REQUIRE_AUTOCOMPLETE && !element.autoComplete()) |
| return false; |
| |
| form->name = element.name(); |
| form->method = element.method(); |
| form->origin = frame->url(); |
| form->action = frame->document().completeURL(element.action()); |
| form->user_submitted = element.wasUserSubmitted(); |
| |
| // If the completed URL is not valid, just use the action we get from |
| // WebKit. |
| 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); |
| |
| // A vector of bools that indicate whether each field in the form meets the |
| // requirements and thus will be in the resulting |form|. |
| std::vector<bool> fields_extracted(control_elements.size(), false); |
| |
| for (size_t i = 0; i < control_elements.size(); ++i) { |
| const WebFormControlElement& control_element = control_elements[i]; |
| |
| if (requirements & REQUIRE_AUTOCOMPLETE && |
| control_element.formControlType() == WebString::fromUTF8("text")) { |
| const WebInputElement& input_element = |
| control_element.toConst<WebInputElement>(); |
| if (!input_element.autoComplete()) |
| continue; |
| } |
| |
| if (requirements & REQUIRE_ENABLED && !control_element.isEnabled()) |
| continue; |
| |
| // Create a new FormField, fill it out and map it to the field's name. |
| FormField* field = new FormField; |
| WebFormControlElementToFormField(control_element, get_values, 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; |
| fields_extracted[i] = true; |
| } |
| |
| // 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 |
| // label.firstChild().nodeValue() of the label element. |
| WebNodeList labels = element.getElementsByTagName("label"); |
| for (unsigned i = 0; i < labels.length(); ++i) { |
| WebLabelElement label = labels.item(i).to<WebLabelElement>(); |
| WebFormControlElement field_element = |
| label.correspondingControl().to<WebFormControlElement>(); |
| if (field_element.isNull() || |
| !field_element.isFormControlElement() || |
| field_element.formControlType() == WebString::fromUTF8("hidden")) |
| continue; |
| |
| std::map<string16, FormField*>::iterator iter = |
| name_map.find(field_element.nameForAutofill()); |
| if (iter != name_map.end()) |
| iter->second->set_label(FindChildText(label)); |
| } |
| |
| // Loop through the form control elements, extracting the label text from the |
| // DOM. We use the |fields_extracted| vector to make sure we assign the |
| // extracted label to the correct field, as it's possible |form_fields| will |
| // not contain all of the elements in |control_elements|. |
| for (size_t i = 0, field_idx = 0; |
| i < control_elements.size() && field_idx < form_fields.size(); ++i) { |
| // This field didn't meet the requirements, so don't try to find a label for |
| // it. |
| if (!fields_extracted[i]) |
| continue; |
| |
| const WebFormControlElement& control_element = control_elements[i]; |
| if (form_fields[field_idx]->label().empty()) |
| form_fields[field_idx]->set_label( |
| FormManager::InferLabelForElement(control_element)); |
| |
| ++field_idx; |
| } |
| |
| // 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) { |
| DCHECK(frame); |
| |
| // Reset the vector of FormElements for this frame. |
| ResetFrame(frame); |
| |
| WebVector<WebFormElement> web_forms; |
| frame->forms(web_forms); |
| |
| for (size_t i = 0; i < web_forms.size(); ++i) { |
| FormElement* form_elements = new FormElement; |
| form_elements->form_element = web_forms[i]; |
| |
| WebVector<WebFormControlElement> control_elements; |
| form_elements->form_element.getFormControlElements(control_elements); |
| for (size_t j = 0; j < control_elements.size(); ++j) { |
| WebFormControlElement element = control_elements[j]; |
| form_elements->control_elements.push_back(element); |
| } |
| |
| form_elements_map_[frame].push_back(form_elements); |
| } |
| } |
| |
| void FormManager::GetForms(RequirementsMask requirements, |
| std::vector<FormData>* forms) { |
| DCHECK(forms); |
| |
| for (WebFrameFormElementMap::iterator iter = form_elements_map_.begin(); |
| iter != form_elements_map_.end(); ++iter) { |
| for (std::vector<FormElement*>::iterator form_iter = iter->second.begin(); |
| form_iter != iter->second.end(); ++form_iter) { |
| FormData form; |
| if (WebFormElementToFormData((*form_iter)->form_element, |
| requirements, |
| true, |
| &form)) |
| forms->push_back(form); |
| } |
| } |
| } |
| |
| void FormManager::GetFormsInFrame(const WebFrame* frame, |
| RequirementsMask requirements, |
| std::vector<FormData>* forms) { |
| DCHECK(frame); |
| DCHECK(forms); |
| |
| WebFrameFormElementMap::iterator iter = form_elements_map_.find(frame); |
| if (iter == form_elements_map_.end()) |
| return; |
| |
| // TODO(jhawkins): Factor this out and use it here and in GetForms. |
| const std::vector<FormElement*>& form_elements = iter->second; |
| for (std::vector<FormElement*>::const_iterator form_iter = |
| form_elements.begin(); |
| form_iter != form_elements.end(); ++form_iter) { |
| FormElement* form_element = *form_iter; |
| |
| // We need at least |kRequiredAutoFillFields| fields before appending this |
| // form to |forms|. |
| if (form_element->control_elements.size() < kRequiredAutoFillFields) |
| continue; |
| |
| if (requirements & REQUIRE_AUTOCOMPLETE && |
| !form_element->form_element.autoComplete()) |
| continue; |
| |
| FormData form; |
| FormElementToFormData(frame, form_element, requirements, &form); |
| if (form.fields.size() >= kRequiredAutoFillFields) |
| forms->push_back(form); |
| } |
| } |
| |
| bool FormManager::FindForm(const WebFormElement& element, |
| RequirementsMask requirements, |
| FormData* form) { |
| DCHECK(form); |
| |
| const WebFrame* frame = element.document().frame(); |
| if (!frame) |
| return false; |
| |
| WebFrameFormElementMap::const_iterator frame_iter = |
| form_elements_map_.find(frame); |
| if (frame_iter == form_elements_map_.end()) |
| return false; |
| |
| for (std::vector<FormElement*>::const_iterator iter = |
| frame_iter->second.begin(); |
| iter != frame_iter->second.end(); ++iter) { |
| if ((*iter)->form_element.name() != element.name()) |
| continue; |
| |
| return FormElementToFormData(frame, *iter, requirements, form); |
| } |
| |
| return false; |
| } |
| |
| bool FormManager::FindFormWithFormControlElement( |
| const WebFormControlElement& element, |
| RequirementsMask requirements, |
| FormData* form) { |
| DCHECK(form); |
| |
| const WebFrame* frame = element.document().frame(); |
| if (!frame) |
| return false; |
| |
| if (form_elements_map_.find(frame) == form_elements_map_.end()) |
| return false; |
| |
| const std::vector<FormElement*> forms = form_elements_map_[frame]; |
| for (std::vector<FormElement*>::const_iterator iter = forms.begin(); |
| iter != forms.end(); ++iter) { |
| const FormElement* form_element = *iter; |
| |
| 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, true, form); |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| bool FormManager::FillForm(const FormData& form, const WebKit::WebNode& node) { |
| FormElement* form_element = NULL; |
| if (!FindCachedFormElement(form, &form_element)) |
| return false; |
| |
| RequirementsMask requirements = static_cast<RequirementsMask>( |
| REQUIRE_AUTOCOMPLETE | REQUIRE_ENABLED | REQUIRE_EMPTY); |
| ForEachMatchingFormField(form_element, |
| node, |
| requirements, |
| form, |
| NewCallback(this, &FormManager::FillFormField)); |
| |
| return true; |
| } |
| |
| bool FormManager::PreviewForm(const FormData& form) { |
| FormElement* form_element = NULL; |
| if (!FindCachedFormElement(form, &form_element)) |
| return false; |
| |
| RequirementsMask requirements = static_cast<RequirementsMask>( |
| REQUIRE_AUTOCOMPLETE | REQUIRE_ENABLED | REQUIRE_EMPTY); |
| ForEachMatchingFormField(form_element, |
| WebNode(), |
| requirements, |
| form, |
| NewCallback(this, &FormManager::PreviewFormField)); |
| |
| return true; |
| } |
| |
| bool FormManager::ClearFormWithNode(const WebKit::WebNode& node) { |
| FormElement* form_element = NULL; |
| if (!FindCachedFormElementWithNode(node, &form_element)) |
| return false; |
| |
| for (size_t i = 0; i < form_element->control_elements.size(); ++i) { |
| WebFormControlElement element = form_element->control_elements[i]; |
| if (element.formControlType() != WebString::fromUTF8("text")) |
| continue; |
| |
| WebInputElement input_element = element.to<WebInputElement>(); |
| |
| // We don't modify the value of disabled fields. |
| if (!input_element.isEnabled()) |
| continue; |
| |
| input_element.setValue(string16()); |
| input_element.setAutofilled(false); |
| } |
| |
| return true; |
| } |
| |
| bool FormManager::ClearPreviewedFormWithNode(const WebKit::WebNode& node) { |
| FormElement* form_element = NULL; |
| if (!FindCachedFormElementWithNode(node, &form_element)) |
| return false; |
| |
| for (size_t i = 0; i < form_element->control_elements.size(); ++i) { |
| WebFormControlElement* element = &form_element->control_elements[i]; |
| |
| // Only input elements can be previewed. |
| if (element->formControlType() != WebString::fromUTF8("text")) |
| continue; |
| |
| // If the input element has not been auto-filled, FormManager has not |
| // previewed this field, so we have nothing to reset. |
| WebInputElement input_element = element->to<WebInputElement>(); |
| if (!input_element.isAutofilled()) |
| continue; |
| |
| // If the user has completed the auto-fill and the values are filled in, we |
| // don't want to reset the auto-filled status. |
| if (!input_element.value().isEmpty()) |
| continue; |
| |
| input_element.setSuggestedValue(string16()); |
| input_element.setAutofilled(false); |
| } |
| |
| return true; |
| } |
| |
| void FormManager::Reset() { |
| for (WebFrameFormElementMap::iterator iter = form_elements_map_.begin(); |
| iter != form_elements_map_.end(); ++iter) { |
| STLDeleteElements(&iter->second); |
| } |
| form_elements_map_.clear(); |
| } |
| |
| void FormManager::ResetFrame(const WebFrame* frame) { |
| WebFrameFormElementMap::iterator iter = form_elements_map_.find(frame); |
| if (iter != form_elements_map_.end()) { |
| STLDeleteElements(&iter->second); |
| form_elements_map_.erase(iter); |
| } |
| } |
| |
| bool FormManager::FormWithNodeIsAutoFilled(const WebKit::WebNode& node) { |
| FormElement* form_element = NULL; |
| if (!FindCachedFormElementWithNode(node, &form_element)) |
| return false; |
| |
| for (size_t i = 0; i < form_element->control_elements.size(); ++i) { |
| WebFormControlElement element = form_element->control_elements[i]; |
| if (element.formControlType() != WebString::fromUTF8("text")) |
| continue; |
| |
| const WebInputElement& input_element = element.to<WebInputElement>(); |
| if (input_element.isAutofilled()) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| // static |
| bool FormManager::FormElementToFormData(const WebFrame* frame, |
| const FormElement* form_element, |
| RequirementsMask requirements, |
| FormData* form) { |
| if (requirements & REQUIRE_AUTOCOMPLETE && |
| !form_element->form_element.autoComplete()) |
| return false; |
| |
| form->name = form_element->form_element.name(); |
| form->method = form_element->form_element.method(); |
| form->origin = frame->url(); |
| form->action = |
| frame->document().completeURL(form_element->form_element.action()); |
| |
| // If the completed URL is not valid, just use the action we get from |
| // WebKit. |
| if (!form->action.is_valid()) |
| form->action = GURL(form_element->form_element.action()); |
| |
| for (std::vector<WebFormControlElement>::const_iterator element_iter = |
| form_element->control_elements.begin(); |
| element_iter != form_element->control_elements.end(); ++element_iter) { |
| const WebFormControlElement& control_element = *element_iter; |
| |
| if (requirements & REQUIRE_AUTOCOMPLETE && |
| control_element.formControlType() == WebString::fromUTF8("text")) { |
| const WebInputElement& input_element = |
| control_element.toConst<WebInputElement>(); |
| if (!input_element.autoComplete()) |
| continue; |
| } |
| |
| if (requirements & REQUIRE_ENABLED && !control_element.isEnabled()) |
| continue; |
| |
| FormField field; |
| WebFormControlElementToFormField(control_element, false, &field); |
| form->fields.push_back(field); |
| } |
| |
| return true; |
| } |
| |
| // static |
| string16 FormManager::InferLabelForElement( |
| const WebFormControlElement& element) { |
| // Don't scrape labels for hidden elements. |
| if (element.formControlType() == WebString::fromUTF8("hidden")) |
| return string16(); |
| |
| string16 inferred_label = InferLabelFromPrevious(element); |
| |
| // 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 a label, check for definition list case. |
| if (inferred_label.empty()) { |
| inferred_label = InferLabelFromDefinitionList(element); |
| } |
| |
| return inferred_label; |
| } |
| |
| bool FormManager::FindCachedFormElementWithNode(const WebKit::WebNode& node, |
| FormElement** form_element) { |
| for (WebFrameFormElementMap::const_iterator frame_iter = |
| form_elements_map_.begin(); |
| frame_iter != form_elements_map_.end(); ++frame_iter) { |
| for (std::vector<FormElement*>::const_iterator form_iter = |
| frame_iter->second.begin(); |
| form_iter != frame_iter->second.end(); ++form_iter) { |
| for (std::vector<WebKit::WebFormControlElement>::const_iterator iter = |
| (*form_iter)->control_elements.begin(); |
| iter != (*form_iter)->control_elements.end(); ++iter) { |
| if (*iter == node) { |
| *form_element = *form_iter; |
| return true; |
| } |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| bool FormManager::FindCachedFormElement(const FormData& form, |
| FormElement** form_element) { |
| for (WebFrameFormElementMap::iterator iter = form_elements_map_.begin(); |
| iter != form_elements_map_.end(); ++iter) { |
| const WebFrame* frame = iter->first; |
| // Remove once http://crbug.com/48857. |
| CHECK(frame); |
| |
| 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 |
| // be unique for the page, nor is it guaranteed to be non-empty. Need to |
| // find a way to uniquely identify the form cross-process. For now we'll |
| // check form name and form action for identity. |
| // http://crbug.com/37990 test file sample8.html. |
| // Also note that WebString() == WebString(string16()) does not seem to |
| // evaluate to |true| for some reason TBD, so forcing to string16. |
| string16 element_name((*form_iter)->form_element.name()); |
| GURL action( |
| frame->document().completeURL((*form_iter)->form_element.action())); |
| if (element_name == form.name && action == form.action) { |
| *form_element = *form_iter; |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| void FormManager::ForEachMatchingFormField(FormElement* form, |
| const WebKit::WebNode& node, |
| RequirementsMask requirements, |
| const FormData& data, |
| Callback* callback) { |
| // It's possible that the site has injected fields into the form after the |
| // page has loaded, so we can't assert that the size of the cached control |
| // elements is equal to the size of the fields in |form|. Fortunately, the |
| // one case in the wild where this happens, paypal.com signup form, the fields |
| // are appended to the end of the form and are not visible. |
| for (size_t i = 0, j = 0; |
| i < form->control_elements.size() && j < data.fields.size(); |
| ++i) { |
| WebFormControlElement* element = &form->control_elements[i]; |
| WebString element_name = element->nameForAutofill(); |
| |
| // Empty WebString != empty string16, so we have to explicitly |
| // check for this case. |
| if (element_name.isEmpty() && data.fields[j].name().empty()) |
| continue; |
| |
| // Search forward in the |form| for a corresponding field. |
| size_t k = j; |
| while (k < data.fields.size() && element_name != data.fields[k].name()) |
| k++; |
| |
| if (k >= data.fields.size()) |
| continue; |
| |
| DCHECK_EQ(data.fields[k].name(), element_name); |
| |
| // More than likely |requirements| will contain REQUIRE_AUTOCOMPLETE and/or |
| // REQUIRE_EMPTY, which both require text form control elements, so special- |
| // case this type of element. |
| if (element->formControlType() == WebString::fromUTF8("text")) { |
| const WebInputElement& input_element = |
| element->toConst<WebInputElement>(); |
| |
| // TODO(jhawkins): WebKit currently doesn't handle the autocomplete |
| // attribute for select control elements, but it probably should. |
| if (requirements & REQUIRE_AUTOCOMPLETE && !input_element.autoComplete()) |
| continue; |
| |
| // Don't require the node that initiated the auto-fill process to be |
| // empty. The user is typing in this field and we should complete the |
| // value when the user selects a value to fill out. |
| if (requirements & REQUIRE_EMPTY && |
| input_element != node && |
| !input_element.value().isEmpty()) |
| continue; |
| } |
| |
| if (requirements & REQUIRE_ENABLED && !element->isEnabled()) |
| continue; |
| |
| callback->Run(element, &data.fields[k]); |
| |
| // We found a matching form field so move on to the next. |
| ++j; |
| } |
| |
| delete callback; |
| } |
| |
| void FormManager::FillFormField(WebKit::WebFormControlElement* field, |
| const FormField* data) { |
| // Nothing to fill. |
| if (data->value().empty()) |
| return; |
| |
| if (field->formControlType() == WebString::fromUTF8("text")) { |
| WebInputElement input_element = field->to<WebInputElement>(); |
| |
| // If the maxlength attribute contains a negative value, maxLength() |
| // returns the default maxlength value. |
| input_element.setValue(data->value().substr(0, input_element.maxLength())); |
| input_element.setAutofilled(true); |
| } else if (field->formControlType() == WebString::fromUTF8("select-one")) { |
| WebSelectElement select_element = field->to<WebSelectElement>(); |
| select_element.setValue(data->value()); |
| } |
| } |
| |
| void FormManager::PreviewFormField(WebKit::WebFormControlElement* field, |
| const FormField* data) { |
| // Nothing to preview. |
| if (data->value().empty()) |
| return; |
| |
| // Only preview input fields. |
| if (field->formControlType() != WebString::fromUTF8("text")) |
| return; |
| |
| WebInputElement input_element = field->to<WebInputElement>(); |
| |
| // If the maxlength attribute contains a negative value, maxLength() |
| // returns the default maxlength value. |
| input_element.setSuggestedValue( |
| data->value().substr(0, input_element.maxLength())); |
| input_element.setAutofilled(true); |
| } |