Skip to content

Commit be2ad71

Browse files
committed
add BiDi method "BrowsingContext.setViewport(null, null, null)" for resetting mobile emulation mode
Implementation detail: according to BiDi spec (https://www.w3.org/TR/webdriver-bidi/#command-browsingContext-setViewport), we have to call method "setViewport" with "viewport" parameter equal to "null".
1 parent d224da6 commit be2ad71

File tree

6 files changed

+119
-56
lines changed

6 files changed

+119
-56
lines changed

java/src/org/openqa/selenium/bidi/Command.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
package org.openqa.selenium.bidi;
1919

20+
2021
import java.lang.reflect.Type;
2122
import java.util.Collections;
2223
import java.util.HashMap;

java/src/org/openqa/selenium/bidi/browsingcontext/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,6 @@ java_library(
2222
"//java/src/org/openqa/selenium/json",
2323
"//java/src/org/openqa/selenium/remote/http",
2424
artifact("com.google.auto.service:auto-service-annotations"),
25+
"@maven//:org_jspecify_jspecify",
2526
],
2627
)

java/src/org/openqa/selenium/bidi/browsingcontext/BrowsingContext.java

Lines changed: 63 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
import java.util.List;
2525
import java.util.Map;
2626
import java.util.function.Function;
27+
import org.jspecify.annotations.NullMarked;
28+
import org.jspecify.annotations.Nullable;
2729
import org.openqa.selenium.WebDriver;
2830
import org.openqa.selenium.WindowType;
2931
import org.openqa.selenium.bidi.BiDi;
@@ -36,6 +38,7 @@
3638
import org.openqa.selenium.json.TypeToken;
3739
import org.openqa.selenium.print.PrintOptions;
3840

41+
@NullMarked
3942
public class BrowsingContext {
4043

4144
private static final Json JSON = new Json();
@@ -91,6 +94,7 @@ public BrowsingContext(WebDriver driver, String id) {
9194

9295
public BrowsingContext(WebDriver driver, WindowType type) {
9396
Require.nonNull("WebDriver", driver);
97+
Require.nonNull("WindowType", type);
9498

9599
if (!(driver instanceof HasBiDi)) {
96100
throw new IllegalArgumentException("WebDriver instance must support BiDi protocol");
@@ -103,6 +107,7 @@ public BrowsingContext(WebDriver driver, WindowType type) {
103107

104108
public BrowsingContext(WebDriver driver, CreateContextParameters parameters) {
105109
Require.nonNull("WebDriver", driver);
110+
Require.nonNull("CreateContextParameters", parameters);
106111

107112
if (!(driver instanceof HasBiDi)) {
108113
throw new IllegalArgumentException("WebDriver instance must support BiDi protocol");
@@ -302,31 +307,68 @@ public String captureElementScreenshot(String elementId, String handle) {
302307
}));
303308
}
304309

305-
public void setViewport(double width, double height) {
306-
Require.positive("Viewport width", width);
307-
Require.positive("Viewport height", height);
310+
public void setViewport(int width, int height) {
311+
setViewport((double) width, (double) height);
312+
}
308313

309-
this.bidi.send(
310-
new Command<>(
311-
"browsingContext.setViewport",
312-
Map.of(CONTEXT, id, "viewport", Map.of("width", width, "height", height))));
314+
public void setViewport(int width, int height, double devicePixelRatio) {
315+
setViewport((double) width, (double) height, devicePixelRatio);
313316
}
314317

315-
public void setViewport(double width, double height, double devicePixelRatio) {
316-
Require.positive("Viewport width", width);
317-
Require.positive("Viewport height", height);
318-
Require.positive("Device pixel ratio.", devicePixelRatio);
318+
/**
319+
* Set viewport size to given width and height (aka "mobile emulation" mode).
320+
*
321+
* <p>If both {@code width} and {@code height} are null, then resets viewport to the initial size
322+
* (aka "desktop" mode).
323+
*
324+
* @param width null or positive
325+
* @param height null or positive
326+
*/
327+
public void setViewport(@Nullable Double width, @Nullable Double height) {
328+
validate(width, height);
319329

320-
this.bidi.send(
321-
new Command<>(
322-
"browsingContext.setViewport",
323-
Map.of(
324-
CONTEXT,
325-
id,
326-
"viewport",
327-
Map.of("width", width, "height", height),
328-
"devicePixelRatio",
329-
devicePixelRatio)));
330+
Map<String, Object> params = new HashMap<>();
331+
params.put(CONTEXT, id);
332+
params.put("viewport", width == null ? null : Map.of("width", width, "height", height));
333+
this.bidi.send(new Command<>("browsingContext.setViewport", params));
334+
}
335+
336+
/**
337+
* Set viewport's size and pixel ratio (aka "mobile emulation" mode).
338+
*
339+
* <p>If both {@code width} and {@code height} are null then resets viewport to the initial size
340+
* (aka "desktop" mode).
341+
*
342+
* <p>If {@code devicePixelRatio} is null then resets DPR to browser’s default DPR (usually 1.0 on
343+
* desktop).
344+
*
345+
* @param width null or positive
346+
* @param height null or positive
347+
* @param devicePixelRatio null or positive
348+
*/
349+
public void setViewport(
350+
@Nullable Double width, @Nullable Double height, @Nullable Double devicePixelRatio) {
351+
validate(width, height);
352+
validate(devicePixelRatio);
353+
354+
Map<String, Object> params = new HashMap<>();
355+
params.put(CONTEXT, id);
356+
params.put("viewport", width == null ? null : Map.of("width", width, "height", height));
357+
params.put("devicePixelRatio", devicePixelRatio);
358+
this.bidi.send(new Command<>("browsingContext.setViewport", params));
359+
}
360+
361+
private void validate(@Nullable Double width, @Nullable Double height) {
362+
if (width != null || height != null) {
363+
Require.positive("Viewport width", width);
364+
Require.positive("Viewport height", height);
365+
}
366+
}
367+
368+
private void validate(@Nullable Double devicePixelRatio) {
369+
if (devicePixelRatio != null) {
370+
Require.positive("Device pixel ratio.", devicePixelRatio);
371+
}
330372
}
331373

332374
public void activate() {

java/src/org/openqa/selenium/bidi/browsingcontext/BrowsingContextInfo.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,4 +134,9 @@ public static BrowsingContextInfo fromJson(JsonInput input) {
134134
return new BrowsingContextInfo(
135135
id, url, children, clientWindow, originalOpener, userContext, parentBrowsingContext);
136136
}
137+
138+
@Override
139+
public String toString() {
140+
return String.format("BrowsingContextInfo(%s %s)", id, url);
141+
}
137142
}

java/test/org/openqa/selenium/bidi/browsingcontext/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ java_selenium_test_suite(
1414
"selenium-remote",
1515
],
1616
deps = [
17+
"//java/src/org/openqa/selenium:core",
1718
"//java/src/org/openqa/selenium/bidi",
1819
"//java/src/org/openqa/selenium/bidi/browsingcontext",
1920
"//java/src/org/openqa/selenium/bidi/log",

java/test/org/openqa/selenium/bidi/browsingcontext/BrowsingContextTest.java

Lines changed: 48 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,16 @@
1717

1818
package org.openqa.selenium.bidi.browsingcontext;
1919

20-
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
21-
import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType;
20+
import static org.assertj.core.api.Assertions.assertThat;
21+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
2222
import static org.openqa.selenium.support.ui.ExpectedConditions.alertIsPresent;
2323
import static org.openqa.selenium.support.ui.ExpectedConditions.titleIs;
2424
import static org.openqa.selenium.support.ui.ExpectedConditions.visibilityOfElementLocated;
2525

2626
import java.util.List;
2727
import org.junit.jupiter.api.Test;
2828
import org.openqa.selenium.By;
29+
import org.openqa.selenium.Dimension;
2930
import org.openqa.selenium.JavascriptExecutor;
3031
import org.openqa.selenium.Rectangle;
3132
import org.openqa.selenium.WebElement;
@@ -136,9 +137,9 @@ void canGetTreeWithAChild() {
136137

137138
List<BrowsingContextInfo> contextInfoList = parentWindow.getTree();
138139

139-
assertThat(contextInfoList.size()).isEqualTo(1);
140+
assertThat(contextInfoList).hasSize(1);
140141
BrowsingContextInfo info = contextInfoList.get(0);
141-
assertThat(info.getChildren().size()).isEqualTo(1);
142+
assertThat(info.getChildren()).hasSize(1);
142143
assertThat(info.getId()).isEqualTo(referenceContextId);
143144
assertThat(info.getChildren().get(0).getUrl()).contains("formPage.html");
144145
}
@@ -155,7 +156,7 @@ void canGetTreeWithDepth() {
155156

156157
List<BrowsingContextInfo> contextInfoList = parentWindow.getTree(0);
157158

158-
assertThat(contextInfoList.size()).isEqualTo(1);
159+
assertThat(contextInfoList).hasSize(1);
159160
BrowsingContextInfo info = contextInfoList.get(0);
160161
assertThat(info.getChildren()).isNull(); // since depth is 0
161162
assertThat(info.getId()).isEqualTo(referenceContextId);
@@ -176,7 +177,7 @@ void canGetTreeWithRootAndDepth() {
176177

177178
List<BrowsingContextInfo> contextInfoList = parentWindow.getTree(referenceContextId, 1);
178179

179-
assertThat(contextInfoList.size()).isEqualTo(1);
180+
assertThat(contextInfoList).hasSize(1);
180181
BrowsingContextInfo info = contextInfoList.get(0);
181182
assertThat(info.getChildren()).isNotNull(); // since depth is 1
182183
assertThat(info.getId()).isEqualTo(referenceContextId);
@@ -199,7 +200,7 @@ void canGetTreeWithRoot() {
199200

200201
List<BrowsingContextInfo> contextInfoList = parentWindow.getTree(tab.getId());
201202

202-
assertThat(contextInfoList.size()).isEqualTo(1);
203+
assertThat(contextInfoList).hasSize(1);
203204
BrowsingContextInfo info = contextInfoList.get(0);
204205
assertThat(info.getId()).isEqualTo(tab.getId());
205206
assertThat(info.getOriginalOpener()).isNull();
@@ -215,7 +216,7 @@ void canGetAllTopLevelContexts() {
215216

216217
List<BrowsingContextInfo> contextInfoList = window1.getTopLevelContexts();
217218

218-
assertThat(contextInfoList.size()).isEqualTo(2);
219+
assertThat(contextInfoList).hasSize(2);
219220
}
220221

221222
@Test
@@ -226,7 +227,9 @@ void canCloseAWindow() {
226227

227228
window2.close();
228229

229-
assertThatExceptionOfType(BiDiException.class).isThrownBy(window2::getTree);
230+
assertThatThrownBy(window2::getTree)
231+
.isInstanceOf(BiDiException.class)
232+
.hasMessageContaining("not found");
230233
}
231234

232235
@Test
@@ -237,7 +240,9 @@ void canCloseATab() {
237240

238241
tab2.close();
239242

240-
assertThatExceptionOfType(BiDiException.class).isThrownBy(tab2::getTree);
243+
assertThatThrownBy(tab2::getTree)
244+
.isInstanceOf(BiDiException.class)
245+
.hasMessageContaining("not found");
241246
}
242247

243248
@Test
@@ -397,7 +402,7 @@ void canCaptureScreenshot() {
397402

398403
String screenshot = browsingContext.captureScreenshot();
399404

400-
assertThat(screenshot.length()).isPositive();
405+
assertThat(screenshot).isNotEmpty();
401406
}
402407

403408
@Test
@@ -423,7 +428,7 @@ void canCaptureScreenshotWithAllParameters() {
423428
.origin(CaptureScreenshotParameters.Origin.DOCUMENT)
424429
.clipRectangle(clipRectangle));
425430

426-
assertThat(screenshot.length()).isPositive();
431+
assertThat(screenshot).isNotEmpty();
427432
}
428433

429434
@Test
@@ -440,7 +445,7 @@ void canCaptureScreenshotOfViewport() {
440445
browsingContext.captureBoxScreenshot(
441446
elementRectangle.getX(), elementRectangle.getY(), 5, 5);
442447

443-
assertThat(screenshot.length()).isPositive();
448+
assertThat(screenshot).isNotEmpty();
444449
}
445450

446451
@Test
@@ -455,46 +460,41 @@ void canCaptureElementScreenshot() {
455460
String screenshot =
456461
browsingContext.captureElementScreenshot(((RemoteWebElement) element).getId());
457462

458-
assertThat(screenshot.length()).isPositive();
463+
assertThat(screenshot).isNotEmpty();
459464
}
460465

461466
@Test
462467
@NeedsFreshDriver
463468
void canSetViewport() {
469+
Dimension initialViewportSize = getViewportSize();
470+
464471
BrowsingContext browsingContext = new BrowsingContext(driver, driver.getWindowHandle());
465472
driver.get(appServer.whereIs("formPage.html"));
466473

467474
browsingContext.setViewport(250, 300);
475+
assertThat(getViewportSize()).isEqualTo(new Dimension(250, 300));
468476

469-
List<Long> newViewportSize =
470-
(List<Long>)
471-
((JavascriptExecutor) driver)
472-
.executeScript("return [window.innerWidth, window.innerHeight];");
473-
474-
assertThat(newViewportSize.get(0)).isEqualTo(250);
475-
assertThat(newViewportSize.get(1)).isEqualTo(300);
477+
browsingContext.setViewport(null, null);
478+
assertThat(getViewportSize()).isEqualTo(initialViewportSize);
476479
}
477480

478481
@Test
479482
@NeedsFreshDriver
480483
void canSetViewportWithDevicePixelRatio() {
484+
Dimension initialViewportSize = getViewportSize();
485+
double initialPixelRation = getDevicePixelRatio();
486+
481487
BrowsingContext browsingContext = new BrowsingContext(driver, driver.getWindowHandle());
482488
driver.get(appServer.whereIs("formPage.html"));
483489

484-
browsingContext.setViewport(250, 300, 5);
490+
browsingContext.setViewport(250, 300, 5.5);
485491

486-
List<Long> newViewportSize =
487-
(List<Long>)
488-
((JavascriptExecutor) driver)
489-
.executeScript("return [window.innerWidth, window.innerHeight];");
492+
assertThat(getViewportSize()).isEqualTo(new Dimension(250, 300));
493+
assertThat(getDevicePixelRatio()).isEqualTo(5.5);
490494

491-
assertThat(newViewportSize.get(0)).isEqualTo(250);
492-
assertThat(newViewportSize.get(1)).isEqualTo(300);
493-
494-
Long newDevicePixelRatio =
495-
(Long) ((JavascriptExecutor) driver).executeScript("return window.devicePixelRatio");
496-
497-
assertThat(newDevicePixelRatio).isEqualTo(5);
495+
browsingContext.setViewport(null, null, null);
496+
assertThat(getViewportSize()).isEqualTo(initialViewportSize);
497+
assertThat(getDevicePixelRatio()).isEqualTo(initialPixelRation);
498498
}
499499

500500
@Test
@@ -507,7 +507,7 @@ void canPrintPage() {
507507

508508
String printPage = browsingContext.print(printOptions);
509509

510-
assertThat(printPage.length()).isPositive();
510+
assertThat(printPage).isNotEmpty();
511511
// Comparing expected PDF is a hard problem.
512512
// As long as we are sending the parameters correctly it should be fine.
513513
// Trusting the browsers to do the right thing.
@@ -568,7 +568,20 @@ private String promptPage() {
568568
"<p id=\"result\"></p>"));
569569
}
570570

571+
private <T> T executeScript(String js) {
572+
return (T) ((JavascriptExecutor) driver).executeScript(js);
573+
}
574+
571575
private boolean getDocumentFocus() {
572-
return (boolean) ((JavascriptExecutor) driver).executeScript("return document.hasFocus();");
576+
return executeScript("return document.hasFocus();");
577+
}
578+
579+
private Dimension getViewportSize() {
580+
List<Number> dimensions = executeScript("return [window.innerWidth, window.innerHeight];");
581+
return new Dimension(dimensions.get(0).intValue(), dimensions.get(1).intValue());
582+
}
583+
584+
private double getDevicePixelRatio() {
585+
return ((Number) executeScript("return window.devicePixelRatio")).doubleValue();
573586
}
574587
}

0 commit comments

Comments
 (0)