Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/autofill/form_structure.h" | 5 #include "chrome/browser/autofill/form_structure.h" |
| 6 | 6 |
| 7 #include <utility> | 7 #include <utility> |
| 8 | 8 |
| 9 #include "base/basictypes.h" | 9 #include "base/basictypes.h" |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 78 // Print all meaningfull bytes into a string. | 78 // Print all meaningfull bytes into a string. |
| 79 std::string data_presence; | 79 std::string data_presence; |
| 80 data_presence.reserve(data_end * 2 + 1); | 80 data_presence.reserve(data_end * 2 + 1); |
| 81 for (size_t i = 0; i < data_end; ++i) { | 81 for (size_t i = 0; i < data_end; ++i) { |
| 82 base::StringAppendF(&data_presence, "%02x", bit_field[i]); | 82 base::StringAppendF(&data_presence, "%02x", bit_field[i]); |
| 83 } | 83 } |
| 84 | 84 |
| 85 return data_presence; | 85 return data_presence; |
| 86 } | 86 } |
| 87 | 87 |
| 88 bool UpdateFromAutocompleteType(const string16& autocomplete_type, | 88 // Returns |true| iff the |token| is a type hint for a contact field, as |
| 89 AutofillField* field) { | 89 // specified in the implementation section of http://is.gd/whatwg_autocomplete |
| 90 if (autocomplete_type == ASCIIToUTF16("given-name")) { | 90 // Note that "fax" and "pager" are intentionally ignored, as Chrome does not |
| 91 field->set_heuristic_type(NAME_FIRST); | 91 // support filling either type of information. |
| 92 return true; | 92 bool IsContactTypeHint(const 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 |