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::IsCheckboxElement; | |
47 using autofill::IsRadioButtonElement; | |
48 using autofill::IsSelectElement; | |
45 using autofill::IsTextInput; | 49 using autofill::IsTextInput; |
46 using autofill::IsSelectElement; | |
47 | 50 |
48 // The maximum length allowed for form data. | 51 // The maximum length allowed for form data. |
49 const size_t kMaxDataLength = 1024; | 52 const size_t kMaxDataLength = 1024; |
50 | 53 |
51 bool IsOptionElement(const WebElement& element) { | 54 bool IsOptionElement(const WebElement& element) { |
52 return element.hasTagName("option"); | 55 return element.hasTagName("option"); |
53 } | 56 } |
54 | 57 |
55 bool IsScriptElement(const WebElement& element) { | 58 bool IsScriptElement(const WebElement& element) { |
56 return element.hasTagName("script"); | 59 return element.hasTagName("script"); |
57 } | 60 } |
58 | 61 |
59 bool IsNoScriptElement(const WebElement& element) { | 62 bool IsNoScriptElement(const WebElement& element) { |
60 return element.hasTagName("noscript"); | 63 return element.hasTagName("noscript"); |
61 } | 64 } |
62 | 65 |
63 bool HasTagName(const WebNode& node, const WebKit::WebString& tag) { | 66 bool HasTagName(const WebNode& node, const WebKit::WebString& tag) { |
64 return node.isElementNode() && node.toConst<WebElement>().hasTagName(tag); | 67 return node.isElementNode() && node.toConst<WebElement>().hasTagName(tag); |
65 } | 68 } |
66 | 69 |
67 bool IsAutofillableElement(const WebFormControlElement& element) { | 70 bool IsAutofillableElement(const WebFormControlElement& element) { |
68 const WebInputElement* input_element = toWebInputElement(&element); | 71 const WebInputElement* input_element = toWebInputElement(&element); |
69 return IsTextInput(input_element) || IsSelectElement(element); | 72 return IsAutofillableInputElement(input_element) || IsSelectElement(element); |
70 } | 73 } |
71 | 74 |
72 // Appends |suffix| to |prefix| so that any intermediary whitespace is collapsed | 75 // 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 | 76 // 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 | 77 // 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| | 78 // result includes a space only if |prefix| has trailing whitespace or |suffix| |
76 // has leading whitespace. | 79 // has leading whitespace. |
77 // A few examples: | 80 // A few examples: |
78 // * CombineAndCollapseWhitespace("foo", "bar", false) -> "foobar" | 81 // * CombineAndCollapseWhitespace("foo", "bar", false) -> "foobar" |
79 // * CombineAndCollapseWhitespace("foo", "bar", true) -> "foo bar" | 82 // * CombineAndCollapseWhitespace("foo", "bar", true) -> "foo bar" |
(...skipping 371 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
451 // 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 |
452 // optimistically including a NOTREACHED(). If you ever trip this check, | 455 // optimistically including a NOTREACHED(). If you ever trip this check, |
453 // please file a bug against me. | 456 // please file a bug against me. |
454 NOTREACHED(); | 457 NOTREACHED(); |
455 continue; | 458 continue; |
456 } | 459 } |
457 | 460 |
458 bool is_initiating_element = (*element == initiating_element); | 461 bool is_initiating_element = (*element == initiating_element); |
459 | 462 |
460 const WebInputElement* input_element = toWebInputElement(element); | 463 const WebInputElement* input_element = toWebInputElement(element); |
461 if (IsTextInput(input_element)) { | 464 if (IsAutofillableInputElement(input_element)) { |
462 // Only autofill empty fields and the field that initiated the filling, | 465 // Only autofill empty fields and the field that initiated the filling, |
463 // i.e. the field the user is currently editing and interacting with. | 466 // i.e. the field the user is currently editing and interacting with. |
464 if (!is_initiating_element && !input_element->value().isEmpty()) | 467 if (!is_initiating_element && IsTextInput(input_element) && |
468 !input_element->value().isEmpty()) | |
Ilya Sherman
2012/12/01 00:54:12
nit: Let's just move this if-stmt to replace the o
Raman Kakilate
2012/12/06 01:54:05
Done.
| |
465 continue; | 469 continue; |
466 } | 470 } |
467 | 471 |
468 if (!element->isEnabled() || element->isReadOnly() || | 472 if (!element->isEnabled() || element->isReadOnly() || |
469 !element->isFocusable()) | 473 !element->isFocusable()) |
470 continue; | 474 continue; |
471 | 475 |
472 callback(element, &data.fields[i], is_initiating_element); | 476 callback(element, &data.fields[i], is_initiating_element); |
473 } | 477 } |
474 } | 478 } |
(...skipping 13 matching lines...) Expand all Loading... | |
488 // returns the default maxlength value. | 492 // returns the default maxlength value. |
489 input_element->setValue( | 493 input_element->setValue( |
490 data->value.substr(0, input_element->maxLength()), true); | 494 data->value.substr(0, input_element->maxLength()), true); |
491 input_element->setAutofilled(true); | 495 input_element->setAutofilled(true); |
492 if (is_initiating_node) { | 496 if (is_initiating_node) { |
493 int length = input_element->value().length(); | 497 int length = input_element->value().length(); |
494 input_element->setSelectionRange(length, length); | 498 input_element->setSelectionRange(length, length); |
495 // Clear the current IME composition (the underline), if there is one. | 499 // Clear the current IME composition (the underline), if there is one. |
496 input_element->document().frame()->unmarkText(); | 500 input_element->document().frame()->unmarkText(); |
497 } | 501 } |
498 } else { | 502 } else if (IsSelectElement(*field)) { |
499 DCHECK(IsSelectElement(*field)); | |
500 WebSelectElement select_element = field->to<WebSelectElement>(); | 503 WebSelectElement select_element = field->to<WebSelectElement>(); |
501 if (select_element.value() != data->value) { | 504 if (select_element.value() != data->value) { |
502 select_element.setValue(data->value); | 505 select_element.setValue(data->value); |
503 select_element.dispatchFormControlChangeEvent(); | 506 select_element.dispatchFormControlChangeEvent(); |
504 } | 507 } |
508 } else { | |
509 DCHECK(IsRadioButtonElement(input_element) || | |
510 IsCheckboxElement(input_element)); | |
511 input_element->setChecked(data->is_checked, true); | |
505 } | 512 } |
506 } | 513 } |
507 | 514 |
508 // Sets the |field|'s "suggested" (non JS visible) value to the value in |data|. | 515 // Sets the |field|'s "suggested" (non JS visible) value to the value in |data|. |
509 // Also sets the "autofilled" attribute, causing the background to be yellow. | 516 // Also sets the "autofilled" attribute, causing the background to be yellow. |
510 void PreviewFormField(WebKit::WebFormControlElement* field, | 517 void PreviewFormField(WebKit::WebFormControlElement* field, |
511 const FormFieldData* data, | 518 const FormFieldData* data, |
512 bool is_initiating_node) { | 519 bool is_initiating_node) { |
513 // Nothing to preview. | 520 // Nothing to preview. |
514 if (data->value.empty()) | 521 if (data->value.empty()) |
515 return; | 522 return; |
516 | 523 |
517 // Only preview input fields. | 524 // Only preview input fields. |
518 WebInputElement* input_element = toWebInputElement(field); | 525 WebInputElement* input_element = toWebInputElement(field); |
526 // Don't preview checkboxes and radio buttons, as there is no provision for | |
527 // setSuggestedCheckedValue in WebInputElement. | |
Ilya Sherman
2012/12/01 00:54:12
nit: Please combine this comment with the one on l
Raman Kakilate
2012/12/06 01:54:05
Done.
| |
519 if (!IsTextInput(input_element)) | 528 if (!IsTextInput(input_element)) |
520 return; | 529 return; |
521 | 530 |
522 // If the maxlength attribute contains a negative value, maxLength() | 531 // If the maxlength attribute contains a negative value, maxLength() |
523 // returns the default maxlength value. | 532 // returns the default maxlength value. |
524 input_element->setSuggestedValue( | 533 input_element->setSuggestedValue( |
525 data->value.substr(0, input_element->maxLength())); | 534 data->value.substr(0, input_element->maxLength())); |
526 input_element->setAutofilled(true); | 535 input_element->setAutofilled(true); |
527 if (is_initiating_node) { | 536 if (is_initiating_node) { |
528 // Select the part of the text that the user didn't type. | 537 // Select the part of the text that the user didn't type. |
(...skipping 14 matching lines...) Expand all Loading... | |
543 if (!element) | 552 if (!element) |
544 return false; | 553 return false; |
545 | 554 |
546 return element->isTextField() && !element->isPasswordField(); | 555 return element->isTextField() && !element->isPasswordField(); |
547 } | 556 } |
548 | 557 |
549 bool IsSelectElement(const WebFormControlElement& element) { | 558 bool IsSelectElement(const WebFormControlElement& element) { |
550 return element.formControlType() == ASCIIToUTF16("select-one"); | 559 return element.formControlType() == ASCIIToUTF16("select-one"); |
551 } | 560 } |
552 | 561 |
562 bool IsCheckboxElement(const WebInputElement* element) { | |
563 if (!element) | |
564 return false; | |
565 | |
566 return element->formControlType() == ASCIIToUTF16("checkbox"); | |
567 } | |
568 | |
569 bool IsRadioButtonElement(const WebInputElement* element) { | |
570 if (!element) | |
571 return false; | |
572 | |
573 return element->formControlType() == ASCIIToUTF16("radio"); | |
574 } | |
575 | |
576 bool IsAutofillableInputElement(const WebInputElement* element) { | |
577 return IsTextInput(element) || IsCheckboxElement(element) || | |
578 IsRadioButtonElement(element); | |
579 } | |
580 | |
553 const string16 GetFormIdentifier(const WebFormElement& form) { | 581 const string16 GetFormIdentifier(const WebFormElement& form) { |
554 string16 identifier = form.name(); | 582 string16 identifier = form.name(); |
555 if (identifier.empty()) | 583 if (identifier.empty()) |
556 identifier = form.getAttribute(WebString("id")); | 584 identifier = form.getAttribute(WebString("id")); |
557 | 585 |
558 return identifier; | 586 return identifier; |
559 } | 587 } |
560 | 588 |
561 // Fills |autofillable_elements| with all the auto-fillable form control | 589 // Fills |autofillable_elements| with all the auto-fillable form control |
562 // elements in |form_element|. | 590 // elements in |form_element|. |
563 void ExtractAutofillableElements( | 591 void ExtractAutofillableElements( |
564 const WebFormElement& form_element, | 592 const WebFormElement& form_element, |
565 RequirementsMask requirements, | 593 RequirementsMask requirements, |
566 std::vector<WebFormControlElement>* autofillable_elements) { | 594 std::vector<WebFormControlElement>* autofillable_elements) { |
567 WebVector<WebFormControlElement> control_elements; | 595 WebVector<WebFormControlElement> control_elements; |
568 form_element.getFormControlElements(control_elements); | 596 form_element.getFormControlElements(control_elements); |
569 | 597 |
570 autofillable_elements->clear(); | 598 autofillable_elements->clear(); |
571 for (size_t i = 0; i < control_elements.size(); ++i) { | 599 for (size_t i = 0; i < control_elements.size(); ++i) { |
572 WebFormControlElement element = control_elements[i]; | 600 WebFormControlElement element = control_elements[i]; |
573 if (!IsAutofillableElement(element)) | 601 if (!IsAutofillableElement(element)) |
574 continue; | 602 continue; |
575 | 603 |
576 if (requirements & REQUIRE_AUTOCOMPLETE) { | 604 if (requirements & REQUIRE_AUTOCOMPLETE) { |
577 // TODO(jhawkins): WebKit currently doesn't handle the autocomplete | 605 // TODO(jhawkins): WebKit currently doesn't handle the autocomplete |
578 // attribute for select control elements, but it probably should. | 606 // attribute for select control elements, but it probably should. |
579 WebInputElement* input_element = toWebInputElement(&control_elements[i]); | 607 WebInputElement* input_element = toWebInputElement(&control_elements[i]); |
580 if (IsTextInput(input_element) && !input_element->autoComplete()) | 608 if (IsAutofillableInputElement(input_element) && |
609 !input_element->autoComplete()) | |
581 continue; | 610 continue; |
582 } | 611 } |
583 | 612 |
584 autofillable_elements->push_back(element); | 613 autofillable_elements->push_back(element); |
585 } | 614 } |
586 } | 615 } |
587 | 616 |
588 void WebFormControlElementToFormField(const WebFormControlElement& element, | 617 void WebFormControlElementToFormField(const WebFormControlElement& element, |
589 ExtractMask extract_mask, | 618 ExtractMask extract_mask, |
590 FormFieldData* field) { | 619 FormFieldData* field) { |
(...skipping 11 matching lines...) Expand all Loading... | |
602 // Discard overly long attribute values to avoid DOS-ing the browser | 631 // Discard overly long attribute values to avoid DOS-ing the browser |
603 // process. However, send over a default string to indicate that the | 632 // process. However, send over a default string to indicate that the |
604 // attribute was present. | 633 // attribute was present. |
605 field->autocomplete_attribute = "x-max-data-length-exceeded"; | 634 field->autocomplete_attribute = "x-max-data-length-exceeded"; |
606 } | 635 } |
607 | 636 |
608 if (!IsAutofillableElement(element)) | 637 if (!IsAutofillableElement(element)) |
609 return; | 638 return; |
610 | 639 |
611 const WebInputElement* input_element = toWebInputElement(&element); | 640 const WebInputElement* input_element = toWebInputElement(&element); |
612 if (IsTextInput(input_element)) { | 641 if (IsAutofillableInputElement(input_element)) { |
613 field->max_length = input_element->maxLength(); | 642 field->max_length = input_element->maxLength(); |
614 field->is_autofilled = input_element->isAutofilled(); | 643 field->is_autofilled = input_element->isAutofilled(); |
615 field->is_focusable = input_element->isFocusable(); | 644 field->is_focusable = input_element->isFocusable(); |
616 field->should_autocomplete = input_element->autoComplete(); | 645 field->should_autocomplete = input_element->autoComplete(); |
646 if (IsRadioButtonElement(input_element) || | |
647 IsCheckboxElement(input_element)) { | |
648 field->is_checkable = true; | |
649 } | |
617 } else if (extract_mask & EXTRACT_OPTIONS) { | 650 } else if (extract_mask & EXTRACT_OPTIONS) { |
618 // Set option strings on the field if available. | 651 // Set option strings on the field if available. |
619 DCHECK(IsSelectElement(element)); | 652 DCHECK(IsSelectElement(element)); |
620 const WebSelectElement select_element = element.toConst<WebSelectElement>(); | 653 const WebSelectElement select_element = element.toConst<WebSelectElement>(); |
621 GetOptionStringsFromElement(select_element, | 654 GetOptionStringsFromElement(select_element, |
622 &field->option_values, | 655 &field->option_values, |
623 &field->option_contents); | 656 &field->option_contents); |
624 } | 657 } |
625 | 658 |
626 if (!(extract_mask & EXTRACT_VALUE)) | 659 if (!(extract_mask & EXTRACT_VALUE)) |
627 return; | 660 return; |
628 | 661 |
629 string16 value; | 662 string16 value; |
630 if (IsTextInput(input_element)) { | 663 if (IsAutofillableInputElement(input_element)) { |
631 value = input_element->value(); | 664 value = input_element->value(); |
632 } else { | 665 } else { |
633 DCHECK(IsSelectElement(element)); | 666 DCHECK(IsSelectElement(element)); |
634 const WebSelectElement select_element = element.toConst<WebSelectElement>(); | 667 const WebSelectElement select_element = element.toConst<WebSelectElement>(); |
635 value = select_element.value(); | 668 value = select_element.value(); |
636 | 669 |
637 // Convert the |select_element| value to text if requested. | 670 // Convert the |select_element| value to text if requested. |
638 if (extract_mask & EXTRACT_OPTION_TEXT) { | 671 if (extract_mask & EXTRACT_OPTION_TEXT) { |
639 WebVector<WebElement> list_items = select_element.listItems(); | 672 WebVector<WebElement> list_items = select_element.listItems(); |
640 for (size_t i = 0; i < list_items.size(); ++i) { | 673 for (size_t i = 0; i < list_items.size(); ++i) { |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
697 // requirements and thus will be in the resulting |form|. | 730 // requirements and thus will be in the resulting |form|. |
698 std::vector<bool> fields_extracted(control_elements.size(), false); | 731 std::vector<bool> fields_extracted(control_elements.size(), false); |
699 | 732 |
700 for (size_t i = 0; i < control_elements.size(); ++i) { | 733 for (size_t i = 0; i < control_elements.size(); ++i) { |
701 const WebFormControlElement& control_element = control_elements[i]; | 734 const WebFormControlElement& control_element = control_elements[i]; |
702 | 735 |
703 if (!IsAutofillableElement(control_element)) | 736 if (!IsAutofillableElement(control_element)) |
704 continue; | 737 continue; |
705 | 738 |
706 const WebInputElement* input_element = toWebInputElement(&control_element); | 739 const WebInputElement* input_element = toWebInputElement(&control_element); |
707 if (requirements & REQUIRE_AUTOCOMPLETE && IsTextInput(input_element) && | 740 if (requirements & REQUIRE_AUTOCOMPLETE && |
741 IsAutofillableInputElement(input_element) && | |
708 !input_element->autoComplete()) | 742 !input_element->autoComplete()) |
709 continue; | 743 continue; |
710 | 744 |
711 // Create a new FormFieldData, fill it out and map it to the field's name. | 745 // Create a new FormFieldData, fill it out and map it to the field's name. |
712 FormFieldData* form_field = new FormFieldData; | 746 FormFieldData* form_field = new FormFieldData; |
713 WebFormControlElementToFormField(control_element, extract_mask, form_field); | 747 WebFormControlElementToFormField(control_element, extract_mask, form_field); |
714 form_fields.push_back(form_field); | 748 form_fields.push_back(form_field); |
715 // TODO(jhawkins): A label element is mapped to a form control element's id. | 749 // TODO(jhawkins): A label element is mapped to a form control element's id. |
716 // field->name() will contain the id only if the name does not exist. Add | 750 // field->name() will contain the id only if the name does not exist. Add |
717 // an id() method to WebFormControlElement and use that here. | 751 // an id() method to WebFormControlElement and use that here. |
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
839 WebFormElement form_element = element.form(); | 873 WebFormElement form_element = element.form(); |
840 if (form_element.isNull()) | 874 if (form_element.isNull()) |
841 return false; | 875 return false; |
842 | 876 |
843 std::vector<WebFormControlElement> control_elements; | 877 std::vector<WebFormControlElement> control_elements; |
844 ExtractAutofillableElements(form_element, REQUIRE_AUTOCOMPLETE, | 878 ExtractAutofillableElements(form_element, REQUIRE_AUTOCOMPLETE, |
845 &control_elements); | 879 &control_elements); |
846 for (size_t i = 0; i < control_elements.size(); ++i) { | 880 for (size_t i = 0; i < control_elements.size(); ++i) { |
847 // Only text input elements can be previewed. | 881 // Only text input elements can be previewed. |
848 WebInputElement* input_element = toWebInputElement(&control_elements[i]); | 882 WebInputElement* input_element = toWebInputElement(&control_elements[i]); |
849 if (!IsTextInput(input_element)) | 883 if (!IsAutofillableInputElement(input_element)) |
Ilya Sherman
2012/12/01 00:54:12
Given the comment on line 881, I would expect this
Raman Kakilate
2012/12/06 01:54:05
Done.
| |
850 continue; | 884 continue; |
851 | 885 |
852 // If the input element is not auto-filled, we did not preview it, so there | 886 // If the input element is not auto-filled, we did not preview it, so there |
853 // is nothing to reset. | 887 // is nothing to reset. |
854 if (!input_element->isAutofilled()) | 888 if (!input_element->isAutofilled()) |
855 continue; | 889 continue; |
856 | 890 |
857 // There might be unrelated elements in this form which have already been | 891 // There might be unrelated elements in this form which have already been |
858 // auto-filled. For example, the user might have already filled the address | 892 // auto-filled. For example, the user might have already filled the address |
859 // part of a form and now be dealing with the credit card section. We only | 893 // part of a form and now be dealing with the credit card section. We only |
(...skipping 25 matching lines...) Expand all Loading... | |
885 bool FormWithElementIsAutofilled(const WebInputElement& element) { | 919 bool FormWithElementIsAutofilled(const WebInputElement& element) { |
886 WebFormElement form_element = element.form(); | 920 WebFormElement form_element = element.form(); |
887 if (form_element.isNull()) | 921 if (form_element.isNull()) |
888 return false; | 922 return false; |
889 | 923 |
890 std::vector<WebFormControlElement> control_elements; | 924 std::vector<WebFormControlElement> control_elements; |
891 ExtractAutofillableElements(form_element, REQUIRE_AUTOCOMPLETE, | 925 ExtractAutofillableElements(form_element, REQUIRE_AUTOCOMPLETE, |
892 &control_elements); | 926 &control_elements); |
893 for (size_t i = 0; i < control_elements.size(); ++i) { | 927 for (size_t i = 0; i < control_elements.size(); ++i) { |
894 WebInputElement* input_element = toWebInputElement(&control_elements[i]); | 928 WebInputElement* input_element = toWebInputElement(&control_elements[i]); |
895 if (!IsTextInput(input_element)) | 929 if (!IsAutofillableInputElement(input_element)) |
896 continue; | 930 continue; |
897 | 931 |
898 if (input_element->isAutofilled()) | 932 if (input_element->isAutofilled()) |
899 return true; | 933 return true; |
900 } | 934 } |
901 | 935 |
902 return false; | 936 return false; |
903 } | 937 } |
904 | 938 |
905 } // namespace autofill | 939 } // namespace autofill |
OLD | NEW |