fix(callbacks): preserve live realms during popup event dispatch

A popup-scoped identity lookup can miss a load callback created in its
parent or another live iframe. The retirement guard then rejects the
callback solely because its context has a Window token, causing the
Promise and Wasm entry-settings WPTs to time out.

Resolve the callback creation context's concrete registration when no
scoped identity was provided, then apply the existing currentness check.
Unregistered Window realms remain retired and captured identities retain
their original authority.

Add a Page-task regression that opens a popup through an iframe and checks
parent and live-child callbacks, their receivers and event targets. A
callback retained from a removed/reinserted iframe Window must not run.

Validation: cargo fmt --all; workspace/all-targets/all-features Clippy with
-D warnings; cargo nextest run --no-fail-fast with retries disabled
(19,166 passed, 16 skipped). The new regression passes 20 stress iterations;
the 60 popup and callback-retirement tests pass all three stress rounds.
This commit is contained in:
ldm0
2026-10-03 15:11:58 +08:00
parent 647abd9d59
commit 451451cec7
2 changed files with 89 additions and 1 deletions
+7 -1
View File
@@ -210,7 +210,13 @@ impl CallbackInvoker {
}
if let Some(host_ptr) = invocation.host_ptr {
let host = unsafe { &*host_ptr };
let is_retired = match invocation.relevant_identity {
// Popup dispatch uses scoped registrations in a shared V8
// context. A callback from another live Window can lack that
// scoped identity while its own concrete realm is still current.
let relevant_identity = invocation.relevant_identity.or_else(|| {
host.window_execution_context_identity_for_access_check(invocation.relevant_context)
});
let is_retired = match relevant_identity {
Some(identity) => !host.window_execution_context_identity_is_current(identity),
// A retained Window can keep its V8 global attached after its
// registry entry is removed. Missing authority is not an
@@ -1105,6 +1105,88 @@ async fn window_open_non_about_returns_lightweight_popup_and_dispatches_load() {
);
}
#[tokio::test]
async fn lightweight_popup_load_keeps_live_callback_realms_and_rejects_retired_ones() {
let loader = ResourceRequestClient::new(&moli_fetch::FetchConfig::default()).expect("loader");
let mut vm = new_storage_page_task_executor_test_vm_with_loader(
"https://popup-callback-realm.test/page.html",
&loader,
);
vm.eval(
r#"
globalThis.__popupCallbackRealmEvents = [];
const makeFrame = () => {
const frame = document.createElement("iframe");
(document.body || document.documentElement || document).appendChild(frame);
return frame;
};
globalThis.__popupSourceWindow = makeFrame().contentWindow;
globalThis.__popupLiveCallbackWindow = makeFrame().contentWindow;
globalThis.__popupRetiredCallbackFrame = makeFrame();
globalThis.__popupRetiredCallbackWindow = __popupRetiredCallbackFrame.contentWindow;
globalThis.__popupRetiredCallback = __popupRetiredCallbackWindow.Function(
"parent.__popupCallbackRealmEvents.push('retired');"
);
__popupRetiredCallbackFrame.remove();
"removed"
"#,
)
.expect("popup callback realms should be created before retirement");
vm.drain_ready_page_task_executor_turns_for_setup(&loader, 128)
.await
.expect("removed callback Window should retire through Page tasks");
assert_eq!(
vm.eval(
r#"
(document.body || document.documentElement || document).appendChild(__popupRetiredCallbackFrame);
String(__popupRetiredCallbackWindow !== __popupRetiredCallbackFrame.contentWindow)
"#,
)
.expect("reinserted iframe should expose a new Window"),
"true"
);
vm.eval(
r#"
const popupURL = URL.createObjectURL(new Blob([
"<!doctype html><title>cross-realm callback</title>"
], { type: "text/html" }));
globalThis.__popupCallbackRealmWindow = __popupSourceWindow.open(popupURL);
__popupCallbackRealmWindow.onload = function(event) {
__popupCallbackRealmEvents.push([
"parent", window === top, this === __popupCallbackRealmWindow, event.target === this
].join(":"));
};
__popupCallbackRealmWindow.addEventListener("load", __popupRetiredCallback);
__popupCallbackRealmWindow.addEventListener("load", __popupLiveCallbackWindow.Function(
"event",
`parent.__popupCallbackRealmEvents.push([
"child", window === parent.__popupLiveCallbackWindow,
this === parent.__popupCallbackRealmWindow, event.target === this
].join(":"));`
));
"queued"
"#,
)
.expect("popup load callbacks should register across Window realms");
advance_page_task_executor_until_eval_equals(
&mut vm,
&loader,
"String(__popupCallbackRealmEvents.includes('child:true:true:true'))",
"true",
"popup load callback from another live Window realm",
)
.await;
assert_eq!(
vm.eval("__popupCallbackRealmEvents.join('|')")
.expect("popup callback realm events should evaluate"),
"parent:true:true:true|child:true:true:true",
"live callback realms must run and reinsertion must not revive a retired callback"
);
}
#[tokio::test]
async fn lightweight_popup_document_write_during_load_replaces_existing_body() {
let loader = ResourceRequestClient::new(&moli_fetch::FetchConfig::default()).expect("loader");
let mut vm =