clearcote 0.38.0 → 0.39.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 +3 -1
- package/dist/cdpws.d.ts +11 -0
- package/dist/cdpws.d.ts.map +1 -0
- package/dist/cdpws.js +208 -0
- package/dist/cdpws.js.map +1 -0
- package/dist/geometry.d.ts +17 -9
- package/dist/geometry.d.ts.map +1 -1
- package/dist/geometry.js +77 -94
- package/dist/geometry.js.map +1 -1
- package/dist/index.d.ts +41 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +205 -72
- package/dist/index.js.map +1 -1
- package/dist/launchopts.d.ts +90 -0
- package/dist/launchopts.d.ts.map +1 -1
- package/dist/launchopts.js +209 -1
- package/dist/launchopts.js.map +1 -1
- package/dist/profileauto.d.ts +15 -0
- package/dist/profileauto.d.ts.map +1 -1
- package/dist/profileauto.js +36 -0
- package/dist/profileauto.js.map +1 -1
- package/dist/shaderdialect.d.ts +19 -6
- package/dist/shaderdialect.d.ts.map +1 -1
- package/dist/shaderdialect.js +61 -11
- package/dist/shaderdialect.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -203,7 +203,9 @@ if (!verdict.coherent) console.warn(verdict.warnings);
|
|
|
203
203
|
|
|
204
204
|
It reads the (unmasked) WebGL vendor/renderer the page sees, flags a software rasterizer (a fatal headless tell — enable the canvas bridge or run headed on a real GPU) and an incoherent vendor/renderer pair. Pass a second arg (`claimedGpu`) to also assert the rendered family.
|
|
205
205
|
|
|
206
|
-
The renderer *string* alone is not enough: a persona renames the backend, so a SwiftShader fallback stops looking like one. The check therefore also measures limits the string cannot move — `MAX_TEXTURE_SIZE`, the vertex/fragment uniform-vector pair, and an actual 16384-wide texture allocation — and reports them as `maxTextureSize`, `maxVertexUniformVectors`, `maxFragmentUniformVectors` and `canAllocate16kTexture`. A renderer naming a desktop GPU while `MAX_TEXTURE_SIZE` is below 16384 is a spoof over a software rasterizer and comes back `coherent: false`. **Headless on Linux with no GPU hits exactly this**
|
|
206
|
+
The renderer *string* alone is not enough: a persona renames the backend, so a SwiftShader fallback stops looking like one. The check therefore also measures limits the string cannot move — `MAX_TEXTURE_SIZE`, the vertex/fragment uniform-vector pair, and an actual 16384-wide texture allocation — and reports them as `maxTextureSize`, `maxVertexUniformVectors`, `maxFragmentUniformVectors` and `canAllocate16kTexture`. A renderer naming a desktop GPU while `MAX_TEXTURE_SIZE` is below 16384 is a spoof over a software rasterizer and comes back `coherent: false`. **Headless on Linux with no GPU hits exactly this** when Chromium renders through SwiftShader (8192): a Windows persona always does, and a Linux persona does on a host without Mesa's EGL (`libegl1`). Run headed under Xvfb, or use the canvas bridge.
|
|
207
|
+
|
|
208
|
+
On a Linux host the SDK picks the software backend that matches the GPU the persona names: a Linux persona (Mesa/OpenGL) renders through Mesa's own GL — over the X display when one is reachable (headed, or headless with `DISPLAY` pointing at an Xvfb), otherwise over EGL when the host has Mesa's EGL (`libegl1` on Debian/Ubuntu) — and a Windows persona (Direct3D11) renders through SwiftShader, whose limits match Direct3D11. Your own `--use-angle=` / `--use-gl=` in `args` wins.
|
|
207
209
|
|
|
208
210
|
### Hardened launch defaults
|
|
209
211
|
|
package/dist/cdpws.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** One browser-level CDP WebSocket. `send()` resolves with its own reply; events are dropped. */
|
|
2
|
+
export interface CdpConnection {
|
|
3
|
+
send(method: string, params?: Record<string, unknown>, sessionId?: string): Promise<Record<string, unknown>>;
|
|
4
|
+
close(): void;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Open a CDP WebSocket (ws:// or wss://). Rejects when the endpoint refuses the upgrade or does not
|
|
8
|
+
* answer within `timeoutMs`; each `send()` gets the same budget for its reply.
|
|
9
|
+
*/
|
|
10
|
+
export declare function connectCdp(wsUrl: string, timeoutMs: number): Promise<CdpConnection>;
|
|
11
|
+
//# sourceMappingURL=cdpws.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cdpws.d.ts","sourceRoot":"","sources":["../src/cdpws.ts"],"names":[],"mappings":"AAeA,iGAAiG;AACjG,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC7G,KAAK,IAAI,IAAI,CAAC;CACf;AA4BD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CA+InF"}
|
package/dist/cdpws.js
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
// A minimal CDP client over a WebSocket, on Node's standard library only. Port of the Python SDK's
|
|
2
|
+
// `_cdpws.py`.
|
|
3
|
+
//
|
|
4
|
+
// serve() launches the engine itself, so there is no Playwright connection to borrow, and Node only
|
|
5
|
+
// has a global WebSocket from 22 on while the SDK supports 20. The headless window fit used to ride
|
|
6
|
+
// on that global, so on Node 20 it was silently skipped: the browser got its display but kept the
|
|
7
|
+
// engine's default window, about 1000px wide inside a 1920px screen. What the fit needs is small: a
|
|
8
|
+
// handful of request/response calls on the browser endpoint (flat sessions for the one page it
|
|
9
|
+
// reads), text frames only, never a subscription. That is all this implements, deliberately not a
|
|
10
|
+
// general client.
|
|
11
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
12
|
+
import net from "node:net";
|
|
13
|
+
import tls from "node:tls";
|
|
14
|
+
const WS_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
|
15
|
+
const OP_CONTINUATION = 0x0, OP_TEXT = 0x1, OP_BINARY = 0x2, OP_CLOSE = 0x8, OP_PING = 0x9, OP_PONG = 0xa;
|
|
16
|
+
/** A client frame: FIN set, payload masked (RFC 6455 5.3 requires it of clients). */
|
|
17
|
+
function frame(opcode, payload) {
|
|
18
|
+
const n = payload.length;
|
|
19
|
+
let head;
|
|
20
|
+
if (n < 126) {
|
|
21
|
+
head = Buffer.from([0x80 | opcode, 0x80 | n]);
|
|
22
|
+
}
|
|
23
|
+
else if (n < 65536) {
|
|
24
|
+
head = Buffer.alloc(4);
|
|
25
|
+
head[0] = 0x80 | opcode;
|
|
26
|
+
head[1] = 0x80 | 126;
|
|
27
|
+
head.writeUInt16BE(n, 2);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
head = Buffer.alloc(10);
|
|
31
|
+
head[0] = 0x80 | opcode;
|
|
32
|
+
head[1] = 0x80 | 127;
|
|
33
|
+
head.writeBigUInt64BE(BigInt(n), 2);
|
|
34
|
+
}
|
|
35
|
+
const mask = randomBytes(4);
|
|
36
|
+
const masked = Buffer.alloc(n);
|
|
37
|
+
for (let i = 0; i < n; i++)
|
|
38
|
+
masked[i] = payload[i] ^ mask[i & 3];
|
|
39
|
+
return Buffer.concat([head, mask, masked]);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Open a CDP WebSocket (ws:// or wss://). Rejects when the endpoint refuses the upgrade or does not
|
|
43
|
+
* answer within `timeoutMs`; each `send()` gets the same budget for its reply.
|
|
44
|
+
*/
|
|
45
|
+
export function connectCdp(wsUrl, timeoutMs) {
|
|
46
|
+
return new Promise((resolveOpen, rejectOpen) => {
|
|
47
|
+
let u;
|
|
48
|
+
try {
|
|
49
|
+
u = new URL(wsUrl);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return rejectOpen(new Error(`expected a ws:// CDP URL, got ${JSON.stringify(wsUrl)}`));
|
|
53
|
+
}
|
|
54
|
+
if ((u.protocol !== "ws:" && u.protocol !== "wss:") || !u.hostname) {
|
|
55
|
+
return rejectOpen(new Error(`expected a ws:// CDP URL, got ${JSON.stringify(wsUrl)}`));
|
|
56
|
+
}
|
|
57
|
+
const secure = u.protocol === "wss:";
|
|
58
|
+
const host = u.hostname.replace(/^\[(.*)\]$/, "$1"); // an IPv6 literal comes bracketed
|
|
59
|
+
const port = Number(u.port) || (secure ? 443 : 80);
|
|
60
|
+
const key = randomBytes(16).toString("base64");
|
|
61
|
+
const accept = createHash("sha1").update(key + WS_GUID).digest("base64");
|
|
62
|
+
let open = false;
|
|
63
|
+
let closed = false;
|
|
64
|
+
let buf = Buffer.alloc(0);
|
|
65
|
+
let parts = [];
|
|
66
|
+
let next = 0;
|
|
67
|
+
const pending = new Map();
|
|
68
|
+
const sock = secure
|
|
69
|
+
? tls.connect({ host, port, servername: net.isIP(host) ? undefined : host })
|
|
70
|
+
: net.connect({ host, port });
|
|
71
|
+
const openTimer = setTimeout(() => fail(new Error("CDP connect timed out")), timeoutMs);
|
|
72
|
+
/** End the connection once: settle everything still waiting, then let the socket go. */
|
|
73
|
+
function fail(err, closeFrame) {
|
|
74
|
+
if (closed)
|
|
75
|
+
return;
|
|
76
|
+
closed = true;
|
|
77
|
+
clearTimeout(openTimer);
|
|
78
|
+
if (closeFrame) {
|
|
79
|
+
// Flush the close frame before letting go; an endpoint that never answers it is cut off.
|
|
80
|
+
sock.end(closeFrame);
|
|
81
|
+
setTimeout(() => sock.destroy(), 1000).unref();
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
sock.destroy();
|
|
85
|
+
}
|
|
86
|
+
if (!open)
|
|
87
|
+
rejectOpen(err);
|
|
88
|
+
for (const p of pending.values())
|
|
89
|
+
p.reject(err);
|
|
90
|
+
pending.clear();
|
|
91
|
+
}
|
|
92
|
+
function onMessage(text) {
|
|
93
|
+
let m;
|
|
94
|
+
try {
|
|
95
|
+
m = JSON.parse(text);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const p = m.id === undefined ? undefined : pending.get(m.id);
|
|
101
|
+
if (!p)
|
|
102
|
+
return; // an event, or a stale reply: this client subscribes to nothing
|
|
103
|
+
pending.delete(m.id);
|
|
104
|
+
if (m.error)
|
|
105
|
+
p.reject(new Error(m.error.message ?? JSON.stringify(m.error)));
|
|
106
|
+
else
|
|
107
|
+
p.resolve(m.result ?? {});
|
|
108
|
+
}
|
|
109
|
+
/** Consume every complete frame in `buf` (fragments joined; pings answered). */
|
|
110
|
+
function readFrames() {
|
|
111
|
+
while (!closed && buf.length >= 2) {
|
|
112
|
+
const fin = buf[0] & 0x80, opcode = buf[0] & 0x0f, masked = buf[1] & 0x80;
|
|
113
|
+
let n = buf[1] & 0x7f;
|
|
114
|
+
let off = 2;
|
|
115
|
+
if (n === 126) {
|
|
116
|
+
if (buf.length < 4)
|
|
117
|
+
return;
|
|
118
|
+
n = buf.readUInt16BE(2);
|
|
119
|
+
off = 4;
|
|
120
|
+
}
|
|
121
|
+
else if (n === 127) {
|
|
122
|
+
if (buf.length < 10)
|
|
123
|
+
return;
|
|
124
|
+
n = Number(buf.readBigUInt64BE(2));
|
|
125
|
+
off = 10;
|
|
126
|
+
}
|
|
127
|
+
const mask = masked ? off : -1;
|
|
128
|
+
if (masked)
|
|
129
|
+
off += 4;
|
|
130
|
+
if (buf.length < off + n)
|
|
131
|
+
return;
|
|
132
|
+
let payload = buf.subarray(off, off + n);
|
|
133
|
+
if (mask >= 0)
|
|
134
|
+
payload = Buffer.from(payload.map((b, i) => b ^ buf[mask + (i & 3)]));
|
|
135
|
+
buf = buf.subarray(off + n);
|
|
136
|
+
if (opcode === OP_CLOSE)
|
|
137
|
+
return fail(new Error("CDP connection closed"));
|
|
138
|
+
if (opcode === OP_PING) {
|
|
139
|
+
sock.write(frame(OP_PONG, payload));
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (opcode === OP_PONG)
|
|
143
|
+
continue;
|
|
144
|
+
if (opcode === OP_TEXT || opcode === OP_BINARY || opcode === OP_CONTINUATION) {
|
|
145
|
+
parts.push(payload);
|
|
146
|
+
if (fin) {
|
|
147
|
+
const text = Buffer.concat(parts).toString("utf8");
|
|
148
|
+
parts = [];
|
|
149
|
+
onMessage(text);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/** The upgrade response: `101` and the accept hash RFC 6455 4.1 derives from our key. */
|
|
155
|
+
function readHandshake() {
|
|
156
|
+
const end = buf.indexOf("\r\n\r\n");
|
|
157
|
+
if (end < 0)
|
|
158
|
+
return;
|
|
159
|
+
const lines = buf.subarray(0, end).toString("latin1").split("\r\n");
|
|
160
|
+
buf = buf.subarray(end + 4);
|
|
161
|
+
if (!/^HTTP\/1\.[01] 101\b/.test(lines[0]))
|
|
162
|
+
return fail(new Error(`CDP WebSocket upgrade refused: ${lines[0]}`));
|
|
163
|
+
const line = lines.slice(1).find((l) => /^sec-websocket-accept:/i.test(l));
|
|
164
|
+
const got = line?.slice(line.indexOf(":") + 1).trim();
|
|
165
|
+
if (got !== accept)
|
|
166
|
+
return fail(new Error("CDP WebSocket upgrade answered with the wrong accept key"));
|
|
167
|
+
open = true;
|
|
168
|
+
clearTimeout(openTimer);
|
|
169
|
+
resolveOpen(connection);
|
|
170
|
+
readFrames();
|
|
171
|
+
}
|
|
172
|
+
const connection = {
|
|
173
|
+
send(method, params = {}, sessionId) {
|
|
174
|
+
return new Promise((resolve, reject) => {
|
|
175
|
+
if (closed)
|
|
176
|
+
return reject(new Error("CDP connection closed"));
|
|
177
|
+
const id = ++next;
|
|
178
|
+
const t = setTimeout(() => { pending.delete(id); reject(new Error(`${method} timed out`)); }, timeoutMs);
|
|
179
|
+
pending.set(id, {
|
|
180
|
+
resolve: (v) => { clearTimeout(t); resolve(v); },
|
|
181
|
+
reject: (e) => { clearTimeout(t); reject(e); },
|
|
182
|
+
});
|
|
183
|
+
sock.write(frame(OP_TEXT, Buffer.from(JSON.stringify({ id, method, params, ...(sessionId ? { sessionId } : {}) }))));
|
|
184
|
+
});
|
|
185
|
+
},
|
|
186
|
+
close() {
|
|
187
|
+
fail(new Error("CDP connection closed"), frame(OP_CLOSE, Buffer.from([0x03, 0xe8]))); // 1000: normal closure
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
sock.on(secure ? "secureConnect" : "connect", () => {
|
|
191
|
+
const path = (u.pathname || "/") + u.search;
|
|
192
|
+
sock.write(`GET ${path} HTTP/1.1\r\nHost: ${u.host}\r\nUpgrade: websocket\r\nConnection: Upgrade\r\n` +
|
|
193
|
+
`Sec-WebSocket-Key: ${key}\r\nSec-WebSocket-Version: 13\r\n\r\n`);
|
|
194
|
+
});
|
|
195
|
+
sock.on("data", (chunk) => {
|
|
196
|
+
if (closed)
|
|
197
|
+
return;
|
|
198
|
+
buf = buf.length ? Buffer.concat([buf, chunk]) : chunk;
|
|
199
|
+
if (open)
|
|
200
|
+
readFrames();
|
|
201
|
+
else
|
|
202
|
+
readHandshake();
|
|
203
|
+
});
|
|
204
|
+
sock.on("error", (err) => fail(new Error(`CDP connection failed: ${err.message}`)));
|
|
205
|
+
sock.on("close", () => fail(new Error("CDP connection closed")));
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=cdpws.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cdpws.js","sourceRoot":"","sources":["../src/cdpws.ts"],"names":[],"mappings":"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"}
|
package/dist/geometry.d.ts
CHANGED
|
@@ -86,13 +86,19 @@ export declare function applyHeadlessGeometry(opts: Record<string, unknown>, see
|
|
|
86
86
|
/**
|
|
87
87
|
* The bounds correction, given what the window reported after the first attempt.
|
|
88
88
|
*
|
|
89
|
-
* Requested bounds and reported outerHeight are not the same quantity: on 149 the window
|
|
90
|
-
* 33px less than the bounds height it was given,
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
89
|
+
* Requested bounds and reported outerHeight are not always the same quantity: on 149 the window
|
|
90
|
+
* reported 33px less than the bounds height it was given, and on 150 a persona's window does (33px in
|
|
91
|
+
* a launch, 89 in serve(); without a persona it reports the bounds exactly). Fitting bounds to the
|
|
92
|
+
* work area then lands short of maximized (real maximized captures have outer == avail). Rather than
|
|
93
|
+
* hardcode it, measure the shortfall and add it back — that self-tunes if the engine changes. Never
|
|
94
|
+
* asks for more than the shortfall.
|
|
95
|
+
*
|
|
96
|
+
* `room` is the space from the window's position to the work area's far edges. On an axis where the
|
|
97
|
+
* window is smaller than that, the correction never takes the bounds past the edge: only a window that
|
|
98
|
+
* fills the work area needs bounds beyond it (the persona engine then clamps the window to the work
|
|
99
|
+
* area), and an engine without a persona does not clamp at all.
|
|
94
100
|
*/
|
|
95
|
-
export declare function fitPlan(avail: number[], outer: number[]): [number, number] | null;
|
|
101
|
+
export declare function fitPlan(avail: number[], outer: number[], room?: readonly number[] | null): [number, number] | null;
|
|
96
102
|
/**
|
|
97
103
|
* Size the headless window to the display's work area — the persona's (regime 1) or the one
|
|
98
104
|
* `--screen-info` set (regime 2) — so the page reports a maximized window (outer == avail) instead
|
|
@@ -174,17 +180,19 @@ export interface ServedFit {
|
|
|
174
180
|
/**
|
|
175
181
|
* Put the served browser's first window on its display's work area (or `windowSize`, clamped into
|
|
176
182
|
* it); under a persona, first make the headless display the persona's own. Over the caller's CDP
|
|
177
|
-
* connection, on the first page target. Only Target/Emulation/Browser commands plus
|
|
178
|
-
* Runtime.evaluate (never Runtime.enable) on that page, and every change is browser-level, so
|
|
183
|
+
* connection, on the first page target. Only Target/Emulation/Browser commands plus
|
|
184
|
+
* Runtime.evaluate reads (never Runtime.enable) on that page, and every change is browser-level, so
|
|
179
185
|
* nothing lingers on the page when the connection closes.
|
|
180
186
|
*
|
|
181
187
|
* Returns null when it could not act (no page, an implausible persona display, or an engine without
|
|
182
188
|
* `Emulation.updateScreen` under a persona, whose window cannot outgrow 800x600 without it). Never
|
|
183
|
-
* throws: geometry must not be able to fail a launch.
|
|
189
|
+
* throws: geometry must not be able to fail a launch. `settleMs` bounds the wait for each resize to
|
|
190
|
+
* reach the page ({@link settledOuter}).
|
|
184
191
|
*/
|
|
185
192
|
export declare function fitWindowOverCdp(cdp: CdpSend, opts: {
|
|
186
193
|
persona: boolean;
|
|
187
194
|
windowSize?: Size | null;
|
|
195
|
+
settleMs?: number;
|
|
188
196
|
}): Promise<ServedFit | null>;
|
|
189
197
|
/** {@link fitWindowOverCdp} on the SDK's own short connection to a served browser. Never throws. */
|
|
190
198
|
export declare function fitServedWindow(wsUrl: string | undefined, opts: {
|
package/dist/geometry.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"geometry.d.ts","sourceRoot":"","sources":["../src/geometry.ts"],"names":[],"mappings":"AAsDA,OAAO,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"geometry.d.ts","sourceRoot":"","sources":["../src/geometry.ts"],"names":[],"mappings":"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"}
|
package/dist/geometry.js
CHANGED
|
@@ -52,6 +52,7 @@
|
|
|
52
52
|
*/
|
|
53
53
|
import { createHash } from "node:crypto";
|
|
54
54
|
import { gunzipSync } from "node:zlib";
|
|
55
|
+
import { connectCdp } from "./cdpws.js";
|
|
55
56
|
import { lightStealthScreen } from "./fingerprint.js";
|
|
56
57
|
/**
|
|
57
58
|
* The linux-x64 engine's window frame around an EMULATED viewport: outer = inner + (WIDTH, HEIGHT).
|
|
@@ -209,18 +210,75 @@ function bounds(width, height) {
|
|
|
209
210
|
/**
|
|
210
211
|
* The bounds correction, given what the window reported after the first attempt.
|
|
211
212
|
*
|
|
212
|
-
* Requested bounds and reported outerHeight are not the same quantity: on 149 the window
|
|
213
|
-
* 33px less than the bounds height it was given,
|
|
214
|
-
*
|
|
215
|
-
*
|
|
216
|
-
*
|
|
213
|
+
* Requested bounds and reported outerHeight are not always the same quantity: on 149 the window
|
|
214
|
+
* reported 33px less than the bounds height it was given, and on 150 a persona's window does (33px in
|
|
215
|
+
* a launch, 89 in serve(); without a persona it reports the bounds exactly). Fitting bounds to the
|
|
216
|
+
* work area then lands short of maximized (real maximized captures have outer == avail). Rather than
|
|
217
|
+
* hardcode it, measure the shortfall and add it back — that self-tunes if the engine changes. Never
|
|
218
|
+
* asks for more than the shortfall.
|
|
219
|
+
*
|
|
220
|
+
* `room` is the space from the window's position to the work area's far edges. On an axis where the
|
|
221
|
+
* window is smaller than that, the correction never takes the bounds past the edge: only a window that
|
|
222
|
+
* fills the work area needs bounds beyond it (the persona engine then clamps the window to the work
|
|
223
|
+
* area), and an engine without a persona does not clamp at all.
|
|
217
224
|
*/
|
|
218
|
-
export function fitPlan(avail, outer) {
|
|
225
|
+
export function fitPlan(avail, outer, room) {
|
|
219
226
|
const dw = avail[0] - outer[0];
|
|
220
227
|
const dh = avail[1] - outer[1];
|
|
221
228
|
if (dw <= 0 && dh <= 0)
|
|
222
229
|
return null;
|
|
223
|
-
|
|
230
|
+
const capped = (want, i) => (room && avail[i] < room[i] ? Math.min(want, room[i]) : want);
|
|
231
|
+
return [capped(avail[0] + Math.max(dw, 0), 0), capped(avail[1] + Math.max(dh, 0), 1)];
|
|
232
|
+
}
|
|
233
|
+
/** How long a window resize may take to reach the page before a read is taken as it stands. */
|
|
234
|
+
const SETTLE_MS = 1000;
|
|
235
|
+
const SETTLE_POLL_MS = 20;
|
|
236
|
+
const sameSize = (a, b) => a[0] === b[0] && a[1] === b[1];
|
|
237
|
+
/**
|
|
238
|
+
* Read `[outerWidth, outerHeight]` until it reflects the last `Browser.setWindowBounds`. The call
|
|
239
|
+
* returns before the page has the new window size, so a read straight after it can still report the
|
|
240
|
+
* previous one (measured on 150/linux-x64: for tens of milliseconds). Correcting from such a
|
|
241
|
+
* read once asked for 1440+495 px on a 1920 px screen. Settled means the asked-for size, or a size
|
|
242
|
+
* other than `before` that is still there one poll later; `settled` is false when neither happened
|
|
243
|
+
* within `timeoutMs`.
|
|
244
|
+
*/
|
|
245
|
+
async function settledOuter(read, before, want, timeoutMs = SETTLE_MS) {
|
|
246
|
+
const deadline = Date.now() + timeoutMs;
|
|
247
|
+
let outer = await read();
|
|
248
|
+
for (;;) {
|
|
249
|
+
if (sameSize(outer, want))
|
|
250
|
+
return { outer, settled: true };
|
|
251
|
+
if (Date.now() >= deadline)
|
|
252
|
+
return { outer, settled: false };
|
|
253
|
+
await new Promise((r) => setTimeout(r, SETTLE_POLL_MS));
|
|
254
|
+
const next = await read();
|
|
255
|
+
if (!sameSize(outer, before) && sameSize(next, outer))
|
|
256
|
+
return { outer, settled: true };
|
|
257
|
+
outer = next;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Give the window `target`, then correct what the engine reports (see {@link fitPlan}), on settled
|
|
262
|
+
* reads only: a correction is planned from a size the page has actually seen, and kept only when the
|
|
263
|
+
* page then sees a window no larger than `target`; otherwise the bounds go back to `target`. With no
|
|
264
|
+
* settled read there is nothing to correct from, and `target` lies inside the work area. Returns the
|
|
265
|
+
* last outer size read.
|
|
266
|
+
*/
|
|
267
|
+
async function fitWindow(setBounds, read, target, room, settleMs) {
|
|
268
|
+
const [w, h] = target;
|
|
269
|
+
const before = await read();
|
|
270
|
+
await setBounds(w, h);
|
|
271
|
+
const first = await settledOuter(read, before, target, settleMs);
|
|
272
|
+
const plan = first.settled ? fitPlan(target, first.outer, room) : null;
|
|
273
|
+
if (!plan)
|
|
274
|
+
return first.outer;
|
|
275
|
+
await setBounds(plan[0], plan[1]);
|
|
276
|
+
const fixed = await settledOuter(read, first.outer, plan, settleMs);
|
|
277
|
+
// Overshooting would trade one impossible geometry for another (outer > target).
|
|
278
|
+
if (fixed.settled && fixed.outer[0] <= w && fixed.outer[1] <= h)
|
|
279
|
+
return fixed.outer;
|
|
280
|
+
await setBounds(w, h);
|
|
281
|
+
return (await settledOuter(read, fixed.outer, target, settleMs)).outer;
|
|
224
282
|
}
|
|
225
283
|
/**
|
|
226
284
|
* Size the headless window to the display's work area — the persona's (regime 1) or the one
|
|
@@ -241,18 +299,7 @@ export async function fitWindowToWorkArea(page, args) {
|
|
|
241
299
|
return null;
|
|
242
300
|
const cdp = await page.context().newCDPSession(page);
|
|
243
301
|
const { windowId } = (await cdp.send("Browser.getWindowForTarget"));
|
|
244
|
-
await cdp.send("Browser.setWindowBounds", { windowId, bounds: bounds(avail[0], avail[1]
|
|
245
|
-
let outer = (await page.evaluate(OUTER_JS));
|
|
246
|
-
const plan = fitPlan(avail, outer);
|
|
247
|
-
if (plan) {
|
|
248
|
-
await cdp.send("Browser.setWindowBounds", { windowId, bounds: bounds(plan[0], plan[1]) });
|
|
249
|
-
outer = (await page.evaluate(OUTER_JS));
|
|
250
|
-
// Overshooting would trade one impossible geometry for another (outer > avail).
|
|
251
|
-
if (outer[0] > avail[0] || outer[1] > avail[1]) {
|
|
252
|
-
await cdp.send("Browser.setWindowBounds", { windowId, bounds: bounds(avail[0], avail[1]) });
|
|
253
|
-
outer = (await page.evaluate(OUTER_JS));
|
|
254
|
-
}
|
|
255
|
-
}
|
|
302
|
+
const outer = await fitWindow((w, h) => cdp.send("Browser.setWindowBounds", { windowId, bounds: bounds(w, h) }), async () => (await page.evaluate(OUTER_JS)), [avail[0], avail[1]], [avail[0], avail[1]], SETTLE_MS);
|
|
256
303
|
return [outer[0], outer[1]];
|
|
257
304
|
}
|
|
258
305
|
catch {
|
|
@@ -294,8 +341,7 @@ export function geometryIsCoherent(screen, avail, inner, outer) {
|
|
|
294
341
|
// fit runs on each context's first page. A raw endpoint has neither, and a CDP emulation override
|
|
295
342
|
// would not survive either, being scoped to the session that set it. What every
|
|
296
343
|
// target and every client of a served browser inherits is the headless DISPLAY and the real WINDOW,
|
|
297
|
-
// both browser-level. Untouched, measured on 153.0.8010.36/win-x64
|
|
298
|
-
// tools/probe-geometry.mjs):
|
|
344
|
+
// both browser-level. Untouched, measured on 153.0.8010.36/win-x64:
|
|
299
345
|
//
|
|
300
346
|
// no persona: screen 800x600 avail 800x600 outer 780x580 <- the headless surface
|
|
301
347
|
// no persona + --window-size=1440,900: outer 1440x900 on that 800x600 screen
|
|
@@ -379,13 +425,14 @@ const DISPLAY_JS = "[screen.width, screen.height, screen.availLeft, screen.avail
|
|
|
379
425
|
/**
|
|
380
426
|
* Put the served browser's first window on its display's work area (or `windowSize`, clamped into
|
|
381
427
|
* it); under a persona, first make the headless display the persona's own. Over the caller's CDP
|
|
382
|
-
* connection, on the first page target. Only Target/Emulation/Browser commands plus
|
|
383
|
-
* Runtime.evaluate (never Runtime.enable) on that page, and every change is browser-level, so
|
|
428
|
+
* connection, on the first page target. Only Target/Emulation/Browser commands plus
|
|
429
|
+
* Runtime.evaluate reads (never Runtime.enable) on that page, and every change is browser-level, so
|
|
384
430
|
* nothing lingers on the page when the connection closes.
|
|
385
431
|
*
|
|
386
432
|
* Returns null when it could not act (no page, an implausible persona display, or an engine without
|
|
387
433
|
* `Emulation.updateScreen` under a persona, whose window cannot outgrow 800x600 without it). Never
|
|
388
|
-
* throws: geometry must not be able to fail a launch.
|
|
434
|
+
* throws: geometry must not be able to fail a launch. `settleMs` bounds the wait for each resize to
|
|
435
|
+
* reach the page ({@link settledOuter}).
|
|
389
436
|
*/
|
|
390
437
|
export async function fitWindowOverCdp(cdp, opts) {
|
|
391
438
|
let sessionId;
|
|
@@ -417,19 +464,9 @@ export async function fitWindowOverCdp(cdp, opts) {
|
|
|
417
464
|
const top = at + Math.min(10, ah - h);
|
|
418
465
|
const { windowId } = (await cdp.send("Browser.getWindowForTarget", { targetId: page.targetId }));
|
|
419
466
|
const setBounds = (bw, bh) => cdp.send("Browser.setWindowBounds", { windowId, bounds: { left, top, width: Math.round(bw), height: Math.round(bh) } });
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
// given gets the shortfall added back, and an overshoot is reverted.
|
|
424
|
-
const plan = fitPlan([w, h], outer);
|
|
425
|
-
if (plan) {
|
|
426
|
-
await setBounds(plan[0], plan[1]);
|
|
427
|
-
outer = await read(OUTER_JS);
|
|
428
|
-
if (outer[0] > w || outer[1] > h) {
|
|
429
|
-
await setBounds(w, h);
|
|
430
|
-
outer = await read(OUTER_JS);
|
|
431
|
-
}
|
|
432
|
-
}
|
|
467
|
+
// Same as fitWindowToWorkArea: an engine that reports less than the bounds it was given gets the
|
|
468
|
+
// shortfall added back, an overshoot is reverted, and only settled reads count.
|
|
469
|
+
const outer = await fitWindow(setBounds, () => read(OUTER_JS), [w, h], [al + aw - left, at + ah - top], opts.settleMs ?? SETTLE_MS);
|
|
433
470
|
return { display: { width: sw, height: sh, availWidth: aw, availHeight: ah }, outer: [outer[0], outer[1]] };
|
|
434
471
|
}
|
|
435
472
|
catch {
|
|
@@ -440,69 +477,15 @@ export async function fitWindowOverCdp(cdp, opts) {
|
|
|
440
477
|
await cdp.send("Target.detachFromTarget", { sessionId }).catch(() => { });
|
|
441
478
|
}
|
|
442
479
|
}
|
|
443
|
-
/** A minimal CDP client on Node's built-in WebSocket (Node 22+), or null where there is none. */
|
|
444
|
-
async function openCdp(wsUrl, timeoutMs) {
|
|
445
|
-
const WS = globalThis.WebSocket;
|
|
446
|
-
if (!WS)
|
|
447
|
-
return null;
|
|
448
|
-
const ws = new WS(wsUrl);
|
|
449
|
-
await new Promise((resolve, reject) => {
|
|
450
|
-
const t = setTimeout(() => reject(new Error("CDP connect timed out")), timeoutMs);
|
|
451
|
-
ws.onopen = () => { clearTimeout(t); resolve(); };
|
|
452
|
-
ws.onerror = () => { clearTimeout(t); reject(new Error("CDP connect failed")); };
|
|
453
|
-
});
|
|
454
|
-
let next = 0;
|
|
455
|
-
const pending = new Map();
|
|
456
|
-
ws.onmessage = (ev) => {
|
|
457
|
-
let m;
|
|
458
|
-
try {
|
|
459
|
-
m = JSON.parse(String(ev.data));
|
|
460
|
-
}
|
|
461
|
-
catch {
|
|
462
|
-
return;
|
|
463
|
-
}
|
|
464
|
-
const p = m.id === undefined ? undefined : pending.get(m.id);
|
|
465
|
-
if (!p)
|
|
466
|
-
return;
|
|
467
|
-
pending.delete(m.id);
|
|
468
|
-
if (m.error)
|
|
469
|
-
p.reject(new Error(m.error.message));
|
|
470
|
-
else
|
|
471
|
-
p.resolve(m.result ?? {});
|
|
472
|
-
};
|
|
473
|
-
ws.onclose = () => {
|
|
474
|
-
for (const p of pending.values())
|
|
475
|
-
p.reject(new Error("CDP connection closed"));
|
|
476
|
-
pending.clear();
|
|
477
|
-
};
|
|
478
|
-
return {
|
|
479
|
-
send(method, params = {}, sessionId) {
|
|
480
|
-
return new Promise((resolve, reject) => {
|
|
481
|
-
const id = ++next;
|
|
482
|
-
const t = setTimeout(() => { pending.delete(id); reject(new Error(`${method} timed out`)); }, timeoutMs);
|
|
483
|
-
pending.set(id, {
|
|
484
|
-
resolve: (v) => { clearTimeout(t); resolve(v); },
|
|
485
|
-
reject: (e) => { clearTimeout(t); reject(e); },
|
|
486
|
-
});
|
|
487
|
-
ws.send(JSON.stringify({ id, method, params, ...(sessionId ? { sessionId } : {}) }));
|
|
488
|
-
});
|
|
489
|
-
},
|
|
490
|
-
close() {
|
|
491
|
-
try {
|
|
492
|
-
ws.close();
|
|
493
|
-
}
|
|
494
|
-
catch { /* ignore */ }
|
|
495
|
-
},
|
|
496
|
-
};
|
|
497
|
-
}
|
|
498
480
|
/** {@link fitWindowOverCdp} on the SDK's own short connection to a served browser. Never throws. */
|
|
499
481
|
export async function fitServedWindow(wsUrl, opts) {
|
|
500
482
|
if (!wsUrl)
|
|
501
483
|
return null;
|
|
502
484
|
let cdp = null;
|
|
503
485
|
try {
|
|
504
|
-
|
|
505
|
-
|
|
486
|
+
// The SDK's own WebSocket client, not Node's global one: that only exists from Node 22 on.
|
|
487
|
+
cdp = await connectCdp(wsUrl, opts.timeoutMs ?? 5000);
|
|
488
|
+
return await fitWindowOverCdp(cdp, opts);
|
|
506
489
|
}
|
|
507
490
|
catch {
|
|
508
491
|
return null;
|