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

Side by Side Diff: chrome/renderer/autofill/form_autofill_util.cc

Issue 11415221: Add support for autofilling radio buttons and checkboxes. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: addressed review comments Created 8 years 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
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
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
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
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
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698