1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/autofill/core/browser/credit_card_field.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/stl_util.h"
11 #include "base/strings/string16.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/time/time.h"
16 #include "components/autofill/core/browser/autofill_field.h"
17 #include "components/autofill/core/browser/autofill_regex_constants.h"
18 #include "components/autofill/core/browser/autofill_scanner.h"
19 #include "components/autofill/core/browser/field_types.h"
20 #include "components/autofill/core/common/autofill_regexes.h"
21 #include "grit/components_strings.h"
22 #include "ui/base/l10n/l10n_util.h"
28 // Credit card numbers are at most 19 digits in length.
29 // [Ref: http://en.wikipedia.org/wiki/Bank_card_number]
30 const size_t kMaxValidCardNumberSize
= 19;
32 // Look for the vector |regex_needles| in |haystack|. Returns true if a
33 // consecutive section of |haystack| matches |regex_needles|.
34 bool FindConsecutiveStrings(const std::vector
<base::string16
>& regex_needles
,
35 const std::vector
<base::string16
>& haystack
) {
36 if (regex_needles
.empty() ||
38 (haystack
.size() < regex_needles
.size()))
41 for (size_t i
= 0; i
< haystack
.size() - regex_needles
.size() + 1; ++i
) {
42 for (size_t j
= 0; j
< regex_needles
.size(); ++j
) {
43 if (!MatchesPattern(haystack
[i
+ j
], regex_needles
[j
]))
46 if (j
== regex_needles
.size() - 1)
53 // Returns true if a field that has |max_length| can fit the data for a field of
55 bool FieldCanFitDataForFieldType(int max_length
, ServerFieldType type
) {
60 case CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR
: {
61 static int kMinimum2YearCcExpLength
= strlen("12/14");
62 return max_length
>= kMinimum2YearCcExpLength
;
64 case CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR
: {
65 static int kMinimum4YearCcExpLength
= strlen("12/2014");
66 return max_length
>= kMinimum4YearCcExpLength
;
78 scoped_ptr
<FormField
> CreditCardField::Parse(AutofillScanner
* scanner
) {
82 scoped_ptr
<CreditCardField
> credit_card_field(new CreditCardField
);
83 size_t saved_cursor
= scanner
->SaveCursor();
85 // Credit card fields can appear in many different orders.
86 // We loop until no more credit card related fields are found, see |break| at
87 // the bottom of the loop.
88 for (int fields
= 0; !scanner
->IsEnd(); ++fields
) {
89 // Ignore gift card fields.
90 if (IsGiftCardField(scanner
))
93 if (!credit_card_field
->cardholder_
) {
94 if (ParseField(scanner
,
95 base::UTF8ToUTF16(kNameOnCardRe
),
96 &credit_card_field
->cardholder_
)) {
100 // Sometimes the cardholder field is just labeled "name". Unfortunately
101 // this is a dangerously generic word to search for, since it will often
102 // match a name (not cardholder name) field before or after credit card
103 // fields. So we search for "name" only when we've already parsed at
104 // least one other credit card field and haven't yet parsed the
105 // expiration date (which usually appears at the end).
107 !credit_card_field
->expiration_month_
&&
109 base::UTF8ToUTF16(kNameOnCardContextualRe
),
110 &credit_card_field
->cardholder_
)) {
115 // Check for a credit card type (Visa, MasterCard, etc.) field.
116 // All CC type fields encountered so far have been of type select.
117 if (!credit_card_field
->type_
&& LikelyCardTypeSelectField(scanner
)) {
118 credit_card_field
->type_
= scanner
->Cursor();
123 // We look for a card security code before we look for a credit card number
124 // and match the general term "number". The security code has a plethora of
125 // names; we've seen "verification #", "verification number", "card
126 // identification number", and others listed in the regex pattern used
128 // Note: Some sites use type="tel" or type="number" for numerical inputs.
129 const int kMatchNumAndTel
= MATCH_DEFAULT
| MATCH_NUMBER
| MATCH_TELEPHONE
;
130 if (!credit_card_field
->verification_
&&
131 ParseFieldSpecifics(scanner
,
132 base::UTF8ToUTF16(kCardCvcRe
),
133 kMatchNumAndTel
| MATCH_PASSWORD
,
134 &credit_card_field
->verification_
)) {
138 AutofillField
* current_number_field
;
139 if (ParseFieldSpecifics(scanner
,
140 base::UTF8ToUTF16(kCardNumberRe
),
142 ¤t_number_field
)) {
143 // Avoid autofilling any credit card number field having very low or high
144 // |start_index| on the HTML form.
145 size_t start_index
= 0;
146 if (!credit_card_field
->numbers_
.empty()) {
147 size_t last_number_field_size
=
148 credit_card_field
->numbers_
.back()->credit_card_number_offset() +
149 credit_card_field
->numbers_
.back()->max_length
;
151 // Distinguish between
152 // (a) one card split across multiple fields
153 // (b) multiple fields for multiple cards
154 // Treat this field as a part of the same card as the last field, except
155 // when doing so would cause overflow.
156 if (last_number_field_size
< kMaxValidCardNumberSize
)
157 start_index
= last_number_field_size
;
160 current_number_field
->set_credit_card_number_offset(start_index
);
161 credit_card_field
->numbers_
.push_back(current_number_field
);
165 if (credit_card_field
->ParseExpirationDate(scanner
))
168 if (credit_card_field
->expiration_month_
&&
169 !credit_card_field
->expiration_year_
&&
170 !credit_card_field
->expiration_date_
) {
171 // Parsed a month but couldn't parse a year; give up.
172 scanner
->RewindTo(saved_cursor
);
179 // Some pages have a billing address field after the cardholder name field.
180 // For that case, allow only just the cardholder name field. The remaining
181 // CC fields will be picked up in a following CreditCardField.
182 if (credit_card_field
->cardholder_
)
183 return credit_card_field
.Pass();
185 // On some pages, the user selects a card type using radio buttons
186 // (e.g. test page Apple Store Billing.html). We can't handle that yet,
187 // so we treat the card type as optional for now.
188 // The existence of a number or cvc in combination with expiration date is
189 // a strong enough signal that this is a credit card. It is possible that
190 // the number and name were parsed in a separate part of the form. So if
191 // the cvc and date were found independently they are returned.
192 bool has_cc_number_or_verification
= (credit_card_field
->verification_
||
193 !credit_card_field
->numbers_
.empty());
194 bool has_date_or_mm_yy
= (credit_card_field
->expiration_date_
||
195 (credit_card_field
->expiration_month_
&&
196 credit_card_field
->expiration_year_
));
197 if (has_cc_number_or_verification
&& has_date_or_mm_yy
)
198 return credit_card_field
.Pass();
200 scanner
->RewindTo(saved_cursor
);
205 bool CreditCardField::LikelyCardMonthSelectField(AutofillScanner
* scanner
) {
206 if (scanner
->IsEnd())
209 AutofillField
* field
= scanner
->Cursor();
210 if (!MatchesFormControlType(field
->form_control_type
, MATCH_SELECT
))
213 if (field
->option_values
.size() < 12 || field
->option_values
.size() > 13)
217 const base::string16 kNumericalYearRe
=
218 base::ASCIIToUTF16("[1-9][0-9][0-9][0-9]");
219 for (const auto& value
: field
->option_values
) {
220 if (MatchesPattern(value
, kNumericalYearRe
))
223 for (const auto& value
: field
->option_contents
) {
224 if (MatchesPattern(value
, kNumericalYearRe
))
228 // Look for numerical months.
229 const base::string16 kNumericalMonthRe
= base::ASCIIToUTF16("12");
230 if (MatchesPattern(field
->option_values
.back(), kNumericalMonthRe
) ||
231 MatchesPattern(field
->option_contents
.back(), kNumericalMonthRe
)) {
235 // Maybe do more matches here. e.g. look for (translated) December.
237 // Unsure? Return false.
242 bool CreditCardField::LikelyCardYearSelectField(AutofillScanner
* scanner
) {
243 if (scanner
->IsEnd())
246 AutofillField
* field
= scanner
->Cursor();
247 if (!MatchesFormControlType(field
->form_control_type
, MATCH_SELECT
))
250 const base::Time time_now
= base::Time::Now();
251 base::Time::Exploded time_exploded
;
252 time_now
.UTCExplode(&time_exploded
);
254 const int kYearsToMatch
= 3;
255 std::vector
<base::string16
> years_to_check
;
256 for (int year
= time_exploded
.year
;
257 year
< time_exploded
.year
+ kYearsToMatch
;
259 years_to_check
.push_back(base::IntToString16(year
));
261 return (FindConsecutiveStrings(years_to_check
, field
->option_values
) ||
262 FindConsecutiveStrings(years_to_check
, field
->option_contents
));
266 bool CreditCardField::LikelyCardTypeSelectField(AutofillScanner
* scanner
) {
267 if (scanner
->IsEnd())
270 AutofillField
* field
= scanner
->Cursor();
271 if (!MatchesFormControlType(field
->form_control_type
, MATCH_SELECT
))
274 return AutofillField::FindValueInSelectControl(
275 *field
, l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_VISA
),
277 AutofillField::FindValueInSelectControl(
278 *field
, l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_MASTERCARD
),
283 bool CreditCardField::IsGiftCardField(AutofillScanner
* scanner
) {
284 if (scanner
->IsEnd())
287 size_t saved_cursor
= scanner
->SaveCursor();
288 if (ParseField(scanner
, base::UTF8ToUTF16(kDebitCardRe
), nullptr)) {
289 scanner
->RewindTo(saved_cursor
);
292 if (ParseField(scanner
, base::UTF8ToUTF16(kDebitGiftCardRe
), nullptr)) {
293 scanner
->RewindTo(saved_cursor
);
297 return ParseField(scanner
, base::UTF8ToUTF16(kGiftCardRe
), nullptr);
300 CreditCardField::CreditCardField()
301 : cardholder_(nullptr),
302 cardholder_last_(nullptr),
304 verification_(nullptr),
305 expiration_month_(nullptr),
306 expiration_year_(nullptr),
307 expiration_date_(nullptr),
308 exp_year_type_(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR
) {
311 CreditCardField::~CreditCardField() {
314 bool CreditCardField::ClassifyField(ServerFieldTypeMap
* map
) const {
316 for (size_t index
= 0; index
< numbers_
.size(); ++index
) {
317 ok
= ok
&& AddClassification(numbers_
[index
], CREDIT_CARD_NUMBER
, map
);
320 ok
= ok
&& AddClassification(type_
, CREDIT_CARD_TYPE
, map
);
322 AddClassification(verification_
, CREDIT_CARD_VERIFICATION_CODE
, map
);
324 // If the heuristics detected first and last name in separate fields,
325 // then ignore both fields. Putting them into separate fields is probably
326 // wrong, because the credit card can also contain a middle name or middle
328 if (cardholder_last_
== nullptr)
329 ok
= ok
&& AddClassification(cardholder_
, CREDIT_CARD_NAME
, map
);
331 if (expiration_date_
) {
332 DCHECK(!expiration_month_
);
333 DCHECK(!expiration_year_
);
335 ok
&& AddClassification(expiration_date_
, GetExpirationYearType(), map
);
337 ok
= ok
&& AddClassification(expiration_month_
, CREDIT_CARD_EXP_MONTH
, map
);
339 ok
&& AddClassification(expiration_year_
, GetExpirationYearType(), map
);
345 bool CreditCardField::ParseExpirationDate(AutofillScanner
* scanner
) {
346 if (!expiration_date_
&&
347 base::LowerCaseEqualsASCII(scanner
->Cursor()->form_control_type
,
349 expiration_date_
= scanner
->Cursor();
350 expiration_month_
= nullptr;
351 expiration_year_
= nullptr;
356 if (expiration_month_
|| expiration_date_
)
359 // First try to parse split month/year expiration fields by looking for a
360 // pair of select fields that look like month/year.
361 size_t month_year_saved_cursor
= scanner
->SaveCursor();
363 if (LikelyCardMonthSelectField(scanner
)) {
364 expiration_month_
= scanner
->Cursor();
366 if (LikelyCardYearSelectField(scanner
)) {
367 expiration_year_
= scanner
->Cursor();
371 expiration_month_
= nullptr;
372 expiration_year_
= nullptr;
375 // If that fails, do a general regex search.
376 scanner
->RewindTo(month_year_saved_cursor
);
377 const int kMatchTelAndSelect
= MATCH_DEFAULT
| MATCH_TELEPHONE
| MATCH_SELECT
;
378 if (ParseFieldSpecifics(scanner
,
379 base::UTF8ToUTF16(kExpirationMonthRe
),
381 &expiration_month_
) &&
382 ParseFieldSpecifics(scanner
,
383 base::UTF8ToUTF16(kExpirationYearRe
),
385 &expiration_year_
)) {
389 // If that fails, look for just MM/YY(YY).
390 scanner
->RewindTo(month_year_saved_cursor
);
391 if (ParseFieldSpecifics(scanner
,
392 base::ASCIIToUTF16("^mm$"),
394 &expiration_month_
) &&
395 ParseFieldSpecifics(scanner
,
396 base::ASCIIToUTF16("^(yy|yyyy)$"),
398 &expiration_year_
)) {
402 // If that fails, try to parse a combined expiration field.
403 // We allow <select> fields, because they're used e.g. on qvc.com.
404 scanner
->RewindTo(month_year_saved_cursor
);
406 // Bail out if the field cannot fit a 2-digit year expiration date.
407 const int current_field_max_length
= scanner
->Cursor()->max_length
;
408 if (!FieldCanFitDataForFieldType(current_field_max_length
,
409 CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR
)) {
413 // Try to look for a 2-digit year expiration date.
414 if (ParseFieldSpecifics(scanner
,
415 base::UTF8ToUTF16(kExpirationDate2DigitYearRe
),
417 &expiration_date_
)) {
418 exp_year_type_
= CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR
;
419 expiration_month_
= nullptr;
423 // Try to look for a generic expiration date field. (2 or 4 digit year)
424 if (ParseFieldSpecifics(scanner
,
425 base::UTF8ToUTF16(kExpirationDateRe
),
427 &expiration_date_
)) {
428 // If such a field exists, but it cannot fit a 4-digit year expiration
429 // date, then the likely possibility is that it is a 2-digit year expiration
431 if (!FieldCanFitDataForFieldType(current_field_max_length
,
432 CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR
)) {
433 exp_year_type_
= CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR
;
435 expiration_month_
= nullptr;
439 // Try to look for a 4-digit year expiration date.
440 if (FieldCanFitDataForFieldType(current_field_max_length
,
441 CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR
) &&
442 ParseFieldSpecifics(scanner
,
443 base::UTF8ToUTF16(kExpirationDate4DigitYearRe
),
445 &expiration_date_
)) {
446 expiration_month_
= nullptr;
453 ServerFieldType
CreditCardField::GetExpirationYearType() const {
454 return (expiration_date_
456 : ((expiration_year_
&& expiration_year_
->max_length
== 2)
457 ? CREDIT_CARD_EXP_2_DIGIT_YEAR
458 : CREDIT_CARD_EXP_4_DIGIT_YEAR
));
461 } // namespace autofill