summaryrefslogtreecommitdiffstats
path: root/toolkit/components/formautofill/FormAutofillNative.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/formautofill/FormAutofillNative.cpp')
-rw-r--r--toolkit/components/formautofill/FormAutofillNative.cpp1475
1 files changed, 1475 insertions, 0 deletions
diff --git a/toolkit/components/formautofill/FormAutofillNative.cpp b/toolkit/components/formautofill/FormAutofillNative.cpp
new file mode 100644
index 0000000000..bea61eb216
--- /dev/null
+++ b/toolkit/components/formautofill/FormAutofillNative.cpp
@@ -0,0 +1,1475 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "FormAutofillNative.h"
+
+#include <math.h>
+
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/ComputedStyle.h"
+#include "mozilla/dom/AutocompleteInfoBinding.h"
+#include "mozilla/dom/Document.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/HTMLInputElement.h"
+#include "mozilla/dom/HTMLLabelElement.h"
+#include "mozilla/dom/HTMLOptionElement.h"
+#include "mozilla/dom/HTMLSelectElement.h"
+#include "mozilla/HashTable.h"
+#include "mozilla/RustRegex.h"
+#include "nsContentUtils.h"
+#include "nsIFrame.h"
+#include "nsIFrameInlines.h"
+#include "nsLayoutUtils.h"
+#include "nsTStringHasher.h"
+#include "mozilla/StaticPtr.h"
+
+namespace mozilla::dom {
+
+static const char kWhitespace[] = "\b\t\r\n ";
+
+enum class RegexKey : uint8_t {
+ CC_NAME,
+ CC_NUMBER,
+ CC_EXP,
+ CC_EXP_MONTH,
+ CC_EXP_YEAR,
+ CC_TYPE,
+ MM_MONTH,
+ YY_OR_YYYY,
+ MONTH,
+ YEAR,
+ MMYY,
+ VISA_CHECKOUT,
+ CREDIT_CARD_NETWORK,
+ CREDIT_CARD_NETWORK_EXACT_MATCH,
+ CREDIT_CARD_NETWORK_LONG,
+ TWO_OR_FOUR_DIGIT_YEAR,
+ DWFRM,
+ BML,
+ TEMPLATED_VALUE,
+ FIRST,
+ LAST,
+ GIFT,
+ SUBSCRIPTION,
+ VALIDATION,
+
+ Count
+};
+
+// We don't follow the coding style (naming start with capital letter) here and
+// the following CCXXX enum class because we want to sync the rule naming with
+// the JS implementation.
+enum class CCNumberParams : uint8_t {
+ idOrNameMatchNumberRegExp,
+ labelsMatchNumberRegExp,
+ closestLabelMatchesNumberRegExp,
+ placeholderMatchesNumberRegExp,
+ ariaLabelMatchesNumberRegExp,
+ idOrNameMatchGift,
+ labelsMatchGift,
+ placeholderMatchesGift,
+ ariaLabelMatchesGift,
+ idOrNameMatchSubscription,
+ idOrNameMatchDwfrmAndBml,
+ hasTemplatedValue,
+ inputTypeNotNumbery,
+
+ Count,
+};
+
+enum class CCNameParams : uint8_t {
+ idOrNameMatchNameRegExp,
+ labelsMatchNameRegExp,
+ closestLabelMatchesNameRegExp,
+ placeholderMatchesNameRegExp,
+ ariaLabelMatchesNameRegExp,
+ idOrNameMatchFirst,
+ labelsMatchFirst,
+ placeholderMatchesFirst,
+ ariaLabelMatchesFirst,
+ idOrNameMatchLast,
+ labelsMatchLast,
+ placeholderMatchesLast,
+ ariaLabelMatchesLast,
+ idOrNameMatchFirstAndLast,
+ idOrNameMatchSubscription,
+ idOrNameMatchDwfrmAndBml,
+ hasTemplatedValue,
+
+ Count,
+};
+
+enum class CCTypeParams : uint8_t {
+ idOrNameMatchTypeRegExp,
+ labelsMatchTypeRegExp,
+ closestLabelMatchesTypeRegExp,
+ idOrNameMatchVisaCheckout,
+ ariaLabelMatchesVisaCheckout,
+ isSelectWithCreditCardOptions,
+ isRadioWithCreditCardText,
+ idOrNameMatchSubscription,
+ idOrNameMatchDwfrmAndBml,
+ hasTemplatedValue,
+
+ Count,
+};
+
+enum class CCExpParams : uint8_t {
+ labelsMatchExpRegExp,
+ closestLabelMatchesExpRegExp,
+ placeholderMatchesExpRegExp,
+ labelsMatchExpWith2Or4DigitYear,
+ placeholderMatchesExpWith2Or4DigitYear,
+ labelsMatchMMYY,
+ placeholderMatchesMMYY,
+ maxLengthIs7,
+ idOrNameMatchSubscription,
+ idOrNameMatchDwfrmAndBml,
+ hasTemplatedValue,
+ isExpirationMonthLikely,
+ isExpirationYearLikely,
+ idOrNameMatchMonth,
+ idOrNameMatchYear,
+ idOrNameMatchExpMonthRegExp,
+ idOrNameMatchExpYearRegExp,
+ idOrNameMatchValidation,
+ Count,
+};
+
+enum class CCExpMonthParams : uint8_t {
+ idOrNameMatchExpMonthRegExp,
+ labelsMatchExpMonthRegExp,
+ closestLabelMatchesExpMonthRegExp,
+ placeholderMatchesExpMonthRegExp,
+ ariaLabelMatchesExpMonthRegExp,
+ idOrNameMatchMonth,
+ labelsMatchMonth,
+ placeholderMatchesMonth,
+ ariaLabelMatchesMonth,
+ nextFieldIdOrNameMatchExpYearRegExp,
+ nextFieldLabelsMatchExpYearRegExp,
+ nextFieldPlaceholderMatchExpYearRegExp,
+ nextFieldAriaLabelMatchExpYearRegExp,
+ nextFieldIdOrNameMatchYear,
+ nextFieldLabelsMatchYear,
+ nextFieldPlaceholderMatchesYear,
+ nextFieldAriaLabelMatchesYear,
+ nextFieldMatchesExpYearAutocomplete,
+ isExpirationMonthLikely,
+ nextFieldIsExpirationYearLikely,
+ maxLengthIs2,
+ placeholderMatchesMM,
+ roleIsMenu,
+ idOrNameMatchSubscription,
+ idOrNameMatchDwfrmAndBml,
+ hasTemplatedValue,
+
+ Count,
+};
+
+enum class CCExpYearParams : uint8_t {
+ idOrNameMatchExpYearRegExp,
+ labelsMatchExpYearRegExp,
+ closestLabelMatchesExpYearRegExp,
+ placeholderMatchesExpYearRegExp,
+ ariaLabelMatchesExpYearRegExp,
+ idOrNameMatchYear,
+ labelsMatchYear,
+ placeholderMatchesYear,
+ ariaLabelMatchesYear,
+ previousFieldIdOrNameMatchExpMonthRegExp,
+ previousFieldLabelsMatchExpMonthRegExp,
+ previousFieldPlaceholderMatchExpMonthRegExp,
+ previousFieldAriaLabelMatchExpMonthRegExp,
+ previousFieldIdOrNameMatchMonth,
+ previousFieldLabelsMatchMonth,
+ previousFieldPlaceholderMatchesMonth,
+ previousFieldAriaLabelMatchesMonth,
+ previousFieldMatchesExpMonthAutocomplete,
+ isExpirationYearLikely,
+ previousFieldIsExpirationMonthLikely,
+ placeholderMatchesYYOrYYYY,
+ roleIsMenu,
+ idOrNameMatchSubscription,
+ idOrNameMatchDwfrmAndBml,
+ hasTemplatedValue,
+
+ Count,
+};
+
+struct AutofillParams {
+ EnumeratedArray<CCNumberParams, CCNumberParams::Count, double>
+ mCCNumberParams;
+ EnumeratedArray<CCNameParams, CCNameParams::Count, double> mCCNameParams;
+ EnumeratedArray<CCTypeParams, CCTypeParams::Count, double> mCCTypeParams;
+ EnumeratedArray<CCExpParams, CCExpParams::Count, double> mCCExpParams;
+ EnumeratedArray<CCExpMonthParams, CCExpMonthParams::Count, double>
+ mCCExpMonthParams;
+ EnumeratedArray<CCExpYearParams, CCExpYearParams::Count, double>
+ mCCExpYearParams;
+};
+
+// clang-format off
+constexpr AutofillParams kCoefficents{
+ .mCCNumberParams = {
+ /* idOrNameMatchNumberRegExp */ 7.679469585418701,
+ /* labelsMatchNumberRegExp */ 5.122580051422119,
+ /* closestLabelMatchesNumberRegExp */ 2.1256935596466064,
+ /* placeholderMatchesNumberRegExp */ 9.471800804138184,
+ /* ariaLabelMatchesNumberRegExp */ 6.067715644836426,
+ /* idOrNameMatchGift */ -22.946273803710938,
+ /* labelsMatchGift */ -7.852959632873535,
+ /* placeholderMatchesGift */ -2.355496406555176,
+ /* ariaLabelMatchesGift */ -2.940307855606079,
+ /* idOrNameMatchSubscription */ 0.11255314946174622,
+ /* idOrNameMatchDwfrmAndBml */ -0.0006645023822784424,
+ /* hasTemplatedValue */ -0.11370040476322174,
+ /* inputTypeNotNumbery */ -3.750155210494995
+ },
+ .mCCNameParams = {
+ /* idOrNameMatchNameRegExp */ 7.496212959289551,
+ /* labelsMatchNameRegExp */ 6.081472873687744,
+ /* closestLabelMatchesNameRegExp */ 2.600574254989624,
+ /* placeholderMatchesNameRegExp */ 5.750874042510986,
+ /* ariaLabelMatchesNameRegExp */ 5.162227153778076,
+ /* idOrNameMatchFirst */ -6.742659091949463,
+ /* labelsMatchFirst */ -0.5234538912773132,
+ /* placeholderMatchesFirst */ -3.4615235328674316,
+ /* ariaLabelMatchesFirst */ -1.3145145177841187,
+ /* idOrNameMatchLast */ -12.561869621276855,
+ /* labelsMatchLast */ -0.27417105436325073,
+ /* placeholderMatchesLast */ -1.434966802597046,
+ /* ariaLabelMatchesLast */ -2.9319725036621094,
+ /* idOrNameMatchFirstAndLast */ 24.123435974121094,
+ /* idOrNameMatchSubscription */ 0.08349418640136719,
+ /* idOrNameMatchDwfrmAndBml */ 0.01882520318031311,
+ /* hasTemplatedValue */ 0.182317852973938
+ },
+ .mCCTypeParams = {
+ /* idOrNameMatchTypeRegExp */ 2.0581533908843994,
+ /* labelsMatchTypeRegExp */ 1.0784518718719482,
+ /* closestLabelMatchesTypeRegExp */ 0.6995877623558044,
+ /* idOrNameMatchVisaCheckout */ -3.320356845855713,
+ /* ariaLabelMatchesVisaCheckout */ -3.4196767807006836,
+ /* isSelectWithCreditCardOptions */ 10.337477684020996,
+ /* isRadioWithCreditCardText */ 4.530318737030029,
+ /* idOrNameMatchSubscription */ -3.7206356525421143,
+ /* idOrNameMatchDwfrmAndBml */ -0.08782318234443665,
+ /* hasTemplatedValue */ 0.1772511601448059
+ },
+ .mCCExpParams = {
+ /* labelsMatchExpRegExp */ 7.588159561157227,
+ /* closestLabelMatchesExpRegExp */ 1.41484534740448,
+ /* placeholderMatchesExpRegExp */ 8.759064674377441,
+ /* labelsMatchExpWith2Or4DigitYear */ -3.876218795776367,
+ /* placeholderMatchesExpWith2Or4DigitYear */ 2.8364884853363037,
+ /* labelsMatchMMYY */ 8.836017608642578,
+ /* placeholderMatchesMMYY */ -0.5231751799583435,
+ /* maxLengthIs7 */ 1.3565447330474854,
+ /* idOrNameMatchSubscription */ 0.1779913753271103,
+ /* idOrNameMatchDwfrmAndBml */ 0.21037884056568146,
+ /* hasTemplatedValue */ 0.14900512993335724,
+ /* isExpirationMonthLikely */ -3.223409652709961,
+ /* isExpirationYearLikely */ -2.536919593811035,
+ /* idOrNameMatchMonth */ -3.6893014907836914,
+ /* idOrNameMatchYear */ -3.108184337615967,
+ /* idOrNameMatchExpMonthRegExp */ -2.264357089996338,
+ /* idOrNameMatchExpYearRegExp */ -2.7957723140716553,
+ /* idOrNameMatchValidation */ -2.29402756690979
+ },
+ .mCCExpMonthParams = {
+ /* idOrNameMatchExpMonthRegExp */ 0.2787344455718994,
+ /* labelsMatchExpMonthRegExp */ 1.298413634300232,
+ /* closestLabelMatchesExpMonthRegExp */ -11.206244468688965,
+ /* placeholderMatchesExpMonthRegExp */ 1.2605619430541992,
+ /* ariaLabelMatchesExpMonthRegExp */ 1.1330018043518066,
+ /* idOrNameMatchMonth */ 6.1464314460754395,
+ /* labelsMatchMonth */ 0.7051732540130615,
+ /* placeholderMatchesMonth */ 0.7463492751121521,
+ /* ariaLabelMatchesMonth */ 1.8244760036468506,
+ /* nextFieldIdOrNameMatchExpYearRegExp */ 0.06347066164016724,
+ /* nextFieldLabelsMatchExpYearRegExp */ -0.1692247837781906,
+ /* nextFieldPlaceholderMatchExpYearRegExp */ 1.0434566736221313,
+ /* nextFieldAriaLabelMatchExpYearRegExp */ 1.751156210899353,
+ /* nextFieldIdOrNameMatchYear */ -0.532447338104248,
+ /* nextFieldLabelsMatchYear */ 1.3248541355133057,
+ /* nextFieldPlaceholderMatchesYear */ 0.604235827922821,
+ /* nextFieldAriaLabelMatchesYear */ 1.5364223718643188,
+ /* nextFieldMatchesExpYearAutocomplete */ 6.285938262939453,
+ /* isExpirationMonthLikely */ 13.117807388305664,
+ /* nextFieldIsExpirationYearLikely */ 7.182341575622559,
+ /* maxLengthIs2 */ 4.477289199829102,
+ /* placeholderMatchesMM */ 14.403288841247559,
+ /* roleIsMenu */ 5.770959854125977,
+ /* idOrNameMatchSubscription */ -0.043085768818855286,
+ /* idOrNameMatchDwfrmAndBml */ 0.02823038399219513,
+ /* hasTemplatedValue */ 0.07234494388103485
+ },
+ .mCCExpYearParams = {
+ /* idOrNameMatchExpYearRegExp */ 5.426016807556152,
+ /* labelsMatchExpYearRegExp */ 1.3240209817886353,
+ /* closestLabelMatchesExpYearRegExp */ -8.702284812927246,
+ /* placeholderMatchesExpYearRegExp */ 0.9059725999832153,
+ /* ariaLabelMatchesExpYearRegExp */ 0.5550334453582764,
+ /* idOrNameMatchYear */ 5.362994194030762,
+ /* labelsMatchYear */ 2.7185044288635254,
+ /* placeholderMatchesYear */ 0.7883157134056091,
+ /* ariaLabelMatchesYear */ 0.311492383480072,
+ /* previousFieldIdOrNameMatchExpMonthRegExp */ 1.8155208826065063,
+ /* previousFieldLabelsMatchExpMonthRegExp */ -0.46133187413215637,
+ /* previousFieldPlaceholderMatchExpMonthRegExp */ 1.0374903678894043,
+ /* previousFieldAriaLabelMatchExpMonthRegExp */ -0.5901495814323425,
+ /* previousFieldIdOrNameMatchMonth */ -5.960310935974121,
+ /* previousFieldLabelsMatchMonth */ 0.6495584845542908,
+ /* previousFieldPlaceholderMatchesMonth */ 0.7198042273521423,
+ /* previousFieldAriaLabelMatchesMonth */ 3.4590985774993896,
+ /* previousFieldMatchesExpMonthAutocomplete */ 2.986003875732422,
+ /* isExpirationYearLikely */ 4.021566390991211,
+ /* previousFieldIsExpirationMonthLikely */ 9.298635482788086,
+ /* placeholderMatchesYYOrYYYY */ 10.457176208496094,
+ /* roleIsMenu */ 1.1051956415176392,
+ /* idOrNameMatchSubscription */ 0.000688597559928894,
+ /* idOrNameMatchDwfrmAndBml */ 0.15687309205532074,
+ /* hasTemplatedValue */ -0.19141331315040588
+ }
+};
+// clang-format off
+
+constexpr float kCCNumberBias = -4.948795795440674;
+constexpr float kCCNameBias = -5.3578081130981445;
+// Comment out code that are not used right now
+/*
+constexpr float kCCTypeBias = -5.979659557342529;
+constexpr float kCCExpBias = -5.849575996398926;
+constexpr float kCCExpMonthBias = -8.844199180603027;
+constexpr float kCCExpYearBias = -6.499860763549805;
+*/
+
+struct Rule {
+ RegexKey key;
+ const char* pattern;
+};
+
+const Rule kFirefoxRules[] = {
+ {RegexKey::MM_MONTH, "^mm$|\\(mm\\)"},
+ {RegexKey::YY_OR_YYYY, "^(yy|yyyy)$|\\(yy\\)|\\(yyyy\\)"},
+ {RegexKey::MONTH, "month"},
+ {RegexKey::YEAR, "year"},
+ {RegexKey::MMYY, "mm\\s*(/|\\\\)\\s*yy"},
+ {RegexKey::VISA_CHECKOUT, "visa(-|\\s)checkout"},
+ // This should be a union of NETWORK_NAMES in CreditCard.sys.mjs
+ {RegexKey::CREDIT_CARD_NETWORK_LONG,
+ "american express|master card|union pay"},
+ // Please also update CREDIT_CARD_NETWORK_EXACT_MATCH while updating
+ // CREDIT_CARD_NETWORK
+ {RegexKey::CREDIT_CARD_NETWORK,
+ "amex|cartebancaire|diners|discover|jcb|mastercard|mir|unionpay|visa"},
+ {RegexKey::CREDIT_CARD_NETWORK_EXACT_MATCH,
+ "^\\s*(?:amex|cartebancaire|diners|discover|jcb|mastercard|mir|unionpay|"
+ "visa)\\s*$"},
+ {RegexKey::TWO_OR_FOUR_DIGIT_YEAR,
+ "(?:exp.*date[^y\\\\n\\\\r]*|mm\\\\s*[-/]?\\\\s*)yy(?:yy)?(?:[^y]|$)"},
+ {RegexKey::DWFRM, "^dwfrm"},
+ {RegexKey::BML, "BML"},
+ {RegexKey::TEMPLATED_VALUE, "^\\{\\{.*\\}\\}$"},
+ {RegexKey::FIRST, "first"},
+ {RegexKey::LAST, "last"},
+ {RegexKey::GIFT, "gift"},
+ {RegexKey::SUBSCRIPTION, "subscription"},
+ {RegexKey::VALIDATION, "validate|validation"},
+};
+
+// These are the rules used by Bitwarden [0], converted into RegExp form.
+// [0]
+// https://github.com/bitwarden/browser/blob/c2b8802201fac5e292d55d5caf3f1f78088d823c/src/services/autofill.service.ts#L436
+const Rule kCreditCardRules[] = {
+ /* eslint-disable */
+ // Let us keep our consistent wrapping.
+ {RegexKey::CC_NAME,
+ // Firefox-specific rules
+ "account.*holder.*name"
+ // de-DE
+ "|^(kredit)?(karten|konto)inhaber"
+ "|^(name).*karte"
+ // fr-FR
+ "|nom.*(titulaire|détenteur)"
+ "|(titulaire|détenteur).*(carte)"
+ // Rules from Bitwarden
+ "|cc-?name"
+ "|card-?name"
+ "|cardholder-?name"
+ "|(^nom$)"
+ // Rules are from Chromium source codes
+ "|card.?(?:holder|owner)|name.*(\\b)?on(\\b)?.*card"
+ "|(?:card|cc).?name|cc.?full.?name"
+ "|(?:card|cc).?owner"
+ "|nombre.*tarjeta" // es
+ "|nom.*carte" // fr-FR
+ "|nome.*cart" // it-IT
+ "|名前" // ja-JP
+ "|Имя.*карты" // ru
+ "|信用卡开户名|开户名|持卡人姓名" // zh-CN
+ "|持卡人姓名"}, // zh-TW
+ /* eslint-enable */
+
+ {RegexKey::CC_NUMBER,
+ // Firefox-specific rules
+ // de-DE
+ "(cc|kk)nr"
+ "|(kredit)?(karten)(nummer|nr)"
+ // fr-FR
+ "|(numero|número|numéro).*(carte)"
+ // Rules from Bitwarden
+ "|cc-?number"
+ "|cc-?num"
+ "|card-?number"
+ "|card-?num"
+ "|cc-?no"
+ "|card-?no"
+ "|numero-?carte"
+ "|num-?carte"
+ "|cb-?num"
+ // Rules are from Chromium source codes
+ "|(add)?(?:card|cc|acct).?(?:number|#|no|num)"
+ "|カード番号" // ja-JP
+ "|Номер.*карты" // ru
+ "|信用卡号|信用卡号码" // zh-CN
+ "|信用卡卡號" // zh-TW
+ "|카드"}, // ko-KR
+
+ {RegexKey::CC_EXP,
+ // Firefox-specific rules
+ "mm\\s*(/|\\|-)\\s*(yy|jj|aa)"
+ "|(month|mois)\\s*(/|\\|-|et)\\s*(year|année)"
+ // de-DE
+ // fr-FR
+ // Rules from Bitwarden
+ "|(^cc-?exp$)"
+ "|(^card-?exp$)"
+ "|(^cc-?expiration$)"
+ "|(^card-?expiration$)"
+ "|(^cc-?ex$)"
+ "|(^card-?ex$)"
+ "|(^card-?expire$)"
+ "|(^card-?expiry$)"
+ "|(^validite$)"
+ "|(^expiration$)"
+ "|(^expiry$)"
+ "|mm-?yy"
+ "|mm-?yyyy"
+ "|yy-?mm"
+ "|yyyy-?mm"
+ "|expiration-?date"
+ "|payment-?card-?expiration"
+ "|(^payment-?cc-?date$)"
+ // Rules are from Chromium source codes
+ "|expir|exp.*date|^expfield$"
+ "|ablaufdatum|gueltig|gültig" // de-DE
+ "|fecha" // es
+ "|date.*exp" // fr-FR
+ "|scadenza" // it-IT
+ "|有効期限" // ja-JP
+ "|validade" // pt-BR, pt-PT
+ "|Срок действия карты"}, // ru
+
+ {RegexKey::CC_EXP_MONTH,
+ // Firefox-specific rules
+ "(cc|kk)month" // de-DE
+ // Rules from Bitwarden
+ "|(^exp-?month$)"
+ "|(^cc-?exp-?month$)"
+ "|(^cc-?month$)"
+ "|(^card-?month$)"
+ "|(^cc-?mo$)"
+ "|(^card-?mo$)"
+ "|(^exp-?mo$)"
+ "|(^card-?exp-?mo$)"
+ "|(^cc-?exp-?mo$)"
+ "|(^card-?expiration-?month$)"
+ "|(^expiration-?month$)"
+ "|(^cc-?mm$)"
+ "|(^cc-?m$)"
+ "|(^card-?mm$)"
+ "|(^card-?m$)"
+ "|(^card-?exp-?mm$)"
+ "|(^cc-?exp-?mm$)"
+ "|(^exp-?mm$)"
+ "|(^exp-?m$)"
+ "|(^expire-?month$)"
+ "|(^expire-?mo$)"
+ "|(^expiry-?month$)"
+ "|(^expiry-?mo$)"
+ "|(^card-?expire-?month$)"
+ "|(^card-?expire-?mo$)"
+ "|(^card-?expiry-?month$)"
+ "|(^card-?expiry-?mo$)"
+ "|(^mois-?validite$)"
+ "|(^mois-?expiration$)"
+ "|(^m-?validite$)"
+ "|(^m-?expiration$)"
+ "|(^expiry-?date-?field-?month$)"
+ "|(^expiration-?date-?month$)"
+ "|(^expiration-?date-?mm$)"
+ "|(^exp-?mon$)"
+ "|(^validity-?mo$)"
+ "|(^exp-?date-?mo$)"
+ "|(^cb-?date-?mois$)"
+ "|(^date-?m$)"
+ // Rules are from Chromium source codes
+ "|exp.*mo|ccmonth|cardmonth|addmonth"
+ "|monat" // de-DE
+ // "|fecha" // es
+ // "|date.*exp" // fr-FR
+ // "|scadenza" // it-IT
+ // "|有効期限" // ja-JP
+ // "|validade" // pt-BR, pt-PT
+ // "|Срок действия карты" // ru
+ "|月"}, // zh-CN
+
+ {RegexKey::CC_EXP_YEAR,
+ // Firefox-specific rules
+ "(cc|kk)year" // de-DE
+ // Rules from Bitwarden
+ "|(^exp-?year$)"
+ "|(^cc-?exp-?year$)"
+ "|(^cc-?year$)"
+ "|(^card-?year$)"
+ "|(^cc-?yr$)"
+ "|(^card-?yr$)"
+ "|(^exp-?yr$)"
+ "|(^card-?exp-?yr$)"
+ "|(^cc-?exp-?yr$)"
+ "|(^card-?expiration-?year$)"
+ "|(^expiration-?year$)"
+ "|(^cc-?yy$)"
+ "|(^cc-?y$)"
+ "|(^card-?yy$)"
+ "|(^card-?y$)"
+ "|(^card-?exp-?yy$)"
+ "|(^cc-?exp-?yy$)"
+ "|(^exp-?yy$)"
+ "|(^exp-?y$)"
+ "|(^cc-?yyyy$)"
+ "|(^card-?yyyy$)"
+ "|(^card-?exp-?yyyy$)"
+ "|(^cc-?exp-?yyyy$)"
+ "|(^expire-?year$)"
+ "|(^expire-?yr$)"
+ "|(^expiry-?year$)"
+ "|(^expiry-?yr$)"
+ "|(^card-?expire-?year$)"
+ "|(^card-?expire-?yr$)"
+ "|(^card-?expiry-?year$)"
+ "|(^card-?expiry-?yr$)"
+ "|(^an-?validite$)"
+ "|(^an-?expiration$)"
+ "|(^annee-?validite$)"
+ "|(^annee-?expiration$)"
+ "|(^expiry-?date-?field-?year$)"
+ "|(^expiration-?date-?year$)"
+ "|(^cb-?date-?ann$)"
+ "|(^expiration-?date-?yy$)"
+ "|(^expiration-?date-?yyyy$)"
+ "|(^validity-?year$)"
+ "|(^exp-?date-?year$)"
+ "|(^date-?y$)"
+ // Rules are from Chromium source codes
+ "|(add)?year"
+ "|jahr" // de-DE
+ // "|fecha" // es
+ // "|scadenza" // it-IT
+ // "|有効期限" // ja-JP
+ // "|validade" // pt-BR, pt-PT
+ // "|Срок действия карты" // ru
+ "|年|有效期"}, // zh-CN
+
+ {RegexKey::CC_TYPE,
+ // Firefox-specific rules
+ "type"
+ // de-DE
+ "|Kartenmarke"
+ // Rules from Bitwarden
+ "|(^cc-?type$)"
+ "|(^card-?type$)"
+ "|(^card-?brand$)"
+ "|(^cc-?brand$)"
+ "|(^cb-?type$)"},
+ // Rules are from Chromium source codes
+};
+
+static double Sigmoid(double x) { return 1.0 / (1.0 + exp(-x)); }
+
+class FormAutofillImpl {
+ public:
+ FormAutofillImpl();
+
+ void GetFormAutofillConfidences(
+ GlobalObject& aGlobal, const Sequence<OwningNonNull<Element>>& aElements,
+ nsTArray<FormAutofillConfidences>& aResults, ErrorResult& aRv);
+
+ private:
+ const RustRegex& GetRegex(RegexKey key);
+
+ bool StringMatchesRegExp(const nsACString& str, RegexKey key);
+ bool StringMatchesRegExp(const nsAString& str, RegexKey key);
+ bool TextContentMatchesRegExp(Element& element, RegexKey key);
+ size_t CountRegExpMatches(const nsACString& str, RegexKey key);
+ size_t CountRegExpMatches(const nsAString& str, RegexKey key);
+ bool IdOrNameMatchRegExp(Element& element, RegexKey key);
+ bool NextFieldMatchesExpYearAutocomplete(Element* aNextField);
+ bool PreviousFieldMatchesExpMonthAutocomplete(Element* aPrevField);
+ bool LabelMatchesRegExp(Element& element, const nsTArray<nsCString>* labels,
+ RegexKey key);
+ bool ClosestLabelMatchesRegExp(Element& aElement, RegexKey aKey);
+ bool PlaceholderMatchesRegExp(Element& element, RegexKey key);
+ bool AriaLabelMatchesRegExp(Element& element, RegexKey key);
+ bool AutocompleteStringMatches(Element& aElement, const nsAString& aKey);
+
+ bool HasTemplatedValue(Element& element);
+ bool MaxLengthIs(Element& aElement, int32_t aValue);
+ bool IsExpirationMonthLikely(Element& element);
+ bool IsExpirationYearLikely(Element& element);
+ bool InputTypeNotNumbery(Element& element);
+ bool IsSelectWithCreditCardOptions(Element& element);
+ bool IsRadioWithCreditCardText(Element& element,
+ const nsTArray<nsCString>* labels,
+ ErrorResult& aRv);
+ bool MatchesExpYearAutocomplete(Element& element);
+ bool RoleIsMenu(Element& element);
+
+ Element* FindRootForField(Element* aElement);
+
+ Element* FindField(const Sequence<OwningNonNull<Element>>& aElements,
+ uint32_t aStartIndex, int8_t aDirection);
+ Element* NextField(const Sequence<OwningNonNull<Element>>& aElements,
+ uint32_t aStartIndex);
+ Element* PrevField(const Sequence<OwningNonNull<Element>>& aElements,
+ uint32_t aStartIndex);
+
+ // Array contains regular expressions to match the corresponding
+ // field. Ex, CC number, CC type, etc.
+ using RegexStringArray =
+ EnumeratedArray<RegexKey, RegexKey::Count, nsCString>;
+ RegexStringArray mRuleMap;
+
+ // Array that holds RegexWrapper that created by regex::ffi::regex_new
+ using RegexWrapperArray =
+ EnumeratedArray<RegexKey, RegexKey::Count,
+ RustRegex>;
+ RegexWrapperArray mRegexes;
+};
+
+FormAutofillImpl::FormAutofillImpl() {
+ const Rule* rulesets[] = {&kFirefoxRules[0], &kCreditCardRules[0]};
+ size_t rulesetLengths[] = {ArrayLength(kFirefoxRules),
+ ArrayLength(kCreditCardRules)};
+
+ for (uint32_t i = 0; i < ArrayLength(rulesetLengths); ++i) {
+ for (uint32_t j = 0; j < rulesetLengths[i]; ++j) {
+ nsCString& rule = mRuleMap[rulesets[i][j].key];
+ if (!rule.IsEmpty()) {
+ rule.Append("|");
+ }
+ rule.Append(rulesets[i][j].pattern);
+ }
+ }
+}
+
+const RustRegex& FormAutofillImpl::GetRegex(RegexKey aKey) {
+ if (!mRegexes[aKey]) {
+ RustRegex regex(mRuleMap[aKey], RustRegexOptions().CaseInsensitive(true));
+ MOZ_DIAGNOSTIC_ASSERT(regex);
+ mRegexes[aKey] = std::move(regex);
+ }
+ return mRegexes[aKey];
+}
+
+bool FormAutofillImpl::StringMatchesRegExp(const nsACString& aStr,
+ RegexKey aKey) {
+ return GetRegex(aKey).IsMatch(aStr);
+}
+
+bool FormAutofillImpl::StringMatchesRegExp(const nsAString& aStr,
+ RegexKey aKey) {
+ return StringMatchesRegExp(NS_ConvertUTF16toUTF8(aStr), aKey);
+}
+
+bool FormAutofillImpl::TextContentMatchesRegExp(Element& element,
+ RegexKey key) {
+ ErrorResult rv;
+ nsAutoString text;
+ element.GetTextContent(text, rv);
+ if (rv.Failed()) {
+ return false;
+ }
+
+ return StringMatchesRegExp(text, key);
+}
+
+size_t FormAutofillImpl::CountRegExpMatches(const nsACString& aStr,
+ RegexKey aKey) {
+ return GetRegex(aKey).CountMatches(aStr);
+}
+
+size_t FormAutofillImpl::CountRegExpMatches(const nsAString& aStr,
+ RegexKey aKey) {
+ return CountRegExpMatches(NS_ConvertUTF16toUTF8(aStr), aKey);
+}
+
+bool FormAutofillImpl::NextFieldMatchesExpYearAutocomplete(
+ Element* aNextField) {
+ return AutocompleteStringMatches(*aNextField, u"cc-exp-year"_ns);
+}
+
+bool FormAutofillImpl::PreviousFieldMatchesExpMonthAutocomplete(
+ Element* aPrevField) {
+ return AutocompleteStringMatches(*aPrevField, u"cc-exp-month"_ns);
+}
+
+bool FormAutofillImpl::IdOrNameMatchRegExp(Element& aElement, RegexKey key) {
+ nsAutoString str;
+ aElement.GetId(str);
+ if (StringMatchesRegExp(str, key)) {
+ return true;
+ }
+ aElement.GetAttr(nsGkAtoms::name, str);
+ return StringMatchesRegExp(str, key);
+}
+
+bool FormAutofillImpl::LabelMatchesRegExp(
+ Element& aElement, const nsTArray<nsCString>* labelStrings, RegexKey key) {
+ if (!labelStrings) {
+ return false;
+ }
+
+ for (const auto& str : *labelStrings) {
+ if (StringMatchesRegExp(str, key)) {
+ return true;
+ }
+ }
+
+ Element* parent = aElement.GetParentElement();
+ if (!parent) {
+ return false;
+ }
+
+ ErrorResult aRv;
+ if (parent->IsHTMLElement(nsGkAtoms::td)) {
+ Element* pp = parent->GetParentElement();
+ if (pp) {
+ return TextContentMatchesRegExp(*pp, key);
+ }
+ }
+ if (parent->IsHTMLElement(nsGkAtoms::td)) {
+ Element* pes = aElement.GetPreviousElementSibling();
+ if (pes) {
+ return TextContentMatchesRegExp(*pes, key);
+ }
+ }
+ return false;
+}
+
+bool FormAutofillImpl::ClosestLabelMatchesRegExp(Element& aElement,
+ RegexKey aKey) {
+ ErrorResult aRv;
+ Element* pes = aElement.GetPreviousElementSibling();
+ if (pes && pes->IsHTMLElement(nsGkAtoms::label)) {
+ return TextContentMatchesRegExp(*pes, aKey);
+ }
+
+ Element* nes = aElement.GetNextElementSibling();
+ if (nes && nes->IsHTMLElement(nsGkAtoms::label)) {
+ return TextContentMatchesRegExp(*nes, aKey);
+ }
+
+ return false;
+}
+
+bool FormAutofillImpl::PlaceholderMatchesRegExp(Element& aElement,
+ RegexKey aKey) {
+ nsAutoString str;
+ if (!aElement.GetAttr(nsGkAtoms::placeholder, str)) {
+ return false;
+ }
+ return StringMatchesRegExp(str, aKey);
+}
+
+bool FormAutofillImpl::AriaLabelMatchesRegExp(Element& aElement,
+ RegexKey aKey) {
+ nsAutoString str;
+ if (!aElement.GetAttr(nsGkAtoms::aria_label, str)) {
+ return false;
+ }
+ return StringMatchesRegExp(str, aKey);
+}
+
+bool FormAutofillImpl::AutocompleteStringMatches(Element& aElement,
+ const nsAString& aKey) {
+ Nullable<AutocompleteInfo> info;
+ if (auto* input = HTMLInputElement::FromNode(aElement)) {
+ input->GetAutocompleteInfo(info);
+ } else {
+ AutocompleteInfo autoInfo;
+ if (auto* select = HTMLSelectElement::FromNode(aElement)) {
+ select->GetAutocompleteInfo(autoInfo);
+ info.SetValue(autoInfo);
+ }
+ }
+
+ if (info.IsNull()) {
+ return false;
+ }
+
+ return info.Value().mFieldName.Equals(aKey);
+}
+
+bool FormAutofillImpl::HasTemplatedValue(Element& aElement) {
+ nsAutoString str;
+ if (!aElement.GetAttr(nsGkAtoms::value, str)) {
+ return false;
+ }
+ return StringMatchesRegExp(str, RegexKey::TEMPLATED_VALUE);
+}
+
+bool FormAutofillImpl::RoleIsMenu(Element& aElement) {
+ return aElement.AttrValueIs(kNameSpaceID_None, nsGkAtoms::role,
+ nsGkAtoms::menu, eCaseMatters);
+}
+
+bool FormAutofillImpl::InputTypeNotNumbery(Element& aElement) {
+ auto* input = HTMLInputElement::FromNode(aElement);
+ if (!input) {
+ return true;
+ }
+
+ auto type = input->ControlType();
+ return type != FormControlType::InputText &&
+ type != FormControlType::InputTel &&
+ type != FormControlType::InputNumber;
+}
+
+bool FormAutofillImpl::IsSelectWithCreditCardOptions(Element& aElement) {
+ auto* select = HTMLSelectElement::FromNode(aElement);
+ if (!select) {
+ return false;
+ }
+
+ nsCOMPtr<nsIHTMLCollection> options = select->Options();
+ for (uint32_t i = 0; i < options->Length(); ++i) {
+ auto* item = options->Item(i);
+ auto* option = HTMLOptionElement::FromNode(item);
+ if (!option) {
+ continue;
+ }
+ // Bug 1756799, consider using getAttribute("value") instead of .value
+ nsAutoString str;
+ option->GetValue(str);
+ if (StringMatchesRegExp(str, RegexKey::CREDIT_CARD_NETWORK_EXACT_MATCH) ||
+ StringMatchesRegExp(str, RegexKey::CREDIT_CARD_NETWORK_LONG)) {
+ return true;
+ }
+
+ option->GetText(str);
+ if (StringMatchesRegExp(str, RegexKey::CREDIT_CARD_NETWORK_EXACT_MATCH) ||
+ StringMatchesRegExp(str, RegexKey::CREDIT_CARD_NETWORK_LONG)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool FormAutofillImpl::IsRadioWithCreditCardText(
+ Element& aElement, const nsTArray<nsCString>* aLabels, ErrorResult& aRv) {
+ auto* input = HTMLInputElement::FromNode(aElement);
+ if (!input) {
+ return false;
+ }
+ auto type = input->ControlType();
+ if (type != FormControlType::InputRadio) {
+ return false;
+ }
+
+ nsAutoString str;
+ input->GetValue(str, CallerType::System);
+ if (CountRegExpMatches(str, RegexKey::CREDIT_CARD_NETWORK) == 1) {
+ return true;
+ }
+
+ if (aLabels) {
+ size_t labelsMatched = 0;
+ for (const auto& label : *aLabels) {
+ size_t labelMatches =
+ CountRegExpMatches(label, RegexKey::CREDIT_CARD_NETWORK);
+ if (labelMatches > 1) {
+ return false;
+ }
+ if (labelMatches > 0) {
+ labelsMatched++;
+ }
+ }
+
+ if (labelsMatched) {
+ return labelsMatched == 1;
+ }
+ }
+
+ // Bug 1756798 : Remove reading text content in a <input>
+ nsAutoString text;
+ aElement.GetTextContent(text, aRv);
+ if (aRv.Failed()) {
+ return false;
+ }
+ return CountRegExpMatches(text, RegexKey::CREDIT_CARD_NETWORK) == 1;
+}
+
+bool FormAutofillImpl::MaxLengthIs(Element& aElement, int32_t aValue) {
+ auto* input = HTMLInputElement::FromNode(aElement);
+ if (!input) {
+ return false;
+ }
+ return input->MaxLength() == aValue;
+}
+
+static bool TestOptionElementForInteger(Element* aElement, int32_t aTestValue) {
+ auto* option = HTMLOptionElement::FromNodeOrNull(aElement);
+ if (!option) {
+ return false;
+ }
+ nsAutoString str;
+ option->GetValue(str);
+ nsContentUtils::ParseHTMLIntegerResultFlags parseFlags;
+ int32_t val = nsContentUtils::ParseHTMLInteger(str, &parseFlags);
+ if (val == aTestValue) {
+ return true;
+ }
+ option->GetRenderedLabel(str);
+ val = nsContentUtils::ParseHTMLInteger(str, &parseFlags);
+ return val == aTestValue;
+}
+
+static bool MatchOptionContiguousInteger(HTMLOptionsCollection* aOptions,
+ uint32_t aNumContiguous,
+ int32_t aInteger) {
+ uint32_t len = aOptions->Length();
+ if (aNumContiguous > len) {
+ return false;
+ }
+
+ for (uint32_t i = 0; i <= aOptions->Length() - aNumContiguous; i++) {
+ bool match = true;
+ for (uint32_t j = 0; j < aNumContiguous; j++) {
+ if (!TestOptionElementForInteger(aOptions->GetElementAt(i + j),
+ aInteger + j)) {
+ match = false;
+ break;
+ }
+ }
+ if (match) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool FormAutofillImpl::IsExpirationYearLikely(Element& aElement) {
+ auto* select = HTMLSelectElement::FromNode(aElement);
+ if (!select) {
+ return false;
+ }
+
+ auto* options = select->Options();
+ if (!options) {
+ return false;
+ }
+
+ PRExplodedTime tm;
+ PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &tm);
+ uint16_t currentYear = tm.tm_year;
+
+ return MatchOptionContiguousInteger(options, 3, currentYear);
+}
+
+bool FormAutofillImpl::IsExpirationMonthLikely(Element& aElement) {
+ auto* select = HTMLSelectElement::FromNode(aElement);
+ if (!select) {
+ return false;
+ }
+
+ auto* options = select->Options();
+ if (!options) {
+ return false;
+ }
+
+ if (options->Length() != 12 && options->Length() != 13) {
+ return false;
+ }
+
+ return MatchOptionContiguousInteger(options, 12, 1);
+}
+
+Element* FormAutofillImpl::FindRootForField(Element* aElement) {
+ if (const auto* control =
+ nsGenericHTMLFormControlElement::FromNode(aElement)) {
+ if (Element* form = control->GetForm()) {
+ return form;
+ }
+ }
+
+ return aElement->OwnerDoc()->GetDocumentElement();
+}
+
+Element* FormAutofillImpl::FindField(
+ const Sequence<OwningNonNull<Element>>& aElements, uint32_t aStartIndex,
+ int8_t aDirection) {
+ MOZ_ASSERT(aDirection == 1 || aDirection == -1);
+ MOZ_ASSERT(aStartIndex < aElements.Length());
+
+ Element* curFieldRoot = FindRootForField(aElements[aStartIndex]);
+ bool isRootForm = curFieldRoot->IsHTMLElement(nsGkAtoms::form);
+
+ uint32_t num =
+ aDirection == 1 ? aElements.Length() - aStartIndex - 1 : aStartIndex;
+ for (uint32_t i = 0, searchIndex = aStartIndex; i < num; i++) {
+ searchIndex += aDirection;
+ const auto& element = aElements[searchIndex];
+ Element* root = FindRootForField(element);
+
+ if (isRootForm) {
+ // Only search fields that are within the same root element.
+ if (curFieldRoot != root) {
+ return nullptr;
+ }
+ } else {
+ // Exclude elements inside the rootElement that are already in a <form>.
+ if (root->IsHTMLElement(nsGkAtoms::form)) {
+ continue;
+ }
+ }
+
+ if (element->IsAnyOfHTMLElements(nsGkAtoms::input, nsGkAtoms::select)) {
+ return element.get();
+ }
+ }
+
+ return nullptr;
+}
+
+Element* FormAutofillImpl::NextField(
+ const Sequence<OwningNonNull<Element>>& aElements, uint32_t aStartIndex) {
+ return FindField(aElements, aStartIndex, 1);
+}
+
+Element* FormAutofillImpl::PrevField(
+ const Sequence<OwningNonNull<Element>>& aElements, uint32_t aStartIndex) {
+ return FindField(aElements, aStartIndex, -1);
+}
+
+static void ExtractLabelStrings(nsINode* aNode, nsTArray<nsCString>& aStrings,
+ ErrorResult& aRv) {
+ if (aNode->IsAnyOfHTMLElements(nsGkAtoms::script, nsGkAtoms::noscript,
+ nsGkAtoms::option, nsGkAtoms::style)) {
+ return;
+ }
+
+ if (aNode->IsText() || !aNode->HasChildren()) {
+ nsAutoString text;
+ aNode->GetTextContent(text, aRv);
+ if (aRv.Failed()) {
+ return;
+ }
+
+ text.Trim(kWhitespace);
+ CopyUTF16toUTF8(text, *aStrings.AppendElement());
+ return;
+ }
+
+ for (nsINode* child = aNode->GetFirstChild(); child;
+ child = child->GetNextSibling()) {
+ if (child->IsElement() || child->IsText()) {
+ ExtractLabelStrings(child, aStrings, aRv);
+ if (aRv.Failed()) {
+ return;
+ }
+ }
+ }
+}
+
+nsTArray<nsCString>* GetLabelStrings(
+ Element* aElement,
+ const nsTHashMap<void*, nsTArray<nsCString>>& aElementMap,
+ const nsTHashMap<nsAtom*, nsTArray<nsCString>>& aIdMap) {
+ if (!aElement) {
+ return nullptr;
+ }
+
+ if (nsAtom* idAtom = aElement->GetID()) {
+ return aIdMap.Lookup(idAtom).DataPtrOrNull();
+ }
+
+ return aElementMap.Lookup(aElement).DataPtrOrNull();
+}
+
+void FormAutofillImpl::GetFormAutofillConfidences(
+ GlobalObject& aGlobal, const Sequence<OwningNonNull<Element>>& aElements,
+ nsTArray<FormAutofillConfidences>& aResults, ErrorResult& aRv) {
+ if (aElements.IsEmpty()) {
+ return;
+ }
+
+ // Create Labels
+ auto* document = aElements[0]->OwnerDoc();
+#ifdef DEBUG
+ for (uint32_t i = 1; i < aElements.Length(); ++i) {
+ MOZ_ASSERT(document == aElements[i]->OwnerDoc());
+ }
+#endif
+
+ RefPtr<nsContentList> labels = document->GetElementsByTagName(u"label"_ns);
+ nsTHashMap<void*, nsTArray<nsCString>> elementsToLabelStrings;
+ nsTHashMap<nsAtom*, nsTArray<nsCString>> elementsIdToLabelStrings;
+ if (labels) {
+ for (uint32_t i = 0; i < labels->Length(); ++i) {
+ auto* item = labels->Item(i);
+ auto* label = HTMLLabelElement::FromNode(item);
+ if (NS_WARN_IF(!label)) {
+ continue;
+ }
+ auto* control = label->GetControl();
+ if (!control) {
+ continue;
+ }
+ nsTArray<nsCString> labelStrings;
+ ExtractLabelStrings(label, labelStrings, aRv);
+ if (aRv.Failed()) {
+ return;
+ }
+
+ // We need two maps here to keep track controls with id and without id.
+ // We can't just use map without id to cover all cases because there
+ // might be multiple elements with the same id.
+ if (control->GetID()) {
+ elementsIdToLabelStrings.LookupOrInsert(control->GetID())
+ .AppendElements(std::move(labelStrings));
+ } else {
+ elementsToLabelStrings.LookupOrInsert(control).AppendElements(
+ std::move(labelStrings));
+ }
+ }
+ }
+
+ nsTArray<AutofillParams> paramSet;
+ paramSet.SetLength(aElements.Length());
+
+ for (uint32_t i = 0; i < aElements.Length(); ++i) {
+ auto& params = paramSet[i];
+ const auto& element = aElements[i];
+
+ const nsTArray<nsCString>* labelStrings = GetLabelStrings(
+ element, elementsToLabelStrings, elementsIdToLabelStrings);
+
+ bool idOrNameMatchDwfrmAndBml =
+ IdOrNameMatchRegExp(element, RegexKey::DWFRM) &&
+ IdOrNameMatchRegExp(element, RegexKey::BML);
+ bool hasTemplatedValue = HasTemplatedValue(element);
+ bool inputTypeNotNumbery = InputTypeNotNumbery(element);
+ bool idOrNameMatchSubscription =
+ IdOrNameMatchRegExp(element, RegexKey::SUBSCRIPTION);
+ bool idOrNameMatchFirstAndLast =
+ IdOrNameMatchRegExp(element, RegexKey::FIRST) &&
+ IdOrNameMatchRegExp(element, RegexKey::LAST);
+
+#define RULE_IMPL2(rule, type) params.m##type##Params[type##Params::rule]
+#define RULE_IMPL(rule, type) RULE_IMPL2(rule, type)
+#define RULE(rule) RULE_IMPL(rule, RULE_TYPE)
+
+ // cc-number
+#define RULE_TYPE CCNumber
+ RULE(idOrNameMatchNumberRegExp) =
+ IdOrNameMatchRegExp(element, RegexKey::CC_NUMBER);
+ RULE(labelsMatchNumberRegExp) =
+ LabelMatchesRegExp(element, labelStrings, RegexKey::CC_NUMBER);
+ RULE(closestLabelMatchesNumberRegExp) =
+ ClosestLabelMatchesRegExp(element, RegexKey::CC_NUMBER);
+ RULE(placeholderMatchesNumberRegExp) =
+ PlaceholderMatchesRegExp(element, RegexKey::CC_NUMBER);
+ RULE(ariaLabelMatchesNumberRegExp) =
+ AriaLabelMatchesRegExp(element, RegexKey::CC_NUMBER);
+ RULE(idOrNameMatchGift) = IdOrNameMatchRegExp(element, RegexKey::GIFT);
+ RULE(labelsMatchGift) =
+ LabelMatchesRegExp(element, labelStrings, RegexKey::GIFT);
+ RULE(placeholderMatchesGift) =
+ PlaceholderMatchesRegExp(element, RegexKey::GIFT);
+ RULE(ariaLabelMatchesGift) =
+ AriaLabelMatchesRegExp(element, RegexKey::GIFT);
+ RULE(idOrNameMatchSubscription) = idOrNameMatchSubscription;
+ RULE(idOrNameMatchDwfrmAndBml) = idOrNameMatchDwfrmAndBml;
+ RULE(hasTemplatedValue) = hasTemplatedValue;
+ RULE(inputTypeNotNumbery) = inputTypeNotNumbery;
+#undef RULE_TYPE
+
+ // cc-name
+#define RULE_TYPE CCName
+ RULE(idOrNameMatchNameRegExp) =
+ IdOrNameMatchRegExp(element, RegexKey::CC_NAME);
+ RULE(labelsMatchNameRegExp) =
+ LabelMatchesRegExp(element, labelStrings, RegexKey::CC_NAME);
+ RULE(closestLabelMatchesNameRegExp) =
+ ClosestLabelMatchesRegExp(element, RegexKey::CC_NAME);
+ RULE(placeholderMatchesNameRegExp) =
+ PlaceholderMatchesRegExp(element, RegexKey::CC_NAME);
+ RULE(ariaLabelMatchesNameRegExp) =
+ AriaLabelMatchesRegExp(element, RegexKey::CC_NAME);
+ RULE(idOrNameMatchFirst) = IdOrNameMatchRegExp(element, RegexKey::FIRST);
+ RULE(labelsMatchFirst) =
+ LabelMatchesRegExp(element, labelStrings, RegexKey::FIRST);
+ RULE(placeholderMatchesFirst) =
+ PlaceholderMatchesRegExp(element, RegexKey::FIRST);
+ RULE(ariaLabelMatchesFirst) =
+ AriaLabelMatchesRegExp(element, RegexKey::FIRST);
+ RULE(idOrNameMatchLast) = IdOrNameMatchRegExp(element, RegexKey::LAST);
+ RULE(labelsMatchLast) =
+ LabelMatchesRegExp(element, labelStrings, RegexKey::LAST);
+ RULE(placeholderMatchesLast) =
+ PlaceholderMatchesRegExp(element, RegexKey::LAST);
+ RULE(ariaLabelMatchesLast) =
+ AriaLabelMatchesRegExp(element, RegexKey::LAST);
+ RULE(idOrNameMatchSubscription) = idOrNameMatchSubscription;
+ RULE(idOrNameMatchFirstAndLast) = idOrNameMatchFirstAndLast;
+ RULE(idOrNameMatchDwfrmAndBml) = idOrNameMatchDwfrmAndBml;
+ RULE(hasTemplatedValue) = hasTemplatedValue;
+#undef RULE_TYPE
+
+ // We only use Fathom to detect cc-number & cc-name fields for now.
+ // Comment out code below instead of removing them to make it clear that
+ // the current design is to support multiple rules.
+/*
+ Element* nextFillableField = NextField(aElements, i);
+ Element* prevFillableField = PrevField(aElements, i);
+
+ const nsTArray<nsCString>* nextLabelStrings = GetLabelStrings(
+ nextFillableField, elementsToLabelStrings, elementsIdToLabelStrings);
+ const nsTArray<nsCString>* prevLabelStrings = GetLabelStrings(
+ prevFillableField, elementsToLabelStrings, elementsIdToLabelStrings);
+ bool roleIsMenu = RoleIsMenu(element);
+
+ // cc-type
+#define RULE_TYPE CCType
+ RULE(idOrNameMatchTypeRegExp) =
+ IdOrNameMatchRegExp(element, RegexKey::CC_TYPE);
+ RULE(labelsMatchTypeRegExp) =
+ LabelMatchesRegExp(element, labelStrings, RegexKey::CC_TYPE);
+ RULE(closestLabelMatchesTypeRegExp) =
+ ClosestLabelMatchesRegExp(element, RegexKey::CC_TYPE);
+ RULE(idOrNameMatchVisaCheckout) =
+ IdOrNameMatchRegExp(element, RegexKey::VISA_CHECKOUT);
+ RULE(ariaLabelMatchesVisaCheckout) =
+ AriaLabelMatchesRegExp(element, RegexKey::VISA_CHECKOUT);
+ RULE(isSelectWithCreditCardOptions) =
+ IsSelectWithCreditCardOptions(element);
+ RULE(isRadioWithCreditCardText) =
+ IsRadioWithCreditCardText(element, labelStrings, aRv);
+ if (aRv.Failed()) {
+ return;
+ }
+
+ RULE(idOrNameMatchSubscription) = idOrNameMatchSubscription;
+ RULE(idOrNameMatchDwfrmAndBml) = idOrNameMatchDwfrmAndBml;
+ RULE(hasTemplatedValue) = hasTemplatedValue;
+#undef RULE_TYPE
+
+ // cc-exp
+#define RULE_TYPE CCExp
+ RULE(labelsMatchExpRegExp) =
+ LabelMatchesRegExp(element, labelStrings, RegexKey::CC_EXP);
+ RULE(closestLabelMatchesExpRegExp) =
+ ClosestLabelMatchesRegExp(element, RegexKey::CC_EXP);
+ RULE(placeholderMatchesExpRegExp) =
+ PlaceholderMatchesRegExp(element, RegexKey::CC_EXP);
+ RULE(labelsMatchExpWith2Or4DigitYear) = LabelMatchesRegExp(
+ element, labelStrings, RegexKey::TWO_OR_FOUR_DIGIT_YEAR);
+ RULE(placeholderMatchesExpWith2Or4DigitYear) =
+ PlaceholderMatchesRegExp(element, RegexKey::TWO_OR_FOUR_DIGIT_YEAR);
+ RULE(labelsMatchMMYY) =
+ LabelMatchesRegExp(element, labelStrings, RegexKey::MMYY);
+ RULE(placeholderMatchesMMYY) =
+ PlaceholderMatchesRegExp(element, RegexKey::MMYY);
+ RULE(maxLengthIs7) = MaxLengthIs(element, 7);
+ RULE(idOrNameMatchSubscription) = idOrNameMatchSubscription;
+ RULE(idOrNameMatchDwfrmAndBml) = idOrNameMatchDwfrmAndBml;
+ RULE(hasTemplatedValue) = hasTemplatedValue;
+ RULE(isExpirationMonthLikely) = IsExpirationMonthLikely(element);
+ RULE(isExpirationYearLikely) = IsExpirationYearLikely(element);
+ RULE(idOrNameMatchMonth) = IdOrNameMatchRegExp(element, RegexKey::MONTH);
+ RULE(idOrNameMatchYear) = IdOrNameMatchRegExp(element, RegexKey::YEAR);
+ RULE(idOrNameMatchExpMonthRegExp) =
+ IdOrNameMatchRegExp(element, RegexKey::CC_EXP_MONTH);
+ RULE(idOrNameMatchExpYearRegExp) =
+ IdOrNameMatchRegExp(element, RegexKey::CC_EXP_YEAR);
+ RULE(idOrNameMatchValidation) =
+ IdOrNameMatchRegExp(element, RegexKey::VALIDATION);
+#undef RULE_TYPE
+
+ // cc-exp-month
+#define RULE_TYPE CCExpMonth
+ RULE(idOrNameMatchExpMonthRegExp) =
+ IdOrNameMatchRegExp(element, RegexKey::CC_EXP_MONTH);
+ RULE(labelsMatchExpMonthRegExp) =
+ LabelMatchesRegExp(element, labelStrings, RegexKey::CC_EXP_MONTH);
+ RULE(closestLabelMatchesExpMonthRegExp) =
+ ClosestLabelMatchesRegExp(element, RegexKey::CC_EXP_MONTH);
+ RULE(placeholderMatchesExpMonthRegExp) =
+ PlaceholderMatchesRegExp(element, RegexKey::CC_EXP_MONTH);
+ RULE(ariaLabelMatchesExpMonthRegExp) =
+ AriaLabelMatchesRegExp(element, RegexKey::CC_EXP_MONTH);
+ RULE(idOrNameMatchMonth) = IdOrNameMatchRegExp(element, RegexKey::MONTH);
+ RULE(labelsMatchMonth) =
+ LabelMatchesRegExp(element, labelStrings, RegexKey::MONTH);
+ RULE(placeholderMatchesMonth) =
+ PlaceholderMatchesRegExp(element, RegexKey::MONTH);
+ RULE(ariaLabelMatchesMonth) =
+ AriaLabelMatchesRegExp(element, RegexKey::MONTH);
+ RULE(nextFieldIdOrNameMatchExpYearRegExp) =
+ nextFillableField &&
+ IdOrNameMatchRegExp(*nextFillableField, RegexKey::CC_EXP_YEAR);
+ RULE(nextFieldLabelsMatchExpYearRegExp) =
+ nextFillableField &&
+ LabelMatchesRegExp(element, nextLabelStrings, RegexKey::CC_EXP_YEAR);
+ RULE(nextFieldPlaceholderMatchExpYearRegExp) =
+ nextFillableField &&
+ PlaceholderMatchesRegExp(*nextFillableField, RegexKey::CC_EXP_YEAR);
+ RULE(nextFieldAriaLabelMatchExpYearRegExp) =
+ nextFillableField &&
+ AriaLabelMatchesRegExp(*nextFillableField, RegexKey::CC_EXP_YEAR);
+ RULE(nextFieldIdOrNameMatchYear) =
+ nextFillableField &&
+ IdOrNameMatchRegExp(*nextFillableField, RegexKey::YEAR);
+ RULE(nextFieldLabelsMatchYear) =
+ nextFillableField &&
+ LabelMatchesRegExp(element, nextLabelStrings, RegexKey::YEAR);
+ RULE(nextFieldPlaceholderMatchesYear) =
+ nextFillableField &&
+ PlaceholderMatchesRegExp(*nextFillableField, RegexKey::YEAR);
+ RULE(nextFieldAriaLabelMatchesYear) =
+ nextFillableField &&
+ AriaLabelMatchesRegExp(*nextFillableField, RegexKey::YEAR);
+ RULE(nextFieldMatchesExpYearAutocomplete) =
+ nextFillableField &&
+ NextFieldMatchesExpYearAutocomplete(nextFillableField);
+ RULE(isExpirationMonthLikely) = IsExpirationMonthLikely(element);
+ RULE(nextFieldIsExpirationYearLikely) =
+ nextFillableField && IsExpirationYearLikely(*nextFillableField);
+ RULE(maxLengthIs2) = MaxLengthIs(element, 2);
+ RULE(placeholderMatchesMM) =
+ PlaceholderMatchesRegExp(element, RegexKey::MM_MONTH);
+ RULE(roleIsMenu) = roleIsMenu;
+ RULE(idOrNameMatchSubscription) = idOrNameMatchSubscription;
+ RULE(idOrNameMatchDwfrmAndBml) = idOrNameMatchDwfrmAndBml;
+ RULE(hasTemplatedValue) = hasTemplatedValue;
+#undef RULE_TYPE
+
+ // cc-exp-year
+#define RULE_TYPE CCExpYear
+ RULE(idOrNameMatchExpYearRegExp) =
+ IdOrNameMatchRegExp(element, RegexKey::CC_EXP_YEAR);
+ RULE(labelsMatchExpYearRegExp) =
+ LabelMatchesRegExp(element, labelStrings, RegexKey::CC_EXP_YEAR);
+ RULE(closestLabelMatchesExpYearRegExp) =
+ ClosestLabelMatchesRegExp(element, RegexKey::CC_EXP_YEAR);
+ RULE(placeholderMatchesExpYearRegExp) =
+ PlaceholderMatchesRegExp(element, RegexKey::CC_EXP_YEAR);
+ RULE(ariaLabelMatchesExpYearRegExp) =
+ AriaLabelMatchesRegExp(element, RegexKey::CC_EXP_YEAR);
+ RULE(idOrNameMatchYear) = IdOrNameMatchRegExp(element, RegexKey::YEAR);
+ RULE(labelsMatchYear) =
+ LabelMatchesRegExp(element, labelStrings, RegexKey::YEAR);
+ RULE(placeholderMatchesYear) =
+ PlaceholderMatchesRegExp(element, RegexKey::YEAR);
+ RULE(ariaLabelMatchesYear) =
+ AriaLabelMatchesRegExp(element, RegexKey::YEAR);
+ RULE(previousFieldIdOrNameMatchExpMonthRegExp) =
+ prevFillableField &&
+ IdOrNameMatchRegExp(*prevFillableField, RegexKey::CC_EXP_MONTH);
+ RULE(previousFieldLabelsMatchExpMonthRegExp) =
+ prevFillableField &&
+ LabelMatchesRegExp(element, prevLabelStrings, RegexKey::CC_EXP_MONTH);
+ RULE(previousFieldPlaceholderMatchExpMonthRegExp) =
+ prevFillableField &&
+ PlaceholderMatchesRegExp(*prevFillableField, RegexKey::CC_EXP_MONTH);
+ RULE(previousFieldAriaLabelMatchExpMonthRegExp) =
+ prevFillableField &&
+ AriaLabelMatchesRegExp(*prevFillableField, RegexKey::CC_EXP_MONTH);
+ RULE(previousFieldIdOrNameMatchMonth) =
+ prevFillableField &&
+ IdOrNameMatchRegExp(*prevFillableField, RegexKey::MONTH);
+ RULE(previousFieldLabelsMatchMonth) =
+ prevFillableField &&
+ LabelMatchesRegExp(element, prevLabelStrings, RegexKey::MONTH);
+ RULE(previousFieldPlaceholderMatchesMonth) =
+ prevFillableField &&
+ PlaceholderMatchesRegExp(*prevFillableField, RegexKey::MONTH);
+ RULE(previousFieldAriaLabelMatchesMonth) =
+ prevFillableField &&
+ AriaLabelMatchesRegExp(*prevFillableField, RegexKey::MONTH);
+ RULE(previousFieldMatchesExpMonthAutocomplete) =
+ prevFillableField &&
+ PreviousFieldMatchesExpMonthAutocomplete(prevFillableField);
+ RULE(isExpirationYearLikely) = IsExpirationYearLikely(element);
+ RULE(previousFieldIsExpirationMonthLikely) =
+ prevFillableField && IsExpirationMonthLikely(*prevFillableField);
+ RULE(placeholderMatchesYYOrYYYY) =
+ PlaceholderMatchesRegExp(element, RegexKey::YY_OR_YYYY);
+ RULE(roleIsMenu) = roleIsMenu;
+ RULE(idOrNameMatchSubscription) = idOrNameMatchSubscription;
+ RULE(idOrNameMatchDwfrmAndBml) = idOrNameMatchDwfrmAndBml;
+ RULE(hasTemplatedValue) = hasTemplatedValue;
+#undef RULE_TYPE
+*/
+
+#undef RULE_IMPL2
+#undef RULE_IMPL
+#undef RULE
+
+#define CALCULATE_SCORE(type, score) \
+ for (auto i : MakeEnumeratedRange(type##Params::Count)) { \
+ (score) += params.m##type##Params[i] * kCoefficents.m##type##Params[i]; \
+ } \
+ (score) = Sigmoid(score + k##type##Bias);
+
+ // Calculating the final score of each rule
+ FormAutofillConfidences score;
+ CALCULATE_SCORE(CCNumber, score.mCcNumber)
+ CALCULATE_SCORE(CCName, score.mCcName)
+
+ // Comment out code that are not used right now
+ // CALCULATE_SCORE(CCType, score.mCcType)
+ // CALCULATE_SCORE(CCExp, score.mCcExp)
+ // CALCULATE_SCORE(CCExpMonth, score.mCcExpMonth)
+ // CALCULATE_SCORE(CCExpYear, score.mCcExpYear)
+
+#undef CALCULATE_SCORE
+
+ aResults.AppendElement(score);
+ }
+}
+
+static StaticAutoPtr<FormAutofillImpl> sFormAutofillInstance;
+
+static FormAutofillImpl* GetFormAutofillImpl() {
+ if (!sFormAutofillInstance) {
+ sFormAutofillInstance = new FormAutofillImpl();
+ ClearOnShutdown(&sFormAutofillInstance);
+ }
+ return sFormAutofillInstance;
+}
+
+/* static */
+void FormAutofillNative::GetFormAutofillConfidences(
+ GlobalObject& aGlobal, const Sequence<OwningNonNull<Element>>& aElements,
+ nsTArray<FormAutofillConfidences>& aResults, ErrorResult& aRv) {
+ GetFormAutofillImpl()->GetFormAutofillConfidences(aGlobal, aElements,
+ aResults, aRv);
+}
+
+} // namespace mozilla::dom