Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(161)

Side by Side Diff: components/autofill/renderer/autofill_agent.cc

Issue 15949025: In components/autofill, move renderer/ to content/renderer (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Response to reviews Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 "components/autofill/renderer/autofill_agent.h"
6
7 #include "base/bind.h"
8 #include "base/command_line.h"
9 #include "base/message_loop.h"
10 #include "base/string_util.h"
11 #include "base/strings/string_split.h"
12 #include "base/time.h"
13 #include "base/utf_string_conversions.h"
14 #include "components/autofill/common/autocheckout_status.h"
15 #include "components/autofill/common/autofill_constants.h"
16 #include "components/autofill/common/autofill_messages.h"
17 #include "components/autofill/common/autofill_switches.h"
18 #include "components/autofill/common/form_data.h"
19 #include "components/autofill/common/form_data_predictions.h"
20 #include "components/autofill/common/form_field_data.h"
21 #include "components/autofill/common/web_element_descriptor.h"
22 #include "components/autofill/renderer/form_autofill_util.h"
23 #include "components/autofill/renderer/page_click_tracker.h"
24 #include "components/autofill/renderer/password_autofill_agent.h"
25 #include "content/public/common/password_form.h"
26 #include "content/public/common/ssl_status.h"
27 #include "content/public/renderer/render_view.h"
28 #include "grit/component_resources.h"
29 #include "third_party/WebKit/public/platform/WebRect.h"
30 #include "third_party/WebKit/public/platform/WebURLRequest.h"
31 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDataSource.h"
32 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
33 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFormControlElement .h"
34 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFormElement.h"
35 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
36 #include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h"
37 #include "third_party/WebKit/Source/WebKit/chromium/public/WebNode.h"
38 #include "third_party/WebKit/Source/WebKit/chromium/public/WebNodeCollection.h"
39 #include "third_party/WebKit/Source/WebKit/chromium/public/WebOptionElement.h"
40 #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
41 #include "ui/base/keycodes/keyboard_codes.h"
42 #include "ui/base/l10n/l10n_util.h"
43
44 using WebKit::WebAutofillClient;
45 using WebKit::WebFormControlElement;
46 using WebKit::WebFormElement;
47 using WebKit::WebFrame;
48 using WebKit::WebInputElement;
49 using WebKit::WebKeyboardEvent;
50 using WebKit::WebNode;
51 using WebKit::WebNodeCollection;
52 using WebKit::WebOptionElement;
53 using WebKit::WebString;
54
55 namespace {
56
57 // The size above which we stop triggering autofill for an input text field
58 // (so to avoid sending long strings through IPC).
59 const size_t kMaximumTextSizeForAutofill = 1000;
60
61 // The maximum number of data list elements to send to the browser process
62 // via IPC (to prevent long IPC messages).
63 const size_t kMaximumDataListSizeForAutofill = 30;
64
65 const int kAutocheckoutClickTimeout = 3;
66
67 void AppendDataListSuggestions(const WebKit::WebInputElement& element,
68 std::vector<base::string16>* values,
69 std::vector<base::string16>* labels,
70 std::vector<base::string16>* icons,
71 std::vector<int>* item_ids) {
72 WebNodeCollection options = element.dataListOptions();
73 if (options.isNull())
74 return;
75
76 base::string16 prefix = element.editingValue();
77 if (element.isMultiple() &&
78 element.formControlType() == WebString::fromUTF8("email")) {
79 std::vector<base::string16> parts;
80 base::SplitStringDontTrim(prefix, ',', &parts);
81 if (parts.size() > 0)
82 TrimWhitespace(parts[parts.size() - 1], TRIM_LEADING, &prefix);
83 }
84 for (WebOptionElement option = options.firstItem().to<WebOptionElement>();
85 !option.isNull(); option = options.nextItem().to<WebOptionElement>()) {
86 if (!StartsWith(option.value(), prefix, false) ||
87 option.value() == prefix ||
88 !element.isValidValue(option.value()))
89 continue;
90
91 values->push_back(option.value());
92 if (option.value() != option.label())
93 labels->push_back(option.label());
94 else
95 labels->push_back(base::string16());
96 icons->push_back(base::string16());
97 item_ids->push_back(WebAutofillClient::MenuItemIDDataListEntry);
98 }
99 }
100
101 // Trim the vectors before sending them to the browser process to ensure we
102 // don't send too much data through the IPC.
103 void TrimDataListsForIPC(std::vector<base::string16>* values,
104 std::vector<base::string16>* labels,
105 std::vector<base::string16>* icons,
106 std::vector<int>* unique_ids) {
107 // Limit the size of the vectors.
108 if (values->size() > kMaximumDataListSizeForAutofill) {
109 values->resize(kMaximumDataListSizeForAutofill);
110 labels->resize(kMaximumDataListSizeForAutofill);
111 icons->resize(kMaximumDataListSizeForAutofill);
112 unique_ids->resize(kMaximumDataListSizeForAutofill);
113 }
114
115 // Limit the size of the strings in the vectors
116 for (size_t i = 0; i < values->size(); ++i) {
117 if ((*values)[i].length() > kMaximumTextSizeForAutofill)
118 (*values)[i].resize(kMaximumTextSizeForAutofill);
119
120 if ((*labels)[i].length() > kMaximumTextSizeForAutofill)
121 (*labels)[i].resize(kMaximumTextSizeForAutofill);
122
123 if ((*icons)[i].length() > kMaximumTextSizeForAutofill)
124 (*icons)[i].resize(kMaximumTextSizeForAutofill);
125 }
126 }
127
128 gfx::RectF GetScaledBoundingBox(float scale, WebInputElement* element) {
129 gfx::Rect bounding_box(element->boundsInViewportSpace());
130 return gfx::RectF(bounding_box.x() * scale,
131 bounding_box.y() * scale,
132 bounding_box.width() * scale,
133 bounding_box.height() * scale);
134 }
135
136 } // namespace
137
138 namespace autofill {
139
140 AutofillAgent::AutofillAgent(content::RenderView* render_view,
141 PasswordAutofillAgent* password_autofill_agent)
142 : content::RenderViewObserver(render_view),
143 password_autofill_agent_(password_autofill_agent),
144 autofill_query_id_(0),
145 autofill_action_(AUTOFILL_NONE),
146 topmost_frame_(NULL),
147 web_view_(render_view->GetWebView()),
148 display_warning_if_disabled_(false),
149 was_query_node_autofilled_(false),
150 has_shown_autofill_popup_for_current_edit_(false),
151 did_set_node_text_(false),
152 autocheckout_click_in_progress_(false),
153 is_autocheckout_supported_(false),
154 has_new_forms_for_browser_(false),
155 ignore_text_changes_(false),
156 weak_ptr_factory_(this) {
157 render_view->GetWebView()->setAutofillClient(this);
158
159 // The PageClickTracker is a RenderViewObserver, and hence will be freed when
160 // the RenderView is destroyed.
161 new PageClickTracker(render_view, this);
162 }
163
164 AutofillAgent::~AutofillAgent() {}
165
166 bool AutofillAgent::OnMessageReceived(const IPC::Message& message) {
167 bool handled = true;
168 IPC_BEGIN_MESSAGE_MAP(AutofillAgent, message)
169 IPC_MESSAGE_HANDLER(AutofillMsg_GetAllForms, OnGetAllForms)
170 IPC_MESSAGE_HANDLER(AutofillMsg_SuggestionsReturned, OnSuggestionsReturned)
171 IPC_MESSAGE_HANDLER(AutofillMsg_FormDataFilled, OnFormDataFilled)
172 IPC_MESSAGE_HANDLER(AutofillMsg_FieldTypePredictionsAvailable,
173 OnFieldTypePredictionsAvailable)
174 IPC_MESSAGE_HANDLER(AutofillMsg_SetAutofillActionFill,
175 OnSetAutofillActionFill)
176 IPC_MESSAGE_HANDLER(AutofillMsg_ClearForm,
177 OnClearForm)
178 IPC_MESSAGE_HANDLER(AutofillMsg_SetAutofillActionPreview,
179 OnSetAutofillActionPreview)
180 IPC_MESSAGE_HANDLER(AutofillMsg_ClearPreviewedForm,
181 OnClearPreviewedForm)
182 IPC_MESSAGE_HANDLER(AutofillMsg_SetNodeText,
183 OnSetNodeText)
184 IPC_MESSAGE_HANDLER(AutofillMsg_AcceptDataListSuggestion,
185 OnAcceptDataListSuggestion)
186 IPC_MESSAGE_HANDLER(AutofillMsg_AcceptPasswordAutofillSuggestion,
187 OnAcceptPasswordAutofillSuggestion)
188 IPC_MESSAGE_HANDLER(AutofillMsg_RequestAutocompleteResult,
189 OnRequestAutocompleteResult)
190 IPC_MESSAGE_HANDLER(AutofillMsg_FillFormsAndClick,
191 OnFillFormsAndClick)
192 IPC_MESSAGE_HANDLER(AutofillMsg_AutocheckoutSupported,
193 OnAutocheckoutSupported)
194 IPC_MESSAGE_UNHANDLED(handled = false)
195 IPC_END_MESSAGE_MAP()
196 return handled;
197 }
198
199 void AutofillAgent::DidFinishDocumentLoad(WebFrame* frame) {
200 // Record timestamp on document load. This is used to record overhead of
201 // Autofill feature.
202 forms_seen_timestamp_ = base::TimeTicks::Now();
203
204 // The document has now been fully loaded. Scan for forms to be sent up to
205 // the browser.
206 std::vector<FormData> forms;
207 bool has_more_forms = false;
208 if (!frame->parent()) {
209 topmost_frame_ = frame;
210 form_elements_.clear();
211 has_more_forms = form_cache_.ExtractFormsAndFormElements(
212 *frame, kRequiredAutofillFields, &forms, &form_elements_);
213 } else {
214 form_cache_.ExtractForms(*frame, &forms);
215 }
216
217 autofill::FormsSeenState state = has_more_forms ?
218 autofill::PARTIAL_FORMS_SEEN : autofill::NO_SPECIAL_FORMS_SEEN;
219
220 // Always communicate to browser process for topmost frame.
221 if (!forms.empty() || !frame->parent()) {
222 Send(new AutofillHostMsg_FormsSeen(routing_id(), forms,
223 forms_seen_timestamp_,
224 state));
225 }
226 }
227
228 void AutofillAgent::DidStartProvisionalLoad(WebFrame* frame) {
229 if (!frame->parent()) {
230 is_autocheckout_supported_ = false;
231 topmost_frame_ = NULL;
232 if (click_timer_.IsRunning()) {
233 click_timer_.Stop();
234 autocheckout_click_in_progress_ = true;
235 }
236 }
237 }
238
239 void AutofillAgent::DidFailProvisionalLoad(WebFrame* frame,
240 const WebKit::WebURLError& error) {
241 if (!frame->parent() && autocheckout_click_in_progress_) {
242 autocheckout_click_in_progress_ = false;
243 ClickFailed();
244 }
245 }
246
247 void AutofillAgent::DidCommitProvisionalLoad(WebFrame* frame,
248 bool is_new_navigation) {
249 in_flight_request_form_.reset();
250 if (!frame->parent())
251 autocheckout_click_in_progress_ = false;
252 }
253
254 void AutofillAgent::FrameDetached(WebFrame* frame) {
255 form_cache_.ResetFrame(*frame);
256 if (!frame->parent()) {
257 // |frame| is about to be destroyed so we need to clear |top_most_frame_|.
258 topmost_frame_ = NULL;
259 click_timer_.Stop();
260 }
261 }
262
263 void AutofillAgent::WillSubmitForm(WebFrame* frame,
264 const WebFormElement& form) {
265 FormData form_data;
266 if (WebFormElementToFormData(form,
267 WebFormControlElement(),
268 REQUIRE_AUTOCOMPLETE,
269 static_cast<ExtractMask>(
270 EXTRACT_VALUE | EXTRACT_OPTION_TEXT),
271 &form_data,
272 NULL)) {
273 Send(new AutofillHostMsg_FormSubmitted(routing_id(), form_data,
274 base::TimeTicks::Now()));
275 }
276 }
277
278 void AutofillAgent::ZoomLevelChanged() {
279 // Any time the zoom level changes, the page's content moves, so any Autofill
280 // popups should be hidden. This is only needed for the new Autofill UI
281 // because WebKit already knows to hide the old UI when this occurs.
282 HideHostAutofillUi();
283 }
284
285 void AutofillAgent::FocusedNodeChanged(const WebKit::WebNode& node) {
286 if (node.isNull() || !node.isElementNode())
287 return;
288
289 WebKit::WebElement web_element = node.toConst<WebKit::WebElement>();
290
291 if (!web_element.document().frame())
292 return;
293
294 const WebInputElement* element = toWebInputElement(&web_element);
295
296 if (!element || !element->isEnabled() || element->isReadOnly() ||
297 !element->isTextField() || element->isPasswordField())
298 return;
299
300 element_ = *element;
301
302 MaybeShowAutocheckoutBubble();
303 }
304
305 void AutofillAgent::MaybeShowAutocheckoutBubble() {
306 if (element_.isNull() || !element_.focused())
307 return;
308
309 FormData form;
310 FormFieldData field;
311 // This must be called to short circuit this method if it fails.
312 if (!FindFormAndFieldForInputElement(element_, &form, &field, REQUIRE_NONE))
313 return;
314
315 form.ssl_status = render_view()->GetSSLStatusOfFrame(
316 element_.document().frame());
317
318 Send(new AutofillHostMsg_MaybeShowAutocheckoutBubble(
319 routing_id(),
320 form,
321 GetScaledBoundingBox(web_view_->pageScaleFactor(), &element_)));
322 }
323
324 void AutofillAgent::DidChangeScrollOffset(WebKit::WebFrame*) {
325 HideAutofillUi();
326 }
327
328 void AutofillAgent::didRequestAutocomplete(WebKit::WebFrame* frame,
329 const WebFormElement& form) {
330 FormData form_data;
331 if (!in_flight_request_form_.isNull() ||
332 !WebFormElementToFormData(form,
333 WebFormControlElement(),
334 REQUIRE_AUTOCOMPLETE,
335 EXTRACT_OPTIONS,
336 &form_data,
337 NULL)) {
338 WebFormElement(form).finishRequestAutocomplete(
339 WebFormElement::AutocompleteResultErrorDisabled);
340 return;
341 }
342
343 // Cancel any pending Autofill requests and hide any currently showing popups.
344 ++autofill_query_id_;
345 HideAutofillUi();
346
347 in_flight_request_form_ = form;
348 // Add SSL Status in the formdata to let browser process alert user
349 // appropriately using browser UI.
350 form_data.ssl_status = render_view()->GetSSLStatusOfFrame(frame);
351 Send(new AutofillHostMsg_RequestAutocomplete(
352 routing_id(),
353 form_data,
354 frame->document().url()));
355 }
356
357 void AutofillAgent::setIgnoreTextChanges(bool ignore) {
358 ignore_text_changes_ = ignore;
359 }
360
361 void AutofillAgent::InputElementClicked(const WebInputElement& element,
362 bool was_focused,
363 bool is_focused) {
364 if (was_focused)
365 ShowSuggestions(element, true, false, true);
366 }
367
368 void AutofillAgent::InputElementLostFocus() {
369 HideHostAutofillUi();
370 }
371
372 void AutofillAgent::didAcceptAutofillSuggestion(const WebNode& node,
373 const WebString& value,
374 const WebString& label,
375 int item_id,
376 unsigned index) {
377 if (password_autofill_agent_->DidAcceptAutofillSuggestion(node, value))
378 return;
379
380 DCHECK(node == element_);
381
382 switch (item_id) {
383 case WebAutofillClient::MenuItemIDWarningMessage:
384 case WebAutofillClient::MenuItemIDSeparator:
385 NOTREACHED();
386 break;
387 case WebAutofillClient::MenuItemIDAutofillOptions:
388 // User selected 'Autofill Options'.
389 Send(new AutofillHostMsg_ShowAutofillDialog(routing_id()));
390 break;
391 case WebAutofillClient::MenuItemIDClearForm:
392 // User selected 'Clear form'.
393 form_cache_.ClearFormWithElement(element_);
394 break;
395 case WebAutofillClient::MenuItemIDAutocompleteEntry:
396 case WebAutofillClient::MenuItemIDPasswordEntry:
397 // User selected an Autocomplete or password entry, so we fill directly.
398 SetNodeText(value, &element_);
399 break;
400 case WebAutofillClient::MenuItemIDDataListEntry:
401 AcceptDataListSuggestion(value);
402 break;
403 default:
404 // A positive item_id is a unique id for an autofill (vs. autocomplete)
405 // suggestion.
406 DCHECK_GT(item_id, 0);
407 // Fill the values for the whole form.
408 FillAutofillFormData(node, item_id, AUTOFILL_FILL);
409 }
410 }
411
412 void AutofillAgent::didSelectAutofillSuggestion(const WebNode& node,
413 const WebString& value,
414 const WebString& label,
415 int item_id) {
416 if (password_autofill_agent_->DidSelectAutofillSuggestion(node))
417 return;
418
419 didClearAutofillSelection(node);
420
421 if (item_id > 0)
422 FillAutofillFormData(node, item_id, AUTOFILL_PREVIEW);
423 }
424
425 void AutofillAgent::didClearAutofillSelection(const WebNode& node) {
426 if (password_autofill_agent_->DidClearAutofillSelection(node))
427 return;
428
429 if (!element_.isNull() && node == element_) {
430 ClearPreviewedFormWithElement(element_, was_query_node_autofilled_);
431 } else {
432 // TODO(isherman): There seem to be rare cases where this code *is*
433 // reachable: see [ http://crbug.com/96321#c6 ]. Ideally we would
434 // understand those cases and fix the code to avoid them. However, so far I
435 // have been unable to reproduce such a case locally. If you hit this
436 // NOTREACHED(), please file a bug against me.
437 NOTREACHED();
438 }
439 }
440
441 void AutofillAgent::removeAutocompleteSuggestion(const WebString& name,
442 const WebString& value) {
443 Send(new AutofillHostMsg_RemoveAutocompleteEntry(routing_id(), name, value));
444 }
445
446 void AutofillAgent::textFieldDidEndEditing(const WebInputElement& element) {
447 password_autofill_agent_->TextFieldDidEndEditing(element);
448 has_shown_autofill_popup_for_current_edit_ = false;
449 Send(new AutofillHostMsg_DidEndTextFieldEditing(routing_id()));
450 }
451
452 void AutofillAgent::textFieldDidChange(const WebInputElement& element) {
453 if (ignore_text_changes_)
454 return;
455
456 if (did_set_node_text_) {
457 did_set_node_text_ = false;
458 return;
459 }
460
461 // We post a task for doing the Autofill as the caret position is not set
462 // properly at this point (http://bugs.webkit.org/show_bug.cgi?id=16976) and
463 // it is needed to trigger autofill.
464 weak_ptr_factory_.InvalidateWeakPtrs();
465 base::MessageLoop::current()->PostTask(
466 FROM_HERE,
467 base::Bind(&AutofillAgent::TextFieldDidChangeImpl,
468 weak_ptr_factory_.GetWeakPtr(),
469 element));
470 }
471
472 void AutofillAgent::TextFieldDidChangeImpl(const WebInputElement& element) {
473 // If the element isn't focused then the changes don't matter. This check is
474 // required to properly handle IME interactions.
475 if (!element.focused())
476 return;
477
478 if (password_autofill_agent_->TextDidChangeInTextField(element)) {
479 element_ = element;
480 return;
481 }
482
483 ShowSuggestions(element, false, true, false);
484
485 FormData form;
486 FormFieldData field;
487 if (FindFormAndFieldForInputElement(element, &form, &field, REQUIRE_NONE)) {
488 Send(new AutofillHostMsg_TextFieldDidChange(routing_id(), form, field,
489 base::TimeTicks::Now()));
490 }
491 }
492
493 void AutofillAgent::textFieldDidReceiveKeyDown(const WebInputElement& element,
494 const WebKeyboardEvent& event) {
495 if (password_autofill_agent_->TextFieldHandlingKeyDown(element, event)) {
496 element_ = element;
497 return;
498 }
499
500 if (event.windowsKeyCode == ui::VKEY_DOWN ||
501 event.windowsKeyCode == ui::VKEY_UP)
502 ShowSuggestions(element, true, true, true);
503 }
504
505 void AutofillAgent::OnSuggestionsReturned(
506 int query_id,
507 const std::vector<base::string16>& values,
508 const std::vector<base::string16>& labels,
509 const std::vector<base::string16>& icons,
510 const std::vector<int>& unique_ids) {
511 if (query_id != autofill_query_id_)
512 return;
513
514 if (element_.isNull() || !element_.isFocusable())
515 return;
516
517 std::vector<base::string16> v(values);
518 std::vector<base::string16> l(labels);
519 std::vector<base::string16> i(icons);
520 std::vector<int> ids(unique_ids);
521
522 if (!element_.autoComplete() && !v.empty()) {
523 // If autofill is disabled and we had suggestions, show a warning instead.
524 v.assign(1, l10n_util::GetStringUTF16(IDS_AUTOFILL_WARNING_FORM_DISABLED));
525 l.assign(1, base::string16());
526 i.assign(1, base::string16());
527 ids.assign(1, WebAutofillClient::MenuItemIDWarningMessage);
528 } else if (ids.size() > 1 &&
529 ids[0] == WebAutofillClient::MenuItemIDWarningMessage) {
530 // If we received an autofill warning plus some autocomplete suggestions,
531 // remove the autofill warning.
532 v.erase(v.begin());
533 l.erase(l.begin());
534 i.erase(i.begin());
535 ids.erase(ids.begin());
536 }
537
538 // If we were about to show a warning and we shouldn't, don't.
539 if (!display_warning_if_disabled_ && !v.empty() &&
540 ids[0] == WebAutofillClient::MenuItemIDWarningMessage) {
541 v.clear();
542 l.clear();
543 i.clear();
544 ids.clear();
545 }
546
547 // Only include "Autofill Options" special menu item if we have Autofill
548 // items, identified by |unique_ids| having at least one valid value.
549 bool has_autofill_item = false;
550 for (size_t i = 0; i < ids.size(); ++i) {
551 if (ids[i] > 0) {
552 has_autofill_item = true;
553 break;
554 }
555 }
556
557 if (has_autofill_item) {
558 v.push_back(base::string16());
559 l.push_back(base::string16());
560 i.push_back(base::string16());
561 ids.push_back(WebAutofillClient::MenuItemIDSeparator);
562
563 if (FormWithElementIsAutofilled(element_)) {
564 // The form has been auto-filled, so give the user the chance to clear the
565 // form. Append the 'Clear form' menu item.
566 v.push_back(l10n_util::GetStringUTF16(IDS_AUTOFILL_CLEAR_FORM_MENU_ITEM));
567 l.push_back(base::string16());
568 i.push_back(base::string16());
569 ids.push_back(WebAutofillClient::MenuItemIDClearForm);
570 }
571
572 // Append the 'Chrome Autofill options' menu item;
573 v.push_back(l10n_util::GetStringUTF16(IDS_AUTOFILL_OPTIONS_POPUP));
574 l.push_back(base::string16());
575 i.push_back(base::string16());
576 ids.push_back(WebAutofillClient::MenuItemIDAutofillOptions);
577 }
578
579 CombineDataListEntriesAndShow(element_, v, l, i, ids, has_autofill_item);
580 }
581
582 void AutofillAgent::CombineDataListEntriesAndShow(
583 const WebKit::WebInputElement& element,
584 const std::vector<base::string16>& values,
585 const std::vector<base::string16>& labels,
586 const std::vector<base::string16>& icons,
587 const std::vector<int>& item_ids,
588 bool has_autofill_item) {
589 std::vector<base::string16> v;
590 std::vector<base::string16> l;
591 std::vector<base::string16> i;
592 std::vector<int> ids;
593
594 AppendDataListSuggestions(element, &v, &l, &i, &ids);
595
596 // If there are both <datalist> items and Autofill suggestions, add a
597 // separator between them.
598 if (!v.empty() && !values.empty()) {
599 v.push_back(base::string16());
600 l.push_back(base::string16());
601 i.push_back(base::string16());
602 ids.push_back(WebAutofillClient::MenuItemIDSeparator);
603 }
604
605 // Append the Autofill suggestions.
606 v.insert(v.end(), values.begin(), values.end());
607 l.insert(l.end(), labels.begin(), labels.end());
608 i.insert(i.end(), icons.begin(), icons.end());
609 ids.insert(ids.end(), item_ids.begin(), item_ids.end());
610
611 if (v.empty()) {
612 // No suggestions, any popup currently showing is obsolete.
613 HideAutofillUi();
614 return;
615 }
616
617 WebKit::WebView* web_view = render_view()->GetWebView();
618 if (!web_view)
619 return;
620
621 // Send to WebKit for display.
622 web_view->applyAutofillSuggestions(element, v, l, i, ids);
623
624 Send(new AutofillHostMsg_DidShowAutofillSuggestions(
625 routing_id(),
626 has_autofill_item && !has_shown_autofill_popup_for_current_edit_));
627 has_shown_autofill_popup_for_current_edit_ |= has_autofill_item;
628 }
629
630 void AutofillAgent::AcceptDataListSuggestion(
631 const base::string16& suggested_value) {
632 base::string16 new_value = suggested_value;
633 // If this element takes multiple values then replace the last part with
634 // the suggestion.
635 if (element_.isMultiple() &&
636 element_.formControlType() == WebString::fromUTF8("email")) {
637 std::vector<base::string16> parts;
638
639 base::SplitStringDontTrim(element_.editingValue(), ',', &parts);
640 if (parts.size() == 0)
641 parts.push_back(base::string16());
642
643 base::string16 last_part = parts.back();
644 // We want to keep just the leading whitespace.
645 for (size_t i = 0; i < last_part.size(); ++i) {
646 if (!IsWhitespace(last_part[i])) {
647 last_part = last_part.substr(0, i);
648 break;
649 }
650 }
651 last_part.append(suggested_value);
652 parts[parts.size() - 1] = last_part;
653
654 new_value = JoinString(parts, ',');
655 }
656 SetNodeText(new_value, &element_);
657 }
658
659 void AutofillAgent::OnFormDataFilled(int query_id,
660 const FormData& form) {
661 if (!render_view()->GetWebView() || query_id != autofill_query_id_)
662 return;
663
664 was_query_node_autofilled_ = element_.isAutofilled();
665
666 switch (autofill_action_) {
667 case AUTOFILL_FILL:
668 FillForm(form, element_);
669 Send(new AutofillHostMsg_DidFillAutofillFormData(routing_id(),
670 base::TimeTicks::Now()));
671 break;
672 case AUTOFILL_PREVIEW:
673 PreviewForm(form, element_);
674 Send(new AutofillHostMsg_DidPreviewAutofillFormData(routing_id()));
675 break;
676 default:
677 NOTREACHED();
678 }
679 autofill_action_ = AUTOFILL_NONE;
680 }
681
682 void AutofillAgent::OnFieldTypePredictionsAvailable(
683 const std::vector<FormDataPredictions>& forms) {
684 for (size_t i = 0; i < forms.size(); ++i) {
685 form_cache_.ShowPredictions(forms[i]);
686 }
687 }
688
689 void AutofillAgent::OnSetAutofillActionFill() {
690 autofill_action_ = AUTOFILL_FILL;
691 }
692
693 void AutofillAgent::OnClearForm() {
694 form_cache_.ClearFormWithElement(element_);
695 }
696
697 void AutofillAgent::OnSetAutofillActionPreview() {
698 autofill_action_ = AUTOFILL_PREVIEW;
699 }
700
701 void AutofillAgent::OnClearPreviewedForm() {
702 didClearAutofillSelection(element_);
703 }
704
705 void AutofillAgent::OnSetNodeText(const base::string16& value) {
706 SetNodeText(value, &element_);
707 }
708
709 void AutofillAgent::OnAcceptDataListSuggestion(const base::string16& value) {
710 AcceptDataListSuggestion(value);
711 }
712
713 void AutofillAgent::OnAcceptPasswordAutofillSuggestion(
714 const base::string16& value) {
715 // We need to make sure this is handled here because the browser process
716 // skipped it handling because it believed it would be handled here. If it
717 // isn't handled here then the browser logic needs to be updated.
718 bool handled = password_autofill_agent_->DidAcceptAutofillSuggestion(
719 element_,
720 value);
721 DCHECK(handled);
722 }
723
724 void AutofillAgent::OnGetAllForms() {
725 form_elements_.clear();
726
727 // Force fetch all non empty forms.
728 std::vector<FormData> forms;
729 form_cache_.ExtractFormsAndFormElements(
730 *topmost_frame_, 0, &forms, &form_elements_);
731
732 // OnGetAllForms should only be called if AutofillAgent reported to
733 // AutofillManager that there are more forms
734 DCHECK(!forms.empty());
735
736 // Report to AutofillManager that all forms are being sent.
737 Send(new AutofillHostMsg_FormsSeen(routing_id(), forms,
738 forms_seen_timestamp_,
739 NO_SPECIAL_FORMS_SEEN));
740 }
741
742 void AutofillAgent::OnRequestAutocompleteResult(
743 WebFormElement::AutocompleteResult result, const FormData& form_data) {
744 if (in_flight_request_form_.isNull())
745 return;
746
747 if (result == WebFormElement::AutocompleteResultSuccess)
748 FillFormIncludingNonFocusableElements(form_data, in_flight_request_form_);
749
750 in_flight_request_form_.finishRequestAutocomplete(result);
751 in_flight_request_form_.reset();
752 }
753
754 void AutofillAgent::OnFillFormsAndClick(
755 const std::vector<FormData>& forms,
756 const WebElementDescriptor& click_element_descriptor) {
757 DCHECK_EQ(forms.size(), form_elements_.size());
758
759 // Fill the form.
760 for (size_t i = 0; i < forms.size(); ++i)
761 FillFormIncludingNonFocusableElements(forms[i], form_elements_[i]);
762
763 // Exit early if there is nothing to click.
764 if (click_element_descriptor.retrieval_method == WebElementDescriptor::NONE)
765 return;
766
767 // It's possible that clicking the element to proceed in an Autocheckout
768 // flow will not actually proceed to the next step in the flow, e.g. there
769 // is a new required field that Autocheckout does not know how to fill. In
770 // order to capture this case and present the user with an error a timer is
771 // set that informs the browser of the error. |click_timer_| has to be started
772 // before clicking so it can start before DidStartProvisionalLoad started.
773 click_timer_.Start(FROM_HERE,
774 base::TimeDelta::FromSeconds(kAutocheckoutClickTimeout),
775 this,
776 &AutofillAgent::ClickFailed);
777 if (!ClickElement(topmost_frame_->document(),
778 click_element_descriptor)) {
779 click_timer_.Stop();
780 Send(new AutofillHostMsg_ClickFailed(routing_id(),
781 MISSING_ADVANCE));
782 }
783 }
784
785 void AutofillAgent::OnAutocheckoutSupported() {
786 is_autocheckout_supported_ = true;
787 if (has_new_forms_for_browser_)
788 MaybeSendDynamicFormsSeen();
789 MaybeShowAutocheckoutBubble();
790 }
791
792 void AutofillAgent::ClickFailed() {
793 Send(new AutofillHostMsg_ClickFailed(routing_id(),
794 CANNOT_PROCEED));
795 }
796
797 void AutofillAgent::ShowSuggestions(const WebInputElement& element,
798 bool autofill_on_empty_values,
799 bool requires_caret_at_end,
800 bool display_warning_if_disabled) {
801 if (!element.isEnabled() || element.isReadOnly() || !element.isTextField() ||
802 element.isPasswordField() || !element.suggestedValue().isEmpty())
803 return;
804
805 // Don't attempt to autofill with values that are too large or if filling
806 // criteria are not met.
807 WebString value = element.editingValue();
808 if (value.length() > kMaximumTextSizeForAutofill ||
809 (!autofill_on_empty_values && value.isEmpty()) ||
810 (requires_caret_at_end &&
811 (element.selectionStart() != element.selectionEnd() ||
812 element.selectionEnd() != static_cast<int>(value.length())))) {
813 // Any popup currently showing is obsolete.
814 HideAutofillUi();
815 return;
816 }
817
818 element_ = element;
819 if (password_autofill_agent_->ShowSuggestions(element))
820 return;
821
822 // If autocomplete is disabled at the form level, then we might want to show a
823 // warning in place of suggestions. However, if autocomplete is disabled
824 // specifically for this field, we never want to show a warning. Otherwise,
825 // we might interfere with custom popups (e.g. search suggestions) used by the
826 // website. Note that we cannot use the WebKit method element.autoComplete()
827 // as it does not allow us to distinguish the case where autocomplete is
828 // disabled for *both* the element and for the form.
829 // Also, if the field has no name, then we won't have values.
830 const base::string16 autocomplete_attribute =
831 element.getAttribute("autocomplete");
832 if (LowerCaseEqualsASCII(autocomplete_attribute, "off") ||
833 element.nameForAutofill().isEmpty()) {
834 CombineDataListEntriesAndShow(element, std::vector<base::string16>(),
835 std::vector<base::string16>(),
836 std::vector<base::string16>(),
837 std::vector<int>(), false);
838 return;
839 }
840
841 QueryAutofillSuggestions(element, display_warning_if_disabled);
842 }
843
844 void AutofillAgent::QueryAutofillSuggestions(const WebInputElement& element,
845 bool display_warning_if_disabled) {
846 if (!element.document().frame())
847 return;
848
849 static int query_counter = 0;
850 autofill_query_id_ = query_counter++;
851 display_warning_if_disabled_ = display_warning_if_disabled;
852
853 // If autocomplete is disabled at the form level, we want to see if there
854 // would have been any suggestions were it enabled, so that we can show a
855 // warning. Otherwise, we want to ignore fields that disable autocomplete, so
856 // that the suggestions list does not include suggestions for these form
857 // fields -- see comment 1 on http://crbug.com/69914
858 // Rather than testing the form's autocomplete enabled state, we test the
859 // element's state. The DCHECK below ensures that this is equivalent.
860 DCHECK(element.autoComplete() || !element.form().autoComplete());
861 const RequirementsMask requirements =
862 element.autoComplete() ? REQUIRE_AUTOCOMPLETE : REQUIRE_NONE;
863
864 FormData form;
865 FormFieldData field;
866 if (!FindFormAndFieldForInputElement(element, &form, &field, requirements)) {
867 // If we didn't find the cached form, at least let autocomplete have a shot
868 // at providing suggestions.
869 WebFormControlElementToFormField(element, EXTRACT_VALUE, &field);
870 }
871
872 gfx::RectF bounding_box_scaled =
873 GetScaledBoundingBox(web_view_->pageScaleFactor(), &element_);
874
875 // Find the datalist values and send them to the browser process.
876 std::vector<base::string16> data_list_values;
877 std::vector<base::string16> data_list_labels;
878 std::vector<base::string16> data_list_icons;
879 std::vector<int> data_list_unique_ids;
880 AppendDataListSuggestions(element_,
881 &data_list_values,
882 &data_list_labels,
883 &data_list_icons,
884 &data_list_unique_ids);
885
886 TrimDataListsForIPC(&data_list_values,
887 &data_list_labels,
888 &data_list_icons,
889 &data_list_unique_ids);
890
891 Send(new AutofillHostMsg_SetDataList(routing_id(),
892 data_list_values,
893 data_list_labels,
894 data_list_icons,
895 data_list_unique_ids));
896
897 // Add SSL Status in the formdata to let browser process alert user
898 // appropriately using browser UI.
899 form.ssl_status = render_view()->GetSSLStatusOfFrame(
900 element.document().frame());
901 Send(new AutofillHostMsg_QueryFormFieldAutofill(routing_id(),
902 autofill_query_id_,
903 form,
904 field,
905 bounding_box_scaled,
906 display_warning_if_disabled));
907 }
908
909 void AutofillAgent::FillAutofillFormData(const WebNode& node,
910 int unique_id,
911 AutofillAction action) {
912 DCHECK_GT(unique_id, 0);
913
914 static int query_counter = 0;
915 autofill_query_id_ = query_counter++;
916
917 FormData form;
918 FormFieldData field;
919 if (!FindFormAndFieldForInputElement(node.toConst<WebInputElement>(), &form,
920 &field, REQUIRE_AUTOCOMPLETE)) {
921 return;
922 }
923
924 autofill_action_ = action;
925 Send(new AutofillHostMsg_FillAutofillFormData(
926 routing_id(), autofill_query_id_, form, field, unique_id));
927 }
928
929 void AutofillAgent::SetNodeText(const base::string16& value,
930 WebKit::WebInputElement* node) {
931 did_set_node_text_ = true;
932 base::string16 substring = value;
933 substring = substring.substr(0, node->maxLength());
934
935 node->setEditingValue(substring);
936 }
937
938 void AutofillAgent::HideAutofillUi() {
939 WebKit::WebView* web_view = render_view()->GetWebView();
940 if (web_view)
941 web_view->hidePopups();
942
943 HideHostAutofillUi();
944 }
945
946 void AutofillAgent::HideHostAutofillUi() {
947 Send(new AutofillHostMsg_HideAutofillUi(routing_id()));
948 }
949
950 void AutofillAgent::didAssociateFormControls(
951 const WebKit::WebVector<WebKit::WebNode>& nodes) {
952 for (size_t i = 0; i < nodes.size(); ++i) {
953 WebKit::WebNode node = nodes[i];
954 if (node.document().frame() == topmost_frame_) {
955 forms_seen_timestamp_ = base::TimeTicks::Now();
956 has_new_forms_for_browser_ = true;
957 break;
958 }
959 }
960
961 if (has_new_forms_for_browser_ && is_autocheckout_supported_)
962 MaybeSendDynamicFormsSeen();
963 }
964
965 void AutofillAgent::MaybeSendDynamicFormsSeen() {
966 has_new_forms_for_browser_ = false;
967 form_elements_.clear();
968 std::vector<FormData> forms;
969 // This will only be called for Autocheckout flows, so send all forms to
970 // save an IPC.
971 form_cache_.ExtractFormsAndFormElements(
972 *topmost_frame_, 0, &forms, &form_elements_);
973 autofill::FormsSeenState state = autofill::DYNAMIC_FORMS_SEEN;
974
975 if (!forms.empty()) {
976 if (click_timer_.IsRunning())
977 click_timer_.Stop();
978 Send(new AutofillHostMsg_FormsSeen(routing_id(), forms,
979 forms_seen_timestamp_,
980 state));
981 }
982 }
983
984 } // namespace autofill
OLDNEW
« no previous file with comments | « components/autofill/renderer/autofill_agent.h ('k') | components/autofill/renderer/form_autofill_util.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698