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

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: More fixes 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 // 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
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698