Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion framework/core/js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"nanoid": "^3.1.30",
"punycode": "^2.1.1",
"textarea-caret": "^3.1.0",
"throttle-debounce": "^3.0.1"
"throttle-debounce": "^3.0.1",
"xslt-polyfill": "^1.0.16"
},
"devDependencies": {
"@flarum/jest-config": "^2.0.0",
Expand Down
53 changes: 52 additions & 1 deletion framework/core/src/Formatter/Formatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -190,10 +190,61 @@ protected function getRenderer(): Renderer

/**
* Get the formatter JavaScript.
*
* Injects XSLT polyfill inline before s9e code to support browsers
* that have removed native XSLT support (Chrome 155+, Nov 2026).
*
* @TODO: Remove polyfill injection when s9e/textformatter has a solution for XSLT removal.
* @see https://github.com/s9e/TextFormatter/issues/250
*/
public function getJs(): string
{
return $this->getComponent('js');
$s9eJs = $this->getComponent('js');

// Try to load XSLT polyfill
// Path 1: Published package (core/js/node_modules)
$polyfillPath = __DIR__.'/../../js/node_modules/xslt-polyfill/xslt-polyfill.min.js';

if (! file_exists($polyfillPath)) {
// Path 2: Monorepo hoisted (framework/node_modules)
$polyfillPath = __DIR__.'/../../../../node_modules/xslt-polyfill/xslt-polyfill.min.js';
}

if (! file_exists($polyfillPath)) {
// Polyfill not available, return s9e JS without it
return $s9eJs;
}

$polyfillJs = file_get_contents($polyfillPath);

// Inject polyfill inline before s9e code
return <<<JS
// XSLT Polyfill for Chrome 155+ (Nov 2026)
// Chrome is removing native XSLT support. This polyfill ensures s9e TextFormatter
// continues to work for live preview functionality.
(function() {
var xsltWorks = false;

if (typeof window !== 'undefined' && window.XSLTProcessor) {
try {
// Test if XSLTProcessor can actually be instantiated
// (constructor exists but throws when XSLT is disabled)
new XSLTProcessor();
xsltWorks = true;
} catch (e) {
// XSLTProcessor exists but is disabled
}
}

if (!xsltWorks) {
// Load polyfill
$polyfillJs
}
})();

// s9e TextFormatter code
$s9eJs
JS;
}

protected function configureDefaultsOnLinks(string $xml): string
Expand Down
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5691,6 +5691,11 @@ xmlchars@^2.2.0:
resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"
integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==

xslt-polyfill@^1.0.16:
version "1.0.16"
resolved "https://registry.yarnpkg.com/xslt-polyfill/-/xslt-polyfill-1.0.16.tgz#86edb1767bc005ce5c5465be309d266785d4a5d6"
integrity sha512-lz0pP+N5X552mZ3IL5qM/7pHwPYNLEF973fFdqnHchm638gw6zWlwroAChnNOzAYLDj0CBvshtupDjqCjZpmXw==

y18n@^5.0.5:
version "5.0.8"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
Expand Down
Loading