browser-debugger-cli 0.15.0 → 0.16.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 +2 -1
- package/dist/cdp/methodTarget.d.ts +92 -0
- package/dist/cdp/methodTarget.js +159 -0
- package/dist/cdp/protocol.d.ts +16 -1
- package/dist/cdp/protocol.js +21 -0
- package/dist/cdp/schema.d.ts +55 -1
- package/dist/cdp/schema.js +134 -25
- package/dist/cdp/types.d.ts +3 -1
- package/dist/commands/cdp.d.ts +38 -1
- package/dist/commands/cdp.js +200 -133
- package/dist/commands/cleanup.js +18 -4
- package/dist/commands/dom/formInteraction.js +8 -4
- package/dist/commands/dom/helpers/index.d.ts +4 -4
- package/dist/commands/dom/helpers/index.js +3 -3
- package/dist/commands/dom/helpers/query.d.ts +2 -2
- package/dist/commands/dom/helpers/query.js +2 -2
- package/dist/commands/dom/helpers/screenshot.d.ts +21 -26
- package/dist/commands/dom/helpers/screenshot.js +50 -668
- package/dist/commands/dom/screenshot.js +56 -36
- package/dist/commands/optionBehaviors.js +18 -8
- package/dist/commands/shared/CommandRunner.d.ts +5 -0
- package/dist/commands/shared/CommandRunner.js +18 -3
- package/dist/commands/shared/interrupt.d.ts +40 -0
- package/dist/commands/shared/interrupt.js +73 -0
- package/dist/commands/shared/optionTypes.d.ts +2 -0
- package/dist/commands/shared/startHelpers.d.ts +26 -3
- package/dist/commands/shared/startHelpers.js +145 -23
- package/dist/commands/types.d.ts +5 -0
- package/dist/connection/cdp.js +1 -16
- package/dist/connection/chromeIdentity.d.ts +24 -5
- package/dist/connection/chromeIdentity.js +53 -22
- package/dist/connection/launcher.d.ts +34 -1
- package/dist/connection/launcher.js +98 -10
- package/dist/connection/typed-cdp.d.ts +3 -2
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/daemon/SessionController.d.ts +10 -5
- package/dist/daemon/SessionController.js +15 -8
- package/dist/daemon/ipcServer.js +1 -1
- package/dist/daemon/launcher.d.ts +5 -0
- package/dist/daemon/launcher.js +8 -1
- package/dist/daemon/session/Session.d.ts +5 -1
- package/dist/daemon/session/Session.js +9 -8
- package/dist/daemon/session/TelemetryStore.d.ts +5 -0
- package/dist/daemon/session/TelemetryStore.js +4 -0
- package/dist/daemon/session/captureGate.d.ts +59 -0
- package/dist/daemon/session/captureGate.js +96 -0
- package/dist/daemon/session/chromeConnection.d.ts +16 -1
- package/dist/daemon/session/chromeConnection.js +34 -4
- package/dist/daemon/session/collectors.d.ts +15 -0
- package/dist/daemon/session/collectors.js +39 -2
- package/dist/daemon/session/commandRegistry.d.ts +14 -1
- package/dist/daemon/session/commandRegistry.js +46 -11
- package/dist/daemon/session/downloads.d.ts +32 -0
- package/dist/daemon/session/downloads.js +96 -0
- package/dist/daemon/session/interactions.d.ts +3 -2
- package/dist/daemon/session/interactions.js +7 -2
- package/dist/daemon/session/plugins.js +6 -0
- package/dist/daemon.js +12843 -11482
- package/dist/errors/CommandError.d.ts +2 -0
- package/dist/errors/issues.d.ts +1 -1
- package/dist/errors/messages.d.ts +58 -0
- package/dist/errors/messages.js +112 -0
- package/dist/index.js +999 -1020
- package/dist/ipc/client.d.ts +14 -1
- package/dist/ipc/client.js +21 -4
- package/dist/ipc/protocol/commands.d.ts +32 -2
- package/dist/ipc/protocol/commands.js +1 -0
- package/dist/ipc/protocol/domTypes.d.ts +24 -1
- package/dist/ipc/session/queries.d.ts +3 -0
- package/dist/ipc/session/types.d.ts +5 -0
- package/dist/ipc/transport/IPCError.d.ts +9 -0
- package/dist/ipc/transport/IPCError.js +12 -0
- package/dist/ipc/transport/errors.d.ts +2 -1
- package/dist/ipc/transport/errors.js +4 -1
- package/dist/ipc/transport/index.d.ts +4 -2
- package/dist/ipc/transport/index.js +13 -3
- package/dist/runtime/dom/actionEffects.d.ts +48 -9
- package/dist/runtime/dom/actionEffects.js +269 -34
- package/dist/runtime/dom/actionEffectsScripts.d.ts +45 -0
- package/dist/runtime/dom/actionEffectsScripts.js +101 -2
- package/dist/runtime/dom/captureArea.d.ts +35 -0
- package/dist/runtime/dom/captureArea.js +203 -0
- package/dist/runtime/dom/elementInfo.d.ts +10 -8
- package/dist/runtime/dom/elementInfo.js +8 -6
- package/dist/runtime/dom/formDiscovery.d.ts +1 -1
- package/dist/runtime/page/bdgWorld.d.ts +9 -0
- package/dist/runtime/page/bdgWorld.js +11 -0
- package/dist/runtime/page/captureEmulation.d.ts +119 -0
- package/dist/runtime/page/captureEmulation.js +189 -0
- package/dist/runtime/page/captureScroll.d.ts +24 -0
- package/dist/runtime/page/captureScroll.js +124 -0
- package/dist/runtime/page/screenshot.d.ts +41 -0
- package/dist/runtime/page/screenshot.js +394 -0
- package/dist/session/paths.d.ts +14 -0
- package/dist/session/paths.js +25 -0
- package/dist/telemetry/downloads.d.ts +127 -0
- package/dist/telemetry/downloads.js +265 -0
- package/dist/telemetry/har/builder.js +22 -7
- package/dist/telemetry/har/sanitize.d.ts +7 -3
- package/dist/telemetry/har/sanitize.js +52 -6
- package/dist/telemetry/har/sanitizeBody.d.ts +47 -7
- package/dist/telemetry/har/sanitizeBody.js +429 -56
- package/dist/telemetry/har/types.d.ts +2 -0
- package/dist/telemetry/network.d.ts +4 -4
- package/dist/telemetry/network.js +38 -4
- package/dist/telemetry/networkRetention.d.ts +35 -14
- package/dist/telemetry/networkRetention.js +62 -26
- package/dist/types.d.ts +9 -14
- package/dist/ui/OutputBuilder.d.ts +3 -2
- package/dist/ui/OutputBuilder.js +4 -3
- package/dist/ui/formatters/cdp.d.ts +32 -9
- package/dist/ui/formatters/cdp.js +77 -6
- package/dist/ui/formatters/details.js +7 -15
- package/dist/ui/formatters/preview.d.ts +2 -0
- package/dist/ui/formatters/preview.js +7 -1
- package/dist/ui/formatters/status.js +6 -1
- package/dist/ui/formatting.d.ts +7 -0
- package/dist/ui/formatting.js +13 -0
- package/dist/ui/logging/logger.d.ts +1 -1
- package/dist/ui/messages/chrome.d.ts +13 -0
- package/dist/ui/messages/chrome.js +26 -0
- package/dist/ui/messages/commands.d.ts +71 -3
- package/dist/ui/messages/commands.js +98 -3
- package/dist/ui/messages/networkMessages.d.ts +24 -5
- package/dist/ui/messages/networkMessages.js +31 -8
- package/dist/utils/async.d.ts +3 -2
- package/dist/utils/async.js +16 -3
- package/dist/utils/http.d.ts +11 -4
- package/dist/utils/http.js +5 -3
- package/package.json +18 -4
- /package/dist/{commands/dom → runtime/page}/screenshotResize.d.ts +0 -0
- /package/dist/{commands/dom → runtime/page}/screenshotResize.js +0 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Page emulation a screenshot changes for its capture, and puts back.
|
|
3
|
+
*
|
|
4
|
+
* A capture at a pixel ratio of 1 on a high-DPI page, and one beyond the
|
|
5
|
+
* viewport, override the device metrics (and hide the scrollbars). Each
|
|
6
|
+
* change is recorded before it is sent, so {@link CaptureEmulation.restore}
|
|
7
|
+
* (called once the capture ended, however it ended) undoes exactly what may
|
|
8
|
+
* have been changed: the session's emulation is put back from the daemon's
|
|
9
|
+
* own record of it as it is then (a `page emulate` during the capture
|
|
10
|
+
* counts), not from a file.
|
|
11
|
+
*/
|
|
12
|
+
import { TypedCDPConnection } from '../../connection/typed-cdp.js';
|
|
13
|
+
import { evaluateInBdgWorld } from './bdgWorld.js';
|
|
14
|
+
import { viewportOverride } from './emulation.js';
|
|
15
|
+
import { createLogger } from '../../ui/logging/index.js';
|
|
16
|
+
import { getErrorMessage } from '../../utils/errors.js';
|
|
17
|
+
const log = createLogger('dom');
|
|
18
|
+
/**
|
|
19
|
+
* Evaluate one of bdg's page scripts and return its value.
|
|
20
|
+
*
|
|
21
|
+
* @param cdp - Session connection
|
|
22
|
+
* @param expression - Script
|
|
23
|
+
* @returns Its value, or undefined when it threw
|
|
24
|
+
*/
|
|
25
|
+
export async function evaluateValue(cdp, expression) {
|
|
26
|
+
const response = await evaluateInBdgWorld(cdp, { expression, returnByValue: true });
|
|
27
|
+
return response.result.value;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Two numbers a page script returned as an array.
|
|
31
|
+
*
|
|
32
|
+
* @param cdp - Session connection
|
|
33
|
+
* @param expression - Script returning `[a, b]`
|
|
34
|
+
* @returns The numbers, undefined where the page did not answer
|
|
35
|
+
*/
|
|
36
|
+
export async function evaluatePair(cdp, expression) {
|
|
37
|
+
const value = await evaluateValue(cdp, expression);
|
|
38
|
+
const [a, b] = Array.isArray(value) ? value : [];
|
|
39
|
+
return [a, b];
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The page's scroll position.
|
|
43
|
+
*
|
|
44
|
+
* @param cdp - Session connection
|
|
45
|
+
* @returns Scroll offsets in CSS px
|
|
46
|
+
*/
|
|
47
|
+
export async function scrollPosition(cdp) {
|
|
48
|
+
const [x, y] = await evaluatePair(cdp, '[window.scrollX, window.scrollY]');
|
|
49
|
+
return { x: x ?? 0, y: y ?? 0 };
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The emulation changes of one capture, undone by {@link restore}.
|
|
53
|
+
*/
|
|
54
|
+
export class CaptureEmulation {
|
|
55
|
+
connection;
|
|
56
|
+
sessionViewport;
|
|
57
|
+
cdp;
|
|
58
|
+
metricsChanged = false;
|
|
59
|
+
scrollbarsHidden = false;
|
|
60
|
+
scrolledFrom;
|
|
61
|
+
/**
|
|
62
|
+
* @param connection - Session connection
|
|
63
|
+
* @param sessionViewport - Reads the session's emulated viewport
|
|
64
|
+
* (`--viewport`, `--mobile`, `page emulate`) as it is now; put back
|
|
65
|
+
* afterwards, none clears the override
|
|
66
|
+
*/
|
|
67
|
+
constructor(connection, sessionViewport) {
|
|
68
|
+
this.connection = connection;
|
|
69
|
+
this.sessionViewport = sessionViewport;
|
|
70
|
+
this.cdp = new TypedCDPConnection(connection);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Capture at a pixel ratio of 1 (CSS px = image px) on a high-DPI page:
|
|
74
|
+
* the viewport is overridden at the session's viewport, else the window's
|
|
75
|
+
* size with its scrollbars (so the layout does not change).
|
|
76
|
+
*
|
|
77
|
+
* @param devicePixelRatio - Page's pixel ratio
|
|
78
|
+
* @param viewport - Visible viewport size, used when the page does not answer
|
|
79
|
+
*/
|
|
80
|
+
async useUnitPixelRatio(devicePixelRatio, viewport) {
|
|
81
|
+
if (devicePixelRatio === 1)
|
|
82
|
+
return;
|
|
83
|
+
const size = this.sessionViewport() ?? (await this.windowSize(viewport));
|
|
84
|
+
this.metricsChanged = true;
|
|
85
|
+
await this.cdp.send('Emulation.setDeviceMetricsOverride', viewportOverride(size, 1));
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Lay the page out at its current width without scrollbars: a capture
|
|
89
|
+
* beyond the viewport hides them, and without this the page would widen by
|
|
90
|
+
* the scrollbar and centered content move after it was measured. The
|
|
91
|
+
* viewport is overridden at the visible size (CSS px, pixel ratio 1; still
|
|
92
|
+
* a phone in a phone session).
|
|
93
|
+
*
|
|
94
|
+
* @param view - Visible viewport size
|
|
95
|
+
*/
|
|
96
|
+
async keepLayoutWithoutScrollbars(view) {
|
|
97
|
+
const phone = this.sessionViewport()?.mobile;
|
|
98
|
+
this.scrollbarsHidden = true;
|
|
99
|
+
await this.cdp.send('Emulation.setScrollbarsHidden', { hidden: true });
|
|
100
|
+
this.metricsChanged = true;
|
|
101
|
+
await this.cdp.send('Emulation.setDeviceMetricsOverride', viewportOverride({ ...view, ...(phone && { mobile: true }) }, 1));
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Record where the page was scrolled before the capture moved it, to
|
|
105
|
+
* scroll it back afterwards (the first position recorded wins).
|
|
106
|
+
*
|
|
107
|
+
* @param position - Scroll position before the capture
|
|
108
|
+
*/
|
|
109
|
+
scrolledAwayFrom(position) {
|
|
110
|
+
this.scrolledFrom ??= position;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Put back what the capture changed: scrollbars shown, the session's
|
|
114
|
+
* device metrics (its viewport and a phone's touch input, which a capture
|
|
115
|
+
* beyond the viewport turns off), else none, and the scroll position. Each
|
|
116
|
+
* step runs even when an earlier one failed.
|
|
117
|
+
*
|
|
118
|
+
* @throws The first step's error, once every step ran
|
|
119
|
+
*/
|
|
120
|
+
async restore() {
|
|
121
|
+
const failures = [];
|
|
122
|
+
for (const [name, step] of this.restoreSteps()) {
|
|
123
|
+
try {
|
|
124
|
+
await step();
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
log.debug(`Screenshot restore (${name}) failed: ${getErrorMessage(error)}`);
|
|
128
|
+
failures.push(error);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (failures.length > 0)
|
|
132
|
+
throw failures[0];
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* The steps {@link restore} runs, for what the capture changed.
|
|
136
|
+
*
|
|
137
|
+
* @returns Named steps, in order
|
|
138
|
+
*/
|
|
139
|
+
restoreSteps() {
|
|
140
|
+
const steps = [];
|
|
141
|
+
if (this.scrollbarsHidden) {
|
|
142
|
+
steps.push([
|
|
143
|
+
'scrollbars',
|
|
144
|
+
() => this.cdp.send('Emulation.setScrollbarsHidden', { hidden: false }),
|
|
145
|
+
]);
|
|
146
|
+
}
|
|
147
|
+
if (this.metricsChanged)
|
|
148
|
+
steps.push(['device metrics', () => this.restoreSessionMetrics()]);
|
|
149
|
+
const scrolledFrom = this.scrolledFrom;
|
|
150
|
+
if (scrolledFrom) {
|
|
151
|
+
const { x, y } = scrolledFrom;
|
|
152
|
+
steps.push(['scroll', () => evaluateValue(this.connection, `window.scrollTo(${x}, ${y})`)]);
|
|
153
|
+
}
|
|
154
|
+
return steps;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Put back the session's device metrics, or clear the override.
|
|
158
|
+
*/
|
|
159
|
+
async restoreSessionMetrics() {
|
|
160
|
+
const viewport = this.sessionViewport();
|
|
161
|
+
if (!viewport) {
|
|
162
|
+
await this.cdp.send('Emulation.clearDeviceMetricsOverride', {});
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
await this.cdp.send('Emulation.setDeviceMetricsOverride', viewportOverride(viewport));
|
|
166
|
+
if (viewport.mobile) {
|
|
167
|
+
await this.cdp.send('Emulation.setTouchEmulationEnabled', {
|
|
168
|
+
enabled: true,
|
|
169
|
+
maxTouchPoints: 5,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* The window's size with its scrollbars (`innerWidth`/`innerHeight`): an
|
|
175
|
+
* override at this size keeps the page's layout, where the visible size
|
|
176
|
+
* (without scrollbars) would narrow it and move centered content.
|
|
177
|
+
*
|
|
178
|
+
* @param viewport - Visible viewport size, used when the page does not answer
|
|
179
|
+
* @returns Width and height in CSS px
|
|
180
|
+
*/
|
|
181
|
+
async windowSize(viewport) {
|
|
182
|
+
const [width, height] = await evaluatePair(this.connection, '[window.innerWidth, window.innerHeight]');
|
|
183
|
+
return {
|
|
184
|
+
width: Math.round(width ?? viewport.width),
|
|
185
|
+
height: Math.round(height ?? viewport.height),
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=captureEmulation.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scrolling an element into view for a page screenshot (`--scroll`), and
|
|
3
|
+
* waiting for the page to settle afterwards (lazy loading, mutations).
|
|
4
|
+
*/
|
|
5
|
+
import type { CDPConnection } from '../../connection/cdp.js';
|
|
6
|
+
import { type ScrollPosition } from './captureEmulation.js';
|
|
7
|
+
/**
|
|
8
|
+
* Scroll an element into view (centered) and wait for the page to settle.
|
|
9
|
+
*
|
|
10
|
+
* @param cdp - Session connection
|
|
11
|
+
* @param selector - The element
|
|
12
|
+
* @returns Scroll position before, to put the page back afterwards
|
|
13
|
+
* @throws CommandError (83) when nothing matches
|
|
14
|
+
*/
|
|
15
|
+
export declare function scrollToElement(cdp: CDPConnection, selector: string): Promise<ScrollPosition>;
|
|
16
|
+
/**
|
|
17
|
+
* Scroll an element into view again (centered), e.g. after an override moved
|
|
18
|
+
* the page.
|
|
19
|
+
*
|
|
20
|
+
* @param cdp - Session connection
|
|
21
|
+
* @param selector - The element
|
|
22
|
+
*/
|
|
23
|
+
export declare function scrollIntoViewAgain(cdp: CDPConnection, selector: string): Promise<void>;
|
|
24
|
+
//# sourceMappingURL=captureScroll.d.ts.map
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scrolling an element into view for a page screenshot (`--scroll`), and
|
|
3
|
+
* waiting for the page to settle afterwards (lazy loading, mutations).
|
|
4
|
+
*/
|
|
5
|
+
import { CommandError } from '../../errors/index.js';
|
|
6
|
+
import { noNodesFoundError } from '../../errors/messages.js';
|
|
7
|
+
import { DEEP_QUERY_JS, selectorArgsJS } from '../dom/targetNode.js';
|
|
8
|
+
import { evaluateValue } from './captureEmulation.js';
|
|
9
|
+
import { createLogger } from '../../ui/logging/index.js';
|
|
10
|
+
import { delay } from '../../utils/async.js';
|
|
11
|
+
import { EXIT_CODES } from '../../utils/exitCodes.js';
|
|
12
|
+
const log = createLogger('dom');
|
|
13
|
+
const POST_SCROLL_NETWORK_IDLE_MS = 150;
|
|
14
|
+
const POST_SCROLL_DOM_STABLE_MS = 200;
|
|
15
|
+
const POST_SCROLL_MAX_WAIT_MS = 2000;
|
|
16
|
+
const STABILITY_CHECK_INTERVAL_MS = 50;
|
|
17
|
+
/** Page-side: start recording resource loads and DOM mutations */
|
|
18
|
+
const WATCH_STABILITY_JS = `(() => {
|
|
19
|
+
window.__bdg_scrollStability = {
|
|
20
|
+
lastNetworkActivity: Date.now(),
|
|
21
|
+
lastDomMutation: Date.now(),
|
|
22
|
+
activeRequests: 0
|
|
23
|
+
};
|
|
24
|
+
const state = window.__bdg_scrollStability;
|
|
25
|
+
if (window.PerformanceObserver) {
|
|
26
|
+
const perfObserver = new PerformanceObserver((list) => {
|
|
27
|
+
for (const entry of list.getEntries()) {
|
|
28
|
+
if (entry.entryType === 'resource') state.lastNetworkActivity = Date.now();
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
try {
|
|
32
|
+
perfObserver.observe({ entryTypes: ['resource'] });
|
|
33
|
+
state.perfObserver = perfObserver;
|
|
34
|
+
} catch (e) {}
|
|
35
|
+
}
|
|
36
|
+
const mutationObserver = new MutationObserver(() => {
|
|
37
|
+
state.lastDomMutation = Date.now();
|
|
38
|
+
});
|
|
39
|
+
mutationObserver.observe(document.body || document.documentElement, {
|
|
40
|
+
childList: true,
|
|
41
|
+
subtree: true,
|
|
42
|
+
attributes: true
|
|
43
|
+
});
|
|
44
|
+
state.mutationObserver = mutationObserver;
|
|
45
|
+
})()`;
|
|
46
|
+
/** Page-side: milliseconds since the last resource load and DOM mutation */
|
|
47
|
+
const STABILITY_JS = `(() => {
|
|
48
|
+
const state = window.__bdg_scrollStability;
|
|
49
|
+
if (!state) return { networkIdle: 999, domIdle: 999 };
|
|
50
|
+
return {
|
|
51
|
+
networkIdle: Date.now() - state.lastNetworkActivity,
|
|
52
|
+
domIdle: Date.now() - state.lastDomMutation
|
|
53
|
+
};
|
|
54
|
+
})()`;
|
|
55
|
+
/** Page-side: stop recording */
|
|
56
|
+
const UNWATCH_STABILITY_JS = `(() => {
|
|
57
|
+
const state = window.__bdg_scrollStability;
|
|
58
|
+
if (state) {
|
|
59
|
+
state.perfObserver?.disconnect();
|
|
60
|
+
state.mutationObserver?.disconnect();
|
|
61
|
+
delete window.__bdg_scrollStability;
|
|
62
|
+
}
|
|
63
|
+
})()`;
|
|
64
|
+
/**
|
|
65
|
+
* Wait for the page to settle after a programmatic scroll (lazy-load idle +
|
|
66
|
+
* DOM mutation idle). Uses shorter thresholds than full page load.
|
|
67
|
+
*
|
|
68
|
+
* @param cdp - Session connection
|
|
69
|
+
*/
|
|
70
|
+
async function waitForPostScrollStability(cdp) {
|
|
71
|
+
const deadline = Date.now() + POST_SCROLL_MAX_WAIT_MS;
|
|
72
|
+
await evaluateValue(cdp, WATCH_STABILITY_JS);
|
|
73
|
+
try {
|
|
74
|
+
while (Date.now() < deadline) {
|
|
75
|
+
const value = (await evaluateValue(cdp, STABILITY_JS));
|
|
76
|
+
const networkIdle = value?.networkIdle ?? 0;
|
|
77
|
+
const domIdle = value?.domIdle ?? 0;
|
|
78
|
+
if (networkIdle >= POST_SCROLL_NETWORK_IDLE_MS && domIdle >= POST_SCROLL_DOM_STABLE_MS) {
|
|
79
|
+
log.debug(`Post-scroll stable: network ${networkIdle}ms, DOM ${domIdle}ms`);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
await delay(STABILITY_CHECK_INTERVAL_MS);
|
|
83
|
+
}
|
|
84
|
+
log.debug('Post-scroll stability timeout, proceeding anyway');
|
|
85
|
+
}
|
|
86
|
+
finally {
|
|
87
|
+
await evaluateValue(cdp, UNWATCH_STABILITY_JS);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Scroll an element into view (centered) and wait for the page to settle.
|
|
92
|
+
*
|
|
93
|
+
* @param cdp - Session connection
|
|
94
|
+
* @param selector - The element
|
|
95
|
+
* @returns Scroll position before, to put the page back afterwards
|
|
96
|
+
* @throws CommandError (83) when nothing matches
|
|
97
|
+
*/
|
|
98
|
+
export async function scrollToElement(cdp, selector) {
|
|
99
|
+
const value = (await evaluateValue(cdp, `(() => {
|
|
100
|
+
const el = (${DEEP_QUERY_JS})(${selectorArgsJS(selector)})[0];
|
|
101
|
+
if (!el) return { found: false };
|
|
102
|
+
const originalX = window.scrollX;
|
|
103
|
+
const originalY = window.scrollY;
|
|
104
|
+
el.scrollIntoView({ block: 'center', behavior: 'instant' });
|
|
105
|
+
return { found: true, originalX, originalY };
|
|
106
|
+
})()`));
|
|
107
|
+
if (!value?.found) {
|
|
108
|
+
const err = noNodesFoundError(selector);
|
|
109
|
+
throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.RESOURCE_NOT_FOUND);
|
|
110
|
+
}
|
|
111
|
+
await waitForPostScrollStability(cdp);
|
|
112
|
+
return { x: value.originalX ?? 0, y: value.originalY ?? 0 };
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Scroll an element into view again (centered), e.g. after an override moved
|
|
116
|
+
* the page.
|
|
117
|
+
*
|
|
118
|
+
* @param cdp - Session connection
|
|
119
|
+
* @param selector - The element
|
|
120
|
+
*/
|
|
121
|
+
export async function scrollIntoViewAgain(cdp, selector) {
|
|
122
|
+
await evaluateValue(cdp, `(${DEEP_QUERY_JS})(${selectorArgsJS(selector)})[0]?.scrollIntoView({ block: 'center', behavior: 'instant' })`);
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=captureScroll.js.map
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Screenshots of the page or one element (`bdg dom screenshot`), taken in the
|
|
3
|
+
* daemon: measuring, the emulation changes the capture needs, the capture and
|
|
4
|
+
* putting the emulation back all happen here, inside one `try`/`finally`, so
|
|
5
|
+
* a CLI interrupted mid-capture (Ctrl-C) cannot leave the page changed. The
|
|
6
|
+
* CLI only writes the returned image.
|
|
7
|
+
*
|
|
8
|
+
* The image travels base64-encoded in one IPC line: Chrome sends it the same
|
|
9
|
+
* way over its WebSocket (100 MiB at most), well below the IPC line limit
|
|
10
|
+
* (`MAX_JSONL_BUFFER_SIZE`).
|
|
11
|
+
*/
|
|
12
|
+
import type { CDPConnection } from '../../connection/cdp.js';
|
|
13
|
+
import type { DomScreenshotCommand, DomScreenshotData } from '../../ipc/protocol/commands.js';
|
|
14
|
+
import { type BusyRecoveryOptions } from '../dom/evalHelpers.js';
|
|
15
|
+
import type { ViewportSize } from '../../types.js';
|
|
16
|
+
/** How a screenshot is run */
|
|
17
|
+
export interface ScreenshotOptions {
|
|
18
|
+
/** Aborted when the requesting client disconnects: the capture is skipped, the restore runs */
|
|
19
|
+
abandoned?: AbortSignal | undefined;
|
|
20
|
+
/** When a busy page is checked */
|
|
21
|
+
recovery?: BusyRecoveryOptions;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Take a screenshot: of the element `backendNodeId` names, else of the page.
|
|
25
|
+
* Whatever emulation the capture changed is put back before this returns or
|
|
26
|
+
* throws, from the session's record of its emulation at that time.
|
|
27
|
+
*
|
|
28
|
+
* A page whose scripts keep it busy gets them terminated
|
|
29
|
+
* ({@link withBusyPageRecovery}, exit 102); the capture then ends, and the
|
|
30
|
+
* error is reported once its emulation is back, so the next command does not
|
|
31
|
+
* race the restore. A capture whose client left (Ctrl-C) is not taken once
|
|
32
|
+
* that is known: only the restore runs.
|
|
33
|
+
*
|
|
34
|
+
* @param cdp - Session connection
|
|
35
|
+
* @param params - What to capture and how
|
|
36
|
+
* @param sessionViewport - Reads the session's emulated viewport, if any
|
|
37
|
+
* @param options - When the client left, and when a busy page is checked (tests shorten it)
|
|
38
|
+
* @returns The image (base64) and what was captured
|
|
39
|
+
*/
|
|
40
|
+
export declare function takeScreenshot(cdp: CDPConnection, params: DomScreenshotCommand, sessionViewport: () => ViewportSize | undefined, options?: ScreenshotOptions): Promise<DomScreenshotData>;
|
|
41
|
+
//# sourceMappingURL=screenshot.d.ts.map
|