Overview and Architecture
Core language support for JavaScript in HtmlUnit is provided by an adapted version of the
Mozilla Rhino engine.
HtmlUnit builds on top of Rhino to provide all browser-specific host objects such as
Window, Document, or Navigator.
HtmlUnitScriptable
All of HtmlUnit's JavaScript host objects subclass HtmlUnitScriptable (or its proxy subclasses)
either directly or indirectly. This base class implements Rhino's ScriptableObject
and manages the binding between JavaScript host objects and their underlying DomNode instances.
Key features provided by HtmlUnitScriptable include:
-
DOM Node Binding: Connects DOM elements to their corresponding JavaScript objects via
getDomNodeOrDie(),getDomNodeOrNull(), andsetDomNode(). -
Automatic Scriptable Factory: Dynamically instantiates the appropriate JavaScript host class
for a given DOM node via
makeScriptableFor(DomNode)by walking up the Java inheritance chain. -
Preemption Property Lookup: Provides
getWithPreemption(String)to allow host objects to resolve dynamic object content or properties before Rhino traverses the standard prototype chain. -
Scope & Window Resolution: Provides helper methods like
getWindow()andgetBrowserVersion()to easily resolve top-level scope context from any host object.
Configuring JavaScript Execution
JavaScript execution is enabled by default in WebClient, matching standard web browser behavior.
Handling JavaScript Exceptions
Key Difference from Real Browsers: By default, HtmlUnit halts script execution when an unhandled JavaScript error occurs. This "fail-early" practice is designed specifically for automated testing environments.
You can instruct HtmlUnit to log exceptions without throwing Java exceptions by setting throwExceptionOnScriptError to false:
final WebClient webClient = new WebClient();
webClient.getOptions().setThrowExceptionOnScriptError(false);
Completely Disabling JavaScript Support
To optimize performance or reduce memory consumption, you can permanently disable JavaScript support.
The most resource-efficient method uses a specific WebClient constructor:
WebClient webClient = new WebClient(BrowserVersion.FIREFOX, false, null, -1);
Temporarily Disabling JavaScript Support
You can toggle JavaScript execution on or off dynamically using WebClientOptions.
Disabling JavaScript this way prevents <script> execution and event handler invocation, though JavaScript host objects remain instantiated in memory.
final WebClient webClient = new WebClient(BrowserVersion.FIREFOX);
webClient.getOptions().setJavaScriptEnabled(false);
// ... perform actions with JS disabled ...
webClient.getOptions().setJavaScriptEnabled(true);
To programmatically check whether JavaScript is active, call
WebClient.isJavaScriptEnabled(),
which accounts for both permanent and temporary disablement states.
Practical Examples and Usage
Example: Using Document.write()
Consider a page that dynamically populates a form table using document.write():
<html><head><title>Table sample</title></head><body>
<form action='/foo' name='form1'>
<table id="table1">
<script type="text/javascript">
for (i = 1; i <= 5; i++) {
document.write("<tr><td>" + i
+ "</td><td><input name='textfield" + i
+ "' type='text'></td></tr>");
}
</script>
</table></form>
</body></html>
You can test that all five input fields were constructed properly:
@Test
public void documentWrite() throws Exception {
final WebClient webClient = new WebClient();
final HtmlPage page = webClient.getPage("https://myserver/test.html");
final HtmlForm form = page.getFormByName("form1");
for (int i = 1; i <= 5; i++) {
final String expectedName = "textfield" + i;
Assert.assertEquals(
"text",
form.<HtmlInput>getInputByName(expectedName).getTypeAttribute());
}
}
To test boundary constraints and verify off-by-one bounds, attempt to retrieve non-existent elements:
try {
form.getInputByName("textfield0");
fail("Expected an ElementNotFoundException");
}
catch (final ElementNotFoundException e) {
// Expected exception path
}
try {
form.getInputByName("textfield6");
fail("Expected an ElementNotFoundException");
}
catch (final ElementNotFoundException e) {
// Expected exception path
}
Example: Watching for Alerts
JavaScript alert dialogs can be captured using an AlertHandler:
<html><head><title>Alert sample</title></head>
<body onload='alert("foo");'>
</body></html>
Registering a CollectingAlertHandler records dialog messages into a list for assertion after page loading:
@Test
public void alerts() throws Exception {
final WebClient webClient = new WebClient();
final List<String> collectedAlerts = new ArrayList<>();
webClient.setAlertHandler(new CollectingAlertHandler(collectedAlerts));
webClient.getPage("https://myserver/test.html");
final List<String> expectedAlerts = Collections.singletonList("foo");
Assert.assertEquals(expectedAlerts, collectedAlerts);
}
Prompts, Confirms, and Status Messages
Handling prompt() dialogs, confirm() dialogs, and status line updates follows the same handler pattern.
See WebClient.setPromptHandler(), WebClient.setConfirmHandler(), and WebClient.setStatusHandler() for details.
Event Handlers
Standard DOM event handlers (e.g., onload, onclick, ondblclick, onmouseup, onsubmit, onreadystatechange) trigger automatically in response to user actions.
If an unhandled custom event needs to be invoked directly, you can trigger it via script execution. However, manipulating elements as an end-user would (e.g., clicking elements and changing input focus) remains the recommended approach.
Executing JavaScript in Page Context
To evaluate custom JavaScript directly within the scope of an active page or window, use HtmlPage.executeJavaScript():
final String script = "window.name;";
final String result = (String) page.executeJavaScript(script).getJavaScriptResult();

