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 token == "home" || token == "work" || token == "mobile"; |
| 94 } |
| 95 |
| 96 // Returns |true| iff the |token| is a type hint appropriate for a field of the |
| 97 // given |field_type|, as specified in the implementation section of |
| 98 // http://is.gd/whatwg_autocomplete |
| 99 bool ContactTypeHintMatchesFieldType(const std::string& token, |
| 100 AutofillFieldType field_type) { |
| 101 // The "home" and "work" type hints are only appropriate for email and phone |
| 102 // number field types. |
| 103 if (token == "home" || token == "work") { |
| 104 return field_type == EMAIL_ADDRESS || |
| 105 (field_type >= PHONE_HOME_NUMBER && |
| 106 field_type <= PHONE_HOME_WHOLE_NUMBER); |
93 } | 107 } |
94 | 108 |
95 if (autocomplete_type == ASCIIToUTF16("middle-name")) { | 109 // The "mobile" type hint is only appropriate for phone number field types. |
96 field->set_heuristic_type(NAME_MIDDLE); | 110 // Note that "fax" and "pager" are intentionally ignored, as Chrome does not |
97 return true; | 111 // support filling either type of information. |
98 } | 112 if (token == "mobile") { |
99 | 113 return field_type >= PHONE_HOME_NUMBER && |
100 if (autocomplete_type == ASCIIToUTF16("middle-initial")) { | 114 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 } | 115 } |
227 | 116 |
228 return false; | 117 return false; |
229 } | 118 } |
230 | 119 |
| 120 // Returns the Chrome Autofill-supported field type corresponding to the given |
| 121 // |autocomplete_type|, if there is one, in the context of the given |field|. |
| 122 // Chrome Autofill supports a subset of the field types listed at |
| 123 // http://is.gd/whatwg_autocomplete |
| 124 AutofillFieldType FieldTypeFromAutocompleteType( |
| 125 const std::string& autocomplete_type, |
| 126 const AutofillField& field) { |
| 127 if (autocomplete_type == "name") |
| 128 return NAME_FULL; |
| 129 |
| 130 if (autocomplete_type == "given-name") |
| 131 return NAME_FIRST; |
| 132 |
| 133 if (autocomplete_type == "additional-name") { |
| 134 if (field.max_length == 1) |
| 135 return NAME_MIDDLE_INITIAL; |
| 136 else |
| 137 return NAME_MIDDLE; |
| 138 } |
| 139 |
| 140 if (autocomplete_type == "family-name") |
| 141 return NAME_LAST; |
| 142 |
| 143 if (autocomplete_type == "honorific-suffix") |
| 144 return NAME_SUFFIX; |
| 145 |
| 146 if (autocomplete_type == "organization") |
| 147 return COMPANY_NAME; |
| 148 |
| 149 if (autocomplete_type == "street-address" || |
| 150 autocomplete_type == "address-line1") |
| 151 return ADDRESS_HOME_LINE1; |
| 152 |
| 153 if (autocomplete_type == "address-line2") |
| 154 return ADDRESS_HOME_LINE2; |
| 155 |
| 156 if (autocomplete_type == "locality") |
| 157 return ADDRESS_HOME_CITY; |
| 158 |
| 159 if (autocomplete_type == "region") |
| 160 return ADDRESS_HOME_STATE; |
| 161 |
| 162 if (autocomplete_type == "country") |
| 163 return ADDRESS_HOME_COUNTRY; |
| 164 |
| 165 if (autocomplete_type == "postal-code") |
| 166 return ADDRESS_HOME_ZIP; |
| 167 |
| 168 if (autocomplete_type == "cc-name") |
| 169 return CREDIT_CARD_NAME; |
| 170 |
| 171 if (autocomplete_type == "cc-number") |
| 172 return CREDIT_CARD_NUMBER; |
| 173 |
| 174 if (autocomplete_type == "cc-exp") { |
| 175 if (field.max_length == 5) |
| 176 return CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR; |
| 177 else |
| 178 return CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR; |
| 179 } |
| 180 |
| 181 if (autocomplete_type == "cc-exp-month") |
| 182 return CREDIT_CARD_EXP_MONTH; |
| 183 |
| 184 if (autocomplete_type == "cc-exp-year") { |
| 185 if (field.max_length == 2) |
| 186 return CREDIT_CARD_EXP_2_DIGIT_YEAR; |
| 187 else |
| 188 return CREDIT_CARD_EXP_4_DIGIT_YEAR; |
| 189 } |
| 190 |
| 191 if (autocomplete_type == "tel") |
| 192 return PHONE_HOME_WHOLE_NUMBER; |
| 193 |
| 194 if (autocomplete_type == "tel-country-code") |
| 195 return PHONE_HOME_COUNTRY_CODE; |
| 196 |
| 197 if (autocomplete_type == "tel-national") |
| 198 return PHONE_HOME_CITY_AND_NUMBER; |
| 199 |
| 200 if (autocomplete_type == "tel-area-code") |
| 201 return PHONE_HOME_CITY_CODE; |
| 202 |
| 203 if (autocomplete_type == "tel-local") |
| 204 return PHONE_HOME_NUMBER; |
| 205 |
| 206 if (autocomplete_type == "tel-local-prefix") |
| 207 return PHONE_HOME_NUMBER; |
| 208 |
| 209 if (autocomplete_type == "tel-local-suffix") |
| 210 return PHONE_HOME_NUMBER; |
| 211 |
| 212 if (autocomplete_type == "email") |
| 213 return EMAIL_ADDRESS; |
| 214 |
| 215 return UNKNOWN_TYPE; |
| 216 } |
| 217 |
231 } // namespace | 218 } // namespace |
232 | 219 |
233 FormStructure::FormStructure(const FormData& form) | 220 FormStructure::FormStructure(const FormData& form) |
234 : form_name_(form.name), | 221 : form_name_(form.name), |
235 source_url_(form.origin), | 222 source_url_(form.origin), |
236 target_url_(form.action), | 223 target_url_(form.action), |
237 autofill_count_(0), | 224 autofill_count_(0), |
238 upload_required_(USE_UPLOAD_RATES), | 225 upload_required_(USE_UPLOAD_RATES), |
239 server_experiment_id_("no server response"), | 226 server_experiment_id_("no server response"), |
240 has_author_specified_types_(false) { | 227 has_author_specified_types_(false) { |
(...skipping 25 matching lines...) Expand all Loading... |
266 } else { | 253 } else { |
267 // Either the method is 'get', or we don't know. In this case we default | 254 // Either the method is 'get', or we don't know. In this case we default |
268 // to GET. | 255 // to GET. |
269 method_ = GET; | 256 method_ = GET; |
270 } | 257 } |
271 } | 258 } |
272 | 259 |
273 FormStructure::~FormStructure() {} | 260 FormStructure::~FormStructure() {} |
274 | 261 |
275 void FormStructure::DetermineHeuristicTypes() { | 262 void FormStructure::DetermineHeuristicTypes() { |
276 // First, try to detect field types based on the fields' |autocompletetype| | 263 // 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 | 264 // 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. | 265 // autocomplete type hint, don't try to apply other heuristics to match fields |
| 266 // in this form. |
279 bool has_author_specified_sections; | 267 bool has_author_specified_sections; |
280 ParseAutocompletetypeAttributes(&has_author_specified_types_, | 268 ParseFieldTypesFromAutocompleteAttributes(&has_author_specified_types_, |
281 &has_author_specified_sections); | 269 &has_author_specified_sections); |
282 | 270 |
283 if (!has_author_specified_types_) { | 271 if (!has_author_specified_types_) { |
284 FieldTypeMap field_type_map; | 272 FieldTypeMap field_type_map; |
285 FormField::ParseFormFields(fields_.get(), &field_type_map); | 273 FormField::ParseFormFields(fields_.get(), &field_type_map); |
286 for (size_t index = 0; index < field_count(); index++) { | 274 for (size_t index = 0; index < field_count(); index++) { |
287 AutofillField* field = fields_[index]; | 275 AutofillField* field = fields_[index]; |
288 FieldTypeMap::iterator iter = field_type_map.find(field->unique_name()); | 276 FieldTypeMap::iterator iter = field_type_map.find(field->unique_name()); |
289 if (iter != field_type_map.end()) | 277 if (iter != field_type_map.end()) |
290 field->set_heuristic_type(iter->second); | 278 field->set_heuristic_type(iter->second); |
291 } | 279 } |
(...skipping 250 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
542 // Rule out http(s)://*/search?... | 530 // Rule out http(s)://*/search?... |
543 // e.g. http://www.google.com/search?q=... | 531 // e.g. http://www.google.com/search?q=... |
544 // http://search.yahoo.com/search?p=... | 532 // http://search.yahoo.com/search?p=... |
545 if (target_url_.path() == "/search") | 533 if (target_url_.path() == "/search") |
546 return false; | 534 return false; |
547 | 535 |
548 // Make sure there as at least one text field. | 536 // Make sure there as at least one text field. |
549 bool has_text_field = false; | 537 bool has_text_field = false; |
550 for (std::vector<AutofillField*>::const_iterator it = begin(); | 538 for (std::vector<AutofillField*>::const_iterator it = begin(); |
551 it != end() && !has_text_field; ++it) { | 539 it != end() && !has_text_field; ++it) { |
552 has_text_field |= (*it)->form_control_type != ASCIIToUTF16("select-one"); | 540 has_text_field |= (*it)->form_control_type != "select-one"; |
553 } | 541 } |
554 if (!has_text_field) | 542 if (!has_text_field) |
555 return false; | 543 return false; |
556 | 544 |
557 return !require_method_post || (method_ == POST); | 545 return !require_method_post || (method_ == POST); |
558 } | 546 } |
559 | 547 |
560 bool FormStructure::ShouldBeCrowdsourced() const { | 548 bool FormStructure::ShouldBeCrowdsourced() const { |
561 return !has_author_specified_types_ && ShouldBeParsed(true); | 549 return !has_author_specified_types_ && ShouldBeParsed(true); |
562 } | 550 } |
563 | 551 |
564 void FormStructure::UpdateFromCache(const FormStructure& cached_form) { | 552 void FormStructure::UpdateFromCache(const FormStructure& cached_form) { |
565 // Map from field signatures to cached fields. | 553 // Map from field signatures to cached fields. |
566 std::map<std::string, const AutofillField*> cached_fields; | 554 std::map<std::string, const AutofillField*> cached_fields; |
567 for (size_t i = 0; i < cached_form.field_count(); ++i) { | 555 for (size_t i = 0; i < cached_form.field_count(); ++i) { |
568 const AutofillField* field = cached_form.field(i); | 556 const AutofillField* field = cached_form.field(i); |
569 cached_fields[field->FieldSignature()] = field; | 557 cached_fields[field->FieldSignature()] = field; |
570 } | 558 } |
571 | 559 |
572 for (std::vector<AutofillField*>::const_iterator iter = begin(); | 560 for (std::vector<AutofillField*>::const_iterator iter = begin(); |
573 iter != end(); ++iter) { | 561 iter != end(); ++iter) { |
574 AutofillField* field = *iter; | 562 AutofillField* field = *iter; |
575 | 563 |
576 std::map<std::string, const AutofillField*>::const_iterator | 564 std::map<std::string, const AutofillField*>::const_iterator |
577 cached_field = cached_fields.find(field->FieldSignature()); | 565 cached_field = cached_fields.find(field->FieldSignature()); |
578 if (cached_field != cached_fields.end()) { | 566 if (cached_field != cached_fields.end()) { |
579 if (field->form_control_type != ASCIIToUTF16("select-one") && | 567 if (field->form_control_type != "select-one" && |
580 field->value == cached_field->second->value) { | 568 field->value == cached_field->second->value) { |
581 // From the perspective of learning user data, text fields containing | 569 // From the perspective of learning user data, text fields containing |
582 // default values are equivalent to empty fields. | 570 // default values are equivalent to empty fields. |
583 field->value = string16(); | 571 field->value = string16(); |
584 } | 572 } |
585 | 573 |
586 field->set_heuristic_type(cached_field->second->heuristic_type()); | 574 field->set_heuristic_type(cached_field->second->heuristic_type()); |
587 field->set_server_type(cached_field->second->server_type()); | 575 field->set_server_type(cached_field->second->server_type()); |
588 } | 576 } |
589 } | 577 } |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
687 } else if (field_types.count(predicted_type)) { | 675 } else if (field_types.count(predicted_type)) { |
688 metric_logger.LogOverallTypePrediction(AutofillMetrics::TYPE_MATCH, | 676 metric_logger.LogOverallTypePrediction(AutofillMetrics::TYPE_MATCH, |
689 field_type, experiment_id); | 677 field_type, experiment_id); |
690 } else { | 678 } else { |
691 metric_logger.LogOverallTypePrediction(AutofillMetrics::TYPE_MISMATCH, | 679 metric_logger.LogOverallTypePrediction(AutofillMetrics::TYPE_MISMATCH, |
692 field_type, experiment_id); | 680 field_type, experiment_id); |
693 } | 681 } |
694 | 682 |
695 // TODO(isherman): <select> fields don't support |is_autofilled()|, so we | 683 // TODO(isherman): <select> fields don't support |is_autofilled()|, so we |
696 // have to skip them for the remaining metrics. | 684 // have to skip them for the remaining metrics. |
697 if (field->form_control_type == ASCIIToUTF16("select-one")) | 685 if (field->form_control_type == "select-one") |
698 continue; | 686 continue; |
699 | 687 |
700 if (field->is_autofilled) { | 688 if (field->is_autofilled) { |
701 metric_logger.LogQualityMetric(AutofillMetrics::FIELD_AUTOFILLED, | 689 metric_logger.LogQualityMetric(AutofillMetrics::FIELD_AUTOFILLED, |
702 experiment_id); | 690 experiment_id); |
703 } else { | 691 } else { |
704 metric_logger.LogQualityMetric(AutofillMetrics::FIELD_NOT_AUTOFILLED, | 692 metric_logger.LogQualityMetric(AutofillMetrics::FIELD_NOT_AUTOFILLED, |
705 experiment_id); | 693 experiment_id); |
706 | 694 |
707 if (heuristic_type == UNKNOWN_TYPE) { | 695 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( | 859 buzz::XmlElement *field_element = new buzz::XmlElement( |
872 buzz::QName(kXMLElementField)); | 860 buzz::QName(kXMLElementField)); |
873 field_element->SetAttr(buzz::QName(kAttributeSignature), | 861 field_element->SetAttr(buzz::QName(kAttributeSignature), |
874 field->FieldSignature()); | 862 field->FieldSignature()); |
875 encompassing_xml_element->AddElement(field_element); | 863 encompassing_xml_element->AddElement(field_element); |
876 } | 864 } |
877 } | 865 } |
878 return true; | 866 return true; |
879 } | 867 } |
880 | 868 |
881 void FormStructure::ParseAutocompletetypeAttributes(bool* found_attribute, | 869 void FormStructure::ParseFieldTypesFromAutocompleteAttributes( |
882 bool* found_sections) { | 870 bool* found_types, |
883 *found_attribute = false; | 871 bool* found_sections) { |
| 872 const std::string kDefaultSection = "-default"; |
| 873 |
| 874 *found_types = false; |
884 *found_sections = false; | 875 *found_sections = false; |
885 for (std::vector<AutofillField*>::iterator field = fields_.begin(); | 876 for (std::vector<AutofillField*>::iterator it = fields_.begin(); |
886 field != fields_.end(); ++field) { | 877 it != fields_.end(); ++it) { |
887 if ((*field)->autocomplete_type.empty()) | 878 AutofillField* field = *it; |
| 879 |
| 880 // To prevent potential section name collisions, add a default suffix for |
| 881 // other fields. Without this, 'autocomplete' attribute values |
| 882 // "section--shipping street-address" and "shipping street-address" would be |
| 883 // parsed identically, given the section handling code below. We do this |
| 884 // before any validation so that fields with invalid attributes still end up |
| 885 // in the default section. These default section names will be overridden |
| 886 // by subsequent heuristic parsing steps if there are no author-specified |
| 887 // section names. |
| 888 field->set_section(kDefaultSection); |
| 889 |
| 890 // Canonicalize the attribute value by trimming whitespace, collapsing |
| 891 // non-space characters (e.g. tab) to spaces, and converting to lowercase. |
| 892 std::string autocomplete_attribute = |
| 893 CollapseWhitespaceASCII(field->autocomplete_attribute, false); |
| 894 autocomplete_attribute = StringToLowerASCII(autocomplete_attribute); |
| 895 |
| 896 // The autocomplete attribute is overloaded: it can specify either a field |
| 897 // type hint or whether autocomplete should be enabled at all. Ignore the |
| 898 // latter type of attribute value. |
| 899 if (autocomplete_attribute.empty() || |
| 900 autocomplete_attribute == "on" || |
| 901 autocomplete_attribute == "off") { |
| 902 continue; |
| 903 } |
| 904 |
| 905 // Any other value, even it is invalid, is considered to be a type hint. |
| 906 // This allows a website's author to specify an attribute like |
| 907 // autocomplete="other" on a field to disable all Autofill heuristics for |
| 908 // the form. |
| 909 *found_types = true; |
| 910 |
| 911 // Tokenize the attribute value. Per the spec, the tokens are parsed in |
| 912 // reverse order. |
| 913 std::vector<std::string> tokens; |
| 914 Tokenize(autocomplete_attribute, " ", &tokens); |
| 915 |
| 916 // The final token must be the field type. |
| 917 // If it is not one of the known types, abort. |
| 918 DCHECK(!tokens.empty()); |
| 919 std::string field_type_token = tokens.back(); |
| 920 tokens.pop_back(); |
| 921 AutofillFieldType field_type = |
| 922 FieldTypeFromAutocompleteType(field_type_token, *field); |
| 923 if (field_type == UNKNOWN_TYPE) |
888 continue; | 924 continue; |
889 | 925 |
890 *found_attribute = true; | 926 // The preceding token, if any, may be a type hint. |
891 std::vector<string16> types; | 927 if (!tokens.empty() && IsContactTypeHint(tokens.back())) { |
892 Tokenize((*field)->autocomplete_type, ASCIIToUTF16(" "), &types); | 928 // If it is, it must match the field type; otherwise, abort. |
| 929 // Note that an invalid token invalidates the entire attribute value, even |
| 930 // if the other tokens are valid. |
| 931 if (!ContactTypeHintMatchesFieldType(tokens.back(), field_type)) |
| 932 continue; |
893 | 933 |
894 // Look for a named section. | 934 // Chrome Autofill ignores these type hints. |
895 const string16 kSectionPrefix = ASCIIToUTF16("section-"); | 935 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 } | 936 } |
900 | 937 |
901 // Look for specified types. | 938 // The preceding token, if any, may be a fixed string that is either |
902 for (std::vector<string16>::const_iterator type = types.begin(); | 939 // "shipping" or "billing". Chrome Autofill treats these as implicit |
903 type != types.end(); ++type) { | 940 // section name suffixes. |
904 if (UpdateFromAutocompleteType(*type, *field)) | 941 DCHECK_EQ(kDefaultSection, field->section()); |
905 break; | 942 std::string section = field->section(); |
| 943 if (!tokens.empty() && |
| 944 (tokens.back() == "shipping" || tokens.back() == "billing")) { |
| 945 section = "-" + tokens.back(); |
| 946 tokens.pop_back(); |
906 } | 947 } |
| 948 |
| 949 // The preceding token, if any, may be a named section. |
| 950 const std::string kSectionPrefix = "section-"; |
| 951 if (!tokens.empty() && |
| 952 StartsWithASCII(tokens.back(), kSectionPrefix, true)) { |
| 953 // Prepend this section name to the suffix set in the preceding block. |
| 954 section = tokens.back().substr(kSectionPrefix.size()) + section; |
| 955 tokens.pop_back(); |
| 956 } |
| 957 |
| 958 // No other tokens are allowed. If there are any remaining, abort. |
| 959 if (!tokens.empty()) |
| 960 continue; |
| 961 |
| 962 if (section != kDefaultSection) { |
| 963 *found_sections = true; |
| 964 field->set_section(section); |
| 965 } |
| 966 |
| 967 // No errors encountered while parsing! |
| 968 // Update the |field|'s type based on what was parsed from the attribute. |
| 969 field->set_heuristic_type(field_type); |
| 970 if (field_type_token == "tel-local-prefix") |
| 971 field->set_phone_part(AutofillField::PHONE_PREFIX); |
| 972 else if (field_type_token == "tel-local-suffix") |
| 973 field->set_phone_part(AutofillField::PHONE_SUFFIX); |
907 } | 974 } |
908 } | 975 } |
909 | 976 |
910 void FormStructure::IdentifySections(bool has_author_specified_sections) { | 977 void FormStructure::IdentifySections(bool has_author_specified_sections) { |
911 if (fields_.empty()) | 978 if (fields_.empty()) |
912 return; | 979 return; |
913 | 980 |
914 if (!has_author_specified_sections) { | 981 if (!has_author_specified_sections) { |
915 // Name sections after the first field in the section. | 982 // Name sections after the first field in the section. |
916 string16 current_section = fields_.front()->unique_name(); | 983 string16 current_section = fields_.front()->unique_name(); |
(...skipping 29 matching lines...) Expand all Loading... |
946 | 1013 |
947 previous_type = current_type; | 1014 previous_type = current_type; |
948 | 1015 |
949 if (current_type != UNKNOWN_TYPE && already_saw_current_type) { | 1016 if (current_type != UNKNOWN_TYPE && already_saw_current_type) { |
950 // We reached the end of a section, so start a new section. | 1017 // We reached the end of a section, so start a new section. |
951 seen_types.clear(); | 1018 seen_types.clear(); |
952 current_section = (*field)->unique_name(); | 1019 current_section = (*field)->unique_name(); |
953 } | 1020 } |
954 | 1021 |
955 seen_types.insert(current_type); | 1022 seen_types.insert(current_type); |
956 (*field)->set_section(current_section); | 1023 (*field)->set_section(UTF16ToUTF8(current_section)); |
957 } | 1024 } |
958 } | 1025 } |
959 | 1026 |
960 // Ensure that credit card and address fields are in separate sections. | 1027 // Ensure that credit card and address fields are in separate sections. |
961 // This simplifies the section-aware logic in autofill_manager.cc. | 1028 // This simplifies the section-aware logic in autofill_manager.cc. |
962 for (std::vector<AutofillField*>::iterator field = fields_.begin(); | 1029 for (std::vector<AutofillField*>::iterator field = fields_.begin(); |
963 field != fields_.end(); ++field) { | 1030 field != fields_.end(); ++field) { |
964 AutofillType::FieldTypeGroup field_type_group = | 1031 AutofillType::FieldTypeGroup field_type_group = |
965 AutofillType((*field)->type()).group(); | 1032 AutofillType((*field)->type()).group(); |
966 if (field_type_group == AutofillType::CREDIT_CARD) | 1033 if (field_type_group == AutofillType::CREDIT_CARD) |
967 (*field)->set_section((*field)->section() + ASCIIToUTF16("-cc")); | 1034 (*field)->set_section((*field)->section() + "-cc"); |
968 else | 1035 else |
969 (*field)->set_section((*field)->section() + ASCIIToUTF16("-default")); | 1036 (*field)->set_section((*field)->section() + "-default"); |
970 } | 1037 } |
971 } | 1038 } |
OLD | NEW |