Skip to content

Commit 4f076e0

Browse files
pujaganidiemol
andauthored
[java][bidi] Add browsing context events support (#11759)
Co-authored-by: Diego Molina <[email protected]>
1 parent 9f5801c commit 4f076e0

File tree

6 files changed

+664
-0
lines changed

6 files changed

+664
-0
lines changed
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
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.bidi;
19+
20+
import org.openqa.selenium.WebDriver;
21+
import org.openqa.selenium.bidi.browsingcontext.BrowsingContextInfo;
22+
import org.openqa.selenium.bidi.browsingcontext.NavigationInfo;
23+
import org.openqa.selenium.bidi.browsingcontext.UserPromptClosed;
24+
import org.openqa.selenium.bidi.browsingcontext.UserPromptOpened;
25+
import org.openqa.selenium.internal.Require;
26+
import org.openqa.selenium.json.Json;
27+
import org.openqa.selenium.json.JsonInput;
28+
29+
import java.io.StringReader;
30+
import java.util.Collections;
31+
import java.util.HashSet;
32+
import java.util.Map;
33+
import java.util.Set;
34+
import java.util.function.Consumer;
35+
import java.util.function.Function;
36+
37+
public class BrowsingContextInspector implements AutoCloseable {
38+
39+
private static final Json JSON = new Json();
40+
41+
private final Set<String> browsingContextIds;
42+
43+
private final BiDi bidi;
44+
45+
private final Function<Map<String, Object>, BrowsingContextInfo> browsingContextInfoMapper =
46+
params -> {
47+
try (
48+
StringReader reader = new StringReader(JSON.toJson(params));
49+
JsonInput input = JSON.newInput(reader)) {
50+
return input.read(BrowsingContextInfo.class);
51+
}
52+
};
53+
54+
private final Function<Map<String, Object>, NavigationInfo> navigationInfoMapper =
55+
params -> {
56+
try (
57+
StringReader reader = new StringReader(JSON.toJson(params));
58+
JsonInput input = JSON.newInput(reader)) {
59+
return input.read(NavigationInfo.class);
60+
}
61+
};
62+
63+
private final Event<BrowsingContextInfo> browsingContextCreated =
64+
new Event<>("browsingContext.contextCreated", browsingContextInfoMapper);
65+
66+
private final Event<BrowsingContextInfo> browsingContextDestroyed =
67+
new Event<>("browsingContext.contextDestroyed", browsingContextInfoMapper);
68+
69+
private final Event<UserPromptClosed> userPromptClosed =
70+
new Event<>("browsingContext.userPromptClosed", params -> {
71+
try (
72+
StringReader reader = new StringReader(JSON.toJson(params));
73+
JsonInput input = JSON.newInput(reader)) {
74+
return input.read(UserPromptClosed.class);
75+
}
76+
});
77+
78+
private final Set<Event<NavigationInfo>> navigationEventSet = new HashSet<>();
79+
80+
private final Event<UserPromptOpened> userPromptOpened =
81+
new Event<>("browsingContext.userPromptOpened", params -> {
82+
try (
83+
StringReader reader = new StringReader(JSON.toJson(params));
84+
JsonInput input = JSON.newInput(reader)) {
85+
return input.read(UserPromptOpened.class);
86+
}
87+
});
88+
89+
public BrowsingContextInspector(WebDriver driver) {
90+
this(new HashSet<>(), driver);
91+
}
92+
93+
public BrowsingContextInspector(String browsingContextId, WebDriver driver) {
94+
this(Collections.singleton(Require.nonNull("Browsing context id", browsingContextId)), driver);
95+
}
96+
97+
public BrowsingContextInspector(Set<String> browsingContextIds, WebDriver driver) {
98+
Require.nonNull("WebDriver", driver);
99+
Require.nonNull("Browsing context id list", browsingContextIds);
100+
101+
if (!(driver instanceof HasBiDi)) {
102+
throw new IllegalArgumentException("WebDriver instance must support BiDi protocol");
103+
}
104+
105+
this.bidi = ((HasBiDi) driver).getBiDi();
106+
this.browsingContextIds = browsingContextIds;
107+
}
108+
109+
public void onBrowsingContextCreated(Consumer<BrowsingContextInfo> consumer) {
110+
if (browsingContextIds.isEmpty()) {
111+
this.bidi.addListener(browsingContextCreated, consumer);
112+
} else {
113+
this.bidi.addListener(browsingContextIds, browsingContextCreated, consumer);
114+
}
115+
}
116+
117+
private void onBrowsingContextDestroyed(Consumer<BrowsingContextInfo> consumer) {
118+
if (browsingContextIds.isEmpty()) {
119+
this.bidi.addListener(browsingContextDestroyed, consumer);
120+
} else {
121+
this.bidi.addListener(browsingContextIds, browsingContextDestroyed, consumer);
122+
}
123+
}
124+
125+
private void onNavigationStarted(Consumer<NavigationInfo> consumer) {
126+
addNavigationEventListener("browsingContext.navigationStarted", consumer);
127+
}
128+
129+
private void onFragmentNavigated(Consumer<NavigationInfo> consumer) {
130+
addNavigationEventListener("browsingContext.fragmentNavigated", consumer);
131+
}
132+
133+
public void onDomContentLoaded(Consumer<NavigationInfo> consumer) {
134+
addNavigationEventListener("browsingContext.domContentLoaded", consumer);
135+
}
136+
137+
public void onBrowsingContextLoaded(Consumer<NavigationInfo> consumer) {
138+
addNavigationEventListener("browsingContext.load", consumer);
139+
}
140+
141+
private void onDownloadWillBegin(Consumer<NavigationInfo> consumer) {
142+
addNavigationEventListener("browsingContext.downloadWillBegin", consumer);
143+
}
144+
145+
private void onNavigationAborted(Consumer<NavigationInfo> consumer) {
146+
addNavigationEventListener("browsingContext.navigationAborted", consumer);
147+
}
148+
149+
private void onNavigationFailed(Consumer<NavigationInfo> consumer) {
150+
addNavigationEventListener("browsingContext.navigationFailed", consumer);
151+
}
152+
153+
private void onUserPromptClosed(Consumer<UserPromptClosed> consumer) {
154+
if (browsingContextIds.isEmpty()) {
155+
this.bidi.addListener(userPromptClosed, consumer);
156+
} else {
157+
this.bidi.addListener(browsingContextIds, userPromptClosed, consumer);
158+
}
159+
}
160+
161+
private void onUserPromptOpened(Consumer<UserPromptOpened> consumer) {
162+
if (browsingContextIds.isEmpty()) {
163+
this.bidi.addListener(userPromptOpened, consumer);
164+
} else {
165+
this.bidi.addListener(browsingContextIds, userPromptOpened, consumer);
166+
}
167+
}
168+
169+
private void addNavigationEventListener(String name, Consumer<NavigationInfo> consumer) {
170+
Event<NavigationInfo> navigationEvent = new Event<>(name, navigationInfoMapper);
171+
172+
navigationEventSet.add(navigationEvent);
173+
174+
if (browsingContextIds.isEmpty()) {
175+
this.bidi.addListener(navigationEvent, consumer);
176+
} else {
177+
this.bidi.addListener(browsingContextIds, navigationEvent, consumer);
178+
}
179+
}
180+
181+
@Override
182+
public void close() {
183+
this.bidi.clearListener(browsingContextCreated);
184+
this.bidi.clearListener(browsingContextDestroyed);
185+
this.bidi.clearListener(userPromptOpened);
186+
this.bidi.clearListener(userPromptClosed);
187+
188+
navigationEventSet.forEach(this.bidi::clearListener);
189+
}
190+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
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.bidi.browsingcontext;
19+
20+
import static java.util.Collections.unmodifiableMap;
21+
22+
import org.openqa.selenium.json.JsonInput;
23+
24+
import java.util.Map;
25+
import java.util.TreeMap;
26+
27+
public class NavigationInfo {
28+
29+
private final String browsingContextId;
30+
31+
private final String navigationId;
32+
33+
private final long timestamp;
34+
35+
private final String url;
36+
37+
private NavigationInfo(String browsingContextId, String navigationId, long timestamp, String url) {
38+
this.browsingContextId = browsingContextId;
39+
this.navigationId = navigationId;
40+
this.timestamp = timestamp;
41+
this.url = url;
42+
}
43+
44+
public static NavigationInfo fromJson(JsonInput input) {
45+
String browsingContextId = null;
46+
String navigationId = null;
47+
long timestamp = 0;
48+
String url = null;
49+
50+
input.beginObject();
51+
while (input.hasNext()) {
52+
switch (input.nextName()) {
53+
case "context":
54+
browsingContextId = input.read(String.class);
55+
break;
56+
57+
case "navigation":
58+
navigationId = input.read(String.class);
59+
break;
60+
61+
case "timestamp":
62+
timestamp = input.read(Long.class);
63+
break;
64+
65+
case "url":
66+
url = input.read(String.class);
67+
break;
68+
69+
default:
70+
input.skipValue();
71+
break;
72+
}
73+
}
74+
75+
input.endObject();
76+
77+
return new NavigationInfo(browsingContextId, navigationId, timestamp, url);
78+
}
79+
80+
public String getBrowsingContextId() {
81+
return browsingContextId;
82+
}
83+
84+
public String getNavigationId() {
85+
return navigationId;
86+
}
87+
88+
public long getTimestamp() {
89+
return timestamp;
90+
}
91+
92+
public String getUrl() {
93+
return url;
94+
}
95+
96+
private Map<String, Object> toJson() {
97+
Map<String, Object> toReturn = new TreeMap<>();
98+
99+
toReturn.put("browsingContextId", this.getBrowsingContextId());
100+
toReturn.put("navigationId", this.getNavigationId());
101+
toReturn.put("timestamp", this.getTimestamp());
102+
toReturn.put("url", this.getUrl());
103+
104+
return unmodifiableMap(toReturn);
105+
}
106+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
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.bidi.browsingcontext;
19+
20+
import static java.util.Collections.unmodifiableMap;
21+
22+
import org.openqa.selenium.json.JsonInput;
23+
24+
import java.util.Map;
25+
import java.util.Optional;
26+
import java.util.TreeMap;
27+
28+
public class UserPromptClosed {
29+
30+
private final String browsingContextId;
31+
32+
private final boolean accepted;
33+
34+
private final Optional<String> userText;
35+
36+
private UserPromptClosed(String browsingContextId, boolean accepted, Optional<String> userText) {
37+
this.browsingContextId = browsingContextId;
38+
this.accepted = accepted;
39+
this.userText = userText;
40+
}
41+
42+
43+
public static UserPromptClosed fromJson(JsonInput input) {
44+
String browsingContextId = null;
45+
boolean accepted = false;
46+
Optional<String> userText = Optional.empty();
47+
48+
input.beginObject();
49+
while (input.hasNext()) {
50+
switch (input.nextName()) {
51+
case "context":
52+
browsingContextId = input.read(String.class);
53+
break;
54+
55+
case "accepted":
56+
accepted = input.read(boolean.class);
57+
break;
58+
59+
case "userText":
60+
userText = Optional.ofNullable(input.read(String.class));
61+
break;
62+
63+
default:
64+
input.skipValue();
65+
break;
66+
}
67+
}
68+
69+
input.endObject();
70+
71+
return new UserPromptClosed(browsingContextId, accepted, userText);
72+
}
73+
74+
public String getBrowsingContextId() {
75+
return browsingContextId;
76+
}
77+
78+
public boolean getAccepted() {
79+
return accepted;
80+
}
81+
82+
public Optional<String> getUserText() {
83+
return userText;
84+
}
85+
86+
private Map<String, Object> toJson() {
87+
Map<String, Object> toReturn = new TreeMap<>();
88+
89+
toReturn.put("browsingContextId", this.getBrowsingContextId());
90+
toReturn.put("accepted", this.getAccepted());
91+
toReturn.put("userText", this.getUserText());
92+
93+
return unmodifiableMap(toReturn);
94+
}
95+
}

0 commit comments

Comments
 (0)