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

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: after 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_regexes.h"
15 #include "components/autofill/core/browser/autofill_scanner.h" 16 #include "components/autofill/core/browser/autofill_scanner.h"
16 #include "components/autofill/core/browser/field_types.h" 17 #include "components/autofill/core/browser/field_types.h"
17 18
18 namespace autofill { 19 namespace autofill {
19 20
20 // Credit card numbers are at most 19 digits in length. 21 // Credit card numbers are at most 19 digits in length.
21 // [Ref: http://en.wikipedia.org/wiki/Bank_card_number] 22 // [Ref: http://en.wikipedia.org/wiki/Bank_card_number]
22 static const size_t kMaxValidCardNumberSize = 19; 23 static const size_t kMaxValidCardNumberSize = 19;
23 24
24 // static 25 // static
26 const int CreditCardField::kMatchNumAndTel =
27 MATCH_DEFAULT | MATCH_NUMBER | MATCH_TELEPHONE;
28
29 // static
30 const int CreditCardField::kMatchNumTelAndPass =
31 kMatchNumAndTel | MATCH_PASSWORD;
32
33 // static
25 scoped_ptr<FormField> CreditCardField::Parse(AutofillScanner* scanner) { 34 scoped_ptr<FormField> CreditCardField::Parse(AutofillScanner* scanner) {
26 if (scanner->IsEnd()) 35 if (scanner->IsEnd())
27 return nullptr; 36 return nullptr;
28 37
29 scoped_ptr<CreditCardField> credit_card_field(new CreditCardField); 38 scoped_ptr<CreditCardField> credit_card_field(new CreditCardField);
30 size_t saved_cursor = scanner->SaveCursor(); 39 size_t saved_cursor = scanner->SaveCursor();
31 40
32 // Credit card fields can appear in many different orders. 41 // Credit card fields can appear in many different orders.
33 // We loop until no more credit card related fields are found, see |break| at 42 // We loop until no more credit card related fields are found, see |break| at
34 // bottom of the loop. 43 // the bottom of the loop.
35 for (int fields = 0; !scanner->IsEnd(); ++fields) { 44 for (int fields = 0; !scanner->IsEnd(); ++fields) {
36 // Ignore gift card fields. 45 // Ignore gift card fields.
37 if (ParseField(scanner, base::UTF8ToUTF16(kGiftCardRe), nullptr)) 46 if (ParseField(scanner, base::UTF8ToUTF16(kGiftCardRe), nullptr))
38 break; 47 break;
39 48
40 if (!credit_card_field->cardholder_) { 49 if (!credit_card_field->cardholder_ &&
50 !LikelyCardNumberField(scanner) &&
Evan Stade 2015/03/13 20:57:13 Why are you checking !LikelyCardNumberField in sev
Lei Zhang 2015/03/13 23:11:00 Changing the order may creates more of the same pr
51 !MayBeCardCVCField(scanner)) {
41 if (ParseField(scanner, 52 if (ParseField(scanner,
42 base::UTF8ToUTF16(kNameOnCardRe), 53 base::UTF8ToUTF16(kNameOnCardRe),
43 &credit_card_field->cardholder_)) { 54 &credit_card_field->cardholder_)) {
44 continue; 55 continue;
45 } 56 }
46 57
47 // Sometimes the cardholder field is just labeled "name". Unfortunately 58 // Sometimes the cardholder field is just labeled "name". Unfortunately
48 // this is a dangerously generic word to search for, since it will often 59 // 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 60 // 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 61 // 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 62 // least one other credit card field and haven't yet parsed the
52 // expiration date (which usually appears at the end). 63 // expiration date (which usually appears at the end).
53 if (fields > 0 && 64 if (fields > 0 &&
54 !credit_card_field->expiration_month_ && 65 !credit_card_field->expiration_month_ &&
55 ParseField(scanner, 66 ParseField(scanner,
56 base::UTF8ToUTF16(kNameOnCardContextualRe), 67 base::UTF8ToUTF16(kNameOnCardContextualRe),
57 &credit_card_field->cardholder_)) { 68 &credit_card_field->cardholder_)) {
58 continue; 69 continue;
59 } 70 }
60 } 71 }
61 72
62 // Check for a credit card type (Visa, MasterCard, etc.) field. 73 // Check for a credit card type (Visa, MasterCard, etc.) field.
63 if (!credit_card_field->type_ && 74 // This used to match text fields, and had a regex for the field name/label,
64 ParseFieldSpecifics(scanner, 75 // but LikelyCardType() works as well and produces fewer incorrect matches.
Evan Stade 2015/03/13 20:57:13 this extra documentation is perhaps relevant to th
Lei Zhang 2015/03/13 23:11:00 Removed.
65 base::UTF8ToUTF16(kCardTypeRe), 76 if (!credit_card_field->type_ && LikelyCardType(scanner)) {
66 MATCH_DEFAULT | MATCH_SELECT, 77 credit_card_field->type_ = scanner->Cursor();
67 &credit_card_field->type_)) { 78 scanner->Advance();
68 continue; 79 continue;
69 } 80 }
70 81
71 // We look for a card security code before we look for a credit card number 82 // 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 83 // and match the general term "number". The security code has a plethora of
73 // names; we've seen "verification #", "verification number", "card 84 // names; we've seen "verification #", "verification number", "card
74 // identification number", and others listed in the regex pattern used 85 // identification number", and others listed in the regex pattern used
75 // below. 86 // below.
76 // Note: Some sites use type="tel" or type="number" for numerical inputs. 87 // 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_ && 88 if (!credit_card_field->verification_ &&
89 !LikelyCardNumberField(scanner) &&
79 ParseFieldSpecifics(scanner, 90 ParseFieldSpecifics(scanner,
80 base::UTF8ToUTF16(kCardCvcRe), 91 base::UTF8ToUTF16(kCardCvcRe),
81 kMatchNumAndTel | MATCH_PASSWORD, 92 kMatchNumTelAndPass,
82 &credit_card_field->verification_)) { 93 &credit_card_field->verification_)) {
83 continue; 94 continue;
84 } 95 }
85 96
86 AutofillField* current_number_field; 97 AutofillField* current_number_field;
87 if (ParseFieldSpecifics(scanner, 98 if (ParseFieldSpecifics(scanner,
88 base::UTF8ToUTF16(kCardNumberRe), 99 base::UTF8ToUTF16(kCardNumberRe),
89 kMatchNumAndTel, 100 kMatchNumAndTel,
90 &current_number_field)) { 101 &current_number_field)) {
91 // Avoid autofilling any credit card number field having very low or high 102 // Avoid autofilling any credit card number field having very low or high
(...skipping 22 matching lines...) Expand all
114 continue; 125 continue;
115 126
116 if (credit_card_field->expiration_month_ && 127 if (credit_card_field->expiration_month_ &&
117 !credit_card_field->expiration_year_ && 128 !credit_card_field->expiration_year_ &&
118 !credit_card_field->expiration_date_) { 129 !credit_card_field->expiration_date_) {
119 // Parsed a month but couldn't parse a year; give up. 130 // Parsed a month but couldn't parse a year; give up.
120 scanner->RewindTo(saved_cursor); 131 scanner->RewindTo(saved_cursor);
121 return nullptr; 132 return nullptr;
122 } 133 }
123 134
124 // Some pages (e.g. ExpediaBilling.html) have a "card description"
Lei Zhang 2015/03/13 08:15:11 With the other changes, this reached the start of
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; 135 break;
133 } 136 }
134 137
135 // Some pages have a billing address field after the cardholder name field. 138 // 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 139 // For that case, allow only just the cardholder name field. The remaining
137 // CC fields will be picked up in a following CreditCardField. 140 // CC fields will be picked up in a following CreditCardField.
138 if (credit_card_field->cardholder_) 141 if (credit_card_field->cardholder_)
139 return credit_card_field.Pass(); 142 return credit_card_field.Pass();
140 143
141 // On some pages, the user selects a card type using radio buttons 144 // 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, 145 // (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. 146 // so we treat the card type as optional for now.
144 // The existence of a number or cvc in combination with expiration date is 147 // 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 148 // 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 149 // 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. 150 // the cvc and date were found independently they are returned.
148 bool has_cc_number_or_verification = (credit_card_field->verification_ || 151 bool has_cc_number_or_verification = (credit_card_field->verification_ ||
149 !credit_card_field->numbers_.empty()); 152 !credit_card_field->numbers_.empty());
150 bool has_date_or_mm_yy = (credit_card_field->expiration_date_ || 153 bool has_date_or_mm_yy = (credit_card_field->expiration_date_ ||
151 (credit_card_field->expiration_month_ && 154 (credit_card_field->expiration_month_ &&
152 credit_card_field->expiration_year_)); 155 credit_card_field->expiration_year_));
153 if (has_cc_number_or_verification && has_date_or_mm_yy) 156 if (has_cc_number_or_verification && has_date_or_mm_yy)
154 return credit_card_field.Pass(); 157 return credit_card_field.Pass();
155 158
156 scanner->RewindTo(saved_cursor); 159 scanner->RewindTo(saved_cursor);
157 return nullptr; 160 return nullptr;
158 } 161 }
159 162
163 // static
164 bool CreditCardField::LikelyCardNumberField(AutofillScanner* scanner) {
165 if (scanner->IsEnd())
166 return false;
167
168 // Look for a form of the right type with a 16 to 19 digit maxlength.
Evan Stade 2015/03/13 20:57:13 the minimum card has less than 16 digits
Lei Zhang 2015/03/13 23:11:00 Yes, but if a website made it less than 16, then t
169 AutofillField* field = scanner->Cursor();
170 return (MatchesFormControlType(field->form_control_type, kMatchNumAndTel) &&
171 field->max_length >= 16 &&
172 field->max_length <= kMaxValidCardNumberSize);
173 }
174
175 // static
176 bool CreditCardField::LikelyCardType(AutofillScanner* scanner) {
177 if (scanner->IsEnd())
178 return false;
179
180 AutofillField* field = scanner->Cursor();
181 if (MatchesFormControlType(field->form_control_type, MATCH_SELECT)) {
Evan Stade 2015/03/13 20:57:13 I feel like this code should go somewhere we can r
Lei Zhang 2015/03/13 23:11:00 Done.
182 // VISA and Mastercard are oftentimes branded as is in other countries, so
183 // translations are probably not needed.
184 const char* const kCardBrands[] = {
185 "visa",
186 "mastercard",
187 };
188 for (const char* brand : kCardBrands) {
189 for (const auto& value : field->option_values) {
190 if (MatchesPattern(value, base::ASCIIToUTF16(brand)))
191 return true;
192 }
193 for (const auto& content : field->option_contents) {
194 if (MatchesPattern(content, base::ASCIIToUTF16(brand)))
195 return true;
196 }
197 }
198 }
199 return false;
200 }
201
202 // static
203 bool CreditCardField::MayBeCardCVCField(AutofillScanner* scanner) {
204 if (scanner->IsEnd())
205 return false;
206
207 // Look for a form of the right type with a 3 or 4 digit maxlength.
208 AutofillField* field = scanner->Cursor();
209 return (MatchesFormControlType(field->form_control_type,
210 kMatchNumTelAndPass) &&
211 (field->max_length == 3 || field->max_length == 4));
212 }
213
160 CreditCardField::CreditCardField() 214 CreditCardField::CreditCardField()
161 : cardholder_(nullptr), 215 : cardholder_(nullptr),
162 cardholder_last_(nullptr), 216 cardholder_last_(nullptr),
163 type_(nullptr), 217 type_(nullptr),
164 verification_(nullptr), 218 verification_(nullptr),
165 expiration_month_(nullptr), 219 expiration_month_(nullptr),
166 expiration_year_(nullptr), 220 expiration_year_(nullptr),
167 expiration_date_(nullptr), 221 expiration_date_(nullptr),
168 exp_year_type_(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR) { 222 exp_year_type_(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR) {
169 } 223 }
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after
268 322
269 ServerFieldType CreditCardField::GetExpirationYearType() const { 323 ServerFieldType CreditCardField::GetExpirationYearType() const {
270 return (expiration_date_ 324 return (expiration_date_
271 ? exp_year_type_ 325 ? exp_year_type_
272 : ((expiration_year_ && expiration_year_->max_length == 2) 326 : ((expiration_year_ && expiration_year_->max_length == 2)
273 ? CREDIT_CARD_EXP_2_DIGIT_YEAR 327 ? CREDIT_CARD_EXP_2_DIGIT_YEAR
274 : CREDIT_CARD_EXP_4_DIGIT_YEAR)); 328 : CREDIT_CARD_EXP_4_DIGIT_YEAR));
275 } 329 }
276 330
277 } // namespace autofill 331 } // namespace autofill
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698