Skip to content

Commit 4b41409

Browse files
committed
Use geckodirver endpoint to take full page screenshot
1 parent a31de31 commit 4b41409

File tree

2 files changed

+64
-16
lines changed

2 files changed

+64
-16
lines changed

pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@
5656
<artifactId>lombok</artifactId>
5757
<version>1.18.8</version>
5858
</dependency>
59+
<dependency>
60+
<groupId>com.github.zafarkhaja</groupId>
61+
<artifactId>java-semver</artifactId>
62+
<version>0.9.0</version>
63+
</dependency>
5964
</dependencies>
6065

6166
<build>

src/main/java/com/assertthat/selenium_shutterbug/utils/web/Browser.java

Lines changed: 59 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package com.assertthat.selenium_shutterbug.utils.web;
77

88
import com.assertthat.selenium_shutterbug.utils.file.FileUtil;
9+
import com.github.zafarkhaja.semver.Version;
910
import com.google.common.collect.ImmutableMap;
1011
import org.openqa.selenium.Dimension;
1112
import org.openqa.selenium.JavascriptExecutor;
@@ -15,6 +16,7 @@
1516
import org.openqa.selenium.WebDriver;
1617
import org.openqa.selenium.WebElement;
1718
import org.openqa.selenium.chrome.ChromeDriver;
19+
import org.openqa.selenium.firefox.FirefoxDriver;
1820
import org.openqa.selenium.remote.CommandInfo;
1921
import org.openqa.selenium.remote.HttpCommandExecutor;
2022
import org.openqa.selenium.remote.RemoteWebDriver;
@@ -112,9 +114,13 @@ public BufferedImage takeScreenshotEntirePage() {
112114

113115
if (driver instanceof ChromeDriver) {
114116
return takeScreenshotEntirePageUsingChromeCommand();
117+
} else if (driver instanceof FirefoxDriver) {
118+
return takeScreenshotEntirePageUsingGeckoDriver();
115119
} else if (driver instanceof RemoteWebDriver) {
116120
if (((RemoteWebDriver) driver).getCapabilities().getBrowserName().equals("chrome")) {
117121
return takeScreenshotEntirePageUsingChromeCommand();
122+
} else if (((RemoteWebDriver) driver).getCapabilities().getBrowserName().equals("firefox")) {
123+
return takeScreenshotEntirePageUsingGeckoDriver();
118124
}
119125
}
120126
return takeScreenshotEntirePageDefault();
@@ -158,14 +164,7 @@ public BufferedImage takeScreenshotEntirePageUsingChromeCommand() {
158164
Object devicePixelRatio = executeJsScript(DEVICE_PIXEL_RATIO);
159165
this.devicePixelRatio = devicePixelRatio instanceof Double ? (Double) devicePixelRatio : (Long) devicePixelRatio * 1.0;
160166

161-
try {
162-
CommandInfo cmd = new CommandInfo("/session/:sessionId/chromium/send_command_and_get_result", HttpMethod.POST);
163-
Method defineCommand = HttpCommandExecutor.class.getDeclaredMethod("defineCommand", String.class, CommandInfo.class);
164-
defineCommand.setAccessible(true);
165-
defineCommand.invoke(((RemoteWebDriver) this.driver).getCommandExecutor(), "sendCommand", cmd);
166-
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
167-
throw new RuntimeException(e);
168-
}
167+
defineCustomCommand("sendCommand", new CommandInfo("/session/:sessionId/chromium/send_command_and_get_result", HttpMethod.POST));
169168

170169
int verticalIterations = (int) Math.ceil(((double) this.getDocHeight()) / this.getViewportHeight());
171170
for (int j = 0; j < verticalIterations; j++) {
@@ -176,15 +175,27 @@ public BufferedImage takeScreenshotEntirePageUsingChromeCommand() {
176175
this.sendCommand("Emulation.setDeviceMetricsOverride", metrics);
177176
Object result = this.sendCommand("Page.captureScreenshot", ImmutableMap.of("format", "png", "fromSurface", true));
178177
this.sendCommand("Emulation.clearDeviceMetricsOverride", ImmutableMap.of());
179-
String base64EncodedPng = (String) ((Map<String, ?>) result).get("data");
180-
InputStream in = new ByteArrayInputStream(OutputType.BYTES.convertFromBase64Png(base64EncodedPng));
181-
BufferedImage bImageFromConvert;
182-
try {
183-
bImageFromConvert = ImageIO.read(in);
184-
} catch (IOException e) {
185-
throw new RuntimeException("Error while converting results from bytes to BufferedImage");
178+
return decodeBase64EncodedPng((String) ((Map<String, ?>) result).get("data"));
179+
}
180+
181+
public BufferedImage takeScreenshotEntirePageUsingGeckoDriver() {
182+
// Check geckodriver version (>= 0.24.0 is requried)
183+
String version = (String) ((RemoteWebDriver) driver).getCapabilities().getCapability("moz:geckodriverVersion");
184+
if (version == null || Version.valueOf(version).satisfies(">=0.24.0")) {
185+
return takeScreenshotEntirePageDefault();
186186
}
187-
return bImageFromConvert;
187+
defineCustomCommand("mozFullPageScreenshot", new CommandInfo("/session/:sessionId/moz/screenshot/full", HttpMethod.GET));
188+
Object result = this.executeCustomCommand("mozFullPageScreenshot");
189+
String base64EncodedPng;
190+
if (result instanceof String) {
191+
base64EncodedPng = (String) result;
192+
} else if (result instanceof byte[]) {
193+
base64EncodedPng = new String((byte[]) result);
194+
} else {
195+
throw new RuntimeException(String.format("Unexpected result for /moz/screenshot/full command: %s",
196+
result == null ? "null" : result.getClass().getName() + "instance"));
197+
}
198+
return decodeBase64EncodedPng(base64EncodedPng);
188199
}
189200

190201
public WebDriver getUnderlyingDriver() {
@@ -258,4 +269,36 @@ public Object evaluate(String script) {
258269
Object result = ((Map<String, ?>) response).get("result");
259270
return ((Map<String, ?>) result).get("value");
260271
}
272+
273+
public Object executeCustomCommand(String commandName) {
274+
try {
275+
Method execute = RemoteWebDriver.class.getDeclaredMethod("execute", String.class);
276+
execute.setAccessible(true);
277+
Response res = (Response) execute.invoke(this.driver, commandName);
278+
return res.getValue();
279+
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
280+
throw new RuntimeException(e);
281+
}
282+
}
283+
284+
private void defineCustomCommand(String name, CommandInfo info) {
285+
try {
286+
Method defineCommand = HttpCommandExecutor.class.getDeclaredMethod("defineCommand", String.class, CommandInfo.class);
287+
defineCommand.setAccessible(true);
288+
defineCommand.invoke(((RemoteWebDriver) this.driver).getCommandExecutor(), name, info);
289+
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
290+
throw new RuntimeException(e);
291+
}
292+
}
293+
294+
private BufferedImage decodeBase64EncodedPng(String base64EncodedPng) {
295+
InputStream in = new ByteArrayInputStream(OutputType.BYTES.convertFromBase64Png(base64EncodedPng));
296+
BufferedImage bImageFromConvert;
297+
try {
298+
bImageFromConvert = ImageIO.read(in);
299+
} catch (IOException e) {
300+
throw new RuntimeException("Error while converting results from bytes to BufferedImage");
301+
}
302+
return bImageFromConvert;
303+
}
261304
}

0 commit comments

Comments
 (0)