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

Side by Side 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: string16 -> std::string and other fixes + cleanup 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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 std::string& token) {
93 return
94 token == "home" ||
95 token == "work" ||
96 token == "mobile" ||
97 token == "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 std::string& 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 == "home" || token == "work") {
108 return field_type == EMAIL_ADDRESS ||
109 (field_type >= PHONE_HOME_NUMBER &&
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 == "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 std::string& autocomplete_type,
130 const AutofillField& field) {
131 if (autocomplete_type == "name")
132 return NAME_FULL;
133
134 if (autocomplete_type == "given-name")
135 return NAME_FIRST;
136
137 if (autocomplete_type == "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 == "family-name")
145 return NAME_LAST;
146
147 if (autocomplete_type == "honorific-suffix")
148 return NAME_SUFFIX;
149
150 if (autocomplete_type == "organization")
151 return COMPANY_NAME;
152
153 if (autocomplete_type == "street-address" ||
154 autocomplete_type == "address-line1")
155 return ADDRESS_HOME_LINE1;
156
157 if (autocomplete_type == "address-line2")
158 return ADDRESS_HOME_LINE2;
159
160 if (autocomplete_type == "locality")
161 return ADDRESS_HOME_CITY;
162
163 if (autocomplete_type == "region")
164 return ADDRESS_HOME_STATE;
165
166 if (autocomplete_type == "country")
167 return ADDRESS_HOME_COUNTRY;
168
169 if (autocomplete_type == "postal-code")
170 return ADDRESS_HOME_ZIP;
171
172 if (autocomplete_type == "cc-name")
173 return CREDIT_CARD_NAME;
174
175 if (autocomplete_type == "cc-number")
176 return CREDIT_CARD_NUMBER;
177
178 if (autocomplete_type == "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 == "cc-exp-month")
186 return CREDIT_CARD_EXP_MONTH;
187
188 if (autocomplete_type == "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 == "tel")
196 return PHONE_HOME_WHOLE_NUMBER;
197
198 if (autocomplete_type == "tel-country-code")
199 return PHONE_HOME_COUNTRY_CODE;
200
201 if (autocomplete_type == "tel-national")
202 return PHONE_HOME_CITY_AND_NUMBER;
203
204 if (autocomplete_type == "tel-area-code")
205 return PHONE_HOME_CITY_CODE;
206
207 if (autocomplete_type == "tel-local")
208 return PHONE_HOME_NUMBER;
209
210 if (autocomplete_type == "tel-local-prefix")
211 return PHONE_HOME_NUMBER;
212
213 if (autocomplete_type == "tel-local-suffix")
214 return PHONE_HOME_NUMBER;
215
216 if (autocomplete_type == "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
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 250 matching lines...) Expand 10 before | Expand all | Expand 10 after
542 // Rule out http(s)://*/search?... 534 // Rule out http(s)://*/search?...
543 // e.g. http://www.google.com/search?q=... 535 // e.g. http://www.google.com/search?q=...
544 // http://search.yahoo.com/search?p=... 536 // http://search.yahoo.com/search?p=...
545 if (target_url_.path() == "/search") 537 if (target_url_.path() == "/search")
546 return false; 538 return false;
547 539
548 // Make sure there as at least one text field. 540 // Make sure there as at least one text field.
549 bool has_text_field = false; 541 bool has_text_field = false;
550 for (std::vector<AutofillField*>::const_iterator it = begin(); 542 for (std::vector<AutofillField*>::const_iterator it = begin();
551 it != end() && !has_text_field; ++it) { 543 it != end() && !has_text_field; ++it) {
552 has_text_field |= (*it)->form_control_type != ASCIIToUTF16("select-one"); 544 has_text_field |= (*it)->form_control_type != "select-one";
553 } 545 }
554 if (!has_text_field) 546 if (!has_text_field)
555 return false; 547 return false;
556 548
557 return !require_method_post || (method_ == POST); 549 return !require_method_post || (method_ == POST);
558 } 550 }
559 551
560 bool FormStructure::ShouldBeCrowdsourced() const { 552 bool FormStructure::ShouldBeCrowdsourced() const {
561 return !has_author_specified_types_ && ShouldBeParsed(true); 553 return !has_author_specified_types_ && ShouldBeParsed(true);
562 } 554 }
563 555
564 void FormStructure::UpdateFromCache(const FormStructure& cached_form) { 556 void FormStructure::UpdateFromCache(const FormStructure& cached_form) {
565 // Map from field signatures to cached fields. 557 // Map from field signatures to cached fields.
566 std::map<std::string, const AutofillField*> cached_fields; 558 std::map<std::string, const AutofillField*> cached_fields;
567 for (size_t i = 0; i < cached_form.field_count(); ++i) { 559 for (size_t i = 0; i < cached_form.field_count(); ++i) {
568 const AutofillField* field = cached_form.field(i); 560 const AutofillField* field = cached_form.field(i);
569 cached_fields[field->FieldSignature()] = field; 561 cached_fields[field->FieldSignature()] = field;
570 } 562 }
571 563
572 for (std::vector<AutofillField*>::const_iterator iter = begin(); 564 for (std::vector<AutofillField*>::const_iterator iter = begin();
573 iter != end(); ++iter) { 565 iter != end(); ++iter) {
574 AutofillField* field = *iter; 566 AutofillField* field = *iter;
575 567
576 std::map<std::string, const AutofillField*>::const_iterator 568 std::map<std::string, const AutofillField*>::const_iterator
577 cached_field = cached_fields.find(field->FieldSignature()); 569 cached_field = cached_fields.find(field->FieldSignature());
578 if (cached_field != cached_fields.end()) { 570 if (cached_field != cached_fields.end()) {
579 if (field->form_control_type != ASCIIToUTF16("select-one") && 571 if (field->form_control_type != "select-one" &&
580 field->value == cached_field->second->value) { 572 field->value == cached_field->second->value) {
581 // From the perspective of learning user data, text fields containing 573 // From the perspective of learning user data, text fields containing
582 // default values are equivalent to empty fields. 574 // default values are equivalent to empty fields.
583 field->value = string16(); 575 field->value = string16();
584 } 576 }
585 577
586 field->set_heuristic_type(cached_field->second->heuristic_type()); 578 field->set_heuristic_type(cached_field->second->heuristic_type());
587 field->set_server_type(cached_field->second->server_type()); 579 field->set_server_type(cached_field->second->server_type());
588 } 580 }
589 } 581 }
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after
687 } else if (field_types.count(predicted_type)) { 679 } else if (field_types.count(predicted_type)) {
688 metric_logger.LogOverallTypePrediction(AutofillMetrics::TYPE_MATCH, 680 metric_logger.LogOverallTypePrediction(AutofillMetrics::TYPE_MATCH,
689 field_type, experiment_id); 681 field_type, experiment_id);
690 } else { 682 } else {
691 metric_logger.LogOverallTypePrediction(AutofillMetrics::TYPE_MISMATCH, 683 metric_logger.LogOverallTypePrediction(AutofillMetrics::TYPE_MISMATCH,
692 field_type, experiment_id); 684 field_type, experiment_id);
693 } 685 }
694 686
695 // TODO(isherman): <select> fields don't support |is_autofilled()|, so we 687 // TODO(isherman): <select> fields don't support |is_autofilled()|, so we
696 // have to skip them for the remaining metrics. 688 // have to skip them for the remaining metrics.
697 if (field->form_control_type == ASCIIToUTF16("select-one")) 689 if (field->form_control_type == "select-one")
698 continue; 690 continue;
699 691
700 if (field->is_autofilled) { 692 if (field->is_autofilled) {
701 metric_logger.LogQualityMetric(AutofillMetrics::FIELD_AUTOFILLED, 693 metric_logger.LogQualityMetric(AutofillMetrics::FIELD_AUTOFILLED,
702 experiment_id); 694 experiment_id);
703 } else { 695 } else {
704 metric_logger.LogQualityMetric(AutofillMetrics::FIELD_NOT_AUTOFILLED, 696 metric_logger.LogQualityMetric(AutofillMetrics::FIELD_NOT_AUTOFILLED,
705 experiment_id); 697 experiment_id);
706 698
707 if (heuristic_type == UNKNOWN_TYPE) { 699 if (heuristic_type == UNKNOWN_TYPE) {
(...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after
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 it = fields_.begin();
886 field != fields_.end(); ++field) { 879 it != fields_.end(); ++it) {
887 if ((*field)->autocomplete_type.empty()) 880 AutofillField* field = *it;
881
882 // Canonicalize the attribute value by trimming whitespace, collapsing
883 // non-space characters (e.g. tab) to spaces, and converting to lowercase.
884 std::string autocomplete_attribute =
885 CollapseWhitespaceASCII(field->autocomplete_attribute, false);
886 autocomplete_attribute = StringToLowerASCII(autocomplete_attribute);
887
888 // The autocomplete attribute is overloaded: it can specify either a field
889 // type hint or whether autocomplete should be enabled at all. Ignore the
890 // latter type of attribute value.
891 if (autocomplete_attribute.empty() ||
892 autocomplete_attribute == "on" ||
893 autocomplete_attribute == "off") {
894 continue;
895 }
896
897 // Any other value, even it is invalid, is considered to be a type hint.
898 // This allows a website's author to specify an attribute like
899 // autocomplete="other" on a field to disable all Autofill heuristics for
900 // the form.
901 *found_types = true;
902
903 // Tokenize the attribute value. Per the spec, the tokens are parsed in
904 // reverse order.
905 std::vector<std::string> tokens;
906 Tokenize(autocomplete_attribute, " ", &tokens);
907
908 // The final token must be the field type.
909 // If it is not one of the known types, abort.
910 DCHECK(!tokens.empty());
911 std::string field_type_token = tokens.back();
912 tokens.pop_back();
913 AutofillFieldType field_type =
914 FieldTypeFromAutocompleteType(field_type_token, *field);
915 if (field_type == UNKNOWN_TYPE)
888 continue; 916 continue;
889 917
890 *found_attribute = true; 918 // The preceding token, if any, may be a type hint.
891 std::vector<string16> types; 919 if (!tokens.empty() && IsContactTypeHint(tokens.back())) {
892 Tokenize((*field)->autocomplete_type, ASCIIToUTF16(" "), &types); 920 // If it is, it must match the field type; otherwise, abort.
921 // Note that an invalid token invalidates the entire attribute value, even
922 // if the other tokens are valid.
923 if (!ContactTypeHintMatchesFieldType(tokens.back(), field_type))
924 continue;
893 925
894 // Look for a named section. 926 // Chrome Autofill ignores these type hints.
895 const string16 kSectionPrefix = ASCIIToUTF16("section-"); 927 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 } 928 }
900 929
901 // Look for specified types. 930 // The preceding token, if any, may be a fixed string that is either
902 for (std::vector<string16>::const_iterator type = types.begin(); 931 // "shipping" or "billing". Chrome Autofill treats these as implicit
903 type != types.end(); ++type) { 932 // section name suffixes.
904 if (UpdateFromAutocompleteType(*type, *field)) 933 std::string section;
905 break; 934 if (!tokens.empty() &&
935 (tokens.back() == "shipping" || tokens.back() == "billing")) {
936 section = "-" + tokens.back();
937 tokens.pop_back();
938 } else {
939 // To prevent potential section name collisions, add a default suffix for
940 // other fields. Without this, 'autocomplete' attribute values
941 // "section--shipping street-address" and "shipping street-address" would
942 // be parsed identically.
943 section = "-default";
906 } 944 }
945
946 // The preceding token, if any, may be a named section.
947 const std::string kSectionPrefix = "section-";
948 if (!tokens.empty() &&
949 StartsWithASCII(tokens.back(), kSectionPrefix, true)) {
950 // Prepend this section name to the suffix set in the preceding block.
951 section = tokens.back().substr(kSectionPrefix.size()) + section;
952 tokens.pop_back();
953 }
954
955 // No other tokens are allowed. If there are any remaining, abort.
956 if (!tokens.empty())
957 continue;
958
959 if (!section.empty()) {
Evan Stade 2012/10/22 18:28:27 is it even possible for section to be empty at thi
Ilya Sherman 2012/10/22 23:28:13 Good catch. Fixed the logic here.
960 *found_sections = true;
961 field->set_section(section);
962 }
963
964 // No errors encountered while parsing!
965 // Update the |field|'s type based on what was parsed from the attribute.
966 field->set_heuristic_type(field_type);
967 if (field_type_token == "tel-local-prefix")
968 field->set_phone_part(AutofillField::PHONE_PREFIX);
969 else if (field_type_token == "tel-local-suffix")
970 field->set_phone_part(AutofillField::PHONE_SUFFIX);
907 } 971 }
908 } 972 }
909 973
910 void FormStructure::IdentifySections(bool has_author_specified_sections) { 974 void FormStructure::IdentifySections(bool has_author_specified_sections) {
911 if (fields_.empty()) 975 if (fields_.empty())
912 return; 976 return;
913 977
914 if (!has_author_specified_sections) { 978 if (!has_author_specified_sections) {
915 // Name sections after the first field in the section. 979 // Name sections after the first field in the section.
916 string16 current_section = fields_.front()->unique_name(); 980 string16 current_section = fields_.front()->unique_name();
(...skipping 29 matching lines...) Expand all
946 1010
947 previous_type = current_type; 1011 previous_type = current_type;
948 1012
949 if (current_type != UNKNOWN_TYPE && already_saw_current_type) { 1013 if (current_type != UNKNOWN_TYPE && already_saw_current_type) {
950 // We reached the end of a section, so start a new section. 1014 // We reached the end of a section, so start a new section.
951 seen_types.clear(); 1015 seen_types.clear();
952 current_section = (*field)->unique_name(); 1016 current_section = (*field)->unique_name();
953 } 1017 }
954 1018
955 seen_types.insert(current_type); 1019 seen_types.insert(current_type);
956 (*field)->set_section(current_section); 1020 (*field)->set_section(UTF16ToUTF8(current_section));
957 } 1021 }
958 } 1022 }
959 1023
960 // Ensure that credit card and address fields are in separate sections. 1024 // Ensure that credit card and address fields are in separate sections.
961 // This simplifies the section-aware logic in autofill_manager.cc. 1025 // This simplifies the section-aware logic in autofill_manager.cc.
962 for (std::vector<AutofillField*>::iterator field = fields_.begin(); 1026 for (std::vector<AutofillField*>::iterator field = fields_.begin();
963 field != fields_.end(); ++field) { 1027 field != fields_.end(); ++field) {
964 AutofillType::FieldTypeGroup field_type_group = 1028 AutofillType::FieldTypeGroup field_type_group =
965 AutofillType((*field)->type()).group(); 1029 AutofillType((*field)->type()).group();
966 if (field_type_group == AutofillType::CREDIT_CARD) 1030 if (field_type_group == AutofillType::CREDIT_CARD)
967 (*field)->set_section((*field)->section() + ASCIIToUTF16("-cc")); 1031 (*field)->set_section((*field)->section() + "-cc");
968 else 1032 else
969 (*field)->set_section((*field)->section() + ASCIIToUTF16("-default")); 1033 (*field)->set_section((*field)->section() + "-default");
970 } 1034 }
971 } 1035 }
OLDNEW
« 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