1// Copyright (c) 2011 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 "chrome/browser/autofill/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/string16.h"
12#include "base/string_util.h"
13#include "base/utf_string_conversions.h"
14#include "chrome/browser/autofill/autofill_field.h"
15#include "chrome/browser/autofill/field_types.h"
16#include "grit/autofill_resources.h"
17#include "ui/base/l10n/l10n_util.h"
18
19bool CreditCardField::GetFieldInfo(FieldTypeMap* field_type_map) const {
20  bool ok = Add(field_type_map, number_, AutofillType(CREDIT_CARD_NUMBER));
21  DCHECK(ok);
22
23  // If the heuristics detected first and last name in separate fields,
24  // then ignore both fields. Putting them into separate fields is probably
25  // wrong, because the credit card can also contain a middle name or middle
26  // initial.
27  if (cardholder_last_ == NULL) {
28    // Add() will check if cardholder_ is != NULL.
29    ok = ok && Add(field_type_map, cardholder_, AutofillType(CREDIT_CARD_NAME));
30    DCHECK(ok);
31  }
32
33  ok = ok && Add(field_type_map, type_, AutofillType(CREDIT_CARD_TYPE));
34  DCHECK(ok);
35  ok = ok && Add(field_type_map, expiration_month_,
36      AutofillType(CREDIT_CARD_EXP_MONTH));
37  DCHECK(ok);
38  ok = ok && Add(field_type_map, expiration_year_,
39      AutofillType(CREDIT_CARD_EXP_4_DIGIT_YEAR));
40  DCHECK(ok);
41
42  return ok;
43}
44
45FormFieldType CreditCardField::GetFormFieldType() const {
46  return kCreditCardType;
47}
48
49// static
50CreditCardField* CreditCardField::Parse(
51    std::vector<AutofillField*>::const_iterator* iter,
52    bool is_ecml) {
53  scoped_ptr<CreditCardField> credit_card_field(new CreditCardField);
54  std::vector<AutofillField*>::const_iterator q = *iter;
55  string16 pattern;
56
57  // Credit card fields can appear in many different orders.
58  // We loop until no more credit card related fields are found, see |break| at
59  // bottom of the loop.
60  for (int fields = 0; true; ++fields) {
61    // Sometimes the cardholder field is just labeled "name". Unfortunately this
62    // is a dangerously generic word to search for, since it will often match a
63    // name (not cardholder name) field before or after credit card fields. So
64    // we search for "name" only when we've already parsed at least one other
65    // credit card field and haven't yet parsed the expiration date (which
66    // usually appears at the end).
67    if (credit_card_field->cardholder_ == NULL) {
68      string16 name_pattern;
69      if (is_ecml) {
70        name_pattern = GetEcmlPattern(kEcmlCardHolder);
71      } else {
72        if (fields == 0 || credit_card_field->expiration_month_) {
73          // at beginning or end
74          name_pattern = l10n_util::GetStringUTF16(
75              IDS_AUTOFILL_NAME_ON_CARD_RE);
76        } else {
77          name_pattern = l10n_util::GetStringUTF16(
78              IDS_AUTOFILL_NAME_ON_CARD_CONTEXTUAL_RE);
79        }
80      }
81
82      if (ParseText(&q, name_pattern, &credit_card_field->cardholder_))
83        continue;
84
85      // As a hard-coded hack for Expedia's billing pages (expedia_checkout.html
86      // and ExpediaBilling.html in our test suite), recognize separate fields
87      // for the cardholder's first and last name if they have the labels "cfnm"
88      // and "clnm".
89      std::vector<AutofillField*>::const_iterator p = q;
90      AutofillField* first;
91      if (!is_ecml && ParseText(&p, ASCIIToUTF16("^cfnm"), &first) &&
92          ParseText(&p, ASCIIToUTF16("^clnm"),
93                    &credit_card_field->cardholder_last_)) {
94        credit_card_field->cardholder_ = first;
95        q = p;
96        continue;
97      }
98    }
99
100    // We look for a card security code before we look for a credit
101    // card number and match the general term "number".  The security code
102    // has a plethora of names; we've seen "verification #",
103    // "verification number", "card identification number" and others listed
104    // in the |pattern| below.
105    if (is_ecml) {
106      pattern = GetEcmlPattern(kEcmlCardVerification);
107    } else {
108      pattern = l10n_util::GetStringUTF16(IDS_AUTOFILL_CARD_CVC_RE);
109    }
110
111    if (credit_card_field->verification_ == NULL &&
112        ParseText(&q, pattern, &credit_card_field->verification_))
113      continue;
114
115    // TODO(jhawkins): Parse the type select control.
116
117    if (is_ecml)
118      pattern = GetEcmlPattern(kEcmlCardNumber);
119    else
120      pattern = l10n_util::GetStringUTF16(IDS_AUTOFILL_CARD_NUMBER_RE);
121
122    if (credit_card_field->number_ == NULL && ParseText(&q, pattern,
123        &credit_card_field->number_))
124      continue;
125
126    if ((*q) && LowerCaseEqualsASCII((*q)->form_control_type, "month")) {
127      credit_card_field->expiration_month_ = *q++;
128    } else {
129      // "Expiration date" is the most common label here, but some pages have
130      // "Expires", "exp. date" or "exp. month" and "exp. year".  We also look
131      // for the field names ccmonth and ccyear, which appear on at least 4 of
132      // our test pages.
133      //
134      // -> On at least one page (The China Shop2.html) we find only the labels
135      // "month" and "year".  So for now we match these words directly; we'll
136      // see if this turns out to be too general.
137      //
138      // Toolbar Bug 51451: indeed, simply matching "month" is too general for
139      //   https://rps.fidelity.com/ftgw/rps/RtlCust/CreatePIN/Init.
140      // Instead, we match only words beginning with "month".
141      if (is_ecml)
142        pattern = GetEcmlPattern(kEcmlCardExpireMonth);
143      else
144        pattern = l10n_util::GetStringUTF16(IDS_AUTOFILL_EXPIRATION_MONTH_RE);
145
146      if ((!credit_card_field->expiration_month_ ||
147          credit_card_field->expiration_month_->IsEmpty()) &&
148        ParseText(&q, pattern, &credit_card_field->expiration_month_)) {
149
150        if (is_ecml)
151          pattern = GetEcmlPattern(kEcmlCardExpireYear);
152        else
153          pattern = l10n_util::GetStringUTF16(IDS_AUTOFILL_EXPIRATION_DATE_RE);
154
155        if (!ParseText(&q, pattern, &credit_card_field->expiration_year_)) {
156          return NULL;
157        }
158        continue;
159      }
160    }
161
162    if (ParseText(&q, GetEcmlPattern(kEcmlCardExpireDay)))
163      continue;
164
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 (ParseText(&q, l10n_util::GetStringUTF16(IDS_AUTOFILL_CARD_IGNORED_RE)))
171      continue;
172
173    break;
174  }
175
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    *iter = q;
181    return credit_card_field.release();
182  }
183
184  // On some pages, the user selects a card type using radio buttons
185  // (e.g. test page Apple Store Billing.html).  We can't handle that yet,
186  // so we treat the card type as optional for now.
187  // The existence of a number or cvc in combination with expiration date is
188  // a strong enough signal that this is a credit card.  It is possible that
189  // the number and name were parsed in a separate part of the form.  So if
190  // the cvc and date were found independently they are returned.
191  if ((credit_card_field->number_ || credit_card_field->verification_) &&
192      credit_card_field->expiration_month_ &&
193      (credit_card_field->expiration_year_ ||
194      (LowerCaseEqualsASCII(
195           credit_card_field->expiration_month_->form_control_type,
196           "month")))) {
197      *iter = q;
198      return credit_card_field.release();
199  }
200
201  return NULL;
202}
203
204CreditCardField::CreditCardField()
205    : cardholder_(NULL),
206      cardholder_last_(NULL),
207      type_(NULL),
208      number_(NULL),
209      verification_(NULL),
210      expiration_month_(NULL),
211      expiration_year_(NULL) {
212}
213