browser-debugger-cli 0.11.0 → 0.13.0
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/.claude/skills/bdg/SKILL.md +4 -4
- package/README.md +143 -79
- package/dist/commands/cdp.d.ts +22 -1
- package/dist/commands/cdp.js +100 -43
- package/dist/commands/console.d.ts +12 -0
- package/dist/commands/console.js +62 -12
- package/dist/commands/css.d.ts +13 -0
- package/dist/commands/css.js +53 -0
- package/dist/commands/dom/DomElementResolver.d.ts +3 -1
- package/dist/commands/dom/DomElementResolver.js +10 -3
- package/dist/commands/dom/a11y.js +3 -2
- package/dist/commands/dom/audit.d.ts +14 -0
- package/dist/commands/dom/audit.js +87 -0
- package/dist/commands/dom/eval.d.ts +3 -2
- package/dist/commands/dom/eval.js +11 -5
- package/dist/commands/dom/form.js +10 -9
- package/dist/commands/dom/formInteraction.js +42 -11
- package/dist/commands/dom/get.js +8 -8
- package/dist/commands/dom/helpers/index.d.ts +1 -1
- package/dist/commands/dom/helpers/index.js +1 -1
- package/dist/commands/dom/helpers/keyAttributes.d.ts +3 -2
- package/dist/commands/dom/helpers/keyAttributes.js +6 -4
- package/dist/commands/dom/helpers/query.d.ts +27 -3
- package/dist/commands/dom/helpers/query.js +152 -64
- package/dist/commands/dom/helpers/screenshot.d.ts +1 -0
- package/dist/commands/dom/helpers/screenshot.js +169 -49
- package/dist/commands/dom/index.js +7 -2
- package/dist/commands/dom/query.d.ts +19 -2
- package/dist/commands/dom/query.js +37 -6
- package/dist/commands/dom/screenshot.js +12 -7
- package/dist/commands/dom/semanticUtils.d.ts +3 -2
- package/dist/commands/dom/semanticUtils.js +40 -9
- package/dist/commands/dom/wait.js +5 -3
- package/dist/commands/helpJson.d.ts +82 -19
- package/dist/commands/helpJson.js +112 -41
- package/dist/commands/helpTopic.d.ts +16 -1
- package/dist/commands/helpTopic.js +59 -1
- package/dist/commands/installSkill.d.ts +15 -5
- package/dist/commands/installSkill.js +86 -16
- package/dist/commands/network/list.js +22 -12
- package/dist/commands/optionBehaviors.js +53 -16
- package/dist/commands/page.js +7 -4
- package/dist/commands/peek.d.ts +7 -0
- package/dist/commands/peek.js +65 -23
- package/dist/commands/shared/daemonErrorHandler.d.ts +5 -2
- package/dist/commands/shared/daemonErrorHandler.js +20 -9
- package/dist/commands/shared/dataFetcher.d.ts +12 -4
- package/dist/commands/shared/dataFetcher.js +12 -4
- package/dist/commands/shared/followMode.d.ts +9 -1
- package/dist/commands/shared/followMode.js +22 -4
- package/dist/commands/shared/optionTypes.d.ts +9 -2
- package/dist/commands/shared/outputFile.js +6 -1
- package/dist/commands/start.d.ts +20 -5
- package/dist/commands/start.js +84 -23
- package/dist/commands/stop.d.ts +11 -0
- package/dist/commands/stop.js +24 -1
- package/dist/commands/tail.d.ts +7 -1
- package/dist/commands/tail.js +13 -62
- package/dist/commands.js +2 -0
- package/dist/connection/cdp.d.ts +7 -0
- package/dist/connection/cdp.js +9 -0
- package/dist/connection/launcher.js +3 -2
- package/dist/daemon/SessionController.js +6 -1
- package/dist/daemon/launcher.d.ts +3 -2
- package/dist/daemon/launcher.js +47 -3
- package/dist/daemon/session/Session.d.ts +4 -1
- package/dist/daemon/session/Session.js +33 -2
- package/dist/daemon/session/TelemetryStore.d.ts +8 -1
- package/dist/daemon/session/TelemetryStore.js +13 -1
- package/dist/daemon/session/commandRegistry.js +36 -14
- package/dist/daemon/session/interactions.d.ts +2 -1
- package/dist/daemon/session/interactions.js +13 -1
- package/dist/daemon/session/plugins.js +19 -53
- package/dist/daemon/session/teardown.js +1 -1
- package/dist/daemon.js +9234 -7222
- package/dist/errors/messages.d.ts +88 -15
- package/dist/errors/messages.js +177 -27
- package/dist/index.js +19322 -13961
- package/dist/ipc/client.d.ts +22 -2
- package/dist/ipc/client.js +34 -5
- package/dist/ipc/protocol/auditTypes.d.ts +135 -0
- package/dist/ipc/protocol/auditTypes.js +6 -0
- package/dist/ipc/protocol/commands.d.ts +35 -0
- package/dist/ipc/protocol/commands.js +2 -0
- package/dist/ipc/protocol/domTypes.d.ts +16 -0
- package/dist/ipc/protocol/inspectTypes.d.ts +73 -8
- package/dist/ipc/session/types.d.ts +2 -0
- package/dist/runtime/css/search.d.ts +39 -0
- package/dist/runtime/css/search.js +122 -0
- package/dist/runtime/dom/actionEffects.d.ts +9 -2
- package/dist/runtime/dom/actionEffects.js +30 -14
- package/dist/runtime/dom/audit.d.ts +19 -0
- package/dist/runtime/dom/audit.js +37 -0
- package/dist/runtime/dom/auditModel.d.ts +45 -0
- package/dist/runtime/dom/auditModel.js +220 -0
- package/dist/runtime/dom/auditScripts.d.ts +113 -0
- package/dist/runtime/dom/auditScripts.js +148 -0
- package/dist/runtime/dom/elementGeometry.d.ts +16 -3
- package/dist/runtime/dom/elementGeometry.js +49 -10
- package/dist/runtime/dom/elementInfo.d.ts +74 -17
- package/dist/runtime/dom/elementInfo.js +187 -34
- package/dist/runtime/dom/evalHelpers.d.ts +12 -2
- package/dist/runtime/dom/evalHelpers.js +67 -7
- package/dist/runtime/dom/formDiscovery.d.ts +6 -2
- package/dist/runtime/dom/formDiscovery.js +20 -3
- package/dist/runtime/dom/formFillHelpers/fill.js +8 -12
- package/dist/runtime/dom/formFillHelpers/pressKey.js +2 -2
- package/dist/runtime/dom/formFillHelpers/shared.d.ts +16 -10
- package/dist/runtime/dom/formFillHelpers/shared.js +19 -52
- package/dist/runtime/dom/formSubmitHelpers.js +4 -3
- package/dist/runtime/dom/frameLayout.js +1 -0
- package/dist/runtime/dom/inspect.d.ts +7 -0
- package/dist/runtime/dom/inspect.js +92 -28
- package/dist/runtime/dom/inspectAllStyles.d.ts +16 -4
- package/dist/runtime/dom/inspectAllStyles.js +90 -7
- package/dist/runtime/dom/inspectCascade.d.ts +19 -2
- package/dist/runtime/dom/inspectCascade.js +214 -44
- package/dist/runtime/dom/inspectCascadeModel.d.ts +8 -0
- package/dist/runtime/dom/inspectCascadeModel.js +108 -34
- package/dist/runtime/dom/inspectHints.d.ts +26 -3
- package/dist/runtime/dom/inspectHints.js +125 -9
- package/dist/runtime/dom/inspectModel.d.ts +5 -1
- package/dist/runtime/dom/inspectModel.js +37 -10
- package/dist/runtime/dom/inspectPaintModel.d.ts +50 -22
- package/dist/runtime/dom/inspectPaintModel.js +182 -68
- package/dist/runtime/dom/inspectRules.d.ts +19 -0
- package/dist/runtime/dom/inspectRules.js +21 -5
- package/dist/runtime/dom/inspectScripts.d.ts +112 -12
- package/dist/runtime/dom/inspectScripts.js +357 -32
- package/dist/runtime/dom/inspectTree.js +10 -2
- package/dist/runtime/dom/inspectWhyModel.d.ts +2 -1
- package/dist/runtime/dom/inspectWhyModel.js +52 -10
- package/dist/runtime/dom/layout.js +40 -16
- package/dist/runtime/dom/reactEventHelpers.d.ts +21 -4
- package/dist/runtime/dom/reactEventHelpers.js +90 -36
- package/dist/runtime/dom/targetNode.d.ts +18 -5
- package/dist/runtime/dom/targetNode.js +268 -8
- package/dist/runtime/dom/wait.js +2 -1
- package/dist/runtime/page/bdgWorld.d.ts +57 -0
- package/dist/runtime/page/bdgWorld.js +180 -0
- package/dist/runtime/page/emulation.d.ts +13 -4
- package/dist/runtime/page/emulation.js +69 -4
- package/dist/runtime/page/replacedBuiltins.d.ts +28 -0
- package/dist/runtime/page/replacedBuiltins.js +136 -0
- package/dist/runtime/page/userAgent.d.ts +17 -0
- package/dist/runtime/page/userAgent.js +57 -0
- package/dist/session/QueryCacheManager.d.ts +4 -1
- package/dist/session/QueryCacheManager.js +5 -2
- package/dist/session/chrome.d.ts +4 -1
- package/dist/session/chrome.js +7 -1
- package/dist/session/cleanup/staleSession.d.ts +21 -4
- package/dist/session/cleanup/staleSession.js +79 -9
- package/dist/session/cleanup/userCommands.d.ts +4 -1
- package/dist/session/cleanup/userCommands.js +10 -5
- package/dist/session/daemonSocket.d.ts +10 -0
- package/dist/session/daemonSocket.js +22 -0
- package/dist/session/lastSession.d.ts +6 -3
- package/dist/session/lastSession.js +11 -5
- package/dist/session/paths.d.ts +3 -1
- package/dist/session/paths.js +5 -5
- package/dist/session/portClaims.js +4 -3
- package/dist/session/sessionList.d.ts +13 -5
- package/dist/session/sessionList.js +31 -7
- package/dist/telemetry/a11y.js +2 -2
- package/dist/telemetry/console.d.ts +2 -1
- package/dist/telemetry/console.js +30 -21
- package/dist/telemetry/pageCrash.d.ts +26 -0
- package/dist/telemetry/pageCrash.js +53 -0
- package/dist/types.d.ts +20 -0
- package/dist/ui/formatters/audit.d.ts +19 -0
- package/dist/ui/formatters/audit.js +115 -0
- package/dist/ui/formatters/cdp.d.ts +138 -0
- package/dist/ui/formatters/cdp.js +131 -0
- package/dist/ui/formatters/console/chronological.js +3 -1
- package/dist/ui/formatters/console/follow.d.ts +2 -1
- package/dist/ui/formatters/console/follow.js +2 -2
- package/dist/ui/formatters/console/json.d.ts +2 -2
- package/dist/ui/formatters/console/json.js +11 -5
- package/dist/ui/formatters/console/shared.d.ts +30 -0
- package/dist/ui/formatters/console/shared.js +16 -0
- package/dist/ui/formatters/console/summarize.d.ts +9 -2
- package/dist/ui/formatters/console/summarize.js +40 -9
- package/dist/ui/formatters/console.d.ts +2 -1
- package/dist/ui/formatters/console.js +7 -5
- package/dist/ui/formatters/details.js +3 -1
- package/dist/ui/formatters/dom.d.ts +2 -2
- package/dist/ui/formatters/dom.js +10 -8
- package/dist/ui/formatters/helpFormatters.js +1 -1
- package/dist/ui/formatters/inspect.js +50 -17
- package/dist/ui/formatters/installSkill.d.ts +9 -1
- package/dist/ui/formatters/installSkill.js +32 -6
- package/dist/ui/formatters/layout.js +2 -1
- package/dist/ui/formatters/networkList.d.ts +1 -1
- package/dist/ui/formatters/networkList.js +1 -2
- package/dist/ui/formatters/preview.d.ts +2 -0
- package/dist/ui/formatters/preview.js +17 -7
- package/dist/ui/formatters/sessions.d.ts +2 -2
- package/dist/ui/formatters/sessions.js +9 -2
- package/dist/ui/formatters/status.js +1 -1
- package/dist/ui/logging/logger.d.ts +1 -1
- package/dist/ui/messages/commands.d.ts +168 -11
- package/dist/ui/messages/commands.js +245 -18
- package/dist/ui/messages/consoleMessages.d.ts +24 -0
- package/dist/ui/messages/consoleMessages.js +32 -0
- package/dist/ui/messages/preview.d.ts +12 -0
- package/dist/ui/messages/preview.js +18 -2
- package/dist/ui/messages/session.d.ts +13 -2
- package/dist/ui/messages/session.js +22 -3
- package/dist/utils/cssValues.js +36 -4
- package/dist/utils/decisionTrees.js +0 -5
- package/dist/utils/directories.d.ts +34 -0
- package/dist/utils/directories.js +88 -0
- package/dist/utils/display.d.ts +16 -0
- package/dist/utils/display.js +42 -0
- package/dist/utils/exitCodes.d.ts +1 -0
- package/dist/utils/exitCodes.js +6 -0
- package/dist/utils/process.d.ts +12 -0
- package/dist/utils/process.js +25 -0
- package/dist/utils/suggestions.d.ts +4 -2
- package/dist/utils/suggestions.js +7 -5
- package/dist/utils/taskMappings.js +1 -1
- package/package.json +3 -2
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* instead of the light DOM, slots replaced by what is assigned to them, and
|
|
13
13
|
* `display: contents` wrappers by their children.
|
|
14
14
|
*/
|
|
15
|
-
import { ELEMENT_CONTEXT_JS, ELEMENT_DESCRIPTION_JS, MASKED_VALUE, SENSITIVE_FIELD_JS, } from './elementInfo.js';
|
|
15
|
+
import { COMPOSED_JS, ELEMENT_CONTEXT_JS, ELEMENT_DESCRIPTION_JS, FLAT_TEXT_JS, LABEL_CLASSES_JS, MASKED_VALUE, SENSITIVE_FIELD_JS, } from './elementInfo.js';
|
|
16
16
|
/** Elements walked for the child tree at most (the rest are counted) */
|
|
17
17
|
export const TREE_NODE_CAP = 400;
|
|
18
18
|
/**
|
|
@@ -20,7 +20,7 @@ export const TREE_NODE_CAP = 400;
|
|
|
20
20
|
* parent, the parent that lays it out (skipping `display: contents`), its
|
|
21
21
|
* element children in the flat tree and whether an element is rendered.
|
|
22
22
|
*/
|
|
23
|
-
const FLAT_TREE_JS = `(view) => {
|
|
23
|
+
export const FLAT_TREE_JS = `(view) => {
|
|
24
24
|
const style = (n, pseudo) => view.getComputedStyle(n, pseudo);
|
|
25
25
|
const skipped = /^(script|style|template|link|meta|noscript|title|head|base)$/;
|
|
26
26
|
const flatParent = (n) => n.assignedSlot || n.parentElement || (n.parentNode && n.parentNode.host) || null;
|
|
@@ -29,37 +29,121 @@ const FLAT_TREE_JS = `(view) => {
|
|
|
29
29
|
while (p && style(p).display === 'contents') p = flatParent(p);
|
|
30
30
|
return p;
|
|
31
31
|
};
|
|
32
|
+
const via = new WeakMap();
|
|
33
|
+
const firstClass = (n) => (${LABEL_CLASSES_JS})(n)[0];
|
|
34
|
+
const label = (n) => n.localName + (n.id ? '#' + n.id : firstClass(n) ? '.' + firstClass(n) : '');
|
|
35
|
+
const slotText = (slot) => slot.assignedNodes({ flatten: true }).some((t) => t.nodeType === 3 && t.data.trim() !== '');
|
|
32
36
|
const children = (n) => {
|
|
33
37
|
const out = [];
|
|
34
|
-
const add = (c) => {
|
|
38
|
+
const add = (c, through) => {
|
|
35
39
|
if (skipped.test(c.localName)) return;
|
|
36
40
|
if (c.localName === 'slot') {
|
|
37
41
|
const assigned = c.assignedElements({ flatten: true });
|
|
38
|
-
(assigned.length
|
|
42
|
+
if (assigned.length === 0 && slotText(c)) out.push(c);
|
|
43
|
+
else (assigned.length ? assigned : Array.from(c.children)).forEach((a) => add(a, label(c)));
|
|
39
44
|
} else if (style(c).display === 'contents') {
|
|
40
|
-
Array.from((c.shadowRoot || c).children).forEach(add);
|
|
45
|
+
Array.from((c.shadowRoot || c).children).forEach((a) => add(a, label(c) + ' (contents)'));
|
|
41
46
|
} else {
|
|
47
|
+
if (through) via.set(c, through);
|
|
42
48
|
out.push(c);
|
|
43
49
|
}
|
|
44
50
|
};
|
|
45
|
-
Array.from((n.shadowRoot || n).children).forEach(add);
|
|
51
|
+
Array.from((n.shadowRoot || n).children).forEach((c) => add(c, null));
|
|
46
52
|
return out;
|
|
47
53
|
};
|
|
48
54
|
const rendered = (n) => n.getClientRects().length > 0 &&
|
|
49
55
|
(!n.checkVisibility || n.checkVisibility({ visibilityProperty: true }));
|
|
50
56
|
const container = (n) => children(n).some((c) => rendered(c) && !/^inline/.test(style(c).display));
|
|
51
|
-
return { style, flatParent, layoutParent, children, rendered, container };
|
|
57
|
+
return { style, skipped, flatParent, layoutParent, children, rendered, container, label, via, slotText };
|
|
58
|
+
}`;
|
|
59
|
+
/**
|
|
60
|
+
* Page-side holder of an element's text: the element whose styles draw most
|
|
61
|
+
* of the visible text in its flat tree (characters, whitespace aside), and
|
|
62
|
+
* the element whose child text nodes those are (for the rendered font).
|
|
63
|
+
* Text slotted into a shadow root is drawn with the slot's styles, and text
|
|
64
|
+
* of an element with a shadow root that is not slotted is not drawn, nor is
|
|
65
|
+
* text inside a fully transparent descendant (a measuring copy under a
|
|
66
|
+
* mask). Null when there is no visible text.
|
|
67
|
+
*/
|
|
68
|
+
const TEXT_HOLDER_JS = `(el, tree) => {
|
|
69
|
+
const counts = new Map();
|
|
70
|
+
const parents = new Map();
|
|
71
|
+
let budget = 2000;
|
|
72
|
+
const shown = (n) => Number(tree.style(n).opacity) !== 0 && (tree.style(n).display === 'contents' || tree.rendered(n));
|
|
73
|
+
const nodesOf = (n) => {
|
|
74
|
+
if (n.localName !== 'slot') return Array.from((n.shadowRoot || n).childNodes);
|
|
75
|
+
const assigned = n.assignedNodes({ flatten: true });
|
|
76
|
+
return assigned.length ? assigned : Array.from(n.childNodes);
|
|
77
|
+
};
|
|
78
|
+
const visit = (n) => {
|
|
79
|
+
if (budget-- <= 0) return;
|
|
80
|
+
for (const c of nodesOf(n)) {
|
|
81
|
+
if (budget <= 0) return;
|
|
82
|
+
if (c.nodeType === 3) {
|
|
83
|
+
const length = c.data.replace(/\\s+/g, '').length;
|
|
84
|
+
if (length === 0) continue;
|
|
85
|
+
counts.set(n, (counts.get(n) || 0) + length);
|
|
86
|
+
if (!parents.has(n)) parents.set(n, c.parentElement || n);
|
|
87
|
+
} else if (c.nodeType === 1 && !tree.skipped.test(c.localName) && shown(c)) {
|
|
88
|
+
visit(c);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
visit(el);
|
|
93
|
+
let best = null;
|
|
94
|
+
for (const [holder, length] of counts) if (!best || length > counts.get(best)) best = holder;
|
|
95
|
+
let total = 0;
|
|
96
|
+
for (const length of counts.values()) total += length;
|
|
97
|
+
return best && { style: best, font: parents.get(best), share: counts.get(best) / total };
|
|
98
|
+
}`;
|
|
99
|
+
/**
|
|
100
|
+
* Page-side: whether loaded `@font-face` (or `FontFace`) faces of the first
|
|
101
|
+
* family of an element's `font-family` cover the characters of its text
|
|
102
|
+
* (their `unicode-range`): then that web font draws the text, whatever
|
|
103
|
+
* name its file gives. A subset that lacks the text's characters (latin
|
|
104
|
+
* loaded, Cyrillic text) does not count.
|
|
105
|
+
*/
|
|
106
|
+
const FAMILY_LOADED_JS = `(n, tree, text) => {
|
|
107
|
+
const unquote = (name) => name.trim().replace(/^["']|["']$/g, '').toLowerCase();
|
|
108
|
+
const match = /^\\s*("[^"]*"|'[^']*'|[^,]*)/.exec(tree.style(n).fontFamily);
|
|
109
|
+
const first = unquote(match ? match[1] : '');
|
|
110
|
+
if (!first || !n.ownerDocument.fonts) return false;
|
|
111
|
+
const ranges = [];
|
|
112
|
+
for (const face of n.ownerDocument.fonts) {
|
|
113
|
+
if (face.status !== 'loaded' || unquote(face.family) !== first) continue;
|
|
114
|
+
for (const part of face.unicodeRange.split(',')) {
|
|
115
|
+
const [from, to] = part.trim().replace(/^U\\+/i, '').split('-');
|
|
116
|
+
const low = parseInt(from.replace(/\\?/g, '0'), 16);
|
|
117
|
+
const high = parseInt((to || from).replace(/\\?/g, 'F'), 16);
|
|
118
|
+
if (!Number.isNaN(low) && !Number.isNaN(high)) ranges.push([low, high]);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (ranges.length === 0) return false;
|
|
122
|
+
const covered = (code) => ranges.some(([low, high]) => code >= low && code <= high);
|
|
123
|
+
return Array.from(text.replace(/\\s+/g, '')).every((char) => covered(char.codePointAt(0)));
|
|
124
|
+
}`;
|
|
125
|
+
/**
|
|
126
|
+
* Page-side: whether a form control draws text of its own (a text field,
|
|
127
|
+
* a select, a button-like input); checkboxes, radios, ranges and color
|
|
128
|
+
* inputs do not. Buttons are not covered: their text is their content.
|
|
129
|
+
*/
|
|
130
|
+
const TEXT_CONTROL_JS = `(el) => {
|
|
131
|
+
if (el.localName === 'textarea' || el.localName === 'select') return true;
|
|
132
|
+
return el.localName === 'input' && !/^(checkbox|radio|range|color|hidden|image)$/.test(el.type);
|
|
52
133
|
}`;
|
|
53
134
|
/**
|
|
54
135
|
* Page-side text of an element: a form control's value or a select's chosen
|
|
55
136
|
* options (masked for secrets, {@link SENSITIVE_FIELD_JS}), else its
|
|
56
|
-
* `innerText` (`textContent` for SVG
|
|
137
|
+
* `innerText` (`textContent` for SVG; the flat tree's text for web
|
|
138
|
+
* components and slots, {@link FLAT_TEXT_JS}), masked when CSS hides it as a secret
|
|
57
139
|
* (`-webkit-text-security`), whitespace collapsed. A container holding a
|
|
58
140
|
* select or textarea gets its visible text without theirs, so a form's text
|
|
59
141
|
* never carries a chosen option or typed text.
|
|
60
142
|
*/
|
|
61
143
|
const TEXT_JS = `(el) => {
|
|
62
144
|
const isSensitive = ${SENSITIVE_FIELD_JS};
|
|
145
|
+
const composed = ${COMPOSED_JS};
|
|
146
|
+
const flatText = ${FLAT_TEXT_JS};
|
|
63
147
|
const clean = (text) => String(text || '').replace(/\\s+/g, ' ').trim().slice(0, 200);
|
|
64
148
|
if (el.localName === 'input' && el.type === 'hidden') return '';
|
|
65
149
|
if (el.localName === 'input' || el.localName === 'textarea') {
|
|
@@ -81,7 +165,8 @@ const TEXT_JS = `(el) => {
|
|
|
81
165
|
return parts.join(' ');
|
|
82
166
|
};
|
|
83
167
|
const holdsControls = Boolean(el.querySelector && el.querySelector('select, textarea'));
|
|
84
|
-
const
|
|
168
|
+
const ownText = () => (typeof el.innerText === 'string' ? el.innerText : el.textContent);
|
|
169
|
+
const raw = holdsControls ? outsideControls() : composed(el) ? flatText(el, 2000) : ownText();
|
|
85
170
|
const text = clean(raw);
|
|
86
171
|
const view = el.ownerDocument.defaultView;
|
|
87
172
|
const security = view ? view.getComputedStyle(el).getPropertyValue('-webkit-text-security') : '';
|
|
@@ -100,14 +185,19 @@ const PLACEMENT_JS = `(el, tree) => {
|
|
|
100
185
|
const r = el.getBoundingClientRect();
|
|
101
186
|
const p = parent.getBoundingClientRect();
|
|
102
187
|
const ps = tree.style(parent);
|
|
103
|
-
const
|
|
188
|
+
const zoom = parent.currentCSSZoom || 1;
|
|
189
|
+
const edge = (side) => (parseFloat(ps['border-' + side + '-width']) + parseFloat(ps['padding-' + side])) * zoom;
|
|
104
190
|
const inParent = {
|
|
105
191
|
left: r.left - (p.left + edge('left')),
|
|
106
192
|
top: r.top - (p.top + edge('top')),
|
|
107
193
|
right: (p.right - edge('right')) - r.right,
|
|
108
194
|
bottom: (p.bottom - edge('bottom')) - r.bottom
|
|
109
195
|
};
|
|
110
|
-
const inFlow = (n) =>
|
|
196
|
+
const inFlow = (n) => {
|
|
197
|
+
if (n === el || !tree.rendered(n) || /^(absolute|fixed)$/.test(tree.style(n).position)) return false;
|
|
198
|
+
const box = n.getBoundingClientRect();
|
|
199
|
+
return box.width > 0 || box.height > 0;
|
|
200
|
+
};
|
|
111
201
|
const siblings = tree.children(parent);
|
|
112
202
|
const at = siblings.indexOf(el);
|
|
113
203
|
const before = siblings.slice(0, Math.max(at, 0)).reverse().find(inFlow);
|
|
@@ -135,8 +225,10 @@ const BACKGROUNDS_JS = `(el, tree, view) => {
|
|
|
135
225
|
let opacity = 1;
|
|
136
226
|
for (let n = el; n && backgrounds.length < 60; n = tree.flatParent(n)) {
|
|
137
227
|
const s = tree.style(n);
|
|
138
|
-
|
|
139
|
-
|
|
228
|
+
const own = Number(s.opacity) || 0;
|
|
229
|
+
const background = { color: s.backgroundColor, image: s.backgroundImage !== 'none' };
|
|
230
|
+
backgrounds.push(own < 1 ? Object.assign(background, { opacity: own }) : background);
|
|
231
|
+
opacity *= own;
|
|
140
232
|
}
|
|
141
233
|
const doc = el.ownerDocument;
|
|
142
234
|
const meta = doc.querySelector('meta[name="color-scheme"]');
|
|
@@ -146,6 +238,94 @@ const BACKGROUNDS_JS = `(el, tree, view) => {
|
|
|
146
238
|
const canvasDark = /dark/.test(scheme) && (!/light/.test(scheme) || prefersDark);
|
|
147
239
|
return { backgrounds: backgrounds, canvasDark: canvasDark, opacity: opacity };
|
|
148
240
|
}`;
|
|
241
|
+
/**
|
|
242
|
+
* Page-side hit test that also sees elements with `pointer-events: none`
|
|
243
|
+
* (hero images, decorative layers and blended duplicates often have it, and
|
|
244
|
+
* `elementsFromPoint` skips them): for the duration of `test`, a constructed
|
|
245
|
+
* stylesheet makes every element of the given roots (documents and shadow
|
|
246
|
+
* roots) hittable. It is added and removed within one script turn, so nothing
|
|
247
|
+
* is painted with it and no DOM mutation is recorded. A root that already has
|
|
248
|
+
* it (an outer call covering a whole page walk) is left as it is, so nested
|
|
249
|
+
* calls cost no extra style recalculation.
|
|
250
|
+
*/
|
|
251
|
+
export const HIT_EVERYTHING_JS = `(roots, test) => {
|
|
252
|
+
const added = [];
|
|
253
|
+
for (const root of roots) {
|
|
254
|
+
if (!root || !root.adoptedStyleSheets) continue;
|
|
255
|
+
const view = (root.ownerDocument || root).defaultView;
|
|
256
|
+
try {
|
|
257
|
+
if (!view.__bdgHitSheet) {
|
|
258
|
+
view.__bdgHitSheet = new view.CSSStyleSheet();
|
|
259
|
+
view.__bdgHitSheet.replaceSync('* { pointer-events: auto !important; }');
|
|
260
|
+
}
|
|
261
|
+
if (root.adoptedStyleSheets.includes(view.__bdgHitSheet)) continue;
|
|
262
|
+
root.adoptedStyleSheets = [...root.adoptedStyleSheets, view.__bdgHitSheet];
|
|
263
|
+
added.push([root, view.__bdgHitSheet]);
|
|
264
|
+
} catch (e) {
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
try {
|
|
269
|
+
return test();
|
|
270
|
+
} finally {
|
|
271
|
+
for (const [root, sheet] of added) root.adoptedStyleSheets = root.adoptedStyleSheets.filter((s) => s !== sheet);
|
|
272
|
+
}
|
|
273
|
+
}`;
|
|
274
|
+
/**
|
|
275
|
+
* Page-side reasons the contrast of an element's text is approximate: a
|
|
276
|
+
* blend mode or filter on it or an ancestor, and, hit-testing the middle of
|
|
277
|
+
* its first line of text (else of its first box; only in the viewport), the
|
|
278
|
+
* nearest element below it that is not an ancestor, above the first opaque
|
|
279
|
+
* background of its own chain, and
|
|
280
|
+
* paints (a canvas, video, image, background) and the nearest one on top of
|
|
281
|
+
* it that paints (an overlay).
|
|
282
|
+
*/
|
|
283
|
+
export const PAINT_RISKS_JS = `(el, tree, textParent) => {
|
|
284
|
+
const risks = [];
|
|
285
|
+
const chain = [];
|
|
286
|
+
for (let n = el; n && chain.length < 60; n = tree.flatParent(n)) chain.push(n);
|
|
287
|
+
for (const n of chain) {
|
|
288
|
+
const s = tree.style(n);
|
|
289
|
+
if (s.mixBlendMode !== 'normal') risks.push('mix-blend-mode ' + s.mixBlendMode + ' on ' + tree.label(n));
|
|
290
|
+
if (s.filter !== 'none') risks.push('filter on ' + tree.label(n));
|
|
291
|
+
}
|
|
292
|
+
const textNode = Array.from(textParent.childNodes).find((c) => c.nodeType === 3 && c.data.trim() !== '');
|
|
293
|
+
const range = el.ownerDocument.createRange();
|
|
294
|
+
if (textNode) range.selectNodeContents(textNode);
|
|
295
|
+
const box = tree.style(el).display === 'contents' ? tree.layoutParent(el) : el;
|
|
296
|
+
const rect = (textNode && range.getClientRects()[0]) || (box && box.getClientRects()[0]);
|
|
297
|
+
const view = el.ownerDocument.defaultView;
|
|
298
|
+
if (!rect) return risks;
|
|
299
|
+
const x = rect.left + rect.width / 2;
|
|
300
|
+
const y = rect.top + rect.height / 2;
|
|
301
|
+
if (x < 0 || y < 0 || x >= view.innerWidth || y >= view.innerHeight) return risks;
|
|
302
|
+
const root = el.getRootNode();
|
|
303
|
+
const hits = (${HIT_EVERYTHING_JS})([el.ownerDocument, root], () => (root.elementsFromPoint ? root : el.ownerDocument).elementsFromPoint(x, y));
|
|
304
|
+
const at = hits.findIndex((h) => chain.includes(h));
|
|
305
|
+
if (at < 0) return risks;
|
|
306
|
+
const clear = (color) => color === 'transparent' || /^rgba\\(.*,\\s*0\\)$/.test(color) || /\\/\\s*0\\)$/.test(color);
|
|
307
|
+
const opaque = (n) => {
|
|
308
|
+
const s = tree.style(n);
|
|
309
|
+
return /^rgb\\(/.test(s.backgroundColor) && s.backgroundImage === 'none' && Number(s.opacity) === 1;
|
|
310
|
+
};
|
|
311
|
+
const paints = (n) => {
|
|
312
|
+
const s = tree.style(n);
|
|
313
|
+
return /^(canvas|video|img|iframe|embed|object|svg)$/.test(n.localName) || s.backgroundImage !== 'none' || !clear(s.backgroundColor);
|
|
314
|
+
};
|
|
315
|
+
let behind = null;
|
|
316
|
+
for (const h of hits.slice(at)) {
|
|
317
|
+
if (chain.includes(h)) {
|
|
318
|
+
if (opaque(h)) break;
|
|
319
|
+
} else if (paints(h)) {
|
|
320
|
+
behind = h;
|
|
321
|
+
break;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
if (behind) risks.push(tree.label(behind) + ' behind');
|
|
325
|
+
const onTop = hits.slice(0, at).find((h) => !el.contains(h) && paints(h));
|
|
326
|
+
if (onTop) risks.push(tree.label(onTop) + ' on top');
|
|
327
|
+
return risks;
|
|
328
|
+
}`;
|
|
149
329
|
/**
|
|
150
330
|
* Page-side child tree to `depth` levels: per element its label, size,
|
|
151
331
|
* display, position relative to its parent's border box (like Figma's x/y
|
|
@@ -157,9 +337,8 @@ const BACKGROUNDS_JS = `(el, tree, view) => {
|
|
|
157
337
|
const TREE_JS = `(el, tree, textOf, depth) => {
|
|
158
338
|
let budget = ${TREE_NODE_CAP};
|
|
159
339
|
let skipped = 0;
|
|
160
|
-
const labelOf = (n) => n.localName + (n.id ? '#' + n.id : n.classList && n.classList.length ? '.' + n.classList[0] : '');
|
|
161
340
|
const walk = (kids, level, origin) => {
|
|
162
|
-
const shown = kids.filter(tree.rendered);
|
|
341
|
+
const shown = kids.filter((k) => tree.rendered(k) || k.localName === 'slot');
|
|
163
342
|
const nodes = [];
|
|
164
343
|
for (const k of shown) {
|
|
165
344
|
if (budget <= 0) { skipped++; continue; }
|
|
@@ -170,7 +349,13 @@ const TREE_JS = `(el, tree, textOf, depth) => {
|
|
|
170
349
|
};
|
|
171
350
|
const nodeOf = (n, level, origin) => {
|
|
172
351
|
const r = n.getBoundingClientRect();
|
|
173
|
-
const node = { label:
|
|
352
|
+
const node = { label: tree.label(n), x: r.left - origin.left, y: r.top - origin.top, w: r.width, h: r.height, display: tree.style(n).display };
|
|
353
|
+
if (tree.via.has(n)) node.via = tree.via.get(n);
|
|
354
|
+
if (n.parentNode && n.parentNode.nodeType === 11 && n.parentNode.host) node.shadow = true;
|
|
355
|
+
if (n.localName === 'slot') {
|
|
356
|
+
node.text = n.assignedNodes({ flatten: true }).map((t) => t.textContent).join(' ').replace(/\\s+/g, ' ').trim().slice(0, 60);
|
|
357
|
+
return node;
|
|
358
|
+
}
|
|
174
359
|
const kids = tree.children(n);
|
|
175
360
|
if (!tree.container(n)) node.text = textOf(n).slice(0, 60);
|
|
176
361
|
if (level >= depth) {
|
|
@@ -186,12 +371,49 @@ const TREE_JS = `(el, tree, textOf, depth) => {
|
|
|
186
371
|
const top = walk(tree.children(el), 1, el.getBoundingClientRect());
|
|
187
372
|
return { tree: top.nodes, hiddenChildren: top.hidden, treeSkipped: skipped };
|
|
188
373
|
}`;
|
|
374
|
+
/**
|
|
375
|
+
* Page-side: rules for the element that set a property but sit under a
|
|
376
|
+
* `@media` or `@supports` condition that does not apply now (at most 5),
|
|
377
|
+
* from same-origin, `@import`ed, constructed and shadow-root stylesheets.
|
|
378
|
+
*/
|
|
379
|
+
const INACTIVE_RULES_JS = `(el, names) => {
|
|
380
|
+
const view = el.ownerDocument.defaultView;
|
|
381
|
+
const found = [];
|
|
382
|
+
let budget = 20000;
|
|
383
|
+
const unmet = (rule) => {
|
|
384
|
+
if (rule.media && rule.media.mediaText && !view.matchMedia(rule.media.mediaText).matches) return '@media ' + rule.media.mediaText;
|
|
385
|
+
if (typeof CSSSupportsRule !== 'undefined' && rule instanceof CSSSupportsRule && !CSS.supports(rule.conditionText)) return '@supports ' + rule.conditionText;
|
|
386
|
+
return null;
|
|
387
|
+
};
|
|
388
|
+
const sets = (rule) => names.map((name) => rule.style.getPropertyValue(name).trim()).find((value) => value !== '');
|
|
389
|
+
const matches = (selector) => {
|
|
390
|
+
try { return el.matches(selector); } catch (e) { return false; }
|
|
391
|
+
};
|
|
392
|
+
const visit = (rules, condition) => {
|
|
393
|
+
for (const rule of Array.from(rules)) {
|
|
394
|
+
if (budget-- <= 0 || found.length >= 5) return;
|
|
395
|
+
if (typeof CSSImportRule !== 'undefined' && rule instanceof CSSImportRule) {
|
|
396
|
+
try { if (rule.styleSheet) visit(rule.styleSheet.cssRules, condition); } catch (e) { continue; }
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
399
|
+
const value = condition && rule.style && rule.selectorText ? sets(rule) : undefined;
|
|
400
|
+
if (value !== undefined && matches(rule.selectorText)) found.push({ selector: rule.selectorText, condition: condition, value: value });
|
|
401
|
+
if (rule.cssRules) visit(rule.cssRules, condition || unmet(rule));
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
for (const scope of new Set([el.getRootNode(), el.ownerDocument])) {
|
|
405
|
+
for (const sheet of [...Array.from(scope.styleSheets || []), ...Array.from(scope.adoptedStyleSheets || [])]) {
|
|
406
|
+
try { visit(sheet.cssRules, null); } catch (e) { continue; }
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
return found;
|
|
410
|
+
}`;
|
|
189
411
|
/**
|
|
190
412
|
* Page-side function run on the element (`this`), see {@link RawInspect}.
|
|
191
|
-
* Arguments: tree depth,
|
|
192
|
-
* (or null).
|
|
413
|
+
* Arguments: tree depth, the property names asked for with `--props` (or
|
|
414
|
+
* null) and the `--why` property (or null).
|
|
193
415
|
*/
|
|
194
|
-
export const INSPECT_PAGE_JS = `function (depth, props) {
|
|
416
|
+
export const INSPECT_PAGE_JS = `function (depth, props, why) {
|
|
195
417
|
const el = this;
|
|
196
418
|
const view = el.ownerDocument.defaultView;
|
|
197
419
|
const tree = (${FLAT_TREE_JS})(view);
|
|
@@ -199,6 +421,11 @@ export const INSPECT_PAGE_JS = `function (depth, props) {
|
|
|
199
421
|
const describe = ${ELEMENT_DESCRIPTION_JS};
|
|
200
422
|
const s = tree.style(el);
|
|
201
423
|
const formControl = /^(input|textarea|select|button)$/.test(el.localName);
|
|
424
|
+
const textControl = (${TEXT_CONTROL_JS})(el);
|
|
425
|
+
const holder = textControl ? null : (${TEXT_HOLDER_JS})(el, tree);
|
|
426
|
+
const textChildren = tree.children(el).filter((c) => tree.rendered(c) && (c.textContent || '').trim() !== '').length;
|
|
427
|
+
const textual = textControl || Boolean(holder && (holder.style === el || (holder.share >= 0.9 && textChildren <= 1) || !tree.container(el)));
|
|
428
|
+
const textFrom = holder && holder.style !== el ? holder.style : el;
|
|
202
429
|
const content = textOf(el);
|
|
203
430
|
const parent = tree.layoutParent(el);
|
|
204
431
|
const typedMap = el.computedStyleMap ? el.computedStyleMap() : null;
|
|
@@ -209,15 +436,26 @@ export const INSPECT_PAGE_JS = `function (depth, props) {
|
|
|
209
436
|
classes: el.classList ? Array.from(el.classList) : [],
|
|
210
437
|
context: (${ELEMENT_CONTEXT_JS})(el),
|
|
211
438
|
content: content,
|
|
212
|
-
textual:
|
|
439
|
+
textual: textual,
|
|
213
440
|
formControl: formControl,
|
|
214
|
-
|
|
441
|
+
rendered: textual && textFrom !== el
|
|
442
|
+
? tree.style(textFrom).visibility !== 'hidden' && tree.rendered(tree.style(textFrom).display === 'contents' ? tree.layoutParent(textFrom) : textFrom)
|
|
443
|
+
: tree.rendered(el),
|
|
444
|
+
hasText: textControl || holder !== null,
|
|
445
|
+
replaced: el instanceof SVGElement || /^(img|video|audio|canvas|iframe|embed|object|input|textarea|select|button|meter|progress)$/.test(el.localName),
|
|
215
446
|
svg: el.namespaceURI === 'http://www.w3.org/2000/svg',
|
|
216
|
-
ownText: Array.from(el.childNodes).some((n) => n.nodeType === 3 && n.data.trim() !== ''),
|
|
217
447
|
typed: { width: typed('width'), height: typed('height') },
|
|
218
448
|
parent: parent ? describe(parent) : undefined,
|
|
219
|
-
animating: el.getAnimations ? [...new Set(el.getAnimations().filter((a) => a.playState === 'running').map((a) => a.transitionProperty || a.animationName || 'animation'))] : []
|
|
449
|
+
animating: el.getAnimations ? [...new Set(el.getAnimations().filter((a) => a.playState === 'running' && (!a.timeline || a.timeline === el.ownerDocument.timeline)).map((a) => a.transitionProperty || a.animationName || 'animation'))] : []
|
|
450
|
+
};
|
|
451
|
+
if (textFrom !== el) result.textHolder = tree.label(textFrom);
|
|
452
|
+
const clipped = (n) => {
|
|
453
|
+
const cs = tree.style(n);
|
|
454
|
+
if (cs.overflowX !== 'visible' && n.scrollWidth > n.clientWidth + 1) return true;
|
|
455
|
+
return cs.webkitLineClamp !== 'none' && cs.overflowY !== 'visible' && n.scrollHeight > n.clientHeight + 1;
|
|
220
456
|
};
|
|
457
|
+
if (textual && !textControl && (clipped(el) || (textFrom !== el && clipped(textFrom)))) result.truncated = true;
|
|
458
|
+
if ((${FAMILY_LOADED_JS})(textFrom, tree, textOf(textFrom))) result.familyLoaded = true;
|
|
221
459
|
if (/^(input|textarea)$/.test(el.localName) && el.placeholder && !el.value) {
|
|
222
460
|
result.placeholder = el.placeholder.replace(/\\s+/g, ' ').trim();
|
|
223
461
|
const placeholderStyle = tree.style(el, '::placeholder');
|
|
@@ -236,28 +474,115 @@ export const INSPECT_PAGE_JS = `function (depth, props) {
|
|
|
236
474
|
return sides.map((side) => side.slice(name.length + 1) + ' ' + s.getPropertyValue(side).trim()).join(' / ');
|
|
237
475
|
};
|
|
238
476
|
result.props = Object.fromEntries(props.filter(known).map((name) => [name, valueOf(name)]));
|
|
477
|
+
result.rootFontSize = parseFloat(tree.style(el.ownerDocument.documentElement).fontSize) || 16;
|
|
239
478
|
result.unknownProps = props.filter((name) => !known(name));
|
|
240
479
|
}
|
|
480
|
+
if (why && !why.startsWith('--')) {
|
|
481
|
+
if (!CSS.supports(why, 'inherit')) result.unknownWhy = true;
|
|
482
|
+
else {
|
|
483
|
+
const probe = el.ownerDocument.createElement('div').style;
|
|
484
|
+
probe.setProperty(why, 'inherit');
|
|
485
|
+
if (probe.length > 1) {
|
|
486
|
+
result.whyLonghands = Array.from(probe);
|
|
487
|
+
result.whyComputed = s.getPropertyValue(why).trim();
|
|
488
|
+
}
|
|
489
|
+
const inactive = (${INACTIVE_RULES_JS})(el, [why].concat(result.whyLonghands || []));
|
|
490
|
+
if (inactive.length > 0) result.whyInactive = inactive;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
241
493
|
return Object.assign(result,
|
|
242
494
|
(${PLACEMENT_JS})(el, tree),
|
|
243
|
-
(${BACKGROUNDS_JS})(el, tree, view),
|
|
495
|
+
(${BACKGROUNDS_JS})(textual ? textFrom : el, tree, view),
|
|
496
|
+
textual ? { paintRisks: (${PAINT_RISKS_JS})(textFrom, tree, holder ? holder.font : el) } : {},
|
|
244
497
|
(${TREE_JS})(el, tree, textOf, depth));
|
|
245
498
|
}`;
|
|
246
499
|
/**
|
|
247
500
|
* Page-side function run on the element (`this`) that returns another
|
|
248
501
|
* element whose styles CDP reads: `"parent"`, the parent that lays it out
|
|
249
|
-
* (`display: contents` skipped, through shadow roots and slots),
|
|
250
|
-
* `"textHolder"`,
|
|
251
|
-
* (
|
|
252
|
-
*
|
|
502
|
+
* (`display: contents` skipped, through shadow roots and slots),
|
|
503
|
+
* `"textHolder"`, the element whose styles draw most of its text
|
|
504
|
+
* ({@link TEXT_HOLDER_JS}; the element itself when that is its own text), or
|
|
505
|
+
* `"fontHolder"`, the parent of that text's nodes (for the rendered font: a
|
|
506
|
+
* slot's text nodes belong to the host's light DOM). Null when there is none.
|
|
253
507
|
*/
|
|
254
508
|
export const RELATED_NODE_JS = `function (which) {
|
|
255
509
|
const tree = (${FLAT_TREE_JS})(this.ownerDocument.defaultView);
|
|
256
510
|
if (which === 'parent') return tree.layoutParent(this);
|
|
257
|
-
const
|
|
258
|
-
|
|
259
|
-
|
|
511
|
+
const holder = (${TEXT_HOLDER_JS})(this, tree);
|
|
512
|
+
if (!holder) return null;
|
|
513
|
+
return which === 'textHolder' ? holder.style : holder.font;
|
|
514
|
+
}`;
|
|
515
|
+
/**
|
|
516
|
+
* Page-side function run on the element (`this`): for each custom property
|
|
517
|
+
* name, a rule in the page's stylesheets (same-origin, `@import`ed,
|
|
518
|
+
* constructed and the element's shadow root's) that sets it, keyframes
|
|
519
|
+
* included: the first one that matches the element or an ancestor now
|
|
520
|
+
* (across shadow roots, so `:root` matches inside a component), else the
|
|
521
|
+
* first that would match without its state (`.btn:hover` for this `.btn`),
|
|
522
|
+
* else the first one, with the `@media`/`@supports` condition it is under
|
|
523
|
+
* when that does not apply. At most 20000 rules are read; cross-origin sheets are
|
|
524
|
+
* skipped.
|
|
525
|
+
*/
|
|
526
|
+
export const VARIABLE_SETTERS_JS = `function (names) {
|
|
527
|
+
const el = this;
|
|
528
|
+
const view = el.ownerDocument.defaultView;
|
|
529
|
+
const matching = {};
|
|
530
|
+
const related = {};
|
|
531
|
+
const other = {};
|
|
532
|
+
let budget = 20000;
|
|
533
|
+
const closestAcross = (selector) => {
|
|
534
|
+
for (let n = el; n; n = n.getRootNode().host || null) {
|
|
535
|
+
if (n.closest(selector)) return true;
|
|
536
|
+
}
|
|
537
|
+
return false;
|
|
538
|
+
};
|
|
539
|
+
const matches = (selector) => {
|
|
540
|
+
try { return closestAcross(selector); } catch (e) { return null; }
|
|
541
|
+
};
|
|
542
|
+
const stateless = (selector) => selector.replace(/:(focus-visible|focus-within|focus|hover|active|visited|checked|target|open)(?![\\w-])/g, '');
|
|
543
|
+
const applies = (rule) => {
|
|
544
|
+
if (rule.media && rule.media.mediaText && !view.matchMedia(rule.media.mediaText).matches) return '@media ' + rule.media.mediaText;
|
|
545
|
+
if (typeof CSSSupportsRule !== 'undefined' && rule instanceof CSSSupportsRule && !CSS.supports(rule.conditionText)) return '@supports ' + rule.conditionText;
|
|
546
|
+
return null;
|
|
547
|
+
};
|
|
548
|
+
const record = (name, rule, keyframes, condition) => {
|
|
549
|
+
const selector = keyframes ? '@keyframes ' + keyframes : rule.selectorText || '';
|
|
550
|
+
const match = keyframes || condition ? false : matches(selector);
|
|
551
|
+
const setter = Object.assign(
|
|
552
|
+
{ selector: selector, value: rule.style.getPropertyValue(name).trim(), matches: match },
|
|
553
|
+
keyframes ? { keyframes: keyframes } : {},
|
|
554
|
+
condition ? { condition: condition } : {}
|
|
555
|
+
);
|
|
556
|
+
if (match && !matching[name]) matching[name] = setter;
|
|
557
|
+
else if (!match && !keyframes && !related[name] && matches(stateless(selector))) related[name] = setter;
|
|
558
|
+
else if (!match && !other[name]) other[name] = setter;
|
|
559
|
+
};
|
|
560
|
+
const visit = (rules, keyframes, condition) => {
|
|
561
|
+
for (const rule of Array.from(rules)) {
|
|
562
|
+
if (budget-- <= 0 || names.every((name) => matching[name])) return;
|
|
563
|
+
if (typeof CSSImportRule !== 'undefined' && rule instanceof CSSImportRule) {
|
|
564
|
+
try { if (rule.styleSheet) visit(rule.styleSheet.cssRules, keyframes, condition); } catch (e) { continue; }
|
|
565
|
+
continue;
|
|
566
|
+
}
|
|
567
|
+
if (typeof CSSKeyframesRule !== 'undefined' && rule instanceof CSSKeyframesRule) {
|
|
568
|
+
visit(rule.cssRules, rule.name, condition);
|
|
569
|
+
continue;
|
|
570
|
+
}
|
|
571
|
+
if (rule.style) {
|
|
572
|
+
for (const name of names) {
|
|
573
|
+
if (!matching[name] && Array.from(rule.style).includes(name)) record(name, rule, keyframes, condition);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
if (rule.cssRules) visit(rule.cssRules, keyframes, condition || applies(rule));
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
const sheets = [];
|
|
580
|
+
for (const scope of new Set([el.getRootNode(), el.ownerDocument])) {
|
|
581
|
+
sheets.push(...Array.from(scope.styleSheets || []), ...Array.from(scope.adoptedStyleSheets || []));
|
|
582
|
+
}
|
|
583
|
+
for (const sheet of sheets) {
|
|
584
|
+
try { visit(sheet.cssRules, null, null); } catch (e) { continue; }
|
|
260
585
|
}
|
|
261
|
-
return
|
|
586
|
+
return Object.assign({}, other, related, matching);
|
|
262
587
|
}`;
|
|
263
588
|
//# sourceMappingURL=inspectScripts.js.map
|
|
@@ -40,6 +40,9 @@ function toNode(raw) {
|
|
|
40
40
|
h: Math.round(raw.h),
|
|
41
41
|
...(layout && { layout }),
|
|
42
42
|
...(raw.text && { text: rowText(raw.text) }),
|
|
43
|
+
...(raw.display === 'contents' && { contents: true }),
|
|
44
|
+
...(raw.via && { via: raw.via }),
|
|
45
|
+
...(raw.shadow && { shadow: true }),
|
|
43
46
|
...(children && children.length > 0 && { children }),
|
|
44
47
|
...(raw.childCount && { childCount: raw.childCount }),
|
|
45
48
|
...(raw.hidden && { hiddenChildren: raw.hidden }),
|
|
@@ -50,10 +53,15 @@ function toNode(raw) {
|
|
|
50
53
|
*
|
|
51
54
|
* @param a - Row
|
|
52
55
|
* @param b - Row
|
|
53
|
-
* @returns True for the same label, size and
|
|
56
|
+
* @returns True for the same label, size, layout and path (slot, shadow root)
|
|
54
57
|
*/
|
|
55
58
|
function identical(a, b) {
|
|
56
|
-
return a.element === b.element &&
|
|
59
|
+
return (a.element === b.element &&
|
|
60
|
+
a.w === b.w &&
|
|
61
|
+
a.h === b.h &&
|
|
62
|
+
a.layout === b.layout &&
|
|
63
|
+
a.via === b.via &&
|
|
64
|
+
a.shadow === b.shadow);
|
|
57
65
|
}
|
|
58
66
|
/**
|
|
59
67
|
* Group runs of identical siblings into one row with a count. A group keeps
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import type { InspectWhy } from '../../ipc/protocol/inspectTypes.js';
|
|
9
9
|
import { type Resolution } from './inspectCascade.js';
|
|
10
10
|
import type { CascadeInput, PropertyGroup } from './inspectCascadeModel.js';
|
|
11
|
+
import type { CssHint } from './inspectHints.js';
|
|
11
12
|
/**
|
|
12
13
|
* `--why` for a property.
|
|
13
14
|
*
|
|
@@ -16,5 +17,5 @@ import type { CascadeInput, PropertyGroup } from './inspectCascadeModel.js';
|
|
|
16
17
|
* @param input - Matched rules, computed style and the label function
|
|
17
18
|
* @returns One entry, or one per longhand when they differ
|
|
18
19
|
*/
|
|
19
|
-
export declare function buildWhy(group: PropertyGroup, cascade: Map<string, Resolution>, input: CascadeInput): InspectWhy[];
|
|
20
|
+
export declare function buildWhy(group: PropertyGroup, cascade: Map<string, Resolution>, input: CascadeInput, hints?: readonly CssHint[]): InspectWhy[];
|
|
20
21
|
//# sourceMappingURL=inspectWhyModel.d.ts.map
|