/* -*- 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 #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 mCCNumberParams; EnumeratedArray mCCNameParams; EnumeratedArray mCCTypeParams; EnumeratedArray mCCExpParams; EnumeratedArray mCCExpMonthParams; EnumeratedArray 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>& aElements, nsTArray& 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* 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* labels, ErrorResult& aRv); bool MatchesExpYearAutocomplete(Element& element); bool RoleIsMenu(Element& element); Element* FindRootForField(Element* aElement); Element* FindField(const Sequence>& aElements, uint32_t aStartIndex, int8_t aDirection); Element* NextField(const Sequence>& aElements, uint32_t aStartIndex); Element* PrevField(const Sequence>& aElements, uint32_t aStartIndex); // Array contains regular expressions to match the corresponding // field. Ex, CC number, CC type, etc. using RegexStringArray = EnumeratedArray; RegexStringArray mRuleMap; // Array that holds RegexWrapper that created by regex::ffi::regex_new using RegexWrapperArray = EnumeratedArray; 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* 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 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 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* 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 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>& 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
. if (root->IsHTMLElement(nsGkAtoms::form)) { continue; } } if (element->IsAnyOfHTMLElements(nsGkAtoms::input, nsGkAtoms::select)) { return element.get(); } } return nullptr; } Element* FormAutofillImpl::NextField( const Sequence>& aElements, uint32_t aStartIndex) { return FindField(aElements, aStartIndex, 1); } Element* FormAutofillImpl::PrevField( const Sequence>& aElements, uint32_t aStartIndex) { return FindField(aElements, aStartIndex, -1); } static void ExtractLabelStrings(nsINode* aNode, nsTArray& 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* GetLabelStrings( Element* aElement, const nsTHashMap>& aElementMap, const nsTHashMap>& 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>& aElements, nsTArray& 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 labels = document->GetElementsByTagName(u"label"_ns); nsTHashMap> elementsToLabelStrings; nsTHashMap> 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 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 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* 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* nextLabelStrings = GetLabelStrings( nextFillableField, elementsToLabelStrings, elementsIdToLabelStrings); const nsTArray* 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 sFormAutofillInstance; static FormAutofillImpl* GetFormAutofillImpl() { if (!sFormAutofillInstance) { sFormAutofillInstance = new FormAutofillImpl(); ClearOnShutdown(&sFormAutofillInstance); } return sFormAutofillInstance; } /* static */ void FormAutofillNative::GetFormAutofillConfidences( GlobalObject& aGlobal, const Sequence>& aElements, nsTArray& aResults, ErrorResult& aRv) { GetFormAutofillImpl()->GetFormAutofillConfidences(aGlobal, aElements, aResults, aRv); } } // namespace mozilla::dom