Skip to content

Commit

Permalink
Add support for Chromium-base Opera browser
Browse files Browse the repository at this point in the history
  • Loading branch information
sbabcoc committed Jan 27, 2025
1 parent 777358f commit 2c6c996
Show file tree
Hide file tree
Showing 10 changed files with 652 additions and 363 deletions.
57 changes: 57 additions & 0 deletions java/src/org/openqa/selenium/opera/AddHasCasting.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

package org.openqa.selenium.opera;

import com.google.auto.service.AutoService;
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.remote.AdditionalHttpCommands;
import org.openqa.selenium.remote.AugmenterProvider;
import org.openqa.selenium.remote.CommandInfo;
import org.openqa.selenium.remote.http.HttpMethod;

import java.util.Map;
import java.util.function.Predicate;

import static org.openqa.selenium.remote.Browser.OPERA;

@SuppressWarnings({"rawtypes", "RedundantSuppression"})
@AutoService({AdditionalHttpCommands.class, AugmenterProvider.class})
public class AddHasCasting extends org.openqa.selenium.chromium.AddHasCasting {

private static final Map<String, CommandInfo> COMMANDS =
Map.of(
GET_CAST_SINKS, new CommandInfo("session/:sessionId/ms/cast/get_sinks", HttpMethod.GET),
SET_CAST_SINK_TO_USE,
new CommandInfo("session/:sessionId/ms/cast/set_sink_to_use", HttpMethod.POST),
START_CAST_TAB_MIRRORING,
new CommandInfo("session/:sessionId/ms/cast/start_tab_mirroring", HttpMethod.POST),
GET_CAST_ISSUE_MESSAGE,
new CommandInfo("session/:sessionId/ms/cast/get_issue_message", HttpMethod.GET),
STOP_CASTING,
new CommandInfo("session/:sessionId/ms/cast/stop_casting", HttpMethod.POST));

@Override
public Map<String, CommandInfo> getAdditionalCommands() {
return COMMANDS;
}

@Override
public Predicate<Capabilities> isApplicable() {
return OPERA::is;
}
}
48 changes: 48 additions & 0 deletions java/src/org/openqa/selenium/opera/AddHasCdp.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

package org.openqa.selenium.opera;

import com.google.auto.service.AutoService;
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.remote.AdditionalHttpCommands;
import org.openqa.selenium.remote.AugmenterProvider;
import org.openqa.selenium.remote.CommandInfo;
import org.openqa.selenium.remote.http.HttpMethod;

import java.util.Map;
import java.util.function.Predicate;

import static org.openqa.selenium.remote.Browser.OPERA;

@SuppressWarnings({"rawtypes", "RedundantSuppression"})
@AutoService({AdditionalHttpCommands.class, AugmenterProvider.class})
public class AddHasCdp extends org.openqa.selenium.chromium.AddHasCdp {

private static final Map<String, CommandInfo> COMMANDS =
Map.of(EXECUTE_CDP, new CommandInfo("session/:sessionId/ms/cdp/execute", HttpMethod.POST));

@Override
public Map<String, CommandInfo> getAdditionalCommands() {
return COMMANDS;
}

@Override
public Predicate<Capabilities> isApplicable() {
return OPERA::is;
}
}
12 changes: 11 additions & 1 deletion java/src/org/openqa/selenium/opera/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,20 @@ java_export(
srcs = glob(["*.java"]),
maven_coordinates = "org.seleniumhq.selenium:selenium-opera-driver:%s" % SE_VERSION,
pom_template = "//java/src/org/openqa/selenium:template-pom",
visibility = ["//visibility:public"],
tags = [
"release-artifact",
],
visibility = [
"//visibility:public",
],
exports = [
"//java/src/org/openqa/selenium/chromium",
],
deps = [
"//java:auto-service",
"//java/src/org/openqa/selenium:core",
"//java/src/org/openqa/selenium/chromium",
"//java/src/org/openqa/selenium/manager",
"//java/src/org/openqa/selenium/remote",
],
)
168 changes: 50 additions & 118 deletions java/src/org/openqa/selenium/opera/OperaDriver.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,149 +17,81 @@

package org.openqa.selenium.opera;

import org.openqa.selenium.Capabilities;
import org.openqa.selenium.Beta;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.html5.LocalStorage;
import org.openqa.selenium.html5.Location;
import org.openqa.selenium.html5.LocationContext;
import org.openqa.selenium.html5.SessionStorage;
import org.openqa.selenium.html5.WebStorage;
import org.openqa.selenium.remote.FileDetector;
import org.openqa.selenium.chromium.ChromiumDriver;
import org.openqa.selenium.chromium.ChromiumDriverCommandExecutor;
import org.openqa.selenium.internal.Require;
import org.openqa.selenium.remote.CommandInfo;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.remote.html5.RemoteLocationContext;
import org.openqa.selenium.remote.html5.RemoteWebStorage;
import org.openqa.selenium.remote.service.DriverCommandExecutor;
import org.openqa.selenium.remote.RemoteWebDriverBuilder;
import org.openqa.selenium.remote.http.ClientConfig;
import org.openqa.selenium.remote.service.DriverFinder;
import org.openqa.selenium.remote.service.DriverService;

import java.io.File;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
* A {@link WebDriver} implementation that controls a Blink-based Opera browser running on the local
* A {@link WebDriver} implementation that controls a Chromium-based Opera browser running on the local
* machine. It requires an <code>operadriver</code> executable to be available in PATH.
*
* @see <a href="https://github.com/operasoftware/operachromiumdriver">operadriver</a>
*
* Since operadriver does not support w3c, Selenium will remove the support in the next version.
* @deprecated Use {@link org.openqa.selenium.chrome.ChromeDriver} with
* {@link org.openqa.selenium.chrome.ChromeOptions#setBinary(File)} or {@link org.openqa.selenium.chrome.ChromeOptions#setBinary(String)}
* to set the path to the Opera browser.
*
* <p>Example usage:
* <pre><code>
* ChromeOptions options = new ChromeOptions()
* options.setBinary(new File("/path/to/opera"));
*
* // For using Opera browser with ChromeDriver:
* ChromeDriver driver = new ChromeDriver(options);
*
* // For use with RemoteWebDriver:
* ChromeOptions options = new ChromeOptions();
* options.setBinary(new File("/path/to/opera"));
* RemoteWebDriver driver = new RemoteWebDriver(
* new URL("http://localhost:4444/"), options);
* </code></pre>
*/
@Deprecated
public class OperaDriver extends RemoteWebDriver
implements LocationContext, WebStorage {

private RemoteLocationContext locationContext;
private RemoteWebStorage webStorage;
public class OperaDriver extends ChromiumDriver {

/**
* Creates a new OperaDriver using the {@link OperaDriverService#createDefaultService default}
* server configuration.
*
* @see #OperaDriver(OperaDriverService, OperaOptions)
*/
public OperaDriver() {
this(OperaDriverService.createDefaultService(), new OperaOptions());
}

/**
* Creates a new OperaDriver instance. The {@code service} will be started along with the driver,
* and shutdown upon calling {@link #quit()}.
*
* @param service The service to use.
* @see #OperaDriver(OperaDriverService, OperaOptions)
*/
public OperaDriver(OperaDriverService service) {
this(service, new OperaOptions());
this(new OperaOptions());
}

/**
* Creates a new OperaDriver instance. The {@code capabilities} will be passed to the
* chromedriver service.
*
* @param capabilities The capabilities required from the OperaDriver.
* @see #OperaDriver(OperaDriverService, Capabilities)
* @deprecated Use {@link #OperaDriver(OperaOptions)} instead.
*/
@Deprecated
public OperaDriver(Capabilities capabilities) {
this(OperaDriverService.createDefaultService(), capabilities);
}

/**
* Creates a new OperaDriver instance with the specified options.
*
* @param options The options to use.
* @see #OperaDriver(OperaDriverService, OperaOptions)
*/
public OperaDriver(OperaOptions options) {
this(OperaDriverService.createDefaultService(), options);
}

/**
* Creates a new OperaDriver instance with the specified options. The {@code service} will be
* started along with the driver, and shutdown upon calling {@link #quit()}.
*
* @param service The service to use.
* @param options The options to use.
*/
public OperaDriver(OperaDriverService service, OperaOptions options) {
this(service, (Capabilities) options);
this(new OperaDriverService.Builder().build(), options);
}

/**
* Creates a new OperaDriver instance. The {@code service} will be started along with the
* driver, and shutdown upon calling {@link #quit()}.
*
* @param service The service to use.
* @param capabilities The capabilities required from the OperaDriver.
* @deprecated Use {@link #OperaDriver(OperaDriverService, OperaOptions)} instead.
*/
@Deprecated
public OperaDriver(OperaDriverService service, Capabilities capabilities) {
super(new DriverCommandExecutor(service), capabilities);
locationContext = new RemoteLocationContext(getExecuteMethod());
webStorage = new RemoteWebStorage(getExecuteMethod());
public OperaDriver(OperaDriverService service) {
this(service, new OperaOptions());
}

@Override
public void setFileDetector(FileDetector detector) {
throw new WebDriverException(
"Setting the file detector only works on remote webdriver instances obtained " +
"via RemoteWebDriver");
public OperaDriver(OperaDriverService service, OperaOptions options) {
this(service, options, ClientConfig.defaultConfig());
}

@Override
public LocalStorage getLocalStorage() {
return webStorage.getLocalStorage();
public OperaDriver(OperaDriverService service, OperaOptions options, ClientConfig clientConfig) {
super(generateExecutor(service, options, clientConfig), options, OperaOptions.CAPABILITY);
casting = new AddHasCasting().getImplementation(getCapabilities(), getExecuteMethod());
cdp = new AddHasCdp().getImplementation(getCapabilities(), getExecuteMethod());
}

@Override
public SessionStorage getSessionStorage() {
return webStorage.getSessionStorage();
private static OperaDriver.OperaDriverCommandExecutor generateExecutor(
OperaDriverService service, OperaOptions options, ClientConfig clientConfig) {
Require.nonNull("Driver service", service);
Require.nonNull("Driver options", options);
Require.nonNull("Driver clientConfig", clientConfig);
DriverFinder finder = new DriverFinder(service, options);
service.setExecutable(finder.getDriverPath());
if (finder.hasBrowserPath()) {
options.setBinary(finder.getBrowserPath());
options.setCapability("browserVersion", (Object) null);
}
return new OperaDriver.OperaDriverCommandExecutor(service, clientConfig);
}

@Override
public Location location() {
return locationContext.location();
@Beta
public static RemoteWebDriverBuilder builder() {
return RemoteWebDriver.builder().oneOf(new OperaOptions());
}

@Override
public void setLocation(Location location) {
locationContext.setLocation(location);
private static class OperaDriverCommandExecutor extends ChromiumDriverCommandExecutor {
public OperaDriverCommandExecutor(DriverService service, ClientConfig clientConfig) {
super(service, getExtraCommands(), clientConfig);
}

private static Map<String, CommandInfo> getExtraCommands() {
return Stream.of(
new AddHasCasting().getAdditionalCommands(), new AddHasCdp().getAdditionalCommands())
.flatMap((m) -> m.entrySet().stream())
.collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue));
}
}
}
29 changes: 16 additions & 13 deletions java/src/org/openqa/selenium/opera/OperaDriverInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,17 @@
import org.openqa.selenium.ImmutableCapabilities;
import org.openqa.selenium.SessionNotCreatedException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.WebDriverInfo;
import org.openqa.selenium.chromium.ChromiumDriverInfo;
import org.openqa.selenium.remote.service.DriverFinder;

import java.util.Optional;

import static org.openqa.selenium.remote.Browser.OPERA;
import static org.openqa.selenium.remote.CapabilityType.BROWSER_NAME;

@AutoService(WebDriverInfo.class)
public class OperaDriverInfo implements WebDriverInfo {
public class OperaDriverInfo extends ChromiumDriverInfo {

@Override
public String getDisplayName() {
Expand All @@ -51,31 +52,33 @@ public boolean isSupporting(Capabilities capabilities) {

@Override
public boolean isSupportingCdp() {
return true;
}

@Override
public boolean isSupportingBiDi() {
return false;
}

@Override
public boolean isAvailable() {
try {
OperaDriverService.createDefaultService();
return true;
} catch (IllegalStateException | WebDriverException e) {
return false;
}
return new DriverFinder(OperaDriverService.createDefaultService(), getCanonicalCapabilities())
.isAvailable();
}

@Override
public int getMaximumSimultaneousSessions() {
return Runtime.getRuntime().availableProcessors();
public boolean isPresent() {
return new DriverFinder(OperaDriverService.createDefaultService(), getCanonicalCapabilities())
.isPresent();
}

@Override
public Optional<WebDriver> createDriver(Capabilities capabilities)
throws SessionNotCreatedException {
if (!isAvailable()) {
throws SessionNotCreatedException {
if (!isAvailable() || !isSupporting(capabilities)) {
return Optional.empty();
}

return Optional.of(new OperaDriver(capabilities));
return Optional.of(new OperaDriver(new OperaOptions().merge(capabilities)));
}
}
Loading

0 comments on commit 2c6c996

Please sign in to comment.