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
20namespace autofill {
21
22// static
23FormField* CreditCardField::Parse(AutofillScanner* scanner) {
24  if (scanner->IsEnd())
25    return NULL;
26
27  scoped_ptr<CreditCardField> credit_card_field(new CreditCardField);
28  size_t saved_cursor = scanner->SaveCursor();
29
30  // Credit card fields can appear in many different orders.
31  // We loop until no more credit card related fields are found, see |break| at
32  // bottom of the loop.
33  for (int fields = 0; !scanner->IsEnd(); ++fields) {
34    // Ignore gift card fields.
35    if (ParseField(scanner, base::UTF8ToUTF16(autofill::kGiftCardRe), NULL))
36      break;
37
38    // Sometimes the cardholder field is just labeled "name". Unfortunately this
39    // is a dangerously generic word to search for, since it will often match a
40    // name (not cardholder name) field before or after credit card fields. So
41    // we search for "name" only when we've already parsed at least one other
42    // credit card field and haven't yet parsed the expiration date (which
43    // usually appears at the end).
44    if (credit_card_field->cardholder_ == NULL) {
45      base::string16 name_pattern;
46      if (fields == 0 || credit_card_field->expiration_month_) {
47        // at beginning or end
48        name_pattern = base::UTF8ToUTF16(autofill::kNameOnCardRe);
49      } else {
50        name_pattern = base::UTF8ToUTF16(autofill::kNameOnCardContextualRe);
51      }
52
53      if (ParseField(scanner, name_pattern, &credit_card_field->cardholder_))
54        continue;
55
56      // As a hard-coded hack for Expedia's billing pages (expedia_checkout.html
57      // and ExpediaBilling.html in our test suite), recognize separate fields
58      // for the cardholder's first and last name if they have the labels "cfnm"
59      // and "clnm".
60      scanner->SaveCursor();
61      const AutofillField* first;
62      if (ParseField(scanner, base::ASCIIToUTF16("^cfnm"), &first) &&
63          ParseField(scanner, base::ASCIIToUTF16("^clnm"),
64                     &credit_card_field->cardholder_last_)) {
65        credit_card_field->cardholder_ = first;
66        continue;
67      }
68      scanner->Rewind();
69    }
70
71    // Check for a credit card type (Visa, MasterCard, etc.) field.
72    base::string16 type_pattern = base::UTF8ToUTF16(autofill::kCardTypeRe);
73    if (!credit_card_field->type_ &&
74        ParseFieldSpecifics(scanner, type_pattern,
75                            MATCH_DEFAULT | MATCH_SELECT,
76                            &credit_card_field->type_)) {
77      continue;
78    }
79
80    // We look for a card security code before we look for a credit
81    // card number and match the general term "number".  The security code
82    // has a plethora of names; we've seen "verification #",
83    // "verification number", "card identification number" and others listed
84    // in the |pattern| below.
85    base::string16 pattern = base::UTF8ToUTF16(autofill::kCardCvcRe);
86    if (!credit_card_field->verification_ &&
87        ParseField(scanner, pattern, &credit_card_field->verification_)) {
88      continue;
89    }
90
91    pattern = base::UTF8ToUTF16(autofill::kCardNumberRe);
92    if (!credit_card_field->number_ &&
93        ParseField(scanner, pattern, &credit_card_field->number_)) {
94      continue;
95    }
96
97    if (LowerCaseEqualsASCII(scanner->Cursor()->form_control_type, "month")) {
98      credit_card_field->expiration_date_ = scanner->Cursor();
99      scanner->Advance();
100    } else {
101      // First try to parse split month/year expiration fields.
102      scanner->SaveCursor();
103      pattern = base::UTF8ToUTF16(autofill::kExpirationMonthRe);
104      if (!credit_card_field->expiration_month_ &&
105          ParseFieldSpecifics(scanner, pattern, MATCH_DEFAULT | MATCH_SELECT,
106                              &credit_card_field->expiration_month_)) {
107        pattern = base::UTF8ToUTF16(autofill::kExpirationYearRe);
108        if (ParseFieldSpecifics(scanner, pattern, MATCH_DEFAULT | MATCH_SELECT,
109                                 &credit_card_field->expiration_year_)) {
110          continue;
111        }
112      }
113
114      // If that fails, try to parse a combined expiration field.
115      if (!credit_card_field->expiration_date_) {
116        // Look for a 2-digit year first.
117        scanner->Rewind();
118        pattern = base::UTF8ToUTF16(autofill::kExpirationDate2DigitYearRe);
119        // We allow <select> fields, because they're used e.g. on qvc.com.
120        if (ParseFieldSpecifics(scanner, pattern,
121                                MATCH_LABEL | MATCH_VALUE | MATCH_TEXT |
122                                    MATCH_SELECT,
123                                &credit_card_field->expiration_date_)) {
124          credit_card_field->is_two_digit_year_ = true;
125          continue;
126        }
127
128        pattern = base::UTF8ToUTF16(autofill::kExpirationDateRe);
129        if (ParseFieldSpecifics(scanner, pattern,
130                                MATCH_LABEL | MATCH_VALUE | MATCH_TEXT |
131                                    MATCH_SELECT,
132                                &credit_card_field->expiration_date_)) {
133          continue;
134        }
135      }
136
137      if (credit_card_field->expiration_month_ &&
138          !credit_card_field->expiration_year_ &&
139          !credit_card_field->expiration_date_) {
140        // Parsed a month but couldn't parse a year; give up.
141        scanner->RewindTo(saved_cursor);
142        return NULL;
143      }
144    }
145
146    // Some pages (e.g. ExpediaBilling.html) have a "card description"
147    // field; we parse this field but ignore it.
148    // We also ignore any other fields within a credit card block that
149    // start with "card", under the assumption that they are related to
150    // the credit card section being processed but are uninteresting to us.
151    if (ParseField(scanner, base::UTF8ToUTF16(autofill::kCardIgnoredRe), NULL))
152      continue;
153
154    break;
155  }
156
157  // Some pages have a billing address field after the cardholder name field.
158  // For that case, allow only just the cardholder name field.  The remaining
159  // CC fields will be picked up in a following CreditCardField.
160  if (credit_card_field->cardholder_)
161    return credit_card_field.release();
162
163  // On some pages, the user selects a card type using radio buttons
164  // (e.g. test page Apple Store Billing.html).  We can't handle that yet,
165  // so we treat the card type as optional for now.
166  // The existence of a number or cvc in combination with expiration date is
167  // a strong enough signal that this is a credit card.  It is possible that
168  // the number and name were parsed in a separate part of the form.  So if
169  // the cvc and date were found independently they are returned.
170  if ((credit_card_field->number_ || credit_card_field->verification_) &&
171      (credit_card_field->expiration_date_ ||
172       (credit_card_field->expiration_month_ &&
173        credit_card_field->expiration_year_))) {
174    return credit_card_field.release();
175  }
176
177  scanner->RewindTo(saved_cursor);
178  return NULL;
179}
180
181CreditCardField::CreditCardField()
182    : cardholder_(NULL),
183      cardholder_last_(NULL),
184      type_(NULL),
185      number_(NULL),
186      verification_(NULL),
187      expiration_month_(NULL),
188      expiration_year_(NULL),
189      expiration_date_(NULL),
190      is_two_digit_year_(false) {
191}
192
193bool CreditCardField::ClassifyField(ServerFieldTypeMap* map) const {
194  bool ok = AddClassification(number_, CREDIT_CARD_NUMBER, map);
195  ok = ok && AddClassification(type_, CREDIT_CARD_TYPE, map);
196  ok = ok && AddClassification(verification_, CREDIT_CARD_VERIFICATION_CODE,
197                               map);
198
199  // If the heuristics detected first and last name in separate fields,
200  // then ignore both fields. Putting them into separate fields is probably
201  // wrong, because the credit card can also contain a middle name or middle
202  // initial.
203  if (cardholder_last_ == NULL)
204    ok = ok && AddClassification(cardholder_, CREDIT_CARD_NAME, map);
205
206  if (expiration_date_) {
207    if (is_two_digit_year_) {
208      ok = ok && AddClassification(expiration_date_,
209                                   CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, map);
210    } else {
211      ok = ok && AddClassification(expiration_date_,
212                                   CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, map);
213    }
214  } else {
215    ok = ok && AddClassification(expiration_month_, CREDIT_CARD_EXP_MONTH, map);
216    if (is_two_digit_year_) {
217      ok = ok && AddClassification(expiration_year_,
218                                   CREDIT_CARD_EXP_2_DIGIT_YEAR,
219                                   map);
220    } else {
221      ok = ok && AddClassification(expiration_year_,
222                                   CREDIT_CARD_EXP_4_DIGIT_YEAR,
223                                   map);
224    }
225  }
226
227  return ok;
228}
229
230}  // namespace autofill
231