diff --git a/Userland/Libraries/LibWeb/DOM/Element.cpp b/Userland/Libraries/LibWeb/DOM/Element.cpp index f814a7eb77..57704c1631 100644 --- a/Userland/Libraries/LibWeb/DOM/Element.cpp +++ b/Userland/Libraries/LibWeb/DOM/Element.cpp @@ -459,6 +459,58 @@ DOMTokenList* Element::class_list() return m_class_list; } +// https://dom.spec.whatwg.org/#dom-element-attachshadow +WebIDL::ExceptionOr> Element::attach_shadow(ShadowRootInit init) +{ + // 1. If this’s namespace is not the HTML namespace, then throw a "NotSupportedError" DOMException. + if (namespace_() != Namespace::HTML) + return WebIDL::NotSupportedError::create(realm(), "Element's namespace is not the HTML namespace"sv); + + // 2. If this’s local name is not one of the following: + // FIXME: - a valid custom element name + // - "article", "aside", "blockquote", "body", "div", "footer", "h1", "h2", "h3", "h4", "h5", "h6", "header", "main", "nav", "p", "section", or "span" + if (!local_name().is_one_of("article", "aside", "blockquote", "body", "div", "footer", "h1", "h2", "h3", "h4", "h5", "h6", "header", "main", "nav", "p", "section", "span")) { + // then throw a "NotSupportedError" DOMException. + return WebIDL::NotSupportedError::create(realm(), DeprecatedString::formatted("Element '{}' cannot be a shadow host", local_name())); + } + + // FIXME: 3. If this’s local name is a valid custom element name, or this’s is value is not null, then: ... + + // 4. If this is a shadow host, then throw an "NotSupportedError" DOMException. + if (is_shadow_host()) + return WebIDL::NotSupportedError::create(realm(), "Element already is a shadow host"); + + // 5. Let shadow be a new shadow root whose node document is this’s node document, host is this, and mode is init["mode"]. + auto shadow = MUST_OR_THROW_OOM(heap().allocate(realm(), document(), *this, init.mode)); + + // 6. Set shadow’s delegates focus to init["delegatesFocus"]. + shadow->set_delegates_focus(init.delegates_focus); + + // FIXME: 7. If this’s custom element state is "precustomized" or "custom", then set shadow’s available to element internals to true. + + // FIXME: 8. Set shadow’s slot assignment to init["slotAssignment"]. + + // 9. Set this’s shadow root to shadow. + set_shadow_root(shadow); + + // 10. Return shadow. + return shadow; +} + +// https://dom.spec.whatwg.org/#dom-element-shadowroot +JS::GCPtr Element::shadow_root() const +{ + // 1. Let shadow be this’s shadow root. + auto shadow = m_shadow_root; + + // 2. If shadow is null or its mode is "closed", then return null. + if (shadow == nullptr || shadow->mode() == Bindings::ShadowRootMode::Closed) + return nullptr; + + // 3. Return shadow. + return shadow; +} + // https://dom.spec.whatwg.org/#dom-element-matches WebIDL::ExceptionOr Element::matches(StringView selectors) const { diff --git a/Userland/Libraries/LibWeb/DOM/Element.h b/Userland/Libraries/LibWeb/DOM/Element.h index f54bf367a3..7b1ca4b494 100644 --- a/Userland/Libraries/LibWeb/DOM/Element.h +++ b/Userland/Libraries/LibWeb/DOM/Element.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -27,6 +28,11 @@ namespace Web::DOM { +struct ShadowRootInit { + Bindings::ShadowRootMode mode; + bool delegates_focus = false; +}; + // https://w3c.github.io/csswg-drafts/cssom-view-1/#dictdef-scrolloptions struct ScrollOptions { Bindings::ScrollBehavior behavior { Bindings::ScrollBehavior::Auto }; @@ -78,6 +84,9 @@ public: DOMTokenList* class_list(); + WebIDL::ExceptionOr> attach_shadow(ShadowRootInit init); + JS::GCPtr shadow_root() const; + WebIDL::ExceptionOr matches(StringView selectors) const; WebIDL::ExceptionOr closest(StringView selectors) const; diff --git a/Userland/Libraries/LibWeb/DOM/Element.idl b/Userland/Libraries/LibWeb/DOM/Element.idl index 0417bbb216..34ea92ca9f 100644 --- a/Userland/Libraries/LibWeb/DOM/Element.idl +++ b/Userland/Libraries/LibWeb/DOM/Element.idl @@ -8,6 +8,7 @@ #import #import #import +#import #import #import @@ -50,6 +51,9 @@ interface Element : Node { [Reflect=class] attribute DOMString className; [SameObject, PutForwards=value] readonly attribute DOMTokenList classList; + ShadowRoot attachShadow(ShadowRootInit init); + readonly attribute ShadowRoot? shadowRoot; + boolean matches(DOMString selectors); Element? closest(DOMString selectors); @@ -81,6 +85,12 @@ interface Element : Node { }; +dictionary ShadowRootInit { + required ShadowRootMode mode; + boolean delegatesFocus = false; + // FIXME: SlotAssignmentMode slotAssignment = "named"; +}; + Element includes ParentNode; Element includes ChildNode; Element includes InnerHTML;