| 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 |