Skip to content

fix(core): applyPositionEdits fails silently across iframe realms#2096

Merged
vanceingalls merged 1 commit into
mainfrom
fix/position-edits-cross-realm
Jul 9, 2026
Merged

fix(core): applyPositionEdits fails silently across iframe realms#2096
vanceingalls merged 1 commit into
mainfrom
fix/position-edits-cross-realm

Conversation

@vanceingalls

Copy link
Copy Markdown
Collaborator

What

applyPositionEdits(doc) in @hyperframes/core/runtime/position-edits guarded each candidate element with instanceof HTMLElement. 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.

Why

Found during an audit of @hyperframes/sdk's surface against pacific's movio integration. Pacific's canvas-react code has an explicit workaround comment for this exact bug: "Upstream fix would be duck-typing in @hyperframes/core — until then, all host code must use this wrapper." Every iframe-hosted consumer has had to reimplement the bulk-apply loop themselves to avoid it.

How

Use the document's own realm's HTMLElement constructor (doc.defaultView?.HTMLElement) instead of the module-scope global. 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.

Test plan

  • New regression test using a real jsdom iframe — confirmed it fails on the old instanceof HTMLElement check (0 applied, expected 1) and passes with the fix
  • Full existing positionEdits.test.ts suite passes (14/14)
  • Full @hyperframes/core suite passes (81 files / 1131 tests)
  • bun run build clean (core + full workspace, incl. studio)

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.
@vanceingalls vanceingalls force-pushed the fix/position-edits-cross-realm branch from d1bf353 to 5a48321 Compare July 9, 2026 06:57

@miga-heygen miga-heygen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean fix for a classic cross-realm instanceof trap. The approach is correct: use the document's own realm's HTMLElement constructor, fall back to duck-typing for detached documents. The regression test is well-constructed — validating the premise (iframeWindow.HTMLElement !== globalThis.HTMLElement) before exercising the fix. applyPositionEditToElement was already realm-safe so the change surface is minimal.

LGTM.

— Miga

@james-russo-rames-d-jusso james-russo-rames-d-jusso left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed at 5a48321.

Small, focused fix. doc.defaultView?.HTMLElement is the right realm-correct primitive, the .style.setProperty duck-type fallback covers the detached/synthetic-doc case, and the regression test's premise sanity check (iframeWindow?.HTMLElement).not.toBe(globalThis.HTMLElement)) pins the exact realm-mismatch condition — nice touch. LGTM from my side.

One micro-observation, non-blocking: the fallback duck-type path (typeof el.style?.setProperty === "function") will accept SVGElements (which have .style.setProperty), whereas the primary RealmHTMLElement path won't. For real usage this is inert — EDIT_BASE_X_ATTR / EDIT_BASE_Y_ATTR are HTML authoring conventions, and no SVG element would carry them — but if the invariant ever loosens, the fallback path becomes slightly less strict than the primary. Not worth fixing now; noting for future.

CI green at HEAD (all Detect changes gates + preflights passing, main matrix still cooking at review time).

Review by Rames D Jusso

@jrusso1020 jrusso1020 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving — realm-local HTMLElement lookup + duck-type fallback for the cross-realm instanceof trap; no changes since RDJ/Miga R1, CI green. Layered on RDJ + Miga's reviews. — Rames Jusso

@vanceingalls vanceingalls merged commit 1bc32bd into main Jul 9, 2026
51 checks passed
@vanceingalls vanceingalls deleted the fix/position-edits-cross-realm branch July 9, 2026 18:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants