0
# JavaScript Injection & Bindings
1
2
JavaScript code injection, binding management, and script execution coordination with full lifecycle control and event monitoring through the v115Javascript class.
3
4
## Capabilities
5
6
### JavaScript Domain
7
8
Manages JavaScript code injection and bidirectional communication between browser and Java code through bindings.
9
10
```java { .api }
11
/**
12
* JavaScript domain for script injection and binding management
13
* Extends the idealized Javascript class with CDP v115 specific implementations
14
*/
15
public class v115Javascript extends Javascript<ScriptIdentifier, BindingCalled> {
16
/**
17
* Creates JavaScript domain with DevTools connection
18
* @param devtools DevTools instance for CDP communication
19
*/
20
public v115Javascript(DevTools devtools);
21
22
/**
23
* Enable runtime domain for JavaScript execution
24
* @return Command to enable runtime domain
25
*/
26
protected Command<Void> enableRuntime();
27
28
/**
29
* Disable runtime domain and stop JavaScript functionality
30
* @return Command to disable runtime domain
31
*/
32
protected Command<Void> disableRuntime();
33
34
/**
35
* Add JavaScript binding for bidirectional communication
36
* @param scriptName Name of the binding function to create
37
* @return Command to add the binding
38
*/
39
protected Command<Void> doAddJsBinding(String scriptName);
40
41
/**
42
* Remove JavaScript binding
43
* @param scriptName Name of the binding function to remove
44
* @return Command to remove the binding
45
*/
46
protected Command<Void> doRemoveJsBinding(String scriptName);
47
48
/**
49
* Enable page domain for script injection
50
* @return Command to enable page domain
51
*/
52
protected Command<Void> enablePage();
53
54
/**
55
* Disable page domain and stop script injection
56
* @return Command to disable page domain
57
*/
58
protected Command<Void> disablePage();
59
60
/**
61
* Add script to evaluate on every new document load
62
* @param script JavaScript code to inject
63
* @return Command returning ScriptIdentifier for the injected script
64
*/
65
protected Command<ScriptIdentifier> addScriptToEvaluateOnNewDocument(String script);
66
67
/**
68
* Remove previously injected script
69
* @param id ScriptIdentifier of the script to remove
70
* @return Command to remove the script
71
*/
72
protected Command<Void> removeScriptToEvaluateOnNewDocument(ScriptIdentifier id);
73
74
/**
75
* Get binding called event stream for monitoring binding invocations
76
* @return Event stream for binding called events
77
*/
78
protected Event<BindingCalled> bindingCalledEvent();
79
80
/**
81
* Extract payload from binding called event
82
* @param event BindingCalled event from CDP
83
* @return String payload sent from JavaScript
84
*/
85
protected String extractPayload(BindingCalled event);
86
}
87
```
88
89
### Script Injection
90
91
Inject JavaScript code that runs on every new document, useful for setting up global functions and variables.
92
93
```java { .api }
94
/**
95
* Pin script for execution on new documents (inherited from Javascript base class)
96
* @param exposeScriptAs Name to expose the script as (for management)
97
* @param script JavaScript code to inject
98
* @return ScriptId for managing the injected script
99
*/
100
public ScriptId pin(String exposeScriptAs, String script);
101
```
102
103
**Usage Examples:**
104
105
```java
106
import org.openqa.selenium.devtools.v115.v115Javascript;
107
import org.openqa.selenium.devtools.idealized.ScriptId;
108
109
// Create JavaScript domain
110
v115Javascript javascript = new v115Javascript(devTools);
111
112
// Inject global utility functions
113
ScriptId utilsScript = javascript.pin("utils", """
114
window.myUtils = {
115
getCurrentTimestamp: function() {
116
return Date.now();
117
},
118
119
highlightElement: function(selector) {
120
const element = document.querySelector(selector);
121
if (element) {
122
element.style.border = '3px solid red';
123
return true;
124
}
125
return false;
126
},
127
128
getPageInfo: function() {
129
return {
130
title: document.title,
131
url: window.location.href,
132
elementCount: document.querySelectorAll('*').length
133
};
134
}
135
};
136
137
console.log('Utility functions loaded');
138
""");
139
140
// Navigate to pages - script will be available immediately
141
driver.get("https://example.com");
142
143
// Use the injected functions
144
Object timestamp = driver.executeScript("return window.myUtils.getCurrentTimestamp();");
145
Boolean highlighted = (Boolean) driver.executeScript(
146
"return window.myUtils.highlightElement('h1');"
147
);
148
149
System.out.println("Timestamp: " + timestamp);
150
System.out.println("Highlighted: " + highlighted);
151
```
152
153
### JavaScript Bindings
154
155
Create bidirectional communication channels between browser JavaScript and Java code.
156
157
```java { .api }
158
/**
159
* Add JavaScript binding (inherited from Javascript base class)
160
* @param scriptName Name of the function to create in browser global scope
161
*/
162
public void addJsBinding(String scriptName);
163
164
/**
165
* Remove JavaScript binding (inherited from Javascript base class)
166
* @param scriptName Name of the binding function to remove
167
*/
168
public void removeJsBinding(String scriptName);
169
170
/**
171
* Add listener for binding calls (inherited from Javascript base class)
172
* @param listener Consumer to handle binding invocations with payload
173
*/
174
public void addBindingCalledListener(Consumer<String> listener);
175
```
176
177
**Usage Examples:**
178
179
```java
180
import java.util.concurrent.CompletableFuture;
181
import java.util.concurrent.TimeUnit;
182
183
// Create JavaScript domain
184
v115Javascript javascript = new v115Javascript(devTools);
185
186
// Set up data collection from browser
187
CompletableFuture<String> dataFuture = new CompletableFuture<>();
188
189
// Add binding and listener
190
javascript.addJsBinding("sendDataToJava");
191
javascript.addBindingCalledListener(payload -> {
192
System.out.println("Received from browser: " + payload);
193
dataFuture.complete(payload);
194
});
195
196
// Navigate and inject collection script
197
driver.get("https://example.com");
198
199
driver.executeScript("""
200
// Collect page data and send to Java
201
const data = {
202
title: document.title,
203
links: Array.from(document.links).length,
204
images: Array.from(document.images).length,
205
scripts: Array.from(document.scripts).length,
206
timestamp: Date.now()
207
};
208
209
// Send to Java via binding
210
window.sendDataToJava(JSON.stringify(data));
211
""");
212
213
// Wait for data from browser
214
try {
215
String pageData = dataFuture.get(5, TimeUnit.SECONDS);
216
System.out.println("Page analysis complete: " + pageData);
217
} catch (Exception e) {
218
System.err.println("Timeout waiting for page data");
219
}
220
221
// Clean up
222
javascript.removeJsBinding("sendDataToJava");
223
```
224
225
### Domain Control
226
227
Control JavaScript domain lifecycle and disable functionality when needed.
228
229
```java { .api }
230
/**
231
* Disable JavaScript domain (inherited from Javascript base class)
232
* Stops runtime and page domains, removes all bindings and scripts
233
*/
234
public void disable();
235
```
236
237
**Usage Examples:**
238
239
```java
240
// JavaScript functionality is automatically enabled when used
241
javascript.addJsBinding("myBinding");
242
ScriptId script = javascript.pin("myScript", "console.log('loaded');");
243
244
// Disable when automation is complete
245
javascript.disable();
246
```
247
248
## Advanced Binding Patterns
249
250
### Request/Response Pattern
251
252
Implement request/response communication using bindings and promises:
253
254
```java
255
// Java side - set up request handler
256
javascript.addJsBinding("handleRequest");
257
Map<String, CompletableFuture<String>> pendingRequests = new ConcurrentHashMap<>();
258
259
javascript.addBindingCalledListener(payload -> {
260
try {
261
ObjectMapper mapper = new ObjectMapper();
262
JsonNode request = mapper.readTree(payload);
263
String requestId = request.get("id").asText();
264
String operation = request.get("operation").asText();
265
266
// Process request
267
String response = processRequest(operation, request.get("data"));
268
269
// Send response back to browser
270
driver.executeScript("""
271
if (window.pendingRequests && window.pendingRequests['%s']) {
272
window.pendingRequests['%s'].resolve('%s');
273
delete window.pendingRequests['%s'];
274
}
275
""".formatted(requestId, requestId, response, requestId));
276
277
} catch (Exception e) {
278
System.err.println("Error processing request: " + e.getMessage());
279
}
280
});
281
282
// Inject client-side request helper
283
javascript.pin("requestHelper", """
284
window.pendingRequests = {};
285
286
window.sendRequest = function(operation, data) {
287
const requestId = 'req_' + Date.now() + '_' + Math.random();
288
289
return new Promise((resolve, reject) => {
290
window.pendingRequests[requestId] = { resolve, reject };
291
292
const request = {
293
id: requestId,
294
operation: operation,
295
data: data
296
};
297
298
window.handleRequest(JSON.stringify(request));
299
300
// Timeout after 10 seconds
301
setTimeout(() => {
302
if (window.pendingRequests[requestId]) {
303
delete window.pendingRequests[requestId];
304
reject(new Error('Request timeout'));
305
}
306
}, 10000);
307
});
308
};
309
""");
310
```
311
312
### Event Broadcasting
313
314
Broadcast events from Java to all pages:
315
316
```java
317
// Set up event broadcasting
318
javascript.pin("eventSystem", """
319
window.eventHandlers = {};
320
321
window.addEventListener = function(eventType, handler) {
322
if (!window.eventHandlers[eventType]) {
323
window.eventHandlers[eventType] = [];
324
}
325
window.eventHandlers[eventType].push(handler);
326
};
327
328
window.broadcastEvent = function(eventType, data) {
329
const handlers = window.eventHandlers[eventType] || [];
330
handlers.forEach(handler => {
331
try {
332
handler(data);
333
} catch (e) {
334
console.error('Event handler error:', e);
335
}
336
});
337
};
338
""");
339
340
// Broadcast events from Java
341
public void broadcastEvent(String eventType, Object data) {
342
String script = String.format(
343
"window.broadcastEvent('%s', %s);",
344
eventType,
345
objectToJson(data)
346
);
347
driver.executeScript(script);
348
}
349
```
350
351
## Error Handling
352
353
### Binding Call Failures
354
355
Binding calls can fail if the browser context is invalid or the binding doesn't exist. The system provides graceful degradation:
356
357
```java
358
// Bindings are automatically cleaned up when pages navigate
359
// Always check binding availability before use
360
driver.executeScript("""
361
if (typeof window.myBinding === 'function') {
362
window.myBinding('data');
363
} else {
364
console.warn('Binding not available');
365
}
366
""");
367
```
368
369
### Script Injection Failures
370
371
Script injection can fail if the page domain is not enabled or if the script is malformed:
372
373
```java
374
try {
375
ScriptId script = javascript.pin("myScript", "invalid javascript syntax");
376
} catch (Exception e) {
377
System.err.println("Script injection failed: " + e.getMessage());
378
}
379
```