@web-auto/camo 0.1.26 → 0.2.1
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/LICENSE +21 -21
- package/README.md +586 -586
- package/bin/browser-service.mjs +11 -11
- package/bin/camo.mjs +22 -22
- package/package.json +48 -48
- package/scripts/build.mjs +19 -19
- package/scripts/bump-version.mjs +34 -34
- package/scripts/check-file-size.mjs +80 -80
- package/scripts/file-size-policy.json +12 -2
- package/scripts/install.mjs +76 -76
- package/scripts/release.sh +54 -54
- package/src/autoscript/action-providers/index.mjs +6 -6
- package/src/autoscript/impact-engine.mjs +78 -78
- package/src/autoscript/runtime.mjs +1017 -1017
- package/src/autoscript/schema.mjs +376 -376
- package/src/cli.mjs +405 -405
- package/src/commands/attach.mjs +141 -141
- package/src/commands/autoscript.mjs +1011 -1011
- package/src/commands/browser.mjs +1255 -1257
- package/src/commands/container.mjs +401 -401
- package/src/commands/cookies.mjs +69 -69
- package/src/commands/create.mjs +98 -98
- package/src/commands/devtools.mjs +349 -349
- package/src/commands/events.mjs +152 -152
- package/src/commands/highlight-mode.mjs +24 -24
- package/src/commands/init.mjs +68 -68
- package/src/commands/lifecycle.mjs +275 -275
- package/src/commands/mouse.mjs +45 -45
- package/src/commands/profile.mjs +46 -46
- package/src/commands/record.mjs +115 -115
- package/src/commands/system.mjs +14 -14
- package/src/commands/window.mjs +123 -123
- package/src/container/change-notifier.mjs +362 -362
- package/src/container/element-filter.mjs +143 -143
- package/src/container/index.mjs +3 -3
- package/src/container/runtime-core/checkpoint.mjs +209 -209
- package/src/container/runtime-core/index.mjs +21 -21
- package/src/container/runtime-core/operations/index.mjs +774 -774
- package/src/container/runtime-core/operations/selector-scripts.mjs +277 -277
- package/src/container/runtime-core/operations/tab-pool.mjs +746 -746
- package/src/container/runtime-core/operations/viewport.mjs +189 -189
- package/src/container/runtime-core/search.mjs +190 -190
- package/src/container/runtime-core/subscription.mjs +224 -224
- package/src/container/runtime-core/utils.mjs +94 -94
- package/src/container/runtime-core/validation.mjs +127 -184
- package/src/container/runtime-core.mjs +1 -1
- package/src/container/subscription-registry.mjs +459 -459
- package/src/core/actions.mjs +561 -561
- package/src/core/browser.mjs +266 -266
- package/src/core/index.mjs +52 -52
- package/src/core/utils.mjs +91 -91
- package/src/events/daemon-entry.mjs +33 -33
- package/src/events/daemon.mjs +80 -80
- package/src/events/progress-log.mjs +109 -109
- package/src/events/ws-server.mjs +239 -239
- package/src/lib/client.mjs +200 -200
- package/src/lifecycle/cleanup.mjs +83 -83
- package/src/lifecycle/lock.mjs +126 -126
- package/src/lifecycle/session-registry.mjs +279 -279
- package/src/lifecycle/session-view.mjs +76 -76
- package/src/lifecycle/session-watchdog.mjs +281 -281
- package/src/services/browser-service/index.js +671 -674
- package/src/services/browser-service/internal/BrowserSession.input.test.js +389 -389
- package/src/services/browser-service/internal/BrowserSession.js +325 -336
- package/src/services/browser-service/internal/ElementRegistry.js +60 -60
- package/src/services/browser-service/internal/ProfileLock.js +84 -84
- package/src/services/browser-service/internal/SessionManager.js +184 -184
- package/src/services/browser-service/internal/SessionManager.test.js +39 -39
- package/src/services/browser-service/internal/browser-session/cookies.js +144 -144
- package/src/services/browser-service/internal/browser-session/input-ops.js +222 -219
- package/src/services/browser-service/internal/browser-session/input-pipeline.js +144 -144
- package/src/services/browser-service/internal/browser-session/logging.js +46 -46
- package/src/services/browser-service/internal/browser-session/navigation.js +38 -38
- package/src/services/browser-service/internal/browser-session/page-hooks.js +442 -442
- package/src/services/browser-service/internal/browser-session/page-management.js +302 -336
- package/src/services/browser-service/internal/browser-session/page-management.test.js +148 -148
- package/src/services/browser-service/internal/browser-session/recording.js +198 -198
- package/src/services/browser-service/internal/browser-session/runtime-events.js +61 -61
- package/src/services/browser-service/internal/browser-session/session-core.js +84 -84
- package/src/services/browser-service/internal/browser-session/session-state.js +38 -38
- package/src/services/browser-service/internal/browser-session/types.js +14 -14
- package/src/services/browser-service/internal/browser-session/utils.js +95 -95
- package/src/services/browser-service/internal/browser-session/viewport-manager.js +46 -46
- package/src/services/browser-service/internal/browser-session/viewport.js +215 -215
- package/src/services/browser-service/internal/container-matcher.js +851 -851
- package/src/services/browser-service/internal/container-registry.js +182 -182
- package/src/services/browser-service/internal/engine-manager.js +259 -259
- package/src/services/browser-service/internal/fingerprint.js +203 -203
- package/src/services/browser-service/internal/heartbeat.js +137 -137
- package/src/services/browser-service/internal/logging.js +46 -46
- package/src/services/browser-service/internal/page-runtime/runtime.js +1317 -1317
- package/src/services/browser-service/internal/pageRuntime.js +28 -28
- package/src/services/browser-service/internal/runtimeInjector.js +31 -31
- package/src/services/browser-service/internal/service-process-logger.js +140 -140
- package/src/services/browser-service/internal/state-bus.js +45 -45
- package/src/services/browser-service/internal/storage-paths.js +42 -42
- package/src/services/browser-service/internal/ws-server.js +1194 -1194
- package/src/services/browser-service/internal/ws-server.test.js +58 -58
- package/src/services/browser-service/server.mjs +6 -6
- package/src/services/controller/cli-bridge.js +93 -93
- package/src/services/controller/container-index.js +50 -50
- package/src/services/controller/container-storage.js +36 -36
- package/src/services/controller/controller-actions.js +207 -207
- package/src/services/controller/controller.js +1138 -1138
- package/src/services/controller/selectors.js +54 -54
- package/src/services/controller/transport.js +125 -125
- package/src/utils/args.mjs +26 -26
- package/src/utils/browser-service.mjs +544 -544
- package/src/utils/command-log.mjs +64 -64
- package/src/utils/config.mjs +214 -214
- package/src/utils/fingerprint.mjs +181 -181
- package/src/utils/help.mjs +216 -216
- package/src/utils/js-policy.mjs +13 -13
- package/src/utils/ws-client.mjs +30 -30
- package/src/container/runtime-core/operations/tab-pool.mjs.bak +0 -762
- package/src/container/runtime-core/operations/tab-pool.mjs.syntax-error +0 -762
- package/src/services/browser-service/index.js.bak +0 -671
|
@@ -1,216 +1,216 @@
|
|
|
1
|
-
import os from 'os';
|
|
2
|
-
export function resolveDeviceScaleFactor(profileId) {
|
|
3
|
-
const raw = String(process.env.CAMO_DEVICE_SCALE || '').trim();
|
|
4
|
-
if (raw) {
|
|
5
|
-
const parsed = Number(raw);
|
|
6
|
-
if (Number.isFinite(parsed) && parsed > 0)
|
|
7
|
-
return parsed;
|
|
8
|
-
}
|
|
9
|
-
if (os.platform() === 'win32' && profileId?.startsWith('xiaohongshu_')) {
|
|
10
|
-
return 1;
|
|
11
|
-
}
|
|
12
|
-
return null;
|
|
13
|
-
}
|
|
14
|
-
export async function syncDeviceScaleFactor(page, context, viewport, engine) {
|
|
15
|
-
if (engine !== 'chromium')
|
|
16
|
-
return;
|
|
17
|
-
const desired = resolveDeviceScaleFactor();
|
|
18
|
-
if (!desired || !context)
|
|
19
|
-
return;
|
|
20
|
-
try {
|
|
21
|
-
const client = await context.newCDPSession(page);
|
|
22
|
-
await client.send('Emulation.setDeviceMetricsOverride', {
|
|
23
|
-
width: viewport.width,
|
|
24
|
-
height: viewport.height,
|
|
25
|
-
deviceScaleFactor: desired,
|
|
26
|
-
mobile: false,
|
|
27
|
-
scale: 1,
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
catch (error) {
|
|
31
|
-
console.warn(`[browser-session] sync device scale failed: ${error?.message || String(error)}`);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
export async function readWindowInnerSize(page) {
|
|
35
|
-
try {
|
|
36
|
-
const metrics = await page.evaluate(() => ({
|
|
37
|
-
width: Math.floor(Number(window.innerWidth || 0)),
|
|
38
|
-
height: Math.floor(Number(window.innerHeight || 0)),
|
|
39
|
-
}));
|
|
40
|
-
const width = Number(metrics?.width);
|
|
41
|
-
const height = Number(metrics?.height);
|
|
42
|
-
if (!Number.isFinite(width) || !Number.isFinite(height))
|
|
43
|
-
return null;
|
|
44
|
-
if (width < 300 || height < 200)
|
|
45
|
-
return null;
|
|
46
|
-
return { width, height };
|
|
47
|
-
}
|
|
48
|
-
catch {
|
|
49
|
-
return null;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
export async function refreshViewportFromWindow(page) {
|
|
53
|
-
const inner = await readWindowInnerSize(page);
|
|
54
|
-
if (!inner)
|
|
55
|
-
return null;
|
|
56
|
-
return {
|
|
57
|
-
width: Math.max(800, Math.floor(inner.width)),
|
|
58
|
-
height: Math.max(700, Math.floor(inner.height)),
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
export async function syncWindowBounds(page, context, viewport, engine, headless) {
|
|
62
|
-
// Log viewport metrics for diagnosis
|
|
63
|
-
try {
|
|
64
|
-
const metrics = await page.evaluate(() => ({
|
|
65
|
-
innerWidth: window.innerWidth || 0,
|
|
66
|
-
innerHeight: window.innerHeight || 0,
|
|
67
|
-
outerWidth: window.outerWidth || 0,
|
|
68
|
-
outerHeight: window.outerHeight || 0,
|
|
69
|
-
screenX: Math.floor(window.screenX || 0),
|
|
70
|
-
screenY: Math.floor(window.screenY || 0),
|
|
71
|
-
devicePixelRatio: window.devicePixelRatio || 1,
|
|
72
|
-
visualViewport: window.visualViewport ? {
|
|
73
|
-
width: window.visualViewport.width || 0,
|
|
74
|
-
height: window.visualViewport.height || 0,
|
|
75
|
-
offsetLeft: window.visualViewport.offsetLeft || 0,
|
|
76
|
-
offsetTop: window.visualViewport.offsetTop || 0,
|
|
77
|
-
scale: window.visualViewport.scale || 1,
|
|
78
|
-
} : null,
|
|
79
|
-
}));
|
|
80
|
-
console.log(`[viewport-metrics] target=${viewport.width}x${viewport.height} inner=${metrics.innerWidth}x${metrics.innerHeight} outer=${metrics.outerWidth}x${metrics.outerHeight} screen=(${metrics.screenX},${metrics.screenY}) dpr=${metrics.devicePixelRatio} visual=${JSON.stringify(metrics.visualViewport)}`);
|
|
81
|
-
// If inner dimensions don't match target, retry setViewportSize
|
|
82
|
-
const widthDelta = Math.abs(metrics.innerWidth - viewport.width);
|
|
83
|
-
const heightDelta = Math.abs(metrics.innerHeight - viewport.height);
|
|
84
|
-
if (widthDelta > 50 || heightDelta > 50) {
|
|
85
|
-
console.warn(`[viewport-metrics] MISMATCH detected: widthDelta=${widthDelta} heightDelta=${heightDelta}, retrying setViewportSize...`);
|
|
86
|
-
await page.setViewportSize({ width: viewport.width, height: viewport.height });
|
|
87
|
-
await page.waitForTimeout(500);
|
|
88
|
-
const retry = await page.evaluate(() => ({
|
|
89
|
-
innerWidth: window.innerWidth || 0,
|
|
90
|
-
innerHeight: window.innerHeight || 0,
|
|
91
|
-
}));
|
|
92
|
-
console.log(`[viewport-metrics] after retry: inner=${retry.innerWidth}x${retry.innerHeight}`);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
catch (err) {
|
|
96
|
-
console.warn(`[viewport-metrics] log failed: ${err?.message || String(err)}`);
|
|
97
|
-
}
|
|
98
|
-
if (engine !== 'chromium')
|
|
99
|
-
return;
|
|
100
|
-
if (headless)
|
|
101
|
-
return;
|
|
102
|
-
if (!context)
|
|
103
|
-
return;
|
|
104
|
-
try {
|
|
105
|
-
const client = await context.newCDPSession(page);
|
|
106
|
-
const { windowId } = await client.send('Browser.getWindowForTarget');
|
|
107
|
-
const metrics = await page.evaluate(() => ({
|
|
108
|
-
innerWidth: window.innerWidth || 0,
|
|
109
|
-
innerHeight: window.innerHeight || 0,
|
|
110
|
-
outerWidth: window.outerWidth || 0,
|
|
111
|
-
outerHeight: window.outerHeight || 0,
|
|
112
|
-
}));
|
|
113
|
-
const deltaW = Math.max(0, Math.floor((metrics.outerWidth || 0) - (metrics.innerWidth || 0)));
|
|
114
|
-
const deltaH = Math.max(0, Math.floor((metrics.outerHeight || 0) - (metrics.innerHeight || 0)));
|
|
115
|
-
const targetWidth = Math.max(300, Math.floor(Number(viewport.width) + deltaW));
|
|
116
|
-
const targetHeight = Math.max(300, Math.floor(Number(viewport.height) + deltaH));
|
|
117
|
-
await client.send('Browser.setWindowBounds', {
|
|
118
|
-
windowId,
|
|
119
|
-
bounds: { width: targetWidth, height: targetHeight },
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
catch (error) {
|
|
123
|
-
console.warn(`[browser-session] sync window bounds failed: ${error?.message || String(error)}`);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
export async function maybeCenterWindow(page, viewport, headless) {
|
|
127
|
-
if (headless)
|
|
128
|
-
return;
|
|
129
|
-
try {
|
|
130
|
-
const metrics = await page.evaluate(() => ({
|
|
131
|
-
screenX: Math.floor(window.screenX || 0),
|
|
132
|
-
screenY: Math.floor(window.screenY || 0),
|
|
133
|
-
outerWidth: Math.floor(window.outerWidth || 0),
|
|
134
|
-
outerHeight: Math.floor(window.outerHeight || 0),
|
|
135
|
-
innerWidth: Math.floor(window.innerWidth || 0),
|
|
136
|
-
innerHeight: Math.floor(window.innerHeight || 0),
|
|
137
|
-
screenWidth: Math.floor(window.screen?.availWidth || window.screen?.width || 0),
|
|
138
|
-
screenHeight: Math.floor(window.screen?.availHeight || window.screen?.height || 0),
|
|
139
|
-
}));
|
|
140
|
-
const sw = Math.max(metrics.screenWidth || 0, viewport.width);
|
|
141
|
-
const sh = Math.max(metrics.screenHeight || 0, viewport.height);
|
|
142
|
-
// Try to resize outer window to fit viewport (inner) + chrome delta
|
|
143
|
-
const deltaW = Math.max(0, (metrics.outerWidth || 0) - (metrics.innerWidth || 0));
|
|
144
|
-
const deltaH = Math.max(0, (metrics.outerHeight || 0) - (metrics.innerHeight || 0));
|
|
145
|
-
const targetOuterW = Math.max(viewport.width + deltaW, 300);
|
|
146
|
-
const targetOuterH = Math.max(viewport.height + deltaH, 300);
|
|
147
|
-
await page.evaluate(({ w, h }) => { try {
|
|
148
|
-
window.resizeTo(w, h);
|
|
149
|
-
}
|
|
150
|
-
catch { } }, { w: targetOuterW, h: targetOuterH });
|
|
151
|
-
await page.waitForTimeout(200);
|
|
152
|
-
const ow = Math.max(metrics.outerWidth || 0, targetOuterW);
|
|
153
|
-
const oh = Math.max(metrics.outerHeight || 0, targetOuterH);
|
|
154
|
-
const targetX = Math.max(0, Math.floor((sw - ow) / 2));
|
|
155
|
-
const targetY = Math.max(0, Math.floor((sh - oh) / 2));
|
|
156
|
-
// Only move if we're clearly off-center
|
|
157
|
-
if (Math.abs(metrics.screenX - targetX) > 5 || Math.abs(metrics.screenY - targetY) > 5) {
|
|
158
|
-
await page.evaluate(({ x, y }) => { try {
|
|
159
|
-
window.moveTo(x, y);
|
|
160
|
-
}
|
|
161
|
-
catch { } }, { x: targetX, y: targetY });
|
|
162
|
-
await page.waitForTimeout(200);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
catch (err) {
|
|
166
|
-
console.warn('[browser-session] maybeCenterWindow failed:', err?.message || String(err));
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
export async function ensurePageViewport(page, state, context, engine, headless) {
|
|
170
|
-
if (state.followWindowViewport) {
|
|
171
|
-
const refreshed = await refreshViewportFromWindow(page).catch(() => null);
|
|
172
|
-
if (refreshed) {
|
|
173
|
-
return { ...state, lastViewport: refreshed };
|
|
174
|
-
}
|
|
175
|
-
return state;
|
|
176
|
-
}
|
|
177
|
-
if (!state.lastViewport)
|
|
178
|
-
return state;
|
|
179
|
-
const current = page.viewportSize();
|
|
180
|
-
if (current && current.width === state.lastViewport.width && current.height === state.lastViewport.height) {
|
|
181
|
-
return state;
|
|
182
|
-
}
|
|
183
|
-
await page.setViewportSize({
|
|
184
|
-
width: state.lastViewport.width,
|
|
185
|
-
height: state.lastViewport.height,
|
|
186
|
-
});
|
|
187
|
-
await syncWindowBounds(page, context, { ...state.lastViewport }, engine, headless);
|
|
188
|
-
await syncDeviceScaleFactor(page, context, { ...state.lastViewport }, engine);
|
|
189
|
-
return state;
|
|
190
|
-
}
|
|
191
|
-
export async function setViewportSizeOnPage(page, opts, state, context, engine, headless) {
|
|
192
|
-
const width = Math.max(800, Math.floor(Number(opts.width) || 0));
|
|
193
|
-
const height = Math.max(700, Math.floor(Number(opts.height) || 0));
|
|
194
|
-
if (!width || !height) {
|
|
195
|
-
throw new Error('invalid_viewport_size');
|
|
196
|
-
}
|
|
197
|
-
if (state.followWindowViewport && !headless) {
|
|
198
|
-
await page.evaluate(({ w, h }) => {
|
|
199
|
-
try {
|
|
200
|
-
window.resizeTo(w, h);
|
|
201
|
-
}
|
|
202
|
-
catch { }
|
|
203
|
-
}, { w: width, h: height });
|
|
204
|
-
await page.waitForTimeout(150);
|
|
205
|
-
const refreshed = await refreshViewportFromWindow(page).catch(() => null);
|
|
206
|
-
const next = refreshed || { width, height };
|
|
207
|
-
await maybeCenterWindow(page, next, headless).catch(() => { });
|
|
208
|
-
return next;
|
|
209
|
-
}
|
|
210
|
-
await page.setViewportSize({ width, height });
|
|
211
|
-
await syncWindowBounds(page, context, { width, height }, engine, headless);
|
|
212
|
-
await syncDeviceScaleFactor(page, context, { width, height }, engine);
|
|
213
|
-
await maybeCenterWindow(page, { width, height }, headless);
|
|
214
|
-
return { width, height };
|
|
215
|
-
}
|
|
1
|
+
import os from 'os';
|
|
2
|
+
export function resolveDeviceScaleFactor(profileId) {
|
|
3
|
+
const raw = String(process.env.CAMO_DEVICE_SCALE || '').trim();
|
|
4
|
+
if (raw) {
|
|
5
|
+
const parsed = Number(raw);
|
|
6
|
+
if (Number.isFinite(parsed) && parsed > 0)
|
|
7
|
+
return parsed;
|
|
8
|
+
}
|
|
9
|
+
if (os.platform() === 'win32' && profileId?.startsWith('xiaohongshu_')) {
|
|
10
|
+
return 1;
|
|
11
|
+
}
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
export async function syncDeviceScaleFactor(page, context, viewport, engine) {
|
|
15
|
+
if (engine !== 'chromium')
|
|
16
|
+
return;
|
|
17
|
+
const desired = resolveDeviceScaleFactor();
|
|
18
|
+
if (!desired || !context)
|
|
19
|
+
return;
|
|
20
|
+
try {
|
|
21
|
+
const client = await context.newCDPSession(page);
|
|
22
|
+
await client.send('Emulation.setDeviceMetricsOverride', {
|
|
23
|
+
width: viewport.width,
|
|
24
|
+
height: viewport.height,
|
|
25
|
+
deviceScaleFactor: desired,
|
|
26
|
+
mobile: false,
|
|
27
|
+
scale: 1,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
console.warn(`[browser-session] sync device scale failed: ${error?.message || String(error)}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export async function readWindowInnerSize(page) {
|
|
35
|
+
try {
|
|
36
|
+
const metrics = await page.evaluate(() => ({
|
|
37
|
+
width: Math.floor(Number(window.innerWidth || 0)),
|
|
38
|
+
height: Math.floor(Number(window.innerHeight || 0)),
|
|
39
|
+
}));
|
|
40
|
+
const width = Number(metrics?.width);
|
|
41
|
+
const height = Number(metrics?.height);
|
|
42
|
+
if (!Number.isFinite(width) || !Number.isFinite(height))
|
|
43
|
+
return null;
|
|
44
|
+
if (width < 300 || height < 200)
|
|
45
|
+
return null;
|
|
46
|
+
return { width, height };
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export async function refreshViewportFromWindow(page) {
|
|
53
|
+
const inner = await readWindowInnerSize(page);
|
|
54
|
+
if (!inner)
|
|
55
|
+
return null;
|
|
56
|
+
return {
|
|
57
|
+
width: Math.max(800, Math.floor(inner.width)),
|
|
58
|
+
height: Math.max(700, Math.floor(inner.height)),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export async function syncWindowBounds(page, context, viewport, engine, headless) {
|
|
62
|
+
// Log viewport metrics for diagnosis
|
|
63
|
+
try {
|
|
64
|
+
const metrics = await page.evaluate(() => ({
|
|
65
|
+
innerWidth: window.innerWidth || 0,
|
|
66
|
+
innerHeight: window.innerHeight || 0,
|
|
67
|
+
outerWidth: window.outerWidth || 0,
|
|
68
|
+
outerHeight: window.outerHeight || 0,
|
|
69
|
+
screenX: Math.floor(window.screenX || 0),
|
|
70
|
+
screenY: Math.floor(window.screenY || 0),
|
|
71
|
+
devicePixelRatio: window.devicePixelRatio || 1,
|
|
72
|
+
visualViewport: window.visualViewport ? {
|
|
73
|
+
width: window.visualViewport.width || 0,
|
|
74
|
+
height: window.visualViewport.height || 0,
|
|
75
|
+
offsetLeft: window.visualViewport.offsetLeft || 0,
|
|
76
|
+
offsetTop: window.visualViewport.offsetTop || 0,
|
|
77
|
+
scale: window.visualViewport.scale || 1,
|
|
78
|
+
} : null,
|
|
79
|
+
}));
|
|
80
|
+
console.log(`[viewport-metrics] target=${viewport.width}x${viewport.height} inner=${metrics.innerWidth}x${metrics.innerHeight} outer=${metrics.outerWidth}x${metrics.outerHeight} screen=(${metrics.screenX},${metrics.screenY}) dpr=${metrics.devicePixelRatio} visual=${JSON.stringify(metrics.visualViewport)}`);
|
|
81
|
+
// If inner dimensions don't match target, retry setViewportSize
|
|
82
|
+
const widthDelta = Math.abs(metrics.innerWidth - viewport.width);
|
|
83
|
+
const heightDelta = Math.abs(metrics.innerHeight - viewport.height);
|
|
84
|
+
if (widthDelta > 50 || heightDelta > 50) {
|
|
85
|
+
console.warn(`[viewport-metrics] MISMATCH detected: widthDelta=${widthDelta} heightDelta=${heightDelta}, retrying setViewportSize...`);
|
|
86
|
+
await page.setViewportSize({ width: viewport.width, height: viewport.height });
|
|
87
|
+
await page.waitForTimeout(500);
|
|
88
|
+
const retry = await page.evaluate(() => ({
|
|
89
|
+
innerWidth: window.innerWidth || 0,
|
|
90
|
+
innerHeight: window.innerHeight || 0,
|
|
91
|
+
}));
|
|
92
|
+
console.log(`[viewport-metrics] after retry: inner=${retry.innerWidth}x${retry.innerHeight}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
console.warn(`[viewport-metrics] log failed: ${err?.message || String(err)}`);
|
|
97
|
+
}
|
|
98
|
+
if (engine !== 'chromium')
|
|
99
|
+
return;
|
|
100
|
+
if (headless)
|
|
101
|
+
return;
|
|
102
|
+
if (!context)
|
|
103
|
+
return;
|
|
104
|
+
try {
|
|
105
|
+
const client = await context.newCDPSession(page);
|
|
106
|
+
const { windowId } = await client.send('Browser.getWindowForTarget');
|
|
107
|
+
const metrics = await page.evaluate(() => ({
|
|
108
|
+
innerWidth: window.innerWidth || 0,
|
|
109
|
+
innerHeight: window.innerHeight || 0,
|
|
110
|
+
outerWidth: window.outerWidth || 0,
|
|
111
|
+
outerHeight: window.outerHeight || 0,
|
|
112
|
+
}));
|
|
113
|
+
const deltaW = Math.max(0, Math.floor((metrics.outerWidth || 0) - (metrics.innerWidth || 0)));
|
|
114
|
+
const deltaH = Math.max(0, Math.floor((metrics.outerHeight || 0) - (metrics.innerHeight || 0)));
|
|
115
|
+
const targetWidth = Math.max(300, Math.floor(Number(viewport.width) + deltaW));
|
|
116
|
+
const targetHeight = Math.max(300, Math.floor(Number(viewport.height) + deltaH));
|
|
117
|
+
await client.send('Browser.setWindowBounds', {
|
|
118
|
+
windowId,
|
|
119
|
+
bounds: { width: targetWidth, height: targetHeight },
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
console.warn(`[browser-session] sync window bounds failed: ${error?.message || String(error)}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
export async function maybeCenterWindow(page, viewport, headless) {
|
|
127
|
+
if (headless)
|
|
128
|
+
return;
|
|
129
|
+
try {
|
|
130
|
+
const metrics = await page.evaluate(() => ({
|
|
131
|
+
screenX: Math.floor(window.screenX || 0),
|
|
132
|
+
screenY: Math.floor(window.screenY || 0),
|
|
133
|
+
outerWidth: Math.floor(window.outerWidth || 0),
|
|
134
|
+
outerHeight: Math.floor(window.outerHeight || 0),
|
|
135
|
+
innerWidth: Math.floor(window.innerWidth || 0),
|
|
136
|
+
innerHeight: Math.floor(window.innerHeight || 0),
|
|
137
|
+
screenWidth: Math.floor(window.screen?.availWidth || window.screen?.width || 0),
|
|
138
|
+
screenHeight: Math.floor(window.screen?.availHeight || window.screen?.height || 0),
|
|
139
|
+
}));
|
|
140
|
+
const sw = Math.max(metrics.screenWidth || 0, viewport.width);
|
|
141
|
+
const sh = Math.max(metrics.screenHeight || 0, viewport.height);
|
|
142
|
+
// Try to resize outer window to fit viewport (inner) + chrome delta
|
|
143
|
+
const deltaW = Math.max(0, (metrics.outerWidth || 0) - (metrics.innerWidth || 0));
|
|
144
|
+
const deltaH = Math.max(0, (metrics.outerHeight || 0) - (metrics.innerHeight || 0));
|
|
145
|
+
const targetOuterW = Math.max(viewport.width + deltaW, 300);
|
|
146
|
+
const targetOuterH = Math.max(viewport.height + deltaH, 300);
|
|
147
|
+
await page.evaluate(({ w, h }) => { try {
|
|
148
|
+
window.resizeTo(w, h);
|
|
149
|
+
}
|
|
150
|
+
catch { } }, { w: targetOuterW, h: targetOuterH });
|
|
151
|
+
await page.waitForTimeout(200);
|
|
152
|
+
const ow = Math.max(metrics.outerWidth || 0, targetOuterW);
|
|
153
|
+
const oh = Math.max(metrics.outerHeight || 0, targetOuterH);
|
|
154
|
+
const targetX = Math.max(0, Math.floor((sw - ow) / 2));
|
|
155
|
+
const targetY = Math.max(0, Math.floor((sh - oh) / 2));
|
|
156
|
+
// Only move if we're clearly off-center
|
|
157
|
+
if (Math.abs(metrics.screenX - targetX) > 5 || Math.abs(metrics.screenY - targetY) > 5) {
|
|
158
|
+
await page.evaluate(({ x, y }) => { try {
|
|
159
|
+
window.moveTo(x, y);
|
|
160
|
+
}
|
|
161
|
+
catch { } }, { x: targetX, y: targetY });
|
|
162
|
+
await page.waitForTimeout(200);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
catch (err) {
|
|
166
|
+
console.warn('[browser-session] maybeCenterWindow failed:', err?.message || String(err));
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
export async function ensurePageViewport(page, state, context, engine, headless) {
|
|
170
|
+
if (state.followWindowViewport) {
|
|
171
|
+
const refreshed = await refreshViewportFromWindow(page).catch(() => null);
|
|
172
|
+
if (refreshed) {
|
|
173
|
+
return { ...state, lastViewport: refreshed };
|
|
174
|
+
}
|
|
175
|
+
return state;
|
|
176
|
+
}
|
|
177
|
+
if (!state.lastViewport)
|
|
178
|
+
return state;
|
|
179
|
+
const current = page.viewportSize();
|
|
180
|
+
if (current && current.width === state.lastViewport.width && current.height === state.lastViewport.height) {
|
|
181
|
+
return state;
|
|
182
|
+
}
|
|
183
|
+
await page.setViewportSize({
|
|
184
|
+
width: state.lastViewport.width,
|
|
185
|
+
height: state.lastViewport.height,
|
|
186
|
+
});
|
|
187
|
+
await syncWindowBounds(page, context, { ...state.lastViewport }, engine, headless);
|
|
188
|
+
await syncDeviceScaleFactor(page, context, { ...state.lastViewport }, engine);
|
|
189
|
+
return state;
|
|
190
|
+
}
|
|
191
|
+
export async function setViewportSizeOnPage(page, opts, state, context, engine, headless) {
|
|
192
|
+
const width = Math.max(800, Math.floor(Number(opts.width) || 0));
|
|
193
|
+
const height = Math.max(700, Math.floor(Number(opts.height) || 0));
|
|
194
|
+
if (!width || !height) {
|
|
195
|
+
throw new Error('invalid_viewport_size');
|
|
196
|
+
}
|
|
197
|
+
if (state.followWindowViewport && !headless) {
|
|
198
|
+
await page.evaluate(({ w, h }) => {
|
|
199
|
+
try {
|
|
200
|
+
window.resizeTo(w, h);
|
|
201
|
+
}
|
|
202
|
+
catch { }
|
|
203
|
+
}, { w: width, h: height });
|
|
204
|
+
await page.waitForTimeout(150);
|
|
205
|
+
const refreshed = await refreshViewportFromWindow(page).catch(() => null);
|
|
206
|
+
const next = refreshed || { width, height };
|
|
207
|
+
await maybeCenterWindow(page, next, headless).catch(() => { });
|
|
208
|
+
return next;
|
|
209
|
+
}
|
|
210
|
+
await page.setViewportSize({ width, height });
|
|
211
|
+
await syncWindowBounds(page, context, { width, height }, engine, headless);
|
|
212
|
+
await syncDeviceScaleFactor(page, context, { width, height }, engine);
|
|
213
|
+
await maybeCenterWindow(page, { width, height }, headless);
|
|
214
|
+
return { width, height };
|
|
215
|
+
}
|
|
216
216
|
//# sourceMappingURL=viewport.js.map
|