• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
GetFieldInfo(FieldTypeMap * field_type_map) const19 bool 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 
GetFormFieldType() const45 FormFieldType CreditCardField::GetFormFieldType() const {
46   return kCreditCardType;
47 }
48 
49 // static
Parse(std::vector<AutofillField * >::const_iterator * iter,bool is_ecml)50 CreditCardField* 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 
CreditCardField()204 CreditCardField::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