// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/autofill/select_control_handler.h" #include #include "base/basictypes.h" #include "base/logging.h" #include "base/string_number_conversions.h" #include "base/string16.h" #include "base/utf_string_conversions.h" #include "chrome/browser/autofill/form_group.h" #include "webkit/glue/form_field.h" namespace { // TODO(jhawkins): Add more states/provinces. See http://crbug.com/45039. class State { public: const char* name; const char* abbreviation; static const State all_states[]; static string16 Abbreviation(const string16& name); static string16 FullName(const string16& abbreviation); }; const State State::all_states[] = { { "alabama", "al" }, { "alaska", "ak" }, { "arizona", "az" }, { "arkansas", "ar" }, { "california", "ca" }, { "colorado", "co" }, { "connecticut", "ct" }, { "delaware", "de" }, { "district of columbia", "dc" }, { "florida", "fl" }, { "georgia", "ga" }, { "hawaii", "hi" }, { "idaho", "id" }, { "illinois", "il" }, { "indiana", "in" }, { "iowa", "ia" }, { "kansas", "ks" }, { "kentucky", "ky" }, { "louisiana", "la" }, { "maine", "me" }, { "maryland", "md" }, { "massachusetts", "ma" }, { "michigan", "mi" }, { "minnesota", "mv" }, { "mississippi", "ms" }, { "missouri", "mo" }, { "montana", "mt" }, { "nebraska", "ne" }, { "nevada", "nv" }, { "new hampshire", "nh" }, { "new jersey", "nj" }, { "new mexico", "nm" }, { "new york", "ny" }, { "north carolina", "nc" }, { "north dakota", "nd" }, { "ohio", "oh" }, { "oklahoma", "ok" }, { "oregon", "or" }, { "pennsylvania", "pa" }, { "puerto rico", "pr" }, { "rhode island", "ri" }, { "south carolina", "sc" }, { "south dakota", "sd" }, { "tennessee", "tn" }, { "texas", "tx" }, { "utah", "ut" }, { "vermont", "vt" }, { "virginia", "va" }, { "washington", "wa" }, { "west Virginia", "wv" }, { "wisconsin", "wi" }, { "wyoming", "wy" }, { NULL, NULL } }; string16 State::Abbreviation(const string16& name) { for (const State *s = all_states ; s->name ; ++s) if (LowerCaseEqualsASCII(name, s->name)) return ASCIIToUTF16(s->abbreviation); return string16(); } string16 State::FullName(const string16& abbreviation) { for (const State *s = all_states ; s->name ; ++s) if (LowerCaseEqualsASCII(abbreviation, s->abbreviation)) return ASCIIToUTF16(s->name); return string16(); } // Holds valid country names, their 2 letter abbreviation, and maps from // the former to the latter class Country { public: const char* name; const char* abbreviation; static const Country all_countries[]; static string16 Abbreviation(const string16 &name); static string16 FullName(const string16& abbreviation); }; // A list of all English country names and code elements. ISO 3166. const Country Country::all_countries[] = { { "united states", "us" }, { "afghanistan", "af" }, { "aland islands", "ax" }, { "albania", "al" }, { "algeria", "dz" }, { "american samoa", "as" }, { "andorra", "ad" }, { "angola", "ao" }, { "anguilla", "ai" }, { "antarctica", "aq" }, { "antigua and barbuda", "ag" }, { "argentina", "ar" }, { "armenia", "am" }, { "aruba", "aw" }, { "australia", "au" }, { "austria", "at" }, { "azerbaijan", "az" }, { "bahamas", "bs" }, { "bahrain", "bh" }, { "bangladesh", "bd" }, { "barbados", "bb" }, { "belarus", "by" }, { "belgium", "be" }, { "belize", "bz" }, { "benin", "bj" }, { "bermuda", "bm" }, { "bhutan", "bt" }, { "bolivia", "bo" }, { "bosnia and herzegovina", "ba" }, { "botswana", "bw" }, { "bouvet island", "bv" }, { "brazil", "br" }, { "british indian ocean territory", "io" }, { "brunei darussalam", "bn" }, { "bulgaria", "bg" }, { "burkina faso", "bf" }, { "burundi", "bi" }, { "cambodia", "kh" }, { "cameroon", "cm" }, { "canada", "ca" }, { "cape verde", "cv" }, { "cayman islands", "ky" }, { "central african republic", "cf" }, { "chad", "td" }, { "chile", "cl" }, { "china", "cn" }, { "christmas island", "cx" }, { "cocos (keeling) islands", "cc" }, { "colombia", "co" }, { "comoros", "km" }, { "congo", "cg" }, { "congo (dem. rep.)", "cd" }, { "cook islands", "ck" }, { "costa rica", "cr" }, { "cote d'ivoire", "ci" }, { "croatia", "hr" }, { "cuba", "cu" }, { "cyprus", "cy" }, { "czech republic", "cz" }, { "denmark", "dk" }, { "djibouti", "dj" }, { "dominica", "dm" }, { "dominican republic", "do" }, { "ecuador", "ec" }, { "egypt", "eg" }, { "el salvador", "sv" }, { "equatorial guinea", "gq" }, { "eritrea", "er" }, { "estonia", "ee" }, { "ethiopia", "et" }, { "falkland islands (malvinas)", "fk" }, { "faroe islands", "fo" }, { "fiji", "fj" }, { "finland", "fi" }, { "france", "fr" }, { "french guiana", "gf" }, { "french polynesia", "pf" }, { "gabon", "ga" }, { "gambia", "gm" }, { "georgia", "ge" }, { "germany", "de" }, { "ghana", "gh" }, { "gibraltar", "gi" }, { "greece", "gr" }, { "greenland", "gl" }, { "grenada", "gd" }, { "guadeloupe", "gp" }, { "guam", "gu" }, { "guatemala", "gt" }, { "guernsey", "cg" }, { "guinea", "gn" }, { "guinea-bissau", "gw" }, { "guyana", "gy" }, { "haiti", "ht" }, { "heard island and mcdonald islands", "hm" }, { "holy see (vatica city state)", "va" }, { "honduras", "hn" }, { "hong kong", "hk" }, { "hungary", "hu" }, { "iceland", "is" }, { "india", "in" }, { "indonesia", "id" }, { "iran", "ir" }, { "iraq", "iq" }, { "ireland", "ie" }, { "isle of man", "im" }, { "israel", "il" }, { "italy", "it" }, { "ivory coast", "ci" }, { "jamaica", "jm" }, { "japan", "jp" }, { "jersey", "je" }, { "jordan", "jo" }, { "kazakhstan", "kz" }, { "kenya", "ke" }, { "kiribati", "ki" }, { "korea (north)", "kp" }, { "korea (south)", "kr" }, { "kuwait", "kw" }, { "kyrgyzstan", "kg" }, { "laos", "la" }, { "latvia", "lv" }, { "lebanon", "lb" }, { "lesotho", "ls" }, { "liberia", "lr" }, { "libya", "ly" }, { "liechtenstein", "li" }, { "lithuania", "lt" }, { "luxembourg", "lu" }, { "macao", "mo" }, { "macedonia", "mk" }, { "madagascar", "mg" }, { "malawi", "mw" }, { "malaysia", "my" }, { "maldives", "mv" }, { "mali", "ml" }, { "malta", "mt" }, { "marshall islands", "mh" }, { "martinique", "mq" }, { "mauritania", "mr" }, { "mauritius", "mu" }, { "mayotte", "yt" }, { "mexico", "mx" }, { "micronesia", "fm" }, { "moldova", "md" }, { "monaco", "mc" }, { "mongolia", "mn" }, { "montserrat", "ms" }, { "morocco", "ma" }, { "mozambique", "mz" }, { "myanmar", "mm" }, { "namibia", "na" }, { "nepal", "np" }, { "netherlands", "nl" }, { "netherlands antilles", "an" }, { "new caledonia", "nc" }, { "new zealand", "nz" }, { "nicaragua", "ni" }, { "niger", "ne" }, { "nigeria", "ng" }, { "niue", "nu" }, { "norfolk island", "nf" }, { "northern mariana islands", "mp" }, { "norway", "no" }, { "oman", "om" }, { "pakistan", "pk" }, { "palau", "pw" }, { "palestine", "ps" }, { "panama", "pa" }, { "papua new guinea", "pg" }, { "paraguay", "py" }, { "peru", "pe" }, { "philippines", "ph" }, { "pitcairn", "pn" }, { "poland", "pl" }, { "portugal", "pt" }, { "puerto rico", "pr" }, { "qatar", "qa" }, { "reunion", "re" }, { "romania", "ro" }, { "russia", "ru" }, { "rwanda", "rw" }, { "saint helena", "sh" }, { "saint kitts and nevis", "kn" }, { "saint lucia", "lc" }, { "saint pierre and miquelon", "pm" }, { "saint vincent and the grenadines", "vc" }, { "samoa", "ws" }, { "san marino", "sm" }, { "sao tome and principe", "st" }, { "saudi arabia", "sa" }, { "senegal", "sn" }, { "serbia and montenegro", "cs" }, { "seychelles", "sc" }, { "sierra leone", "sl" }, { "singapore", "sg" }, { "slovakia", "sk" }, { "slovenia", "si" }, { "solomon islands", "sb" }, { "somalia", "so" }, { "south africa", "za" }, { "south georgia and the south sandwich islands", "gs" }, { "spain", "es" }, { "sri lanka", "lk" }, { "sudan", "sd" }, { "suriname", "sr" }, { "svalbard and jan mayen", "sj" }, { "swaziland", "sz" }, { "sweden", "se" }, { "switzerland", "ch" }, { "syria", "sy" }, { "taiwan", "tw" }, { "tajikistan", "tj" }, { "tanzania", "tz" }, { "thailand", "th" }, { "timor-leste", "tl" }, { "togo", "tg" }, { "tokelau", "tk" }, { "tonga", "to" }, { "trinidad and tobago", "tt" }, { "tunisia", "tn" }, { "turkey", "tr" }, { "turkmenistan", "tm" }, { "turks and caicos islands", "tc" }, { "tuvalu", "tv" }, { "uganda", "ug" }, { "ukraine", "ua" }, { "united arab emirates", "ae" }, { "united kingdom", "gb" }, { "u.s. minor outlying islands", "um" }, { "uruguay", "uy" }, { "uzbekistan", "uz" }, { "vanuatu", "vu" }, { "venezuela", "ve" }, { "vietnam", "vn" }, { "virgin islands, british", "vg" }, { "virgin islands, u.s.", "vi" }, { "wallis and futuna", "wf" }, { "western sahara", "eh" }, { "yemen", "ye" }, { "zambia", "zm" }, { "zimbabwe", "zw" }, { NULL, NULL } }; string16 Country::Abbreviation(const string16& name) { for (const Country *s = all_countries ; s->name ; ++s) if (LowerCaseEqualsASCII(name, s->name)) return ASCIIToUTF16(s->abbreviation); return string16(); } string16 Country::FullName(const string16& abbreviation) { for (const Country *s = all_countries ; s->name ; ++s) if (LowerCaseEqualsASCII(abbreviation, s->abbreviation)) return ASCIIToUTF16(s->name); return string16(); } const char* const kMonthsAbbreviated[] = { NULL, // Padding so index 1 = month 1 = January. "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", }; const char* const kMonthsFull[] = { NULL, // Padding so index 1 = month 1 = January. "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", }; // Returns true if the value was successfully set, meaning |value| was found in // the list of select options in |field|. bool SetSelectControlValue(const string16& value, webkit_glue::FormField* field) { string16 value_lowercase = StringToLowerASCII(value); for (std::vector::const_iterator iter = field->option_strings().begin(); iter != field->option_strings().end(); ++iter) { if (value_lowercase == StringToLowerASCII(*iter)) { field->set_value(*iter); return true; } } return false; } bool FillStateSelectControl(const string16& value, webkit_glue::FormField* field) { if (value.empty()) return false; string16 abbrev, full; if (value.size() < 4U) { abbrev = value; full = State::FullName(value); } else { abbrev = State::Abbreviation(value); full = value; } // Try the abbreviation name first. if (!abbrev.empty() && SetSelectControlValue(abbrev, field)) return true; if (full.empty()) return false; return SetSelectControlValue(full, field); } bool FillCountrySelectControl(const string16& value, webkit_glue::FormField* field) { if (value.empty()) return false; string16 abbrev, full; if (value.size() < 4U) { abbrev = value; full = Country::FullName(value); } else { abbrev = Country::Abbreviation(value); full = value; } // Try the abbreviation name first. if (!abbrev.empty() && SetSelectControlValue(abbrev, field)) return true; if (full.empty()) return false; return SetSelectControlValue(full, field); } bool FillExpirationMonthSelectControl(const string16& value, webkit_glue::FormField* field) { if (value.empty()) return false; if (SetSelectControlValue(value, field)) return true; int index = 0; if (!base::StringToInt(value, &index) || index <= 0 || static_cast(index) >= arraysize(kMonthsFull)) return false; bool filled = SetSelectControlValue(ASCIIToUTF16(kMonthsAbbreviated[index]), field) || SetSelectControlValue(ASCIIToUTF16(kMonthsFull[index]), field); return filled; } } // namespace namespace autofill { void FillSelectControl(const FormGroup* form_group, AutoFillType type, webkit_glue::FormField* field) { DCHECK(form_group); DCHECK(field); DCHECK(field->form_control_type() == ASCIIToUTF16("select-one")); string16 value; string16 field_text = form_group->GetFieldText(type); for (size_t i = 0; i < field->option_strings().size(); ++i) { if (field_text == field->option_strings()[i]) { // An exact match, use it. value = field_text; break; } if (StringToLowerASCII(field->option_strings()[i]) == StringToLowerASCII(field_text)) { // A match, but not in the same case. Save it in case an exact match is // not found. value = field->option_strings()[i]; } } if (!value.empty()) { field->set_value(value); return; } if (type.field_type() == ADDRESS_HOME_STATE || type.field_type() == ADDRESS_BILLING_STATE) { FillStateSelectControl(field_text, field); } else if (type.field_type() == ADDRESS_HOME_COUNTRY || type.field_type() == ADDRESS_BILLING_COUNTRY) { FillCountrySelectControl(field_text, field); } else if (type.field_type() == CREDIT_CARD_EXP_MONTH) { FillExpirationMonthSelectControl(field_text, field); } return; } } // namespace autofill