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.
- package/README.md +4 -1
- package/dist/cdp/schema.d.ts +4 -1
- package/dist/cdp/schema.js +48 -7
- package/dist/commands/cdp.js +3 -2
- package/dist/commands/cleanup.d.ts +11 -0
- package/dist/commands/cleanup.js +161 -57
- package/dist/commands/console.d.ts +20 -1
- package/dist/commands/console.js +57 -17
- package/dist/commands/details.js +3 -2
- package/dist/commands/dom/DomElementResolver.d.ts +10 -3
- package/dist/commands/dom/DomElementResolver.js +35 -17
- package/dist/commands/dom/a11y.d.ts +10 -0
- package/dist/commands/dom/a11y.js +27 -5
- package/dist/commands/dom/eval.d.ts +3 -1
- package/dist/commands/dom/eval.js +29 -4
- package/dist/commands/dom/form.js +16 -62
- package/dist/commands/dom/formInteraction.js +152 -113
- package/dist/commands/dom/formSummary.d.ts +49 -0
- package/dist/commands/dom/formSummary.js +180 -0
- package/dist/commands/dom/frames.d.ts +2 -1
- package/dist/commands/dom/frames.js +17 -2
- package/dist/commands/dom/get.d.ts +6 -5
- package/dist/commands/dom/get.js +92 -82
- package/dist/commands/dom/helpers/index.d.ts +1 -1
- package/dist/commands/dom/helpers/index.js +1 -1
- package/dist/commands/dom/helpers/query.d.ts +44 -17
- package/dist/commands/dom/helpers/query.js +244 -97
- package/dist/commands/dom/helpers/runElementCommand.d.ts +10 -2
- package/dist/commands/dom/helpers/runElementCommand.js +97 -30
- package/dist/commands/dom/helpers/screenshot.d.ts +4 -1
- package/dist/commands/dom/helpers/screenshot.js +164 -49
- package/dist/commands/dom/index.d.ts +3 -1
- package/dist/commands/dom/index.js +16 -6
- package/dist/commands/dom/layout.d.ts +14 -0
- package/dist/commands/dom/layout.js +54 -0
- package/dist/commands/dom/listeners.d.ts +5 -1
- package/dist/commands/dom/listeners.js +13 -3
- package/dist/commands/dom/query.js +2 -3
- package/dist/commands/dom/screenshot.d.ts +12 -2
- package/dist/commands/dom/screenshot.js +27 -3
- package/dist/commands/dom/semanticUtils.d.ts +6 -13
- package/dist/commands/dom/semanticUtils.js +15 -19
- package/dist/commands/dom/wait.d.ts +13 -0
- package/dist/commands/dom/wait.js +83 -0
- package/dist/commands/helpJson.js +2 -2
- package/dist/commands/network/list.js +4 -11
- package/dist/commands/optionBehaviors.js +112 -21
- package/dist/commands/page.d.ts +2 -1
- package/dist/commands/page.js +41 -5
- package/dist/commands/peek.js +4 -11
- package/dist/commands/sessions.d.ts +8 -0
- package/dist/commands/sessions.js +19 -0
- package/dist/commands/shared/CommandRunner.js +4 -4
- package/dist/commands/shared/dataFetcher.js +2 -2
- package/dist/commands/shared/followMode.d.ts +21 -1
- package/dist/commands/shared/followMode.js +29 -2
- package/dist/commands/shared/handleValidationError.d.ts +2 -2
- package/dist/commands/shared/handleValidationError.js +12 -3
- package/dist/commands/shared/optionTypes.d.ts +40 -5
- package/dist/commands/shared/startHelpers.js +12 -3
- package/dist/commands/shared/validation.d.ts +3 -2
- package/dist/commands/shared/validation.js +4 -3
- package/dist/commands/start.d.ts +63 -0
- package/dist/commands/start.js +115 -15
- package/dist/commands/status.js +29 -7
- package/dist/commands/stop.js +7 -6
- package/dist/commands/tail.js +4 -11
- package/dist/commands/types.d.ts +2 -0
- package/dist/commands.js +2 -0
- package/dist/connection/chromeIdentity.d.ts +65 -0
- package/dist/connection/chromeIdentity.js +143 -0
- package/dist/connection/launcher/profilePreferences.d.ts +47 -0
- package/dist/connection/launcher/profilePreferences.js +151 -0
- package/dist/connection/launcher.d.ts +21 -2
- package/dist/connection/launcher.js +42 -16
- package/dist/connection/portReservation.d.ts +14 -4
- package/dist/connection/portReservation.js +21 -6
- package/dist/connection/startupExit.d.ts +8 -0
- package/dist/connection/startupExit.js +15 -6
- package/dist/constants.d.ts +6 -2
- package/dist/constants.js +9 -2
- package/dist/daemon/SessionController.js +23 -7
- package/dist/daemon/errors.d.ts +1 -1
- package/dist/daemon/errors.js +1 -1
- package/dist/daemon/launcher.d.ts +2 -1
- package/dist/daemon/launcher.js +5 -6
- package/dist/daemon/server/SocketServer.js +1 -2
- package/dist/daemon/session/Session.d.ts +13 -0
- package/dist/daemon/session/Session.js +57 -8
- package/dist/daemon/session/chromeConnection.d.ts +9 -0
- package/dist/daemon/session/chromeConnection.js +45 -8
- package/dist/daemon/session/commandRegistry.js +52 -62
- package/dist/daemon/session/interactions.d.ts +35 -9
- package/dist/daemon/session/interactions.js +36 -9
- package/dist/daemon/session/triggeredRequests.d.ts +67 -0
- package/dist/daemon/session/triggeredRequests.js +157 -0
- package/dist/daemon/session/types.d.ts +5 -1
- package/dist/daemon.js +5393 -1600
- package/dist/errors/messages.d.ts +387 -24
- package/dist/errors/messages.js +761 -67
- package/dist/index.js +3976 -1558
- package/dist/ipc/client.d.ts +12 -1
- package/dist/ipc/client.js +22 -3
- package/dist/ipc/protocol/commands.d.ts +89 -4
- package/dist/ipc/protocol/commands.js +2 -0
- package/dist/ipc/protocol/domTypes.d.ts +258 -7
- package/dist/ipc/session/lifecycle.d.ts +8 -1
- package/dist/ipc/session/queries.d.ts +5 -1
- package/dist/ipc/session/types.d.ts +5 -0
- package/dist/ipc/transport/index.d.ts +2 -1
- package/dist/ipc/transport/index.js +2 -2
- package/dist/runtime/dom/actionEffects.d.ts +106 -0
- package/dist/runtime/dom/actionEffects.js +256 -0
- package/dist/runtime/dom/actionEffectsScripts.d.ts +52 -0
- package/dist/runtime/dom/actionEffectsScripts.js +234 -0
- package/dist/runtime/dom/elementGeometry.d.ts +170 -0
- package/dist/runtime/dom/elementGeometry.js +553 -0
- package/dist/runtime/dom/elementInfo.d.ts +77 -0
- package/dist/runtime/dom/elementInfo.js +191 -0
- package/dist/runtime/dom/evalHelpers.d.ts +51 -6
- package/dist/runtime/dom/evalHelpers.js +136 -26
- package/dist/runtime/dom/eventListeners.d.ts +2 -1
- package/dist/runtime/dom/eventListeners.js +174 -47
- package/dist/runtime/dom/formDiscovery.d.ts +1 -1
- package/dist/runtime/dom/formDiscovery.js +116 -16
- package/dist/runtime/dom/formFillHelpers/fill.d.ts +10 -0
- package/dist/runtime/dom/formFillHelpers/fill.js +125 -10
- package/dist/runtime/dom/formFillHelpers/index.d.ts +2 -2
- package/dist/runtime/dom/formFillHelpers/index.js +2 -2
- package/dist/runtime/dom/formFillHelpers/pressKey.js +14 -3
- package/dist/runtime/dom/formFillHelpers/scroll.d.ts +3 -0
- package/dist/runtime/dom/formFillHelpers/scroll.js +60 -18
- package/dist/runtime/dom/formFillHelpers/shared.d.ts +17 -0
- package/dist/runtime/dom/formFillHelpers/shared.js +25 -1
- package/dist/runtime/dom/formFillHelpers/stability.d.ts +20 -6
- package/dist/runtime/dom/formFillHelpers/stability.js +50 -19
- package/dist/runtime/dom/formSubmitHelpers.d.ts +3 -0
- package/dist/runtime/dom/formSubmitHelpers.js +89 -15
- package/dist/runtime/dom/frameLayout.d.ts +60 -0
- package/dist/runtime/dom/frameLayout.js +140 -0
- package/dist/runtime/dom/frameOrigin.d.ts +50 -0
- package/dist/runtime/dom/frameOrigin.js +62 -0
- package/dist/runtime/dom/frameScopedConnection.d.ts +92 -0
- package/dist/runtime/dom/frameScopedConnection.js +252 -0
- package/dist/runtime/dom/frameSelection.d.ts +1 -1
- package/dist/runtime/dom/frameSelection.js +2 -2
- package/dist/runtime/dom/frames.d.ts +25 -2
- package/dist/runtime/dom/frames.js +202 -63
- package/dist/runtime/dom/layout.d.ts +67 -0
- package/dist/runtime/dom/layout.js +333 -0
- package/dist/runtime/dom/listenerPageScripts.d.ts +66 -0
- package/dist/runtime/dom/listenerPageScripts.js +279 -0
- package/dist/runtime/dom/listenerSummary.d.ts +132 -11
- package/dist/runtime/dom/listenerSummary.js +344 -22
- package/dist/runtime/dom/pageActivity.d.ts +41 -0
- package/dist/runtime/dom/pageActivity.js +123 -0
- package/dist/runtime/dom/reactEventHelpers.d.ts +58 -2
- package/dist/runtime/dom/reactEventHelpers.js +212 -41
- package/dist/runtime/dom/targetNode.d.ts +80 -27
- package/dist/runtime/dom/targetNode.js +249 -33
- package/dist/runtime/dom/wait.d.ts +25 -0
- package/dist/runtime/dom/wait.js +199 -0
- package/dist/runtime/dom/waitCondition.d.ts +71 -0
- package/dist/runtime/dom/waitCondition.js +75 -0
- package/dist/runtime/page/emulation.d.ts +51 -0
- package/dist/runtime/page/emulation.js +80 -0
- package/dist/runtime/page/loadingState.d.ts +36 -0
- package/dist/runtime/page/loadingState.js +86 -0
- package/dist/runtime/page/navigation.d.ts +46 -2
- package/dist/runtime/page/navigation.js +69 -33
- package/dist/session/QueryCacheManager.d.ts +11 -1
- package/dist/session/QueryCacheManager.js +25 -3
- package/dist/session/chromeOwners.d.ts +34 -0
- package/dist/session/chromeOwners.js +51 -0
- package/dist/session/cleanup/staleSession.d.ts +11 -1
- package/dist/session/cleanup/staleSession.js +17 -6
- package/dist/session/cleanup/userCommands.js +2 -4
- package/dist/session/metadata.d.ts +5 -1
- package/dist/session/metadata.js +2 -1
- package/dist/session/paths.d.ts +77 -3
- package/dist/session/paths.js +111 -5
- package/dist/session/port.d.ts +31 -7
- package/dist/session/port.js +50 -43
- package/dist/session/portClaims.d.ts +66 -0
- package/dist/session/portClaims.js +284 -0
- package/dist/session/sessionList.d.ts +58 -0
- package/dist/session/sessionList.js +199 -0
- package/dist/session/sessionName.d.ts +46 -0
- package/dist/session/sessionName.js +97 -0
- package/dist/telemetry/a11y.d.ts +8 -3
- package/dist/telemetry/a11y.js +92 -28
- package/dist/telemetry/requestKinds.d.ts +32 -0
- package/dist/telemetry/requestKinds.js +61 -0
- package/dist/telemetry/requestState.d.ts +31 -0
- package/dist/telemetry/requestState.js +38 -0
- package/dist/types.d.ts +80 -3
- package/dist/ui/formatters/a11y.js +3 -0
- package/dist/ui/formatters/console/chronological.d.ts +8 -0
- package/dist/ui/formatters/console/chronological.js +17 -4
- package/dist/ui/formatters/console/json.js +3 -4
- package/dist/ui/formatters/console/shared.d.ts +12 -0
- package/dist/ui/formatters/console.d.ts +2 -2
- package/dist/ui/formatters/console.js +1 -1
- package/dist/ui/formatters/details.js +2 -1
- package/dist/ui/formatters/dom.d.ts +26 -14
- package/dist/ui/formatters/dom.js +65 -52
- package/dist/ui/formatters/form.js +29 -18
- package/dist/ui/formatters/layout.d.ts +31 -0
- package/dist/ui/formatters/layout.js +53 -0
- package/dist/ui/formatters/listeners.d.ts +3 -2
- package/dist/ui/formatters/listeners.js +73 -9
- package/dist/ui/formatters/networkHeaders.js +13 -0
- package/dist/ui/formatters/preview.js +2 -1
- package/dist/ui/formatters/requestStatus.d.ts +1 -17
- package/dist/ui/formatters/requestStatus.js +2 -30
- package/dist/ui/formatters/sessions.d.ts +12 -0
- package/dist/ui/formatters/sessions.js +40 -0
- package/dist/ui/formatters/status.d.ts +21 -2
- package/dist/ui/formatters/status.js +47 -10
- package/dist/ui/formatters/triggeredRequests.d.ts +36 -0
- package/dist/ui/formatters/triggeredRequests.js +65 -0
- package/dist/ui/formatting.d.ts +10 -0
- package/dist/ui/formatting.js +28 -36
- package/dist/ui/messages/chrome.d.ts +9 -0
- package/dist/ui/messages/chrome.js +17 -5
- package/dist/ui/messages/commands.d.ts +388 -14
- package/dist/ui/messages/commands.js +664 -21
- package/dist/ui/messages/consoleMessages.d.ts +10 -0
- package/dist/ui/messages/consoleMessages.js +17 -0
- package/dist/ui/messages/hints.js +2 -1
- package/dist/ui/messages/preview.js +5 -4
- package/dist/ui/messages/session.d.ts +16 -21
- package/dist/ui/messages/session.js +28 -26
- package/dist/ui/messages/sessionCommand.d.ts +43 -0
- package/dist/ui/messages/sessionCommand.js +52 -0
- package/dist/utils/async.d.ts +8 -0
- package/dist/utils/async.js +19 -0
- package/dist/utils/http.d.ts +22 -1
- package/dist/utils/http.js +28 -9
- package/dist/utils/selectorFilters.d.ts +36 -8
- package/dist/utils/selectorFilters.js +267 -53
- package/dist/utils/shellDetection.d.ts +8 -2
- package/dist/utils/shellDetection.js +120 -33
- package/dist/utils/suggestions.d.ts +26 -0
- package/dist/utils/suggestions.js +73 -0
- package/dist/utils/taskMappings.js +10 -0
- package/dist/utils/url.d.ts +12 -2
- package/dist/utils/url.js +69 -7
- package/package.json +1 -1
- package/dist/ui/formatters/sessionFormatters.d.ts +0 -58
- 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
|