6
6
package com .assertthat .selenium_shutterbug .utils .web ;
7
7
8
8
import com .assertthat .selenium_shutterbug .utils .file .FileUtil ;
9
+ import com .github .zafarkhaja .semver .Version ;
9
10
import com .google .common .collect .ImmutableMap ;
10
11
import org .openqa .selenium .Dimension ;
11
12
import org .openqa .selenium .JavascriptExecutor ;
15
16
import org .openqa .selenium .WebDriver ;
16
17
import org .openqa .selenium .WebElement ;
17
18
import org .openqa .selenium .chrome .ChromeDriver ;
19
+ import org .openqa .selenium .firefox .FirefoxDriver ;
18
20
import org .openqa .selenium .remote .CommandInfo ;
19
21
import org .openqa .selenium .remote .HttpCommandExecutor ;
20
22
import org .openqa .selenium .remote .RemoteWebDriver ;
@@ -101,6 +103,7 @@ public BufferedImage takeScreenshot() {
101
103
/**
102
104
* Using different screenshot strategy dependently on driver:
103
105
* for chrome - chrome command will be used
106
+ * for firefox - geckodriver endpoint will be used if available
104
107
* for others - their default screenshot methods
105
108
*
106
109
* @return BufferedImage resulting image
@@ -112,9 +115,13 @@ public BufferedImage takeScreenshotEntirePage() {
112
115
113
116
if (driver instanceof ChromeDriver ) {
114
117
return takeScreenshotEntirePageUsingChromeCommand ();
118
+ } else if (driver instanceof FirefoxDriver ) {
119
+ return takeScreenshotEntirePageUsingGeckoDriver ();
115
120
} else if (driver instanceof RemoteWebDriver ) {
116
121
if (((RemoteWebDriver ) driver ).getCapabilities ().getBrowserName ().equals ("chrome" )) {
117
122
return takeScreenshotEntirePageUsingChromeCommand ();
123
+ } else if (((RemoteWebDriver ) driver ).getCapabilities ().getBrowserName ().equals ("firefox" )) {
124
+ return takeScreenshotEntirePageUsingGeckoDriver ();
118
125
}
119
126
}
120
127
return takeScreenshotEntirePageDefault ();
@@ -158,14 +165,7 @@ public BufferedImage takeScreenshotEntirePageUsingChromeCommand() {
158
165
Object devicePixelRatio = executeJsScript (DEVICE_PIXEL_RATIO );
159
166
this .devicePixelRatio = devicePixelRatio instanceof Double ? (Double ) devicePixelRatio : (Long ) devicePixelRatio * 1.0 ;
160
167
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
- }
168
+ defineCustomCommand ("sendCommand" , new CommandInfo ("/session/:sessionId/chromium/send_command_and_get_result" , HttpMethod .POST ));
169
169
170
170
int verticalIterations = (int ) Math .ceil (((double ) this .getDocHeight ()) / this .getViewportHeight ());
171
171
for (int j = 0 ; j < verticalIterations ; j ++) {
@@ -176,15 +176,27 @@ public BufferedImage takeScreenshotEntirePageUsingChromeCommand() {
176
176
this .sendCommand ("Emulation.setDeviceMetricsOverride" , metrics );
177
177
Object result = this .sendCommand ("Page.captureScreenshot" , ImmutableMap .of ("format" , "png" , "fromSurface" , true ));
178
178
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" );
179
+ return decodeBase64EncodedPng ((String ) ((Map <String , ?>) result ).get ("data" ));
180
+ }
181
+
182
+ public BufferedImage takeScreenshotEntirePageUsingGeckoDriver () {
183
+ // Check geckodriver version (>= 0.24.0 is requried)
184
+ String version = (String ) ((RemoteWebDriver ) driver ).getCapabilities ().getCapability ("moz:geckodriverVersion" );
185
+ if (version == null || Version .valueOf (version ).satisfies ("<0.24.0" )) {
186
+ return takeScreenshotEntirePageDefault ();
186
187
}
187
- return bImageFromConvert ;
188
+ defineCustomCommand ("mozFullPageScreenshot" , new CommandInfo ("/session/:sessionId/moz/screenshot/full" , HttpMethod .GET ));
189
+ Object result = this .executeCustomCommand ("mozFullPageScreenshot" );
190
+ String base64EncodedPng ;
191
+ if (result instanceof String ) {
192
+ base64EncodedPng = (String ) result ;
193
+ } else if (result instanceof byte []) {
194
+ base64EncodedPng = new String ((byte []) result );
195
+ } else {
196
+ throw new RuntimeException (String .format ("Unexpected result for /moz/screenshot/full command: %s" ,
197
+ result == null ? "null" : result .getClass ().getName () + "instance" ));
198
+ }
199
+ return decodeBase64EncodedPng (base64EncodedPng );
188
200
}
189
201
190
202
public WebDriver getUnderlyingDriver () {
@@ -258,4 +270,36 @@ public Object evaluate(String script) {
258
270
Object result = ((Map <String , ?>) response ).get ("result" );
259
271
return ((Map <String , ?>) result ).get ("value" );
260
272
}
273
+
274
+ public Object executeCustomCommand (String commandName ) {
275
+ try {
276
+ Method execute = RemoteWebDriver .class .getDeclaredMethod ("execute" , String .class );
277
+ execute .setAccessible (true );
278
+ Response res = (Response ) execute .invoke (this .driver , commandName );
279
+ return res .getValue ();
280
+ } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e ) {
281
+ throw new RuntimeException (e );
282
+ }
283
+ }
284
+
285
+ private void defineCustomCommand (String name , CommandInfo info ) {
286
+ try {
287
+ Method defineCommand = HttpCommandExecutor .class .getDeclaredMethod ("defineCommand" , String .class , CommandInfo .class );
288
+ defineCommand .setAccessible (true );
289
+ defineCommand .invoke (((RemoteWebDriver ) this .driver ).getCommandExecutor (), name , info );
290
+ } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e ) {
291
+ throw new RuntimeException (e );
292
+ }
293
+ }
294
+
295
+ private BufferedImage decodeBase64EncodedPng (String base64EncodedPng ) {
296
+ InputStream in = new ByteArrayInputStream (OutputType .BYTES .convertFromBase64Png (base64EncodedPng ));
297
+ BufferedImage bImageFromConvert ;
298
+ try {
299
+ bImageFromConvert = ImageIO .read (in );
300
+ } catch (IOException e ) {
301
+ throw new RuntimeException ("Error while converting results from bytes to BufferedImage" );
302
+ }
303
+ return bImageFromConvert ;
304
+ }
261
305
}
0 commit comments