Source code

Revision control

Copy as Markdown

Other Tools

# 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.