Commit 1b5f69a1 authored by isherman@chromium.org's avatar isherman@chromium.org

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
parent d8cef10c
...@@ -423,8 +423,10 @@ void AutofillManager::OnFormSubmitted(const FormData& form) { ...@@ -423,8 +423,10 @@ void AutofillManager::OnFormSubmitted(const FormData& form) {
if (!personal_data_->profiles().empty() || if (!personal_data_->profiles().empty() ||
!personal_data_->credit_cards().empty()) { !personal_data_->credit_cards().empty()) {
DeterminePossibleFieldTypesForUpload(&submitted_form); DeterminePossibleFieldTypesForUpload(&submitted_form);
UploadFormData(submitted_form);
submitted_form.LogQualityMetrics(*metric_logger_); submitted_form.LogQualityMetrics(*metric_logger_);
if (submitted_form.ShouldBeCrowdsourced())
UploadFormData(submitted_form);
} }
if (!submitted_form.IsAutofillable(true)) if (!submitted_form.IsAutofillable(true))
...@@ -1097,9 +1099,9 @@ void AutofillManager::ParseForms(const std::vector<FormData>& forms) { ...@@ -1097,9 +1099,9 @@ void AutofillManager::ParseForms(const std::vector<FormData>& forms) {
form_structure->DetermineHeuristicTypes(); form_structure->DetermineHeuristicTypes();
// Set aside forms with method GET so that they are not included in the // Set aside forms with method GET or author-specified types, so that they
// query to the server. // are not included in the query to the server.
if (form_structure->ShouldBeParsed(true)) if (form_structure->ShouldBeCrowdsourced())
form_structures_.push_back(form_structure.release()); form_structures_.push_back(form_structure.release());
else else
non_queryable_forms.push_back(form_structure.release()); non_queryable_forms.push_back(form_structure.release());
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
#include "chrome/browser/autofill/form_structure.h" #include "chrome/browser/autofill/form_structure.h"
#include <utility>
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/sha1.h" #include "base/sha1.h"
...@@ -85,6 +87,177 @@ std::string EncodeFieldTypes(const FieldTypeSet& available_field_types) { ...@@ -85,6 +87,177 @@ std::string EncodeFieldTypes(const FieldTypeSet& available_field_types) {
return data_presence; 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 } // namespace
FormStructure::FormStructure(const FormData& form) FormStructure::FormStructure(const FormData& form)
...@@ -93,7 +266,8 @@ FormStructure::FormStructure(const FormData& form) ...@@ -93,7 +266,8 @@ FormStructure::FormStructure(const FormData& form)
target_url_(form.action), target_url_(form.action),
autofill_count_(0), autofill_count_(0),
upload_required_(USE_UPLOAD_RATES), 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. // Copy the form fields.
std::vector<webkit_glue::FormField>::const_iterator field; std::vector<webkit_glue::FormField>::const_iterator field;
for (field = form.fields.begin(); for (field = form.fields.begin();
...@@ -125,7 +299,14 @@ void FormStructure::DetermineHeuristicTypes() { ...@@ -125,7 +299,14 @@ void FormStructure::DetermineHeuristicTypes() {
autofill_count_ = 0; autofill_count_ = 0;
FieldTypeMap field_type_map; 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++) { for (size_t index = 0; index < field_count(); index++) {
AutofillField* field = fields_[index]; AutofillField* field = fields_[index];
...@@ -140,8 +321,6 @@ void FormStructure::DetermineHeuristicTypes() { ...@@ -140,8 +321,6 @@ void FormStructure::DetermineHeuristicTypes() {
} }
field->set_heuristic_type(heuristic_autofill_type); field->set_heuristic_type(heuristic_autofill_type);
AutofillType autofill_type(field->type());
} }
} }
...@@ -149,8 +328,8 @@ bool FormStructure::EncodeUploadRequest( ...@@ -149,8 +328,8 @@ bool FormStructure::EncodeUploadRequest(
const FieldTypeSet& available_field_types, const FieldTypeSet& available_field_types,
bool form_was_autofilled, bool form_was_autofilled,
std::string* encoded_xml) const { std::string* encoded_xml) const {
if (!ShouldBeParsed(true)) { if (!ShouldBeCrowdsourced()) {
NOTREACHED(); // Caller should've checked for search pages. NOTREACHED();
return false; return false;
} }
...@@ -403,6 +582,10 @@ bool FormStructure::ShouldBeParsed(bool require_method_post) const { ...@@ -403,6 +582,10 @@ bool FormStructure::ShouldBeParsed(bool require_method_post) const {
return !require_method_post || (method_ == POST); return !require_method_post || (method_ == POST);
} }
bool FormStructure::ShouldBeCrowdsourced() const {
return !has_author_specified_types_ && ShouldBeParsed(true);
}
void FormStructure::UpdateFromCache(const FormStructure& cached_form) { void FormStructure::UpdateFromCache(const FormStructure& cached_form) {
// Map from field signatures to cached fields. // Map from field signatures to cached fields.
std::map<std::string, const AutofillField*> cached_fields; std::map<std::string, const AutofillField*> cached_fields;
......
...@@ -96,6 +96,11 @@ class FormStructure { ...@@ -96,6 +96,11 @@ class FormStructure {
// |require_method_post| is true. // |require_method_post| is true.
bool ShouldBeParsed(bool require_method_post) const; 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|. // Sets the field types and experiment id to be those set for |cached_form|.
void UpdateFromCache(const FormStructure& cached_form); void UpdateFromCache(const FormStructure& cached_form);
...@@ -181,6 +186,10 @@ class FormStructure { ...@@ -181,6 +186,10 @@ class FormStructure {
// GET or POST. // GET or POST.
RequestMethod method_; 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); DISALLOW_COPY_AND_ASSIGN(FormStructure);
}; };
......
...@@ -339,6 +339,137 @@ TEST(FormStructureTest, HeuristicsContactInfo) { ...@@ -339,6 +339,137 @@ TEST(FormStructureTest, HeuristicsContactInfo) {
EXPECT_EQ(UNKNOWN_TYPE, form_structure->field(8)->heuristic_type()); 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) { TEST(FormStructureTest, HeuristicsSample8) {
scoped_ptr<FormStructure> form_structure; scoped_ptr<FormStructure> form_structure;
FormData form; FormData form;
......
...@@ -22,6 +22,7 @@ IPC_STRUCT_TRAITS_BEGIN(webkit_glue::FormField) ...@@ -22,6 +22,7 @@ IPC_STRUCT_TRAITS_BEGIN(webkit_glue::FormField)
IPC_STRUCT_TRAITS_MEMBER(name) IPC_STRUCT_TRAITS_MEMBER(name)
IPC_STRUCT_TRAITS_MEMBER(value) IPC_STRUCT_TRAITS_MEMBER(value)
IPC_STRUCT_TRAITS_MEMBER(form_control_type) IPC_STRUCT_TRAITS_MEMBER(form_control_type)
IPC_STRUCT_TRAITS_MEMBER(autocomplete_type)
IPC_STRUCT_TRAITS_MEMBER(max_length) IPC_STRUCT_TRAITS_MEMBER(max_length)
IPC_STRUCT_TRAITS_MEMBER(is_autofilled) IPC_STRUCT_TRAITS_MEMBER(is_autofilled)
IPC_STRUCT_TRAITS_MEMBER(option_values) IPC_STRUCT_TRAITS_MEMBER(option_values)
......
...@@ -598,6 +598,14 @@ void FormManager::WebFormControlElementToFormField( ...@@ -598,6 +598,14 @@ void FormManager::WebFormControlElementToFormField(
// WebFormElementToFormData. // WebFormElementToFormData.
field->name = element.nameForAutofill(); field->name = element.nameForAutofill();
field->form_control_type = element.formControlType(); 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)) if (!IsAutofillableElement(element))
return; return;
......
...@@ -331,6 +331,136 @@ TEST_F(FormManagerTest, WebFormControlElementToFormFieldInvalidType) { ...@@ -331,6 +331,136 @@ TEST_F(FormManagerTest, WebFormControlElementToFormFieldInvalidType) {
EXPECT_FORM_FIELD_EQUALS(expected, result); 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) { TEST_F(FormManagerTest, WebFormElementToFormData) {
LoadHTML("<FORM name=\"TestForm\" action=\"http://cnn.com\" method=\"post\">" LoadHTML("<FORM name=\"TestForm\" action=\"http://cnn.com\" method=\"post\">"
" <INPUT type=\"text\" id=\"firstname\" value=\"John\"/>" " <INPUT type=\"text\" id=\"firstname\" value=\"John\"/>"
......
<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>
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 | |
...@@ -33,6 +33,7 @@ bool FormField::operator==(const FormField& field) const { ...@@ -33,6 +33,7 @@ bool FormField::operator==(const FormField& field) const {
return (label == field.label && return (label == field.label &&
name == field.name && name == field.name &&
form_control_type == field.form_control_type && form_control_type == field.form_control_type &&
autocomplete_type == field.autocomplete_type &&
max_length == field.max_length); max_length == field.max_length);
} }
...@@ -50,6 +51,8 @@ std::ostream& operator<<(std::ostream& os, const FormField& field) { ...@@ -50,6 +51,8 @@ std::ostream& operator<<(std::ostream& os, const FormField& field) {
<< " " << " "
<< UTF16ToUTF8(field.form_control_type) << UTF16ToUTF8(field.form_control_type)
<< " " << " "
<< UTF16ToUTF8(field.autocomplete_type)
<< " "
<< field.max_length << field.max_length
<< " " << " "
<< (field.is_autofilled ? "true" : "false"); << (field.is_autofilled ? "true" : "false");
......
...@@ -28,6 +28,7 @@ struct FormField { ...@@ -28,6 +28,7 @@ struct FormField {
string16 name; string16 name;
string16 value; string16 value;
string16 form_control_type; string16 form_control_type;
string16 autocomplete_type;
int max_length; int max_length;
bool is_autofilled; bool is_autofilled;
...@@ -50,6 +51,7 @@ std::ostream& operator<<(std::ostream& os, const FormField& field); ...@@ -50,6 +51,7 @@ std::ostream& operator<<(std::ostream& os, const FormField& field);
EXPECT_EQ(expected.name, actual.name); \ EXPECT_EQ(expected.name, actual.name); \
EXPECT_EQ(expected.value, actual.value); \ EXPECT_EQ(expected.value, actual.value); \
EXPECT_EQ(expected.form_control_type, actual.form_control_type); \ 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.max_length, actual.max_length); \
EXPECT_EQ(expected.is_autofilled, actual.is_autofilled); \ EXPECT_EQ(expected.is_autofilled, actual.is_autofilled); \
} while (0) } while (0)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment