| Index: chrome/browser/autofill/form_structure.cc
|
| diff --git a/chrome/browser/autofill/form_structure.cc b/chrome/browser/autofill/form_structure.cc
|
| index b9e0a2eb23036dfb385429681362bb3c786c7ada..076e3c415a1eed0c567199075e91b3cd201c95bf 100644
|
| --- a/chrome/browser/autofill/form_structure.cc
|
| +++ b/chrome/browser/autofill/form_structure.cc
|
| @@ -221,43 +221,13 @@ bool ConvertToAutofillFieldType(const AutofillField& field,
|
|
|
| if (autocomplete_type == ASCIIToUTF16("cc-exp")) {
|
| // TODO(isherman): Choose variant based on HTML5 validation regex.
|
| - *autofill_type = CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR;
|
| + *autofill_type = CREDIT_CARD_EXP_DATE_4_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)
|
| @@ -296,31 +266,39 @@ FormStructure::FormStructure(const FormData& form)
|
| FormStructure::~FormStructure() {}
|
|
|
| void FormStructure::DetermineHeuristicTypes() {
|
| - autofill_count_ = 0;
|
| -
|
| - FieldTypeMap 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);
|
| + bool found_sections;
|
| + ParseAutocompletetypeAttributes(&has_author_specified_types_,
|
| + &found_sections);
|
|
|
| - for (size_t index = 0; index < field_count(); index++) {
|
| - AutofillField* field = fields_[index];
|
| - FieldTypeMap::iterator iter = field_type_map.find(field->unique_name());
|
| -
|
| - AutofillFieldType heuristic_autofill_type;
|
| - if (iter == field_type_map.end()) {
|
| - heuristic_autofill_type = UNKNOWN_TYPE;
|
| - } else {
|
| - heuristic_autofill_type = iter->second;
|
| - ++autofill_count_;
|
| + if (!has_author_specified_types_) {
|
| + FieldTypeMap field_type_map;
|
| + FormField::ParseFormFields(fields_.get(), &field_type_map);
|
| + for (size_t index = 0; index < field_count(); index++) {
|
| + AutofillField* field = fields_[index];
|
| + FieldTypeMap::iterator iter = field_type_map.find(field->unique_name());
|
| + if (iter != field_type_map.end())
|
| + field->set_heuristic_type(iter->second);
|
| }
|
| + }
|
| +
|
| + UpdateAutofillCount();
|
|
|
| - field->set_heuristic_type(heuristic_autofill_type);
|
| + if (!found_sections)
|
| + IdentifySections();
|
| +
|
| + // Ensure that credit card and address fields are in separate sections.
|
| + // This simplifies the section-aware logic in autofill_manager.cc.
|
| + for (std::vector<AutofillField*>::iterator field = fields_->begin();
|
| + field != fields_->end(); ++field) {
|
| + AutofillType::FieldTypeGroup field_type_group =
|
| + AutofillType((*field)->type()).group();
|
| + if (field_type_group == AutofillType::CREDIT_CARD)
|
| + (*field)->set_section((*field)->section() + ASCIIToUTF16("-cc"));
|
| + else
|
| + (*field)->set_section((*field)->section() + ASCIIToUTF16("-default"));
|
| }
|
| }
|
|
|
| @@ -852,3 +830,86 @@ bool FormStructure::EncodeFormRequest(
|
| }
|
| return true;
|
| }
|
| +
|
| +void FormStructure::ParseAutocompletetypeAttributes(bool* found_attribute,
|
| + bool* found_sections) {
|
| + *found_attribute = false;
|
| + *found_sections = false;
|
| + for (std::vector<AutofillField*>::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);
|
| +
|
| + // 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()));
|
| + }
|
| +
|
| + // Look for specified types.
|
| + for (std::vector<string16>::const_iterator type = types.begin();
|
| + type != types.end(); ++type) {
|
| + AutofillFieldType autofill_type = UNKNOWN_TYPE;
|
| + if (ConvertToAutofillFieldType(**field, *type, &autofill_type)) {
|
| + (*field)->set_heuristic_type(autofill_type);
|
| + break;
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +void FormStructure::IdentifySections() {
|
| + if (fields_.empty())
|
| + return;
|
| +
|
| + // Name sections after the first field in the section.
|
| + string16 current_section = fields_->front()->unique_name();
|
| +
|
| + // Keep track of the types we've seen in this section.
|
| + std::set<AutofillFieldType> seen_types;
|
| + AutofillFieldType previous_type = UNKNOWN_TYPE;
|
| +
|
| + for (std::vector<AutofillField*>::iterator field = fields_->begin();
|
| + field != fields_->end(); ++field) {
|
| + const AutofillFieldType current_type =
|
| + AutofillType::GetEquivalentFieldType((*field)->type());
|
| +
|
| + bool already_saw_current_type = seen_types.count(current_type) > 0;
|
| +
|
| + // Forms often ask for multiple phone numbers -- e.g. both a daytime and
|
| + // evening phone number. Our phone and fax number detection is also
|
| + // generally a little off. Hence, ignore both field types as a signal here.
|
| + AutofillType::FieldTypeGroup current_type_group =
|
| + AutofillType(current_type).group();
|
| + if (current_type_group == AutofillType::PHONE_HOME ||
|
| + current_type_group == AutofillType::PHONE_FAX)
|
| + already_saw_current_type = false;
|
| +
|
| + // Some forms have adjacent fields of the same type. Two common examples:
|
| + // * Forms with two email fields, where the second is meant to "confirm"
|
| + // the first.
|
| + // * Forms with a <select> menu for states in some countries, and a
|
| + // freeform <input> field for states in other countries. (Usually, only
|
| + // one of these two will be visible for any given choice of country.)
|
| + // Generally, adjacent fields of the same type belong in the same logical
|
| + // section.
|
| + if (current_type == previous_type)
|
| + already_saw_current_type = false;
|
| +
|
| + previous_type = current_type;
|
| +
|
| + if (current_type != UNKNOWN_TYPE && already_saw_current_type) {
|
| + // We reached the end of a section, so start a new section.
|
| + seen_types.clear();
|
| + current_section = (*field)->unique_name();
|
| + }
|
| +
|
| + seen_types.insert(current_type);
|
| + (*field)->set_section(current_section);
|
| + }
|
| +}
|
|
|