form_cache.cc revision 8bcbed890bc3ce4d7a057a8f32cab53fa534672e
1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "components/autofill/content/renderer/form_cache.h"
6
7#include "base/logging.h"
8#include "base/strings/utf_string_conversions.h"
9#include "components/autofill/content/renderer/form_autofill_util.h"
10#include "components/autofill/core/common/autofill_constants.h"
11#include "components/autofill/core/common/form_data.h"
12#include "components/autofill/core/common/form_data_predictions.h"
13#include "components/autofill/core/common/form_field_data.h"
14#include "components/autofill/core/common/form_field_data_predictions.h"
15#include "grit/component_strings.h"
16#include "third_party/WebKit/public/platform/WebString.h"
17#include "third_party/WebKit/public/platform/WebVector.h"
18#include "third_party/WebKit/public/web/WebDocument.h"
19#include "third_party/WebKit/public/web/WebFormControlElement.h"
20#include "third_party/WebKit/public/web/WebFormElement.h"
21#include "third_party/WebKit/public/web/WebFrame.h"
22#include "third_party/WebKit/public/web/WebInputElement.h"
23#include "third_party/WebKit/public/web/WebSelectElement.h"
24#include "third_party/WebKit/public/web/WebTextAreaElement.h"
25#include "ui/base/l10n/l10n_util.h"
26
27using WebKit::WebDocument;
28using WebKit::WebFormControlElement;
29using WebKit::WebFormElement;
30using WebKit::WebFrame;
31using WebKit::WebInputElement;
32using WebKit::WebSelectElement;
33using WebKit::WebTextAreaElement;
34using WebKit::WebString;
35using WebKit::WebVector;
36
37namespace autofill {
38
39// Helper function to discard state of various WebFormElements when they go out
40// of web frame's scope. This is done to release memory that we no longer need
41// to hold.
42// K should inherit from WebFormControlElement as the function looks to extract
43// WebFormElement for K.form().
44template <class K, class V>
45void RemoveOldElements(const WebFrame& frame, std::map<const K, V>* states) {
46  std::vector<K> to_remove;
47  for (typename std::map<const K, V>::const_iterator it = states->begin();
48       it != states->end(); ++it) {
49    WebFormElement form_element = it->first.form();
50    if (form_element.isNull()) {
51      to_remove.push_back(it->first);
52    } else {
53      const WebFrame* element_frame = form_element.document().frame();
54      if (!element_frame || element_frame == &frame)
55        to_remove.push_back(it->first);
56    }
57  }
58
59  for (typename std::vector<K>::const_iterator it = to_remove.begin();
60       it != to_remove.end(); ++it) {
61    states->erase(*it);
62  }
63}
64
65FormCache::FormCache() {
66}
67
68FormCache::~FormCache() {
69}
70
71void FormCache::ExtractForms(const WebFrame& frame,
72                             std::vector<FormData>* forms) {
73  ExtractFormsAndFormElements(frame, kRequiredAutofillFields, forms, NULL);
74}
75
76bool FormCache::ExtractFormsAndFormElements(
77    const WebFrame& frame,
78    size_t minimum_required_fields,
79    std::vector<FormData>* forms,
80    std::vector<WebFormElement>* web_form_elements) {
81  // Reset the cache for this frame.
82  ResetFrame(frame);
83
84  WebDocument document = frame.document();
85  if (document.isNull())
86    return false;
87
88  web_documents_.insert(document);
89
90  WebVector<WebFormElement> web_forms;
91  document.forms(web_forms);
92
93  size_t num_fields_seen = 0;
94  bool has_skipped_forms = false;
95  for (size_t i = 0; i < web_forms.size(); ++i) {
96    WebFormElement form_element = web_forms[i];
97
98    std::vector<WebFormControlElement> control_elements;
99    ExtractAutofillableElements(form_element, autofill::REQUIRE_NONE,
100                                &control_elements);
101
102    size_t num_editable_elements = 0;
103    for (size_t j = 0; j < control_elements.size(); ++j) {
104      WebFormControlElement element = control_elements[j];
105
106      // Save original values of <select> elements so we can restore them
107      // when |ClearFormWithNode()| is invoked.
108      if (IsSelectElement(element)) {
109        const WebSelectElement select_element =
110            element.toConst<WebSelectElement>();
111        initial_select_values_.insert(std::make_pair(select_element,
112                                                     select_element.value()));
113        ++num_editable_elements;
114      } else if (IsTextAreaElement(element)) {
115        ++num_editable_elements;
116      } else {
117        const WebInputElement input_element =
118            element.toConst<WebInputElement>();
119        if (IsCheckableElement(&input_element)) {
120          initial_checked_state_.insert(
121              std::make_pair(input_element, input_element.isChecked()));
122        } else {
123          ++num_editable_elements;
124        }
125      }
126    }
127
128    // To avoid overly expensive computation, we impose a minimum number of
129    // allowable fields.  The corresponding maximum number of allowable fields
130    // is imposed by WebFormElementToFormData().
131    if (num_editable_elements < minimum_required_fields &&
132        control_elements.size() > 0) {
133      has_skipped_forms = true;
134      continue;
135    }
136
137    FormData form;
138    ExtractMask extract_mask =
139      static_cast<ExtractMask>(EXTRACT_VALUE | EXTRACT_OPTIONS);
140
141    if (!WebFormElementToFormData(form_element, WebFormControlElement(),
142                                  REQUIRE_NONE, extract_mask, &form, NULL)) {
143      continue;
144    }
145
146    num_fields_seen += form.fields.size();
147    if (num_fields_seen > kMaxParseableFields)
148      break;
149
150    if (form.fields.size() >= minimum_required_fields) {
151      forms->push_back(form);
152      if (web_form_elements)
153        web_form_elements->push_back(form_element);
154    } else {
155      has_skipped_forms = true;
156    }
157  }
158
159  // Return true if there are any WebFormElements skipped, else false.
160  return has_skipped_forms;
161}
162
163void FormCache::ResetFrame(const WebFrame& frame) {
164  std::vector<WebDocument> documents_to_delete;
165  for (std::set<WebDocument>::const_iterator it = web_documents_.begin();
166       it != web_documents_.end(); ++it) {
167    const WebFrame* document_frame = it->frame();
168    if (!document_frame || document_frame == &frame)
169      documents_to_delete.push_back(*it);
170  }
171
172  for (std::vector<WebDocument>::const_iterator it =
173           documents_to_delete.begin();
174       it != documents_to_delete.end(); ++it) {
175    web_documents_.erase(*it);
176  }
177
178  RemoveOldElements(frame, &initial_select_values_);
179  RemoveOldElements(frame, &initial_checked_state_);
180}
181
182bool FormCache::ClearFormWithElement(const WebInputElement& element) {
183  WebFormElement form_element = element.form();
184  if (form_element.isNull())
185    return false;
186
187  std::vector<WebFormControlElement> control_elements;
188  ExtractAutofillableElements(form_element, autofill::REQUIRE_NONE,
189                              &control_elements);
190  for (size_t i = 0; i < control_elements.size(); ++i) {
191    WebFormControlElement control_element = control_elements[i];
192    // Don't modify the value of disabled fields.
193    if (!control_element.isEnabled())
194      continue;
195
196    control_element.setAutofilled(false);
197
198    WebInputElement* input_element = toWebInputElement(&control_element);
199    if (IsTextInput(input_element)) {
200      input_element->setValue(base::string16(), true);
201
202      // Clearing the value in the focused node (above) can cause selection
203      // to be lost. We force selection range to restore the text cursor.
204      if (element == *input_element) {
205        int length = input_element->value().length();
206        input_element->setSelectionRange(length, length);
207      }
208    } else if (IsTextAreaElement(control_element)) {
209      WebTextAreaElement text_area = control_element.to<WebTextAreaElement>();
210      text_area.setValue(base::string16());
211      text_area.dispatchFormControlChangeEvent();
212    } else if (IsSelectElement(control_element)) {
213      WebSelectElement select_element = control_element.to<WebSelectElement>();
214
215      std::map<const WebSelectElement, base::string16>::const_iterator
216          initial_value_iter = initial_select_values_.find(select_element);
217      if (initial_value_iter != initial_select_values_.end() &&
218          select_element.value() != initial_value_iter->second) {
219        select_element.setValue(initial_value_iter->second);
220        select_element.dispatchFormControlChangeEvent();
221      }
222    } else {
223      WebInputElement input_element = control_element.to<WebInputElement>();
224      DCHECK(IsCheckableElement(&input_element));
225      std::map<const WebInputElement, bool>::const_iterator it =
226          initial_checked_state_.find(input_element);
227      if (it != initial_checked_state_.end() &&
228          input_element.isChecked() != it->second) {
229        input_element.setChecked(it->second, true);
230      }
231    }
232  }
233
234  return true;
235}
236
237bool FormCache::ShowPredictions(const FormDataPredictions& form) {
238  DCHECK_EQ(form.data.fields.size(), form.fields.size());
239
240  // Find the form.
241  bool found_form = false;
242  WebFormElement form_element;
243  for (std::set<WebDocument>::const_iterator it = web_documents_.begin();
244       it != web_documents_.end() && !found_form; ++it) {
245    WebVector<WebFormElement> web_forms;
246    it->forms(web_forms);
247
248    for (size_t i = 0; i < web_forms.size(); ++i) {
249      form_element = web_forms[i];
250
251      // Note: matching on the form name here which is not guaranteed to be
252      // unique for the page, nor is it guaranteed to be non-empty.  Ideally, we
253      // would have a way to uniquely identify the form cross-process.  For now,
254      // we'll check form name and form action for identity.
255      // Also note that WebString() == WebString(string16()) does not evaluate
256      // to |true| -- WebKit distinguishes between a "null" string (lhs) and an
257      // "empty" string (rhs).  We don't want that distinction, so forcing to
258      // string16.
259      base::string16 element_name = GetFormIdentifier(form_element);
260      GURL action(form_element.document().completeURL(form_element.action()));
261      if (element_name == form.data.name && action == form.data.action) {
262        found_form = true;
263        break;
264      }
265    }
266  }
267
268  if (!found_form)
269    return false;
270
271  std::vector<WebFormControlElement> control_elements;
272  ExtractAutofillableElements(form_element, autofill::REQUIRE_NONE,
273                              &control_elements);
274  if (control_elements.size() != form.fields.size()) {
275    // Keep things simple.  Don't show predictions for forms that were modified
276    // between page load and the server's response to our query.
277    return false;
278  }
279
280  for (size_t i = 0; i < control_elements.size(); ++i) {
281    WebFormControlElement* element = &control_elements[i];
282
283    if (base::string16(element->nameForAutofill()) !=
284        form.data.fields[i].name) {
285      // Keep things simple.  Don't show predictions for elements whose names
286      // were modified between page load and the server's response to our query.
287      continue;
288    }
289
290    std::string placeholder = form.fields[i].overall_type;
291    base::string16 title = l10n_util::GetStringFUTF16(
292        IDS_AUTOFILL_SHOW_PREDICTIONS_TITLE,
293        UTF8ToUTF16(form.fields[i].heuristic_type),
294        UTF8ToUTF16(form.fields[i].server_type),
295        UTF8ToUTF16(form.fields[i].signature),
296        UTF8ToUTF16(form.signature),
297        UTF8ToUTF16(form.experiment_id));
298    if (!element->hasAttribute("placeholder"))
299      element->setAttribute("placeholder", WebString(UTF8ToUTF16(placeholder)));
300    element->setAttribute("title", WebString(title));
301  }
302
303  return true;
304}
305
306}  // namespace autofill
307