Bug 1942239 - Add option to explicitly enable incremental origin initialization in...
[gecko.git] / toolkit / components / formautofill / FormAutofillNative.cpp
blob7bfb634fe30f0d50013654179697c1c261c395ac
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "FormAutofillNative.h"
8 #include <math.h>
10 #include "mozilla/ClearOnShutdown.h"
11 #include "mozilla/ComputedStyle.h"
12 #include "mozilla/dom/AutocompleteInfoBinding.h"
13 #include "mozilla/dom/Document.h"
14 #include "mozilla/dom/Element.h"
15 #include "mozilla/dom/HTMLInputElement.h"
16 #include "mozilla/dom/HTMLLabelElement.h"
17 #include "mozilla/dom/HTMLOptionElement.h"
18 #include "mozilla/dom/HTMLSelectElement.h"
19 #include "mozilla/HashTable.h"
20 #include "mozilla/RustRegex.h"
21 #include "nsContentUtils.h"
22 #include "nsIFrame.h"
23 #include "nsIFrameInlines.h"
24 #include "nsLayoutUtils.h"
25 #include "nsTStringHasher.h"
26 #include "mozilla/StaticPtr.h"
28 namespace mozilla::dom {
30 static const char kWhitespace[] = "\b\t\r\n ";
32 enum class RegexKey : uint8_t {
33 CC_NAME,
34 CC_NUMBER,
35 CC_EXP,
36 CC_EXP_MONTH,
37 CC_EXP_YEAR,
38 CC_TYPE,
39 MM_MONTH,
40 YY_OR_YYYY,
41 MONTH,
42 YEAR,
43 MMYY,
44 VISA_CHECKOUT,
45 CREDIT_CARD_NETWORK,
46 CREDIT_CARD_NETWORK_EXACT_MATCH,
47 CREDIT_CARD_NETWORK_LONG,
48 TWO_OR_FOUR_DIGIT_YEAR,
49 DWFRM,
50 BML,
51 TEMPLATED_VALUE,
52 FIRST,
53 LAST,
54 GIFT,
55 SUBSCRIPTION,
56 VALIDATION,
58 Count
61 // We don't follow the coding style (naming start with capital letter) here and
62 // the following CCXXX enum class because we want to sync the rule naming with
63 // the JS implementation.
64 enum class CCNumberParams : uint8_t {
65 idOrNameMatchNumberRegExp,
66 labelsMatchNumberRegExp,
67 closestLabelMatchesNumberRegExp,
68 placeholderMatchesNumberRegExp,
69 ariaLabelMatchesNumberRegExp,
70 idOrNameMatchGift,
71 labelsMatchGift,
72 placeholderMatchesGift,
73 ariaLabelMatchesGift,
74 idOrNameMatchSubscription,
75 idOrNameMatchDwfrmAndBml,
76 hasTemplatedValue,
77 inputTypeNotNumbery,
79 Count,
82 enum class CCNameParams : uint8_t {
83 idOrNameMatchNameRegExp,
84 labelsMatchNameRegExp,
85 closestLabelMatchesNameRegExp,
86 placeholderMatchesNameRegExp,
87 ariaLabelMatchesNameRegExp,
88 idOrNameMatchFirst,
89 labelsMatchFirst,
90 placeholderMatchesFirst,
91 ariaLabelMatchesFirst,
92 idOrNameMatchLast,
93 labelsMatchLast,
94 placeholderMatchesLast,
95 ariaLabelMatchesLast,
96 idOrNameMatchFirstAndLast,
97 idOrNameMatchSubscription,
98 idOrNameMatchDwfrmAndBml,
99 hasTemplatedValue,
101 Count,
104 enum class CCTypeParams : uint8_t {
105 idOrNameMatchTypeRegExp,
106 labelsMatchTypeRegExp,
107 closestLabelMatchesTypeRegExp,
108 idOrNameMatchVisaCheckout,
109 ariaLabelMatchesVisaCheckout,
110 isSelectWithCreditCardOptions,
111 isRadioWithCreditCardText,
112 idOrNameMatchSubscription,
113 idOrNameMatchDwfrmAndBml,
114 hasTemplatedValue,
116 Count,
119 enum class CCExpParams : uint8_t {
120 labelsMatchExpRegExp,
121 closestLabelMatchesExpRegExp,
122 placeholderMatchesExpRegExp,
123 labelsMatchExpWith2Or4DigitYear,
124 placeholderMatchesExpWith2Or4DigitYear,
125 labelsMatchMMYY,
126 placeholderMatchesMMYY,
127 maxLengthIs7,
128 idOrNameMatchSubscription,
129 idOrNameMatchDwfrmAndBml,
130 hasTemplatedValue,
131 isExpirationMonthLikely,
132 isExpirationYearLikely,
133 idOrNameMatchMonth,
134 idOrNameMatchYear,
135 idOrNameMatchExpMonthRegExp,
136 idOrNameMatchExpYearRegExp,
137 idOrNameMatchValidation,
138 Count,
141 enum class CCExpMonthParams : uint8_t {
142 idOrNameMatchExpMonthRegExp,
143 labelsMatchExpMonthRegExp,
144 closestLabelMatchesExpMonthRegExp,
145 placeholderMatchesExpMonthRegExp,
146 ariaLabelMatchesExpMonthRegExp,
147 idOrNameMatchMonth,
148 labelsMatchMonth,
149 placeholderMatchesMonth,
150 ariaLabelMatchesMonth,
151 nextFieldIdOrNameMatchExpYearRegExp,
152 nextFieldLabelsMatchExpYearRegExp,
153 nextFieldPlaceholderMatchExpYearRegExp,
154 nextFieldAriaLabelMatchExpYearRegExp,
155 nextFieldIdOrNameMatchYear,
156 nextFieldLabelsMatchYear,
157 nextFieldPlaceholderMatchesYear,
158 nextFieldAriaLabelMatchesYear,
159 nextFieldMatchesExpYearAutocomplete,
160 isExpirationMonthLikely,
161 nextFieldIsExpirationYearLikely,
162 maxLengthIs2,
163 placeholderMatchesMM,
164 roleIsMenu,
165 idOrNameMatchSubscription,
166 idOrNameMatchDwfrmAndBml,
167 hasTemplatedValue,
169 Count,
172 enum class CCExpYearParams : uint8_t {
173 idOrNameMatchExpYearRegExp,
174 labelsMatchExpYearRegExp,
175 closestLabelMatchesExpYearRegExp,
176 placeholderMatchesExpYearRegExp,
177 ariaLabelMatchesExpYearRegExp,
178 idOrNameMatchYear,
179 labelsMatchYear,
180 placeholderMatchesYear,
181 ariaLabelMatchesYear,
182 previousFieldIdOrNameMatchExpMonthRegExp,
183 previousFieldLabelsMatchExpMonthRegExp,
184 previousFieldPlaceholderMatchExpMonthRegExp,
185 previousFieldAriaLabelMatchExpMonthRegExp,
186 previousFieldIdOrNameMatchMonth,
187 previousFieldLabelsMatchMonth,
188 previousFieldPlaceholderMatchesMonth,
189 previousFieldAriaLabelMatchesMonth,
190 previousFieldMatchesExpMonthAutocomplete,
191 isExpirationYearLikely,
192 previousFieldIsExpirationMonthLikely,
193 placeholderMatchesYYOrYYYY,
194 roleIsMenu,
195 idOrNameMatchSubscription,
196 idOrNameMatchDwfrmAndBml,
197 hasTemplatedValue,
199 Count,
202 struct AutofillParams {
203 EnumeratedArray<CCNumberParams, double, size_t(CCNumberParams::Count)>
204 mCCNumberParams;
205 EnumeratedArray<CCNameParams, double, size_t(CCNameParams::Count)>
206 mCCNameParams;
207 EnumeratedArray<CCTypeParams, double, size_t(CCTypeParams::Count)>
208 mCCTypeParams;
209 EnumeratedArray<CCExpParams, double, size_t(CCExpParams::Count)> mCCExpParams;
210 EnumeratedArray<CCExpMonthParams, double, size_t(CCExpMonthParams::Count)>
211 mCCExpMonthParams;
212 EnumeratedArray<CCExpYearParams, double, size_t(CCExpYearParams::Count)>
213 mCCExpYearParams;
216 // clang-format off
217 constexpr AutofillParams kCoefficents{
218 .mCCNumberParams = {
219 /* idOrNameMatchNumberRegExp */ 7.679469585418701,
220 /* labelsMatchNumberRegExp */ 5.122580051422119,
221 /* closestLabelMatchesNumberRegExp */ 2.1256935596466064,
222 /* placeholderMatchesNumberRegExp */ 9.471800804138184,
223 /* ariaLabelMatchesNumberRegExp */ 6.067715644836426,
224 /* idOrNameMatchGift */ -22.946273803710938,
225 /* labelsMatchGift */ -7.852959632873535,
226 /* placeholderMatchesGift */ -2.355496406555176,
227 /* ariaLabelMatchesGift */ -2.940307855606079,
228 /* idOrNameMatchSubscription */ 0.11255314946174622,
229 /* idOrNameMatchDwfrmAndBml */ -0.0006645023822784424,
230 /* hasTemplatedValue */ -0.11370040476322174,
231 /* inputTypeNotNumbery */ -3.750155210494995
233 .mCCNameParams = {
234 /* idOrNameMatchNameRegExp */ 7.496212959289551,
235 /* labelsMatchNameRegExp */ 6.081472873687744,
236 /* closestLabelMatchesNameRegExp */ 2.600574254989624,
237 /* placeholderMatchesNameRegExp */ 5.750874042510986,
238 /* ariaLabelMatchesNameRegExp */ 5.162227153778076,
239 /* idOrNameMatchFirst */ -6.742659091949463,
240 /* labelsMatchFirst */ -0.5234538912773132,
241 /* placeholderMatchesFirst */ -3.4615235328674316,
242 /* ariaLabelMatchesFirst */ -1.3145145177841187,
243 /* idOrNameMatchLast */ -12.561869621276855,
244 /* labelsMatchLast */ -0.27417105436325073,
245 /* placeholderMatchesLast */ -1.434966802597046,
246 /* ariaLabelMatchesLast */ -2.9319725036621094,
247 /* idOrNameMatchFirstAndLast */ 24.123435974121094,
248 /* idOrNameMatchSubscription */ 0.08349418640136719,
249 /* idOrNameMatchDwfrmAndBml */ 0.01882520318031311,
250 /* hasTemplatedValue */ 0.182317852973938
252 .mCCTypeParams = {
253 /* idOrNameMatchTypeRegExp */ 2.0581533908843994,
254 /* labelsMatchTypeRegExp */ 1.0784518718719482,
255 /* closestLabelMatchesTypeRegExp */ 0.6995877623558044,
256 /* idOrNameMatchVisaCheckout */ -3.320356845855713,
257 /* ariaLabelMatchesVisaCheckout */ -3.4196767807006836,
258 /* isSelectWithCreditCardOptions */ 10.337477684020996,
259 /* isRadioWithCreditCardText */ 4.530318737030029,
260 /* idOrNameMatchSubscription */ -3.7206356525421143,
261 /* idOrNameMatchDwfrmAndBml */ -0.08782318234443665,
262 /* hasTemplatedValue */ 0.1772511601448059
264 .mCCExpParams = {
265 /* labelsMatchExpRegExp */ 7.588159561157227,
266 /* closestLabelMatchesExpRegExp */ 1.41484534740448,
267 /* placeholderMatchesExpRegExp */ 8.759064674377441,
268 /* labelsMatchExpWith2Or4DigitYear */ -3.876218795776367,
269 /* placeholderMatchesExpWith2Or4DigitYear */ 2.8364884853363037,
270 /* labelsMatchMMYY */ 8.836017608642578,
271 /* placeholderMatchesMMYY */ -0.5231751799583435,
272 /* maxLengthIs7 */ 1.3565447330474854,
273 /* idOrNameMatchSubscription */ 0.1779913753271103,
274 /* idOrNameMatchDwfrmAndBml */ 0.21037884056568146,
275 /* hasTemplatedValue */ 0.14900512993335724,
276 /* isExpirationMonthLikely */ -3.223409652709961,
277 /* isExpirationYearLikely */ -2.536919593811035,
278 /* idOrNameMatchMonth */ -3.6893014907836914,
279 /* idOrNameMatchYear */ -3.108184337615967,
280 /* idOrNameMatchExpMonthRegExp */ -2.264357089996338,
281 /* idOrNameMatchExpYearRegExp */ -2.7957723140716553,
282 /* idOrNameMatchValidation */ -2.29402756690979
284 .mCCExpMonthParams = {
285 /* idOrNameMatchExpMonthRegExp */ 0.2787344455718994,
286 /* labelsMatchExpMonthRegExp */ 1.298413634300232,
287 /* closestLabelMatchesExpMonthRegExp */ -11.206244468688965,
288 /* placeholderMatchesExpMonthRegExp */ 1.2605619430541992,
289 /* ariaLabelMatchesExpMonthRegExp */ 1.1330018043518066,
290 /* idOrNameMatchMonth */ 6.1464314460754395,
291 /* labelsMatchMonth */ 0.7051732540130615,
292 /* placeholderMatchesMonth */ 0.7463492751121521,
293 /* ariaLabelMatchesMonth */ 1.8244760036468506,
294 /* nextFieldIdOrNameMatchExpYearRegExp */ 0.06347066164016724,
295 /* nextFieldLabelsMatchExpYearRegExp */ -0.1692247837781906,
296 /* nextFieldPlaceholderMatchExpYearRegExp */ 1.0434566736221313,
297 /* nextFieldAriaLabelMatchExpYearRegExp */ 1.751156210899353,
298 /* nextFieldIdOrNameMatchYear */ -0.532447338104248,
299 /* nextFieldLabelsMatchYear */ 1.3248541355133057,
300 /* nextFieldPlaceholderMatchesYear */ 0.604235827922821,
301 /* nextFieldAriaLabelMatchesYear */ 1.5364223718643188,
302 /* nextFieldMatchesExpYearAutocomplete */ 6.285938262939453,
303 /* isExpirationMonthLikely */ 13.117807388305664,
304 /* nextFieldIsExpirationYearLikely */ 7.182341575622559,
305 /* maxLengthIs2 */ 4.477289199829102,
306 /* placeholderMatchesMM */ 14.403288841247559,
307 /* roleIsMenu */ 5.770959854125977,
308 /* idOrNameMatchSubscription */ -0.043085768818855286,
309 /* idOrNameMatchDwfrmAndBml */ 0.02823038399219513,
310 /* hasTemplatedValue */ 0.07234494388103485
312 .mCCExpYearParams = {
313 /* idOrNameMatchExpYearRegExp */ 5.426016807556152,
314 /* labelsMatchExpYearRegExp */ 1.3240209817886353,
315 /* closestLabelMatchesExpYearRegExp */ -8.702284812927246,
316 /* placeholderMatchesExpYearRegExp */ 0.9059725999832153,
317 /* ariaLabelMatchesExpYearRegExp */ 0.5550334453582764,
318 /* idOrNameMatchYear */ 5.362994194030762,
319 /* labelsMatchYear */ 2.7185044288635254,
320 /* placeholderMatchesYear */ 0.7883157134056091,
321 /* ariaLabelMatchesYear */ 0.311492383480072,
322 /* previousFieldIdOrNameMatchExpMonthRegExp */ 1.8155208826065063,
323 /* previousFieldLabelsMatchExpMonthRegExp */ -0.46133187413215637,
324 /* previousFieldPlaceholderMatchExpMonthRegExp */ 1.0374903678894043,
325 /* previousFieldAriaLabelMatchExpMonthRegExp */ -0.5901495814323425,
326 /* previousFieldIdOrNameMatchMonth */ -5.960310935974121,
327 /* previousFieldLabelsMatchMonth */ 0.6495584845542908,
328 /* previousFieldPlaceholderMatchesMonth */ 0.7198042273521423,
329 /* previousFieldAriaLabelMatchesMonth */ 3.4590985774993896,
330 /* previousFieldMatchesExpMonthAutocomplete */ 2.986003875732422,
331 /* isExpirationYearLikely */ 4.021566390991211,
332 /* previousFieldIsExpirationMonthLikely */ 9.298635482788086,
333 /* placeholderMatchesYYOrYYYY */ 10.457176208496094,
334 /* roleIsMenu */ 1.1051956415176392,
335 /* idOrNameMatchSubscription */ 0.000688597559928894,
336 /* idOrNameMatchDwfrmAndBml */ 0.15687309205532074,
337 /* hasTemplatedValue */ -0.19141331315040588
340 // clang-format off
342 constexpr float kCCNumberBias = -4.948795795440674;
343 constexpr float kCCNameBias = -5.3578081130981445;
344 // Comment out code that are not used right now
346 constexpr float kCCTypeBias = -5.979659557342529;
347 constexpr float kCCExpBias = -5.849575996398926;
348 constexpr float kCCExpMonthBias = -8.844199180603027;
349 constexpr float kCCExpYearBias = -6.499860763549805;
352 struct Rule {
353 RegexKey key;
354 const char* pattern;
357 const Rule kFirefoxRules[] = {
358 {RegexKey::MM_MONTH, "^mm$|\\(mm\\)"},
359 {RegexKey::YY_OR_YYYY, "^(yy|yyyy)$|\\(yy\\)|\\(yyyy\\)"},
360 {RegexKey::MONTH, "month"},
361 {RegexKey::YEAR, "year"},
362 {RegexKey::MMYY, "mm\\s*(/|\\\\)\\s*yy"},
363 {RegexKey::VISA_CHECKOUT, "visa(-|\\s)checkout"},
364 // This should be a union of NETWORK_NAMES in CreditCard.sys.mjs
365 {RegexKey::CREDIT_CARD_NETWORK_LONG,
366 "american express|master card|union pay"},
367 // Please also update CREDIT_CARD_NETWORK_EXACT_MATCH while updating
368 // CREDIT_CARD_NETWORK
369 {RegexKey::CREDIT_CARD_NETWORK,
370 "amex|cartebancaire|diners|discover|jcb|mastercard|mir|unionpay|visa"},
371 {RegexKey::CREDIT_CARD_NETWORK_EXACT_MATCH,
372 "^\\s*(?:amex|cartebancaire|diners|discover|jcb|mastercard|mir|unionpay|"
373 "visa)\\s*$"},
374 {RegexKey::TWO_OR_FOUR_DIGIT_YEAR,
375 "(?:exp.*date[^y\\\\n\\\\r]*|mm\\\\s*[-/]?\\\\s*)yy(?:yy)?(?:[^y]|$)"},
376 {RegexKey::DWFRM, "^dwfrm"},
377 {RegexKey::BML, "BML"},
378 {RegexKey::TEMPLATED_VALUE, "^\\{\\{.*\\}\\}$"},
379 {RegexKey::FIRST, "first"},
380 {RegexKey::LAST, "last"},
381 {RegexKey::GIFT, "gift"},
382 {RegexKey::SUBSCRIPTION, "subscription"},
383 {RegexKey::VALIDATION, "validate|validation"},
386 // These are the rules used by Bitwarden [0], converted into RegExp form.
387 // [0]
388 // https://github.com/bitwarden/browser/blob/c2b8802201fac5e292d55d5caf3f1f78088d823c/src/services/autofill.service.ts#L436
389 const Rule kCreditCardRules[] = {
390 /* eslint-disable */
391 // Let us keep our consistent wrapping.
392 {RegexKey::CC_NAME,
393 // Firefox-specific rules
394 "account.*holder.*name"
395 "|^(credit[-\\s]?card|card).*name"
396 // de-DE
397 "|^(kredit)?(karten|konto)inhaber"
398 "|^(name).*karte"
399 // fr-FR
400 "|nom.*(titulaire|détenteur)"
401 "|(titulaire|détenteur).*(carte)"
402 // it-IT
403 "|titolare.*carta"
404 // pl-PL
405 "|posiadacz.*karty"
406 // es-ES
407 "|nombre.*(titular|tarjeta)"
408 // nl-NL
409 "|naam.*op.*kaart"
410 // Rules from Bitwarden
411 "|cc-?name"
412 "|card-?name"
413 "|cardholder-?name"
414 "|(^nom$)"
415 // Rules are from Chromium source codes
416 "|card.?(?:holder|owner)|name.*(\\b)?on(\\b)?.*card"
417 "|(?:card|cc).?name|cc.?full.?name"
418 "|(?:card|cc).?owner"
419 "|nom.*carte" // fr-FR
420 "|nome.*cart" // it-IT
421 "|名前" // ja-JP
422 "|Имя.*карты" // ru
423 "|信用卡开户名|开户名|持卡人姓名" // zh-CN
424 "|持卡人姓名"}, // zh-TW
425 /* eslint-enable */
427 {RegexKey::CC_NUMBER,
428 // Firefox-specific rules
429 // de-DE
430 "(cc|kk)nr"
431 "|(kredit)?(karten)(nummer|nr)"
432 // it-IT
433 "|numero.*carta"
434 // fr-FR
435 "|(numero|número|numéro).*(carte)"
436 // pl-PL
437 "|numer.*karty"
438 // es-ES
439 "|(número|numero).*tarjeta"
440 // nl-NL
441 "|kaartnummer"
442 // Rules from Bitwarden
443 "|cc-?number"
444 "|cc-?num"
445 "|card-?number"
446 "|card-?num"
447 "|cc-?no"
448 "|card-?no"
449 "|numero-?carte"
450 "|num-?carte"
451 "|cb-?num"
452 // Rules are from Chromium source codes
453 "|(add)?(?:card|cc|acct).?(?:number|#|no|num)"
454 "|カード番号" // ja-JP
455 "|Номер.*карты" // ru
456 "|信用卡号|信用卡号码" // zh-CN
457 "|信用卡卡號" // zh-TW
458 "|카드"}, // ko-KR
460 {RegexKey::CC_EXP,
461 // Firefox-specific rules
462 "mm\\s*(/|\\|-)\\s*(yy|jj|aa)"
463 "|(month|mois)\\s*(/|\\|-|et)\\s*(year|année)"
464 // de-DE
465 // fr-FR
466 // Rules from Bitwarden
467 "|(^cc-?exp$)"
468 "|(^card-?exp$)"
469 "|(^cc-?expiration$)"
470 "|(^card-?expiration$)"
471 "|(^cc-?ex$)"
472 "|(^card-?ex$)"
473 "|(^card-?expire$)"
474 "|(^card-?expiry$)"
475 "|(^validite$)"
476 "|(^expiration$)"
477 "|(^expiry$)"
478 "|mm-?yy"
479 "|mm-?yyyy"
480 "|yy-?mm"
481 "|yyyy-?mm"
482 "|expiration-?date"
483 "|payment-?card-?expiration"
484 "|(^payment-?cc-?date$)"
485 // Rules are from Chromium source codes
486 "|expir|exp.*date|^expfield$"
487 "|ablaufdatum|gueltig|gültig" // de-DE
488 "|fecha" // es
489 "|date.*exp" // fr-FR
490 "|scadenza" // it-IT
491 "|有効期限" // ja-JP
492 "|validade" // pt-BR, pt-PT
493 "|Срок действия карты"}, // ru
495 {RegexKey::CC_EXP_MONTH,
496 // Firefox-specific rules
497 "(cc|kk)month" // de-DE
498 // Rules from Bitwarden
499 "|(^exp-?month$)"
500 "|(^cc-?exp-?month$)"
501 "|(^cc-?month$)"
502 "|(^card-?month$)"
503 "|(^cc-?mo$)"
504 "|(^card-?mo$)"
505 "|(^exp-?mo$)"
506 "|(^card-?exp-?mo$)"
507 "|(^cc-?exp-?mo$)"
508 "|(^card-?expiration-?month$)"
509 "|(^expiration-?month$)"
510 "|(^cc-?mm$)"
511 "|(^cc-?m$)"
512 "|(^card-?mm$)"
513 "|(^card-?m$)"
514 "|(^card-?exp-?mm$)"
515 "|(^cc-?exp-?mm$)"
516 "|(^exp-?mm$)"
517 "|(^exp-?m$)"
518 "|(^expire-?month$)"
519 "|(^expire-?mo$)"
520 "|(^expiry-?month$)"
521 "|(^expiry-?mo$)"
522 "|(^card-?expire-?month$)"
523 "|(^card-?expire-?mo$)"
524 "|(^card-?expiry-?month$)"
525 "|(^card-?expiry-?mo$)"
526 "|(^mois-?validite$)"
527 "|(^mois-?expiration$)"
528 "|(^m-?validite$)"
529 "|(^m-?expiration$)"
530 "|(^expiry-?date-?field-?month$)"
531 "|(^expiration-?date-?month$)"
532 "|(^expiration-?date-?mm$)"
533 "|(^exp-?mon$)"
534 "|(^validity-?mo$)"
535 "|(^exp-?date-?mo$)"
536 "|(^cb-?date-?mois$)"
537 "|(^date-?m$)"
538 // Rules are from Chromium source codes
539 "|exp.*mo|ccmonth|cardmonth|addmonth"
540 "|monat" // de-DE
541 // "|fecha" // es
542 // "|date.*exp" // fr-FR
543 // "|scadenza" // it-IT
544 // "|有効期限" // ja-JP
545 // "|validade" // pt-BR, pt-PT
546 // "|Срок действия карты" // ru
547 "|月"}, // zh-CN
549 {RegexKey::CC_EXP_YEAR,
550 // Firefox-specific rules
551 "(cc|kk)year" // de-DE
552 // Rules from Bitwarden
553 "|(^exp-?year$)"
554 "|(^cc-?exp-?year$)"
555 "|(^cc-?year$)"
556 "|(^card-?year$)"
557 "|(^cc-?yr$)"
558 "|(^card-?yr$)"
559 "|(^exp-?yr$)"
560 "|(^card-?exp-?yr$)"
561 "|(^cc-?exp-?yr$)"
562 "|(^card-?expiration-?year$)"
563 "|(^expiration-?year$)"
564 "|(^cc-?yy$)"
565 "|(^cc-?y$)"
566 "|(^card-?yy$)"
567 "|(^card-?y$)"
568 "|(^card-?exp-?yy$)"
569 "|(^cc-?exp-?yy$)"
570 "|(^exp-?yy$)"
571 "|(^exp-?y$)"
572 "|(^cc-?yyyy$)"
573 "|(^card-?yyyy$)"
574 "|(^card-?exp-?yyyy$)"
575 "|(^cc-?exp-?yyyy$)"
576 "|(^expire-?year$)"
577 "|(^expire-?yr$)"
578 "|(^expiry-?year$)"
579 "|(^expiry-?yr$)"
580 "|(^card-?expire-?year$)"
581 "|(^card-?expire-?yr$)"
582 "|(^card-?expiry-?year$)"
583 "|(^card-?expiry-?yr$)"
584 "|(^an-?validite$)"
585 "|(^an-?expiration$)"
586 "|(^annee-?validite$)"
587 "|(^annee-?expiration$)"
588 "|(^expiry-?date-?field-?year$)"
589 "|(^expiration-?date-?year$)"
590 "|(^cb-?date-?ann$)"
591 "|(^expiration-?date-?yy$)"
592 "|(^expiration-?date-?yyyy$)"
593 "|(^validity-?year$)"
594 "|(^exp-?date-?year$)"
595 "|(^date-?y$)"
596 // Rules are from Chromium source codes
597 "|(add)?year"
598 "|jahr" // de-DE
599 // "|fecha" // es
600 // "|scadenza" // it-IT
601 // "|有効期限" // ja-JP
602 // "|validade" // pt-BR, pt-PT
603 // "|Срок действия карты" // ru
604 "|年|有效期"}, // zh-CN
606 {RegexKey::CC_TYPE,
607 // Firefox-specific rules
608 "type"
609 // de-DE
610 "|Kartenmarke"
611 // Rules from Bitwarden
612 "|(^cc-?type$)"
613 "|(^card-?type$)"
614 "|(^card-?brand$)"
615 "|(^cc-?brand$)"
616 "|(^cb-?type$)"},
617 // Rules are from Chromium source codes
620 static double Sigmoid(double x) { return 1.0 / (1.0 + exp(-x)); }
622 class FormAutofillImpl {
623 public:
624 FormAutofillImpl();
626 void GetFormAutofillConfidences(
627 GlobalObject& aGlobal, const Sequence<OwningNonNull<Element>>& aElements,
628 nsTArray<FormAutofillConfidences>& aResults, ErrorResult& aRv);
630 private:
631 const RustRegex& GetRegex(RegexKey key);
633 bool StringMatchesRegExp(const nsACString& str, RegexKey key);
634 bool StringMatchesRegExp(const nsAString& str, RegexKey key);
635 bool TextContentMatchesRegExp(Element& element, RegexKey key);
636 size_t CountRegExpMatches(const nsACString& str, RegexKey key);
637 size_t CountRegExpMatches(const nsAString& str, RegexKey key);
638 bool IdOrNameMatchRegExp(Element& element, RegexKey key);
639 bool NextFieldMatchesExpYearAutocomplete(Element* aNextField);
640 bool PreviousFieldMatchesExpMonthAutocomplete(Element* aPrevField);
641 bool LabelMatchesRegExp(Element& element, const nsTArray<nsCString>* labels,
642 RegexKey key);
643 bool ClosestLabelMatchesRegExp(Element& aElement, RegexKey aKey);
644 bool PlaceholderMatchesRegExp(Element& element, RegexKey key);
645 bool AriaLabelMatchesRegExp(Element& element, RegexKey key);
646 bool AutocompleteStringMatches(Element& aElement, const nsAString& aKey);
648 bool HasTemplatedValue(Element& element);
649 bool MaxLengthIs(Element& aElement, int32_t aValue);
650 bool IsExpirationMonthLikely(Element& element);
651 bool IsExpirationYearLikely(Element& element);
652 bool InputTypeNotNumbery(Element& element);
653 bool IsSelectWithCreditCardOptions(Element& element);
654 bool IsRadioWithCreditCardText(Element& element,
655 const nsTArray<nsCString>* labels,
656 ErrorResult& aRv);
657 bool MatchesExpYearAutocomplete(Element& element);
658 bool RoleIsMenu(Element& element);
660 Element* FindRootForField(Element* aElement);
662 Element* FindField(const Sequence<OwningNonNull<Element>>& aElements,
663 uint32_t aStartIndex, int8_t aDirection);
664 Element* NextField(const Sequence<OwningNonNull<Element>>& aElements,
665 uint32_t aStartIndex);
666 Element* PrevField(const Sequence<OwningNonNull<Element>>& aElements,
667 uint32_t aStartIndex);
669 // Array contains regular expressions to match the corresponding
670 // field. Ex, CC number, CC type, etc.
671 using RegexStringArray =
672 EnumeratedArray<RegexKey, nsCString, size_t(RegexKey::Count)>;
673 RegexStringArray mRuleMap;
675 // Array that holds RegexWrapper that created by regex::ffi::regex_new
676 using RegexWrapperArray = EnumeratedArray<RegexKey, RustRegex, size_t(RegexKey::Count)>;
677 RegexWrapperArray mRegexes;
680 FormAutofillImpl::FormAutofillImpl() {
681 const Rule* rulesets[] = {&kFirefoxRules[0], &kCreditCardRules[0]};
682 size_t rulesetLengths[] = {std::size(kFirefoxRules),
683 std::size(kCreditCardRules)};
685 for (uint32_t i = 0; i < std::size(rulesetLengths); ++i) {
686 for (uint32_t j = 0; j < rulesetLengths[i]; ++j) {
687 nsCString& rule = mRuleMap[rulesets[i][j].key];
688 if (!rule.IsEmpty()) {
689 rule.Append("|");
691 rule.Append(rulesets[i][j].pattern);
696 const RustRegex& FormAutofillImpl::GetRegex(RegexKey aKey) {
697 if (!mRegexes[aKey]) {
698 RustRegex regex(mRuleMap[aKey], RustRegexOptions().CaseInsensitive(true));
699 MOZ_DIAGNOSTIC_ASSERT(regex);
700 mRegexes[aKey] = std::move(regex);
702 return mRegexes[aKey];
705 bool FormAutofillImpl::StringMatchesRegExp(const nsACString& aStr,
706 RegexKey aKey) {
707 return GetRegex(aKey).IsMatch(aStr);
710 bool FormAutofillImpl::StringMatchesRegExp(const nsAString& aStr,
711 RegexKey aKey) {
712 return StringMatchesRegExp(NS_ConvertUTF16toUTF8(aStr), aKey);
715 bool FormAutofillImpl::TextContentMatchesRegExp(Element& element,
716 RegexKey key) {
717 ErrorResult rv;
718 nsAutoString text;
719 element.GetTextContent(text, rv);
720 if (rv.Failed()) {
721 return false;
724 return StringMatchesRegExp(text, key);
727 size_t FormAutofillImpl::CountRegExpMatches(const nsACString& aStr,
728 RegexKey aKey) {
729 return GetRegex(aKey).CountMatches(aStr);
732 size_t FormAutofillImpl::CountRegExpMatches(const nsAString& aStr,
733 RegexKey aKey) {
734 return CountRegExpMatches(NS_ConvertUTF16toUTF8(aStr), aKey);
737 bool FormAutofillImpl::NextFieldMatchesExpYearAutocomplete(
738 Element* aNextField) {
739 return AutocompleteStringMatches(*aNextField, u"cc-exp-year"_ns);
742 bool FormAutofillImpl::PreviousFieldMatchesExpMonthAutocomplete(
743 Element* aPrevField) {
744 return AutocompleteStringMatches(*aPrevField, u"cc-exp-month"_ns);
747 bool FormAutofillImpl::IdOrNameMatchRegExp(Element& aElement, RegexKey key) {
748 nsAutoString str;
749 aElement.GetId(str);
750 if (StringMatchesRegExp(str, key)) {
751 return true;
753 aElement.GetAttr(nsGkAtoms::name, str);
754 return StringMatchesRegExp(str, key);
757 bool FormAutofillImpl::LabelMatchesRegExp(
758 Element& aElement, const nsTArray<nsCString>* labelStrings, RegexKey key) {
759 if (labelStrings) {
760 for (const auto& str : *labelStrings) {
761 if (StringMatchesRegExp(str, key)) {
762 return true;
767 Element* parent = aElement.GetParentElement();
768 if (!parent) {
769 return false;
772 ErrorResult aRv;
773 if (parent->IsHTMLElement(nsGkAtoms::td)) {
774 Element* pp = parent->GetParentElement();
775 if (pp) {
776 return TextContentMatchesRegExp(*pp, key);
779 if (parent->IsHTMLElement(nsGkAtoms::td)) {
780 Element* pes = aElement.GetPreviousElementSibling();
781 if (pes) {
782 return TextContentMatchesRegExp(*pes, key);
785 return false;
788 bool FormAutofillImpl::ClosestLabelMatchesRegExp(Element& aElement,
789 RegexKey aKey) {
790 ErrorResult aRv;
791 Element* pes = aElement.GetPreviousElementSibling();
792 if (pes && pes->IsHTMLElement(nsGkAtoms::label)) {
793 return TextContentMatchesRegExp(*pes, aKey);
796 Element* nes = aElement.GetNextElementSibling();
797 if (nes && nes->IsHTMLElement(nsGkAtoms::label)) {
798 return TextContentMatchesRegExp(*nes, aKey);
801 return false;
804 bool FormAutofillImpl::PlaceholderMatchesRegExp(Element& aElement,
805 RegexKey aKey) {
806 nsAutoString str;
807 if (!aElement.GetAttr(nsGkAtoms::placeholder, str)) {
808 return false;
810 return StringMatchesRegExp(str, aKey);
813 bool FormAutofillImpl::AriaLabelMatchesRegExp(Element& aElement,
814 RegexKey aKey) {
815 nsAutoString str;
816 if (!aElement.GetAttr(nsGkAtoms::aria_label, str)) {
817 return false;
819 return StringMatchesRegExp(str, aKey);
822 bool FormAutofillImpl::AutocompleteStringMatches(Element& aElement,
823 const nsAString& aKey) {
824 Nullable<AutocompleteInfo> info;
825 if (auto* input = HTMLInputElement::FromNode(aElement)) {
826 input->GetAutocompleteInfo(info);
827 } else {
828 AutocompleteInfo autoInfo;
829 if (auto* select = HTMLSelectElement::FromNode(aElement)) {
830 select->GetAutocompleteInfo(autoInfo);
831 info.SetValue(autoInfo);
835 if (info.IsNull()) {
836 return false;
839 return info.Value().mFieldName.Equals(aKey);
842 bool FormAutofillImpl::HasTemplatedValue(Element& aElement) {
843 nsAutoString str;
844 if (!aElement.GetAttr(nsGkAtoms::value, str)) {
845 return false;
847 return StringMatchesRegExp(str, RegexKey::TEMPLATED_VALUE);
850 bool FormAutofillImpl::RoleIsMenu(Element& aElement) {
851 return aElement.AttrValueIs(kNameSpaceID_None, nsGkAtoms::role,
852 nsGkAtoms::menu, eCaseMatters);
855 bool FormAutofillImpl::InputTypeNotNumbery(Element& aElement) {
856 auto* input = HTMLInputElement::FromNode(aElement);
857 if (!input) {
858 return true;
861 auto type = input->ControlType();
862 return type != FormControlType::InputText &&
863 type != FormControlType::InputTel &&
864 type != FormControlType::InputNumber;
867 bool FormAutofillImpl::IsSelectWithCreditCardOptions(Element& aElement) {
868 auto* select = HTMLSelectElement::FromNode(aElement);
869 if (!select) {
870 return false;
873 nsCOMPtr<nsIHTMLCollection> options = select->Options();
874 for (uint32_t i = 0; i < options->Length(); ++i) {
875 auto* item = options->Item(i);
876 auto* option = HTMLOptionElement::FromNode(item);
877 if (!option) {
878 continue;
880 // Bug 1756799, consider using getAttribute("value") instead of .value
881 nsAutoString str;
882 option->GetValue(str);
883 if (StringMatchesRegExp(str, RegexKey::CREDIT_CARD_NETWORK_EXACT_MATCH) ||
884 StringMatchesRegExp(str, RegexKey::CREDIT_CARD_NETWORK_LONG)) {
885 return true;
888 option->GetText(str);
889 if (StringMatchesRegExp(str, RegexKey::CREDIT_CARD_NETWORK_EXACT_MATCH) ||
890 StringMatchesRegExp(str, RegexKey::CREDIT_CARD_NETWORK_LONG)) {
891 return true;
894 return false;
897 bool FormAutofillImpl::IsRadioWithCreditCardText(
898 Element& aElement, const nsTArray<nsCString>* aLabels, ErrorResult& aRv) {
899 auto* input = HTMLInputElement::FromNode(aElement);
900 if (!input) {
901 return false;
903 auto type = input->ControlType();
904 if (type != FormControlType::InputRadio) {
905 return false;
908 nsAutoString str;
909 input->GetValue(str, CallerType::System);
910 if (CountRegExpMatches(str, RegexKey::CREDIT_CARD_NETWORK) == 1) {
911 return true;
914 if (aLabels) {
915 size_t labelsMatched = 0;
916 for (const auto& label : *aLabels) {
917 size_t labelMatches =
918 CountRegExpMatches(label, RegexKey::CREDIT_CARD_NETWORK);
919 if (labelMatches > 1) {
920 return false;
922 if (labelMatches > 0) {
923 labelsMatched++;
927 if (labelsMatched) {
928 return labelsMatched == 1;
932 // Bug 1756798 : Remove reading text content in a <input>
933 nsAutoString text;
934 aElement.GetTextContent(text, aRv);
935 if (aRv.Failed()) {
936 return false;
938 return CountRegExpMatches(text, RegexKey::CREDIT_CARD_NETWORK) == 1;
941 bool FormAutofillImpl::MaxLengthIs(Element& aElement, int32_t aValue) {
942 auto* input = HTMLInputElement::FromNode(aElement);
943 if (!input) {
944 return false;
946 return input->MaxLength() == aValue;
949 static bool TestOptionElementForInteger(Element* aElement, int32_t aTestValue) {
950 auto* option = HTMLOptionElement::FromNodeOrNull(aElement);
951 if (!option) {
952 return false;
954 nsAutoString str;
955 option->GetValue(str);
956 nsContentUtils::ParseHTMLIntegerResultFlags parseFlags;
957 int32_t val = nsContentUtils::ParseHTMLInteger(str, &parseFlags);
958 if (val == aTestValue) {
959 return true;
961 option->GetRenderedLabel(str);
962 val = nsContentUtils::ParseHTMLInteger(str, &parseFlags);
963 return val == aTestValue;
966 static bool MatchOptionContiguousInteger(HTMLOptionsCollection* aOptions,
967 uint32_t aNumContiguous,
968 int32_t aInteger) {
969 uint32_t len = aOptions->Length();
970 if (aNumContiguous > len) {
971 return false;
974 for (uint32_t i = 0; i <= aOptions->Length() - aNumContiguous; i++) {
975 bool match = true;
976 for (uint32_t j = 0; j < aNumContiguous; j++) {
977 if (!TestOptionElementForInteger(aOptions->GetElementAt(i + j),
978 aInteger + j)) {
979 match = false;
980 break;
983 if (match) {
984 return true;
987 return false;
990 bool FormAutofillImpl::IsExpirationYearLikely(Element& aElement) {
991 auto* select = HTMLSelectElement::FromNode(aElement);
992 if (!select) {
993 return false;
996 auto* options = select->Options();
997 if (!options) {
998 return false;
1001 PRExplodedTime tm;
1002 PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &tm);
1003 uint16_t currentYear = tm.tm_year;
1005 return MatchOptionContiguousInteger(options, 3, currentYear);
1008 bool FormAutofillImpl::IsExpirationMonthLikely(Element& aElement) {
1009 auto* select = HTMLSelectElement::FromNode(aElement);
1010 if (!select) {
1011 return false;
1014 auto* options = select->Options();
1015 if (!options) {
1016 return false;
1019 if (options->Length() != 12 && options->Length() != 13) {
1020 return false;
1023 return MatchOptionContiguousInteger(options, 12, 1);
1026 Element* FormAutofillImpl::FindRootForField(Element* aElement) {
1027 if (const auto* control =
1028 nsGenericHTMLFormControlElement::FromNode(aElement)) {
1029 if (Element* form = control->GetForm()) {
1030 return form;
1034 return aElement->OwnerDoc()->GetDocumentElement();
1037 Element* FormAutofillImpl::FindField(
1038 const Sequence<OwningNonNull<Element>>& aElements, uint32_t aStartIndex,
1039 int8_t aDirection) {
1040 MOZ_ASSERT(aDirection == 1 || aDirection == -1);
1041 MOZ_ASSERT(aStartIndex < aElements.Length());
1043 Element* curFieldRoot = FindRootForField(aElements[aStartIndex]);
1044 bool isRootForm = curFieldRoot->IsHTMLElement(nsGkAtoms::form);
1046 uint32_t num =
1047 aDirection == 1 ? aElements.Length() - aStartIndex - 1 : aStartIndex;
1048 for (uint32_t i = 0, searchIndex = aStartIndex; i < num; i++) {
1049 searchIndex += aDirection;
1050 const auto& element = aElements[searchIndex];
1051 Element* root = FindRootForField(element);
1053 if (isRootForm) {
1054 // Only search fields that are within the same root element.
1055 if (curFieldRoot != root) {
1056 return nullptr;
1058 } else {
1059 // Exclude elements inside the rootElement that are already in a <form>.
1060 if (root->IsHTMLElement(nsGkAtoms::form)) {
1061 continue;
1065 if (element->IsAnyOfHTMLElements(nsGkAtoms::input, nsGkAtoms::select)) {
1066 return element.get();
1070 return nullptr;
1073 Element* FormAutofillImpl::NextField(
1074 const Sequence<OwningNonNull<Element>>& aElements, uint32_t aStartIndex) {
1075 return FindField(aElements, aStartIndex, 1);
1078 Element* FormAutofillImpl::PrevField(
1079 const Sequence<OwningNonNull<Element>>& aElements, uint32_t aStartIndex) {
1080 return FindField(aElements, aStartIndex, -1);
1083 static void ExtractLabelStrings(nsINode* aNode, nsTArray<nsCString>& aStrings,
1084 ErrorResult& aRv) {
1085 if (aNode->IsAnyOfHTMLElements(nsGkAtoms::script, nsGkAtoms::noscript,
1086 nsGkAtoms::option, nsGkAtoms::style)) {
1087 return;
1090 if (aNode->IsText() || !aNode->HasChildren()) {
1091 nsAutoString text;
1092 aNode->GetTextContent(text, aRv);
1093 if (aRv.Failed()) {
1094 return;
1097 text.Trim(kWhitespace);
1098 CopyUTF16toUTF8(text, *aStrings.AppendElement());
1099 return;
1102 for (nsINode* child = aNode->GetFirstChild(); child;
1103 child = child->GetNextSibling()) {
1104 if (child->IsElement() || child->IsText()) {
1105 ExtractLabelStrings(child, aStrings, aRv);
1106 if (aRv.Failed()) {
1107 return;
1113 nsTArray<nsCString>* GetLabelStrings(
1114 Element* aElement,
1115 const nsTHashMap<void*, nsTArray<nsCString>>& aElementMap,
1116 const nsTHashMap<nsAtom*, nsTArray<nsCString>>& aIdMap) {
1117 if (!aElement) {
1118 return nullptr;
1121 if (nsAtom* idAtom = aElement->GetID()) {
1122 return aIdMap.Lookup(idAtom).DataPtrOrNull();
1125 return aElementMap.Lookup(aElement).DataPtrOrNull();
1128 void FormAutofillImpl::GetFormAutofillConfidences(
1129 GlobalObject& aGlobal, const Sequence<OwningNonNull<Element>>& aElements,
1130 nsTArray<FormAutofillConfidences>& aResults, ErrorResult& aRv) {
1131 if (aElements.IsEmpty()) {
1132 return;
1135 // Create Labels
1136 auto* document = aElements[0]->OwnerDoc();
1137 #ifdef DEBUG
1138 for (uint32_t i = 1; i < aElements.Length(); ++i) {
1139 MOZ_ASSERT(document == aElements[i]->OwnerDoc());
1141 #endif
1143 RefPtr<nsContentList> labels = document->GetElementsByTagName(u"label"_ns);
1144 nsTHashMap<void*, nsTArray<nsCString>> elementsToLabelStrings;
1145 nsTHashMap<nsAtom*, nsTArray<nsCString>> elementsIdToLabelStrings;
1146 if (labels) {
1147 for (uint32_t i = 0; i < labels->Length(); ++i) {
1148 auto* item = labels->Item(i);
1149 auto* label = HTMLLabelElement::FromNode(item);
1150 if (NS_WARN_IF(!label)) {
1151 continue;
1153 auto* control = label->GetControl();
1154 if (!control) {
1155 continue;
1157 nsTArray<nsCString> labelStrings;
1158 ExtractLabelStrings(label, labelStrings, aRv);
1159 if (aRv.Failed()) {
1160 return;
1163 // We need two maps here to keep track controls with id and without id.
1164 // We can't just use map without id to cover all cases because there
1165 // might be multiple elements with the same id.
1166 if (control->GetID()) {
1167 elementsIdToLabelStrings.LookupOrInsert(control->GetID())
1168 .AppendElements(std::move(labelStrings));
1169 } else {
1170 elementsToLabelStrings.LookupOrInsert(control).AppendElements(
1171 std::move(labelStrings));
1176 nsTArray<AutofillParams> paramSet;
1177 paramSet.SetLength(aElements.Length());
1179 for (uint32_t i = 0; i < aElements.Length(); ++i) {
1180 auto& params = paramSet[i];
1181 const auto& element = aElements[i];
1183 const nsTArray<nsCString>* labelStrings = GetLabelStrings(
1184 element, elementsToLabelStrings, elementsIdToLabelStrings);
1186 bool idOrNameMatchDwfrmAndBml =
1187 IdOrNameMatchRegExp(element, RegexKey::DWFRM) &&
1188 IdOrNameMatchRegExp(element, RegexKey::BML);
1189 bool hasTemplatedValue = HasTemplatedValue(element);
1190 bool inputTypeNotNumbery = InputTypeNotNumbery(element);
1191 bool idOrNameMatchSubscription =
1192 IdOrNameMatchRegExp(element, RegexKey::SUBSCRIPTION);
1193 bool idOrNameMatchFirstAndLast =
1194 IdOrNameMatchRegExp(element, RegexKey::FIRST) &&
1195 IdOrNameMatchRegExp(element, RegexKey::LAST);
1197 #define RULE_IMPL2(rule, type) params.m##type##Params[type##Params::rule]
1198 #define RULE_IMPL(rule, type) RULE_IMPL2(rule, type)
1199 #define RULE(rule) RULE_IMPL(rule, RULE_TYPE)
1201 // cc-number
1202 #define RULE_TYPE CCNumber
1203 RULE(idOrNameMatchNumberRegExp) =
1204 IdOrNameMatchRegExp(element, RegexKey::CC_NUMBER);
1205 RULE(labelsMatchNumberRegExp) =
1206 LabelMatchesRegExp(element, labelStrings, RegexKey::CC_NUMBER);
1207 RULE(closestLabelMatchesNumberRegExp) =
1208 ClosestLabelMatchesRegExp(element, RegexKey::CC_NUMBER);
1209 RULE(placeholderMatchesNumberRegExp) =
1210 PlaceholderMatchesRegExp(element, RegexKey::CC_NUMBER);
1211 RULE(ariaLabelMatchesNumberRegExp) =
1212 AriaLabelMatchesRegExp(element, RegexKey::CC_NUMBER);
1213 RULE(idOrNameMatchGift) = IdOrNameMatchRegExp(element, RegexKey::GIFT);
1214 RULE(labelsMatchGift) =
1215 LabelMatchesRegExp(element, labelStrings, RegexKey::GIFT);
1216 RULE(placeholderMatchesGift) =
1217 PlaceholderMatchesRegExp(element, RegexKey::GIFT);
1218 RULE(ariaLabelMatchesGift) =
1219 AriaLabelMatchesRegExp(element, RegexKey::GIFT);
1220 RULE(idOrNameMatchSubscription) = idOrNameMatchSubscription;
1221 RULE(idOrNameMatchDwfrmAndBml) = idOrNameMatchDwfrmAndBml;
1222 RULE(hasTemplatedValue) = hasTemplatedValue;
1223 RULE(inputTypeNotNumbery) = inputTypeNotNumbery;
1224 #undef RULE_TYPE
1226 // cc-name
1227 #define RULE_TYPE CCName
1228 RULE(idOrNameMatchNameRegExp) =
1229 IdOrNameMatchRegExp(element, RegexKey::CC_NAME);
1230 RULE(labelsMatchNameRegExp) =
1231 LabelMatchesRegExp(element, labelStrings, RegexKey::CC_NAME);
1232 RULE(closestLabelMatchesNameRegExp) =
1233 ClosestLabelMatchesRegExp(element, RegexKey::CC_NAME);
1234 RULE(placeholderMatchesNameRegExp) =
1235 PlaceholderMatchesRegExp(element, RegexKey::CC_NAME);
1236 RULE(ariaLabelMatchesNameRegExp) =
1237 AriaLabelMatchesRegExp(element, RegexKey::CC_NAME);
1238 RULE(idOrNameMatchFirst) = IdOrNameMatchRegExp(element, RegexKey::FIRST);
1239 RULE(labelsMatchFirst) =
1240 LabelMatchesRegExp(element, labelStrings, RegexKey::FIRST);
1241 RULE(placeholderMatchesFirst) =
1242 PlaceholderMatchesRegExp(element, RegexKey::FIRST);
1243 RULE(ariaLabelMatchesFirst) =
1244 AriaLabelMatchesRegExp(element, RegexKey::FIRST);
1245 RULE(idOrNameMatchLast) = IdOrNameMatchRegExp(element, RegexKey::LAST);
1246 RULE(labelsMatchLast) =
1247 LabelMatchesRegExp(element, labelStrings, RegexKey::LAST);
1248 RULE(placeholderMatchesLast) =
1249 PlaceholderMatchesRegExp(element, RegexKey::LAST);
1250 RULE(ariaLabelMatchesLast) =
1251 AriaLabelMatchesRegExp(element, RegexKey::LAST);
1252 RULE(idOrNameMatchSubscription) = idOrNameMatchSubscription;
1253 RULE(idOrNameMatchFirstAndLast) = idOrNameMatchFirstAndLast;
1254 RULE(idOrNameMatchDwfrmAndBml) = idOrNameMatchDwfrmAndBml;
1255 RULE(hasTemplatedValue) = hasTemplatedValue;
1256 #undef RULE_TYPE
1258 // We only use Fathom to detect cc-number & cc-name fields for now.
1259 // Comment out code below instead of removing them to make it clear that
1260 // the current design is to support multiple rules.
1262 Element* nextFillableField = NextField(aElements, i);
1263 Element* prevFillableField = PrevField(aElements, i);
1265 const nsTArray<nsCString>* nextLabelStrings = GetLabelStrings(
1266 nextFillableField, elementsToLabelStrings, elementsIdToLabelStrings);
1267 const nsTArray<nsCString>* prevLabelStrings = GetLabelStrings(
1268 prevFillableField, elementsToLabelStrings, elementsIdToLabelStrings);
1269 bool roleIsMenu = RoleIsMenu(element);
1271 // cc-type
1272 #define RULE_TYPE CCType
1273 RULE(idOrNameMatchTypeRegExp) =
1274 IdOrNameMatchRegExp(element, RegexKey::CC_TYPE);
1275 RULE(labelsMatchTypeRegExp) =
1276 LabelMatchesRegExp(element, labelStrings, RegexKey::CC_TYPE);
1277 RULE(closestLabelMatchesTypeRegExp) =
1278 ClosestLabelMatchesRegExp(element, RegexKey::CC_TYPE);
1279 RULE(idOrNameMatchVisaCheckout) =
1280 IdOrNameMatchRegExp(element, RegexKey::VISA_CHECKOUT);
1281 RULE(ariaLabelMatchesVisaCheckout) =
1282 AriaLabelMatchesRegExp(element, RegexKey::VISA_CHECKOUT);
1283 RULE(isSelectWithCreditCardOptions) =
1284 IsSelectWithCreditCardOptions(element);
1285 RULE(isRadioWithCreditCardText) =
1286 IsRadioWithCreditCardText(element, labelStrings, aRv);
1287 if (aRv.Failed()) {
1288 return;
1291 RULE(idOrNameMatchSubscription) = idOrNameMatchSubscription;
1292 RULE(idOrNameMatchDwfrmAndBml) = idOrNameMatchDwfrmAndBml;
1293 RULE(hasTemplatedValue) = hasTemplatedValue;
1294 #undef RULE_TYPE
1296 // cc-exp
1297 #define RULE_TYPE CCExp
1298 RULE(labelsMatchExpRegExp) =
1299 LabelMatchesRegExp(element, labelStrings, RegexKey::CC_EXP);
1300 RULE(closestLabelMatchesExpRegExp) =
1301 ClosestLabelMatchesRegExp(element, RegexKey::CC_EXP);
1302 RULE(placeholderMatchesExpRegExp) =
1303 PlaceholderMatchesRegExp(element, RegexKey::CC_EXP);
1304 RULE(labelsMatchExpWith2Or4DigitYear) = LabelMatchesRegExp(
1305 element, labelStrings, RegexKey::TWO_OR_FOUR_DIGIT_YEAR);
1306 RULE(placeholderMatchesExpWith2Or4DigitYear) =
1307 PlaceholderMatchesRegExp(element, RegexKey::TWO_OR_FOUR_DIGIT_YEAR);
1308 RULE(labelsMatchMMYY) =
1309 LabelMatchesRegExp(element, labelStrings, RegexKey::MMYY);
1310 RULE(placeholderMatchesMMYY) =
1311 PlaceholderMatchesRegExp(element, RegexKey::MMYY);
1312 RULE(maxLengthIs7) = MaxLengthIs(element, 7);
1313 RULE(idOrNameMatchSubscription) = idOrNameMatchSubscription;
1314 RULE(idOrNameMatchDwfrmAndBml) = idOrNameMatchDwfrmAndBml;
1315 RULE(hasTemplatedValue) = hasTemplatedValue;
1316 RULE(isExpirationMonthLikely) = IsExpirationMonthLikely(element);
1317 RULE(isExpirationYearLikely) = IsExpirationYearLikely(element);
1318 RULE(idOrNameMatchMonth) = IdOrNameMatchRegExp(element, RegexKey::MONTH);
1319 RULE(idOrNameMatchYear) = IdOrNameMatchRegExp(element, RegexKey::YEAR);
1320 RULE(idOrNameMatchExpMonthRegExp) =
1321 IdOrNameMatchRegExp(element, RegexKey::CC_EXP_MONTH);
1322 RULE(idOrNameMatchExpYearRegExp) =
1323 IdOrNameMatchRegExp(element, RegexKey::CC_EXP_YEAR);
1324 RULE(idOrNameMatchValidation) =
1325 IdOrNameMatchRegExp(element, RegexKey::VALIDATION);
1326 #undef RULE_TYPE
1328 // cc-exp-month
1329 #define RULE_TYPE CCExpMonth
1330 RULE(idOrNameMatchExpMonthRegExp) =
1331 IdOrNameMatchRegExp(element, RegexKey::CC_EXP_MONTH);
1332 RULE(labelsMatchExpMonthRegExp) =
1333 LabelMatchesRegExp(element, labelStrings, RegexKey::CC_EXP_MONTH);
1334 RULE(closestLabelMatchesExpMonthRegExp) =
1335 ClosestLabelMatchesRegExp(element, RegexKey::CC_EXP_MONTH);
1336 RULE(placeholderMatchesExpMonthRegExp) =
1337 PlaceholderMatchesRegExp(element, RegexKey::CC_EXP_MONTH);
1338 RULE(ariaLabelMatchesExpMonthRegExp) =
1339 AriaLabelMatchesRegExp(element, RegexKey::CC_EXP_MONTH);
1340 RULE(idOrNameMatchMonth) = IdOrNameMatchRegExp(element, RegexKey::MONTH);
1341 RULE(labelsMatchMonth) =
1342 LabelMatchesRegExp(element, labelStrings, RegexKey::MONTH);
1343 RULE(placeholderMatchesMonth) =
1344 PlaceholderMatchesRegExp(element, RegexKey::MONTH);
1345 RULE(ariaLabelMatchesMonth) =
1346 AriaLabelMatchesRegExp(element, RegexKey::MONTH);
1347 RULE(nextFieldIdOrNameMatchExpYearRegExp) =
1348 nextFillableField &&
1349 IdOrNameMatchRegExp(*nextFillableField, RegexKey::CC_EXP_YEAR);
1350 RULE(nextFieldLabelsMatchExpYearRegExp) =
1351 nextFillableField &&
1352 LabelMatchesRegExp(element, nextLabelStrings, RegexKey::CC_EXP_YEAR);
1353 RULE(nextFieldPlaceholderMatchExpYearRegExp) =
1354 nextFillableField &&
1355 PlaceholderMatchesRegExp(*nextFillableField, RegexKey::CC_EXP_YEAR);
1356 RULE(nextFieldAriaLabelMatchExpYearRegExp) =
1357 nextFillableField &&
1358 AriaLabelMatchesRegExp(*nextFillableField, RegexKey::CC_EXP_YEAR);
1359 RULE(nextFieldIdOrNameMatchYear) =
1360 nextFillableField &&
1361 IdOrNameMatchRegExp(*nextFillableField, RegexKey::YEAR);
1362 RULE(nextFieldLabelsMatchYear) =
1363 nextFillableField &&
1364 LabelMatchesRegExp(element, nextLabelStrings, RegexKey::YEAR);
1365 RULE(nextFieldPlaceholderMatchesYear) =
1366 nextFillableField &&
1367 PlaceholderMatchesRegExp(*nextFillableField, RegexKey::YEAR);
1368 RULE(nextFieldAriaLabelMatchesYear) =
1369 nextFillableField &&
1370 AriaLabelMatchesRegExp(*nextFillableField, RegexKey::YEAR);
1371 RULE(nextFieldMatchesExpYearAutocomplete) =
1372 nextFillableField &&
1373 NextFieldMatchesExpYearAutocomplete(nextFillableField);
1374 RULE(isExpirationMonthLikely) = IsExpirationMonthLikely(element);
1375 RULE(nextFieldIsExpirationYearLikely) =
1376 nextFillableField && IsExpirationYearLikely(*nextFillableField);
1377 RULE(maxLengthIs2) = MaxLengthIs(element, 2);
1378 RULE(placeholderMatchesMM) =
1379 PlaceholderMatchesRegExp(element, RegexKey::MM_MONTH);
1380 RULE(roleIsMenu) = roleIsMenu;
1381 RULE(idOrNameMatchSubscription) = idOrNameMatchSubscription;
1382 RULE(idOrNameMatchDwfrmAndBml) = idOrNameMatchDwfrmAndBml;
1383 RULE(hasTemplatedValue) = hasTemplatedValue;
1384 #undef RULE_TYPE
1386 // cc-exp-year
1387 #define RULE_TYPE CCExpYear
1388 RULE(idOrNameMatchExpYearRegExp) =
1389 IdOrNameMatchRegExp(element, RegexKey::CC_EXP_YEAR);
1390 RULE(labelsMatchExpYearRegExp) =
1391 LabelMatchesRegExp(element, labelStrings, RegexKey::CC_EXP_YEAR);
1392 RULE(closestLabelMatchesExpYearRegExp) =
1393 ClosestLabelMatchesRegExp(element, RegexKey::CC_EXP_YEAR);
1394 RULE(placeholderMatchesExpYearRegExp) =
1395 PlaceholderMatchesRegExp(element, RegexKey::CC_EXP_YEAR);
1396 RULE(ariaLabelMatchesExpYearRegExp) =
1397 AriaLabelMatchesRegExp(element, RegexKey::CC_EXP_YEAR);
1398 RULE(idOrNameMatchYear) = IdOrNameMatchRegExp(element, RegexKey::YEAR);
1399 RULE(labelsMatchYear) =
1400 LabelMatchesRegExp(element, labelStrings, RegexKey::YEAR);
1401 RULE(placeholderMatchesYear) =
1402 PlaceholderMatchesRegExp(element, RegexKey::YEAR);
1403 RULE(ariaLabelMatchesYear) =
1404 AriaLabelMatchesRegExp(element, RegexKey::YEAR);
1405 RULE(previousFieldIdOrNameMatchExpMonthRegExp) =
1406 prevFillableField &&
1407 IdOrNameMatchRegExp(*prevFillableField, RegexKey::CC_EXP_MONTH);
1408 RULE(previousFieldLabelsMatchExpMonthRegExp) =
1409 prevFillableField &&
1410 LabelMatchesRegExp(element, prevLabelStrings, RegexKey::CC_EXP_MONTH);
1411 RULE(previousFieldPlaceholderMatchExpMonthRegExp) =
1412 prevFillableField &&
1413 PlaceholderMatchesRegExp(*prevFillableField, RegexKey::CC_EXP_MONTH);
1414 RULE(previousFieldAriaLabelMatchExpMonthRegExp) =
1415 prevFillableField &&
1416 AriaLabelMatchesRegExp(*prevFillableField, RegexKey::CC_EXP_MONTH);
1417 RULE(previousFieldIdOrNameMatchMonth) =
1418 prevFillableField &&
1419 IdOrNameMatchRegExp(*prevFillableField, RegexKey::MONTH);
1420 RULE(previousFieldLabelsMatchMonth) =
1421 prevFillableField &&
1422 LabelMatchesRegExp(element, prevLabelStrings, RegexKey::MONTH);
1423 RULE(previousFieldPlaceholderMatchesMonth) =
1424 prevFillableField &&
1425 PlaceholderMatchesRegExp(*prevFillableField, RegexKey::MONTH);
1426 RULE(previousFieldAriaLabelMatchesMonth) =
1427 prevFillableField &&
1428 AriaLabelMatchesRegExp(*prevFillableField, RegexKey::MONTH);
1429 RULE(previousFieldMatchesExpMonthAutocomplete) =
1430 prevFillableField &&
1431 PreviousFieldMatchesExpMonthAutocomplete(prevFillableField);
1432 RULE(isExpirationYearLikely) = IsExpirationYearLikely(element);
1433 RULE(previousFieldIsExpirationMonthLikely) =
1434 prevFillableField && IsExpirationMonthLikely(*prevFillableField);
1435 RULE(placeholderMatchesYYOrYYYY) =
1436 PlaceholderMatchesRegExp(element, RegexKey::YY_OR_YYYY);
1437 RULE(roleIsMenu) = roleIsMenu;
1438 RULE(idOrNameMatchSubscription) = idOrNameMatchSubscription;
1439 RULE(idOrNameMatchDwfrmAndBml) = idOrNameMatchDwfrmAndBml;
1440 RULE(hasTemplatedValue) = hasTemplatedValue;
1441 #undef RULE_TYPE
1444 #undef RULE_IMPL2
1445 #undef RULE_IMPL
1446 #undef RULE
1448 #define CALCULATE_SCORE(type, score) \
1449 for (auto i : MakeEnumeratedRange(type##Params::Count)) { \
1450 (score) += params.m##type##Params[i] * kCoefficents.m##type##Params[i]; \
1452 (score) = Sigmoid(score + k##type##Bias);
1454 // Calculating the final score of each rule
1455 FormAutofillConfidences score;
1456 CALCULATE_SCORE(CCNumber, score.mCcNumber)
1457 CALCULATE_SCORE(CCName, score.mCcName)
1459 // Comment out code that are not used right now
1460 // CALCULATE_SCORE(CCType, score.mCcType)
1461 // CALCULATE_SCORE(CCExp, score.mCcExp)
1462 // CALCULATE_SCORE(CCExpMonth, score.mCcExpMonth)
1463 // CALCULATE_SCORE(CCExpYear, score.mCcExpYear)
1465 #undef CALCULATE_SCORE
1467 aResults.AppendElement(score);
1471 static StaticAutoPtr<FormAutofillImpl> sFormAutofillInstance;
1473 static FormAutofillImpl* GetFormAutofillImpl() {
1474 if (!sFormAutofillInstance) {
1475 sFormAutofillInstance = new FormAutofillImpl();
1476 ClearOnShutdown(&sFormAutofillInstance);
1478 return sFormAutofillInstance;
1481 /* static */
1482 void FormAutofillNative::GetFormAutofillConfidences(
1483 GlobalObject& aGlobal, const Sequence<OwningNonNull<Element>>& aElements,
1484 nsTArray<FormAutofillConfidences>& aResults, ErrorResult& aRv) {
1485 GetFormAutofillImpl()->GetFormAutofillConfidences(aGlobal, aElements,
1486 aResults, aRv);
1489 } // namespace mozilla::dom