mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-16 19:33:11 +02:00
fix edge translation bug (#6158)
This commit is contained in:
@@ -0,0 +1,99 @@
|
|||||||
|
// Browser page translators (Edge, Chrome, extensions) wrap text nodes in
|
||||||
|
// injected <font> elements, reparenting nodes React is holding. React's commit
|
||||||
|
// phase then throws NotFoundError on removeChild/insertBefore and the
|
||||||
|
// ErrorBoundary unmounts the app. https://github.com/facebook/react/issues/11538
|
||||||
|
//
|
||||||
|
// We watch for translator fingerprints (Google Translate's translated-* class
|
||||||
|
// on <html>, or any injected <font>) and install guards on Node.prototype only
|
||||||
|
// once one appears, so native DOM semantics are preserved when no translator
|
||||||
|
// is active.
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Node {
|
||||||
|
__stirlingTranslatorPatched?: boolean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let patchApplied = false;
|
||||||
|
|
||||||
|
function isGoogleTranslateActive(): boolean {
|
||||||
|
const cls = document.documentElement.classList;
|
||||||
|
return cls.contains("translated-ltr") || cls.contains("translated-rtl");
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyDomPatch(trigger: string): void {
|
||||||
|
if (patchApplied) return;
|
||||||
|
if (typeof Node === "undefined" || !Node.prototype) return;
|
||||||
|
if (Node.prototype.__stirlingTranslatorPatched) return;
|
||||||
|
patchApplied = true;
|
||||||
|
Node.prototype.__stirlingTranslatorPatched = true;
|
||||||
|
|
||||||
|
console.warn(
|
||||||
|
`[dom-patch] Browser page translator detected (${trigger}). ` +
|
||||||
|
"Installing removeChild/insertBefore guards to prevent React crashes. " +
|
||||||
|
"The UI may show minor glitches while the translator is active.",
|
||||||
|
);
|
||||||
|
|
||||||
|
const originalRemoveChild = Node.prototype.removeChild;
|
||||||
|
Node.prototype.removeChild = function patchedRemoveChild<T extends Node>(
|
||||||
|
this: Node,
|
||||||
|
child: T,
|
||||||
|
): T {
|
||||||
|
if (child.parentNode !== this) return child;
|
||||||
|
return originalRemoveChild.call(this, child) as T;
|
||||||
|
} as typeof Node.prototype.removeChild;
|
||||||
|
|
||||||
|
const originalInsertBefore = Node.prototype.insertBefore;
|
||||||
|
Node.prototype.insertBefore = function patchedInsertBefore<T extends Node>(
|
||||||
|
this: Node,
|
||||||
|
newNode: T,
|
||||||
|
referenceNode: Node | null,
|
||||||
|
): T {
|
||||||
|
if (referenceNode && referenceNode.parentNode !== this) return newNode;
|
||||||
|
return originalInsertBefore.call(this, newNode, referenceNode) as T;
|
||||||
|
} as typeof Node.prototype.insertBefore;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function armTranslatorDetector(): void {
|
||||||
|
if (typeof window === "undefined" || typeof MutationObserver === "undefined")
|
||||||
|
return;
|
||||||
|
if (typeof document === "undefined" || !document.documentElement) return;
|
||||||
|
|
||||||
|
// Edge case: class already set (e.g., bfcache restore).
|
||||||
|
if (isGoogleTranslateActive()) {
|
||||||
|
applyDomPatch("html class was already translated-* on arm");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const observer = new MutationObserver((mutations) => {
|
||||||
|
for (const m of mutations) {
|
||||||
|
if (
|
||||||
|
m.type === "attributes" &&
|
||||||
|
m.target === document.documentElement &&
|
||||||
|
isGoogleTranslateActive()
|
||||||
|
) {
|
||||||
|
applyDomPatch("<html> translated-* class appeared");
|
||||||
|
observer.disconnect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (m.type === "childList") {
|
||||||
|
for (const n of m.addedNodes) {
|
||||||
|
if (n.nodeName === "FONT") {
|
||||||
|
applyDomPatch("<font> element injected into DOM");
|
||||||
|
observer.disconnect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(document.documentElement, {
|
||||||
|
attributes: true,
|
||||||
|
attributeFilter: ["class"],
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
armTranslatorDetector();
|
||||||
@@ -1,3 +1,8 @@
|
|||||||
|
// Must be imported before React so the DOM-prototype patch is installed
|
||||||
|
// before React's commit phase runs. Prevents browser page translators
|
||||||
|
// (Edge / Google Translate / extensions) from crashing the app via
|
||||||
|
// parent-mismatch DOMExceptions. See the module for details.
|
||||||
|
import "@app/utils/patchDomForTranslators";
|
||||||
import "@mantine/core/styles.css";
|
import "@mantine/core/styles.css";
|
||||||
import "@mantine/dates/styles.css";
|
import "@mantine/dates/styles.css";
|
||||||
import "../vite-env.d.ts"; // eslint-disable-line no-restricted-imports -- Outside app paths
|
import "../vite-env.d.ts"; // eslint-disable-line no-restricted-imports -- Outside app paths
|
||||||
|
|||||||
Reference in New Issue
Block a user