@protontech/autofill 0.0.22991789 → 0.0.33835493
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/constants/features.d.ts +1 -0
- package/constants/features.js +1 -0
- package/constants/heuristics.d.ts +5 -4
- package/constants/heuristics.js +5 -10
- package/constants/selectors.d.ts +13 -7
- package/constants/selectors.js +29 -17
- package/debug.d.ts +24 -1
- package/debug.js +15 -13
- package/dictionary/generate.js +30 -21
- package/dictionary/generated/dictionary.d.ts +18 -4
- package/dictionary/generated/dictionary.js +31 -17
- package/dictionary/source/dictionary.d.ts +4 -3
- package/dictionary/source/dictionary.js +155 -89
- package/dictionary/source/patterns.js +2 -2
- package/features/feature.d.ts +18 -0
- package/features/feature.js +79 -0
- package/features/feature.spec.d.ts +1 -0
- package/features/feature.spec.js +55 -0
- package/features/v1/abstract.field.d.ts +20401 -0
- package/features/v1/abstract.field.js +95 -0
- package/features/v1/abstract.form.d.ts +1226 -0
- package/features/v1/abstract.form.js +336 -0
- package/features/v1/field.email.d.ts +62 -0
- package/features/v1/field.email.js +27 -0
- package/features/v1/field.otp.d.ts +142 -0
- package/features/v1/field.otp.js +105 -0
- package/features/v1/field.password.d.ts +162 -0
- package/features/v1/field.password.js +81 -0
- package/features/v1/field.username-hidden.d.ts +62 -0
- package/features/v1/field.username-hidden.js +25 -0
- package/features/v1/field.username.d.ts +72 -0
- package/features/v1/field.username.js +31 -0
- package/features/v1/index.d.ts +4428 -0
- package/features/v1/index.js +22 -0
- package/features/v1/index.spec.d.ts +1 -0
- package/features/v1/index.spec.js +30 -0
- package/index.d.ts +7 -2
- package/index.js +11 -3
- package/models/perceptron/index.d.ts +2 -0
- package/models/perceptron/index.js +42 -0
- package/models/perceptron/params/email-model.json +53 -0
- package/models/perceptron/params/login-model.json +465 -0
- package/models/perceptron/params/new-password-model.json +133 -0
- package/models/perceptron/params/otp-model.json +117 -0
- package/models/perceptron/params/password-change-model.json +465 -0
- package/models/perceptron/params/password-model.json +133 -0
- package/models/perceptron/params/recovery-model.json +465 -0
- package/models/perceptron/params/register-model.json +465 -0
- package/models/perceptron/params/username-hidden-model.json +53 -0
- package/models/perceptron/params/username-model.json +61 -0
- package/models/random_forest/index.d.ts +2 -0
- package/models/random_forest/index.js +42 -0
- package/models/random_forest/params/email-model.json +1456 -0
- package/models/random_forest/params/login-model.json +4194 -0
- package/models/random_forest/params/new-password-model.json +1448 -0
- package/models/random_forest/params/otp-model.json +2004 -0
- package/models/random_forest/params/password-change-model.json +1422 -0
- package/models/random_forest/params/password-model.json +1292 -0
- package/models/random_forest/params/recovery-model.json +2754 -0
- package/models/random_forest/params/register-model.json +3678 -0
- package/models/random_forest/params/username-hidden-model.json +1108 -0
- package/models/random_forest/params/username-model.json +1052 -0
- package/package.json +17 -15
- package/rules/v1/index.d.ts +4 -0
- package/rules/v1/index.js +66 -0
- package/rulesets.d.ts +9 -2
- package/rulesets.js +14 -9
- package/types/index.d.ts +70 -17
- package/types/index.js +34 -1
- package/utils/attributes.js +1 -1
- package/utils/clustering.js +18 -5
- package/utils/credit-card.d.ts +32 -0
- package/utils/credit-card.js +259 -0
- package/utils/credit-card.samples.spec.d.ts +1 -0
- package/utils/credit-card.samples.spec.js +452 -0
- package/utils/credit-card.spec.d.ts +1 -0
- package/utils/credit-card.spec.js +296 -0
- package/utils/dom.d.ts +3 -2
- package/utils/dom.js +12 -7
- package/utils/exclusion.d.ts +1 -0
- package/utils/exclusion.js +22 -10
- package/utils/extract.d.ts +1 -0
- package/utils/extract.js +26 -7
- package/utils/fathom.d.ts +10 -23
- package/utils/fathom.js +7 -12
- package/utils/field.d.ts +12 -4
- package/utils/field.js +25 -14
- package/utils/flags.d.ts +9 -3
- package/utils/flags.js +27 -9
- package/utils/form.d.ts +16 -5
- package/utils/form.js +35 -14
- package/utils/identity.d.ts +12 -21
- package/utils/identity.js +66 -41
- package/utils/identity.samples.spec.d.ts +1 -0
- package/utils/identity.samples.spec.js +28 -0
- package/utils/iframe.d.ts +2 -0
- package/utils/iframe.js +22 -0
- package/utils/overrides.d.ts +19 -0
- package/utils/overrides.js +40 -0
- package/utils/prepass.js +6 -4
- package/utils/re.d.ts +19 -4
- package/utils/re.js +22 -7
- package/utils/re.spec.d.ts +1 -0
- package/utils/re.spec.js +62 -0
- package/utils/shadow-dom.d.ts +8 -0
- package/utils/shadow-dom.js +53 -0
- package/utils/shadow-dom.spec.d.ts +1 -0
- package/utils/shadow-dom.spec.js +215 -0
- package/utils/visible.d.ts +3 -2
- package/utils/visible.js +42 -22
- package/cli.d.ts +0 -2
- package/cli.js +0 -128
- package/features/abstract.field.d.ts +0 -123
- package/features/abstract.field.js +0 -63
- package/features/abstract.form.d.ts +0 -98
- package/features/abstract.form.js +0 -281
- package/features/field.email.d.ts +0 -18
- package/features/field.email.js +0 -43
- package/features/field.otp.d.ts +0 -36
- package/features/field.otp.js +0 -116
- package/features/field.password.d.ts +0 -35
- package/features/field.password.js +0 -104
- package/features/field.username-hidden.d.ts +0 -15
- package/features/field.username-hidden.js +0 -40
- package/features/field.username.d.ts +0 -16
- package/features/field.username.js +0 -41
- package/features/form.combined.d.ts +0 -1
- package/features/form.combined.js +0 -6
- package/trainees/field.email.d.ts +0 -2
- package/trainees/field.email.js +0 -16
- package/trainees/field.identity.d.ts +0 -2
- package/trainees/field.identity.js +0 -9
- package/trainees/field.otp.d.ts +0 -2
- package/trainees/field.otp.js +0 -16
- package/trainees/field.password.current.d.ts +0 -2
- package/trainees/field.password.current.js +0 -16
- package/trainees/field.password.new.d.ts +0 -2
- package/trainees/field.password.new.js +0 -16
- package/trainees/field.username-hidden.d.ts +0 -2
- package/trainees/field.username-hidden.js +0 -22
- package/trainees/field.username.d.ts +0 -2
- package/trainees/field.username.js +0 -16
- package/trainees/form.login.d.ts +0 -2
- package/trainees/form.login.js +0 -16
- package/trainees/form.noop.d.ts +0 -1
- package/trainees/form.noop.js +0 -7
- package/trainees/form.password-change.d.ts +0 -2
- package/trainees/form.password-change.js +0 -16
- package/trainees/form.recovery.d.ts +0 -2
- package/trainees/form.recovery.js +0 -16
- package/trainees/form.register.d.ts +0 -2
- package/trainees/form.register.js +0 -16
- package/trainees/index.d.ts +0 -9
- package/trainees/index.js +0 -72
- package/trainees/results/result.email.d.ts +0 -2
- package/trainees/results/result.email.js +0 -17
- package/trainees/results/result.login.d.ts +0 -2
- package/trainees/results/result.login.js +0 -110
- package/trainees/results/result.new-password.d.ts +0 -2
- package/trainees/results/result.new-password.js +0 -36
- package/trainees/results/result.otp.d.ts +0 -2
- package/trainees/results/result.otp.js +0 -43
- package/trainees/results/result.password-change.d.ts +0 -2
- package/trainees/results/result.password-change.js +0 -110
- package/trainees/results/result.password.d.ts +0 -2
- package/trainees/results/result.password.js +0 -36
- package/trainees/results/result.recovery.d.ts +0 -2
- package/trainees/results/result.recovery.js +0 -110
- package/trainees/results/result.register.d.ts +0 -2
- package/trainees/results/result.register.js +0 -110
- package/trainees/results/result.username-hidden.d.ts +0 -2
- package/trainees/results/result.username-hidden.js +0 -15
- package/trainees/results/result.username.d.ts +0 -2
- package/trainees/results/result.username.js +0 -16
- package/utils/memoize.d.ts +0 -5
- package/utils/memoize.js +0 -12
package/utils/re.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { CAPTCHA_ATTR_RE, CONFIRM_ACTION_ATTR_END_RE, CONFIRM_ACTION_RE, CREATE_ACTION_ATTR_END_RE, CREATE_ACTION_RE, CURRENT_VALUE_RE, EMAIL_ATTR_RE, EMAIL_RE, HIDDEN_ATTR_RE, IDENTITY_ADDRESS_ATTR_RE, IDENTITY_ADDRESS_LINES_ATTR_END_RE, IDENTITY_CITY_ATTR_RE, IDENTITY_COUNTRY_ATTR_RE, IDENTITY_COUNTRY_CODE_ATTR_RE, IDENTITY_FIRSTNAME_ATTR_RE, IDENTITY_FULLNAME_ATTR_RE, IDENTITY_LASTNAME_ATTR_RE, IDENTITY_MIDDLENAME_ATTR_RE, IDENTITY_ORGANIZATION_ATTR_RE, IDENTITY_STATE_ATTR_RE, IDENTITY_TELEPHONE_ATTR_RE,
|
|
1
|
+
import { AUTHENTICATOR_ATTR_RE, CAPTCHA_ATTR_RE, CC_CODE_ATTR_RE, CC_CVC_ATTR_RE, CC_EXP_ATTR_RE, CC_EXP_MONTH_ATTR_END_RE, CC_EXP_MONTH_ATTR_RE, CC_EXP_YEAR_ATTR_END_RE, CC_EXP_YEAR_ATTR_RE, CC_NAME_ATTR_RE, CC_NUMBER_ATTR_RE, CC_OUTLIER_ATTR_RE, CC_PREFIX_ATTR_RE, CONFIRM_ACTION_ATTR_END_RE, CONFIRM_ACTION_RE, CREATE_ACTION_ATTR_END_RE, CREATE_ACTION_RE, CURRENT_VALUE_RE, DOCUMENT_ATTR_RE, EMAIL_ATTR_RE, EMAIL_RE, HIDDEN_ATTR_RE, IDENTITY_ADDRESS_ATTR_RE, IDENTITY_ADDRESS_LINES_ATTR_END_RE, IDENTITY_CITY_ATTR_RE, IDENTITY_COUNTRY_ATTR_RE, IDENTITY_COUNTRY_CODE_ATTR_RE, IDENTITY_FIRSTNAME_ATTR_RE, IDENTITY_FULLNAME_ATTR_RE, IDENTITY_LASTNAME_ATTR_RE, IDENTITY_MIDDLENAME_ATTR_RE, IDENTITY_ORGANIZATION_ATTR_RE, IDENTITY_STATE_ATTR_RE, IDENTITY_TELEPHONE_ATTR_RE, IDENTITY_TELEPHONE_OUTLIER_ATTR_RE, IDENTITY_ZIPCODE_ATTR_RE, IFRAME_FIELD_ATTR_RE, LOGIN_RE, MULTI_STEP_RE, NEWSLETTER_ATTR_RE, NEWSLETTER_RE, OAUTH_ATTR_RE, OTP_ATTR_RE, OTP_OUTLIER_ATTR_RE, OTP_OUTLIER_RE, PASSWORD_OUTLIER_RE, PASSWORD_RE, PAYMENT_ATTR_RE, RECOVERY_RE, REGISTER_RE, REMEMBER_ACTION_RE, RESET_ACTION_RE, SEARCH_ACTION_RE, STEP_ACTION_RE, TEL_RE, TOS_RE, TROUBLE_RE, TWO_FA_ATTR_RE, TWO_FA_RE, USERNAME_ATTR_RE, USERNAME_OUTLIER_RE, USERNAME_RE, } from "../dictionary/generated/dictionary";
|
|
2
2
|
import { EMAIL_VALUE_RE, TEL_VALUE_RE, USERNAME_VALUE_RE } from "../dictionary/source/patterns";
|
|
3
|
-
import { and, or } from "./combinators";
|
|
3
|
+
import { and, not, or } from "./combinators";
|
|
4
4
|
export const reSanityCheck = (cb, options) => (str) => {
|
|
5
5
|
if (options.maxLength && str.length > options.maxLength)
|
|
6
6
|
return false;
|
|
@@ -44,21 +44,36 @@ export const matchPasswordCurrent = and(andRe([PASSWORD_RE, CURRENT_VALUE_RE]),
|
|
|
44
44
|
export const matchPasswordCurrentAttr = and(matchPasswordCurrent, notRe(CONFIRM_ACTION_ATTR_END_RE));
|
|
45
45
|
export const matchPasswordOutlier = test(PASSWORD_OUTLIER_RE);
|
|
46
46
|
export const matchHidden = test(HIDDEN_ATTR_RE);
|
|
47
|
-
export const
|
|
48
|
-
export const matchMfa = test(MFA_RE);
|
|
49
|
-
export const matchMfaAttr = test(MFA_ATTR_RE);
|
|
47
|
+
export const matchTwoFa = orRe([TWO_FA_RE, TWO_FA_ATTR_RE]);
|
|
50
48
|
export const matchOtpAttr = test(OTP_ATTR_RE);
|
|
51
|
-
export const matchOtpOutlier =
|
|
49
|
+
export const matchOtpOutlier = test(OTP_OUTLIER_ATTR_RE);
|
|
50
|
+
export const matchOtpFieldOutlier = orRe([OTP_OUTLIER_ATTR_RE, USERNAME_RE]);
|
|
51
|
+
export const matchOtpOutlierAction = test(OTP_OUTLIER_RE);
|
|
52
|
+
export const matchAuthenticator = test(AUTHENTICATOR_ATTR_RE);
|
|
53
|
+
export const matchMFAAttr = test(TWO_FA_ATTR_RE);
|
|
54
|
+
export const matchMFA = orRe([TWO_FA_RE, TWO_FA_ATTR_RE, OTP_ATTR_RE]);
|
|
52
55
|
export const matchNewsletter = test(NEWSLETTER_RE);
|
|
53
56
|
export const matchNewsletterAttr = orRe([NEWSLETTER_RE, NEWSLETTER_ATTR_RE]);
|
|
54
57
|
export const matchFullName = and(test(IDENTITY_FULLNAME_ATTR_RE), (str) => !str.includes("[name]"));
|
|
55
58
|
export const matchFirstName = test(IDENTITY_FIRSTNAME_ATTR_RE);
|
|
56
59
|
export const matchMiddleName = test(IDENTITY_MIDDLENAME_ATTR_RE);
|
|
57
60
|
export const matchLastName = test(IDENTITY_LASTNAME_ATTR_RE);
|
|
58
|
-
export const matchTelephone = and(test(IDENTITY_TELEPHONE_ATTR_RE), notRe(
|
|
61
|
+
export const matchTelephone = and(test(IDENTITY_TELEPHONE_ATTR_RE), notRe(IDENTITY_TELEPHONE_OUTLIER_ATTR_RE));
|
|
59
62
|
export const matchOrganization = test(IDENTITY_ORGANIZATION_ATTR_RE);
|
|
60
63
|
export const matchCity = test(IDENTITY_CITY_ATTR_RE);
|
|
61
64
|
export const matchZipCode = test(IDENTITY_ZIPCODE_ATTR_RE);
|
|
62
65
|
export const matchState = test(IDENTITY_STATE_ATTR_RE);
|
|
63
66
|
export const matchCountry = and(test(IDENTITY_COUNTRY_ATTR_RE), notRe(IDENTITY_COUNTRY_CODE_ATTR_RE));
|
|
64
67
|
export const matchAddress = and(test(IDENTITY_ADDRESS_ATTR_RE), notRe(IDENTITY_ADDRESS_LINES_ATTR_END_RE));
|
|
68
|
+
export const matchCCName = test(CC_NAME_ATTR_RE);
|
|
69
|
+
export const matchCCFirstName = andRe([CC_PREFIX_ATTR_RE, IDENTITY_FIRSTNAME_ATTR_RE]);
|
|
70
|
+
export const matchCCLastName = andRe([CC_PREFIX_ATTR_RE, IDENTITY_LASTNAME_ATTR_RE]);
|
|
71
|
+
export const matchCCNumber = test(CC_NUMBER_ATTR_RE);
|
|
72
|
+
export const matchCCSecurityCode = andRe([CC_CODE_ATTR_RE, CC_PREFIX_ATTR_RE]);
|
|
73
|
+
export const matchCCV = test(CC_CVC_ATTR_RE);
|
|
74
|
+
export const matchCCOutlier = orRe([CC_OUTLIER_ATTR_RE, DOCUMENT_ATTR_RE]);
|
|
75
|
+
export const matchCCExp = and(test(CC_EXP_ATTR_RE), notRe(DOCUMENT_ATTR_RE));
|
|
76
|
+
export const matchCCExpMonth = and(test(CC_EXP_MONTH_ATTR_RE), not(test(CC_EXP_YEAR_ATTR_END_RE)));
|
|
77
|
+
export const matchCCExpYear = and(test(CC_EXP_YEAR_ATTR_RE), not(test(CC_EXP_MONTH_ATTR_END_RE)));
|
|
78
|
+
export const matchPayment = test(PAYMENT_ATTR_RE);
|
|
79
|
+
export const matchIFrameField = orRe([IFRAME_FIELD_ATTR_RE, PAYMENT_ATTR_RE]);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/utils/re.spec.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import * as matchers from "./re";
|
|
2
|
+
const getRandomString = (length) => {
|
|
3
|
+
const charList = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()_+-={}[]|:;\"'<>,.?/~ `";
|
|
4
|
+
return Array.from({ length }, () => charList.charAt(Math.floor(Math.random() * charList.length))).join("");
|
|
5
|
+
};
|
|
6
|
+
const executionTime = (cb) => {
|
|
7
|
+
const start = performance.now();
|
|
8
|
+
cb();
|
|
9
|
+
return performance.now() - start;
|
|
10
|
+
};
|
|
11
|
+
const STR_TEST_LENGTHS = [25, 50, 100, 250, 500, 1000, 10000, 100000];
|
|
12
|
+
const MAX_EXPECTED_REG_EXEC_TIME = 100;
|
|
13
|
+
const MATCHERS = [
|
|
14
|
+
"matchCaptcha",
|
|
15
|
+
"matchConfirmAction",
|
|
16
|
+
"matchCreateAction",
|
|
17
|
+
"matchEmail",
|
|
18
|
+
"matchEmailAttr",
|
|
19
|
+
"matchEmailValue",
|
|
20
|
+
"matchHidden",
|
|
21
|
+
"matchLogin",
|
|
22
|
+
"matchTwoFa",
|
|
23
|
+
"matchMFA",
|
|
24
|
+
"matchMultiStep",
|
|
25
|
+
"matchNewsletter",
|
|
26
|
+
"matchNewsletterAttr",
|
|
27
|
+
"matchOAuth",
|
|
28
|
+
"matchOtpAttr",
|
|
29
|
+
"matchOtpOutlier",
|
|
30
|
+
"matchPasswordConfirm",
|
|
31
|
+
"matchPasswordCreate",
|
|
32
|
+
"matchPasswordCreateAttr",
|
|
33
|
+
"matchPasswordCurrent",
|
|
34
|
+
"matchPasswordCurrentAttr",
|
|
35
|
+
"matchPasswordOutlier",
|
|
36
|
+
"matchPasswordReset",
|
|
37
|
+
"matchPasswordResetAttr",
|
|
38
|
+
"matchRecovery",
|
|
39
|
+
"matchRegister",
|
|
40
|
+
"matchRememberMe",
|
|
41
|
+
"matchSearchAction",
|
|
42
|
+
"matchStepAction",
|
|
43
|
+
"matchTOS",
|
|
44
|
+
"matchTel",
|
|
45
|
+
"matchTelValue",
|
|
46
|
+
"matchTrouble",
|
|
47
|
+
"matchUsername",
|
|
48
|
+
"matchUsernameAttr",
|
|
49
|
+
"matchUsernameOutlier",
|
|
50
|
+
"matchUsernameValue",
|
|
51
|
+
];
|
|
52
|
+
describe("regex performance tests", () => {
|
|
53
|
+
MATCHERS.forEach((matcher) => {
|
|
54
|
+
describe(`${matcher}`, () => {
|
|
55
|
+
STR_TEST_LENGTHS.forEach((length) => {
|
|
56
|
+
test(`string of ${length} chars under 100ms`, () => {
|
|
57
|
+
expect(executionTime(() => matchers[matcher](getRandomString(length)))).toBeLessThan(MAX_EXPECTED_REG_EXEC_TIME);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const isShadowRoot: (el: Node) => el is ShadowRoot;
|
|
2
|
+
export declare const isShadowElement: (el: Node) => boolean;
|
|
3
|
+
export declare const isCustomElementWithShadowRoot: (el: Element) => el is HTMLElement & {
|
|
4
|
+
shadowRoot: ShadowRoot;
|
|
5
|
+
};
|
|
6
|
+
export declare const shadowPiercingAncestors: (element: Node) => Generator<Node, void, unknown>;
|
|
7
|
+
export declare const shadowPiercingContains: (container: HTMLElement | Document, el: HTMLElement) => boolean;
|
|
8
|
+
export declare const shallowShadowQuerySelector: (el: HTMLElement | Document, selector: string) => HTMLElement | null;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export const isShadowRoot = (el) => el instanceof ShadowRoot;
|
|
2
|
+
export const isShadowElement = (el) => isShadowRoot(el.getRootNode());
|
|
3
|
+
export const isCustomElementWithShadowRoot = (el) => Boolean(el.tagName.includes("-") && el.shadowRoot);
|
|
4
|
+
export const shadowPiercingAncestors = function* (element) {
|
|
5
|
+
yield element;
|
|
6
|
+
let current = element;
|
|
7
|
+
let parent;
|
|
8
|
+
while ((parent = getShadowPiercingParent(current)) && (parent === null || parent === void 0 ? void 0 : parent.nodeType) === Node.ELEMENT_NODE) {
|
|
9
|
+
yield parent;
|
|
10
|
+
current = parent;
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
const getShadowPiercingParent = (node) => {
|
|
14
|
+
const parent = node.parentNode;
|
|
15
|
+
if (!parent)
|
|
16
|
+
return null;
|
|
17
|
+
if (isShadowRoot(parent))
|
|
18
|
+
return parent.host;
|
|
19
|
+
else
|
|
20
|
+
return parent;
|
|
21
|
+
};
|
|
22
|
+
export const shadowPiercingContains = (container, el) => {
|
|
23
|
+
let current = el;
|
|
24
|
+
if (container.contains(el))
|
|
25
|
+
return true;
|
|
26
|
+
if (!isShadowElement(el))
|
|
27
|
+
return false;
|
|
28
|
+
const containerShadowRoot = "shadowRoot" in container ? container.shadowRoot : null;
|
|
29
|
+
if (containerShadowRoot === null || containerShadowRoot === void 0 ? void 0 : containerShadowRoot.contains(el))
|
|
30
|
+
return true;
|
|
31
|
+
while (current) {
|
|
32
|
+
const rootNode = current.getRootNode();
|
|
33
|
+
if (rootNode === document)
|
|
34
|
+
return false;
|
|
35
|
+
if (!isShadowRoot(rootNode))
|
|
36
|
+
return false;
|
|
37
|
+
const host = rootNode.host;
|
|
38
|
+
if (host === container)
|
|
39
|
+
return true;
|
|
40
|
+
if (container.contains(host))
|
|
41
|
+
return true;
|
|
42
|
+
if (containerShadowRoot === null || containerShadowRoot === void 0 ? void 0 : containerShadowRoot.contains(host))
|
|
43
|
+
return true;
|
|
44
|
+
current = host;
|
|
45
|
+
}
|
|
46
|
+
return false;
|
|
47
|
+
};
|
|
48
|
+
export const shallowShadowQuerySelector = (el, selector) => {
|
|
49
|
+
var _a, _b, _c;
|
|
50
|
+
if (!(el instanceof HTMLElement))
|
|
51
|
+
return el.querySelector(selector);
|
|
52
|
+
return (_c = (_a = el.querySelector(selector)) !== null && _a !== void 0 ? _a : (_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector(selector)) !== null && _c !== void 0 ? _c : null;
|
|
53
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { isShadowElement, shadowPiercingAncestors, shadowPiercingContains, shallowShadowQuerySelector } from "./shadow-dom";
|
|
2
|
+
class WrapperComponent extends HTMLElement {
|
|
3
|
+
constructor() {
|
|
4
|
+
super();
|
|
5
|
+
const shadow = this.attachShadow({ mode: "open" });
|
|
6
|
+
shadow.innerHTML = `
|
|
7
|
+
<div class="wrapper">
|
|
8
|
+
<slot name="header"></slot>
|
|
9
|
+
<div class="content">
|
|
10
|
+
<p class="text">Shadow content</p>
|
|
11
|
+
<slot></slot>
|
|
12
|
+
</div>
|
|
13
|
+
</div>
|
|
14
|
+
`;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
class NestedComponent extends HTMLElement {
|
|
18
|
+
constructor() {
|
|
19
|
+
super();
|
|
20
|
+
const shadow = this.attachShadow({ mode: "open" });
|
|
21
|
+
shadow.innerHTML = `
|
|
22
|
+
<div class="outer">
|
|
23
|
+
<wrapper-component>
|
|
24
|
+
<span class="nested-content">Nested content</span>
|
|
25
|
+
</wrapper-component>
|
|
26
|
+
</div>
|
|
27
|
+
`;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
class CardComponent extends HTMLElement {
|
|
31
|
+
constructor() {
|
|
32
|
+
super();
|
|
33
|
+
const shadow = this.attachShadow({ mode: "open" });
|
|
34
|
+
shadow.innerHTML = `
|
|
35
|
+
<div class="card">
|
|
36
|
+
<div class="card-header">
|
|
37
|
+
<slot name="header"></slot>
|
|
38
|
+
</div>
|
|
39
|
+
<div class="card-body">
|
|
40
|
+
<slot></slot>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
`;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
class CustomInput extends HTMLElement {
|
|
47
|
+
constructor() {
|
|
48
|
+
super();
|
|
49
|
+
const shadow = this.attachShadow({ mode: "open" });
|
|
50
|
+
shadow.innerHTML = `
|
|
51
|
+
<div class="input-wrapper">
|
|
52
|
+
<input type="text" class="actual-input">
|
|
53
|
+
<div class="validation-message"></div>
|
|
54
|
+
</div>
|
|
55
|
+
`;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
customElements.define("wrapper-component", WrapperComponent);
|
|
59
|
+
customElements.define("nested-component", NestedComponent);
|
|
60
|
+
customElements.define("custom-input", CustomInput);
|
|
61
|
+
customElements.define("card-component", CardComponent);
|
|
62
|
+
describe("Shadow DOM Utilities", () => {
|
|
63
|
+
let container;
|
|
64
|
+
beforeEach(() => {
|
|
65
|
+
document.body.innerHTML = "";
|
|
66
|
+
container = document.createElement("div");
|
|
67
|
+
container.id = "container";
|
|
68
|
+
document.body.appendChild(container);
|
|
69
|
+
});
|
|
70
|
+
describe("`isShadowElement`", () => {
|
|
71
|
+
test("should detect elements inside custom element shadow DOM", () => {
|
|
72
|
+
var _a;
|
|
73
|
+
container.innerHTML = `<wrapper-component></wrapper-component>`;
|
|
74
|
+
const component = container.querySelector("wrapper-component");
|
|
75
|
+
const shadowText = (_a = component === null || component === void 0 ? void 0 : component.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector(".text");
|
|
76
|
+
expect(isShadowElement(shadowText)).toBe(true);
|
|
77
|
+
});
|
|
78
|
+
test("should return `false` for custom element host", () => {
|
|
79
|
+
container.innerHTML = `<wrapper-component></wrapper-component>`;
|
|
80
|
+
const component = container.querySelector("wrapper-component");
|
|
81
|
+
expect(isShadowElement(component)).toBe(false);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
describe("`shadowPiercingAncestors`", () => {
|
|
85
|
+
test("should traverse from shadow content to light DOM", () => {
|
|
86
|
+
var _a;
|
|
87
|
+
container.innerHTML = `<wrapper-component></wrapper-component>`;
|
|
88
|
+
const component = container.querySelector("wrapper-component");
|
|
89
|
+
const shadowText = (_a = component === null || component === void 0 ? void 0 : component.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector(".text");
|
|
90
|
+
const ancestors = Array.from(shadowPiercingAncestors(shadowText));
|
|
91
|
+
const expected = [HTMLParagraphElement, HTMLDivElement, HTMLDivElement, WrapperComponent, HTMLDivElement, HTMLBodyElement, HTMLHtmlElement];
|
|
92
|
+
expect(ancestors[0]).toBe(shadowText);
|
|
93
|
+
expect(ancestors.length).toEqual(expected.length);
|
|
94
|
+
expected.forEach((instance, idx) => expect(ancestors[idx]).toBeInstanceOf(instance));
|
|
95
|
+
});
|
|
96
|
+
test("should handle nested custom elements", () => {
|
|
97
|
+
var _a, _b;
|
|
98
|
+
container.innerHTML = `<nested-component></nested-component>`;
|
|
99
|
+
const outerComponent = container.querySelector("nested-component");
|
|
100
|
+
const innerComponent = (_a = outerComponent === null || outerComponent === void 0 ? void 0 : outerComponent.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector("wrapper-component");
|
|
101
|
+
const nestedContent = (_b = innerComponent === null || innerComponent === void 0 ? void 0 : innerComponent.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector(".text");
|
|
102
|
+
const ancestors = Array.from(shadowPiercingAncestors(nestedContent));
|
|
103
|
+
const expected = [HTMLParagraphElement, HTMLDivElement, HTMLDivElement, WrapperComponent, HTMLDivElement, NestedComponent, HTMLDivElement, HTMLBodyElement, HTMLHtmlElement];
|
|
104
|
+
expect(ancestors[0]).toBe(nestedContent);
|
|
105
|
+
expect(ancestors.length).toEqual(expected.length);
|
|
106
|
+
expected.forEach((instance, idx) => expect(ancestors[idx]).toBeInstanceOf(instance));
|
|
107
|
+
});
|
|
108
|
+
test("should handle slotted content", () => {
|
|
109
|
+
container.innerHTML = `
|
|
110
|
+
<wrapper-component>
|
|
111
|
+
<span class="slotted">Slotted content</span>
|
|
112
|
+
</wrapper-component>
|
|
113
|
+
`;
|
|
114
|
+
const slottedContent = container.querySelector(".slotted");
|
|
115
|
+
const ancestors = Array.from(shadowPiercingAncestors(slottedContent));
|
|
116
|
+
const expected = [HTMLSpanElement, WrapperComponent, HTMLDivElement, HTMLBodyElement, HTMLHtmlElement];
|
|
117
|
+
expect(ancestors[0]).toBe(slottedContent);
|
|
118
|
+
expect(ancestors.length).toEqual(expected.length);
|
|
119
|
+
expected.forEach((instance, idx) => expect(ancestors[idx]).toBeInstanceOf(instance));
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
describe("`shadowPiercingContains`", () => {
|
|
123
|
+
test("should find containment across custom element boundaries", () => {
|
|
124
|
+
var _a;
|
|
125
|
+
container.innerHTML = `<wrapper-component></wrapper-component>`;
|
|
126
|
+
const component = container.querySelector("wrapper-component");
|
|
127
|
+
const shadowText = (_a = component === null || component === void 0 ? void 0 : component.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector(".text");
|
|
128
|
+
expect(shadowPiercingContains(container, shadowText)).toBe(true);
|
|
129
|
+
expect(shadowPiercingContains(component, shadowText)).toBe(true);
|
|
130
|
+
});
|
|
131
|
+
test("should work with nested custom elements", () => {
|
|
132
|
+
var _a, _b;
|
|
133
|
+
container.innerHTML = `<nested-component></nested-component>`;
|
|
134
|
+
const outerComponent = container.querySelector("nested-component");
|
|
135
|
+
const innerComponent = (_a = outerComponent === null || outerComponent === void 0 ? void 0 : outerComponent.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector("wrapper-component");
|
|
136
|
+
const deepContent = (_b = innerComponent === null || innerComponent === void 0 ? void 0 : innerComponent.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector(".text");
|
|
137
|
+
expect(shadowPiercingContains(container, deepContent)).toBe(true);
|
|
138
|
+
expect(shadowPiercingContains(outerComponent, deepContent)).toBe(true);
|
|
139
|
+
});
|
|
140
|
+
test("should handle mixed light/shadow DOM", () => {
|
|
141
|
+
var _a;
|
|
142
|
+
container.innerHTML = `
|
|
143
|
+
<div class="wrapper">
|
|
144
|
+
<wrapper-component></wrapper-component>
|
|
145
|
+
</div>
|
|
146
|
+
`;
|
|
147
|
+
const wrapper = container.querySelector(".wrapper");
|
|
148
|
+
const component = wrapper === null || wrapper === void 0 ? void 0 : wrapper.querySelector("wrapper-component");
|
|
149
|
+
const shadowContent = (_a = component === null || component === void 0 ? void 0 : component.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector(".content");
|
|
150
|
+
expect(shadowPiercingContains(container, shadowContent)).toBe(true);
|
|
151
|
+
expect(shadowPiercingContains(wrapper, shadowContent)).toBe(true);
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
describe("`shallowShadowQuerySelector`", () => {
|
|
155
|
+
test("should find elements in custom element shadow DOM", () => {
|
|
156
|
+
var _a;
|
|
157
|
+
container.innerHTML = `<wrapper-component></wrapper-component>`;
|
|
158
|
+
const component = container.querySelector("wrapper-component");
|
|
159
|
+
const result = shallowShadowQuerySelector(component, ".text");
|
|
160
|
+
const expected = (_a = component === null || component === void 0 ? void 0 : component.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector(".text");
|
|
161
|
+
expect(result).toBe(expected);
|
|
162
|
+
});
|
|
163
|
+
test("should prioritize light DOM over shadow DOM in custom elements", () => {
|
|
164
|
+
container.innerHTML = `
|
|
165
|
+
<wrapper-component>
|
|
166
|
+
<p class="text">Light DOM text</p>
|
|
167
|
+
</wrapper-component>
|
|
168
|
+
`;
|
|
169
|
+
const component = container.querySelector("wrapper-component");
|
|
170
|
+
const result = shallowShadowQuerySelector(component, ".text");
|
|
171
|
+
expect(result === null || result === void 0 ? void 0 : result.textContent).toBe("Light DOM text");
|
|
172
|
+
});
|
|
173
|
+
test("should fall back to shadow DOM when not found in light DOM", () => {
|
|
174
|
+
var _a;
|
|
175
|
+
container.innerHTML = `<wrapper-component></wrapper-component>`;
|
|
176
|
+
const component = container.querySelector("wrapper-component");
|
|
177
|
+
const result = shallowShadowQuerySelector(component, ".wrapper");
|
|
178
|
+
const expected = (_a = component === null || component === void 0 ? void 0 : component.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector(".wrapper");
|
|
179
|
+
expect(result).toBe(expected);
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
describe("Real-world scenarios", () => {
|
|
183
|
+
test("should handle form controls with shadow DOM", () => {
|
|
184
|
+
var _a;
|
|
185
|
+
container.innerHTML = `
|
|
186
|
+
<form>
|
|
187
|
+
<custom-input></custom-input>
|
|
188
|
+
</form>
|
|
189
|
+
`;
|
|
190
|
+
const form = container.querySelector("form");
|
|
191
|
+
const customInput = form === null || form === void 0 ? void 0 : form.querySelector("custom-input");
|
|
192
|
+
const actualInput = (_a = customInput === null || customInput === void 0 ? void 0 : customInput.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector(".actual-input");
|
|
193
|
+
expect(shadowPiercingContains(form, actualInput)).toBe(true);
|
|
194
|
+
expect(shadowPiercingContains(container, actualInput)).toBe(true);
|
|
195
|
+
});
|
|
196
|
+
test("should work with component composition", () => {
|
|
197
|
+
var _a;
|
|
198
|
+
container.innerHTML = `
|
|
199
|
+
<card-component>
|
|
200
|
+
<h2 slot="header">Title</h2>
|
|
201
|
+
<wrapper-component></wrapper-component>
|
|
202
|
+
</card-component>
|
|
203
|
+
`;
|
|
204
|
+
const card = container.querySelector("card-component");
|
|
205
|
+
const wrapper = card === null || card === void 0 ? void 0 : card.querySelector("wrapper-component");
|
|
206
|
+
const shadowText = (_a = wrapper === null || wrapper === void 0 ? void 0 : wrapper.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector(".text");
|
|
207
|
+
const ancestors = Array.from(shadowPiercingAncestors(shadowText));
|
|
208
|
+
const expected = [HTMLParagraphElement, HTMLDivElement, HTMLDivElement, WrapperComponent, CardComponent, HTMLDivElement, HTMLBodyElement, HTMLHtmlElement];
|
|
209
|
+
expect(ancestors.length).toEqual(expected.length);
|
|
210
|
+
expected.forEach((instance, idx) => expect(ancestors[idx]).toBeInstanceOf(instance));
|
|
211
|
+
expect(shadowPiercingContains(container, shadowText)).toBe(true);
|
|
212
|
+
expect(shadowPiercingContains(card, shadowText)).toBe(true);
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
});
|
package/utils/visible.d.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { Fnode } from "@protontech/fathom";
|
|
1
|
+
import type { Fnode } from "@protontech/fathom";
|
|
2
2
|
type VisibilityCache = WeakMap<HTMLElement, boolean>;
|
|
3
3
|
type IsVisibleOptions = {
|
|
4
4
|
opacity: boolean;
|
|
5
|
+
skipCache?: boolean;
|
|
5
6
|
};
|
|
6
7
|
export declare const cacheContext: Record<string, VisibilityCache>;
|
|
7
8
|
export declare const getVisibilityCache: (key: string) => VisibilityCache;
|
|
8
9
|
export declare const clearVisibilityCache: () => void;
|
|
9
10
|
export declare const isVisible: (fnodeOrElement: Fnode | HTMLElement, options: IsVisibleOptions) => boolean;
|
|
10
11
|
export declare const isVisibleEl: (el: HTMLElement) => boolean;
|
|
11
|
-
export declare const isVisibleForm: (form: HTMLElement) => boolean;
|
|
12
|
+
export declare const isVisibleForm: (form: HTMLElement, options?: Partial<Pick<IsVisibleOptions, "skipCache">>) => boolean;
|
|
12
13
|
export declare const isVisibleField: (field: HTMLElement) => boolean;
|
|
13
14
|
export {};
|
package/utils/visible.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { utils } from "@protontech/fathom";
|
|
2
|
-
import { MIN_FIELD_HEIGHT, MIN_FIELD_WIDTH } from "
|
|
3
|
-
import { inputCandidateSelector } from "
|
|
2
|
+
import { MIN_FIELD_HEIGHT, MIN_FIELD_WIDTH, SCROLLBAR_WIDTH } from "@protontech/autofill/constants/heuristics";
|
|
3
|
+
import { inputCandidateSelector } from "@protontech/autofill/constants/selectors";
|
|
4
4
|
import { any } from "./combinators";
|
|
5
5
|
import { flagAsHidden } from "./flags";
|
|
6
|
+
import { isIFrameField } from "./iframe";
|
|
6
7
|
import { matchHidden } from "./re";
|
|
8
|
+
import { isCustomElementWithShadowRoot, isShadowElement, shadowPiercingAncestors } from "./shadow-dom";
|
|
7
9
|
import { sanitizeStringWithSpaces } from "./text";
|
|
8
10
|
export const cacheContext = {};
|
|
9
11
|
export const getVisibilityCache = (key) => { var _a; return (cacheContext[key] = (_a = cacheContext[key]) !== null && _a !== void 0 ? _a : new WeakMap()); };
|
|
10
12
|
export const clearVisibilityCache = () => Object.keys(cacheContext).forEach((key) => delete cacheContext[key]);
|
|
11
|
-
const SCROLLBAR_WIDTH = 16;
|
|
12
13
|
const getCachedVisbility = (el, options) => {
|
|
13
14
|
var _a;
|
|
14
15
|
const opacityCache = getVisibilityCache("visibility:op");
|
|
@@ -21,13 +22,23 @@ const getCachedVisbility = (el, options) => {
|
|
|
21
22
|
const setCachedVisibility = (cacheMap) => (els, visible) => els.forEach((el) => cacheMap.set(el, visible));
|
|
22
23
|
const containedInAncestor = (rect, ancestorRect) => rect.top <= ancestorRect.bottom && rect.bottom >= ancestorRect.top && rect.left <= ancestorRect.right && rect.right >= ancestorRect.left;
|
|
23
24
|
const isNegligibleRect = (rect) => rect.width <= 1 || rect.height <= 1;
|
|
25
|
+
const HIDDEN_CLIP = ["rect(0px, 0px, 0px, 0px)", "rect(0, 0, 0, 0)", "rect(1px, 1px, 1px, 1px)"];
|
|
26
|
+
const HIDDEN_CLIP_PATH = ["circle(0", "polygon()", "inset(100%)", "inset(50% 50% 50% 50%)"];
|
|
27
|
+
const isClipped = (clip, path) => {
|
|
28
|
+
if (clip && clip !== "auto")
|
|
29
|
+
return HIDDEN_CLIP.some((value) => clip.includes(value));
|
|
30
|
+
if (path && path !== "none")
|
|
31
|
+
return HIDDEN_CLIP_PATH.some((value) => path.includes(value));
|
|
32
|
+
return false;
|
|
33
|
+
};
|
|
24
34
|
export const isVisible = (fnodeOrElement, options) => {
|
|
35
|
+
const useCache = !options.skipCache;
|
|
25
36
|
const element = utils.toDomElement(fnodeOrElement);
|
|
26
37
|
const seen = [];
|
|
27
38
|
let transparent = false;
|
|
28
39
|
const cache = getVisibilityCache(options.opacity ? "visibility:op" : "visibility");
|
|
29
40
|
const cachedVisibility = getCachedVisbility(element, options);
|
|
30
|
-
if (cachedVisibility !== undefined)
|
|
41
|
+
if (useCache && cachedVisibility !== undefined)
|
|
31
42
|
return cachedVisibility;
|
|
32
43
|
const win = utils.windowForElement(element);
|
|
33
44
|
const doc = win.document;
|
|
@@ -46,21 +57,28 @@ export const isVisible = (fnodeOrElement, options) => {
|
|
|
46
57
|
const check = () => {
|
|
47
58
|
var _a;
|
|
48
59
|
let prevRef = null;
|
|
49
|
-
for (const ancestor of
|
|
60
|
+
for (const ancestor of shadowPiercingAncestors(element)) {
|
|
50
61
|
let rect = null;
|
|
51
62
|
const getRect = () => (rect = rect !== null && rect !== void 0 ? rect : ancestor.getBoundingClientRect());
|
|
52
63
|
if (ancestor === doc.body)
|
|
53
64
|
return (prevRef === null || prevRef === void 0 ? void 0 : prevRef.absolute) ? isOnScreen(prevRef.rect) : true;
|
|
54
65
|
const cachedVisibility = getCachedVisbility(ancestor, options);
|
|
55
|
-
if (cachedVisibility !== undefined)
|
|
66
|
+
if (useCache && cachedVisibility !== undefined)
|
|
56
67
|
return cachedVisibility;
|
|
57
|
-
const { opacity, display, position, overflow, visibility } = win.getComputedStyle(ancestor);
|
|
68
|
+
const { opacity, display, position, overflow, visibility, clip, clipPath } = win.getComputedStyle(ancestor);
|
|
58
69
|
seen.push(ancestor);
|
|
59
|
-
|
|
70
|
+
const opacityValue = Math.floor(parseFloat(opacity) * 10);
|
|
71
|
+
if (opacityValue === 0 && options.opacity) {
|
|
60
72
|
transparent = true;
|
|
61
73
|
return false;
|
|
62
74
|
}
|
|
63
|
-
if (visibility === "hidden")
|
|
75
|
+
if (visibility === "hidden" || display === "none")
|
|
76
|
+
return false;
|
|
77
|
+
if ((prevRef === null || prevRef === void 0 ? void 0 : prevRef.absolute) && position === "static") {
|
|
78
|
+
seen.pop();
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (isClipped(clip, clipPath))
|
|
64
82
|
return false;
|
|
65
83
|
if (overflow === "hidden") {
|
|
66
84
|
if ((prevRef === null || prevRef === void 0 ? void 0 : prevRef.rect) && !containedInAncestor(prevRef.rect, getRect()))
|
|
@@ -68,10 +86,6 @@ export const isVisible = (fnodeOrElement, options) => {
|
|
|
68
86
|
if (isNegligibleRect(getRect()))
|
|
69
87
|
return false;
|
|
70
88
|
}
|
|
71
|
-
if ((prevRef === null || prevRef === void 0 ? void 0 : prevRef.absolute) && position === "static") {
|
|
72
|
-
seen.pop();
|
|
73
|
-
continue;
|
|
74
|
-
}
|
|
75
89
|
if (position === "absolute" && !isOnScreen(getRect()))
|
|
76
90
|
return false;
|
|
77
91
|
if (position === "fixed")
|
|
@@ -88,13 +102,15 @@ export const isVisible = (fnodeOrElement, options) => {
|
|
|
88
102
|
return true;
|
|
89
103
|
};
|
|
90
104
|
const visible = check();
|
|
91
|
-
if (
|
|
92
|
-
if (
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
105
|
+
if (useCache) {
|
|
106
|
+
if (options.opacity) {
|
|
107
|
+
if (visible || !transparent)
|
|
108
|
+
setCachedVisibility(getVisibilityCache("visibility"))(seen, visible);
|
|
109
|
+
else
|
|
110
|
+
setCachedVisibility(getVisibilityCache("visibility"))(seen.slice(0, -1), visible);
|
|
111
|
+
}
|
|
112
|
+
setCachedVisibility(cache)(seen, visible);
|
|
96
113
|
}
|
|
97
|
-
setCachedVisibility(cache)(seen, visible);
|
|
98
114
|
return visible;
|
|
99
115
|
};
|
|
100
116
|
const quickVisibilityCheck = (el, options) => {
|
|
@@ -122,12 +138,16 @@ const quickVisibilityCheck = (el, options) => {
|
|
|
122
138
|
return visible;
|
|
123
139
|
};
|
|
124
140
|
export const isVisibleEl = (el) => quickVisibilityCheck(el, { minHeight: 0, minWidth: 0 });
|
|
125
|
-
export const isVisibleForm = (form) => {
|
|
141
|
+
export const isVisibleForm = (form, options = {}) => {
|
|
126
142
|
const visible = (() => {
|
|
127
|
-
if (!isVisible(form, { opacity: true }))
|
|
143
|
+
if (!isVisible(form, { opacity: true, ...options }))
|
|
128
144
|
return false;
|
|
145
|
+
if (isCustomElementWithShadowRoot(form) || isShadowElement(form))
|
|
146
|
+
return true;
|
|
129
147
|
const inputs = Array.from(form.querySelectorAll(inputCandidateSelector)).filter((field) => !field.disabled);
|
|
130
|
-
|
|
148
|
+
const iframes = Array.from(form.querySelectorAll("iframe")).filter(isIFrameField);
|
|
149
|
+
const fields = [...inputs, ...iframes];
|
|
150
|
+
return fields.length > 0 && fields.some((input) => isVisible(input, { opacity: false, ...options }));
|
|
131
151
|
})();
|
|
132
152
|
if (!visible)
|
|
133
153
|
flagAsHidden(form);
|
package/cli.d.ts
DELETED