From f1e89c58a61c15b7d76343f1eb1f950e6ff43103 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20W=C3=BClker?= Date: Mon, 2 Dec 2024 19:33:25 +0100 Subject: [PATCH] Don't register unconnected shadow roots with their owner document (#34361) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Don't falsely register Shadow Roots as connected Previously, a shadowroot would be registered as connected during the shadow hosts bind_to_tree call, even if the host was being bound to an element that was not itself connected to a document. Signed-off-by: Simon Wülker * Update WPT expectations Signed-off-by: Simon Wülker * Move bind/unbind methods into a VirtualMethod impl Signed-off-by: Simon Wülker * Add DocumentFragment/Shadowroot to vtable_for Signed-off-by: Simon Wülker --------- Signed-off-by: Simon Wülker --- components/script/dom/documentfragment.rs | 7 +++ components/script/dom/element.rs | 24 +++------ components/script/dom/shadowroot.rs | 47 +++++++++++++++- components/script/dom/virtualmethods.rs | 14 +++-- .../CustomElementRegistry.html.ini | 1 - .../custom-element-registry/upgrade.html.ini | 1 - .../radio-disconnected-group-owner.html.ini | 1 - ...dialog-focus-shadow-double-nested.html.ini | 6 ++- .../Extensions-to-Event-Interface.html.ini | 33 +++++++++++- .../event-with-related-target.html.ini | 54 ++++++++++++++++++- 10 files changed, 161 insertions(+), 27 deletions(-) diff --git a/components/script/dom/documentfragment.rs b/components/script/dom/documentfragment.rs index bbf382c584f..2f57692e1fb 100644 --- a/components/script/dom/documentfragment.rs +++ b/components/script/dom/documentfragment.rs @@ -20,6 +20,7 @@ use crate::dom::element::Element; use crate::dom::htmlcollection::HTMLCollection; use crate::dom::node::{window_from_node, Node}; use crate::dom::nodelist::NodeList; +use crate::dom::virtualmethods::VirtualMethods; use crate::dom::window::Window; use crate::script_runtime::CanGc; @@ -132,3 +133,9 @@ impl DocumentFragmentMethods for DocumentFragment { self.upcast::().query_selector_all(selectors) } } + +impl VirtualMethods for DocumentFragment { + fn super_type(&self) -> Option<&dyn VirtualMethods> { + Some(self.upcast::() as &dyn VirtualMethods) + } +} diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index 1ee2169cfd6..88963521d71 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -552,9 +552,11 @@ impl Element { .upcast::() .set_containing_shadow_root(Some(&shadow_root)); - if self.is_connected() { - self.node.owner_doc().register_shadow_root(&shadow_root); - } + let bind_context = BindContext { + tree_connected: self.upcast::().is_connected(), + tree_in_doc: self.upcast::().is_in_doc(), + }; + shadow_root.bind_to_tree(&bind_context); self.upcast::().dirty(NodeDamage::OtherNodeDamage); @@ -3586,13 +3588,7 @@ impl VirtualMethods for Element { let doc = document_from_node(self); if let Some(ref shadow_root) = self.shadow_root() { - doc.register_shadow_root(shadow_root); - let shadow_root = shadow_root.upcast::(); - shadow_root.set_flag(NodeFlags::IS_CONNECTED, context.tree_connected); - for node in shadow_root.children() { - node.set_flag(NodeFlags::IS_CONNECTED, context.tree_connected); - node.bind_to_tree(context); - } + shadow_root.bind_to_tree(context); } if !context.tree_connected { @@ -3637,13 +3633,7 @@ impl VirtualMethods for Element { let doc = document_from_node(self); if let Some(ref shadow_root) = self.shadow_root() { - doc.unregister_shadow_root(shadow_root); - let shadow_root = shadow_root.upcast::(); - shadow_root.set_flag(NodeFlags::IS_CONNECTED, false); - for node in shadow_root.children() { - node.set_flag(NodeFlags::IS_CONNECTED, false); - node.unbind_from_tree(context); - } + shadow_root.unbind_from_tree(context); } let fullscreen = doc.GetFullscreenElement(); diff --git a/components/script/dom/shadowroot.rs b/components/script/dom/shadowroot.rs index ca53ac9606d..58fb42b57da 100644 --- a/components/script/dom/shadowroot.rs +++ b/components/script/dom/shadowroot.rs @@ -24,8 +24,11 @@ use crate::dom::document::Document; use crate::dom::documentfragment::DocumentFragment; use crate::dom::documentorshadowroot::{DocumentOrShadowRoot, StyleSheetInDocument}; use crate::dom::element::Element; -use crate::dom::node::{Node, NodeDamage, NodeFlags, ShadowIncluding, UnbindContext}; +use crate::dom::node::{ + document_from_node, BindContext, Node, NodeDamage, NodeFlags, ShadowIncluding, UnbindContext, +}; use crate::dom::stylesheetlist::{StyleSheetList, StyleSheetListOwner}; +use crate::dom::virtualmethods::VirtualMethods; use crate::dom::window::Window; use crate::script_runtime::CanGc; use crate::stylesheet_set::StylesheetSetRef; @@ -280,6 +283,48 @@ impl ShadowRootMethods for ShadowRoot { } } +impl VirtualMethods for ShadowRoot { + fn super_type(&self) -> Option<&dyn VirtualMethods> { + Some(self.upcast::() as &dyn VirtualMethods) + } + + fn bind_to_tree(&self, context: &BindContext) { + if let Some(s) = self.super_type() { + s.bind_to_tree(context); + } + + if context.tree_connected { + let document = document_from_node(self); + document.register_shadow_root(self); + } + + let shadow_root = self.upcast::(); + shadow_root.set_flag(NodeFlags::IS_CONNECTED, context.tree_connected); + for node in shadow_root.children() { + node.set_flag(NodeFlags::IS_CONNECTED, context.tree_connected); + node.bind_to_tree(context); + } + } + + fn unbind_from_tree(&self, context: &UnbindContext) { + if let Some(s) = self.super_type() { + s.unbind_from_tree(context); + } + + if context.tree_connected { + let document = document_from_node(self); + document.unregister_shadow_root(self); + } + + let shadow_root = self.upcast::(); + shadow_root.set_flag(NodeFlags::IS_CONNECTED, false); + for node in shadow_root.children() { + node.set_flag(NodeFlags::IS_CONNECTED, false); + node.unbind_from_tree(context); + } + } +} + #[allow(unsafe_code)] pub trait LayoutShadowRootHelpers<'dom> { fn get_host_for_layout(self) -> LayoutDom<'dom, Element>; diff --git a/components/script/dom/virtualmethods.rs b/components/script/dom/virtualmethods.rs index 26cee31d302..16516356e83 100644 --- a/components/script/dom/virtualmethods.rs +++ b/components/script/dom/virtualmethods.rs @@ -5,14 +5,14 @@ use html5ever::LocalName; use style::attr::AttrValue; -use super::htmltablecolelement::HTMLTableColElement; use crate::dom::attr::Attr; use crate::dom::bindings::inheritance::{ - Castable, ElementTypeId, HTMLElementTypeId, HTMLMediaElementTypeId, NodeTypeId, - SVGElementTypeId, SVGGraphicsElementTypeId, + Castable, DocumentFragmentTypeId, ElementTypeId, HTMLElementTypeId, HTMLMediaElementTypeId, + NodeTypeId, SVGElementTypeId, SVGGraphicsElementTypeId, }; use crate::dom::bindings::str::DOMString; use crate::dom::document::Document; +use crate::dom::documentfragment::DocumentFragment; use crate::dom::element::{AttributeMutation, Element}; use crate::dom::event::Event; use crate::dom::htmlanchorelement::HTMLAnchorElement; @@ -46,6 +46,7 @@ use crate::dom::htmlselectelement::HTMLSelectElement; use crate::dom::htmlsourceelement::HTMLSourceElement; use crate::dom::htmlstyleelement::HTMLStyleElement; use crate::dom::htmltablecellelement::HTMLTableCellElement; +use crate::dom::htmltablecolelement::HTMLTableColElement; use crate::dom::htmltableelement::HTMLTableElement; use crate::dom::htmltablerowelement::HTMLTableRowElement; use crate::dom::htmltablesectionelement::HTMLTableSectionElement; @@ -54,6 +55,7 @@ use crate::dom::htmltextareaelement::HTMLTextAreaElement; use crate::dom::htmltitleelement::HTMLTitleElement; use crate::dom::htmlvideoelement::HTMLVideoElement; use crate::dom::node::{BindContext, ChildrenMutation, CloneChildrenFlag, Node, UnbindContext}; +use crate::dom::shadowroot::ShadowRoot; use crate::dom::svgelement::SVGElement; use crate::dom::svgsvgelement::SVGSVGElement; @@ -283,6 +285,12 @@ pub fn vtable_for(node: &Node) -> &dyn VirtualMethods { node.downcast::().unwrap() as &dyn VirtualMethods }, NodeTypeId::Element(_) => node.downcast::().unwrap() as &dyn VirtualMethods, + NodeTypeId::DocumentFragment(DocumentFragmentTypeId::ShadowRoot) => { + node.downcast::().unwrap() as &dyn VirtualMethods + }, + NodeTypeId::DocumentFragment(_) => { + node.downcast::().unwrap() as &dyn VirtualMethods + }, _ => node as &dyn VirtualMethods, } } diff --git a/tests/wpt/meta/custom-elements/CustomElementRegistry.html.ini b/tests/wpt/meta/custom-elements/CustomElementRegistry.html.ini index 7b89879bb01..7b862b2d71c 100644 --- a/tests/wpt/meta/custom-elements/CustomElementRegistry.html.ini +++ b/tests/wpt/meta/custom-elements/CustomElementRegistry.html.ini @@ -1,5 +1,4 @@ [CustomElementRegistry.html] - expected: CRASH [customElements.define must upgrade elements in the shadow-including tree order] expected: FAIL diff --git a/tests/wpt/meta/custom-elements/custom-element-registry/upgrade.html.ini b/tests/wpt/meta/custom-elements/custom-element-registry/upgrade.html.ini index 78c1c9a2b99..21545b9e453 100644 --- a/tests/wpt/meta/custom-elements/custom-element-registry/upgrade.html.ini +++ b/tests/wpt/meta/custom-elements/custom-element-registry/upgrade.html.ini @@ -1,4 +1,3 @@ [upgrade.html] - expected: CRASH [Two elements as shadow-including descendants (and not descendants) of the upgraded node] expected: FAIL diff --git a/tests/wpt/meta/html/semantics/forms/the-input-element/radio-disconnected-group-owner.html.ini b/tests/wpt/meta/html/semantics/forms/the-input-element/radio-disconnected-group-owner.html.ini index 404eea105e2..225fb3f96e3 100644 --- a/tests/wpt/meta/html/semantics/forms/the-input-element/radio-disconnected-group-owner.html.ini +++ b/tests/wpt/meta/html/semantics/forms/the-input-element/radio-disconnected-group-owner.html.ini @@ -1,5 +1,4 @@ [radio-disconnected-group-owner.html] - expected: CRASH [Removed elements are moved into separate radio groups.] expected: FAIL diff --git a/tests/wpt/meta/html/semantics/interactive-elements/the-dialog-element/dialog-focus-shadow-double-nested.html.ini b/tests/wpt/meta/html/semantics/interactive-elements/the-dialog-element/dialog-focus-shadow-double-nested.html.ini index 5108f6d77ee..e2018508f8b 100644 --- a/tests/wpt/meta/html/semantics/interactive-elements/the-dialog-element/dialog-focus-shadow-double-nested.html.ini +++ b/tests/wpt/meta/html/semantics/interactive-elements/the-dialog-element/dialog-focus-shadow-double-nested.html.ini @@ -1,2 +1,6 @@ [dialog-focus-shadow-double-nested.html] - expected: CRASH + [show()] + expected: FAIL + + [showModal()] + expected: FAIL diff --git a/tests/wpt/meta/shadow-dom/Extensions-to-Event-Interface.html.ini b/tests/wpt/meta/shadow-dom/Extensions-to-Event-Interface.html.ini index dabcc9a71a4..86cec313299 100644 --- a/tests/wpt/meta/shadow-dom/Extensions-to-Event-Interface.html.ini +++ b/tests/wpt/meta/shadow-dom/Extensions-to-Event-Interface.html.ini @@ -1,2 +1,33 @@ [Extensions-to-Event-Interface.html] - expected: CRASH + [composedPath() must return an empty array when the event is no longer dispatched] + expected: FAIL + + [composed on EventInit must default to false] + expected: FAIL + + [composed on EventInit must set the composed flag] + expected: FAIL + + [The event must propagate out of open mode shadow boundaries when the composed flag is set] + expected: FAIL + + [The event must propagate out of closed mode shadow boundaries when the composed flag is set] + expected: FAIL + + [The event must not propagate out of open mode shadow tree of the target but must propagate out of inner shadow trees when the scoped flag is set] + expected: FAIL + + [The event must not propagate out of closed mode shadow tree of the target but must propagate out of inner shadow trees when the scoped flag is set] + expected: FAIL + + [The event must propagate out of open mode shadow tree in which the relative target and the relative related target are the same] + expected: FAIL + + [The event must propagate out of closed mode shadow tree in which the relative target and the relative related target are the same] + expected: FAIL + + [composedPath() must contain and only contain the unclosed nodes of target in open mode shadow trees] + expected: FAIL + + [composedPath() must contain and only contain the unclosed nodes of target in closed mode shadow trees] + expected: FAIL diff --git a/tests/wpt/meta/shadow-dom/event-with-related-target.html.ini b/tests/wpt/meta/shadow-dom/event-with-related-target.html.ini index dab914708ee..71e61882548 100644 --- a/tests/wpt/meta/shadow-dom/event-with-related-target.html.ini +++ b/tests/wpt/meta/shadow-dom/event-with-related-target.html.ini @@ -1,2 +1,54 @@ [event-with-related-target.html] - expected: CRASH + [Firing an event at B1a with relatedNode at B1 with open mode shadow trees] + expected: FAIL + + [Firing an event at B1a with relatedNode at B1 with closed mode shadow trees] + expected: FAIL + + [Firing an event at B1a with relatedNode at B1b1 with open mode shadow trees] + expected: FAIL + + [Firing an event at B1a with relatedNode at B1b1 with closed mode shadow trees] + expected: FAIL + + [Firing an event at B1b1 with relatedNode at B1a with open mode shadow trees] + expected: FAIL + + [Firing an event at B1b1 with relatedNode at B1a with closed mode shadow trees] + expected: FAIL + + [Firing an event at B1a with relatedNode at D1 with open mode shadow trees] + expected: FAIL + + [Firing an event at B1a with relatedNode at D1 with closed mode shadow trees] + expected: FAIL + + [Firing an event at D1 with relatedNode at B1a with open mode shadow trees] + expected: FAIL + + [Firing an event at D1 with relatedNode at B1a with closed mode shadow trees] + expected: FAIL + + [Firing an event at B1a with relatedNode at A1a with open mode shadow trees] + expected: FAIL + + [Firing an event at B1a with relatedNode at A1a with closed mode shadow trees] + expected: FAIL + + [Firing an event at A1a with relatedNode at B1a with open mode shadow trees] + expected: FAIL + + [Firing an event at A1a with relatedNode at B1a with closed mode shadow trees] + expected: FAIL + + [Firing an event at B1a with relatedNode at A1a (detached) with open mode shadow trees] + expected: FAIL + + [Firing an event at B1a with relatedNode at A1a (detached) with closed mode shadow trees] + expected: FAIL + + [Firing an event at A1a with relatedNode at B1a (detached) with open mode shadow trees] + expected: FAIL + + [Firing an event at A1a with relatedNode at B1a (detached) with closed mode shadow trees] + expected: FAIL