Skip to content

Commit f4b1c69

Browse files
committed
Add support for Chromium-base Opera browser
1 parent 85b465e commit f4b1c69

File tree

10 files changed

+652
-363
lines changed

10 files changed

+652
-363
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Licensed to the Software Freedom Conservancy (SFC) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The SFC licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package org.openqa.selenium.opera;
19+
20+
import com.google.auto.service.AutoService;
21+
import org.openqa.selenium.Capabilities;
22+
import org.openqa.selenium.remote.AdditionalHttpCommands;
23+
import org.openqa.selenium.remote.AugmenterProvider;
24+
import org.openqa.selenium.remote.CommandInfo;
25+
import org.openqa.selenium.remote.http.HttpMethod;
26+
27+
import java.util.Map;
28+
import java.util.function.Predicate;
29+
30+
import static org.openqa.selenium.remote.Browser.OPERA;
31+
32+
@SuppressWarnings({"rawtypes", "RedundantSuppression"})
33+
@AutoService({AdditionalHttpCommands.class, AugmenterProvider.class})
34+
public class AddHasCasting extends org.openqa.selenium.chromium.AddHasCasting {
35+
36+
private static final Map<String, CommandInfo> COMMANDS =
37+
Map.of(
38+
GET_CAST_SINKS, new CommandInfo("session/:sessionId/ms/cast/get_sinks", HttpMethod.GET),
39+
SET_CAST_SINK_TO_USE,
40+
new CommandInfo("session/:sessionId/ms/cast/set_sink_to_use", HttpMethod.POST),
41+
START_CAST_TAB_MIRRORING,
42+
new CommandInfo("session/:sessionId/ms/cast/start_tab_mirroring", HttpMethod.POST),
43+
GET_CAST_ISSUE_MESSAGE,
44+
new CommandInfo("session/:sessionId/ms/cast/get_issue_message", HttpMethod.GET),
45+
STOP_CASTING,
46+
new CommandInfo("session/:sessionId/ms/cast/stop_casting", HttpMethod.POST));
47+
48+
@Override
49+
public Map<String, CommandInfo> getAdditionalCommands() {
50+
return COMMANDS;
51+
}
52+
53+
@Override
54+
public Predicate<Capabilities> isApplicable() {
55+
return OPERA::is;
56+
}
57+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Licensed to the Software Freedom Conservancy (SFC) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The SFC licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package org.openqa.selenium.opera;
19+
20+
import com.google.auto.service.AutoService;
21+
import org.openqa.selenium.Capabilities;
22+
import org.openqa.selenium.remote.AdditionalHttpCommands;
23+
import org.openqa.selenium.remote.AugmenterProvider;
24+
import org.openqa.selenium.remote.CommandInfo;
25+
import org.openqa.selenium.remote.http.HttpMethod;
26+
27+
import java.util.Map;
28+
import java.util.function.Predicate;
29+
30+
import static org.openqa.selenium.remote.Browser.OPERA;
31+
32+
@SuppressWarnings({"rawtypes", "RedundantSuppression"})
33+
@AutoService({AdditionalHttpCommands.class, AugmenterProvider.class})
34+
public class AddHasCdp extends org.openqa.selenium.chromium.AddHasCdp {
35+
36+
private static final Map<String, CommandInfo> COMMANDS =
37+
Map.of(EXECUTE_CDP, new CommandInfo("session/:sessionId/ms/cdp/execute", HttpMethod.POST));
38+
39+
@Override
40+
public Map<String, CommandInfo> getAdditionalCommands() {
41+
return COMMANDS;
42+
}
43+
44+
@Override
45+
public Predicate<Capabilities> isApplicable() {
46+
return OPERA::is;
47+
}
48+
}

java/src/org/openqa/selenium/opera/BUILD.bazel

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,20 @@ java_export(
66
srcs = glob(["*.java"]),
77
maven_coordinates = "org.seleniumhq.selenium:selenium-opera-driver:%s" % SE_VERSION,
88
pom_template = "//java/src/org/openqa/selenium:template-pom",
9-
visibility = ["//visibility:public"],
9+
tags = [
10+
"release-artifact",
11+
],
12+
visibility = [
13+
"//visibility:public",
14+
],
15+
exports = [
16+
"//java/src/org/openqa/selenium/chromium",
17+
],
1018
deps = [
1119
"//java:auto-service",
1220
"//java/src/org/openqa/selenium:core",
21+
"//java/src/org/openqa/selenium/chromium",
22+
"//java/src/org/openqa/selenium/manager",
1323
"//java/src/org/openqa/selenium/remote",
1424
],
1525
)

java/src/org/openqa/selenium/opera/OperaDriver.java

Lines changed: 50 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -17,149 +17,81 @@
1717

1818
package org.openqa.selenium.opera;
1919

20-
import org.openqa.selenium.Capabilities;
20+
import org.openqa.selenium.Beta;
2121
import org.openqa.selenium.WebDriver;
22-
import org.openqa.selenium.WebDriverException;
23-
import org.openqa.selenium.html5.LocalStorage;
24-
import org.openqa.selenium.html5.Location;
25-
import org.openqa.selenium.html5.LocationContext;
26-
import org.openqa.selenium.html5.SessionStorage;
27-
import org.openqa.selenium.html5.WebStorage;
28-
import org.openqa.selenium.remote.FileDetector;
22+
import org.openqa.selenium.chromium.ChromiumDriver;
23+
import org.openqa.selenium.chromium.ChromiumDriverCommandExecutor;
24+
import org.openqa.selenium.internal.Require;
25+
import org.openqa.selenium.remote.CommandInfo;
2926
import org.openqa.selenium.remote.RemoteWebDriver;
30-
import org.openqa.selenium.remote.html5.RemoteLocationContext;
31-
import org.openqa.selenium.remote.html5.RemoteWebStorage;
32-
import org.openqa.selenium.remote.service.DriverCommandExecutor;
27+
import org.openqa.selenium.remote.RemoteWebDriverBuilder;
28+
import org.openqa.selenium.remote.http.ClientConfig;
29+
import org.openqa.selenium.remote.service.DriverFinder;
30+
import org.openqa.selenium.remote.service.DriverService;
3331

34-
import java.io.File;
32+
import java.util.Map;
33+
import java.util.stream.Collectors;
34+
import java.util.stream.Stream;
3535

3636
/**
37-
* A {@link WebDriver} implementation that controls a Blink-based Opera browser running on the local
37+
* A {@link WebDriver} implementation that controls a Chromium-based Opera browser running on the local
3838
* machine. It requires an <code>operadriver</code> executable to be available in PATH.
3939
*
4040
* @see <a href="https://github.com/operasoftware/operachromiumdriver">operadriver</a>
41-
*
42-
* Since operadriver does not support w3c, Selenium will remove the support in the next version.
43-
* @deprecated Use {@link org.openqa.selenium.chrome.ChromeDriver} with
44-
* {@link org.openqa.selenium.chrome.ChromeOptions#setBinary(File)} or {@link org.openqa.selenium.chrome.ChromeOptions#setBinary(String)}
45-
* to set the path to the Opera browser.
46-
*
47-
* <p>Example usage:
48-
* <pre><code>
49-
* ChromeOptions options = new ChromeOptions()
50-
* options.setBinary(new File("/path/to/opera"));
51-
*
52-
* // For using Opera browser with ChromeDriver:
53-
* ChromeDriver driver = new ChromeDriver(options);
54-
*
55-
* // For use with RemoteWebDriver:
56-
* ChromeOptions options = new ChromeOptions();
57-
* options.setBinary(new File("/path/to/opera"));
58-
* RemoteWebDriver driver = new RemoteWebDriver(
59-
* new URL("http://localhost:4444/"), options);
60-
* </code></pre>
6141
*/
62-
@Deprecated
63-
public class OperaDriver extends RemoteWebDriver
64-
implements LocationContext, WebStorage {
65-
66-
private RemoteLocationContext locationContext;
67-
private RemoteWebStorage webStorage;
42+
public class OperaDriver extends ChromiumDriver {
6843

69-
/**
70-
* Creates a new OperaDriver using the {@link OperaDriverService#createDefaultService default}
71-
* server configuration.
72-
*
73-
* @see #OperaDriver(OperaDriverService, OperaOptions)
74-
*/
7544
public OperaDriver() {
76-
this(OperaDriverService.createDefaultService(), new OperaOptions());
77-
}
78-
79-
/**
80-
* Creates a new OperaDriver instance. The {@code service} will be started along with the driver,
81-
* and shutdown upon calling {@link #quit()}.
82-
*
83-
* @param service The service to use.
84-
* @see #OperaDriver(OperaDriverService, OperaOptions)
85-
*/
86-
public OperaDriver(OperaDriverService service) {
87-
this(service, new OperaOptions());
45+
this(new OperaOptions());
8846
}
8947

90-
/**
91-
* Creates a new OperaDriver instance. The {@code capabilities} will be passed to the
92-
* chromedriver service.
93-
*
94-
* @param capabilities The capabilities required from the OperaDriver.
95-
* @see #OperaDriver(OperaDriverService, Capabilities)
96-
* @deprecated Use {@link #OperaDriver(OperaOptions)} instead.
97-
*/
98-
@Deprecated
99-
public OperaDriver(Capabilities capabilities) {
100-
this(OperaDriverService.createDefaultService(), capabilities);
101-
}
102-
103-
/**
104-
* Creates a new OperaDriver instance with the specified options.
105-
*
106-
* @param options The options to use.
107-
* @see #OperaDriver(OperaDriverService, OperaOptions)
108-
*/
10948
public OperaDriver(OperaOptions options) {
110-
this(OperaDriverService.createDefaultService(), options);
111-
}
112-
113-
/**
114-
* Creates a new OperaDriver instance with the specified options. The {@code service} will be
115-
* started along with the driver, and shutdown upon calling {@link #quit()}.
116-
*
117-
* @param service The service to use.
118-
* @param options The options to use.
119-
*/
120-
public OperaDriver(OperaDriverService service, OperaOptions options) {
121-
this(service, (Capabilities) options);
49+
this(new OperaDriverService.Builder().build(), options);
12250
}
12351

124-
/**
125-
* Creates a new OperaDriver instance. The {@code service} will be started along with the
126-
* driver, and shutdown upon calling {@link #quit()}.
127-
*
128-
* @param service The service to use.
129-
* @param capabilities The capabilities required from the OperaDriver.
130-
* @deprecated Use {@link #OperaDriver(OperaDriverService, OperaOptions)} instead.
131-
*/
132-
@Deprecated
133-
public OperaDriver(OperaDriverService service, Capabilities capabilities) {
134-
super(new DriverCommandExecutor(service), capabilities);
135-
locationContext = new RemoteLocationContext(getExecuteMethod());
136-
webStorage = new RemoteWebStorage(getExecuteMethod());
52+
public OperaDriver(OperaDriverService service) {
53+
this(service, new OperaOptions());
13754
}
13855

139-
@Override
140-
public void setFileDetector(FileDetector detector) {
141-
throw new WebDriverException(
142-
"Setting the file detector only works on remote webdriver instances obtained " +
143-
"via RemoteWebDriver");
56+
public OperaDriver(OperaDriverService service, OperaOptions options) {
57+
this(service, options, ClientConfig.defaultConfig());
14458
}
14559

146-
@Override
147-
public LocalStorage getLocalStorage() {
148-
return webStorage.getLocalStorage();
60+
public OperaDriver(OperaDriverService service, OperaOptions options, ClientConfig clientConfig) {
61+
super(generateExecutor(service, options, clientConfig), options, OperaOptions.CAPABILITY);
62+
casting = new AddHasCasting().getImplementation(getCapabilities(), getExecuteMethod());
63+
cdp = new AddHasCdp().getImplementation(getCapabilities(), getExecuteMethod());
14964
}
15065

151-
@Override
152-
public SessionStorage getSessionStorage() {
153-
return webStorage.getSessionStorage();
66+
private static OperaDriver.OperaDriverCommandExecutor generateExecutor(
67+
OperaDriverService service, OperaOptions options, ClientConfig clientConfig) {
68+
Require.nonNull("Driver service", service);
69+
Require.nonNull("Driver options", options);
70+
Require.nonNull("Driver clientConfig", clientConfig);
71+
DriverFinder finder = new DriverFinder(service, options);
72+
service.setExecutable(finder.getDriverPath());
73+
if (finder.hasBrowserPath()) {
74+
options.setBinary(finder.getBrowserPath());
75+
options.setCapability("browserVersion", (Object) null);
76+
}
77+
return new OperaDriver.OperaDriverCommandExecutor(service, clientConfig);
15478
}
15579

156-
@Override
157-
public Location location() {
158-
return locationContext.location();
80+
@Beta
81+
public static RemoteWebDriverBuilder builder() {
82+
return RemoteWebDriver.builder().oneOf(new OperaOptions());
15983
}
16084

161-
@Override
162-
public void setLocation(Location location) {
163-
locationContext.setLocation(location);
85+
private static class OperaDriverCommandExecutor extends ChromiumDriverCommandExecutor {
86+
public OperaDriverCommandExecutor(DriverService service, ClientConfig clientConfig) {
87+
super(service, getExtraCommands(), clientConfig);
88+
}
89+
90+
private static Map<String, CommandInfo> getExtraCommands() {
91+
return Stream.of(
92+
new AddHasCasting().getAdditionalCommands(), new AddHasCdp().getAdditionalCommands())
93+
.flatMap((m) -> m.entrySet().stream())
94+
.collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue));
95+
}
16496
}
16597
}

java/src/org/openqa/selenium/opera/OperaDriverInfo.java

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,17 @@
2323
import org.openqa.selenium.ImmutableCapabilities;
2424
import org.openqa.selenium.SessionNotCreatedException;
2525
import org.openqa.selenium.WebDriver;
26-
import org.openqa.selenium.WebDriverException;
2726
import org.openqa.selenium.WebDriverInfo;
27+
import org.openqa.selenium.chromium.ChromiumDriverInfo;
28+
import org.openqa.selenium.remote.service.DriverFinder;
2829

2930
import java.util.Optional;
3031

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

3435
@AutoService(WebDriverInfo.class)
35-
public class OperaDriverInfo implements WebDriverInfo {
36+
public class OperaDriverInfo extends ChromiumDriverInfo {
3637

3738
@Override
3839
public String getDisplayName() {
@@ -51,31 +52,33 @@ public boolean isSupporting(Capabilities capabilities) {
5152

5253
@Override
5354
public boolean isSupportingCdp() {
55+
return true;
56+
}
57+
58+
@Override
59+
public boolean isSupportingBiDi() {
5460
return false;
5561
}
5662

5763
@Override
5864
public boolean isAvailable() {
59-
try {
60-
OperaDriverService.createDefaultService();
61-
return true;
62-
} catch (IllegalStateException | WebDriverException e) {
63-
return false;
64-
}
65+
return new DriverFinder(OperaDriverService.createDefaultService(), getCanonicalCapabilities())
66+
.isAvailable();
6567
}
6668

6769
@Override
68-
public int getMaximumSimultaneousSessions() {
69-
return Runtime.getRuntime().availableProcessors();
70+
public boolean isPresent() {
71+
return new DriverFinder(OperaDriverService.createDefaultService(), getCanonicalCapabilities())
72+
.isPresent();
7073
}
7174

7275
@Override
7376
public Optional<WebDriver> createDriver(Capabilities capabilities)
74-
throws SessionNotCreatedException {
75-
if (!isAvailable()) {
77+
throws SessionNotCreatedException {
78+
if (!isAvailable() || !isSupporting(capabilities)) {
7679
return Optional.empty();
7780
}
7881

79-
return Optional.of(new OperaDriver(capabilities));
82+
return Optional.of(new OperaDriver(new OperaOptions().merge(capabilities)));
8083
}
8184
}

0 commit comments

Comments
 (0)