diff --git a/moli-renderer-v8/src/context_bootstrap/window_accessors/helpers.rs b/moli-renderer-v8/src/context_bootstrap/window_accessors/helpers.rs index 26a1d871d2..e83f16e785 100644 --- a/moli-renderer-v8/src/context_bootstrap/window_accessors/helpers.rs +++ b/moli-renderer-v8/src/context_bootstrap/window_accessors/helpers.rs @@ -48,6 +48,19 @@ pub(in crate::context_bootstrap) fn window_child_context_handle<'s>( if marked_handle.is_some() { return marked_handle; } + // A callback can run promise reactions from another realm before its + // legacy dispatch marker is restored. The holder's native realm remains + // authoritative for WindowProperties, including a top-level holder. + if let Some(context) = receiver.get_creation_context(scope) + && let Some(host_ptr) = context_host_ptr_from_context_slot(context) + && let Some(identity) = + unsafe { &*host_ptr }.window_execution_context_identity_for_access_check(context) + { + return unsafe { &*host_ptr } + .window_execution_context_identity_is_current(identity) + .then(|| identity.dispatch_scope().child_window()) + .flatten(); + } if let Some(handle) = crate::native_bridge::active_child_window_handle(scope) { if receiver_is_current_global { return Some(handle); @@ -76,22 +89,7 @@ pub(in crate::context_bootstrap) fn window_child_context_handle<'s>( } } - // WindowProperties is a per-realm prototype-chain object rather than the - // global proxy, so it has no child-handle marker of its own. Resolve its - // native Window identity through the creation context, mirroring Blink's - // native DOMWindow association on WindowProperties. - let receiver_context = receiver.get_creation_context(scope)?; - let host_ptr = context_host_ptr_from_context_slot(receiver_context)?; - let host = unsafe { &*host_ptr }; - let identity = host.window_execution_context_identity_for_access_check(receiver_context)?; - if !host.window_execution_context_identity_is_current(identity) { - return None; - } - match identity.dispatch_scope() { - crate::native_bridge::OwnerDispatchScope::Child(handle) => Some(handle), - crate::native_bridge::OwnerDispatchScope::Top - | crate::native_bridge::OwnerDispatchScope::LightweightPopup(_) => None, - } + None } pub(crate) fn window_host_ptr( diff --git a/moli-renderer-v8/src/script_vm/tests/dom_elements/dom_surface.rs b/moli-renderer-v8/src/script_vm/tests/dom_elements/dom_surface.rs index 5f01fecaa6..bae007532e 100644 --- a/moli-renderer-v8/src/script_vm/tests/dom_elements/dom_surface.rs +++ b/moli-renderer-v8/src/script_vm/tests/dom_elements/dom_surface.rs @@ -14524,3 +14524,36 @@ fn child_browsing_context_lookup_tolerates_document_handle_cycle() { Some(handle) ); } + +#[test] +fn window_named_access_uses_its_realm_during_child_history_callbacks() { + let mut vm = new_storage_test_vm("https://joint-history.test/parent"); + vm.eval( + r#" + const frame = document.createElement('iframe'); + frame.id = 'topFrame'; + frame.srcdoc = '

child

'; + (document.body || document.documentElement || document).appendChild(frame); + "#, + ) + .unwrap(); + vm.drain_pending_child_frame_work_for_test(); + let child = vm + ._context_host + .borrow() + .child_browsing_context_handles_in_document_order()[0]; + let context = &vm.page_default_context as *const v8::Global; + vm.with_context_scope_by_ptr(context, |scope, _| { + crate::native_bridge::enter_active_child_window_scope(scope, Some(child)); + Ok(()) + }) + .unwrap(); + let result = + vm.eval("[topFrame.id, typeof childOnly, frame.contentWindow.childOnly.id].join('|')"); + vm.with_context_scope_by_ptr(context, |scope, _| { + crate::native_bridge::enter_active_child_window_scope(scope, None); + Ok(()) + }) + .unwrap(); + assert_eq!(result.unwrap(), "topFrame|undefined|childOnly"); +}