OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 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 "chrome/renderer/autofill/form_autofill_util.h" | 5 #include "chrome/renderer/autofill/form_autofill_util.h" |
6 | 6 |
7 #include <map> | 7 #include <map> |
8 | 8 |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "base/memory/scoped_vector.h" | 10 #include "base/memory/scoped_vector.h" |
(...skipping 24 matching lines...) Expand all Loading... | |
35 using WebKit::WebNode; | 35 using WebKit::WebNode; |
36 using WebKit::WebNodeList; | 36 using WebKit::WebNodeList; |
37 using WebKit::WebOptionElement; | 37 using WebKit::WebOptionElement; |
38 using WebKit::WebSelectElement; | 38 using WebKit::WebSelectElement; |
39 using WebKit::WebString; | 39 using WebKit::WebString; |
40 using WebKit::WebVector; | 40 using WebKit::WebVector; |
41 | 41 |
42 namespace { | 42 namespace { |
43 | 43 |
44 using autofill::ExtractAutofillableElements; | 44 using autofill::ExtractAutofillableElements; |
45 using autofill::IsAutofillableInputElement; | |
46 using autofill::IsCheckableElement; | |
47 using autofill::IsSelectElement; | |
45 using autofill::IsTextInput; | 48 using autofill::IsTextInput; |
46 using autofill::IsSelectElement; | |
47 | 49 |
48 // The maximum length allowed for form data. | 50 // The maximum length allowed for form data. |
49 const size_t kMaxDataLength = 1024; | 51 const size_t kMaxDataLength = 1024; |
50 | 52 |
51 bool IsOptionElement(const WebElement& element) { | 53 bool IsOptionElement(const WebElement& element) { |
52 return element.hasTagName("option"); | 54 return element.hasTagName("option"); |
53 } | 55 } |
54 | 56 |
55 bool IsScriptElement(const WebElement& element) { | 57 bool IsScriptElement(const WebElement& element) { |
56 return element.hasTagName("script"); | 58 return element.hasTagName("script"); |
57 } | 59 } |
58 | 60 |
59 bool IsNoScriptElement(const WebElement& element) { | 61 bool IsNoScriptElement(const WebElement& element) { |
60 return element.hasTagName("noscript"); | 62 return element.hasTagName("noscript"); |
61 } | 63 } |
62 | 64 |
63 bool HasTagName(const WebNode& node, const WebKit::WebString& tag) { | 65 bool HasTagName(const WebNode& node, const WebKit::WebString& tag) { |
64 return node.isElementNode() && node.toConst<WebElement>().hasTagName(tag); | 66 return node.isElementNode() && node.toConst<WebElement>().hasTagName(tag); |
65 } | 67 } |
66 | 68 |
67 bool IsAutofillableElement(const WebFormControlElement& element) { | 69 bool IsAutofillableElement(const WebFormControlElement& element) { |
68 const WebInputElement* input_element = toWebInputElement(&element); | 70 const WebInputElement* input_element = toWebInputElement(&element); |
69 return IsTextInput(input_element) || IsSelectElement(element); | 71 return IsAutofillableInputElement(input_element) || IsSelectElement(element); |
70 } | 72 } |
71 | 73 |
72 // Appends |suffix| to |prefix| so that any intermediary whitespace is collapsed | 74 // Appends |suffix| to |prefix| so that any intermediary whitespace is collapsed |
73 // to a single space. If |force_whitespace| is true, then the resulting string | 75 // to a single space. If |force_whitespace| is true, then the resulting string |
74 // is guaranteed to have a space between |prefix| and |suffix|. Otherwise, the | 76 // is guaranteed to have a space between |prefix| and |suffix|. Otherwise, the |
75 // result includes a space only if |prefix| has trailing whitespace or |suffix| | 77 // result includes a space only if |prefix| has trailing whitespace or |suffix| |
76 // has leading whitespace. | 78 // has leading whitespace. |
77 // A few examples: | 79 // A few examples: |
78 // * CombineAndCollapseWhitespace("foo", "bar", false) -> "foobar" | 80 // * CombineAndCollapseWhitespace("foo", "bar", false) -> "foobar" |
79 // * CombineAndCollapseWhitespace("foo", "bar", true) -> "foo bar" | 81 // * CombineAndCollapseWhitespace("foo", "bar", true) -> "foo bar" |
(...skipping 371 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
451 // rename form fields while the user is interacting with the Autofill | 453 // rename form fields while the user is interacting with the Autofill |
452 // popup. I (isherman) am not aware of any such websites, and so am | 454 // popup. I (isherman) am not aware of any such websites, and so am |
453 // optimistically including a NOTREACHED(). If you ever trip this check, | 455 // optimistically including a NOTREACHED(). If you ever trip this check, |
454 // please file a bug against me. | 456 // please file a bug against me. |
455 NOTREACHED(); | 457 NOTREACHED(); |
456 continue; | 458 continue; |
457 } | 459 } |
458 | 460 |
459 bool is_initiating_element = (*element == initiating_element); | 461 bool is_initiating_element = (*element == initiating_element); |
460 | 462 |
463 // Only autofill empty fields and the field that initiated the filling, | |
464 // i.e. the field the user is currently editing and interacting with. | |
461 const WebInputElement* input_element = toWebInputElement(element); | 465 const WebInputElement* input_element = toWebInputElement(element); |
462 if (IsTextInput(input_element)) { | 466 if (!is_initiating_element && IsTextInput(input_element) && |
463 // Only autofill empty fields and the field that initiated the filling, | 467 !input_element->value().isEmpty()) |
464 // i.e. the field the user is currently editing and interacting with. | 468 continue; |
465 if (!is_initiating_element && !input_element->value().isEmpty()) | |
466 continue; | |
467 } | |
468 | 469 |
469 if (!element->isEnabled() || element->isReadOnly() || | 470 if (!element->isEnabled() || element->isReadOnly() || |
470 (only_focusable_elements && !element->isFocusable())) | 471 (only_focusable_elements && !element->isFocusable())) |
471 continue; | 472 continue; |
472 | 473 |
473 callback(data.fields[i], is_initiating_element, element); | 474 callback(data.fields[i], is_initiating_element, element); |
474 } | 475 } |
475 } | 476 } |
476 | 477 |
477 // Sets the |field|'s value to the value in |data|. | 478 // Sets the |field|'s value to the value in |data|. |
(...skipping 11 matching lines...) Expand all Loading... | |
489 // returns the default maxlength value. | 490 // returns the default maxlength value. |
490 input_element->setValue( | 491 input_element->setValue( |
491 data.value.substr(0, input_element->maxLength()), true); | 492 data.value.substr(0, input_element->maxLength()), true); |
492 input_element->setAutofilled(true); | 493 input_element->setAutofilled(true); |
493 if (is_initiating_node) { | 494 if (is_initiating_node) { |
494 int length = input_element->value().length(); | 495 int length = input_element->value().length(); |
495 input_element->setSelectionRange(length, length); | 496 input_element->setSelectionRange(length, length); |
496 // Clear the current IME composition (the underline), if there is one. | 497 // Clear the current IME composition (the underline), if there is one. |
497 input_element->document().frame()->unmarkText(); | 498 input_element->document().frame()->unmarkText(); |
498 } | 499 } |
499 } else { | 500 } else if (IsSelectElement(*field)) { |
500 DCHECK(IsSelectElement(*field)); | |
501 WebSelectElement select_element = field->to<WebSelectElement>(); | 501 WebSelectElement select_element = field->to<WebSelectElement>(); |
502 if (select_element.value() != data.value) { | 502 if (select_element.value() != data.value) { |
503 select_element.setValue(data.value); | 503 select_element.setValue(data.value); |
504 select_element.dispatchFormControlChangeEvent(); | 504 select_element.dispatchFormControlChangeEvent(); |
505 } | 505 } |
506 } else { | |
507 DCHECK(IsCheckableElement(input_element)); | |
508 input_element->setChecked(data.is_checked, true); | |
506 } | 509 } |
507 } | 510 } |
508 | 511 |
509 // Sets the |field|'s "suggested" (non JS visible) value to the value in |data|. | 512 // Sets the |field|'s "suggested" (non JS visible) value to the value in |data|. |
510 // Also sets the "autofilled" attribute, causing the background to be yellow. | 513 // Also sets the "autofilled" attribute, causing the background to be yellow. |
511 void PreviewFormField(const FormFieldData& data, | 514 void PreviewFormField(const FormFieldData& data, |
512 bool is_initiating_node, | 515 bool is_initiating_node, |
513 WebKit::WebFormControlElement* field) { | 516 WebKit::WebFormControlElement* field) { |
514 // Nothing to preview. | 517 // Nothing to preview. |
515 if (data.value.empty()) | 518 if (data.value.empty()) |
516 return; | 519 return; |
517 | 520 |
518 // Only preview input fields. | 521 // Only preview input fields. Excludes checkboxes and radio buttons, as there |
522 // is no provision for setSuggestedCheckedValue in WebInputElement. | |
519 WebInputElement* input_element = toWebInputElement(field); | 523 WebInputElement* input_element = toWebInputElement(field); |
520 if (!IsTextInput(input_element)) | 524 if (!IsTextInput(input_element)) |
521 return; | 525 return; |
522 | 526 |
523 // If the maxlength attribute contains a negative value, maxLength() | 527 // If the maxlength attribute contains a negative value, maxLength() |
524 // returns the default maxlength value. | 528 // returns the default maxlength value. |
525 input_element->setSuggestedValue( | 529 input_element->setSuggestedValue( |
526 data.value.substr(0, input_element->maxLength())); | 530 data.value.substr(0, input_element->maxLength())); |
527 input_element->setAutofilled(true); | 531 input_element->setAutofilled(true); |
528 if (is_initiating_node) { | 532 if (is_initiating_node) { |
(...skipping 15 matching lines...) Expand all Loading... | |
544 if (!element) | 548 if (!element) |
545 return false; | 549 return false; |
546 | 550 |
547 return element->isTextField() && !element->isPasswordField(); | 551 return element->isTextField() && !element->isPasswordField(); |
548 } | 552 } |
549 | 553 |
550 bool IsSelectElement(const WebFormControlElement& element) { | 554 bool IsSelectElement(const WebFormControlElement& element) { |
551 return element.formControlType() == ASCIIToUTF16("select-one"); | 555 return element.formControlType() == ASCIIToUTF16("select-one"); |
552 } | 556 } |
553 | 557 |
558 bool IsCheckableElement(const WebInputElement* element) { | |
559 if (!element) | |
560 return false; | |
561 | |
562 return element->formControlType() == ASCIIToUTF16("checkbox") || | |
Albert Bodenhamer
2013/01/08 17:12:57
There was some evidence that this function was the
Evan Stade
2013/01/08 21:18:55
ASCIIToUTF16 is cheap (it's just a copy, unlike UT
| |
563 element->formControlType() == ASCIIToUTF16("radio"); | |
564 } | |
565 | |
566 bool IsAutofillableInputElement(const WebInputElement* element) { | |
567 return IsTextInput(element) || IsCheckableElement(element); | |
568 } | |
569 | |
554 const string16 GetFormIdentifier(const WebFormElement& form) { | 570 const string16 GetFormIdentifier(const WebFormElement& form) { |
555 string16 identifier = form.name(); | 571 string16 identifier = form.name(); |
556 if (identifier.empty()) | 572 if (identifier.empty()) |
557 identifier = form.getAttribute(WebString("id")); | 573 identifier = form.getAttribute(WebString("id")); |
558 | 574 |
559 return identifier; | 575 return identifier; |
560 } | 576 } |
561 | 577 |
562 // Fills |autofillable_elements| with all the auto-fillable form control | 578 // Fills |autofillable_elements| with all the auto-fillable form control |
563 // elements in |form_element|. | 579 // elements in |form_element|. |
564 void ExtractAutofillableElements( | 580 void ExtractAutofillableElements( |
565 const WebFormElement& form_element, | 581 const WebFormElement& form_element, |
566 RequirementsMask requirements, | 582 RequirementsMask requirements, |
567 std::vector<WebFormControlElement>* autofillable_elements) { | 583 std::vector<WebFormControlElement>* autofillable_elements) { |
568 WebVector<WebFormControlElement> control_elements; | 584 WebVector<WebFormControlElement> control_elements; |
569 form_element.getFormControlElements(control_elements); | 585 form_element.getFormControlElements(control_elements); |
570 | 586 |
571 autofillable_elements->clear(); | 587 autofillable_elements->clear(); |
572 for (size_t i = 0; i < control_elements.size(); ++i) { | 588 for (size_t i = 0; i < control_elements.size(); ++i) { |
573 WebFormControlElement element = control_elements[i]; | 589 WebFormControlElement element = control_elements[i]; |
574 if (!IsAutofillableElement(element)) | 590 if (!IsAutofillableElement(element)) |
575 continue; | 591 continue; |
576 | 592 |
577 if (requirements & REQUIRE_AUTOCOMPLETE) { | 593 if (requirements & REQUIRE_AUTOCOMPLETE) { |
578 // TODO(jhawkins): WebKit currently doesn't handle the autocomplete | 594 // TODO(jhawkins): WebKit currently doesn't handle the autocomplete |
579 // attribute for select control elements, but it probably should. | 595 // attribute for select control elements, but it probably should. |
580 WebInputElement* input_element = toWebInputElement(&control_elements[i]); | 596 WebInputElement* input_element = toWebInputElement(&control_elements[i]); |
581 if (IsTextInput(input_element) && !input_element->autoComplete()) | 597 if (IsAutofillableInputElement(input_element) && |
598 !input_element->autoComplete()) | |
582 continue; | 599 continue; |
583 } | 600 } |
584 | 601 |
585 autofillable_elements->push_back(element); | 602 autofillable_elements->push_back(element); |
586 } | 603 } |
587 } | 604 } |
588 | 605 |
589 void WebFormControlElementToFormField(const WebFormControlElement& element, | 606 void WebFormControlElementToFormField(const WebFormControlElement& element, |
590 ExtractMask extract_mask, | 607 ExtractMask extract_mask, |
591 FormFieldData* field) { | 608 FormFieldData* field) { |
(...skipping 11 matching lines...) Expand all Loading... | |
603 // Discard overly long attribute values to avoid DOS-ing the browser | 620 // Discard overly long attribute values to avoid DOS-ing the browser |
604 // process. However, send over a default string to indicate that the | 621 // process. However, send over a default string to indicate that the |
605 // attribute was present. | 622 // attribute was present. |
606 field->autocomplete_attribute = "x-max-data-length-exceeded"; | 623 field->autocomplete_attribute = "x-max-data-length-exceeded"; |
607 } | 624 } |
608 | 625 |
609 if (!IsAutofillableElement(element)) | 626 if (!IsAutofillableElement(element)) |
610 return; | 627 return; |
611 | 628 |
612 const WebInputElement* input_element = toWebInputElement(&element); | 629 const WebInputElement* input_element = toWebInputElement(&element); |
613 if (IsTextInput(input_element)) { | 630 if (IsAutofillableInputElement(input_element)) { |
614 field->max_length = input_element->maxLength(); | 631 if (IsTextInput(input_element)) |
632 field->max_length = input_element->maxLength(); | |
633 | |
615 field->is_autofilled = input_element->isAutofilled(); | 634 field->is_autofilled = input_element->isAutofilled(); |
616 field->is_focusable = input_element->isFocusable(); | 635 field->is_focusable = input_element->isFocusable(); |
617 field->should_autocomplete = input_element->autoComplete(); | 636 field->should_autocomplete = input_element->autoComplete(); |
637 field->is_checkable = IsCheckableElement(input_element); | |
618 } else if (extract_mask & EXTRACT_OPTIONS) { | 638 } else if (extract_mask & EXTRACT_OPTIONS) { |
619 // Set option strings on the field if available. | 639 // Set option strings on the field if available. |
620 DCHECK(IsSelectElement(element)); | 640 DCHECK(IsSelectElement(element)); |
621 const WebSelectElement select_element = element.toConst<WebSelectElement>(); | 641 const WebSelectElement select_element = element.toConst<WebSelectElement>(); |
622 GetOptionStringsFromElement(select_element, | 642 GetOptionStringsFromElement(select_element, |
623 &field->option_values, | 643 &field->option_values, |
624 &field->option_contents); | 644 &field->option_contents); |
625 } | 645 } |
626 | 646 |
627 if (!(extract_mask & EXTRACT_VALUE)) | 647 if (!(extract_mask & EXTRACT_VALUE)) |
628 return; | 648 return; |
629 | 649 |
630 string16 value; | 650 string16 value; |
631 if (IsTextInput(input_element)) { | 651 if (IsAutofillableInputElement(input_element)) { |
632 value = input_element->value(); | 652 value = input_element->value(); |
633 } else { | 653 } else { |
634 DCHECK(IsSelectElement(element)); | 654 DCHECK(IsSelectElement(element)); |
635 const WebSelectElement select_element = element.toConst<WebSelectElement>(); | 655 const WebSelectElement select_element = element.toConst<WebSelectElement>(); |
636 value = select_element.value(); | 656 value = select_element.value(); |
637 | 657 |
638 // Convert the |select_element| value to text if requested. | 658 // Convert the |select_element| value to text if requested. |
639 if (extract_mask & EXTRACT_OPTION_TEXT) { | 659 if (extract_mask & EXTRACT_OPTION_TEXT) { |
640 WebVector<WebElement> list_items = select_element.listItems(); | 660 WebVector<WebElement> list_items = select_element.listItems(); |
641 for (size_t i = 0; i < list_items.size(); ++i) { | 661 for (size_t i = 0; i < list_items.size(); ++i) { |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
698 // requirements and thus will be in the resulting |form|. | 718 // requirements and thus will be in the resulting |form|. |
699 std::vector<bool> fields_extracted(control_elements.size(), false); | 719 std::vector<bool> fields_extracted(control_elements.size(), false); |
700 | 720 |
701 for (size_t i = 0; i < control_elements.size(); ++i) { | 721 for (size_t i = 0; i < control_elements.size(); ++i) { |
702 const WebFormControlElement& control_element = control_elements[i]; | 722 const WebFormControlElement& control_element = control_elements[i]; |
703 | 723 |
704 if (!IsAutofillableElement(control_element)) | 724 if (!IsAutofillableElement(control_element)) |
705 continue; | 725 continue; |
706 | 726 |
707 const WebInputElement* input_element = toWebInputElement(&control_element); | 727 const WebInputElement* input_element = toWebInputElement(&control_element); |
708 if (requirements & REQUIRE_AUTOCOMPLETE && IsTextInput(input_element) && | 728 if (requirements & REQUIRE_AUTOCOMPLETE && |
729 IsAutofillableInputElement(input_element) && | |
709 !input_element->autoComplete()) | 730 !input_element->autoComplete()) |
710 continue; | 731 continue; |
711 | 732 |
712 // Create a new FormFieldData, fill it out and map it to the field's name. | 733 // Create a new FormFieldData, fill it out and map it to the field's name. |
713 FormFieldData* form_field = new FormFieldData; | 734 FormFieldData* form_field = new FormFieldData; |
714 WebFormControlElementToFormField(control_element, extract_mask, form_field); | 735 WebFormControlElementToFormField(control_element, extract_mask, form_field); |
715 form_fields.push_back(form_field); | 736 form_fields.push_back(form_field); |
716 // TODO(jhawkins): A label element is mapped to a form control element's id. | 737 // TODO(jhawkins): A label element is mapped to a form control element's id. |
717 // field->name() will contain the id only if the name does not exist. Add | 738 // field->name() will contain the id only if the name does not exist. Add |
718 // an id() method to WebFormControlElement and use that here. | 739 // an id() method to WebFormControlElement and use that here. |
(...skipping 181 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
900 bool FormWithElementIsAutofilled(const WebInputElement& element) { | 921 bool FormWithElementIsAutofilled(const WebInputElement& element) { |
901 WebFormElement form_element = element.form(); | 922 WebFormElement form_element = element.form(); |
902 if (form_element.isNull()) | 923 if (form_element.isNull()) |
903 return false; | 924 return false; |
904 | 925 |
905 std::vector<WebFormControlElement> control_elements; | 926 std::vector<WebFormControlElement> control_elements; |
906 ExtractAutofillableElements(form_element, REQUIRE_AUTOCOMPLETE, | 927 ExtractAutofillableElements(form_element, REQUIRE_AUTOCOMPLETE, |
907 &control_elements); | 928 &control_elements); |
908 for (size_t i = 0; i < control_elements.size(); ++i) { | 929 for (size_t i = 0; i < control_elements.size(); ++i) { |
909 WebInputElement* input_element = toWebInputElement(&control_elements[i]); | 930 WebInputElement* input_element = toWebInputElement(&control_elements[i]); |
910 if (!IsTextInput(input_element)) | 931 if (!IsAutofillableInputElement(input_element)) |
911 continue; | 932 continue; |
912 | 933 |
913 if (input_element->isAutofilled()) | 934 if (input_element->isAutofilled()) |
914 return true; | 935 return true; |
915 } | 936 } |
916 | 937 |
917 return false; | 938 return false; |
918 } | 939 } |
919 | 940 |
920 } // namespace autofill | 941 } // namespace autofill |
942 | |
OLD | NEW |