diff options
Diffstat (limited to 'WebKit/android/WebCoreSupport/autofill')
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); |