Commit 0067981c authored by thestig's avatar thestig Committed by Commit bot

Autofill: Recognize month/year selects when searching for credit cards.

Also add ccv as yet another abbreviation for the card security code.

BUG=428892,464028,466685

Review URL: https://codereview.chromium.org/1012363002

Cr-Commit-Position: refs/heads/master@{#321469}
parent 90e2bff3
......@@ -21,10 +21,10 @@ UNKNOWN_TYPE | | Employee Number | | _1-default
UNKNOWN_TYPE | discount | % | | _1-default
UNKNOWN_TYPE | discount | % | | _1-default
UNKNOWN_TYPE | | CREDIT CARD | | _1-default
UNKNOWN_TYPE | | Card Number Information | | _1-default
UNKNOWN_TYPE | | | | _1-default
UNKNOWN_TYPE | | | | _1-default
UNKNOWN_TYPE | credit-ccv | Security Code Information | | _1-default
CREDIT_CARD_NUMBER | | Card Number Information | | _1-cc
CREDIT_CARD_EXP_MONTH | | | | _1-cc
CREDIT_CARD_EXP_4_DIGIT_YEAR | | | | _1-cc
CREDIT_CARD_VERIFICATION_CODE | credit-ccv | Security Code Information | | _1-cc
UNKNOWN_TYPE | | Yes, I would like to save my credit card information. | on | _1-default
UNKNOWN_TYPE | | BILL TO | | _1-default
NAME_FIRST | | First Name | | _1-default
......
......@@ -147,7 +147,7 @@ const char kCardNumberRe[] =
"|카드"; // ko-KR
const char kCardCvcRe[] =
"verification|card identification|security code|card code"
"|cvn|cvv|cvc|csc|cvd|cid"
"|cvn|cvv|cvc|csc|cvd|cid|ccv"
"|\\bcid\\b";
const char kCardTypeRe[] =
"(card|cc|payment).?type|payment.?method|card.*brand";
......
......@@ -7,19 +7,48 @@
#include <stddef.h>
#include "base/memory/scoped_ptr.h"
#include "base/stl_util.h"
#include "base/strings/string16.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "components/autofill/core/browser/autofill_field.h"
#include "components/autofill/core/browser/autofill_regex_constants.h"
#include "components/autofill/core/browser/autofill_regexes.h"
#include "components/autofill/core/browser/autofill_scanner.h"
#include "components/autofill/core/browser/field_types.h"
namespace autofill {
namespace {
// Credit card numbers are at most 19 digits in length.
// [Ref: http://en.wikipedia.org/wiki/Bank_card_number]
static const size_t kMaxValidCardNumberSize = 19;
const size_t kMaxValidCardNumberSize = 19;
// Look for the vector |regex_needles| in |haystack|. Returns true if a
// consecutive section of |haystack| matches |regex_needles|.
bool FindConsecutiveStrings(const std::vector<base::string16>& regex_needles,
const std::vector<base::string16>& haystack) {
if (regex_needles.empty() ||
haystack.empty() ||
(haystack.size() < regex_needles.size()))
return false;
for (size_t i = 0; i < haystack.size() - regex_needles.size() + 1; ++i) {
for (size_t j = 0; j < regex_needles.size(); ++j) {
if (!MatchesPattern(haystack[i + j], regex_needles[j]))
break;
if (j == regex_needles.size() - 1)
return true;
}
}
return false;
}
} // namespace
// static
scoped_ptr<FormField> CreditCardField::Parse(AutofillScanner* scanner) {
......@@ -157,6 +186,67 @@ scoped_ptr<FormField> CreditCardField::Parse(AutofillScanner* scanner) {
return nullptr;
}
// static
bool CreditCardField::LikelyCardMonthSelectField(AutofillScanner* scanner) {
if (scanner->IsEnd())
return false;
AutofillField* field = scanner->Cursor();
if (!MatchesFormControlType(field->form_control_type, MATCH_SELECT))
return false;
if (field->option_values.size() < 12 || field->option_values.size() > 13)
return false;
// Filter out years.
const base::string16 kNumericalYearRe =
base::ASCIIToUTF16("[1-9][0-9][0-9][0-9]");
for (const auto& value : field->option_values) {
if (MatchesPattern(value, kNumericalYearRe))
return false;
}
for (const auto& value : field->option_contents) {
if (MatchesPattern(value, kNumericalYearRe))
return false;
}
// Look for numerical months.
const base::string16 kNumericalMonthRe = base::ASCIIToUTF16("12");
if (MatchesPattern(field->option_values.back(), kNumericalMonthRe) ||
MatchesPattern(field->option_contents.back(), kNumericalMonthRe)) {
return true;
}
// Maybe do more matches here. e.g. look for (translated) December.
// Unsure? Return false.
return false;
}
// static
bool CreditCardField::LikelyCardYearSelectField(AutofillScanner* scanner) {
if (scanner->IsEnd())
return false;
AutofillField* field = scanner->Cursor();
if (!MatchesFormControlType(field->form_control_type, MATCH_SELECT))
return false;
const base::Time time_now = base::Time::Now();
base::Time::Exploded time_exploded;
time_now.UTCExplode(&time_exploded);
const int kYearsToMatch = 3;
std::vector<base::string16> years_to_check;
for (int year = time_exploded.year;
year < time_exploded.year + kYearsToMatch;
++year) {
years_to_check.push_back(base::IntToString16(year));
}
return (FindConsecutiveStrings(years_to_check, field->option_values) ||
FindConsecutiveStrings(years_to_check, field->option_contents));
}
CreditCardField::CreditCardField()
: cardholder_(nullptr),
cardholder_last_(nullptr),
......@@ -215,8 +305,24 @@ bool CreditCardField::ParseExpirationDate(AutofillScanner* scanner) {
if (expiration_month_ || expiration_date_)
return false;
// First try to parse split month/year expiration fields.
// First try to parse split month/year expiration fields by looking for a
// pair of select fields that look like month/year.
size_t month_year_saved_cursor = scanner->SaveCursor();
if (LikelyCardMonthSelectField(scanner)) {
expiration_month_ = scanner->Cursor();
scanner->Advance();
if (LikelyCardYearSelectField(scanner)) {
expiration_year_ = scanner->Cursor();
scanner->Advance();
return true;
}
expiration_month_ = nullptr;
expiration_year_ = nullptr;
}
// If that fails, do a general regex search.
scanner->RewindTo(month_year_saved_cursor);
const int kMatchTelAndSelect = MATCH_DEFAULT | MATCH_TELEPHONE | MATCH_SELECT;
if (ParseFieldSpecifics(scanner,
base::UTF8ToUTF16(kExpirationMonthRe),
......
......@@ -31,6 +31,15 @@ class CreditCardField : public FormField {
private:
friend class CreditCardFieldTest;
// Returns true if |scanner| points to a field that looks like a month
// <select>.
static bool LikelyCardMonthSelectField(AutofillScanner* scanner);
// Returns true if |scanner| points to a field that looks like a year
// <select> for a credit card. i.e. it contains the current year and
// the next few years.
static bool LikelyCardYearSelectField(AutofillScanner* scanner);
CreditCardField();
// Parses the expiration month/year/date fields. Returns true if it finds
......
......@@ -83,6 +83,9 @@ class FormField {
ServerFieldType type,
ServerFieldTypeMap* map);
// Returns true iff |type| matches |match_type|.
static bool MatchesFormControlType(const std::string& type, int match_type);
// Derived classes must implement this interface to supply field type
// information. |ParseFormFields| coordinates the parsing and extraction
// of types from an input vector of |AutofillField| objects and delegates
......@@ -120,9 +123,6 @@ class FormField {
std::vector<AutofillField*>* fields,
ServerFieldTypeMap* map);
// Returns true iff |type| matches |match_type|.
static bool MatchesFormControlType(const std::string& type, int match_type);
DISALLOW_COPY_AND_ASSIGN(FormField);
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment