diff --git a/cloakbrowser/human/scroll.py b/cloakbrowser/human/scroll.py index 4bc7cca..1087ea3 100644 --- a/cloakbrowser/human/scroll.py +++ b/cloakbrowser/human/scroll.py @@ -64,6 +64,13 @@ def human_scroll_into_view( """ viewport = page.viewport_size 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") viewport_height = viewport["height"] diff --git a/cloakbrowser/human/scroll_async.py b/cloakbrowser/human/scroll_async.py index 3ec4894..7bbb981 100644 --- a/cloakbrowser/human/scroll_async.py +++ b/cloakbrowser/human/scroll_async.py @@ -60,6 +60,13 @@ async def async_human_scroll_into_view( """ viewport = page.viewport_size 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") viewport_height = viewport["height"] diff --git a/js/src/human-puppeteer/scroll.ts b/js/src/human-puppeteer/scroll.ts index 8ef8bab..3108be5 100644 --- a/js/src/human-puppeteer/scroll.ts +++ b/js/src/human-puppeteer/scroll.ts @@ -93,8 +93,16 @@ export async function humanScrollIntoView( cursorY: number, cfg: HumanConfig, ): Promise<{ box: ElementBounds; cursorX: number; cursorY: number }> { - const viewport = page.viewport(); - if (!viewport) throw new Error('Viewport size not available'); + // Headed launches default to null defaultViewport so the page tracks the real + // 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(); if (!box) throw new Error('Element not found while scrolling into view'); diff --git a/js/src/human/scroll.ts b/js/src/human/scroll.ts index fe235e9..d959469 100644 --- a/js/src/human/scroll.ts +++ b/js/src/human/scroll.ts @@ -53,8 +53,16 @@ export async function humanScrollIntoView( cursorY: number, cfg: HumanConfig, ): Promise<{ box: ElementBounds; cursorX: number; cursorY: number; didScroll: boolean }> { - const viewport = page.viewportSize(); - if (!viewport) throw new Error('Viewport size not available'); + // Headed launches default to no_viewport so the page tracks the real OS + // 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(); if (!box) throw new Error('Element not found while scrolling into view');