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/strings/string16.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "components/autofill/core/browser/autofill_field.h"
14 #include "components/autofill/core/browser/autofill_regex_constants.h"
15 #include "components/autofill/core/browser/autofill_scanner.h"
16 #include "components/autofill/core/browser/field_types.h"
20 // Credit card numbers are at most 19 digits in length.
21 // [Ref: http://en.wikipedia.org/wiki/Bank_card_number]
22 static const size_t kMaxValidCardNumberSize
= 19;
25 scoped_ptr
<FormField
> CreditCardField::Parse(AutofillScanner
* scanner
) {
29 scoped_ptr
<CreditCardField
> credit_card_field(new CreditCardField
);
30 size_t saved_cursor
= scanner
->SaveCursor();
32 // Credit card fields can appear in many different orders.
33 // We loop until no more credit card related fields are found, see |break| at
34 // bottom of the loop.
35 for (int fields
= 0; !scanner
->IsEnd(); ++fields
) {
36 // Ignore gift card fields.
37 if (ParseField(scanner
, base::UTF8ToUTF16(kGiftCardRe
), NULL
))
40 // Sometimes the cardholder field is just labeled "name". Unfortunately this
41 // is a dangerously generic word to search for, since it will often match a
42 // name (not cardholder name) field before or after credit card fields. So
43 // we search for "name" only when we've already parsed at least one other
44 // credit card field and haven't yet parsed the expiration date (which
45 // usually appears at the end).
46 if (!credit_card_field
->cardholder_
) {
47 base::string16 name_pattern
;
48 if (fields
== 0 || credit_card_field
->expiration_month_
) {
49 // at beginning or end
50 name_pattern
= base::UTF8ToUTF16(kNameOnCardRe
);
52 name_pattern
= base::UTF8ToUTF16(kNameOnCardContextualRe
);
55 if (ParseField(scanner
, name_pattern
, &credit_card_field
->cardholder_
))
59 // Check for a credit card type (Visa, MasterCard, etc.) field.
60 if (!credit_card_field
->type_
&&
61 ParseFieldSpecifics(scanner
,
62 base::UTF8ToUTF16(kCardTypeRe
),
63 MATCH_DEFAULT
| MATCH_SELECT
,
64 &credit_card_field
->type_
)) {
68 // We look for a card security code before we look for a credit card number
69 // and match the general term "number". The security code has a plethora of
70 // names; we've seen "verification #", "verification number", "card
71 // identification number", and others listed in the regex pattern used
73 // Note: Some sites use type="tel" for numerical inputs.
74 if (!credit_card_field
->verification_
&&
75 ParseFieldSpecifics(scanner
,
76 base::UTF8ToUTF16(kCardCvcRe
),
77 MATCH_DEFAULT
| MATCH_TELEPHONE
| MATCH_PASSWORD
,
78 &credit_card_field
->verification_
)) {
82 AutofillField
* current_number_field
;
83 if (ParseFieldSpecifics(scanner
,
84 base::UTF8ToUTF16(kCardNumberRe
),
85 MATCH_DEFAULT
| MATCH_TELEPHONE
,
86 ¤t_number_field
)) {
87 // Avoid autofilling any credit card number field having very low or high
88 // |start_index| on the HTML form.
89 size_t start_index
= 0;
90 if (!credit_card_field
->numbers_
.empty()) {
91 size_t last_number_field_size
=
92 credit_card_field
->numbers_
.back()->credit_card_number_offset() +
93 credit_card_field
->numbers_
.back()->max_length
;
95 // Distinguish between
96 // (a) one card split across multiple fields
97 // (b) multiple fields for multiple cards
98 // Treat this field as a part of the same card as the last field, except
99 // when doing so would cause overflow.
100 if (last_number_field_size
< kMaxValidCardNumberSize
)
101 start_index
= last_number_field_size
;
104 current_number_field
->set_credit_card_number_offset(start_index
);
105 credit_card_field
->numbers_
.push_back(current_number_field
);
109 if (!credit_card_field
->expiration_date_
&&
110 LowerCaseEqualsASCII(scanner
->Cursor()->form_control_type
, "month")) {
111 credit_card_field
->expiration_date_
= scanner
->Cursor();
112 credit_card_field
->expiration_month_
= nullptr;
113 credit_card_field
->expiration_year_
= nullptr;
118 if (!credit_card_field
->expiration_month_
&&
119 !credit_card_field
->expiration_date_
) {
120 // First try to parse split month/year expiration fields.
121 scanner
->SaveCursor();
122 if (ParseFieldSpecifics(scanner
,
123 base::UTF8ToUTF16(kExpirationMonthRe
),
124 MATCH_DEFAULT
| MATCH_SELECT
,
125 &credit_card_field
->expiration_month_
) &&
126 ParseFieldSpecifics(scanner
,
127 base::UTF8ToUTF16(kExpirationYearRe
),
128 MATCH_DEFAULT
| MATCH_SELECT
,
129 &credit_card_field
->expiration_year_
)) {
133 // If that fails, try to parse a combined expiration field.
134 // Look for a 2-digit year first.
135 // We allow <select> fields, because they're used e.g. on qvc.com.
137 if (ParseFieldSpecifics(
139 base::UTF8ToUTF16(kExpirationDate2DigitYearRe
),
140 MATCH_LABEL
| MATCH_VALUE
| MATCH_TEXT
| MATCH_SELECT
,
141 &credit_card_field
->expiration_date_
)) {
142 credit_card_field
->exp_year_type_
= CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR
;
143 credit_card_field
->expiration_month_
= nullptr;
147 if (ParseFieldSpecifics(
149 base::UTF8ToUTF16(kExpirationDateRe
),
150 MATCH_LABEL
| MATCH_VALUE
| MATCH_TEXT
| MATCH_SELECT
,
151 &credit_card_field
->expiration_date_
)) {
152 credit_card_field
->expiration_month_
= nullptr;
156 if (credit_card_field
->expiration_month_
&&
157 !credit_card_field
->expiration_year_
&&
158 !credit_card_field
->expiration_date_
) {
159 // Parsed a month but couldn't parse a year; give up.
160 scanner
->RewindTo(saved_cursor
);
165 // Some pages (e.g. ExpediaBilling.html) have a "card description"
166 // field; we parse this field but ignore it.
167 // We also ignore any other fields within a credit card block that
168 // start with "card", under the assumption that they are related to
169 // the credit card section being processed but are uninteresting to us.
170 if (ParseField(scanner
, base::UTF8ToUTF16(kCardIgnoredRe
), NULL
))
176 // Some pages have a billing address field after the cardholder name field.
177 // For that case, allow only just the cardholder name field. The remaining
178 // CC fields will be picked up in a following CreditCardField.
179 if (credit_card_field
->cardholder_
)
180 return credit_card_field
.Pass();
182 // On some pages, the user selects a card type using radio buttons
183 // (e.g. test page Apple Store Billing.html). We can't handle that yet,
184 // so we treat the card type as optional for now.
185 // The existence of a number or cvc in combination with expiration date is
186 // a strong enough signal that this is a credit card. It is possible that
187 // the number and name were parsed in a separate part of the form. So if
188 // the cvc and date were found independently they are returned.
189 if ((!credit_card_field
->numbers_
.empty() ||
190 credit_card_field
->verification_
) &&
191 (credit_card_field
->expiration_date_
||
192 (credit_card_field
->expiration_month_
&&
193 credit_card_field
->expiration_year_
))) {
194 return credit_card_field
.Pass();
197 scanner
->RewindTo(saved_cursor
);
201 CreditCardField::CreditCardField()
203 cardholder_last_(NULL
),
206 expiration_month_(NULL
),
207 expiration_year_(NULL
),
208 expiration_date_(NULL
),
209 exp_year_type_(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR
) {
212 CreditCardField::~CreditCardField() {
215 bool CreditCardField::ClassifyField(ServerFieldTypeMap
* map
) const {
217 for (size_t index
= 0; index
< numbers_
.size(); ++index
) {
218 ok
= ok
&& AddClassification(numbers_
[index
], CREDIT_CARD_NUMBER
, map
);
221 ok
= ok
&& AddClassification(type_
, CREDIT_CARD_TYPE
, map
);
223 AddClassification(verification_
, CREDIT_CARD_VERIFICATION_CODE
, map
);
225 // If the heuristics detected first and last name in separate fields,
226 // then ignore both fields. Putting them into separate fields is probably
227 // wrong, because the credit card can also contain a middle name or middle
229 if (cardholder_last_
== NULL
)
230 ok
= ok
&& AddClassification(cardholder_
, CREDIT_CARD_NAME
, map
);
232 if (expiration_date_
) {
233 DCHECK(!expiration_month_
);
234 DCHECK(!expiration_year_
);
236 ok
&& AddClassification(expiration_date_
, GetExpirationYearType(), map
);
238 ok
= ok
&& AddClassification(expiration_month_
, CREDIT_CARD_EXP_MONTH
, map
);
240 ok
&& AddClassification(expiration_year_
, GetExpirationYearType(), map
);
246 ServerFieldType
CreditCardField::GetExpirationYearType() const {
247 return (expiration_date_
249 : ((expiration_year_
&& expiration_year_
->max_length
== 2)
250 ? CREDIT_CARD_EXP_2_DIGIT_YEAR
251 : CREDIT_CARD_EXP_4_DIGIT_YEAR
));
254 } // namespace autofill