• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &current_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