ozone: evdev: Sync caps lock LED state to evdev
[chromium-blink-merge.git] / components / autofill / core / browser / credit_card_field.cc
blobdc0d3e7ecc33feaeb9f62fec925b33044a06b8b0
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.
5 #include "components/autofill/core/browser/credit_card_field.h"
7 #include <stddef.h>
9 #include "base/memory/scoped_ptr.h"
10 #include "base/strings/string16.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "components/autofill/core/browser/autofill_field.h"
14 #include "components/autofill/core/browser/autofill_regex_constants.h"
15 #include "components/autofill/core/browser/autofill_scanner.h"
16 #include "components/autofill/core/browser/field_types.h"
18 namespace autofill {
20 // Credit card numbers are at most 19 digits in length.
21 // [Ref: http://en.wikipedia.org/wiki/Bank_card_number]
22 static const size_t kMaxValidCardNumberSize = 19;
24 // static
25 scoped_ptr<FormField> CreditCardField::Parse(AutofillScanner* scanner) {
26 if (scanner->IsEnd())
27 return NULL;
29 scoped_ptr<CreditCardField> credit_card_field(new CreditCardField);
30 size_t saved_cursor = scanner->SaveCursor();
32 // Credit card fields can appear in many different orders.
33 // We loop until no more credit card related fields are found, see |break| at
34 // bottom of the loop.
35 for (int fields = 0; !scanner->IsEnd(); ++fields) {
36 // Ignore gift card fields.
37 if (ParseField(scanner, base::UTF8ToUTF16(kGiftCardRe), NULL))
38 break;
40 // Sometimes the cardholder field is just labeled "name". Unfortunately this
41 // is a dangerously generic word to search for, since it will often match a
42 // name (not cardholder name) field before or after credit card fields. So
43 // we search for "name" only when we've already parsed at least one other
44 // credit card field and haven't yet parsed the expiration date (which
45 // usually appears at the end).
46 if (!credit_card_field->cardholder_) {
47 base::string16 name_pattern;
48 if (fields == 0 || credit_card_field->expiration_month_) {
49 // at beginning or end
50 name_pattern = base::UTF8ToUTF16(kNameOnCardRe);
51 } else {
52 name_pattern = base::UTF8ToUTF16(kNameOnCardContextualRe);
55 if (ParseField(scanner, name_pattern, &credit_card_field->cardholder_))
56 continue;
59 // Check for a credit card type (Visa, MasterCard, etc.) field.
60 if (!credit_card_field->type_ &&
61 ParseFieldSpecifics(scanner,
62 base::UTF8ToUTF16(kCardTypeRe),
63 MATCH_DEFAULT | MATCH_SELECT,
64 &credit_card_field->type_)) {
65 continue;
68 // We look for a card security code before we look for a credit card number
69 // and match the general term "number". The security code has a plethora of
70 // names; we've seen "verification #", "verification number", "card
71 // identification number", and others listed in the regex pattern used
72 // below.
73 // Note: Some sites use type="tel" for numerical inputs.
74 if (!credit_card_field->verification_ &&
75 ParseFieldSpecifics(scanner,
76 base::UTF8ToUTF16(kCardCvcRe),
77 MATCH_DEFAULT | MATCH_TELEPHONE | MATCH_PASSWORD,
78 &credit_card_field->verification_)) {
79 continue;
82 AutofillField* current_number_field;
83 if (ParseFieldSpecifics(scanner,
84 base::UTF8ToUTF16(kCardNumberRe),
85 MATCH_DEFAULT | MATCH_TELEPHONE,
86 &current_number_field)) {
87 // Avoid autofilling any credit card number field having very low or high
88 // |start_index| on the HTML form.
89 size_t start_index = 0;
90 if (!credit_card_field->numbers_.empty()) {
91 size_t last_number_field_size =
92 credit_card_field->numbers_.back()->credit_card_number_offset() +
93 credit_card_field->numbers_.back()->max_length;
95 // Distinguish between
96 // (a) one card split across multiple fields
97 // (b) multiple fields for multiple cards
98 // Treat this field as a part of the same card as the last field, except
99 // when doing so would cause overflow.
100 if (last_number_field_size < kMaxValidCardNumberSize)
101 start_index = last_number_field_size;
104 current_number_field->set_credit_card_number_offset(start_index);
105 credit_card_field->numbers_.push_back(current_number_field);
106 continue;
109 if (!credit_card_field->expiration_date_ &&
110 LowerCaseEqualsASCII(scanner->Cursor()->form_control_type, "month")) {
111 credit_card_field->expiration_date_ = scanner->Cursor();
112 credit_card_field->expiration_month_ = nullptr;
113 credit_card_field->expiration_year_ = nullptr;
114 scanner->Advance();
115 continue;
118 if (!credit_card_field->expiration_month_ &&
119 !credit_card_field->expiration_date_) {
120 // First try to parse split month/year expiration fields.
121 scanner->SaveCursor();
122 if (ParseFieldSpecifics(scanner,
123 base::UTF8ToUTF16(kExpirationMonthRe),
124 MATCH_DEFAULT | MATCH_SELECT,
125 &credit_card_field->expiration_month_) &&
126 ParseFieldSpecifics(scanner,
127 base::UTF8ToUTF16(kExpirationYearRe),
128 MATCH_DEFAULT | MATCH_SELECT,
129 &credit_card_field->expiration_year_)) {
130 continue;
133 // If that fails, try to parse a combined expiration field.
134 // Look for a 2-digit year first.
135 // We allow <select> fields, because they're used e.g. on qvc.com.
136 scanner->Rewind();
137 if (ParseFieldSpecifics(
138 scanner,
139 base::UTF8ToUTF16(kExpirationDate2DigitYearRe),
140 MATCH_LABEL | MATCH_VALUE | MATCH_TEXT | MATCH_SELECT,
141 &credit_card_field->expiration_date_)) {
142 credit_card_field->exp_year_type_ = CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR;
143 credit_card_field->expiration_month_ = nullptr;
144 continue;
147 if (ParseFieldSpecifics(
148 scanner,
149 base::UTF8ToUTF16(kExpirationDateRe),
150 MATCH_LABEL | MATCH_VALUE | MATCH_TEXT | MATCH_SELECT,
151 &credit_card_field->expiration_date_)) {
152 credit_card_field->expiration_month_ = nullptr;
153 continue;
156 if (credit_card_field->expiration_month_ &&
157 !credit_card_field->expiration_year_ &&
158 !credit_card_field->expiration_date_) {
159 // Parsed a month but couldn't parse a year; give up.
160 scanner->RewindTo(saved_cursor);
161 return NULL;
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 (ParseField(scanner, base::UTF8ToUTF16(kCardIgnoredRe), NULL))
171 continue;
173 break;
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 return credit_card_field.Pass();
182 // On some pages, the user selects a card type using radio buttons
183 // (e.g. test page Apple Store Billing.html). We can't handle that yet,
184 // so we treat the card type as optional for now.
185 // The existence of a number or cvc in combination with expiration date is
186 // a strong enough signal that this is a credit card. It is possible that
187 // the number and name were parsed in a separate part of the form. So if
188 // the cvc and date were found independently they are returned.
189 if ((!credit_card_field->numbers_.empty() ||
190 credit_card_field->verification_) &&
191 (credit_card_field->expiration_date_ ||
192 (credit_card_field->expiration_month_ &&
193 credit_card_field->expiration_year_))) {
194 return credit_card_field.Pass();
197 scanner->RewindTo(saved_cursor);
198 return NULL;
201 CreditCardField::CreditCardField()
202 : cardholder_(NULL),
203 cardholder_last_(NULL),
204 type_(NULL),
205 verification_(NULL),
206 expiration_month_(NULL),
207 expiration_year_(NULL),
208 expiration_date_(NULL),
209 exp_year_type_(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR) {
212 CreditCardField::~CreditCardField() {
215 bool CreditCardField::ClassifyField(ServerFieldTypeMap* map) const {
216 bool ok = true;
217 for (size_t index = 0; index < numbers_.size(); ++index) {
218 ok = ok && AddClassification(numbers_[index], CREDIT_CARD_NUMBER, map);
221 ok = ok && AddClassification(type_, CREDIT_CARD_TYPE, map);
222 ok = ok &&
223 AddClassification(verification_, CREDIT_CARD_VERIFICATION_CODE, map);
225 // If the heuristics detected first and last name in separate fields,
226 // then ignore both fields. Putting them into separate fields is probably
227 // wrong, because the credit card can also contain a middle name or middle
228 // initial.
229 if (cardholder_last_ == NULL)
230 ok = ok && AddClassification(cardholder_, CREDIT_CARD_NAME, map);
232 if (expiration_date_) {
233 DCHECK(!expiration_month_);
234 DCHECK(!expiration_year_);
235 ok =
236 ok && AddClassification(expiration_date_, GetExpirationYearType(), map);
237 } else {
238 ok = ok && AddClassification(expiration_month_, CREDIT_CARD_EXP_MONTH, map);
239 ok =
240 ok && AddClassification(expiration_year_, GetExpirationYearType(), map);
243 return ok;
246 ServerFieldType CreditCardField::GetExpirationYearType() const {
247 return (expiration_date_
248 ? exp_year_type_
249 : ((expiration_year_ && expiration_year_->max_length == 2)
250 ? CREDIT_CARD_EXP_2_DIGIT_YEAR
251 : CREDIT_CARD_EXP_4_DIGIT_YEAR));
254 } // namespace autofill