browser-debugger-cli 0.9.0 → 0.10.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.
Files changed (133) hide show
  1. package/README.md +4 -1
  2. package/dist/commands/dom/a11y.js +2 -1
  3. package/dist/commands/dom/formInteraction.js +56 -25
  4. package/dist/commands/dom/helpers/keyAttributes.d.ts +20 -0
  5. package/dist/commands/dom/helpers/keyAttributes.js +54 -0
  6. package/dist/commands/dom/helpers/query.d.ts +1 -1
  7. package/dist/commands/dom/helpers/query.js +66 -19
  8. package/dist/commands/dom/helpers/runElementCommand.js +4 -3
  9. package/dist/commands/dom/helpers/screenshot.js +85 -12
  10. package/dist/commands/dom/index.d.ts +1 -0
  11. package/dist/commands/dom/index.js +8 -3
  12. package/dist/commands/dom/inspect.d.ts +15 -0
  13. package/dist/commands/dom/inspect.js +82 -0
  14. package/dist/commands/dom/layout.js +2 -2
  15. package/dist/commands/dom/listeners.js +2 -2
  16. package/dist/commands/dom/semanticUtils.d.ts +14 -1
  17. package/dist/commands/dom/semanticUtils.js +44 -3
  18. package/dist/commands/network/list.js +13 -2
  19. package/dist/commands/optionBehaviors.js +48 -6
  20. package/dist/commands/page.d.ts +1 -1
  21. package/dist/commands/page.js +62 -3
  22. package/dist/commands/shared/commonOptions.d.ts +4 -0
  23. package/dist/commands/shared/commonOptions.js +9 -0
  24. package/dist/commands/shared/optionTypes.d.ts +21 -0
  25. package/dist/commands/shared/startHelpers.d.ts +66 -0
  26. package/dist/commands/shared/startHelpers.js +91 -10
  27. package/dist/commands/shared/validation.d.ts +11 -0
  28. package/dist/commands/shared/validation.js +16 -0
  29. package/dist/daemon/launcher.d.ts +8 -1
  30. package/dist/daemon/launcher.js +3 -1
  31. package/dist/daemon/session/Session.d.ts +7 -0
  32. package/dist/daemon/session/Session.js +23 -1
  33. package/dist/daemon/session/commandRegistry.d.ts +14 -1
  34. package/dist/daemon/session/commandRegistry.js +65 -9
  35. package/dist/daemon/session/interactions.d.ts +18 -5
  36. package/dist/daemon/session/interactions.js +22 -12
  37. package/dist/daemon.js +3565 -329
  38. package/dist/errors/messages.d.ts +69 -0
  39. package/dist/errors/messages.js +102 -1
  40. package/dist/index.js +2416 -1320
  41. package/dist/ipc/client.d.ts +9 -0
  42. package/dist/ipc/client.js +13 -0
  43. package/dist/ipc/protocol/commands.d.ts +56 -1
  44. package/dist/ipc/protocol/commands.js +2 -0
  45. package/dist/ipc/protocol/domTypes.d.ts +35 -2
  46. package/dist/ipc/protocol/inspectTypes.d.ts +388 -0
  47. package/dist/ipc/protocol/inspectTypes.js +10 -0
  48. package/dist/runtime/dom/actionEffects.d.ts +94 -15
  49. package/dist/runtime/dom/actionEffects.js +173 -27
  50. package/dist/runtime/dom/actionEffectsScripts.d.ts +52 -14
  51. package/dist/runtime/dom/actionEffectsScripts.js +224 -32
  52. package/dist/runtime/dom/elementInfo.d.ts +26 -0
  53. package/dist/runtime/dom/elementInfo.js +65 -0
  54. package/dist/runtime/dom/eventListeners.js +14 -4
  55. package/dist/runtime/dom/formFillHelpers/fill.d.ts +3 -4
  56. package/dist/runtime/dom/formFillHelpers/fill.js +77 -28
  57. package/dist/runtime/dom/frameSelection.d.ts +11 -0
  58. package/dist/runtime/dom/frameSelection.js +20 -1
  59. package/dist/runtime/dom/frames.d.ts +38 -5
  60. package/dist/runtime/dom/frames.js +136 -21
  61. package/dist/runtime/dom/inspect.d.ts +28 -0
  62. package/dist/runtime/dom/inspect.js +557 -0
  63. package/dist/runtime/dom/inspectAllStyles.d.ts +62 -0
  64. package/dist/runtime/dom/inspectAllStyles.js +385 -0
  65. package/dist/runtime/dom/inspectCascade.d.ts +94 -0
  66. package/dist/runtime/dom/inspectCascade.js +371 -0
  67. package/dist/runtime/dom/inspectCascadeModel.d.ts +39 -0
  68. package/dist/runtime/dom/inspectCascadeModel.js +232 -0
  69. package/dist/runtime/dom/inspectHints.d.ts +62 -0
  70. package/dist/runtime/dom/inspectHints.js +305 -0
  71. package/dist/runtime/dom/inspectLayoutModel.d.ts +87 -0
  72. package/dist/runtime/dom/inspectLayoutModel.js +346 -0
  73. package/dist/runtime/dom/inspectModel.d.ts +74 -0
  74. package/dist/runtime/dom/inspectModel.js +184 -0
  75. package/dist/runtime/dom/inspectPaintModel.d.ts +157 -0
  76. package/dist/runtime/dom/inspectPaintModel.js +461 -0
  77. package/dist/runtime/dom/inspectRules.d.ts +37 -0
  78. package/dist/runtime/dom/inspectRules.js +101 -0
  79. package/dist/runtime/dom/inspectScripts.d.ts +132 -0
  80. package/dist/runtime/dom/inspectScripts.js +263 -0
  81. package/dist/runtime/dom/inspectTree.d.ts +40 -0
  82. package/dist/runtime/dom/inspectTree.js +134 -0
  83. package/dist/runtime/dom/inspectVariables.d.ts +33 -0
  84. package/dist/runtime/dom/inspectVariables.js +94 -0
  85. package/dist/runtime/dom/inspectWhyModel.d.ts +20 -0
  86. package/dist/runtime/dom/inspectWhyModel.js +134 -0
  87. package/dist/runtime/dom/layout.d.ts +5 -1
  88. package/dist/runtime/dom/layout.js +10 -3
  89. package/dist/runtime/dom/listenerPageScripts.d.ts +11 -5
  90. package/dist/runtime/dom/listenerPageScripts.js +95 -9
  91. package/dist/runtime/dom/listenerSummary.d.ts +4 -0
  92. package/dist/runtime/dom/listenerSummary.js +26 -9
  93. package/dist/runtime/dom/reactEventHelpers.d.ts +5 -0
  94. package/dist/runtime/dom/reactEventHelpers.js +12 -4
  95. package/dist/runtime/page/emulation.d.ts +20 -0
  96. package/dist/runtime/page/emulation.js +37 -0
  97. package/dist/telemetry/a11y.d.ts +10 -0
  98. package/dist/telemetry/a11y.js +78 -1
  99. package/dist/telemetry/console.d.ts +1 -0
  100. package/dist/telemetry/console.js +100 -5
  101. package/dist/telemetry/network.js +3 -1
  102. package/dist/types.d.ts +32 -0
  103. package/dist/ui/formatters/details.d.ts +8 -0
  104. package/dist/ui/formatters/details.js +59 -3
  105. package/dist/ui/formatters/dom.d.ts +2 -1
  106. package/dist/ui/formatters/dom.js +25 -9
  107. package/dist/ui/formatters/inspect.d.ts +39 -0
  108. package/dist/ui/formatters/inspect.js +596 -0
  109. package/dist/ui/formatters/keyAttributes.d.ts +19 -0
  110. package/dist/ui/formatters/keyAttributes.js +84 -0
  111. package/dist/ui/formatters/layout.js +2 -2
  112. package/dist/ui/formatters/networkHeaders.d.ts +13 -0
  113. package/dist/ui/formatters/networkHeaders.js +23 -3
  114. package/dist/ui/formatters/networkList.d.ts +29 -1
  115. package/dist/ui/formatters/networkList.js +86 -20
  116. package/dist/ui/formatters/status.js +1 -1
  117. package/dist/ui/formatting.d.ts +9 -0
  118. package/dist/ui/formatting.js +6 -3
  119. package/dist/ui/messages/commands.d.ts +123 -7
  120. package/dist/ui/messages/commands.js +181 -10
  121. package/dist/ui/messages/networkMessages.d.ts +14 -0
  122. package/dist/ui/messages/networkMessages.js +18 -0
  123. package/dist/ui/messages/session.d.ts +14 -0
  124. package/dist/ui/messages/session.js +20 -0
  125. package/dist/utils/async.d.ts +9 -0
  126. package/dist/utils/async.js +17 -0
  127. package/dist/utils/color.d.ts +84 -0
  128. package/dist/utils/color.js +376 -0
  129. package/dist/utils/cssValues.d.ts +109 -0
  130. package/dist/utils/cssValues.js +236 -0
  131. package/dist/utils/selectorFilters.d.ts +12 -0
  132. package/dist/utils/selectorFilters.js +29 -0
  133. package/package.json +1 -1
@@ -0,0 +1,134 @@
1
+ /**
2
+ * The child tree of `bdg dom inspect`: the page-side walk's children
3
+ * ({@link RawTreeNode}) with sizes rounded, consecutive identical siblings
4
+ * (same label, size and layout) grouped into one row with a count
5
+ * (`li.card ×33 266x107`), and cut to a number of rows in document order.
6
+ */
7
+ import { containerKind } from './inspectLayoutModel.js';
8
+ /** Default depth of the tree */
9
+ export const DEFAULT_TREE_DEPTH = 2;
10
+ /** Default number of rows */
11
+ export const DEFAULT_TREE_LIMIT = 20;
12
+ /** Characters of text kept per row */
13
+ const ROW_TEXT_LENGTH = 30;
14
+ /**
15
+ * Cut a row's text on a whole character.
16
+ *
17
+ * @param text - Text
18
+ * @returns At most {@link ROW_TEXT_LENGTH} characters, with `…` when cut
19
+ */
20
+ export function rowText(text) {
21
+ const characters = Array.from(text);
22
+ return characters.length > ROW_TEXT_LENGTH
23
+ ? `${characters.slice(0, ROW_TEXT_LENGTH).join('').trimEnd()}…`
24
+ : text;
25
+ }
26
+ /**
27
+ * One child as a tree row (its children converted too).
28
+ *
29
+ * @param raw - Child from the page-side walk
30
+ * @returns Tree row
31
+ */
32
+ function toNode(raw) {
33
+ const layout = containerKind(raw.display);
34
+ const children = raw.children ? groupSiblings(raw.children.map(toNode)) : undefined;
35
+ return {
36
+ element: raw.label,
37
+ x: Math.round(raw.x * 10) / 10,
38
+ y: Math.round(raw.y * 10) / 10,
39
+ w: Math.round(raw.w),
40
+ h: Math.round(raw.h),
41
+ ...(layout && { layout }),
42
+ ...(raw.text && { text: rowText(raw.text) }),
43
+ ...(children && children.length > 0 && { children }),
44
+ ...(raw.childCount && { childCount: raw.childCount }),
45
+ ...(raw.hidden && { hiddenChildren: raw.hidden }),
46
+ };
47
+ }
48
+ /**
49
+ * Whether two siblings look the same in the tree.
50
+ *
51
+ * @param a - Row
52
+ * @param b - Row
53
+ * @returns True for the same label, size and layout
54
+ */
55
+ function identical(a, b) {
56
+ return a.element === b.element && a.w === b.w && a.h === b.h && a.layout === b.layout;
57
+ }
58
+ /**
59
+ * Group runs of identical siblings into one row with a count. A group keeps
60
+ * its text only when every member has the same, and lists no children.
61
+ *
62
+ * @param nodes - Sibling rows
63
+ * @returns Rows with runs grouped
64
+ */
65
+ export function groupSiblings(nodes) {
66
+ const grouped = [];
67
+ for (let i = 0; i < nodes.length;) {
68
+ const first = nodes[i];
69
+ let run = 1;
70
+ while (i + run < nodes.length && identical(first, nodes[i + run]))
71
+ run++;
72
+ if (run === 1) {
73
+ grouped.push(first);
74
+ }
75
+ else {
76
+ const members = nodes.slice(i, i + run);
77
+ const sameText = members.every((member) => member.text === first.text);
78
+ const { children: _children, childCount: _count, hiddenChildren: _hidden, text, ...row } = first;
79
+ grouped.push({ ...row, ...(sameText && text && { text }), count: run });
80
+ }
81
+ i += run;
82
+ }
83
+ return grouped;
84
+ }
85
+ /**
86
+ * Rows a node takes in the tree (itself and its listed descendants).
87
+ *
88
+ * @param node - Row
89
+ * @returns Row count
90
+ */
91
+ function rowCount(node) {
92
+ return 1 + (node.children ?? []).reduce((sum, child) => sum + rowCount(child), 0);
93
+ }
94
+ /**
95
+ * Keep the first rows in document order.
96
+ *
97
+ * @param nodes - Rows
98
+ * @param budget - Rows still allowed (decremented)
99
+ * @returns Kept rows and the number left out
100
+ */
101
+ function prune(nodes, budget) {
102
+ const kept = [];
103
+ let dropped = 0;
104
+ for (const node of nodes) {
105
+ if (budget.left <= 0) {
106
+ dropped += rowCount(node);
107
+ continue;
108
+ }
109
+ budget.left--;
110
+ if (!node.children) {
111
+ kept.push(node);
112
+ continue;
113
+ }
114
+ const sub = prune(node.children, budget);
115
+ dropped += sub.dropped;
116
+ const { children: _children, ...row } = node;
117
+ kept.push(sub.kept.length > 0 ? { ...row, children: sub.kept } : row);
118
+ }
119
+ return { kept, dropped };
120
+ }
121
+ /**
122
+ * The child tree from the page-side walk: grouped, then cut to `limit` rows.
123
+ *
124
+ * @param raw - Children from the walk
125
+ * @param limit - Rows shown at most
126
+ * @param skipped - Children the walk did not reach (counted as left out)
127
+ * @returns Rows and the number of rows left out
128
+ */
129
+ export function buildTree(raw, limit, skipped = 0) {
130
+ const rows = groupSiblings(raw.map(toNode));
131
+ const { kept, dropped } = prune(rows, { left: limit });
132
+ return { children: kept, moreRows: dropped + skipped };
133
+ }
134
+ //# sourceMappingURL=inspectTree.js.map
@@ -0,0 +1,33 @@
1
+ /**
2
+ * `var()` in declared values: which custom properties a value uses, which of
3
+ * them are not set, and the value with them substituted. A fallback counts
4
+ * only when its custom property is not set.
5
+ */
6
+ import type { StyleMap } from './inspectLayoutModel.js';
7
+ /**
8
+ * The custom properties a value uses that are not set and have no fallback:
9
+ * the fallback of an unset one is used instead (and checked in turn).
10
+ *
11
+ * @param value - Value as written
12
+ * @param style - Computed styles (custom properties included)
13
+ * @returns Names of the unset custom properties
14
+ */
15
+ export declare function unsetVariables(value: string, style: StyleMap): string[];
16
+ /**
17
+ * The custom properties a value takes its value from (fallbacks only when
18
+ * used).
19
+ *
20
+ * @param value - Value as written
21
+ * @param style - Computed styles
22
+ * @returns Names
23
+ */
24
+ export declare function usedVariables(value: string, style: StyleMap): string[];
25
+ /**
26
+ * A value with its custom properties substituted.
27
+ *
28
+ * @param value - Value as written
29
+ * @param style - Computed styles
30
+ * @returns Substituted value (unset ones without fallback left as written)
31
+ */
32
+ export declare function substituteVariables(value: string, style: StyleMap): string;
33
+ //# sourceMappingURL=inspectVariables.d.ts.map
@@ -0,0 +1,94 @@
1
+ /**
2
+ * `var()` in declared values: which custom properties a value uses, which of
3
+ * them are not set, and the value with them substituted. A fallback counts
4
+ * only when its custom property is not set.
5
+ */
6
+ /**
7
+ * The top-level `var()` calls of a value.
8
+ *
9
+ * @param value - Value as written
10
+ * @returns Calls in order (nested ones are inside a fallback)
11
+ */
12
+ function varCalls(value) {
13
+ const calls = [];
14
+ for (let start = value.indexOf('var('); start !== -1; start = value.indexOf('var(', start + 1)) {
15
+ const end = closingParen(value, start + 3);
16
+ const [name = '', ...fallback] = value.slice(start + 4, end).split(',');
17
+ calls.push({
18
+ start,
19
+ end,
20
+ name: name.trim(),
21
+ ...(fallback.length > 0 && { fallback: fallback.join(',').trim() }),
22
+ });
23
+ start = end;
24
+ }
25
+ return calls;
26
+ }
27
+ /**
28
+ * Index of the parenthesis that closes the one at `open`.
29
+ *
30
+ * @param text - Text
31
+ * @param open - Index of `(`
32
+ * @returns Index of the matching `)` (the end of the text when unbalanced)
33
+ */
34
+ function closingParen(text, open) {
35
+ let depth = 0;
36
+ for (let i = open; i < text.length; i++) {
37
+ if (text[i] === '(')
38
+ depth++;
39
+ if (text[i] === ')' && --depth === 0)
40
+ return i;
41
+ }
42
+ return text.length;
43
+ }
44
+ /**
45
+ * The custom properties a value uses that are not set and have no fallback:
46
+ * the fallback of an unset one is used instead (and checked in turn).
47
+ *
48
+ * @param value - Value as written
49
+ * @param style - Computed styles (custom properties included)
50
+ * @returns Names of the unset custom properties
51
+ */
52
+ export function unsetVariables(value, style) {
53
+ return varCalls(value).flatMap((call) => {
54
+ if (style[call.name] !== undefined)
55
+ return [];
56
+ return call.fallback !== undefined ? unsetVariables(call.fallback, style) : [call.name];
57
+ });
58
+ }
59
+ /**
60
+ * The custom properties a value takes its value from (fallbacks only when
61
+ * used).
62
+ *
63
+ * @param value - Value as written
64
+ * @param style - Computed styles
65
+ * @returns Names
66
+ */
67
+ export function usedVariables(value, style) {
68
+ return varCalls(value).flatMap((call) => style[call.name] !== undefined || call.fallback === undefined
69
+ ? [call.name]
70
+ : usedVariables(call.fallback, style));
71
+ }
72
+ /**
73
+ * A value with its custom properties substituted.
74
+ *
75
+ * @param value - Value as written
76
+ * @param style - Computed styles
77
+ * @returns Substituted value (unset ones without fallback left as written)
78
+ */
79
+ export function substituteVariables(value, style) {
80
+ let result = '';
81
+ let last = 0;
82
+ for (const call of varCalls(value)) {
83
+ const set = style[call.name];
84
+ const replacement = set !== undefined
85
+ ? set.trim()
86
+ : call.fallback !== undefined
87
+ ? substituteVariables(call.fallback, style)
88
+ : value.slice(call.start, call.end + 1);
89
+ result += value.slice(last, call.start) + replacement;
90
+ last = call.end + 1;
91
+ }
92
+ return result + value.slice(last);
93
+ }
94
+ //# sourceMappingURL=inspectVariables.js.map
@@ -0,0 +1,20 @@
1
+ /**
2
+ * `dom inspect --why <property>`: every declaration of a property on the
3
+ * element, the winner first, with `var()` values substituted and where the
4
+ * winner's custom properties are set. A shorthand gives one answer when one
5
+ * declaration sets all its longhands (listing every declaration it beats on
6
+ * any of them), else one per longhand.
7
+ */
8
+ import type { InspectWhy } from '../../ipc/protocol/inspectTypes.js';
9
+ import { type Resolution } from './inspectCascade.js';
10
+ import type { CascadeInput, PropertyGroup } from './inspectCascadeModel.js';
11
+ /**
12
+ * `--why` for a property.
13
+ *
14
+ * @param group - The property and its longhands
15
+ * @param cascade - Resolved properties
16
+ * @param input - Matched rules, computed style and the label function
17
+ * @returns One entry, or one per longhand when they differ
18
+ */
19
+ export declare function buildWhy(group: PropertyGroup, cascade: Map<string, Resolution>, input: CascadeInput): InspectWhy[];
20
+ //# sourceMappingURL=inspectWhyModel.d.ts.map
@@ -0,0 +1,134 @@
1
+ /**
2
+ * `dom inspect --why <property>`: every declaration of a property on the
3
+ * element, the winner first, with `var()` values substituted and where the
4
+ * winner's custom properties are set. A shorthand gives one answer when one
5
+ * declaration sets all its longhands (listing every declaration it beats on
6
+ * any of them), else one per longhand.
7
+ */
8
+ import { normalizeProperty } from './inspectAllStyles.js';
9
+ import { resolveCascade } from './inspectCascade.js';
10
+ import { substituteVariables, unsetVariables, usedVariables, } from './inspectVariables.js';
11
+ /**
12
+ * `--why` for a property.
13
+ *
14
+ * @param group - The property and its longhands
15
+ * @param cascade - Resolved properties
16
+ * @param input - Matched rules, computed style and the label function
17
+ * @returns One entry, or one per longhand when they differ
18
+ */
19
+ export function buildWhy(group, cascade, input) {
20
+ const whys = group.longhands.map((longhand) => whyOf(longhand, cascade.get(longhand), input));
21
+ const [first] = whys;
22
+ if (!first || group.shorthand === undefined)
23
+ return whys;
24
+ const winner = JSON.stringify(first.chain[0]);
25
+ if (!whys.every((why) => JSON.stringify(why.chain[0]) === winner))
26
+ return whys;
27
+ const values = whys.map((why) => why.computed);
28
+ const computed = values.every((value) => value === first.computed)
29
+ ? first.computed
30
+ : values.join(' ');
31
+ const beaten = whys.flatMap((why) => why.chain.slice(1));
32
+ const overridden = [...new Map(beaten.map((entry) => [JSON.stringify(entry), entry])).values()];
33
+ const chain = [...first.chain.slice(0, 1), ...overridden];
34
+ return [{ ...first, property: group.shorthand, computed, chain }];
35
+ }
36
+ /**
37
+ * `--why` for one longhand.
38
+ *
39
+ * @param property - Longhand
40
+ * @param resolution - Its resolution
41
+ * @param input - Matched rules, computed style and the label function
42
+ * @returns Why
43
+ */
44
+ function whyOf(property, resolution, input) {
45
+ const winner = resolution?.winner;
46
+ const chain = [
47
+ ...(winner
48
+ ? [whyEntry(winner, winner.ancestor !== undefined ? 'inherited' : 'applied', input)]
49
+ : []),
50
+ ...(resolution?.overridden ?? []).map((d) => whyEntry(d, 'overridden', input)),
51
+ ];
52
+ const variables = winner ? variableSources(winner, input) : [];
53
+ return {
54
+ property,
55
+ computed: normalizeProperty(property, input.style[property] ?? ''),
56
+ chain,
57
+ ...(variables.length > 0 && { variables }),
58
+ };
59
+ }
60
+ /**
61
+ * One declaration in a `--why` chain: a shorthand's as written, `var()`
62
+ * substituted (or the custom properties that are not set), with the
63
+ * selector's specificity.
64
+ *
65
+ * @param declaration - Declaration
66
+ * @param status - Applied, overridden or inherited
67
+ * @param input - Computed style and the label function
68
+ * @returns Entry
69
+ */
70
+ function whyEntry(declaration, status, input) {
71
+ const value = declaration.via ? (declaration.written ?? declaration.value) : declaration.value;
72
+ const unset = value.includes('var(') ? unsetVariables(value, input.style) : [];
73
+ const resolved = value.includes('var(') ? resolvedValue(declaration, value, input) : value;
74
+ const { specificity, layer, condition } = declaration.source;
75
+ return {
76
+ value,
77
+ ...(declaration.via && { via: declaration.via }),
78
+ ...(unset.length > 0 ? { unset } : resolved !== value && { resolved }),
79
+ source: input.label(declaration),
80
+ ...(specificity && { specificity }),
81
+ status,
82
+ ...(declaration.important && { important: true }),
83
+ ...(layer && { layer }),
84
+ ...(condition && { condition }),
85
+ };
86
+ }
87
+ /** How many levels of custom properties set from others are followed */
88
+ const MAX_VARIABLE_DEPTH = 5;
89
+ /**
90
+ * Where the custom properties the winning value uses are set, and those
91
+ * their values use in turn (`--bs-btn-border-width: var(--bs-border-width)`).
92
+ *
93
+ * @param winner - Winning declaration
94
+ * @param input - Matched rules, computed style and the label function
95
+ * @returns The variables with a declaration (on the element or an ancestor)
96
+ */
97
+ function variableSources(winner, input) {
98
+ const found = [];
99
+ let names = usedVariables(winner.written ?? winner.value, input.style);
100
+ for (let depth = 0; names.length > 0 && depth < MAX_VARIABLE_DEPTH; depth++) {
101
+ const cascade = resolveCascade(input.matched, names);
102
+ const next = names.flatMap((name) => {
103
+ const declaration = cascade.get(name)?.winner;
104
+ if (!declaration || found.some((variable) => variable.name === name))
105
+ return [];
106
+ found.push({
107
+ name,
108
+ value: declaration.value,
109
+ source: input.label(declaration),
110
+ ...(declaration.ancestor !== undefined && { inherited: declaration.ancestor }),
111
+ });
112
+ return usedVariables(declaration.value, input.style);
113
+ });
114
+ names = [...new Set(next)];
115
+ }
116
+ return found;
117
+ }
118
+ /**
119
+ * A `var()` value substituted; a color longhand's in hex (Tailwind's
120
+ * `lab()` and `oklch()` tokens are unreadable).
121
+ *
122
+ * @param declaration - Declaration
123
+ * @param value - Its value as shown
124
+ * @param input - Computed style
125
+ * @returns Substituted value
126
+ */
127
+ function resolvedValue(declaration, value, input) {
128
+ const substituted = substituteVariables(value, input.style);
129
+ const isColor = declaration.via === undefined && declaration.property.endsWith('color');
130
+ return isColor && !substituted.includes('var(')
131
+ ? normalizeProperty(declaration.property, substituted)
132
+ : substituted;
133
+ }
134
+ //# sourceMappingURL=inspectWhyModel.js.map
@@ -30,7 +30,11 @@ export interface RawElementLayout {
30
30
  text: string;
31
31
  context: string;
32
32
  geometry: ElementGeometry;
33
- coveredBy: string | null;
33
+ /** The element on top at its visible center, and whether it paints nothing there */
34
+ cover: {
35
+ element: string;
36
+ transparent: boolean;
37
+ } | null;
34
38
  computed: LayoutComputedStyle;
35
39
  }
36
40
  /** What {@link LAYOUT_JS} returns. */
@@ -150,6 +150,12 @@ const LAYOUT_JS = `function (found, index, limit) {
150
150
  }
151
151
  return best;
152
152
  };
153
+ const paintsNothing = (node) => {
154
+ const s = node.ownerDocument.defaultView.getComputedStyle(node);
155
+ const clearColor = (c) => c === 'transparent' || /^rgba\\(.*,\\s*0\\)$/.test(c);
156
+ const ownText = Array.from(node.childNodes).some((n) => n.nodeType === 3 && n.data.trim() !== '');
157
+ return clearColor(s.backgroundColor) && s.backgroundImage === 'none' && s.boxShadow === 'none' && !ownText;
158
+ };
153
159
  const hitTestable = (node) => node.ownerDocument.defaultView.getComputedStyle(node).pointerEvents !== 'none';
154
160
  const coveredBy = (el, g) => {
155
161
  const center = hitTestable(el) && visibleCenter(el, g);
@@ -164,7 +170,7 @@ const LAYOUT_JS = `function (found, index, limit) {
164
170
  y += offset.y;
165
171
  cover = coverAt(view.frameElement, x, y);
166
172
  }
167
- return cover ? describe(cover) : null;
173
+ return cover ? { element: describe(cover), transparent: paintsNothing(cover) } : null;
168
174
  };
169
175
  const elements = picked.map(([i, el]) => {
170
176
  const style = el.ownerDocument.defaultView.getComputedStyle(el);
@@ -176,7 +182,7 @@ const LAYOUT_JS = `function (found, index, limit) {
176
182
  text: String(textOf(el)).replace(/\\s+/g, ' ').trim().slice(0, 1000),
177
183
  context: contextOf(el),
178
184
  geometry: geometry,
179
- coveredBy: geometry.hidden ? null : coveredBy(el, geometry),
185
+ cover: geometry.hidden ? null : coveredBy(el, geometry),
180
186
  computed: { display: style.display, visibility: style.visibility, position: style.position, opacity: style.opacity, zIndex: style.zIndex }
181
187
  };
182
188
  });
@@ -324,7 +330,8 @@ function elementLayout(raw, page) {
324
330
  },
325
331
  viewport: { x: Math.round(rect.x), y: Math.round(rect.y) },
326
332
  ...placement,
327
- ...(inView && raw.coveredBy && { coveredBy: raw.coveredBy }),
333
+ ...(inView && raw.cover && { coveredBy: raw.cover.element }),
334
+ ...(inView && raw.cover?.transparent && { coverTransparent: true }),
328
335
  ...(raw.geometry.invisible && { invisible: raw.geometry.invisible }),
329
336
  ...(raw.geometry.inert && { inert: true }),
330
337
  computed: raw.computed,
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * Page-side functions of `bdg dom listeners`: what only the page can tell
3
3
  * about an element's listeners (handler names and identities, React root
4
- * containers, the handlers behind jQuery's dispatcher, React's `on…` props
5
- * of the element and its ancestors, the element's iframe).
4
+ * containers, the handlers behind jQuery's dispatcher and Preact's event
5
+ * proxy, React's `on…` props of the element and its ancestors, the
6
+ * element's iframe).
6
7
  *
7
8
  * Each piece is a function expression in a string, combined into
8
9
  * {@link ELEMENT_INFO_JS}; every lookup of page globals is guarded, so a
@@ -14,17 +15,18 @@ export declare const MAX_JQUERY_HANDLERS = 50;
14
15
  export declare const MAX_REACT_HANDLERS = 50;
15
16
  /**
16
17
  * Page function, called on the element with the listeners (position in the
17
- * chain and event type of each), the requested event types (null for all;
18
+ * chain, event type and capture flag of each), the requested event types (null for all;
18
19
  * React props are filtered before the limit), then the chain's objects, then each
19
20
  * listener's handler, then the function each handler calls (a bound
20
21
  * function's target, else null).
21
22
  *
22
- * Returns `[info, ...jQueryHandlers, ...reactHandlers]`. `info` has the
23
+ * Returns `[info, ...jQueryAndPreactHandlers, ...reactHandlers]`. `info` has the
23
24
  * iframe element holding the element's document (`frame`), a framework
24
25
  * label per chain entry (`roots`), per listener the handler's name, the
25
26
  * identity and name of the function it calls (equal identities are the same
26
27
  * function object) and, for jQuery's dispatcher, the jQuery handlers that
27
- * run for the element; then the React `on…` props that run for the
28
+ * run for the element, for Preact's event proxy the handler Preact runs
29
+ * (`preact`); then the React `on…` props that run for the
28
30
  * element's events (`react`, see {@link REACT_HANDLERS_JS}). Their functions follow `info` in the same order.
29
31
  * After {@link MAX_JQUERY_HANDLERS}, dispatchers stay unresolved and are
30
32
  * counted in `jquerySkipped`; after {@link MAX_REACT_HANDLERS}, React props
@@ -44,6 +46,10 @@ export interface ElementInfo {
44
46
  selector: string | null;
45
47
  name: string;
46
48
  }>;
49
+ /** The handler Preact runs from this listener (set when it is Preact's event proxy) */
50
+ preact?: {
51
+ name: string;
52
+ };
47
53
  }>;
48
54
  /** jQuery handlers left unresolved (over the limit) */
49
55
  jquerySkipped: number;
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * Page-side functions of `bdg dom listeners`: what only the page can tell
3
3
  * about an element's listeners (handler names and identities, React root
4
- * containers, the handlers behind jQuery's dispatcher, React's `on…` props
5
- * of the element and its ancestors, the element's iframe).
4
+ * containers, the handlers behind jQuery's dispatcher and Preact's event
5
+ * proxy, React's `on…` props of the element and its ancestors, the
6
+ * element's iframe).
6
7
  *
7
8
  * Each piece is a function expression in a string, combined into
8
9
  * {@link ELEMENT_INFO_JS}; every lookup of page globals is guarded, so a
@@ -95,12 +96,30 @@ const REACT_PROPS_JS = `(node) => {
95
96
  }`;
96
97
  /** Most fibers walked up from the element (guards against cycles) */
97
98
  const MAX_FIBER_STEPS = 1000;
99
+ /**
100
+ * `(fiber) => fiber | null`: for a React root's fiber, the fiber of the
101
+ * nearest React-rendered DOM element at or above its container (where
102
+ * React continues for nested roots: an outer root's handlers run for
103
+ * events in an inner one); null for other fibers and for top-level roots.
104
+ */
105
+ const OUTER_ROOT_FIBER_JS = `(fiber) => {
106
+ const own = ${OWN_VALUE_JS};
107
+ const container = own(own(fiber, 'stateNode'), 'containerInfo');
108
+ try {
109
+ for (let node = container; node && typeof node === 'object'; node = node.parentNode) {
110
+ const outer = (${REACT_FIBER_JS})(node);
111
+ if (outer) return outer;
112
+ }
113
+ } catch (e) { return null; }
114
+ return null;
115
+ }`;
98
116
  /**
99
117
  * `(element, nodes) => Array<{ node, position }>`: the DOM elements whose
100
118
  * React props run for events on the element, nearest first. With a fiber,
101
119
  * the host components on its `.return` path (so a portal's React parents
102
- * count and DOM parents outside the React path don't); `position` is the
103
- * node's place in the chain, null outside it. Without one, the chain's elements.
120
+ * count and DOM parents outside the React path don't), continued from a
121
+ * nested root's container into the outer root; `position` is the node's
122
+ * place in the chain, null outside it. Without one, the chain's elements.
104
123
  */
105
124
  const REACT_HOSTS_JS = `(element, nodes) => {
106
125
  const own = ${OWN_VALUE_JS};
@@ -115,10 +134,70 @@ const REACT_HOSTS_JS = `(element, nodes) => {
115
134
  const position = nodes.indexOf(node);
116
135
  hosts.push({ node, position: position === -1 ? null : position });
117
136
  }
118
- fiber = own(fiber, 'return');
137
+ fiber = own(fiber, 'return') || (${OUTER_ROOT_FIBER_JS})(fiber);
119
138
  }
120
139
  return hosts;
121
140
  }`;
141
+ /** Longest source of a minified Preact event proxy (Preact 10 and 11: about 120 characters) */
142
+ const MAX_PREACT_PROXY_LENGTH = 200;
143
+ /**
144
+ * `(source, read) => boolean`: whether a proxy source calls the handler it
145
+ * reads (`read`: the match of `this.<key>[<event>.type + …]`) with the
146
+ * event, directly (`this.l[e.type + false](e)`) or through a variable
147
+ * (`var t = this.l[u.type + n]; … return t(l.event ? l.event(u) : u)`).
148
+ */
149
+ const CALLS_READ_HANDLER_JS = `(source, read) => {
150
+ const event = read[2];
151
+ const passesEvent = (from) => {
152
+ const open = source.indexOf('(', from);
153
+ const close = source.indexOf(')', open);
154
+ if (open === -1 || close === -1 || source.slice(from, open).trim() !== '') return false;
155
+ return source.slice(open + 1, close).split(/[^\\w$]+/).includes(event);
156
+ };
157
+ const end = read.index + read[0].length;
158
+ if (passesEvent(end)) return true;
159
+ const assigned = /([\\w$]+)\\s*=\\s*$/.exec(source.slice(0, read.index));
160
+ if (!assigned) return false;
161
+ const variable = assigned[1];
162
+ for (let at = source.indexOf(variable, end); at !== -1; at = source.indexOf(variable, at + 1)) {
163
+ const before = source[at - 1] || '';
164
+ if (/[\\w$.]/.test(before)) continue;
165
+ if (passesEvent(at + variable.length)) return true;
166
+ }
167
+ return false;
168
+ }`;
169
+ /**
170
+ * `(node, type, capture, handler) => function | null`: the handler Preact
171
+ * runs from `handler` when it is Preact's event proxy on `node`. Preact
172
+ * keeps an element's handlers in an object on the element under a mangled
173
+ * key (`l` in Preact 10, `__e` in 11, `_listeners` unmangled), keyed by
174
+ * event type plus the capture flag (`clickfalse`); the key is read from the
175
+ * proxy's own source (`this.l[e.type + useCapture]`), so it follows
176
+ * Preact's renames. A proxy named `eventProxy`/`eventProxyCapture`
177
+ * (unminified builds; Preact 8 keys by type alone) is accepted as such;
178
+ * any other must look like Preact's minified one: a short function whose
179
+ * key holds a function for exactly the type plus capture flag, which it
180
+ * calls with the event. A generic dispatcher (`this.handlers[e.type](e)`)
181
+ * is left alone.
182
+ */
183
+ const PREACT_HANDLER_JS = `(node, type, capture, handler) => {
184
+ const own = ${OWN_VALUE_JS};
185
+ try {
186
+ if (typeof handler !== 'function' || !node || typeof node !== 'object') return null;
187
+ const source = Function.prototype.toString.call(handler);
188
+ const read = /this\\.([\\w$]+)\\[([\\w$]+)\\.type(?:\\s*\\+\\s*(![01]|true|false|[\\w$]+))?\\s*\\]/.exec(source);
189
+ const store = read ? own(node, read[1]) : undefined;
190
+ if (!store || typeof store !== 'object') return null;
191
+ const flag = { true: true, '!0': true, false: false, '!1': false }[read[3]];
192
+ if (flag !== undefined && flag !== capture) return null;
193
+ const fn = own(store, read[3] ? type + capture : type);
194
+ if (typeof fn !== 'function') return null;
195
+ const name = own(handler, 'name');
196
+ if (name === 'eventProxy' || name === 'eventProxyCapture') return fn;
197
+ const minified = read[3] && source.length < ${MAX_PREACT_PROXY_LENGTH};
198
+ return minified && (${CALLS_READ_HANDLER_JS})(source, read) ? fn : null;
199
+ } catch (e) { return null; }
200
+ }`;
122
201
  /** `(node) => string`: CDP-like description of an element, e.g. `div#app.card` */
123
202
  const DESCRIBE_NODE_JS = `(node) => {
124
203
  try {
@@ -222,17 +301,18 @@ const REACT_HANDLERS_JS = `(element, nodes, wanted, fns) => {
222
301
  }`;
223
302
  /**
224
303
  * Page function, called on the element with the listeners (position in the
225
- * chain and event type of each), the requested event types (null for all;
304
+ * chain, event type and capture flag of each), the requested event types (null for all;
226
305
  * React props are filtered before the limit), then the chain's objects, then each
227
306
  * listener's handler, then the function each handler calls (a bound
228
307
  * function's target, else null).
229
308
  *
230
- * Returns `[info, ...jQueryHandlers, ...reactHandlers]`. `info` has the
309
+ * Returns `[info, ...jQueryAndPreactHandlers, ...reactHandlers]`. `info` has the
231
310
  * iframe element holding the element's document (`frame`), a framework
232
311
  * label per chain entry (`roots`), per listener the handler's name, the
233
312
  * identity and name of the function it calls (equal identities are the same
234
313
  * function object) and, for jQuery's dispatcher, the jQuery handlers that
235
- * run for the element; then the React `on…` props that run for the
314
+ * run for the element, for Preact's event proxy the handler Preact runs
315
+ * (`preact`); then the React `on…` props that run for the
236
316
  * element's events (`react`, see {@link REACT_HANDLERS_JS}). Their functions follow `info` in the same order.
237
317
  * After {@link MAX_JQUERY_HANDLERS}, dispatchers stay unresolved and are
238
318
  * counted in `jquerySkipped`; after {@link MAX_REACT_HANDLERS}, React props
@@ -264,7 +344,13 @@ export const ELEMENT_INFO_JS = `function (listeners, types, ...rest) {
264
344
  const entry = { name: typeof handler === 'function' ? handler.name : null, identity: identity(target),
265
345
  targetName: typeof target === 'function' ? target.name : null };
266
346
  const resolved = jq ? (${JQUERY_HANDLERS_JS})(jq, this, nodes[listener.position], listener.type, handler) : null;
267
- if (!resolved) return entry;
347
+ if (!resolved) {
348
+ const preact = (${PREACT_HANDLER_JS})(nodes[listener.position], listener.type, listener.capture, handler);
349
+ if (!preact) return entry;
350
+ fns.push(preact);
351
+ const preactName = (${OWN_VALUE_JS})(preact, 'name');
352
+ return { ...entry, preact: { name: typeof preactName === 'string' ? preactName : '' } };
353
+ }
268
354
  if (fns.length + resolved.length > ${MAX_JQUERY_HANDLERS}) {
269
355
  jquerySkipped += resolved.length;
270
356
  return entry;