Skip to content

Commit 690683f

Browse files
committed
fix(player): defer initial iframe navigation
1 parent 587e392 commit 690683f

2 files changed

Lines changed: 38 additions & 0 deletions

File tree

packages/player/src/hyperframes-player.test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,7 @@ describe("HyperframesPlayer shader transition options", () => {
525525
const player = document.createElement("hyperframes-player") as PlayerWithIframe;
526526
player.setAttribute("shader-capture-scale", "0.5");
527527
player.setAttribute("shader-loading", "player");
528+
document.body.appendChild(player);
528529
player.setAttribute("src", "/api/projects/demo/preview?x=1#stage");
529530

530531
const url = new URL(player.iframeElement.src);
@@ -539,6 +540,7 @@ describe("HyperframesPlayer shader transition options", () => {
539540
const player = document.createElement("hyperframes-player") as PlayerWithIframe;
540541
player.setAttribute("shader-capture-scale", "0.5");
541542
player.setAttribute("shader-loading", "player");
543+
document.body.appendChild(player);
542544
player.setAttribute(
543545
"srcdoc",
544546
'<!doctype html><html><head><script src="composition.js"></script></head><body></body></html>',
@@ -1512,6 +1514,36 @@ describe("HyperframesPlayer srcdoc attribute", () => {
15121514
player.remove();
15131515
});
15141516

1517+
it("does not navigate initial srcdoc before the runtime listener is connected", () => {
1518+
// React assigns custom-element attributes before inserting the element. If the observed
1519+
// attribute callback navigates the child iframe immediately, a fast srcdoc runtime can post
1520+
// its one-shot `ready` message before connectedCallback subscribes to `window.message`.
1521+
// Retained runtime data then waits forever and a caption style appears stuck on its bootstrap
1522+
// frame. The connect path owns the first navigation; attributeChangedCallback owns only
1523+
// subsequent swaps.
1524+
const player = document.createElement("hyperframes-player") as PlayerInternal;
1525+
player.setAttribute("srcdoc", "<!doctype html><html><body>deferred</body></html>");
1526+
1527+
expect(player.iframe.hasAttribute("srcdoc")).toBe(false);
1528+
1529+
document.body.appendChild(player);
1530+
expect(player.iframe.getAttribute("srcdoc")).toContain("<body>deferred</body>");
1531+
1532+
player.remove();
1533+
});
1534+
1535+
it("does not navigate initial src before the runtime listener is connected", () => {
1536+
const player = document.createElement("hyperframes-player") as PlayerInternal;
1537+
player.setAttribute("src", "/api/projects/deferred/preview");
1538+
1539+
expect(player.iframe.hasAttribute("src")).toBe(false);
1540+
1541+
document.body.appendChild(player);
1542+
expect(player.iframe.getAttribute("src")).toBe("/api/projects/deferred/preview");
1543+
1544+
player.remove();
1545+
});
1546+
15151547
it("forwards a srcdoc attribute set after connect to the iframe", () => {
15161548
// The composition-switching flow: same player element, new HTML.
15171549
// Without `attributeChangedCallback` wiring this would no-op.

packages/player/src/hyperframes-player.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,11 @@ class HyperframesPlayer extends HTMLElement {
218218
attributeChangedCallback(name: string, oldVal: string | null, val: string | null) {
219219
switch (name) {
220220
case "src":
221+
// Custom-element attributes are normally assigned before insertion (React does this for
222+
// every render). Navigating the inner iframe here would let its one-shot runtime `ready`
223+
// message fire before connectedCallback installs the parent message listener. Initial
224+
// attributes are applied below by connectedCallback; only live changes navigate here.
225+
if (!this.isConnected) break;
221226
if (val) {
222227
this._ready = false;
223228
this._runtimeBridgeReady = false;
@@ -228,6 +233,7 @@ class HyperframesPlayer extends HTMLElement {
228233
}
229234
break;
230235
case "srcdoc":
236+
if (!this.isConnected) break;
231237
this._ready = false;
232238
this._runtimeBridgeReady = false;
233239
this._rejectAllRuntimeDataDeliveries(

0 commit comments

Comments
 (0)