浏览代码

LibWeb: Implement stub for ElementInternals

This implements a stub ElementInternals object which implements the
shadowRoot getter only.

Also implement attachInternals function.
Luke Warlow 1 年之前
父节点
当前提交
a65f1ecc37

+ 1 - 0
Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp

@@ -48,6 +48,7 @@ static bool is_platform_object(Type const& type)
         "DocumentType"sv,
         "DOMRectReadOnly"sv,
         "DynamicsCompressorNode"sv,
+        "ElementInternals"sv,
         "EventTarget"sv,
         "FileList"sv,
         "FontFace"sv,

+ 1 - 0
Meta/gn/secondary/Userland/Libraries/LibWeb/HTML/BUILD.gn

@@ -30,6 +30,7 @@ source_set("HTML") {
     "Dates.cpp",
     "DecodedImageData.cpp",
     "DocumentState.cpp",
+    "ElementInternals.cpp",
     "ErrorEvent.cpp",
     "EventHandler.cpp",
     "EventNames.cpp",

+ 1 - 0
Meta/gn/secondary/Userland/Libraries/LibWeb/idl_files.gni

@@ -119,6 +119,7 @@ standard_idl_files = [
   "//Userland/Libraries/LibWeb/HTML/DataTransfer.idl",
   "//Userland/Libraries/LibWeb/HTML/DOMParser.idl",
   "//Userland/Libraries/LibWeb/HTML/DOMStringMap.idl",
+  "//Userland/Libraries/LibWeb/HTML/ElementInternals.idl",
   "//Userland/Libraries/LibWeb/HTML/ErrorEvent.idl",
   "//Userland/Libraries/LibWeb/HTML/EventSource.idl",
   "//Userland/Libraries/LibWeb/HTML/FormDataEvent.idl",

+ 1 - 0
Userland/Libraries/LibWeb/CMakeLists.txt

@@ -273,6 +273,7 @@ set(SOURCES
     HTML/DOMParser.cpp
     HTML/DOMStringMap.cpp
     HTML/DataTransfer.cpp
+    HTML/ElementInternals.cpp
     HTML/ErrorEvent.cpp
     HTML/EventHandler.cpp
     HTML/EventSource.cpp

+ 2 - 0
Userland/Libraries/LibWeb/DOM/Element.h

@@ -405,6 +405,8 @@ protected:
 
     virtual bool id_reference_exists(String const&) const override;
 
+    CustomElementState custom_element_state() const { return m_custom_element_state; }
+
 private:
     void make_html_uppercased_qualified_name();
 

+ 1 - 0
Userland/Libraries/LibWeb/Forward.h

@@ -352,6 +352,7 @@ class DecodedImageData;
 class DocumentState;
 class DOMParser;
 class DOMStringMap;
+class ElementInternals;
 class ErrorEvent;
 class EventHandler;
 class EventLoop;

+ 61 - 0
Userland/Libraries/LibWeb/HTML/ElementInternals.cpp

@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2024, the Ladybird developers.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibWeb/Bindings/ElementInternalsPrototype.h>
+#include <LibWeb/Bindings/Intrinsics.h>
+#include <LibWeb/DOM/ShadowRoot.h>
+#include <LibWeb/HTML/ElementInternals.h>
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+JS_DEFINE_ALLOCATOR(ElementInternals);
+
+JS::NonnullGCPtr<ElementInternals> ElementInternals::create(JS::Realm& realm, HTMLElement& target_element)
+{
+    return realm.heap().allocate<ElementInternals>(realm, realm, target_element);
+}
+
+ElementInternals::ElementInternals(JS::Realm& realm, HTMLElement& target_element)
+    : Bindings::PlatformObject(realm)
+    , m_target_element(target_element)
+{
+}
+
+// https://html.spec.whatwg.org/#dom-elementinternals-shadowroot
+JS::GCPtr<DOM::ShadowRoot> ElementInternals::shadow_root() const
+{
+    // 1. Let target be this's target element.
+    auto target = m_target_element;
+
+    // 2. If target is not a shadow host, then return null.
+    if (!target->is_shadow_host())
+        return nullptr;
+
+    // 3. Let shadow be target's shadow root.
+    auto shadow = target->shadow_root();
+
+    // 4. If shadow's available to element internals is false, then return null.
+    if (!shadow->available_to_element_internals())
+        return nullptr;
+
+    // 5. Return shadow.
+    return shadow;
+}
+
+void ElementInternals::initialize(JS::Realm& realm)
+{
+    Base::initialize(realm);
+    WEB_SET_PROTOTYPE_FOR_INTERFACE(ElementInternals);
+}
+
+void ElementInternals::visit_edges(JS::Cell::Visitor& visitor)
+{
+    Base::visit_edges(visitor);
+    visitor.visit(m_target_element);
+}
+
+}

+ 36 - 0
Userland/Libraries/LibWeb/HTML/ElementInternals.h

@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2024, the Ladybird developers.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/WeakPtr.h>
+#include <LibJS/Heap/GCPtr.h>
+#include <LibWeb/Bindings/PlatformObject.h>
+#include <LibWeb/Forward.h>
+
+namespace Web::HTML {
+
+// https://html.spec.whatwg.org/multipage/custom-elements.html#elementinternals
+class ElementInternals final : public Bindings::PlatformObject {
+    WEB_PLATFORM_OBJECT(ElementInternals, Bindings::PlatformObject);
+    JS_DECLARE_ALLOCATOR(ElementInternals);
+
+public:
+    static JS::NonnullGCPtr<ElementInternals> create(JS::Realm&, HTMLElement& target_element);
+
+    JS::GCPtr<DOM::ShadowRoot> shadow_root() const;
+
+private:
+    explicit ElementInternals(JS::Realm&, HTMLElement& target_element);
+
+    virtual void initialize(JS::Realm&) override;
+    virtual void visit_edges(JS::Cell::Visitor& visitor) override;
+
+    // https://html.spec.whatwg.org/multipage/custom-elements.html#internals-target
+    JS::NonnullGCPtr<HTMLElement> m_target_element;
+};
+
+}

+ 44 - 0
Userland/Libraries/LibWeb/HTML/ElementInternals.idl

@@ -0,0 +1,44 @@
+#import <DOM/ShadowRoot.idl>
+
+// https://html.spec.whatwg.org/multipage/custom-elements.html#elementinternals
+[Exposed=Window]
+interface ElementInternals {
+    // Shadow root access
+    readonly attribute ShadowRoot? shadowRoot;
+
+    // Form-associated custom elements
+    [FIXME] undefined setFormValue((File or USVString or FormData)? value,
+                            optional (File or USVString or FormData)? state);
+
+    [FIXME] readonly attribute HTMLFormElement? form;
+
+    [FIXME] undefined setValidity(optional ValidityStateFlags flags = {},
+                          optional DOMString message,
+                          optional HTMLElement anchor);
+    [FIXME] readonly attribute boolean willValidate;
+    [FIXME] readonly attribute ValidityState validity;
+    [FIXME] readonly attribute DOMString validationMessage;
+    [FIXME] boolean checkValidity();
+    [FIXME] boolean reportValidity();
+
+    [FIXME] readonly attribute NodeList labels;
+
+    // Custom state pseudo-class
+    [FIXME, SameObject] readonly attribute CustomStateSet states;
+};
+
+// Accessibility semantics
+// ElementInternals includes ARIAMixin;
+
+dictionary ValidityStateFlags {
+    boolean valueMissing = false;
+    boolean typeMismatch = false;
+    boolean patternMismatch = false;
+    boolean tooLong = false;
+    boolean tooShort = false;
+    boolean rangeUnderflow = false;
+    boolean rangeOverflow = false;
+    boolean stepMismatch = false;
+    boolean badInput = false;
+    boolean customError = false;
+};

+ 37 - 0
Userland/Libraries/LibWeb/HTML/HTMLElement.cpp

@@ -13,7 +13,9 @@
 #include <LibWeb/DOM/LiveNodeList.h>
 #include <LibWeb/DOM/ShadowRoot.h>
 #include <LibWeb/HTML/BrowsingContext.h>
+#include <LibWeb/HTML/CustomElements/CustomElementDefinition.h>
 #include <LibWeb/HTML/DOMStringMap.h>
+#include <LibWeb/HTML/ElementInternals.h>
 #include <LibWeb/HTML/EventHandler.h>
 #include <LibWeb/HTML/Focus.h>
 #include <LibWeb/HTML/HTMLAnchorElement.h>
@@ -60,6 +62,7 @@ void HTMLElement::visit_edges(Cell::Visitor& visitor)
     Base::visit_edges(visitor);
     visitor.visit(m_dataset);
     visitor.visit(m_labels);
+    visitor.visit(m_attached_internals);
 }
 
 JS::NonnullGCPtr<DOMStringMap> HTMLElement::dataset()
@@ -608,6 +611,40 @@ TokenizedFeature::NoOpener HTMLElement::get_an_elements_noopener(StringView targ
     return TokenizedFeature::NoOpener::No;
 }
 
+WebIDL::ExceptionOr<JS::NonnullGCPtr<ElementInternals>> HTMLElement::attach_internals()
+{
+    // 1. If this's is value is not null, then throw a "NotSupportedError" DOMException.
+    if (is_value().has_value())
+        return WebIDL::NotSupportedError::create(realm(), "ElementInternals cannot be attached to a customized build-in element"_fly_string);
+
+    // 2. Let definition be the result of looking up a custom element definition given this's node document, its namespace, its local name, and null as the is value.
+    auto definition = document().lookup_custom_element_definition(namespace_uri(), local_name(), is_value());
+
+    // 3. If definition is null, then throw an "NotSupportedError" DOMException.
+    if (!definition)
+        return WebIDL::NotSupportedError::create(realm(), "ElementInternals cannot be attached to an element that is not a custom element"_fly_string);
+
+    // 4. If definition's disable internals is true, then throw a "NotSupportedError" DOMException.
+    if (definition->disable_internals())
+        return WebIDL::NotSupportedError::create(realm(), "ElementInternals are disabled for this custom element"_fly_string);
+
+    // 5. If this's attached internals is non-null, then throw an "NotSupportedError" DOMException.
+    if (m_attached_internals)
+        return WebIDL::NotSupportedError::create(realm(), "ElementInternals already attached"_fly_string);
+
+    // 6. If this's custom element state is not "precustomized" or "custom", then throw a "NotSupportedError" DOMException.
+    if (!first_is_one_of(custom_element_state(), DOM::CustomElementState::Precustomized, DOM::CustomElementState::Custom))
+        return WebIDL::NotSupportedError::create(realm(), "Custom element is in an invalid state to attach ElementInternals"_fly_string);
+
+    // 7. Set this's attached internals to a new ElementInternals instance whose target element is this.
+    auto internals = ElementInternals::create(realm(), *this);
+
+    m_attached_internals = internals;
+
+    // 8. Return this's attached internals.
+    return { internals };
+}
+
 void HTMLElement::did_receive_focus()
 {
     if (m_content_editable_state != ContentEditableState::True)

+ 5 - 0
Userland/Libraries/LibWeb/HTML/HTMLElement.h

@@ -75,6 +75,8 @@ public:
     String get_an_elements_target() const;
     TokenizedFeature::NoOpener get_an_elements_noopener(StringView target) const;
 
+    WebIDL::ExceptionOr<JS::NonnullGCPtr<ElementInternals>> attach_internals();
+
 protected:
     HTMLElement(DOM::Document&, DOM::QualifiedName);
 
@@ -97,6 +99,9 @@ private:
 
     JS::GCPtr<DOM::NodeList> m_labels;
 
+    // https://html.spec.whatwg.org/multipage/custom-elements.html#attached-internals
+    JS::GCPtr<ElementInternals> m_attached_internals;
+
     enum class ContentEditableState {
         True,
         False,

+ 2 - 1
Userland/Libraries/LibWeb/HTML/HTMLElement.idl

@@ -1,5 +1,6 @@
 #import <CSS/ElementCSSInlineStyle.idl>
 #import <HTML/DOMStringMap.idl>
+#import <HTML/ElementInternals.idl>
 #import <DOM/Element.idl>
 #import <DOM/EventHandler.idl>
 
@@ -28,7 +29,7 @@ interface HTMLElement : Element {
     [LegacyNullToEmptyString, CEReactions] attribute DOMString innerText;
     [LegacyNullToEmptyString, CEReactions] attribute DOMString outerText;
 
-    [FIXME] ElementInternals attachInternals();
+    ElementInternals attachInternals();
 
     // The popover API
     [FIXME] undefined showPopover();

+ 1 - 0
Userland/Libraries/LibWeb/idl_files.cmake

@@ -103,6 +103,7 @@ libweb_js_bindings(HTML/CustomElements/CustomElementRegistry)
 libweb_js_bindings(HTML/DOMParser)
 libweb_js_bindings(HTML/DOMStringMap)
 libweb_js_bindings(HTML/DataTransfer)
+libweb_js_bindings(HTML/ElementInternals)
 libweb_js_bindings(HTML/ErrorEvent)
 libweb_js_bindings(HTML/EventSource)
 libweb_js_bindings(HTML/FormDataEvent)