OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "components/autofill/content/renderer/autofill_agent.h" | 5 #include "components/autofill/content/renderer/autofill_agent.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/command_line.h" | 8 #include "base/command_line.h" |
9 #include "base/message_loop/message_loop.h" | 9 #include "base/message_loop/message_loop.h" |
10 #include "base/strings/string_split.h" | 10 #include "base/strings/string_split.h" |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
112 | 112 |
113 // Limit the size of the strings in the vector. | 113 // Limit the size of the strings in the vector. |
114 for (size_t i = 0; i < strings->size(); ++i) { | 114 for (size_t i = 0; i < strings->size(); ++i) { |
115 if ((*strings)[i].length() > kMaxDataLength) | 115 if ((*strings)[i].length() > kMaxDataLength) |
116 (*strings)[i].resize(kMaxDataLength); | 116 (*strings)[i].resize(kMaxDataLength); |
117 } | 117 } |
118 } | 118 } |
119 | 119 |
120 } // namespace | 120 } // namespace |
121 | 121 |
| 122 AutofillAgent::ShowSuggestionsOptions::ShowSuggestionsOptions() |
| 123 : autofill_on_empty_values(false), |
| 124 requires_caret_at_end(false), |
| 125 display_warning_if_disabled(false), |
| 126 datalist_only(false), |
| 127 show_full_suggestion_list(false), |
| 128 show_password_suggestions_only(false) { |
| 129 } |
| 130 |
122 AutofillAgent::AutofillAgent(content::RenderView* render_view, | 131 AutofillAgent::AutofillAgent(content::RenderView* render_view, |
123 PasswordAutofillAgent* password_autofill_agent, | 132 PasswordAutofillAgent* password_autofill_agent, |
124 PasswordGenerationAgent* password_generation_agent) | 133 PasswordGenerationAgent* password_generation_agent) |
125 : content::RenderViewObserver(render_view), | 134 : content::RenderViewObserver(render_view), |
126 password_autofill_agent_(password_autofill_agent), | 135 password_autofill_agent_(password_autofill_agent), |
127 password_generation_agent_(password_generation_agent), | 136 password_generation_agent_(password_generation_agent), |
128 autofill_query_id_(0), | 137 autofill_query_id_(0), |
129 web_view_(render_view->GetWebView()), | 138 web_view_(render_view->GetWebView()), |
130 display_warning_if_disabled_(false), | 139 display_warning_if_disabled_(false), |
131 was_query_node_autofilled_(false), | 140 was_query_node_autofilled_(false), |
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
257 const WebFormElement& form) { | 266 const WebFormElement& form) { |
258 // Disallow the dialog over non-https or broken https, except when the | 267 // Disallow the dialog over non-https or broken https, except when the |
259 // ignore SSL flag is passed. See http://crbug.com/272512. | 268 // ignore SSL flag is passed. See http://crbug.com/272512. |
260 // TODO(palmer): this should be moved to the browser process after frames | 269 // TODO(palmer): this should be moved to the browser process after frames |
261 // get their own processes. | 270 // get their own processes. |
262 GURL url(form.document().url()); | 271 GURL url(form.document().url()); |
263 content::SSLStatus ssl_status = | 272 content::SSLStatus ssl_status = |
264 render_view()->GetSSLStatusOfFrame(form.document().frame()); | 273 render_view()->GetSSLStatusOfFrame(form.document().frame()); |
265 bool is_safe = url.SchemeIs(url::kHttpsScheme) && | 274 bool is_safe = url.SchemeIs(url::kHttpsScheme) && |
266 !net::IsCertStatusError(ssl_status.cert_status); | 275 !net::IsCertStatusError(ssl_status.cert_status); |
267 bool allow_unsafe = CommandLine::ForCurrentProcess()->HasSwitch( | 276 bool allow_unsafe = base::CommandLine::ForCurrentProcess()->HasSwitch( |
268 ::switches::kReduceSecurityForTesting); | 277 ::switches::kReduceSecurityForTesting); |
269 | 278 |
270 FormData form_data; | 279 FormData form_data; |
271 std::string error_message; | 280 std::string error_message; |
272 if (!in_flight_request_form_.isNull()) { | 281 if (!in_flight_request_form_.isNull()) { |
273 error_message = "already active."; | 282 error_message = "already active."; |
274 } else if (!is_safe && !allow_unsafe) { | 283 } else if (!is_safe && !allow_unsafe) { |
275 error_message = | 284 error_message = |
276 "must use a secure connection or --reduce-security-for-testing."; | 285 "must use a secure connection or --reduce-security-for-testing."; |
277 } else if (!WebFormElementToFormData(form, | 286 } else if (!WebFormElementToFormData(form, |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
309 ignore_text_changes_ = ignore; | 318 ignore_text_changes_ = ignore; |
310 } | 319 } |
311 | 320 |
312 void AutofillAgent::FormControlElementClicked( | 321 void AutofillAgent::FormControlElementClicked( |
313 const WebFormControlElement& element, | 322 const WebFormControlElement& element, |
314 bool was_focused) { | 323 bool was_focused) { |
315 const WebInputElement* input_element = toWebInputElement(&element); | 324 const WebInputElement* input_element = toWebInputElement(&element); |
316 if (!input_element && !IsTextAreaElement(element)) | 325 if (!input_element && !IsTextAreaElement(element)) |
317 return; | 326 return; |
318 | 327 |
319 bool show_full_suggestion_list = element.isAutofilled() || was_focused; | 328 ShowSuggestionsOptions options; |
320 bool show_password_suggestions_only = !was_focused; | 329 options.autofill_on_empty_values = true; |
| 330 options.display_warning_if_disabled = true; |
| 331 options.show_full_suggestion_list = element.isAutofilled(); |
321 | 332 |
322 // TODO(gcasto): Remove after crbug.com/430318 has been fixed. | 333 if (!base::CommandLine::ForCurrentProcess()->HasSwitch( |
323 bool show_suggestions = true; | 334 switches::kEnableSingleClickAutofill)) { |
| 335 // Show full suggestions when clicking on an already-focused form field. On |
| 336 // the initial click (not focused yet), only show password suggestions. |
324 #if defined(OS_ANDROID) | 337 #if defined(OS_ANDROID) |
325 show_suggestions = was_focused; | 338 // TODO(gcasto): Remove after crbug.com/430318 has been fixed. |
| 339 if (!was_focused) |
| 340 return; |
326 #endif | 341 #endif |
327 | 342 |
328 if (show_suggestions) { | 343 options.show_full_suggestion_list = |
329 ShowSuggestions(element, | 344 options.show_full_suggestion_list || was_focused; |
330 true, | 345 options.show_password_suggestions_only = !was_focused; |
331 false, | |
332 true, | |
333 false, | |
334 show_full_suggestion_list, | |
335 show_password_suggestions_only); | |
336 } | 346 } |
| 347 ShowSuggestions(element, options); |
337 } | 348 } |
338 | 349 |
339 void AutofillAgent::textFieldDidEndEditing(const WebInputElement& element) { | 350 void AutofillAgent::textFieldDidEndEditing(const WebInputElement& element) { |
340 password_autofill_agent_->TextFieldDidEndEditing(element); | 351 password_autofill_agent_->TextFieldDidEndEditing(element); |
341 has_shown_autofill_popup_for_current_edit_ = false; | 352 has_shown_autofill_popup_for_current_edit_ = false; |
342 Send(new AutofillHostMsg_DidEndTextFieldEditing(routing_id())); | 353 Send(new AutofillHostMsg_DidEndTextFieldEditing(routing_id())); |
343 } | 354 } |
344 | 355 |
345 void AutofillAgent::textFieldDidChange(const WebFormControlElement& element) { | 356 void AutofillAgent::textFieldDidChange(const WebFormControlElement& element) { |
346 if (ignore_text_changes_) | 357 if (ignore_text_changes_) |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
378 is_popup_possibly_visible_ = true; | 389 is_popup_possibly_visible_ = true; |
379 return; | 390 return; |
380 } | 391 } |
381 | 392 |
382 if (password_autofill_agent_->TextDidChangeInTextField(*input_element)) { | 393 if (password_autofill_agent_->TextDidChangeInTextField(*input_element)) { |
383 element_ = element; | 394 element_ = element; |
384 return; | 395 return; |
385 } | 396 } |
386 } | 397 } |
387 | 398 |
388 ShowSuggestions(element, false, true, false, false, false, false); | 399 ShowSuggestionsOptions options; |
| 400 options.requires_caret_at_end = true; |
| 401 ShowSuggestions(element, options); |
389 | 402 |
390 FormData form; | 403 FormData form; |
391 FormFieldData field; | 404 FormFieldData field; |
392 if (FindFormAndFieldForFormControlElement(element, | 405 if (FindFormAndFieldForFormControlElement(element, |
393 &form, | 406 &form, |
394 &field, | 407 &field, |
395 REQUIRE_NONE)) { | 408 REQUIRE_NONE)) { |
396 Send(new AutofillHostMsg_TextFieldDidChange(routing_id(), form, field, | 409 Send(new AutofillHostMsg_TextFieldDidChange(routing_id(), form, field, |
397 base::TimeTicks::Now())); | 410 base::TimeTicks::Now())); |
398 } | 411 } |
399 } | 412 } |
400 | 413 |
401 void AutofillAgent::textFieldDidReceiveKeyDown(const WebInputElement& element, | 414 void AutofillAgent::textFieldDidReceiveKeyDown(const WebInputElement& element, |
402 const WebKeyboardEvent& event) { | 415 const WebKeyboardEvent& event) { |
403 if (password_autofill_agent_->TextFieldHandlingKeyDown(element, event)) { | 416 if (password_autofill_agent_->TextFieldHandlingKeyDown(element, event)) { |
404 element_ = element; | 417 element_ = element; |
405 return; | 418 return; |
406 } | 419 } |
407 | 420 |
408 if (event.windowsKeyCode == ui::VKEY_DOWN || | 421 if (event.windowsKeyCode == ui::VKEY_DOWN || |
409 event.windowsKeyCode == ui::VKEY_UP) | 422 event.windowsKeyCode == ui::VKEY_UP) { |
410 ShowSuggestions(element, true, true, true, false, false, false); | 423 ShowSuggestionsOptions options; |
| 424 options.autofill_on_empty_values = true; |
| 425 options.requires_caret_at_end = true; |
| 426 options.display_warning_if_disabled = true; |
| 427 ShowSuggestions(element, options); |
| 428 } |
411 } | 429 } |
412 | 430 |
413 void AutofillAgent::openTextDataListChooser(const WebInputElement& element) { | 431 void AutofillAgent::openTextDataListChooser(const WebInputElement& element) { |
414 ShowSuggestions(element, true, false, false, true, false, false); | 432 ShowSuggestionsOptions options; |
| 433 options.autofill_on_empty_values = true; |
| 434 options.datalist_only = true; |
| 435 ShowSuggestions(element, options); |
415 } | 436 } |
416 | 437 |
417 void AutofillAgent::firstUserGestureObserved() { | 438 void AutofillAgent::firstUserGestureObserved() { |
418 password_autofill_agent_->FirstUserGestureObserved(); | 439 password_autofill_agent_->FirstUserGestureObserved(); |
419 } | 440 } |
420 | 441 |
421 void AutofillAgent::AcceptDataListSuggestion( | 442 void AutofillAgent::AcceptDataListSuggestion( |
422 const base::string16& suggested_value) { | 443 const base::string16& suggested_value) { |
423 WebInputElement* input_element = toWebInputElement(&element_); | 444 WebInputElement* input_element = toWebInputElement(&element_); |
424 DCHECK(input_element); | 445 DCHECK(input_element); |
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
555 WebConsoleMessage console_message = WebConsoleMessage( | 576 WebConsoleMessage console_message = WebConsoleMessage( |
556 WebConsoleMessage::LevelLog, WebString(prefix + message)); | 577 WebConsoleMessage::LevelLog, WebString(prefix + message)); |
557 in_flight_request_form_.document().frame()->addMessageToConsole( | 578 in_flight_request_form_.document().frame()->addMessageToConsole( |
558 console_message); | 579 console_message); |
559 } | 580 } |
560 | 581 |
561 in_flight_request_form_.reset(); | 582 in_flight_request_form_.reset(); |
562 } | 583 } |
563 | 584 |
564 void AutofillAgent::ShowSuggestions(const WebFormControlElement& element, | 585 void AutofillAgent::ShowSuggestions(const WebFormControlElement& element, |
565 bool autofill_on_empty_values, | 586 const ShowSuggestionsOptions& options) { |
566 bool requires_caret_at_end, | |
567 bool display_warning_if_disabled, | |
568 bool datalist_only, | |
569 bool show_full_suggestion_list, | |
570 bool show_password_suggestions_only) { | |
571 if (!element.isEnabled() || element.isReadOnly()) | 587 if (!element.isEnabled() || element.isReadOnly()) |
572 return; | 588 return; |
573 if (!datalist_only && !element.suggestedValue().isEmpty()) | 589 if (!options.datalist_only && !element.suggestedValue().isEmpty()) |
574 return; | 590 return; |
575 | 591 |
576 const WebInputElement* input_element = toWebInputElement(&element); | 592 const WebInputElement* input_element = toWebInputElement(&element); |
577 if (input_element) { | 593 if (input_element) { |
578 if (!input_element->isTextField() || input_element->isPasswordField()) | 594 if (!input_element->isTextField() || input_element->isPasswordField()) |
579 return; | 595 return; |
580 if (!datalist_only && !input_element->suggestedValue().isEmpty()) | 596 if (!options.datalist_only && !input_element->suggestedValue().isEmpty()) |
581 return; | 597 return; |
582 } else { | 598 } else { |
583 DCHECK(IsTextAreaElement(element)); | 599 DCHECK(IsTextAreaElement(element)); |
584 if (!element.toConst<WebTextAreaElement>().suggestedValue().isEmpty()) | 600 if (!element.toConst<WebTextAreaElement>().suggestedValue().isEmpty()) |
585 return; | 601 return; |
586 } | 602 } |
587 | 603 |
588 // Don't attempt to autofill with values that are too large or if filling | 604 // Don't attempt to autofill with values that are too large or if filling |
589 // criteria are not met. | 605 // criteria are not met. |
590 WebString value = element.editingValue(); | 606 WebString value = element.editingValue(); |
591 if (!datalist_only && | 607 if (!options.datalist_only && |
592 (value.length() > kMaxDataLength || | 608 (value.length() > kMaxDataLength || |
593 (!autofill_on_empty_values && value.isEmpty()) || | 609 (!options.autofill_on_empty_values && value.isEmpty()) || |
594 (requires_caret_at_end && | 610 (options.requires_caret_at_end && |
595 (element.selectionStart() != element.selectionEnd() || | 611 (element.selectionStart() != element.selectionEnd() || |
596 element.selectionEnd() != static_cast<int>(value.length()))))) { | 612 element.selectionEnd() != static_cast<int>(value.length()))))) { |
597 // Any popup currently showing is obsolete. | 613 // Any popup currently showing is obsolete. |
598 HidePopup(); | 614 HidePopup(); |
599 return; | 615 return; |
600 } | 616 } |
601 | 617 |
602 element_ = element; | 618 element_ = element; |
603 if (IsAutofillableInputElement(input_element) && | 619 if (IsAutofillableInputElement(input_element) && |
604 (password_autofill_agent_->ShowSuggestions(*input_element, | 620 (password_autofill_agent_->ShowSuggestions( |
605 show_full_suggestion_list) || | 621 *input_element, options.show_full_suggestion_list) || |
606 show_password_suggestions_only)) { | 622 options.show_password_suggestions_only)) { |
607 is_popup_possibly_visible_ = true; | 623 is_popup_possibly_visible_ = true; |
608 return; | 624 return; |
609 } | 625 } |
610 | 626 |
611 // If autocomplete is disabled at the field level, ensure that the native | 627 // If autocomplete is disabled at the field level, ensure that the native |
612 // UI won't try to show a warning, since that may conflict with a custom | 628 // UI won't try to show a warning, since that may conflict with a custom |
613 // popup. Note that we cannot use the WebKit method element.autoComplete() | 629 // popup. Note that we cannot use the WebKit method element.autoComplete() |
614 // as it does not allow us to distinguish the case where autocomplete is | 630 // as it does not allow us to distinguish the case where autocomplete is |
615 // disabled for *both* the element and for the form. | 631 // disabled for *both* the element and for the form. |
616 const base::string16 autocomplete_attribute = | 632 bool display_warning = options.display_warning_if_disabled; |
617 element.getAttribute("autocomplete"); | 633 if (display_warning) { |
618 if (LowerCaseEqualsASCII(autocomplete_attribute, "off")) | 634 const base::string16 autocomplete_attribute = |
619 display_warning_if_disabled = false; | 635 element.getAttribute("autocomplete"); |
| 636 if (LowerCaseEqualsASCII(autocomplete_attribute, "off")) |
| 637 display_warning = false; |
| 638 } |
620 | 639 |
621 QueryAutofillSuggestions(element, | 640 QueryAutofillSuggestions(element, display_warning, options.datalist_only); |
622 display_warning_if_disabled, | |
623 datalist_only); | |
624 } | 641 } |
625 | 642 |
626 void AutofillAgent::QueryAutofillSuggestions( | 643 void AutofillAgent::QueryAutofillSuggestions( |
627 const WebFormControlElement& element, | 644 const WebFormControlElement& element, |
628 bool display_warning_if_disabled, | 645 bool display_warning_if_disabled, |
629 bool datalist_only) { | 646 bool datalist_only) { |
630 if (!element.document().frame()) | 647 if (!element.document().frame()) |
631 return; | 648 return; |
632 | 649 |
633 DCHECK(toWebInputElement(&element) || IsTextAreaElement(element)); | 650 DCHECK(toWebInputElement(&element) || IsTextAreaElement(element)); |
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
745 ProcessForms(*frame); | 762 ProcessForms(*frame); |
746 password_autofill_agent_->OnDynamicFormsSeen(frame); | 763 password_autofill_agent_->OnDynamicFormsSeen(frame); |
747 if (password_generation_agent_) | 764 if (password_generation_agent_) |
748 password_generation_agent_->OnDynamicFormsSeen(frame); | 765 password_generation_agent_->OnDynamicFormsSeen(frame); |
749 return; | 766 return; |
750 } | 767 } |
751 } | 768 } |
752 } | 769 } |
753 | 770 |
754 } // namespace autofill | 771 } // namespace autofill |
OLD | NEW |