| 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::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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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") || |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 |
| OLD | NEW |