Skip to main content

The capture cascade

Screenshots are taken in two stages, and it is worth understanding which one you get, because their fidelity differs.

Stage one — the browser's own screen capture. Pixel-exact: it is a picture of what the screen actually showed, including everything CSS can do. The browser asks the user for permission once per capture, and that prompt is expected behavior, not a failure — it appears only after the user clicks "Capture screenshot", never on page load.

Stage two — the DOM renderer. No permission prompt, works everywhere including iOS Safari, and reconstructs the page from the DOM rather than photographing it. That reconstruction is very good and has named limits, listed under what the DOM stage does not reproduce.

screenshot.strategy picks the behavior:

ValueBehavior
auto (default)try stage one, fall back to stage two when it is unavailable or declined
nativestage one only
domstage two only — no permission prompt ever
offno screenshot button at all

The report records which stage the reporter's browser says produced the screenshot, as capture_method (native or dom) in its technical details. Treat it as a hint about what to expect from the picture, not as proof that the picture is exact: the value comes from the browser, the server cannot check it against the file, and the report mail marks it as reported by the browser.

Whatever the stage, the reporter sees the screenshot before sending and can discard or retake it. Nothing is uploaded until they attach it.

Which is why Send is refused while a capture is still sitting in that preview. It exists only in their browser at that point, so sending would file the report without the one thing they took a screenshot for — and the old behavior did exactly that, silently, followed by a success message. The widget now says which of the two ways out there is and puts focus on the Attach button. It does not attach for them: Discard is there because a capture is sometimes deliberately not sent, and choosing on their behalf overrides that.

If you drive the capture component yourself rather than through the shipped widget, that refusal is a seam you have to fill: onPending sits beside onStage and is called when a capture starts waiting in the preview, and cleared when it is attached, discarded or fails. The shipped implementation sets the component's screenshotPending property, which is what the submit reads. A custom integration that never calls it simply keeps the older behavior, where Send goes through and the capture is dropped. onEdited is the third seam: it is told whether the preview shows an edited picture, and the shipped implementation sets screenshotEdited, which puts the flag described below on the report.

What ends up in the frame​

The visible viewport, not the whole page. screenshot.viewport_only is on by default and both stages honor it, so a report about something further down the page arrives as the band the reporter was actually looking at. That is usually the point — but it is worth knowing before you go looking for the rest of the page in the image.

Stage one captures whatever the reporter agreed to share. The browser owns that choice, and this package does not get to make it: Chromium narrows the picker to the current tab, elsewhere the reporter can pick another window or the whole screen, and what they pick is what arrives. Two consequences worth carrying: a report can contain more of their desktop than either of you intended, and region redaction covers regions of your page — it has no reach into a window that is not yours.

Cropping and marking before attaching​

The preview offers Edit beside Attach. It opens the capture in an editor where the reporter crops it: by dragging the handles or the crop itself, with the arrow keys on the crop area (Shift resizes, Alt moves in single pixels), or by typing the four percentages. Apply puts the cropped picture in the preview, fitted to screenshot.max_bytes like a capture, and only that picture is uploaded when they attach it. The uncropped capture never leaves the browser. Cancel and Escape close the editor without applying, and what was set is still there the next time it opens, until the capture is discarded or retaken.

The same editor marks what the report is about. Beside Crop it offers an arrow, a line, an ellipse, a rectangle and a freehand line, in five colors and three widths. The reporter draws a mark with the pointer, or presses a tool's button from the keyboard and gets one in the middle of the picture. A press on a mark's stroke selects it, a drag moves it and its handles resize it, and Shift while drawing holds a line to steps of 45 degrees and a box square. Undo and Redo, or Ctrl+Z and Ctrl+Shift+Z, step through every change. Apply draws each mark into the picture with a light seam under its color, so it reads on a dark page and on a light one.

Cropping is the reporter's answer to the paragraph above: a share that took in more of their screen than they meant can be cut down to your page before anything is sent. It removes what lies outside the crop and nothing inside it, and a mark is an outline, so neither is a way to blank out part of the picture. Region redaction is.

A report with an edited screenshot says so: screenshot_edited is true in its technical details, because a cropped picture no longer matches the viewport the other details describe, and a marked one shows more than the page did. Like capture_method, it is the browser's statement.

The editor is a file of its own, visual-feedback-editor.iife.js, fetched the first time somebody presses Edit. Setting screenshot.editor (VISUAL_FEEDBACK_SCREENSHOT_EDITOR) to false takes the button away, and the file is never fetched. When the file cannot be fetched, the preview says so and the capture can still be attached as it is.

The byte cap holds on both ends​

screenshot.max_bytes (8 MB) is the number the browser fits a capture to and the server checks it against. A capture over the cap is scaled down in the browser, in up to four passes, before the preview shows it, so the reporter sees what is sent. The server still refuses one that does not fit, which only a cap far below what the page can be drawn at reaches. Each pass costs detail: if your captures are routinely too large, lower screenshot.scale, which sets the size the browser renders at, rather than the cap.