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.
4
5 #include "components/autofill/core/browser/credit_card_field.h"
6
7 #include <stddef.h>
8
9 #include "base/logging.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/strings/string16.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "components/autofill/core/browser/autofill_field.h"
15 #include "components/autofill/core/browser/autofill_regex_constants.h"
16 #include "components/autofill/core/browser/autofill_scanner.h"
17 #include "components/autofill/core/browser/field_types.h"
18 #include "ui/base/l10n/l10n_util.h"
19
20 namespace autofill {
21
22 // Credit card numbers are at most 19 digits in length.
23 // [Ref: http://en.wikipedia.org/wiki/Bank_card_number]
24 static const size_t kMaxValidCardNumberSize = 19;
25
26 // static
Parse(AutofillScanner * scanner)27 FormField* CreditCardField::Parse(AutofillScanner* scanner) {
28 if (scanner->IsEnd())
29 return NULL;
30
31 scoped_ptr<CreditCardField> credit_card_field(new CreditCardField);
32 size_t saved_cursor = scanner->SaveCursor();
33 bool form_has_valid_card_number_fields = true;
34
35 // Credit card fields can appear in many different orders.
36 // We loop until no more credit card related fields are found, see |break| at
37 // bottom of the loop.
38 for (int fields = 0; !scanner->IsEnd(); ++fields) {
39 // Ignore gift card fields.
40 if (ParseField(scanner, base::UTF8ToUTF16(autofill::kGiftCardRe), NULL))
41 break;
42
43 // Sometimes the cardholder field is just labeled "name". Unfortunately this
44 // is a dangerously generic word to search for, since it will often match a
45 // name (not cardholder name) field before or after credit card fields. So
46 // we search for "name" only when we've already parsed at least one other
47 // credit card field and haven't yet parsed the expiration date (which
48 // usually appears at the end).
49 if (credit_card_field->cardholder_ == NULL) {
50 base::string16 name_pattern;
51 if (fields == 0 || credit_card_field->expiration_month_) {
52 // at beginning or end
53 name_pattern = base::UTF8ToUTF16(autofill::kNameOnCardRe);
54 } else {
55 name_pattern = base::UTF8ToUTF16(autofill::kNameOnCardContextualRe);
56 }
57
58 if (ParseField(scanner, name_pattern, &credit_card_field->cardholder_))
59 continue;
60
61 // As a hard-coded hack for Expedia's billing pages (expedia_checkout.html
62 // and ExpediaBilling.html in our test suite), recognize separate fields
63 // for the cardholder's first and last name if they have the labels "cfnm"
64 // and "clnm".
65 scanner->SaveCursor();
66 AutofillField* first;
67 if (ParseField(scanner, base::ASCIIToUTF16("^cfnm"), &first) &&
68 ParseField(scanner,
69 base::ASCIIToUTF16("^clnm"),
70 &credit_card_field->cardholder_last_)) {
71 credit_card_field->cardholder_ = first;
72 continue;
73 }
74 scanner->Rewind();
75 }
76
77 // Check for a credit card type (Visa, MasterCard, etc.) field.
78 base::string16 type_pattern = base::UTF8ToUTF16(autofill::kCardTypeRe);
79 if (!credit_card_field->type_ &&
80 ParseFieldSpecifics(scanner,
81 type_pattern,
82 MATCH_DEFAULT | MATCH_SELECT,
83 &credit_card_field->type_)) {
84 continue;
85 }
86
87 // We look for a card security code before we look for a credit
88 // card number and match the general term "number". The security code
89 // has a plethora of names; we've seen "verification #",
90 // "verification number", "card identification number" and others listed
91 // in the |pattern| below.
92 base::string16 pattern = base::UTF8ToUTF16(autofill::kCardCvcRe);
93 if (!credit_card_field->verification_ &&
94 ParseField(scanner, pattern, &credit_card_field->verification_)) {
95 continue;
96 }
97
98 pattern = base::UTF8ToUTF16(autofill::kCardNumberRe);
99 AutofillField* current_number_field;
100 if (ParseField(scanner, pattern, ¤t_number_field)) {
101 // Avoid autofilling any credit card number field having very low or high
102 // |start_index| on the HTML form.
103 size_t start_index = 0;
104 if (!credit_card_field->numbers_.empty()) {
105 size_t last_number_field_size =
106 credit_card_field->numbers_.back()->credit_card_number_offset() +
107 credit_card_field->numbers_.back()->max_length;
108
109 // In some cases, HTML form may have credit card number split across
110 // multiple input fields and either one or cumulatively having
111 // |max_length| more than |kMaxValidCardNumberSize|, mark these input
112 // form fields as invalid and skip autofilling them.
113 if (last_number_field_size == 0U ||
114 last_number_field_size >= kMaxValidCardNumberSize) {
115 // Mark that the credit card number splits are invalid. But keep
116 // scanning HTML form so that cursor moves beyond related fields.
117 form_has_valid_card_number_fields = false;
118 }
119
120 start_index = last_number_field_size;
121 }
122
123 current_number_field->set_credit_card_number_offset(start_index);
124 credit_card_field->numbers_.push_back(current_number_field);
125 continue;
126 }
127
128 if (LowerCaseEqualsASCII(scanner->Cursor()->form_control_type, "month")) {
129 credit_card_field->expiration_date_ = scanner->Cursor();
130 scanner->Advance();
131 } else {
132 // First try to parse split month/year expiration fields.
133 scanner->SaveCursor();
134 pattern = base::UTF8ToUTF16(autofill::kExpirationMonthRe);
135 if (!credit_card_field->expiration_month_ &&
136 ParseFieldSpecifics(scanner,
137 pattern,
138 MATCH_DEFAULT | MATCH_SELECT,
139 &credit_card_field->expiration_month_)) {
140 pattern = base::UTF8ToUTF16(autofill::kExpirationYearRe);
141 if (ParseFieldSpecifics(scanner,
142 pattern,
143 MATCH_DEFAULT | MATCH_SELECT,
144 &credit_card_field->expiration_year_)) {
145 continue;
146 }
147 }
148
149 // If that fails, try to parse a combined expiration field.
150 if (!credit_card_field->expiration_date_) {
151 // Look for a 2-digit year first.
152 scanner->Rewind();
153 pattern = base::UTF8ToUTF16(autofill::kExpirationDate2DigitYearRe);
154 // We allow <select> fields, because they're used e.g. on qvc.com.
155 if (ParseFieldSpecifics(
156 scanner,
157 pattern,
158 MATCH_LABEL | MATCH_VALUE | MATCH_TEXT | MATCH_SELECT,
159 &credit_card_field->expiration_date_)) {
160 credit_card_field->exp_year_type_ = CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR;
161 continue;
162 }
163
164 pattern = base::UTF8ToUTF16(autofill::kExpirationDateRe);
165 if (ParseFieldSpecifics(
166 scanner,
167 pattern,
168 MATCH_LABEL | MATCH_VALUE | MATCH_TEXT | MATCH_SELECT,
169 &credit_card_field->expiration_date_)) {
170 continue;
171 }
172 }
173
174 if (credit_card_field->expiration_month_ &&
175 !credit_card_field->expiration_year_ &&
176 !credit_card_field->expiration_date_) {
177 // Parsed a month but couldn't parse a year; give up.
178 scanner->RewindTo(saved_cursor);
179 return NULL;
180 }
181 }
182
183 // Some pages (e.g. ExpediaBilling.html) have a "card description"
184 // field; we parse this field but ignore it.
185 // We also ignore any other fields within a credit card block that
186 // start with "card", under the assumption that they are related to
187 // the credit card section being processed but are uninteresting to us.
188 if (ParseField(scanner, base::UTF8ToUTF16(autofill::kCardIgnoredRe), NULL))
189 continue;
190
191 break;
192 }
193
194 // Cases where heuristic misinterprets input field as credit card number
195 // field, refuse to autofill credit card number fields.
196 if (!form_has_valid_card_number_fields)
197 credit_card_field->numbers_.clear();
198
199 // Some pages have a billing address field after the cardholder name field.
200 // For that case, allow only just the cardholder name field. The remaining
201 // CC fields will be picked up in a following CreditCardField.
202 if (credit_card_field->cardholder_)
203 return credit_card_field.release();
204
205 // On some pages, the user selects a card type using radio buttons
206 // (e.g. test page Apple Store Billing.html). We can't handle that yet,
207 // so we treat the card type as optional for now.
208 // The existence of a number or cvc in combination with expiration date is
209 // a strong enough signal that this is a credit card. It is possible that
210 // the number and name were parsed in a separate part of the form. So if
211 // the cvc and date were found independently they are returned.
212 if ((!credit_card_field->numbers_.empty() ||
213 credit_card_field->verification_ ||
214 !form_has_valid_card_number_fields) &&
215 (credit_card_field->expiration_date_ ||
216 (credit_card_field->expiration_month_ &&
217 credit_card_field->expiration_year_))) {
218 return credit_card_field.release();
219 }
220
221 scanner->RewindTo(saved_cursor);
222 return NULL;
223 }
224
CreditCardField()225 CreditCardField::CreditCardField()
226 : cardholder_(NULL),
227 cardholder_last_(NULL),
228 type_(NULL),
229 verification_(NULL),
230 expiration_month_(NULL),
231 expiration_year_(NULL),
232 expiration_date_(NULL),
233 exp_year_type_(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR) {
234 }
235
~CreditCardField()236 CreditCardField::~CreditCardField() {
237 }
238
ClassifyField(ServerFieldTypeMap * map) const239 bool CreditCardField::ClassifyField(ServerFieldTypeMap* map) const {
240 bool ok = true;
241 for (size_t index = 0; index < numbers_.size(); ++index) {
242 ok = ok && AddClassification(numbers_[index], CREDIT_CARD_NUMBER, map);
243 }
244
245 ok = ok && AddClassification(type_, CREDIT_CARD_TYPE, map);
246 ok = ok &&
247 AddClassification(verification_, CREDIT_CARD_VERIFICATION_CODE, map);
248
249 // If the heuristics detected first and last name in separate fields,
250 // then ignore both fields. Putting them into separate fields is probably
251 // wrong, because the credit card can also contain a middle name or middle
252 // initial.
253 if (cardholder_last_ == NULL)
254 ok = ok && AddClassification(cardholder_, CREDIT_CARD_NAME, map);
255
256 if (expiration_date_) {
257 ok =
258 ok && AddClassification(expiration_date_, GetExpirationYearType(), map);
259 } else {
260 ok = ok && AddClassification(expiration_month_, CREDIT_CARD_EXP_MONTH, map);
261 ok =
262 ok && AddClassification(expiration_year_, GetExpirationYearType(), map);
263 }
264
265 return ok;
266 }
267
GetExpirationYearType() const268 ServerFieldType CreditCardField::GetExpirationYearType() const {
269 return (expiration_date_
270 ? exp_year_type_
271 : ((expiration_year_ && expiration_year_->max_length == 2)
272 ? CREDIT_CARD_EXP_2_DIGIT_YEAR
273 : CREDIT_CARD_EXP_4_DIGIT_YEAR));
274 }
275
276 } // namespace autofill
277