[email protected] | 7b37fbb | 2011-03-07 16:16:03 | [diff] [blame] | 1 | // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
[email protected] | a7b29dd | 2010-07-12 22:40:06 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
[email protected] | e4b2fa3 | 2013-03-09 22:56:56 | [diff] [blame] | 5 | #include "components/autofill/browser/form_group.h" |
[email protected] | a7b29dd | 2010-07-12 22:40:06 | [diff] [blame] | 6 | |
[email protected] | 64853b1b | 2012-11-13 08:18:46 | [diff] [blame] | 7 | #include <algorithm> |
[email protected] | b703864 | 2011-01-30 22:46:44 | [diff] [blame] | 8 | #include <iterator> |
| 9 | |
[email protected] | 64853b1b | 2012-11-13 08:18:46 | [diff] [blame] | 10 | #include "base/basictypes.h" |
| 11 | #include "base/logging.h" |
[email protected] | 3ea1b18 | 2013-02-08 22:38:41 | [diff] [blame] | 12 | #include "base/strings/string_number_conversions.h" |
[email protected] | 64853b1b | 2012-11-13 08:18:46 | [diff] [blame] | 13 | #include "base/utf_string_conversions.h" |
[email protected] | e4b2fa3 | 2013-03-09 22:56:56 | [diff] [blame] | 14 | #include "components/autofill/browser/autofill_country.h" |
[email protected] | edf48d4 | 2013-03-07 05:44:43 | [diff] [blame] | 15 | #include "components/autofill/common/form_field_data.h" |
[email protected] | 64853b1b | 2012-11-13 08:18:46 | [diff] [blame] | 16 | #include "grit/generated_resources.h" |
| 17 | #include "ui/base/l10n/l10n_util.h" |
| 18 | |
[email protected] | e217c563 | 2013-04-12 19:11:48 | [diff] [blame^] | 19 | namespace autofill { |
[email protected] | 64853b1b | 2012-11-13 08:18:46 | [diff] [blame] | 20 | namespace { |
| 21 | |
| 22 | // TODO(jhawkins): Add more states/provinces. See http://crbug.com/45039. |
| 23 | |
| 24 | class State { |
| 25 | public: |
| 26 | const char* name; |
| 27 | const char* abbreviation; |
| 28 | |
| 29 | static const State all_states[]; |
| 30 | |
[email protected] | d5ca8fb | 2013-04-11 17:54:31 | [diff] [blame] | 31 | static base::string16 Abbreviation(const base::string16& name); |
| 32 | static base::string16 FullName(const base::string16& abbreviation); |
[email protected] | 64853b1b | 2012-11-13 08:18:46 | [diff] [blame] | 33 | }; |
| 34 | |
| 35 | const State State::all_states[] = { |
| 36 | { "alabama", "al" }, |
| 37 | { "alaska", "ak" }, |
| 38 | { "arizona", "az" }, |
| 39 | { "arkansas", "ar" }, |
| 40 | { "california", "ca" }, |
| 41 | { "colorado", "co" }, |
| 42 | { "connecticut", "ct" }, |
| 43 | { "delaware", "de" }, |
| 44 | { "district of columbia", "dc" }, |
| 45 | { "florida", "fl" }, |
| 46 | { "georgia", "ga" }, |
| 47 | { "hawaii", "hi" }, |
| 48 | { "idaho", "id" }, |
| 49 | { "illinois", "il" }, |
| 50 | { "indiana", "in" }, |
| 51 | { "iowa", "ia" }, |
| 52 | { "kansas", "ks" }, |
| 53 | { "kentucky", "ky" }, |
| 54 | { "louisiana", "la" }, |
| 55 | { "maine", "me" }, |
| 56 | { "maryland", "md" }, |
| 57 | { "massachusetts", "ma" }, |
| 58 | { "michigan", "mi" }, |
| 59 | { "minnesota", "mv" }, |
| 60 | { "mississippi", "ms" }, |
| 61 | { "missouri", "mo" }, |
| 62 | { "montana", "mt" }, |
| 63 | { "nebraska", "ne" }, |
| 64 | { "nevada", "nv" }, |
| 65 | { "new hampshire", "nh" }, |
| 66 | { "new jersey", "nj" }, |
| 67 | { "new mexico", "nm" }, |
| 68 | { "new york", "ny" }, |
| 69 | { "north carolina", "nc" }, |
| 70 | { "north dakota", "nd" }, |
| 71 | { "ohio", "oh" }, |
| 72 | { "oklahoma", "ok" }, |
| 73 | { "oregon", "or" }, |
| 74 | { "pennsylvania", "pa" }, |
| 75 | { "puerto rico", "pr" }, |
| 76 | { "rhode island", "ri" }, |
| 77 | { "south carolina", "sc" }, |
| 78 | { "south dakota", "sd" }, |
| 79 | { "tennessee", "tn" }, |
| 80 | { "texas", "tx" }, |
| 81 | { "utah", "ut" }, |
| 82 | { "vermont", "vt" }, |
| 83 | { "virginia", "va" }, |
| 84 | { "washington", "wa" }, |
| 85 | { "west virginia", "wv" }, |
| 86 | { "wisconsin", "wi" }, |
| 87 | { "wyoming", "wy" }, |
| 88 | { NULL, NULL } |
| 89 | }; |
| 90 | |
[email protected] | d5ca8fb | 2013-04-11 17:54:31 | [diff] [blame] | 91 | base::string16 State::Abbreviation(const base::string16& name) { |
[email protected] | 64853b1b | 2012-11-13 08:18:46 | [diff] [blame] | 92 | for (const State* state = all_states; state->name; ++state) { |
| 93 | if (LowerCaseEqualsASCII(name, state->name)) |
| 94 | return ASCIIToUTF16(state->abbreviation); |
| 95 | } |
[email protected] | d5ca8fb | 2013-04-11 17:54:31 | [diff] [blame] | 96 | return base::string16(); |
[email protected] | 64853b1b | 2012-11-13 08:18:46 | [diff] [blame] | 97 | } |
| 98 | |
[email protected] | d5ca8fb | 2013-04-11 17:54:31 | [diff] [blame] | 99 | base::string16 State::FullName(const base::string16& abbreviation) { |
[email protected] | 64853b1b | 2012-11-13 08:18:46 | [diff] [blame] | 100 | for (const State* state = all_states; state->name; ++state) { |
| 101 | if (LowerCaseEqualsASCII(abbreviation, state->abbreviation)) |
| 102 | return ASCIIToUTF16(state->name); |
| 103 | } |
[email protected] | d5ca8fb | 2013-04-11 17:54:31 | [diff] [blame] | 104 | return base::string16(); |
[email protected] | 64853b1b | 2012-11-13 08:18:46 | [diff] [blame] | 105 | } |
| 106 | |
| 107 | const char* const kMonthsAbbreviated[] = { |
| 108 | NULL, // Padding so index 1 = month 1 = January. |
| 109 | "Jan", "Feb", "Mar", "Apr", "May", "Jun", |
| 110 | "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", |
| 111 | }; |
| 112 | |
| 113 | const char* const kMonthsFull[] = { |
| 114 | NULL, // Padding so index 1 = month 1 = January. |
| 115 | "January", "February", "March", "April", "May", "June", |
| 116 | "July", "August", "September", "October", "November", "December", |
| 117 | }; |
| 118 | |
| 119 | const char* const kMonthsNumeric[] = { |
| 120 | NULL, // Padding so index 1 = month 1 = January. |
| 121 | "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", |
| 122 | }; |
| 123 | |
| 124 | // Returns true if the value was successfully set, meaning |value| was found in |
| 125 | // the list of select options in |field|. |
[email protected] | d5ca8fb | 2013-04-11 17:54:31 | [diff] [blame] | 126 | bool SetSelectControlValue(const base::string16& value, |
[email protected] | 64853b1b | 2012-11-13 08:18:46 | [diff] [blame] | 127 | FormFieldData* field) { |
[email protected] | d5ca8fb | 2013-04-11 17:54:31 | [diff] [blame] | 128 | base::string16 value_lowercase = StringToLowerASCII(value); |
[email protected] | 64853b1b | 2012-11-13 08:18:46 | [diff] [blame] | 129 | |
| 130 | DCHECK_EQ(field->option_values.size(), field->option_contents.size()); |
| 131 | for (size_t i = 0; i < field->option_values.size(); ++i) { |
| 132 | if (value_lowercase == StringToLowerASCII(field->option_values[i]) || |
| 133 | value_lowercase == StringToLowerASCII(field->option_contents[i])) { |
| 134 | field->value = field->option_values[i]; |
| 135 | return true; |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | return false; |
| 140 | } |
| 141 | |
[email protected] | d5ca8fb | 2013-04-11 17:54:31 | [diff] [blame] | 142 | bool FillStateSelectControl(const base::string16& value, |
[email protected] | 64853b1b | 2012-11-13 08:18:46 | [diff] [blame] | 143 | FormFieldData* field) { |
[email protected] | d5ca8fb | 2013-04-11 17:54:31 | [diff] [blame] | 144 | base::string16 abbrev, full; |
[email protected] | 64853b1b | 2012-11-13 08:18:46 | [diff] [blame] | 145 | if (value.size() < 4U) { |
| 146 | abbrev = value; |
| 147 | full = State::FullName(value); |
| 148 | } else { |
| 149 | abbrev = State::Abbreviation(value); |
| 150 | full = value; |
| 151 | } |
| 152 | |
| 153 | // Try the abbreviation name first. |
| 154 | if (!abbrev.empty() && SetSelectControlValue(abbrev, field)) |
| 155 | return true; |
| 156 | |
| 157 | if (full.empty()) |
| 158 | return false; |
| 159 | |
| 160 | return SetSelectControlValue(full, field); |
| 161 | } |
| 162 | |
[email protected] | d5ca8fb | 2013-04-11 17:54:31 | [diff] [blame] | 163 | bool FillExpirationMonthSelectControl(const base::string16& value, |
[email protected] | 64853b1b | 2012-11-13 08:18:46 | [diff] [blame] | 164 | FormFieldData* field) { |
| 165 | int index = 0; |
| 166 | if (!base::StringToInt(value, &index) || |
| 167 | index <= 0 || |
| 168 | static_cast<size_t>(index) >= arraysize(kMonthsFull)) |
| 169 | return false; |
| 170 | |
| 171 | bool filled = |
| 172 | SetSelectControlValue(ASCIIToUTF16(kMonthsAbbreviated[index]), field) || |
| 173 | SetSelectControlValue(ASCIIToUTF16(kMonthsFull[index]), field) || |
| 174 | SetSelectControlValue(ASCIIToUTF16(kMonthsNumeric[index]), field); |
| 175 | return filled; |
| 176 | } |
| 177 | |
| 178 | // Try to fill a credit card type |value| (Visa, MasterCard, etc.) into the |
| 179 | // given |field|. |
[email protected] | d5ca8fb | 2013-04-11 17:54:31 | [diff] [blame] | 180 | bool FillCreditCardTypeSelectControl(const base::string16& value, |
[email protected] | 64853b1b | 2012-11-13 08:18:46 | [diff] [blame] | 181 | FormFieldData* field) { |
| 182 | // Try stripping off spaces. |
[email protected] | d5ca8fb | 2013-04-11 17:54:31 | [diff] [blame] | 183 | base::string16 value_stripped; |
[email protected] | 64853b1b | 2012-11-13 08:18:46 | [diff] [blame] | 184 | RemoveChars(StringToLowerASCII(value), kWhitespaceUTF16, &value_stripped); |
| 185 | |
| 186 | for (size_t i = 0; i < field->option_values.size(); ++i) { |
[email protected] | d5ca8fb | 2013-04-11 17:54:31 | [diff] [blame] | 187 | base::string16 option_value_lowercase; |
[email protected] | 64853b1b | 2012-11-13 08:18:46 | [diff] [blame] | 188 | RemoveChars(StringToLowerASCII(field->option_values[i]), kWhitespaceUTF16, |
| 189 | &option_value_lowercase); |
[email protected] | d5ca8fb | 2013-04-11 17:54:31 | [diff] [blame] | 190 | base::string16 option_contents_lowercase; |
[email protected] | 64853b1b | 2012-11-13 08:18:46 | [diff] [blame] | 191 | RemoveChars(StringToLowerASCII(field->option_contents[i]), kWhitespaceUTF16, |
| 192 | &option_contents_lowercase); |
| 193 | |
| 194 | // Perform a case-insensitive comparison; but fill the form with the |
| 195 | // original text, not the lowercased version. |
| 196 | if (value_stripped == option_value_lowercase || |
| 197 | value_stripped == option_contents_lowercase) { |
| 198 | field->value = field->option_values[i]; |
| 199 | return true; |
| 200 | } |
| 201 | } |
| 202 | |
| 203 | // For American Express, also try filling as "AmEx". |
| 204 | if (value == l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_AMEX)) |
| 205 | return FillCreditCardTypeSelectControl(ASCIIToUTF16("AmEx"), field); |
| 206 | |
| 207 | return false; |
| 208 | } |
| 209 | |
| 210 | } // namespace |
| 211 | |
[email protected] | 8da04b85 | 2012-11-16 22:10:39 | [diff] [blame] | 212 | std::string FormGroup::GetGUID() const { |
| 213 | NOTREACHED(); |
| 214 | return std::string(); |
| 215 | } |
| 216 | |
[email protected] | d5ca8fb | 2013-04-11 17:54:31 | [diff] [blame] | 217 | void FormGroup::GetMatchingTypes(const base::string16& text, |
[email protected] | 4d11fcd | 2012-12-05 05:34:48 | [diff] [blame] | 218 | const std::string& app_locale, |
[email protected] | fcfece4 | 2011-07-22 08:29:32 | [diff] [blame] | 219 | FieldTypeSet* matching_types) const { |
| 220 | if (text.empty()) { |
| 221 | matching_types->insert(EMPTY_TYPE); |
| 222 | return; |
| 223 | } |
| 224 | |
| 225 | FieldTypeSet types; |
| 226 | GetSupportedTypes(&types); |
| 227 | for (FieldTypeSet::const_iterator type = types.begin(); |
| 228 | type != types.end(); ++type) { |
| 229 | // TODO(isherman): Matches are case-sensitive for now. Let's keep an eye on |
| 230 | // this and decide whether there are compelling reasons to add case- |
| 231 | // insensitivity. |
[email protected] | 4d11fcd | 2012-12-05 05:34:48 | [diff] [blame] | 232 | if (GetInfo(*type, app_locale) == text) |
[email protected] | fcfece4 | 2011-07-22 08:29:32 | [diff] [blame] | 233 | matching_types->insert(*type); |
| 234 | } |
| 235 | } |
| 236 | |
[email protected] | 4d11fcd | 2012-12-05 05:34:48 | [diff] [blame] | 237 | void FormGroup::GetNonEmptyTypes(const std::string& app_locale, |
| 238 | FieldTypeSet* non_empty_types) const { |
[email protected] | fcfece4 | 2011-07-22 08:29:32 | [diff] [blame] | 239 | FieldTypeSet types; |
| 240 | GetSupportedTypes(&types); |
| 241 | for (FieldTypeSet::const_iterator type = types.begin(); |
| 242 | type != types.end(); ++type) { |
[email protected] | 4d11fcd | 2012-12-05 05:34:48 | [diff] [blame] | 243 | if (!GetInfo(*type, app_locale).empty()) |
[email protected] | fcfece4 | 2011-07-22 08:29:32 | [diff] [blame] | 244 | non_empty_types->insert(*type); |
| 245 | } |
| 246 | } |
| 247 | |
[email protected] | d5ca8fb | 2013-04-11 17:54:31 | [diff] [blame] | 248 | base::string16 FormGroup::GetInfo(AutofillFieldType type, |
[email protected] | 4d11fcd | 2012-12-05 05:34:48 | [diff] [blame] | 249 | const std::string& app_locale) const { |
[email protected] | 6b44b58 | 2012-11-10 06:31:18 | [diff] [blame] | 250 | return GetRawInfo(type); |
[email protected] | fcfece4 | 2011-07-22 08:29:32 | [diff] [blame] | 251 | } |
| 252 | |
[email protected] | 4d11fcd | 2012-12-05 05:34:48 | [diff] [blame] | 253 | bool FormGroup::SetInfo(AutofillFieldType type, |
[email protected] | d5ca8fb | 2013-04-11 17:54:31 | [diff] [blame] | 254 | const base::string16& value, |
[email protected] | 4d11fcd | 2012-12-05 05:34:48 | [diff] [blame] | 255 | const std::string& app_locale) { |
[email protected] | 6b44b58 | 2012-11-10 06:31:18 | [diff] [blame] | 256 | SetRawInfo(type, value); |
[email protected] | fcfece4 | 2011-07-22 08:29:32 | [diff] [blame] | 257 | return true; |
| 258 | } |
[email protected] | 64853b1b | 2012-11-13 08:18:46 | [diff] [blame] | 259 | |
[email protected] | 8da04b85 | 2012-11-16 22:10:39 | [diff] [blame] | 260 | void FormGroup::FillFormField(const AutofillField& field, |
| 261 | size_t variant, |
[email protected] | 0b2f513b | 2013-04-05 20:13:23 | [diff] [blame] | 262 | const std::string& app_locale, |
[email protected] | 8da04b85 | 2012-11-16 22:10:39 | [diff] [blame] | 263 | FormFieldData* field_data) const { |
| 264 | NOTREACHED(); |
| 265 | } |
| 266 | |
[email protected] | 64853b1b | 2012-11-13 08:18:46 | [diff] [blame] | 267 | void FormGroup::FillSelectControl(AutofillFieldType type, |
[email protected] | 0b2f513b | 2013-04-05 20:13:23 | [diff] [blame] | 268 | const std::string& app_locale, |
[email protected] | 64853b1b | 2012-11-13 08:18:46 | [diff] [blame] | 269 | FormFieldData* field) const { |
| 270 | DCHECK(field); |
| 271 | DCHECK_EQ("select-one", field->form_control_type); |
| 272 | DCHECK_EQ(field->option_values.size(), field->option_contents.size()); |
| 273 | |
[email protected] | d5ca8fb | 2013-04-11 17:54:31 | [diff] [blame] | 274 | base::string16 field_text = GetInfo(type, app_locale); |
| 275 | base::string16 field_text_lower = StringToLowerASCII(field_text); |
[email protected] | 64853b1b | 2012-11-13 08:18:46 | [diff] [blame] | 276 | if (field_text.empty()) |
| 277 | return; |
| 278 | |
[email protected] | d5ca8fb | 2013-04-11 17:54:31 | [diff] [blame] | 279 | base::string16 value; |
[email protected] | 64853b1b | 2012-11-13 08:18:46 | [diff] [blame] | 280 | for (size_t i = 0; i < field->option_values.size(); ++i) { |
| 281 | if (field_text == field->option_values[i] || |
| 282 | field_text == field->option_contents[i]) { |
| 283 | // An exact match, use it. |
| 284 | value = field->option_values[i]; |
| 285 | break; |
| 286 | } |
| 287 | |
| 288 | if (field_text_lower == StringToLowerASCII(field->option_values[i]) || |
| 289 | field_text_lower == StringToLowerASCII(field->option_contents[i])) { |
| 290 | // A match, but not in the same case. Save it in case an exact match is |
| 291 | // not found. |
| 292 | value = field->option_values[i]; |
| 293 | } |
| 294 | } |
| 295 | |
| 296 | if (!value.empty()) { |
| 297 | field->value = value; |
| 298 | return; |
| 299 | } |
| 300 | |
| 301 | if (type == ADDRESS_HOME_STATE || type == ADDRESS_BILLING_STATE) { |
| 302 | FillStateSelectControl(field_text, field); |
| 303 | } else if (type == ADDRESS_HOME_COUNTRY || type == ADDRESS_BILLING_COUNTRY) { |
[email protected] | 0b2f513b | 2013-04-05 20:13:23 | [diff] [blame] | 304 | FillCountrySelectControl(app_locale, field); |
[email protected] | 64853b1b | 2012-11-13 08:18:46 | [diff] [blame] | 305 | } else if (type == CREDIT_CARD_EXP_MONTH) { |
| 306 | FillExpirationMonthSelectControl(field_text, field); |
| 307 | } else if (type == CREDIT_CARD_EXP_4_DIGIT_YEAR) { |
| 308 | // Attempt to fill the year as a 2-digit year. This compensates for the |
| 309 | // fact that our heuristics do not always correctly detect when a website |
| 310 | // requests a 2-digit rather than a 4-digit year. |
[email protected] | 0b2f513b | 2013-04-05 20:13:23 | [diff] [blame] | 311 | FillSelectControl(CREDIT_CARD_EXP_2_DIGIT_YEAR, app_locale, field); |
[email protected] | 64853b1b | 2012-11-13 08:18:46 | [diff] [blame] | 312 | } else if (type == CREDIT_CARD_TYPE) { |
| 313 | FillCreditCardTypeSelectControl(field_text, field); |
| 314 | } |
| 315 | } |
| 316 | |
[email protected] | 0b2f513b | 2013-04-05 20:13:23 | [diff] [blame] | 317 | bool FormGroup::FillCountrySelectControl(const std::string& app_locale, |
| 318 | FormFieldData* field_data) const { |
[email protected] | 64853b1b | 2012-11-13 08:18:46 | [diff] [blame] | 319 | return false; |
| 320 | } |
| 321 | |
| 322 | // static |
[email protected] | d5ca8fb | 2013-04-11 17:54:31 | [diff] [blame] | 323 | bool FormGroup::IsValidState(const base::string16& value) { |
[email protected] | 64853b1b | 2012-11-13 08:18:46 | [diff] [blame] | 324 | return !State::Abbreviation(value).empty() || !State::FullName(value).empty(); |
| 325 | } |
[email protected] | e217c563 | 2013-04-12 19:11:48 | [diff] [blame^] | 326 | |
| 327 | } // namespace autofill |