Use "slot" attribute for slottable name (#35191)

* Implement Element::slot attribute

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

* Simplify slottable name update

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-29 13:50:03 +01:00 committed by GitHub
parent c633ca1cde
commit f6d1b30e97
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 18 additions and 98 deletions

View file

@ -2241,6 +2241,12 @@ impl ElementMethods<crate::DomTypeHolder> for Element {
.or_init(|| DOMTokenList::new(self, &local_name!("class"), None))
}
// https://dom.spec.whatwg.org/#dom-element-slot
make_getter!(Slot, "slot");
// https://dom.spec.whatwg.org/#dom-element-slot
make_setter!(SetSlot, "slot");
// https://dom.spec.whatwg.org/#dom-element-attributes
fn Attributes(&self) -> DomRoot<NamedNodeMap> {
self.attr_list
@ -3666,7 +3672,12 @@ impl VirtualMethods for Element {
// 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())
// Slottable name change steps from https://dom.spec.whatwg.org/#light-tree-slotables
if let Some(assigned_slot) = slottable.assigned_slot() {
assigned_slot.assign_slottables();
}
slottable.assign_a_slot();
},
_ => {
// FIXME(emilio): This is pretty dubious, and should be done in

View file

@ -8,8 +8,6 @@ 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::{
@ -382,58 +380,6 @@ impl Slottable {
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.
@ -452,7 +398,7 @@ impl Slottable {
}
}
fn assigned_slot(&self) -> Option<DomRoot<HTMLSlotElement>> {
pub(crate) fn assigned_slot(&self) -> Option<DomRoot<HTMLSlotElement>> {
match self {
Self::Element(element) => element.assigned_slot(),
Self::Text(text) => {
@ -501,30 +447,13 @@ impl Slottable {
}
}
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()
element.get_string_attribute(&local_name!("slot"))
}
}

View file

@ -30,6 +30,7 @@ interface Element : Node {
attribute DOMString className;
[SameObject, PutForwards=value]
readonly attribute DOMTokenList classList;
[CEReactions, Unscopable] attribute DOMString slot;
[Pure]
boolean hasAttributes();

View file

@ -1,6 +0,0 @@
[Element.html]
[slot on Element must enqueue an attributeChanged reaction when adding slot content attribute]
expected: FAIL
[slot on Element must enqueue an attributeChanged reaction when replacing an existing attribute]
expected: FAIL

View file

@ -56,9 +56,6 @@
[CharacterData interface: operation remove()]
expected: FAIL
[Element interface: element must inherit property "slot" with the proper type]
expected: FAIL
[AbortController interface: new AbortController() must inherit property "signal" with the proper type]
expected: FAIL

View file

@ -8,15 +8,6 @@
[assignedNodes({"flattened":true}) must return the list of assigned nodes when none of the assigned nodes themselves are slots]
expected: FAIL
[assignedNodes() must update when slot and name attributes are modified]
expected: FAIL
[assignedNodes({"flattened":false}) must update when slot and name attributes are modified]
expected: FAIL
[assignedNodes({"flattened":true}) must update when slot and name attributes are modified]
expected: FAIL
[assignedNodes() must update when slot elements are inserted or removed]
expected: FAIL

View file

@ -1,3 +0,0 @@
[Slottable-mixin.html]
[assignedSlot must return the assigned slot]
expected: FAIL

View file

@ -3,8 +3,5 @@
[slotchange event: Append a child to a host.]
expected: TIMEOUT
[slotchange event: A slot is assigned to another slot.]
expected: TIMEOUT
[slotchange event: Child content is added to nested slots.]
expected: TIMEOUT

View file

@ -31,3 +31,6 @@
[Slots: Mutation: Change slot slot= attribute.]
expected: FAIL
[Slots: Mutation: Remove a slot.]
expected: FAIL