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

Unified Diff: chrome/browser/autofill/form_structure.cc

Issue 11198048: [Autofill] Update the autocomplete types implementation to match the current HTML spec. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Update AutofillFieldTest expectations Created 8 years, 2 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/browser/autofill/form_structure.h ('k') | chrome/browser/autofill/form_structure_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/autofill/form_structure.cc
diff --git a/chrome/browser/autofill/form_structure.cc b/chrome/browser/autofill/form_structure.cc
index 07af74814c5b73571643406b3e44d9b7dbed6f24..36eaf6931b895608b0945f489cb38b9e4674d61b 100644
--- a/chrome/browser/autofill/form_structure.cc
+++ b/chrome/browser/autofill/form_structure.cc
@@ -85,147 +85,134 @@ std::string EncodeFieldTypes(const FieldTypeSet& available_field_types) {
return data_presence;
}
-bool UpdateFromAutocompleteType(const string16& autocomplete_type,
- AutofillField* field) {
- if (autocomplete_type == ASCIIToUTF16("given-name")) {
- field->set_heuristic_type(NAME_FIRST);
- return true;
- }
+// Returns |true| iff the |token| is a type hint for a contact field, as
+// specified in the implementation section of http://is.gd/whatwg_autocomplete
+// Note that "fax" and "pager" are intentionally ignored, as Chrome does not
+// support filling either type of information.
+bool IsContactTypeHint(const std::string& token) {
+ return token == "home" || token == "work" || token == "mobile";
+}
- if (autocomplete_type == ASCIIToUTF16("middle-name")) {
- field->set_heuristic_type(NAME_MIDDLE);
- return true;
+// Returns |true| iff the |token| is a type hint appropriate for a field of the
+// given |field_type|, as specified in the implementation section of
+// http://is.gd/whatwg_autocomplete
+bool ContactTypeHintMatchesFieldType(const std::string& token,
+ AutofillFieldType field_type) {
+ // The "home" and "work" type hints are only appropriate for email and phone
+ // number field types.
+ if (token == "home" || token == "work") {
+ return field_type == EMAIL_ADDRESS ||
+ (field_type >= PHONE_HOME_NUMBER &&
+ field_type <= PHONE_HOME_WHOLE_NUMBER);
}
- if (autocomplete_type == ASCIIToUTF16("middle-initial")) {
- field->set_heuristic_type(NAME_MIDDLE_INITIAL);
- return true;
+ // The "mobile" type hint is only appropriate for phone number field types.
+ // Note that "fax" and "pager" are intentionally ignored, as Chrome does not
+ // support filling either type of information.
+ if (token == "mobile") {
+ return field_type >= PHONE_HOME_NUMBER &&
+ field_type <= PHONE_HOME_WHOLE_NUMBER;
}
- if (autocomplete_type == ASCIIToUTF16("surname")) {
- field->set_heuristic_type(NAME_LAST);
- return true;
- }
+ return false;
+}
- if (autocomplete_type == ASCIIToUTF16("full-name")) {
- field->set_heuristic_type(NAME_FULL);
- return true;
+// Returns the Chrome Autofill-supported field type corresponding to the given
+// |autocomplete_type|, if there is one, in the context of the given |field|.
+// Chrome Autofill supports a subset of the field types listed at
+// http://is.gd/whatwg_autocomplete
+AutofillFieldType FieldTypeFromAutocompleteType(
+ const std::string& autocomplete_type,
+ const AutofillField& field) {
+ if (autocomplete_type == "name")
+ return NAME_FULL;
+
+ if (autocomplete_type == "given-name")
+ return NAME_FIRST;
+
+ if (autocomplete_type == "additional-name") {
+ if (field.max_length == 1)
+ return NAME_MIDDLE_INITIAL;
+ else
+ return NAME_MIDDLE;
}
- if (autocomplete_type == ASCIIToUTF16("street-address") ||
- autocomplete_type == ASCIIToUTF16("address-line1")) {
- field->set_heuristic_type(ADDRESS_HOME_LINE1);
- return true;
- }
+ if (autocomplete_type == "family-name")
+ return NAME_LAST;
- if (autocomplete_type == ASCIIToUTF16("address-line2")) {
- field->set_heuristic_type(ADDRESS_HOME_LINE2);
- return true;
- }
+ if (autocomplete_type == "honorific-suffix")
+ return NAME_SUFFIX;
- if (autocomplete_type == ASCIIToUTF16("locality") ||
- autocomplete_type == ASCIIToUTF16("city")) {
- field->set_heuristic_type(ADDRESS_HOME_CITY);
- return true;
- }
+ if (autocomplete_type == "organization")
+ return COMPANY_NAME;
- if (autocomplete_type == ASCIIToUTF16("administrative-area") ||
- autocomplete_type == ASCIIToUTF16("state") ||
- autocomplete_type == ASCIIToUTF16("province") ||
- autocomplete_type == ASCIIToUTF16("region")) {
- field->set_heuristic_type(ADDRESS_HOME_STATE);
- return true;
- }
+ if (autocomplete_type == "street-address" ||
+ autocomplete_type == "address-line1")
+ return ADDRESS_HOME_LINE1;
- if (autocomplete_type == ASCIIToUTF16("postal-code")) {
- field->set_heuristic_type(ADDRESS_HOME_ZIP);
- return true;
- }
+ if (autocomplete_type == "address-line2")
+ return ADDRESS_HOME_LINE2;
- if (autocomplete_type == ASCIIToUTF16("country")) {
- field->set_heuristic_type(ADDRESS_HOME_COUNTRY);
- return true;
- }
+ if (autocomplete_type == "locality")
+ return ADDRESS_HOME_CITY;
- if (autocomplete_type == ASCIIToUTF16("organization")) {
- field->set_heuristic_type(COMPANY_NAME);
- return true;
- }
+ if (autocomplete_type == "region")
+ return ADDRESS_HOME_STATE;
- if (autocomplete_type == ASCIIToUTF16("email")) {
- field->set_heuristic_type(EMAIL_ADDRESS);
- return true;
- }
+ if (autocomplete_type == "country")
+ return ADDRESS_HOME_COUNTRY;
- if (autocomplete_type == ASCIIToUTF16("phone-full")) {
- field->set_heuristic_type(PHONE_HOME_WHOLE_NUMBER);
- return true;
- }
+ if (autocomplete_type == "postal-code")
+ return ADDRESS_HOME_ZIP;
- if (autocomplete_type == ASCIIToUTF16("phone-country-code")) {
- field->set_heuristic_type(PHONE_HOME_COUNTRY_CODE);
- return true;
- }
+ if (autocomplete_type == "cc-name")
+ return CREDIT_CARD_NAME;
- if (autocomplete_type == ASCIIToUTF16("phone-national")) {
- field->set_heuristic_type(PHONE_HOME_CITY_AND_NUMBER);
- return true;
- }
+ if (autocomplete_type == "cc-number")
+ return CREDIT_CARD_NUMBER;
- if (autocomplete_type == ASCIIToUTF16("phone-area-code")) {
- field->set_heuristic_type(PHONE_HOME_CITY_CODE);
- return true;
+ if (autocomplete_type == "cc-exp") {
+ if (field.max_length == 5)
+ return CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR;
+ else
+ return CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR;
}
- if (autocomplete_type == ASCIIToUTF16("phone-local")) {
- field->set_heuristic_type(PHONE_HOME_NUMBER);
- return true;
- }
+ if (autocomplete_type == "cc-exp-month")
+ return CREDIT_CARD_EXP_MONTH;
- if (autocomplete_type == ASCIIToUTF16("phone-local-prefix")) {
- field->set_heuristic_type(PHONE_HOME_NUMBER);
- field->set_phone_part(AutofillField::PHONE_PREFIX);
- return true;
+ if (autocomplete_type == "cc-exp-year") {
+ if (field.max_length == 2)
+ return CREDIT_CARD_EXP_2_DIGIT_YEAR;
+ else
+ return CREDIT_CARD_EXP_4_DIGIT_YEAR;
}
- if (autocomplete_type == ASCIIToUTF16("phone-local-suffix")) {
- field->set_heuristic_type(PHONE_HOME_NUMBER);
- field->set_phone_part(AutofillField::PHONE_SUFFIX);
- return true;
- }
+ if (autocomplete_type == "tel")
+ return PHONE_HOME_WHOLE_NUMBER;
- if (autocomplete_type == ASCIIToUTF16("cc-full-name")) {
- field->set_heuristic_type(CREDIT_CARD_NAME);
- return true;
- }
+ if (autocomplete_type == "tel-country-code")
+ return PHONE_HOME_COUNTRY_CODE;
- if (autocomplete_type == ASCIIToUTF16("cc-number")) {
- field->set_heuristic_type(CREDIT_CARD_NUMBER);
- return true;
- }
+ if (autocomplete_type == "tel-national")
+ return PHONE_HOME_CITY_AND_NUMBER;
- if (autocomplete_type == ASCIIToUTF16("cc-exp-month")) {
- field->set_heuristic_type(CREDIT_CARD_EXP_MONTH);
- return true;
- }
+ if (autocomplete_type == "tel-area-code")
+ return PHONE_HOME_CITY_CODE;
- if (autocomplete_type == ASCIIToUTF16("cc-exp-year")) {
- if (field->max_length == 2)
- field->set_heuristic_type(CREDIT_CARD_EXP_2_DIGIT_YEAR);
- else
- field->set_heuristic_type(CREDIT_CARD_EXP_4_DIGIT_YEAR);
- return true;
- }
+ if (autocomplete_type == "tel-local")
+ return PHONE_HOME_NUMBER;
- if (autocomplete_type == ASCIIToUTF16("cc-exp")) {
- if (field->max_length == 5)
- field->set_heuristic_type(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR);
- else
- field->set_heuristic_type(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR);
- return true;
- }
+ if (autocomplete_type == "tel-local-prefix")
+ return PHONE_HOME_NUMBER;
- return false;
+ if (autocomplete_type == "tel-local-suffix")
+ return PHONE_HOME_NUMBER;
+
+ if (autocomplete_type == "email")
+ return EMAIL_ADDRESS;
+
+ return UNKNOWN_TYPE;
}
} // namespace
@@ -273,12 +260,13 @@ FormStructure::FormStructure(const FormData& form)
FormStructure::~FormStructure() {}
void FormStructure::DetermineHeuristicTypes() {
- // 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.
+ // First, try to detect field types based on each field's |autocomplete|
+ // attribute value. If there is at least one form field that specifies an
+ // autocomplete type hint, don't try to apply other heuristics to match fields
+ // in this form.
bool has_author_specified_sections;
- ParseAutocompletetypeAttributes(&has_author_specified_types_,
- &has_author_specified_sections);
+ ParseFieldTypesFromAutocompleteAttributes(&has_author_specified_types_,
+ &has_author_specified_sections);
if (!has_author_specified_types_) {
FieldTypeMap field_type_map;
@@ -549,7 +537,7 @@ bool FormStructure::ShouldBeParsed(bool require_method_post) const {
bool has_text_field = false;
for (std::vector<AutofillField*>::const_iterator it = begin();
it != end() && !has_text_field; ++it) {
- has_text_field |= (*it)->form_control_type != ASCIIToUTF16("select-one");
+ has_text_field |= (*it)->form_control_type != "select-one";
}
if (!has_text_field)
return false;
@@ -576,7 +564,7 @@ void FormStructure::UpdateFromCache(const FormStructure& cached_form) {
std::map<std::string, const AutofillField*>::const_iterator
cached_field = cached_fields.find(field->FieldSignature());
if (cached_field != cached_fields.end()) {
- if (field->form_control_type != ASCIIToUTF16("select-one") &&
+ if (field->form_control_type != "select-one" &&
field->value == cached_field->second->value) {
// From the perspective of learning user data, text fields containing
// default values are equivalent to empty fields.
@@ -694,7 +682,7 @@ void FormStructure::LogQualityMetrics(
// TODO(isherman): <select> fields don't support |is_autofilled()|, so we
// have to skip them for the remaining metrics.
- if (field->form_control_type == ASCIIToUTF16("select-one"))
+ if (field->form_control_type == "select-one")
continue;
if (field->is_autofilled) {
@@ -878,32 +866,111 @@ bool FormStructure::EncodeFormRequest(
return true;
}
-void FormStructure::ParseAutocompletetypeAttributes(bool* found_attribute,
- bool* found_sections) {
- *found_attribute = false;
+void FormStructure::ParseFieldTypesFromAutocompleteAttributes(
+ bool* found_types,
+ bool* found_sections) {
+ const std::string kDefaultSection = "-default";
+
+ *found_types = false;
*found_sections = false;
- for (std::vector<AutofillField*>::iterator field = fields_.begin();
- field != fields_.end(); ++field) {
- if ((*field)->autocomplete_type.empty())
+ for (std::vector<AutofillField*>::iterator it = fields_.begin();
+ it != fields_.end(); ++it) {
+ AutofillField* field = *it;
+
+ // To prevent potential section name collisions, add a default suffix for
+ // other fields. Without this, 'autocomplete' attribute values
+ // "section--shipping street-address" and "shipping street-address" would be
+ // parsed identically, given the section handling code below. We do this
+ // before any validation so that fields with invalid attributes still end up
+ // in the default section. These default section names will be overridden
+ // by subsequent heuristic parsing steps if there are no author-specified
+ // section names.
+ field->set_section(kDefaultSection);
+
+ // Canonicalize the attribute value by trimming whitespace, collapsing
+ // non-space characters (e.g. tab) to spaces, and converting to lowercase.
+ std::string autocomplete_attribute =
+ CollapseWhitespaceASCII(field->autocomplete_attribute, false);
+ autocomplete_attribute = StringToLowerASCII(autocomplete_attribute);
+
+ // The autocomplete attribute is overloaded: it can specify either a field
+ // type hint or whether autocomplete should be enabled at all. Ignore the
+ // latter type of attribute value.
+ if (autocomplete_attribute.empty() ||
+ autocomplete_attribute == "on" ||
+ autocomplete_attribute == "off") {
+ continue;
+ }
+
+ // Any other value, even it is invalid, is considered to be a type hint.
+ // This allows a website's author to specify an attribute like
+ // autocomplete="other" on a field to disable all Autofill heuristics for
+ // the form.
+ *found_types = true;
+
+ // Tokenize the attribute value. Per the spec, the tokens are parsed in
+ // reverse order.
+ std::vector<std::string> tokens;
+ Tokenize(autocomplete_attribute, " ", &tokens);
+
+ // The final token must be the field type.
+ // If it is not one of the known types, abort.
+ DCHECK(!tokens.empty());
+ std::string field_type_token = tokens.back();
+ tokens.pop_back();
+ AutofillFieldType field_type =
+ FieldTypeFromAutocompleteType(field_type_token, *field);
+ if (field_type == UNKNOWN_TYPE)
continue;
- *found_attribute = true;
- std::vector<string16> types;
- Tokenize((*field)->autocomplete_type, ASCIIToUTF16(" "), &types);
+ // The preceding token, if any, may be a type hint.
+ if (!tokens.empty() && IsContactTypeHint(tokens.back())) {
+ // If it is, it must match the field type; otherwise, abort.
+ // Note that an invalid token invalidates the entire attribute value, even
+ // if the other tokens are valid.
+ if (!ContactTypeHintMatchesFieldType(tokens.back(), field_type))
+ continue;
- // Look for a named section.
- const string16 kSectionPrefix = ASCIIToUTF16("section-");
- if (!types.empty() && StartsWith(types.front(), kSectionPrefix, true)) {
- *found_sections = true;
- (*field)->set_section(types.front().substr(kSectionPrefix.size()));
+ // Chrome Autofill ignores these type hints.
+ tokens.pop_back();
}
- // Look for specified types.
- for (std::vector<string16>::const_iterator type = types.begin();
- type != types.end(); ++type) {
- if (UpdateFromAutocompleteType(*type, *field))
- break;
+ // The preceding token, if any, may be a fixed string that is either
+ // "shipping" or "billing". Chrome Autofill treats these as implicit
+ // section name suffixes.
+ DCHECK_EQ(kDefaultSection, field->section());
+ std::string section = field->section();
+ if (!tokens.empty() &&
+ (tokens.back() == "shipping" || tokens.back() == "billing")) {
+ section = "-" + tokens.back();
+ tokens.pop_back();
+ }
+
+ // The preceding token, if any, may be a named section.
+ const std::string kSectionPrefix = "section-";
+ if (!tokens.empty() &&
+ StartsWithASCII(tokens.back(), kSectionPrefix, true)) {
+ // Prepend this section name to the suffix set in the preceding block.
+ section = tokens.back().substr(kSectionPrefix.size()) + section;
+ tokens.pop_back();
}
+
+ // No other tokens are allowed. If there are any remaining, abort.
+ if (!tokens.empty())
+ continue;
+
+ if (section != kDefaultSection) {
+ *found_sections = true;
+ field->set_section(section);
+ }
+
+ // No errors encountered while parsing!
+ // Update the |field|'s type based on what was parsed from the attribute.
+ field->set_heuristic_type(field_type);
+ if (field_type_token == "tel-local-prefix")
+ field->set_phone_part(AutofillField::PHONE_PREFIX);
+ else if (field_type_token == "tel-local-suffix")
+ field->set_phone_part(AutofillField::PHONE_SUFFIX);
}
}
@@ -953,7 +1020,7 @@ void FormStructure::IdentifySections(bool has_author_specified_sections) {
}
seen_types.insert(current_type);
- (*field)->set_section(current_section);
+ (*field)->set_section(UTF16ToUTF8(current_section));
}
}
@@ -964,8 +1031,8 @@ void FormStructure::IdentifySections(bool has_author_specified_sections) {
AutofillType::FieldTypeGroup field_type_group =
AutofillType((*field)->type()).group();
if (field_type_group == AutofillType::CREDIT_CARD)
- (*field)->set_section((*field)->section() + ASCIIToUTF16("-cc"));
+ (*field)->set_section((*field)->section() + "-cc");
else
- (*field)->set_section((*field)->section() + ASCIIToUTF16("-default"));
+ (*field)->set_section((*field)->section() + "-default");
}
}
« no previous file with comments | « chrome/browser/autofill/form_structure.h ('k') | chrome/browser/autofill/form_structure_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698