ladybird/Userland/Libraries/LibWeb/DOM/ChildNode.h

172 lines
5.6 KiB
C
Raw Normal View History

2021-09-29 18:25:48 +03:00
/*
* Copyright (c) 2021-2022, Luke Wilde <lukew@serenityos.org>
2021-09-29 18:25:48 +03:00
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
2022-01-30 00:33:12 +03:00
#include <LibWeb/DOM/NodeOperations.h>
#include <LibWeb/WebIDL/ExceptionOr.h>
2022-01-30 00:33:12 +03:00
2021-09-29 18:25:48 +03:00
namespace Web::DOM {
// https://dom.spec.whatwg.org/#childnode
template<typename NodeType>
class ChildNode {
public:
2022-01-30 00:33:12 +03:00
// https://dom.spec.whatwg.org/#dom-childnode-before
WebIDL::ExceptionOr<void> before(Vector<Variant<JS::Handle<Node>, String>> const& nodes)
2022-01-30 00:33:12 +03:00
{
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()));
2022-01-30 00:33:12 +03:00
// 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));
2022-01-30 00:33:12 +03:00
return {};
}
2022-01-30 00:44:41 +03:00
// https://dom.spec.whatwg.org/#dom-childnode-after
WebIDL::ExceptionOr<void> after(Vector<Variant<JS::Handle<Node>, String>> const& nodes)
2022-01-30 00:44:41 +03:00
{
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_nest_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()));
2022-01-30 00:44:41 +03:00
// 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(Vector<Variant<JS::Handle<Node>, String>> const& 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_nest_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));
2022-01-30 00:44:41 +03:00
return {};
}
2021-09-29 18:25:48 +03:00
// 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;
2022-01-30 00:33:12 +03:00
private:
JS::GCPtr<Node> viable_previous_sibling_for_insertion(Vector<Variant<JS::Handle<Node>, String>> const& nodes)
2022-01-30 00:33:12 +03:00
{
2023-02-25 20:44:51 +03:00
auto* node = static_cast<NodeType*>(this);
2022-01-30 00:33:12 +03:00
while (auto* previous_sibling = node->previous_sibling()) {
bool contained_in_nodes = false;
for (auto const& node_or_string : nodes) {
if (!node_or_string.template has<JS::Handle<Node>>())
2022-01-30 00:33:12 +03:00
continue;
auto node_in_vector = node_or_string.template get<JS::Handle<Node>>();
if (node_in_vector.cell() == previous_sibling) {
2022-01-30 00:33:12 +03:00
contained_in_nodes = true;
break;
}
}
if (!contained_in_nodes)
return previous_sibling;
}
return nullptr;
}
2022-01-30 00:44:41 +03:00
JS::GCPtr<Node> viable_nest_sibling_for_insertion(Vector<Variant<JS::Handle<Node>, String>> const& nodes)
2022-01-30 00:44:41 +03:00
{
2023-02-25 20:44:51 +03:00
auto* node = static_cast<NodeType*>(this);
2022-01-30 00:44:41 +03:00
while (auto* next_sibling = node->next_sibling()) {
bool contained_in_nodes = false;
for (auto const& node_or_string : nodes) {
if (!node_or_string.template has<JS::Handle<Node>>())
2022-01-30 00:44:41 +03:00
continue;
auto& node_in_vector = node_or_string.template get<JS::Handle<Node>>();
if (node_in_vector.cell() == next_sibling) {
2022-01-30 00:44:41 +03:00
contained_in_nodes = true;
break;
}
}
if (!contained_in_nodes)
return next_sibling;
}
return nullptr;
}
2021-09-29 18:25:48 +03:00
};
}