browser-debugger-cli 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/bdg/SKILL.md +4 -4
- package/README.md +143 -79
- package/dist/commands/cdp.d.ts +22 -1
- package/dist/commands/cdp.js +100 -43
- package/dist/commands/console.d.ts +12 -0
- package/dist/commands/console.js +62 -12
- package/dist/commands/css.d.ts +13 -0
- package/dist/commands/css.js +53 -0
- package/dist/commands/dom/DomElementResolver.d.ts +3 -1
- package/dist/commands/dom/DomElementResolver.js +10 -3
- package/dist/commands/dom/a11y.js +3 -2
- package/dist/commands/dom/audit.d.ts +14 -0
- package/dist/commands/dom/audit.js +87 -0
- package/dist/commands/dom/eval.d.ts +3 -2
- package/dist/commands/dom/eval.js +11 -5
- package/dist/commands/dom/form.js +10 -9
- package/dist/commands/dom/formInteraction.js +42 -11
- package/dist/commands/dom/get.js +8 -8
- package/dist/commands/dom/helpers/index.d.ts +1 -1
- package/dist/commands/dom/helpers/index.js +1 -1
- package/dist/commands/dom/helpers/keyAttributes.d.ts +3 -2
- package/dist/commands/dom/helpers/keyAttributes.js +6 -4
- package/dist/commands/dom/helpers/query.d.ts +27 -3
- package/dist/commands/dom/helpers/query.js +152 -64
- package/dist/commands/dom/helpers/screenshot.d.ts +1 -0
- package/dist/commands/dom/helpers/screenshot.js +169 -49
- package/dist/commands/dom/index.js +7 -2
- package/dist/commands/dom/query.d.ts +19 -2
- package/dist/commands/dom/query.js +37 -6
- package/dist/commands/dom/screenshot.js +12 -7
- package/dist/commands/dom/semanticUtils.d.ts +3 -2
- package/dist/commands/dom/semanticUtils.js +40 -9
- package/dist/commands/dom/wait.js +5 -3
- package/dist/commands/helpJson.d.ts +82 -19
- package/dist/commands/helpJson.js +112 -41
- package/dist/commands/helpTopic.d.ts +16 -1
- package/dist/commands/helpTopic.js +59 -1
- package/dist/commands/installSkill.d.ts +15 -5
- package/dist/commands/installSkill.js +86 -16
- package/dist/commands/network/list.js +22 -12
- package/dist/commands/optionBehaviors.js +53 -16
- package/dist/commands/page.js +7 -4
- package/dist/commands/peek.d.ts +7 -0
- package/dist/commands/peek.js +65 -23
- package/dist/commands/shared/daemonErrorHandler.d.ts +5 -2
- package/dist/commands/shared/daemonErrorHandler.js +20 -9
- package/dist/commands/shared/dataFetcher.d.ts +12 -4
- package/dist/commands/shared/dataFetcher.js +12 -4
- package/dist/commands/shared/followMode.d.ts +9 -1
- package/dist/commands/shared/followMode.js +22 -4
- package/dist/commands/shared/optionTypes.d.ts +9 -2
- package/dist/commands/shared/outputFile.js +6 -1
- package/dist/commands/start.d.ts +20 -5
- package/dist/commands/start.js +84 -23
- package/dist/commands/stop.d.ts +11 -0
- package/dist/commands/stop.js +24 -1
- package/dist/commands/tail.d.ts +7 -1
- package/dist/commands/tail.js +13 -62
- package/dist/commands.js +2 -0
- package/dist/connection/cdp.d.ts +7 -0
- package/dist/connection/cdp.js +9 -0
- package/dist/connection/launcher.js +3 -2
- package/dist/daemon/SessionController.js +6 -1
- package/dist/daemon/launcher.d.ts +3 -2
- package/dist/daemon/launcher.js +47 -3
- package/dist/daemon/session/Session.d.ts +4 -1
- package/dist/daemon/session/Session.js +33 -2
- package/dist/daemon/session/TelemetryStore.d.ts +8 -1
- package/dist/daemon/session/TelemetryStore.js +13 -1
- package/dist/daemon/session/commandRegistry.js +36 -14
- package/dist/daemon/session/interactions.d.ts +2 -1
- package/dist/daemon/session/interactions.js +13 -1
- package/dist/daemon/session/plugins.js +19 -53
- package/dist/daemon/session/teardown.js +1 -1
- package/dist/daemon.js +9234 -7222
- package/dist/errors/messages.d.ts +88 -15
- package/dist/errors/messages.js +177 -27
- package/dist/index.js +19322 -13961
- package/dist/ipc/client.d.ts +22 -2
- package/dist/ipc/client.js +34 -5
- package/dist/ipc/protocol/auditTypes.d.ts +135 -0
- package/dist/ipc/protocol/auditTypes.js +6 -0
- package/dist/ipc/protocol/commands.d.ts +35 -0
- package/dist/ipc/protocol/commands.js +2 -0
- package/dist/ipc/protocol/domTypes.d.ts +16 -0
- package/dist/ipc/protocol/inspectTypes.d.ts +73 -8
- package/dist/ipc/session/types.d.ts +2 -0
- package/dist/runtime/css/search.d.ts +39 -0
- package/dist/runtime/css/search.js +122 -0
- package/dist/runtime/dom/actionEffects.d.ts +9 -2
- package/dist/runtime/dom/actionEffects.js +30 -14
- package/dist/runtime/dom/audit.d.ts +19 -0
- package/dist/runtime/dom/audit.js +37 -0
- package/dist/runtime/dom/auditModel.d.ts +45 -0
- package/dist/runtime/dom/auditModel.js +220 -0
- package/dist/runtime/dom/auditScripts.d.ts +113 -0
- package/dist/runtime/dom/auditScripts.js +148 -0
- package/dist/runtime/dom/elementGeometry.d.ts +16 -3
- package/dist/runtime/dom/elementGeometry.js +49 -10
- package/dist/runtime/dom/elementInfo.d.ts +74 -17
- package/dist/runtime/dom/elementInfo.js +187 -34
- package/dist/runtime/dom/evalHelpers.d.ts +12 -2
- package/dist/runtime/dom/evalHelpers.js +67 -7
- package/dist/runtime/dom/formDiscovery.d.ts +6 -2
- package/dist/runtime/dom/formDiscovery.js +20 -3
- package/dist/runtime/dom/formFillHelpers/fill.js +8 -12
- package/dist/runtime/dom/formFillHelpers/pressKey.js +2 -2
- package/dist/runtime/dom/formFillHelpers/shared.d.ts +16 -10
- package/dist/runtime/dom/formFillHelpers/shared.js +19 -52
- package/dist/runtime/dom/formSubmitHelpers.js +4 -3
- package/dist/runtime/dom/frameLayout.js +1 -0
- package/dist/runtime/dom/inspect.d.ts +7 -0
- package/dist/runtime/dom/inspect.js +92 -28
- package/dist/runtime/dom/inspectAllStyles.d.ts +16 -4
- package/dist/runtime/dom/inspectAllStyles.js +90 -7
- package/dist/runtime/dom/inspectCascade.d.ts +19 -2
- package/dist/runtime/dom/inspectCascade.js +214 -44
- package/dist/runtime/dom/inspectCascadeModel.d.ts +8 -0
- package/dist/runtime/dom/inspectCascadeModel.js +108 -34
- package/dist/runtime/dom/inspectHints.d.ts +26 -3
- package/dist/runtime/dom/inspectHints.js +125 -9
- package/dist/runtime/dom/inspectModel.d.ts +5 -1
- package/dist/runtime/dom/inspectModel.js +37 -10
- package/dist/runtime/dom/inspectPaintModel.d.ts +50 -22
- package/dist/runtime/dom/inspectPaintModel.js +182 -68
- package/dist/runtime/dom/inspectRules.d.ts +19 -0
- package/dist/runtime/dom/inspectRules.js +21 -5
- package/dist/runtime/dom/inspectScripts.d.ts +112 -12
- package/dist/runtime/dom/inspectScripts.js +357 -32
- package/dist/runtime/dom/inspectTree.js +10 -2
- package/dist/runtime/dom/inspectWhyModel.d.ts +2 -1
- package/dist/runtime/dom/inspectWhyModel.js +52 -10
- package/dist/runtime/dom/layout.js +40 -16
- package/dist/runtime/dom/reactEventHelpers.d.ts +21 -4
- package/dist/runtime/dom/reactEventHelpers.js +90 -36
- package/dist/runtime/dom/targetNode.d.ts +18 -5
- package/dist/runtime/dom/targetNode.js +268 -8
- package/dist/runtime/dom/wait.js +2 -1
- package/dist/runtime/page/bdgWorld.d.ts +57 -0
- package/dist/runtime/page/bdgWorld.js +180 -0
- package/dist/runtime/page/emulation.d.ts +13 -4
- package/dist/runtime/page/emulation.js +69 -4
- package/dist/runtime/page/replacedBuiltins.d.ts +28 -0
- package/dist/runtime/page/replacedBuiltins.js +136 -0
- package/dist/runtime/page/userAgent.d.ts +17 -0
- package/dist/runtime/page/userAgent.js +57 -0
- package/dist/session/QueryCacheManager.d.ts +4 -1
- package/dist/session/QueryCacheManager.js +5 -2
- package/dist/session/chrome.d.ts +4 -1
- package/dist/session/chrome.js +7 -1
- package/dist/session/cleanup/staleSession.d.ts +21 -4
- package/dist/session/cleanup/staleSession.js +79 -9
- package/dist/session/cleanup/userCommands.d.ts +4 -1
- package/dist/session/cleanup/userCommands.js +10 -5
- package/dist/session/daemonSocket.d.ts +10 -0
- package/dist/session/daemonSocket.js +22 -0
- package/dist/session/lastSession.d.ts +6 -3
- package/dist/session/lastSession.js +11 -5
- package/dist/session/paths.d.ts +3 -1
- package/dist/session/paths.js +5 -5
- package/dist/session/portClaims.js +4 -3
- package/dist/session/sessionList.d.ts +13 -5
- package/dist/session/sessionList.js +31 -7
- package/dist/telemetry/a11y.js +2 -2
- package/dist/telemetry/console.d.ts +2 -1
- package/dist/telemetry/console.js +30 -21
- package/dist/telemetry/pageCrash.d.ts +26 -0
- package/dist/telemetry/pageCrash.js +53 -0
- package/dist/types.d.ts +20 -0
- package/dist/ui/formatters/audit.d.ts +19 -0
- package/dist/ui/formatters/audit.js +115 -0
- package/dist/ui/formatters/cdp.d.ts +138 -0
- package/dist/ui/formatters/cdp.js +131 -0
- package/dist/ui/formatters/console/chronological.js +3 -1
- package/dist/ui/formatters/console/follow.d.ts +2 -1
- package/dist/ui/formatters/console/follow.js +2 -2
- package/dist/ui/formatters/console/json.d.ts +2 -2
- package/dist/ui/formatters/console/json.js +11 -5
- package/dist/ui/formatters/console/shared.d.ts +30 -0
- package/dist/ui/formatters/console/shared.js +16 -0
- package/dist/ui/formatters/console/summarize.d.ts +9 -2
- package/dist/ui/formatters/console/summarize.js +40 -9
- package/dist/ui/formatters/console.d.ts +2 -1
- package/dist/ui/formatters/console.js +7 -5
- package/dist/ui/formatters/details.js +3 -1
- package/dist/ui/formatters/dom.d.ts +2 -2
- package/dist/ui/formatters/dom.js +10 -8
- package/dist/ui/formatters/helpFormatters.js +1 -1
- package/dist/ui/formatters/inspect.js +50 -17
- package/dist/ui/formatters/installSkill.d.ts +9 -1
- package/dist/ui/formatters/installSkill.js +32 -6
- package/dist/ui/formatters/layout.js +2 -1
- package/dist/ui/formatters/networkList.d.ts +1 -1
- package/dist/ui/formatters/networkList.js +1 -2
- package/dist/ui/formatters/preview.d.ts +2 -0
- package/dist/ui/formatters/preview.js +17 -7
- package/dist/ui/formatters/sessions.d.ts +2 -2
- package/dist/ui/formatters/sessions.js +9 -2
- package/dist/ui/formatters/status.js +1 -1
- package/dist/ui/logging/logger.d.ts +1 -1
- package/dist/ui/messages/commands.d.ts +168 -11
- package/dist/ui/messages/commands.js +245 -18
- package/dist/ui/messages/consoleMessages.d.ts +24 -0
- package/dist/ui/messages/consoleMessages.js +32 -0
- package/dist/ui/messages/preview.d.ts +12 -0
- package/dist/ui/messages/preview.js +18 -2
- package/dist/ui/messages/session.d.ts +13 -2
- package/dist/ui/messages/session.js +22 -3
- package/dist/utils/cssValues.js +36 -4
- package/dist/utils/decisionTrees.js +0 -5
- package/dist/utils/directories.d.ts +34 -0
- package/dist/utils/directories.js +88 -0
- package/dist/utils/display.d.ts +16 -0
- package/dist/utils/display.js +42 -0
- package/dist/utils/exitCodes.d.ts +1 -0
- package/dist/utils/exitCodes.js +6 -0
- package/dist/utils/process.d.ts +12 -0
- package/dist/utils/process.js +25 -0
- package/dist/utils/suggestions.d.ts +4 -2
- package/dist/utils/suggestions.js +7 -5
- package/dist/utils/taskMappings.js +1 -1
- package/package.json +3 -2
|
@@ -12,8 +12,8 @@ import type { CDPConnection } from '../../connection/cdp.js';
|
|
|
12
12
|
import { CommandError } from '../../errors/index.js';
|
|
13
13
|
/** Selector placeholder that makes page scripts use the bound node. */
|
|
14
14
|
export declare const BOUND_TARGET_SELECTOR = "__bdg_bound_target__";
|
|
15
|
-
/** Removes the node bound for index-based commands from the window
|
|
16
|
-
export declare const UNBIND_TARGET_SCRIPT = "delete window.__bdgTarget";
|
|
15
|
+
/** Removes the node bound for index-based commands, and matches bound for a selector, from the window they were stored on */
|
|
16
|
+
export declare const UNBIND_TARGET_SCRIPT = "delete window.__bdgTarget; delete window.__bdgMatches";
|
|
17
17
|
/**
|
|
18
18
|
* Page-side matching of filters ({@link SelectorFilter}) and scoped steps
|
|
19
19
|
* ({@link ScopedStep}): called with the open shadow roots of the page, returns
|
|
@@ -54,9 +54,11 @@ export declare const DEEP_QUERY_JS = "function (selector, parts) {\n const css
|
|
|
54
54
|
* Page-side element lookup shared by the interaction scripts.
|
|
55
55
|
*
|
|
56
56
|
* Returns the bound node (if still in the page) for the placeholder selector,
|
|
57
|
-
*
|
|
57
|
+
* the matches bound for the selector when the page replaced the search
|
|
58
|
+
* built-ins ({@link bindMatches}), otherwise all matches of the selector
|
|
59
|
+
* ({@link DEEP_QUERY_JS}).
|
|
58
60
|
*/
|
|
59
|
-
export declare const FIND_ELEMENTS_JS = "function (selector, parts) {\n if (selector === '__bdg_bound_target__') {\n const el = window.__bdgTarget;\n return el && el.isConnected ? [el] : [];\n }\n return (function (selector, parts) {\n const css = parts ? parts.map((part) => part.css).join(', ') : selector;\n const found = [];\n const roots = [];\n const visit = (root) => {\n roots.push(root);\n for (const match of root.querySelectorAll(css)) found.push(match);\n for (const el of root.querySelectorAll('*')) {\n if (el.shadowRoot) visit(el.shadowRoot);\n if (el.tagName === 'IFRAME' || el.tagName === 'FRAME') {\n let frameDocument = null;\n try { frameDocument = el.contentDocument; } catch (e) { frameDocument = null; }\n if (frameDocument) visit(frameDocument);\n }\n }\n };\n visit(document);\n if (!parts) return found;\n const shadowRoots = roots.filter((root) => root.host);\n const { passesAll, descend } = ((shadowRoots) => {\n const skipped = /^(script|style|noscript|template)$/;\n const hiddenText = (el) => {\n const walker = el.ownerDocument.createTreeWalker(el, 5, {\n acceptNode: (node) => (node.nodeType === 1 && skipped.test(node.localName) ? 2 : 1)\n });\n let text = '';\n for (let node = walker.nextNode(); node; node = walker.nextNode()) {\n if (node.nodeType === 3) text += node.data;\n }\n return text;\n };\n const textOf = (el) => {\n if (el.localName === 'input' && /^(submit|button|reset)$/i.test(el.type)) return el.value;\n const rendered = typeof el.innerText === 'string' &&\n (typeof el.checkVisibility !== 'function' || el.checkVisibility({ visibilityProperty: true }));\n return (rendered ? el.innerText : hiddenText(el)).replace(/\\s+/g, ' ').trim();\n };\n const passes = (el, filter) => {\n if (filter.kind === 'visible') {\n if (typeof el.checkVisibility === 'function' && !el.checkVisibility()) return false;\n const rect = el.getBoundingClientRect();\n return rect.width > 0 && rect.height > 0 && el.ownerDocument.defaultView.getComputedStyle(el).visibility === 'visible';\n }\n if (filter.kind === 'has') return filter.selectors.some((chain) => descend(el, chain).length > 0);\n const text = textOf(el);\n return filter.kind === 'text-is' ? text === filter.text : text.toLowerCase().includes(filter.text);\n };\n const passesAll = (el, filters) =>\n filters.every((filter) => filter.kind !== 'visible' || passes(el, filter)) &&\n filters.every((filter) => filter.kind === 'visible' || passes(el, filter));\n const isUnder = (scope, node) => {\n for (let current = node; current; current = current.getRootNode().host) {\n if (scope === current || scope.contains(current)) return true;\n }\n return false;\n };\n const stepMatches = (scope, step) => {\n const matches = [...scope.querySelectorAll(':scope ' + step.combinator + ' ' + step.css)];\n if (step.combinator !== ' ') return matches;\n for (const root of shadowRoots) {\n if (isUnder(scope, root.host)) matches.push(...root.querySelectorAll(step.css));\n }\n return matches;\n };\n const descend = (scope, steps) => {\n let current = [scope];\n for (const step of steps) {\n const next = new Set();\n for (const el of current) {\n for (const match of stepMatches(el, step)) {\n if (passesAll(match, step.filters)) next.add(match);\n }\n }\n current = [...next];\n }\n return current;\n };\n return { passesAll: passesAll, descend: descend };\n})(shadowRoots);\n const result = new Set();\n for (const el of found) {\n for (const part of parts) {\n if (!el.matches(part.css) || !passesAll(el, part.filters)) continue;\n for (const match of descend(el, part.steps || [])) result.add(match);\n }\n }\n const rootOrder = new Map(roots.map((root, i) => [root, i]));\n const rootIndex = (el) => rootOrder.get(el.getRootNode()) ?? roots.length;\n return [...result].sort((a, b) =>\n rootIndex(a) - rootIndex(b) || (a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1)\n );\n})(selector, parts);\n}";
|
|
61
|
+
export declare const FIND_ELEMENTS_JS = "function (selector, parts) {\n if (selector === '__bdg_bound_target__') {\n const el = window.__bdgTarget;\n return el && el.isConnected ? [el] : [];\n }\n const bound = window.__bdgMatches;\n if (bound && bound.selector === selector) {\n const connected = [];\n for (let i = 0; i < bound.nodes.length; i++) {\n if (bound.nodes[i] && bound.nodes[i].isConnected) connected[connected.length] = bound.nodes[i];\n }\n return connected;\n }\n return (function (selector, parts) {\n const css = parts ? parts.map((part) => part.css).join(', ') : selector;\n const found = [];\n const roots = [];\n const visit = (root) => {\n roots.push(root);\n for (const match of root.querySelectorAll(css)) found.push(match);\n for (const el of root.querySelectorAll('*')) {\n if (el.shadowRoot) visit(el.shadowRoot);\n if (el.tagName === 'IFRAME' || el.tagName === 'FRAME') {\n let frameDocument = null;\n try { frameDocument = el.contentDocument; } catch (e) { frameDocument = null; }\n if (frameDocument) visit(frameDocument);\n }\n }\n };\n visit(document);\n if (!parts) return found;\n const shadowRoots = roots.filter((root) => root.host);\n const { passesAll, descend } = ((shadowRoots) => {\n const skipped = /^(script|style|noscript|template)$/;\n const hiddenText = (el) => {\n const walker = el.ownerDocument.createTreeWalker(el, 5, {\n acceptNode: (node) => (node.nodeType === 1 && skipped.test(node.localName) ? 2 : 1)\n });\n let text = '';\n for (let node = walker.nextNode(); node; node = walker.nextNode()) {\n if (node.nodeType === 3) text += node.data;\n }\n return text;\n };\n const textOf = (el) => {\n if (el.localName === 'input' && /^(submit|button|reset)$/i.test(el.type)) return el.value;\n const rendered = typeof el.innerText === 'string' &&\n (typeof el.checkVisibility !== 'function' || el.checkVisibility({ visibilityProperty: true }));\n return (rendered ? el.innerText : hiddenText(el)).replace(/\\s+/g, ' ').trim();\n };\n const passes = (el, filter) => {\n if (filter.kind === 'visible') {\n if (typeof el.checkVisibility === 'function' && !el.checkVisibility()) return false;\n const rect = el.getBoundingClientRect();\n return rect.width > 0 && rect.height > 0 && el.ownerDocument.defaultView.getComputedStyle(el).visibility === 'visible';\n }\n if (filter.kind === 'has') return filter.selectors.some((chain) => descend(el, chain).length > 0);\n const text = textOf(el);\n return filter.kind === 'text-is' ? text === filter.text : text.toLowerCase().includes(filter.text);\n };\n const passesAll = (el, filters) =>\n filters.every((filter) => filter.kind !== 'visible' || passes(el, filter)) &&\n filters.every((filter) => filter.kind === 'visible' || passes(el, filter));\n const isUnder = (scope, node) => {\n for (let current = node; current; current = current.getRootNode().host) {\n if (scope === current || scope.contains(current)) return true;\n }\n return false;\n };\n const stepMatches = (scope, step) => {\n const matches = [...scope.querySelectorAll(':scope ' + step.combinator + ' ' + step.css)];\n if (step.combinator !== ' ') return matches;\n for (const root of shadowRoots) {\n if (isUnder(scope, root.host)) matches.push(...root.querySelectorAll(step.css));\n }\n return matches;\n };\n const descend = (scope, steps) => {\n let current = [scope];\n for (const step of steps) {\n const next = new Set();\n for (const el of current) {\n for (const match of stepMatches(el, step)) {\n if (passesAll(match, step.filters)) next.add(match);\n }\n }\n current = [...next];\n }\n return current;\n };\n return { passesAll: passesAll, descend: descend };\n})(shadowRoots);\n const result = new Set();\n for (const el of found) {\n for (const part of parts) {\n if (!el.matches(part.css) || !passesAll(el, part.filters)) continue;\n for (const match of descend(el, part.steps || [])) result.add(match);\n }\n }\n const rootOrder = new Map(roots.map((root, i) => [root, i]));\n const rootIndex = (el) => rootOrder.get(el.getRootNode()) ?? roots.length;\n return [...result].sort((a, b) =>\n rootIndex(a) - rootIndex(b) || (a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1)\n );\n})(selector, parts);\n}";
|
|
60
62
|
/**
|
|
61
63
|
* Page-side check of what selectors cannot search in the top document:
|
|
62
64
|
* iframes whose document cannot be read (cross-origin) and `<object>`/`<embed>`
|
|
@@ -94,23 +96,34 @@ export interface ScriptTarget {
|
|
|
94
96
|
index?: number;
|
|
95
97
|
/** Connection for the page scripts (scoped to the element's frame when the top page cannot reach it) */
|
|
96
98
|
cdp: CDPConnection;
|
|
99
|
+
/** Built-ins the page replaced that the page scripts use (they run in the page's world) */
|
|
100
|
+
replacedBuiltins?: string[];
|
|
101
|
+
/** The page replaced the selector search, so the matches were found in bdg's world */
|
|
102
|
+
boundInBdgWorld?: boolean;
|
|
97
103
|
}
|
|
98
104
|
/**
|
|
99
105
|
* Run an interaction on the element a request targets. Results and errors
|
|
100
106
|
* never show the internal placeholder: a bound node the page scripts could
|
|
101
107
|
* not find is reported as stale (87), and results carry the user's selector.
|
|
108
|
+
* On a page that replaced the selector search, a result warns that the
|
|
109
|
+
* element was found in bdg's world; on a page that replaced built-ins the
|
|
110
|
+
* scripts use, a failure adds that they may be the cause, and a script that
|
|
111
|
+
* threw is reported as broken by the page (90), naming them.
|
|
102
112
|
*
|
|
103
113
|
* @param cdp - CDP connection
|
|
104
114
|
* @param params - Request with a selector (and optional index) or a backend node id
|
|
105
115
|
* @param work - The interaction, given the script target
|
|
106
116
|
* @returns The interaction's result
|
|
107
|
-
* @throws CommandError (87) when the bound node left the page during the
|
|
117
|
+
* @throws CommandError (87) when the bound node left the page during the
|
|
118
|
+
* action, (90) when its script threw on a page that replaced built-ins
|
|
108
119
|
*/
|
|
109
120
|
export declare function onScriptTarget<T extends {
|
|
110
121
|
selector?: string;
|
|
111
122
|
error?: string;
|
|
112
123
|
suggestion?: string;
|
|
113
124
|
exitCode?: number;
|
|
125
|
+
warning?: string;
|
|
126
|
+
replacedBuiltins?: string[];
|
|
114
127
|
}>(cdp: CDPConnection, params: {
|
|
115
128
|
selector?: string;
|
|
116
129
|
index?: number;
|
|
@@ -9,16 +9,86 @@
|
|
|
9
9
|
* {@link BOUND_TARGET_SELECTOR}.
|
|
10
10
|
*/
|
|
11
11
|
import { CommandError } from '../../errors/index.js';
|
|
12
|
-
import { emptySelectorError, indexOutOfRangeError, noNodesFoundError, staleNodeError, } from '../../errors/messages.js';
|
|
12
|
+
import { actionBrokenByPageError, actionScriptFailedError, emptySelectorError, indexOutOfRangeError, noNodesFoundError, staleNodeError, } from '../../errors/messages.js';
|
|
13
|
+
import { ActionScriptError, throwIfInvalidSelector } from './formFillHelpers/shared.js';
|
|
13
14
|
import { frameScopedConnection } from './frameScopedConnection.js';
|
|
15
|
+
import { evaluateInBdgWorld } from '../page/bdgWorld.js';
|
|
16
|
+
import { findReplacedBuiltins } from '../page/replacedBuiltins.js';
|
|
14
17
|
import { createLogger } from '../../ui/logging/index.js';
|
|
18
|
+
import { brokenByReplacedBuiltinsSuggestion, replacedBuiltinsWarning, } from '../../ui/messages/commands.js';
|
|
15
19
|
import { getErrorMessage } from '../../utils/errors.js';
|
|
16
20
|
import { EXIT_CODES } from '../../utils/exitCodes.js';
|
|
17
21
|
import { parseSelectorFilters, } from '../../utils/selectorFilters.js';
|
|
18
22
|
/** Selector placeholder that makes page scripts use the bound node. */
|
|
19
23
|
export const BOUND_TARGET_SELECTOR = '__bdg_bound_target__';
|
|
20
|
-
/** Removes the node bound for index-based commands from the window
|
|
21
|
-
export const UNBIND_TARGET_SCRIPT = 'delete window.__bdgTarget';
|
|
24
|
+
/** Removes the node bound for index-based commands, and matches bound for a selector, from the window they were stored on */
|
|
25
|
+
export const UNBIND_TARGET_SCRIPT = 'delete window.__bdgTarget; delete window.__bdgMatches';
|
|
26
|
+
/**
|
|
27
|
+
* Built-ins the selector search ({@link DEEP_QUERY_JS}) cannot do without:
|
|
28
|
+
* when the page replaced one, the search runs in bdg's world instead.
|
|
29
|
+
* Helpers that libraries replace with working versions (Prototype.js's
|
|
30
|
+
* `Array.prototype.map`) are left to {@link SCRIPT_BUILTINS}.
|
|
31
|
+
*/
|
|
32
|
+
const SELECTION_BUILTINS = [
|
|
33
|
+
'Element.prototype.querySelectorAll',
|
|
34
|
+
'Element.prototype.querySelector',
|
|
35
|
+
'Document.prototype.querySelectorAll',
|
|
36
|
+
'Document.prototype.querySelector',
|
|
37
|
+
'DocumentFragment.prototype.querySelectorAll',
|
|
38
|
+
'Element.prototype.matches',
|
|
39
|
+
'Element.prototype.closest',
|
|
40
|
+
'Node.prototype.getRootNode',
|
|
41
|
+
'Node.prototype.compareDocumentPosition',
|
|
42
|
+
'Node.prototype.contains',
|
|
43
|
+
'NodeList.prototype[Symbol.iterator]',
|
|
44
|
+
'Array.prototype[Symbol.iterator]',
|
|
45
|
+
'Array.prototype.push',
|
|
46
|
+
'Set',
|
|
47
|
+
'Map',
|
|
48
|
+
];
|
|
49
|
+
/** Other built-ins the interaction scripts use */
|
|
50
|
+
const SCRIPT_BUILTINS = [
|
|
51
|
+
'Array.from',
|
|
52
|
+
'Array.prototype.map',
|
|
53
|
+
'Array.prototype.filter',
|
|
54
|
+
'Array.prototype.forEach',
|
|
55
|
+
'Array.prototype.some',
|
|
56
|
+
'Array.prototype.find',
|
|
57
|
+
'Array.prototype.includes',
|
|
58
|
+
'Object.keys',
|
|
59
|
+
'Object.assign',
|
|
60
|
+
'Object.getOwnPropertyDescriptor',
|
|
61
|
+
'Function.prototype.call',
|
|
62
|
+
'JSON.stringify',
|
|
63
|
+
'JSON.parse',
|
|
64
|
+
];
|
|
65
|
+
/** DOM built-ins the interaction scripts act through (anti-bot scripts often make them throw) */
|
|
66
|
+
const DOM_ACTION_BUILTINS = [
|
|
67
|
+
'Element.prototype.getBoundingClientRect',
|
|
68
|
+
'Element.prototype.getClientRects',
|
|
69
|
+
'Element.prototype.scrollIntoView',
|
|
70
|
+
'window.getComputedStyle',
|
|
71
|
+
'Document.prototype.elementFromPoint',
|
|
72
|
+
'Document.prototype.createEvent',
|
|
73
|
+
'EventTarget.prototype.dispatchEvent',
|
|
74
|
+
'HTMLElement.prototype.focus',
|
|
75
|
+
'HTMLElement.prototype.blur',
|
|
76
|
+
'HTMLElement.prototype.click',
|
|
77
|
+
'Event',
|
|
78
|
+
'MouseEvent',
|
|
79
|
+
'HTMLInputElement.prototype.value',
|
|
80
|
+
'HTMLTextAreaElement.prototype.value',
|
|
81
|
+
'HTMLSelectElement.prototype.value',
|
|
82
|
+
];
|
|
83
|
+
/** Matches bound for a selector when the page replaced the selector search built-ins (enough for --index) */
|
|
84
|
+
const MATCH_BIND_LIMIT = 100;
|
|
85
|
+
/** Page-side: stores a node as match `i` of the selector being bound on the top window (runs on the node, in its frame; a cross-origin frame cannot reach the top window and fails) */
|
|
86
|
+
const BIND_MATCH_FUNCTION = `function (selector, i) {
|
|
87
|
+
const w = window.top;
|
|
88
|
+
if (!w.__bdgMatches || w.__bdgMatches.selector !== selector) return false;
|
|
89
|
+
w.__bdgMatches.nodes[i] = this;
|
|
90
|
+
return true;
|
|
91
|
+
}`;
|
|
22
92
|
const log = createLogger('dom');
|
|
23
93
|
/**
|
|
24
94
|
* Page-side matching of filters ({@link SelectorFilter}) and scoped steps
|
|
@@ -151,13 +221,23 @@ export const DEEP_QUERY_JS = `function (selector, parts) {
|
|
|
151
221
|
* Page-side element lookup shared by the interaction scripts.
|
|
152
222
|
*
|
|
153
223
|
* Returns the bound node (if still in the page) for the placeholder selector,
|
|
154
|
-
*
|
|
224
|
+
* the matches bound for the selector when the page replaced the search
|
|
225
|
+
* built-ins ({@link bindMatches}), otherwise all matches of the selector
|
|
226
|
+
* ({@link DEEP_QUERY_JS}).
|
|
155
227
|
*/
|
|
156
228
|
export const FIND_ELEMENTS_JS = `function (selector, parts) {
|
|
157
229
|
if (selector === '${BOUND_TARGET_SELECTOR}') {
|
|
158
230
|
const el = window.__bdgTarget;
|
|
159
231
|
return el && el.isConnected ? [el] : [];
|
|
160
232
|
}
|
|
233
|
+
const bound = window.__bdgMatches;
|
|
234
|
+
if (bound && bound.selector === selector) {
|
|
235
|
+
const connected = [];
|
|
236
|
+
for (let i = 0; i < bound.nodes.length; i++) {
|
|
237
|
+
if (bound.nodes[i] && bound.nodes[i].isConnected) connected[connected.length] = bound.nodes[i];
|
|
238
|
+
}
|
|
239
|
+
return connected;
|
|
240
|
+
}
|
|
161
241
|
return (${DEEP_QUERY_JS})(selector, parts);
|
|
162
242
|
}`;
|
|
163
243
|
/**
|
|
@@ -269,13 +349,124 @@ async function bindTargetNode(cdp, backendNodeId) {
|
|
|
269
349
|
*/
|
|
270
350
|
async function resolveScriptTarget(cdp, params) {
|
|
271
351
|
if (params.backendNodeId === undefined) {
|
|
352
|
+
const selector = params.selector ?? '';
|
|
353
|
+
const replaced = await replacedBuiltins(cdp);
|
|
354
|
+
const searchReplaced = replaced.some((name) => SELECTION_BUILTINS.includes(name));
|
|
355
|
+
const bound = searchReplaced && (await bindMatches(cdp, selector, params.index ?? 0));
|
|
272
356
|
return {
|
|
273
|
-
selector
|
|
357
|
+
selector,
|
|
274
358
|
...(params.index !== undefined && { index: params.index }),
|
|
275
359
|
cdp,
|
|
360
|
+
...(replaced.length > 0 && { replacedBuiltins: replaced }),
|
|
361
|
+
...(bound && { boundInBdgWorld: true }),
|
|
276
362
|
};
|
|
277
363
|
}
|
|
278
|
-
|
|
364
|
+
const scriptCdp = await bindTargetNode(cdp, params.backendNodeId);
|
|
365
|
+
const replaced = await replacedBuiltins(scriptCdp);
|
|
366
|
+
return {
|
|
367
|
+
selector: BOUND_TARGET_SELECTOR,
|
|
368
|
+
cdp: scriptCdp,
|
|
369
|
+
...(replaced.length > 0 && { replacedBuiltins: replaced }),
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* The built-ins the interaction scripts use that the page replaced.
|
|
374
|
+
*
|
|
375
|
+
* @param cdp - CDP connection
|
|
376
|
+
* @returns Their dotted names (empty when all are the browser's, or the check failed)
|
|
377
|
+
*/
|
|
378
|
+
function replacedBuiltins(cdp) {
|
|
379
|
+
return findReplacedBuiltins(cdp, [
|
|
380
|
+
...SELECTION_BUILTINS,
|
|
381
|
+
...SCRIPT_BUILTINS,
|
|
382
|
+
...DOM_ACTION_BUILTINS,
|
|
383
|
+
]);
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Search the selector in bdg's own world, where the page's replacements do
|
|
387
|
+
* not apply, and hand the matches (the first {@link MATCH_BIND_LIMIT}, or up
|
|
388
|
+
* to the index asked for) to the page scripts, which run in the page's world.
|
|
389
|
+
* When a match cannot be handed over (or the search fails other than on an
|
|
390
|
+
* invalid selector), nothing is bound and the scripts search themselves.
|
|
391
|
+
*
|
|
392
|
+
* @param cdp - CDP connection
|
|
393
|
+
* @param selector - Selector as the user gave it
|
|
394
|
+
* @param index - Match the action is for
|
|
395
|
+
* @returns Whether the matches were bound
|
|
396
|
+
* @throws CommandError (81) for an invalid selector
|
|
397
|
+
*/
|
|
398
|
+
async function bindMatches(cdp, selector, index) {
|
|
399
|
+
const objectGroup = `bdg-bind-${Date.now()}`;
|
|
400
|
+
const limit = Math.max(MATCH_BIND_LIMIT, index + 1);
|
|
401
|
+
try {
|
|
402
|
+
const found = await evaluateInBdgWorld(cdp, {
|
|
403
|
+
expression: `(${DEEP_QUERY_JS})(${selectorArgsJS(selector)}).slice(0, ${limit})`,
|
|
404
|
+
objectGroup,
|
|
405
|
+
});
|
|
406
|
+
if (found.exceptionDetails)
|
|
407
|
+
throwIfInvalidSelector(found.exceptionDetails, selector);
|
|
408
|
+
const arrayId = found.result.objectId;
|
|
409
|
+
if (found.exceptionDetails || !arrayId)
|
|
410
|
+
return false;
|
|
411
|
+
const { result } = (await cdp.send('Runtime.getProperties', {
|
|
412
|
+
objectId: arrayId,
|
|
413
|
+
ownProperties: true,
|
|
414
|
+
}));
|
|
415
|
+
const matches = result
|
|
416
|
+
.filter((property) => /^\d+$/.test(property.name))
|
|
417
|
+
.map((property) => ({ i: Number(property.name), objectId: property.value?.objectId }));
|
|
418
|
+
await cdp.send('Runtime.evaluate', {
|
|
419
|
+
expression: `window.__bdgMatches = { selector: ${JSON.stringify(selector)}, nodes: [] }`,
|
|
420
|
+
});
|
|
421
|
+
const bound = await Promise.all(matches.map(({ i, objectId }) => objectId ? bindMatch(cdp, selector, i, objectId) : Promise.resolve(false)));
|
|
422
|
+
if (bound.every(Boolean))
|
|
423
|
+
return true;
|
|
424
|
+
await cdp.send('Runtime.evaluate', { expression: 'delete window.__bdgMatches' });
|
|
425
|
+
return false;
|
|
426
|
+
}
|
|
427
|
+
catch (error) {
|
|
428
|
+
if (error instanceof CommandError)
|
|
429
|
+
throw error;
|
|
430
|
+
log.debug(`Matches not bound: ${getErrorMessage(error)}`);
|
|
431
|
+
return false;
|
|
432
|
+
}
|
|
433
|
+
finally {
|
|
434
|
+
await cdp
|
|
435
|
+
.send('Runtime.releaseObjectGroup', { objectGroup })
|
|
436
|
+
.catch((error) => log.debug(`Matches not released: ${getErrorMessage(error)}`));
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Hand one match from bdg's world to the page's world as match `i`.
|
|
441
|
+
*
|
|
442
|
+
* @param cdp - CDP connection
|
|
443
|
+
* @param selector - Selector the matches are for
|
|
444
|
+
* @param i - Its position among the matches
|
|
445
|
+
* @param objectId - The match in bdg's world
|
|
446
|
+
* @returns Whether it was handed over
|
|
447
|
+
*/
|
|
448
|
+
async function bindMatch(cdp, selector, i, objectId) {
|
|
449
|
+
try {
|
|
450
|
+
const { node } = (await cdp.send('DOM.describeNode', {
|
|
451
|
+
objectId,
|
|
452
|
+
}));
|
|
453
|
+
const resolved = (await cdp.send('DOM.resolveNode', {
|
|
454
|
+
backendNodeId: node.backendNodeId,
|
|
455
|
+
}));
|
|
456
|
+
if (!resolved.object.objectId)
|
|
457
|
+
return false;
|
|
458
|
+
const stored = (await cdp.send('Runtime.callFunctionOn', {
|
|
459
|
+
objectId: resolved.object.objectId,
|
|
460
|
+
functionDeclaration: BIND_MATCH_FUNCTION,
|
|
461
|
+
arguments: [{ value: selector }, { value: i }],
|
|
462
|
+
returnByValue: true,
|
|
463
|
+
}));
|
|
464
|
+
return stored.result.value === true;
|
|
465
|
+
}
|
|
466
|
+
catch (error) {
|
|
467
|
+
log.debug(`Match ${i} not bound: ${getErrorMessage(error)}`);
|
|
468
|
+
return false;
|
|
469
|
+
}
|
|
279
470
|
}
|
|
280
471
|
/**
|
|
281
472
|
* Whether an error is about the bound node: the placeholder only appears in
|
|
@@ -291,19 +482,28 @@ function boundNodeMissing(text) {
|
|
|
291
482
|
* Run an interaction on the element a request targets. Results and errors
|
|
292
483
|
* never show the internal placeholder: a bound node the page scripts could
|
|
293
484
|
* not find is reported as stale (87), and results carry the user's selector.
|
|
485
|
+
* On a page that replaced the selector search, a result warns that the
|
|
486
|
+
* element was found in bdg's world; on a page that replaced built-ins the
|
|
487
|
+
* scripts use, a failure adds that they may be the cause, and a script that
|
|
488
|
+
* threw is reported as broken by the page (90), naming them.
|
|
294
489
|
*
|
|
295
490
|
* @param cdp - CDP connection
|
|
296
491
|
* @param params - Request with a selector (and optional index) or a backend node id
|
|
297
492
|
* @param work - The interaction, given the script target
|
|
298
493
|
* @returns The interaction's result
|
|
299
|
-
* @throws CommandError (87) when the bound node left the page during the
|
|
494
|
+
* @throws CommandError (87) when the bound node left the page during the
|
|
495
|
+
* action, (90) when its script threw on a page that replaced built-ins
|
|
300
496
|
*/
|
|
301
497
|
export async function onScriptTarget(cdp, params, work) {
|
|
302
498
|
const err = staleNodeError();
|
|
303
499
|
let target;
|
|
304
500
|
try {
|
|
305
501
|
target = await resolveScriptTarget(cdp, params);
|
|
306
|
-
const
|
|
502
|
+
const replaced = target.replacedBuiltins ?? [];
|
|
503
|
+
const result = withReplacedBuiltins(withUserSelector(await work(target), params.selector), target.boundInBdgWorld ? replaced : []);
|
|
504
|
+
if (result.error && replaced.length > 0) {
|
|
505
|
+
return { ...result, suggestion: withBrokenHint(result.suggestion, replaced) };
|
|
506
|
+
}
|
|
307
507
|
if (!boundNodeMissing(result.error))
|
|
308
508
|
return result;
|
|
309
509
|
return {
|
|
@@ -314,6 +514,16 @@ export async function onScriptTarget(cdp, params, work) {
|
|
|
314
514
|
};
|
|
315
515
|
}
|
|
316
516
|
catch (error) {
|
|
517
|
+
const replaced = target?.replacedBuiltins ?? [];
|
|
518
|
+
if (error instanceof ActionScriptError) {
|
|
519
|
+
throw actionScriptFailure(error, replaced, params.selector ?? '');
|
|
520
|
+
}
|
|
521
|
+
if (error instanceof CommandError && replaced.length > 0) {
|
|
522
|
+
const suggestion = error.metadata['suggestion'];
|
|
523
|
+
throw new CommandError(error.message, {
|
|
524
|
+
suggestion: withBrokenHint(typeof suggestion === 'string' ? suggestion : undefined, replaced),
|
|
525
|
+
}, error.exitCode);
|
|
526
|
+
}
|
|
317
527
|
if (!(error instanceof CommandError) || !boundNodeMissing(error.message))
|
|
318
528
|
throw error;
|
|
319
529
|
throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.STALE_CACHE);
|
|
@@ -323,6 +533,24 @@ export async function onScriptTarget(cdp, params, work) {
|
|
|
323
533
|
unbindInFrame(target.cdp);
|
|
324
534
|
}
|
|
325
535
|
}
|
|
536
|
+
/**
|
|
537
|
+
* The error for an action script that threw: on a page that replaced
|
|
538
|
+
* built-ins the scripts use, that the page broke it (90, naming them);
|
|
539
|
+
* otherwise what it threw (110), with the user's selector.
|
|
540
|
+
*
|
|
541
|
+
* @param error - What the action reported
|
|
542
|
+
* @param replaced - Built-ins the page replaced
|
|
543
|
+
* @param selector - Selector the user gave (or the cached query's selector)
|
|
544
|
+
* @returns Error to throw
|
|
545
|
+
*/
|
|
546
|
+
function actionScriptFailure(error, replaced, selector) {
|
|
547
|
+
if (replaced.length > 0) {
|
|
548
|
+
const err = actionBrokenByPageError(error.action, error.exception, replaced);
|
|
549
|
+
return new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.RESOURCE_CONFLICT);
|
|
550
|
+
}
|
|
551
|
+
const err = actionScriptFailedError(error.action, error.exception, selector);
|
|
552
|
+
return new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.SOFTWARE_ERROR);
|
|
553
|
+
}
|
|
326
554
|
/**
|
|
327
555
|
* Remove a node bound in a cross-origin frame from that frame's window
|
|
328
556
|
* (through the scoped connection, so the script runs where the bind ran; the
|
|
@@ -336,6 +564,38 @@ function unbindInFrame(frameConnection) {
|
|
|
336
564
|
.send('Runtime.evaluate', { expression: UNBIND_TARGET_SCRIPT })
|
|
337
565
|
.catch((error) => log.debug(`Frame target not unbound: ${getErrorMessage(error)}`));
|
|
338
566
|
}
|
|
567
|
+
/**
|
|
568
|
+
* A failed action's suggestion with the hint that the page's replaced
|
|
569
|
+
* built-ins may have broken it.
|
|
570
|
+
*
|
|
571
|
+
* @param suggestion - The action's own suggestion
|
|
572
|
+
* @param replaced - Built-ins the page replaced
|
|
573
|
+
* @returns Both, the action's first
|
|
574
|
+
*/
|
|
575
|
+
function withBrokenHint(suggestion, replaced) {
|
|
576
|
+
const hint = brokenByReplacedBuiltinsSuggestion(replaced);
|
|
577
|
+
return suggestion ? `${suggestion}. ${hint}` : hint;
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Warn that the page replaced built-ins the page scripts use: they run in
|
|
581
|
+
* the page's world, so the action may misbehave (the element itself was
|
|
582
|
+
* found in bdg's world). The warning names the first few; `replacedBuiltins`
|
|
583
|
+
* lists them all.
|
|
584
|
+
*
|
|
585
|
+
* @param result - Script result
|
|
586
|
+
* @param replaced - Built-ins the page replaced
|
|
587
|
+
* @returns Result with the warning added to any it has
|
|
588
|
+
*/
|
|
589
|
+
function withReplacedBuiltins(result, replaced) {
|
|
590
|
+
if (!replaced || replaced.length === 0)
|
|
591
|
+
return result;
|
|
592
|
+
const warning = replacedBuiltinsWarning(replaced);
|
|
593
|
+
return {
|
|
594
|
+
...result,
|
|
595
|
+
warning: result.warning ? `${result.warning}; ${warning}` : warning,
|
|
596
|
+
replacedBuiltins: replaced,
|
|
597
|
+
};
|
|
598
|
+
}
|
|
339
599
|
/**
|
|
340
600
|
* Report the user's selector instead of the internal placeholder.
|
|
341
601
|
*
|
package/dist/runtime/dom/wait.js
CHANGED
|
@@ -15,6 +15,7 @@ import { invalidSelectorError, waitTimeoutError } from '../../errors/messages.js
|
|
|
15
15
|
import { isContextLostError } from './evalHelpers.js';
|
|
16
16
|
import { DEEP_QUERY_JS, FILTER_MATCHING_JS, selectorArgsJS } from './targetNode.js';
|
|
17
17
|
import { isWaitConditionMet, needsGoneConfirmation, normalizeWaitText, } from './waitCondition.js';
|
|
18
|
+
import { evaluateInBdgWorld } from '../page/bdgWorld.js';
|
|
18
19
|
import { createLogger } from '../../ui/logging/index.js';
|
|
19
20
|
import { delay, raceTimeout } from '../../utils/async.js';
|
|
20
21
|
import { getErrorMessage } from '../../utils/errors.js';
|
|
@@ -157,7 +158,7 @@ async function nextSnapshot(cdp, condition, previous, deadline) {
|
|
|
157
158
|
const selectorArgs = condition.selector === undefined ? 'null, null' : selectorArgsJS(condition.selector);
|
|
158
159
|
const text = condition.text === undefined ? null : normalizeWaitText(condition.text);
|
|
159
160
|
const args = `${selectorArgs}, ${JSON.stringify(text)}, ${JSON.stringify(previous)}, ${Math.min(remaining, WAIT_SLICE_MS)}`;
|
|
160
|
-
const evaluated = cdp
|
|
161
|
+
const evaluated = evaluateInBdgWorld(cdp, {
|
|
161
162
|
expression: `(${WAIT_SNAPSHOT_JS})(${args})`,
|
|
162
163
|
awaitPromise: true,
|
|
163
164
|
returnByValue: true,
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bdg's own JavaScript world in the page (a CDP isolated world): it shares
|
|
3
|
+
* the DOM with the page but has its own built-ins, so a page that replaces
|
|
4
|
+
* `Element.prototype.querySelectorAll`, `JSON.stringify` or
|
|
5
|
+
* `Array.prototype.map` (polyfills, old frameworks, anti-bot scripts) cannot
|
|
6
|
+
* change what bdg's page scripts find or return.
|
|
7
|
+
*
|
|
8
|
+
* Only the entry points need the world: `Runtime.evaluate` runs in it with
|
|
9
|
+
* its `contextId`, and `DOM.resolveNode` hands out objects of it with its
|
|
10
|
+
* `executionContextId`; `Runtime.callFunctionOn` on such an object runs in
|
|
11
|
+
* the same world. The world belongs to the top frame (same-origin iframes are
|
|
12
|
+
* reached through it); a node of another frame, a connection without the
|
|
13
|
+
* Page domain (a frame-scoped one) or a failure to create it fall back to the
|
|
14
|
+
* main world, as before.
|
|
15
|
+
*/
|
|
16
|
+
import type { CDPConnection } from '../../connection/cdp.js';
|
|
17
|
+
import type { Protocol } from '../../connection/typed-cdp.js';
|
|
18
|
+
/**
|
|
19
|
+
* What the world needs of a connection: commands, and events to forget the
|
|
20
|
+
* world when the page navigates (a sender without events, such as a test
|
|
21
|
+
* double, runs scripts in the main world)
|
|
22
|
+
*/
|
|
23
|
+
type PageConnection = Pick<CDPConnection, 'send'> & Partial<Pick<CDPConnection, 'on'>>;
|
|
24
|
+
/** Name of bdg's isolated world (shown in DevTools' context selector) */
|
|
25
|
+
export declare const BDG_WORLD_NAME = "bdg";
|
|
26
|
+
/**
|
|
27
|
+
* `Runtime.evaluate` in bdg's world of the top frame. A call that names its
|
|
28
|
+
* own context is sent as it is.
|
|
29
|
+
*
|
|
30
|
+
* @param cdp - Connection to the page
|
|
31
|
+
* @param params - Evaluate parameters
|
|
32
|
+
* @returns Evaluate response
|
|
33
|
+
*/
|
|
34
|
+
export declare function evaluateInBdgWorld(cdp: PageConnection, params: Protocol.Runtime.EvaluateRequest): Promise<Protocol.Runtime.EvaluateResponse>;
|
|
35
|
+
/**
|
|
36
|
+
* `DOM.resolveNode` into bdg's world of the top frame, so functions called on
|
|
37
|
+
* the node run there. A node the world cannot use (one of a cross-origin
|
|
38
|
+
* frame, or one no longer in the page) is resolved in its own frame's main
|
|
39
|
+
* world, as before.
|
|
40
|
+
*
|
|
41
|
+
* @param cdp - Connection to the page
|
|
42
|
+
* @param params - Resolve parameters
|
|
43
|
+
* @returns Resolve response
|
|
44
|
+
*/
|
|
45
|
+
export declare function resolveNodeInBdgWorld(cdp: PageConnection, params: Protocol.DOM.ResolveNodeRequest): Promise<Protocol.DOM.ResolveNodeResponse>;
|
|
46
|
+
/**
|
|
47
|
+
* Send a CDP command for bdg's own scripts: `Runtime.evaluate` and
|
|
48
|
+
* `DOM.resolveNode` go to bdg's world, other methods as they are.
|
|
49
|
+
*
|
|
50
|
+
* @param cdp - Connection to the page
|
|
51
|
+
* @param method - CDP method
|
|
52
|
+
* @param params - Its parameters
|
|
53
|
+
* @returns The command's result
|
|
54
|
+
*/
|
|
55
|
+
export declare function sendForBdgScript(cdp: PageConnection, method: string, params: Record<string, unknown>): Promise<unknown>;
|
|
56
|
+
export {};
|
|
57
|
+
//# sourceMappingURL=bdgWorld.d.ts.map
|