Skip to content

fix(iframe-embed): render iframe only after framable check (XFO-DENY fallback race)#17

Merged
rubenvdlinde merged 1 commit into
developmentfrom
fix/iframe-render-gate
Jul 26, 2026
Merged

fix(iframe-embed): render iframe only after framable check (XFO-DENY fallback race)#17
rubenvdlinde merged 1 commit into
developmentfrom
fix/iframe-render-gate

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

Problem

The iframe-embed widget's server-side framable check (REQ-IFRAME-003) is async, but the iframe was rendered immediately in the loading state. For an X-Frame-Options: DENY target, the browser fires a load event for its own "refused to connect" page with a null contentDocument — indistinguishable from a successful cross-origin embed — so onLoad flipped the widget to ready before the framable check resolved. The check's state !== 'loading' guard then no-oped, leaving a permanently blank frame instead of the fallback card.

This was invisible to unit tests (the mocked check resolves synchronously) and only surfaced under live async ordering.

Fix

Render the iframe only after the server confirms framing is permitted (framableConfirmed). The premature blocked-frame load can no longer fire, so the framable check always wins.

Verification

  • Vitest: 14/14 green (existing frame-attribute tests now flush the async check; new regression test asserts no iframe exists while the check is pending).
  • Live Playwright on the dev instance: a github.com (XFO: deny) widget now renders the fallback card ("This site does not allow itself to be embedded… Open in new tab" → github.com) with no blank iframe. Previously it showed a blank frame.

Follow-up to #16.

…NY fallback wins the race

The server-side framable check (REQ-IFRAME-003) is async, but the iframe was
rendered immediately in the 'loading' state. For an X-Frame-Options: DENY
target the browser fires a 'load' event for its own 'refused to connect' page
with a null contentDocument — indistinguishable from a successful cross-origin
embed — so onLoad flipped the widget to 'ready' before the framable check
resolved, and the check's 'state !== loading' guard then no-oped. Result: a
permanently blank frame instead of the fallback card.

Only render the iframe once the server confirms framing is permitted
(framableConfirmed). The premature blocked-frame load can no longer fire.
Caught live via Playwright (unit test passed because the mock resolves
synchronously); adds a regression test asserting no iframe exists while the
check is pending.
@rubenvdlinde
rubenvdlinde merged commit b4396f2 into development Jul 26, 2026
1 check passed
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.

1 participant