Don't recurse in Node::GetRootNode (#35725)

* Don't recurse in Node::GetRootNode

This causes servo to crash when computing
the root of deeply nested shadow roots.

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>

* Add test case

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>

---------

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
This commit is contained in:
Simon Wülker 2025-03-01 18:19:27 +01:00 committed by GitHub
parent ce977636f6
commit 25cc675101
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 59 additions and 13 deletions

View file

@ -2887,19 +2887,16 @@ impl NodeMethods<crate::DomTypeHolder> for Node {
/// <https://dom.spec.whatwg.org/#dom-node-getrootnode>
fn GetRootNode(&self, options: &GetRootNodeOptions) -> DomRoot<Node> {
if !options.composed {
if let Some(shadow_root) = self.containing_shadow_root() {
return if options.composed {
// shadow-including root.
shadow_root.Host().upcast::<Node>().GetRootNode(options)
} else {
DomRoot::from_ref(shadow_root.upcast::<Node>())
};
return DomRoot::upcast(shadow_root);
}
}
if self.is_in_a_document_tree() {
if self.is_connected() {
DomRoot::from_ref(self.owner_doc().upcast::<Node>())
} else {
self.inclusive_ancestors(ShadowIncluding::No)
self.inclusive_ancestors(ShadowIncluding::Yes)
.last()
.unwrap()
}

View file

@ -13496,14 +13496,14 @@
]
],
"interfaces.https.html": [
"6c48986beea5c8aca4e54c6da0fb8f3b7a4390e0",
"81f4d942f94366d8f9ecf22cfc3e1e22fe4ab8f1",
[
null,
{}
]
],
"interfaces.worker.js": [
"4ac5c822dcc6fbb021050ac2ab2ba4d04452d945",
"06eb8d3ba2334951cb1e0f791527ba118d4f13ec",
[
"mozilla/interfaces.worker.html",
{}
@ -14337,6 +14337,15 @@
]
]
},
"shadow-dom": {
"getrootnode-in-deeply-nested-shadow.html": [
"355cc270e6020f40176afac26c49a14a59b67169",
[
null,
{}
]
]
},
"webxr": {
"create_session.https.html": [
"5b5d485b372bfffb22204bc162c9e182306395cb",

1
tests/wpt/mozilla/meta/__dir__.ini vendored Normal file
View file

@ -0,0 +1 @@
prefs: ["dom_imagebitmap_enabled:true", "dom_offscreen_canvas_enabled:true", "dom_shadowdom_enabled:true", "dom_xpath_enabled:true", "dom_intersection_observer_enabled:true", "dom_resize_observer_enabled:true", "dom_notification_enabled:true", "dom_fontface_enabled:true"]

View file

@ -1,2 +0,0 @@
[partial_shadow_dom_layout_style.html]
expected: FAIL

View file

@ -92,6 +92,7 @@ test_interfaces([
"FileList",
"FileReader",
"FocusEvent",
"FontFace",
"FontFaceSet",
"FormData",
"FormDataEvent",
@ -181,7 +182,10 @@ test_interfaces([
"IIRFilterNode",
"ImageData",
"Image",
"ImageBitmap",
"InputEvent",
"IntersectionObserver",
"IntersectionObserverEntry",
"KeyboardEvent",
"Location",
"MediaElementAudioSourceNode",
@ -213,6 +217,8 @@ test_interfaces([
"Notification",
"OfflineAudioCompletionEvent",
"OfflineAudioContext",
"OffscreenCanvas",
"OffscreenCanvasRenderingContext2D",
"Option",
"OscillatorNode",
"PageTransitionEvent",
@ -242,6 +248,9 @@ test_interfaces([
"ReadableByteStreamController",
"ReadableStreamBYOBRequest",
"Request",
"ResizeObserver",
"ResizeObserverEntry",
"ResizeObserverSize",
"Response",
"Screen",
"SecurityPolicyViolationEvent",
@ -303,6 +312,9 @@ test_interfaces([
"XMLHttpRequestEventTarget",
"XMLHttpRequestUpload",
"XMLSerializer",
"XPathEvaluator",
"XPathExpression",
"XPathResult",
"XRBoundedReferenceSpace",
"XRFrame",
"XRHand",

View file

@ -37,11 +37,14 @@ test_interfaces([
"FormData",
"Headers",
"History",
"ImageBitmap",
"ImageData",
"MessageChannel",
"MessageEvent",
"MessagePort",
"Notification",
"OffscreenCanvas",
"OffscreenCanvasRenderingContext2D",
"Performance",
"PerformanceEntry",
"PerformanceMark",

View file

@ -0,0 +1,26 @@
<!--https://github.com/servo/servo/issues/35724 -->
<html>
<head>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body>
<script>
'use strict';
test(() => {
let original_host = document.createElement("div");
original_host.id = "original-host";
let current_host = original_host;
for (let i = 0; i < 10000; i++) {
let root = current_host.attachShadow({ mode: "open" });
let new_host = document.createElement("div");
root.appendChild(new_host);
current_host = new_host;
}
let root_of_tree = current_host.getRootNode({ composed: true });
assert_equals(root_of_tree.id, "original-host");
}, "Calling Node.getRootNode in a deeply nested shadow tree must return the correct result without crashing");
</script>
</body>
</html>