ladybird/Userland/Libraries/LibWeb/DOM/HTMLCollection.h
Andreas Kling 6bb4a2bfaa LibWeb: Let HTMLCollection cache its element list
Use the new DOM tree version mechanism to allow HTMLCollection to
remember its internal list of elements instead of rebuilding it on
every access.

This avoids thousands of full DOM walks while loading our GitHub repo.

~15% speed-up on jQuery subtests in Speedometer 3.0 :^)
2024-03-19 20:59:36 +01:00

72 lines
2.4 KiB
C++

/*
* Copyright (c) 2021-2022, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2023, Luke Wilde <lukew@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Function.h>
#include <LibJS/Heap/GCPtr.h>
#include <LibWeb/Bindings/PlatformObject.h>
#include <LibWeb/Forward.h>
namespace Web::DOM {
// NOTE: HTMLCollection is in the DOM namespace because it's part of the DOM specification.
// This class implements a live, filtered view of a DOM subtree.
// When constructing an HTMLCollection, you provide a root node + a filter.
// The filter is a simple Function object that answers the question
// "is this Element part of the collection?"
// FIXME: HTMLCollection currently does no caching. It will re-filter on every access!
// We should teach it how to cache results. The main challenge is invalidating
// these caches, since this needs to happen on various kinds of DOM mutation.
class HTMLCollection : public Bindings::PlatformObject {
WEB_PLATFORM_OBJECT(HTMLCollection, Bindings::PlatformObject);
JS_DECLARE_ALLOCATOR(HTMLCollection);
public:
enum class Scope {
Children,
Descendants,
};
[[nodiscard]] static JS::NonnullGCPtr<HTMLCollection> create(ParentNode& root, Scope, Function<bool(Element const&)> filter);
virtual ~HTMLCollection() override;
size_t length() const;
Element* item(size_t index) const;
Element* named_item(FlyString const& name) const;
JS::MarkedVector<Element*> collect_matching_elements() const;
virtual WebIDL::ExceptionOr<JS::Value> item_value(size_t index) const override;
virtual WebIDL::ExceptionOr<JS::Value> named_item_value(FlyString const& name) const override;
virtual Vector<FlyString> supported_property_names() const override;
virtual bool is_supported_property_index(u32) const override;
protected:
HTMLCollection(ParentNode& root, Scope, Function<bool(Element const&)> filter);
virtual void initialize(JS::Realm&) override;
JS::NonnullGCPtr<ParentNode> root() { return *m_root; }
JS::NonnullGCPtr<ParentNode const> root() const { return *m_root; }
private:
virtual void visit_edges(Cell::Visitor&) override;
mutable u64 m_cached_dom_tree_version { 0 };
mutable Vector<JS::NonnullGCPtr<Element>> m_cached_elements;
JS::NonnullGCPtr<ParentNode> m_root;
Function<bool(Element const&)> m_filter;
Scope m_scope { Scope::Descendants };
};
}