browser-debugger-cli 0.8.0 → 0.9.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 (251) hide show
  1. package/README.md +4 -1
  2. package/dist/cdp/schema.d.ts +4 -1
  3. package/dist/cdp/schema.js +48 -7
  4. package/dist/commands/cdp.js +3 -2
  5. package/dist/commands/cleanup.d.ts +11 -0
  6. package/dist/commands/cleanup.js +161 -57
  7. package/dist/commands/console.d.ts +20 -1
  8. package/dist/commands/console.js +57 -17
  9. package/dist/commands/details.js +3 -2
  10. package/dist/commands/dom/DomElementResolver.d.ts +10 -3
  11. package/dist/commands/dom/DomElementResolver.js +35 -17
  12. package/dist/commands/dom/a11y.d.ts +10 -0
  13. package/dist/commands/dom/a11y.js +27 -5
  14. package/dist/commands/dom/eval.d.ts +3 -1
  15. package/dist/commands/dom/eval.js +29 -4
  16. package/dist/commands/dom/form.js +16 -62
  17. package/dist/commands/dom/formInteraction.js +152 -113
  18. package/dist/commands/dom/formSummary.d.ts +49 -0
  19. package/dist/commands/dom/formSummary.js +180 -0
  20. package/dist/commands/dom/frames.d.ts +2 -1
  21. package/dist/commands/dom/frames.js +17 -2
  22. package/dist/commands/dom/get.d.ts +6 -5
  23. package/dist/commands/dom/get.js +92 -82
  24. package/dist/commands/dom/helpers/index.d.ts +1 -1
  25. package/dist/commands/dom/helpers/index.js +1 -1
  26. package/dist/commands/dom/helpers/query.d.ts +44 -17
  27. package/dist/commands/dom/helpers/query.js +244 -97
  28. package/dist/commands/dom/helpers/runElementCommand.d.ts +10 -2
  29. package/dist/commands/dom/helpers/runElementCommand.js +97 -30
  30. package/dist/commands/dom/helpers/screenshot.d.ts +4 -1
  31. package/dist/commands/dom/helpers/screenshot.js +164 -49
  32. package/dist/commands/dom/index.d.ts +3 -1
  33. package/dist/commands/dom/index.js +16 -6
  34. package/dist/commands/dom/layout.d.ts +14 -0
  35. package/dist/commands/dom/layout.js +54 -0
  36. package/dist/commands/dom/listeners.d.ts +5 -1
  37. package/dist/commands/dom/listeners.js +13 -3
  38. package/dist/commands/dom/query.js +2 -3
  39. package/dist/commands/dom/screenshot.d.ts +12 -2
  40. package/dist/commands/dom/screenshot.js +27 -3
  41. package/dist/commands/dom/semanticUtils.d.ts +6 -13
  42. package/dist/commands/dom/semanticUtils.js +15 -19
  43. package/dist/commands/dom/wait.d.ts +13 -0
  44. package/dist/commands/dom/wait.js +83 -0
  45. package/dist/commands/helpJson.js +2 -2
  46. package/dist/commands/network/list.js +4 -11
  47. package/dist/commands/optionBehaviors.js +112 -21
  48. package/dist/commands/page.d.ts +2 -1
  49. package/dist/commands/page.js +41 -5
  50. package/dist/commands/peek.js +4 -11
  51. package/dist/commands/sessions.d.ts +8 -0
  52. package/dist/commands/sessions.js +19 -0
  53. package/dist/commands/shared/CommandRunner.js +4 -4
  54. package/dist/commands/shared/dataFetcher.js +2 -2
  55. package/dist/commands/shared/followMode.d.ts +21 -1
  56. package/dist/commands/shared/followMode.js +29 -2
  57. package/dist/commands/shared/handleValidationError.d.ts +2 -2
  58. package/dist/commands/shared/handleValidationError.js +12 -3
  59. package/dist/commands/shared/optionTypes.d.ts +40 -5
  60. package/dist/commands/shared/startHelpers.js +12 -3
  61. package/dist/commands/shared/validation.d.ts +3 -2
  62. package/dist/commands/shared/validation.js +4 -3
  63. package/dist/commands/start.d.ts +63 -0
  64. package/dist/commands/start.js +115 -15
  65. package/dist/commands/status.js +29 -7
  66. package/dist/commands/stop.js +7 -6
  67. package/dist/commands/tail.js +4 -11
  68. package/dist/commands/types.d.ts +2 -0
  69. package/dist/commands.js +2 -0
  70. package/dist/connection/chromeIdentity.d.ts +65 -0
  71. package/dist/connection/chromeIdentity.js +143 -0
  72. package/dist/connection/launcher/profilePreferences.d.ts +47 -0
  73. package/dist/connection/launcher/profilePreferences.js +151 -0
  74. package/dist/connection/launcher.d.ts +21 -2
  75. package/dist/connection/launcher.js +42 -16
  76. package/dist/connection/portReservation.d.ts +14 -4
  77. package/dist/connection/portReservation.js +21 -6
  78. package/dist/connection/startupExit.d.ts +8 -0
  79. package/dist/connection/startupExit.js +15 -6
  80. package/dist/constants.d.ts +6 -2
  81. package/dist/constants.js +9 -2
  82. package/dist/daemon/SessionController.js +23 -7
  83. package/dist/daemon/errors.d.ts +1 -1
  84. package/dist/daemon/errors.js +1 -1
  85. package/dist/daemon/launcher.d.ts +2 -1
  86. package/dist/daemon/launcher.js +5 -6
  87. package/dist/daemon/server/SocketServer.js +1 -2
  88. package/dist/daemon/session/Session.d.ts +13 -0
  89. package/dist/daemon/session/Session.js +57 -8
  90. package/dist/daemon/session/chromeConnection.d.ts +9 -0
  91. package/dist/daemon/session/chromeConnection.js +45 -8
  92. package/dist/daemon/session/commandRegistry.js +52 -62
  93. package/dist/daemon/session/interactions.d.ts +35 -9
  94. package/dist/daemon/session/interactions.js +36 -9
  95. package/dist/daemon/session/triggeredRequests.d.ts +67 -0
  96. package/dist/daemon/session/triggeredRequests.js +157 -0
  97. package/dist/daemon/session/types.d.ts +5 -1
  98. package/dist/daemon.js +5393 -1600
  99. package/dist/errors/messages.d.ts +387 -24
  100. package/dist/errors/messages.js +761 -67
  101. package/dist/index.js +3976 -1558
  102. package/dist/ipc/client.d.ts +12 -1
  103. package/dist/ipc/client.js +22 -3
  104. package/dist/ipc/protocol/commands.d.ts +89 -4
  105. package/dist/ipc/protocol/commands.js +2 -0
  106. package/dist/ipc/protocol/domTypes.d.ts +258 -7
  107. package/dist/ipc/session/lifecycle.d.ts +8 -1
  108. package/dist/ipc/session/queries.d.ts +5 -1
  109. package/dist/ipc/session/types.d.ts +5 -0
  110. package/dist/ipc/transport/index.d.ts +2 -1
  111. package/dist/ipc/transport/index.js +2 -2
  112. package/dist/runtime/dom/actionEffects.d.ts +106 -0
  113. package/dist/runtime/dom/actionEffects.js +256 -0
  114. package/dist/runtime/dom/actionEffectsScripts.d.ts +52 -0
  115. package/dist/runtime/dom/actionEffectsScripts.js +234 -0
  116. package/dist/runtime/dom/elementGeometry.d.ts +170 -0
  117. package/dist/runtime/dom/elementGeometry.js +553 -0
  118. package/dist/runtime/dom/elementInfo.d.ts +77 -0
  119. package/dist/runtime/dom/elementInfo.js +191 -0
  120. package/dist/runtime/dom/evalHelpers.d.ts +51 -6
  121. package/dist/runtime/dom/evalHelpers.js +136 -26
  122. package/dist/runtime/dom/eventListeners.d.ts +2 -1
  123. package/dist/runtime/dom/eventListeners.js +174 -47
  124. package/dist/runtime/dom/formDiscovery.d.ts +1 -1
  125. package/dist/runtime/dom/formDiscovery.js +116 -16
  126. package/dist/runtime/dom/formFillHelpers/fill.d.ts +10 -0
  127. package/dist/runtime/dom/formFillHelpers/fill.js +125 -10
  128. package/dist/runtime/dom/formFillHelpers/index.d.ts +2 -2
  129. package/dist/runtime/dom/formFillHelpers/index.js +2 -2
  130. package/dist/runtime/dom/formFillHelpers/pressKey.js +14 -3
  131. package/dist/runtime/dom/formFillHelpers/scroll.d.ts +3 -0
  132. package/dist/runtime/dom/formFillHelpers/scroll.js +60 -18
  133. package/dist/runtime/dom/formFillHelpers/shared.d.ts +17 -0
  134. package/dist/runtime/dom/formFillHelpers/shared.js +25 -1
  135. package/dist/runtime/dom/formFillHelpers/stability.d.ts +20 -6
  136. package/dist/runtime/dom/formFillHelpers/stability.js +50 -19
  137. package/dist/runtime/dom/formSubmitHelpers.d.ts +3 -0
  138. package/dist/runtime/dom/formSubmitHelpers.js +89 -15
  139. package/dist/runtime/dom/frameLayout.d.ts +60 -0
  140. package/dist/runtime/dom/frameLayout.js +140 -0
  141. package/dist/runtime/dom/frameOrigin.d.ts +50 -0
  142. package/dist/runtime/dom/frameOrigin.js +62 -0
  143. package/dist/runtime/dom/frameScopedConnection.d.ts +92 -0
  144. package/dist/runtime/dom/frameScopedConnection.js +252 -0
  145. package/dist/runtime/dom/frameSelection.d.ts +1 -1
  146. package/dist/runtime/dom/frameSelection.js +2 -2
  147. package/dist/runtime/dom/frames.d.ts +25 -2
  148. package/dist/runtime/dom/frames.js +202 -63
  149. package/dist/runtime/dom/layout.d.ts +67 -0
  150. package/dist/runtime/dom/layout.js +333 -0
  151. package/dist/runtime/dom/listenerPageScripts.d.ts +66 -0
  152. package/dist/runtime/dom/listenerPageScripts.js +279 -0
  153. package/dist/runtime/dom/listenerSummary.d.ts +132 -11
  154. package/dist/runtime/dom/listenerSummary.js +344 -22
  155. package/dist/runtime/dom/pageActivity.d.ts +41 -0
  156. package/dist/runtime/dom/pageActivity.js +123 -0
  157. package/dist/runtime/dom/reactEventHelpers.d.ts +58 -2
  158. package/dist/runtime/dom/reactEventHelpers.js +212 -41
  159. package/dist/runtime/dom/targetNode.d.ts +80 -27
  160. package/dist/runtime/dom/targetNode.js +249 -33
  161. package/dist/runtime/dom/wait.d.ts +25 -0
  162. package/dist/runtime/dom/wait.js +199 -0
  163. package/dist/runtime/dom/waitCondition.d.ts +71 -0
  164. package/dist/runtime/dom/waitCondition.js +75 -0
  165. package/dist/runtime/page/emulation.d.ts +51 -0
  166. package/dist/runtime/page/emulation.js +80 -0
  167. package/dist/runtime/page/loadingState.d.ts +36 -0
  168. package/dist/runtime/page/loadingState.js +86 -0
  169. package/dist/runtime/page/navigation.d.ts +46 -2
  170. package/dist/runtime/page/navigation.js +69 -33
  171. package/dist/session/QueryCacheManager.d.ts +11 -1
  172. package/dist/session/QueryCacheManager.js +25 -3
  173. package/dist/session/chromeOwners.d.ts +34 -0
  174. package/dist/session/chromeOwners.js +51 -0
  175. package/dist/session/cleanup/staleSession.d.ts +11 -1
  176. package/dist/session/cleanup/staleSession.js +17 -6
  177. package/dist/session/cleanup/userCommands.js +2 -4
  178. package/dist/session/metadata.d.ts +5 -1
  179. package/dist/session/metadata.js +2 -1
  180. package/dist/session/paths.d.ts +77 -3
  181. package/dist/session/paths.js +111 -5
  182. package/dist/session/port.d.ts +31 -7
  183. package/dist/session/port.js +50 -43
  184. package/dist/session/portClaims.d.ts +66 -0
  185. package/dist/session/portClaims.js +284 -0
  186. package/dist/session/sessionList.d.ts +58 -0
  187. package/dist/session/sessionList.js +199 -0
  188. package/dist/session/sessionName.d.ts +46 -0
  189. package/dist/session/sessionName.js +97 -0
  190. package/dist/telemetry/a11y.d.ts +8 -3
  191. package/dist/telemetry/a11y.js +92 -28
  192. package/dist/telemetry/requestKinds.d.ts +32 -0
  193. package/dist/telemetry/requestKinds.js +61 -0
  194. package/dist/telemetry/requestState.d.ts +31 -0
  195. package/dist/telemetry/requestState.js +38 -0
  196. package/dist/types.d.ts +80 -3
  197. package/dist/ui/formatters/a11y.js +3 -0
  198. package/dist/ui/formatters/console/chronological.d.ts +8 -0
  199. package/dist/ui/formatters/console/chronological.js +17 -4
  200. package/dist/ui/formatters/console/json.js +3 -4
  201. package/dist/ui/formatters/console/shared.d.ts +12 -0
  202. package/dist/ui/formatters/console.d.ts +2 -2
  203. package/dist/ui/formatters/console.js +1 -1
  204. package/dist/ui/formatters/details.js +2 -1
  205. package/dist/ui/formatters/dom.d.ts +26 -14
  206. package/dist/ui/formatters/dom.js +65 -52
  207. package/dist/ui/formatters/form.js +29 -18
  208. package/dist/ui/formatters/layout.d.ts +31 -0
  209. package/dist/ui/formatters/layout.js +53 -0
  210. package/dist/ui/formatters/listeners.d.ts +3 -2
  211. package/dist/ui/formatters/listeners.js +73 -9
  212. package/dist/ui/formatters/networkHeaders.js +13 -0
  213. package/dist/ui/formatters/preview.js +2 -1
  214. package/dist/ui/formatters/requestStatus.d.ts +1 -17
  215. package/dist/ui/formatters/requestStatus.js +2 -30
  216. package/dist/ui/formatters/sessions.d.ts +12 -0
  217. package/dist/ui/formatters/sessions.js +40 -0
  218. package/dist/ui/formatters/status.d.ts +21 -2
  219. package/dist/ui/formatters/status.js +47 -10
  220. package/dist/ui/formatters/triggeredRequests.d.ts +36 -0
  221. package/dist/ui/formatters/triggeredRequests.js +65 -0
  222. package/dist/ui/formatting.d.ts +10 -0
  223. package/dist/ui/formatting.js +28 -36
  224. package/dist/ui/messages/chrome.d.ts +9 -0
  225. package/dist/ui/messages/chrome.js +17 -5
  226. package/dist/ui/messages/commands.d.ts +388 -14
  227. package/dist/ui/messages/commands.js +664 -21
  228. package/dist/ui/messages/consoleMessages.d.ts +10 -0
  229. package/dist/ui/messages/consoleMessages.js +17 -0
  230. package/dist/ui/messages/hints.js +2 -1
  231. package/dist/ui/messages/preview.js +5 -4
  232. package/dist/ui/messages/session.d.ts +16 -21
  233. package/dist/ui/messages/session.js +28 -26
  234. package/dist/ui/messages/sessionCommand.d.ts +43 -0
  235. package/dist/ui/messages/sessionCommand.js +52 -0
  236. package/dist/utils/async.d.ts +8 -0
  237. package/dist/utils/async.js +19 -0
  238. package/dist/utils/http.d.ts +22 -1
  239. package/dist/utils/http.js +28 -9
  240. package/dist/utils/selectorFilters.d.ts +36 -8
  241. package/dist/utils/selectorFilters.js +267 -53
  242. package/dist/utils/shellDetection.d.ts +8 -2
  243. package/dist/utils/shellDetection.js +120 -33
  244. package/dist/utils/suggestions.d.ts +26 -0
  245. package/dist/utils/suggestions.js +73 -0
  246. package/dist/utils/taskMappings.js +10 -0
  247. package/dist/utils/url.d.ts +12 -2
  248. package/dist/utils/url.js +69 -7
  249. package/package.json +1 -1
  250. package/dist/ui/formatters/sessionFormatters.d.ts +0 -58
  251. package/dist/ui/formatters/sessionFormatters.js +0 -121
@@ -0,0 +1,234 @@
1
+ /**
2
+ * Page scripts behind the "what changed" part of DOM action results: one
3
+ * snapshot before the action ({@link EFFECTS_START_SCRIPT}) and one read after
4
+ * it ({@link EFFECTS_READ_SCRIPT}).
5
+ */
6
+ import { ELEMENT_DESCRIPTION_JS } from './elementInfo.js';
7
+ /** Class or id words that mark an element as a message (flash, toast, field error) */
8
+ const MESSAGE_WORDS = [
9
+ 'flash',
10
+ 'alert',
11
+ 'error',
12
+ 'toast',
13
+ 'notice',
14
+ 'message',
15
+ 'invalid',
16
+ 'feedback',
17
+ ];
18
+ /** Elements that show messages by role, plus a cheap prefilter for the class and id words */
19
+ const MESSAGE_CANDIDATES = [
20
+ '[role="alert"]',
21
+ '[role="status"]',
22
+ '[aria-live]',
23
+ 'output',
24
+ ...MESSAGE_WORDS.flatMap((word) => [`[class*="${word}" i]`, `[id*="${word}" i]`]),
25
+ ].join(', ');
26
+ /** Messages kept per snapshot (after filtering) */
27
+ const MAX_MESSAGES = 50;
28
+ /** Time a message snapshot may spend before it stops looking (ms) */
29
+ const MESSAGES_BUDGET_MS = 5;
30
+ /** Longer texts are containers (a page, a form), not messages */
31
+ const MAX_MESSAGE_TEXT = 300;
32
+ /** How long a page keeps watching when no read or stop arrives (ms) */
33
+ const MAX_WATCH_MS = 30000;
34
+ /**
35
+ * Page-side test whether an element is part of a message's chrome rather
36
+ * than its text: aria-hidden parts, buttons, and elements whose class or
37
+ * aria-label names a close/dismiss control (`close`, `btn-close`, `close_x`;
38
+ * not `closeable`, `enclosed` or `disclosure`).
39
+ */
40
+ export const MESSAGE_CHROME_JS = `(node) => {
41
+ const closer = /(^|[-_\\s])(close|dismiss)($|[-_\\s])/i;
42
+ return node.getAttribute('aria-hidden') === 'true' ||
43
+ /^(button|script|style|template)$/.test(node.localName) ||
44
+ node.getAttribute('role') === 'button' ||
45
+ closer.test(node.getAttribute('class') || '') ||
46
+ closer.test(node.getAttribute('aria-label') || '');
47
+ }`;
48
+ /**
49
+ * Page-side list of the messages a page shows: visible elements with an
50
+ * alert/status role, `aria-live`, `<output>`, or a class or id word from
51
+ * {@link MESSAGE_WORDS} (timers, marquees and progress bars left out), with
52
+ * their text (parts matching {@link MESSAGE_CHROME_JS}, such as the "×"
53
+ * close link, left out). Elements are filtered first, then the first
54
+ * {@link MAX_MESSAGES} kept, and the whole pass stops after
55
+ * {@link MESSAGES_BUDGET_MS}. Only the innermost of nested messages is kept,
56
+ * described by tag, id and classes (or its role when it has neither,
57
+ * `h3[role="alert"]`). Each element gets a number from `ids` that stays the
58
+ * same within the document, so a later snapshot can tell a new element from
59
+ * one that was there.
60
+ */
61
+ const MESSAGES_JS = `(ids) => {
62
+ const deadline = performance.now() + ${MESSAGES_BUDGET_MS};
63
+ const description = ${ELEMENT_DESCRIPTION_JS};
64
+ const chrome = ${MESSAGE_CHROME_JS};
65
+ const describe = (el) => {
66
+ const text = description(el);
67
+ const role = el.getAttribute('role');
68
+ return text === el.localName && role ? text + '[role="' + role + '"]' : text;
69
+ };
70
+ const word = /\\b(${MESSAGE_WORDS.join('|')})\\b/i;
71
+ const named = (el) => !/^(timer|marquee|progressbar)$/.test(el.getAttribute('role') || '') &&
72
+ (el.matches('[role="alert"], [role="status"], [aria-live]:not([aria-live="off"]), output') ||
73
+ word.test(el.getAttribute('class') || '') || word.test(el.id || ''));
74
+ const shown = (el) => !el.closest('[aria-hidden="true"]') && el.getClientRects().length > 0 &&
75
+ (!el.checkVisibility || el.checkVisibility({ visibilityProperty: true, opacityProperty: true }));
76
+ const inChrome = (node, el) => {
77
+ for (let n = node; n && n !== el; n = n.parentElement) if (chrome(n)) return true;
78
+ return false;
79
+ };
80
+ const textOf = (el) => {
81
+ let text = '';
82
+ const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT);
83
+ while (text.length <= ${MAX_MESSAGE_TEXT} && walker.nextNode()) {
84
+ const parent = walker.currentNode.parentElement;
85
+ if (!parent || inChrome(parent, el)) continue;
86
+ if (parent.checkVisibility && !parent.checkVisibility({ visibilityProperty: true, opacityProperty: true })) continue;
87
+ text += ' ' + walker.currentNode.data;
88
+ }
89
+ return text.replace(/\\s+/g, ' ').trim();
90
+ };
91
+ const found = [];
92
+ for (const el of document.querySelectorAll(${JSON.stringify(MESSAGE_CANDIDATES)})) {
93
+ if (found.length >= ${MAX_MESSAGES} || performance.now() > deadline) break;
94
+ if (!named(el) || !shown(el)) continue;
95
+ const text = textOf(el);
96
+ if (text !== '' && text.length <= ${MAX_MESSAGE_TEXT}) found.push({ el: el, text: text });
97
+ }
98
+ return found
99
+ .filter((m) => !found.some((other) => other !== m && m.el.contains(other.el)))
100
+ .map((m) => {
101
+ let id = ids.map.get(m.el);
102
+ if (id === undefined) {
103
+ id = ids.next++;
104
+ ids.map.set(m.el, id);
105
+ }
106
+ return { id: id, text: m.text, element: describe(m.el) };
107
+ });
108
+ }`;
109
+ /**
110
+ * Page-side test whether a mutation is only focus/hover churn: a class
111
+ * change on an element the action's events hit (`targets`) that only adds
112
+ * or removes classes containing "focus" or "hover".
113
+ */
114
+ export const CHURN_ONLY_JS = `(record, targets) => {
115
+ if (record.type !== 'attributes' || record.attributeName !== 'class' || !targets.has(record.target)) return false;
116
+ const tokens = (text) => new Set((text || '').split(/\\s+/).filter(Boolean));
117
+ const before = tokens(record.oldValue);
118
+ const after = tokens(record.target.getAttribute('class'));
119
+ const changed = [...before].filter((c) => !after.has(c)).concat([...after].filter((c) => !before.has(c)));
120
+ return changed.every((c) => /focus|hover/i.test(c));
121
+ }`;
122
+ /**
123
+ * Page-side reason why "no effect" can't be claimed even without DOM
124
+ * changes, or undefined: `clipboard` (a copy or cut happened), `no-event`
125
+ * (no event reached the page), `control` (form controls, labels, media,
126
+ * frames, popover/command buttons: their effect needs no DOM change),
127
+ * `new-window` (download or `target` links), `external-link` (mailto:, tel:,
128
+ * javascript: and other non-http links), `closed-shadow` (a custom element
129
+ * whose inside is not observed) or `focus` (focus moved to an element that
130
+ * may reveal content with CSS).
131
+ */
132
+ export const UNCERTAIN_JS = `(state, active) => {
133
+ if (state.copied) return 'clipboard';
134
+ if (state.targets.size === 0) return 'no-event';
135
+ const controls = /^(input|select|textarea|option|label|canvas|video|audio|iframe|embed|object)$/;
136
+ for (const node of state.path) {
137
+ if (controls.test(node.localName) || node.isContentEditable) return 'control';
138
+ if (node.hasAttribute('popovertarget') || node.hasAttribute('commandfor')) return 'control';
139
+ if (node.localName === 'a' && node.hasAttribute('href')) {
140
+ if (node.hasAttribute('download') || (node.target && node.target !== '_self')) return 'new-window';
141
+ if (!/^https?:$/.test(node.protocol)) return 'external-link';
142
+ }
143
+ }
144
+ for (const node of state.targets) if (node.localName.includes('-') && !node.shadowRoot) return 'closed-shadow';
145
+ const plain = (node) => /^(body|button|summary)$/.test(node.localName) ||
146
+ (node.localName === 'a' && node.hasAttribute('href')) ||
147
+ (node.localName === 'input' && /^(button|submit|reset)$/i.test(node.type || ''));
148
+ if (active && active !== state.focus && !plain(active)) return 'focus';
149
+ return undefined;
150
+ }`;
151
+ /**
152
+ * Snapshot before an action, left in `window.__bdgEffects`: the messages
153
+ * shown, and a MutationObserver (on the document, its open shadow roots and
154
+ * any shadow root attached while watching, which also counts as a change)
155
+ * counting changes other than {@link CHURN_ONLY_JS}. Capture listeners
156
+ * record which elements the action's events reached, copy/cut events, and
157
+ * the scroll position at the first press (a click scrolls its target into
158
+ * view first). The watch stops itself after {@link MAX_WATCH_MS}, so a
159
+ * snapshot that ran late (after a navigation, with nobody reading it)
160
+ * leaves nothing behind. Evaluates to the URL and the messages.
161
+ */
162
+ export const EFFECTS_START_SCRIPT = `(() => {
163
+ if (window.__bdgEffects) window.__bdgEffects.stop();
164
+ const churnOnly = ${CHURN_ONLY_JS};
165
+ const ids = { map: new WeakMap(), next: 1 };
166
+ const state = { ids: ids, changes: 0, targets: new Set(), path: new Set(), focus: document.activeElement, pressScroll: null, copied: false, stopped: false };
167
+ const count = (records) => {
168
+ for (const record of records) if (!churnOnly(record, state.targets)) state.changes++;
169
+ };
170
+ const observer = new MutationObserver(count);
171
+ const options = { subtree: true, childList: true, characterData: true, attributes: true, attributeOldValue: true };
172
+ const observe = (root) => {
173
+ observer.observe(root, options);
174
+ for (const el of root.querySelectorAll('*')) if (el.shadowRoot) observe(el.shadowRoot);
175
+ };
176
+ observe(document);
177
+ const attachShadow = Element.prototype.attachShadow;
178
+ const watchedAttach = function (init) {
179
+ const root = attachShadow.call(this, init);
180
+ state.changes++;
181
+ observer.observe(root, options);
182
+ return root;
183
+ };
184
+ Element.prototype.attachShadow = watchedAttach;
185
+ const types = ['pointerdown', 'mousedown', 'click', 'keydown', 'focusin', 'submit', 'mouseover', 'input', 'change', 'copy', 'cut'];
186
+ const pressTypes = ['pointerdown', 'mousedown', 'click', 'keydown', 'submit'];
187
+ const record = (event) => {
188
+ if (event.type === 'copy' || event.type === 'cut') state.copied = true;
189
+ const path = event.composedPath().filter((node) => node.nodeType === 1);
190
+ if (path[0]) state.targets.add(path[0]);
191
+ path.forEach((node) => state.path.add(node));
192
+ if (!state.pressScroll && pressTypes.includes(event.type)) state.pressScroll = [scrollX, scrollY];
193
+ };
194
+ types.forEach((type) => window.addEventListener(type, record, true));
195
+ state.flush = () => count(observer.takeRecords());
196
+ state.stop = () => {
197
+ if (state.stopped) return;
198
+ state.stopped = true;
199
+ clearTimeout(expiry);
200
+ observer.disconnect();
201
+ if (Element.prototype.attachShadow === watchedAttach) Element.prototype.attachShadow = attachShadow;
202
+ types.forEach((type) => window.removeEventListener(type, record, true));
203
+ };
204
+ const expiry = setTimeout(state.stop, ${MAX_WATCH_MS});
205
+ window.__bdgEffects = state;
206
+ return { href: location.href, messages: (${MESSAGES_JS})(ids) };
207
+ })()`;
208
+ /**
209
+ * Read after an action (call with `true` to also stop watching): the URL,
210
+ * the messages shown and, when the snapshot is still there (same document),
211
+ * the number of changes counted (plus one when the page scrolled after the
212
+ * press) and why "no effect" could not be claimed ({@link UNCERTAIN_JS}).
213
+ * `fresh` means a new document (everything shown is new).
214
+ */
215
+ export const EFFECTS_READ_SCRIPT = `((stop) => {
216
+ const state = window.__bdgEffects;
217
+ if (!state) return { href: location.href, fresh: true, messages: (${MESSAGES_JS})({ map: new WeakMap(), next: 1 }) };
218
+ if (!state.stopped) state.flush();
219
+ if (stop) {
220
+ state.stop();
221
+ delete window.__bdgEffects;
222
+ }
223
+ const scrolled = state.pressScroll !== null && (state.pressScroll[0] !== scrollX || state.pressScroll[1] !== scrollY);
224
+ return {
225
+ href: location.href,
226
+ fresh: false,
227
+ changes: state.changes + (scrolled ? 1 : 0),
228
+ uncertain: (${UNCERTAIN_JS})(state, document.activeElement),
229
+ messages: (${MESSAGES_JS})(state.ids)
230
+ };
231
+ })`;
232
+ /** Stops the watch {@link EFFECTS_START_SCRIPT} left (when no read stopped it) */
233
+ export const EFFECTS_STOP_SCRIPT = 'if (window.__bdgEffects) { window.__bdgEffects.stop(); delete window.__bdgEffects; }';
234
+ //# sourceMappingURL=actionEffectsScripts.js.map
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Where elements are relative to the viewport of the top-level page.
3
+ *
4
+ * The page-side scripts measure an element in top-level viewport coordinates
5
+ * (adding the offsets of the same-origin iframes it is in) together with what
6
+ * clips it: the viewports of those iframes and ancestors that cut off
7
+ * overflowing content (scroll containers, `overflow: hidden`). Classifying the
8
+ * measurements is done here, outside the page, so `dom query` and `dom layout`
9
+ * agree and the rules can be tested without a browser.
10
+ */
11
+ import type { LayoutBox, LayoutPoint, LayoutSize } from '../../ipc/protocol/domTypes.js';
12
+ import type { ViewportPosition } from '../../types.js';
13
+ /** Measurements of one element ({@link ELEMENT_GEOMETRY_JS}). */
14
+ export interface ElementGeometry {
15
+ /** Border box in top-level viewport coordinates */
16
+ rect: LayoutBox;
17
+ /** Area its iframes and overflow-clipping ancestors leave visible, in the same coordinates */
18
+ clip: LayoutBox | null;
19
+ /** Whether overlay scrollbars of the clipping containers show along the clip's right and bottom edges */
20
+ clipOverlay: OverlayEdges;
21
+ /** Innermost ancestor or iframe cutting off part of it, e.g. `ul#list` */
22
+ clipper: string | null;
23
+ /** Why it cannot be seen regardless of position, e.g. `display: none`, `clipped by div#acc: zero height` */
24
+ hidden: string | null;
25
+ /**
26
+ * Why it cannot be seen although it is rendered ({@link INVISIBLE_REASON_JS}),
27
+ * e.g. `opacity: 0 on div#menu`
28
+ */
29
+ invisible: string | null;
30
+ /** Inside an `inert` element: shown, but not interactive */
31
+ inert: boolean;
32
+ /** Fixed to the top-level viewport (it or a container is `position: fixed`): page scroll does not move it */
33
+ fixed: boolean;
34
+ /** It or an ancestor is `position: sticky`: page scroll moves it only until it sticks */
35
+ sticky?: boolean;
36
+ /** How far the top-level page can scroll from where it is now */
37
+ pageScroll: ScrollRange;
38
+ /**
39
+ * Why the top-level page cannot scroll although content is below the fold
40
+ * ({@link SCROLL_LOCK_JS}); null when it is not locked
41
+ */
42
+ scrollLock?: ScrollLock | null;
43
+ /** Offset of its document's viewport within the top-level viewport (iframes) */
44
+ offset: LayoutPoint;
45
+ }
46
+ /** What keeps the top-level page from scrolling ({@link SCROLL_LOCK_JS}). */
47
+ export interface ScrollLock {
48
+ /** The styles locking it, e.g. `position: fixed, overflow: hidden on body` */
49
+ by: string;
50
+ /** A visible dialog that is likely the reason, e.g. `div#consent`; null when there is none */
51
+ dialog: string | null;
52
+ }
53
+ /** Edges of a viewport or clip along which overlay scrollbars (that take no space) show after a scroll. */
54
+ export interface OverlayEdges {
55
+ right: boolean;
56
+ bottom: boolean;
57
+ }
58
+ /** Distances (CSS px, never negative) a page can scroll in each direction. */
59
+ export interface ScrollRange {
60
+ left: number;
61
+ up: number;
62
+ right: number;
63
+ down: number;
64
+ }
65
+ /** How an element relates to the viewport ({@link classifyViewportPosition}). */
66
+ export interface ViewportPlacement {
67
+ inViewport: ViewportPosition;
68
+ /** Share in view (1-99), for `partly` */
69
+ percentVisible?: number;
70
+ /** Why it is `hidden` */
71
+ hiddenReason?: string;
72
+ /**
73
+ * Page scroll that brings it fully into view, when the page can scroll there:
74
+ * the smallest one for an element partly in view, centring it for one out of view
75
+ */
76
+ scrollBy?: LayoutPoint;
77
+ /** Ancestor or iframe cutting it off (page scroll alone does not show it) */
78
+ clippedBy?: string;
79
+ /** Why page scroll cannot bring it fully into view ({@link OFF_SCREEN_REASONS}) */
80
+ offScreenReason?: string;
81
+ }
82
+ /** Why page scroll cannot bring an element that is not fully in view into view. */
83
+ export declare const OFF_SCREEN_REASONS: {
84
+ readonly fixed: "fixed position, page scroll does not move it";
85
+ readonly sticky: "sticky position, page scroll moves it only until it sticks";
86
+ readonly outOfRange: "beyond the page's scroll range";
87
+ };
88
+ /**
89
+ * Page-side size of a window's viewport without scrollbars (the window size
90
+ * for quirks-mode documents, whose root element is as large as the content).
91
+ */
92
+ export declare const VIEWPORT_SIZE_JS = "(view) => {\n const root = view.document.documentElement;\n const standards = view.document.compatMode === 'CSS1Compat' && root;\n return {\n width: standards ? root.clientWidth : view.innerWidth,\n height: standards ? root.clientHeight : view.innerHeight\n };\n}";
93
+ /**
94
+ * Page-side distances a window's page can scroll in each direction from its
95
+ * current position. Right-to-left pages scroll to negative `scrollX`. Content
96
+ * that a scrolling body cuts off is reported through the body as a clipper.
97
+ */
98
+ export declare const SCROLL_RANGE_JS = "(view) => {\n const doc = view.document;\n const scroller = doc.scrollingElement || doc.documentElement;\n const maxX = Math.max(0, scroller.scrollWidth - scroller.clientWidth);\n const maxY = Math.max(0, scroller.scrollHeight - scroller.clientHeight);\n const rtl = view.scrollX < 0 || view.getComputedStyle(doc.documentElement).direction === 'rtl';\n const left = rtl ? maxX + view.scrollX : view.scrollX;\n return { left: Math.max(0, left), up: Math.max(0, view.scrollY), right: Math.max(0, maxX - left), down: Math.max(0, maxY - view.scrollY) };\n}";
99
+ /**
100
+ * Page-side offset of an iframe's content (inside its border and padding)
101
+ * within the viewport of the document holding the iframe.
102
+ */
103
+ export declare const FRAME_OFFSET_JS = "(frame) => {\n const rect = frame.getBoundingClientRect();\n const style = frame.ownerDocument.defaultView.getComputedStyle(frame);\n return {\n x: rect.left + frame.clientLeft + parseFloat(style.paddingLeft),\n y: rect.top + frame.clientTop + parseFloat(style.paddingTop)\n };\n}";
104
+ /**
105
+ * Page-side test of computed styles: true when they only scale or move the
106
+ * box (no `rotate`, and a 2D `transform` matrix without rotation or skew), so
107
+ * its bounding box is the box itself, scaled.
108
+ */
109
+ export declare const SCALES_ONLY_JS = "(style) => {\n if (style.rotate && style.rotate !== 'none') return false;\n if (!style.transform || style.transform === 'none') return true;\n const matrix = /^matrix\\(([^)]*)\\)$/.exec(style.transform);\n if (!matrix) return false;\n const values = matrix[1].split(',').map(parseFloat);\n return Math.abs(values[1]) < 1e-6 && Math.abs(values[2]) < 1e-6;\n}";
110
+ /** Page-side test of a `clip-path` that cuts everything away: `inset()` with percentages leaving no area. */
111
+ export declare const CLIP_PATH_CUTS_ALL_JS = "(clipPath) => {\n const inset = /^inset\\(([^)]*)\\)/.exec(clipPath || '');\n const values = inset ? inset[1].split(' round ')[0].trim().split(/\\s+/) : [];\n if (values.length === 0 || values.some((v) => !/%$/.test(v))) return false;\n const [top, right = top, bottom = top, left = right] = values.map(parseFloat);\n return top + bottom >= 100 || left + right >= 100;\n}";
112
+ /**
113
+ * Page-side test of a `clip` that cuts everything away: `rect()` with no area
114
+ * on an absolutely positioned or fixed element (the "visually hidden"
115
+ * pattern; `clip` applies to no other element).
116
+ */
117
+ export declare const CLIP_RECT_CUTS_ALL_JS = "(position, clip) => {\n if ((position !== 'absolute' && position !== 'fixed') || !/^rect\\(/.test(clip || '')) return false;\n const edges = (clip.match(/-?[\\d.]+/g) || []).map(Number);\n return edges.length === 4 && (edges[1] <= edges[3] || edges[2] <= edges[0]);\n}";
118
+ /**
119
+ * Page-side measurement of an element ({@link ElementGeometry}): its box in
120
+ * top-level viewport coordinates, its clip by ancestors
121
+ * ({@link ANCESTOR_CLIP_JS}) and by the viewports of its iframes, why it
122
+ * cannot be seen at all (not rendered, or inside a clipping container
123
+ * collapsed to zero size), why a rendered one is still invisible
124
+ * ({@link INVISIBLE_REASON_JS}), whether it is inert (an `inert` element
125
+ * around it, through shadow roots) or fixed to the top-level viewport, and how
126
+ * far the top-level page can scroll ({@link SCROLL_RANGE_JS}) or what locks
127
+ * its scrolling ({@link SCROLL_LOCK_JS}). Content of a body fixed to lock the
128
+ * page is not counted as fixed: it is in-flow content the lock holds in place.
129
+ *
130
+ * Rendering is decided by `checkVisibility()`, so content Chrome skips
131
+ * (inside a closed `<details>`, under `content-visibility: hidden`) is hidden
132
+ * although it still has a box. Content skipped by `content-visibility: auto`
133
+ * is not: it is rendered once scrolled near the viewport.
134
+ */
135
+ export declare const ELEMENT_GEOMETRY_JS: string;
136
+ /**
137
+ * How a scroll brings an element into view: `centre` it (an element out of
138
+ * view, like `bdg dom scroll <selector>`, so sticky headers and fixed footers
139
+ * at the edges do not cover it), or the `minimal` scroll that shows all of
140
+ * it (an element partly in view: the part cut off at one edge).
141
+ */
142
+ export type ScrollAim = 'centre' | 'minimal';
143
+ /**
144
+ * Where along one axis a scroll should bring a span's start (in the current
145
+ * viewport coordinates): centred, or just inside the edge cutting it off; a
146
+ * span larger than the viewport gets its start aligned either way.
147
+ *
148
+ * @param start - Start of the span in viewport coordinates
149
+ * @param size - Length of the span
150
+ * @param viewSize - Length of the viewport
151
+ * @param aim - Centre it, or the smallest scroll showing all of it
152
+ * @returns Scroll that would do it (negative: back), before limiting it to the page
153
+ */
154
+ export declare function scrollTarget(start: number, size: number, viewSize: number, aim: ScrollAim): number;
155
+ /**
156
+ * Classify where an element is relative to the top-level viewport.
157
+ *
158
+ * Visible parts are what lies inside the viewport and the element's clip
159
+ * (iframes, overflow containers). An element with no visible part is `above`,
160
+ * `below`, `left` or `right` of the viewport (or of its clip, e.g. scrolled
161
+ * out of a list), and `hidden` when it is not rendered at all. Being inert
162
+ * does not change the position: inert elements are shown.
163
+ *
164
+ * @param geometry - Page-side measurements
165
+ * @param viewport - Top-level viewport size
166
+ * @returns Position, with the visible share, hidden reason, clipping
167
+ * ancestor or page scroll when relevant
168
+ */
169
+ export declare function classifyViewportPosition(geometry: ElementGeometry, viewport: LayoutSize): ViewportPlacement;
170
+ //# sourceMappingURL=elementGeometry.d.ts.map