summaryrefslogtreecommitdiffstats
path: root/chrome/browser/autofill/credit_card_field.cc
blob: b79467d7b9e9969f3864752529c70012da28f98b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/autofill/credit_card_field.h"

#include "base/string16.h"
#include "chrome/browser/autofill/autofill_field.h"

bool CreditCardField::GetFieldInfo(FieldTypeMap* field_type_map) const {
  bool ok = Add(field_type_map, number_, AutoFillType(CREDIT_CARD_NUMBER));
  DCHECK(ok);

  // If the heuristics detected first and last name in separate fields,
  // then ignore both fields. Putting them into separate fields is probably
  // wrong, because the credit card can also contain a middle name or middle
  // initial.
  if (cardholder_last_ == NULL) {
    // Add() will check if cardholder_ is != NULL.
    Add(field_type_map, cardholder_, AutoFillType(CREDIT_CARD_NAME));
  }

  Add(field_type_map, type_, AutoFillType(CREDIT_CARD_TYPE));

  ok = ok && Add(field_type_map, expiration_month_,
      AutoFillType(CREDIT_CARD_EXP_MONTH));
  DCHECK(ok);
  ok = ok && Add(field_type_map, expiration_year_,
      AutoFillType(CREDIT_CARD_EXP_4_DIGIT_YEAR));
  DCHECK(ok);

  Add(field_type_map, verification_,
      AutoFillType(CREDIT_CARD_VERIFICATION_CODE));

  return ok;
}

// static
CreditCardField* CreditCardField::Parse(
    std::vector<AutoFillField*>::const_iterator* iter,
    bool is_ecml) {
  CreditCardField credit_card_field;
  std::vector<AutoFillField*>::const_iterator q = *iter;
  string16 pattern;

  // Credit card fields can appear in many different orders.
  // We loop until no more credit card related fields are found, see |break| at
  // bottom of the loop.
  for (int fields = 0; true; ++fields) {
    // Sometimes the cardholder field is just labeled "name" (e.g. on test page
    // Starbucks - Credit card.html).  Unfortunately this is a dangerously
    // generic word to search for, since it will often match a name (not
    // cardholder name) field before or after credit card fields.  So we search
    // for "name" only when we've already parsed at least one other credit card
    // field and haven't yet parsed the expiration date (which usually appears
    // at the end).
    if (credit_card_field.cardholder_ == NULL) {
      string16 name_pattern;
      if (is_ecml) {
        name_pattern = GetEcmlPattern(kEcmlCardHolder);
      } else {
        if (fields == 0 || credit_card_field.expiration_month_) {
          // at beginning or end
          name_pattern = ASCIIToUTF16("card holder|name on card");
        } else {
          name_pattern = ASCIIToUTF16("name");
        }
      }

      if (ParseText(&q, name_pattern, &credit_card_field.cardholder_)) {
        continue;
      }

      // As a hard-coded hack for Expedia's billing pages (expedia_checkout.html
      // and ExpediaBilling.html in our test suite), recognize separate fields
      // for the cardholder's first and last name if they have the labels "cfnm"
      // and "clnm".
      std::vector<AutoFillField*>::const_iterator p = q;
      AutoFillField* first;
      if (!is_ecml && ParseText(&p, ASCIIToUTF16("^cfnm"), &first) &&
          ParseText(&p, ASCIIToUTF16("^clnm"),
                    &credit_card_field.cardholder_last_)) {
        credit_card_field.cardholder_ = first;
        q = p;
        continue;
      }
    }

    // TODO(jhawkins): Parse the type select control.

    // We look for a card security code before we look for a credit card number
    // and match the general term "number".  The security code has a plethora of
    // names; we've seen "verification #", "verification number",
    // "card identification number" and others listed in the ParseText() call
    // below.
    if (is_ecml) {
      pattern = GetEcmlPattern(kEcmlCardVerification);
    } else {
      pattern = ASCIIToUTF16(
          "verification|card identification|cvn|security code|cvv code|cvc");
    }

    if (credit_card_field.verification_ == NULL &&
        ParseText(&q, pattern, &credit_card_field.verification_)) {
      continue;
    }

    if (is_ecml)
      pattern = GetEcmlPattern(kEcmlCardNumber);
    else
      pattern = ASCIIToUTF16("number|card #|card no.|card_number");

    if (credit_card_field.number_ == NULL && ParseText(&q, pattern,
        &credit_card_field.number_)) {
      continue;
    }

    // "Expiration date" is the most common label here, but some pages have
    // "Expires", "exp. date" or "exp. month" and "exp. year".  We also look for
    // the field names ccmonth and ccyear, which appear on at least 4 of our
    // test pages.
    //
    // -> On at least one page (The China Shop2.html) we find only the labels
    // "month" and "year".  So for now we match these words directly; we'll
    // see if this turns out to be too general.
    //
    // Toolbar Bug 51451: indeed, simply matching "month" is too general for
    //   https://rps.fidelity.com/ftgw/rps/RtlCust/CreatePIN/Init.
    // Instead, we match only words beginning with "month".
    if (is_ecml)
      pattern = GetEcmlPattern(kEcmlCardExpireMonth);
    else
      pattern = ASCIIToUTF16("expir|exp month|exp date|ccmonth|&month");

    if ((!credit_card_field.expiration_month_ ||
        credit_card_field.expiration_month_->IsEmpty()) &&
        ParseText(&q, pattern, &credit_card_field.expiration_month_)) {
      if (is_ecml)
        pattern = GetEcmlPattern(kEcmlCardExpireYear);
      else
        pattern = ASCIIToUTF16("|exp|^/|ccyear|year");

      if (!ParseText(&q, pattern, &credit_card_field.expiration_year_)) {
        return NULL;
      }

      continue;
    }

    if (ParseText(&q, GetEcmlPattern(kEcmlCardExpireDay)))
      continue;

    // Some pages (e.g. ExpediaBilling.html) have a "card description"
    // field; we parse this field but ignore it.
    if (ParseText(&q, ASCIIToUTF16("card description")))
      continue;

    break;
  }

  // On some pages, the user selects a card type using radio buttons
  // (e.g. test page Apple Store Billing.html).  We can't handle that yet,
  // so we treat the card type as optional for now.
  if (credit_card_field.number_ &&
      credit_card_field.expiration_month_ &&
      credit_card_field.expiration_year_) {
      *iter = q;
      return new CreditCardField(credit_card_field);
  }

  return NULL;
}

CreditCardField::CreditCardField()
    : cardholder_(NULL),
      cardholder_last_(NULL),
      type_(NULL),
      number_(NULL),
      verification_(NULL),
      expiration_month_(NULL),
      expiration_year_(NULL) {
}

CreditCardField::CreditCardField(const CreditCardField& field)
    : FormField(),
      cardholder_(field.cardholder_),
      cardholder_last_(field.cardholder_last_),
      type_(field.type_),
      number_(field.number_),
      verification_(field.verification_),
      expiration_month_(field.expiration_month_),
      expiration_year_(field.expiration_year_) {
}