Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions packages/devframe/src/in-page-channel/in-page-channel.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -675,6 +675,39 @@ function createWindowPair(origin = 'https://app.test'): { hostWin: FakeWindow, p
const fastHello = { helloIntervalMs: 5, heartbeat: false as const }

describe('in-page channel handshake', () => {
it.each([0, 1, 2])('connects through a popup opener with %i nested panel frames', async (depth) => {
const { hostWin, panelWin } = createWindowPair()
let popupWin = panelWin
for (let i = 0; i < depth; i++) {
const parent = createFakeWindow(hostWin.location.origin)
popupWin.parent = parent
popupWin = parent
}
popupWin.parent = popupWin
popupWin.opener = hostWin
const pageScript = createPageScriptChannel<TestProtocol>({
name: 'devframes:test',
window: asWindow(hostWin),
heartbeat: false,
functions: defaultPageScriptFunctions,
})
const panel = connectPanelChannel<TestProtocol>({
name: 'devframes:test',
window: asWindow(panelWin),
...fastHello,
functions: defaultPanelFunctions,
})
try {
await panel.whenConnected(200)
expect(panel.pageScript?.instanceId).toBe(pageScript.instanceId)
await expect(panel.call('echo', 'popup')).resolves.toBe('popup')
}
finally {
panel.close()
pageScript.close()
}
})

it('connects a panel to the page script and survives page-script restarts', async () => {
const { hostWin, panelWin } = createWindowPair()
const pageScript = createPageScriptChannel<TestProtocol>({
Expand Down
2 changes: 1 addition & 1 deletion packages/devframe/src/in-page-channel/panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const DEFAULT_EVENT_BUFFER_LIMIT = 64
* Connect the panel endpoint of an in-page channel.
*
* The panel initiates: it posts a versioned hello to every window a
* same-tab page script can live in (its ancestor chain and its `opener`),
* page script can live in (its ancestor chain and those windows' openers),
* retrying with backoff until one answers with a dedicated port, so boot
* order never matters, and a reload of either side is just a re-handshake
* (`WindowProxy` references survive navigations). While `connecting`,
Expand Down
20 changes: 11 additions & 9 deletions packages/devframe/src/in-page-channel/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ export function resolveAllowedOrigins(allowedOrigins: string[] | undefined, win:
}

/**
* Default handshake targets of a panel: its ancestor chain plus its
* `opener`, every same-tab window a page script can live in. `WindowProxy`
* Default handshake targets of a panel: its ancestor chain plus the
* `opener` of each window, including a popup containing the panel iframe. `WindowProxy`
* references stay valid across navigations, so hellos posted to these reach
* a page script even after the host page reloads.
*/
Expand All @@ -111,13 +111,15 @@ export function defaultHandshakeTargets(win: Window): Window[] {
catch {
// Walking stopped by the browser; keep what we have.
}
try {
const opener = win.opener as Window | null
if (opener && opener !== win)
targets.push(opener)
}
catch {
// Inaccessible opener; ignore.
for (const current of [win, ...targets]) {
try {
const opener = current.opener as Window | null
if (opener && opener !== win && !targets.includes(opener))
targets.push(opener)
}
catch {
// Inaccessible opener; continue with the other ancestors.
}
}
return targets
}
2 changes: 1 addition & 1 deletion packages/devframe/src/in-page-channel/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ export interface ConnectPanelChannelOptions<Protocol extends InPageChannelProtoc
window?: Window | false
/**
* Windows the hello is posted to. Defaults to the panel's ancestor chain
* plus its `opener`, the places a same-tab page script can live. When
* plus those windows' openers, the places a page script can live. When
* empty and no `transport` is given, the endpoint stays `connecting` and
* warns once.
*/
Expand Down
Loading