Chromium Code Reviews| 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/browser/autofill/form_structure.h" | 5 #include "chrome/browser/autofill/form_structure.h" |
| 6 | 6 |
| 7 #include <utility> | 7 #include <utility> |
| 8 | 8 |
| 9 #include "base/basictypes.h" | 9 #include "base/basictypes.h" |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 78 // Print all meaningfull bytes into a string. | 78 // Print all meaningfull bytes into a string. |
| 79 std::string data_presence; | 79 std::string data_presence; |
| 80 data_presence.reserve(data_end * 2 + 1); | 80 data_presence.reserve(data_end * 2 + 1); |
| 81 for (size_t i = 0; i < data_end; ++i) { | 81 for (size_t i = 0; i < data_end; ++i) { |
| 82 base::StringAppendF(&data_presence, "%02x", bit_field[i]); | 82 base::StringAppendF(&data_presence, "%02x", bit_field[i]); |
| 83 } | 83 } |
| 84 | 84 |
| 85 return data_presence; | 85 return data_presence; |
| 86 } | 86 } |
| 87 | 87 |
| 88 bool UpdateFromAutocompleteType(const string16& autocomplete_type, | 88 // Returns |true| iff the |token| is a type hint for a contact field, as |
| 89 AutofillField* field) { | 89 // specified in the implementation section of http://is.gd/whatwg_autocomplete |
| 90 if (autocomplete_type == ASCIIToUTF16("given-name")) { | 90 // Note that "fax" and "pager" are intentionally ignored, as Chrome does not |
| 91 field->set_heuristic_type(NAME_FIRST); | 91 // support filling either type of information. |
| 92 return true; | 92 bool IsContactTypeHint(const string16& token) { |
| 93 return | |
| 94 token == ASCIIToUTF16("home") || | |
|
Evan Stade
2012/10/19 20:36:47
use LowerCaseEqualsASCII (here and many other plac
Evan Stade
2012/10/19 20:38:13
(meant to delete this comment)
| |
| 95 token == ASCIIToUTF16("work") || | |
| 96 token == ASCIIToUTF16("mobile") || | |
| 97 token == ASCIIToUTF16("pager"); | |
| 98 } | |
| 99 | |
| 100 // Returns |true| iff the |token| is a type hint appropriate for a field of the | |
| 101 // given |field_type|, as specified in the implementation section of | |
| 102 // http://is.gd/whatwg_autocomplete | |
| 103 bool ContactTypeHintMatchesFieldType(const string16& token, | |
| 104 AutofillFieldType field_type) { | |
| 105 // The "home" and "work" type hints are only appropriate for email and phone | |
| 106 // number field types. | |
| 107 if (token == ASCIIToUTF16("home") || token == ASCIIToUTF16("work")) { | |
| 108 return field_type == EMAIL_ADDRESS || | |
| 109 (field_type >= PHONE_HOME_NUMBER && | |
|
Evan Stade
2012/10/19 20:36:47
why is it called PHONE_HOME if it applies to work
Ilya Sherman
2012/10/20 05:16:08
Historical reasons. We used to have more phone nu
| |
| 110 field_type <= PHONE_HOME_WHOLE_NUMBER); | |
| 93 } | 111 } |
| 94 | 112 |
| 95 if (autocomplete_type == ASCIIToUTF16("middle-name")) { | 113 // The "mobile" type hint is only appropriate for phone number field types. |
| 96 field->set_heuristic_type(NAME_MIDDLE); | 114 // Note that "fax" and "pager" are intentionally ignored, as Chrome does not |
| 97 return true; | 115 // support filling either type of information. |
| 98 } | 116 if (token == ASCIIToUTF16("mobile")) { |
| 99 | 117 return field_type >= PHONE_HOME_NUMBER && |
| 100 if (autocomplete_type == ASCIIToUTF16("middle-initial")) { | 118 field_type <= PHONE_HOME_WHOLE_NUMBER; |
| 101 field->set_heuristic_type(NAME_MIDDLE_INITIAL); | |
| 102 return true; | |
| 103 } | |
| 104 | |
| 105 if (autocomplete_type == ASCIIToUTF16("surname")) { | |
| 106 field->set_heuristic_type(NAME_LAST); | |
| 107 return true; | |
| 108 } | |
| 109 | |
| 110 if (autocomplete_type == ASCIIToUTF16("full-name")) { | |
| 111 field->set_heuristic_type(NAME_FULL); | |
| 112 return true; | |
| 113 } | |
| 114 | |
| 115 if (autocomplete_type == ASCIIToUTF16("street-address") || | |
| 116 autocomplete_type == ASCIIToUTF16("address-line1")) { | |
| 117 field->set_heuristic_type(ADDRESS_HOME_LINE1); | |
| 118 return true; | |
| 119 } | |
| 120 | |
| 121 if (autocomplete_type == ASCIIToUTF16("address-line2")) { | |
| 122 field->set_heuristic_type(ADDRESS_HOME_LINE2); | |
| 123 return true; | |
| 124 } | |
| 125 | |
| 126 if (autocomplete_type == ASCIIToUTF16("locality") || | |
| 127 autocomplete_type == ASCIIToUTF16("city")) { | |
| 128 field->set_heuristic_type(ADDRESS_HOME_CITY); | |
| 129 return true; | |
| 130 } | |
| 131 | |
| 132 if (autocomplete_type == ASCIIToUTF16("administrative-area") || | |
| 133 autocomplete_type == ASCIIToUTF16("state") || | |
| 134 autocomplete_type == ASCIIToUTF16("province") || | |
| 135 autocomplete_type == ASCIIToUTF16("region")) { | |
| 136 field->set_heuristic_type(ADDRESS_HOME_STATE); | |
| 137 return true; | |
| 138 } | |
| 139 | |
| 140 if (autocomplete_type == ASCIIToUTF16("postal-code")) { | |
| 141 field->set_heuristic_type(ADDRESS_HOME_ZIP); | |
| 142 return true; | |
| 143 } | |
| 144 | |
| 145 if (autocomplete_type == ASCIIToUTF16("country")) { | |
| 146 field->set_heuristic_type(ADDRESS_HOME_COUNTRY); | |
| 147 return true; | |
| 148 } | |
| 149 | |
| 150 if (autocomplete_type == ASCIIToUTF16("organization")) { | |
| 151 field->set_heuristic_type(COMPANY_NAME); | |
| 152 return true; | |
| 153 } | |
| 154 | |
| 155 if (autocomplete_type == ASCIIToUTF16("email")) { | |
| 156 field->set_heuristic_type(EMAIL_ADDRESS); | |
| 157 return true; | |
| 158 } | |
| 159 | |
| 160 if (autocomplete_type == ASCIIToUTF16("phone-full")) { | |
| 161 field->set_heuristic_type(PHONE_HOME_WHOLE_NUMBER); | |
| 162 return true; | |
| 163 } | |
| 164 | |
| 165 if (autocomplete_type == ASCIIToUTF16("phone-country-code")) { | |
| 166 field->set_heuristic_type(PHONE_HOME_COUNTRY_CODE); | |
| 167 return true; | |
| 168 } | |
| 169 | |
| 170 if (autocomplete_type == ASCIIToUTF16("phone-national")) { | |
| 171 field->set_heuristic_type(PHONE_HOME_CITY_AND_NUMBER); | |
| 172 return true; | |
| 173 } | |
| 174 | |
| 175 if (autocomplete_type == ASCIIToUTF16("phone-area-code")) { | |
| 176 field->set_heuristic_type(PHONE_HOME_CITY_CODE); | |
| 177 return true; | |
| 178 } | |
| 179 | |
| 180 if (autocomplete_type == ASCIIToUTF16("phone-local")) { | |
| 181 field->set_heuristic_type(PHONE_HOME_NUMBER); | |
| 182 return true; | |
| 183 } | |
| 184 | |
| 185 if (autocomplete_type == ASCIIToUTF16("phone-local-prefix")) { | |
| 186 field->set_heuristic_type(PHONE_HOME_NUMBER); | |
| 187 field->set_phone_part(AutofillField::PHONE_PREFIX); | |
| 188 return true; | |
| 189 } | |
| 190 | |
| 191 if (autocomplete_type == ASCIIToUTF16("phone-local-suffix")) { | |
| 192 field->set_heuristic_type(PHONE_HOME_NUMBER); | |
| 193 field->set_phone_part(AutofillField::PHONE_SUFFIX); | |
| 194 return true; | |
| 195 } | |
| 196 | |
| 197 if (autocomplete_type == ASCIIToUTF16("cc-full-name")) { | |
| 198 field->set_heuristic_type(CREDIT_CARD_NAME); | |
| 199 return true; | |
| 200 } | |
| 201 | |
| 202 if (autocomplete_type == ASCIIToUTF16("cc-number")) { | |
| 203 field->set_heuristic_type(CREDIT_CARD_NUMBER); | |
| 204 return true; | |
| 205 } | |
| 206 | |
| 207 if (autocomplete_type == ASCIIToUTF16("cc-exp-month")) { | |
| 208 field->set_heuristic_type(CREDIT_CARD_EXP_MONTH); | |
| 209 return true; | |
| 210 } | |
| 211 | |
| 212 if (autocomplete_type == ASCIIToUTF16("cc-exp-year")) { | |
| 213 if (field->max_length == 2) | |
| 214 field->set_heuristic_type(CREDIT_CARD_EXP_2_DIGIT_YEAR); | |
| 215 else | |
| 216 field->set_heuristic_type(CREDIT_CARD_EXP_4_DIGIT_YEAR); | |
| 217 return true; | |
| 218 } | |
| 219 | |
| 220 if (autocomplete_type == ASCIIToUTF16("cc-exp")) { | |
| 221 if (field->max_length == 5) | |
| 222 field->set_heuristic_type(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR); | |
| 223 else | |
| 224 field->set_heuristic_type(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR); | |
| 225 return true; | |
| 226 } | 119 } |
| 227 | 120 |
| 228 return false; | 121 return false; |
| 229 } | 122 } |
| 230 | 123 |
| 124 // Returns the Chrome Autofill-supported field type corresponding to the given | |
| 125 // |autocomplete_type|, if there is one, in the context of the given |field|. | |
| 126 // Chrome Autofill supports a subset of the field types listed at | |
| 127 // http://is.gd/whatwg_autocomplete | |
| 128 AutofillFieldType FieldTypeFromAutocompleteType( | |
| 129 const string16& autocomplete_type, | |
| 130 const AutofillField& field) { | |
| 131 if (autocomplete_type == ASCIIToUTF16("name")) | |
| 132 return NAME_FULL; | |
| 133 | |
| 134 if (autocomplete_type == ASCIIToUTF16("given-name")) | |
| 135 return NAME_FIRST; | |
| 136 | |
| 137 if (autocomplete_type == ASCIIToUTF16("additional-name")) { | |
| 138 if (field.max_length == 1) | |
| 139 return NAME_MIDDLE_INITIAL; | |
| 140 else | |
| 141 return NAME_MIDDLE; | |
| 142 } | |
| 143 | |
| 144 if (autocomplete_type == ASCIIToUTF16("family-name")) | |
| 145 return NAME_LAST; | |
| 146 | |
| 147 if (autocomplete_type == ASCIIToUTF16("honorific-suffix")) | |
| 148 return NAME_SUFFIX; | |
| 149 | |
| 150 if (autocomplete_type == ASCIIToUTF16("organization")) | |
| 151 return COMPANY_NAME; | |
| 152 | |
| 153 if (autocomplete_type == ASCIIToUTF16("street-address") || | |
| 154 autocomplete_type == ASCIIToUTF16("address-line1")) | |
| 155 return ADDRESS_HOME_LINE1; | |
| 156 | |
| 157 if (autocomplete_type == ASCIIToUTF16("address-line2")) | |
| 158 return ADDRESS_HOME_LINE2; | |
| 159 | |
| 160 if (autocomplete_type == ASCIIToUTF16("locality")) | |
| 161 return ADDRESS_HOME_CITY; | |
| 162 | |
| 163 if (autocomplete_type == ASCIIToUTF16("region")) | |
| 164 return ADDRESS_HOME_STATE; | |
| 165 | |
| 166 if (autocomplete_type == ASCIIToUTF16("country")) | |
| 167 return ADDRESS_HOME_COUNTRY; | |
| 168 | |
| 169 if (autocomplete_type == ASCIIToUTF16("postal-code")) | |
| 170 return ADDRESS_HOME_ZIP; | |
| 171 | |
| 172 if (autocomplete_type == ASCIIToUTF16("cc-name")) | |
| 173 return CREDIT_CARD_NAME; | |
| 174 | |
| 175 if (autocomplete_type == ASCIIToUTF16("cc-number")) | |
| 176 return CREDIT_CARD_NUMBER; | |
| 177 | |
| 178 if (autocomplete_type == ASCIIToUTF16("cc-exp")) { | |
| 179 if (field.max_length == 5) | |
| 180 return CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR; | |
| 181 else | |
| 182 return CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR; | |
| 183 } | |
| 184 | |
| 185 if (autocomplete_type == ASCIIToUTF16("cc-exp-month")) | |
| 186 return CREDIT_CARD_EXP_MONTH; | |
| 187 | |
| 188 if (autocomplete_type == ASCIIToUTF16("cc-exp-year")) { | |
| 189 if (field.max_length == 2) | |
| 190 return CREDIT_CARD_EXP_2_DIGIT_YEAR; | |
| 191 else | |
| 192 return CREDIT_CARD_EXP_4_DIGIT_YEAR; | |
| 193 } | |
| 194 | |
| 195 if (autocomplete_type == ASCIIToUTF16("tel")) | |
| 196 return PHONE_HOME_WHOLE_NUMBER; | |
| 197 | |
| 198 if (autocomplete_type == ASCIIToUTF16("tel-country-code")) | |
| 199 return PHONE_HOME_COUNTRY_CODE; | |
| 200 | |
| 201 if (autocomplete_type == ASCIIToUTF16("tel-national")) | |
| 202 return PHONE_HOME_CITY_AND_NUMBER; | |
| 203 | |
| 204 if (autocomplete_type == ASCIIToUTF16("tel-area-code")) | |
| 205 return PHONE_HOME_CITY_CODE; | |
| 206 | |
| 207 if (autocomplete_type == ASCIIToUTF16("tel-local")) | |
| 208 return PHONE_HOME_NUMBER; | |
| 209 | |
| 210 if (autocomplete_type == ASCIIToUTF16("tel-local-prefix")) | |
| 211 return PHONE_HOME_NUMBER; | |
| 212 | |
| 213 if (autocomplete_type == ASCIIToUTF16("tel-local-suffix")) | |
| 214 return PHONE_HOME_NUMBER; | |
| 215 | |
| 216 if (autocomplete_type == ASCIIToUTF16("email")) | |
| 217 return EMAIL_ADDRESS; | |
| 218 | |
| 219 return UNKNOWN_TYPE; | |
| 220 } | |
| 221 | |
| 231 } // namespace | 222 } // namespace |
| 232 | 223 |
| 233 FormStructure::FormStructure(const FormData& form) | 224 FormStructure::FormStructure(const FormData& form) |
| 234 : form_name_(form.name), | 225 : form_name_(form.name), |
| 235 source_url_(form.origin), | 226 source_url_(form.origin), |
| 236 target_url_(form.action), | 227 target_url_(form.action), |
| 237 autofill_count_(0), | 228 autofill_count_(0), |
| 238 upload_required_(USE_UPLOAD_RATES), | 229 upload_required_(USE_UPLOAD_RATES), |
| 239 server_experiment_id_("no server response"), | 230 server_experiment_id_("no server response"), |
| 240 has_author_specified_types_(false) { | 231 has_author_specified_types_(false) { |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 266 } else { | 257 } else { |
| 267 // Either the method is 'get', or we don't know. In this case we default | 258 // Either the method is 'get', or we don't know. In this case we default |
| 268 // to GET. | 259 // to GET. |
| 269 method_ = GET; | 260 method_ = GET; |
| 270 } | 261 } |
| 271 } | 262 } |
| 272 | 263 |
| 273 FormStructure::~FormStructure() {} | 264 FormStructure::~FormStructure() {} |
| 274 | 265 |
| 275 void FormStructure::DetermineHeuristicTypes() { | 266 void FormStructure::DetermineHeuristicTypes() { |
| 276 // First, try to detect field types based on the fields' |autocompletetype| | 267 // First, try to detect field types based on each field's |autocomplete| |
| 277 // attributes. If there is at least one form field with this attribute, don't | 268 // attribute value. If there is at least one form field that specifies an |
| 278 // try to apply other heuristics to match fields in this form. | 269 // autocomplete type hint, don't try to apply other heuristics to match fields |
| 270 // in this form. | |
| 279 bool has_author_specified_sections; | 271 bool has_author_specified_sections; |
| 280 ParseAutocompletetypeAttributes(&has_author_specified_types_, | 272 ParseFieldTypesFromAutocompleteAttributes(&has_author_specified_types_, |
| 281 &has_author_specified_sections); | 273 &has_author_specified_sections); |
| 282 | 274 |
| 283 if (!has_author_specified_types_) { | 275 if (!has_author_specified_types_) { |
| 284 FieldTypeMap field_type_map; | 276 FieldTypeMap field_type_map; |
| 285 FormField::ParseFormFields(fields_.get(), &field_type_map); | 277 FormField::ParseFormFields(fields_.get(), &field_type_map); |
| 286 for (size_t index = 0; index < field_count(); index++) { | 278 for (size_t index = 0; index < field_count(); index++) { |
| 287 AutofillField* field = fields_[index]; | 279 AutofillField* field = fields_[index]; |
| 288 FieldTypeMap::iterator iter = field_type_map.find(field->unique_name()); | 280 FieldTypeMap::iterator iter = field_type_map.find(field->unique_name()); |
| 289 if (iter != field_type_map.end()) | 281 if (iter != field_type_map.end()) |
| 290 field->set_heuristic_type(iter->second); | 282 field->set_heuristic_type(iter->second); |
| 291 } | 283 } |
| (...skipping 579 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 871 buzz::XmlElement *field_element = new buzz::XmlElement( | 863 buzz::XmlElement *field_element = new buzz::XmlElement( |
| 872 buzz::QName(kXMLElementField)); | 864 buzz::QName(kXMLElementField)); |
| 873 field_element->SetAttr(buzz::QName(kAttributeSignature), | 865 field_element->SetAttr(buzz::QName(kAttributeSignature), |
| 874 field->FieldSignature()); | 866 field->FieldSignature()); |
| 875 encompassing_xml_element->AddElement(field_element); | 867 encompassing_xml_element->AddElement(field_element); |
| 876 } | 868 } |
| 877 } | 869 } |
| 878 return true; | 870 return true; |
| 879 } | 871 } |
| 880 | 872 |
| 881 void FormStructure::ParseAutocompletetypeAttributes(bool* found_attribute, | 873 void FormStructure::ParseFieldTypesFromAutocompleteAttributes( |
| 882 bool* found_sections) { | 874 bool* found_types, |
| 883 *found_attribute = false; | 875 bool* found_sections) { |
| 876 *found_types = false; | |
| 884 *found_sections = false; | 877 *found_sections = false; |
| 885 for (std::vector<AutofillField*>::iterator field = fields_.begin(); | 878 for (std::vector<AutofillField*>::iterator field = fields_.begin(); |
| 886 field != fields_.end(); ++field) { | 879 field != fields_.end(); ++field) { |
|
Evan Stade
2012/10/19 20:36:47
nit: can you call field iter, and then do
Autofil
Ilya Sherman
2012/10/20 05:16:08
Done.
| |
| 887 if ((*field)->autocomplete_type.empty()) | 880 // Canonicalize the attribute value by trimming whitespace and converting to |
| 881 // lowercase ASCII. | |
| 882 string16 autocomplete_attribute = (*field)->autocomplete_attribute; | |
|
Evan Stade
2012/10/19 20:36:47
see my note elsewhere that the field's attribute s
Ilya Sherman
2012/10/20 05:16:08
Done.
| |
| 883 TrimWhitespace(autocomplete_attribute, TRIM_ALL, &autocomplete_attribute); | |
| 884 autocomplete_attribute = StringToLowerASCII(autocomplete_attribute); | |
| 885 | |
| 886 // The autocomplete attribute is overloaded: it can specify either a field | |
| 887 // type hint or whether autocomplete should be enabled at all. Ignore the | |
| 888 // latter type of attribute value. | |
| 889 if (autocomplete_attribute.empty() || | |
| 890 autocomplete_attribute == ASCIIToUTF16("on") || | |
| 891 autocomplete_attribute == ASCIIToUTF16("off")) { | |
| 892 continue; | |
| 893 } | |
| 894 | |
| 895 // Any other value, even it is invalid, is considered to be a type hint. | |
| 896 // This allows a website's author to specify an attribute like | |
| 897 // autocomplete="other" on a field to disable all Autofill heuristics for | |
|
Evan Stade
2012/10/19 20:36:47
don't follow this logic. Are you saying that autoc
Ilya Sherman
2012/10/20 05:16:08
The two are different. autocomplete="off" means d
| |
| 898 // the form. | |
| 899 *found_types = true; | |
| 900 | |
| 901 // Tokenize the attribute value. Per the spec, the tokens are parsed in | |
| 902 // reverse order. | |
| 903 std::vector<string16> tokens; | |
|
Evan Stade
2012/10/19 20:36:47
likewise, should be std::string
Ilya Sherman
2012/10/20 05:16:08
Done.
| |
| 904 Tokenize(autocomplete_attribute, ASCIIToUTF16(" "), &tokens); | |
|
Evan Stade
2012/10/19 20:59:44
should this include other space characters, or wer
Ilya Sherman
2012/10/20 05:16:08
Done + added test coverage (to the advanced.html t
| |
| 905 | |
| 906 // The final token must be the field type. | |
| 907 // If it is not one of the known types, abort. | |
| 908 DCHECK(!tokens.empty()); | |
| 909 string16 field_type_token = tokens.back(); | |
| 910 tokens.pop_back(); | |
| 911 AutofillFieldType field_type = | |
| 912 FieldTypeFromAutocompleteType(field_type_token, **field); | |
| 913 if (field_type == UNKNOWN_TYPE) | |
| 888 continue; | 914 continue; |
| 889 | 915 |
| 890 *found_attribute = true; | 916 // The preceding token, if any, may be a type hint. |
| 891 std::vector<string16> types; | 917 if (!tokens.empty() && IsContactTypeHint(tokens.back())) { |
| 892 Tokenize((*field)->autocomplete_type, ASCIIToUTF16(" "), &types); | 918 // If it is, it must match the field type; otherwise, abort. |
| 919 // Note that an invalid token invalidates the entire attribute value, even | |
| 920 // if the other tokens are valid. | |
| 921 if (!ContactTypeHintMatchesFieldType(tokens.back(), field_type)) | |
| 922 continue; | |
| 893 | 923 |
| 894 // Look for a named section. | 924 // Chrome Autofill ignores these type hints. |
| 895 const string16 kSectionPrefix = ASCIIToUTF16("section-"); | 925 tokens.pop_back(); |
| 896 if (!types.empty() && StartsWith(types.front(), kSectionPrefix, true)) { | |
| 897 *found_sections = true; | |
| 898 (*field)->set_section(types.front().substr(kSectionPrefix.size())); | |
| 899 } | 926 } |
| 900 | 927 |
| 901 // Look for specified types. | 928 // The preceding token, if any, may be a fixed string that is either |
| 902 for (std::vector<string16>::const_iterator type = types.begin(); | 929 // "shipping" or "billing". Chrome Autofill treats these as implicit |
| 903 type != types.end(); ++type) { | 930 // section name suffixes. |
| 904 if (UpdateFromAutocompleteType(*type, *field)) | 931 if (!tokens.empty() && |
| 905 break; | 932 (tokens.back() == ASCIIToUTF16("shipping") || |
| 933 tokens.back() == ASCIIToUTF16("billing"))) { | |
| 934 *found_sections = true; | |
| 935 (*field)->set_section(ASCIIToUTF16("-") + tokens.back()); | |
| 936 tokens.pop_back(); | |
| 937 } else { | |
| 938 // To prevent potential section name collisions, add a default suffix for | |
| 939 // other fields. Without this, 'autocomplete' attribute values | |
| 940 // "section--shipping street-address" and "shipping street-address" would | |
|
Evan Stade
2012/10/19 20:36:47
can you help me understand this section? It seems
Evan Stade
2012/10/19 20:57:27
Dan explained this to me. So is it intentional tha
Ilya Sherman
2012/10/20 05:16:08
Yes, it's intentional. We fill differently named
| |
| 941 // be parsed identically. | |
| 942 (*field)->set_section(ASCIIToUTF16("-default")); | |
| 906 } | 943 } |
| 944 | |
| 945 // The preceding token, if any, may be a named section. | |
| 946 const string16 kSectionPrefix = ASCIIToUTF16("section-"); | |
| 947 if (!tokens.empty() && StartsWith(tokens.back(), kSectionPrefix, true)) { | |
| 948 *found_sections = true; | |
| 949 // Prepend this section name to the suffix set in the preceding block. | |
| 950 (*field)->set_section( | |
| 951 tokens.back().substr(kSectionPrefix.size()) + (*field)->section()); | |
|
Evan Stade
2012/10/19 20:57:27
do you need to check for "section- " (i.e. with no
Ilya Sherman
2012/10/20 05:16:08
It's covered in the tests: we treat this as equiva
| |
| 952 tokens.pop_back(); | |
| 953 } | |
| 954 | |
| 955 // No other tokens are allowed. If there are any remaining, abort. | |
|
Evan Stade
2012/10/19 20:36:47
if you are aborting, and you just set found_sectio
Ilya Sherman
2012/10/20 05:16:08
Good call. Fixed, and added test coverage. The f
| |
| 956 if (!tokens.empty()) | |
| 957 continue; | |
| 958 | |
| 959 // No errors encountered while parsing! | |
| 960 // Update the |field|'s type based on what was parsed from the attribute. | |
| 961 (*field)->set_heuristic_type(field_type); | |
| 962 if (field_type_token == ASCIIToUTF16("tel-local-prefix")) | |
| 963 (*field)->set_phone_part(AutofillField::PHONE_PREFIX); | |
| 964 else if (field_type_token == ASCIIToUTF16("tel-local-suffix")) | |
| 965 (*field)->set_phone_part(AutofillField::PHONE_SUFFIX); | |
| 907 } | 966 } |
| 908 } | 967 } |
| 909 | 968 |
| 910 void FormStructure::IdentifySections(bool has_author_specified_sections) { | 969 void FormStructure::IdentifySections(bool has_author_specified_sections) { |
| 911 if (fields_.empty()) | 970 if (fields_.empty()) |
| 912 return; | 971 return; |
| 913 | 972 |
| 914 if (!has_author_specified_sections) { | 973 if (!has_author_specified_sections) { |
| 915 // Name sections after the first field in the section. | 974 // Name sections after the first field in the section. |
| 916 string16 current_section = fields_.front()->unique_name(); | 975 string16 current_section = fields_.front()->unique_name(); |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 962 for (std::vector<AutofillField*>::iterator field = fields_.begin(); | 1021 for (std::vector<AutofillField*>::iterator field = fields_.begin(); |
| 963 field != fields_.end(); ++field) { | 1022 field != fields_.end(); ++field) { |
| 964 AutofillType::FieldTypeGroup field_type_group = | 1023 AutofillType::FieldTypeGroup field_type_group = |
| 965 AutofillType((*field)->type()).group(); | 1024 AutofillType((*field)->type()).group(); |
| 966 if (field_type_group == AutofillType::CREDIT_CARD) | 1025 if (field_type_group == AutofillType::CREDIT_CARD) |
| 967 (*field)->set_section((*field)->section() + ASCIIToUTF16("-cc")); | 1026 (*field)->set_section((*field)->section() + ASCIIToUTF16("-cc")); |
| 968 else | 1027 else |
| 969 (*field)->set_section((*field)->section() + ASCIIToUTF16("-default")); | 1028 (*field)->set_section((*field)->section() + ASCIIToUTF16("-default")); |
| 970 } | 1029 } |
| 971 } | 1030 } |
| OLD | NEW |