Skip to content

Commit 60fd55c

Browse files
committed
Extract capability filters for the protocol handshake
Introduces a `CapabilitiesFilter` class that can be implemented to extract a set of capabilities for a w3c payload.
1 parent 77048aa commit 60fd55c

File tree

10 files changed

+246
-66
lines changed

10 files changed

+246
-66
lines changed

java/client/src/org/openqa/selenium/remote/BUCK

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ java_library(name = 'remote-lib',
134134
':api',
135135
':capabilities',
136136
'//java/client/src/org/openqa/selenium/json:json',
137+
'//java/client/src/org/openqa/selenium/remote/session:session',
137138
'//third_party/java/httpcomponents:httpclient',
138139
'//third_party/java/httpcomponents:httpcore',
139140
],

java/client/src/org/openqa/selenium/remote/ProtocolHandshake.java

Lines changed: 38 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,6 @@
2222
import static com.google.common.net.HttpHeaders.CONTENT_TYPE;
2323
import static com.google.common.net.MediaType.JSON_UTF_8;
2424
import static org.openqa.selenium.json.Json.MAP_TYPE;
25-
import static org.openqa.selenium.remote.BrowserType.CHROME;
26-
import static org.openqa.selenium.remote.BrowserType.EDGE;
27-
import static org.openqa.selenium.remote.BrowserType.FIREFOX;
28-
import static org.openqa.selenium.remote.BrowserType.IE;
29-
import static org.openqa.selenium.remote.BrowserType.OPERA;
30-
import static org.openqa.selenium.remote.BrowserType.OPERA_BLINK;
31-
import static org.openqa.selenium.remote.BrowserType.SAFARI;
3225
import static org.openqa.selenium.remote.CapabilityType.PROXY;
3326

3427
import com.google.common.base.Preconditions;
@@ -46,6 +39,13 @@
4639
import org.openqa.selenium.remote.http.HttpMethod;
4740
import org.openqa.selenium.remote.http.HttpRequest;
4841
import org.openqa.selenium.remote.http.HttpResponse;
42+
import org.openqa.selenium.remote.session.CapabilitiesFilter;
43+
import org.openqa.selenium.remote.session.ChromeFilter;
44+
import org.openqa.selenium.remote.session.EdgeFilter;
45+
import org.openqa.selenium.remote.session.FirefoxFilter;
46+
import org.openqa.selenium.remote.session.InternetExplorerFilter;
47+
import org.openqa.selenium.remote.session.OperaFilter;
48+
import org.openqa.selenium.remote.session.SafariFilter;
4949

5050
import java.io.BufferedInputStream;
5151
import java.io.BufferedWriter;
@@ -56,7 +56,9 @@
5656
import java.util.Collection;
5757
import java.util.List;
5858
import java.util.Map;
59+
import java.util.Objects;
5960
import java.util.Optional;
61+
import java.util.ServiceLoader;
6062
import java.util.Set;
6163
import java.util.TreeMap;
6264
import java.util.function.Function;
@@ -89,12 +91,31 @@ public class ProtocolHandshake {
8991
.map(Pattern::asPredicate)
9092
.reduce(identity -> false, Predicate::or);
9193

94+
private final Set<CapabilitiesFilter> adapters;
95+
96+
public ProtocolHandshake() {
97+
ImmutableSet.Builder<CapabilitiesFilter> builder = ImmutableSet.builder();
98+
99+
ServiceLoader<CapabilitiesFilter> loader = ServiceLoader.load(CapabilitiesFilter.class);
100+
loader.forEach(builder::add);
101+
102+
builder
103+
.add(new ChromeFilter())
104+
.add(new EdgeFilter())
105+
.add(new FirefoxFilter())
106+
.add(new InternetExplorerFilter())
107+
.add(new OperaFilter())
108+
.add(new SafariFilter());
109+
110+
this.adapters = builder.build();
111+
}
112+
92113
public Result createSession(HttpClient client, Command command)
93114
throws IOException {
94115
Capabilities desired = (Capabilities) command.getParameters().get("desiredCapabilities");
95116
desired = desired == null ? new ImmutableCapabilities() : desired;
96117

97-
Map<String, ?> des = desired.asMap();
118+
@SuppressWarnings("unchecked") Map<String, Object> des = (Map<String, Object>) desired.asMap();
98119

99120
// We don't know how large the generated JSON is going to be. Spool it to disk, and then read
100121
// the file size, then stream it to the remote end. If we could be sure the remote end could
@@ -142,14 +163,14 @@ public Result createSession(HttpClient client, Command command)
142163

143164
private void streamJsonWireProtocolParameters(
144165
JsonOutput out,
145-
Map<String, ?> des) throws IOException {
166+
Map<String, Object> des) throws IOException {
146167
out.name("desiredCapabilities");
147168
out.write(des, MAP_TYPE);
148169
}
149170

150171
private void streamW3CProtocolParameters(
151172
JsonOutput out,
152-
Map<String, ?> des) throws IOException {
173+
Map<String, Object> des) throws IOException {
153174
// Technically we should be building up a combination of "alwaysMatch" and "firstMatch" options.
154175
// We're going to do a little processing to figure out what we might be able to do, and assume
155176
// that people don't really understand the difference between required and desired (which is
@@ -170,61 +191,12 @@ private void streamW3CProtocolParameters(
170191
// We can't use the constants defined in the classes because it would introduce circular
171192
// dependencies between the remote library and the implementations. Yay!
172193

173-
Map<String, ?> chrome = des.entrySet().stream()
174-
.filter(entry ->
175-
("browserName".equals(entry.getKey()) && CHROME.equals(entry.getValue())) ||
176-
"chromeOptions".equals(entry.getKey()))
177-
.distinct()
178-
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (left, right) -> right));
179-
180-
Map<String, ?> edge = des.entrySet().stream()
181-
.filter(entry -> ("browserName".equals(entry.getKey()) && EDGE.equals(entry.getValue())))
182-
.distinct()
183-
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (left, right) -> right));
184-
185-
Map<String, ?> firefox = des.entrySet().stream()
186-
.filter(entry ->
187-
("browserName".equals(entry.getKey()) && FIREFOX.equals(entry.getValue())) ||
188-
entry.getKey().startsWith("firefox_") ||
189-
entry.getKey().startsWith("moz:"))
190-
.distinct()
191-
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (left, right) -> right));
192-
193-
Map<String, ?> ie = des.entrySet().stream()
194-
.filter(entry ->
195-
("browserName".equals(entry.getKey()) && IE.equals(entry.getValue())) ||
196-
"browserAttachTimeout".equals(entry.getKey()) ||
197-
"enableElementCacheCleanup".equals(entry.getKey()) ||
198-
"enablePersistentHover".equals(entry.getKey()) ||
199-
"extractPath".equals(entry.getKey()) ||
200-
"host".equals(entry.getKey()) ||
201-
"ignoreZoomSetting".equals(entry.getKey()) ||
202-
"initialBrowserZoom".equals(entry.getKey()) ||
203-
"logFile".equals(entry.getKey()) ||
204-
"logLevel".equals(entry.getKey()) ||
205-
"requireWindowFocus".equals(entry.getKey()) ||
206-
"se:ieOptions".equals(entry.getKey()) ||
207-
"silent".equals(entry.getKey()) ||
208-
entry.getKey().startsWith("ie."))
209-
.distinct()
210-
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (left, right) -> right));
211-
212-
Map<String, ?> opera = des.entrySet().stream()
213-
.filter(entry ->
214-
("browserName".equals(entry.getKey()) && OPERA_BLINK.equals(entry.getValue())) ||
215-
("browserName".equals(entry.getKey()) && OPERA.equals(entry.getValue())) ||
216-
"operaOptions".equals(entry.getKey()))
217-
.distinct()
218-
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (left, right) -> right));
219-
220-
Map<String, ?> safari = des.entrySet().stream()
221-
.filter(entry ->
222-
("browserName".equals(entry.getKey()) && SAFARI.equals(entry.getValue())) ||
223-
"safari.options".equals(entry.getKey()))
224-
.distinct()
225-
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (left, right) -> right));
194+
ImmutableSet<Map<String, Object>> browserSpecific = adapters.stream()
195+
.map(adapter -> adapter.apply(des))
196+
.filter(Objects::nonNull)
197+
.collect(ImmutableSet.toImmutableSet());
226198

227-
Set<String> excludedKeys = Stream.of(chrome, edge, firefox, ie, opera, safari)
199+
Set<String> excludedKeys = browserSpecific.stream()
228200
.map(Map::keySet)
229201
.flatMap(Collection::stream)
230202
.distinct()
@@ -249,7 +221,7 @@ private void streamW3CProtocolParameters(
249221
}));
250222

251223
// Now, hopefully we're left with just the browser-specific pieces. Skip the empty ones.
252-
List<Map<String, Object>> firstMatch = Stream.of(chrome, edge, firefox, ie, opera, safari)
224+
List<Map<String, Object>> firstMatch = browserSpecific.stream()
253225
.map(map -> {
254226
TreeMap<String, Object> json = new TreeMap<>();
255227
for (Map.Entry<String, ?> entry : map.entrySet()) {
@@ -328,7 +300,7 @@ public Optional<Result> createSession(HttpClient client, InputStream newSessionB
328300

329301
private void streamGeckoDriver013Parameters(
330302
JsonOutput out,
331-
Map<String, ?> des) throws IOException {
303+
Map<String, Object> des) throws IOException {
332304
out.name("desiredCapabilities");
333305
out.write(des, MAP_TYPE);
334306
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# BUILD FILE SYNTAX: SKYLARK
2+
java_library(
3+
name = "session",
4+
srcs = glob(["*.java"]),
5+
deps = [
6+
"//java/client/src/org/openqa/selenium:core",
7+
"//third_party/java/guava:guava",
8+
],
9+
visibility = [
10+
"//java/client/src/org/openqa/selenium/remote:remote-lib",
11+
],
12+
)

java/client/src/org/openqa/selenium/remote/session/CapabilitiesFilter.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
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+
118
package org.openqa.selenium.remote.session;
219

320
import java.util.Map;

java/client/src/org/openqa/selenium/remote/session/ChromeFilter.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
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+
118
package org.openqa.selenium.remote.session;
219

320
import com.google.common.collect.ImmutableMap;

java/client/src/org/openqa/selenium/remote/session/EdgeFilter.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
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+
118
package org.openqa.selenium.remote.session;
219

320
import com.google.common.collect.ImmutableMap;

java/client/src/org/openqa/selenium/remote/session/FirefoxFilter.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
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+
118
package org.openqa.selenium.remote.session;
219

320
import com.google.common.collect.ImmutableMap;
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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.remote.session;
19+
20+
import com.google.common.collect.ImmutableMap;
21+
22+
import java.util.Map;
23+
import java.util.Objects;
24+
25+
public class InternetExplorerFilter implements CapabilitiesFilter {
26+
@Override
27+
public Map<String, Object> apply(Map<String, Object> unmodifiedCaps) {
28+
ImmutableMap<String, Object> caps = unmodifiedCaps.entrySet().parallelStream()
29+
.filter(entry ->
30+
("browserName".equals(entry.getKey()) && "internet explorer".equals(entry.getValue())) ||
31+
"browserAttachTimeout".equals(entry.getKey()) ||
32+
"enableElementCacheCleanup".equals(entry.getKey()) ||
33+
"enablePersistentHover".equals(entry.getKey()) ||
34+
"extractPath".equals(entry.getKey()) ||
35+
"host".equals(entry.getKey()) ||
36+
"ignoreZoomSetting".equals(entry.getKey()) ||
37+
"initialBrowserZoom".equals(entry.getKey()) ||
38+
"logFile".equals(entry.getKey()) ||
39+
"logLevel".equals(entry.getKey()) ||
40+
"requireWindowFocus".equals(entry.getKey()) ||
41+
"se:ieOptions".equals(entry.getKey()) ||
42+
"silent".equals(entry.getKey()) ||
43+
entry.getKey().startsWith("ie."))
44+
.distinct()
45+
.filter(entry -> Objects.nonNull(entry.getValue()))
46+
.collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue));
47+
48+
return caps.isEmpty() ? null : caps;
49+
}
50+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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.remote.session;
19+
20+
import com.google.common.collect.ImmutableMap;
21+
22+
import java.util.Map;
23+
import java.util.Objects;
24+
25+
public class OperaFilter implements CapabilitiesFilter {
26+
@Override
27+
public Map<String, Object> apply(Map<String, Object> unmodifiedCaps) {
28+
ImmutableMap<String, Object> caps = unmodifiedCaps.entrySet().parallelStream()
29+
.filter(entry ->
30+
("browserName".equals(entry.getKey()) && "opera".equals(entry.getValue())) ||
31+
("browserName".equals(entry.getKey()) && "operablink".equals(entry.getValue())) ||
32+
"operaOptions".equals(entry.getKey()))
33+
.distinct()
34+
.filter(entry -> Objects.nonNull(entry.getValue()))
35+
.collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue));
36+
37+
return caps.isEmpty() ? null : caps;
38+
}
39+
}

0 commit comments

Comments
 (0)