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

Side by Side Diff: components/autofill/content/renderer/form_autofill_util.cc

Issue 140093005: Add supports that allow Autofill to be initiated from textarea field (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase and clean code using common methods introduced in WebFormControlElement interface Created 6 years, 9 months 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 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 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 "components/autofill/content/renderer/form_autofill_util.h" 5 #include "components/autofill/content/renderer/form_autofill_util.h"
6 6
7 #include <map> 7 #include <map>
8 8
9 #include "base/command_line.h" 9 #include "base/command_line.h"
10 #include "base/logging.h" 10 #include "base/logging.h"
(...skipping 489 matching lines...) Expand 10 before | Expand all | Expand 10 after
500 NOTREACHED(); 500 NOTREACHED();
501 continue; 501 continue;
502 } 502 }
503 503
504 bool is_initiating_element = (*element == initiating_element); 504 bool is_initiating_element = (*element == initiating_element);
505 505
506 // Only autofill empty fields and the field that initiated the filling, 506 // Only autofill empty fields and the field that initiated the filling,
507 // i.e. the field the user is currently editing and interacting with. 507 // i.e. the field the user is currently editing and interacting with.
508 const WebInputElement* input_element = toWebInputElement(element); 508 const WebInputElement* input_element = toWebInputElement(element);
509 if (!force_override && !is_initiating_element && 509 if (!force_override && !is_initiating_element &&
510 ((IsAutofillableInputElement(input_element) && 510 ((IsAutofillableInputElement(input_element) ||
Ilya Sherman 2014/03/14 07:24:46 nit: No need for the first parenthesis on this lin
ziran.sun 2014/03/14 17:01:42 I feel that it's still needed. The condition is -
Ilya Sherman 2014/03/14 21:42:55 Sorry, you're absolutely right. Thanks for correc
511 !input_element->value().isEmpty()) || 511 IsTextAreaElement(*element)) &&
512 (IsTextAreaElement(*element) && 512 !element->value().isEmpty()))
513 !element->toConst<WebTextAreaElement>().value().isEmpty())))
514 continue; 513 continue;
515 514
516 if (((filters & FILTER_DISABLED_ELEMENTS) && !element->isEnabled()) || 515 if (((filters & FILTER_DISABLED_ELEMENTS) && !element->isEnabled()) ||
517 ((filters & FILTER_READONLY_ELEMENTS) && element->isReadOnly()) || 516 ((filters & FILTER_READONLY_ELEMENTS) && element->isReadOnly()) ||
518 ((filters & FILTER_NON_FOCUSABLE_ELEMENTS) && !element->isFocusable())) 517 ((filters & FILTER_NON_FOCUSABLE_ELEMENTS) && !element->isFocusable()))
519 continue; 518 continue;
520 519
521 callback(data.fields[i], is_initiating_element, element); 520 callback(data.fields[i], is_initiating_element, element);
522 } 521 }
523 } 522 }
524 523
525 // Sets the |field|'s value to the value in |data|. 524 // Sets the |field|'s value to the value in |data|.
526 // Also sets the "autofilled" attribute, causing the background to be yellow. 525 // Also sets the "autofilled" attribute, causing the background to be yellow.
527 void FillFormField(const FormFieldData& data, 526 void FillFormField(const FormFieldData& data,
528 bool is_initiating_node, 527 bool is_initiating_node,
529 blink::WebFormControlElement* field) { 528 blink::WebFormControlElement* field) {
530 // Nothing to fill. 529 // Nothing to fill.
531 if (data.value.empty()) 530 if (data.value.empty())
532 return; 531 return;
533 532
534 field->setAutofilled(true); 533 field->setAutofilled(true);
535 534
536 WebInputElement* input_element = toWebInputElement(field); 535 WebInputElement* input_element = toWebInputElement(field);
537 if (IsTextInput(input_element) || IsMonthInput(input_element)) { 536 if (IsTextInput(input_element) || IsMonthInput(input_element)) {
538 // If the maxlength attribute contains a negative value, maxLength() 537 // If the maxlength attribute contains a negative value, maxLength()
539 // returns the default maxlength value. 538 // returns the default maxlength value.
540 input_element->setValue( 539 input_element->setValue(
541 data.value.substr(0, input_element->maxLength()), true); 540 data.value.substr(0, input_element->maxLength()), true);
542 if (is_initiating_node) { 541 } else if ((IsTextAreaElement(*field) || IsSelectElement(*field)) &&
543 int length = input_element->value().length(); 542 field->value() != data.value) {
544 input_element->setSelectionRange(length, length); 543 field->setValue(data.value);
545 // Clear the current IME composition (the underline), if there is one. 544 field->dispatchFormControlChangeEvent();
546 input_element->document().frame()->unmarkText();
547 }
548 } else if (IsTextAreaElement(*field)) {
549 WebTextAreaElement text_area = field->to<WebTextAreaElement>();
550 if (text_area.value() != data.value) {
551 text_area.setValue(data.value);
552 text_area.dispatchFormControlChangeEvent();
553 }
554 } else if (IsSelectElement(*field)) {
555 WebSelectElement select_element = field->to<WebSelectElement>();
556 if (select_element.value() != data.value) {
557 select_element.setValue(data.value);
558 select_element.dispatchFormControlChangeEvent();
559 }
560 } else { 545 } else {
561 DCHECK(IsCheckableElement(input_element)); 546 DCHECK(IsCheckableElement(input_element));
562 input_element->setChecked(data.is_checked, true); 547 input_element->setChecked(data.is_checked, true);
563 } 548 }
549
550 if (is_initiating_node &&
551 ((IsTextInput(input_element) || IsMonthInput(input_element)) ||
552 IsTextAreaElement(*field))) {
553 int length = field->value().length();
554 field->setSelectionRange(length, length);
555 // Clear the current IME composition (the underline), if there is one.
556 field->document().frame()->unmarkText();
557 }
564 } 558 }
565 559
566 // Sets the |field|'s "suggested" (non JS visible) value to the value in |data|. 560 // Sets the |field|'s "suggested" (non JS visible) value to the value in |data|.
567 // Also sets the "autofilled" attribute, causing the background to be yellow. 561 // Also sets the "autofilled" attribute, causing the background to be yellow.
568 void PreviewFormField(const FormFieldData& data, 562 void PreviewFormField(const FormFieldData& data,
569 bool is_initiating_node, 563 bool is_initiating_node,
570 blink::WebFormControlElement* field) { 564 blink::WebFormControlElement* field) {
571 // Nothing to preview. 565 // Nothing to preview.
572 if (data.value.empty()) 566 if (data.value.empty())
573 return; 567 return;
574 568
575 // Preview input and textarea fields. For input fields, excludes checkboxes 569 // Preview input and textarea fields. For input fields, excludes checkboxes
576 // and radio buttons, as there is no provision for setSuggestedCheckedValue 570 // and radio buttons, as there is no provision for setSuggestedCheckedValue
577 // in WebInputElement. 571 // in WebInputElement.
578 WebInputElement* input_element = toWebInputElement(field); 572 WebInputElement* input_element = toWebInputElement(field);
579 if (IsTextInput(input_element) || IsMonthInput(input_element)) { 573 if (IsTextInput(input_element) || IsMonthInput(input_element)) {
580 // If the maxlength attribute contains a negative value, maxLength() 574 // If the maxlength attribute contains a negative value, maxLength()
581 // returns the default maxlength value. 575 // returns the default maxlength value.
582 input_element->setSuggestedValue( 576 input_element->setSuggestedValue(
583 data.value.substr(0, input_element->maxLength())); 577 data.value.substr(0, input_element->maxLength()));
584 input_element->setAutofilled(true); 578 input_element->setAutofilled(true);
585 if (is_initiating_node) {
586 // Select the part of the text that the user didn't type.
587 input_element->setSelectionRange(
588 input_element->value().length(),
589 input_element->suggestedValue().length());
590 }
591 } else if (IsTextAreaElement(*field)) { 579 } else if (IsTextAreaElement(*field)) {
592 WebTextAreaElement textarea = field->to<WebTextAreaElement>(); 580 field->setSuggestedValue(data.value);
593 textarea.setSuggestedValue(data.value);
594 field->setAutofilled(true); 581 field->setAutofilled(true);
595 } 582 }
583
584 if (is_initiating_node &&
585 (IsTextInput(input_element) || IsTextAreaElement(*field))) {
586 // Select the part of the text that the user didn't type.
587 int start = field->value().length();
588 int end = field->suggestedValue().length();
589 field->setSelectionRange(start, end);
590 }
596 } 591 }
597 592
598 std::string RetrievalMethodToString( 593 std::string RetrievalMethodToString(
599 const WebElementDescriptor::RetrievalMethod& method) { 594 const WebElementDescriptor::RetrievalMethod& method) {
600 switch (method) { 595 switch (method) {
601 case WebElementDescriptor::CSS_SELECTOR: 596 case WebElementDescriptor::CSS_SELECTOR:
602 return "CSS_SELECTOR"; 597 return "CSS_SELECTOR";
603 case WebElementDescriptor::ID: 598 case WebElementDescriptor::ID:
604 return "ID"; 599 return "ID";
605 case WebElementDescriptor::NONE: 600 case WebElementDescriptor::NONE:
(...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after
765 // Discard overly long attribute values to avoid DOS-ing the browser 760 // Discard overly long attribute values to avoid DOS-ing the browser
766 // process. However, send over a default string to indicate that the 761 // process. However, send over a default string to indicate that the
767 // attribute was present. 762 // attribute was present.
768 field->autocomplete_attribute = "x-max-data-length-exceeded"; 763 field->autocomplete_attribute = "x-max-data-length-exceeded";
769 } 764 }
770 765
771 if (!IsAutofillableElement(element)) 766 if (!IsAutofillableElement(element))
772 return; 767 return;
773 768
774 const WebInputElement* input_element = toWebInputElement(&element); 769 const WebInputElement* input_element = toWebInputElement(&element);
770 if (IsAutofillableInputElement(input_element) ||
771 IsTextAreaElement(element)) {
772 field->is_autofilled = element.isAutofilled();
773 field->is_focusable = element.isFocusable();
774 field->should_autocomplete = element.autoComplete();
775 field->text_direction = element.directionForFormData() ==
776 "rtl" ? base::i18n::RIGHT_TO_LEFT : base::i18n::LEFT_TO_RIGHT;
777 }
778
775 if (IsAutofillableInputElement(input_element)) { 779 if (IsAutofillableInputElement(input_element)) {
776 if (IsTextInput(input_element)) 780 if (IsTextInput(input_element))
777 field->max_length = input_element->maxLength(); 781 field->max_length = input_element->maxLength();
778 782
779 field->is_autofilled = input_element->isAutofilled();
780 field->is_focusable = input_element->isFocusable();
781 field->is_checkable = IsCheckableElement(input_element); 783 field->is_checkable = IsCheckableElement(input_element);
782 field->is_checked = input_element->isChecked(); 784 field->is_checked = input_element->isChecked();
783 field->should_autocomplete = input_element->autoComplete();
784 field->text_direction = input_element->directionForFormData() == "rtl" ?
785 base::i18n::RIGHT_TO_LEFT : base::i18n::LEFT_TO_RIGHT;
786 } else if (IsTextAreaElement(element)) { 785 } else if (IsTextAreaElement(element)) {
787 // Nothing more to do in this case. 786 // Nothing more to do in this case.
788 } else if (extract_mask & EXTRACT_OPTIONS) { 787 } else if (extract_mask & EXTRACT_OPTIONS) {
789 // Set option strings on the field if available. 788 // Set option strings on the field if available.
790 DCHECK(IsSelectElement(element)); 789 DCHECK(IsSelectElement(element));
791 const WebSelectElement select_element = element.toConst<WebSelectElement>(); 790 const WebSelectElement select_element = element.toConst<WebSelectElement>();
792 GetOptionStringsFromElement(select_element, 791 GetOptionStringsFromElement(select_element,
793 &field->option_values, 792 &field->option_values,
794 &field->option_contents); 793 &field->option_contents);
795 } 794 }
796 795
797 if (!(extract_mask & EXTRACT_VALUE)) 796 if (!(extract_mask & EXTRACT_VALUE))
798 return; 797 return;
799 798
800 base::string16 value; 799 base::string16 value = element.value();
801 if (IsAutofillableInputElement(input_element)) { 800
802 value = input_element->value(); 801 if (IsSelectElement(element)) {
803 } else if (IsTextAreaElement(element)) {
804 value = element.toConst<WebTextAreaElement>().value();
805 } else {
806 DCHECK(IsSelectElement(element));
807 const WebSelectElement select_element = element.toConst<WebSelectElement>(); 802 const WebSelectElement select_element = element.toConst<WebSelectElement>();
808 value = select_element.value();
809
810 // Convert the |select_element| value to text if requested. 803 // Convert the |select_element| value to text if requested.
811 if (extract_mask & EXTRACT_OPTION_TEXT) { 804 if (extract_mask & EXTRACT_OPTION_TEXT) {
812 WebVector<WebElement> list_items = select_element.listItems(); 805 WebVector<WebElement> list_items = select_element.listItems();
813 for (size_t i = 0; i < list_items.size(); ++i) { 806 for (size_t i = 0; i < list_items.size(); ++i) {
814 if (IsOptionElement(list_items[i])) { 807 if (IsOptionElement(list_items[i])) {
815 const WebOptionElement option_element = 808 const WebOptionElement option_element =
816 list_items[i].toConst<WebOptionElement>(); 809 list_items[i].toConst<WebOptionElement>();
817 if (option_element.value() == value) { 810 if (option_element.value() == value) {
818 value = option_element.text(); 811 value = option_element.text();
819 break; 812 break;
(...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after
964 957
965 // Copy the created FormFields into the resulting FormData object. 958 // Copy the created FormFields into the resulting FormData object.
966 for (ScopedVector<FormFieldData>::const_iterator iter = form_fields.begin(); 959 for (ScopedVector<FormFieldData>::const_iterator iter = form_fields.begin();
967 iter != form_fields.end(); ++iter) { 960 iter != form_fields.end(); ++iter) {
968 form->fields.push_back(**iter); 961 form->fields.push_back(**iter);
969 } 962 }
970 963
971 return true; 964 return true;
972 } 965 }
973 966
974 bool FindFormAndFieldForInputElement(const WebInputElement& element, 967 bool FindFormAndFieldForFormControlElement(const WebFormControlElement& element,
975 FormData* form, 968 FormData* form,
976 FormFieldData* field, 969 FormFieldData* field,
977 RequirementsMask requirements) { 970 RequirementsMask requirements) {
978 if (!IsAutofillableElement(element)) 971 if (!IsAutofillableElement(element))
979 return false; 972 return false;
980 973
981 const WebFormElement form_element = element.form(); 974 const WebFormElement form_element = element.form();
982 if (form_element.isNull()) 975 if (form_element.isNull())
983 return false; 976 return false;
984 977
985 ExtractMask extract_mask = 978 ExtractMask extract_mask =
986 static_cast<ExtractMask>(EXTRACT_VALUE | EXTRACT_OPTIONS); 979 static_cast<ExtractMask>(EXTRACT_VALUE | EXTRACT_OPTIONS);
987 return WebFormElementToFormData(form_element, 980 return WebFormElementToFormData(form_element,
988 element, 981 element,
989 requirements, 982 requirements,
990 extract_mask, 983 extract_mask,
991 form, 984 form,
992 field); 985 field);
993 } 986 }
994 987
995 void FillForm(const FormData& form, const WebInputElement& element) { 988 void FillForm(const FormData& form, const WebFormControlElement& element) {
996 WebFormElement form_element = element.form(); 989 WebFormElement form_element = element.form();
997 if (form_element.isNull()) 990 if (form_element.isNull())
998 return; 991 return;
999 992
1000 ForEachMatchingFormField(form_element, 993 ForEachMatchingFormField(form_element,
1001 element, 994 element,
1002 form, 995 form,
1003 FILTER_ALL_NON_EDITIABLE_ELEMENTS, 996 FILTER_ALL_NON_EDITIABLE_ELEMENTS,
1004 false, /* dont force override */ 997 false, /* dont force override */
1005 &FillFormField); 998 &FillFormField);
(...skipping 20 matching lines...) Expand all
1026 return; 1019 return;
1027 1020
1028 ForEachMatchingFormField(form_element, 1021 ForEachMatchingFormField(form_element,
1029 WebInputElement(), 1022 WebInputElement(),
1030 form_data, 1023 form_data,
1031 FILTER_NONE, 1024 FILTER_NONE,
1032 true, /* force override */ 1025 true, /* force override */
1033 &FillFormField); 1026 &FillFormField);
1034 } 1027 }
1035 1028
1036 void PreviewForm(const FormData& form, const WebInputElement& element) { 1029 void PreviewForm(const FormData& form, const WebFormControlElement& element) {
1037 WebFormElement form_element = element.form(); 1030 WebFormElement form_element = element.form();
1038 if (form_element.isNull()) 1031 if (form_element.isNull())
1039 return; 1032 return;
1040 1033
1041 ForEachMatchingFormField(form_element, 1034 ForEachMatchingFormField(form_element,
1042 element, 1035 element,
1043 form, 1036 form,
1044 FILTER_ALL_NON_EDITIABLE_ELEMENTS, 1037 FILTER_ALL_NON_EDITIABLE_ELEMENTS,
1045 false, /* dont force override */ 1038 false, /* dont force override */
1046 &PreviewFormField); 1039 &PreviewFormField);
1047 } 1040 }
1048 1041
1049 bool ClearPreviewedFormWithElement(const WebInputElement& element, 1042 bool ClearPreviewedFormWithElement(const WebFormControlElement& element,
1050 bool was_autofilled) { 1043 bool was_autofilled) {
1051 WebFormElement form_element = element.form(); 1044 WebFormElement form_element = element.form();
1052 if (form_element.isNull()) 1045 if (form_element.isNull())
1053 return false; 1046 return false;
1054 1047
1055 std::vector<WebFormControlElement> control_elements; 1048 std::vector<WebFormControlElement> control_elements;
1056 ExtractAutofillableElements(form_element, REQUIRE_AUTOCOMPLETE, 1049 ExtractAutofillableElements(form_element, REQUIRE_AUTOCOMPLETE,
1057 &control_elements); 1050 &control_elements);
1058 for (size_t i = 0; i < control_elements.size(); ++i) { 1051 for (size_t i = 0; i < control_elements.size(); ++i) {
1059 // There might be unrelated elements in this form which have already been 1052 // There might be unrelated elements in this form which have already been
(...skipping 12 matching lines...) Expand all
1072 // If the element is not auto-filled, we did not preview it, 1065 // If the element is not auto-filled, we did not preview it,
1073 // so there is nothing to reset. 1066 // so there is nothing to reset.
1074 if(!control_element.isAutofilled()) 1067 if(!control_element.isAutofilled())
1075 continue; 1068 continue;
1076 1069
1077 if ((IsTextInput(input_element) && 1070 if ((IsTextInput(input_element) &&
1078 input_element->suggestedValue().isEmpty()) || 1071 input_element->suggestedValue().isEmpty()) ||
1079 (IsMonthInput(input_element) && 1072 (IsMonthInput(input_element) &&
1080 input_element->suggestedValue().isEmpty()) || 1073 input_element->suggestedValue().isEmpty()) ||
1081 (IsTextAreaElement(control_element) && 1074 (IsTextAreaElement(control_element) &&
1082 control_element.to<WebTextAreaElement>().suggestedValue().isEmpty())) 1075 control_element.suggestedValue().isEmpty()))
Ilya Sherman 2014/03/14 07:24:46 nit: Can this be shortened to the following? if (
ziran.sun 2014/03/14 17:01:42 Done.
1083 continue; 1076 continue;
1084 1077
1085 // Clear the suggested value. For the initiating node, also restore the 1078 // Clear the suggested value. For the initiating node, also restore the
1086 // original value. 1079 // original value.
1080 bool is_initiating_node = false;
Ilya Sherman 2014/03/14 07:24:46 nit: Can the logic be extracted out to here as "bo
ziran.sun 2014/03/14 17:01:42 Done.
1087 if (IsTextInput(input_element) || IsMonthInput(input_element)) { 1081 if (IsTextInput(input_element) || IsMonthInput(input_element)) {
1088 input_element->setSuggestedValue(WebString()); 1082 input_element->setSuggestedValue(WebString());
1089 bool is_initiating_node = (element == *input_element); 1083 is_initiating_node = (element == *input_element);
1090 if (is_initiating_node) 1084 if (is_initiating_node)
1091 input_element->setAutofilled(was_autofilled); 1085 input_element->setAutofilled(was_autofilled);
1092 else 1086 else
1093 input_element->setAutofilled(false); 1087 input_element->setAutofilled(false);
Ilya Sherman 2014/03/14 07:24:46 nit: Please share these four lines between input e
ziran.sun 2014/03/14 17:01:42 Done.
1094
1095 // Clearing the suggested value in the focused node (above) can cause
1096 // selection to be lost. We force selection range to restore the text
1097 // cursor.
1098 if (is_initiating_node) {
1099 int length = input_element->value().length();
1100 input_element->setSelectionRange(length, length);
1101 }
1102 } else if (IsTextAreaElement(control_element)) { 1088 } else if (IsTextAreaElement(control_element)) {
1103 WebTextAreaElement text_area = control_element.to<WebTextAreaElement>(); 1089 control_element.setSuggestedValue(WebString());
1104 text_area.setSuggestedValue(WebString()); 1090 is_initiating_node = (element == control_element);
1105 bool is_initiating_node = (element == text_area);
1106 if (is_initiating_node) 1091 if (is_initiating_node)
1107 control_element.setAutofilled(was_autofilled); 1092 control_element.setAutofilled(was_autofilled);
1108 else 1093 else
1109 control_element.setAutofilled(false); 1094 control_element.setAutofilled(false);
1110 } 1095 }
1096
1097 // Clearing the suggested value in the focused node (above) can cause
1098 // selection to be lost. We force selection range to restore the text
1099 // cursor.
1100 if (is_initiating_node) {
1101 int length = control_element.value().length();
1102 control_element.setSelectionRange(length, length);
1103 }
1111 } 1104 }
1112 1105
1113 return true; 1106 return true;
1114 } 1107 }
1115 1108
1116 bool FormWithElementIsAutofilled(const WebInputElement& element) { 1109 bool FormWithElementIsAutofilled(const WebInputElement& element) {
1117 WebFormElement form_element = element.form(); 1110 WebFormElement form_element = element.form();
1118 if (form_element.isNull()) 1111 if (form_element.isNull())
1119 return false; 1112 return false;
1120 1113
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
1177 tag_is_allowed = true; 1170 tag_is_allowed = true;
1178 break; 1171 break;
1179 } 1172 }
1180 } 1173 }
1181 if (!tag_is_allowed) 1174 if (!tag_is_allowed)
1182 return false; 1175 return false;
1183 } 1176 }
1184 return true; 1177 return true;
1185 } 1178 }
1186 1179
1187 gfx::RectF GetScaledBoundingBox(float scale, WebInputElement* element) { 1180 gfx::RectF GetScaledBoundingBox(float scale, WebFormControlElement* element) {
1188 gfx::Rect bounding_box(element->boundsInViewportSpace()); 1181 gfx::Rect bounding_box(element->boundsInViewportSpace());
1189 return gfx::RectF(bounding_box.x() * scale, 1182 return gfx::RectF(bounding_box.x() * scale,
1190 bounding_box.y() * scale, 1183 bounding_box.y() * scale,
1191 bounding_box.width() * scale, 1184 bounding_box.width() * scale,
1192 bounding_box.height() * scale); 1185 bounding_box.height() * scale);
1193 } 1186 }
1194 1187
1195 } // namespace autofill 1188 } // namespace autofill
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698