Browse Source

LibWeb: Add support for NodeList

This introduces 3 classes: NodeList, StaticNodeList and LiveNodeList.
NodeList is the base of the static and live versions. Static is a
snapshot whereas live acts on the underlying data and thus inhibits
the same issues we have currently with HTMLCollection.

They were split into separate classes to not have them weirdly
mis-mashed together.

The create functions for static and live both return a NNRP to the base
class. This is to prevent having to do awkward casting at creation
and/or return, as the bindings expect to see the base NodeList only.
Luke Wilde 3 năm trước cách đây
mục cha
commit
8d6db36cbb

+ 2 - 1
Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator.cpp

@@ -1226,7 +1226,7 @@ static void generate_wrap_statement(SourceGenerator& generator, String const& va
 )~~~");
     } else if (type.name == "ArrayFromVector") {
         // FIXME: Remove this fake type hack once it's no longer needed.
-        //        Basically once we have NodeList we can throw this out.
+        //        Basically once we have sequence<T> we can throw this out.
         scoped_generator.append(R"~~~(
     auto* new_array = JS::Array::create(global_object, 0);
     for (auto& element : @value@)
@@ -2736,6 +2736,7 @@ void generate_prototype_implementation(IDL::Interface const& interface)
 #include <LibWeb/Bindings/ImageDataWrapper.h>
 #include <LibWeb/Bindings/LocationObject.h>
 #include <LibWeb/Bindings/MessagePortWrapper.h>
+#include <LibWeb/Bindings/NodeListWrapper.h>
 #include <LibWeb/Bindings/NodeWrapperFactory.h>
 #include <LibWeb/Bindings/PerformanceTimingWrapper.h>
 #include <LibWeb/Bindings/RangeWrapper.h>

+ 3 - 0
Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h

@@ -212,6 +212,8 @@
 #include <LibWeb/Bindings/MouseEventConstructor.h>
 #include <LibWeb/Bindings/MouseEventPrototype.h>
 #include <LibWeb/Bindings/NodeConstructor.h>
+#include <LibWeb/Bindings/NodeListConstructor.h>
+#include <LibWeb/Bindings/NodeListPrototype.h>
 #include <LibWeb/Bindings/NodePrototype.h>
 #include <LibWeb/Bindings/PageTransitionEventConstructor.h>
 #include <LibWeb/Bindings/PageTransitionEventPrototype.h>
@@ -373,6 +375,7 @@
     ADD_WINDOW_OBJECT_INTERFACE(MessageEvent)              \
     ADD_WINDOW_OBJECT_INTERFACE(MouseEvent)                \
     ADD_WINDOW_OBJECT_INTERFACE(Node)                      \
+    ADD_WINDOW_OBJECT_INTERFACE(NodeList)                  \
     ADD_WINDOW_OBJECT_INTERFACE(PageTransitionEvent)       \
     ADD_WINDOW_OBJECT_INTERFACE(Performance)               \
     ADD_WINDOW_OBJECT_INTERFACE(PerformanceTiming)         \

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

@@ -66,12 +66,14 @@ set(SOURCES
     DOM/EventListener.cpp
     DOM/EventTarget.cpp
     DOM/HTMLCollection.cpp
+    DOM/LiveNodeList.cpp
     DOM/Node.cpp
     DOM/ParentNode.cpp
     DOM/Position.cpp
     DOM/ProcessingInstruction.cpp
     DOM/Range.cpp
     DOM/ShadowRoot.cpp
+    DOM/StaticNodeList.cpp
     DOM/Text.cpp
     DOM/Text.idl
     DOM/Timer.cpp
@@ -371,6 +373,7 @@ libweb_js_wrapper(DOM/HTMLCollection)
 libweb_js_wrapper(DOM/ProcessingInstruction)
 libweb_js_wrapper(DOM/ShadowRoot)
 libweb_js_wrapper(DOM/Node)
+libweb_js_wrapper(DOM/NodeList)
 libweb_js_wrapper(DOM/Range)
 libweb_js_wrapper(DOM/Text)
 libweb_js_wrapper(Geometry/DOMRect)

+ 54 - 0
Userland/Libraries/LibWeb/DOM/LiveNodeList.cpp

@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibWeb/DOM/LiveNodeList.h>
+#include <LibWeb/DOM/Node.h>
+
+namespace Web::DOM {
+
+LiveNodeList::LiveNodeList(Node& root, Function<bool(Node const&)> filter)
+    : m_root(root)
+    , m_filter(move(filter))
+{
+}
+
+NonnullRefPtrVector<Node> LiveNodeList::collection() const
+{
+    NonnullRefPtrVector<Node> nodes;
+    m_root->for_each_in_inclusive_subtree_of_type<Node>([&](auto& node) {
+        if (m_filter(node))
+            nodes.append(node);
+
+        return IterationDecision::Continue;
+    });
+    return nodes;
+}
+
+// https://dom.spec.whatwg.org/#dom-nodelist-length
+u32 LiveNodeList::length() const
+{
+    return collection().size();
+}
+
+// https://dom.spec.whatwg.org/#dom-nodelist-item
+Node const* LiveNodeList::item(u32 index) const
+{
+    // The item(index) method must return the indexth node in the collection. If there is no indexth node in the collection, then the method must return null.
+    auto nodes = collection();
+    if (index >= nodes.size())
+        return nullptr;
+    return &nodes[index];
+}
+
+// https://dom.spec.whatwg.org/#ref-for-dfn-supported-property-indices
+bool LiveNodeList::is_supported_property_index(u32 index) const
+{
+    // The object’s supported property indices are the numbers in the range zero to one less than the number of nodes represented by the collection.
+    // If there are no such elements, then there are no supported property indices.
+    return index < length();
+}
+
+}

+ 38 - 0
Userland/Libraries/LibWeb/DOM/LiveNodeList.h

@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/Function.h>
+#include <AK/NonnullRefPtrVector.h>
+#include <LibWeb/DOM/NodeList.h>
+
+namespace Web::DOM {
+
+// FIXME: Just like HTMLCollection, LiveNodeList currently does no caching.
+
+class LiveNodeList : public NodeList {
+public:
+    static NonnullRefPtr<NodeList> create(Node& root, Function<bool(Node const&)> filter)
+    {
+        return adopt_ref(*new LiveNodeList(root, move(filter)));
+    }
+
+    virtual u32 length() const override;
+    virtual Node const* item(u32 index) const override;
+
+    virtual bool is_supported_property_index(u32) const override;
+
+private:
+    LiveNodeList(Node& root, Function<bool(Node const&)> filter);
+
+    NonnullRefPtrVector<Node> collection() const;
+
+    NonnullRefPtr<Node> m_root;
+    Function<bool(Node const&)> m_filter;
+};
+
+}

+ 37 - 0
Userland/Libraries/LibWeb/DOM/NodeList.h

@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/Noncopyable.h>
+#include <AK/RefCounted.h>
+#include <LibWeb/Bindings/Wrappable.h>
+#include <LibWeb/Forward.h>
+
+namespace Web::DOM {
+
+// https://dom.spec.whatwg.org/#nodelist
+class NodeList
+    : public RefCounted<NodeList>
+    , public Bindings::Wrappable {
+    AK_MAKE_NONCOPYABLE(NodeList);
+    AK_MAKE_NONMOVABLE(NodeList);
+
+public:
+    using WrapperType = Bindings::NodeListWrapper;
+
+    virtual ~NodeList() override = default;
+
+    virtual u32 length() const = 0;
+    virtual Node const* item(u32 index) const = 0;
+
+    virtual bool is_supported_property_index(u32) const = 0;
+
+protected:
+    NodeList() = default;
+};
+
+}

+ 6 - 0
Userland/Libraries/LibWeb/DOM/NodeList.idl

@@ -0,0 +1,6 @@
+[Exposed=Window]
+interface NodeList {
+    getter Node? item(unsigned long index);
+    readonly attribute unsigned long length;
+    iterable<Node>;
+};

+ 39 - 0
Userland/Libraries/LibWeb/DOM/StaticNodeList.cpp

@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibWeb/DOM/StaticNodeList.h>
+
+namespace Web::DOM {
+
+StaticNodeList::StaticNodeList(NonnullRefPtrVector<Node>&& static_nodes)
+    : m_static_nodes(move(static_nodes))
+{
+}
+
+// https://dom.spec.whatwg.org/#dom-nodelist-length
+u32 StaticNodeList::length() const
+{
+    return m_static_nodes.size();
+}
+
+// https://dom.spec.whatwg.org/#dom-nodelist-item
+Node const* StaticNodeList::item(u32 index) const
+{
+    // The item(index) method must return the indexth node in the collection. If there is no indexth node in the collection, then the method must return null.
+    if (index >= m_static_nodes.size())
+        return nullptr;
+    return &m_static_nodes[index];
+}
+
+// https://dom.spec.whatwg.org/#ref-for-dfn-supported-property-indices
+bool StaticNodeList::is_supported_property_index(u32 index) const
+{
+    // The object’s supported property indices are the numbers in the range zero to one less than the number of nodes represented by the collection.
+    // If there are no such elements, then there are no supported property indices.
+    return index < m_static_nodes.size();
+}
+
+}

+ 35 - 0
Userland/Libraries/LibWeb/DOM/StaticNodeList.h

@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/NonnullRefPtrVector.h>
+#include <LibWeb/DOM/Node.h>
+#include <LibWeb/DOM/NodeList.h>
+
+namespace Web::DOM {
+
+class StaticNodeList : public NodeList {
+public:
+    static NonnullRefPtr<NodeList> create(NonnullRefPtrVector<Node>&& static_nodes)
+    {
+        return adopt_ref(*new StaticNodeList(move(static_nodes)));
+    }
+
+    virtual ~StaticNodeList() override = default;
+
+    virtual u32 length() const override;
+    virtual Node const* item(u32 index) const override;
+
+    virtual bool is_supported_property_index(u32) const override;
+
+private:
+    StaticNodeList(NonnullRefPtrVector<Node>&& static_nodes);
+
+    NonnullRefPtrVector<Node> m_static_nodes;
+};
+
+}

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

@@ -82,11 +82,14 @@ class EventHandler;
 class EventListener;
 class EventTarget;
 class HTMLCollection;
+class LiveNodeList;
 class Node;
+class NodeList;
 class ParentNode;
 class Position;
 class ProcessingInstruction;
 class ShadowRoot;
+class StaticNodeList;
 class Text;
 class Timer;
 class Window;
@@ -374,6 +377,7 @@ class MessageChannelWrapper;
 class MessageEventWrapper;
 class MessagePortWrapper;
 class MouseEventWrapper;
+class NodeListWrapper;
 class NodeWrapper;
 class PageTransitionEventWrapper;
 class PerformanceTimingWrapper;