diff options
Diffstat (limited to 'toolkit/components/formautofill/FormAutofillNative.cpp')
-rw-r--r-- | toolkit/components/formautofill/FormAutofillNative.cpp | 1483 |
1 files changed, 1483 insertions, 0 deletions
diff --git a/toolkit/components/formautofill/FormAutofillNative.cpp b/toolkit/components/formautofill/FormAutofillNative.cpp new file mode 100644 index 0000000000..f86dc695b7 --- /dev/null +++ b/toolkit/components/formautofill/FormAutofillNative.cpp @@ -0,0 +1,1483 @@ +/* -*- 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)" + // it-IT + "|titolare.*carta" + // pl-PL + "|posiadacz.*karty" + // 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)" + // it-IT + "|numero.*carta" + // fr-FR + "|(numero|número|numéro).*(carte)" + // pl-PL + "|numer.*karty" + // 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 |