diff --git a/java/src/org/openqa/selenium/bidi/Command.java b/java/src/org/openqa/selenium/bidi/Command.java
index a2e141a11792f..7cee659af223d 100644
--- a/java/src/org/openqa/selenium/bidi/Command.java
+++ b/java/src/org/openqa/selenium/bidi/Command.java
@@ -17,6 +17,7 @@
package org.openqa.selenium.bidi;
+
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.HashMap;
diff --git a/java/src/org/openqa/selenium/bidi/browsingcontext/BUILD.bazel b/java/src/org/openqa/selenium/bidi/browsingcontext/BUILD.bazel
index fbf0948a2d6eb..f613f2b689506 100644
--- a/java/src/org/openqa/selenium/bidi/browsingcontext/BUILD.bazel
+++ b/java/src/org/openqa/selenium/bidi/browsingcontext/BUILD.bazel
@@ -22,5 +22,6 @@ java_library(
"//java/src/org/openqa/selenium/json",
"//java/src/org/openqa/selenium/remote/http",
artifact("com.google.auto.service:auto-service-annotations"),
+ "@maven//:org_jspecify_jspecify",
],
)
diff --git a/java/src/org/openqa/selenium/bidi/browsingcontext/BrowsingContext.java b/java/src/org/openqa/selenium/bidi/browsingcontext/BrowsingContext.java
index 6be01cbefafa0..e72251326dfae 100644
--- a/java/src/org/openqa/selenium/bidi/browsingcontext/BrowsingContext.java
+++ b/java/src/org/openqa/selenium/bidi/browsingcontext/BrowsingContext.java
@@ -24,6 +24,8 @@
import java.util.List;
import java.util.Map;
import java.util.function.Function;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WindowType;
import org.openqa.selenium.bidi.BiDi;
@@ -36,6 +38,7 @@
import org.openqa.selenium.json.TypeToken;
import org.openqa.selenium.print.PrintOptions;
+@NullMarked
public class BrowsingContext {
private static final Json JSON = new Json();
@@ -91,6 +94,7 @@ public BrowsingContext(WebDriver driver, String id) {
public BrowsingContext(WebDriver driver, WindowType type) {
Require.nonNull("WebDriver", driver);
+ Require.nonNull("WindowType", type);
if (!(driver instanceof HasBiDi)) {
throw new IllegalArgumentException("WebDriver instance must support BiDi protocol");
@@ -103,6 +107,7 @@ public BrowsingContext(WebDriver driver, WindowType type) {
public BrowsingContext(WebDriver driver, CreateContextParameters parameters) {
Require.nonNull("WebDriver", driver);
+ Require.nonNull("CreateContextParameters", parameters);
if (!(driver instanceof HasBiDi)) {
throw new IllegalArgumentException("WebDriver instance must support BiDi protocol");
@@ -302,31 +307,68 @@ public String captureElementScreenshot(String elementId, String handle) {
}));
}
- public void setViewport(double width, double height) {
- Require.positive("Viewport width", width);
- Require.positive("Viewport height", height);
+ public void setViewport(int width, int height) {
+ setViewport((double) width, (double) height);
+ }
- this.bidi.send(
- new Command<>(
- "browsingContext.setViewport",
- Map.of(CONTEXT, id, "viewport", Map.of("width", width, "height", height))));
+ public void setViewport(int width, int height, double devicePixelRatio) {
+ setViewport((double) width, (double) height, devicePixelRatio);
}
- public void setViewport(double width, double height, double devicePixelRatio) {
- Require.positive("Viewport width", width);
- Require.positive("Viewport height", height);
- Require.positive("Device pixel ratio.", devicePixelRatio);
+ /**
+ * Set viewport size to given width and height (aka "mobile emulation" mode).
+ *
+ *
If both {@code width} and {@code height} are null, then resets viewport to the initial size
+ * (aka "desktop" mode).
+ *
+ * @param width null or positive
+ * @param height null or positive
+ */
+ public void setViewport(@Nullable Double width, @Nullable Double height) {
+ validate(width, height);
- this.bidi.send(
- new Command<>(
- "browsingContext.setViewport",
- Map.of(
- CONTEXT,
- id,
- "viewport",
- Map.of("width", width, "height", height),
- "devicePixelRatio",
- devicePixelRatio)));
+ Map params = new HashMap<>();
+ params.put(CONTEXT, id);
+ params.put("viewport", width == null ? null : Map.of("width", width, "height", height));
+ this.bidi.send(new Command<>("browsingContext.setViewport", params));
+ }
+
+ /**
+ * Set viewport's size and pixel ratio (aka "mobile emulation" mode).
+ *
+ * If both {@code width} and {@code height} are null then resets viewport to the initial size
+ * (aka "desktop" mode).
+ *
+ *
If {@code devicePixelRatio} is null then resets DPR to browser’s default DPR (usually 1.0 on
+ * desktop).
+ *
+ * @param width null or positive
+ * @param height null or positive
+ * @param devicePixelRatio null or positive
+ */
+ public void setViewport(
+ @Nullable Double width, @Nullable Double height, @Nullable Double devicePixelRatio) {
+ validate(width, height);
+ validate(devicePixelRatio);
+
+ Map params = new HashMap<>();
+ params.put(CONTEXT, id);
+ params.put("viewport", width == null ? null : Map.of("width", width, "height", height));
+ params.put("devicePixelRatio", devicePixelRatio);
+ this.bidi.send(new Command<>("browsingContext.setViewport", params));
+ }
+
+ private void validate(@Nullable Double width, @Nullable Double height) {
+ if (width != null || height != null) {
+ Require.positive("Viewport width", width);
+ Require.positive("Viewport height", height);
+ }
+ }
+
+ private void validate(@Nullable Double devicePixelRatio) {
+ if (devicePixelRatio != null) {
+ Require.positive("Device pixel ratio.", devicePixelRatio);
+ }
}
public void activate() {
diff --git a/java/src/org/openqa/selenium/bidi/browsingcontext/BrowsingContextInfo.java b/java/src/org/openqa/selenium/bidi/browsingcontext/BrowsingContextInfo.java
index 0414e6284f8ef..1edb9627798bc 100644
--- a/java/src/org/openqa/selenium/bidi/browsingcontext/BrowsingContextInfo.java
+++ b/java/src/org/openqa/selenium/bidi/browsingcontext/BrowsingContextInfo.java
@@ -134,4 +134,9 @@ public static BrowsingContextInfo fromJson(JsonInput input) {
return new BrowsingContextInfo(
id, url, children, clientWindow, originalOpener, userContext, parentBrowsingContext);
}
+
+ @Override
+ public String toString() {
+ return String.format("BrowsingContextInfo(%s %s)", id, url);
+ }
}
diff --git a/java/test/org/openqa/selenium/bidi/browsingcontext/BUILD.bazel b/java/test/org/openqa/selenium/bidi/browsingcontext/BUILD.bazel
index 71b67efb132f2..27ee17ef974d2 100644
--- a/java/test/org/openqa/selenium/bidi/browsingcontext/BUILD.bazel
+++ b/java/test/org/openqa/selenium/bidi/browsingcontext/BUILD.bazel
@@ -14,6 +14,7 @@ java_selenium_test_suite(
"selenium-remote",
],
deps = [
+ "//java/src/org/openqa/selenium:core",
"//java/src/org/openqa/selenium/bidi",
"//java/src/org/openqa/selenium/bidi/browsingcontext",
"//java/src/org/openqa/selenium/bidi/log",
diff --git a/java/test/org/openqa/selenium/bidi/browsingcontext/BrowsingContextTest.java b/java/test/org/openqa/selenium/bidi/browsingcontext/BrowsingContextTest.java
index 7e57600542f47..0ee75e4e5eed0 100644
--- a/java/test/org/openqa/selenium/bidi/browsingcontext/BrowsingContextTest.java
+++ b/java/test/org/openqa/selenium/bidi/browsingcontext/BrowsingContextTest.java
@@ -17,8 +17,8 @@
package org.openqa.selenium.bidi.browsingcontext;
-import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
-import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.openqa.selenium.support.ui.ExpectedConditions.alertIsPresent;
import static org.openqa.selenium.support.ui.ExpectedConditions.titleIs;
import static org.openqa.selenium.support.ui.ExpectedConditions.visibilityOfElementLocated;
@@ -26,6 +26,7 @@
import java.util.List;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.By;
+import org.openqa.selenium.Dimension;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.Rectangle;
import org.openqa.selenium.WebElement;
@@ -136,9 +137,9 @@ void canGetTreeWithAChild() {
List contextInfoList = parentWindow.getTree();
- assertThat(contextInfoList.size()).isEqualTo(1);
+ assertThat(contextInfoList).hasSize(1);
BrowsingContextInfo info = contextInfoList.get(0);
- assertThat(info.getChildren().size()).isEqualTo(1);
+ assertThat(info.getChildren()).hasSize(1);
assertThat(info.getId()).isEqualTo(referenceContextId);
assertThat(info.getChildren().get(0).getUrl()).contains("formPage.html");
}
@@ -155,7 +156,7 @@ void canGetTreeWithDepth() {
List contextInfoList = parentWindow.getTree(0);
- assertThat(contextInfoList.size()).isEqualTo(1);
+ assertThat(contextInfoList).hasSize(1);
BrowsingContextInfo info = contextInfoList.get(0);
assertThat(info.getChildren()).isNull(); // since depth is 0
assertThat(info.getId()).isEqualTo(referenceContextId);
@@ -176,7 +177,7 @@ void canGetTreeWithRootAndDepth() {
List contextInfoList = parentWindow.getTree(referenceContextId, 1);
- assertThat(contextInfoList.size()).isEqualTo(1);
+ assertThat(contextInfoList).hasSize(1);
BrowsingContextInfo info = contextInfoList.get(0);
assertThat(info.getChildren()).isNotNull(); // since depth is 1
assertThat(info.getId()).isEqualTo(referenceContextId);
@@ -199,7 +200,7 @@ void canGetTreeWithRoot() {
List contextInfoList = parentWindow.getTree(tab.getId());
- assertThat(contextInfoList.size()).isEqualTo(1);
+ assertThat(contextInfoList).hasSize(1);
BrowsingContextInfo info = contextInfoList.get(0);
assertThat(info.getId()).isEqualTo(tab.getId());
assertThat(info.getOriginalOpener()).isNull();
@@ -215,7 +216,7 @@ void canGetAllTopLevelContexts() {
List contextInfoList = window1.getTopLevelContexts();
- assertThat(contextInfoList.size()).isEqualTo(2);
+ assertThat(contextInfoList).hasSize(2);
}
@Test
@@ -226,7 +227,9 @@ void canCloseAWindow() {
window2.close();
- assertThatExceptionOfType(BiDiException.class).isThrownBy(window2::getTree);
+ assertThatThrownBy(window2::getTree)
+ .isInstanceOf(BiDiException.class)
+ .hasMessageContaining("not found");
}
@Test
@@ -237,7 +240,9 @@ void canCloseATab() {
tab2.close();
- assertThatExceptionOfType(BiDiException.class).isThrownBy(tab2::getTree);
+ assertThatThrownBy(tab2::getTree)
+ .isInstanceOf(BiDiException.class)
+ .hasMessageContaining("not found");
}
@Test
@@ -397,7 +402,7 @@ void canCaptureScreenshot() {
String screenshot = browsingContext.captureScreenshot();
- assertThat(screenshot.length()).isPositive();
+ assertThat(screenshot).isNotEmpty();
}
@Test
@@ -423,7 +428,7 @@ void canCaptureScreenshotWithAllParameters() {
.origin(CaptureScreenshotParameters.Origin.DOCUMENT)
.clipRectangle(clipRectangle));
- assertThat(screenshot.length()).isPositive();
+ assertThat(screenshot).isNotEmpty();
}
@Test
@@ -440,7 +445,7 @@ void canCaptureScreenshotOfViewport() {
browsingContext.captureBoxScreenshot(
elementRectangle.getX(), elementRectangle.getY(), 5, 5);
- assertThat(screenshot.length()).isPositive();
+ assertThat(screenshot).isNotEmpty();
}
@Test
@@ -455,46 +460,41 @@ void canCaptureElementScreenshot() {
String screenshot =
browsingContext.captureElementScreenshot(((RemoteWebElement) element).getId());
- assertThat(screenshot.length()).isPositive();
+ assertThat(screenshot).isNotEmpty();
}
@Test
@NeedsFreshDriver
void canSetViewport() {
+ Dimension initialViewportSize = getViewportSize();
+
BrowsingContext browsingContext = new BrowsingContext(driver, driver.getWindowHandle());
driver.get(appServer.whereIs("formPage.html"));
browsingContext.setViewport(250, 300);
+ assertThat(getViewportSize()).isEqualTo(new Dimension(250, 300));
- List newViewportSize =
- (List)
- ((JavascriptExecutor) driver)
- .executeScript("return [window.innerWidth, window.innerHeight];");
-
- assertThat(newViewportSize.get(0)).isEqualTo(250);
- assertThat(newViewportSize.get(1)).isEqualTo(300);
+ browsingContext.setViewport(null, null);
+ assertThat(getViewportSize()).isEqualTo(initialViewportSize);
}
@Test
@NeedsFreshDriver
void canSetViewportWithDevicePixelRatio() {
+ Dimension initialViewportSize = getViewportSize();
+ double initialPixelRation = getDevicePixelRatio();
+
BrowsingContext browsingContext = new BrowsingContext(driver, driver.getWindowHandle());
driver.get(appServer.whereIs("formPage.html"));
- browsingContext.setViewport(250, 300, 5);
+ browsingContext.setViewport(250, 300, 5.5);
- List newViewportSize =
- (List)
- ((JavascriptExecutor) driver)
- .executeScript("return [window.innerWidth, window.innerHeight];");
+ assertThat(getViewportSize()).isEqualTo(new Dimension(250, 300));
+ assertThat(getDevicePixelRatio()).isEqualTo(5.5);
- assertThat(newViewportSize.get(0)).isEqualTo(250);
- assertThat(newViewportSize.get(1)).isEqualTo(300);
-
- Long newDevicePixelRatio =
- (Long) ((JavascriptExecutor) driver).executeScript("return window.devicePixelRatio");
-
- assertThat(newDevicePixelRatio).isEqualTo(5);
+ browsingContext.setViewport(null, null, null);
+ assertThat(getViewportSize()).isEqualTo(initialViewportSize);
+ assertThat(getDevicePixelRatio()).isEqualTo(initialPixelRation);
}
@Test
@@ -507,7 +507,7 @@ void canPrintPage() {
String printPage = browsingContext.print(printOptions);
- assertThat(printPage.length()).isPositive();
+ assertThat(printPage).isNotEmpty();
// Comparing expected PDF is a hard problem.
// As long as we are sending the parameters correctly it should be fine.
// Trusting the browsers to do the right thing.
@@ -568,7 +568,20 @@ private String promptPage() {
""));
}
+ private T executeScript(String js) {
+ return (T) ((JavascriptExecutor) driver).executeScript(js);
+ }
+
private boolean getDocumentFocus() {
- return (boolean) ((JavascriptExecutor) driver).executeScript("return document.hasFocus();");
+ return executeScript("return document.hasFocus();");
+ }
+
+ private Dimension getViewportSize() {
+ List dimensions = executeScript("return [window.innerWidth, window.innerHeight];");
+ return new Dimension(dimensions.get(0).intValue(), dimensions.get(1).intValue());
+ }
+
+ private double getDevicePixelRatio() {
+ return ((Number) executeScript("return window.devicePixelRatio")).doubleValue();
}
}