Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(130)

Side by Side Diff: components/autofill/core/browser/credit_card_field.cc

Issue 1001193002: Autofill: Better recognize credit card fields. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: address comments Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/autofill/core/browser/credit_card_field.h" 5 #include "components/autofill/core/browser/credit_card_field.h"
6 6
7 #include <stddef.h> 7 #include <stddef.h>
8 8
9 #include "base/memory/scoped_ptr.h" 9 #include "base/memory/scoped_ptr.h"
10 #include "base/strings/string16.h" 10 #include "base/strings/string16.h"
11 #include "base/strings/string_util.h" 11 #include "base/strings/string_util.h"
12 #include "base/strings/utf_string_conversions.h" 12 #include "base/strings/utf_string_conversions.h"
13 #include "components/autofill/core/browser/autofill_field.h" 13 #include "components/autofill/core/browser/autofill_field.h"
14 #include "components/autofill/core/browser/autofill_regex_constants.h" 14 #include "components/autofill/core/browser/autofill_regex_constants.h"
15 #include "components/autofill/core/browser/autofill_scanner.h" 15 #include "components/autofill/core/browser/autofill_scanner.h"
16 #include "components/autofill/core/browser/field_types.h" 16 #include "components/autofill/core/browser/field_types.h"
17 17
18 namespace autofill { 18 namespace autofill {
19 19
20 // Credit card numbers are at most 19 digits in length. 20 // Credit card numbers are at most 19 digits in length.
21 // [Ref: http://en.wikipedia.org/wiki/Bank_card_number] 21 // [Ref: http://en.wikipedia.org/wiki/Bank_card_number]
22 static const size_t kMaxValidCardNumberSize = 19; 22 static const size_t kMaxValidCardNumberSize = 19;
23 23
24 // static 24 // static
25 const int CreditCardField::kMatchNumAndTel =
26 MATCH_DEFAULT | MATCH_NUMBER | MATCH_TELEPHONE;
27
28 // static
29 const int CreditCardField::kMatchNumTelAndPass =
30 kMatchNumAndTel | MATCH_PASSWORD;
31
32 // static
25 scoped_ptr<FormField> CreditCardField::Parse(AutofillScanner* scanner) { 33 scoped_ptr<FormField> CreditCardField::Parse(AutofillScanner* scanner) {
26 if (scanner->IsEnd()) 34 if (scanner->IsEnd())
27 return nullptr; 35 return nullptr;
28 36
29 scoped_ptr<CreditCardField> credit_card_field(new CreditCardField); 37 scoped_ptr<CreditCardField> credit_card_field(new CreditCardField);
30 size_t saved_cursor = scanner->SaveCursor(); 38 size_t saved_cursor = scanner->SaveCursor();
31 39
32 // Credit card fields can appear in many different orders. 40 // Credit card fields can appear in many different orders.
33 // We loop until no more credit card related fields are found, see |break| at 41 // We loop until no more credit card related fields are found, see |break| at
34 // bottom of the loop. 42 // the bottom of the loop.
35 for (int fields = 0; !scanner->IsEnd(); ++fields) { 43 for (int fields = 0; !scanner->IsEnd(); ++fields) {
36 // Ignore gift card fields. 44 // Ignore gift card fields.
37 if (ParseField(scanner, base::UTF8ToUTF16(kGiftCardRe), nullptr)) 45 if (ParseField(scanner, base::UTF8ToUTF16(kGiftCardRe), nullptr))
38 break; 46 break;
39 47
40 if (!credit_card_field->cardholder_) { 48 if (!credit_card_field->cardholder_ &&
49 !LikelyCardNumberField(scanner) &&
50 !MayBeCardCVCField(scanner)) {
41 if (ParseField(scanner, 51 if (ParseField(scanner,
42 base::UTF8ToUTF16(kNameOnCardRe), 52 base::UTF8ToUTF16(kNameOnCardRe),
43 &credit_card_field->cardholder_)) { 53 &credit_card_field->cardholder_)) {
44 continue; 54 continue;
45 } 55 }
46 56
47 // Sometimes the cardholder field is just labeled "name". Unfortunately 57 // Sometimes the cardholder field is just labeled "name". Unfortunately
48 // this is a dangerously generic word to search for, since it will often 58 // this is a dangerously generic word to search for, since it will often
49 // match a name (not cardholder name) field before or after credit card 59 // match a name (not cardholder name) field before or after credit card
50 // fields. So we search for "name" only when we've already parsed at 60 // fields. So we search for "name" only when we've already parsed at
51 // least one other credit card field and haven't yet parsed the 61 // least one other credit card field and haven't yet parsed the
52 // expiration date (which usually appears at the end). 62 // expiration date (which usually appears at the end).
53 if (fields > 0 && 63 if (fields > 0 &&
54 !credit_card_field->expiration_month_ && 64 !credit_card_field->expiration_month_ &&
55 ParseField(scanner, 65 ParseField(scanner,
56 base::UTF8ToUTF16(kNameOnCardContextualRe), 66 base::UTF8ToUTF16(kNameOnCardContextualRe),
57 &credit_card_field->cardholder_)) { 67 &credit_card_field->cardholder_)) {
58 continue; 68 continue;
59 } 69 }
60 } 70 }
61 71
62 // Check for a credit card type (Visa, MasterCard, etc.) field. 72 // Check for a credit card type (Visa, MasterCard, etc.) field.
63 if (!credit_card_field->type_ && 73 if (!credit_card_field->type_ && LikelyCardType(scanner)) {
64 ParseFieldSpecifics(scanner, 74 credit_card_field->type_ = scanner->Cursor();
65 base::UTF8ToUTF16(kCardTypeRe), 75 scanner->Advance();
66 MATCH_DEFAULT | MATCH_SELECT,
67 &credit_card_field->type_)) {
68 continue; 76 continue;
69 } 77 }
70 78
71 // We look for a card security code before we look for a credit card number 79 // We look for a card security code before we look for a credit card number
72 // and match the general term "number". The security code has a plethora of 80 // and match the general term "number". The security code has a plethora of
73 // names; we've seen "verification #", "verification number", "card 81 // names; we've seen "verification #", "verification number", "card
74 // identification number", and others listed in the regex pattern used 82 // identification number", and others listed in the regex pattern used
75 // below. 83 // below.
76 // Note: Some sites use type="tel" or type="number" for numerical inputs. 84 // Note: Some sites use type="tel" or type="number" for numerical inputs.
77 const int kMatchNumAndTel = MATCH_DEFAULT | MATCH_NUMBER | MATCH_TELEPHONE;
78 if (!credit_card_field->verification_ && 85 if (!credit_card_field->verification_ &&
86 !LikelyCardNumberField(scanner) &&
79 ParseFieldSpecifics(scanner, 87 ParseFieldSpecifics(scanner,
80 base::UTF8ToUTF16(kCardCvcRe), 88 base::UTF8ToUTF16(kCardCvcRe),
81 kMatchNumAndTel | MATCH_PASSWORD, 89 kMatchNumTelAndPass,
82 &credit_card_field->verification_)) { 90 &credit_card_field->verification_)) {
83 continue; 91 continue;
84 } 92 }
85 93
86 AutofillField* current_number_field; 94 AutofillField* current_number_field;
87 if (ParseFieldSpecifics(scanner, 95 if (ParseFieldSpecifics(scanner,
88 base::UTF8ToUTF16(kCardNumberRe), 96 base::UTF8ToUTF16(kCardNumberRe),
89 kMatchNumAndTel, 97 kMatchNumAndTel,
90 &current_number_field)) { 98 &current_number_field)) {
91 // Avoid autofilling any credit card number field having very low or high 99 // Avoid autofilling any credit card number field having very low or high
(...skipping 22 matching lines...) Expand all
114 continue; 122 continue;
115 123
116 if (credit_card_field->expiration_month_ && 124 if (credit_card_field->expiration_month_ &&
117 !credit_card_field->expiration_year_ && 125 !credit_card_field->expiration_year_ &&
118 !credit_card_field->expiration_date_) { 126 !credit_card_field->expiration_date_) {
119 // Parsed a month but couldn't parse a year; give up. 127 // Parsed a month but couldn't parse a year; give up.
120 scanner->RewindTo(saved_cursor); 128 scanner->RewindTo(saved_cursor);
121 return nullptr; 129 return nullptr;
122 } 130 }
123 131
124 // Some pages (e.g. ExpediaBilling.html) have a "card description"
125 // field; we parse this field but ignore it.
126 // We also ignore any other fields within a credit card block that
127 // start with "card", under the assumption that they are related to
128 // the credit card section being processed but are uninteresting to us.
129 if (ParseField(scanner, base::UTF8ToUTF16(kCardIgnoredRe), nullptr))
130 continue;
131
132 break; 132 break;
133 } 133 }
134 134
135 // Some pages have a billing address field after the cardholder name field. 135 // Some pages have a billing address field after the cardholder name field.
136 // For that case, allow only just the cardholder name field. The remaining 136 // For that case, allow only just the cardholder name field. The remaining
137 // CC fields will be picked up in a following CreditCardField. 137 // CC fields will be picked up in a following CreditCardField.
138 if (credit_card_field->cardholder_) 138 if (credit_card_field->cardholder_)
139 return credit_card_field.Pass(); 139 return credit_card_field.Pass();
140 140
141 // On some pages, the user selects a card type using radio buttons 141 // On some pages, the user selects a card type using radio buttons
142 // (e.g. test page Apple Store Billing.html). We can't handle that yet, 142 // (e.g. test page Apple Store Billing.html). We can't handle that yet,
143 // so we treat the card type as optional for now. 143 // so we treat the card type as optional for now.
144 // The existence of a number or cvc in combination with expiration date is 144 // The existence of a number or cvc in combination with expiration date is
145 // a strong enough signal that this is a credit card. It is possible that 145 // a strong enough signal that this is a credit card. It is possible that
146 // the number and name were parsed in a separate part of the form. So if 146 // the number and name were parsed in a separate part of the form. So if
147 // the cvc and date were found independently they are returned. 147 // the cvc and date were found independently they are returned.
148 bool has_cc_number_or_verification = (credit_card_field->verification_ || 148 bool has_cc_number_or_verification = (credit_card_field->verification_ ||
149 !credit_card_field->numbers_.empty()); 149 !credit_card_field->numbers_.empty());
150 bool has_date_or_mm_yy = (credit_card_field->expiration_date_ || 150 bool has_date_or_mm_yy = (credit_card_field->expiration_date_ ||
151 (credit_card_field->expiration_month_ && 151 (credit_card_field->expiration_month_ &&
152 credit_card_field->expiration_year_)); 152 credit_card_field->expiration_year_));
153 if (has_cc_number_or_verification && has_date_or_mm_yy) 153 if (has_cc_number_or_verification && has_date_or_mm_yy)
154 return credit_card_field.Pass(); 154 return credit_card_field.Pass();
155 155
156 scanner->RewindTo(saved_cursor); 156 scanner->RewindTo(saved_cursor);
157 return nullptr; 157 return nullptr;
158 } 158 }
159 159
160 // static
161 bool CreditCardField::LikelyCardNumberField(AutofillScanner* scanner) {
162 if (scanner->IsEnd())
163 return false;
164
165 // Look for a form of the right type with a 16 to 19 digit maxlength.
166 AutofillField* field = scanner->Cursor();
167 return (MatchesFormControlType(field->form_control_type, kMatchNumAndTel) &&
168 field->max_length >= 16 &&
169 field->max_length <= kMaxValidCardNumberSize);
170 }
171
172 // static
173 bool CreditCardField::LikelyCardType(AutofillScanner* scanner) {
174 if (scanner->IsEnd())
175 return false;
176
177 AutofillField* field = scanner->Cursor();
178 if (MatchesFormControlType(field->form_control_type, MATCH_SELECT)) {
179 // VISA and Mastercard are oftentimes branded as is in other countries, so
180 // translations are probably not needed.
181 const char* const kCardBrands[] = {
182 "visa",
183 "mastercard",
184 };
185 for (const char* brand : kCardBrands) {
186 if (AutofillField::FindValueInCreditTypeSelectControl(
187 *field, base::ASCIIToUTF16(brand), nullptr)) {
188 return true;
189 }
190 }
191 }
192 return false;
193 }
194
195 // static
196 bool CreditCardField::MayBeCardCVCField(AutofillScanner* scanner) {
197 if (scanner->IsEnd())
198 return false;
199
200 // Look for a form of the right type with a 3 or 4 digit maxlength.
201 AutofillField* field = scanner->Cursor();
202 return (MatchesFormControlType(field->form_control_type,
203 kMatchNumTelAndPass) &&
204 (field->max_length == 3 || field->max_length == 4));
205 }
206
160 CreditCardField::CreditCardField() 207 CreditCardField::CreditCardField()
161 : cardholder_(nullptr), 208 : cardholder_(nullptr),
162 cardholder_last_(nullptr), 209 cardholder_last_(nullptr),
163 type_(nullptr), 210 type_(nullptr),
164 verification_(nullptr), 211 verification_(nullptr),
165 expiration_month_(nullptr), 212 expiration_month_(nullptr),
166 expiration_year_(nullptr), 213 expiration_year_(nullptr),
167 expiration_date_(nullptr), 214 expiration_date_(nullptr),
168 exp_year_type_(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR) { 215 exp_year_type_(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR) {
169 } 216 }
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after
268 315
269 ServerFieldType CreditCardField::GetExpirationYearType() const { 316 ServerFieldType CreditCardField::GetExpirationYearType() const {
270 return (expiration_date_ 317 return (expiration_date_
271 ? exp_year_type_ 318 ? exp_year_type_
272 : ((expiration_year_ && expiration_year_->max_length == 2) 319 : ((expiration_year_ && expiration_year_->max_length == 2)
273 ? CREDIT_CARD_EXP_2_DIGIT_YEAR 320 ? CREDIT_CARD_EXP_2_DIGIT_YEAR
274 : CREDIT_CARD_EXP_4_DIGIT_YEAR)); 321 : CREDIT_CARD_EXP_4_DIGIT_YEAR));
275 } 322 }
276 323
277 } // namespace autofill 324 } // namespace autofill
OLDNEW
« no previous file with comments | « components/autofill/core/browser/credit_card_field.h ('k') | components/autofill/core/browser/credit_card_field_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698