Use captureBeyondViewport for full-page screenshots instead of window resize#580
Conversation
Replaces the window-resize hack in capture_screenshot with CDP's native captureBeyondViewport: true flag on Page.captureScreenshot. The previous approach called Browser.setWindowBounds to put the window into windowState: "fullscreen" before capture and back to "normal" in an ensure block. On macOS Chrome this triggers *native* fullscreen, moving the window to its own Space and stealing focus from the user's foreground app on every full-page screenshot. captureBeyondViewport has been stable in Chrome since v81 and is the same flag Playwright and puppeteer use for their fullPage path. Output is pixel-identical, one CDP round-trip instead of three, no window-state mutation. Also implicitly fixes rubycdp#178 — huge pages that returned blank under the resize approach (because the window can't grow past the display size) now render correctly, since the native path has no viewport ceiling.
|
Very nice! Mind adding changelog entry? And we are good to go! |
|
No problem! Done ✅ |
|
Thank you so much! |
|
Just wondering if this change is why I've started ending up with a much smaller viewport size when exporting a screenshot? Previously |
|
@brendon it's hard to say without small reproduction test, can you come up with one? |
|
Yep, I'll have a go next week. I think it may be due to using a newer version of Browserless as it seems to be ignoring other options like cpu pinning and queue size. |
|
I got Claude to fix things for me (it worked). Here's the report it gave of why the bug happened. Hopefully it's reproducible by you. Let me know if this is unrelated to this PR and I'll open a new issue:
|
|
I think the bug has occurred because Browserless changed their default viewport size at some point recently thought I can't see it in the changelog. |
|
I think this is probably best dealt with in a seperate issue but here's the root source of the change in behaviour (Claude): Root cause: Chrome's RenderDocument discards emulation on navigation; Ferrum doesn't re-apply itAfter more digging, this isn't a browserless or Chrome-version bug — it's a gap in Ferrum, newly exposed by a Chrome feature rollout. What happens
Because Why it surfaced nowRenderDocument is rolling out in Chrome, and in puppeteer-launched setups it became active when puppeteer-core 24.41.0 stopped disabling it (puppeteer#14745, "remove RenderDocument from disabled Chrome features"). Previously the override happened to survive navigation, so the missing re-apply was invisible. As RenderDocument becomes the default more broadly, this will hit more Ferrum users. Puppeteer and Playwright both re-apply viewport emulation after each navigation — Ferrum does not, which is the underlying gap. Reproductionrequire "ferrum"
# window smaller than the requested viewport makes the crop visible
browser = Ferrum::Browser.new(window_size: [800, 600])
page = browser.create_page
page.set_viewport(width: 1280, height: 720)
page.go_to("http://google.com") # ends on a different origin -> document swap
p page.evaluate("[window.innerWidth, window.innerHeight]")
# expected: [1280, 720] actual: [800, 600] (override discarded)
page.screenshot(path: "out.png", full: false)
# expected: 1280x720 actual: top-left 800x600Suggested fix
Workaround for othersRe-assert the viewport right before the screenshot: page.command("Emulation.clearDeviceMetricsOverride") # clear is required — identical re-set is a no-op
page.set_viewport(width: 1280, height: 720, scale_factor: 1) |
Problem
Page#screenshot(full: true)resizes the window to fullscreen and back (maybe_resize_fullscreen). On macOS Chrome this triggers native fullscreen so the window steals focus from whatever app you're in if you're working while letting an agent run a task on your browser (this is how I found the issue).Fix
Pass
captureBeyondViewport: truetoPage.captureScreenshotand drop the resize. CDP-native since Chrome 81; same flag Playwright and puppeteer use for full-page screenshots. Output is the same, one CDP round-trip instead of three, no window-state mutation.