summaryrefslogtreecommitdiffstats
path: root/WebKit/android/WebCoreSupport/autofill
diff options
context:
space:
mode:
Diffstat (limited to 'WebKit/android/WebCoreSupport/autofill')
-rw-r--r--WebKit/android/WebCoreSupport/autofill/FormFieldAndroid.cpp87
-rw-r--r--WebKit/android/WebCoreSupport/autofill/FormFieldAndroid.h42
-rw-r--r--WebKit/android/WebCoreSupport/autofill/FormManagerAndroid.cpp830
-rw-r--r--WebKit/android/WebCoreSupport/autofill/FormManagerAndroid.h237
-rw-r--r--WebKit/android/WebCoreSupport/autofill/StringUtils.h73
-rw-r--r--WebKit/android/WebCoreSupport/autofill/WebAutoFill.cpp18
-rw-r--r--WebKit/android/WebCoreSupport/autofill/WebAutoFill.h4
7 files changed, 786 insertions, 505 deletions
diff --git a/WebKit/android/WebCoreSupport/autofill/FormFieldAndroid.cpp b/WebKit/android/WebCoreSupport/autofill/FormFieldAndroid.cpp
index 00213a1..07f8338 100644
--- a/WebKit/android/WebCoreSupport/autofill/FormFieldAndroid.cpp
+++ b/WebKit/android/WebCoreSupport/autofill/FormFieldAndroid.cpp
@@ -28,45 +28,66 @@
#include "FormFieldAndroid.h"
#include "ChromiumIncludes.h"
+#include "Element.h"
#include "HTMLFormControlElement.h"
#include "HTMLInputElement.h"
+#include "HTMLNames.h"
+#include "HTMLOptionElement.h"
#include "HTMLSelectElement.h"
+#include "StringUtils.h"
+#include <wtf/Vector.h>
-// TODO: This file is taken from chromium/webkit/glue/form_field.h and
+using WebCore::Element;
+using WebCore::HTMLFormControlElement;
+using WebCore::HTMLInputElement;
+using WebCore::HTMLOptionElement;
+using WebCore::HTMLSelectElement;
+
+using namespace WebCore::HTMLNames;
+
+// TODO: This file is taken from chromium/webkit/glue/form_field.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.
namespace webkit_glue {
+
FormField::FormField()
: size_(0) {
}
-FormField::FormField(WebCore::HTMLFormControlElement& element)
+// TODO: This constructor should probably be deprecated and the
+// functionality moved to FormManager.
+FormField::FormField(const HTMLFormControlElement& element)
: size_(0) {
- name_ = string16(element.name().characters());
+ name_ = nameForAutoFill(element);
// TODO: Extract the field label. For now we just use the field
// name.
label_ = name_;
- form_control_type_ = string16(element.type().characters());
- if (form_control_type_ == ASCIIToUTF16("text")) {
- WebCore::HTMLInputElement* input_element = static_cast<WebCore::HTMLInputElement*>(&element);
- value_ = string16(input_element->value().characters());
- size_ = input_element->size();
- } else if (form_control_type_ == ASCIIToUTF16("select-one")) {
- WebCore::HTMLSelectElement* select_element = static_cast<WebCore::HTMLSelectElement*>(&element);
- value_ = string16(select_element->value().characters());
+ form_control_type_ = formControlType(element);
+ if (form_control_type_ == kText) {
+ const HTMLInputElement& input_element = static_cast<const HTMLInputElement&>(element);
+ value_ = WTFStringToString16(input_element.value());
+ size_ = input_element.size();
+ } else if (form_control_type_ == kSelectOne) {
+ const HTMLSelectElement& const_select_element = static_cast<const HTMLSelectElement&>(element);
+ HTMLSelectElement& select_element = const_cast<HTMLSelectElement&>(const_select_element);
+ value_ = WTFStringToString16(select_element.value());
+
+ // For select-one elements copy option strings.
+ 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 (list_items[i]->hasTagName(optionTag))
+ option_strings_.push_back(WTFStringToString16(static_cast<HTMLOptionElement*>(list_items[i])->value()));
+ }
}
TrimWhitespace(value_, TRIM_LEADING, &value_);
}
-FormField::FormField(const string16& label,
- const string16& name,
- const string16& value,
- const string16& form_control_type,
- int size)
+FormField::FormField(const string16& label, const string16& name, const string16& value, const string16& form_control_type, int size)
: label_(label),
name_(name),
value_(value),
@@ -74,7 +95,41 @@ FormField::FormField(const string16& label,
size_(size) {
}
+FormField::~FormField() {
+}
+
+bool FormField::operator==(const FormField& field) const {
+ // A FormField stores a value, but the value is not part of the identity of
+ // the field, so we don't want to compare the values.
+ return (label_ == field.label_ &&
+ name_ == field.name_ &&
+ form_control_type_ == field.form_control_type_ &&
+ size_ == field.size_);
+}
+
bool FormField::operator!=(const FormField& field) const {
return !operator==(field);
}
+
+bool FormField::StrictlyEqualsHack(const FormField& field) const {
+ return (label_ == field.label_ &&
+ name_ == field.name_ &&
+ value_ == field.value_ &&
+ form_control_type_ == field.form_control_type_ &&
+ size_ == field.size_);
+}
+
+std::ostream& operator<<(std::ostream& os, const FormField& field) {
+ return os
+ << UTF16ToUTF8(field.label())
+ << " "
+ << UTF16ToUTF8(field.name())
+ << " "
+ << UTF16ToUTF8(field.value())
+ << " "
+ << UTF16ToUTF8(field.form_control_type())
+ << " "
+ << field.size();
+}
+
} // namespace webkit_glue
diff --git a/WebKit/android/WebCoreSupport/autofill/FormFieldAndroid.h b/WebKit/android/WebCoreSupport/autofill/FormFieldAndroid.h
index 51bb24b..8fb13a1 100644
--- a/WebKit/android/WebCoreSupport/autofill/FormFieldAndroid.h
+++ b/WebKit/android/WebCoreSupport/autofill/FormFieldAndroid.h
@@ -30,7 +30,7 @@
#include <base/string16.h>
#include <vector>
-// TODO: This file is taken from chromium/webkit/glue/form_field.cc and
+// TODO: This file is taken from chromium/webkit/glue/form_field.h 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.
@@ -44,12 +44,9 @@ namespace webkit_glue {
class FormField {
public:
FormField();
- explicit FormField(WebCore::HTMLFormControlElement& element);
- FormField(const string16& label,
- const string16& name,
- const string16& value,
- const string16& form_control_type,
- int size);
+ explicit FormField(const WebCore::HTMLFormControlElement& element);
+ FormField(const string16& label, const string16& name, const string16& value, const string16& form_control_type, int size);
+ virtual ~FormField();
const string16& label() const { return label_; }
const string16& name() const { return name_; }
@@ -58,25 +55,26 @@ public:
int size() const { return size_; }
// Returns option string for elements for which they make sense (select-one,
// for example) for the rest of elements return an empty array.
- const std::vector<string16>& option_strings() const {
- return option_strings_;
- }
-
+ const std::vector<string16>& option_strings() const { return option_strings_; }
void set_label(const string16& label) { label_ = label; }
void set_name(const string16& name) { name_ = name; }
void set_value(const string16& value) { value_ = value; }
- void set_form_control_type(const string16& form_control_type) {
- form_control_type_ = form_control_type;
- }
+ void set_form_control_type(const string16& form_control_type) { form_control_type_ = form_control_type; }
void set_size(int size) { size_ = size; }
- void set_option_strings(const std::vector<string16>& strings) {
- option_strings_ = strings;
- }
+ void set_option_strings(const std::vector<string16>& strings) { option_strings_ = strings; }
+ // Equality tests for identity which does not include |value_| or |size_|.
+ // Use |StrictlyEqualsHack| method to test all members.
+ // TODO: These operators need to be revised when we implement field
+ // ids.
bool operator==(const FormField& field) const;
bool operator!=(const FormField& field) const;
+ // Test equality of all data members.
+ // TODO: This will be removed when we implement field ids.
+ bool StrictlyEqualsHack(const FormField& field) const;
+
private:
string16 label_;
string16 name_;
@@ -86,14 +84,8 @@ private:
std::vector<string16> option_strings_;
};
-inline bool FormField::operator==(const FormField& field) const {
- // A FormField stores a value, but the value is not part of the identity of
- // the field, so we don't want to compare the values.
- return (label_ == field.label_ &&
- name_ == field.name_ &&
- form_control_type_ == field.form_control_type_);
-
-}
+// So we can compare FormFields with EXPECT_EQ().
+std::ostream& operator<<(std::ostream& os, const FormField& field);
} // namespace webkit_glue
diff --git a/WebKit/android/WebCoreSupport/autofill/FormManagerAndroid.cpp b/WebKit/android/WebCoreSupport/autofill/FormManagerAndroid.cpp
index 8f0467b..bec6042 100644
--- a/WebKit/android/WebCoreSupport/autofill/FormManagerAndroid.cpp
+++ b/WebKit/android/WebCoreSupport/autofill/FormManagerAndroid.cpp
@@ -27,18 +27,24 @@
#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 "HTMLTextAreaElement.h"
#include "HTMLInputElement.h"
#include "HTMLLabelElement.h"
#include "HTMLNames.h"
+#include "HTMLOptionElement.h"
#include "HTMLSelectElement.h"
+#include "Node.h"
#include "NodeList.h"
#include "HTMLCollection.h"
#include "FormFieldAndroid.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
@@ -46,10 +52,13 @@
using webkit_glue::FormData;
using webkit_glue::FormField;
-using WebCore::HTMLElement;
+using WebCore::Element;
+using WebCore::HTMLCollection;
using WebCore::HTMLFormControlElement;
using WebCore::HTMLFormElement;
+using WebCore::HTMLInputElement;
using WebCore::HTMLLabelElement;
+using WebCore::HTMLOptionElement;
using WebCore::HTMLSelectElement;
using WebCore::Node;
using WebCore::NodeList;
@@ -67,61 +76,174 @@ namespace {
// device experience where form filling can be time consuming and frustrating.
const size_t kRequiredAutoFillFields = 2;
-string16 WTFStringToString16(const WTF::String& wtfString)
-{
- WTF::String str = wtfString;
-
- if (str.charactersWithNullTermination())
- return string16(str.charactersWithNullTermination());
- else
- return string16();
-}
-
-string16 S(const WTF::AtomicString& str)
-{
- return WTFStringToString16(str.string());
-}
-
-string16 S(const WTF::String& string)
-{
- return WTFStringToString16(string);
-}
+// The maximum length allowed for form data.
+const size_t kMaxDataLength = 1024;
// 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.
-string16 FindChildTextInner(const Node* node) {
+// can be used when the structure is not directly known. The text is
+// accumulated after the whitespace has been stropped. Search depth is limited
+// with the |depth| parameter.
+string16 FindChildTextInner(Node* node, int depth) {
string16 element_text;
- if (!node)
+ if (!node || depth <= 0)
return element_text;
- element_text = S(node->nodeValue());
- TrimWhitespace(element_text, TRIM_ALL, &element_text);
- if (!element_text.empty())
- return element_text;
+ string16 node_text = WTFStringToString16(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(), depth-1);
+ 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(), depth-1);
+ if (!sibling_text.empty())
+ element_text = element_text + sibling_text;
return element_text;
}
// Returns the node value of the first decendant of |element| that is a
// non-empty text node. "Non-empty" in this case means non-empty after the
-// whitespace has been stripped.
-string16 FindChildText(const HTMLElement* element) {
+// whitespace has been stripped. Search is limited to withing 10 siblings and/or
+// descendants.
+string16 FindChildText(Element* element) {
Node* child = element->firstChild();
- return FindChildTextInner(child);
+
+ const int kChildSearchDepth = 10;
+ return FindChildTextInner(child, kChildSearchDepth);
+}
+
+string16 InferLabelFromPrevious(const HTMLFormControlElement& element) {
+ string16 inferred_label;
+ Node* previous = element.previousSibling();
+ if (previous) {
+ // Eg. Some Text<input ...>
+ 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()) {
+ if (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()) {
+ previous = previous->previousSibling();
+ if (previous && previous->isElementNode()) {
+ Element* element = static_cast<Element*>(previous);
+ 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();
+ }
+ }
+ }
+ 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();
+
+ if (parent && parent->isElementNode()) {
+ Element* element = static_cast<Element*>(parent);
+ if (element->hasTagName(tdTag)) {
+ 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(tdTag))
+ 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 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;
+}
+
+void GetOptionStringsFromElement(HTMLFormControlElement* element, std::vector<string16>* option_strings) {
+ DCHECK(element);
+ DCHECK(option_strings);
+ option_strings->clear();
+ if (formControlType(*element) == kSelectOne) {
+ HTMLSelectElement* select_element = static_cast<HTMLSelectElement*>(element);
+
+ // For select-one elements copy option strings.
+ 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 (list_items[i]->hasTagName(optionTag))
+ option_strings->push_back(WTFStringToString16(static_cast<HTMLOptionElement*>(list_items[i])->value()));
+ }
+ }
}
} // namespace
@@ -136,36 +258,57 @@ FormManager::~FormManager() {
}
// static
-void FormManager::HTMLFormControlElementToFormField(
- const HTMLFormControlElement& element, bool get_value, FormField* field) {
- ASSERT(field);
+void FormManager::HTMLFormControlElementToFormField(HTMLFormControlElement* element, bool get_value, bool get_options, FormField* field) {
+ DCHECK(field);
// 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->set_name(S(element.name()));
- field->set_form_control_type(S(element.type()));
+ field->set_name(nameForAutoFill(*element));
+ field->set_form_control_type(formControlType(*element));
+
+ if (get_options) {
+ std::vector<string16> option_strings;
+ GetOptionStringsFromElement(element, &option_strings);
+ field->set_option_strings(option_strings);
+ }
+
+ if (formControlType(*element) == kText) {
+ HTMLInputElement* input_element = static_cast<HTMLInputElement*>(element);
+ field->set_size(input_element->size());
+ }
if (!get_value)
return;
+ // TODO: In WebKit, move value() and setValue() to
+ // WebFormControlElement.
string16 value;
- if (element.type() == WTF::AtomicString("text")) {
- const WebCore::HTMLTextAreaElement* input_element = static_cast<const WebCore::HTMLTextAreaElement*>(&element);
- if (input_element->renderer())
- value = S(input_element->value());
- } else if (element.type() == WTF::AtomicString("select-one")) {
- // TODO: This is ugly. SelectElement::value() is a non-const
- // method. Look into fixing this on the WebKit side.
- HTMLFormControlElement& e = const_cast<HTMLFormControlElement&>(element);
- HTMLSelectElement& select_element = static_cast<HTMLSelectElement&>(e);
- value = S(select_element.value());
+ if (formControlType(*element) == kText ||
+ formControlType(*element) == kHidden) {
+ HTMLInputElement* input_element = static_cast<HTMLInputElement*>(element);
+ value = WTFStringToString16(input_element->value());
+ } else if (formControlType(*element) == kSelectOne) {
+ HTMLSelectElement* select_element = static_cast<HTMLSelectElement*>(element);
+ value = WTFStringToString16(select_element->value());
}
+
+ // 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->set_value(value);
}
// static
string16 FormManager::LabelForElement(const HTMLFormControlElement& element) {
+ // Don't scrape labels for hidden elements.
+ if (formControlType(element) == kHidden)
+ return string16();
+
RefPtr<NodeList> labels = element.document()->getElementsByTagName("label");
for (unsigned i = 0; i < labels->length(); ++i) {
Node* e = labels->item(i);
@@ -181,27 +324,26 @@ string16 FormManager::LabelForElement(const HTMLFormControlElement& element) {
}
// static
-bool FormManager::HTMLFormElementToFormData(HTMLFormElement& element,
- RequirementsMask requirements,
- bool get_values,
- FormData* form) {
- ASSERT(form);
-
- const WebCore::Document* document = element.document();
- if (!document)
+bool FormManager::HTMLFormElementToFormData(HTMLFormElement* element, RequirementsMask requirements, bool get_values, bool get_options, FormData* form) {
+ DCHECK(form);
+
+ Frame* frame = element->document()->frame();
+ if (!frame)
return false;
- if (requirements & REQUIRE_AUTOCOMPLETE && !element.autoComplete())
+ if (requirements & REQUIRE_AUTOCOMPLETE && !element->autoComplete())
return false;
- form->name = S(element.name());
- form->method = S(element.method());
- form->origin = GURL(S(document->documentURI()));
- form->action = GURL(S(document->completeURL(element.action())));
+ form->name = WTFStringToString16(element->name());
+ 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(S(element.action()));
+ form->action = GURL(WTFStringToString16(element->action()));
// A map from a FormField's name to the FormField itself.
std::map<string16, FormField*> name_map;
@@ -210,29 +352,30 @@ bool FormManager::HTMLFormElementToFormData(HTMLFormElement& element,
// |name_map|.
ScopedVector<FormField> form_fields;
- WTF::Vector<WebCore::HTMLFormControlElement*> control_elements = element.associatedElements();
+ WTF::Vector<HTMLFormControlElement*> 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) {
- const HTMLFormControlElement* control_element = control_elements[i];
+ HTMLFormControlElement* control_element = control_elements[i];
if (!(control_element->hasTagName(inputTag) || control_element->hasTagName(selectTag)))
continue;
if (requirements & REQUIRE_AUTOCOMPLETE &&
- control_element->type() == WTF::String("text")) {
+ formControlType(*control_element) == kText) {
const WebCore::HTMLInputElement* input_element = static_cast<const WebCore::HTMLInputElement*>(control_element);
if (!input_element->autoComplete())
continue;
}
- if (requirements & REQUIRE_ELEMENTS_ENABLED && !control_element->isEnabledFormControl())
+ 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, get_values, field);
+ HTMLFormControlElementToFormField(control_element, get_values, get_options, 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
@@ -250,15 +393,15 @@ bool FormManager::HTMLFormElementToFormData(HTMLFormElement& element,
// 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");
+ RefPtr<WebCore::NodeList> labels = element->getElementsByTagName("label");
for (unsigned i = 0; i < labels->length(); ++i) {
- WebCore::HTMLLabelElement* label = static_cast<WebCore::HTMLLabelElement*>(labels->item(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(S(field_element->name()));
+ name_map.find(nameForAutoFill(*field_element));
if (iter != name_map.end())
iter->second->set_label(FindChildText(label));
}
@@ -267,8 +410,7 @@ bool FormManager::HTMLFormElementToFormData(HTMLFormElement& element,
// 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) {
+ 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])
@@ -276,365 +418,359 @@ bool FormManager::HTMLFormElementToFormData(HTMLFormElement& element,
const HTMLFormControlElement* control_element = control_elements[i];
if (form_fields[field_idx]->label().empty())
- form_fields[field_idx]->set_label(
- FormManager::InferLabelForElement(*control_element));
+ 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) {
+ for (ScopedVector<FormField>::const_iterator iter = form_fields.begin(); iter != form_fields.end(); ++iter)
form->fields.push_back(**iter);
- }
return true;
}
-void FormManager::ExtractForms(WebCore::Document* document) {
+void FormManager::ExtractForms(Frame* frame) {
+
+ ResetFrame(frame);
- WTF::PassRefPtr<WebCore::HTMLCollection> collection = document->forms();
+ WTF::PassRefPtr<HTMLCollection> web_forms = frame->document()->forms();
- WebCore::HTMLFormElement* form;
- WebCore::HTMLInputElement* input;
- for (Node* node = collection->firstItem();
- node && !node->namespaceURI().isNull() && !node->namespaceURI().isEmpty();
- node = collection->nextItem()) {
+ for (size_t i = 0; i < web_forms->length(); ++i) {
FormElement* form_elements = new FormElement;
- form = static_cast<WebCore::HTMLFormElement*>(node);
- if (form->autoComplete()) {
- WTF::Vector<WebCore::HTMLFormControlElement*> elements = form->associatedElements();
- size_t size = elements.size();
- for (size_t i = 0; i < size; i++) {
- WebCore::HTMLFormControlElement* e = elements[i];
- if (e->hasTagName(inputTag) || e->hasTagName(selectTag))
- form_elements->control_elements.push_back(e);
- }
- form_elements->form_element = form;
+ HTMLFormElement* form_element = static_cast<HTMLFormElement*>(web_forms->item(i));
+ form_elements->form_element = form_element;
+
+ WTF::Vector<HTMLFormControlElement*> control_elements = form_element->associatedElements();
+ for (size_t j = 0; j < control_elements.size(); ++j) {
+ HTMLFormControlElement* element = control_elements[j];
+ form_elements->control_elements.push_back(element);
+
+ // Save original values of "select-one" inputs so we can restore them
+ // when |ClearFormWithNode()| is invoked.
+ if (formControlType(*element) == kSelectOne) {
+ HTMLSelectElement* select_element = static_cast<HTMLSelectElement*>(element);
+ string16 value = WTFStringToString16(select_element->value());
+ form_elements->control_values.push_back(value);
+ } else
+ form_elements->control_values.push_back(string16());
}
- form_elements_map_[document].push_back(form_elements);
- }
-}
-void FormManager::GetForms(RequirementsMask requirements,
- std::vector<FormData>* forms) {
- ASSERT(forms);
-
- for (DocumentFormElementMap::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 (HTMLFormElementToFormData(*(*form_iter)->form_element,
- requirements,
- true,
- &form))
- forms->push_back(form);
- }
+ form_elements_.push_back(form_elements);
}
}
-void FormManager::GetFormsInDocument(const WebCore::Document* document,
- RequirementsMask requirements,
- std::vector<FormData>* forms) {
- ASSERT(document);
- ASSERT(forms);
-
- DocumentFormElementMap::iterator iter = form_elements_map_.find(document);
- if (iter == form_elements_map_.end())
- return;
+void FormManager::GetFormsInFrame(const Frame* frame, RequirementsMask requirements, std::vector<FormData>* forms) {
+ DCHECK(frame);
+ DCHECK(forms);
- // TODO: 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) {
+ 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;
+
// 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())
+ if (requirements & REQUIRE_AUTOCOMPLETE && !form_element->form_element->autoComplete())
continue;
FormData form;
- FormElementToFormData(document, form_element, requirements, &form);
+ HTMLFormElementToFormData(form_element->form_element, requirements, true, false, &form);
if (form.fields.size() >= kRequiredAutoFillFields)
forms->push_back(form);
}
}
-bool FormManager::FindForm(const HTMLFormElement& element,
- RequirementsMask requirements,
- FormData* form) {
- ASSERT(form);
+bool FormManager::FindFormWithFormControlElement(HTMLFormControlElement* element, RequirementsMask requirements, FormData* form) {
+ DCHECK(form);
- const WebCore::Document* document = element.document();
- DocumentFormElementMap::const_iterator document_iter =
- form_elements_map_.find(document);
- if (document_iter == form_elements_map_.end())
+ const Frame* frame = element->document()->frame();
+ if (!frame)
return false;
- for (std::vector<FormElement*>::const_iterator iter =
- document_iter->second.begin();
- iter != document_iter->second.end(); ++iter) {
- if ((*iter)->form_element->name() != element.name())
+ 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;
- return FormElementToFormData(document, *iter, requirements, form);
+
+ for (std::vector<HTMLFormControlElement*>::const_iterator iter = form_element->control_elements.begin(); iter != form_element->control_elements.end(); ++iter) {
+ if (nameForAutoFill(**iter) == nameForAutoFill(*element)) {
+ HTMLFormElementToFormData(form_element->form_element, requirements, true, true, form);
+ return true;
+ }
+ }
}
return false;
}
-bool FormManager::FindFormWithFormControlElement(
- const HTMLFormControlElement& element,
- RequirementsMask requirements,
- FormData* form) {
- ASSERT(form);
-
- const WebCore::Document* document = element.document();
- if (form_elements_map_.find(document) == form_elements_map_.end())
+bool FormManager::FillForm(const FormData& form, Node* node) {
+ FormElement* form_element = NULL;
+ if (!FindCachedFormElement(form, &form_element))
return false;
- const std::vector<FormElement*> forms = form_elements_map_[document];
- for (std::vector<FormElement*>::const_iterator iter = forms.begin();
- iter != forms.end(); ++iter) {
- const FormElement* form_element = *iter;
+ RequirementsMask requirements = static_cast<RequirementsMask>(REQUIRE_AUTOCOMPLETE | REQUIRE_ENABLED | REQUIRE_EMPTY);
+ ForEachMatchingFormField(form_element, node, requirements, form, NewCallback(this, &FormManager::FillFormField));
- for (std::vector<HTMLFormControlElement*>::const_iterator iter =
- form_element->control_elements.begin();
- iter != form_element->control_elements.end(); ++iter) {
- if ((*iter)->name() == element.name()) {
- HTMLFormElementToFormData(
- *form_element->form_element, requirements, true, form);
- return true;
- }
- }
- }
+ return true;
+}
- return false;
+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, 0, requirements, form, NewCallback(this, &FormManager::PreviewFormField));
+
+ return true;
}
-bool FormManager::FillForm(const FormData& form) {
+bool FormManager::ClearFormWithNode(Node* node) {
FormElement* form_element = NULL;
+ if (!FindCachedFormElementWithNode(node, &form_element))
+ return false;
- for (DocumentFormElementMap::iterator iter = form_elements_map_.begin();
- iter != form_elements_map_.end(); ++iter) {
- const WebCore::Document* document = iter->first;
-
- for (std::vector<FormElement*>::iterator form_iter = iter->second.begin();
- form_iter != iter->second.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 seem to
- // evaluate to |true| for some reason TBD, so forcing to string16.
- string16 element_name(S((*form_iter)->form_element->name()));
- GURL action(
- S(document->completeURL((*form_iter)->form_element->action()).string()));
- if (element_name == form.name && action == form.action) {
- form_element = *form_iter;
- break;
- }
+ for (size_t i = 0; i < form_element->control_elements.size(); ++i) {
+ HTMLFormControlElement* element = form_element->control_elements[i];
+ if (formControlType(*element) == kText) {
+ HTMLInputElement* input_element = static_cast<HTMLInputElement*>(element);
+
+ // We don't modify the value of disabled fields.
+ if (!input_element->isEnabledFormControl())
+ continue;
+
+ input_element->setValue("");
+ input_element->setAutofilled(false);
+ } else if (formControlType(*element) == kSelectOne) {
+ HTMLSelectElement* select_element = static_cast<HTMLSelectElement*>(element);
+ select_element->setValue(form_element->control_values[i].c_str());
}
}
- if (!form_element)
+ return true;
+}
+
+bool FormManager::ClearPreviewedFormWithNode(Node* node) {
+ FormElement* form_element = NULL;
+ if (!FindCachedFormElementWithNode(node, &form_element))
return false;
- // 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; i < form_element->control_elements.size(); ++i) {
+ HTMLFormControlElement* element = form_element->control_elements[i];
- for (size_t i = 0, j = 0;
- i < form_element->control_elements.size() && j < form.fields.size();
- ++i, ++j) {
- // Once again, empty WebString != empty string16, so we have to explicitly
- // check for this case.
- if (form_element->control_elements[i]->name().length() == 0 &&
- form.fields[j].name().empty())
+ // Only input elements can be previewed.
+ if (formControlType(*element) != kText)
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 (S(form_element->control_elements[i]->name()) !=
- 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 {
- // Shouldn't get here.
- ASSERT(false);
- }
+ // If the input element has not been auto-filled, FormManager has not
+ // previewed this field, so we have nothing to reset.
+ HTMLInputElement* input_element = static_cast<HTMLInputElement*>(element);
+ 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;
- }
- HTMLFormControlElement* element = form_element->control_elements[i];
- if (!form.fields[j].value().empty() &&
- element->type() != WTF::String("submit")) {
- if (element->type() == WTF::String("text")) {
- WebCore::HTMLInputElement* input_element = static_cast<WebCore::HTMLInputElement*>(element);
- // If the maxlength attribute contains a negative value, maxLength()
- // returns the default maxlength value.
- input_element->setValue(
- WTF::String(form.fields[j].value().substr(0, input_element->maxLength()).c_str()));
- input_element->setAutofilled(true);
- } else if (element->type() ==
- WTF::String("select-one")) {
- WebCore::HTMLSelectElement* select_element = static_cast<WebCore::HTMLSelectElement*>(element);
- select_element->setValue(WTF::String(form.fields[j].value().c_str()));
- }
+ input_element->setSuggestedValue("");
+ 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 (node == input_element) {
+ input_element->setSelectionRange(input_element->value().length(),
+ input_element->value().length());
}
}
return true;
}
-void FormManager::FillForms(const std::vector<webkit_glue::FormData>& forms) {
- for (std::vector<webkit_glue::FormData>::const_iterator iter = forms.begin();
- iter != forms.end(); ++iter) {
- FillForm(*iter);
- }
+void FormManager::Reset() {
+ STLDeleteElements(&form_elements_);
}
-void FormManager::Reset() {
- for (DocumentFormElementMap::iterator iter = form_elements_map_.begin();
- iter != form_elements_map_.end(); ++iter) {
- STLDeleteElements(&iter->second);
+void FormManager::ResetFrame(const Frame* frame) {
+ FormElementList::iterator iter = form_elements_.begin();
+ while (iter != form_elements_.end()) {
+ if ((*iter)->form_element->document()->frame() == frame) {
+ delete *iter;
+ iter = form_elements_.erase(iter);
+ } else
+ ++iter;
}
- form_elements_map_.clear();
}
-void FormManager::ResetFrame(const WebCore::Document* document) {
+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];
+ if (formControlType(*element) != kText)
+ continue;
- DocumentFormElementMap::iterator iter = form_elements_map_.find(document);
- if (iter != form_elements_map_.end()) {
- STLDeleteElements(&iter->second);
- form_elements_map_.erase(iter);
+ HTMLInputElement* input_element = static_cast<HTMLInputElement*>(element);
+ if (input_element->isAutofilled())
+ return true;
}
+
+ return false;
}
// static
-bool FormManager::FormElementToFormData(const WebCore::Document* document,
- const FormElement* form_element,
- RequirementsMask requirements,
- FormData* form) {
- if (requirements & REQUIRE_AUTOCOMPLETE &&
- !form_element->form_element->autoComplete())
- return false;
+string16 FormManager::InferLabelForElement(const HTMLFormControlElement& element) {
+ // Don't scrape labels for hidden elements.
+ if (formControlType(element) == kHidden)
+ return string16();
- form->name = S(form_element->form_element->name());
- form->method = S(form_element->form_element->method());
- form->origin = GURL(S(document->documentURI()));
- form->action = GURL(S(document->completeURL(form_element->form_element->action())));
+ string16 inferred_label = InferLabelFromPrevious(element);
- // If the completed URL is not valid, just use the action we get from
- // WebKit.
- if (!form->action.is_valid())
- form->action = GURL(S(form_element->form_element->action()));
+ // If we didn't find a label, check for table cell case.
+ if (inferred_label.empty())
+ inferred_label = InferLabelFromTable(element);
- // Form elements loop.
- for (std::vector<HTMLFormControlElement*>::const_iterator element_iter =
- form_element->control_elements.begin();
- element_iter != form_element->control_elements.end(); ++element_iter) {
- const HTMLFormControlElement* control_element = *element_iter;
+ // If we didn't find a label, check for definition list case.
+ if (inferred_label.empty())
+ inferred_label = InferLabelFromDefinitionList(element);
- if (requirements & REQUIRE_AUTOCOMPLETE &&
- control_element->type() == WTF::String("text")) {
- const WebCore::HTMLInputElement* input_element =
- static_cast<const WebCore::HTMLInputElement*>(control_element);
- if (!input_element->autoComplete())
- continue;
+ return inferred_label;
+}
+
+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<HTMLFormControlElement*>::const_iterator iter = (*form_iter)->control_elements.begin(); iter != (*form_iter)->control_elements.end(); ++iter) {
+ if (*iter == node) {
+ *form_element = *form_iter;
+ return true;
+ }
}
+ }
- if (requirements & REQUIRE_ELEMENTS_ENABLED && !control_element->isEnabledFormControl())
- continue;
+ return false;
+}
- FormField field;
- HTMLFormControlElementToFormField(*control_element, false, &field);
- form->fields.push_back(field);
+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 seem to
+ // evaluate to |true| for some reason TBD, so forcing to string16.
+ string16 element_name(WTFStringToString16((*form_iter)->form_element->name()));
+ 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 true;
+ return false;
}
-// static
-string16 FormManager::InferLabelForElement(
- const HTMLFormControlElement& element) {
- string16 inferred_label;
- Node* previous = element.previousSibling();
- if (previous) {
- if (previous->isTextNode()) {
- inferred_label = S(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()) {
- if (previous->hasTagName(pTag)) {
- inferred_label = FindChildText((HTMLElement*)previous);
- }
- }
- }
+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];
+ string16 element_name = nameForAutoFill(*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) {
- if (previous->hasTagName(pTag)) {
- inferred_label = FindChildText((HTMLElement*)previous);
- }
- }
+ if (element_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 (formControlType(*element) == kText) {
+ HTMLInputElement* input_element = static_cast<HTMLInputElement*>(element);
+
+ // TODO: 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->isEnabledFormControl())
+ continue;
+
+ callback->Run(element, &data.fields[k]);
+
+ // We found a matching form field so move on to the next.
+ ++j;
}
- // 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 (inferred_label.empty()) {
- Node* parent = element.parentNode();
- while (parent &&
- !parent->hasTagName(tdTag))
- parent = parent->parentNode();
-
- if (parent) {
- if (parent->hasTagName(tdTag)) {
- previous = parent->previousSibling();
-
- // Skip by any intervening text nodes.
- while (previous && previous->isTextNode())
- previous = previous->previousSibling();
-
- if (previous) {
- if (previous->hasTagName(tdTag)) {
- inferred_label = FindChildText((HTMLElement*)previous);
- }
- }
- }
- }
+ delete callback;
+}
+
+void FormManager::FillFormField(HTMLFormControlElement* field, const FormField* data) {
+ // Nothing to fill.
+ if (data->value().empty())
+ return;
+
+ if (formControlType(*field) == kText) {
+ HTMLInputElement* input_element = static_cast<HTMLInputElement*>(field);
+
+ // If the maxlength attribute contains a negative value, maxLength()
+ // returns the default maxlength value.
+ // TODO: The call here to |setSuggestedValue| is a work-around
+ // to a WebKit change in r67122. See http://crbug.com/56081 for details.
+ // Once the core issue is fixed in WebKit, this work-around should be
+ // removed.
+ input_element->setSuggestedValue(data->value().substr(0, input_element->maxLength()).c_str());
+ input_element->setValue(data->value().substr(0, input_element->maxLength()).c_str());
+ input_element->setAutofilled(true);
+ } else if (formControlType(*field) == kSelectOne) {
+ HTMLSelectElement* select_element = static_cast<HTMLSelectElement*>(field);
+ select_element->setValue(data->value().c_str());
}
- return inferred_label;
+}
+
+void FormManager::PreviewFormField(HTMLFormControlElement* field, const FormField* data) {
+ // Nothing to preview.
+ if (data->value().empty())
+ return;
+
+ // Only preview input fields.
+ if (formControlType(*field) != kText)
+ return;
+
+ HTMLInputElement* input_element = static_cast<HTMLInputElement*>(field);
+
+ // 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);
}
}
diff --git a/WebKit/android/WebCoreSupport/autofill/FormManagerAndroid.h b/WebKit/android/WebCoreSupport/autofill/FormManagerAndroid.h
index 395e651..4d85c1b 100644
--- a/WebKit/android/WebCoreSupport/autofill/FormManagerAndroid.h
+++ b/WebKit/android/WebCoreSupport/autofill/FormManagerAndroid.h
@@ -42,120 +42,141 @@ class FormField;
} // namespace webkit_glue
namespace WebCore {
- class HTMLFormControlElement;
- class HTMLFormElement;
- class Document;
+class Frame;
+class HTMLFormControlElement;
+class HTMLFormElement;
+class Node;
}
+using WebCore::Frame;
+using WebCore::HTMLFormControlElement;
+using WebCore::HTMLFormElement;
+using WebCore::Node;
+
namespace android {
// Manages the forms in a Document.
class FormManager {
- public:
- // A bit field mask for form requirements.
- enum RequirementsMask {
- REQUIRE_NONE = 0x0, // No requirements.
- REQUIRE_AUTOCOMPLETE = 0x1, // Require that autocomplete != off.
- REQUIRE_ELEMENTS_ENABLED = 0x2 // Require that disabled attribute is off.
- };
-
- FormManager();
- virtual ~FormManager();
-
- // Fills out a FormField object from a given WebFormControlElement.
- // If |get_value| is true, |field| will have the value set from |element|.
- static void HTMLFormControlElementToFormField(
- const WebCore::HTMLFormControlElement& element,
- bool get_value,
- webkit_glue::FormField* field);
-
- // Returns the corresponding label for |element|. WARNING: This method can
- // potentially be very slow. Do not use during any code paths where the page
- // is loading.
- static string16 LabelForElement(const WebCore::HTMLFormControlElement& element);
-
- // Fills out a FormData object from a given WebFormElement. If |get_values|
- // is true, the fields in |form| will have the values filled out. Returns
- // true if |form| is filled out; it's possible that |element| won't meet the
- // requirements in |requirements|. This also returns false if there are no
- // fields in |form|.
- // TODO: Remove the user of this in RenderView and move this to
- // private.
- static bool HTMLFormElementToFormData(WebCore::HTMLFormElement& element,
- RequirementsMask requirements,
- bool get_values,
- webkit_glue::FormData* form);
-
- // Scans the DOM in |document| extracting and storing forms.
- void ExtractForms(WebCore::Document* document);
-
- // Returns a vector of forms that match |requirements|.
- void GetForms(RequirementsMask requirements,
- std::vector<webkit_glue::FormData>* forms);
-
- // Returns a vector of forms in |document| that match |requirements|.
- void GetFormsInDocument(const WebCore::Document* document,
- RequirementsMask requirements,
- std::vector<webkit_glue::FormData>* forms);
-
- // Returns the cached FormData for |element|. Returns true if the form was
- // found in the cache.
- bool FindForm(const WebCore::HTMLFormElement& element,
- RequirementsMask requirements,
- webkit_glue::FormData* form);
-
- // Finds the form that contains |element| and returns it in |form|. Returns
- // false if the form is not found.
- bool FindFormWithFormControlElement(
- const WebCore::HTMLFormControlElement& element,
- RequirementsMask requirements,
- webkit_glue::FormData* form);
-
- // Fills the form represented by |form|. |form| should have the name set to
- // the name of the form to fill out, and the number of elements and values
- // must match the number of stored elements in the form.
- // TODO: Is matching on name alone good enough? It's possible to
- // store multiple forms with the same names from different frames.
- bool FillForm(const webkit_glue::FormData& form);
-
- // Fills all of the forms in the cache with form data from |forms|.
- void FillForms(const std::vector<webkit_glue::FormData>& forms);
-
- // Resets the stored set of forms.
- void Reset();
-
- // Resets the forms for the specified |document|.
- void ResetFrame(const WebCore::Document* document);
-
- private:
- // Stores the HTMLFormElement and the form control elements for a form.
- struct FormElement {
- WebCore::HTMLFormElement* form_element;
- std::vector<WebCore::HTMLFormControlElement*> control_elements;
- };
-
- // A map of vectors of FormElements keyed by the Document containing each
- // form.
- typedef std::map<const WebCore::Document*, std::vector<FormElement*> >
- DocumentFormElementMap;
-
- // Converts a FormElement to FormData storage. Returns false if the form does
- // not meet all the requirements in the requirements mask.
- static bool FormElementToFormData(const WebCore::Document* document,
- const FormElement* form_element,
- RequirementsMask requirements,
- webkit_glue::FormData* form);
-
- // Infers corresponding label for |element| from surrounding context in the
- // DOM. Contents of preceeding <p> tag or preceeding text element found in
- // the form.
- static string16 InferLabelForElement(
- const WebCore::HTMLFormControlElement& element);
-
- // The map of form elements.
- DocumentFormElementMap form_elements_map_;
-
- DISALLOW_COPY_AND_ASSIGN(FormManager);
+public:
+ // A bit field mask for form requirements.
+ enum RequirementsMask {
+ REQUIRE_NONE = 0x0, // No requirements.
+ REQUIRE_AUTOCOMPLETE = 0x1, // Require that autocomplete != off.
+ REQUIRE_ENABLED = 0x2, // Require that disabled attribute is off.
+ REQUIRE_EMPTY = 0x4 // Require that the fields are empty.
+ };
+
+ FormManager();
+ virtual ~FormManager();
+
+ // Fills out a FormField object from a given HTMLFormControlElement.
+ // If |get_value| is true, |field| will have the value set from |element|.
+ // If |get_options| is true, |field| will have the select options set from
+ // |element|.
+ // TODO: Use a bit-field instead of two parameters.
+ static void HTMLFormControlElementToFormField(HTMLFormControlElement* element, bool get_value, bool get_options, webkit_glue::FormField* field);
+
+ // Returns the corresponding label for |element|. WARNING: This method can
+ // potentially be very slow. Do not use during any code paths where the page
+ // is loading.
+ static string16 LabelForElement(const HTMLFormControlElement& element);
+
+ // Fills out a FormData object from a given WebFormElement. If |get_values|
+ // is true, the fields in |form| will have the values filled out. Returns
+ // true if |form| is filled out; it's possible that |element| won't meet the
+ // requirements in |requirements|. This also returns false if there are no
+ // fields in |form|.
+ // TODO: Remove the user of this in RenderView and move this to
+ // private.
+ static bool HTMLFormElementToFormData(HTMLFormElement* element, RequirementsMask requirements, bool get_values, bool get_options, webkit_glue::FormData* form);
+
+ // Scans the DOM in |frame| extracting and storing forms.
+ void ExtractForms(Frame* frame);
+
+ // Returns a vector of forms in |frame| that match |requirements|.
+ void GetFormsInFrame(const Frame* frame, RequirementsMask requirements, std::vector<webkit_glue::FormData>* forms);
+
+ // Finds the form that contains |element| and returns it in |form|. Returns
+ // false if the form is not found.
+ bool FindFormWithFormControlElement(HTMLFormControlElement* element, RequirementsMask requirements, webkit_glue::FormData* form);
+
+ // Fills the form represented by |form|. |form| should have the name set to
+ // the name of the form to fill out, and the number of elements and values
+ // must match the number of stored elements in the form. |node| is the form
+ // control element that initiated the auto-fill process.
+ // TODO: Is matching on name alone good enough? It's possible to
+ // store multiple forms with the same names from different frames.
+ bool FillForm(const webkit_glue::FormData& form, Node* node);
+
+ // Previews the form represented by |form|. Same conditions as FillForm.
+ bool PreviewForm(const webkit_glue::FormData& form);
+
+ // Clears the values of all input elements in the form that contains |node|.
+ // Returns false if the form is not found.
+ bool ClearFormWithNode(Node* node);
+
+ // Clears the placeholder values and the auto-filled background for any fields
+ // in the form containing |node| that have been previewed. Returns false if
+ // the form is not found.
+ bool ClearPreviewedFormWithNode(Node* node);
+
+ // Resets the stored set of forms.
+ void Reset();
+
+ // Resets the forms for the specified |frame|.
+ void ResetFrame(const Frame* frame);
+
+ // Returns true if |form| has any auto-filled fields.
+ bool FormWithNodeIsAutoFilled(Node* node);
+
+private:
+ // Stores the HTMLFormElement and the form control elements for a form.
+ // Original form values are stored so when we clear a form we can reset
+ // "select-one" values to their original state.
+ struct FormElement {
+ HTMLFormElement* form_element;
+ std::vector<HTMLFormControlElement*> control_elements;
+ std::vector<string16> control_values;
+ };
+
+ // Type for cache of FormElement objects.
+ typedef std::vector<FormElement*> FormElementList;
+
+ // The callback type used by ForEachMatchingFormField().
+ typedef Callback2<HTMLFormControlElement*, const webkit_glue::FormField*>::Type Callback;
+
+ // Infers corresponding label for |element| from surrounding context in the
+ // DOM. Contents of preceeding <p> tag or preceeding text element found in
+ // the form.
+ static string16 InferLabelForElement(const HTMLFormControlElement& element);
+
+ // Finds the cached FormElement that contains |node|.
+ bool FindCachedFormElementWithNode(Node* node, FormElement** form_element);
+
+ // Uses the data in |form| to find the cached FormElement.
+ bool FindCachedFormElement(const webkit_glue::FormData& form, FormElement** form_element);
+
+ // For each field in |data| that matches the corresponding field in |form|
+ // and meets the |requirements|, |callback| is called with the actual
+ // WebFormControlElement and the FormField data from |form|. The field that
+ // matches |node| is not required to be empty if |requirements| includes
+ // REQUIRE_EMPTY. This method owns |callback|.
+ void ForEachMatchingFormField(FormElement* form, Node* node, RequirementsMask requirements, const webkit_glue::FormData& data, Callback* callback);
+
+ // A ForEachMatchingFormField() callback that sets |field|'s value using the
+ // value in |data|. This method also sets the autofill attribute, causing the
+ // background to be yellow.
+ void FillFormField(HTMLFormControlElement* field, const webkit_glue::FormField* data);
+
+ // A ForEachMatchingFormField() callback that sets |field|'s placeholder value
+ // using the value in |data|, causing the test to be greyed-out. This method
+ // also sets the autofill attribute, causing the background to be yellow.
+ void PreviewFormField(HTMLFormControlElement* field, const webkit_glue::FormField* data);
+
+ // The cached FormElement objects.
+ FormElementList form_elements_;
+
+ DISALLOW_COPY_AND_ASSIGN(FormManager);
};
} // namespace android
diff --git a/WebKit/android/WebCoreSupport/autofill/StringUtils.h b/WebKit/android/WebCoreSupport/autofill/StringUtils.h
new file mode 100644
index 0000000..aa408a5
--- /dev/null
+++ b/WebKit/android/WebCoreSupport/autofill/StringUtils.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2010 The Android Open Source Project. All rights reserved.
+ *
+ * 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.
+ */
+
+
+#ifndef AutoFillStringUtils_h_
+#define AutoFillStringUtils_h_
+
+#include "ChromiumIncludes.h"
+#include "HTMLFormControlElement.h"
+#include <wtf/text/WTFString.h>
+
+using WebCore::HTMLFormControlElement;
+
+const string16 kText = ASCIIToUTF16("text");
+const string16 kHidden = ASCIIToUTF16("hidden");
+const string16 kSelectOne = ASCIIToUTF16("select-one");
+
+inline string16 WTFStringToString16(const WTF::String& wtfString)
+{
+ WTF::String str = wtfString;
+
+ if (str.charactersWithNullTermination())
+ return string16(str.charactersWithNullTermination());
+ else
+ return string16();
+}
+
+inline string16 nameForAutoFill(const HTMLFormControlElement& element)
+{
+ // Taken from WebKit/chromium/src/WebFormControlElement.cpp, ported
+ // to use WebCore types for accessing element properties.
+ String name = element.name();
+ String trimmedName = name.stripWhiteSpace();
+ if (!trimmedName.isEmpty())
+ return WTFStringToString16(trimmedName);
+ name = element.getIdAttribute();
+ trimmedName = name.stripWhiteSpace();
+ if (!trimmedName.isEmpty())
+ return WTFStringToString16(trimmedName);
+ return string16();
+}
+
+inline string16 formControlType(const HTMLFormControlElement& element)
+{
+ // Taken from WebKit/chromium/src/WebFormControlElement.cpp, ported
+ // to use WebCore types for accessing element properties.
+ return WTFStringToString16(element.type());
+}
+
+#endif
+
diff --git a/WebKit/android/WebCoreSupport/autofill/WebAutoFill.cpp b/WebKit/android/WebCoreSupport/autofill/WebAutoFill.cpp
index 217bbc0..f258f2f 100644
--- a/WebKit/android/WebCoreSupport/autofill/WebAutoFill.cpp
+++ b/WebKit/android/WebCoreSupport/autofill/WebAutoFill.cpp
@@ -29,7 +29,6 @@
#if ENABLE(WEB_AUTOFILL)
#include "AutoFillHostAndroid.h"
-#include "Document.h"
#include "Frame.h"
#include "FormData.h"
#include "FormManagerAndroid.h"
@@ -75,7 +74,7 @@ WebAutoFill::~WebAutoFill()
mUniqueIdMap.clear();
}
-void WebAutoFill::searchDocument(WebCore::Document* document)
+void WebAutoFill::searchDocument(WebCore::Frame* frame)
{
// TODO: This method is called when the main frame finishes loading and
// scans the document for forms and tries to work out the type of the
@@ -94,9 +93,9 @@ void WebAutoFill::searchDocument(WebCore::Document* document)
mQueryId = 1;
mAutoFillManager->Reset();
mFormManager->Reset();
- mFormManager->ExtractForms(document);
+ mFormManager->ExtractForms(frame);
mForms.clear();
- mFormManager->GetForms(FormManager::REQUIRE_AUTOCOMPLETE, &mForms);
+ mFormManager->GetFormsInFrame(frame, FormManager::REQUIRE_AUTOCOMPLETE, &mForms);
mAutoFillManager->FormsSeen(mForms);
}
@@ -112,11 +111,11 @@ void WebAutoFill::formFieldFocused(WebCore::HTMLFormControlElement* formFieldEle
// Get the FormField from the Node.
webkit_glue::FormField formField;
- FormManager::HTMLFormControlElementToFormField(*formFieldElement, false, &formField);
+ FormManager::HTMLFormControlElementToFormField(formFieldElement, false, false, &formField);
formField.set_label(FormManager::LabelForElement(*formFieldElement));
webkit_glue::FormData* form = new webkit_glue::FormData;
- mFormManager->FindFormWithFormControlElement(*formFieldElement, FormManager::REQUIRE_AUTOCOMPLETE, form);
+ mFormManager->FindFormWithFormControlElement(formFieldElement, FormManager::REQUIRE_AUTOCOMPLETE, form);
mQueryMap[mQueryId] = form;
bool suggestions = mAutoFillManager->GetAutoFillSuggestions(mQueryId, false, formField);
@@ -159,7 +158,12 @@ void WebAutoFill::fillFormInPage(int queryId, const webkit_glue::FormData& form)
if (!enabled())
return;
- mFormManager->FillForm(form);
+ // FIXME: Pass a pointer to the Node that triggered the AutoFill flow here instead of 0.
+ // The consquence of passing 0 is that we should always fail the test in FormManader::ForEachMathcingFormField():169
+ // that says "only overwrite an elements current value if the user triggered autofill through that element"
+ // for elements that have a value already. But by a quirk of Android text views we are OK. We should still
+ // fix this though.
+ mFormManager->FillForm(form, 0);
}
bool WebAutoFill::enabled() const
diff --git a/WebKit/android/WebCoreSupport/autofill/WebAutoFill.h b/WebKit/android/WebCoreSupport/autofill/WebAutoFill.h
index c4b63b0..f77948e 100644
--- a/WebKit/android/WebCoreSupport/autofill/WebAutoFill.h
+++ b/WebKit/android/WebCoreSupport/autofill/WebAutoFill.h
@@ -40,7 +40,7 @@ class AutoFillProfile;
class AutoFillHost;
namespace WebCore {
-class Document;
+class Frame;
class HTMLFormControlElement;
}
@@ -55,7 +55,7 @@ public:
WebAutoFill();
virtual ~WebAutoFill();
- void searchDocument(WebCore::Document*);
+ void searchDocument(WebCore::Frame*);
void formFieldFocused(WebCore::HTMLFormControlElement*);
void fillFormFields(int queryId);
void querySuccessful(int queryId, const string16& value, const string16& label, int uniqueId);