fix(window): resolve named properties in the holder realm

This commit is contained in:
ldm0
2026-09-22 23:04:44 +08:00
committed by Donough Liu
parent 199abbb005
commit 8e7be5c3fb
2 changed files with 47 additions and 16 deletions
@@ -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(
@@ -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 = '<p id="childOnly">child</p>';
(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<v8::Context>;
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");
}