LibWeb: Actually traverse the shadow root of the inclusive descendant

Previously, the inclusive descendant, which is the node that
for_each_shadow_including_inclusive_descendant was called on, would not
have it's shadow root traversed if it had one.

This is because the shadow root traversal was in the `for` loop, which
begins with the node's first child. The fix here is to move the shadow
root traversal outside of the loop, and check if the current node is an
element instead.
This commit is contained in:
Luke Wilde 2024-11-12 18:08:49 +00:00 committed by Andreas Kling
parent d3c21e4038
commit 6df4e5f5e7
Notes: github-actions[bot] 2024-11-13 13:41:02 +00:00
3 changed files with 37 additions and 12 deletions

View file

@ -97,21 +97,37 @@ private:
template<>
inline bool Node::fast_is<ShadowRoot>() const { return node_type() == to_underlying(NodeType::DOCUMENT_FRAGMENT_NODE) && is_shadow_root(); }
// https://dom.spec.whatwg.org/#concept-shadow-including-tree-order
// In shadow-including tree order is shadow-including preorder, depth-first traversal of a node tree.
// Shadow-including preorder, depth-first traversal of a node tree tree is preorder, depth-first traversal
// of tree, with for each shadow host encountered in tree, shadow-including preorder, depth-first traversal
// of that elements shadow roots node tree just after it is encountered.
// https://dom.spec.whatwg.org/#concept-shadow-including-descendant
// An object A is a shadow-including descendant of an object B, if A is a descendant of B, or As root is a
// shadow root and As roots host is a shadow-including inclusive descendant of B.
// https://dom.spec.whatwg.org/#concept-shadow-including-inclusive-descendant
// A shadow-including inclusive descendant is an object or one of its shadow-including descendants.
template<typename Callback>
inline TraversalDecision Node::for_each_shadow_including_inclusive_descendant(Callback callback)
{
if (callback(*this) == TraversalDecision::Break)
return TraversalDecision::Break;
for (auto* child = first_child(); child; child = child->next_sibling()) {
if (child->is_element()) {
if (auto shadow_root = static_cast<Element*>(child)->shadow_root()) {
if (shadow_root->for_each_shadow_including_inclusive_descendant(callback) == TraversalDecision::Break)
return TraversalDecision::Break;
}
if (is_element()) {
if (auto shadow_root = static_cast<Element*>(this)->shadow_root()) {
if (shadow_root->for_each_shadow_including_inclusive_descendant(callback) == TraversalDecision::Break)
return TraversalDecision::Break;
}
}
for (auto* child = first_child(); child; child = child->next_sibling()) {
if (child->for_each_shadow_including_inclusive_descendant(callback) == TraversalDecision::Break)
return TraversalDecision::Break;
}
return TraversalDecision::Continue;
}
@ -119,12 +135,6 @@ template<typename Callback>
inline TraversalDecision Node::for_each_shadow_including_descendant(Callback callback)
{
for (auto* child = first_child(); child; child = child->next_sibling()) {
if (child->is_element()) {
if (JS::GCPtr<ShadowRoot> shadow_root = static_cast<Element*>(child)->shadow_root()) {
if (shadow_root->for_each_shadow_including_inclusive_descendant(callback) == TraversalDecision::Break)
return TraversalDecision::Break;
}
}
if (child->for_each_shadow_including_inclusive_descendant(callback) == TraversalDecision::Break)
return TraversalDecision::Break;
}

View file

@ -0,0 +1 @@
Hello from script in the shadow root of the just inserted div!

View file

@ -0,0 +1,14 @@
<!DOCTYPE html>
<script src="../include.js"></script>
<script>
test(() => {
const div = document.createElement("div");
const shadowRoot = div.attachShadow({ mode: "closed" });
const script = document.createElement("script");
script.innerText = "println('Hello from script in the shadow root of the just inserted div!')";
shadowRoot.appendChild(script);
document.body.appendChild(div);
});
</script>