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

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: fix the nit. 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::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
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
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698