Skip to content

Commit

Permalink
LibWeb: Make "assign slottables for a tree" fast when there are no slots
Browse files Browse the repository at this point in the history
We achieve this by keeping track of the number of HTMLSlotElements
inside each ShadowRoot (do via ad-hoc insertion and removal steps.)

This allows slottables assignment to skip over entire shadow roots when
we know they have no slots anyway.

Massive speedup on https://wpt.fyi/ which no longer takes minutes/hours
to load, but instead a "mere" 19 seconds. :^)
  • Loading branch information
awesomekling committed Jan 23, 2025
1 parent 51a9177 commit 03b504f
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 11 deletions.
17 changes: 17 additions & 0 deletions Libraries/LibWeb/DOM/ShadowRoot.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,20 @@ class ShadowRoot final : public DocumentFragment {

virtual void finalize() override;

void increment_slot_count() { ++m_slot_count; }
void decrement_slot_count() { --m_slot_count; }
[[nodiscard]] size_t slot_count() const { return m_slot_count; }

template<typename Callback>
void for_each_slot(Callback callback)
{
if (m_slot_count == 0)
return;
for_each_in_subtree_of_type<HTML::HTMLSlotElement>([&](HTML::HTMLSlotElement& slot) {
return callback(slot);
});
}

protected:
virtual void visit_edges(Cell::Visitor&) override;

Expand Down Expand Up @@ -92,6 +106,9 @@ class ShadowRoot final : public DocumentFragment {

GC::Ptr<CSS::StyleSheetList> m_style_sheets;
mutable GC::Ptr<WebIDL::ObservableArray> m_adopted_style_sheets;

// AD-HOC: Number of HTMLSlotElement nodes that are descendants of this ShadowRoot.
size_t m_slot_count { 0 };
};

template<>
Expand Down
22 changes: 11 additions & 11 deletions Libraries/LibWeb/DOM/Slottable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,32 +74,32 @@ GC::Ptr<HTML::HTMLSlotElement> find_a_slot(Slottable const& slottable, OpenFlag
// 5. If shadow’s slot assignment is "manual", then return the slot in shadow’s descendants whose manually assigned
// nodes contains slottable, if any; otherwise null.
if (shadow->slot_assignment() == Bindings::SlotAssignmentMode::Manual) {
GC::Ptr<HTML::HTMLSlotElement> slot;
GC::Ptr<HTML::HTMLSlotElement> found_slot;

shadow->for_each_in_subtree_of_type<HTML::HTMLSlotElement>([&](auto& child) {
if (!child.manually_assigned_nodes().contains_slow(slottable))
shadow->for_each_slot([&](auto& slot) {
if (!slot.manually_assigned_nodes().contains_slow(slottable))
return TraversalDecision::Continue;

slot = child;
found_slot = slot;
return TraversalDecision::Break;
});

return slot;
return found_slot;
}

// 6. Return the first slot in tree order in shadow’s descendants whose name is slottable’s name, if any; otherwise null.
auto const& slottable_name = slottable.visit([](auto const& node) { return node->slottable_name(); });
GC::Ptr<HTML::HTMLSlotElement> slot;
GC::Ptr<HTML::HTMLSlotElement> found_slot;

shadow->for_each_in_subtree_of_type<HTML::HTMLSlotElement>([&](auto& child) {
if (child.slot_name() != slottable_name)
shadow->for_each_slot([&](auto& slot) {
if (slot.slot_name() != slottable_name)
return TraversalDecision::Continue;

slot = child;
found_slot = slot;
return TraversalDecision::Break;
});

return slot;
return found_slot;
}

// https://dom.spec.whatwg.org/#find-slotables
Expand Down Expand Up @@ -186,7 +186,7 @@ void assign_slottables_for_a_tree(GC::Ref<Node> root)

// To assign slottables for a tree, given a node root, run assign slottables for each slot slot in root’s inclusive
// descendants, in tree order.
root->for_each_in_inclusive_subtree_of_type<HTML::HTMLSlotElement>([](auto& slot) {
static_cast<DOM::ShadowRoot&>(*root).for_each_slot([](auto& slot) {
assign_slottables(slot);
return TraversalDecision::Continue;
});
Expand Down
21 changes: 21 additions & 0 deletions Libraries/LibWeb/HTML/HTMLSlotElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <LibWeb/Bindings/HTMLSlotElementPrototype.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/DOM/Element.h>
#include <LibWeb/DOM/ShadowRoot.h>
#include <LibWeb/DOM/Text.h>
#include <LibWeb/HTML/HTMLSlotElement.h>

Expand Down Expand Up @@ -147,4 +148,24 @@ void HTMLSlotElement::attribute_changed(FlyString const& local_name, Optional<St
}
}

void HTMLSlotElement::inserted()
{
Base::inserted();
auto& root = this->root();
if (!root.is_shadow_root())
return;
static_cast<DOM::ShadowRoot&>(root).increment_slot_count();
}

void HTMLSlotElement::removed_from(Node* old_parent)
{
Base::removed_from(old_parent);
if (!old_parent)
return;
auto& root = old_parent->root();
if (!root.is_shadow_root())
return;
static_cast<DOM::ShadowRoot&>(root).decrement_slot_count();
}

}
3 changes: 3 additions & 0 deletions Libraries/LibWeb/HTML/HTMLSlotElement.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ class HTMLSlotElement final
virtual void initialize(JS::Realm&) override;
virtual void visit_edges(JS::Cell::Visitor&) override;

virtual void inserted() override;
virtual void removed_from(Node*) override;

virtual void attribute_changed(FlyString const& name, Optional<String> const& old_value, Optional<String> const& value, Optional<FlyString> const& namespace_) override;

// https://html.spec.whatwg.org/multipage/scripting.html#manually-assigned-nodes
Expand Down

0 comments on commit 03b504f

Please sign in to comment.