浏览代码

LibWeb: Introduce the slottable concept for DOM elements and text nodes

A slottable is either a DOM element or a DOM text node. They may be
assigned to slots (HTMLSlotElement) either automatically or manually.
Automatic assignment occurs by matching a slot's `name` attribute to
a slottable's `slot` attribute. Manual assignment occurs by using the
slot's (not yet implemented) `assign` API.

This commit does not perform the above assignments. It just sets up the
slottable concept via IDL and hooks the slottable mixin into the element
and text nodes.
Timothy Flynn 1 年之前
父节点
当前提交
45b36bd08a

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

@@ -175,6 +175,7 @@ set(SOURCES
     DOM/RadioNodeList.cpp
     DOM/Range.cpp
     DOM/ShadowRoot.cpp
+    DOM/Slottable.cpp
     DOM/StaticNodeList.cpp
     DOM/StaticRange.cpp
     DOM/StyleElementUtils.cpp

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

@@ -86,6 +86,8 @@ void Element::initialize(JS::Realm& realm)
 void Element::visit_edges(Cell::Visitor& visitor)
 {
     Base::visit_edges(visitor);
+    SlottableMixin::visit_edges(visitor);
+
     visitor.visit(m_attributes.ptr());
     visitor.visit(m_inline_style.ptr());
     visitor.visit(m_class_list.ptr());

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

@@ -17,6 +17,7 @@
 #include <LibWeb/DOM/NonDocumentTypeChildNode.h>
 #include <LibWeb/DOM/ParentNode.h>
 #include <LibWeb/DOM/QualifiedName.h>
+#include <LibWeb/DOM/Slottable.h>
 #include <LibWeb/HTML/AttributeNames.h>
 #include <LibWeb/HTML/EventLoop/Task.h>
 #include <LibWeb/HTML/ScrollOptions.h>
@@ -66,6 +67,7 @@ class Element
     : public ParentNode
     , public ChildNode<Element>
     , public NonDocumentTypeChildNode<Element>
+    , public SlottableMixin
     , public ARIA::ARIAMixin {
     WEB_PLATFORM_OBJECT(Element, ParentNode);
 

+ 3 - 0
Userland/Libraries/LibWeb/DOM/Element.idl

@@ -8,8 +8,10 @@
 #import <DOM/NodeList.idl>
 #import <DOM/ParentNode.idl>
 #import <DOM/ShadowRoot.idl>
+#import <DOM/Slottable.idl>
 #import <Geometry/DOMRect.idl>
 #import <Geometry/DOMRectList.idl>
+#import <HTML/HTMLSlotElement.idl>
 #import <HTML/Window.idl>
 
 enum ScrollLogicalPosition { "start", "center", "end", "nearest" };
@@ -99,3 +101,4 @@ Element includes ChildNode;
 Element includes InnerHTML;
 // https://www.w3.org/TR/wai-aria-1.2/#idl_element
 Element includes ARIAMixin;
+Element includes Slottable;

+ 9 - 0
Userland/Libraries/LibWeb/DOM/Node.cpp

@@ -930,6 +930,15 @@ Element* Node::parent_or_shadow_host_element()
     return nullptr;
 }
 
+Slottable Node::as_slottable()
+{
+    VERIFY(is_slottable());
+
+    if (is_element())
+        return JS::NonnullGCPtr { static_cast<Element&>(*this) };
+    return JS::NonnullGCPtr { static_cast<Text&>(*this) };
+}
+
 JS::NonnullGCPtr<NodeList> Node::child_nodes()
 {
     if (!m_child_nodes) {

+ 3 - 0
Userland/Libraries/LibWeb/DOM/Node.h

@@ -15,6 +15,7 @@
 #include <AK/Vector.h>
 #include <LibWeb/DOM/AccessibilityTreeNode.h>
 #include <LibWeb/DOM/EventTarget.h>
+#include <LibWeb/DOM/Slottable.h>
 #include <LibWeb/DOMParsing/XMLSerializer.h>
 #include <LibWeb/WebIDL/ExceptionOr.h>
 
@@ -266,6 +267,8 @@ public:
     template<typename Callback>
     IterationDecision for_each_shadow_including_descendant(Callback);
 
+    Slottable as_slottable();
+
     Node* parent() { return m_parent.ptr(); }
     Node const* parent() const { return m_parent.ptr(); }
 

+ 32 - 0
Userland/Libraries/LibWeb/DOM/Slottable.cpp

@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibWeb/Bindings/MainThreadVM.h>
+#include <LibWeb/DOM/Element.h>
+#include <LibWeb/DOM/Node.h>
+#include <LibWeb/DOM/ShadowRoot.h>
+#include <LibWeb/DOM/Slottable.h>
+#include <LibWeb/DOM/Text.h>
+#include <LibWeb/HTML/HTMLSlotElement.h>
+
+namespace Web::DOM {
+
+SlottableMixin::~SlottableMixin() = default;
+
+void SlottableMixin::visit_edges(JS::Cell::Visitor& visitor)
+{
+    visitor.visit(m_assigned_slot);
+    visitor.visit(m_manual_slot_assignment);
+}
+
+// https://dom.spec.whatwg.org/#dom-slotable-assignedslot
+JS::GCPtr<HTML::HTMLSlotElement> SlottableMixin::assigned_slot()
+{
+    // FIXME: The assignedSlot getter steps are to return the result of find a slot given this and with the open flag set.
+    return nullptr;
+}
+
+}

+ 50 - 0
Userland/Libraries/LibWeb/DOM/Slottable.h

@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/String.h>
+#include <AK/Variant.h>
+#include <LibJS/Heap/Cell.h>
+#include <LibJS/Heap/GCPtr.h>
+#include <LibWeb/Forward.h>
+
+namespace Web::DOM {
+
+// https://dom.spec.whatwg.org/#concept-slotable
+using Slottable = Variant<JS::NonnullGCPtr<Element>, JS::NonnullGCPtr<Text>>;
+
+// https://dom.spec.whatwg.org/#mixin-slotable
+class SlottableMixin {
+public:
+    virtual ~SlottableMixin();
+
+    String const& slottable_name() const { return m_name; } // Not called `name` to distinguish from `Element::name`.
+    void set_slottable_name(String name) { m_name = move(name); }
+
+    JS::GCPtr<HTML::HTMLSlotElement> assigned_slot();
+
+    JS::GCPtr<HTML::HTMLSlotElement> assigned_slot_internal() const { return m_assigned_slot; }
+    void set_assigned_slot(JS::GCPtr<HTML::HTMLSlotElement> assigned_slot) { m_assigned_slot = assigned_slot; }
+
+    JS::GCPtr<HTML::HTMLSlotElement> manual_slot_assignment() { return m_manual_slot_assignment; }
+    void set_manual_slot_assignment(JS::GCPtr<HTML::HTMLSlotElement> manual_slot_assignment) { m_manual_slot_assignment = manual_slot_assignment; }
+
+protected:
+    void visit_edges(JS::Cell::Visitor&);
+
+private:
+    // https://dom.spec.whatwg.org/#slotable-name
+    String m_name;
+
+    // https://dom.spec.whatwg.org/#slotable-assigned-slot
+    JS::GCPtr<HTML::HTMLSlotElement> m_assigned_slot;
+
+    // https://dom.spec.whatwg.org/#slottable-manual-slot-assignment
+    JS::GCPtr<HTML::HTMLSlotElement> m_manual_slot_assignment;
+};
+
+}

+ 4 - 0
Userland/Libraries/LibWeb/DOM/Slottable.idl

@@ -0,0 +1,4 @@
+// https://dom.spec.whatwg.org/#mixin-slotable
+interface mixin Slottable {
+    readonly attribute HTMLSlotElement? assignedSlot;
+};

+ 2 - 0
Userland/Libraries/LibWeb/DOM/Text.cpp

@@ -32,6 +32,8 @@ void Text::initialize(JS::Realm& realm)
 void Text::visit_edges(Cell::Visitor& visitor)
 {
     Base::visit_edges(visitor);
+    SlottableMixin::visit_edges(visitor);
+
     visitor.visit(dynamic_cast<JS::Cell*>(m_owner.ptr()));
 }
 

+ 4 - 1
Userland/Libraries/LibWeb/DOM/Text.h

@@ -10,6 +10,7 @@
 #include <AK/DeprecatedFlyString.h>
 #include <AK/DeprecatedString.h>
 #include <LibWeb/DOM/CharacterData.h>
+#include <LibWeb/DOM/Slottable.h>
 
 namespace Web::DOM {
 
@@ -19,7 +20,9 @@ public:
     virtual void did_edit_text_node(Badge<HTML::BrowsingContext>) = 0;
 };
 
-class Text : public CharacterData {
+class Text
+    : public CharacterData
+    , public SlottableMixin {
     WEB_PLATFORM_OBJECT(Text, CharacterData);
 
 public:

+ 4 - 0
Userland/Libraries/LibWeb/DOM/Text.idl

@@ -1,4 +1,6 @@
 #import <DOM/CharacterData.idl>
+#import <DOM/Slottable.idl>
+#import <HTML/HTMLSlotElement.idl>
 
 // https://dom.spec.whatwg.org/#text
 [Exposed=Window]
@@ -8,3 +10,5 @@ interface Text : CharacterData {
     [NewObject] Text splitText(unsigned long offset);
 
 };
+
+Text includes Slottable;