Skip to content

Commit cbebb7e

Browse files
authored
Merge branch 'trunk' into https
2 parents c9d4440 + 4b6af2e commit cbebb7e

File tree

19 files changed

+2855
-2416
lines changed

19 files changed

+2855
-2416
lines changed

java/src/org/openqa/selenium/bidi/script/RemoteValue.java

+47-28
Original file line numberDiff line numberDiff line change
@@ -193,38 +193,57 @@ private Map<String, Object> toJson() {
193193
}
194194

195195
private static Object deserializeValue(Object value, Type type) {
196-
197-
if (Type.ARRAY.equals(type) || Type.SET.equals(type)) {
198-
try (StringReader reader = new StringReader(JSON.toJson(value));
199-
JsonInput input = JSON.newInput(reader)) {
200-
value = input.read(new TypeToken<List<RemoteValue>>() {}.getType());
201-
}
202-
} else if (Type.MAP.equals(type) || Type.OBJECT.equals(type)) {
203-
List<List<Object>> result = (List<List<Object>>) value;
204-
Map<Object, RemoteValue> map = new HashMap<>();
205-
206-
for (List<Object> list : result) {
207-
Object key = list.get(0);
208-
if (!(key instanceof String)) {
209-
try (StringReader reader = new StringReader(JSON.toJson(key));
210-
JsonInput keyInput = JSON.newInput(reader)) {
211-
key = keyInput.read(RemoteValue.class);
196+
Object finalValue;
197+
198+
switch (type) {
199+
case ARRAY:
200+
case SET:
201+
try (StringReader reader = new StringReader(JSON.toJson(value));
202+
JsonInput input = JSON.newInput(reader)) {
203+
finalValue = input.read(new TypeToken<List<RemoteValue>>() {}.getType());
204+
}
205+
break;
206+
207+
case MAP:
208+
case OBJECT:
209+
List<List<Object>> result = (List<List<Object>>) value;
210+
Map<Object, RemoteValue> map = new HashMap<>();
211+
212+
for (List<Object> list : result) {
213+
Object key = list.get(0);
214+
if (!(key instanceof String)) {
215+
try (StringReader reader = new StringReader(JSON.toJson(key));
216+
JsonInput keyInput = JSON.newInput(reader)) {
217+
key = keyInput.read(RemoteValue.class);
218+
}
219+
}
220+
try (StringReader reader = new StringReader(JSON.toJson(list.get(1)));
221+
JsonInput valueInput = JSON.newInput(reader)) {
222+
RemoteValue value1 = valueInput.read(RemoteValue.class);
223+
map.put(key, value1);
212224
}
213225
}
214-
try (StringReader reader = new StringReader(JSON.toJson(list.get(1)));
215-
JsonInput valueInput = JSON.newInput(reader)) {
216-
RemoteValue value1 = valueInput.read(RemoteValue.class);
217-
map.put(key, value1);
226+
finalValue = map;
227+
break;
228+
229+
case REGULAR_EXPRESSION:
230+
try (StringReader reader = new StringReader(JSON.toJson(value));
231+
JsonInput input = JSON.newInput(reader)) {
232+
finalValue = input.read(RegExpValue.class);
218233
}
219-
}
220-
value = map;
221-
} else if (Type.REGULAR_EXPRESSION.equals(type)) {
222-
try (StringReader reader = new StringReader(JSON.toJson(value));
223-
JsonInput input = JSON.newInput(reader)) {
224-
value = input.read(RegExpValue.class);
225-
}
234+
break;
235+
236+
case WINDOW:
237+
try (StringReader reader = new StringReader(JSON.toJson(value));
238+
JsonInput input = JSON.newInput(reader)) {
239+
finalValue = input.read(WindowProxyProperties.class);
240+
}
241+
break;
242+
243+
default:
244+
finalValue = value;
226245
}
227246

228-
return value;
247+
return finalValue;
229248
}
230249
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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+
package org.openqa.selenium.bidi.script;
18+
19+
import org.openqa.selenium.json.JsonInput;
20+
21+
public class WindowProxyProperties {
22+
23+
private final String browsingContext;
24+
25+
private WindowProxyProperties(String browsingContext) {
26+
this.browsingContext = browsingContext;
27+
}
28+
29+
public static WindowProxyProperties fromJson(JsonInput input) {
30+
String browsingContext = null;
31+
32+
input.beginObject();
33+
while (input.hasNext()) {
34+
switch (input.nextName()) {
35+
case "context":
36+
browsingContext = input.read(String.class);
37+
break;
38+
39+
default:
40+
input.skipValue();
41+
break;
42+
}
43+
}
44+
45+
input.endObject();
46+
47+
return new WindowProxyProperties(browsingContext);
48+
}
49+
50+
public String getBrowsingContext() {
51+
return browsingContext;
52+
}
53+
}

java/src/org/openqa/selenium/grid/commands/Standalone.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ protected Handlers createHandlers(Config config) {
222222
httpHandler = combine(httpHandler, Route.get("/readyz").to(() -> readinessCheck));
223223
Node node = createNode(config, bus, distributor, combinedHandler);
224224

225-
return new Handlers(httpHandler, new ProxyNodeWebsockets(clientFactory, node));
225+
return new Handlers(httpHandler, new ProxyNodeWebsockets(clientFactory, node, subPath));
226226
}
227227

228228
@Override

java/src/org/openqa/selenium/grid/node/ProxyNodeWebsockets.java

+7-5
Original file line numberDiff line numberDiff line change
@@ -56,18 +56,20 @@ public class ProxyNodeWebsockets
5656
ImmutableSet.of("goog:chromeOptions", "moz:debuggerAddress", "ms:edgeOptions");
5757
private final HttpClient.Factory clientFactory;
5858
private final Node node;
59+
private final String gridSubPath;
5960

60-
public ProxyNodeWebsockets(HttpClient.Factory clientFactory, Node node) {
61+
public ProxyNodeWebsockets(HttpClient.Factory clientFactory, Node node, String gridSubPath) {
6162
this.clientFactory = Objects.requireNonNull(clientFactory);
6263
this.node = Objects.requireNonNull(node);
64+
this.gridSubPath = gridSubPath;
6365
}
6466

6567
@Override
6668
public Optional<Consumer<Message>> apply(String uri, Consumer<Message> downstream) {
67-
UrlTemplate.Match fwdMatch = FWD_TEMPLATE.match(uri);
68-
UrlTemplate.Match cdpMatch = CDP_TEMPLATE.match(uri);
69-
UrlTemplate.Match bidiMatch = BIDI_TEMPLATE.match(uri);
70-
UrlTemplate.Match vncMatch = VNC_TEMPLATE.match(uri);
69+
UrlTemplate.Match fwdMatch = FWD_TEMPLATE.match(uri, gridSubPath);
70+
UrlTemplate.Match cdpMatch = CDP_TEMPLATE.match(uri, gridSubPath);
71+
UrlTemplate.Match bidiMatch = BIDI_TEMPLATE.match(uri, gridSubPath);
72+
UrlTemplate.Match vncMatch = VNC_TEMPLATE.match(uri, gridSubPath);
7173

7274
if (bidiMatch == null && cdpMatch == null && vncMatch == null && fwdMatch == null) {
7375
return Optional.empty();

java/src/org/openqa/selenium/grid/node/config/NodeOptions.java

+15
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,21 @@ public boolean isManagedDownloadsEnabled() {
162162
return config.getBool(NODE_SECTION, "enable-managed-downloads").orElse(Boolean.FALSE);
163163
}
164164

165+
public String getGridSubPath() {
166+
return normalizeSubPath(getPublicGridUri().map(URI::getPath).orElse(""));
167+
}
168+
169+
public static String normalizeSubPath(String prefix) {
170+
prefix = prefix.trim();
171+
if (!prefix.startsWith("/")) {
172+
prefix = "/" + prefix; // Prefix with a '/' if absent.
173+
}
174+
if (prefix.endsWith("/")) {
175+
prefix = prefix.substring(0, prefix.length() - 1); // Remove the trailing '/' if present.
176+
}
177+
return prefix;
178+
}
179+
165180
public Node getNode() {
166181
return config.getClass(NODE_SECTION, "implementation", Node.class, DEFAULT_NODE_IMPLEMENTATION);
167182
}

java/src/org/openqa/selenium/grid/node/httpd/NodeServer.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,8 @@ protected Handlers createHandlers(Config config) {
171171

172172
Route httpHandler = Route.combine(node, get("/readyz").to(() -> readinessCheck));
173173

174-
return new Handlers(httpHandler, new ProxyNodeWebsockets(clientFactory, node));
174+
return new Handlers(
175+
httpHandler, new ProxyNodeWebsockets(clientFactory, node, nodeOptions.getGridSubPath()));
175176
}
176177

177178
@Override

java/src/org/openqa/selenium/grid/node/local/LocalNode.java

+1-3
Original file line numberDiff line numberDiff line change
@@ -838,9 +838,7 @@ private Session createExternalSession(
838838
private URI rewrite(String path) {
839839
try {
840840
String scheme = "https".equals(gridUri.getScheme()) ? "wss" : "ws";
841-
if (gridUri.getPath() != null && !gridUri.getPath().equals("/")) {
842-
path = gridUri.getPath() + path;
843-
}
841+
path = NodeOptions.normalizeSubPath(gridUri.getPath()) + path;
844842
return new URI(
845843
scheme, gridUri.getUserInfo(), gridUri.getHost(), gridUri.getPort(), path, null, null);
846844
} catch (URISyntaxException e) {

java/src/org/openqa/selenium/remote/http/UrlTemplate.java

+14
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,20 @@ public UrlTemplate.Match match(String matchAgainst) {
139139
return compiled.apply(matchAgainst);
140140
}
141141

142+
/**
143+
* @return A {@link Match} with all parameters filled if successful, null otherwise. Remove
144+
* subPath from matchAgainst before matching.
145+
*/
146+
public UrlTemplate.Match match(String matchAgainst, String prefix) {
147+
if (matchAgainst == null || prefix == null) {
148+
return null;
149+
}
150+
if (!prefix.isEmpty() && !prefix.equals("/")) {
151+
matchAgainst = matchAgainst.replaceFirst(prefix, "");
152+
}
153+
return match(matchAgainst);
154+
}
155+
142156
@SuppressWarnings("InnerClassMayBeStatic")
143157
public class Match {
144158
private final String url;

java/test/org/openqa/selenium/bidi/script/ScriptCommandsTest.java

+38
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.junit.jupiter.api.AfterEach;
3939
import org.junit.jupiter.api.BeforeEach;
4040
import org.junit.jupiter.api.Test;
41+
import org.openqa.selenium.By;
4142
import org.openqa.selenium.WebDriverException;
4243
import org.openqa.selenium.WindowType;
4344
import org.openqa.selenium.bidi.LogInspector;
@@ -114,6 +115,43 @@ void canCallFunctionWithArguments() {
114115
assertThat(((List<Object>) successResult.getResult().getValue().get()).size()).isEqualTo(2);
115116
}
116117

118+
@Test
119+
@NotYetImplemented(SAFARI)
120+
@NotYetImplemented(IE)
121+
@NotYetImplemented(EDGE)
122+
@NotYetImplemented(CHROME)
123+
void canCallFunctionToGetIFrameBrowsingContext() {
124+
String url = appServer.whereIs("click_too_big_in_frame.html");
125+
driver.get(url);
126+
127+
driver.findElement(By.id("iframe1"));
128+
129+
String id = driver.getWindowHandle();
130+
Script script = new Script(id, driver);
131+
132+
List<LocalValue> arguments = new ArrayList<>();
133+
134+
EvaluateResult result =
135+
script.callFunctionInBrowsingContext(
136+
id,
137+
"() => document.querySelector('iframe[id=\"iframe1\"]').contentWindow",
138+
false,
139+
Optional.of(arguments),
140+
Optional.empty(),
141+
Optional.empty());
142+
143+
assertThat(result.getResultType()).isEqualTo(EvaluateResult.Type.SUCCESS);
144+
assertThat(result.getRealmId()).isNotNull();
145+
146+
EvaluateResultSuccess successResult = (EvaluateResultSuccess) result;
147+
assertThat(successResult.getResult().getType()).isEqualTo("window");
148+
assertThat(successResult.getResult().getValue().isPresent()).isTrue();
149+
assertThat(
150+
((WindowProxyProperties) successResult.getResult().getValue().get())
151+
.getBrowsingContext())
152+
.isNotNull();
153+
}
154+
117155
@Test
118156
@NotYetImplemented(SAFARI)
119157
@NotYetImplemented(IE)

java/test/org/openqa/selenium/grid/node/config/NodeOptionsTest.java

+22
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,28 @@ void settingTheHubWithDefaultValueSetsTheGridUrlToTheNonLoopbackAddress() {
673673
.isEqualTo(Optional.of(URI.create(nonLoopbackAddressUrl)));
674674
}
675675

676+
@Test
677+
void settingSubPathForNodeServerExtractFromGridUrl() {
678+
String[] rawConfig =
679+
new String[] {
680+
"[node]", "grid-url = \"http://localhost:4444/mySubPath\"",
681+
};
682+
Config config = new TomlConfig(new StringReader(String.join("\n", rawConfig)));
683+
NodeOptions nodeOptions = new NodeOptions(config);
684+
assertThat(nodeOptions.getGridSubPath()).isEqualTo("/mySubPath");
685+
}
686+
687+
@Test
688+
void settingSubPathForNodeServerExtractFromHub() {
689+
String[] rawConfig =
690+
new String[] {
691+
"[node]", "hub = \"http://0.0.0.0:4444/mySubPath\"",
692+
};
693+
Config config = new TomlConfig(new StringReader(String.join("\n", rawConfig)));
694+
NodeOptions nodeOptions = new NodeOptions(config);
695+
assertThat(nodeOptions.getGridSubPath()).isEqualTo("/mySubPath");
696+
}
697+
676698
@Test
677699
void notSettingSlotMatcherAvailable() {
678700
String[] rawConfig =

java/test/org/openqa/selenium/remote/http/UrlTemplateTest.java

+18
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,24 @@ void itIsFineForTheFirstCharacterToBeAPattern() {
6767
assertThat(match.getParameters()).isEqualTo(ImmutableMap.of("cake", "cheese"));
6868
}
6969

70+
@Test
71+
void shouldMatchAgainstUrlTemplateExcludePrefix() {
72+
UrlTemplate.Match match =
73+
new UrlTemplate("/session/{id}/se/vnc").match("/prefix/session/1234/se/vnc", "/prefix");
74+
75+
assertThat(match.getUrl()).isEqualTo("/session/1234/se/vnc");
76+
assertThat(match.getParameters()).isEqualTo(ImmutableMap.of("id", "1234"));
77+
}
78+
79+
@Test
80+
void shouldMatchAgainstUrlTemplateWithEmptyPrefix() {
81+
UrlTemplate.Match match =
82+
new UrlTemplate("/session/{id}/se/vnc").match("/session/1234/se/vnc", "");
83+
84+
assertThat(match.getUrl()).isEqualTo("/session/1234/se/vnc");
85+
assertThat(match.getParameters()).isEqualTo(ImmutableMap.of("id", "1234"));
86+
}
87+
7088
@Test
7189
void aNullMatchDoesNotCauseANullPointerExceptionToBeThrown() {
7290
assertThat(new UrlTemplate("/").match(null)).isNull();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
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+
'use strict'
19+
20+
const assert = require('assert')
21+
const firefox = require('../../firefox')
22+
const {Browser} = require('../../')
23+
const {suite} = require('../../lib/test')
24+
25+
suite(
26+
function (env) {
27+
let driver
28+
29+
beforeEach(async function () {
30+
driver = await env
31+
.builder()
32+
.setFirefoxOptions(new firefox.Options().enableBidi())
33+
.build()
34+
})
35+
36+
afterEach(async function () {
37+
await driver.quit()
38+
})
39+
40+
describe('Session', function () {
41+
it('can create bidi session', async function () {
42+
const bidi = await driver.getBidi()
43+
const status = await bidi.status
44+
45+
assert('ready' in status['result'])
46+
assert.notEqual(status['result']['message'], null)
47+
})
48+
})
49+
},
50+
{browsers: [Browser.FIREFOX]}
51+
)

0 commit comments

Comments
 (0)