diff options
author | isherman@chromium.org <isherman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-08-10 05:38:45 +0000 |
---|---|---|
committer | isherman@chromium.org <isherman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-08-10 05:38:45 +0000 |
commit | 1b5f69a1bc11fe1670f994c80a2ae32247c8e549 (patch) | |
tree | 3939ba8519f83b4cabd6cef81bad92f309ce9078 | |
parent | d8cef10c18dd5378ada0acc799c754ae39c06bd0 (diff) | |
download | chromium_src-1b5f69a1bc11fe1670f994c80a2ae32247c8e549.zip chromium_src-1b5f69a1bc11fe1670f994c80a2ae32247c8e549.tar.gz chromium_src-1b5f69a1bc11fe1670f994c80a2ae32247c8e549.tar.bz2 |
Add preliminary Autofill support for the 'autocompletetype' attribute.
BUG=92121
TEST=unit_tests --gtest_filter=FormStructureTest.*Autocompletetype*; browser_tests --gtest_filter=FormManagerTest.WebFormControlElementToFormFieldAutocompletetype
Review URL: http://codereview.chromium.org/7602006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@96131 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/autofill/autofill_manager.cc | 10 | ||||
-rw-r--r-- | chrome/browser/autofill/form_structure.cc | 195 | ||||
-rw-r--r-- | chrome/browser/autofill/form_structure.h | 9 | ||||
-rw-r--r-- | chrome/browser/autofill/form_structure_unittest.cc | 131 | ||||
-rw-r--r-- | chrome/common/autofill_messages.h | 1 | ||||
-rw-r--r-- | chrome/renderer/autofill/form_manager.cc | 8 | ||||
-rw-r--r-- | chrome/renderer/autofill/form_manager_browsertest.cc | 130 | ||||
-rw-r--r-- | chrome/test/data/autofill/heuristics/input/01_autocompletetype_attribute.html | 29 | ||||
-rw-r--r-- | chrome/test/data/autofill/heuristics/output/01_autocompletetype_attribute.out | 27 | ||||
-rw-r--r-- | webkit/glue/form_field.cc | 3 | ||||
-rw-r--r-- | webkit/glue/form_field.h | 2 |
11 files changed, 535 insertions, 10 deletions
diff --git a/chrome/browser/autofill/autofill_manager.cc b/chrome/browser/autofill/autofill_manager.cc index ce59133..d8c025b 100644 --- a/chrome/browser/autofill/autofill_manager.cc +++ b/chrome/browser/autofill/autofill_manager.cc @@ -423,8 +423,10 @@ void AutofillManager::OnFormSubmitted(const FormData& form) { if (!personal_data_->profiles().empty() || !personal_data_->credit_cards().empty()) { DeterminePossibleFieldTypesForUpload(&submitted_form); - UploadFormData(submitted_form); submitted_form.LogQualityMetrics(*metric_logger_); + + if (submitted_form.ShouldBeCrowdsourced()) + UploadFormData(submitted_form); } if (!submitted_form.IsAutofillable(true)) @@ -1097,9 +1099,9 @@ void AutofillManager::ParseForms(const std::vector<FormData>& forms) { form_structure->DetermineHeuristicTypes(); - // Set aside forms with method GET so that they are not included in the - // query to the server. - if (form_structure->ShouldBeParsed(true)) + // Set aside forms with method GET or author-specified types, so that they + // are not included in the query to the server. + if (form_structure->ShouldBeCrowdsourced()) form_structures_.push_back(form_structure.release()); else non_queryable_forms.push_back(form_structure.release()); diff --git a/chrome/browser/autofill/form_structure.cc b/chrome/browser/autofill/form_structure.cc index 3f2eb00..b9e0a2e 100644 --- a/chrome/browser/autofill/form_structure.cc +++ b/chrome/browser/autofill/form_structure.cc @@ -4,6 +4,8 @@ #include "chrome/browser/autofill/form_structure.h" +#include <utility> + #include "base/basictypes.h" #include "base/logging.h" #include "base/sha1.h" @@ -85,6 +87,177 @@ std::string EncodeFieldTypes(const FieldTypeSet& available_field_types) { return data_presence; } +bool ConvertToAutofillFieldType(const AutofillField& field, + const string16& autocomplete_type, + AutofillFieldType* autofill_type) { + if (autocomplete_type == ASCIIToUTF16("given-name")) { + *autofill_type = NAME_FIRST; + return true; + } + + if (autocomplete_type == ASCIIToUTF16("middle-name")) { + *autofill_type = NAME_MIDDLE; + return true; + } + + if (autocomplete_type == ASCIIToUTF16("middle-initial")) { + *autofill_type = NAME_MIDDLE_INITIAL; + return true; + } + + if (autocomplete_type == ASCIIToUTF16("surname")) { + *autofill_type = NAME_LAST; + return true; + } + + if (autocomplete_type == ASCIIToUTF16("full-name")) { + *autofill_type = NAME_FULL; + return true; + } + + if (autocomplete_type == ASCIIToUTF16("street-address") || + autocomplete_type == ASCIIToUTF16("address-line1")) { + *autofill_type = ADDRESS_HOME_LINE1; + return true; + } + + if (autocomplete_type == ASCIIToUTF16("address-line2")) { + *autofill_type = ADDRESS_HOME_LINE2; + return true; + } + + if (autocomplete_type == ASCIIToUTF16("locality")) { + *autofill_type = ADDRESS_HOME_CITY; + return true; + } + + if (autocomplete_type == ASCIIToUTF16("administrative-area")) { + *autofill_type = ADDRESS_HOME_STATE; + return true; + } + + if (autocomplete_type == ASCIIToUTF16("postal-code")) { + *autofill_type = ADDRESS_HOME_ZIP; + return true; + } + + if (autocomplete_type == ASCIIToUTF16("country")) { + *autofill_type = ADDRESS_HOME_COUNTRY; + return true; + } + + if (autocomplete_type == ASCIIToUTF16("organization")) { + *autofill_type = COMPANY_NAME; + return true; + } + + if (autocomplete_type == ASCIIToUTF16("email")) { + *autofill_type = EMAIL_ADDRESS; + return true; + } + + if (autocomplete_type == ASCIIToUTF16("phone-full")) { + *autofill_type = PHONE_HOME_WHOLE_NUMBER; + return true; + } + + if (autocomplete_type == ASCIIToUTF16("phone-country-code")) { + *autofill_type = PHONE_HOME_COUNTRY_CODE; + return true; + } + + if (autocomplete_type == ASCIIToUTF16("phone-city-code")) { + *autofill_type = PHONE_HOME_CITY_CODE; + return true; + } + + if (autocomplete_type == ASCIIToUTF16("phone-number")) { + *autofill_type = PHONE_HOME_NUMBER; + return true; + } + + if (autocomplete_type == ASCIIToUTF16("fax-full")) { + *autofill_type = PHONE_FAX_WHOLE_NUMBER; + return true; + } + + if (autocomplete_type == ASCIIToUTF16("fax-country-code")) { + *autofill_type = PHONE_FAX_COUNTRY_CODE; + return true; + } + + if (autocomplete_type == ASCIIToUTF16("fax-city-code")) { + *autofill_type = PHONE_FAX_CITY_CODE; + return true; + } + + if (autocomplete_type == ASCIIToUTF16("fax-number")) { + *autofill_type = PHONE_FAX_NUMBER; + return true; + } + + if (autocomplete_type == ASCIIToUTF16("cc-full-name")) { + *autofill_type = CREDIT_CARD_NAME; + return true; + } + + if (autocomplete_type == ASCIIToUTF16("cc-number")) { + *autofill_type = CREDIT_CARD_NUMBER; + return true; + } + + if (autocomplete_type == ASCIIToUTF16("cc-exp-month")) { + *autofill_type = CREDIT_CARD_EXP_MONTH; + return true; + } + + if (autocomplete_type == ASCIIToUTF16("cc-exp-year")) { + if (field.max_length == 2) + *autofill_type = CREDIT_CARD_EXP_2_DIGIT_YEAR; + else + *autofill_type = CREDIT_CARD_EXP_4_DIGIT_YEAR; + return true; + } + + if (autocomplete_type == ASCIIToUTF16("cc-exp")) { + // TODO(isherman): Choose variant based on HTML5 validation regex. + *autofill_type = CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR; + return true; + } + + return false; +} + +// Classifies each field in |fields| based upon its |autocompletetype| +// attribute, if the attribute is available. The association is stored into +// |map|. Returns |true| if the attribute is available (and non-empty) for at +// least one field. +bool ParseAutocompletetypeAttributes(const std::vector<AutofillField*>& fields, + FieldTypeMap* map) { + bool found_attribute = false; + for (std::vector<AutofillField*>::const_iterator field = fields.begin(); + field != fields.end(); ++field) { + if ((*field)->autocomplete_type.empty()) + continue; + + found_attribute = true; + std::vector<string16> types; + Tokenize((*field)->autocomplete_type, ASCIIToUTF16(" "), &types); + + // TODO(isherman): Handle sections: http://crbug.com/92121 + for (std::vector<string16>::const_iterator type = types.begin(); + type != types.end(); ++type) { + AutofillFieldType autofill_type = UNKNOWN_TYPE; + if (ConvertToAutofillFieldType(**field, *type, &autofill_type)) { + map->insert(make_pair((*field)->unique_name(), autofill_type)); + break; + } + } + } + + return found_attribute; +} + } // namespace FormStructure::FormStructure(const FormData& form) @@ -93,7 +266,8 @@ FormStructure::FormStructure(const FormData& form) target_url_(form.action), autofill_count_(0), upload_required_(USE_UPLOAD_RATES), - server_experiment_id_("no server response") { + server_experiment_id_("no server response"), + has_author_specified_types_(false) { // Copy the form fields. std::vector<webkit_glue::FormField>::const_iterator field; for (field = form.fields.begin(); @@ -125,7 +299,14 @@ void FormStructure::DetermineHeuristicTypes() { autofill_count_ = 0; FieldTypeMap field_type_map; - FormField::ParseFormFields(fields_.get(), &field_type_map); + + // First, try to detect field types based on the fields' |autocompletetype| + // attributes. If there is at least one form field with this attribute, don't + // try to apply other heuristics to match fields in this form. + has_author_specified_types_ = + ParseAutocompletetypeAttributes(fields_.get(), &field_type_map); + if (!has_author_specified_types_) + FormField::ParseFormFields(fields_.get(), &field_type_map); for (size_t index = 0; index < field_count(); index++) { AutofillField* field = fields_[index]; @@ -140,8 +321,6 @@ void FormStructure::DetermineHeuristicTypes() { } field->set_heuristic_type(heuristic_autofill_type); - - AutofillType autofill_type(field->type()); } } @@ -149,8 +328,8 @@ bool FormStructure::EncodeUploadRequest( const FieldTypeSet& available_field_types, bool form_was_autofilled, std::string* encoded_xml) const { - if (!ShouldBeParsed(true)) { - NOTREACHED(); // Caller should've checked for search pages. + if (!ShouldBeCrowdsourced()) { + NOTREACHED(); return false; } @@ -403,6 +582,10 @@ bool FormStructure::ShouldBeParsed(bool require_method_post) const { return !require_method_post || (method_ == POST); } +bool FormStructure::ShouldBeCrowdsourced() const { + return !has_author_specified_types_ && ShouldBeParsed(true); +} + void FormStructure::UpdateFromCache(const FormStructure& cached_form) { // Map from field signatures to cached fields. std::map<std::string, const AutofillField*> cached_fields; diff --git a/chrome/browser/autofill/form_structure.h b/chrome/browser/autofill/form_structure.h index d94be92..92e4de9 100644 --- a/chrome/browser/autofill/form_structure.h +++ b/chrome/browser/autofill/form_structure.h @@ -96,6 +96,11 @@ class FormStructure { // |require_method_post| is true. bool ShouldBeParsed(bool require_method_post) const; + // Returns true if we should query the crowdsourcing server to determine this + // form's field types. If the form includes author-specified types, this will + // return false. + bool ShouldBeCrowdsourced() const; + // Sets the field types and experiment id to be those set for |cached_form|. void UpdateFromCache(const FormStructure& cached_form); @@ -181,6 +186,10 @@ class FormStructure { // GET or POST. RequestMethod method_; + // Whether the form includes any field types explicitly specified by the site + // author, via the |autocompletetype| attribute. + bool has_author_specified_types_; + DISALLOW_COPY_AND_ASSIGN(FormStructure); }; diff --git a/chrome/browser/autofill/form_structure_unittest.cc b/chrome/browser/autofill/form_structure_unittest.cc index 4bfeb77..c6ff870 100644 --- a/chrome/browser/autofill/form_structure_unittest.cc +++ b/chrome/browser/autofill/form_structure_unittest.cc @@ -339,6 +339,137 @@ TEST(FormStructureTest, HeuristicsContactInfo) { EXPECT_EQ(UNKNOWN_TYPE, form_structure->field(8)->heuristic_type()); } +// Verify that we can correctly process the 'autocompletetype' attribute. +TEST(FormStructureTest, HeuristicsAutocompletetype) { + scoped_ptr<FormStructure> form_structure; + FormData form; + form.method = ASCIIToUTF16("post"); + + FormField field; + field.form_control_type = ASCIIToUTF16("text"); + + field.label = string16(); + field.name = ASCIIToUTF16("field1"); + field.autocomplete_type = ASCIIToUTF16("given-name"); + form.fields.push_back(field); + + field.label = string16(); + field.name = ASCIIToUTF16("field2"); + field.autocomplete_type = ASCIIToUTF16("surname"); + form.fields.push_back(field); + + field.label = string16(); + field.name = ASCIIToUTF16("field3"); + field.autocomplete_type = ASCIIToUTF16("email"); + form.fields.push_back(field); + + form_structure.reset(new FormStructure(form)); + form_structure->DetermineHeuristicTypes(); + EXPECT_TRUE(form_structure->IsAutofillable(true)); + + // Expect the correct number of fields. + ASSERT_EQ(3U, form_structure->field_count()); + ASSERT_EQ(3U, form_structure->autofill_count()); + + EXPECT_EQ(NAME_FIRST, form_structure->field(0)->heuristic_type()); + EXPECT_EQ(NAME_LAST, form_structure->field(1)->heuristic_type()); + EXPECT_EQ(EMAIL_ADDRESS, form_structure->field(2)->heuristic_type()); +} + +// If at least one field includes the 'autocompletetype' attribute, we should +// not try to apply any other heuristics. +TEST(FormStructureTest, AutocompletetypeOverridesOtherHeuristics) { + scoped_ptr<FormStructure> form_structure; + FormData form; + form.method = ASCIIToUTF16("post"); + + // Start with a regular contact form. + FormField field; + field.form_control_type = ASCIIToUTF16("text"); + + field.label = ASCIIToUTF16("First Name"); + field.name = ASCIIToUTF16("firstname"); + form.fields.push_back(field); + + field.label = ASCIIToUTF16("Last Name"); + field.name = ASCIIToUTF16("lastname"); + form.fields.push_back(field); + + field.label = ASCIIToUTF16("Email"); + field.name = ASCIIToUTF16("email"); + form.fields.push_back(field); + + form_structure.reset(new FormStructure(form)); + form_structure->DetermineHeuristicTypes(); + EXPECT_TRUE(form_structure->IsAutofillable(true)); + EXPECT_TRUE(form_structure->ShouldBeCrowdsourced()); + + ASSERT_EQ(3U, form_structure->field_count()); + ASSERT_EQ(3U, form_structure->autofill_count()); + + EXPECT_EQ(NAME_FIRST, form_structure->field(0)->heuristic_type()); + EXPECT_EQ(NAME_LAST, form_structure->field(1)->heuristic_type()); + EXPECT_EQ(EMAIL_ADDRESS, form_structure->field(2)->heuristic_type()); + + // Now update the first form field to include an 'autocompletetype' attribute. + form.fields.front().autocomplete_type = ASCIIToUTF16("x-other"); + form_structure.reset(new FormStructure(form)); + form_structure->DetermineHeuristicTypes(); + EXPECT_FALSE(form_structure->IsAutofillable(true)); + EXPECT_FALSE(form_structure->ShouldBeCrowdsourced()); + + ASSERT_EQ(3U, form_structure->field_count()); + ASSERT_EQ(0U, form_structure->autofill_count()); + + EXPECT_EQ(UNKNOWN_TYPE, form_structure->field(0)->heuristic_type()); + EXPECT_EQ(UNKNOWN_TYPE, form_structure->field(1)->heuristic_type()); + EXPECT_EQ(UNKNOWN_TYPE, form_structure->field(2)->heuristic_type()); +} + +// Verify that we can correctly process fallback types listed in the +// 'autocompletetype' attribute. +TEST(FormStructureTest, HeuristicsAutocompletetypeWithFallbacks) { + scoped_ptr<FormStructure> form_structure; + FormData form; + form.method = ASCIIToUTF16("post"); + + FormField field; + field.form_control_type = ASCIIToUTF16("text"); + + // Skip over any sections and "x"-prefixed types. + field.label = string16(); + field.name = ASCIIToUTF16("field1"); + field.autocomplete_type = + ASCIIToUTF16("section-full-name x-given-name-initial given-name"); + form.fields.push_back(field); + + // Stop processing once we see a known type. + field.label = string16(); + field.name = ASCIIToUTF16("field2"); + field.autocomplete_type = ASCIIToUTF16("section-full-name surname full-name"); + form.fields.push_back(field); + + // Skip over unknown types even if they are not prefixed with "x-". + field.label = string16(); + field.name = ASCIIToUTF16("field3"); + field.autocomplete_type = + ASCIIToUTF16("section-shipping mobile-phone-full phone-full"); + form.fields.push_back(field); + + form_structure.reset(new FormStructure(form)); + form_structure->DetermineHeuristicTypes(); + EXPECT_TRUE(form_structure->IsAutofillable(true)); + + // Expect the correct number of fields. + ASSERT_EQ(3U, form_structure->field_count()); + ASSERT_EQ(3U, form_structure->autofill_count()); + + EXPECT_EQ(NAME_FIRST, form_structure->field(0)->heuristic_type()); + EXPECT_EQ(NAME_LAST, form_structure->field(1)->heuristic_type()); + EXPECT_EQ(PHONE_HOME_WHOLE_NUMBER, + form_structure->field(2)->heuristic_type()); +} + TEST(FormStructureTest, HeuristicsSample8) { scoped_ptr<FormStructure> form_structure; FormData form; diff --git a/chrome/common/autofill_messages.h b/chrome/common/autofill_messages.h index 42575bd..a9750ef 100644 --- a/chrome/common/autofill_messages.h +++ b/chrome/common/autofill_messages.h @@ -22,6 +22,7 @@ IPC_STRUCT_TRAITS_BEGIN(webkit_glue::FormField) IPC_STRUCT_TRAITS_MEMBER(name) IPC_STRUCT_TRAITS_MEMBER(value) IPC_STRUCT_TRAITS_MEMBER(form_control_type) + IPC_STRUCT_TRAITS_MEMBER(autocomplete_type) IPC_STRUCT_TRAITS_MEMBER(max_length) IPC_STRUCT_TRAITS_MEMBER(is_autofilled) IPC_STRUCT_TRAITS_MEMBER(option_values) diff --git a/chrome/renderer/autofill/form_manager.cc b/chrome/renderer/autofill/form_manager.cc index e33ef38..3465f93 100644 --- a/chrome/renderer/autofill/form_manager.cc +++ b/chrome/renderer/autofill/form_manager.cc @@ -598,6 +598,14 @@ void FormManager::WebFormControlElementToFormField( // WebFormElementToFormData. field->name = element.nameForAutofill(); field->form_control_type = element.formControlType(); + field->autocomplete_type = element.getAttribute("x-autocompletetype"); + TrimWhitespace(field->autocomplete_type, TRIM_ALL, &field->autocomplete_type); + if (field->autocomplete_type.size() > kMaxDataLength) { + // Discard overly long attribute values to avoid DOS-ing the browser + // process. However, send over a default string to indicate that the + // attribute was present. + field->autocomplete_type = ASCIIToUTF16("x-max-data-length-exceeded"); + } if (!IsAutofillableElement(element)) return; diff --git a/chrome/renderer/autofill/form_manager_browsertest.cc b/chrome/renderer/autofill/form_manager_browsertest.cc index f39f050..856cdba 100644 --- a/chrome/renderer/autofill/form_manager_browsertest.cc +++ b/chrome/renderer/autofill/form_manager_browsertest.cc @@ -331,6 +331,136 @@ TEST_F(FormManagerTest, WebFormControlElementToFormFieldInvalidType) { EXPECT_FORM_FIELD_EQUALS(expected, result); } +// We should be able to extract the autocompletetype attribute. +TEST_F(FormManagerTest, WebFormControlElementToFormFieldAutocompletetype) { + std::string html = + "<INPUT type=\"text\" id=\"absent\"/>" + "<INPUT type=\"text\" id=\"empty\" x-autocompletetype=\"\"/>" + "<INPUT type=\"text\" id=\"whitespace\" x-autocompletetype=\" \"/>" + "<INPUT type=\"text\" id=\"regular\" x-autocompletetype=\"email\"/>" + "<INPUT type=\"text\" id=\"multi-valued\" " + " x-autocompletetype=\"x-confirm-email email\"/>" + "<INPUT type=\"text\" id=\"unprefixed\" autocompletetype=\"email\"/>" + "<SELECT id=\"select\" x-autocompletetype=\"state\"/>" + " <OPTION value=\"CA\">California</OPTION>" + " <OPTION value=\"TX\">Texas</OPTION>" + "</SELECT>"; + html += + "<INPUT type=\"text\" id=\"malicious\" x-autocompletetype=\"" + + std::string(10000, 'x') + "\"/>"; + LoadHTML(html.c_str()); + + WebFrame* frame = GetMainFrame(); + ASSERT_NE(static_cast<WebFrame*>(NULL), frame); + + // An absent attribute is equivalent to an empty one. + WebElement web_element = frame->document().getElementById("absent"); + WebFormControlElement element = web_element.to<WebFormControlElement>(); + FormField result1; + FormManager::WebFormControlElementToFormField(element, + FormManager::EXTRACT_NONE, + &result1); + + FormField expected; + expected.name = ASCIIToUTF16("absent"); + expected.form_control_type = ASCIIToUTF16("text"); + expected.autocomplete_type = string16(); + expected.max_length = WebInputElement::defaultMaxLength(); + EXPECT_FORM_FIELD_EQUALS(expected, result1); + + web_element = frame->document().getElementById("empty"); + element = web_element.to<WebFormControlElement>(); + FormField result2; + FormManager::WebFormControlElementToFormField(element, + FormManager::EXTRACT_NONE, + &result2); + expected.name = ASCIIToUTF16("empty"); + expected.form_control_type = ASCIIToUTF16("text"); + expected.autocomplete_type = string16(); + expected.max_length = WebInputElement::defaultMaxLength(); + EXPECT_FORM_FIELD_EQUALS(expected, result2); + + // The renderer should trim whitespace. + web_element = frame->document().getElementById("whitespace"); + element = web_element.to<WebFormControlElement>(); + FormField result3; + FormManager::WebFormControlElementToFormField(element, + FormManager::EXTRACT_NONE, + &result3); + expected.name = ASCIIToUTF16("whitespace"); + expected.form_control_type = ASCIIToUTF16("text"); + expected.autocomplete_type = string16(); + expected.max_length = WebInputElement::defaultMaxLength(); + EXPECT_FORM_FIELD_EQUALS(expected, result3); + + // Common case: exactly one type specified. + web_element = frame->document().getElementById("regular"); + element = web_element.to<WebFormControlElement>(); + FormField result4; + FormManager::WebFormControlElementToFormField(element, + FormManager::EXTRACT_NONE, + &result4); + expected.name = ASCIIToUTF16("regular"); + expected.form_control_type = ASCIIToUTF16("text"); + expected.autocomplete_type = ASCIIToUTF16("email"); + expected.max_length = WebInputElement::defaultMaxLength(); + EXPECT_FORM_FIELD_EQUALS(expected, result4); + + // Verify that we correctly extract fallback types as well. + web_element = frame->document().getElementById("multi-valued"); + element = web_element.to<WebFormControlElement>(); + FormField result5; + FormManager::WebFormControlElementToFormField(element, + FormManager::EXTRACT_NONE, + &result5); + expected.name = ASCIIToUTF16("multi-valued"); + expected.form_control_type = ASCIIToUTF16("text"); + expected.autocomplete_type = ASCIIToUTF16("x-confirm-email email"); + expected.max_length = WebInputElement::defaultMaxLength(); + EXPECT_FORM_FIELD_EQUALS(expected, result5); + + // The attribute is not yet part of the HTML standard, so we only recognize + // the prefixed version -- 'x-autocompletetype' -- and not the unprefixed one. + web_element = frame->document().getElementById("unprefixed"); + element = web_element.to<WebFormControlElement>(); + FormField result6; + FormManager::WebFormControlElementToFormField(element, + FormManager::EXTRACT_NONE, + &result6); + expected.name = ASCIIToUTF16("unprefixed"); + expected.form_control_type = ASCIIToUTF16("text"); + expected.autocomplete_type = string16(); + expected.max_length = WebInputElement::defaultMaxLength(); + EXPECT_FORM_FIELD_EQUALS(expected, result6); + + // <select> elements should behave no differently from text fields here. + web_element = frame->document().getElementById("select"); + element = web_element.to<WebFormControlElement>(); + FormField result7; + FormManager::WebFormControlElementToFormField(element, + FormManager::EXTRACT_NONE, + &result7); + expected.name = ASCIIToUTF16("select"); + expected.form_control_type = ASCIIToUTF16("select-one"); + expected.autocomplete_type = ASCIIToUTF16("state"); + expected.max_length = 0; + EXPECT_FORM_FIELD_EQUALS(expected, result7); + + // Very long attribute values should be replaced by a default string, to + // prevent malicious websites from DOSing the browser process. + web_element = frame->document().getElementById("malicious"); + element = web_element.to<WebFormControlElement>(); + FormField result8; + FormManager::WebFormControlElementToFormField(element, + FormManager::EXTRACT_NONE, + &result8); + expected.name = ASCIIToUTF16("malicious"); + expected.form_control_type = ASCIIToUTF16("text"); + expected.autocomplete_type = ASCIIToUTF16("x-max-data-length-exceeded"); + expected.max_length = WebInputElement::defaultMaxLength(); + EXPECT_FORM_FIELD_EQUALS(expected, result8); +} + TEST_F(FormManagerTest, WebFormElementToFormData) { LoadHTML("<FORM name=\"TestForm\" action=\"http://cnn.com\" method=\"post\">" " <INPUT type=\"text\" id=\"firstname\" value=\"John\"/>" diff --git a/chrome/test/data/autofill/heuristics/input/01_autocompletetype_attribute.html b/chrome/test/data/autofill/heuristics/input/01_autocompletetype_attribute.html new file mode 100644 index 0000000..8413064 --- /dev/null +++ b/chrome/test/data/autofill/heuristics/input/01_autocompletetype_attribute.html @@ -0,0 +1,29 @@ +<form action="autocompletetype.html" method="post"> + <input type="text" id="fn" x-autocompletetype="given-name"><br> + <input type="text" id="mn" x-autocompletetype="middle-name"><br> + <input type="text" id="mi" x-autocompletetype="middle-initial"><br> + <input type="text" id="ln" x-autocompletetype="surname"><br> + <input type="text" id="nn" x-autocompletetype="full-name"><br> + <input type="text" id="cm" x-autocompletetype="organization"><br> + <input type="text" id="af" x-autocompletetype="street-address"><br> + <input type="text" id="a1" x-autocompletetype="address-line1"><br> + <input type="text" id="a2" x-autocompletetype="address-line2"><br> + <input type="text" id="ct" x-autocompletetype="locality"><br> + <input type="text" id="zc" x-autocompletetype="postal-code"><br> + <input type="text" id="st" x-autocompletetype="administrative-area"><br> + <input type="text" id="em" x-autocompletetype="email"><br> + <input type="text" id="pf" x-autocompletetype="phone-full"><br> + <input type="text" id="pc" x-autocompletetype="phone-country-code"><br> + <input type="text" id="pa" x-autocompletetype="phone-city-code"><br> + <input type="text" id="pn" x-autocompletetype="phone-number"><br> + <input type="text" id="ff" x-autocompletetype="fax-full"><br> + <input type="text" id="fc" x-autocompletetype="fax-country-code"><br> + <input type="text" id="fa" x-autocompletetype="fax-city-code"><br> + <input type="text" id="fx" x-autocompletetype="fax-number"><br> + <input type="text" id="c1" x-autocompletetype="cc-full-name"><br> + <input type="text" id="c2" x-autocompletetype="cc-number"><br> + <input type="text" id="c3" x-autocompletetype="cc-exp-month"><br> + <input type="text" id="c4" x-autocompletetype="cc-exp-year"><br> + <input type="text" id="c5" x-autocompletetype="cc-exp-year" maxlength="2"><br> + <input type="text" id="c6" x-autocompletetype="cc-exp"><br> +</form> diff --git a/chrome/test/data/autofill/heuristics/output/01_autocompletetype_attribute.out b/chrome/test/data/autofill/heuristics/output/01_autocompletetype_attribute.out new file mode 100644 index 0000000..f9e2358 --- /dev/null +++ b/chrome/test/data/autofill/heuristics/output/01_autocompletetype_attribute.out @@ -0,0 +1,27 @@ +NAME_FIRST | fn | | +NAME_MIDDLE | mn | | +NAME_MIDDLE_INITIAL | mi | | +NAME_LAST | ln | | +NAME_FULL | nn | | +COMPANY_NAME | cm | | +ADDRESS_HOME_LINE1 | af | | +ADDRESS_HOME_LINE1 | a1 | | +ADDRESS_HOME_LINE2 | a2 | | +ADDRESS_HOME_CITY | ct | | +ADDRESS_HOME_ZIP | zc | | +ADDRESS_HOME_STATE | st | | +EMAIL_ADDRESS | em | | +PHONE_HOME_WHOLE_NUMBER | pf | | +PHONE_HOME_COUNTRY_CODE | pc | | +PHONE_HOME_CITY_CODE | pa | | +PHONE_HOME_NUMBER | pn | | +PHONE_FAX_WHOLE_NUMBER | ff | | +PHONE_FAX_COUNTRY_CODE | fc | | +PHONE_FAX_CITY_CODE | fa | | +PHONE_FAX_NUMBER | fx | | +CREDIT_CARD_NAME | c1 | | +CREDIT_CARD_NUMBER | c2 | | +CREDIT_CARD_EXP_MONTH | c3 | | +CREDIT_CARD_EXP_4_DIGIT_YEAR | c4 | | +CREDIT_CARD_EXP_2_DIGIT_YEAR | c5 | | +CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR | c6 | | diff --git a/webkit/glue/form_field.cc b/webkit/glue/form_field.cc index 798c5b2..6c8faff 100644 --- a/webkit/glue/form_field.cc +++ b/webkit/glue/form_field.cc @@ -33,6 +33,7 @@ bool FormField::operator==(const FormField& field) const { return (label == field.label && name == field.name && form_control_type == field.form_control_type && + autocomplete_type == field.autocomplete_type && max_length == field.max_length); } @@ -50,6 +51,8 @@ std::ostream& operator<<(std::ostream& os, const FormField& field) { << " " << UTF16ToUTF8(field.form_control_type) << " " + << UTF16ToUTF8(field.autocomplete_type) + << " " << field.max_length << " " << (field.is_autofilled ? "true" : "false"); diff --git a/webkit/glue/form_field.h b/webkit/glue/form_field.h index 622dabf..bde8a53 100644 --- a/webkit/glue/form_field.h +++ b/webkit/glue/form_field.h @@ -28,6 +28,7 @@ struct FormField { string16 name; string16 value; string16 form_control_type; + string16 autocomplete_type; int max_length; bool is_autofilled; @@ -50,6 +51,7 @@ std::ostream& operator<<(std::ostream& os, const FormField& field); EXPECT_EQ(expected.name, actual.name); \ EXPECT_EQ(expected.value, actual.value); \ EXPECT_EQ(expected.form_control_type, actual.form_control_type); \ + EXPECT_EQ(expected.autocomplete_type, actual.autocomplete_type); \ EXPECT_EQ(expected.max_length, actual.max_length); \ EXPECT_EQ(expected.is_autofilled, actual.is_autofilled); \ } while (0) |