ladybird/Libraries/LibWeb/HTML/HTMLSlotElement.cpp
Shannon Booth 637fd51595 LibWeb: Unify WebIDL C++ type generation
Represent WebIDL C++ types with a single CppType model that tracks
nullability, optional presence, and contained storage.

GC-like values now use GC::Ref/GC::Ptr directly, while containers choose
"plain", "Root", or "Conservative" container types depending on what
they contain. For example, sequence<Element> becomes a RootVector of
GC::Ref values, while sequence<SomeDictionary> becomes a
ConservativeVector only when the dictionary contains GC-like values.
This moves the generated bindings away from wrapping GC values in
GC::Root by default.

This has broad fallout as the types passed to interfaces for GC
objects changes almost fully across the board.
2026-05-23 18:26:12 +02:00

174 lines
6.3 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2020, the SerenityOS developers.
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
* Copyright (c) 2025, Shannon Booth <shannon@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/Bindings/HTMLSlotElement.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>
namespace Web::HTML {
GC_DEFINE_ALLOCATOR(HTMLSlotElement);
HTMLSlotElement::HTMLSlotElement(DOM::Document& document, DOM::QualifiedName qualified_name)
: HTMLElement(document, move(qualified_name))
{
}
HTMLSlotElement::~HTMLSlotElement() = default;
void HTMLSlotElement::initialize(JS::Realm& realm)
{
WEB_SET_PROTOTYPE_FOR_INTERFACE(HTMLSlotElement);
Base::initialize(realm);
}
void HTMLSlotElement::visit_edges(JS::Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
Slot::visit_edges(visitor);
visitor.visit(m_manually_assigned_nodes);
}
// https://html.spec.whatwg.org/multipage/scripting.html#dom-slot-assignednodes
Vector<GC::Root<DOM::Node>> HTMLSlotElement::assigned_nodes(Bindings::AssignedNodesOptions options) const
{
// 1. If options["flatten"] is false, then return this's assigned nodes.
if (!options.flatten) {
Vector<GC::Root<DOM::Node>> assigned_nodes;
assigned_nodes.ensure_capacity(assigned_nodes_internal().size());
for (auto const& node : assigned_nodes_internal()) {
node.visit([&](auto const& node) {
assigned_nodes.unchecked_append(*node);
});
}
return assigned_nodes;
}
// 2. Return the result of finding flattened slottables with this.
// FIXME: Make this a lot less awkward!
auto nodes = DOM::find_flattened_slottables(const_cast<HTMLSlotElement&>(*this));
Vector<GC::Root<DOM::Node>> assigned_nodes;
assigned_nodes.ensure_capacity(nodes.size());
for (auto const& node : nodes) {
node.visit([&](auto const& slottable) {
assigned_nodes.unchecked_append(*slottable);
});
}
return assigned_nodes;
}
// https://html.spec.whatwg.org/multipage/scripting.html#dom-slot-assignedelements
Vector<GC::Root<DOM::Element>> HTMLSlotElement::assigned_elements(Bindings::AssignedNodesOptions options) const
{
// 1. If options["flatten"] is false, then return this's assigned nodes, filtered to contain only Element nodes.
if (!options.flatten) {
Vector<GC::Root<DOM::Element>> assigned_nodes;
for (auto const& node : assigned_nodes_internal()) {
if (auto const* element = node.get_pointer<GC::Ref<DOM::Element>>())
assigned_nodes.append(*element);
}
return assigned_nodes;
}
// 2. Return the result of finding flattened slottables with this, filtered to contain only Element nodes.
auto result = DOM::find_flattened_slottables(const_cast<HTMLSlotElement&>(*this));
Vector<GC::Root<DOM::Element>> assigned_nodes;
for (auto const& node : result) {
if (auto const* element = node.get_pointer<GC::Ref<DOM::Element>>())
assigned_nodes.append(*element);
}
return assigned_nodes;
}
// https://html.spec.whatwg.org/multipage/scripting.html#dom-slot-assign
void HTMLSlotElement::assign(GC::ConservativeVector<SlottableHandle> nodes)
{
// 1. For each node of this's manually assigned nodes, set node's manual slot assignment to null.
for (auto& node : m_manually_assigned_nodes) {
node.visit([&](auto& node) {
node->set_manual_slot_assignment(nullptr);
});
}
// 2. Let nodesSet be a new ordered set.
Vector<DOM::Slottable> nodes_set;
// 3. For each node of nodes:
for (auto& node_handle : nodes) {
auto& node = node_handle.visit([](auto& node) -> DOM::SlottableMixin& { return *node; });
auto slottable = node_handle.visit([](auto& node) { return node->as_slottable(); });
// 1. If node's manual slot assignment refers to a slot, then remove node from that slot's manually assigned nodes.
if (node.manual_slot_assignment() != nullptr) {
m_manually_assigned_nodes.remove_all_matching([&](auto const& manually_assigned_node) {
return slottable == manually_assigned_node;
});
}
// 2. Set node's manual slot assignment to this.
node.set_manual_slot_assignment(this);
// 3. Append node to nodesSet.
nodes_set.append(slottable);
}
// 4. Set this's manually assigned nodes to nodesSet.
m_manually_assigned_nodes = move(nodes_set);
// 5. Run assign slottables for a tree for this's root.
assign_slottables_for_a_tree(root());
}
// https://dom.spec.whatwg.org/#ref-for-concept-element-attributes-change-ext
void HTMLSlotElement::attribute_changed(FlyString const& local_name, Optional<String> const& old_value, Optional<String> const& value, Optional<FlyString> const& namespace_)
{
Base::attribute_changed(local_name, old_value, value, namespace_);
// 1. If element is a slot, localName is name, and namespace is null, then:
if (local_name == AttributeNames::name && !namespace_.has_value()) {
// 1. If value is oldValue, then return.
if (value == old_value)
return;
// 2. If value is null and oldValue is the empty string, then return.
if (!value.has_value() && old_value == String {})
return;
// 3. If value is the empty string and oldValue is null, then return.
if (value == String {} && !old_value.has_value())
return;
// OPTIMIZATION: Update the slot registry before changing the name.
auto* shadow_root = as_if<DOM::ShadowRoot>(root());
if (shadow_root)
shadow_root->unregister_slot(*this);
// 4. If value is null or the empty string, then set elements name to the empty string.
if (!value.has_value())
set_slot_name({});
// 5. Otherwise, set elements name to value.
else
set_slot_name(*value);
// OPTIMIZATION: Register the slot with its new name.
if (shadow_root)
shadow_root->register_slot(*this);
// 6. Run assign slottables for a tree with elements root.
DOM::assign_slottables_for_a_tree(root());
}
}
}