diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index b5f31e89bf6..57dfa35e501 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -737,11 +737,7 @@ impl Element { .upcast::() .set_containing_shadow_root(Some(&shadow_root)); - let bind_context = BindContext { - tree_connected: self.upcast::().is_connected(), - tree_is_in_a_document_tree: self.upcast::().is_in_a_document_tree(), - tree_is_in_a_shadow_tree: true, - }; + let bind_context = BindContext::new(&self.node); shadow_root.bind_to_tree(&bind_context, can_gc); let node = self.upcast::(); diff --git a/components/script/dom/html/htmlimageelement.rs b/components/script/dom/html/htmlimageelement.rs index 8121a387da5..d56145219e0 100644 --- a/components/script/dom/html/htmlimageelement.rs +++ b/components/script/dom/html/htmlimageelement.rs @@ -1926,6 +1926,7 @@ impl VirtualMethods for HTMLImageElement { } } + /// fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) { if let Some(s) = self.super_type() { s.bind_to_tree(context, can_gc); @@ -1935,23 +1936,24 @@ impl VirtualMethods for HTMLImageElement { document.register_responsive_image(self); } - // The element is inserted into a picture parent element - // https://html.spec.whatwg.org/multipage/#relevant-mutations - if let Some(parent) = self.upcast::().GetParentElement() { - if parent.is::() { - self.update_the_image_data(can_gc); - } + let parent = self.upcast::().GetParentNode().unwrap(); + + // Step 1. If insertedNode's parent is a picture element, then, count this as a relevant + // mutation for insertedNode. + if parent.is::() && std::ptr::eq(&*parent, context.parent) { + self.update_the_image_data(can_gc); } } + /// fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) { self.super_type().unwrap().unbind_from_tree(context, can_gc); let document = self.owner_document(); document.unregister_responsive_image(self); - // The element is removed from a picture parent element - // https://html.spec.whatwg.org/multipage/#relevant-mutations - if context.parent.is::() { + // Step 1. If oldParent is a picture element, then, count this as a relevant mutation for + // removedNode. + if context.parent.is::() && !self.upcast::().has_parent() { self.update_the_image_data(can_gc); } } diff --git a/components/script/dom/html/htmlsourceelement.rs b/components/script/dom/html/htmlsourceelement.rs index daa2796b315..761508d4605 100644 --- a/components/script/dom/html/htmlsourceelement.rs +++ b/components/script/dom/html/htmlsourceelement.rs @@ -17,6 +17,7 @@ use crate::dom::element::AttributeMutation; use crate::dom::html::htmlelement::HTMLElement; use crate::dom::html::htmlimageelement::HTMLImageElement; use crate::dom::html::htmlmediaelement::HTMLMediaElement; +use crate::dom::html::htmlpictureelement::HTMLPictureElement; use crate::dom::node::{BindContext, Node, UnbindContext}; use crate::dom::virtualmethods::VirtualMethods; use crate::script_runtime::CanGc; @@ -81,40 +82,60 @@ impl VirtualMethods for HTMLSourceElement { &local_name!("sizes") | &local_name!("media") | &local_name!("type") => { - let next_sibling_iterator = self.upcast::().following_siblings(); - HTMLSourceElement::iterate_next_html_image_element_siblings( - next_sibling_iterator, - CanGc::note(), - ); + if let Some(parent) = self.upcast::().GetParentElement() { + if parent.is::() { + let next_sibling_iterator = self.upcast::().following_siblings(); + HTMLSourceElement::iterate_next_html_image_element_siblings( + next_sibling_iterator, + can_gc, + ); + } + } }, _ => {}, } } - /// + /// fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) { self.super_type().unwrap().bind_to_tree(context, can_gc); - let parent = self.upcast::().GetParentNode().unwrap(); - if let Some(media) = parent.downcast::() { - media.handle_source_child_insertion(CanGc::note()); - } - let next_sibling_iterator = self.upcast::().following_siblings(); - HTMLSourceElement::iterate_next_html_image_element_siblings( - next_sibling_iterator, - CanGc::note(), - ); - } - fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) { - self.super_type().unwrap().unbind_from_tree(context, can_gc); - if let Some(next_sibling) = context.next_sibling { - let next_sibling_iterator = next_sibling.inclusively_following_siblings(); + // Step 1. Let parent be insertedNode's parent. + let parent = self.upcast::().GetParentNode().unwrap(); + + // Step 2. If parent is a media element that has no src attribute and whose networkState has + // the value NETWORK_EMPTY, then invoke that media element's resource selection algorithm. + if let Some(media) = parent.downcast::() { + media.handle_source_child_insertion(can_gc); + } + + // Step 3. If parent is a picture element, then for each child of parent's children, if + // child is an img element, then count this as a relevant mutation for child. + if parent.is::() && std::ptr::eq(&*parent, context.parent) { + let next_sibling_iterator = self.upcast::().following_siblings(); HTMLSourceElement::iterate_next_html_image_element_siblings( next_sibling_iterator, - CanGc::note(), + can_gc, ); } } + + /// + fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) { + self.super_type().unwrap().unbind_from_tree(context, can_gc); + + // Step 1. If oldParent is a picture element, then for each child of oldParent's children, + // if child is an img element, then count this as a relevant mutation for child. + if context.parent.is::() && !self.upcast::().has_parent() { + if let Some(next_sibling) = context.next_sibling { + let next_sibling_iterator = next_sibling.inclusively_following_siblings(); + HTMLSourceElement::iterate_next_html_image_element_siblings( + next_sibling_iterator, + can_gc, + ); + } + } + } } impl HTMLSourceElementMethods for HTMLSourceElement { diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index a0f3e7e5dad..85e7ceee0b6 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -300,6 +300,8 @@ impl Node { let parent_is_connected = self.is_connected(); let parent_is_in_ua_widget = self.is_in_ua_widget(); + let context = BindContext::new(self); + for node in new_child.traverse_preorder(ShadowIncluding::No) { if parent_in_shadow_tree { if let Some(shadow_root) = self.containing_shadow_root() { @@ -317,14 +319,7 @@ impl Node { // Out-of-document elements never have the descendants flag set. debug_assert!(!node.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS)); - vtable_for(&node).bind_to_tree( - &BindContext { - tree_connected: parent_is_connected, - tree_is_in_a_document_tree: parent_is_in_a_document_tree, - tree_is_in_a_shadow_tree: parent_in_shadow_tree, - }, - can_gc, - ); + vtable_for(&node).bind_to_tree(&context, can_gc); } } @@ -4259,7 +4254,10 @@ impl<'a> ChildrenMutation<'a> { } /// The context of the binding to tree of a node. -pub(crate) struct BindContext { +pub(crate) struct BindContext<'a> { + /// The parent of the inclusive ancestor that was inserted. + pub(crate) parent: &'a Node, + /// Whether the tree is connected. /// /// @@ -4274,7 +4272,17 @@ pub(crate) struct BindContext { pub(crate) tree_is_in_a_shadow_tree: bool, } -impl BindContext { +impl<'a> BindContext<'a> { + /// Create a new `BindContext` value. + pub(crate) fn new(parent: &'a Node) -> Self { + BindContext { + parent, + tree_connected: parent.is_connected(), + tree_is_in_a_document_tree: parent.is_in_a_document_tree(), + tree_is_in_a_shadow_tree: parent.is_in_a_shadow_tree(), + } + } + /// Return true iff the tree is inside either a document- or a shadow tree. pub(crate) fn is_in_tree(&self) -> bool { self.tree_is_in_a_document_tree || self.tree_is_in_a_shadow_tree diff --git a/components/script/dom/shadowroot.rs b/components/script/dom/shadowroot.rs index 02c0191ba2a..c27e2487c42 100644 --- a/components/script/dom/shadowroot.rs +++ b/components/script/dom/shadowroot.rs @@ -586,20 +586,15 @@ impl VirtualMethods for ShadowRoot { shadow_root.set_flag(NodeFlags::IS_CONNECTED, context.tree_connected); + let context = BindContext::new(shadow_root); + // avoid iterate over the shadow root itself for node in shadow_root.traverse_preorder(ShadowIncluding::Yes).skip(1) { node.set_flag(NodeFlags::IS_CONNECTED, context.tree_connected); // Out-of-document elements never have the descendants flag set debug_assert!(!node.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS)); - vtable_for(&node).bind_to_tree( - &BindContext { - tree_connected: context.tree_connected, - tree_is_in_a_document_tree: false, - tree_is_in_a_shadow_tree: true, - }, - can_gc, - ); + vtable_for(&node).bind_to_tree(&context, can_gc); } } diff --git a/tests/wpt/meta/html/semantics/embedded-content/the-img-element/relevant-mutations.html.ini b/tests/wpt/meta/html/semantics/embedded-content/the-img-element/relevant-mutations.html.ini index f34826e0a7e..33b778fb584 100644 --- a/tests/wpt/meta/html/semantics/embedded-content/the-img-element/relevant-mutations.html.ini +++ b/tests/wpt/meta/html/semantics/embedded-content/the-img-element/relevant-mutations.html.ini @@ -1,30 +1,12 @@ [relevant-mutations.html] - [ancestor picture; previous sibling source inserted] - expected: FAIL - - [picture is inserted; img has previous sibling source] - expected: FAIL - - [ancestor picture; previous sibling source removed] - expected: FAIL - [crossorigin state not changed: empty to anonymous] expected: FAIL - [picture is inserted; img has following sibling source] - expected: FAIL - - [picture is inserted; img has src] - expected: FAIL - [crossorigin state not changed: use-credentials to USE-CREDENTIALS] expected: FAIL [crossorigin state not changed: anonymous to foobar] expected: FAIL - [picture is inserted; img has srcset] - expected: FAIL - [sizes is set to same value] expected: FAIL