In Selenium 4, how do you capture a whole-page image in Firefox, and what changes when the session is remote?
answer
- Only one browser has this built in
- The endpoint carries a vendor prefix
- The interface is marked provisional
- A remote session is the wrong Java type
- Wrap the driver before you cast it
basics
~20 sFirefox exposes getFullPageScreenshotAs through the HasFullPageScreenshot interface, backed by a Mozilla extension endpoint rather than a standard command. Over a remote session the object is a RemoteWebDriver, so you must wrap it with Augmenter before the cast succeeds.
solid answer
~30 sSelenium 4's only built-in whole-page capture is Firefox-specific: `org.openqa.selenium.firefox.HasFullPageScreenshot`, marked `@Beta`, declaring `<X> X getFullPageScreenshotAs(OutputType<X> outputType)`. `FirefoxDriver` implements it directly, so a local session just calls the method. Underneath it is not a W3C command — `AddHasFullPageScreenshot` binds it to `GET /session/:sessionId/moz/screenshot/full`, a geckodriver vendor extension with no Chromium equivalent. Remotely you construct `new RemoteWebDriver(url, new FirefoxOptions())`, and that object implements `TakesScreenshot` but **not** `HasFullPageScreenshot`, so the cast throws `ClassCastException`. Wrap it first: `WebDriver augmented = new Augmenter().augment(driver);` then cast the augmented object. The provider's applicability predicate matches the Firefox browser name, so augmenting a Chrome session correctly grants nothing.
code
java · 26 linesimport java.io.File;
import java.net.URL;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.openqa.selenium.firefox.HasFullPageScreenshot;
import org.openqa.selenium.io.FileHandler;
import org.openqa.selenium.remote.Augmenter;
import org.openqa.selenium.remote.RemoteWebDriver;
public class RemoteFullPageShot {
public static void main(String[] args) throws Exception {
WebDriver driver =
new RemoteWebDriver(new URL("http://grid.internal:4444"), new FirefoxOptions());
try {
driver.get("https://redactor.example/matters/8842/agreement");
WebDriver augmented = new Augmenter().augment(driver);
File shot =
((HasFullPageScreenshot) augmented).getFullPageScreenshotAs(OutputType.FILE);
FileHandler.copy(shot, new File("matter-8842-full-page.png"));
} finally {
driver.quit();
}
}
}go deeper
Recall that a whole-page image is not standard WebDriver behaviour and that Firefox is the browser with a built-in method for it. Knowing the standard command is viewport-only is the prerequisite.
Explain the mechanics: HasFullPageScreenshot, the getFullPageScreenshotAs signature, and that it is backed by a Mozilla vendor endpoint rather than a W3C command that other drivers implement.
Show you have hit the remote failure. Describe the ClassCastException on a Grid session, the Augmenter fix, why the provider's Firefox predicate makes augmenting Chrome a no-op, and how you branch per browser.
Own the tradeoff: whether tying evidence quality to one browser engine is acceptable, or whether the suite should standardise on a portable route and accept a PDF or a stitched composite instead.
## The one built-in full-page capture in Selenium 4 The W3C **Take Screenshot** command is viewport-only, so Selenium 4 exposes exactly one first-class way to get a whole document as an image, and it is Firefox-specific. The interface is `org.openqa.selenium.firefox.HasFullPageScreenshot`, it is annotated `@Beta`, and it declares a single method: ```java <X> X getFullPageScreenshotAs(OutputType<X> outputType); ``` `FirefoxDriver` extends `RemoteWebDriver` and implements `HasExtensions`, `HasFullPageScreenshot`, `HasContext` and `HasBiDi`, so with a local `new FirefoxDriver()` the method is simply there — no cast beyond the driver's own type. The `OutputType` conversion is identical to the ordinary screenshot path: the remote end answers with a base64 PNG, and `FILE`, `BYTES` or `BASE64` decide the Java type. Underneath it is **not** a standard command. `AddHasFullPageScreenshot` registers a command named `fullPageScreenshot` bound to: ``` GET /session/:sessionId/moz/screenshot/full ``` The `moz/` prefix is the tell — this is a Mozilla vendor extension that geckodriver implements and no other driver does. There is no equivalent on `ChromiumDriver`, which in Selenium 4 implements `HasAuthentication`, `HasBiDi`, `HasCasting`, `HasCdp`, `HasDevTools`, `HasLaunchApp`, `HasLogEvents`, `HasNetworkConditions` and `HasPermissions` — and no full-page screenshot interface at all. ## What changes the moment the session is remote This is where suites break. A Grid or container session is constructed as `new RemoteWebDriver(url, new FirefoxOptions())`. The *browser* is Firefox, but the *Java object* is a `RemoteWebDriver`, and `RemoteWebDriver` implements `TakesScreenshot` — not `HasFullPageScreenshot`. Casting it throws `ClassCastException` before a single byte moves. The fix is `org.openqa.selenium.remote.Augmenter`: 1. create the remote session as usual with `FirefoxOptions`; 2. call `WebDriver augmented = new Augmenter().augment(driver);`, which builds a subclass at runtime carrying the extra interfaces; 3. cast **the augmented object** — not the original — to `HasFullPageScreenshot` and call `getFullPageScreenshotAs(OutputType.FILE)`. `AddHasFullPageScreenshot` is registered as an `AugmenterProvider` and an `AdditionalHttpCommands` source, and its `isApplicable()` predicate matches on the Firefox browser name. Two consequences follow directly: - **Augmenting a Chrome session grants nothing.** The predicate does not match, the interface is not mixed in, and the cast still throws — which is the correct outcome, because the endpoint does not exist there. - **The augmented driver is a different object.** Keep using it; calling the original `driver` afterwards works for ordinary commands but will not carry the extra interface. ## Diagnosing it from the stack trace The failure is loud but easy to misread, because nothing in it mentions screenshots. - The exception is a plain `ClassCastException` naming `RemoteWebDriver` and `HasFullPageScreenshot`, and it is thrown **at the cast line, before any HTTP request is made** — so the driver log shows no failed command to correlate against. - A session that genuinely reaches the endpoint on the wrong browser fails differently: a `WebDriverException` raised by the remote end, not a client-side cast. - The local developer run constructs `new FirefoxDriver()` and the pipeline constructs `new RemoteWebDriver(url, new FirefoxOptions())`, so the same shared evidence helper passes on a laptop and fails only in CI. That asymmetry is the classic shape of this defect. - Augmenting and then throwing the result away is the mistake that actually recurs — the code compiles, the cast still targets the original driver, and the exception looks identical. ## The three routes compared | route | call | scope | portability | |---|---|---|---| | Firefox vendor command | `getFullPageScreenshotAs(OutputType.BYTES)` | whole document, one image | Firefox only, `@Beta` | | WebDriver BiDi capture | `BrowsingContext.captureScreenshot(new CaptureScreenshotParameters().origin(Origin.DOCUMENT))` | whole document, base64 string | needs a BiDi-enabled session | | Print Page | `((PrintsPage) driver).print(new PrintOptions())` | whole document, paginated | W3C standard, but yields a `Pdf` | `PrintsPage` is implemented by `RemoteWebDriver` itself, so it needs no augmentation, and `Pdf.getContent()` returns the base64 PDF. It is the most portable whole-document capture in Selenium 4 — at the cost of not being an image, and of paginating the redaction tool's continuous scroll into printed pages. ## Operating this in a redaction suite - **Branch, do not assume.** A cross-browser evidence helper should attempt the Firefox path when the session's capabilities say Firefox and fall back to a viewport shot or a stitch elsewhere, rather than casting unconditionally. - **Treat `@Beta` as a real signal.** The annotation is Selenium's marker that the shape may change; pin the version you test against and keep the call behind one helper so a rename is a one-line fix. - **Expect a very tall PNG.** A 40-page agreement produces an image thousands of pixels high; if the evidence goes into a report, size and rendering cost are real considerations. - **Failures still surface as `WebDriverException`.** The vendor command can fail like any other, and `Require.nonNull` rejects a null `OutputType` outright. The short version to say in an interview: full-page capture is not in the WebDriver standard, Firefox has a `moz/`-prefixed extension for it, and over a remote session you must go through `Augmenter` or the cast fails.
- Why does augmenting a remote Chrome session not grant HasFullPageScreenshot?Because AddHasFullPageScreenshot is an AugmenterProvider whose applicability predicate matches on the Firefox browser name. Against Chrome capabilities the provider does not apply, the interface is not mixed into the generated subclass, and the cast still throws. That is the correct outcome: no Chromium driver implements the endpoint, so a successful cast would only defer the failure to the HTTP call.
- What portable alternatives exist if the suite cannot standardise on Firefox?Two. WebDriver BiDi's browsingContext.captureScreenshot takes CaptureScreenshotParameters with origin set to Origin.DOCUMENT for a whole-document image. Or PrintsPage.print(PrintOptions), the W3C Print Page command implemented by RemoteWebDriver itself, which needs no augmentation but returns a Pdf whose getContent() is base64 — a document, not an image.
- What does the @Beta annotation on HasFullPageScreenshot tell you about relying on it?It is Selenium's marker that the API shape may change between releases without the usual deprecation courtesy. In practice: pin the Selenium version your suite is tested against, and keep the call behind a single evidence helper so a rename or signature change is a one-line fix rather than a sweep through every test.
saying these in an interview costs you the question
- Claiming full-page capture is part of the W3C WebDriver standard
- Expecting Chrome or Edge to expose getFullPageScreenshotAs
- Casting a RemoteWebDriver straight to HasFullPageScreenshot
- Using the original driver after augmenting instead of the returned one
- Treating a @Beta interface as a stable long-term API