Skip to content
47 changes: 0 additions & 47 deletions java/src/org/openqa/selenium/chromium/ChromiumDriver.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import static org.openqa.selenium.remote.Browser.OPERA;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand All @@ -44,8 +43,6 @@
import org.openqa.selenium.ScriptKey;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.bidi.BiDi;
import org.openqa.selenium.bidi.BiDiException;
import org.openqa.selenium.bidi.HasBiDi;
import org.openqa.selenium.devtools.CdpEndpointFinder;
import org.openqa.selenium.devtools.CdpInfo;
Expand Down Expand Up @@ -87,7 +84,6 @@ public class ChromiumDriver extends RemoteWebDriver
private final HasPermissions permissions;
private final HasLaunchApp launch;
private final Optional<DevTools> devTools;
private final Optional<BiDi> biDi;

/**
* May be null when the driver does not support casting; initialized during setup if available.
Expand Down Expand Up @@ -118,22 +114,6 @@ protected ChromiumDriver(
HttpClient.Factory factory = HttpClient.Factory.createDefault();
Capabilities originalCapabilities = super.getCapabilities();

Optional<String> webSocketUrl =
Optional.ofNullable((String) originalCapabilities.getCapability("webSocketUrl"));

Optional<URI> biDiUri =
webSocketUrl.map(
uri -> {
try {
return new URI(uri);
} catch (URISyntaxException e) {
LOG.warning(e.getMessage());
}
return null;
});

this.biDi = createBiDi(biDiUri, clientConfig);

Optional<URI> reportedUri =
CdpEndpointFinder.getReportedUri(capabilityKey, originalCapabilities);
Optional<HttpClient> client =
Expand Down Expand Up @@ -296,33 +276,6 @@ public Optional<DevTools> maybeGetDevTools() {
return devTools;
}

private Optional<BiDi> createBiDi(Optional<URI> biDiUri, ClientConfig clientConfig) {
if (biDiUri.isEmpty()) {
return Optional.empty();
}

URI wsUri =
biDiUri.orElseThrow(
() ->
new BiDiException(
"Check if this browser version supports BiDi and if the 'webSocketUrl: true'"
+ " capability is set."));

HttpClient.Factory clientFactory = HttpClient.Factory.createDefault();
ClientConfig wsConfig = clientConfig.baseUri(wsUri);
HttpClient wsClient = clientFactory.createClient(wsConfig);

org.openqa.selenium.bidi.Connection biDiConnection =
new org.openqa.selenium.bidi.Connection(wsClient, wsUri.toString());

return Optional.of(new BiDi(biDiConnection, wsConfig.wsTimeout()));
}

@Override
public Optional<BiDi> maybeGetBiDi() {
return biDi;
}

@Override
public List<Map<String, String>> getCastSinks() {
if (this.casting == null) {
Expand Down
59 changes: 1 addition & 58 deletions java/src/org/openqa/selenium/firefox/FirefoxDriver.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,8 @@

import static org.openqa.selenium.remote.CapabilityType.PROXY;

import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.util.Map;
import java.util.Optional;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;
Expand All @@ -34,16 +31,13 @@
import org.openqa.selenium.OutputType;
import org.openqa.selenium.Proxy;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.bidi.BiDi;
import org.openqa.selenium.bidi.BiDiException;
import org.openqa.selenium.bidi.HasBiDi;
import org.openqa.selenium.internal.Require;
import org.openqa.selenium.remote.CommandInfo;
import org.openqa.selenium.remote.FileDetector;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.remote.RemoteWebDriverBuilder;
import org.openqa.selenium.remote.http.ClientConfig;
import org.openqa.selenium.remote.http.HttpClient;
import org.openqa.selenium.remote.service.DriverCommandExecutor;
import org.openqa.selenium.remote.service.DriverFinder;
import org.openqa.selenium.remote.service.DriverService;
Expand Down Expand Up @@ -71,8 +65,6 @@ public class FirefoxDriver extends RemoteWebDriver
private final HasExtensions extensions;
private final HasFullPageScreenshot fullPageScreenshot;
private final HasContext context;
private final Optional<URI> biDiUri;
private final Optional<BiDi> biDi;

/**
* Creates a new FirefoxDriver using the {@link GeckoDriverService#createDefaultService)} server
Expand Down Expand Up @@ -140,25 +132,7 @@ private FirefoxDriver(
new AddHasFullPageScreenshot().getImplementation(getCapabilities(), getExecuteMethod());
context = new AddHasContext().getImplementation(getCapabilities(), getExecuteMethod());

Capabilities capabilities = super.getCapabilities();

Optional<String> webSocketUrl =
Optional.ofNullable((String) capabilities.getCapability("webSocketUrl"));

this.biDiUri =
webSocketUrl.map(
uri -> {
try {
return new URI(uri);
} catch (URISyntaxException e) {
LOG.warning(e.getMessage());
}
return null;
});

this.biDi = createBiDi(clientConfig, biDiUri);

this.capabilities = new ImmutableCapabilities(capabilities);
this.capabilities = new ImmutableCapabilities(super.getCapabilities());
}

@Beta
Expand Down Expand Up @@ -242,37 +216,6 @@ public void setContext(FirefoxCommandContext commandContext) {
context.setContext(commandContext);
}

private Optional<BiDi> createBiDi(ClientConfig clientConfig, Optional<URI> biDiUri) {
return biDiUri.map(
(URI wsUri) -> {
HttpClient.Factory clientFactory = HttpClient.Factory.createDefault();
ClientConfig wsConfig = clientConfig.baseUri(wsUri);
HttpClient wsClient = clientFactory.createClient(wsConfig);

org.openqa.selenium.bidi.Connection biDiConnection =
new org.openqa.selenium.bidi.Connection(wsClient, wsUri.toString());

return new BiDi(biDiConnection, wsConfig.wsTimeout());
});
}

@Override
public Optional<BiDi> maybeGetBiDi() {
return biDi;
}

@Override
public BiDi getBiDi() {
if (biDiUri.isEmpty()) {
throw new BiDiException(
"Check if this browser version supports BiDi and if the 'webSocketUrl: true' capability"
+ " is set.");
}

return maybeGetBiDi()
.orElseThrow(() -> new BiDiException("Unable to initialize Bidi connection"));
}

@Override
public void quit() {
super.quit();
Expand Down
49 changes: 44 additions & 5 deletions java/src/org/openqa/selenium/remote/RemoteWebDriver.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
Expand All @@ -42,6 +44,7 @@
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.logging.Level;
Expand Down Expand Up @@ -77,6 +80,8 @@
import org.openqa.selenium.WebElement;
import org.openqa.selenium.WindowType;
import org.openqa.selenium.bidi.BiDi;
import org.openqa.selenium.bidi.BiDiException;
import org.openqa.selenium.bidi.Connection;
import org.openqa.selenium.bidi.HasBiDi;
import org.openqa.selenium.devtools.DevTools;
import org.openqa.selenium.devtools.HasDevTools;
Expand Down Expand Up @@ -106,6 +111,7 @@
@Augmentable
public class RemoteWebDriver
implements WebDriver,
HasBiDi,
JavascriptExecutor,
Comment thread
pujagani marked this conversation as resolved.
HasCapabilities,
HasDownloads,
Expand Down Expand Up @@ -142,6 +148,8 @@ public class RemoteWebDriver

@Nullable private Network remoteNetwork;

private Optional<BiDi> biDi = Optional.empty();

// For cglib
@SuppressWarnings("DataFlowIssue")
protected RemoteWebDriver() {
Expand Down Expand Up @@ -283,6 +291,9 @@ protected void startSession(Capabilities capabilities) {

this.capabilities = returnedCapabilities;
sessionId = new SessionId(response.getSessionId());
if (Boolean.TRUE.equals(capabilities.getCapability("webSocketUrl"))) {
this.biDi = createBiDi();
}
Comment thread
qodo-code-review[bot] marked this conversation as resolved.
} catch (Exception e) {
// If session creation fails, stop the driver service to prevent zombie processes
if (executor instanceof DriverCommandExecutor) {
Expand Down Expand Up @@ -422,6 +433,36 @@ public String getPageSource() {
return (String) execute(DriverCommand.GET_PAGE_SOURCE).getValue();
}

private Optional<BiDi> createBiDi() {
Object rawUrl = this.capabilities.getCapability("webSocketUrl");
if (!(rawUrl instanceof String)
|| (!((String) rawUrl).startsWith("ws://") && !((String) rawUrl).startsWith("wss://"))) {
throw new BiDiException(
"Check if this browser version supports BiDi and if the"
+ " 'webSocketUrl: true' capability is set.");
}
Comment thread
pujagani marked this conversation as resolved.
String webSocketUrl = (String) rawUrl;
try {
URI wsUri = new URI(webSocketUrl);
HttpClient.Factory clientFactory = HttpClient.Factory.createDefault();
ClientConfig wsConfig = this.clientConfig.baseUri(wsUri);
HttpClient wsClient = clientFactory.createClient(wsConfig);
Connection biDiConnection = new Connection(wsClient, wsUri.toString());
return Optional.of(new BiDi(biDiConnection, wsConfig.wsTimeout()));
Comment thread
qodo-code-review[bot] marked this conversation as resolved.
Outdated
} catch (URISyntaxException e) {
throw new BiDiException(
"Check if this browser version supports BiDi and if the"
+ " 'webSocketUrl: true' capability is set.",
e);
}
}

@Deprecated(since = "4.46", forRemoval = true)
@Override
public Optional<BiDi> maybeGetBiDi() {
return biDi;
}
Comment thread
qodo-code-review[bot] marked this conversation as resolved.

// Misc

@Override
Expand All @@ -447,11 +488,11 @@ public void close() {
Object value = response.getValue();
List<String> windowHandles = (ArrayList<String>) value;

if (windowHandles.isEmpty() && this instanceof HasBiDi) {
if (windowHandles.isEmpty()) {
// If no top-level browsing contexts are open after calling close, it indicates that the
// WebDriver session is closed.
// If the WebDriver session is closed, the BiDi session also needs to be closed.
((HasBiDi) this).maybeGetBiDi().ifPresent(BiDi::close);
biDi.ifPresent(BiDi::close);
}
}

Expand All @@ -467,9 +508,7 @@ public void quit() {
((HasDevTools) this).maybeGetDevTools().ifPresent(DevTools::close);
}

if (this instanceof HasBiDi) {
((HasBiDi) this).maybeGetBiDi().ifPresent(BiDi::close);
}
biDi.ifPresent(BiDi::close);

execute(DriverCommand.QUIT);
} finally {
Expand Down
17 changes: 17 additions & 0 deletions java/src/org/openqa/selenium/safari/SafariOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

import java.util.Collections;
import java.util.Set;
import java.util.logging.Logger;
import org.jspecify.annotations.Nullable;
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.WebDriverException;
Expand All @@ -49,6 +50,8 @@
*/
public class SafariOptions extends AbstractDriverOptions<SafariOptions> {

private static final Logger LOG = Logger.getLogger(SafariOptions.class.getName());

public SafariOptions() {
setUseTechnologyPreview(false);
setCapability(BROWSER_NAME, SAFARI.browserName());
Expand Down Expand Up @@ -89,6 +92,19 @@ public SafariOptions merge(Capabilities extraCapabilities) {
return newInstance;
}

/**
* Enables the WebDriver BiDi protocol. BiDi support in Safari is experimental and requires Safari
* Technology Preview; see {@link #setUseTechnologyPreview(boolean)}.
*
* @return this {@link SafariOptions} instance for chaining
*/
public SafariOptions enableBiDi() {
LOG.warning("Safari's WebDriver BiDi support is experimental and may not work as expected.");
setCapability("webSocketUrl", true);
Comment thread
pujagani marked this conversation as resolved.
setCapability(Option.EXPERIMENTAL_WEB_SOCKET_URL, true);
return this;
}
Comment thread
pujagani marked this conversation as resolved.

public boolean getAutomaticInspection() {
return Boolean.TRUE.equals(getCapability(Option.AUTOMATIC_INSPECTION));
}
Expand Down Expand Up @@ -156,5 +172,6 @@ private interface Option {
// Defined by Apple
String AUTOMATIC_INSPECTION = "safari:automaticInspection";
String AUTOMATIC_PROFILING = "safari:automaticProfiling";
String EXPERIMENTAL_WEB_SOCKET_URL = "safari:experimentalWebSocketUrl";
}
}
Loading