A headless browser intended for use in testing web-based applications.
—
JavaScript engine integration for executing JavaScript code within web pages, handling browser API calls, and managing JavaScript events. Critical for modern web application automation and testing dynamic content.
Execute JavaScript code directly within HtmlPage context.
/**
* JavaScript execution methods in HtmlPage
*/
public class HtmlPage extends SgmlPage {
/** Execute JavaScript code in page context */
public ScriptResult executeJavaScript(String sourceCode);
/** Execute JavaScript with source file information */
public ScriptResult executeJavaScript(String sourceCode, String sourceName, int startLine);
/** Execute JavaScript and return specific type */
public <T> T executeJavaScriptFunction(String functionCall, Class<T> returnType);
/** Check if JavaScript is enabled for this page */
public boolean isJavaScriptEnabled();
/** Get JavaScript engine for this page */
public AbstractJavaScriptEngine<?> getJavaScriptEngine();
/** Get window object for JavaScript context */
public Window getJavaScriptWindow();
}Usage Examples:
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.gargoylesoftware.htmlunit.ScriptResult;
try (WebClient webClient = new WebClient()) {
// Enable JavaScript
webClient.getOptions().setJavaScriptEnabled(true);
HtmlPage page = webClient.getPage("https://example.com");
// Execute simple JavaScript
ScriptResult result = page.executeJavaScript("document.title");
String title = result.getJavaScriptResult().toString();
System.out.println("Page title: " + title);
// Execute complex JavaScript
String jsCode = """
var elements = document.querySelectorAll('.menu-item');
var texts = [];
for (var i = 0; i < elements.length; i++) {
texts.push(elements[i].textContent.trim());
}
texts;
""";
ScriptResult menuResult = page.executeJavaScript(jsCode);
Object menuItems = menuResult.getJavaScriptResult();
// Execute JavaScript function
page.executeJavaScript("function greet(name) { return 'Hello, ' + name + '!'; }");
ScriptResult greeting = page.executeJavaScript("greet('World')");
System.out.println(greeting.getJavaScriptResult());
// Modify page content with JavaScript
page.executeJavaScript("document.getElementById('myButton').style.display = 'none';");
// Check for navigation after JavaScript execution
if (result.getNewPage() != null) {
System.out.println("JavaScript caused navigation to: " + result.getNewPage().getUrl());
}
}Result object returned from JavaScript execution.
/**
* Result from JavaScript execution
*/
public class ScriptResult {
/** Get JavaScript execution result value */
public Object getJavaScriptResult();
/** Get new page if JavaScript caused navigation */
public Page getNewPage();
/** Check if JavaScript caused page navigation */
public boolean hasNewPage();
}/**
* Main JavaScript execution engine
*/
public class JavaScriptEngine extends AbstractJavaScriptEngine<HtmlUnitScriptable> {
/** Execute JavaScript code */
public Object execute(HtmlPage page, String sourceCode, String sourceName, int startLine);
/** Call JavaScript function */
public Object callFunction(HtmlPage page, Function function, Scriptable scope, Scriptable thisObj, Object[] args);
/** Initialize engine for page */
public void initialize(WebWindow webWindow, Page page);
/** Shutdown engine */
public void shutdown();
/** Get JavaScript configuration */
public JavaScriptConfiguration getConfiguration();
/** Set JavaScript configuration */
public void setJavaScriptConfiguration(JavaScriptConfiguration config);
/** Get JavaScript job manager for background tasks */
public JavaScriptJobManager getJavaScriptJobManager();
/** Check if JavaScript is enabled */
public boolean isScriptEnabled();
/** Set JavaScript enabled state */
public void setScriptEnabled(boolean enabled);
/** Get script preprocessor */
public ScriptPreProcessor getScriptPreProcessor();
/** Set script preprocessor */
public void setScriptPreProcessor(ScriptPreProcessor scriptPreProcessor);
}
/**
* Abstract base for JavaScript engines
*/
public abstract class AbstractJavaScriptEngine<T extends Scriptable> {
/** Execute script and return result */
public abstract ScriptResult execute(HtmlPage page, String code, String sourceName);
/** Call function with arguments */
public abstract Object callFunction(HtmlPage page, Function function, Scriptable scope, Scriptable thisObj, Object[] args);
/** Get timeout for JavaScript execution */
public long getJavaScriptTimeout();
/** Set timeout for JavaScript execution */
public void setJavaScriptTimeout(long timeout);
/** Add script exception listener */
public void addScriptExceptionListener(ScriptExceptionListener listener);
/** Remove script exception listener */
public void removeScriptExceptionListener(ScriptExceptionListener listener);
}/**
* Interface for handling JavaScript errors
*/
public interface JavaScriptErrorListener {
/** Handle JavaScript runtime exception */
void scriptException(HtmlPage page, ScriptException scriptException);
/** Handle JavaScript timeout error */
void timeoutError(HtmlPage page, long allowedTime, long executionTime);
/** Handle malformed script URL */
void malformedScriptURL(HtmlPage page, String url, MalformedURLException malformedURLException);
/** Handle script load error */
void loadScriptError(HtmlPage page, URL scriptUrl, Exception exception);
/** Handle script warning */
void warn(String message, String sourceName, int line, String lineSource, int lineOffset);
}
/**
* Default JavaScript error listener
*/
public class DefaultJavaScriptErrorListener implements JavaScriptErrorListener {
/** Log script exception to console */
public void scriptException(HtmlPage page, ScriptException scriptException);
/** Log timeout error */
public void timeoutError(HtmlPage page, long allowedTime, long executionTime);
/** Log malformed URL error */
public void malformedScriptURL(HtmlPage page, String url, MalformedURLException malformedURLException);
/** Log script load error */
public void loadScriptError(HtmlPage page, URL scriptUrl, Exception exception);
/** Log warning */
public void warn(String message, String sourceName, int line, String lineSource, int lineOffset);
}
/**
* Silent JavaScript error listener (ignores all errors)
*/
public class SilentJavaScriptErrorListener implements JavaScriptErrorListener {
/** Silently ignore script exception */
public void scriptException(HtmlPage page, ScriptException scriptException);
/** Silently ignore timeout error */
public void timeoutError(HtmlPage page, long allowedTime, long executionTime);
/** Silently ignore malformed URL */
public void malformedScriptURL(HtmlPage page, String url, MalformedURLException malformedURLException);
/** Silently ignore load error */
public void loadScriptError(HtmlPage page, URL scriptUrl, Exception exception);
/** Silently ignore warning */
public void warn(String message, String sourceName, int line, String lineSource, int lineOffset);
}Usage Examples:
// Set up JavaScript error handling
webClient.setJavaScriptErrorListener(new JavaScriptErrorListener() {
@Override
public void scriptException(HtmlPage page, ScriptException scriptException) {
System.err.println("JavaScript error on " + page.getUrl() + ": " + scriptException.getMessage());
}
@Override
public void timeoutError(HtmlPage page, long allowedTime, long executionTime) {
System.err.println("JavaScript timeout on " + page.getUrl() +
" (allowed: " + allowedTime + "ms, actual: " + executionTime + "ms)");
}
@Override
public void malformedScriptURL(HtmlPage page, String url, MalformedURLException malformedURLException) {
System.err.println("Malformed script URL: " + url);
}
@Override
public void loadScriptError(HtmlPage page, URL scriptUrl, Exception exception) {
System.err.println("Failed to load script: " + scriptUrl + " - " + exception.getMessage());
}
@Override
public void warn(String message, String sourceName, int line, String lineSource, int lineOffset) {
System.out.println("JavaScript warning: " + message + " at " + sourceName + ":" + line);
}
});
// Or use silent listener to ignore all JavaScript errors
webClient.setJavaScriptErrorListener(new SilentJavaScriptErrorListener());/**
* Manager for background JavaScript tasks (setTimeout, setInterval, etc.)
*/
public class JavaScriptJobManager {
/** Get count of pending jobs */
public int getJobCount();
/** Wait for all jobs to complete */
public int waitForJobs(long timeoutMillis);
/** Wait for jobs started before specified time */
public int waitForJobsStartingBefore(long delayMillis);
/** Cancel all pending jobs */
public void shutdown();
/** Check if job manager is shutdown */
public boolean isShutdown();
/** Add job to queue */
public void addJob(JavaScriptJob job, Page page);
/** Remove job from queue */
public void removeJob(JavaScriptJob job);
/** Get number of completed jobs */
public int getCompletedJobCount();
/** Clear completed job history */
public void clearCompletedJobs();
}
/**
* Background JavaScript job interface
*/
public interface JavaScriptJob {
/** Execute the job */
void run();
/** Get job ID */
Integer getId();
/** Get target execution time */
long getTargetExecutionTime();
/** Check if job is recurring (setInterval) */
boolean isPeriodic();
/** Get period for recurring jobs */
Long getPeriod();
}Usage Examples:
// Wait for background JavaScript to complete
try (WebClient webClient = new WebClient()) {
webClient.getOptions().setJavaScriptEnabled(true);
HtmlPage page = webClient.getPage("https://example.com/ajax-page");
// Execute JavaScript that starts background jobs
page.executeJavaScript("""
setTimeout(function() {
document.getElementById('status').textContent = 'Loaded';
}, 1000);
setInterval(function() {
console.log('Heartbeat');
}, 5000);
""");
// Wait for background jobs to complete (up to 10 seconds)
int jobsRemaining = webClient.waitForBackgroundJavaScript(10000);
if (jobsRemaining == 0) {
System.out.println("All JavaScript jobs completed");
} else {
System.out.println(jobsRemaining + " JavaScript jobs still running");
}
// Or wait for jobs that started before a delay
webClient.waitForBackgroundJavaScriptStartingBefore(2000);
}/**
* Interface for preprocessing JavaScript before execution
*/
public interface ScriptPreProcessor {
/** Preprocess JavaScript source code */
String preProcess(HtmlPage htmlPage, String sourceCode, String sourceName, int lineNumber, HtmlElement htmlElement);
}Usage Example:
// Custom script preprocessor
webClient.getJavaScriptEngine().setScriptPreProcessor(new ScriptPreProcessor() {
@Override
public String preProcess(HtmlPage htmlPage, String sourceCode, String sourceName, int lineNumber, HtmlElement htmlElement) {
// Add debugging information to all scripts
String debug = "console.log('Executing script from " + sourceName + " at line " + lineNumber + "');";
return debug + "\n" + sourceCode;
}
});HtmlUnit provides JavaScript implementations of browser APIs through host objects:
/**
* JavaScript window object
*/
public class Window extends EventTarget {
/** Show alert dialog */
public void alert(String message);
/** Show confirm dialog */
public boolean confirm(String message);
/** Show prompt dialog */
public String prompt(String message, String defaultText);
/** Set timeout for function execution */
public int setTimeout(Function function, int timeout);
/** Set interval for repeated function execution */
public int setInterval(Function function, int interval);
/** Clear timeout */
public void clearTimeout(int timeoutId);
/** Clear interval */
public void clearInterval(int intervalId);
/** Open new window */
public Window open(String url, String windowName, String windowFeatures);
/** Close window */
public void close();
/** Get document object */
public HTMLDocument getDocument();
/** Get location object */
public Location getLocation();
/** Get navigator object */
public Navigator getNavigator();
/** Get history object */
public History getHistory();
/** Get screen object */
public Screen getScreen();
/** Get console object */
public Console getConsole();
/** Get window name */
public String getName();
/** Set window name */
public void setName(String name);
/** Get parent window */
public Window getParent();
/** Get top window */
public Window getTop();
/** Execute eval */
public Object eval(String script);
}
/**
* JavaScript document object
*/
public class HTMLDocument extends Document {
/** Get element by ID */
public Element getElementById(String elementId);
/** Get elements by tag name */
public NodeList getElementsByTagName(String tagName);
/** Get elements by class name */
public NodeList getElementsByClassName(String className);
/** Query selector (first match) */
public Element querySelector(String selectors);
/** Query selector all */
public NodeList querySelectorAll(String selectors);
/** Create element */
public Element createElement(String tagName);
/** Create text node */
public Text createTextNode(String data);
/** Get document title */
public String getTitle();
/** Set document title */
public void setTitle(String title);
/** Get/set document cookie */
public String getCookie();
public void setCookie(String cookie);
/** Document.write */
public void write(String text);
/** Document.writeln */
public void writeln(String text);
/** Get document URL */
public String getURL();
/** Get document ready state */
public String getReadyState();
/** Get document body */
public HTMLElement getBody();
/** Get document head */
public HTMLElement getHead();
}/**
* Configuration options for JavaScript engine
*/
public class JavaScriptConfiguration {
/** Create default configuration */
public JavaScriptConfiguration();
/** Get classes allowed in JavaScript */
public Set<String> getClassesBeingExtended();
/** Add class to be extended */
public void addClassToExtend(String className);
/** Check if class can be extended */
public boolean isClassExtendable(String className);
/** Get disabled classes */
public Set<String> getDisabledClasses();
/** Disable class in JavaScript */
public void disableClass(String className);
/** Check if class is disabled */
public boolean isClassDisabled(String className);
}Waiting for Elements:
// Wait for element to appear using JavaScript
String waitForElementScript = """
(function() {
var element = document.getElementById('dynamicContent');
var attempts = 0;
var maxAttempts = 50;
function checkElement() {
element = document.getElementById('dynamicContent');
attempts++;
if (element) {
return element.textContent;
} else if (attempts < maxAttempts) {
setTimeout(checkElement, 100);
return null;
} else {
return 'TIMEOUT';
}
}
return checkElement();
})();
""";
ScriptResult result = page.executeJavaScript(waitForElementScript);AJAX Request Simulation:
// Execute AJAX request through JavaScript
String ajaxScript = """
var xhr = new XMLHttpRequest();
xhr.open('GET', '/api/data', false); // Synchronous for simplicity
xhr.send();
if (xhr.status === 200) {
JSON.parse(xhr.responseText);
} else {
{error: 'HTTP ' + xhr.status};
}
""";
ScriptResult ajaxResult = page.executeJavaScript(ajaxScript);
Object data = ajaxResult.getJavaScriptResult();Form Manipulation:
// Fill and submit form via JavaScript
String formScript = """
var form = document.forms['loginForm'];
form.username.value = 'testuser';
form.password.value = 'testpass';
// Trigger events
var event = new Event('change', { bubbles: true });
form.username.dispatchEvent(event);
form.password.dispatchEvent(event);
// Submit form
form.submit();
'Form submitted';
""";
page.executeJavaScript(formScript);Install with Tessl CLI
npx tessl i tessl/maven-net-sourceforge-htmlunit--htmlunit