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 // rename form fields while the user is interacting with the Autofill | 454 // 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 | 455 // popup. I (isherman) am not aware of any such websites, and so am |
453 // optimistically including a NOTREACHED(). If you ever trip this check, | 456 // optimistically including a NOTREACHED(). If you ever trip this check, |
454 // please file a bug against me. | 457 // please file a bug against me. |
455 NOTREACHED(); | 458 NOTREACHED(); |
456 continue; | 459 continue; |
457 } | 460 } |
458 | 461 |
459 bool is_initiating_element = (*element == initiating_element); | 462 bool is_initiating_element = (*element == initiating_element); |
460 | 463 |
461 const WebInputElement* input_element = toWebInputElement(element); | 464 const WebInputElement* input_element = toWebInputElement(element); |
Ilya Sherman
2012/12/12 00:26:44
nit: Please move this to be after the comment, i.e
Raman Kakilate
2012/12/12 01:22:04
Done.
| |
462 if (IsTextInput(input_element)) { | 465 // Only autofill empty fields and the field that initiated the filling, |
463 // Only autofill empty fields and the field that initiated the filling, | 466 // i.e. the field the user is currently editing and interacting with. |
464 // i.e. the field the user is currently editing and interacting with. | 467 if (!is_initiating_element && IsTextInput(input_element) && |
465 if (!is_initiating_element && !input_element->value().isEmpty()) | 468 !input_element->value().isEmpty()) |
466 continue; | 469 continue; |
467 } | |
468 | 470 |
469 if (!element->isEnabled() || element->isReadOnly() || | 471 if (!element->isEnabled() || element->isReadOnly() || |
470 (only_focusable_elements && !element->isFocusable())) | 472 (only_focusable_elements && !element->isFocusable())) |
471 continue; | 473 continue; |
472 | 474 |
473 callback(data.fields[i], is_initiating_element, element); | 475 callback(data.fields[i], is_initiating_element, element); |
474 } | 476 } |
475 } | 477 } |
476 | 478 |
477 // Sets the |field|'s value to the value in |data|. | 479 // Sets the |field|'s value to the value in |data|. |
(...skipping 11 matching lines...) Expand all Loading... | |
489 // returns the default maxlength value. | 491 // returns the default maxlength value. |
490 input_element->setValue( | 492 input_element->setValue( |
491 data.value.substr(0, input_element->maxLength()), true); | 493 data.value.substr(0, input_element->maxLength()), true); |
492 input_element->setAutofilled(true); | 494 input_element->setAutofilled(true); |
493 if (is_initiating_node) { | 495 if (is_initiating_node) { |
494 int length = input_element->value().length(); | 496 int length = input_element->value().length(); |
495 input_element->setSelectionRange(length, length); | 497 input_element->setSelectionRange(length, length); |
496 // Clear the current IME composition (the underline), if there is one. | 498 // Clear the current IME composition (the underline), if there is one. |
497 input_element->document().frame()->unmarkText(); | 499 input_element->document().frame()->unmarkText(); |
498 } | 500 } |
499 } else { | 501 } else if (IsSelectElement(*field)) { |
500 DCHECK(IsSelectElement(*field)); | |
501 WebSelectElement select_element = field->to<WebSelectElement>(); | 502 WebSelectElement select_element = field->to<WebSelectElement>(); |
502 if (select_element.value() != data.value) { | 503 if (select_element.value() != data.value) { |
503 select_element.setValue(data.value); | 504 select_element.setValue(data.value); |
504 select_element.dispatchFormControlChangeEvent(); | 505 select_element.dispatchFormControlChangeEvent(); |
505 } | 506 } |
507 } else { | |
508 DCHECK(IsRadioButtonElement(input_element) || | |
509 IsCheckboxElement(input_element)); | |
510 input_element->setChecked(data.is_checked, true); | |
506 } | 511 } |
507 } | 512 } |
508 | 513 |
509 // Sets the |field|'s "suggested" (non JS visible) value to the value in |data|. | 514 // 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. | 515 // Also sets the "autofilled" attribute, causing the background to be yellow. |
511 void PreviewFormField(const FormFieldData& data, | 516 void PreviewFormField(const FormFieldData& data, |
512 bool is_initiating_node, | 517 bool is_initiating_node, |
513 WebKit::WebFormControlElement* field) { | 518 WebKit::WebFormControlElement* field) { |
514 // Nothing to preview. | 519 // Nothing to preview. |
515 if (data.value.empty()) | 520 if (data.value.empty()) |
516 return; | 521 return; |
517 | 522 |
518 // Only preview input fields. | 523 // Only preview input fields. Excludes checkboxes and radio buttons, as there |
524 // is no provision for setSuggestedCheckedValue in WebInputElement. | |
519 WebInputElement* input_element = toWebInputElement(field); | 525 WebInputElement* input_element = toWebInputElement(field); |
520 if (!IsTextInput(input_element)) | 526 if (!IsTextInput(input_element)) |
521 return; | 527 return; |
522 | 528 |
523 // If the maxlength attribute contains a negative value, maxLength() | 529 // If the maxlength attribute contains a negative value, maxLength() |
524 // returns the default maxlength value. | 530 // returns the default maxlength value. |
525 input_element->setSuggestedValue( | 531 input_element->setSuggestedValue( |
526 data.value.substr(0, input_element->maxLength())); | 532 data.value.substr(0, input_element->maxLength())); |
527 input_element->setAutofilled(true); | 533 input_element->setAutofilled(true); |
528 if (is_initiating_node) { | 534 if (is_initiating_node) { |
(...skipping 15 matching lines...) Expand all Loading... | |
544 if (!element) | 550 if (!element) |
545 return false; | 551 return false; |
546 | 552 |
547 return element->isTextField() && !element->isPasswordField(); | 553 return element->isTextField() && !element->isPasswordField(); |
548 } | 554 } |
549 | 555 |
550 bool IsSelectElement(const WebFormControlElement& element) { | 556 bool IsSelectElement(const WebFormControlElement& element) { |
551 return element.formControlType() == ASCIIToUTF16("select-one"); | 557 return element.formControlType() == ASCIIToUTF16("select-one"); |
552 } | 558 } |
553 | 559 |
560 bool IsCheckboxElement(const WebInputElement* element) { | |
561 if (!element) | |
562 return false; | |
563 | |
564 return element->formControlType() == ASCIIToUTF16("checkbox"); | |
565 } | |
566 | |
567 bool IsRadioButtonElement(const WebInputElement* element) { | |
568 if (!element) | |
569 return false; | |
570 | |
571 return element->formControlType() == ASCIIToUTF16("radio"); | |
572 } | |
573 | |
574 bool IsAutofillableInputElement(const WebInputElement* element) { | |
575 return IsTextInput(element) || IsCheckboxElement(element) || | |
576 IsRadioButtonElement(element); | |
577 } | |
578 | |
554 const string16 GetFormIdentifier(const WebFormElement& form) { | 579 const string16 GetFormIdentifier(const WebFormElement& form) { |
555 string16 identifier = form.name(); | 580 string16 identifier = form.name(); |
556 if (identifier.empty()) | 581 if (identifier.empty()) |
557 identifier = form.getAttribute(WebString("id")); | 582 identifier = form.getAttribute(WebString("id")); |
558 | 583 |
559 return identifier; | 584 return identifier; |
560 } | 585 } |
561 | 586 |
562 // Fills |autofillable_elements| with all the auto-fillable form control | 587 // Fills |autofillable_elements| with all the auto-fillable form control |
563 // elements in |form_element|. | 588 // elements in |form_element|. |
564 void ExtractAutofillableElements( | 589 void ExtractAutofillableElements( |
565 const WebFormElement& form_element, | 590 const WebFormElement& form_element, |
566 RequirementsMask requirements, | 591 RequirementsMask requirements, |
567 std::vector<WebFormControlElement>* autofillable_elements) { | 592 std::vector<WebFormControlElement>* autofillable_elements) { |
568 WebVector<WebFormControlElement> control_elements; | 593 WebVector<WebFormControlElement> control_elements; |
569 form_element.getFormControlElements(control_elements); | 594 form_element.getFormControlElements(control_elements); |
570 | 595 |
571 autofillable_elements->clear(); | 596 autofillable_elements->clear(); |
572 for (size_t i = 0; i < control_elements.size(); ++i) { | 597 for (size_t i = 0; i < control_elements.size(); ++i) { |
573 WebFormControlElement element = control_elements[i]; | 598 WebFormControlElement element = control_elements[i]; |
574 if (!IsAutofillableElement(element)) | 599 if (!IsAutofillableElement(element)) |
575 continue; | 600 continue; |
576 | 601 |
577 if (requirements & REQUIRE_AUTOCOMPLETE) { | 602 if (requirements & REQUIRE_AUTOCOMPLETE) { |
578 // TODO(jhawkins): WebKit currently doesn't handle the autocomplete | 603 // TODO(jhawkins): WebKit currently doesn't handle the autocomplete |
579 // attribute for select control elements, but it probably should. | 604 // attribute for select control elements, but it probably should. |
580 WebInputElement* input_element = toWebInputElement(&control_elements[i]); | 605 WebInputElement* input_element = toWebInputElement(&control_elements[i]); |
581 if (IsTextInput(input_element) && !input_element->autoComplete()) | 606 if (IsAutofillableInputElement(input_element) && |
607 !input_element->autoComplete()) | |
582 continue; | 608 continue; |
583 } | 609 } |
584 | 610 |
585 autofillable_elements->push_back(element); | 611 autofillable_elements->push_back(element); |
586 } | 612 } |
587 } | 613 } |
588 | 614 |
589 void WebFormControlElementToFormField(const WebFormControlElement& element, | 615 void WebFormControlElementToFormField(const WebFormControlElement& element, |
590 ExtractMask extract_mask, | 616 ExtractMask extract_mask, |
591 FormFieldData* field) { | 617 FormFieldData* field) { |
(...skipping 11 matching lines...) Expand all Loading... | |
603 // Discard overly long attribute values to avoid DOS-ing the browser | 629 // Discard overly long attribute values to avoid DOS-ing the browser |
604 // process. However, send over a default string to indicate that the | 630 // process. However, send over a default string to indicate that the |
605 // attribute was present. | 631 // attribute was present. |
606 field->autocomplete_attribute = "x-max-data-length-exceeded"; | 632 field->autocomplete_attribute = "x-max-data-length-exceeded"; |
607 } | 633 } |
608 | 634 |
609 if (!IsAutofillableElement(element)) | 635 if (!IsAutofillableElement(element)) |
610 return; | 636 return; |
611 | 637 |
612 const WebInputElement* input_element = toWebInputElement(&element); | 638 const WebInputElement* input_element = toWebInputElement(&element); |
613 if (IsTextInput(input_element)) { | 639 if (IsAutofillableInputElement(input_element)) { |
614 field->max_length = input_element->maxLength(); | 640 field->max_length = input_element->maxLength(); |
615 field->is_autofilled = input_element->isAutofilled(); | 641 field->is_autofilled = input_element->isAutofilled(); |
616 field->is_focusable = input_element->isFocusable(); | 642 field->is_focusable = input_element->isFocusable(); |
617 field->should_autocomplete = input_element->autoComplete(); | 643 field->should_autocomplete = input_element->autoComplete(); |
644 if (IsRadioButtonElement(input_element) || | |
645 IsCheckboxElement(input_element)) { | |
646 field->is_checkable = true; | |
647 } | |
Ilya Sherman
2012/12/12 00:26:44
nit: It's better to always explicitly set the valu
Raman Kakilate
2012/12/12 01:22:04
Done.
| |
618 } else if (extract_mask & EXTRACT_OPTIONS) { | 648 } else if (extract_mask & EXTRACT_OPTIONS) { |
619 // Set option strings on the field if available. | 649 // Set option strings on the field if available. |
620 DCHECK(IsSelectElement(element)); | 650 DCHECK(IsSelectElement(element)); |
621 const WebSelectElement select_element = element.toConst<WebSelectElement>(); | 651 const WebSelectElement select_element = element.toConst<WebSelectElement>(); |
622 GetOptionStringsFromElement(select_element, | 652 GetOptionStringsFromElement(select_element, |
623 &field->option_values, | 653 &field->option_values, |
624 &field->option_contents); | 654 &field->option_contents); |
625 } | 655 } |
626 | 656 |
627 if (!(extract_mask & EXTRACT_VALUE)) | 657 if (!(extract_mask & EXTRACT_VALUE)) |
628 return; | 658 return; |
629 | 659 |
630 string16 value; | 660 string16 value; |
631 if (IsTextInput(input_element)) { | 661 if (IsAutofillableInputElement(input_element)) { |
632 value = input_element->value(); | 662 value = input_element->value(); |
633 } else { | 663 } else { |
634 DCHECK(IsSelectElement(element)); | 664 DCHECK(IsSelectElement(element)); |
635 const WebSelectElement select_element = element.toConst<WebSelectElement>(); | 665 const WebSelectElement select_element = element.toConst<WebSelectElement>(); |
636 value = select_element.value(); | 666 value = select_element.value(); |
637 | 667 |
638 // Convert the |select_element| value to text if requested. | 668 // Convert the |select_element| value to text if requested. |
639 if (extract_mask & EXTRACT_OPTION_TEXT) { | 669 if (extract_mask & EXTRACT_OPTION_TEXT) { |
640 WebVector<WebElement> list_items = select_element.listItems(); | 670 WebVector<WebElement> list_items = select_element.listItems(); |
641 for (size_t i = 0; i < list_items.size(); ++i) { | 671 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|. | 728 // requirements and thus will be in the resulting |form|. |
699 std::vector<bool> fields_extracted(control_elements.size(), false); | 729 std::vector<bool> fields_extracted(control_elements.size(), false); |
700 | 730 |
701 for (size_t i = 0; i < control_elements.size(); ++i) { | 731 for (size_t i = 0; i < control_elements.size(); ++i) { |
702 const WebFormControlElement& control_element = control_elements[i]; | 732 const WebFormControlElement& control_element = control_elements[i]; |
703 | 733 |
704 if (!IsAutofillableElement(control_element)) | 734 if (!IsAutofillableElement(control_element)) |
705 continue; | 735 continue; |
706 | 736 |
707 const WebInputElement* input_element = toWebInputElement(&control_element); | 737 const WebInputElement* input_element = toWebInputElement(&control_element); |
708 if (requirements & REQUIRE_AUTOCOMPLETE && IsTextInput(input_element) && | 738 if (requirements & REQUIRE_AUTOCOMPLETE && |
739 IsAutofillableInputElement(input_element) && | |
709 !input_element->autoComplete()) | 740 !input_element->autoComplete()) |
710 continue; | 741 continue; |
711 | 742 |
712 // Create a new FormFieldData, fill it out and map it to the field's name. | 743 // Create a new FormFieldData, fill it out and map it to the field's name. |
713 FormFieldData* form_field = new FormFieldData; | 744 FormFieldData* form_field = new FormFieldData; |
714 WebFormControlElementToFormField(control_element, extract_mask, form_field); | 745 WebFormControlElementToFormField(control_element, extract_mask, form_field); |
715 form_fields.push_back(form_field); | 746 form_fields.push_back(form_field); |
716 // TODO(jhawkins): A label element is mapped to a form control element's id. | 747 // 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 | 748 // field->name() will contain the id only if the name does not exist. Add |
718 // an id() method to WebFormControlElement and use that here. | 749 // 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) { | 931 bool FormWithElementIsAutofilled(const WebInputElement& element) { |
901 WebFormElement form_element = element.form(); | 932 WebFormElement form_element = element.form(); |
902 if (form_element.isNull()) | 933 if (form_element.isNull()) |
903 return false; | 934 return false; |
904 | 935 |
905 std::vector<WebFormControlElement> control_elements; | 936 std::vector<WebFormControlElement> control_elements; |
906 ExtractAutofillableElements(form_element, REQUIRE_AUTOCOMPLETE, | 937 ExtractAutofillableElements(form_element, REQUIRE_AUTOCOMPLETE, |
907 &control_elements); | 938 &control_elements); |
908 for (size_t i = 0; i < control_elements.size(); ++i) { | 939 for (size_t i = 0; i < control_elements.size(); ++i) { |
909 WebInputElement* input_element = toWebInputElement(&control_elements[i]); | 940 WebInputElement* input_element = toWebInputElement(&control_elements[i]); |
910 if (!IsTextInput(input_element)) | 941 if (!IsAutofillableInputElement(input_element)) |
911 continue; | 942 continue; |
912 | 943 |
913 if (input_element->isAutofilled()) | 944 if (input_element->isAutofilled()) |
914 return true; | 945 return true; |
915 } | 946 } |
916 | 947 |
917 return false; | 948 return false; |
918 } | 949 } |
919 | 950 |
920 } // namespace autofill | 951 } // namespace autofill |
OLD | NEW |