Avoid host-frame registry scans and retained frame wrappers (#35275)

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
OrcaWin
2026-10-28 13:00:39 -07:00
committed by GitHub
co-authored by OrcaWin
parent 30bb30e25c
commit 09bd3b96d4
2 changed files with 29 additions and 18 deletions
@@ -28,7 +28,7 @@ describe('HostFrameNavigationRegistry', () => {
expect(registry.shouldBlock(notebook, notebook, 'https://example.com')).toBe(false)
})
it('keeps pre-parse identity after name mutation and prunes destroyed frames', () => {
it('keeps pre-parse identity after name mutation and ignores destroyed frames', () => {
const registry = new HostFrameNavigationRegistry()
const plugin = frame({ id: 1, name: `${PLUGIN_PANEL_FRAME_NAME_PREFIX}demo` })
registry.register(plugin)
@@ -55,4 +55,26 @@ describe('HostFrameNavigationRegistry', () => {
const child = frame({ id: 4 })
expect(registry.shouldBlock(child, visual, 'https://example.com')).toBe(true)
})
it('does not inspect unrelated guarded frames during registration or navigation', () => {
const registry = new HostFrameNavigationRegistry()
let livenessChecks = 0
const panels = Array.from({ length: 100 }, (_, id) => ({
...frame({ id, name: `${PLUGIN_PANEL_FRAME_NAME_PREFIX}${id}` }),
isDestroyed: () => {
livenessChecks++
return false
}
}))
for (const panel of panels) {
registry.register(panel)
}
const ordinary = frame({ id: 101 })
registry.register(ordinary)
expect(registry.shouldBlock(ordinary, null, 'https://example.com')).toBe(false)
expect(registry.shouldBlock(panels[0], null, 'about:srcdoc')).toBe(false)
expect(registry.shouldBlock(panels[0], panels[0], 'https://example.com')).toBe(true)
expect(registry.shouldBlock(ordinary, panels[1], 'https://example.com')).toBe(true)
expect(livenessChecks).toBeLessThanOrEqual(4)
})
})
+6 -17
View File
@@ -13,7 +13,6 @@ const HOST_FRAME_KINDS = [
export type HostFrameKind = (typeof HOST_FRAME_KINDS)[number]['kind']
type RegisteredFrame = {
frame: NavigationFrame
kind: HostFrameKind
initialSrcdocPending: boolean
}
@@ -25,13 +24,12 @@ export function hostFrameKindForName(name: string): HostFrameKind | null {
/** Records host-marked frame identities at browsing-context creation, before their content can
* mutate window.name. */
export class HostFrameNavigationRegistry {
private readonly frames = new Map<number, RegisteredFrame>()
private frames = new WeakMap<NavigationFrame, RegisteredFrame>()
register(frame: NavigationFrame): void {
this.prune()
const kind = hostFrameKindForName(frame.name)
if (kind) {
this.frames.set(frame.frameTreeNodeId, { frame, kind, initialSrcdocPending: true })
this.frames.set(frame, { kind, initialSrcdocPending: true })
}
}
@@ -40,9 +38,8 @@ export class HostFrameNavigationRegistry {
initiator: NavigationFrame | null,
destinationUrl: string
): boolean {
this.prune()
const registeredTarget = frame ? this.frames.get(frame.frameTreeNodeId) : undefined
if (registeredTarget) {
const registeredTarget = frame ? this.frames.get(frame) : undefined
if (registeredTarget && frame && !frame.isDestroyed()) {
// Registration precedes srcdoc commit; allow its initial document only, then contain all navigation.
if (registeredTarget.initialSrcdocPending && destinationUrl === 'about:srcdoc') {
registeredTarget.initialSrcdocPending = false
@@ -50,19 +47,11 @@ export class HostFrameNavigationRegistry {
}
return true
}
return Boolean(initiator && this.frames.has(initiator.frameTreeNodeId))
return Boolean(initiator && this.frames.has(initiator) && !initiator.isDestroyed())
}
clear(): void {
this.frames.clear()
}
private prune(): void {
for (const [id, registered] of this.frames) {
if (registered.frame.isDestroyed()) {
this.frames.delete(id)
}
}
this.frames = new WeakMap()
}
}