Make Element::attach_shadow() and ShadowRoot closer to spec (#36024)

Signed-off-by: batu_hoang <longvatrong111@gmail.com>
This commit is contained in:
batu_hoang 2025-03-20 01:58:16 +08:00 committed by GitHub
parent 11d47558b3
commit db74179dc8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 117 additions and 158 deletions

View file

@ -508,24 +508,25 @@ impl Element {
}
/// <https://dom.spec.whatwg.org/#dom-element-attachshadow>
#[allow(clippy::too_many_arguments)]
pub(crate) fn attach_shadow(
&self,
// TODO: remove is_ua_widget argument
is_ua_widget: IsUserAgentWidget,
mode: ShadowRootMode,
clonable: bool,
serializable: bool,
delegates_focus: bool,
slot_assignment_mode: SlotAssignmentMode,
can_gc: CanGc,
) -> Fallible<DomRoot<ShadowRoot>> {
// Step 1.
// If elements namespace is not the HTML namespace,
// Step 1. If elements namespace is not the HTML namespace,
// then throw a "NotSupportedError" DOMException.
if self.namespace != ns!(html) {
return Err(Error::NotSupported);
}
// Step 2.
// If elements local name is not a valid shadow host name,
// Step 2. If elements local name is not a valid shadow host name,
// then throw a "NotSupportedError" DOMException.
if !is_valid_shadow_host_name(self.local_name()) {
// UA shadow roots may be attached to anything
@ -534,13 +535,51 @@ impl Element {
}
}
// TODO: Update the following steps to align with the newer spec.
// Step 3.
if self.is_shadow_host() {
return Err(Error::InvalidState);
// Step 3. If elements local name is a valid custom element name,
// or elements is value is non-null
if is_valid_custom_element_name(self.local_name()) || self.get_is().is_some() {
// Step 3.1. Let definition be the result of looking up a custom element definition
// given elements node document, its namespace, its local name, and its is value.
let definition = self.get_custom_element_definition();
// Step 3.2. If definition is not null and definitions disable shadow
// is true, then throw a "NotSupportedError" DOMException.
if definition.is_some_and(|definition| definition.disable_shadow) {
return Err(Error::NotSupported);
}
}
// Steps 4, 5 and 6.
// Step 4. If element is a shadow host:
// Step 4.1. Let currentShadowRoot be elements shadow root.
if let Some(current_shadow_root) = self.shadow_root() {
// Step 4.2. If currentShadowRoots declarative is false
// or currentShadowRoots mode is not mode
// then throw a "NotSupportedError" DOMException.
if !current_shadow_root.is_declarative() ||
current_shadow_root.shadow_root_mode() != mode
{
return Err(Error::NotSupported);
}
// Step 4.3.1. Remove all of currentShadowRoots children, in tree order.
let node = self.upcast::<Node>();
for child in node.children() {
child.remove_self();
}
// Step 4.3.2. Set currentShadowRoots declarative to false.
current_shadow_root.set_declarative(false);
// Step 4.3.3. Return
return Ok(current_shadow_root);
}
// Step 5. Let shadow be a new shadow root whose node document
// is elements node document, host is element, and mode is mode
//
// Step 8. Set shadows slot assignment to slotAssignment
//
// Step 10. Set shadows clonable to clonable
let shadow_root = ShadowRoot::new(
self,
&self.node.owner_doc(),
@ -551,6 +590,9 @@ impl Element {
can_gc,
);
// Step 6. Set shadow's delegates focus to delegatesFocus
shadow_root.set_delegates_focus(delegates_focus);
// Step 7. If elements custom element state is "precustomized" or "custom",
// then set shadows available to element internals to true.
if matches!(
@ -560,6 +602,13 @@ impl Element {
shadow_root.set_available_to_element_internals(true);
}
// Step 9. Set shadow's declarative to false
shadow_root.set_declarative(false);
// Step 11. Set shadow's serializable to serializable
shadow_root.set_serializable(serializable);
// Step 12. Set elements shadow root to shadow
self.ensure_rare_data().shadow_root = Some(Dom::from_ref(&*shadow_root));
shadow_root
.upcast::<Node>()
@ -3234,6 +3283,8 @@ impl ElementMethods<crate::DomTypeHolder> for Element {
IsUserAgentWidget::No,
init.mode,
init.clonable,
init.serializable,
init.delegatesFocus,
init.slotAssignment,
CanGc::note(),
)?;