blob: 29692520ef802a77a64635958f9becea41651d2c [file] [log] [blame]
/*
* Copyright (c) 2010 The Chromium Authors. All rights reserved.
* Copyright 2010, The Android Open Source Project
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "FormManagerAndroid.h"
#include "DocumentLoader.h"
#include "Element.h"
#include "Frame.h"
#include "FrameLoader.h"
#include "HTMLCollection.h"
#include "HTMLFormControlElement.h"
#include "HTMLFormElement.h"
#include "HTMLInputElement.h"
#include "HTMLLabelElement.h"
#include "HTMLNames.h"
#include "HTMLOptionElement.h"
#include "HTMLSelectElement.h"
#include "InputElement.h"
#include "Node.h"
#include "NodeList.h"
#include "HTMLCollection.h"
#include "FormAssociatedElement.h"
#include "QualifiedName.h"
#include "StringUtils.h"
// TODO: This file is taken from chromium/chrome/renderer/form_manager.cc and
// customised to use WebCore types rather than WebKit API types. It would be
// nice and would ease future merge pain if the two could be combined.
using webkit_glue::FormData;
using webkit_glue::FormField;
using WebCore::Element;
using WebCore::FormAssociatedElement;
using WebCore::HTMLCollection;
using WebCore::HTMLElement;
using WebCore::HTMLFormControlElement;
using WebCore::HTMLFormElement;
using WebCore::HTMLInputElement;
using WebCore::HTMLLabelElement;
using WebCore::HTMLOptionElement;
using WebCore::HTMLSelectElement;
using WebCore::InputElement;
using WebCore::Node;
using WebCore::NodeList;
using namespace WebCore::HTMLNames;
namespace {
// Android helper function.
HTMLInputElement* HTMLFormControlElementToHTMLInputElement(const HTMLFormControlElement& element) {
Node* node = const_cast<Node*>(static_cast<const Node*>(&element));
InputElement* input_element = node->toInputElement();
if (node && node->isHTMLElement())
return static_cast<HTMLInputElement*>(input_element);
return 0;
}
// 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.
// Note the on ANDROID we reduce this from Chromium's 3 as it allows us to
// Autofill simple name/email forms for example. This improves the mobile
// device experience where form filling can be time consuming and frustrating.
const size_t kRequiredAutofillFields = 2;
// The maximum number of form fields we are willing to parse, due to
// computational costs. This is a very conservative upper bound.
const size_t kMaxParseableFields = 1000;
// The maximum length allowed for form data.
const size_t kMaxDataLength = 1024;
// In HTML5, all text fields except password are text input fields to
// autocomplete.
bool IsTextInput(const HTMLInputElement* element) {
if (!element)
return false;
return element->isTextField() && !element->isPasswordField();
}
bool IsSelectElement(const HTMLFormControlElement& element) {
return formControlType(element) == kSelectOne;
}
bool IsOptionElement(Element& element) {
return element.hasTagName(optionTag);
}
bool IsAutofillableElement(const HTMLFormControlElement& element) {
HTMLInputElement* html_input_element = HTMLFormControlElementToHTMLInputElement(element);
return (html_input_element && IsTextInput(html_input_element)) || IsSelectElement(element);
}
// This is a helper function for the FindChildText() function (see below).
// Search depth is limited with the |depth| parameter.
string16 FindChildTextInner(Node* node, int depth) {
string16 element_text;
if (!node || depth <= 0)
return element_text;
string16 node_text = WTFStringToString16(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 aggregated values of the decendants or siblings of |element| 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. Whitesapce is trimmed from
// text accumulated at descendant and sibling. Search is limited to within 10
// siblings and/or descendants.
string16 FindChildText(Element* element) {
Node* 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 HTMLFormControlElement& element) {
string16 inferred_label;
Node* previous = element.previousSibling();
if (!previous)
return string16();
if (previous->isTextNode()) {
inferred_label = WTFStringToString16(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() && previous->isElementNode()) {
Element* element = static_cast<Element*>(previous);
if (element->hasTagName(pTag))
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()) {
Node* sibling = previous->previousSibling();
if (sibling && sibling->isElementNode()) {
Element* element = static_cast<Element*>(sibling);
if (element->hasTagName(pTag))
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) {
if (previous->isTextNode()) {
inferred_label = WTFStringToString16(previous->nodeValue());
TrimWhitespace(inferred_label, TRIM_ALL, &inferred_label);
} else if (previous->isElementNode()) {
Element* element = static_cast<Element*>(previous);
if (!element->hasTagName(imgTag))
break;
} else
break;
previous = previous->previousSibling();
}
}
// Look for label node prior to <input> tag.
// Eg. <label>Some Text</label><input ...>
if (inferred_label.empty()) {
while (inferred_label.empty() && previous) {
if (previous->isTextNode()) {
inferred_label = WTFStringToString16(previous->nodeValue());
TrimWhitespace(inferred_label, TRIM_ALL, &inferred_label);
} else if (previous->isElementNode()) {
Element* element = static_cast<Element*>(previous);
if (element->hasTagName(labelTag)) {
inferred_label = FindChildText(element);
} else {
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 HTMLFormControlElement& element) {
string16 inferred_label;
Node* parent = element.parentNode();
while (parent && parent->isElementNode() && !static_cast<Element*>(parent)->hasTagName(tdTag))
parent = parent->parentNode();
// Check all previous siblings, skipping non-element nodes, until we find a
// non-empty text block.
Node* previous = parent;
while(previous) {
if (previous->isElementNode()) {
Element* e = static_cast<Element*>(previous);
if (e->hasTagName(tdTag)) {
inferred_label = FindChildText(e);
if (!inferred_label.empty())
break;
}
}
previous = previous->previousSibling();
}
return inferred_label;
}
// Helper for |InferLabelForElement()| that infers a label, if possible, from
// a surrounding div table.
// Eg. <div>Some Text<span><input ...></span></div>
string16 InferLabelFromDivTable(const HTMLFormControlElement& element) {
Node* parent = element.parentNode();
while (parent && parent->isElementNode() && !static_cast<Element*>(parent)->hasTagName(divTag))
parent = parent->parentNode();
if (!parent || !parent->isElementNode())
return string16();
Element* e = static_cast<Element*>(parent);
if (!e || !e->hasTagName(divTag))
return string16();
return FindChildText(e);
}
// 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 HTMLFormControlElement& element) {
string16 inferred_label;
Node* parent = element.parentNode();
while (parent && parent->isElementNode() && !static_cast<Element*>(parent)->hasTagName(ddTag))
parent = parent->parentNode();
if (parent && parent->isElementNode()) {
Element* element = static_cast<Element*>(parent);
if (element->hasTagName(ddTag)) {
Node* previous = parent->previousSibling();
// Skip by any intervening text nodes.
while (previous && previous->isTextNode())
previous = previous->previousSibling();
if (previous && previous->isElementNode()) {
element = static_cast<Element*>(previous);
if (element->hasTagName(dtTag))
inferred_label = FindChildText(element);
}
}
}
return inferred_label;
}
// Infers corresponding label for |element| from surrounding context in the DOM.
// Contents of preceding <p> tag or preceding text element found in the form.
string16 InferLabelForElement(const HTMLFormControlElement& element) {
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 div table case.
if (inferred_label.empty())
inferred_label = InferLabelFromDivTable(element);
// If we didn't find a label, check for definition list case.
if (inferred_label.empty())
inferred_label = InferLabelFromDefinitionList(element);
return inferred_label;
}
void GetOptionStringsFromElement(const HTMLSelectElement& select_element, std::vector<string16>* option_strings) {
DCHECK(option_strings);
option_strings->clear();
WTF::Vector<Element*> list_items = select_element.listItems();
option_strings->reserve(list_items.size());
for (size_t i = 0; i < list_items.size(); ++i) {
if (IsOptionElement(*list_items[i])) {
option_strings->push_back(WTFStringToString16(static_cast<HTMLOptionElement*>(list_items[i])->value()));
}
}
}
// Returns the form's |name| attribute if non-empty; otherwise the form's |id|
// attribute.
const string16 GetFormIdentifier(const HTMLFormElement& form) {
string16 identifier = WTFStringToString16(form.name());
if (identifier.empty())
identifier = WTFStringToString16(form.getIdAttribute());
return identifier;
}
} // namespace
namespace android {
struct FormManager::FormElement {
RefPtr<HTMLFormElement> form_element;
std::vector<RefPtr<HTMLFormControlElement> > control_elements;
std::vector<string16> control_values;
};
FormManager::FormManager() {
}
FormManager::~FormManager() {
Reset();
}
// static
void FormManager::HTMLFormControlElementToFormField(HTMLFormControlElement* element, ExtractMask extract_mask, FormField* field) {
DCHECK(field);
DCHECK(element);
// The label is not officially part of a HTMLFormControlElement; however, the
// labels for all form control elements are scraped from the DOM and set in
// WebFormElementToFormData.
field->name = nameForAutofill(*element);
field->form_control_type = formControlType(*element);
if (!IsAutofillableElement(*element))
return;
HTMLInputElement* input_element = HTMLFormControlElementToHTMLInputElement(*element);
if (IsTextInput(input_element)) {
field->max_length = input_element->maxLength();
field->is_autofilled = input_element->isAutofilled();
} else if (extract_mask & EXTRACT_OPTIONS) {
// Set option strings on the field is available.
DCHECK(IsSelectElement(*element));
HTMLSelectElement* select_element = static_cast<HTMLSelectElement*>(element);
std::vector<string16> option_strings;
GetOptionStringsFromElement(*select_element, &option_strings);
field->option_strings = option_strings;
}
if (!(extract_mask & EXTRACT_VALUE))
return;
string16 value;
if (IsTextInput(input_element)) {
value = WTFStringToString16(input_element->value());
} else {
DCHECK(IsSelectElement(*element));
HTMLSelectElement* select_element = static_cast<HTMLSelectElement*>(element);
value = WTFStringToString16(select_element->value());
// Convert the |select_element| value to text if requested.
if (extract_mask & EXTRACT_OPTION_TEXT) {
Vector<Element*> list_items = select_element->listItems();
for (size_t i = 0; i < list_items.size(); ++i) {
if (IsOptionElement(*list_items[i]) &&
WTFStringToString16(static_cast<HTMLOptionElement*>(list_items[i])->value()) == value) {
value = WTFStringToString16(static_cast<HTMLOptionElement*>(list_items[i])->text());
break;
}
}
}
}
// TODO: 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->value = value;
}
// static
string16 FormManager::LabelForElement(const HTMLFormControlElement& element) {
// Don't scrape labels for elements we can't possible autofill anyway.
if (!IsAutofillableElement(element))
return string16();
RefPtr<NodeList> labels = element.document()->getElementsByTagName("label");
for (unsigned i = 0; i < labels->length(); ++i) {
Node* e = labels->item(i);
DCHECK(e->hasTagName(labelTag));
HTMLLabelElement* label = static_cast<HTMLLabelElement*>(e);
if (label->control() == &element)
return FindChildText(label);
}
// Infer the label from context if not found in label element.
return InferLabelForElement(element);
}
// static
bool FormManager::HTMLFormElementToFormData(HTMLFormElement* element, RequirementsMask requirements, ExtractMask extract_mask, FormData* form) {
DCHECK(form);
Frame* frame = element->document()->frame();
if (!frame)
return false;
if (requirements & REQUIRE_AUTOCOMPLETE && !element->autoComplete())
return false;
form->name = GetFormIdentifier(*element);
form->method = WTFStringToString16(element->method());
form->origin = GURL(WTFStringToString16(frame->loader()->documentLoader()->url().string()));
form->action = GURL(WTFStringToString16(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(WTFStringToString16(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;
WTF::Vector<WebCore::FormAssociatedElement*> control_elements = element->associatedElements();
// 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) {
if (!control_elements[i]->isFormControlElement())
continue;
HTMLFormControlElement* control_element = static_cast<HTMLFormControlElement*>(control_elements[i]);
if(!IsAutofillableElement(*control_element))
continue;
HTMLInputElement* input_element = HTMLFormControlElementToHTMLInputElement(*control_element);
if (requirements & REQUIRE_AUTOCOMPLETE && IsTextInput(input_element) &&
!input_element->autoComplete())
continue;
if (requirements & REQUIRE_ENABLED && !control_element->isEnabledFormControl())
continue;
// Create a new FormField, fill it out and map it to the field's name.
FormField* field = new FormField;
HTMLFormControlElementToFormField(control_element, extract_mask, field);
form_fields.push_back(field);
// TODO: 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 HTMLFormControlElement 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.
RefPtr<WebCore::NodeList> labels = element->getElementsByTagName("label");
for (unsigned i = 0; i < labels->length(); ++i) {
HTMLLabelElement* label = static_cast<WebCore::HTMLLabelElement*>(labels->item(i));
HTMLFormControlElement* field_element = label->control();
if (!field_element || field_element->type() == "hidden")
continue;
std::map<string16, FormField*>::iterator iter =
name_map.find(nameForAutofill(*field_element));
// Concatenate labels because some sites might have multiple label
// candidates.
if (iter != name_map.end())
iter->second->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;
if (!control_elements[i]->isFormControlElement())
continue;
const HTMLFormControlElement* control_element = static_cast<HTMLFormControlElement*>(control_elements[i]);
if (form_fields[field_idx]->label.empty())
form_fields[field_idx]->label = 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(Frame* frame) {
ResetFrame(frame);
WTF::RefPtr<HTMLCollection> web_forms = frame->document()->forms();
for (size_t i = 0; i < web_forms->length(); ++i) {
// Owned by |form_elements|.
FormElement* form_element = new FormElement;
HTMLFormElement* html_form_element = static_cast<HTMLFormElement*>(web_forms->item(i));
form_element->form_element = html_form_element;
WTF::Vector<FormAssociatedElement*> control_elements = html_form_element->associatedElements();
for (size_t j = 0; j < control_elements.size(); ++j) {
if (!control_elements[j]->isFormControlElement())
continue;
HTMLFormControlElement* element = static_cast<HTMLFormControlElement*>(control_elements[j]);
if (!IsAutofillableElement(*element))
continue;
form_element->control_elements.push_back(element);
// Save original values of <select> elements so we can restore them
// when |ClearFormWithNode()| is invoked.
if (IsSelectElement(*element)) {
HTMLSelectElement* select_element = static_cast<HTMLSelectElement*>(element);
form_element->control_values.push_back(WTFStringToString16(select_element->value()));
} else
form_element->control_values.push_back(string16());
}
form_elements_.push_back(form_element);
}
}
void FormManager::GetFormsInFrame(const Frame* frame, RequirementsMask requirements, std::vector<FormData>* forms) {
DCHECK(frame);
DCHECK(forms);
size_t num_fields_seen = 0;
for (FormElementList::const_iterator form_iter = form_elements_.begin(); form_iter != form_elements_.end(); ++form_iter) {
FormElement* form_element = *form_iter;
if (form_element->form_element->document()->frame() != frame)
continue;
// To avoid overly expensive computation, we impose both a minimum and a
// maximum number of allowable fields.
if (form_element->control_elements.size() < kRequiredAutofillFields ||
form_element->control_elements.size() > kMaxParseableFields)
continue;
if (requirements & REQUIRE_AUTOCOMPLETE && !form_element->form_element->autoComplete())
continue;
FormData form;
HTMLFormElementToFormData(form_element->form_element.get(), requirements, EXTRACT_VALUE, &form);
num_fields_seen += form.fields.size();
if (num_fields_seen > kMaxParseableFields)
break;
if (form.fields.size() >= kRequiredAutofillFields)
forms->push_back(form);
}
}
bool FormManager::FindFormWithFormControlElement(HTMLFormControlElement* element, RequirementsMask requirements, FormData* form) {
DCHECK(form);
const Frame* frame = element->document()->frame();
if (!frame)
return false;
for (FormElementList::const_iterator iter = form_elements_.begin(); iter != form_elements_.end(); ++iter) {
const FormElement* form_element = *iter;
if (form_element->form_element->document()->frame() != frame)
continue;
for (std::vector<RefPtr<HTMLFormControlElement> >::const_iterator iter = form_element->control_elements.begin(); iter != form_element->control_elements.end(); ++iter) {
HTMLFormControlElement* candidate = iter->get();
if (nameForAutofill(*candidate) == nameForAutofill(*element)) {
ExtractMask extract_mask = static_cast<ExtractMask>(EXTRACT_VALUE | EXTRACT_OPTIONS);
return HTMLFormElementToFormData(form_element->form_element.get(), requirements, extract_mask, form);
}
}
}
return false;
}
bool FormManager::FillForm(const FormData& form, Node* 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, Node* 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::PreviewFormField));
return true;
}
bool FormManager::ClearFormWithNode(Node* node) {
FormElement* form_element = NULL;
if (!FindCachedFormElementWithNode(node, &form_element))
return false;
for (size_t i = 0; i < form_element->control_elements.size(); ++i) {
HTMLFormControlElement* element = form_element->control_elements[i].get();
HTMLInputElement* input_element = HTMLFormControlElementToHTMLInputElement(*element);
if (IsTextInput(input_element)) {
// We don't modify the value of disabled fields.
if (!input_element->isEnabledFormControl())
continue;
input_element->setValue("");
input_element->setAutofilled(false);
// Clearing the value in the focused node (above) can cause selection
// to be lost. We force selection range to restore the text cursor.
if (node == input_element) {
int length = input_element->value().length();
input_element->setSelectionRange(length, length);
}
} else {
DCHECK(IsSelectElement(*element));
HTMLSelectElement* select_element = static_cast<HTMLSelectElement*>(element);
if (WTFStringToString16(select_element->value()) != form_element->control_values[i]) {
select_element->setValue(form_element->control_values[i].c_str());
select_element->dispatchFormControlChangeEvent();
}
}
}
return true;
}
bool FormManager::ClearPreviewedFormWithNode(Node* node, bool was_autofilled) {
FormElement* form_element = NULL;
if (!FindCachedFormElementWithNode(node, &form_element))
return false;
for (size_t i = 0; i < form_element->control_elements.size(); ++i) {
HTMLFormControlElement* element = form_element->control_elements[i].get();
HTMLInputElement* input_element = HTMLFormControlElementToHTMLInputElement(*element);
// Only input elements can be previewed.
if (!IsTextInput(input_element))
continue;
// If the input element has not been auto-filled, FormManager has not
// previewed this field, so we have nothing to reset.
if (!input_element->isAutofilled())
continue;
// There might be unrelated elements in this form which have already been
// auto-filled. For example, the user might have already filled the address
// part of a form and now be dealing with the credit card section. We only
// want to reset the auto-filled status for fields that were previewed.
if (input_element->suggestedValue().isEmpty())
continue;
// Clear the suggested value. For the initiating node, also restore the
// original value.
input_element->setSuggestedValue("");
bool is_initiating_node = (node == input_element);
if (is_initiating_node) {
input_element->setAutofilled(was_autofilled);
} else {
input_element->setAutofilled(false);
}
// Clearing the suggested value in the focused node (above) can cause
// selection to be lost. We force selection range to restore the text
// cursor.
if (is_initiating_node) {
int length = input_element->value().length();
input_element->setSelectionRange(length, length);
}
}
return true;
}
void FormManager::Reset() {
form_elements_.reset();
}
void FormManager::ResetFrame(const Frame* frame) {
FormElementList::iterator iter = form_elements_.begin();
while (iter != form_elements_.end()) {
if ((*iter)->form_element->document()->frame() == frame)
iter = form_elements_.erase(iter);
else
++iter;
}
}
bool FormManager::FormWithNodeIsAutofilled(Node* node) {
FormElement* form_element = NULL;
if (!FindCachedFormElementWithNode(node, &form_element))
return false;
for (size_t i = 0; i < form_element->control_elements.size(); ++i) {
HTMLFormControlElement* element = form_element->control_elements[i].get();
HTMLInputElement* input_element = HTMLFormControlElementToHTMLInputElement(*element);
if (!IsTextInput(input_element))
continue;
if (input_element->isAutofilled())
return true;
}
return false;
}
bool FormManager::FindCachedFormElementWithNode(Node* node, FormElement** form_element) {
for (FormElementList::const_iterator form_iter = form_elements_.begin(); form_iter != form_elements_.end(); ++form_iter) {
for (std::vector<RefPtr<HTMLFormControlElement> >::const_iterator iter = (*form_iter)->control_elements.begin(); iter != (*form_iter)->control_elements.end(); ++iter) {
if (iter->get() == node) {
*form_element = *form_iter;
return true;
}
}
}
return false;
}
bool FormManager::FindCachedFormElement(const FormData& form, FormElement** form_element) {
for (FormElementList::iterator form_iter = form_elements_.begin(); form_iter != form_elements_.end(); ++form_iter) {
// TODO: 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 evaluate to
// |true| -- WebKit distinguisges between a "null" string (lhs) and an
// "empty" string (rhs). We don't want that distinction, so forcing to
// string16.
string16 element_name = GetFormIdentifier(*(*form_iter)->form_element);
GURL action(WTFStringToString16((*form_iter)->form_element->document()->completeURL((*form_iter)->form_element->action()).string()));
if (element_name == form.name && action == form.action) {
*form_element = *form_iter;
return true;
}
}
return false;
}
void FormManager::ForEachMatchingFormField(FormElement* form, Node* 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) {
HTMLFormControlElement* element = form->control_elements[i].get();
string16 element_name = nameForAutofill(*element);
// 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);
bool is_initiating_node = false;
HTMLInputElement* input_element = HTMLFormControlElementToHTMLInputElement(*element);
if (IsTextInput(input_element)) {
// TODO: WebKit currently doesn't handle the autocomplete
// attribute for select control elements, but it probably should.
if (!input_element->autoComplete())
continue;
is_initiating_node = (input_element == node);
// Only autofill empty fields and the firls that initiated the filling,
// i.e. the field the user is currently editing and interacting with.
if (!is_initiating_node && !input_element->value().isEmpty())
continue;
}
if (!element->isEnabledFormControl() || element->isReadOnlyFormControl() || !element->isFocusable())
continue;
callback->Run(element, &data.fields[k], is_initiating_node);
// We found a matching form field so move on to the next.
++j;
}
delete callback;
}
void FormManager::FillFormField(HTMLFormControlElement* field, const FormField* data, bool is_initiating_node) {
// Nothing to fill.
if (data->value.empty())
return;
HTMLInputElement* input_element = HTMLFormControlElementToHTMLInputElement(*field);
if (IsTextInput(input_element)) {
// If the maxlength attribute contains a negative value, maxLength()
// returns the default maxlength value.
input_element->setValue(data->value.substr(0, input_element->maxLength()).c_str(), true);
input_element->setAutofilled(true);
if (is_initiating_node) {
int length = input_element->value().length();
input_element->setSelectionRange(length, length);
}
} else {
DCHECK(IsSelectElement(*field));
HTMLSelectElement* select_element = static_cast<HTMLSelectElement*>(field);
if (WTFStringToString16(select_element->value()) != data->value) {
select_element->setValue(data->value.c_str());
select_element->dispatchFormControlChangeEvent();
}
}
}
void FormManager::PreviewFormField(HTMLFormControlElement* field, const FormField* data, bool is_initiating_node) {
// Nothing to preview.
if (data->value.empty())
return;
// Only preview input fields.
HTMLInputElement* input_element = HTMLFormControlElementToHTMLInputElement(*field);
if (!IsTextInput(input_element))
return;
// If the maxlength attribute contains a negative value, maxLength()
// returns the default maxlength value.
input_element->setSuggestedValue(data->value.substr(0, input_element->maxLength()).c_str());
input_element->setAutofilled(true);
if (is_initiating_node) {
// Select the part of the text that the user didn't type.
input_element->setSelectionRange(input_element->value().length(), input_element->suggestedValue().length());
}
}
}