OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 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 "chrome/browser/autofill/credit_card_field.h" | 5 #include "chrome/browser/autofill/credit_card_field.h" |
6 | 6 |
7 #include <stddef.h> | 7 #include <stddef.h> |
8 | 8 |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "base/memory/scoped_ptr.h" | 10 #include "base/memory/scoped_ptr.h" |
11 #include "base/string16.h" | 11 #include "base/string16.h" |
12 #include "base/string_util.h" | 12 #include "base/string_util.h" |
13 #include "base/utf_string_conversions.h" | 13 #include "base/utf_string_conversions.h" |
14 #include "chrome/browser/autofill/autofill_field.h" | 14 #include "chrome/browser/autofill/autofill_field.h" |
| 15 #include "chrome/browser/autofill/autofill_regex_constants.h" |
15 #include "chrome/browser/autofill/autofill_scanner.h" | 16 #include "chrome/browser/autofill/autofill_scanner.h" |
16 #include "chrome/browser/autofill/field_types.h" | 17 #include "chrome/browser/autofill/field_types.h" |
17 #include "ui/base/l10n/l10n_util.h" | 18 #include "ui/base/l10n/l10n_util.h" |
18 | 19 |
19 namespace { | |
20 | |
21 // The UTF-8 version of these regular expressions are in | |
22 // regular_expressions.txt. | |
23 const char kNameOnCardRe[] = | |
24 "card.?holder|name.?on.?card|ccname|ccfullname|owner" | |
25 // de-DE | |
26 "|karteninhaber" | |
27 // es | |
28 "|nombre.*tarjeta" | |
29 // fr-FR | |
30 "|nom.*carte" | |
31 // it-IT | |
32 "|nome.*cart" | |
33 // ja-JP | |
34 "|\xe5\x90\x8d\xe5\x89\x8d" | |
35 // ru | |
36 "|\xd0\x98\xd0\xbc\xd1\x8f.*\xd0\xba\xd0\xb0\xd1\x80\xd1\x82\xd1\x8b" | |
37 // zh-CN | |
38 "|\xe4\xbf\xa1\xe7\x94\xa8\xe5\x8d\xa1\xe5\xbc\x80\xe6\x88\xb7\xe5\x90\x8d" | |
39 "|\xe5\xbc\x80\xe6\x88\xb7\xe5\x90\x8d|\xe6\x8c\x81\xe5\x8d\xa1\xe4" | |
40 "\xba\xba\xe5\xa7\x93\xe5\x90\x8d" | |
41 // zh-TW | |
42 "|\xe6\x8c\x81\xe5\x8d\xa1\xe4\xba\xba\xe5\xa7\x93\xe5\x90\x8d"; | |
43 const char kNameOnCardContextualRe[] = | |
44 "name"; | |
45 const char kCardNumberRe[] = | |
46 "card.?number|card.?#|card.?no|ccnum|acctnum" | |
47 // de-DE | |
48 "|nummer" | |
49 // es | |
50 "|credito|numero|n\xc3\xbamero" | |
51 // fr-FR | |
52 "|num\xc3\xa9ro" | |
53 // ja-JP | |
54 "|\xe3\x82\xab\xe3\x83\xbc\xe3\x83\x89\xe7\x95\xaa\xe5\x8f\xb7" | |
55 // ru | |
56 "|\xd0\x9d\xd0\xbe\xd0\xbc\xd0\xb5\xd1\x80.*\xd0\xba\xd0\xb0\xd1\x80\xd1" | |
57 "\x82\xd1\x8b" | |
58 // zh-CN | |
59 "|\xe4\xbf\xa1\xe7\x94\xa8\xe5\x8d\xa1\xe5\x8f\xb7|\xe4\xbf\xa1\xe7\x94" | |
60 "\xa8\xe5\x8d\xa1\xe5\x8f\xb7\xe7\xa0\x81" | |
61 // zh-TW | |
62 "|\xe4\xbf\xa1\xe7\x94\xa8\xe5\x8d\xa1\xe5\x8d\xa1\xe8\x99\x9f" | |
63 // ko-KR | |
64 "|\xec\xb9\xb4\xeb\x93\x9c"; | |
65 const char kCardCvcRe[] = | |
66 "verification|card identification|security code|cvn|cvv|cvc|csc"; | |
67 | |
68 // "Expiration date" is the most common label here, but some pages have | |
69 // "Expires", "exp. date" or "exp. month" and "exp. year". We also look | |
70 // for the field names ccmonth and ccyear, which appear on at least 4 of | |
71 // our test pages. | |
72 | |
73 // On at least one page (The China Shop2.html) we find only the labels | |
74 // "month" and "year". So for now we match these words directly; we'll | |
75 // see if this turns out to be too general. | |
76 | |
77 // Toolbar Bug 51451: indeed, simply matching "month" is too general for | |
78 // https://rps.fidelity.com/ftgw/rps/RtlCust/CreatePIN/Init. | |
79 // Instead, we match only words beginning with "month". | |
80 const char kExpirationMonthRe[] = | |
81 "expir|exp.*mo|exp.*date|ccmonth" | |
82 // de-DE | |
83 "|gueltig|g\xc3\xbcltig|monat" | |
84 // es | |
85 "|fecha" | |
86 // fr-FR | |
87 "|date.*exp" | |
88 // it-IT | |
89 "|scadenza" | |
90 // ja-JP | |
91 "|\xe6\x9c\x89\xe5\x8a\xb9\xe6\x9c\x9f\xe9\x99\x90" | |
92 // pt-BR, pt-PT | |
93 "|validade" | |
94 // ru | |
95 "|\xd0\xa1\xd1\x80\xd0\xbe\xd0\xba \xd0\xb4\xd0\xb5\xd0\xb9\xd1\x81\xd1" | |
96 "\x82\xd0\xb2\xd0\xb8\xd1\x8f \xd0\xba\xd0\xb0\xd1\x80\xd1\x82\xd1\x8b" | |
97 // zh-CN | |
98 "|\xe6\x9c\x88"; | |
99 const char kExpirationYearRe[] = | |
100 "exp|^/|year" | |
101 // de-DE | |
102 "|ablaufdatum|gueltig|g\xc3\xbcltig|yahr" | |
103 // es | |
104 "|fecha" | |
105 // it-IT | |
106 "|scadenza" | |
107 // ja-JP | |
108 "|\xe6\x9c\x89\xe5\x8a\xb9\xe6\x9c\x9f\xe9\x99\x90" | |
109 // pt-BR, pt-PT | |
110 "|validade" | |
111 // ru | |
112 "|\xd0\xa1\xd1\x80\xd0\xbe\xd0\xba \xd0\xb4\xd0\xb5\xd0\xb9\xd1\x81\xd1" | |
113 "\x82\xd0\xb2\xd0\xb8\xd1\x8f \xd0\xba\xd0\xb0\xd1\x80\xd1\x82\xd1\x8b" | |
114 // zh-CN | |
115 "|\xe5\xb9\xb4|\xe6\x9c\x89\xe6\x95\x88\xe6\x9c\x9f"; | |
116 | |
117 // This regex is a little bit nasty, but it is simply requiring exactly two | |
118 // adjacent y's. | |
119 const char kExpirationDate2DigitYearRe[] = | |
120 "exp.*date.*[^y]yy([^y]|$)"; | |
121 const char kExpirationDateRe[] = | |
122 "expir|exp.*date" | |
123 // de-DE | |
124 "|gueltig|g\xc3\xbcltig" | |
125 // es | |
126 "|fecha" | |
127 // fr-FR | |
128 "|date.*exp" | |
129 // it-IT | |
130 "|scadenza" | |
131 // ja-JP | |
132 "|\xe6\x9c\x89\xe5\x8a\xb9\xe6\x9c\x9f\xe9\x99\x90" | |
133 // pt-BR, pt-PT | |
134 "|validade" | |
135 // ru | |
136 "|\xd0\xa1\xd1\x80\xd0\xbe\xd0\xba \xd0\xb4\xd0\xb5\xd0\xb9\xd1\x81\xd1" | |
137 "\x82\xd0\xb2\xd0\xb8\xd1\x8f\xd0\xba\xd0\xb0\xd1\x80\xd1\x82\xd1\x8b"; | |
138 const char kCardIgnoredRe[] = | |
139 "^card"; | |
140 | |
141 } // namespace | |
142 | |
143 // static | 20 // static |
144 FormField* CreditCardField::Parse(AutofillScanner* scanner) { | 21 FormField* CreditCardField::Parse(AutofillScanner* scanner) { |
145 if (scanner->IsEnd()) | 22 if (scanner->IsEnd()) |
146 return NULL; | 23 return NULL; |
147 | 24 |
148 scoped_ptr<CreditCardField> credit_card_field(new CreditCardField); | 25 scoped_ptr<CreditCardField> credit_card_field(new CreditCardField); |
149 size_t saved_cursor = scanner->SaveCursor(); | 26 size_t saved_cursor = scanner->SaveCursor(); |
150 | 27 |
151 // Credit card fields can appear in many different orders. | 28 // Credit card fields can appear in many different orders. |
152 // We loop until no more credit card related fields are found, see |break| at | 29 // We loop until no more credit card related fields are found, see |break| at |
153 // bottom of the loop. | 30 // bottom of the loop. |
154 for (int fields = 0; !scanner->IsEnd(); ++fields) { | 31 for (int fields = 0; !scanner->IsEnd(); ++fields) { |
155 // Sometimes the cardholder field is just labeled "name". Unfortunately this | 32 // Sometimes the cardholder field is just labeled "name". Unfortunately this |
156 // is a dangerously generic word to search for, since it will often match a | 33 // is a dangerously generic word to search for, since it will often match a |
157 // name (not cardholder name) field before or after credit card fields. So | 34 // name (not cardholder name) field before or after credit card fields. So |
158 // we search for "name" only when we've already parsed at least one other | 35 // we search for "name" only when we've already parsed at least one other |
159 // credit card field and haven't yet parsed the expiration date (which | 36 // credit card field and haven't yet parsed the expiration date (which |
160 // usually appears at the end). | 37 // usually appears at the end). |
161 if (credit_card_field->cardholder_ == NULL) { | 38 if (credit_card_field->cardholder_ == NULL) { |
162 string16 name_pattern; | 39 string16 name_pattern; |
163 if (fields == 0 || credit_card_field->expiration_month_) { | 40 if (fields == 0 || credit_card_field->expiration_month_) { |
164 // at beginning or end | 41 // at beginning or end |
165 name_pattern = UTF8ToUTF16(kNameOnCardRe); | 42 name_pattern = UTF8ToUTF16(autofill::kNameOnCardRe); |
166 } else { | 43 } else { |
167 name_pattern = UTF8ToUTF16(kNameOnCardContextualRe); | 44 name_pattern = UTF8ToUTF16(autofill::kNameOnCardContextualRe); |
168 } | 45 } |
169 | 46 |
170 if (ParseField(scanner, name_pattern, &credit_card_field->cardholder_)) | 47 if (ParseField(scanner, name_pattern, &credit_card_field->cardholder_)) |
171 continue; | 48 continue; |
172 | 49 |
173 // As a hard-coded hack for Expedia's billing pages (expedia_checkout.html | 50 // As a hard-coded hack for Expedia's billing pages (expedia_checkout.html |
174 // and ExpediaBilling.html in our test suite), recognize separate fields | 51 // and ExpediaBilling.html in our test suite), recognize separate fields |
175 // for the cardholder's first and last name if they have the labels "cfnm" | 52 // for the cardholder's first and last name if they have the labels "cfnm" |
176 // and "clnm". | 53 // and "clnm". |
177 scanner->SaveCursor(); | 54 scanner->SaveCursor(); |
178 const AutofillField* first; | 55 const AutofillField* first; |
179 if (ParseField(scanner, ASCIIToUTF16("^cfnm"), &first) && | 56 if (ParseField(scanner, ASCIIToUTF16("^cfnm"), &first) && |
180 ParseField(scanner, ASCIIToUTF16("^clnm"), | 57 ParseField(scanner, ASCIIToUTF16("^clnm"), |
181 &credit_card_field->cardholder_last_)) { | 58 &credit_card_field->cardholder_last_)) { |
182 credit_card_field->cardholder_ = first; | 59 credit_card_field->cardholder_ = first; |
183 continue; | 60 continue; |
184 } | 61 } |
185 scanner->Rewind(); | 62 scanner->Rewind(); |
186 } | 63 } |
187 | 64 |
188 // We look for a card security code before we look for a credit | 65 // We look for a card security code before we look for a credit |
189 // card number and match the general term "number". The security code | 66 // card number and match the general term "number". The security code |
190 // has a plethora of names; we've seen "verification #", | 67 // has a plethora of names; we've seen "verification #", |
191 // "verification number", "card identification number" and others listed | 68 // "verification number", "card identification number" and others listed |
192 // in the |pattern| below. | 69 // in the |pattern| below. |
193 string16 pattern = UTF8ToUTF16(kCardCvcRe); | 70 string16 pattern = UTF8ToUTF16(autofill::kCardCvcRe); |
194 if (!credit_card_field->verification_ && | 71 if (!credit_card_field->verification_ && |
195 ParseField(scanner, pattern, &credit_card_field->verification_)) { | 72 ParseField(scanner, pattern, &credit_card_field->verification_)) { |
196 continue; | 73 continue; |
197 } | 74 } |
198 // TODO(jhawkins): Parse the type select control. | 75 // TODO(jhawkins): Parse the type select control. |
199 | 76 |
200 pattern = UTF8ToUTF16(kCardNumberRe); | 77 pattern = UTF8ToUTF16(autofill::kCardNumberRe); |
201 if (!credit_card_field->number_ && | 78 if (!credit_card_field->number_ && |
202 ParseField(scanner, pattern, &credit_card_field->number_)) { | 79 ParseField(scanner, pattern, &credit_card_field->number_)) { |
203 continue; | 80 continue; |
204 } | 81 } |
205 | 82 |
206 if (LowerCaseEqualsASCII(scanner->Cursor()->form_control_type, "month")) { | 83 if (LowerCaseEqualsASCII(scanner->Cursor()->form_control_type, "month")) { |
207 credit_card_field->expiration_month_ = scanner->Cursor(); | 84 credit_card_field->expiration_month_ = scanner->Cursor(); |
208 scanner->Advance(); | 85 scanner->Advance(); |
209 } else { | 86 } else { |
210 // First try to parse split month/year expiration fields. | 87 // First try to parse split month/year expiration fields. |
211 scanner->SaveCursor(); | 88 scanner->SaveCursor(); |
212 pattern = UTF8ToUTF16(kExpirationMonthRe); | 89 pattern = UTF8ToUTF16(autofill::kExpirationMonthRe); |
213 if (!credit_card_field->expiration_month_ && | 90 if (!credit_card_field->expiration_month_ && |
214 ParseFieldSpecifics(scanner, pattern, MATCH_DEFAULT | MATCH_SELECT, | 91 ParseFieldSpecifics(scanner, pattern, MATCH_DEFAULT | MATCH_SELECT, |
215 &credit_card_field->expiration_month_)) { | 92 &credit_card_field->expiration_month_)) { |
216 pattern = UTF8ToUTF16(kExpirationYearRe); | 93 pattern = UTF8ToUTF16(autofill::kExpirationYearRe); |
217 if (ParseFieldSpecifics(scanner, pattern, MATCH_DEFAULT | MATCH_SELECT, | 94 if (ParseFieldSpecifics(scanner, pattern, MATCH_DEFAULT | MATCH_SELECT, |
218 &credit_card_field->expiration_year_)) { | 95 &credit_card_field->expiration_year_)) { |
219 continue; | 96 continue; |
220 } | 97 } |
221 } | 98 } |
222 | 99 |
223 // If that fails, try to parse a combined expiration field. | 100 // If that fails, try to parse a combined expiration field. |
224 if (!credit_card_field->expiration_date_) { | 101 if (!credit_card_field->expiration_date_) { |
225 // Look for a 2-digit year first. | 102 // Look for a 2-digit year first. |
226 scanner->Rewind(); | 103 scanner->Rewind(); |
227 pattern = UTF8ToUTF16(kExpirationDate2DigitYearRe); | 104 pattern = UTF8ToUTF16(autofill::kExpirationDate2DigitYearRe); |
228 if (ParseFieldSpecifics(scanner, pattern, | 105 if (ParseFieldSpecifics(scanner, pattern, |
229 MATCH_LABEL | MATCH_VALUE | MATCH_TEXT, | 106 MATCH_LABEL | MATCH_VALUE | MATCH_TEXT, |
230 &credit_card_field->expiration_date_)) { | 107 &credit_card_field->expiration_date_)) { |
231 credit_card_field->is_two_digit_year_ = true; | 108 credit_card_field->is_two_digit_year_ = true; |
232 continue; | 109 continue; |
233 } | 110 } |
234 | 111 |
235 pattern = UTF8ToUTF16(kExpirationDateRe); | 112 pattern = UTF8ToUTF16(autofill::kExpirationDateRe); |
236 if (ParseFieldSpecifics(scanner, pattern, | 113 if (ParseFieldSpecifics(scanner, pattern, |
237 MATCH_LABEL | MATCH_VALUE | MATCH_TEXT, | 114 MATCH_LABEL | MATCH_VALUE | MATCH_TEXT, |
238 &credit_card_field->expiration_date_)) { | 115 &credit_card_field->expiration_date_)) { |
239 continue; | 116 continue; |
240 } | 117 } |
241 } | 118 } |
242 | 119 |
243 if (credit_card_field->expiration_month_ && | 120 if (credit_card_field->expiration_month_ && |
244 !credit_card_field->expiration_year_ && | 121 !credit_card_field->expiration_year_ && |
245 !credit_card_field->expiration_date_) { | 122 !credit_card_field->expiration_date_) { |
246 // Parsed a month but couldn't parse a year; give up. | 123 // Parsed a month but couldn't parse a year; give up. |
247 scanner->RewindTo(saved_cursor); | 124 scanner->RewindTo(saved_cursor); |
248 return NULL; | 125 return NULL; |
249 } | 126 } |
250 } | 127 } |
251 | 128 |
252 // Some pages (e.g. ExpediaBilling.html) have a "card description" | 129 // Some pages (e.g. ExpediaBilling.html) have a "card description" |
253 // field; we parse this field but ignore it. | 130 // field; we parse this field but ignore it. |
254 // We also ignore any other fields within a credit card block that | 131 // We also ignore any other fields within a credit card block that |
255 // start with "card", under the assumption that they are related to | 132 // start with "card", under the assumption that they are related to |
256 // the credit card section being processed but are uninteresting to us. | 133 // the credit card section being processed but are uninteresting to us. |
257 if (ParseField(scanner, UTF8ToUTF16(kCardIgnoredRe), NULL)) { | 134 if (ParseField(scanner, UTF8ToUTF16(autofill::kCardIgnoredRe), NULL)) { |
258 continue; | 135 continue; |
259 } | 136 } |
260 | 137 |
261 break; | 138 break; |
262 } | 139 } |
263 | 140 |
264 // Some pages have a billing address field after the cardholder name field. | 141 // Some pages have a billing address field after the cardholder name field. |
265 // For that case, allow only just the cardholder name field. The remaining | 142 // For that case, allow only just the cardholder name field. The remaining |
266 // CC fields will be picked up in a following CreditCardField. | 143 // CC fields will be picked up in a following CreditCardField. |
267 if (credit_card_field->cardholder_) | 144 if (credit_card_field->cardholder_) |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
327 map); | 204 map); |
328 } else { | 205 } else { |
329 ok = ok && AddClassification(expiration_year_, | 206 ok = ok && AddClassification(expiration_year_, |
330 CREDIT_CARD_EXP_4_DIGIT_YEAR, | 207 CREDIT_CARD_EXP_4_DIGIT_YEAR, |
331 map); | 208 map); |
332 } | 209 } |
333 } | 210 } |
334 | 211 |
335 return ok; | 212 return ok; |
336 } | 213 } |
OLD | NEW |