aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/element.rs
diff options
context:
space:
mode:
authorSimon Wülker <simon.wuelker@arcor.de>2025-01-19 15:05:05 +0100
committerGitHub <noreply@github.com>2025-01-19 14:05:05 +0000
commitdabe162d44a2b8a47b7a9dc8566e7f51ae678d05 (patch)
tree0b915202184ab6924ecb38cc961bbd4dd8ed830e /components/script/dom/element.rs
parent8bb50fa3c9d82e2575b02cefb6558467a8187439 (diff)
downloadservo-dabe162d44a2b8a47b7a9dc8566e7f51ae678d05.tar.gz
servo-dabe162d44a2b8a47b7a9dc8566e7f51ae678d05.zip
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>
Diffstat (limited to 'components/script/dom/element.rs')
-rw-r--r--components/script/dom/element.rs73
1 files changed, 70 insertions, 3 deletions
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<DomRoot<ShadowRoot>> {
// 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::<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 this’s 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.