fix(window): hide shadow frames from indexed access

This commit is contained in:
ldm0
2026-09-27 19:23:03 +08:00
parent ebe7795f24
commit 9dc80da2ca
3 changed files with 52 additions and 30 deletions
@@ -19,7 +19,7 @@ impl JsContextHost {
}
pub(crate) fn child_browsing_context_count_for_document(&self, document: DomHandle) -> usize {
self.child_browsing_context_direct_frame_handles_for_document(document)
self.window_child_browsing_context_handles_for_document(document)
.len()
}
@@ -136,6 +136,22 @@ impl JsContextHost {
.collect()
}
fn window_child_browsing_context_handles_for_document(
&self,
document: DomHandle,
) -> Vec<DomHandle> {
// A frame inside a shadow tree still owns a browsing context, but it is
// not exposed through the containing Window's indexed properties.
self.child_browsing_context_direct_frame_handles_for_document(document)
.into_iter()
.filter(|handle| {
self.dom_host()
.node(*handle)
.is_some_and(|node| node.flags().in_document_tree())
})
.collect()
}
#[cfg(test)]
pub(crate) fn child_browsing_context_handle_by_index(&self, index: usize) -> Option<DomHandle> {
// Tests use this to inspect the global frame-tree projection rather
@@ -154,7 +170,7 @@ impl JsContextHost {
if self.child_browsing_contexts.is_empty() {
return None;
}
self.child_browsing_context_direct_frame_handles_for_document(document)
self.window_child_browsing_context_handles_for_document(document)
.into_iter()
.nth(index)
}
@@ -2857,3 +2857,37 @@ async fn embed_and_object_javascript_attributes_use_resource_fetch_not_script_na
"resource failures must not execute javascript or load, and object must enter fallback"
);
}
#[test]
fn iframe_in_shadow_tree_is_not_a_window_child_property() {
let mut vm = new_storage_test_vm("https://shadow-iframe-named-property.test/");
let result = vm
.eval(
r#"
const host = document.createElement('div');
(document.body || document.documentElement || document).appendChild(host);
const shadow = host.attachShadow({ mode: 'open' });
const shadowFrame = document.createElement('iframe');
shadowFrame.name = 'shadowTarget';
shadow.appendChild(shadowFrame);
const lightFrame = document.createElement('iframe');
lightFrame.name = 'lightTarget';
(document.body || document.documentElement || document).appendChild(lightFrame);
[
window.length,
window.frames.length,
window[0] === lightFrame.contentWindow,
window[1] === undefined,
'shadowTarget' in window,
window.shadowTarget === undefined,
shadowFrame.contentWindow !== null,
'lightTarget' in window,
window.lightTarget === lightFrame.contentWindow
].join('|')
"#,
)
.expect("shadow iframe named property probe should evaluate");
assert_eq!(result, "1|1|true|true|false|true|true|true|true");
}
@@ -470,35 +470,7 @@ frame.name = 'target';
assert_eq!(result, "[object Window]|true|object");
}
#[test]
fn iframe_in_shadow_tree_is_not_a_named_window_property() {
let mut vm = new_storage_test_vm("https://shadow-iframe-named-property.test/");
let result = vm
.eval(
r#"
const host = document.createElement('div');
(document.body || document.documentElement || document).appendChild(host);
const shadow = host.attachShadow({ mode: 'open' });
const shadowFrame = document.createElement('iframe');
shadowFrame.name = 'shadowTarget';
shadow.appendChild(shadowFrame);
const lightFrame = document.createElement('iframe');
lightFrame.name = 'lightTarget';
(document.body || document.documentElement || document).appendChild(lightFrame);
[
'shadowTarget' in window,
window.shadowTarget === undefined,
shadowFrame.contentWindow !== null,
'lightTarget' in window,
window.lightTarget === lightFrame.contentWindow
].join('|')
"#,
)
.expect("shadow iframe named property probe should evaluate");
assert_eq!(result, "false|true|true|true|true");
}
#[test]
fn child_webassembly_native_values_use_public_intrinsic_prototypes() {
let mut vm = new_storage_test_vm("https://child-wasm-intrinsics.test/");