ladybird/Libraries/LibWeb/DOM/SlotRegistry.cpp
Tim Ledbetter 18b8ba1fd3 LibWeb: Avoid subtree iteration when assigning slottables
This change introduces SlotRegistry to track slot elements per shadow
root. This allows us to iterate slots directly when assigning
slottables for a tree instead of walking an entire subtree.
2026-01-31 11:44:20 +01:00

74 lines
2 KiB
C++

/*
* Copyright (c) 2026, Tim Ledbetter <tim.ledbetter@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/DOM/SlotRegistry.h>
#include <LibWeb/HTML/HTMLSlotElement.h>
namespace Web::DOM {
void SlotRegistry::add(HTML::HTMLSlotElement& slot)
{
m_slot_count -= m_slots.remove_all_matching([](GC::Weak<HTML::HTMLSlotElement>& s) {
return !s;
});
auto slot_removed = m_slots.remove_first_matching([&](auto const& other_slot) {
return &slot == other_slot.ptr();
});
if (slot_removed)
--m_slot_count;
if (m_slots.is_empty() || m_slots.last()->is_before(slot) || !try_insert_in_tree_order(slot)) {
m_slots.empend(slot);
++m_slot_count;
}
}
bool SlotRegistry::try_insert_in_tree_order(HTML::HTMLSlotElement& slot)
{
// Walk forward from the slot we're adding to find the next registered slot. If we find one, insert before it.
auto& shadow_root = slot.root();
for (auto* node = slot.next_in_pre_order(&shadow_root); node; node = node->next_in_pre_order(&shadow_root)) {
auto* following_slot = as_if<HTML::HTMLSlotElement>(*node);
if (!following_slot)
continue;
auto index = m_slots.find_first_index_if([&](auto const& s) {
return s.ptr() == following_slot;
});
if (index.has_value()) {
m_slots.insert(index.value(), GC::Weak { slot });
++m_slot_count;
return true;
}
}
return false;
}
void SlotRegistry::remove(HTML::HTMLSlotElement& slot)
{
m_slot_count -= m_slots.remove_all_matching([&](GC::Weak<HTML::HTMLSlotElement>& s) {
return !s || &slot == s.ptr();
});
}
GC::Ptr<HTML::HTMLSlotElement> SlotRegistry::first_slot_with_name(FlyString const& name) const
{
for (auto const& slot : m_slots) {
if (slot && slot->slot_name() == name)
return slot.ptr();
}
return nullptr;
}
bool SlotRegistry::is_empty() const
{
return m_slot_count == 0;
}
}