From 6bb4a2bfaa1dd919e3f5e6bc370e9ea5d449e8c6 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Tue, 19 Mar 2024 15:41:02 +0100 Subject: [PATCH] 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 :^) --- .../Libraries/LibWeb/DOM/HTMLCollection.cpp | 36 ++++++++++++------- .../Libraries/LibWeb/DOM/HTMLCollection.h | 3 ++ 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/Userland/Libraries/LibWeb/DOM/HTMLCollection.cpp b/Userland/Libraries/LibWeb/DOM/HTMLCollection.cpp index b899f20c60c..da67dd22f97 100644 --- a/Userland/Libraries/LibWeb/DOM/HTMLCollection.cpp +++ b/Userland/Libraries/LibWeb/DOM/HTMLCollection.cpp @@ -6,6 +6,7 @@ */ #include +#include #include #include #include @@ -45,24 +46,33 @@ void HTMLCollection::visit_edges(Cell::Visitor& visitor) { Base::visit_edges(visitor); visitor.visit(m_root); + for (auto& element : m_cached_elements) + visitor.visit(element); } JS::MarkedVector HTMLCollection::collect_matching_elements() const { - JS::MarkedVector elements(m_root->heap()); - if (m_scope == Scope::Descendants) { - m_root->for_each_in_subtree_of_type([&](auto& element) { - if (m_filter(element)) - elements.append(const_cast(&element)); - return IterationDecision::Continue; - }); - } else { - m_root->for_each_child_of_type([&](auto& element) { - if (m_filter(element)) - elements.append(const_cast(&element)); - return IterationDecision::Continue; - }); + if (m_cached_dom_tree_version != root()->document().dom_tree_version()) { + m_cached_elements.clear(); + if (m_scope == Scope::Descendants) { + m_root->for_each_in_subtree_of_type([&](auto& element) { + if (m_filter(element)) + m_cached_elements.append(element); + return IterationDecision::Continue; + }); + } else { + m_root->for_each_child_of_type([&](auto& element) { + if (m_filter(element)) + m_cached_elements.append(element); + return IterationDecision::Continue; + }); + } + m_cached_dom_tree_version = root()->document().dom_tree_version(); } + + JS::MarkedVector elements(heap()); + for (auto& element : m_cached_elements) + elements.append(element); return elements; } diff --git a/Userland/Libraries/LibWeb/DOM/HTMLCollection.h b/Userland/Libraries/LibWeb/DOM/HTMLCollection.h index 447f99ac0ac..563061c0820 100644 --- a/Userland/Libraries/LibWeb/DOM/HTMLCollection.h +++ b/Userland/Libraries/LibWeb/DOM/HTMLCollection.h @@ -60,6 +60,9 @@ protected: private: virtual void visit_edges(Cell::Visitor&) override; + mutable u64 m_cached_dom_tree_version { 0 }; + mutable Vector> m_cached_elements; + JS::NonnullGCPtr m_root; Function m_filter;