Java bindings for Chrome DevTools Protocol version 101, enabling browser automation and debugging capabilities through CDP integration
—
The JavaScript domain provides capabilities for executing JavaScript code, creating custom bindings between browser JavaScript and Java code, and managing script injection into pages.
Main JavaScript handler that extends the base Javascript class with version-specific CDP implementations.
/**
* Manages JavaScript bindings and script evaluation for CDP version 101
* Extends Javascript with ScriptIdentifier and BindingCalled event types
*/
public class V101Javascript extends Javascript<ScriptIdentifier, BindingCalled> {
/**
* Creates a new JavaScript handler instance
* @param devtools DevTools instance for CDP communication
*/
public V101Javascript(DevTools devtools);
}Inherited Methods from Javascript Base Class:
/**
* Pin a script to be evaluated on every new document load
* @param exposeScriptAs Name to expose the script result as
* @param script JavaScript code to execute
* @return ScriptId that can be used to remove the script later
*/
public ScriptId pin(String exposeScriptAs, String script);
/**
* Add a listener for JavaScript binding calls from the browser
* @param listener Consumer that receives the payload string from binding calls
*/
public void addBindingCalledListener(Consumer<String> listener);
/**
* Add a JavaScript binding that allows browser code to call Java
* @param scriptName Name of the binding function to create in browser
*/
public void addJsBinding(String scriptName);
/**
* Remove a previously added JavaScript binding
* @param scriptName Name of the binding function to remove
*/
public void removeJsBinding(String scriptName);
/**
* Disable the JavaScript domain and clean up all bindings and listeners
*/
public void disable();Usage Examples:
import org.openqa.selenium.devtools.v101.V101Javascript;
// Create JavaScript handler
V101Javascript javascript = new V101Javascript(devTools);
// Add a custom binding
javascript.addJsBinding("sendDataToJava");
// Listen for binding calls
javascript.addBindingCalledListener(payload -> {
System.out.println("Received from browser: " + payload);
// Process the data sent from browser JavaScript
processDataFromBrowser(payload);
});
// Pin a script to run on every page load
ScriptId scriptId = javascript.pin("myUtility",
"window.myUtility = { " +
" sendMessage: function(msg) { " +
" sendDataToJava(JSON.stringify({type: 'message', data: msg})); " +
" } " +
"};"
);
// Navigate to a page
driver.get("https://example.com");
// The pinned script is now available on the page
driver.executeScript("myUtility.sendMessage('Hello from browser!');");
// Clean up when done
javascript.disable();Wrapper for script identifiers returned when pinning scripts to pages.
/**
* Represents a script identifier for pinned scripts
* Used to manage and remove scripts that are evaluated on new documents
*/
public class ScriptId {
/**
* Get the underlying script identifier
* @return The actual script identifier object from CDP
*/
public Object getActualId();
}Usage Example:
// Pin multiple scripts and manage them
List<ScriptId> pinnedScripts = new ArrayList<>();
// Add utility functions
ScriptId utilityScript = javascript.pin("utils",
"window.utils = { " +
" logMessage: function(msg) { console.log('Utils:', msg); }, " +
" sendEvent: function(event, data) { " +
" myEventHandler(JSON.stringify({event: event, data: data})); " +
" } " +
"};"
);
pinnedScripts.add(utilityScript);
// Add monitoring script
ScriptId monitorScript = javascript.pin("monitor",
"window.addEventListener('error', function(e) { " +
" errorHandler(JSON.stringify({" +
" message: e.message, " +
" filename: e.filename, " +
" lineno: e.lineno " +
" })); " +
"});"
);
pinnedScripts.add(monitorScript);
// Scripts are now available on all new pages
// Remove specific scripts if needed (implementation would require CDP commands)Setting up Two-way Communication:
// Set up bidirectional communication between Java and browser JavaScript
// 1. Add Java-to-browser functions (via executeScript)
// 2. Add browser-to-Java bindings
javascript.addJsBinding("sendToJava");
javascript.addJsBinding("reportError");
javascript.addJsBinding("requestData");
// Handle different types of messages from browser
javascript.addBindingCalledListener(payload -> {
try {
JsonObject message = JsonParser.parseString(payload).getAsJsonObject();
String type = message.get("type").getAsString();
switch (type) {
case "data":
handleDataFromBrowser(message.get("data"));
break;
case "error":
handleErrorFromBrowser(message.get("error"));
break;
case "request":
handleRequestFromBrowser(message.get("request"));
break;
default:
System.out.println("Unknown message type: " + type);
}
} catch (Exception e) {
System.err.println("Error parsing browser message: " + e.getMessage());
}
});
// Pin helper script for structured communication
javascript.pin("bridge",
"window.bridge = { " +
" sendData: function(data) { " +
" sendToJava(JSON.stringify({type: 'data', data: data})); " +
" }, " +
" reportError: function(error) { " +
" reportError(JSON.stringify({type: 'error', error: error})); " +
" }, " +
" requestData: function(requestId, params) { " +
" requestData(JSON.stringify({type: 'request', id: requestId, params: params})); " +
" } " +
"};"
);The underlying CDP protocol types used by the JavaScript domain:
/**
* CDP Page.ScriptIdentifier for pinned scripts
* Raw identifier from the Chrome DevTools Protocol
*/
public class ScriptIdentifier {
public String getId();
}
/**
* CDP Runtime.bindingCalled event data
* Raw event data when a JavaScript binding is called
*/
public class BindingCalled {
public String getName();
public String getPayload();
public Optional<ExecutionContextId> getExecutionContextId();
}
/**
* CDP Runtime.addBinding command parameters
*/
public static Command<Void> addBinding(
String name,
Optional<String> executionContextName,
Optional<Integer> executionContextId
);
/**
* CDP Runtime.removeBinding command
*/
public static Command<Void> removeBinding(String name);
/**
* CDP Page.addScriptToEvaluateOnNewDocument command
*/
public static Command<ScriptIdentifier> addScriptToEvaluateOnNewDocument(
String source,
Optional<String> worldName,
Optional<Boolean> includeCommandLineAPI
);
/**
* CDP Page.removeScriptToEvaluateOnNewDocument command
*/
public static Command<Void> removeScriptToEvaluateOnNewDocument(ScriptIdentifier identifier);The V101Javascript class internally uses these CDP commands:
// Runtime domain commands for bindings
public static Command<Void> Runtime.enable();
public static Command<Void> Runtime.disable();
public static Command<Void> Runtime.addBinding(String name, Optional<String> executionContextName, Optional<Integer> executionContextId);
public static Command<Void> Runtime.removeBinding(String name);
public static Event<BindingCalled> Runtime.bindingCalled();
// Page domain commands for script injection
public static Command<Void> Page.enable();
public static Command<Void> Page.disable();
public static Command<ScriptIdentifier> Page.addScriptToEvaluateOnNewDocument(String script, Optional<String> worldName, Optional<Boolean> includeCommandLineAPI);
public static Command<Void> Page.removeScriptToEvaluateOnNewDocument(ScriptIdentifier identifier);// Pattern 1: Utility functions available on all pages
ScriptId utilities = javascript.pin("pageUtils",
"window.pageUtils = { " +
" getElementInfo: function(selector) { " +
" const el = document.querySelector(selector); " +
" return el ? { " +
" tagName: el.tagName, " +
" id: el.id, " +
" className: el.className, " +
" textContent: el.textContent.substring(0, 100) " +
" } : null; " +
" }, " +
" highlightElement: function(selector) { " +
" const el = document.querySelector(selector); " +
" if (el) { " +
" el.style.border = '3px solid red'; " +
" setTimeout(() => el.style.border = '', 2000); " +
" } " +
" } " +
"};"
);
// Pattern 2: Event monitoring and reporting
javascript.pin("monitor",
"window.addEventListener('click', function(e) { " +
" if (e.target) { " +
" reportEvent(JSON.stringify({ " +
" type: 'click', " +
" element: e.target.tagName, " +
" id: e.target.id, " +
" classes: e.target.className " +
" })); " +
" } " +
"});"
);// Robust binding setup with error handling
try {
javascript.addJsBinding("dataHandler");
javascript.addJsBinding("errorReporter");
javascript.addBindingCalledListener(payload -> {
try {
processBinding(payload);
} catch (Exception e) {
System.err.println("Error processing binding: " + e.getMessage());
// Could implement retry logic or error reporting here
}
});
} catch (Exception e) {
System.err.println("Failed to set up JavaScript bindings: " + e.getMessage());
// Implement fallback strategies
}// Efficient script injection - avoid heavy operations in pinned scripts
javascript.pin("lightweight",
"// Keep pinned scripts minimal and fast " +
"window.quickUtils = { " +
" ready: true, " +
" version: '1.0' " +
"};"
);
// Use binding calls for heavy operations instead of pinned scripts
javascript.addBindingCalledListener(payload -> {
// Process heavy operations in Java rather than browser
CompletableFuture.supplyAsync(() -> {
return processLargeDataSet(payload);
}).thenAccept(result -> {
// Send result back to browser via executeScript if needed
driver.executeScript("window.processResult = arguments[0];", result);
});
});Install with Tessl CLI
npx tessl i tessl/maven-org-seleniumhq-selenium--selenium-devtools-v101