summaryrefslogtreecommitdiffstats
path: root/components/autofill/renderer/form_cache.cc
diff options
context:
space:
mode:
Diffstat (limited to 'components/autofill/renderer/form_cache.cc')
-rw-r--r--components/autofill/renderer/form_cache.cc295
1 files changed, 295 insertions, 0 deletions
diff --git a/components/autofill/renderer/form_cache.cc b/components/autofill/renderer/form_cache.cc
new file mode 100644
index 0000000..ab92f46
--- /dev/null
+++ b/components/autofill/renderer/form_cache.cc
@@ -0,0 +1,295 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/renderer/form_cache.h"
+
+#include "base/logging.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/common/form_data.h"
+#include "components/autofill/common/form_data_predictions.h"
+#include "components/autofill/common/form_field_data.h"
+#include "components/autofill/common/form_field_data_predictions.h"
+#include "components/autofill/renderer/form_autofill_util.h"
+#include "grit/generated_resources.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebString.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebVector.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebFormControlElement.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebFormElement.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputElement.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebSelectElement.h"
+#include "ui/base/l10n/l10n_util.h"
+
+using WebKit::WebDocument;
+using WebKit::WebFormControlElement;
+using WebKit::WebFormElement;
+using WebKit::WebFrame;
+using WebKit::WebInputElement;
+using WebKit::WebSelectElement;
+using WebKit::WebString;
+using WebKit::WebVector;
+
+namespace {
+
+// The number of fields required by Autofill. Ideally we could send the forms
+// to Autofill no matter how many fields are in the forms; however, finding the
+// label for each field is a costly operation and we can't spare the cycles if
+// it's not necessary.
+const size_t kRequiredAutofillFields = 3;
+
+} // namespace
+
+namespace autofill {
+
+// Helper function to discard state of various WebFormElements when they go out
+// of web frame's scope. This is done to release memory that we no longer need
+// to hold.
+// K should inherit from WebFormControlElement as the function looks to extract
+// WebFormElement for K.form().
+template <class K, class V>
+void RemoveOldElements(const WebFrame& frame, std::map<const K, V>* states) {
+ std::vector<K> to_remove;
+ for (typename std::map<const K, V>::const_iterator it = states->begin();
+ it != states->end(); ++it) {
+ WebFormElement form_element = it->first.form();
+ if (form_element.isNull()) {
+ to_remove.push_back(it->first);
+ } else {
+ const WebFrame* element_frame = form_element.document().frame();
+ if (!element_frame || element_frame == &frame)
+ to_remove.push_back(it->first);
+ }
+ }
+
+ for (typename std::vector<K>::const_iterator it = to_remove.begin();
+ it != to_remove.end(); ++it) {
+ states->erase(*it);
+ }
+}
+
+FormCache::FormCache() {
+}
+
+FormCache::~FormCache() {
+}
+
+void FormCache::ExtractForms(const WebFrame& frame,
+ std::vector<FormData>* forms) {
+ ExtractFormsAndFormElements(frame, forms, NULL);
+}
+
+void FormCache::ExtractFormsAndFormElements(
+ const WebFrame& frame,
+ std::vector<FormData>* forms,
+ std::vector<WebFormElement>* web_form_elements) {
+ // Reset the cache for this frame.
+ ResetFrame(frame);
+
+ WebDocument document = frame.document();
+ if (document.isNull())
+ return;
+
+ web_documents_.insert(document);
+
+ WebVector<WebFormElement> web_forms;
+ document.forms(web_forms);
+
+ size_t num_fields_seen = 0;
+ for (size_t i = 0; i < web_forms.size(); ++i) {
+ WebFormElement form_element = web_forms[i];
+
+ std::vector<WebFormControlElement> control_elements;
+ ExtractAutofillableElements(form_element, autofill::REQUIRE_NONE,
+ &control_elements);
+
+ size_t num_editable_elements = 0;
+ for (size_t j = 0; j < control_elements.size(); ++j) {
+ WebFormControlElement element = control_elements[j];
+
+ // Save original values of <select> elements so we can restore them
+ // when |ClearFormWithNode()| is invoked.
+ if (IsSelectElement(element)) {
+ const WebSelectElement select_element =
+ element.toConst<WebSelectElement>();
+ initial_select_values_.insert(std::make_pair(select_element,
+ select_element.value()));
+ ++num_editable_elements;
+ } else {
+ const WebInputElement input_element =
+ element.toConst<WebInputElement>();
+ if (IsCheckableElement(&input_element)) {
+ initial_checked_state_.insert(
+ std::make_pair(input_element, input_element.isChecked()));
+ } else {
+ ++num_editable_elements;
+ }
+ }
+ }
+
+ // To avoid overly expensive computation, we impose a minimum number of
+ // allowable fields. The corresponding maximum number of allowable fields
+ // is imposed by WebFormElementToFormData().
+ if (num_editable_elements < kRequiredAutofillFields)
+ continue;
+
+ FormData form;
+ ExtractMask extract_mask =
+ static_cast<ExtractMask>(EXTRACT_VALUE | EXTRACT_OPTIONS);
+
+ if (!WebFormElementToFormData(form_element, WebFormControlElement(),
+ REQUIRE_NONE, extract_mask, &form, NULL)) {
+ continue;
+ }
+
+ num_fields_seen += form.fields.size();
+ if (num_fields_seen > kMaxParseableFields)
+ break;
+
+ if (form.fields.size() >= kRequiredAutofillFields) {
+ forms->push_back(form);
+ if (web_form_elements)
+ web_form_elements->push_back(form_element);
+ }
+ }
+}
+
+void FormCache::ResetFrame(const WebFrame& frame) {
+ std::vector<WebDocument> documents_to_delete;
+ for (std::set<WebDocument>::const_iterator it = web_documents_.begin();
+ it != web_documents_.end(); ++it) {
+ const WebFrame* document_frame = it->frame();
+ if (!document_frame || document_frame == &frame)
+ documents_to_delete.push_back(*it);
+ }
+
+ for (std::vector<WebDocument>::const_iterator it =
+ documents_to_delete.begin();
+ it != documents_to_delete.end(); ++it) {
+ web_documents_.erase(*it);
+ }
+
+ RemoveOldElements(frame, &initial_select_values_);
+ RemoveOldElements(frame, &initial_checked_state_);
+}
+
+bool FormCache::ClearFormWithElement(const WebInputElement& element) {
+ WebFormElement form_element = element.form();
+ if (form_element.isNull())
+ return false;
+
+ std::vector<WebFormControlElement> control_elements;
+ ExtractAutofillableElements(form_element, autofill::REQUIRE_NONE,
+ &control_elements);
+ for (size_t i = 0; i < control_elements.size(); ++i) {
+ WebFormControlElement control_element = control_elements[i];
+ WebInputElement* input_element = toWebInputElement(&control_element);
+ if (IsTextInput(input_element)) {
+ // We don't modify the value of disabled fields.
+ if (!input_element->isEnabled())
+ continue;
+
+ input_element->setValue(string16(), true);
+ input_element->setAutofilled(false);
+
+ // Clearing the value in the focused node (above) can cause selection
+ // to be lost. We force selection range to restore the text cursor.
+ if (element == *input_element) {
+ int length = input_element->value().length();
+ input_element->setSelectionRange(length, length);
+ }
+ } else if (IsSelectElement(control_element)) {
+ WebSelectElement select_element = control_element.to<WebSelectElement>();
+
+ std::map<const WebSelectElement, string16>::const_iterator
+ initial_value_iter = initial_select_values_.find(select_element);
+ if (initial_value_iter != initial_select_values_.end() &&
+ select_element.value() != initial_value_iter->second) {
+ select_element.setValue(initial_value_iter->second);
+ select_element.dispatchFormControlChangeEvent();
+ }
+ } else {
+ WebInputElement input_element = control_element.to<WebInputElement>();
+ DCHECK(IsCheckableElement(&input_element));
+ std::map<const WebInputElement, bool>::const_iterator it =
+ initial_checked_state_.find(input_element);
+ if (it != initial_checked_state_.end() &&
+ input_element.isChecked() != it->second) {
+ input_element.setChecked(it->second, true);
+ }
+ }
+ }
+
+ return true;
+}
+
+bool FormCache::ShowPredictions(const FormDataPredictions& form) {
+ DCHECK_EQ(form.data.fields.size(), form.fields.size());
+
+ // Find the form.
+ bool found_form = false;
+ WebFormElement form_element;
+ for (std::set<WebDocument>::const_iterator it = web_documents_.begin();
+ it != web_documents_.end() && !found_form; ++it) {
+ WebVector<WebFormElement> web_forms;
+ it->forms(web_forms);
+
+ for (size_t i = 0; i < web_forms.size(); ++i) {
+ form_element = web_forms[i];
+
+ // Note: matching on the form name here which is not guaranteed to be
+ // unique for the page, nor is it guaranteed to be non-empty. Ideally, we
+ // would have a way to uniquely identify the form cross-process. For now,
+ // we'll check form name and form action for identity.
+ // Also note that WebString() == WebString(string16()) does not evaluate
+ // to |true| -- WebKit distinguishes between a "null" string (lhs) and an
+ // "empty" string (rhs). We don't want that distinction, so forcing to
+ // string16.
+ string16 element_name = GetFormIdentifier(form_element);
+ GURL action(form_element.document().completeURL(form_element.action()));
+ if (element_name == form.data.name && action == form.data.action) {
+ found_form = true;
+ break;
+ }
+ }
+ }
+
+ if (!found_form)
+ return false;
+
+ std::vector<WebFormControlElement> control_elements;
+ ExtractAutofillableElements(form_element, autofill::REQUIRE_NONE,
+ &control_elements);
+ if (control_elements.size() != form.fields.size()) {
+ // Keep things simple. Don't show predictions for forms that were modified
+ // between page load and the server's response to our query.
+ return false;
+ }
+
+ for (size_t i = 0; i < control_elements.size(); ++i) {
+ WebFormControlElement* element = &control_elements[i];
+
+ if (string16(element->nameForAutofill()) != form.data.fields[i].name) {
+ // Keep things simple. Don't show predictions for elements whose names
+ // were modified between page load and the server's response to our query.
+ continue;
+ }
+
+ std::string placeholder = form.fields[i].overall_type;
+ string16 title = l10n_util::GetStringFUTF16(
+ IDS_AUTOFILL_SHOW_PREDICTIONS_TITLE,
+ UTF8ToUTF16(form.fields[i].heuristic_type),
+ UTF8ToUTF16(form.fields[i].server_type),
+ UTF8ToUTF16(form.fields[i].signature),
+ UTF8ToUTF16(form.signature),
+ UTF8ToUTF16(form.experiment_id));
+ if (!element->hasAttribute("placeholder"))
+ element->setAttribute("placeholder", WebString(UTF8ToUTF16(placeholder)));
+ element->setAttribute("title", WebString(title));
+ }
+
+ return true;
+}
+
+} // namespace autofill