Browse Source

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 :^)
Andreas Kling 1 year ago
parent
commit
6bb4a2bfaa

+ 23 - 13
Userland/Libraries/LibWeb/DOM/HTMLCollection.cpp

@@ -6,6 +6,7 @@
  */
 
 #include <LibWeb/Bindings/Intrinsics.h>
+#include <LibWeb/DOM/Document.h>
 #include <LibWeb/DOM/Element.h>
 #include <LibWeb/DOM/HTMLCollection.h>
 #include <LibWeb/DOM/ParentNode.h>
@@ -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<Element*> HTMLCollection::collect_matching_elements() const
 {
-    JS::MarkedVector<Element*> elements(m_root->heap());
-    if (m_scope == Scope::Descendants) {
-        m_root->for_each_in_subtree_of_type<Element>([&](auto& element) {
-            if (m_filter(element))
-                elements.append(const_cast<Element*>(&element));
-            return IterationDecision::Continue;
-        });
-    } else {
-        m_root->for_each_child_of_type<Element>([&](auto& element) {
-            if (m_filter(element))
-                elements.append(const_cast<Element*>(&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<Element>([&](auto& element) {
+                if (m_filter(element))
+                    m_cached_elements.append(element);
+                return IterationDecision::Continue;
+            });
+        } else {
+            m_root->for_each_child_of_type<Element>([&](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<Element*> elements(heap());
+    for (auto& element : m_cached_elements)
+        elements.append(element);
     return elements;
 }
 

+ 3 - 0
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<JS::NonnullGCPtr<Element>> m_cached_elements;
+
     JS::NonnullGCPtr<ParentNode> m_root;
     Function<bool(Element const&)> m_filter;