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