fix(core): applyPositionEdits fails silently across iframe realms

applyPositionEdits(doc) guarded each element with `instanceof HTMLElement` —
but `doc` is frequently an iframe's document (the SDK's edit preview, any host
embedding a composition), whose elements are HTMLElement instances of THAT
frame's realm, never this module's. The check silently no-ops on every single
element cross-realm, so bulk position edits never apply inside an iframe.

Use the document's own realm's HTMLElement constructor (doc.defaultView);
duck-type on `.style` when defaultView is unavailable (a detached/synthetic
document). The single-element applyPositionEditToElement was already
realm-safe — only the bulk wrapper had the bug.

Added a regression test using a real jsdom iframe, confirmed it fails on the
old `instanceof HTMLElement` check and passes with the fix.
This commit is contained in:
Vance Ingalls
2026-07-08 23:56:20 -07:00
parent 31f0810be8
commit 5a48321726
2 changed files with 38 additions and 2 deletions
@@ -115,6 +115,32 @@ describe("applyPositionEdits", () => {
b.remove();
});
it("applies edits to elements from a DIFFERENT realm (an iframe's document)", () => {
// Regression test: a module-scope `instanceof HTMLElement` check fails for
// elements from another window's realm even though they're genuine,
// stylable HTMLElements — exactly the case for any iframe-hosted editor
// (the SDK's edit preview, a host embedding a composition).
const iframe = document.createElement("iframe");
document.body.appendChild(iframe);
const iframeDoc = iframe.contentDocument;
if (!iframeDoc) throw new Error("iframe.contentDocument unavailable in this test env");
// Sanity check the premise: the iframe's HTMLElement is NOT this realm's.
const iframeWindow = iframe.contentWindow as (Window & typeof globalThis) | null;
expect(iframeWindow?.HTMLElement).not.toBe(globalThis.HTMLElement);
const el = iframeDoc.createElement("div");
el.setAttribute("data-x", "50");
el.setAttribute("data-y", "-10");
el.setAttribute(EDIT_BASE_X_ATTR, "0");
el.setAttribute(EDIT_BASE_Y_ATTR, "0");
iframeDoc.body.appendChild(el);
expect(applyPositionEdits(iframeDoc)).toBe(1);
expect(el.style.getPropertyValue("translate")).toBe("50px -10px");
iframe.remove();
});
it("skips re-apply when the written translate was consumed externally (GSAP fold)", () => {
const el = makeElement({
"data-x": "10",