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 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 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 250 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
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 Loading... | |
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 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 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 Loading... | |
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 } |
OLD | NEW |