blob: 82031f0afd3f19e931de5dd7b65d9b6b50902ef9 [file] [log] [blame]
// Copyright (c) 2011 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 "components/autofill/browser/form_group.h"
#include <algorithm>
#include <iterator>
#include "base/basictypes.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/utf_string_conversions.h"
#include "components/autofill/browser/autofill_country.h"
#include "components/autofill/common/form_field_data.h"
#include "grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"
namespace autofill {
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 base::string16 Abbreviation(const base::string16& name);
static base::string16 FullName(const base::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 }
};
base::string16 State::Abbreviation(const base::string16& name) {
for (const State* state = all_states; state->name; ++state) {
if (LowerCaseEqualsASCII(name, state->name))
return ASCIIToUTF16(state->abbreviation);
}
return base::string16();
}
base::string16 State::FullName(const base::string16& abbreviation) {
for (const State* state = all_states; state->name; ++state) {
if (LowerCaseEqualsASCII(abbreviation, state->abbreviation))
return ASCIIToUTF16(state->name);
}
return base::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",
};
const char* const kMonthsNumeric[] = {
NULL, // Padding so index 1 = month 1 = January.
"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
};
// Returns true if the value was successfully set, meaning |value| was found in
// the list of select options in |field|.
bool SetSelectControlValue(const base::string16& value,
FormFieldData* field) {
base::string16 value_lowercase = StringToLowerASCII(value);
DCHECK_EQ(field->option_values.size(), field->option_contents.size());
for (size_t i = 0; i < field->option_values.size(); ++i) {
if (value_lowercase == StringToLowerASCII(field->option_values[i]) ||
value_lowercase == StringToLowerASCII(field->option_contents[i])) {
field->value = field->option_values[i];
return true;
}
}
return false;
}
bool FillStateSelectControl(const base::string16& value,
FormFieldData* field) {
base::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 FillExpirationMonthSelectControl(const base::string16& value,
FormFieldData* field) {
int index = 0;
if (!base::StringToInt(value, &index) ||
index <= 0 ||
static_cast<size_t>(index) >= arraysize(kMonthsFull))
return false;
bool filled =
SetSelectControlValue(ASCIIToUTF16(kMonthsAbbreviated[index]), field) ||
SetSelectControlValue(ASCIIToUTF16(kMonthsFull[index]), field) ||
SetSelectControlValue(ASCIIToUTF16(kMonthsNumeric[index]), field);
return filled;
}
// Try to fill a credit card type |value| (Visa, MasterCard, etc.) into the
// given |field|.
bool FillCreditCardTypeSelectControl(const base::string16& value,
FormFieldData* field) {
// Try stripping off spaces.
base::string16 value_stripped;
RemoveChars(StringToLowerASCII(value), kWhitespaceUTF16, &value_stripped);
for (size_t i = 0; i < field->option_values.size(); ++i) {
base::string16 option_value_lowercase;
RemoveChars(StringToLowerASCII(field->option_values[i]), kWhitespaceUTF16,
&option_value_lowercase);
base::string16 option_contents_lowercase;
RemoveChars(StringToLowerASCII(field->option_contents[i]), kWhitespaceUTF16,
&option_contents_lowercase);
// Perform a case-insensitive comparison; but fill the form with the
// original text, not the lowercased version.
if (value_stripped == option_value_lowercase ||
value_stripped == option_contents_lowercase) {
field->value = field->option_values[i];
return true;
}
}
// For American Express, also try filling as "AmEx".
if (value == l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_AMEX))
return FillCreditCardTypeSelectControl(ASCIIToUTF16("AmEx"), field);
return false;
}
} // namespace
std::string FormGroup::GetGUID() const {
NOTREACHED();
return std::string();
}
void FormGroup::GetMatchingTypes(const base::string16& text,
const std::string& app_locale,
FieldTypeSet* matching_types) const {
if (text.empty()) {
matching_types->insert(EMPTY_TYPE);
return;
}
FieldTypeSet types;
GetSupportedTypes(&types);
for (FieldTypeSet::const_iterator type = types.begin();
type != types.end(); ++type) {
// TODO(isherman): Matches are case-sensitive for now. Let's keep an eye on
// this and decide whether there are compelling reasons to add case-
// insensitivity.
if (GetInfo(*type, app_locale) == text)
matching_types->insert(*type);
}
}
void FormGroup::GetNonEmptyTypes(const std::string& app_locale,
FieldTypeSet* non_empty_types) const {
FieldTypeSet types;
GetSupportedTypes(&types);
for (FieldTypeSet::const_iterator type = types.begin();
type != types.end(); ++type) {
if (!GetInfo(*type, app_locale).empty())
non_empty_types->insert(*type);
}
}
base::string16 FormGroup::GetInfo(AutofillFieldType type,
const std::string& app_locale) const {
return GetRawInfo(type);
}
bool FormGroup::SetInfo(AutofillFieldType type,
const base::string16& value,
const std::string& app_locale) {
SetRawInfo(type, value);
return true;
}
void FormGroup::FillFormField(const AutofillField& field,
size_t variant,
const std::string& app_locale,
FormFieldData* field_data) const {
NOTREACHED();
}
void FormGroup::FillSelectControl(AutofillFieldType type,
const std::string& app_locale,
FormFieldData* field) const {
DCHECK(field);
DCHECK_EQ("select-one", field->form_control_type);
DCHECK_EQ(field->option_values.size(), field->option_contents.size());
base::string16 field_text = GetInfo(type, app_locale);
base::string16 field_text_lower = StringToLowerASCII(field_text);
if (field_text.empty())
return;
base::string16 value;
for (size_t i = 0; i < field->option_values.size(); ++i) {
if (field_text == field->option_values[i] ||
field_text == field->option_contents[i]) {
// An exact match, use it.
value = field->option_values[i];
break;
}
if (field_text_lower == StringToLowerASCII(field->option_values[i]) ||
field_text_lower == StringToLowerASCII(field->option_contents[i])) {
// A match, but not in the same case. Save it in case an exact match is
// not found.
value = field->option_values[i];
}
}
if (!value.empty()) {
field->value = value;
return;
}
if (type == ADDRESS_HOME_STATE || type == ADDRESS_BILLING_STATE) {
FillStateSelectControl(field_text, field);
} else if (type == ADDRESS_HOME_COUNTRY || type == ADDRESS_BILLING_COUNTRY) {
FillCountrySelectControl(app_locale, field);
} else if (type == CREDIT_CARD_EXP_MONTH) {
FillExpirationMonthSelectControl(field_text, field);
} else if (type == CREDIT_CARD_EXP_4_DIGIT_YEAR) {
// Attempt to fill the year as a 2-digit year. This compensates for the
// fact that our heuristics do not always correctly detect when a website
// requests a 2-digit rather than a 4-digit year.
FillSelectControl(CREDIT_CARD_EXP_2_DIGIT_YEAR, app_locale, field);
} else if (type == CREDIT_CARD_TYPE) {
FillCreditCardTypeSelectControl(field_text, field);
}
}
bool FormGroup::FillCountrySelectControl(const std::string& app_locale,
FormFieldData* field_data) const {
return false;
}
// static
bool FormGroup::IsValidState(const base::string16& value) {
return !State::Abbreviation(value).empty() || !State::FullName(value).empty();
}
} // namespace autofill