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.
This commit is contained in:
Luke Wilde 2021-10-02 20:33:45 +01:00 committed by Andreas Kling
parent bfd089fbd1
commit 8d6db36cbb
Notes: sideshowbarker 2024-07-18 03:11:18 +09:00
10 changed files with 221 additions and 1 deletions

View file

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

View file

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

View file

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

View file

@ -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 objects 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();
}
}

View file

@ -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;
};
}

View file

@ -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;
};
}

View file

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

View file

@ -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 objects 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();
}
}

View file

@ -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;
};
}

View file

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