Repository: LadybirdBrowser/ladybird
File: Libraries/LibWeb/DOM/SlotRegistry.cpp (C++)

Code before:
 * 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;

    // Insert in tree order.
    if (m_slots.is_empty() || m_slots.last()->is_before(slot)) {
        m_slots.append(GC::Weak { slot });
    } else {
        m_slots.insert_before_matching(GC::Weak { slot }, [&](auto& other_slot) {
            return slot.is_before(*other_slot);
        });
    }
    ++m_slot_count;
}

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(StringView 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;

Review comment (performance):
This could potentially become _very_ slow since we're iterating over part of the DOM tree for each registered slot that's before the slot we're trying to add.

Maybe this alternative approach could work as well?
1. Find the next slot in tree order from the slot we're trying to add
2. If no such slot exists, append to `m_slots`
3. Otherwise, insert before that slot in `m_slots`

Code after:
 * 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) {