# Capturing WebRender screenshots for debugging When investigating a WebRender-specific rendering artifact (pixel snapping, blending, compositing, tiling), the usual automated screenshot paths are not useful: WebDriver, Marionette and the DevTools screenshot commands all capture pixels by re-rendering the document through the software `drawSnapshot` (`CrossProcessPaint`) path, which does **not** go through the WebRender compositor. A screenshot taken that way can look correct even when the real on-screen output is wrong. The `remote.screenshot.use_readback` preference makes automated screenshots read back the **actual WebRender composited framebuffer** instead, so captured pixels match what is really on screen (including out-of-process content). ## Usage 1. Set `remote.screenshot.use_readback` to `true` (documented under the Remote Agent "Preferences" page). 2. Take a screenshot the usual way: - WebDriver BiDi `browsingContext.captureScreenshot` - Marionette / WebDriver classic *Take Screenshot* - The `firefox-devtools` MCP `screenshot_page` tool (which drives BiDi) The returned image now reflects WebRender output. No other change to the client is required. ## How it works With the preference enabled, `capture.canvas` (`remote/shared/Capture.sys.mjs`) draws the content area's on-screen rectangle with `CanvasRenderingContext2D.drawWindow(..., DRAWWINDOW_USE_WIDGET_LAYERS)` instead of taking a `drawSnapshot`. In the parent process that flag drives a real compositor readback (`WebRenderLayerManager::MakeSnapshotIfRequired` -> `WebRenderBridgeParent::RecvGetSnapshot` -> `WebRenderAPI::Readback` -> `wr_renderer_readback`), i.e. a `glReadPixels` of the composited framebuffer. ## Limitations Compositor readback can only return the pixels currently composited on screen, so this is a debugging aid, not a general-purpose screenshot mode: - Every capture degrades to the **full content area** of the **foreground tab**. Full-document (`origin: "document"`) screenshots, clip regions and element screenshots all return the viewport instead of the requested region. - The target must be a **content** browsing context: drive the WebDriver session in content scope. A capture with no content area to read back, such as one taken while the session is in chrome (privileged) scope, keeps using the non-WebRender `drawSnapshot` path. - **Not supported on macOS.** There the compositor runs in the GPU process and cannot read back the composited window itself, so it asks the parent process over `PNativeLayerRemote::RequestReadback`. The parent only honours that request in automation (see `NativeLayerRootRemoteMacParent::RecvRequestReadback`), and rejects it with `IPC_FAIL`, which crashes the parent process in a debug build and kills the GPU process in a release build. - The capture reflects **whatever is currently composited**, so apply any zoom, scroll, or DOM change *before* capturing. Note that browser full-zoom is a chrome-scope operation and cannot be combined with a content-scope capture in a single step; CSS `zoom` is content-drivable and exercises the same effective-zoom rendering path. - Leave the preference at its default (`false`) for normal automation.