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,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Page-side descriptions of elements for DOM command output (`dom query`,
|
|
3
|
+
* `dom layout`, DOM context of console messages): their text as a user sees
|
|
4
|
+
* it and where they live (iframes, shadow roots).
|
|
5
|
+
*/
|
|
6
|
+
/** Length of the text preview shown for elements. */
|
|
7
|
+
const PREVIEW_LENGTH = 80;
|
|
8
|
+
/** Length of the text `dom get` shows for an element. */
|
|
9
|
+
export const ELEMENT_TEXT_LENGTH = 500;
|
|
10
|
+
/**
|
|
11
|
+
* Page-side removal of decorations from an element's text: close buttons
|
|
12
|
+
* (`.close`, `aria-label="Close"` or `"Dismiss"`, a button or link showing
|
|
13
|
+
* just `×`) and `aria-hidden` icons (text without letters or digits), so a
|
|
14
|
+
* flash message does not end in "×". `aria-hidden` text with words is kept:
|
|
15
|
+
* it is often the visible twin of a screen-reader text. Each decoration's
|
|
16
|
+
* text is removed once, from the end.
|
|
17
|
+
*/
|
|
18
|
+
export const WITHOUT_DECORATIONS_JS = `(el, text) => {
|
|
19
|
+
if (!text || typeof el.querySelectorAll !== 'function') return text;
|
|
20
|
+
const glyph = /^\\s*[×✕✖✗⨯]\\s*$/;
|
|
21
|
+
const textOf = (node) => (typeof node.innerText === 'string' ? node.innerText : node.textContent || '').trim();
|
|
22
|
+
const closer = '.close, [aria-label="close" i], [aria-label="dismiss" i]';
|
|
23
|
+
const rendered = (node) => !node.checkVisibility || node.checkVisibility();
|
|
24
|
+
const found = Array.from(el.querySelectorAll(closer + ', [aria-hidden="true"]'))
|
|
25
|
+
.filter((node) => rendered(node) && (node.matches(closer) || !/[\\p{L}\\p{N}]/u.test(textOf(node))));
|
|
26
|
+
if (/[×✕✖✗⨯]/.test(text)) {
|
|
27
|
+
found.push(...Array.from(el.querySelectorAll('button, a, [role="button"]')).filter((node) => glyph.test(node.textContent || '')));
|
|
28
|
+
}
|
|
29
|
+
const unique = Array.from(new Set(found));
|
|
30
|
+
const outermost = unique.filter((node) => !unique.some((other) => other !== node && other.contains(node)));
|
|
31
|
+
let result = text;
|
|
32
|
+
for (const node of outermost) {
|
|
33
|
+
const part = textOf(node);
|
|
34
|
+
const at = part ? result.lastIndexOf(part) : -1;
|
|
35
|
+
if (at >= 0) result = result.slice(0, at) + result.slice(at + part.length);
|
|
36
|
+
}
|
|
37
|
+
return result;
|
|
38
|
+
}`;
|
|
39
|
+
/**
|
|
40
|
+
* Page-side text of an element as a user sees it: `innerText` for a rendered
|
|
41
|
+
* element (CSS-hidden parts left out, inline elements not split apart), none
|
|
42
|
+
* for an element that is not rendered, `textContent` for SVG and other
|
|
43
|
+
* elements without `innerText` and for `display: contents` wrappers (no box
|
|
44
|
+
* of their own, but their children are shown), and the label of an
|
|
45
|
+
* `<option>` (which its `<select>` renders). For large containers (more
|
|
46
|
+
* than 2000 characters of text) only the start is read, from the text nodes
|
|
47
|
+
* whose parent is rendered, so a preview never lays out a whole page's text,
|
|
48
|
+
* unless `full` is set. Decorations are left out ({@link WITHOUT_DECORATIONS_JS}).
|
|
49
|
+
*/
|
|
50
|
+
export const ELEMENT_TEXT_JS = `(el, full) => {
|
|
51
|
+
const withoutDecorations = ${WITHOUT_DECORATIONS_JS};
|
|
52
|
+
const all = el.textContent || '';
|
|
53
|
+
if (el.tagName === 'OPTION') return el.label;
|
|
54
|
+
if (typeof el.innerText !== 'string') return full ? all : all.slice(0, 2000);
|
|
55
|
+
const rendered = (node) => !node.checkVisibility || node.checkVisibility();
|
|
56
|
+
if (!rendered(el)) {
|
|
57
|
+
const boxless = el.ownerDocument.defaultView.getComputedStyle(el).display === 'contents';
|
|
58
|
+
return boxless ? withoutDecorations(el, full ? all : all.slice(0, 2000)) : '';
|
|
59
|
+
}
|
|
60
|
+
if (full || all.length <= 2000) return withoutDecorations(el, el.innerText);
|
|
61
|
+
const walker = el.ownerDocument.createTreeWalker(el, NodeFilter.SHOW_TEXT);
|
|
62
|
+
let start = '';
|
|
63
|
+
while (start.length < 1000 && walker.nextNode()) {
|
|
64
|
+
const parent = walker.currentNode.parentElement;
|
|
65
|
+
if (!parent || rendered(parent)) start += walker.currentNode.data;
|
|
66
|
+
}
|
|
67
|
+
return withoutDecorations(el, start);
|
|
68
|
+
}`;
|
|
69
|
+
/**
|
|
70
|
+
* Page-side short description of an element: tag, id and up to two classes,
|
|
71
|
+
* e.g. `button#save.primary.large`.
|
|
72
|
+
*/
|
|
73
|
+
export const ELEMENT_DESCRIPTION_JS = `(node) => node.tagName.toLowerCase() +
|
|
74
|
+
(node.id ? '#' + node.id : '') +
|
|
75
|
+
(node.classList && node.classList.length ? '.' + Array.from(node.classList).slice(0, 2).join('.') : '')`;
|
|
76
|
+
/**
|
|
77
|
+
* Page-side position of an element among its parent's children with the
|
|
78
|
+
* same short description ({@link ELEMENT_DESCRIPTION_JS}), e.g. `(2nd of 3)`;
|
|
79
|
+
* empty when it is the only one.
|
|
80
|
+
*/
|
|
81
|
+
export const SIBLING_POSITION_JS = `(el) => {
|
|
82
|
+
const describe = ${ELEMENT_DESCRIPTION_JS};
|
|
83
|
+
const parent = el.parentElement;
|
|
84
|
+
if (!parent) return '';
|
|
85
|
+
const own = describe(el);
|
|
86
|
+
const same = Array.from(parent.children).filter((child) => describe(child) === own);
|
|
87
|
+
if (same.length < 2) return '';
|
|
88
|
+
const n = same.indexOf(el) + 1;
|
|
89
|
+
const teen = n % 100 >= 11 && n % 100 <= 13;
|
|
90
|
+
const suffix = teen ? 'th' : ({ 1: 'st', 2: 'nd', 3: 'rd' })[n % 10] || 'th';
|
|
91
|
+
return '(' + n + suffix + ' of ' + same.length + ')';
|
|
92
|
+
}`;
|
|
93
|
+
/**
|
|
94
|
+
* Page-side identity of an element an action hit, so the output says which
|
|
95
|
+
* one it was, as one string: its short description
|
|
96
|
+
* ({@link ELEMENT_DESCRIPTION_JS}) and visible text (button value for button
|
|
97
|
+
* inputs, decorations left out), e.g. `button#add.btn "Add to cart"`. A
|
|
98
|
+
* `<select>` is named by its label, aria-label or name, else by its selected
|
|
99
|
+
* option, e.g. `select.sort "Sort products"`. An element without visible
|
|
100
|
+
* text is described by itself first: by its position among same-looking
|
|
101
|
+
* siblings ({@link SIBLING_POSITION_JS}), e.g. `div.figure (2nd of 3)`, with
|
|
102
|
+
* its aria-label, placeholder or title when it has one. Only an element
|
|
103
|
+
* without an id that is the only one of its kind is named by the nearest of
|
|
104
|
+
* three ancestors that has text, e.g.
|
|
105
|
+
* `input.toggle in div.view "Write report"` (rows of a list share their
|
|
106
|
+
* aria-label), leaving out the options
|
|
107
|
+
* of `<select>`s in it (read like `innerText`: CSS-hidden text is left out of
|
|
108
|
+
* a rendered ancestor; at most 500 text nodes; selects in shadow roots are
|
|
109
|
+
* not looked into), and never past an editable ancestor, whose text may be
|
|
110
|
+
* typed input; otherwise its aria-label, placeholder or title is used. Texts
|
|
111
|
+
* are cut at 40 characters. Contenteditable elements count as controls: what
|
|
112
|
+
* was typed into them is never echoed (nor their ancestors' text).
|
|
113
|
+
*/
|
|
114
|
+
export const ELEMENT_IDENTITY_JS = `(el) => {
|
|
115
|
+
const describe = ${ELEMENT_DESCRIPTION_JS};
|
|
116
|
+
const siblingPosition = ${SIBLING_POSITION_JS};
|
|
117
|
+
const withoutDecorations = ${WITHOUT_DECORATIONS_JS};
|
|
118
|
+
const clean = (text) => (text || '').replace(/\\s+/g, ' ').trim();
|
|
119
|
+
const cut = (text) => {
|
|
120
|
+
const characters = Array.from(text);
|
|
121
|
+
return characters.length > 40 ? characters.slice(0, 40).join('') + '…' : text;
|
|
122
|
+
};
|
|
123
|
+
const isControl = (node) => /^(input|select|textarea)$/.test(node.localName) || node.isContentEditable;
|
|
124
|
+
const shownText = (node) =>
|
|
125
|
+
isControl(node) ? '' : clean(withoutDecorations(node, typeof node.innerText === 'string' ? node.innerText : node.textContent));
|
|
126
|
+
const buttonValue = (node) => (node.localName === 'input' && /^(submit|button|reset)$/i.test(node.type) ? clean(node.value) : '');
|
|
127
|
+
const attributeText = (node) =>
|
|
128
|
+
clean(node.getAttribute('aria-label')) || clean(node.getAttribute('placeholder')) || clean(node.getAttribute('title'));
|
|
129
|
+
const selectName = (node) =>
|
|
130
|
+
clean(node.labels && node.labels[0] && node.labels[0].innerText) ||
|
|
131
|
+
clean(node.getAttribute('aria-label')) ||
|
|
132
|
+
clean(node.getAttribute('name')) ||
|
|
133
|
+
clean(node.selectedOptions && node.selectedOptions[0] && node.selectedOptions[0].label);
|
|
134
|
+
const shown = (node, options) => !node.checkVisibility || node.checkVisibility(options);
|
|
135
|
+
const textOutsideSelects = (node) => {
|
|
136
|
+
if (!node.querySelector('select')) return shownText(node);
|
|
137
|
+
const hiddenLeftOut = shown(node);
|
|
138
|
+
const walker = node.ownerDocument.createTreeWalker(node, NodeFilter.SHOW_TEXT);
|
|
139
|
+
let text = '';
|
|
140
|
+
for (let visited = 0; visited < 500 && text.length < 200 && walker.nextNode(); visited++) {
|
|
141
|
+
const parent = walker.currentNode.parentElement;
|
|
142
|
+
if (!parent || parent.closest('select, textarea')) continue;
|
|
143
|
+
if (!hiddenLeftOut || shown(parent, { visibilityProperty: true })) text += ' ' + walker.currentNode.data;
|
|
144
|
+
}
|
|
145
|
+
return clean(text);
|
|
146
|
+
};
|
|
147
|
+
const quoted = (text) => (text ? ' "' + cut(text) + '"' : '');
|
|
148
|
+
const visible = shownText(el) || buttonValue(el) || (el.localName === 'select' ? selectName(el) : '');
|
|
149
|
+
if (visible) return describe(el) + quoted(visible);
|
|
150
|
+
const attribute = attributeText(el);
|
|
151
|
+
const position = siblingPosition(el);
|
|
152
|
+
if (position) return describe(el) + ' ' + position + quoted(attribute);
|
|
153
|
+
let ancestor = el.id || el.isContentEditable ? null : el.parentElement;
|
|
154
|
+
for (let depth = 0; ancestor && depth < 3; depth++, ancestor = ancestor.parentElement) {
|
|
155
|
+
if (isControl(ancestor)) break;
|
|
156
|
+
const text = textOutsideSelects(ancestor);
|
|
157
|
+
if (text) return describe(el) + ' in ' + describe(ancestor) + quoted(text);
|
|
158
|
+
}
|
|
159
|
+
return describe(el) + quoted(attribute);
|
|
160
|
+
}`;
|
|
161
|
+
/**
|
|
162
|
+
* Page-side location of an element: the iframes it is in (outermost first)
|
|
163
|
+
* and the shadow root holding it, e.g. `iframe#pay > shadow root of <x-card>`;
|
|
164
|
+
* empty for the main document.
|
|
165
|
+
*/
|
|
166
|
+
export const ELEMENT_CONTEXT_JS = `(el) => {
|
|
167
|
+
const describe = (node) => node.tagName.toLowerCase() + (node.id ? '#' + node.id : '');
|
|
168
|
+
const parts = [];
|
|
169
|
+
for (let doc = el.ownerDocument; doc && doc.defaultView && doc.defaultView.frameElement; ) {
|
|
170
|
+
const frame = doc.defaultView.frameElement;
|
|
171
|
+
parts.unshift(describe(frame));
|
|
172
|
+
doc = frame.ownerDocument;
|
|
173
|
+
}
|
|
174
|
+
const root = el.getRootNode();
|
|
175
|
+
if (root.host) parts.push('shadow root of <' + describe(root.host) + '>');
|
|
176
|
+
return parts.join(' > ');
|
|
177
|
+
}`;
|
|
178
|
+
/**
|
|
179
|
+
* Short text preview of an element's text: whitespace collapsed, cut on a
|
|
180
|
+
* whole character (an emoji is never split, which would make JSON invalid).
|
|
181
|
+
*
|
|
182
|
+
* @param text - Element text as the page renders it
|
|
183
|
+
* @param length - Characters kept
|
|
184
|
+
* @returns Collapsed text, truncated to `length` characters (followed by `...`)
|
|
185
|
+
*/
|
|
186
|
+
export function textPreview(text, length = PREVIEW_LENGTH) {
|
|
187
|
+
const collapsed = text.replace(/\s+/g, ' ').trim();
|
|
188
|
+
const characters = Array.from(collapsed);
|
|
189
|
+
return characters.length > length ? characters.slice(0, length).join('') + '...' : collapsed;
|
|
190
|
+
}
|
|
191
|
+
//# sourceMappingURL=elementInfo.js.map
|
|
@@ -31,6 +31,25 @@ export interface EvalResult {
|
|
|
31
31
|
* throwing getter becomes `[Error: …]`.
|
|
32
32
|
*/
|
|
33
33
|
export declare const JSON_SAFE_COPY_FUNCTION = "function () {\n const MAX_ITEMS = 1000;\n const ancestors = new Set();\n const kind = (value) => Object.prototype.toString.call(value).slice(8, -1);\n const isNode = (value) => typeof value.nodeType === 'number' && typeof value.nodeName === 'string';\n const describeNode = (node) => {\n if (node.nodeType !== 1) return node.nodeName.toLowerCase();\n return node.tagName.toLowerCase() + (node.id ? '#' + node.id : '') +\n (node.classList && node.classList.length ? '.' + Array.from(node.classList).join('.') : '');\n };\n const items = (list, next) => {\n const result = [];\n let count = 0;\n for (const item of list) {\n if (count++ === MAX_ITEMS) { result.push('\u2026'); break; }\n result.push(next(item));\n }\n return result;\n };\n const leaf = (value) => {\n if (value === undefined) return null;\n if (typeof value === 'number') {\n if (Number.isNaN(value) || !Number.isFinite(value)) return String(value);\n return Object.is(value, -0) ? '-0' : value;\n }\n if (typeof value === 'bigint') return value + 'n';\n if (typeof value === 'symbol') return value.toString();\n if (typeof value === 'function') return 'function ' + (value.name || '(anonymous)') + '()';\n return value;\n };\n const copy = (value, depth) => {\n if (value === null || typeof value !== 'object') return leaf(value);\n if (ancestors.has(value)) return '[Circular]';\n if (depth > 20) return '[\u2026]';\n const next = (item) => copy(item, depth + 1);\n const type = kind(value);\n if (isNode(value)) return describeNode(value);\n if (value.window === value) return 'Window';\n if (type === 'Date') return isNaN(value) ? 'Invalid Date' : value.toISOString();\n if (type === 'RegExp') return String(value);\n if (type === 'Error' || value instanceof Error) return value.name + ': ' + value.message;\n ancestors.add(value);\n try {\n if (type === 'Map') return items(value, ([k, v]) => [next(k), next(v)]);\n if (type === 'Set' || type === 'NodeList' || type === 'HTMLCollection') return items(value, next);\n if (ArrayBuffer.isView(value) && type !== 'DataView') return items(value, next);\n if (Array.isArray(value)) return items(value, next);\n const result = {};\n const keys = Object.keys(value);\n for (const key of keys.slice(0, MAX_ITEMS)) {\n try { result[key] = next(value[key]); } catch (e) { result[key] = '[Error: ' + (e && e.message) + ']'; }\n }\n if (keys.length > MAX_ITEMS) result['\u2026'] = (keys.length - MAX_ITEMS) + ' more keys';\n return result;\n } finally {\n ancestors.delete(value);\n }\n };\n return copy(this, 0);\n}";
|
|
34
|
+
/** What a busy target is called in messages: the page, or an iframe (`--frame`) */
|
|
35
|
+
export type BusyScope = 'page' | 'frame';
|
|
36
|
+
/** When {@link withBusyPageRecovery} checks the target, and what it calls it */
|
|
37
|
+
export interface BusyRecoveryOptions {
|
|
38
|
+
/** Time after which a command that has not answered gets the target checked */
|
|
39
|
+
busyAfterMs?: number;
|
|
40
|
+
/** How long the target gets to answer the check */
|
|
41
|
+
livenessMs?: number;
|
|
42
|
+
/** The page (default) or an iframe, for messages */
|
|
43
|
+
scope?: BusyScope;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Whether Chrome failed a command because the document it ran in went away
|
|
47
|
+
* (navigation, a removed iframe).
|
|
48
|
+
*
|
|
49
|
+
* @param error - Error thrown by a CDP command
|
|
50
|
+
* @returns True for a lost context
|
|
51
|
+
*/
|
|
52
|
+
export declare function isContextLostError(error: unknown): boolean;
|
|
34
53
|
/**
|
|
35
54
|
* Run a CDP command on the page, recovering a page kept busy by its own
|
|
36
55
|
* scripts. If the command has not answered when a script would have been
|
|
@@ -41,21 +60,45 @@ export declare const JSON_SAFE_COPY_FUNCTION = "function () {\n const MAX_ITEMS
|
|
|
41
60
|
*
|
|
42
61
|
* @param cdp - CDP connection
|
|
43
62
|
* @param command - The pending command
|
|
44
|
-
* @param
|
|
63
|
+
* @param options - When to check the page, how long it gets to answer, what to call it
|
|
45
64
|
* @returns The command's result
|
|
46
65
|
* @throws CommandError (102) when the page was busy
|
|
47
66
|
*/
|
|
48
|
-
export declare function withBusyPageRecovery<T>(cdp: CDPSender, command: Promise<T>,
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
67
|
+
export declare function withBusyPageRecovery<T>(cdp: CDPSender, command: Promise<T>, options?: BusyRecoveryOptions): Promise<T>;
|
|
68
|
+
/**
|
|
69
|
+
* The work's value if it settles within `ms` (its rejection is passed on).
|
|
70
|
+
*
|
|
71
|
+
* @param work - Pending work
|
|
72
|
+
* @param ms - Time limit
|
|
73
|
+
* @returns `{ settled: true, value }`, or `{ settled: false }` when time ran out
|
|
74
|
+
*/
|
|
75
|
+
export declare function settledWithin<T>(work: Promise<T>, ms: number): Promise<{
|
|
76
|
+
settled: true;
|
|
77
|
+
value: T;
|
|
78
|
+
} | {
|
|
79
|
+
settled: false;
|
|
80
|
+
}>;
|
|
52
81
|
/** Where a `bdg dom eval` script runs (default: the page's main frame) */
|
|
53
82
|
export interface EvalTarget {
|
|
54
83
|
/** Session of an attached target (out-of-process iframe) */
|
|
55
84
|
sessionId?: string;
|
|
85
|
+
/**
|
|
86
|
+
* Where to check and recover the target when it does not answer (default:
|
|
87
|
+
* the session itself); a session attached while the target was busy gets
|
|
88
|
+
* no answer at all
|
|
89
|
+
*/
|
|
90
|
+
recovery?: CDPSender;
|
|
56
91
|
/** Execution context of a frame (`ExecutionContextDescription.uniqueId`) */
|
|
57
92
|
uniqueContextId?: string;
|
|
58
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* Whether the page still answers. A page that does not answer in time is
|
|
96
|
+
* taken as open: commands wait while a navigation is pending.
|
|
97
|
+
*
|
|
98
|
+
* @param cdp - CDP connection of the page
|
|
99
|
+
* @returns False when the page (tab) was closed
|
|
100
|
+
*/
|
|
101
|
+
export declare function pageStillOpen(cdp: CDPSender): Promise<boolean>;
|
|
59
102
|
/**
|
|
60
103
|
* Evaluate a `bdg dom eval` script.
|
|
61
104
|
*
|
|
@@ -70,7 +113,9 @@ export interface EvalTarget {
|
|
|
70
113
|
* @param script - JavaScript expression
|
|
71
114
|
* @param target - Session and execution context to run in (default: the page)
|
|
72
115
|
* @returns Value and type
|
|
73
|
-
* @throws CommandError (102) when the script was terminated
|
|
116
|
+
* @throws CommandError (102) when the script was terminated or a promise did
|
|
117
|
+
* not settle, (91) when it threw, (83) when the page navigated while it ran
|
|
118
|
+
* (in a frame, Chrome's context-lost error is passed on for the caller to explain)
|
|
74
119
|
*/
|
|
75
120
|
export declare function evaluateScript(cdp: CDPConnection, script: string, target?: EvalTarget): Promise<EvalResult>;
|
|
76
121
|
//# sourceMappingURL=evalHelpers.d.ts.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { CDPProtocolError } from '../../connection/errors.js';
|
|
1
2
|
import { CommandError } from '../../errors/index.js';
|
|
2
|
-
import { navigationPendingError, pageBusyError, promiseTimeoutError, scriptExecutionError, scriptTimeoutError, } from '../../errors/messages.js';
|
|
3
|
+
import { navigationPendingError, pageBusyError, pageClosedDuringEvalError, pageNavigatedDuringEvalError, promiseTimeoutError, scriptExecutionError, scriptTimeoutError, } from '../../errors/messages.js';
|
|
3
4
|
import { pendingNavigationUrl } from '../page/navigation.js';
|
|
4
5
|
import { senderFor } from '../../telemetry/objectExpander.js';
|
|
5
6
|
import { formatRemoteObject } from '../../telemetry/remoteObject.js';
|
|
@@ -302,9 +303,10 @@ async function withDeadline(work, ms, onTimeout) {
|
|
|
302
303
|
* answering because a navigation still waits for the server is left alone.
|
|
303
304
|
*
|
|
304
305
|
* @param cdp - CDP connection
|
|
306
|
+
* @param scope - Whether the target is the page or an iframe
|
|
305
307
|
* @returns The timeout error to report
|
|
306
308
|
*/
|
|
307
|
-
async function terminatePageScripts(cdp) {
|
|
309
|
+
async function terminatePageScripts(cdp, scope) {
|
|
308
310
|
const pendingUrl = await pendingNavigationUrl(cdp).catch((error) => {
|
|
309
311
|
log.debug(`Could not check for a pending navigation: ${getErrorMessage(error)}`);
|
|
310
312
|
return undefined;
|
|
@@ -313,14 +315,50 @@ async function terminatePageScripts(cdp) {
|
|
|
313
315
|
const err = navigationPendingError(pendingUrl);
|
|
314
316
|
return new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.CDP_TIMEOUT);
|
|
315
317
|
}
|
|
316
|
-
await cdp
|
|
317
|
-
.send('Runtime.terminateExecution')
|
|
318
|
-
.catch((error) => log.debug(`terminateExecution failed: ${getErrorMessage(error)}`));
|
|
319
|
-
const err = pageBusyError(EVAL_TIMEOUT_MS);
|
|
318
|
+
const err = pageBusyError(EVAL_TIMEOUT_MS, scope, await terminateExecution(cdp));
|
|
320
319
|
return new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.CDP_TIMEOUT);
|
|
321
320
|
}
|
|
321
|
+
/**
|
|
322
|
+
* Ask V8 to terminate the target's running script.
|
|
323
|
+
*
|
|
324
|
+
* @param cdp - CDP connection of the target
|
|
325
|
+
* @returns Whether the target acknowledged it in time
|
|
326
|
+
*/
|
|
327
|
+
async function terminateExecution(cdp) {
|
|
328
|
+
try {
|
|
329
|
+
const stopped = await settledWithin(cdp.send('Runtime.terminateExecution'), LIVENESS_CHECK_MS);
|
|
330
|
+
return stopped.settled;
|
|
331
|
+
}
|
|
332
|
+
catch (error) {
|
|
333
|
+
log.debug(`terminateExecution failed: ${getErrorMessage(error)}`);
|
|
334
|
+
return false;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
322
337
|
/** How long a page gets to answer a liveness check before it counts as busy */
|
|
323
338
|
const LIVENESS_CHECK_MS = 2_000;
|
|
339
|
+
/**
|
|
340
|
+
* Whether the target answers a trivial evaluation in time.
|
|
341
|
+
*
|
|
342
|
+
* @param cdp - CDP connection
|
|
343
|
+
* @param ms - Time limit
|
|
344
|
+
* @returns False when its scripts keep it busy
|
|
345
|
+
*/
|
|
346
|
+
async function isAnswering(cdp, ms) {
|
|
347
|
+
const probe = await settledWithin(cdp.send('Runtime.evaluate', { expression: '1', returnByValue: true }), ms);
|
|
348
|
+
return probe.settled;
|
|
349
|
+
}
|
|
350
|
+
/** Chrome's messages for a command whose execution context or target went away */
|
|
351
|
+
const CONTEXT_LOST_PATTERN = /Execution context was destroyed|Inspected target navigated or closed|Cannot find context with specified id|uniqueContextId not found|Session with given id not found/i;
|
|
352
|
+
/**
|
|
353
|
+
* Whether Chrome failed a command because the document it ran in went away
|
|
354
|
+
* (navigation, a removed iframe).
|
|
355
|
+
*
|
|
356
|
+
* @param error - Error thrown by a CDP command
|
|
357
|
+
* @returns True for a lost context
|
|
358
|
+
*/
|
|
359
|
+
export function isContextLostError(error) {
|
|
360
|
+
return error instanceof CDPProtocolError && CONTEXT_LOST_PATTERN.test(error.message);
|
|
361
|
+
}
|
|
324
362
|
/**
|
|
325
363
|
* Run a CDP command on the page, recovering a page kept busy by its own
|
|
326
364
|
* scripts. If the command has not answered when a script would have been
|
|
@@ -331,19 +369,19 @@ const LIVENESS_CHECK_MS = 2_000;
|
|
|
331
369
|
*
|
|
332
370
|
* @param cdp - CDP connection
|
|
333
371
|
* @param command - The pending command
|
|
334
|
-
* @param
|
|
372
|
+
* @param options - When to check the page, how long it gets to answer, what to call it
|
|
335
373
|
* @returns The command's result
|
|
336
374
|
* @throws CommandError (102) when the page was busy
|
|
337
375
|
*/
|
|
338
|
-
export async function withBusyPageRecovery(cdp, command,
|
|
376
|
+
export async function withBusyPageRecovery(cdp, command, options = {}) {
|
|
377
|
+
const { busyAfterMs = EVAL_TIMEOUT_MS + TERMINATION_GRACE_MS, livenessMs = LIVENESS_CHECK_MS, scope = 'page', } = options;
|
|
339
378
|
command.catch(() => undefined);
|
|
340
|
-
const early = await settledWithin(command,
|
|
379
|
+
const early = await settledWithin(command, busyAfterMs);
|
|
341
380
|
if (early.settled)
|
|
342
381
|
return early.value;
|
|
343
|
-
|
|
344
|
-
if (probe.settled)
|
|
382
|
+
if (await isAnswering(cdp, livenessMs))
|
|
345
383
|
return command;
|
|
346
|
-
throw await terminatePageScripts(cdp);
|
|
384
|
+
throw await terminatePageScripts(cdp, scope);
|
|
347
385
|
}
|
|
348
386
|
/**
|
|
349
387
|
* The work's value if it settles within `ms` (its rejection is passed on).
|
|
@@ -352,7 +390,7 @@ export async function withBusyPageRecovery(cdp, command, limits = { busyAfterMs:
|
|
|
352
390
|
* @param ms - Time limit
|
|
353
391
|
* @returns `{ settled: true, value }`, or `{ settled: false }` when time ran out
|
|
354
392
|
*/
|
|
355
|
-
async function settledWithin(work, ms) {
|
|
393
|
+
export async function settledWithin(work, ms) {
|
|
356
394
|
let timer;
|
|
357
395
|
const timeout = new Promise((resolve) => {
|
|
358
396
|
timer = setTimeout(() => resolve({ settled: false }), ms);
|
|
@@ -364,6 +402,80 @@ async function settledWithin(work, ms) {
|
|
|
364
402
|
clearTimeout(timer);
|
|
365
403
|
}
|
|
366
404
|
}
|
|
405
|
+
/**
|
|
406
|
+
* The error for an evaluation that did not answer in time: a target that
|
|
407
|
+
* still answers was only waiting for a promise that never settled
|
|
408
|
+
* (`await new Promise(() => {})`); one that does not answer is busy, and its
|
|
409
|
+
* scripts are terminated.
|
|
410
|
+
*
|
|
411
|
+
* @param cdp - CDP connection of the target
|
|
412
|
+
* @param scope - Whether the script ran in the page or an iframe
|
|
413
|
+
* @returns The error to report (102)
|
|
414
|
+
*/
|
|
415
|
+
async function evaluationTimeoutError(cdp, scope) {
|
|
416
|
+
if (await isAnswering(cdp, LIVENESS_CHECK_MS))
|
|
417
|
+
return promiseTimeout();
|
|
418
|
+
return terminatePageScripts(cdp, scope);
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* A promise the script awaited or returned did not settle in time.
|
|
422
|
+
*
|
|
423
|
+
* @returns Command error (102)
|
|
424
|
+
*/
|
|
425
|
+
function promiseTimeout() {
|
|
426
|
+
const err = promiseTimeoutError(EVAL_TIMEOUT_MS);
|
|
427
|
+
return new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.CDP_TIMEOUT);
|
|
428
|
+
}
|
|
429
|
+
/** Chrome's message for a command sent to a session (target) that is gone */
|
|
430
|
+
const SESSION_GONE_PATTERN = /Session with given id not found/i;
|
|
431
|
+
/**
|
|
432
|
+
* Whether the page still answers. A page that does not answer in time is
|
|
433
|
+
* taken as open: commands wait while a navigation is pending.
|
|
434
|
+
*
|
|
435
|
+
* @param cdp - CDP connection of the page
|
|
436
|
+
* @returns False when the page (tab) was closed
|
|
437
|
+
*/
|
|
438
|
+
export async function pageStillOpen(cdp) {
|
|
439
|
+
try {
|
|
440
|
+
await settledWithin(cdp.send('Runtime.evaluate', { expression: '1', returnByValue: true }), LIVENESS_CHECK_MS);
|
|
441
|
+
return true;
|
|
442
|
+
}
|
|
443
|
+
catch (error) {
|
|
444
|
+
log.debug(`Page did not answer: ${getErrorMessage(error)}`);
|
|
445
|
+
return false;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* The error for a page script whose context went away: the page navigated,
|
|
450
|
+
* or the tab was closed.
|
|
451
|
+
*
|
|
452
|
+
* @param error - Chrome's context-lost error
|
|
453
|
+
* @param cdp - CDP connection of the page
|
|
454
|
+
* @returns Command error (83)
|
|
455
|
+
*/
|
|
456
|
+
async function pageContextLostError(error, cdp) {
|
|
457
|
+
const sessionGone = SESSION_GONE_PATTERN.test(getErrorMessage(error));
|
|
458
|
+
const err = sessionGone || !(await pageStillOpen(cdp))
|
|
459
|
+
? pageClosedDuringEvalError()
|
|
460
|
+
: pageNavigatedDuringEvalError();
|
|
461
|
+
return new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.RESOURCE_NOT_FOUND);
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* `Runtime.evaluate` options of a `bdg dom eval` script.
|
|
465
|
+
*
|
|
466
|
+
* @param target - Execution context to run in
|
|
467
|
+
* @returns Options (REPL mode, previews, 20 s limit)
|
|
468
|
+
*/
|
|
469
|
+
function evaluateOptions(target) {
|
|
470
|
+
return {
|
|
471
|
+
returnByValue: false,
|
|
472
|
+
generatePreview: true,
|
|
473
|
+
replMode: true,
|
|
474
|
+
objectGroup: EVAL_OBJECT_GROUP,
|
|
475
|
+
timeout: EVAL_TIMEOUT_MS,
|
|
476
|
+
...(target.uniqueContextId && { uniqueContextId: target.uniqueContextId }),
|
|
477
|
+
};
|
|
478
|
+
}
|
|
367
479
|
/**
|
|
368
480
|
* Evaluate a `bdg dom eval` script.
|
|
369
481
|
*
|
|
@@ -378,25 +490,23 @@ async function settledWithin(work, ms) {
|
|
|
378
490
|
* @param script - JavaScript expression
|
|
379
491
|
* @param target - Session and execution context to run in (default: the page)
|
|
380
492
|
* @returns Value and type
|
|
381
|
-
* @throws CommandError (102) when the script was terminated
|
|
493
|
+
* @throws CommandError (102) when the script was terminated or a promise did
|
|
494
|
+
* not settle, (91) when it threw, (83) when the page navigated while it ran
|
|
495
|
+
* (in a frame, Chrome's context-lost error is passed on for the caller to explain)
|
|
382
496
|
*/
|
|
383
497
|
export async function evaluateScript(cdp, script, target = {}) {
|
|
384
498
|
const session = senderFor(cdp, target.sessionId);
|
|
499
|
+
const scope = target.uniqueContextId ? 'frame' : 'page';
|
|
385
500
|
try {
|
|
386
|
-
const response = await withDeadline(executeScript(session, script,
|
|
387
|
-
|
|
388
|
-
generatePreview: true,
|
|
389
|
-
replMode: true,
|
|
390
|
-
objectGroup: EVAL_OBJECT_GROUP,
|
|
391
|
-
timeout: EVAL_TIMEOUT_MS,
|
|
392
|
-
...(target.uniqueContextId && { uniqueContextId: target.uniqueContextId }),
|
|
393
|
-
}), EVAL_TIMEOUT_MS + TERMINATION_GRACE_MS, () => terminatePageScripts(session));
|
|
394
|
-
const settled = await withDeadline(settlePromise(session, response.result, script), EVAL_TIMEOUT_MS, () => {
|
|
395
|
-
const err = promiseTimeoutError(EVAL_TIMEOUT_MS);
|
|
396
|
-
return new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.CDP_TIMEOUT);
|
|
397
|
-
});
|
|
501
|
+
const response = await withDeadline(executeScript(session, script, evaluateOptions(target)), EVAL_TIMEOUT_MS + TERMINATION_GRACE_MS, () => evaluationTimeoutError(target.recovery ?? session, scope));
|
|
502
|
+
const settled = await withDeadline(settlePromise(session, response.result, script), EVAL_TIMEOUT_MS, promiseTimeout);
|
|
398
503
|
return await toEvalResult(session, settled);
|
|
399
504
|
}
|
|
505
|
+
catch (error) {
|
|
506
|
+
throw scope === 'page' && isContextLostError(error)
|
|
507
|
+
? await pageContextLostError(error, session)
|
|
508
|
+
: error;
|
|
509
|
+
}
|
|
400
510
|
finally {
|
|
401
511
|
void session
|
|
402
512
|
.send('Runtime.releaseObjectGroup', { objectGroup: EVAL_OBJECT_GROUP })
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* Collects the listeners of the element, every ancestor (through open shadow
|
|
5
5
|
* roots), its document and its window with `DOMDebugger.getEventListeners`,
|
|
6
6
|
* so handlers that frameworks attach by delegation (React on its root
|
|
7
|
-
* container, jQuery on `document`) are found too
|
|
7
|
+
* container, jQuery on `document`) are found too, along with the handlers
|
|
8
|
+
* behind them (jQuery's handlers, React's `on…` props). The Debugger domain is not
|
|
8
9
|
* enabled: that would make `debugger;` statements pause the page.
|
|
9
10
|
*/
|
|
10
11
|
import type { CDPConnection } from '../../connection/cdp.js';
|