0
# JavaScript Bindings and Script Evaluation
1
2
The JavaScript domain provides capabilities for executing JavaScript code, creating custom bindings between browser JavaScript and Java code, and managing script injection into pages.
3
4
## Capabilities
5
6
### V101Javascript
7
8
Main JavaScript handler that extends the base Javascript class with version-specific CDP implementations.
9
10
```java { .api }
11
/**
12
* Manages JavaScript bindings and script evaluation for CDP version 101
13
* Extends Javascript with ScriptIdentifier and BindingCalled event types
14
*/
15
public class V101Javascript extends Javascript<ScriptIdentifier, BindingCalled> {
16
17
/**
18
* Creates a new JavaScript handler instance
19
* @param devtools DevTools instance for CDP communication
20
*/
21
public V101Javascript(DevTools devtools);
22
}
23
```
24
25
**Inherited Methods from Javascript Base Class:**
26
27
```java { .api }
28
/**
29
* Pin a script to be evaluated on every new document load
30
* @param exposeScriptAs Name to expose the script result as
31
* @param script JavaScript code to execute
32
* @return ScriptId that can be used to remove the script later
33
*/
34
public ScriptId pin(String exposeScriptAs, String script);
35
36
/**
37
* Add a listener for JavaScript binding calls from the browser
38
* @param listener Consumer that receives the payload string from binding calls
39
*/
40
public void addBindingCalledListener(Consumer<String> listener);
41
42
/**
43
* Add a JavaScript binding that allows browser code to call Java
44
* @param scriptName Name of the binding function to create in browser
45
*/
46
public void addJsBinding(String scriptName);
47
48
/**
49
* Remove a previously added JavaScript binding
50
* @param scriptName Name of the binding function to remove
51
*/
52
public void removeJsBinding(String scriptName);
53
54
/**
55
* Disable the JavaScript domain and clean up all bindings and listeners
56
*/
57
public void disable();
58
```
59
60
**Usage Examples:**
61
62
```java
63
import org.openqa.selenium.devtools.v101.V101Javascript;
64
65
// Create JavaScript handler
66
V101Javascript javascript = new V101Javascript(devTools);
67
68
// Add a custom binding
69
javascript.addJsBinding("sendDataToJava");
70
71
// Listen for binding calls
72
javascript.addBindingCalledListener(payload -> {
73
System.out.println("Received from browser: " + payload);
74
// Process the data sent from browser JavaScript
75
processDataFromBrowser(payload);
76
});
77
78
// Pin a script to run on every page load
79
ScriptId scriptId = javascript.pin("myUtility",
80
"window.myUtility = { " +
81
" sendMessage: function(msg) { " +
82
" sendDataToJava(JSON.stringify({type: 'message', data: msg})); " +
83
" } " +
84
"};"
85
);
86
87
// Navigate to a page
88
driver.get("https://example.com");
89
90
// The pinned script is now available on the page
91
driver.executeScript("myUtility.sendMessage('Hello from browser!');");
92
93
// Clean up when done
94
javascript.disable();
95
```
96
97
### ScriptId
98
99
Wrapper for script identifiers returned when pinning scripts to pages.
100
101
```java { .api }
102
/**
103
* Represents a script identifier for pinned scripts
104
* Used to manage and remove scripts that are evaluated on new documents
105
*/
106
public class ScriptId {
107
108
/**
109
* Get the underlying script identifier
110
* @return The actual script identifier object from CDP
111
*/
112
public Object getActualId();
113
}
114
```
115
116
**Usage Example:**
117
118
```java
119
// Pin multiple scripts and manage them
120
List<ScriptId> pinnedScripts = new ArrayList<>();
121
122
// Add utility functions
123
ScriptId utilityScript = javascript.pin("utils",
124
"window.utils = { " +
125
" logMessage: function(msg) { console.log('Utils:', msg); }, " +
126
" sendEvent: function(event, data) { " +
127
" myEventHandler(JSON.stringify({event: event, data: data})); " +
128
" } " +
129
"};"
130
);
131
pinnedScripts.add(utilityScript);
132
133
// Add monitoring script
134
ScriptId monitorScript = javascript.pin("monitor",
135
"window.addEventListener('error', function(e) { " +
136
" errorHandler(JSON.stringify({" +
137
" message: e.message, " +
138
" filename: e.filename, " +
139
" lineno: e.lineno " +
140
" })); " +
141
"});"
142
);
143
pinnedScripts.add(monitorScript);
144
145
// Scripts are now available on all new pages
146
// Remove specific scripts if needed (implementation would require CDP commands)
147
```
148
149
### JavaScript Binding Communication
150
151
**Setting up Two-way Communication:**
152
153
```java
154
// Set up bidirectional communication between Java and browser JavaScript
155
156
// 1. Add Java-to-browser functions (via executeScript)
157
// 2. Add browser-to-Java bindings
158
javascript.addJsBinding("sendToJava");
159
javascript.addJsBinding("reportError");
160
javascript.addJsBinding("requestData");
161
162
// Handle different types of messages from browser
163
javascript.addBindingCalledListener(payload -> {
164
try {
165
JsonObject message = JsonParser.parseString(payload).getAsJsonObject();
166
String type = message.get("type").getAsString();
167
168
switch (type) {
169
case "data":
170
handleDataFromBrowser(message.get("data"));
171
break;
172
case "error":
173
handleErrorFromBrowser(message.get("error"));
174
break;
175
case "request":
176
handleRequestFromBrowser(message.get("request"));
177
break;
178
default:
179
System.out.println("Unknown message type: " + type);
180
}
181
} catch (Exception e) {
182
System.err.println("Error parsing browser message: " + e.getMessage());
183
}
184
});
185
186
// Pin helper script for structured communication
187
javascript.pin("bridge",
188
"window.bridge = { " +
189
" sendData: function(data) { " +
190
" sendToJava(JSON.stringify({type: 'data', data: data})); " +
191
" }, " +
192
" reportError: function(error) { " +
193
" reportError(JSON.stringify({type: 'error', error: error})); " +
194
" }, " +
195
" requestData: function(requestId, params) { " +
196
" requestData(JSON.stringify({type: 'request', id: requestId, params: params})); " +
197
" } " +
198
"};"
199
);
200
```
201
202
### CDP Protocol Types
203
204
The underlying CDP protocol types used by the JavaScript domain:
205
206
```java { .api }
207
/**
208
* CDP Page.ScriptIdentifier for pinned scripts
209
* Raw identifier from the Chrome DevTools Protocol
210
*/
211
public class ScriptIdentifier {
212
public String getId();
213
}
214
215
/**
216
* CDP Runtime.bindingCalled event data
217
* Raw event data when a JavaScript binding is called
218
*/
219
public class BindingCalled {
220
public String getName();
221
public String getPayload();
222
public Optional<ExecutionContextId> getExecutionContextId();
223
}
224
225
/**
226
* CDP Runtime.addBinding command parameters
227
*/
228
public static Command<Void> addBinding(
229
String name,
230
Optional<String> executionContextName,
231
Optional<Integer> executionContextId
232
);
233
234
/**
235
* CDP Runtime.removeBinding command
236
*/
237
public static Command<Void> removeBinding(String name);
238
239
/**
240
* CDP Page.addScriptToEvaluateOnNewDocument command
241
*/
242
public static Command<ScriptIdentifier> addScriptToEvaluateOnNewDocument(
243
String source,
244
Optional<String> worldName,
245
Optional<Boolean> includeCommandLineAPI
246
);
247
248
/**
249
* CDP Page.removeScriptToEvaluateOnNewDocument command
250
*/
251
public static Command<Void> removeScriptToEvaluateOnNewDocument(ScriptIdentifier identifier);
252
```
253
254
### Runtime and Page Commands
255
256
The V101Javascript class internally uses these CDP commands:
257
258
```java { .api }
259
// Runtime domain commands for bindings
260
public static Command<Void> Runtime.enable();
261
public static Command<Void> Runtime.disable();
262
public static Command<Void> Runtime.addBinding(String name, Optional<String> executionContextName, Optional<Integer> executionContextId);
263
public static Command<Void> Runtime.removeBinding(String name);
264
public static Event<BindingCalled> Runtime.bindingCalled();
265
266
// Page domain commands for script injection
267
public static Command<Void> Page.enable();
268
public static Command<Void> Page.disable();
269
public static Command<ScriptIdentifier> Page.addScriptToEvaluateOnNewDocument(String script, Optional<String> worldName, Optional<Boolean> includeCommandLineAPI);
270
public static Command<Void> Page.removeScriptToEvaluateOnNewDocument(ScriptIdentifier identifier);
271
```
272
273
## Advanced Usage Patterns
274
275
### Script Injection Patterns
276
277
```java
278
// Pattern 1: Utility functions available on all pages
279
ScriptId utilities = javascript.pin("pageUtils",
280
"window.pageUtils = { " +
281
" getElementInfo: function(selector) { " +
282
" const el = document.querySelector(selector); " +
283
" return el ? { " +
284
" tagName: el.tagName, " +
285
" id: el.id, " +
286
" className: el.className, " +
287
" textContent: el.textContent.substring(0, 100) " +
288
" } : null; " +
289
" }, " +
290
" highlightElement: function(selector) { " +
291
" const el = document.querySelector(selector); " +
292
" if (el) { " +
293
" el.style.border = '3px solid red'; " +
294
" setTimeout(() => el.style.border = '', 2000); " +
295
" } " +
296
" } " +
297
"};"
298
);
299
300
// Pattern 2: Event monitoring and reporting
301
javascript.pin("monitor",
302
"window.addEventListener('click', function(e) { " +
303
" if (e.target) { " +
304
" reportEvent(JSON.stringify({ " +
305
" type: 'click', " +
306
" element: e.target.tagName, " +
307
" id: e.target.id, " +
308
" classes: e.target.className " +
309
" })); " +
310
" } " +
311
"});"
312
);
313
```
314
315
### Error Handling and Recovery
316
317
```java
318
// Robust binding setup with error handling
319
try {
320
javascript.addJsBinding("dataHandler");
321
javascript.addJsBinding("errorReporter");
322
323
javascript.addBindingCalledListener(payload -> {
324
try {
325
processBinding(payload);
326
} catch (Exception e) {
327
System.err.println("Error processing binding: " + e.getMessage());
328
// Could implement retry logic or error reporting here
329
}
330
});
331
332
} catch (Exception e) {
333
System.err.println("Failed to set up JavaScript bindings: " + e.getMessage());
334
// Implement fallback strategies
335
}
336
```
337
338
### Performance Considerations
339
340
```java
341
// Efficient script injection - avoid heavy operations in pinned scripts
342
javascript.pin("lightweight",
343
"// Keep pinned scripts minimal and fast " +
344
"window.quickUtils = { " +
345
" ready: true, " +
346
" version: '1.0' " +
347
"};"
348
);
349
350
// Use binding calls for heavy operations instead of pinned scripts
351
javascript.addBindingCalledListener(payload -> {
352
// Process heavy operations in Java rather than browser
353
CompletableFuture.supplyAsync(() -> {
354
return processLargeDataSet(payload);
355
}).thenAccept(result -> {
356
// Send result back to browser via executeScript if needed
357
driver.executeScript("window.processResult = arguments[0];", result);
358
});
359
});
360
```