mirror of
https://github.com/LadybirdBrowser/ladybird
synced 2026-04-26 01:35:08 +02:00
Previously, the LibWeb bindings generator would output multiple per interface files like Prototype/Constructor/Namespace/GlobalMixin depending on the contents of that IDL file. This complicates the build system as it means that it does not know what files will be generated without knowledge of the contents of that IDL file. Instead, for each IDL file only generate a single Bindings/<IDLFile>.h and Bindings/<IDLFile>.cpp.
177 lines
6.3 KiB
C++
177 lines
6.3 KiB
C++
/*
|
||
* 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);
|
||
|
||
for (auto const& node : m_manually_assigned_nodes)
|
||
node.visit([&](auto const& slottable) { visitor.visit(slottable); });
|
||
}
|
||
|
||
// https://html.spec.whatwg.org/multipage/scripting.html#dom-slot-assignednodes
|
||
Vector<GC::Root<DOM::Node>> HTMLSlotElement::assigned_nodes(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(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(Vector<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 element’s name to the empty string.
|
||
if (!value.has_value())
|
||
set_slot_name({});
|
||
// 5. Otherwise, set element’s 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 element’s root.
|
||
DOM::assign_slottables_for_a_tree(root());
|
||
}
|
||
}
|
||
|
||
}
|