diff --git a/components/script/dom/create.rs b/components/script/dom/create.rs index cde5bcbb13f..5d4e09915c6 100644 --- a/components/script/dom/create.rs +++ b/components/script/dom/create.rs @@ -66,6 +66,7 @@ use crate::dom::htmlprogresselement::HTMLProgressElement; use crate::dom::htmlquoteelement::HTMLQuoteElement; use crate::dom::htmlscriptelement::HTMLScriptElement; use crate::dom::htmlselectelement::HTMLSelectElement; +use crate::dom::htmlslotelement::HTMLSlotElement; use crate::dom::htmlsourceelement::HTMLSourceElement; use crate::dom::htmlspanelement::HTMLSpanElement; use crate::dom::htmlstyleelement::HTMLStyleElement; @@ -357,6 +358,7 @@ pub(crate) fn create_native_html_element( local_name!("script") => make!(HTMLScriptElement, creator), local_name!("section") => make!(HTMLElement), local_name!("select") => make!(HTMLSelectElement), + local_name!("slot") => make!(HTMLSlotElement), local_name!("small") => make!(HTMLElement), local_name!("source") => make!(HTMLSourceElement), // https://html.spec.whatwg.org/multipage/#other-elements,-attributes-and-apis:spacer diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index 166b9707e31..58a4d26a6a5 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -74,7 +74,7 @@ use crate::dom::bindings::codegen::Bindings::FunctionBinding::Function; use crate::dom::bindings::codegen::Bindings::HTMLTemplateElementBinding::HTMLTemplateElementMethods; use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{ - ShadowRootMethods, ShadowRootMode, + ShadowRootMethods, ShadowRootMode, SlotAssignmentMode, }; use crate::dom::bindings::codegen::Bindings::WindowBinding::{ ScrollBehavior, ScrollToOptions, WindowMethods, @@ -105,6 +105,7 @@ use crate::dom::domrectlist::DOMRectList; use crate::dom::domtokenlist::DOMTokenList; use crate::dom::elementinternals::ElementInternals; use crate::dom::eventtarget::EventTarget; +use crate::dom::globalscope::GlobalScope; use crate::dom::htmlanchorelement::HTMLAnchorElement; use crate::dom::htmlbodyelement::{HTMLBodyElement, HTMLBodyElementLayoutHelpers}; use crate::dom::htmlbuttonelement::HTMLButtonElement; @@ -124,6 +125,7 @@ use crate::dom::htmlobjectelement::HTMLObjectElement; use crate::dom::htmloptgroupelement::HTMLOptGroupElement; use crate::dom::htmloutputelement::HTMLOutputElement; use crate::dom::htmlselectelement::HTMLSelectElement; +use crate::dom::htmlslotelement::{HTMLSlotElement, Slottable}; use crate::dom::htmlstyleelement::HTMLStyleElement; use crate::dom::htmltablecellelement::{HTMLTableCellElement, HTMLTableCellElementLayoutHelpers}; use crate::dom::htmltableelement::{HTMLTableElement, HTMLTableElementLayoutHelpers}; @@ -510,6 +512,7 @@ impl Element { is_ua_widget: IsUserAgentWidget, mode: ShadowRootMode, clonable: bool, + slot_assignment_mode: SlotAssignmentMode, ) -> Fallible> { // Step 1. // If element’s namespace is not the HTML namespace, @@ -536,7 +539,13 @@ impl Element { } // Steps 4, 5 and 6. - let shadow_root = ShadowRoot::new(self, &self.node.owner_doc(), mode, clonable); + let shadow_root = ShadowRoot::new( + self, + &self.node.owner_doc(), + mode, + slot_assignment_mode, + clonable, + ); self.ensure_rare_data().shadow_root = Some(Dom::from_ref(&*shadow_root)); shadow_root .upcast::() @@ -603,6 +612,43 @@ impl Element { Some(node) => node.is::(), } } + + pub(crate) fn assigned_slot(&self) -> Option> { + let assigned_slot = self + .rare_data + .borrow() + .as_ref()? + .slottable_data + .assigned_slot + .as_ref()? + .as_rooted(); + Some(assigned_slot) + } + + pub(crate) fn set_assigned_slot(&self, assigned_slot: DomRoot) { + self.ensure_rare_data().slottable_data.assigned_slot = Some(assigned_slot.as_traced()); + } + + pub(crate) fn manual_slot_assignment(&self) -> Option> { + let manually_assigned_slot = self + .rare_data + .borrow() + .as_ref()? + .slottable_data + .manual_slot_assignment + .as_ref()? + .as_rooted(); + Some(manually_assigned_slot) + } + + pub(crate) fn set_manual_slot_assignment( + &self, + manually_assigned_slot: Option<&HTMLSlotElement>, + ) { + self.ensure_rare_data() + .slottable_data + .manual_slot_assignment = manually_assigned_slot.map(Dom::from_ref); + } } /// @@ -3084,7 +3130,12 @@ impl ElementMethods for Element { fn AttachShadow(&self, init: &ShadowRootInit) -> Fallible> { // Step 1. Run attach a shadow root with this, init["mode"], init["clonable"], init["serializable"], // init["delegatesFocus"], and init["slotAssignment"]. - let shadow_root = self.attach_shadow(IsUserAgentWidget::No, init.mode, init.clonable)?; + let shadow_root = self.attach_shadow( + IsUserAgentWidget::No, + init.mode, + init.clonable, + init.slotAssignment, + )?; // Step 2. Return this’s shadow root. Ok(shadow_root) @@ -3460,6 +3511,16 @@ impl ElementMethods for Element { fn SetAriaValueText(&self, value: Option, can_gc: CanGc) { self.set_nullable_string_attribute(&local_name!("aria-valuetext"), value, can_gc); } + + /// + fn GetAssignedSlot(&self) -> Option> { + let cx = GlobalScope::get_cx(); + + // > The assignedSlot getter steps are to return the result of + // > find a slot given this and with the open flag set. + rooted!(in(*cx) let slottable = Slottable::Element(Dom::from_ref(self))); + slottable.find_a_slot(true) + } } impl VirtualMethods for Element { @@ -3600,6 +3661,12 @@ impl VirtualMethods for Element { } } }, + &local_name!("slot") => { + // Update slottable data + let cx = GlobalScope::get_cx(); + rooted!(in(*cx) let slottable = Slottable::Element(Dom::from_ref(self))); + slottable.update_slot_name(attr, mutation, CanGc::note()) + }, _ => { // FIXME(emilio): This is pretty dubious, and should be done in // the relevant super-classes. diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs index b836f414496..c7fd9462b21 100644 --- a/components/script/dom/htmlmediaelement.rs +++ b/components/script/dom/htmlmediaelement.rs @@ -54,7 +54,9 @@ use crate::dom::bindings::codegen::Bindings::MediaErrorBinding::MediaErrorConsta use crate::dom::bindings::codegen::Bindings::MediaErrorBinding::MediaErrorMethods; use crate::dom::bindings::codegen::Bindings::NavigatorBinding::Navigator_Binding::NavigatorMethods; use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods; -use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::ShadowRootMode; +use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{ + ShadowRootMode, SlotAssignmentMode, +}; use crate::dom::bindings::codegen::Bindings::TextTrackBinding::{TextTrackKind, TextTrackMode}; use crate::dom::bindings::codegen::Bindings::URLBinding::URLMethods; use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods; @@ -1888,7 +1890,12 @@ impl HTMLMediaElement { return; } let shadow_root = element - .attach_shadow(IsUserAgentWidget::Yes, ShadowRootMode::Closed, false) + .attach_shadow( + IsUserAgentWidget::Yes, + ShadowRootMode::Closed, + false, + SlotAssignmentMode::Manual, + ) .unwrap(); let document = self.owner_document(); let script = HTMLScriptElement::new( diff --git a/components/script/dom/htmlslotelement.rs b/components/script/dom/htmlslotelement.rs new file mode 100644 index 00000000000..b5fcdb91c6d --- /dev/null +++ b/components/script/dom/htmlslotelement.rs @@ -0,0 +1,547 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use std::cell::RefCell; + +use dom_struct::dom_struct; +use html5ever::{local_name, namespace_url, ns, LocalName, Prefix}; +use js::gc::{RootedGuard, RootedVec}; +use js::rust::HandleObject; +use servo_atoms::Atom; +use style::attr::AttrValue; + +use crate::dom::attr::Attr; +use crate::dom::bindings::codegen::Bindings::HTMLSlotElementBinding::{ + AssignedNodesOptions, HTMLSlotElementMethods, +}; +use crate::dom::bindings::codegen::Bindings::NodeBinding::{GetRootNodeOptions, NodeMethods}; +use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::ShadowRoot_Binding::ShadowRootMethods; +use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{ + ShadowRootMode, SlotAssignmentMode, +}; +use crate::dom::bindings::codegen::UnionTypes::ElementOrText; +use crate::dom::bindings::inheritance::Castable; +use crate::dom::bindings::root::{Dom, DomRoot}; +use crate::dom::bindings::str::DOMString; +use crate::dom::document::Document; +use crate::dom::element::{AttributeMutation, Element}; +use crate::dom::globalscope::GlobalScope; +use crate::dom::htmlelement::HTMLElement; +use crate::dom::node::{Node, ShadowIncluding}; +use crate::dom::text::Text; +use crate::dom::virtualmethods::VirtualMethods; +use crate::script_runtime::CanGc; + +/// +#[dom_struct] +pub struct HTMLSlotElement { + htmlelement: HTMLElement, + + /// + assigned_nodes: RefCell>, + + /// + manually_assigned_nodes: RefCell>, +} + +impl HTMLSlotElementMethods for HTMLSlotElement { + // https://html.spec.whatwg.org/multipage/#dom-slot-name + make_getter!(Name, "name"); + + // https://html.spec.whatwg.org/multipage/#dom-slot-name + make_atomic_setter!(SetName, "name"); + + /// + fn AssignedNodes(&self, options: &AssignedNodesOptions) -> Vec> { + // Step 1. If options["flatten"] is false, then return this's assigned nodes. + if !options.flatten { + return self + .assigned_nodes + .borrow() + .iter() + .map(|slottable| slottable.node()) + .map(DomRoot::from_ref) + .collect(); + } + + // Step 2. Return the result of finding flattened slottables with this. + rooted_vec!(let mut flattened_slottables); + self.find_flattened_slottables(&mut flattened_slottables); + + flattened_slottables + .iter() + .map(|slottable| DomRoot::from_ref(slottable.node())) + .collect() + } + + /// + fn AssignedElements(&self, options: &AssignedNodesOptions) -> Vec> { + self.AssignedNodes(options) + .into_iter() + .flat_map(|node| node.downcast::().map(DomRoot::from_ref)) + .collect() + } + + /// + fn Assign(&self, nodes: Vec) { + let cx = GlobalScope::get_cx(); + + // Step 1. For each node of this's manually assigned nodes, set node's manual slot assignment to null. + for slottable in self.manually_assigned_nodes.borrow().iter() { + slottable.set_manual_slot_assignment(None); + } + + // Step 2. Let nodesSet be a new ordered set. + rooted_vec!(let mut nodes_set); + + // Step 3. For each node of nodes: + for element_or_text in nodes.into_iter() { + rooted!(in(*cx) let node = match element_or_text { + ElementOrText::Element(element) => Slottable::Element(Dom::from_ref(&element)), + ElementOrText::Text(text) => Slottable::Text(Dom::from_ref(&text)), + }); + + // Step 3.1 If node's manual slot assignment refers to a slot, + // then remove node from that slot's manually assigned nodes. + if let Some(slot) = node.manual_slot_assignment() { + let mut manually_assigned_nodes = slot.manually_assigned_nodes.borrow_mut(); + if let Some(position) = manually_assigned_nodes + .iter() + .position(|value| *value == *node) + { + manually_assigned_nodes.remove(position); + } + } + + // Step 3.2 Set node's manual slot assignment to this. + node.set_manual_slot_assignment(Some(self)); + + // Step 3.3 Append node to nodesSet. + if !nodes_set.contains(&*node) { + nodes_set.push(node.clone()); + } + } + + // Step 4. Set this's manually assigned nodes to nodesSet. + *self.manually_assigned_nodes.borrow_mut() = nodes_set.iter().cloned().collect(); + + // Step 5. Run assign slottables for a tree for this's root. + self.upcast::() + .GetRootNode(&GetRootNodeOptions::empty()) + .assign_slottables_for_a_tree(); + } +} + +/// +#[derive(Clone, JSTraceable, MallocSizeOf, PartialEq)] +#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)] +pub(crate) enum Slottable { + Element(Dom), + Text(Dom), +} + +/// Data shared between all [slottables](https://dom.spec.whatwg.org/#concept-slotable) +/// +/// Note that the [slottable name](https://dom.spec.whatwg.org/#slotable-name) is not +/// part of this. While the spec says that all slottables have a name, only Element's +/// can ever have a non-empty name, so they store it seperately +#[derive(Default, JSTraceable, MallocSizeOf)] +#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)] +pub struct SlottableData { + /// + pub(crate) assigned_slot: Option>, + + /// + pub(crate) manual_slot_assignment: Option>, +} + +impl HTMLSlotElement { + fn new_inherited( + local_name: LocalName, + prefix: Option, + document: &Document, + ) -> HTMLSlotElement { + HTMLSlotElement { + htmlelement: HTMLElement::new_inherited(local_name, prefix, document), + assigned_nodes: Default::default(), + manually_assigned_nodes: Default::default(), + } + } + + #[cfg_attr(crown, allow(crown::unrooted_must_root))] + pub(crate) fn new( + local_name: LocalName, + prefix: Option, + document: &Document, + proto: Option, + can_gc: CanGc, + ) -> DomRoot { + Node::reflect_node_with_proto( + Box::new(HTMLSlotElement::new_inherited(local_name, prefix, document)), + document, + proto, + can_gc, + ) + } + + /// + fn find_flattened_slottables(&self, result: &mut RootedVec) { + // Step 1. Let result be an empty list. + debug_assert!(result.is_empty()); + + // Step 2. If slot’s root is not a shadow root, then return result. + if self.upcast::().containing_shadow_root().is_none() { + return; + }; + + // Step 3. Let slottables be the result of finding slottables given slot. + rooted_vec!(let mut slottables); + self.find_slottables(&mut slottables); + + // Step 4. If slottables is the empty list, then append each slottable + // child of slot, in tree order, to slottables. + if slottables.is_empty() { + for child in self.upcast::().children() { + if let Some(element) = child.downcast::() { + slottables.push(Slottable::Element(Dom::from_ref(element))); + } else if let Some(text) = child.downcast::() { + slottables.push(Slottable::Text(Dom::from_ref(text))); + } + } + } + + // Step 5. For each node in slottables: + for slottable in slottables.iter() { + // Step 5.1 If node is a slot whose root is a shadow root: + // NOTE: Only elements can be slots + let maybe_slot_element = match &slottable { + Slottable::Element(element) => element.downcast::(), + Slottable::Text(_) => None, + }; + match maybe_slot_element { + Some(slot_element) + if slot_element + .upcast::() + .containing_shadow_root() + .is_some() => + { + // Step 5.1.1 Let temporaryResult be the result of finding flattened slottables given node. + rooted_vec!(let mut temporary_result); + slot_element.find_flattened_slottables(&mut temporary_result); + + // Step 5.1.2 Append each slottable in temporaryResult, in order, to result. + result.extend_from_slice(&temporary_result); + }, + // Step 5.2 Otherwise, append node to result. + _ => { + result.push(slottable.clone()); + }, + }; + } + + // Step 6. Return result. + } + + /// + /// + /// To avoid rooting shenanigans, this writes the returned slottables + /// into the `result` argument + fn find_slottables(&self, result: &mut RootedVec) { + let cx = GlobalScope::get_cx(); + + // Step 1. Let result be an empty list. + debug_assert!(result.is_empty()); + + // Step 2. Let root be slot’s root. + // Step 3. If root is not a shadow root, then return result. + let Some(root) = self.upcast::().containing_shadow_root() else { + return; + }; + + // Step 4. Let host be root’s host. + let host = root.Host(); + + // Step 5. If root’s slot assignment is "manual": + if root.SlotAssignment() == SlotAssignmentMode::Manual { + // Step 5.1 Let result be « ». + // NOTE: redundant. + + // Step 5.2 For each slottable slottable of slot’s manually assigned nodes, + // if slottable’s parent is host, append slottable to result. + for slottable in self.manually_assigned_nodes.borrow().iter() { + if slottable + .node() + .GetParentNode() + .is_some_and(|node| &*node == host.upcast::()) + { + result.push(slottable.clone()); + } + } + } + // Step 6. Otherwise, for each slottable child slottable of host, in tree order: + else { + let mut for_slottable = |slottable: RootedGuard| { + // Step 6.1 Let foundSlot be the result of finding a slot given slottable. + let found_slot = slottable.find_a_slot(false); + + // Step 6.2 If foundSlot is slot, then append slottable to result. + if found_slot.is_some_and(|found_slot| &*found_slot == self) { + result.push(slottable.clone()); + } + }; + for child in host.upcast::().children() { + if let Some(element) = child.downcast::() { + rooted!(in(*cx) let slottable = Slottable::Element(Dom::from_ref(element))); + for_slottable(slottable); + continue; + } + if let Some(text) = child.downcast::() { + rooted!(in(*cx) let slottable = Slottable::Text(Dom::from_ref(text))); + for_slottable(slottable); + } + } + } + + // Step 7. Return result. + } + + /// + pub(crate) fn assign_slottables(&self) { + // Step 1. Let slottables be the result of finding slottables for slot. + rooted_vec!(let mut slottables); + self.find_slottables(&mut slottables); + + // Step 2. TODO If slottables and slot’s assigned nodes are not identical, + // then run signal a slot change for slot. + + // Step 3. Set slot’s assigned nodes to slottables. + *self.assigned_nodes.borrow_mut() = slottables.iter().cloned().collect(); + + // Step 4. For each slottable in slottables, set slottable’s assigned slot to slot. + for slottable in slottables.iter() { + slottable.set_assigned_slot(DomRoot::from_ref(self)); + } + } +} + +impl Slottable { + /// + pub(crate) fn find_a_slot(&self, open_flag: bool) -> Option> { + // Step 1. If slottable’s parent is null, then return null. + let parent = self.node().GetParentNode()?; + + // Step 2. Let shadow be slottable’s parent’s shadow root. + // Step 3. If shadow is null, then return null. + let shadow_root = parent + .downcast::() + .and_then(Element::shadow_root)?; + + // Step 4. If the open flag is set and shadow’s mode is not "open", then return null. + if open_flag && shadow_root.Mode() != ShadowRootMode::Open { + return None; + } + + // Step 5. If shadow’s slot assignment is "manual", then return the slot in shadow’s descendants whose + // manually assigned nodes contains slottable, if any; otherwise null. + if shadow_root.SlotAssignment() == SlotAssignmentMode::Manual { + for node in shadow_root + .upcast::() + .traverse_preorder(ShadowIncluding::No) + { + if let Some(slot) = node.downcast::() { + if slot.manually_assigned_nodes.borrow().contains(self) { + return Some(DomRoot::from_ref(slot)); + } + } + } + return None; + } + + // Step 6. Return the first slot in tree order in shadow’s descendants whose + // name is slottable’s name, if any; otherwise null. + for node in shadow_root + .upcast::() + .traverse_preorder(ShadowIncluding::No) + { + if let Some(slot) = node.downcast::() { + if slot.Name() == self.name() { + return Some(DomRoot::from_ref(slot)); + } + } + } + None + } + + /// Slottable name change steps from + pub(crate) fn update_slot_name(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) { + debug_assert!(matches!(self, Self::Element(_))); + + // Step 1. If localName is slot and namespace is null: + // NOTE: This is done by the caller + let old_value = if let AttributeMutation::Set(old_name) = mutation { + old_name.and_then(|attr| match attr { + AttrValue::String(s) => Some(s.clone()), + _ => None, + }) + } else { + None + }; + let value = mutation.new_value(attr).and_then(|attr| match &*attr { + AttrValue::String(s) => Some(s.clone()), + _ => None, + }); + + // Step 1.1 If value is oldValue, then return. + if value == old_value { + return; + } + + // Step 1.2 If value is null and oldValue is the empty string, then return. + if value.is_none() && old_value.as_ref().is_some_and(|s| s.is_empty()) { + return; + } + + // Step 1.3 If value is the empty string and oldValue is null, then return. + if old_value.is_none() && value.as_ref().is_some_and(|s| s.is_empty()) { + return; + } + + // Step 1.4 If value is null or the empty string, then set element’s name to the empty string. + if value.as_ref().is_none_or(|s| s.is_empty()) { + self.set_name(DOMString::new(), can_gc); + } + // Step 1.5 Otherwise, set element’s name to value. + else { + self.set_name(DOMString::from(value.unwrap_or_default()), can_gc); + } + + // Step 1.6 If element is assigned, then run assign slottables for element’s assigned slot. + if let Some(assigned_slot) = self.assigned_slot() { + assigned_slot.assign_slottables(); + } + + // Step 1.7 Run assign a slot for element. + self.assign_a_slot(); + } + + /// + pub(crate) fn assign_a_slot(&self) { + // Step 1. Let slot be the result of finding a slot with slottable. + let slot = self.find_a_slot(false); + + // Step 2. If slot is non-null, then run assign slottables for slot. + if let Some(slot) = slot { + slot.assign_slottables(); + } + } + + fn node(&self) -> &Node { + match self { + Self::Element(element) => element.upcast(), + Self::Text(text) => text.upcast(), + } + } + + fn assigned_slot(&self) -> Option> { + match self { + Self::Element(element) => element.assigned_slot(), + Self::Text(text) => { + let assigned_slot = text + .slottable_data() + .borrow() + .assigned_slot + .as_ref()? + .as_rooted(); + Some(assigned_slot) + }, + } + } + + pub(crate) fn set_assigned_slot(&self, assigned_slot: DomRoot) { + match self { + Self::Element(element) => element.set_assigned_slot(assigned_slot), + Self::Text(text) => { + text.slottable_data().borrow_mut().assigned_slot = Some(assigned_slot.as_traced()) + }, + } + } + + pub(crate) fn set_manual_slot_assignment( + &self, + manually_assigned_slot: Option<&HTMLSlotElement>, + ) { + match self { + Self::Element(element) => element.set_manual_slot_assignment(manually_assigned_slot), + Self::Text(text) => { + text.slottable_data().borrow_mut().manual_slot_assignment = + manually_assigned_slot.map(Dom::from_ref) + }, + } + } + + pub(crate) fn manual_slot_assignment(&self) -> Option> { + match self { + Self::Element(element) => element.manual_slot_assignment(), + Self::Text(text) => text + .slottable_data() + .borrow() + .manual_slot_assignment + .as_ref() + .map(Dom::as_rooted), + } + } + + fn set_name(&self, name: DOMString, can_gc: CanGc) { + // NOTE: Only elements have non-empty names + let Self::Element(element) = self else { + return; + }; + let element = element.as_rooted(); + element.set_attribute( + &local_name!("name"), + AttrValue::Atom(Atom::from(name)), + can_gc, + ); + } + + fn name(&self) -> DOMString { + // NOTE: Only elements have non-empty names + let Self::Element(element) = self else { + return DOMString::new(); + }; + + element + .name_attribute() + .map(|a| DOMString::from(a.as_ref())) + .unwrap_or_default() + .clone() + } +} + +impl VirtualMethods for HTMLSlotElement { + fn super_type(&self) -> Option<&dyn VirtualMethods> { + Some(self.upcast::() as &dyn VirtualMethods) + } + + /// + fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) { + self.super_type().unwrap().attribute_mutated(attr, mutation); + + if attr.local_name() == &local_name!("name") && attr.namespace() == &ns!() { + self.upcast::() + .GetRootNode(&GetRootNodeOptions::empty()) + .assign_slottables_for_a_tree() + } + } +} + +impl js::gc::Rootable for Slottable {} + +impl js::gc::Initialize for Slottable { + #[allow(unsafe_code)] + #[cfg_attr(crown, allow(crown::unrooted_must_root))] + unsafe fn initial() -> Option { + None + } +} diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 2848f5ed1e5..4d31684cfb4 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -398,6 +398,7 @@ pub(crate) mod htmlquoteelement; #[allow(dead_code)] pub(crate) mod htmlscriptelement; pub(crate) mod htmlselectelement; +pub(crate) mod htmlslotelement; pub(crate) mod htmlsourceelement; pub(crate) mod htmlspanelement; pub(crate) mod htmlstyleelement; diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index 3518e3d9b7f..1f991d3b715 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -91,6 +91,7 @@ use crate::dom::htmliframeelement::{HTMLIFrameElement, HTMLIFrameElementLayoutMe use crate::dom::htmlimageelement::{HTMLImageElement, LayoutHTMLImageElementHelpers}; use crate::dom::htmlinputelement::{HTMLInputElement, LayoutHTMLInputElementHelpers}; use crate::dom::htmllinkelement::HTMLLinkElement; +use crate::dom::htmlslotelement::HTMLSlotElement; use crate::dom::htmlstyleelement::HTMLStyleElement; use crate::dom::htmltextareaelement::{HTMLTextAreaElement, LayoutHTMLTextAreaElementHelpers}; use crate::dom::htmlvideoelement::{HTMLVideoElement, LayoutHTMLVideoElementHelpers}; @@ -1316,6 +1317,24 @@ impl Node { .as_ref() .map(|data| data.element_data.borrow().styles.primary().clone()) } + + /// + pub(crate) fn assign_slottables_for_a_tree(&self) { + // NOTE: This method traverses all descendants of the node and is potentially very + // expensive. If the node is not a shadow root then assigning slottables to it won't + // have any effect, so we take a fast path out. + if !self.is::() { + return; + } + + // > To assign slottables for a tree, given a node root, run assign slottables for each slot + // > slot in root’s inclusive descendants, in tree order. + for node in self.traverse_preorder(ShadowIncluding::No) { + if let Some(slot) = node.downcast::() { + slot.assign_slottables(); + } + } + } } /// Iterate through `nodes` until we find a `Node` that is not in `not_in` @@ -2113,6 +2132,11 @@ impl Node { for kid in new_nodes { // Step 7.1. parent.add_child(kid, child); + + // Step 7.6 Run assign slottables for a tree with node’s root. + kid.GetRootNode(&GetRootNodeOptions::empty()) + .assign_slottables_for_a_tree(); + // Step 7.7. for descendant in kid .traverse_preorder(ShadowIncluding::Yes) @@ -2464,7 +2488,12 @@ impl Node { // node’s shadow root’s serializable, node’s shadow root’s delegates focus, // and node’s shadow root’s slot assignment. let copy_shadow_root = - copy_elem.attach_shadow(IsUserAgentWidget::No, shadow_root.Mode(), true) + copy_elem.attach_shadow( + IsUserAgentWidget::No, + shadow_root.Mode(), + true, + shadow_root.SlotAssignment() + ) .expect("placement of attached shadow root must be valid, as this is a copy of an existing one"); // TODO: Step 7.3 Set copy’s shadow root’s declarative to node’s shadow root’s declarative. diff --git a/components/script/dom/raredata.rs b/components/script/dom/raredata.rs index d9b087d3b63..383eaaf70c3 100644 --- a/components/script/dom/raredata.rs +++ b/components/script/dom/raredata.rs @@ -12,6 +12,7 @@ use crate::dom::customelementregistry::{ CustomElementDefinition, CustomElementReaction, CustomElementState, }; use crate::dom::elementinternals::ElementInternals; +use crate::dom::htmlslotelement::SlottableData; use crate::dom::mutationobserver::RegisteredObserver; use crate::dom::node::UniqueId; use crate::dom::shadowroot::ShadowRoot; @@ -57,4 +58,6 @@ pub(crate) struct ElementRareData { pub(crate) client_rect: Option>>, /// pub(crate) element_internals: Option>, + + pub(crate) slottable_data: SlottableData, } diff --git a/components/script/dom/shadowroot.rs b/components/script/dom/shadowroot.rs index c3911b35642..46fcb38cf1b 100644 --- a/components/script/dom/shadowroot.rs +++ b/components/script/dom/shadowroot.rs @@ -12,8 +12,10 @@ use style::stylesheets::Stylesheet; use style::stylist::{CascadeData, Stylist}; use crate::dom::bindings::cell::DomRefCell; -use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::ShadowRootMode; use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::ShadowRoot_Binding::ShadowRootMethods; +use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{ + ShadowRootMode, SlotAssignmentMode, +}; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::num::Finite; use crate::dom::bindings::reflector::reflect_dom_object; @@ -57,6 +59,9 @@ pub(crate) struct ShadowRoot { /// mode: ShadowRootMode, + /// + slot_assignment_mode: SlotAssignmentMode, + /// clonable: bool, } @@ -67,6 +72,7 @@ impl ShadowRoot { host: &Element, document: &Document, mode: ShadowRootMode, + slot_assignment_mode: SlotAssignmentMode, clonable: bool, ) -> ShadowRoot { let document_fragment = DocumentFragment::new_inherited(document); @@ -86,6 +92,7 @@ impl ShadowRoot { stylesheet_list: MutNullableDom::new(None), window: Dom::from_ref(document.window()), mode, + slot_assignment_mode, clonable, } } @@ -94,10 +101,17 @@ impl ShadowRoot { host: &Element, document: &Document, mode: ShadowRootMode, + slot_assignment_mode: SlotAssignmentMode, clonable: bool, ) -> DomRoot { reflect_dom_object( - Box::new(ShadowRoot::new_inherited(host, document, mode, clonable)), + Box::new(ShadowRoot::new_inherited( + host, + document, + mode, + slot_assignment_mode, + clonable, + )), document.window(), CanGc::note(), ) @@ -306,6 +320,11 @@ impl ShadowRootMethods for ShadowRoot { // Step 4. Replace all with fragment within this. Node::replace_all(Some(frag.upcast()), self.upcast()); } + + /// + fn SlotAssignment(&self) -> SlotAssignmentMode { + self.slot_assignment_mode + } } impl VirtualMethods for ShadowRoot { diff --git a/components/script/dom/text.rs b/components/script/dom/text.rs index 2f0e1c66857..3f81f0be6e2 100644 --- a/components/script/dom/text.rs +++ b/components/script/dom/text.rs @@ -2,6 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +use std::cell::RefCell; + use dom_struct::dom_struct; use js::rust::HandleObject; @@ -12,10 +14,12 @@ use crate::dom::bindings::codegen::Bindings::TextBinding::TextMethods; use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use crate::dom::bindings::error::{Error, Fallible}; use crate::dom::bindings::inheritance::Castable; -use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::bindings::str::DOMString; use crate::dom::characterdata::CharacterData; use crate::dom::document::Document; +use crate::dom::globalscope::GlobalScope; +use crate::dom::htmlslotelement::{HTMLSlotElement, Slottable, SlottableData}; use crate::dom::node::Node; use crate::dom::window::Window; use crate::script_runtime::CanGc; @@ -24,12 +28,14 @@ use crate::script_runtime::CanGc; #[dom_struct] pub(crate) struct Text { characterdata: CharacterData, + slottable_data: RefCell, } impl Text { pub(crate) fn new_inherited(text: DOMString, document: &Document) -> Text { Text { characterdata: CharacterData::new_inherited(text, document), + slottable_data: Default::default(), } } @@ -50,6 +56,10 @@ impl Text { can_gc, ) } + + pub(crate) fn slottable_data(&self) -> &RefCell { + &self.slottable_data + } } impl TextMethods for Text { @@ -119,4 +129,14 @@ impl TextMethods for Text { } DOMString::from(text) } + + /// + fn GetAssignedSlot(&self) -> Option> { + let cx = GlobalScope::get_cx(); + + // > The assignedSlot getter steps are to return the result of + // > find a slot given this and with the open flag set. + rooted!(in(*cx) let slottable = Slottable::Text(Dom::from_ref(self))); + slottable.find_a_slot(true) + } } diff --git a/components/script/dom/virtualmethods.rs b/components/script/dom/virtualmethods.rs index 4f552cefee1..6c2f8b646c6 100644 --- a/components/script/dom/virtualmethods.rs +++ b/components/script/dom/virtualmethods.rs @@ -43,6 +43,7 @@ use crate::dom::htmloutputelement::HTMLOutputElement; use crate::dom::htmlpreelement::HTMLPreElement; use crate::dom::htmlscriptelement::HTMLScriptElement; use crate::dom::htmlselectelement::HTMLSelectElement; +use crate::dom::htmlslotelement::HTMLSlotElement; use crate::dom::htmlsourceelement::HTMLSourceElement; use crate::dom::htmlstyleelement::HTMLStyleElement; use crate::dom::htmltablecellelement::HTMLTableCellElement; @@ -257,6 +258,9 @@ pub(crate) fn vtable_for(node: &Node) -> &dyn VirtualMethods { NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLSourceElement)) => { node.downcast::().unwrap() as &dyn VirtualMethods }, + NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLSlotElement)) => { + node.downcast::().unwrap() as &dyn VirtualMethods + }, NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLStyleElement)) => { node.downcast::().unwrap() as &dyn VirtualMethods }, diff --git a/components/script/dom/webidls/Element.webidl b/components/script/dom/webidls/Element.webidl index c7884925541..60a6db4e4e1 100644 --- a/components/script/dom/webidls/Element.webidl +++ b/components/script/dom/webidls/Element.webidl @@ -90,7 +90,7 @@ interface Element : Node { dictionary ShadowRootInit { required ShadowRootMode mode; // boolean delegatesFocus = false; - // SlotAssignmentMode slotAssignment = "named"; + SlotAssignmentMode slotAssignment = "named"; boolean clonable = false; // boolean serializable = false; }; diff --git a/components/script/dom/webidls/HTMLSlotElement.webidl b/components/script/dom/webidls/HTMLSlotElement.webidl new file mode 100644 index 00000000000..bec872ab1fc --- /dev/null +++ b/components/script/dom/webidls/HTMLSlotElement.webidl @@ -0,0 +1,25 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +// https://html.spec.whatwg.org/multipage/#the-slot-element +[Exposed=Window] +interface HTMLSlotElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute DOMString name; + sequence assignedNodes(optional AssignedNodesOptions options = {}); + sequence assignedElements(optional AssignedNodesOptions options = {}); + undefined assign((Element or Text)... nodes); +}; + +dictionary AssignedNodesOptions { + boolean flatten = false; +}; + +// https://dom.spec.whatwg.org/#mixin-slotable +interface mixin Slottable { + readonly attribute HTMLSlotElement? assignedSlot; +}; +Element includes Slottable; +Text includes Slottable; diff --git a/components/script/dom/webidls/ShadowRoot.webidl b/components/script/dom/webidls/ShadowRoot.webidl index 40301f48517..444dd53d22c 100644 --- a/components/script/dom/webidls/ShadowRoot.webidl +++ b/components/script/dom/webidls/ShadowRoot.webidl @@ -10,7 +10,7 @@ interface ShadowRoot : DocumentFragment { readonly attribute ShadowRootMode mode; // readonly attribute boolean delegatesFocus; - // readonly attribute SlotAssignmentMode slotAssignment; + readonly attribute SlotAssignmentMode slotAssignment; readonly attribute boolean clonable; // readonly attribute boolean serializable; readonly attribute Element host; @@ -19,7 +19,7 @@ interface ShadowRoot : DocumentFragment { enum ShadowRootMode { "open", "closed"}; -// enum SlotAssignmentMode { "manual", "named" }; +enum SlotAssignmentMode { "manual", "named" }; ShadowRoot includes DocumentOrShadowRoot; diff --git a/tests/unit/script/size_of.rs b/tests/unit/script/size_of.rs index d39f6ebacd7..3cbc005ac53 100644 --- a/tests/unit/script/size_of.rs +++ b/tests/unit/script/size_of.rs @@ -35,5 +35,5 @@ sizeof_checker!(size_element, Element, 376); sizeof_checker!(size_htmlelement, HTMLElement, 392); sizeof_checker!(size_div, HTMLDivElement, 392); sizeof_checker!(size_span, HTMLSpanElement, 392); -sizeof_checker!(size_text, Text, 232); +sizeof_checker!(size_text, Text, 256); sizeof_checker!(size_characterdata, CharacterData, 232); diff --git a/tests/wpt/meta/custom-elements/builtin-coverage.html.ini b/tests/wpt/meta/custom-elements/builtin-coverage.html.ini index 74b0816750a..a10dd340315 100644 --- a/tests/wpt/meta/custom-elements/builtin-coverage.html.ini +++ b/tests/wpt/meta/custom-elements/builtin-coverage.html.ini @@ -328,3 +328,6 @@ [dialog: Operator 'new' should instantiate a customized built-in element] expected: FAIL + + [slot: Define a customized built-in element] + expected: FAIL diff --git a/tests/wpt/meta/custom-elements/reactions/customized-builtins/HTMLSlotElement.html.ini b/tests/wpt/meta/custom-elements/reactions/customized-builtins/HTMLSlotElement.html.ini index 06f319e7a49..c6223bd8ced 100644 --- a/tests/wpt/meta/custom-elements/reactions/customized-builtins/HTMLSlotElement.html.ini +++ b/tests/wpt/meta/custom-elements/reactions/customized-builtins/HTMLSlotElement.html.ini @@ -1,2 +1,6 @@ [HTMLSlotElement.html] - expected: ERROR + [name on HTMLSlotElement must enqueue an attributeChanged reaction when adding name content attribute] + expected: FAIL + + [name on HTMLSlotElement must enqueue an attributeChanged reaction when replacing an existing attribute] + expected: FAIL diff --git a/tests/wpt/meta/dom/idlharness.window.js.ini b/tests/wpt/meta/dom/idlharness.window.js.ini index 3fa526f6ab8..011ac74fab1 100644 --- a/tests/wpt/meta/dom/idlharness.window.js.ini +++ b/tests/wpt/meta/dom/idlharness.window.js.ini @@ -8,18 +8,9 @@ [AbortSignal must be primary interface of new AbortController().signal] expected: FAIL - [Element interface: element must inherit property "assignedSlot" with the proper type] - expected: FAIL - - [Text interface: document.createTextNode("abc") must inherit property "assignedSlot" with the proper type] - expected: FAIL - [Event interface: attribute composed] expected: FAIL - [Text interface: attribute assignedSlot] - expected: FAIL - [AbortSignal interface: existence and properties of interface prototype object's @@unscopables property] expected: FAIL @@ -65,9 +56,6 @@ [Element interface: operation after((Node or DOMString)...)] expected: FAIL - [Element interface: attribute assignedSlot] - expected: FAIL - [CharacterData interface: existence and properties of interface prototype object's @@unscopables property] expected: FAIL @@ -197,9 +185,6 @@ [ShadowRoot interface: attribute delegatesFocus] expected: FAIL - [ShadowRoot interface: attribute slotAssignment] - expected: FAIL - [XSLTProcessor interface: existence and properties of interface object] expected: FAIL diff --git a/tests/wpt/meta/html/dom/elements/name-content-attribute-and-property.html.ini b/tests/wpt/meta/html/dom/elements/name-content-attribute-and-property.html.ini index 9b781a2f47c..a866b073e17 100644 --- a/tests/wpt/meta/html/dom/elements/name-content-attribute-and-property.html.ini +++ b/tests/wpt/meta/html/dom/elements/name-content-attribute-and-property.html.ini @@ -13,6 +13,3 @@ [param element's name property reflects its content attribute] expected: FAIL - - [slot element's name property reflects its content attribute] - expected: FAIL diff --git a/tests/wpt/meta/html/dom/idlharness.https.html.ini b/tests/wpt/meta/html/dom/idlharness.https.html.ini index cbd7c1d7808..fa346c7f862 100644 --- a/tests/wpt/meta/html/dom/idlharness.https.html.ini +++ b/tests/wpt/meta/html/dom/idlharness.https.html.ini @@ -7858,63 +7858,6 @@ [HTMLTemplateElement interface: document.createElement("template") must inherit property "shadowRootSerializable" with the proper type] expected: FAIL - [HTMLSlotElement interface: existence and properties of interface object] - expected: FAIL - - [HTMLSlotElement interface object length] - expected: FAIL - - [HTMLSlotElement interface object name] - expected: FAIL - - [HTMLSlotElement interface: existence and properties of interface prototype object] - expected: FAIL - - [HTMLSlotElement interface: existence and properties of interface prototype object's "constructor" property] - expected: FAIL - - [HTMLSlotElement interface: existence and properties of interface prototype object's @@unscopables property] - expected: FAIL - - [HTMLSlotElement interface: attribute name] - expected: FAIL - - [HTMLSlotElement interface: operation assignedNodes(optional AssignedNodesOptions)] - expected: FAIL - - [HTMLSlotElement interface: operation assignedElements(optional AssignedNodesOptions)] - expected: FAIL - - [HTMLSlotElement interface: operation assign((Element or Text)...)] - expected: FAIL - - [HTMLSlotElement must be primary interface of document.createElement("slot")] - expected: FAIL - - [Stringification of document.createElement("slot")] - expected: FAIL - - [HTMLSlotElement interface: document.createElement("slot") must inherit property "name" with the proper type] - expected: FAIL - - [HTMLSlotElement interface: document.createElement("slot") must inherit property "assignedNodes(optional AssignedNodesOptions)" with the proper type] - expected: FAIL - - [HTMLSlotElement interface: calling assignedNodes(optional AssignedNodesOptions) on document.createElement("slot") with too few arguments must throw TypeError] - expected: FAIL - - [HTMLSlotElement interface: document.createElement("slot") must inherit property "assignedElements(optional AssignedNodesOptions)" with the proper type] - expected: FAIL - - [HTMLSlotElement interface: calling assignedElements(optional AssignedNodesOptions) on document.createElement("slot") with too few arguments must throw TypeError] - expected: FAIL - - [HTMLSlotElement interface: document.createElement("slot") must inherit property "assign((Element or Text)...)" with the proper type] - expected: FAIL - - [HTMLSlotElement interface: calling assign((Element or Text)...) on document.createElement("slot") with too few arguments must throw TypeError] - expected: FAIL - [HTMLMarqueeElement interface: existence and properties of interface object] expected: FAIL diff --git a/tests/wpt/meta/html/dom/reflection-misc.html.ini b/tests/wpt/meta/html/dom/reflection-misc.html.ini index c4c1d9d5111..9bc98397655 100644 --- a/tests/wpt/meta/html/dom/reflection-misc.html.ini +++ b/tests/wpt/meta/html/dom/reflection-misc.html.ini @@ -1073,120 +1073,6 @@ [slot.tabIndex: IDL set to -2147483648] expected: FAIL - [slot.name: typeof IDL attribute] - expected: FAIL - - [slot.name: IDL get with DOM attribute unset] - expected: FAIL - - [slot.name: setAttribute() to ""] - expected: FAIL - - [slot.name: setAttribute() to " \\0\\x01\\x02\\x03\\x04\\x05\\x06\\x07 \\b\\t\\n\\v\\f\\r\\x0e\\x0f \\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17 \\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f foo "] - expected: FAIL - - [slot.name: setAttribute() to undefined] - expected: FAIL - - [slot.name: setAttribute() to 7] - expected: FAIL - - [slot.name: setAttribute() to 1.5] - expected: FAIL - - [slot.name: setAttribute() to "5%"] - expected: FAIL - - [slot.name: setAttribute() to "+100"] - expected: FAIL - - [slot.name: setAttribute() to ".5"] - expected: FAIL - - [slot.name: setAttribute() to true] - expected: FAIL - - [slot.name: setAttribute() to false] - expected: FAIL - - [slot.name: setAttribute() to object "[object Object\]"] - expected: FAIL - - [slot.name: setAttribute() to NaN] - expected: FAIL - - [slot.name: setAttribute() to Infinity] - expected: FAIL - - [slot.name: setAttribute() to -Infinity] - expected: FAIL - - [slot.name: setAttribute() to "\\0"] - expected: FAIL - - [slot.name: setAttribute() to null] - expected: FAIL - - [slot.name: setAttribute() to object "test-toString"] - expected: FAIL - - [slot.name: setAttribute() to object "test-valueOf"] - expected: FAIL - - [slot.name: IDL set to ""] - expected: FAIL - - [slot.name: IDL set to " \\0\\x01\\x02\\x03\\x04\\x05\\x06\\x07 \\b\\t\\n\\v\\f\\r\\x0e\\x0f \\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17 \\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f foo "] - expected: FAIL - - [slot.name: IDL set to undefined] - expected: FAIL - - [slot.name: IDL set to 7] - expected: FAIL - - [slot.name: IDL set to 1.5] - expected: FAIL - - [slot.name: IDL set to "5%"] - expected: FAIL - - [slot.name: IDL set to "+100"] - expected: FAIL - - [slot.name: IDL set to ".5"] - expected: FAIL - - [slot.name: IDL set to true] - expected: FAIL - - [slot.name: IDL set to false] - expected: FAIL - - [slot.name: IDL set to object "[object Object\]"] - expected: FAIL - - [slot.name: IDL set to NaN] - expected: FAIL - - [slot.name: IDL set to Infinity] - expected: FAIL - - [slot.name: IDL set to -Infinity] - expected: FAIL - - [slot.name: IDL set to "\\0"] - expected: FAIL - - [slot.name: IDL set to null] - expected: FAIL - - [slot.name: IDL set to object "test-toString"] - expected: FAIL - - [slot.name: IDL set to object "test-valueOf"] - expected: FAIL - [ins.accessKey: typeof IDL attribute] expected: FAIL diff --git a/tests/wpt/meta/html/semantics/interfaces.html.ini b/tests/wpt/meta/html/semantics/interfaces.html.ini index bc426a7a6f2..152ab28b257 100644 --- a/tests/wpt/meta/html/semantics/interfaces.html.ini +++ b/tests/wpt/meta/html/semantics/interfaces.html.ini @@ -53,15 +53,6 @@ [Interfaces for RTC: createElement] expected: FAIL - [Interfaces for slot: useNS] - expected: FAIL - - [Interfaces for slot: useParser] - expected: FAIL - - [Interfaces for SLOT: createElement] - expected: FAIL - [Interfaces for permission: useNS] expected: FAIL diff --git a/tests/wpt/meta/shadow-dom/HTMLSlotElement-interface.html.ini b/tests/wpt/meta/shadow-dom/HTMLSlotElement-interface.html.ini index 858ebe7170d..edb9c4274b9 100644 --- a/tests/wpt/meta/shadow-dom/HTMLSlotElement-interface.html.ini +++ b/tests/wpt/meta/shadow-dom/HTMLSlotElement-interface.html.ini @@ -1,19 +1,4 @@ [HTMLSlotElement-interface.html] - [HTMLSlotElement must be defined on window] - expected: FAIL - - ["name" attribute on HTMLSlotElement must reflect "name" attribute] - expected: FAIL - - [assignedNodes() on a HTMLSlotElement must return an empty array when the slot element is not in a tree or in a document tree] - expected: FAIL - - [assignedNodes({"flattened":false}) on a HTMLSlotElement must return an empty array when the slot element is not in a tree or in a document tree] - expected: FAIL - - [assignedNodes({"flattened":true}) on a HTMLSlotElement must return an empty array when the slot element is not in a tree or in a document tree] - expected: FAIL - [assignedNodes() must return the list of assigned nodes when none of the assigned nodes themselves are slots] expected: FAIL @@ -32,15 +17,6 @@ [assignedNodes({"flattened":true}) must update when slot and name attributes are modified] expected: FAIL - [assignedNodes() must update when a default slot is introduced dynamically by a slot rename] - expected: FAIL - - [assignedNodes({"flattened":false}) must update when a default slot is introduced dynamically by a slot rename] - expected: FAIL - - [assignedNodes({"flattened":true}) must update when a default slot is introduced dynamically by a slot rename] - expected: FAIL - [assignedNodes() must update when slot elements are inserted or removed] expected: FAIL diff --git a/tests/wpt/meta/shadow-dom/Slottable-mixin.html.ini b/tests/wpt/meta/shadow-dom/Slottable-mixin.html.ini index 5bde06d5006..b76c3d007b9 100644 --- a/tests/wpt/meta/shadow-dom/Slottable-mixin.html.ini +++ b/tests/wpt/meta/shadow-dom/Slottable-mixin.html.ini @@ -1,12 +1,3 @@ [Slottable-mixin.html] - [assignedSlot attribute must be defined on Element and Text interfaces] - expected: FAIL - - [assignedSlot must return null when the node does not have an assigned node] - expected: FAIL - [assignedSlot must return the assigned slot] expected: FAIL - - [assignedSlot must return null when the assigned slot element is inside a closed shadow tree] - expected: FAIL diff --git a/tests/wpt/meta/shadow-dom/imperative-slot-api-slotchange.html.ini b/tests/wpt/meta/shadow-dom/imperative-slot-api-slotchange.html.ini index f04f405a114..6e76442ca80 100644 --- a/tests/wpt/meta/shadow-dom/imperative-slot-api-slotchange.html.ini +++ b/tests/wpt/meta/shadow-dom/imperative-slot-api-slotchange.html.ini @@ -1,33 +1,31 @@ [imperative-slot-api-slotchange.html] + expected: TIMEOUT [slotchange event must not fire synchronously.] - expected: FAIL - - [slotchange event should not fire when assignments do not change assignedNodes.] - expected: FAIL + expected: TIMEOUT [slotchange event should not fire when same node is assigned.] - expected: FAIL + expected: TIMEOUT [Fire slotchange event when slot's assigned nodes changes.] - expected: FAIL + expected: TIMEOUT [Fire slotchange event on previous slot and new slot when node is reassigned.] - expected: FAIL + expected: TIMEOUT [Fire slotchange event on node assignment and when assigned node is removed.] - expected: FAIL + expected: TIMEOUT [Fire slotchange event when order of assigned nodes changes.] - expected: FAIL + expected: TIMEOUT [Fire slotchange event when assigned node is removed.] - expected: FAIL + expected: TIMEOUT [Fire slotchange event when removing a slot from Shadows Root that changes its assigned nodes.] - expected: FAIL + expected: NOTRUN [Fire slotchange event when assign node to nested slot, ensure event bubbles ups.] - expected: FAIL + expected: TIMEOUT [Signal a slot change should be done in tree order.] - expected: FAIL + expected: NOTRUN diff --git a/tests/wpt/meta/shadow-dom/imperative-slot-api.html.ini b/tests/wpt/meta/shadow-dom/imperative-slot-api.html.ini index 37375c88cc2..0123afe0d5b 100644 --- a/tests/wpt/meta/shadow-dom/imperative-slot-api.html.ini +++ b/tests/wpt/meta/shadow-dom/imperative-slot-api.html.ini @@ -1,22 +1,4 @@ [imperative-slot-api.html] - [attachShadow can take slotAssignment parameter.] - expected: FAIL - - [slot.attach() should take variadic not sequence.] - expected: FAIL - - [Imperative slot API can assign nodes in manual slot assignment.] - expected: FAIL - - [Order of slottables is preserved in manual slot assignment.] - expected: FAIL - - [Previously assigned slottable is moved to new slot when it's reassigned.] - expected: FAIL - - [Order and assignment of nodes are preserved during multiple assignment in a row.] - expected: FAIL - [Assigning invalid nodes should be allowed.] expected: FAIL @@ -29,15 +11,6 @@ [Appending slottable to different host, it loses slot assignment. It can be re-assigned within a new host.] expected: FAIL - [Previously assigned node should not be assigned if slot moved to a new shadow root. The node is re-assigned when moved back.] - expected: FAIL - - [Assignment with the same node in parameters should be ignored, first one wins.] - expected: FAIL - - [Removing a slot from DOM resets its slottable's slot assignment.] - expected: FAIL - [Nodes can be assigned even if slots or nodes aren't in the same tree.] expected: FAIL diff --git a/tests/wpt/meta/shadow-dom/imperative-slot-initial-fallback.html.ini b/tests/wpt/meta/shadow-dom/imperative-slot-initial-fallback.html.ini deleted file mode 100644 index 24068147e76..00000000000 --- a/tests/wpt/meta/shadow-dom/imperative-slot-initial-fallback.html.ini +++ /dev/null @@ -1,6 +0,0 @@ -[imperative-slot-initial-fallback.html] - [Unassigned imperative slot can render text node as the initial fallback] - expected: FAIL - - [Unassigned imperative slot can render element as the initial fallback] - expected: FAIL diff --git a/tests/wpt/meta/shadow-dom/slots-fallback.html.ini b/tests/wpt/meta/shadow-dom/slots-fallback.html.ini index 2e7c99956cc..cdc9c6beda1 100644 --- a/tests/wpt/meta/shadow-dom/slots-fallback.html.ini +++ b/tests/wpt/meta/shadow-dom/slots-fallback.html.ini @@ -1,22 +1,4 @@ [slots-fallback.html] - [Slots fallback: Basic.] - expected: FAIL - - [Slots fallback: Basic, elements only.] - expected: FAIL - - [Slots fallback: Slots in Slots.] - expected: FAIL - - [Slots fallback: Slots in Slots, elements only.] - expected: FAIL - - [Slots fallback: Fallback contents should not be used if a node is assigned.] - expected: FAIL - - [Slots fallback: Slots in Slots: Assigned nodes should be used as fallback contents of another slot] - expected: FAIL - [Slots fallback: Complex case.] expected: FAIL diff --git a/tests/wpt/meta/shadow-dom/slots-outside-shadow-dom.html.ini b/tests/wpt/meta/shadow-dom/slots-outside-shadow-dom.html.ini deleted file mode 100644 index d9a5833db83..00000000000 --- a/tests/wpt/meta/shadow-dom/slots-outside-shadow-dom.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[slots-outside-shadow-dom.html] - [Light DOM slot element should be in flattened assignedNodes] - expected: FAIL diff --git a/tests/wpt/meta/shadow-dom/slots.html.ini b/tests/wpt/meta/shadow-dom/slots.html.ini index aeb985ce493..d7d2877a5ea 100644 --- a/tests/wpt/meta/shadow-dom/slots.html.ini +++ b/tests/wpt/meta/shadow-dom/slots.html.ini @@ -1,37 +1,4 @@ [slots.html] - [Slots: Basic.] - expected: FAIL - - [Slots: Basic, elements only.] - expected: FAIL - - [Slots: Slots in closed.] - expected: FAIL - - [Slots: Slots in closed, elements only.] - expected: FAIL - - [Slots: Slots not in a shadow tree.] - expected: FAIL - - [Slots: Slots not in a shadow tree, elements only.] - expected: FAIL - - [Slots: Distributed nodes for Slots not in a shadow tree.] - expected: FAIL - - [Slots: Name matching] - expected: FAIL - - [Slots: No direct host child.] - expected: FAIL - - [Slots: Default Slot.] - expected: FAIL - - [Slots: Slot in Slot does not matter in assignment.] - expected: FAIL - [Slots: Slot is assigned to another slot] expected: FAIL @@ -50,21 +17,12 @@ [Slots: Mutation: appendChild.] expected: FAIL - [Slots: Mutation: Change slot= attribute 1.] - expected: FAIL - [Slots: Mutation: Change slot= attribute 2.] expected: FAIL [Slots: Mutation: Change slot= attribute 3.] expected: FAIL - [Slots: Mutation: Remove a child.] - expected: FAIL - - [Slots: Mutation: Add a slot: after.] - expected: FAIL - [Slots: Mutation: Add a slot: before.] expected: FAIL diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index 5a5063ea176..ae5c9073ac9 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -13499,7 +13499,7 @@ ] ], "interfaces.https.html": [ - "71d2291cb162143e4abf298f0a23e6a06fe5c1bc", + "501caf7410bba6533e11c0d83acb2729da48a289", [ null, {} diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.https.html b/tests/wpt/mozilla/tests/mozilla/interfaces.https.html index 71d2291cb16..501caf7410b 100644 --- a/tests/wpt/mozilla/tests/mozilla/interfaces.https.html +++ b/tests/wpt/mozilla/tests/mozilla/interfaces.https.html @@ -160,6 +160,7 @@ test_interfaces([ "HTMLQuoteElement", "HTMLScriptElement", "HTMLSelectElement", + "HTMLSlotElement", "HTMLSourceElement", "HTMLSpanElement", "HTMLStyleElement",