HtmlUnit 5.0.0 has been released — a major milestone for the project! This is the first release to require JDK 17 or higher, which opens the door to modernizing the codebase further in the releases to come. Let’s take a look at the highlights.

🚀 The Big One: JDK 17 Baseline

Starting with 5.0.0, Java 17 is the minimum required version. All sub-projects — htmlunit-neko, htmlunit-csp, htmlunit-cssparser, htmlunit-corejs, and htmlunit-xpath — have been updated accordingly. Along with this, module-info.java has been added to all projects, making HtmlUnit a proper set of named Java modules.

If you are still on JDK 8, the 4.x branch remains available. See the Legacy Support section in the README for details.

The Xerces dependency has also been cleaned up as part of this modernization: XmlUtilsXercesHelper, XmlUtilsSunXercesHelper, and XmlUtilsHelperAPI have been removed, and the xercesImpl runtime dependency is gone.

🔐 SubtleCrypto — Real Implementation

This is big. Thanks to the outstanding work of Lai Quang Duong, SubtleCrypto now has a real implementation covering the most important Web Crypto API operations:

  • digest() — SHA-1, SHA-256, SHA-384, SHA-512
  • generateKey() — RSASSA-PKCS1-v1_5, RSA-PSS, RSA-OAEP, ECDSA, ECDH, AES-CBC, AES-CTR, AES-GCM, AES-KW, HMAC
  • importKey() / exportKey() — raw format for symmetric algorithms
  • sign() / verify() — HMAC, RSASSA-PKCS1-v1_5, RSA-PSS, ECDSA
  • encrypt() / decrypt() — AES-CBC, AES-GCM, AES-CTR, RSA-OAEP

This is a massive step forward for testing modern web applications that rely on the Web Crypto API.

⚙️ JavaScript Engine (core-js)

The Rhino team continues to deliver, with further improvements in 5.0.0:

  • Major refactoring to properly separate the top-level scope from globalThis
  • IdFunctionObject.isConstructor() fixed — methods like Date.prototype.getDate and RegExp.prototype.exec no longer incorrectly report themselves as constructors
  • RegExp.prototype[Symbol.split] and RegExp.prototype[Symbol.replace] received major performance improvements
  • Resizable ArrayBuffer support as per ES2024 added
  • DataView now supports Float16, BigInt64, and BigUInt64
  • JSON.parse fixed to preserve negative zero
  • Symbol.hasInstance custom implementations are now respected; prototype chain lookup works correctly
  • Rest parameters in destructuring are now supported
  • NativeDate migrated away from IdScriptableObject
  • WrapFactory API tightened — users must now override the new wrap(...) and wrapAsJavaObject(...) methods
  • generator.return() now correctly includes the value when the generator is in a completed state
  • Automatic semicolon insertion for let fixed
  • ClassSizeException introduced for better class compilation error reporting
  • Internal cleanup: dropped support for legacy engine versions 1.0–1.4

🔌 WebSocket Overhaul

The WebSocket support has been significantly hardened and cleaned up:

  • The WebSocket constructor now properly validates the URL and throws the correct error types
  • Socket connect errors are handled correctly — status is set and listeners are triggered
  • The was clean flag on close events is now only set when the status code is 1000, matching the spec
  • Incompatible change: WebSocketListener.onWebSocketBinary(byte[], int, int) has been replaced with onWebSocketBinary(ByteBuffer payload)
  • Incompatible change: WebSocketAdapter.closeIncommingSession() has been renamed to the correctly spelled closeIncomingSession()
  • Incompatible change: WebClientOptions.getWebSocketMaxTextMessageBufferSize() / getWebSocketMaxBinaryMessageBufferSize() and their setters have been removed
  • Jetty websocket-client updated to 12.1.8

🧩 New DOM & HTML Features

  • HtmlHeadingGroup introduced — <hgroup> is now a first-class element
  • Document.parseHTMLUnsafe() — first implementation added
  • HTMLAreaElement now exposes all URL decomposition properties: hash, host, hostname, href, origin, password, pathname, port, protocol, search, and username
  • HTMLHyperlinkElementUtils extracted as shared base for HTMLAnchorElement and HTMLAreaElement
  • DomNode.insertBefore() now correctly dissolves DocumentFragment into its children before insertion
  • select.remove() with no arguments now correctly removes itself from the DOM
  • HtmlInputElement types email, url, date, and time now correctly fire the input event
  • FileReader now fires ProgressEvent and exposes all missing event handlers
  • window.queueMicrotask() added
  • HtmlPage id/name lookup index is now built lazily on first read (performance improvement, thanks to Ronny Shapiro)

🔢 Intl Improvements

  • Intl.Locale implementation added
  • Intl.supportedLocalesOf() added to DateTimeFormat and NumberFormat
  • Intl.getCanonicalLocales() implemented
  • toString symbol fixed for Intl, Collator, DateTimeFormat, Locale, and NumberFormat

🎤 Speech API Support

The Web Speech API has arrived in HtmlUnit. SpeechRecognition, SpeechGrammar, and SpeechGrammarList are now implemented and exposed under both their standard names and their webkit-prefixed variants (webkitSpeechRecognition, webkitSpeechGrammar, webkitSpeechGrammarList). The accompanying SpeechRecognitionEvent and SpeechRecognitionErrorEvent are also included.

🐛 Neko HTML Parser

  • hgroup element support added
  • html5lib test suite integrated for broader conformance coverage
  • Null character handling in script content fixed
  • Surrogate character reference parse error handling fixed
  • Consecutive ampersands before named entity parsing fixed
  • Invalid numeric entity handling improved
  • HTMLScanner.scanName now has an ASCII fast-path for the inner character loop

🌍 Browser Compatibility: Firefox 150, Chrome/Edge 148

As always, the browser simulation has been kept up to date:

  • Firefox 150 and Chrome/Edge 148 are now supported
  • Several document.createEvent() calls now throw in Chrome/Edge to match current browser behaviour (WheelEvent, CloseEvent, AnimationEvent, PopStateEvent)
  • The <command> HTML tag is no longer supported in Chrome/Edge
  • NumberFormat for de-CH and it-CH locales updated

📦 Other Incompatible Changes

  • Cookie moved from package org.htmlunit.util to org.htmlunit.http
  • Test suite migrated from JUnit 5 to JUnit 6
  • Logging configuration in tests switched from log4j to logback-classic

Thank You!

A huge thank you to everyone who contributed to this release — Lai Quang Duong for the exceptional SubtleCrypto and DOM work, the Rhino Team for the continued JavaScript engine improvements, Ronny Shapiro and Kanoko Yamamoto for their contributions, and to everyone who filed bug reports and sent pull requests.


As always, 5.0.0 is available on Maven Central. Check out the full changes report for the complete list of changes.

Happy testing!

— RBRi


Resources: