From 451451cec7b3d9426d024a5830565faa510b4e3b Mon Sep 17 00:00:00 2001 From: ldm0 Date: Sat, 3 Oct 2026 15:11:58 +0800 Subject: [PATCH] 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. --- moli-renderer-v8/src/callback_invocation.rs | 8 +- .../misc/extracted/popup_window.rs | 82 +++++++++++++++++++ 2 files changed, 89 insertions(+), 1 deletion(-) diff --git a/moli-renderer-v8/src/callback_invocation.rs b/moli-renderer-v8/src/callback_invocation.rs index 3e3bfbc427..8ef688f301 100644 --- a/moli-renderer-v8/src/callback_invocation.rs +++ b/moli-renderer-v8/src/callback_invocation.rs @@ -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 diff --git a/moli-renderer-v8/src/script_vm/tests/browser_api/misc/extracted/popup_window.rs b/moli-renderer-v8/src/script_vm/tests/browser_api/misc/extracted/popup_window.rs index 2ffe05e8a8..a7285c5c49 100644 --- a/moli-renderer-v8/src/script_vm/tests/browser_api/misc/extracted/popup_window.rs +++ b/moli-renderer-v8/src/script_vm/tests/browser_api/misc/extracted/popup_window.rs @@ -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([ + "cross-realm callback" +], { 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 =