Skip to content

Commit d96520b

Browse files
committed
Merge branch 'trunk' into https
Signed-off-by: Viet Nguyen Duc <[email protected]>
2 parents c9d4440 + 4b6af2e commit d96520b

26 files changed

+2868
-2422
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/distributor/config/BUILD.bazel

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ java_library(
1313
"//java/src/org/openqa/selenium/grid/data",
1414
"//java/src/org/openqa/selenium/grid/distributor",
1515
"//java/src/org/openqa/selenium/grid/distributor/selector",
16+
"//java/src/org/openqa/selenium/grid/server",
1617
artifact("com.beust:jcommander"),
1718
],
1819
)

java/src/org/openqa/selenium/grid/distributor/config/DistributorOptions.java

-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ public class DistributorOptions {
3232

3333
public static final int DEFAULT_HEALTHCHECK_INTERVAL = 120;
3434
public static final String DISTRIBUTOR_SECTION = "distributor";
35-
private static final String SERVER_SECTION = "server";
3635
static final String DEFAULT_DISTRIBUTOR_IMPLEMENTATION =
3736
"org.openqa.selenium.grid.distributor.local.LocalDistributor";
3837
static final String DEFAULT_SLOT_MATCHER = "org.openqa.selenium.grid.data.DefaultSlotMatcher";

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/grid/security/SecretOptions.java

+10-3
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
import org.openqa.selenium.UsernameAndPassword;
2828
import org.openqa.selenium.grid.config.Config;
2929
import org.openqa.selenium.grid.config.ConfigException;
30-
import org.openqa.selenium.grid.server.BaseServerOptions;
3130

3231
public class SecretOptions {
3332

@@ -42,8 +41,7 @@ public SecretOptions(Config config) {
4241

4342
public Secret getRegistrationSecret() {
4443
String secret = "";
45-
BaseServerOptions serverOptions = new BaseServerOptions(config);
46-
if ((serverOptions.isSecure() || serverOptions.isSelfSigned())
44+
if ((isSecure() || isSelfSigned())
4745
&& !config.get(SERVER_SECTION, "registration-secret").isPresent()) {
4846
try {
4947
secret =
@@ -72,6 +70,15 @@ public UsernameAndPassword getServerAuthentication() {
7270
return new UsernameAndPassword(username.get(), password.get());
7371
}
7472

73+
private boolean isSecure() {
74+
return config.get(SERVER_SECTION, "https-private-key").isPresent()
75+
&& config.get(SERVER_SECTION, "https-certificate").isPresent();
76+
}
77+
78+
private boolean isSelfSigned() {
79+
return config.getBool(SERVER_SECTION, "https-self-signed").orElse(false);
80+
}
81+
7582
private File getCertificate() {
7683
String certificatePath = config.get(SERVER_SECTION, "https-certificate").orElse(null);
7784
if (certificatePath != null) {

java/src/org/openqa/selenium/grid/sessionmap/config/BUILD.bazel

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ java_library(
1010
deps = [
1111
"//java:auto-service",
1212
"//java/src/org/openqa/selenium/grid/config",
13+
"//java/src/org/openqa/selenium/grid/server",
1314
"//java/src/org/openqa/selenium/grid/sessionmap",
1415
artifact("com.beust:jcommander"),
1516
],

java/src/org/openqa/selenium/grid/sessionmap/config/SessionMapOptions.java

-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
public class SessionMapOptions {
2929

3030
private static final String SESSIONS_SECTION = "sessions";
31-
private static final String SERVER_SECTION = "server";
3231

3332
private static final String DEFAULT_SESSION_MAP =
3433
"org.openqa.selenium.grid.sessionmap.remote.RemoteSessionMap";

java/src/org/openqa/selenium/grid/sessionqueue/config/BUILD.bazel

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ java_library(
1212
"//java:auto-service",
1313
"//java/src/org/openqa/selenium/grid/config",
1414
"//java/src/org/openqa/selenium/grid/jmx",
15+
"//java/src/org/openqa/selenium/grid/server",
1516
"//java/src/org/openqa/selenium/grid/sessionqueue",
1617
artifact("com.beust:jcommander"),
1718
],

java/src/org/openqa/selenium/grid/sessionqueue/config/NewSessionQueueOptions.java

-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
public class NewSessionQueueOptions {
3636

3737
static final String SESSION_QUEUE_SECTION = "sessionqueue";
38-
private static final String SERVER_SECTION = "server";
3938
static final int DEFAULT_REQUEST_TIMEOUT = 300;
4039
static final int DEFAULT_REQUEST_TIMEOUT_PERIOD = 10;
4140
static final int DEFAULT_RETRY_INTERVAL = 15;

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)

0 commit comments

Comments
 (0)