v152 · removal · javascript api
XSLTProcessor
XSLTProcessor is the JavaScript API for running XSLT 1.0 transformations in the page: load a stylesheet with importStylesheet(), then transform XML with transformToFragment() or transformToDocument(), with setParameter()/getParameter()/removeParameter()/clearParameters()/reset() managing stylesheet parameters and reuse. The entire interface is deprecated since Chrome 143 and removed from Stable in Chrome 158 (Nov 17, 2026); when the XSLT runtime feature is disabled, window.XSLTProcessor is simply gone.
This page exists so you can recognize, detect, and migrate existing usage. New code should use SaxonJS (XSLT 3.0 in JS), server-side transformation, or JSON+JS rendering. Existing code can be kept alive with the WASM polyfill (a drop-in XSLTProcessor replacement) or, temporarily, the “XSLT” deprecation origin trial / XSLTEnabled policy (until Chrome 176).
syntax
The WHATWG DOM Standard's §9.1 Interface XSLTProcessor (verbatim):
[Exposed=Window]
interface XSLTProcessor {
constructor();
undefined importStylesheet(Node style);
[CEReactions] DocumentFragment transformToFragment(Node source, Document output);
[CEReactions] Document transformToDocument(Node source);
undefined setParameter([LegacyNullToEmptyString] DOMString namespaceURI, DOMString localName, any value);
any getParameter([LegacyNullToEmptyString] DOMString namespaceURI, DOMString localName);
undefined removeParameter([LegacyNullToEmptyString] DOMString namespaceURI, DOMString localName);
undefined clearParameters();
undefined reset();
};
What Chrome actually ships (xslt_processor.idl, verbatim — note the differences that matter for migration):
[ Exposed=Window, RuntimeEnabled=XSLT ] interface XSLTProcessor {
[RaisesException, CallWith=Document] constructor();
undefined importStylesheet(Node style);
// TODO(foolip): In Gecko, the transformTo*() methods throw an exception in
// case of error instead of returning null.
DocumentFragment? transformToFragment(Node source, Document output);
Document? transformToDocument(Node source);
undefined setParameter(DOMString? namespaceURI, DOMString localName, DOMString value);
DOMString? getParameter(DOMString? namespaceURI, DOMString localName);
undefined removeParameter(DOMString? namespaceURI, DOMString localName);
undefined clearParameters();
undefined reset();
};
Key divergences between the spec text and the shipping implementation:
- Gating — Chromium gates the whole interface with
[RuntimeEnabled=XSLT]: disabled feature ⇒ no global at all. This is the removal mechanism. - Nullable returns — Chromium's
transformToFragment()/transformToDocument()returnnullon transformation error; the IDL comment records that Gecko instead throws. The WHATWG text declares non-nullable returns. Code that only checks for exceptions is not portable across engines — a migration hazard the polyfill must also mimic. - Parameter types — Chromium types parameter values as
DOMString(with nullable namespace); the WHATWG text usesanyand[LegacyNullToEmptyString]. Chromium's IDL records a TODO that Gecko can set/get any value type. - Constructor — Chromium's constructor is
[RaisesException]. - Spec maturity — the DOM Standard's section is IDL-only (“Complete definitions of these APIs remain necessary”, tracked in whatwg/dom#181, still open); Chromium's own IDL comment states “There is no spec for XSLTProcessor” and points to Gecko's WebIDL as the closest thing to one.
members and behavior when removed
| Member | Behavior today | After removal |
|---|---|---|
new XSLTProcessor() | Constructs a processor; can throw in Chromium ([RaisesException]) | ReferenceError/TypeError — the global no longer exists |
importStylesheet(style: Node) | Loads/compiles an XSLT 1.0 stylesheet node for later transforms | Unreachable (no global) |
transformToFragment(source: Node, output: Document) | Transforms source, returns a DocumentFragment owned by output; null on error in Chromium, throws in Gecko | Unreachable — polyfill implements the same contract |
transformToDocument(source: Node) | Transforms source into a new Document; nullable in Chromium | Unreachable — polyfill implements the same contract |
setParameter(ns, name, value) / getParameter(ns, name) / removeParameter(ns, name) | Manage stylesheet parameters (Chromium: string values, nullable namespace) | Unreachable — polyfill implements parameter management |
clearParameters() | Drops all set parameters | Unreachable |
reset() | Resets the processor to its initial state (stylesheet and parameters cleared) | Unreachable |
All methods are synchronous — including network-adjacent work: native XSLT loads xsl:include/xsl:import/document() resources synchronously and can do so cross-origin. That combination is precisely what the polyfill cannot fully reproduce (its fetches go through the CORS-checked async fetch()), which is the main behavioral cliff for migrated code.
feature detection
Because the interface is [RuntimeEnabled=XSLT]-gated, detection is a simple existence check — and it is exactly what the surveyed sites that keep working with XSLT disabled already do (roughly 72% of ~220 surveyed sites):
const hasNativeXSLT = typeof XSLTProcessor !== "undefined";
if (!hasNativeXSLT) {
// Chrome with XSLT disabled (pre-stable now, Stable from 158).
// Fall back to SaxonJS or the WASM polyfill.
}
To test the removed world before it ships: toggle chrome://flags/#xslt to Disabled (flag wired to blink::features::kXSLT in about_flags.cc) or launch with --disable-blink-features=XSLT.
migration: the polyfill and SaxonJS
The XSLT polyfill (npm: xslt-polyfill) is a WebAssembly build of the same libxslt+libxml2 engine, exposing a full XSLTProcessor replacement — importStylesheet, transformToDocument, transformToFragment, and parameter management — so existing call sites keep working:
<script src="xslt-polyfill.min.js"></script>
<script>
const xsltProcessor = new XSLTProcessor();
xsltProcessor.importStylesheet(xsltDoc);
const fragment = xsltProcessor.transformToFragment(xmlDoc, document);
</script>
Documented limitations (from the polyfill README):
- CORS — all external resources (
xsl:include,xsl:import,document(), the stylesheet itself) load viafetch()and are CORS-checked; native XSLT could load them despite CORS. This is the primary reason (18% of broken-survey cases) the polyfill cannot restore a site. - Sync vs async — because includes load through async
fetch(), the synchronousXSLTProcessormethods fail for documents that use them. - Encoding — the single-file WASM build requires UTF-8.
- XML parsing stays native — the polyfill still relies on
DOMParser.parseFromString(text, "application/xml"), which is not being removed.
SaxonJS is the maintained, full-fidelity path: XSLT 3.0 (and eventually 4.0) implemented in JavaScript — strictly more capable than the browser feature being removed, at the cost of a library dependency and a different API.
Source: xslt_polyfill README (usage, implementation, limitations); removal guide (client-side XSLT in JavaScript).error behavior
| Situation | Chromium (shipping) | Gecko (per Chromium IDL comment) |
|---|---|---|
Transformation fails inside transformToFragment/transformToDocument | Returns null | Throws an exception |
| Constructor failure | Can throw ([RaisesException]) | — |
| Using the API after the feature is disabled | XSLTProcessor is undefined — accessing it is a ReferenceError in sloppy-scope lookups; calling new XSLTProcessor() fails before any XSLT runs | |
lifecycle and deprecation mechanics
- Chrome 143+: constructing/using the API logs a deprecation warning (console + Lighthouse); a deprecation report with id
"XSLT"is emitted. - Pre-stable channels (Canary/Dev/Beta): the
XSLTruntime feature is Finch-disabled by default as an early warning;XSLTSpecialTrialswitches the console message to a “special trial run” variant. - Chrome 158: Stable disables the feature; the interface disappears unless the origin serves a deprecation-trial token or the
XSLTEnabledpolicy is set. - Chrome 176: trial and policy stop working; the API is gone for everyone.
browser compatibility
| Browser | Supported since (BCD) | Removal plan |
|---|---|---|
| Chrome | 1 | Deprecated 143; disabled pre-stable; Stable removal 158; trial/policy until 176 |
| Edge | 12 | Follows Chromium |
| Firefox | 1 | Removal planned (Gecko supportive); no dated plan on the record |
| Safari | 3.1 | Removal planned (WebKit cautiously supportive) |
BCD (api/XSLTProcessor.json) records standard_track: true and does not yet record the deprecation — treat the deprecation columns as interim data from the ChromeStatus record until BCD catches up.
security and privacy
- Why removal is a security win — libxslt is an aging C library exposed to untrusted content with recent high-severity CVEs (CVE-2025-7425, CVE-2022-22834) and demonstrated browser exploits (Ivan Fratric, OffensiveCon 2025); it was unmaintained for ~6 months of 2025.
- Native XSLT's network behavior — synchronous cross-origin resource loads via
xsl:include/xsl:import/document()bypass the CORS model scripts live under; the polyfill's CORS-checked behavior is stricter, not weaker. - Polyfill provenance — the WASM polyfill embeds the same libxslt/libxml2 code running in a Wasm sandbox instead of the privileged browser process; evaluate that trade-off for your threat model.