November brings HtmlUnit 5.7.0 with Firefox 159, Firefox 153.5.0 ESR and Chrome/Edge 156 support. This release is a mix of security fixes, a large set of text and layout corrections that bring HtmlUnit’s output closer to real browsers, and a thorough cleanup of the Web Crypto API. Together with the Rhino team, we also continued fixing JavaScript engine issues.

🚀 Version 5.7.0 - November 2, 2026

Security Fixes

Several issues in this release were reported or found while hardening the parser and CSS engine:

  • CSS regex backtracking: The url and position detection inside the background shorthand (used by backgroundImage and backgroundPosition) was vulnerable to regex backtracking (CWE-1333). Some long values could block processing for a very long time. This is fixed.
  • Authorization header forwarding: The Authorization header is no longer forwarded to another origin, neither for redirects nor for requests of external scripts. Thanks to Vidya m for reporting this.
  • Parser and CSS nesting limits: The CSS parser limits the nesting depth of functions, calc() blocks, and selectors to 32 levels. Deeper input is reported as a parse error instead of causing a StackOverflowError. The HTML tree builder limits the nesting depth of the generated document to 512 elements, like browsers do.
  • Denial of service protection in the HTML parser: Searches on the element stack are limited to 100 entries, so crafted input with a deep stack no longer causes quadratic parsing time. This is a behavior change: an end tag that would have to close more than 100 unclosed elements is now ignored.
  • Scanner fix: A fuzzer found an ArrayIndexOutOfBoundsException when the input ends in the middle of the end tag of a title, textarea, style, or similar element. This is fixed.

For more background on HtmlUnit’s security considerations, see the Details about HtmlUnit Security page.

Text Width and Layout

HtmlUnit now estimates the width of text based on the character widths of the default font (Times) used by browsers, instead of using a fixed width per character. This affects:

  • offsetWidth and clientWidth of shrink-to-fit elements
  • the left position of elements that follow some text
  • the rects returned by Range.getClientRects() and Range.getBoundingClientRect()

The results are much closer to real browsers. A new method, BrowserVersion.getTextWidth(String, String), exposes the estimate directly.

Related fixes to getBoundingClientRect() and Range handling:

  • Ignored whitespace-only text nodes and block-level siblings when calculating the horizontal offset of a text node
  • Shrink-wrap width calculation for block elements, returning a tight bounding box around the content
  • Correct character offsets when range boundaries are set on a parent element

innerText, outerText, and getText()

A large part of this release reworks how text is extracted, both for the innerText/outerText properties and for WebDriver’s getText():

innerText and outerText now follow the innerText algorithm of the HTML spec:

  • Required line breaks of block elements are merged and dropped at the start and end
  • Paragraphs are surrounded by an empty line
  • br is a line break that is part of the content
  • Table cells are separated by a tab and table rows by a line break
  • Inputs, images, and selects are treated as atomic inlines
  • Options of a list box are placed on lines of their own
  • Elements with display: none or content-visibility: hidden are skipped
  • Fallback content of canvas, meter, progress, iframe, video, audio, and (when JavaScript is enabled) noscript is no longer included

getText() (DomNode.getVisibleText()):

  • Line breaks around blocks are only added when the current line has content, like the Selenium atoms do
  • Elements with zero opacity, content-visibility: hidden, zero size with hidden overflow, or positions outside the page are not visible
  • White-space handling reworked: pre and pre-wrap preserve spaces, normal and nowrap collapse them, and zero-width spaces are removed
  • Fixes for select, option, lists, tables (including CSS table cells and footers placed before the body), closed details, SVG, iframe, and br

Visibility and display:

  • isDisplayed() now handles the hidden attribute and the open state of a dialog through the computed display style; an explicit display style overrides both
  • getDisplay() returns none for a closed dialog and block for an open one

HTML Parsing and Serialization

Parser behavior:

  • A trailing slash on non-void elements is only honored for XHTML pages and XHTML fragments; in HTML documents it is ignored, like in browsers
  • A NUL character is replaced by U+FFFD in the content of several elements and in comments and attribute values
  • A line feed directly after the start tag of pre, listing, and textarea is no longer part of the content
  • Elements like <a> and <nobr> are un-nested like real browsers do, so <a href="1">1<div><a href="2">2</a></div></a> no longer produces nested anchors

SVG:

  • The HTML parser now restores the case of SVG names listed in the HTML spec (for example foreignObject and clipPath)
  • The content of an SVG element set via innerHTML is parsed as SVG content
  • createElementNS() keeps the case of the name

Serialization (innerHTML and outerHTML):

  • XHTML and XML documents use the XML serialization: namespace declarations, empty elements written as <foo/>, void elements as <br />
  • Attribute values are escaped like in real browsers
  • Text of raw text elements such as iframe, xmp, and noscript is no longer escaped
  • Processing instructions end with > (Firefox) or ?> (Chrome/Edge)
  • XmlUtils.escapeHtmlAttributeValue() and XmlUtils.escapeXmlAttributeValue() are now available

Attributes and properties:

  • setAttribute() lowercases the attribute name in HTML documents
  • Setting disabled = true sets the attribute to an empty string, and an existing value is kept
  • createCDATASection() throws a NotSupportedError for HTML documents
  • DOMImplementation.createDocument() treats a null namespace as no namespace

Web Crypto (SubtleCrypto)

The Web Crypto API received a thorough review. Highlights:

  • Error handling: Invalid input is now rejected with the correct error types: TypeError for invalid arguments, DataError for invalid key data, and OperationError for failing operations. Decryption failures no longer throw an ArrayIndexOutOfBoundsException.
  • AES-CTR now increments only the rightmost length bits of the counter block.
  • CryptoKey: algorithm and usages return the same object on every access, and changing these objects no longer changes the key’s behavior.
  • Algorithm names are matched case-insensitively, like in real browsers. Keys report the registered name, for example "AES-GCM".
  • importKey() accepts the algorithm as a string.
  • verify() resolves to false for an RSA signature that does not match the modulus length.
  • crypto.getRandomValues() now fills the array bytes directly. Previously Uint8ClampedArray received only the values 0 and 255, and BigInt64Array/BigUint64Array received only 32 random bits per element.

Downloads and File Names

A new ContentDispositionParser parses the Content-Disposition header according to RFC 6266, RFC 2183, and RFC 8187.

  • AttachmentHandler.isAttachment() detects the disposition type the way browsers do. Responses with application/octet-stream are always handled as attachments.
  • Attachment.getSuggestedFilename() now determines file names like browsers do: case-insensitive parameter names, quoted strings, filename* taking precedence over filename, unsafe characters replaced by _, and leading and trailing dots and whitespace removed.
  • WebDriverTestCase supports testing file downloads with real browsers (needDownloads()) and optional WebDriver BiDi runs.

XMLHttpRequest and DOM

  • Added the Firefox properties mozAnon and mozSystem (always false)
  • Added the Chrome/Edge methods setAttributionReporting() and setPrivateToken() (argument and state checks only)
  • Added responseURL
  • getResponseHeader() returns null for a network-error response
  • responseText is an empty string after a failed redirect
  • A same-origin or initial XHR that follows a 3xx redirect to a cross-origin resource without proper CORS headers is now blocked
  • Added DOMImplementation.createHTMLDocument(), HTMLDocument.appendChild(), and simple implementations of createAttributeNS(), createEntityReference(), and createProcessingInstruction()
  • Added the CSS pseudo-class :scope; pseudo-class selector validation is now case-insensitive
  • Added CSS media query resolution unit x as an alias for dppx (issue #992)

JavaScript Engine

Together with the Rhino team:

  • super property increment/decrement operator fixed
  • catch scope fixes
  • BigInt.prototype.toLocaleString() no longer treats its argument as a radix
  • JSON.parse() and JSON.stringify() callbacks receive numeric keys correctly
  • TypedArray.prototype.subarray handles infinite and large indexes correctly
  • Converting the smallest subnormal numbers to strings is fixed
  • TypedArray.prototype.with reads the length before coercing its arguments
  • ArrayBuffer transfer methods no longer use the species constructor
  • Object.fromEntries() no longer stores array index keys as named properties
  • Function.prototype.toString() keeps the name of object literal methods
  • Several more fixes for lexical scoping and FinalizationRegistry
  • Convenience methods for manipulating Promises from Java were added

I also fixed several inconsistencies in Math.

PerformanceObserver

PerformanceObserver now supports observe(), disconnect(), takeRecords(), and supportedEntryTypes, including the argument validation of real browsers (issue #1136). HtmlUnit does not record performance entries, so the callback is never called.

Content Security Policy

Thanks to kingthorin, the CSP support gained Trusted Types query methods:

  • enforcesTrustedTypesForScript()
  • allowsTrustedTypesPolicyName(String)
  • allowsAnyTrustedTypesPolicyName()
  • allowsDuplicateTrustedTypesPolicies()

Two warnings were corrected: the warning for an empty trusted-types directive now states that it blocks all policy names, and the incorrect “‘allow-duplicates’ is redundant” warning is removed.

Other Changes

  • MockWebConnection is now Serializable. Thanks to Vladimir Babin (issue #91).
  • Chrome/Edge: FontFaceSet and the SVG TEXTPATH_SIDETYPE_* constants are available.
  • Firefox: Notification constants and Error.stackTraceLimit-related updates.
  • CSSStyleDeclaration and ComputedCssStyleDeclaration received optimizations and fixes.
  • Several HTMLTagBalancer performance improvements, including O(1) “is element open” checks.

Version 5.7.0 is available on Maven Central. Check out the full changes report for the complete list of changes.

Happy testing!

— RBRi


Resources: