fix(humanize): fall back to window dims when viewport is null

Headed launches default to no_viewport, so page.viewport_size is None and
human scroll raised "Viewport size not available". Fall back to live
window.innerWidth/innerHeight. Covers Playwright (py sync/async, JS) and
Puppeteer paths.
This commit is contained in:
CloakHQ
2026-06-23 01:34:07 +02:00
parent 29679a73bf
commit 9c3ed2dcba
4 changed files with 34 additions and 4 deletions
+7
View File
@@ -64,6 +64,13 @@ def human_scroll_into_view(
""" """
viewport = page.viewport_size viewport = page.viewport_size
if not viewport: if not viewport:
# Headed launches default to no_viewport so the page tracks the real OS
# window; page.viewport_size is then None. Fall back to the live window
# dimensions so humanize works headed (the stealth-relevant mode).
viewport = page.evaluate(
"() => ({ width: window.innerWidth, height: window.innerHeight })"
)
if not viewport or not viewport.get("height"):
raise RuntimeError("Viewport size not available") raise RuntimeError("Viewport size not available")
viewport_height = viewport["height"] viewport_height = viewport["height"]
+7
View File
@@ -60,6 +60,13 @@ async def async_human_scroll_into_view(
""" """
viewport = page.viewport_size viewport = page.viewport_size
if not viewport: if not viewport:
# Headed launches default to no_viewport so the page tracks the real OS
# window; page.viewport_size is then None. Fall back to the live window
# dimensions so humanize works headed (the stealth-relevant mode).
viewport = await page.evaluate(
"() => ({ width: window.innerWidth, height: window.innerHeight })"
)
if not viewport or not viewport.get("height"):
raise RuntimeError("Viewport size not available") raise RuntimeError("Viewport size not available")
viewport_height = viewport["height"] viewport_height = viewport["height"]
+10 -2
View File
@@ -93,8 +93,16 @@ export async function humanScrollIntoView(
cursorY: number, cursorY: number,
cfg: HumanConfig, cfg: HumanConfig,
): Promise<{ box: ElementBounds; cursorX: number; cursorY: number }> { ): Promise<{ box: ElementBounds; cursorX: number; cursorY: number }> {
const viewport = page.viewport(); // Headed launches default to null defaultViewport so the page tracks the real
if (!viewport) throw new Error('Viewport size not available'); // OS window; page.viewport() is then null. Fall back to the live window
// dimensions so humanize works headed (the stealth-relevant mode).
let viewport = page.viewport();
if (!viewport) {
viewport = await page.evaluate(
() => ({ width: window.innerWidth, height: window.innerHeight }),
);
}
if (!viewport || !viewport.height) throw new Error('Viewport size not available');
let box = await getBox(); let box = await getBox();
if (!box) throw new Error('Element not found while scrolling into view'); if (!box) throw new Error('Element not found while scrolling into view');
+10 -2
View File
@@ -53,8 +53,16 @@ export async function humanScrollIntoView(
cursorY: number, cursorY: number,
cfg: HumanConfig, cfg: HumanConfig,
): Promise<{ box: ElementBounds; cursorX: number; cursorY: number; didScroll: boolean }> { ): Promise<{ box: ElementBounds; cursorX: number; cursorY: number; didScroll: boolean }> {
const viewport = page.viewportSize(); // Headed launches default to no_viewport so the page tracks the real OS
if (!viewport) throw new Error('Viewport size not available'); // window; page.viewportSize() is then null. Fall back to the live window
// dimensions so humanize works headed (the stealth-relevant mode).
let viewport = page.viewportSize();
if (!viewport) {
viewport = await page.evaluate(
() => ({ width: window.innerWidth, height: window.innerHeight }),
);
}
if (!viewport || !viewport.height) throw new Error('Viewport size not available');
let box = await getBox(); let box = await getBox();
if (!box) throw new Error('Element not found while scrolling into view'); if (!box) throw new Error('Element not found while scrolling into view');