- add sources.
[platform/framework/web/crosswalk.git] / src / components / autofill / core / browser / credit_card_field.cc
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 // static
23 FormField* 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, 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 = UTF8ToUTF16(autofill::kNameOnCardRe);
49       } else {
50         name_pattern = 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, ASCIIToUTF16("^cfnm"), &first) &&
63           ParseField(scanner, 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 = 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 = UTF8ToUTF16(autofill::kCardCvcRe);
86     if (!credit_card_field->verification_ &&
87         ParseField(scanner, pattern, &credit_card_field->verification_)) {
88       continue;
89     }
90
91     pattern = 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 = 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 = 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 = 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 = 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, 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
181 CreditCardField::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
193 bool 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