ladybird/Libraries/LibWeb/DOM/ChildNode.h
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

171 lines
5.7 KiB
C++
Raw Permalink 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) 2021-2022, Luke Wilde <lukew@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibWeb/DOM/NodeOperations.h>
#include <LibWeb/WebIDL/ExceptionOr.h>
namespace Web::DOM {
// https://dom.spec.whatwg.org/#childnode
template<typename NodeType>
class ChildNode {
public:
// https://dom.spec.whatwg.org/#dom-childnode-before
WebIDL::ExceptionOr<void> before(ReadonlySpan<Variant<GC::Ref<Node>, Utf16String>> nodes)
{
auto* node = static_cast<NodeType*>(this);
// 1. Let parent be thiss parent.
auto* parent = node->parent();
// 2. If parent is null, then return.
if (!parent)
return {};
// 3. Let viablePreviousSibling be thiss first preceding sibling not in nodes; otherwise null.
auto viable_previous_sibling = viable_previous_sibling_for_insertion(nodes);
// 4. Let node be the result of converting nodes into a node, given nodes and thiss node document.
auto node_to_insert = TRY(convert_nodes_to_single_node(nodes, node->document()));
// 5. If viablePreviousSibling is null, then set it to parents first child; otherwise to viablePreviousSiblings next sibling.
if (!viable_previous_sibling)
viable_previous_sibling = parent->first_child();
else
viable_previous_sibling = viable_previous_sibling->next_sibling();
// 6. Pre-insert node into parent before viablePreviousSibling.
(void)TRY(parent->pre_insert(node_to_insert, viable_previous_sibling));
return {};
}
// https://dom.spec.whatwg.org/#dom-childnode-after
WebIDL::ExceptionOr<void> after(ReadonlySpan<Variant<GC::Ref<Node>, Utf16String>> nodes)
{
auto* node = static_cast<NodeType*>(this);
// 1. Let parent be thiss parent.
auto* parent = node->parent();
// 2. If parent is null, then return.
if (!parent)
return {};
// 3. Let viableNextSibling be thiss first following sibling not in nodes; otherwise null.
auto viable_next_sibling = viable_next_sibling_for_insertion(nodes);
// 4. Let node be the result of converting nodes into a node, given nodes and thiss node document.
auto node_to_insert = TRY(convert_nodes_to_single_node(nodes, node->document()));
// 5. Pre-insert node into parent before viableNextSibling.
(void)TRY(parent->pre_insert(node_to_insert, viable_next_sibling));
return {};
}
// https://dom.spec.whatwg.org/#dom-childnode-replacewith
WebIDL::ExceptionOr<void> replace_with(ReadonlySpan<Variant<GC::Ref<Node>, Utf16String>> nodes)
{
auto* node = static_cast<NodeType*>(this);
// 1. Let parent be thiss parent.
auto* parent = node->parent();
// 2. If parent is null, then return.
if (!parent)
return {};
// 3. Let viableNextSibling be thiss first following sibling not in nodes; otherwise null.
auto viable_next_sibling = viable_next_sibling_for_insertion(nodes);
// 4. Let node be the result of converting nodes into a node, given nodes and thiss node document.
auto node_to_insert = TRY(convert_nodes_to_single_node(nodes, node->document()));
// 5. If thiss parent is parent, replace this with node within parent.
// Note: This could have been inserted into node.
if (node->parent() == parent) {
(void)TRY(parent->replace_child(node_to_insert, *node));
return {};
}
// 6. Otherwise, pre-insert node into parent before viableNextSibling.
(void)TRY(parent->pre_insert(node_to_insert, viable_next_sibling));
return {};
}
// https://dom.spec.whatwg.org/#dom-childnode-remove
void remove_binding()
{
auto* node = static_cast<NodeType*>(this);
// 1. If thiss parent is null, then return.
if (!node->parent())
return;
// 2. Remove this.
node->remove();
}
protected:
ChildNode() = default;
private:
GC::Ptr<Node> viable_previous_sibling_for_insertion(ReadonlySpan<Variant<GC::Ref<Node>, Utf16String>> const& nodes)
{
auto* node = static_cast<NodeType*>(this);
for (auto* sibling = node->previous_sibling(); sibling; sibling = sibling->previous_sibling()) {
bool contained_in_nodes = false;
for (auto const& node_or_string : nodes) {
if (!node_or_string.template has<GC::Ref<Node>>())
continue;
auto const& node_in_vector = node_or_string.template get<GC::Ref<Node>>();
if (node_in_vector == sibling) {
contained_in_nodes = true;
break;
}
}
if (!contained_in_nodes)
return sibling;
}
return nullptr;
}
GC::Ptr<Node> viable_next_sibling_for_insertion(ReadonlySpan<Variant<GC::Ref<Node>, Utf16String>> const& nodes)
{
auto* node = static_cast<NodeType*>(this);
for (auto* sibling = node->next_sibling(); sibling; sibling = sibling->next_sibling()) {
bool contained_in_nodes = false;
for (auto const& node_or_string : nodes) {
if (!node_or_string.template has<GC::Ref<Node>>())
continue;
auto const& node_in_vector = node_or_string.template get<GC::Ref<Node>>();
if (node_in_vector == sibling) {
contained_in_nodes = true;
break;
}
}
if (!contained_in_nodes)
return sibling;
}
return nullptr;
}
};
}