blob: 5418dca2abc71cb269125494476d1675ddd87166 [file] [log] [blame]
[email protected]c4a9e932011-03-05 04:05:551// Copyright (c) 2011 The Chromium Authors. All rights reserved.
[email protected]679f128f2010-07-22 22:57:442// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]78192082011-01-29 05:43:445#include "chrome/renderer/autofill/autofill_agent.h"
[email protected]679f128f2010-07-22 22:57:446
[email protected]8704f89b2011-04-15 00:30:057#include "base/message_loop.h"
[email protected]2377cafe2010-10-27 01:19:088#include "base/utf_string_conversions.h"
[email protected]19d6e1e82011-01-26 05:08:589#include "chrome/common/autofill_messages.h"
[email protected]2377cafe2010-10-27 01:19:0810#include "chrome/common/chrome_constants.h"
[email protected]78192082011-01-29 05:43:4411#include "chrome/renderer/autofill/password_autofill_manager.h"
[email protected]60916042011-03-19 00:43:3612#include "content/renderer/render_view.h"
[email protected]679f128f2010-07-22 22:57:4413#include "grit/generated_resources.h"
[email protected]8bd0fe62011-01-17 06:44:3714#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
15#include "third_party/WebKit/Source/WebKit/chromium/public/WebFormControlElement.h"
16#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
17#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputElement.h"
[email protected]a22f7e02011-02-09 07:15:3518#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h"
[email protected]8bd0fe62011-01-17 06:44:3719#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
[email protected]b6d81262011-01-13 17:36:0920#include "ui/base/keycodes/keyboard_codes.h"
[email protected]c051a1b2011-01-21 23:30:1721#include "ui/base/l10n/l10n_util.h"
[email protected]6a8ddba52010-09-05 04:38:0622#include "webkit/glue/form_data.h"
23#include "webkit/glue/form_field.h"
[email protected]679f128f2010-07-22 22:57:4424#include "webkit/glue/password_form.h"
25
26using WebKit::WebFormControlElement;
27using WebKit::WebFormElement;
28using WebKit::WebFrame;
29using WebKit::WebInputElement;
[email protected]7aab84b2010-11-20 01:04:2630using WebKit::WebKeyboardEvent;
[email protected]679f128f2010-07-22 22:57:4431using WebKit::WebNode;
32using WebKit::WebString;
[email protected]2a5b1732011-04-01 23:55:5533using webkit_glue::FormData;
[email protected]679f128f2010-07-22 22:57:4434
[email protected]e47aec52010-08-12 00:50:3035namespace {
36
37// The size above which we stop triggering autofill for an input text field
38// (so to avoid sending long strings through IPC).
[email protected]663bd9e2011-03-21 01:07:0139const size_t kMaximumTextSizeForAutofill = 1000;
[email protected]e47aec52010-08-12 00:50:3040
41} // namespace
42
[email protected]78192082011-01-29 05:43:4443namespace autofill {
44
[email protected]663bd9e2011-03-21 01:07:0145AutofillAgent::AutofillAgent(
[email protected]676126f72011-01-15 00:03:5146 RenderView* render_view,
[email protected]c4a9e932011-03-05 04:05:5547 PasswordAutofillManager* password_autofill_manager)
[email protected]676126f72011-01-15 00:03:5148 : RenderViewObserver(render_view),
[email protected]78192082011-01-29 05:43:4449 password_autofill_manager_(password_autofill_manager),
[email protected]679f128f2010-07-22 22:57:4450 autofill_query_id_(0),
51 autofill_action_(AUTOFILL_NONE),
[email protected]6bcd58a2010-11-25 02:31:2052 display_warning_if_disabled_(false),
[email protected]d77ddc8062010-11-24 01:14:0653 was_query_node_autofilled_(false),
[email protected]679f128f2010-07-22 22:57:4454 suggestions_clear_index_(-1),
[email protected]676126f72011-01-15 00:03:5155 suggestions_options_index_(-1),
56 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
[email protected]2a5b1732011-04-01 23:55:5557 render_view->webview()->setAutoFillClient(this);
[email protected]679f128f2010-07-22 22:57:4458}
59
[email protected]663bd9e2011-03-21 01:07:0160AutofillAgent::~AutofillAgent() {}
[email protected]d2f05d02011-01-27 18:51:0161
[email protected]663bd9e2011-03-21 01:07:0162bool AutofillAgent::OnMessageReceived(const IPC::Message& message) {
[email protected]676126f72011-01-15 00:03:5163 bool handled = true;
[email protected]663bd9e2011-03-21 01:07:0164 IPC_BEGIN_MESSAGE_MAP(AutofillAgent, message)
65 IPC_MESSAGE_HANDLER(AutofillMsg_SuggestionsReturned, OnSuggestionsReturned)
66 IPC_MESSAGE_HANDLER(AutofillMsg_FormDataFilled, OnFormDataFilled)
[email protected]676126f72011-01-15 00:03:5167 IPC_MESSAGE_UNHANDLED(handled = false)
68 IPC_END_MESSAGE_MAP()
69 return handled;
70}
71
[email protected]2fa18c22011-06-14 23:40:4372void AutofillAgent::DidFinishDocumentLoad(WebFrame* frame) {
[email protected]676126f72011-01-15 00:03:5173 // The document has now been fully loaded. Scan for forms to be sent up to
74 // the browser.
[email protected]2fa18c22011-06-14 23:40:4375 std::vector<webkit_glue::FormData> forms;
76 form_manager_.ExtractForms(frame, &forms);
77
78 if (!forms.empty())
79 Send(new AutofillHostMsg_FormsSeen(routing_id(), forms));
[email protected]676126f72011-01-15 00:03:5180}
81
[email protected]2fa18c22011-06-14 23:40:4382void AutofillAgent::FrameDetached(WebFrame* frame) {
[email protected]676126f72011-01-15 00:03:5183 form_manager_.ResetFrame(frame);
84}
85
[email protected]2fa18c22011-06-14 23:40:4386void AutofillAgent::FrameWillClose(WebFrame* frame) {
[email protected]676126f72011-01-15 00:03:5187 form_manager_.ResetFrame(frame);
88}
89
[email protected]2a5b1732011-04-01 23:55:5590void AutofillAgent::WillSubmitForm(WebFrame* frame,
91 const WebFormElement& form) {
92 FormData form_data;
93 if (FormManager::WebFormElementToFormData(
94 form,
95 FormManager::REQUIRE_AUTOCOMPLETE,
96 static_cast<FormManager::ExtractMask>(
97 FormManager::EXTRACT_VALUE | FormManager::EXTRACT_OPTION_TEXT),
98 &form_data)) {
99 Send(new AutofillHostMsg_FormSubmitted(routing_id(), form_data));
100 }
101}
102
[email protected]2fa18c22011-06-14 23:40:43103void AutofillAgent::FrameTranslated(WebFrame* frame) {
[email protected]676126f72011-01-15 00:03:51104 // The page is translated, so try to extract the form data again.
105 DidFinishDocumentLoad(frame);
106}
107
[email protected]663bd9e2011-03-21 01:07:01108bool AutofillAgent::InputElementClicked(const WebInputElement& element,
[email protected]78192082011-01-29 05:43:44109 bool was_focused,
110 bool is_focused) {
[email protected]676126f72011-01-15 00:03:51111 if (was_focused)
112 ShowSuggestions(element, true, false, true);
[email protected]2fa18c22011-06-14 23:40:43113
[email protected]676126f72011-01-15 00:03:51114 return false;
115}
116
[email protected]2fa18c22011-06-14 23:40:43117void AutofillAgent::didAcceptAutoFillSuggestion(const WebNode& node,
118 const WebString& value,
119 const WebString& label,
[email protected]78192082011-01-29 05:43:44120 int unique_id,
121 unsigned index) {
[email protected]663bd9e2011-03-21 01:07:01122 if (password_autofill_manager_->DidAcceptAutofillSuggestion(node, value))
[email protected]b43c1352011-03-04 23:55:34123 return;
124
[email protected]676126f72011-01-15 00:03:51125 if (suggestions_options_index_ != -1 &&
126 index == static_cast<unsigned>(suggestions_options_index_)) {
[email protected]663bd9e2011-03-21 01:07:01127 // User selected 'Autofill Options'.
128 Send(new AutofillHostMsg_ShowAutofillDialog(routing_id()));
[email protected]676126f72011-01-15 00:03:51129 } else if (suggestions_clear_index_ != -1 &&
130 index == static_cast<unsigned>(suggestions_clear_index_)) {
131 // User selected 'Clear form'.
132 form_manager_.ClearFormWithNode(node);
133 } else if (!unique_id) {
134 // User selected an Autocomplete entry, so we fill directly.
135 WebInputElement element = node.toConst<WebInputElement>();
136
137 string16 substring = value;
138 substring = substring.substr(0, element.maxLength());
[email protected]3b3b94d2011-03-10 09:52:20139 element.setValue(substring, true);
[email protected]676126f72011-01-15 00:03:51140
141 WebFrame* webframe = node.document().frame();
142 if (webframe)
143 webframe->notifiyPasswordListenerOfAutocomplete(element);
144 } else {
145 // Fill the values for the whole form.
[email protected]663bd9e2011-03-21 01:07:01146 FillAutofillFormData(node, unique_id, AUTOFILL_FILL);
[email protected]676126f72011-01-15 00:03:51147 }
148
149 suggestions_clear_index_ = -1;
150 suggestions_options_index_ = -1;
151}
152
[email protected]2fa18c22011-06-14 23:40:43153void AutofillAgent::didSelectAutoFillSuggestion(const WebNode& node,
154 const WebString& value,
155 const WebString& label,
[email protected]78192082011-01-29 05:43:44156 int unique_id) {
[email protected]676126f72011-01-15 00:03:51157 DCHECK_GE(unique_id, 0);
[email protected]663bd9e2011-03-21 01:07:01158 if (password_autofill_manager_->DidSelectAutofillSuggestion(node))
[email protected]fc1964a2011-01-20 19:33:07159 return;
[email protected]676126f72011-01-15 00:03:51160
161 didClearAutoFillSelection(node);
[email protected]663bd9e2011-03-21 01:07:01162 FillAutofillFormData(node, unique_id, AUTOFILL_PREVIEW);
[email protected]676126f72011-01-15 00:03:51163}
164
[email protected]2fa18c22011-06-14 23:40:43165void AutofillAgent::didClearAutoFillSelection(const WebNode& node) {
[email protected]676126f72011-01-15 00:03:51166 form_manager_.ClearPreviewedFormWithNode(node, was_query_node_autofilled_);
167}
168
[email protected]2fa18c22011-06-14 23:40:43169void AutofillAgent::removeAutocompleteSuggestion(const WebString& name,
170 const WebString& value) {
[email protected]679f128f2010-07-22 22:57:44171 // The index of clear & options will have shifted down.
172 if (suggestions_clear_index_ != -1)
173 suggestions_clear_index_--;
174 if (suggestions_options_index_ != -1)
175 suggestions_options_index_--;
176
[email protected]663bd9e2011-03-21 01:07:01177 Send(new AutofillHostMsg_RemoveAutocompleteEntry(routing_id(), name, value));
[email protected]679f128f2010-07-22 22:57:44178}
179
[email protected]2fa18c22011-06-14 23:40:43180void AutofillAgent::textFieldDidEndEditing(const WebInputElement& element) {
[email protected]78192082011-01-29 05:43:44181 password_autofill_manager_->TextFieldDidEndEditing(element);
[email protected]676126f72011-01-15 00:03:51182}
183
[email protected]2fa18c22011-06-14 23:40:43184void AutofillAgent::textFieldDidChange(const WebInputElement& element) {
[email protected]663bd9e2011-03-21 01:07:01185 // We post a task for doing the Autofill as the caret position is not set
[email protected]676126f72011-01-15 00:03:51186 // properly at this point (http://bugs.webkit.org/show_bug.cgi?id=16976) and
187 // it is needed to trigger autofill.
188 method_factory_.RevokeAll();
189 MessageLoop::current()->PostTask(
190 FROM_HERE,
191 method_factory_.NewRunnableMethod(
[email protected]663bd9e2011-03-21 01:07:01192 &AutofillAgent::TextFieldDidChangeImpl, element));
[email protected]676126f72011-01-15 00:03:51193}
194
[email protected]2fa18c22011-06-14 23:40:43195void AutofillAgent::TextFieldDidChangeImpl(const WebInputElement& element) {
[email protected]78192082011-01-29 05:43:44196 if (password_autofill_manager_->TextDidChangeInTextField(element))
[email protected]676126f72011-01-15 00:03:51197 return;
198
199 ShowSuggestions(element, false, true, false);
200}
201
[email protected]2fa18c22011-06-14 23:40:43202void AutofillAgent::textFieldDidReceiveKeyDown(const WebInputElement& element,
203 const WebKeyboardEvent& event) {
[email protected]78192082011-01-29 05:43:44204 if (password_autofill_manager_->TextFieldHandlingKeyDown(element, event))
[email protected]fc1964a2011-01-20 19:33:07205 return;
[email protected]676126f72011-01-15 00:03:51206
207 if (event.windowsKeyCode == ui::VKEY_DOWN ||
208 event.windowsKeyCode == ui::VKEY_UP)
209 ShowSuggestions(element, true, true, true);
210}
211
[email protected]663bd9e2011-03-21 01:07:01212void AutofillAgent::OnSuggestionsReturned(int query_id,
[email protected]78192082011-01-29 05:43:44213 const std::vector<string16>& values,
214 const std::vector<string16>& labels,
215 const std::vector<string16>& icons,
216 const std::vector<int>& unique_ids) {
[email protected]676126f72011-01-15 00:03:51217 WebKit::WebView* web_view = render_view()->webview();
[email protected]679f128f2010-07-22 22:57:44218 if (!web_view || query_id != autofill_query_id_)
219 return;
220
[email protected]43138472010-08-17 22:45:45221 if (values.empty()) {
222 // No suggestions, any popup currently showing is obsolete.
223 web_view->hidePopups();
[email protected]679f128f2010-07-22 22:57:44224 return;
[email protected]43138472010-08-17 22:45:45225 }
[email protected]679f128f2010-07-22 22:57:44226
227 std::vector<string16> v(values);
228 std::vector<string16> l(labels);
[email protected]a53e06832010-07-28 22:46:41229 std::vector<string16> i(icons);
[email protected]679f128f2010-07-22 22:57:44230 std::vector<int> ids(unique_ids);
231 int separator_index = -1;
232
[email protected]e417b1a2010-11-29 23:34:18233 if (ids[0] < 0 && ids.size() > 1) {
[email protected]1866d062010-11-16 06:19:56234 // If we received a warning instead of suggestions from autofill but regular
235 // suggestions from autocomplete, don't show the autofill warning.
236 v.erase(v.begin());
237 l.erase(l.begin());
238 i.erase(i.begin());
239 ids.erase(ids.begin());
240 }
241
[email protected]6bcd58a2010-11-25 02:31:20242 // If we were about to show a warning and we shouldn't, don't.
243 if (ids[0] < 0 && !display_warning_if_disabled_)
244 return;
245
[email protected]663bd9e2011-03-21 01:07:01246 // Only include "Autofill Options" special menu item if we have Autofill
[email protected]4f03a2d32010-12-01 01:49:36247 // items, identified by |unique_ids| having at least one valid value.
248 bool has_autofill_item = false;
249 for (size_t i = 0; i < ids.size(); ++i) {
250 if (ids[i] > 0) {
251 has_autofill_item = true;
252 break;
253 }
254 }
255
[email protected]679f128f2010-07-22 22:57:44256 // The form has been auto-filled, so give the user the chance to clear the
257 // form. Append the 'Clear form' menu item.
[email protected]4f03a2d32010-12-01 01:49:36258 if (has_autofill_item &&
[email protected]663bd9e2011-03-21 01:07:01259 form_manager_.FormWithNodeIsAutofilled(autofill_query_node_)) {
[email protected]679f128f2010-07-22 22:57:44260 v.push_back(l10n_util::GetStringUTF16(IDS_AUTOFILL_CLEAR_FORM_MENU_ITEM));
261 l.push_back(string16());
[email protected]a53e06832010-07-28 22:46:41262 i.push_back(string16());
[email protected]679f128f2010-07-22 22:57:44263 ids.push_back(0);
264 suggestions_clear_index_ = v.size() - 1;
[email protected]1866d062010-11-16 06:19:56265 separator_index = v.size() - 1;
[email protected]679f128f2010-07-22 22:57:44266 }
267
[email protected]4f03a2d32010-12-01 01:49:36268 if (has_autofill_item) {
[email protected]2377cafe2010-10-27 01:19:08269 // Append the 'Chrome Autofill options' menu item;
270 v.push_back(l10n_util::GetStringFUTF16(IDS_AUTOFILL_OPTIONS_POPUP,
271 WideToUTF16(chrome::kBrowserAppName)));
[email protected]679f128f2010-07-22 22:57:44272 l.push_back(string16());
[email protected]a53e06832010-07-28 22:46:41273 i.push_back(string16());
[email protected]679f128f2010-07-22 22:57:44274 ids.push_back(0);
275 suggestions_options_index_ = v.size() - 1;
276 separator_index = values.size();
277 }
278
279 // Send to WebKit for display.
[email protected]3b2ff662011-02-24 00:50:14280 if (!v.empty() && !autofill_query_node_.isNull() &&
[email protected]0dcec9592011-03-25 07:55:02281 autofill_query_node_.isFocusable()) {
[email protected]679f128f2010-07-22 22:57:44282 web_view->applyAutoFillSuggestions(
[email protected]a53e06832010-07-28 22:46:41283 autofill_query_node_, v, l, i, ids, separator_index);
[email protected]679f128f2010-07-22 22:57:44284 }
[email protected]468327f2010-10-04 20:02:29285
[email protected]663bd9e2011-03-21 01:07:01286 Send(new AutofillHostMsg_DidShowAutofillSuggestions(routing_id()));
[email protected]679f128f2010-07-22 22:57:44287}
288
[email protected]663bd9e2011-03-21 01:07:01289void AutofillAgent::OnFormDataFilled(int query_id,
[email protected]78192082011-01-29 05:43:44290 const webkit_glue::FormData& form) {
[email protected]676126f72011-01-15 00:03:51291 if (!render_view()->webview() || query_id != autofill_query_id_)
[email protected]679f128f2010-07-22 22:57:44292 return;
293
294 switch (autofill_action_) {
295 case AUTOFILL_FILL:
296 form_manager_.FillForm(form, autofill_query_node_);
297 break;
298 case AUTOFILL_PREVIEW:
[email protected]e7d8b512010-11-01 16:09:05299 form_manager_.PreviewForm(form, autofill_query_node_);
[email protected]679f128f2010-07-22 22:57:44300 break;
301 default:
302 NOTREACHED();
303 }
304 autofill_action_ = AUTOFILL_NONE;
[email protected]663bd9e2011-03-21 01:07:01305 Send(new AutofillHostMsg_DidFillAutofillFormData(routing_id()));
[email protected]e47aec52010-08-12 00:50:30306}
307
[email protected]663bd9e2011-03-21 01:07:01308void AutofillAgent::ShowSuggestions(const WebInputElement& element,
[email protected]78192082011-01-29 05:43:44309 bool autofill_on_empty_values,
310 bool requires_caret_at_end,
311 bool display_warning_if_disabled) {
[email protected]ee8d79f2011-03-14 20:15:18312 if (!element.isEnabled() || element.isReadOnly() || !element.autoComplete() ||
[email protected]62445812011-05-03 22:41:32313 !element.isTextField() || element.isPasswordField() ||
314 !element.suggestedValue().isEmpty())
[email protected]dc9144f2010-11-16 02:18:28315 return;
[email protected]dc9144f2010-11-16 02:18:28316
[email protected]1866d062010-11-16 06:19:56317 // If the field has no name, then we won't have values.
318 if (element.nameForAutofill().isEmpty())
[email protected]e47aec52010-08-12 00:50:30319 return;
320
321 // Don't attempt to autofill with values that are too large.
322 WebString value = element.value();
[email protected]663bd9e2011-03-21 01:07:01323 if (value.length() > kMaximumTextSizeForAutofill)
[email protected]e47aec52010-08-12 00:50:30324 return;
325
326 if (!autofill_on_empty_values && value.isEmpty())
327 return;
328
329 if (requires_caret_at_end &&
330 (element.selectionStart() != element.selectionEnd() ||
[email protected]1866d062010-11-16 06:19:56331 element.selectionEnd() != static_cast<int>(value.length())))
[email protected]e47aec52010-08-12 00:50:30332 return;
[email protected]e47aec52010-08-12 00:50:30333
[email protected]663bd9e2011-03-21 01:07:01334 QueryAutofillSuggestions(element, display_warning_if_disabled);
[email protected]77bb0da2010-11-20 01:55:30335}
336
[email protected]663bd9e2011-03-21 01:07:01337void AutofillAgent::QueryAutofillSuggestions(const WebNode& node,
[email protected]78192082011-01-29 05:43:44338 bool display_warning_if_disabled) {
[email protected]77bb0da2010-11-20 01:55:30339 static int query_counter = 0;
340 autofill_query_id_ = query_counter++;
341 autofill_query_node_ = node;
[email protected]6bcd58a2010-11-25 02:31:20342 display_warning_if_disabled_ = display_warning_if_disabled;
[email protected]77bb0da2010-11-20 01:55:30343
[email protected]3bb80e42010-11-29 22:27:16344 webkit_glue::FormData form;
[email protected]77bb0da2010-11-20 01:55:30345 webkit_glue::FormField field;
[email protected]7837be62011-01-18 23:45:08346 if (!FindFormAndFieldForNode(node, &form, &field)) {
347 // If we didn't find the cached form, at least let autocomplete have a shot
348 // at providing suggestions.
349 FormManager::WebFormControlElementToFormField(
350 node.toConst<WebFormControlElement>(), FormManager::EXTRACT_VALUE,
351 &field);
352 }
[email protected]77bb0da2010-11-20 01:55:30353
[email protected]663bd9e2011-03-21 01:07:01354 Send(new AutofillHostMsg_QueryFormFieldAutofill(
[email protected]676126f72011-01-15 00:03:51355 routing_id(), autofill_query_id_, form, field));
[email protected]e47aec52010-08-12 00:50:30356}
357
[email protected]663bd9e2011-03-21 01:07:01358void AutofillAgent::FillAutofillFormData(const WebNode& node,
[email protected]78192082011-01-29 05:43:44359 int unique_id,
[email protected]663bd9e2011-03-21 01:07:01360 AutofillAction action) {
[email protected]679f128f2010-07-22 22:57:44361 static int query_counter = 0;
362 autofill_query_id_ = query_counter++;
363
364 webkit_glue::FormData form;
[email protected]3bb80e42010-11-29 22:27:16365 webkit_glue::FormField field;
366 if (!FindFormAndFieldForNode(node, &form, &field))
[email protected]679f128f2010-07-22 22:57:44367 return;
368
369 autofill_action_ = action;
[email protected]e2aaa812011-03-08 18:46:04370 was_query_node_autofilled_ = field.is_autofilled;
[email protected]663bd9e2011-03-21 01:07:01371 Send(new AutofillHostMsg_FillAutofillFormData(
[email protected]676126f72011-01-15 00:03:51372 routing_id(), autofill_query_id_, form, field, unique_id));
[email protected]679f128f2010-07-22 22:57:44373}
374
[email protected]663bd9e2011-03-21 01:07:01375bool AutofillAgent::FindFormAndFieldForNode(const WebNode& node,
[email protected]78192082011-01-29 05:43:44376 webkit_glue::FormData* form,
377 webkit_glue::FormField* field) {
[email protected]3bb80e42010-11-29 22:27:16378 const WebInputElement& element = node.toConst<WebInputElement>();
[email protected]2fa18c22011-06-14 23:40:43379 if (!form_manager_.FindFormWithFormControlElement(element, form))
[email protected]3bb80e42010-11-29 22:27:16380 return false;
381
382 FormManager::WebFormControlElementToFormField(element,
383 FormManager::EXTRACT_VALUE,
384 field);
385
386 // WebFormControlElementToFormField does not scrape the DOM for the field
387 // label, so find the label here.
[email protected]2fa18c22011-06-14 23:40:43388 // TODO(isherman): Add form and field identities so we can use the cached form
[email protected]3bb80e42010-11-29 22:27:16389 // data in FormManager.
[email protected]e2aaa812011-03-08 18:46:04390 field->label = FormManager::LabelForElement(element);
[email protected]3bb80e42010-11-29 22:27:16391
392 return true;
393}
[email protected]78192082011-01-29 05:43:44394
395} // namespace autofill