blob: 0e3f72693b49b529996c55ee9ddfb0c42ef33b35 [file] [log] [blame]
[email protected]679f128f2010-07-22 22:57:441// Copyright (c) 2010 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_helper.h"
6
7#include "app/l10n_util.h"
8#include "chrome/renderer/form_manager.h"
9#include "chrome/renderer/render_view.h"
10#include "grit/generated_resources.h"
11#include "third_party/WebKit/WebKit/chromium/public/WebDocument.h"
[email protected]e47aec52010-08-12 00:50:3012#include "third_party/WebKit/WebKit/chromium/public/WebInputEvent.h"
[email protected]679f128f2010-07-22 22:57:4413#include "third_party/WebKit/WebKit/chromium/public/WebFormControlElement.h"
14#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h"
[email protected]4d51d5bf2010-07-26 18:48:2615#include "third_party/WebKit/WebKit/chromium/public/WebView.h"
[email protected]679f128f2010-07-22 22:57:4416#include "webkit/glue/password_form.h"
17
18using WebKit::WebFormControlElement;
19using WebKit::WebFormElement;
20using WebKit::WebFrame;
21using WebKit::WebInputElement;
22using WebKit::WebNode;
23using WebKit::WebString;
24
[email protected]e47aec52010-08-12 00:50:3025namespace {
26
27// The size above which we stop triggering autofill for an input text field
28// (so to avoid sending long strings through IPC).
29const size_t kMaximumTextSizeForAutoFill = 1000;
30
31} // namespace
32
[email protected]679f128f2010-07-22 22:57:4433AutoFillHelper::AutoFillHelper(RenderView* render_view)
34 : render_view_(render_view),
35 autofill_query_id_(0),
36 autofill_action_(AUTOFILL_NONE),
37 suggestions_clear_index_(-1),
38 suggestions_options_index_(-1) {
39}
40
[email protected]e47aec52010-08-12 00:50:3041void AutoFillHelper::QueryAutoFillSuggestions(const WebNode& node,
42 const WebString& name,
43 const WebString& value) {
[email protected]679f128f2010-07-22 22:57:4444 static int query_counter = 0;
45 autofill_query_id_ = query_counter++;
46 autofill_query_node_ = node;
47
48 const WebFormControlElement& element = node.toConst<WebFormControlElement>();
49 webkit_glue::FormField field;
50 FormManager::WebFormControlElementToFormField(element, true, &field);
51
52 // WebFormControlElementToFormField does not scrape the DOM for the field
53 // label, so find the label here.
54 // TODO(jhawkins): Add form and field identities so we can use the cached form
55 // data in FormManager.
56 field.set_label(FormManager::LabelForElement(element));
57
58 bool form_autofilled = form_manager_.FormWithNodeIsAutoFilled(node);
59 render_view_->Send(new ViewHostMsg_QueryFormFieldAutoFill(
60 render_view_->routing_id(), autofill_query_id_, form_autofilled, field));
61}
62
63void AutoFillHelper::RemoveAutocompleteSuggestion(
64 const WebKit::WebString& name, const WebKit::WebString& value) {
65 // The index of clear & options will have shifted down.
66 if (suggestions_clear_index_ != -1)
67 suggestions_clear_index_--;
68 if (suggestions_options_index_ != -1)
69 suggestions_options_index_--;
70
71 render_view_->Send(new ViewHostMsg_RemoveAutocompleteEntry(
72 render_view_->routing_id(), name, value));
73}
74
75void AutoFillHelper::SuggestionsReceived(int query_id,
76 const std::vector<string16>& values,
77 const std::vector<string16>& labels,
[email protected]a53e06832010-07-28 22:46:4178 const std::vector<string16>& icons,
[email protected]679f128f2010-07-22 22:57:4479 const std::vector<int>& unique_ids) {
80 WebKit::WebView* web_view = render_view_->webview();
81 if (!web_view || query_id != autofill_query_id_)
82 return;
83
84 // Any popup currently showing is now obsolete.
85 web_view->hidePopups();
86
87 // No suggestions: nothing to do.
88 if (values.empty())
89 return;
90
91 std::vector<string16> v(values);
92 std::vector<string16> l(labels);
[email protected]a53e06832010-07-28 22:46:4193 std::vector<string16> i(icons);
[email protected]679f128f2010-07-22 22:57:4494 std::vector<int> ids(unique_ids);
95 int separator_index = -1;
96
97 // The form has been auto-filled, so give the user the chance to clear the
98 // form. Append the 'Clear form' menu item.
99 if (form_manager_.FormWithNodeIsAutoFilled(autofill_query_node_)) {
100 v.push_back(l10n_util::GetStringUTF16(IDS_AUTOFILL_CLEAR_FORM_MENU_ITEM));
101 l.push_back(string16());
[email protected]a53e06832010-07-28 22:46:41102 i.push_back(string16());
[email protected]679f128f2010-07-22 22:57:44103 ids.push_back(0);
104 suggestions_clear_index_ = v.size() - 1;
105 separator_index = values.size();
106 }
107
108 // Only include "AutoFill Options" special menu item if we have AutoFill
109 // items, identified by |unique_ids| having at least one valid value.
110 bool show_options = false;
111 for (size_t i = 0; i < ids.size(); ++i) {
112 if (ids[i] != 0) {
113 show_options = true;
114 break;
115 }
116 }
117 if (show_options) {
118 // Append the 'AutoFill Options...' menu item.
119 v.push_back(l10n_util::GetStringUTF16(IDS_AUTOFILL_OPTIONS));
120 l.push_back(string16());
[email protected]a53e06832010-07-28 22:46:41121 i.push_back(string16());
[email protected]679f128f2010-07-22 22:57:44122 ids.push_back(0);
123 suggestions_options_index_ = v.size() - 1;
124 separator_index = values.size();
125 }
126
127 // Send to WebKit for display.
128 if (!v.empty()) {
129 web_view->applyAutoFillSuggestions(
[email protected]a53e06832010-07-28 22:46:41130 autofill_query_node_, v, l, i, ids, separator_index);
[email protected]679f128f2010-07-22 22:57:44131 }
132}
133
134void AutoFillHelper::FormDataFilled(int query_id,
135 const webkit_glue::FormData& form) {
136 if (!render_view_->webview() || query_id != autofill_query_id_)
137 return;
138
139 switch (autofill_action_) {
140 case AUTOFILL_FILL:
141 form_manager_.FillForm(form, autofill_query_node_);
142 break;
143 case AUTOFILL_PREVIEW:
144 form_manager_.PreviewForm(form);
145 break;
146 default:
147 NOTREACHED();
148 }
149 autofill_action_ = AUTOFILL_NONE;
150}
151
152void AutoFillHelper::DidSelectAutoFillSuggestion(const WebNode& node,
153 const WebString& value,
154 const WebString& label,
155 int unique_id) {
156 DidClearAutoFillSelection(node);
157 QueryAutoFillFormData(node, value, label, unique_id, AUTOFILL_PREVIEW);
158}
159
160void AutoFillHelper::DidAcceptAutoFillSuggestion(const WebNode& node,
161 const WebString& value,
162 const WebString& label,
163 int unique_id,
164 unsigned index) {
165 if (suggestions_options_index_ != -1 &&
166 index == static_cast<unsigned>(suggestions_options_index_)) {
167 // User selected 'AutoFill Options'.
168 render_view_->Send(new ViewHostMsg_ShowAutoFillDialog(
169 render_view_->routing_id()));
170 } else if (suggestions_clear_index_ != -1 &&
171 index == static_cast<unsigned>(suggestions_clear_index_)) {
172 // User selected 'Clear form'.
173 // The form has been auto-filled, so give the user the chance to clear the
174 // form.
175 form_manager_.ClearFormWithNode(node);
176 } else if (form_manager_.FormWithNodeIsAutoFilled(node) || !unique_id) {
177 // User selected an Autocomplete entry, so we fill directly.
178 WebInputElement element = node.toConst<WebInputElement>();
[email protected]f909f7b62010-08-03 17:42:32179
180 // Set the suggested value to update input element value immediately in UI.
181 // The |setValue| call has update delayed until element loses focus.
[email protected]1b1eecda2010-08-12 00:15:58182 string16 substring = value;
183 substring = substring.substr(0, element.maxLength());
184 element.setSuggestedValue(substring);
185 element.setValue(substring);
[email protected]679f128f2010-07-22 22:57:44186
187 WebFrame* webframe = node.document().frame();
188 if (webframe)
189 webframe->notifiyPasswordListenerOfAutocomplete(element);
190 } else {
191 // Fill the values for the whole form.
192 QueryAutoFillFormData(node, value, label, unique_id, AUTOFILL_FILL);
193 }
194
195 suggestions_clear_index_ = -1;
196 suggestions_options_index_ = -1;
197}
198
199void AutoFillHelper::DidClearAutoFillSelection(const WebNode& node) {
[email protected]940e621e82010-08-06 01:34:34200 form_manager_.ClearPreviewedFormWithNode(node);
[email protected]679f128f2010-07-22 22:57:44201}
202
203void AutoFillHelper::FrameContentsAvailable(WebFrame* frame) {
204 form_manager_.ExtractForms(frame);
205 SendForms(frame);
206}
207
208void AutoFillHelper::FrameWillClose(WebFrame* frame) {
209 form_manager_.ResetFrame(frame);
210}
211
[email protected]5041f982010-08-11 21:40:45212void AutoFillHelper::FrameDetached(WebFrame* frame) {
213 form_manager_.ResetFrame(frame);
214}
215
[email protected]e47aec52010-08-12 00:50:30216void AutoFillHelper::TextDidChangeInTextField(const WebInputElement& element) {
217 ShowSuggestions(element, false, true);
218}
219
220void AutoFillHelper::InputElementClicked(const WebInputElement& element,
221 bool already_focused) {
222 if (already_focused)
223 ShowSuggestions(element, true, false);
224}
225
226void AutoFillHelper::ShowSuggestions(
227 const WebInputElement& const_element,
228 bool autofill_on_empty_values,
229 bool requires_caret_at_end) {
230 // We need to call non-const methods.
231 WebInputElement element(const_element);
232 if (!element.isEnabledFormControl() ||
233 element.inputType() != WebInputElement::Text ||
234 element.inputType() == WebInputElement::Password ||
235 !element.autoComplete() || element.isReadOnly()) {
236 return;
237 }
238
239 WebString name = element.nameForAutofill();
240 if (name.isEmpty()) // If the field has no name, then we won't have values.
241 return;
242
243 // Don't attempt to autofill with values that are too large.
244 WebString value = element.value();
245 if (value.length() > kMaximumTextSizeForAutoFill)
246 return;
247
248 if (!autofill_on_empty_values && value.isEmpty())
249 return;
250
251 if (requires_caret_at_end &&
252 (element.selectionStart() != element.selectionEnd() ||
253 element.selectionEnd() != static_cast<int>(value.length()))) {
254 return;
255 }
256
257 QueryAutoFillSuggestions(element, name, value);
258}
259
[email protected]679f128f2010-07-22 22:57:44260void AutoFillHelper::QueryAutoFillFormData(const WebNode& node,
261 const WebString& value,
262 const WebString& label,
263 int unique_id,
264 AutoFillAction action) {
265 static int query_counter = 0;
266 autofill_query_id_ = query_counter++;
267
268 webkit_glue::FormData form;
269 const WebInputElement element = node.toConst<WebInputElement>();
270 if (!form_manager_.FindFormWithFormControlElement(
271 element, FormManager::REQUIRE_NONE, &form))
272 return;
273
274 autofill_action_ = action;
275 render_view_->Send(new ViewHostMsg_FillAutoFillFormData(
276 render_view_->routing_id(), autofill_query_id_, form, value, label,
277 unique_id));
278}
279
280void AutoFillHelper::SendForms(WebFrame* frame) {
281 // TODO(jhawkins): Use FormManager once we have strict ordering of form
282 // control elements in the cache.
283 WebKit::WebVector<WebFormElement> web_forms;
284 frame->forms(web_forms);
285
286 std::vector<webkit_glue::FormData> forms;
287 for (size_t i = 0; i < web_forms.size(); ++i) {
288 const WebFormElement& web_form = web_forms[i];
289
290 webkit_glue::FormData form;
291 if (FormManager::WebFormElementToFormData(
292 web_form, FormManager::REQUIRE_NONE, false, &form)) {
293 forms.push_back(form);
294 }
295 }
296
297 if (!forms.empty()) {
298 render_view_->Send(new ViewHostMsg_FormsSeen(render_view_->routing_id(),
299 forms));
300 }
301}