Implement shadow dom slots (#35013)

* Implement slot-related algorithms

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

* Hook up slot elements to DOM creation logic

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

* Set a slot assignment mode for servo-internal shadow roots

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

* Assign slots when a slottable's slot attribute changes

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

* Properly compute slot name

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

* ./mach test-tidy

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

* Update <slot> name when name attribute changes

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

* Implement fast path for Node::assign_slottables_for_a_tree

assign_slottables_for_a_tree 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.

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

* Move slottable data into ElementRareData

This shrinks all element descendants back to their
original size.

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

* Address review comments

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

* Update WPT expectations

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-01-19 15:05:05 +01:00 committed by GitHub
parent 8bb50fa3c9
commit dabe162d44
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
31 changed files with 758 additions and 355 deletions

View file

@ -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

View file

@ -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<DomRoot<ShadowRoot>> {
// Step 1.
// If elements 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::<Node>()
@ -603,6 +612,43 @@ impl Element {
Some(node) => node.is::<Document>(),
}
}
pub(crate) fn assigned_slot(&self) -> Option<DomRoot<HTMLSlotElement>> {
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<HTMLSlotElement>) {
self.ensure_rare_data().slottable_data.assigned_slot = Some(assigned_slot.as_traced());
}
pub(crate) fn manual_slot_assignment(&self) -> Option<DomRoot<HTMLSlotElement>> {
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);
}
}
/// <https://dom.spec.whatwg.org/#valid-shadow-host-name>
@ -3084,7 +3130,12 @@ impl ElementMethods<crate::DomTypeHolder> for Element {
fn AttachShadow(&self, init: &ShadowRootInit) -> Fallible<DomRoot<ShadowRoot>> {
// 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 thiss shadow root.
Ok(shadow_root)
@ -3460,6 +3511,16 @@ impl ElementMethods<crate::DomTypeHolder> for Element {
fn SetAriaValueText(&self, value: Option<DOMString>, can_gc: CanGc) {
self.set_nullable_string_attribute(&local_name!("aria-valuetext"), value, can_gc);
}
/// <https://dom.spec.whatwg.org/#dom-slotable-assignedslot>
fn GetAssignedSlot(&self) -> Option<DomRoot<HTMLSlotElement>> {
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.

View file

@ -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(

View file

@ -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;
/// <https://html.spec.whatwg.org/multipage/#the-slot-element>
#[dom_struct]
pub struct HTMLSlotElement {
htmlelement: HTMLElement,
/// <https://dom.spec.whatwg.org/#slot-assigned-nodes>
assigned_nodes: RefCell<Vec<Slottable>>,
/// <https://html.spec.whatwg.org/multipage/#manually-assigned-nodes>
manually_assigned_nodes: RefCell<Vec<Slottable>>,
}
impl HTMLSlotElementMethods<crate::DomTypeHolder> 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");
/// <https://html.spec.whatwg.org/multipage/#dom-slot-assignednodes>
fn AssignedNodes(&self, options: &AssignedNodesOptions) -> Vec<DomRoot<Node>> {
// 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()
}
/// <https://html.spec.whatwg.org/multipage/#dom-slot-assignedelements>
fn AssignedElements(&self, options: &AssignedNodesOptions) -> Vec<DomRoot<Element>> {
self.AssignedNodes(options)
.into_iter()
.flat_map(|node| node.downcast::<Element>().map(DomRoot::from_ref))
.collect()
}
/// <https://html.spec.whatwg.org/multipage/#dom-slot-assign>
fn Assign(&self, nodes: Vec<ElementOrText>) {
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::<Node>()
.GetRootNode(&GetRootNodeOptions::empty())
.assign_slottables_for_a_tree();
}
}
/// <https://dom.spec.whatwg.org/#concept-slotable>
#[derive(Clone, JSTraceable, MallocSizeOf, PartialEq)]
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
pub(crate) enum Slottable {
Element(Dom<Element>),
Text(Dom<Text>),
}
/// 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 {
/// <https://dom.spec.whatwg.org/#slotable-assigned-slot>
pub(crate) assigned_slot: Option<Dom<HTMLSlotElement>>,
/// <https://dom.spec.whatwg.org/#slottable-manual-slot-assignment>
pub(crate) manual_slot_assignment: Option<Dom<HTMLSlotElement>>,
}
impl HTMLSlotElement {
fn new_inherited(
local_name: LocalName,
prefix: Option<Prefix>,
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<Prefix>,
document: &Document,
proto: Option<HandleObject>,
can_gc: CanGc,
) -> DomRoot<HTMLSlotElement> {
Node::reflect_node_with_proto(
Box::new(HTMLSlotElement::new_inherited(local_name, prefix, document)),
document,
proto,
can_gc,
)
}
/// <https://dom.spec.whatwg.org/#find-flattened-slotables>
fn find_flattened_slottables(&self, result: &mut RootedVec<Slottable>) {
// Step 1. Let result be an empty list.
debug_assert!(result.is_empty());
// Step 2. If slots root is not a shadow root, then return result.
if self.upcast::<Node>().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::<Node>().children() {
if let Some(element) = child.downcast::<Element>() {
slottables.push(Slottable::Element(Dom::from_ref(element)));
} else if let Some(text) = child.downcast::<Text>() {
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::<HTMLSlotElement>(),
Slottable::Text(_) => None,
};
match maybe_slot_element {
Some(slot_element)
if slot_element
.upcast::<Node>()
.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.
}
/// <https://dom.spec.whatwg.org/#find-slotables>
///
/// To avoid rooting shenanigans, this writes the returned slottables
/// into the `result` argument
fn find_slottables(&self, result: &mut RootedVec<Slottable>) {
let cx = GlobalScope::get_cx();
// Step 1. Let result be an empty list.
debug_assert!(result.is_empty());
// Step 2. Let root be slots root.
// Step 3. If root is not a shadow root, then return result.
let Some(root) = self.upcast::<Node>().containing_shadow_root() else {
return;
};
// Step 4. Let host be roots host.
let host = root.Host();
// Step 5. If roots 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 slots manually assigned nodes,
// if slottables 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::<Node>())
{
result.push(slottable.clone());
}
}
}
// Step 6. Otherwise, for each slottable child slottable of host, in tree order:
else {
let mut for_slottable = |slottable: RootedGuard<Slottable>| {
// 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::<Node>().children() {
if let Some(element) = child.downcast::<Element>() {
rooted!(in(*cx) let slottable = Slottable::Element(Dom::from_ref(element)));
for_slottable(slottable);
continue;
}
if let Some(text) = child.downcast::<Text>() {
rooted!(in(*cx) let slottable = Slottable::Text(Dom::from_ref(text)));
for_slottable(slottable);
}
}
}
// Step 7. Return result.
}
/// <https://dom.spec.whatwg.org/#assign-slotables>
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 slots assigned nodes are not identical,
// then run signal a slot change for slot.
// Step 3. Set slots assigned nodes to slottables.
*self.assigned_nodes.borrow_mut() = slottables.iter().cloned().collect();
// Step 4. For each slottable in slottables, set slottables assigned slot to slot.
for slottable in slottables.iter() {
slottable.set_assigned_slot(DomRoot::from_ref(self));
}
}
}
impl Slottable {
/// <https://dom.spec.whatwg.org/#find-a-slot>
pub(crate) fn find_a_slot(&self, open_flag: bool) -> Option<DomRoot<HTMLSlotElement>> {
// Step 1. If slottables parent is null, then return null.
let parent = self.node().GetParentNode()?;
// Step 2. Let shadow be slottables parents shadow root.
// Step 3. If shadow is null, then return null.
let shadow_root = parent
.downcast::<Element>()
.and_then(Element::shadow_root)?;
// Step 4. If the open flag is set and shadows mode is not "open", then return null.
if open_flag && shadow_root.Mode() != ShadowRootMode::Open {
return None;
}
// Step 5. If shadows slot assignment is "manual", then return the slot in shadows descendants whose
// manually assigned nodes contains slottable, if any; otherwise null.
if shadow_root.SlotAssignment() == SlotAssignmentMode::Manual {
for node in shadow_root
.upcast::<Node>()
.traverse_preorder(ShadowIncluding::No)
{
if let Some(slot) = node.downcast::<HTMLSlotElement>() {
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 shadows descendants whose
// name is slottables name, if any; otherwise null.
for node in shadow_root
.upcast::<Node>()
.traverse_preorder(ShadowIncluding::No)
{
if let Some(slot) = node.downcast::<HTMLSlotElement>() {
if slot.Name() == self.name() {
return Some(DomRoot::from_ref(slot));
}
}
}
None
}
/// Slottable name change steps from <https://dom.spec.whatwg.org/#light-tree-slotables>
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 elements 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 elements 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 elements 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();
}
/// <https://dom.spec.whatwg.org/#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<DomRoot<HTMLSlotElement>> {
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<HTMLSlotElement>) {
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<DomRoot<HTMLSlotElement>> {
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::<HTMLElement>() as &dyn VirtualMethods)
}
/// <https://dom.spec.whatwg.org/#shadow-tree-slots>
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::<Node>()
.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<Self> {
None
}
}

View file

@ -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;

View file

@ -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())
}
/// <https://dom.spec.whatwg.org/#assign-slotables-for-a-tree>
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::<ShadowRoot>() {
return;
}
// > To assign slottables for a tree, given a node root, run assign slottables for each slot
// > slot in roots inclusive descendants, in tree order.
for node in self.traverse_preorder(ShadowIncluding::No) {
if let Some(slot) = node.downcast::<HTMLSlotElement>() {
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 nodes 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 {
// nodes shadow roots serializable, nodes shadow roots delegates focus,
// and nodes shadow roots 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 copys shadow roots declarative to nodes shadow roots declarative.

View file

@ -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<LayoutValue<Rect<i32>>>,
/// <https://html.spec.whatwg.org/multipage#elementinternals>
pub(crate) element_internals: Option<Dom<ElementInternals>>,
pub(crate) slottable_data: SlottableData,
}

View file

@ -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 {
/// <https://dom.spec.whatwg.org/#dom-shadowroot-mode>
mode: ShadowRootMode,
/// <https://dom.spec.whatwg.org/#dom-shadowroot-slotassignment>
slot_assignment_mode: SlotAssignmentMode,
/// <https://dom.spec.whatwg.org/#dom-shadowroot-clonable>
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<ShadowRoot> {
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<crate::DomTypeHolder> for ShadowRoot {
// Step 4. Replace all with fragment within this.
Node::replace_all(Some(frag.upcast()), self.upcast());
}
/// <https://dom.spec.whatwg.org/#dom-shadowroot-slotassignment>
fn SlotAssignment(&self) -> SlotAssignmentMode {
self.slot_assignment_mode
}
}
impl VirtualMethods for ShadowRoot {

View file

@ -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<SlottableData>,
}
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<SlottableData> {
&self.slottable_data
}
}
impl TextMethods<crate::DomTypeHolder> for Text {
@ -119,4 +129,14 @@ impl TextMethods<crate::DomTypeHolder> for Text {
}
DOMString::from(text)
}
/// <https://dom.spec.whatwg.org/#dom-slotable-assignedslot>
fn GetAssignedSlot(&self) -> Option<DomRoot<HTMLSlotElement>> {
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)
}
}

View file

@ -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::<HTMLSourceElement>().unwrap() as &dyn VirtualMethods
},
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLSlotElement)) => {
node.downcast::<HTMLSlotElement>().unwrap() as &dyn VirtualMethods
},
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLStyleElement)) => {
node.downcast::<HTMLStyleElement>().unwrap() as &dyn VirtualMethods
},

View file

@ -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;
};

View file

@ -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<Node> assignedNodes(optional AssignedNodesOptions options = {});
sequence<Element> 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;

View file

@ -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;

View file

@ -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);

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -1,3 +0,0 @@
[slots-outside-shadow-dom.html]
[Light DOM slot element should be in flattened assignedNodes]
expected: FAIL

View file

@ -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

View file

@ -13499,7 +13499,7 @@
]
],
"interfaces.https.html": [
"71d2291cb162143e4abf298f0a23e6a06fe5c1bc",
"501caf7410bba6533e11c0d83acb2729da48a289",
[
null,
{}

View file

@ -160,6 +160,7 @@ test_interfaces([
"HTMLQuoteElement",
"HTMLScriptElement",
"HTMLSelectElement",
"HTMLSlotElement",
"HTMLSourceElement",
"HTMLSpanElement",
"HTMLStyleElement",