2021-09-29 16:25:48 +01:00
|
|
|
|
/*
|
2022-01-29 20:23:48 +00:00
|
|
|
|
* Copyright (c) 2021-2022, Luke Wilde <lukew@serenityos.org>
|
2021-09-29 16:25:48 +01:00
|
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
2022-01-29 21:33:12 +00:00
|
|
|
|
#include <LibWeb/DOM/NodeOperations.h>
|
2022-09-25 17:03:42 +01:00
|
|
|
|
#include <LibWeb/WebIDL/ExceptionOr.h>
|
2022-01-29 21:33:12 +00:00
|
|
|
|
|
2021-09-29 16:25:48 +01:00
|
|
|
|
namespace Web::DOM {
|
|
|
|
|
|
|
|
|
|
// https://dom.spec.whatwg.org/#childnode
|
|
|
|
|
template<typename NodeType>
|
|
|
|
|
class ChildNode {
|
|
|
|
|
public:
|
2022-01-29 21:33:12 +00:00
|
|
|
|
// https://dom.spec.whatwg.org/#dom-childnode-before
|
2023-09-17 11:35:34 +12:00
|
|
|
|
WebIDL::ExceptionOr<void> before(Vector<Variant<JS::Handle<Node>, String>> const& nodes)
|
2022-01-29 21:33:12 +00:00
|
|
|
|
{
|
|
|
|
|
auto* node = static_cast<NodeType*>(this);
|
|
|
|
|
|
|
|
|
|
// 1. Let parent be this’s parent.
|
|
|
|
|
auto* parent = node->parent();
|
|
|
|
|
|
|
|
|
|
// 2. If parent is null, then return.
|
|
|
|
|
if (!parent)
|
|
|
|
|
return {};
|
|
|
|
|
|
|
|
|
|
// 3. Let viablePreviousSibling be this’s 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 this’s node document.
|
2023-09-17 11:35:34 +12:00
|
|
|
|
auto node_to_insert = TRY(convert_nodes_to_single_node(nodes, node->document()));
|
2022-01-29 21:33:12 +00:00
|
|
|
|
|
|
|
|
|
// 5. If viablePreviousSibling is null, then set it to parent’s first child; otherwise to viablePreviousSibling’s 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.
|
2022-03-22 12:36:30 +00:00
|
|
|
|
(void)TRY(parent->pre_insert(node_to_insert, viable_previous_sibling));
|
2022-01-29 21:33:12 +00:00
|
|
|
|
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-29 21:44:41 +00:00
|
|
|
|
// https://dom.spec.whatwg.org/#dom-childnode-after
|
2023-09-17 11:35:34 +12:00
|
|
|
|
WebIDL::ExceptionOr<void> after(Vector<Variant<JS::Handle<Node>, String>> const& nodes)
|
2022-01-29 21:44:41 +00:00
|
|
|
|
{
|
|
|
|
|
auto* node = static_cast<NodeType*>(this);
|
|
|
|
|
|
|
|
|
|
// 1. Let parent be this’s parent.
|
|
|
|
|
auto* parent = node->parent();
|
|
|
|
|
|
|
|
|
|
// 2. If parent is null, then return.
|
|
|
|
|
if (!parent)
|
|
|
|
|
return {};
|
|
|
|
|
|
|
|
|
|
// 3. Let viableNextSibling be this’s 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 this’s node document.
|
2023-09-17 11:35:34 +12:00
|
|
|
|
auto node_to_insert = TRY(convert_nodes_to_single_node(nodes, node->document()));
|
2022-01-29 21:44:41 +00:00
|
|
|
|
|
|
|
|
|
// 5. Pre-insert node into parent before viableNextSibling.
|
2022-03-22 12:36:30 +00:00
|
|
|
|
(void)TRY(parent->pre_insert(node_to_insert, viable_next_sibling));
|
2022-01-29 22:25:51 +00:00
|
|
|
|
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// https://dom.spec.whatwg.org/#dom-childnode-replacewith
|
2023-09-17 11:35:34 +12:00
|
|
|
|
WebIDL::ExceptionOr<void> replace_with(Vector<Variant<JS::Handle<Node>, String>> const& nodes)
|
2022-01-29 22:25:51 +00:00
|
|
|
|
{
|
|
|
|
|
auto* node = static_cast<NodeType*>(this);
|
|
|
|
|
|
|
|
|
|
// 1. Let parent be this’s parent.
|
|
|
|
|
auto* parent = node->parent();
|
|
|
|
|
|
|
|
|
|
// 2. If parent is null, then return.
|
|
|
|
|
if (!parent)
|
|
|
|
|
return {};
|
|
|
|
|
|
|
|
|
|
// 3. Let viableNextSibling be this’s 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 this’s node document.
|
2023-09-17 11:35:34 +12:00
|
|
|
|
auto node_to_insert = TRY(convert_nodes_to_single_node(nodes, node->document()));
|
2022-01-29 22:25:51 +00:00
|
|
|
|
|
|
|
|
|
// 5. If this’s parent is parent, replace this with node within parent.
|
|
|
|
|
// Note: This could have been inserted into node.
|
|
|
|
|
if (node->parent() == parent) {
|
2022-03-22 12:36:30 +00:00
|
|
|
|
(void)TRY(parent->replace_child(node_to_insert, *node));
|
2022-01-29 22:25:51 +00:00
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 6. Otherwise, pre-insert node into parent before viableNextSibling.
|
2022-03-22 12:36:30 +00:00
|
|
|
|
(void)TRY(parent->pre_insert(node_to_insert, viable_next_sibling));
|
2022-01-29 21:44:41 +00:00
|
|
|
|
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-29 16:25:48 +01:00
|
|
|
|
// https://dom.spec.whatwg.org/#dom-childnode-remove
|
|
|
|
|
void remove_binding()
|
|
|
|
|
{
|
|
|
|
|
auto* node = static_cast<NodeType*>(this);
|
|
|
|
|
|
|
|
|
|
// 1. If this’s parent is null, then return.
|
|
|
|
|
if (!node->parent())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// 2. Remove this.
|
|
|
|
|
node->remove();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
ChildNode() = default;
|
2022-01-29 21:33:12 +00:00
|
|
|
|
|
|
|
|
|
private:
|
2023-09-17 11:35:34 +12:00
|
|
|
|
JS::GCPtr<Node> viable_previous_sibling_for_insertion(Vector<Variant<JS::Handle<Node>, String>> const& nodes)
|
2022-01-29 21:33:12 +00:00
|
|
|
|
{
|
2023-02-25 10:44:51 -07:00
|
|
|
|
auto* node = static_cast<NodeType*>(this);
|
2022-01-29 21:33:12 +00:00
|
|
|
|
|
|
|
|
|
while (auto* previous_sibling = node->previous_sibling()) {
|
|
|
|
|
bool contained_in_nodes = false;
|
|
|
|
|
|
|
|
|
|
for (auto const& node_or_string : nodes) {
|
2022-08-28 13:42:07 +02:00
|
|
|
|
if (!node_or_string.template has<JS::Handle<Node>>())
|
2022-01-29 21:33:12 +00:00
|
|
|
|
continue;
|
|
|
|
|
|
2022-08-28 13:42:07 +02:00
|
|
|
|
auto node_in_vector = node_or_string.template get<JS::Handle<Node>>();
|
|
|
|
|
if (node_in_vector.cell() == previous_sibling) {
|
2022-01-29 21:33:12 +00:00
|
|
|
|
contained_in_nodes = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!contained_in_nodes)
|
|
|
|
|
return previous_sibling;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2022-01-29 21:44:41 +00:00
|
|
|
|
|
2023-09-17 11:35:34 +12:00
|
|
|
|
JS::GCPtr<Node> viable_nest_sibling_for_insertion(Vector<Variant<JS::Handle<Node>, String>> const& nodes)
|
2022-01-29 21:44:41 +00:00
|
|
|
|
{
|
2023-02-25 10:44:51 -07:00
|
|
|
|
auto* node = static_cast<NodeType*>(this);
|
2022-01-29 21:44:41 +00:00
|
|
|
|
|
|
|
|
|
while (auto* next_sibling = node->next_sibling()) {
|
|
|
|
|
bool contained_in_nodes = false;
|
|
|
|
|
|
|
|
|
|
for (auto const& node_or_string : nodes) {
|
2022-08-28 13:42:07 +02:00
|
|
|
|
if (!node_or_string.template has<JS::Handle<Node>>())
|
2022-01-29 21:44:41 +00:00
|
|
|
|
continue;
|
|
|
|
|
|
2022-08-28 13:42:07 +02:00
|
|
|
|
auto& node_in_vector = node_or_string.template get<JS::Handle<Node>>();
|
|
|
|
|
if (node_in_vector.cell() == next_sibling) {
|
2022-01-29 21:44:41 +00:00
|
|
|
|
contained_in_nodes = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!contained_in_nodes)
|
|
|
|
|
return next_sibling;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2021-09-29 16:25:48 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
}
|