1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
|
/*
* Copyright (c) 2021-2022, Luke Wilde <lukew@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibWeb/DOM/ExceptionOr.h>
#include <LibWeb/DOM/NodeOperations.h>
namespace Web::DOM {
// https://dom.spec.whatwg.org/#childnode
template<typename NodeType>
class ChildNode {
public:
// https://dom.spec.whatwg.org/#dom-childnode-before
ExceptionOr<void> before(Vector<Variant<NonnullRefPtr<Node>, String>> const& nodes)
{
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.
auto node_to_insert = TRY(convert_nodes_to_single_node(nodes, node->document()));
// 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.
(void)TRY(parent->pre_insert(node_to_insert, viable_previous_sibling));
return {};
}
// https://dom.spec.whatwg.org/#dom-childnode-after
ExceptionOr<void> after(Vector<Variant<NonnullRefPtr<Node>, String>> const& nodes)
{
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.
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
ExceptionOr<void> replace_with(Vector<Variant<NonnullRefPtr<Node>, String>> const& nodes)
{
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.
auto node_to_insert = TRY(convert_nodes_to_single_node(nodes, node->document()));
// 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) {
(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 this’s parent is null, then return.
if (!node->parent())
return;
// 2. Remove this.
node->remove();
}
protected:
ChildNode() = default;
private:
RefPtr<Node> viable_previous_sibling_for_insertion(Vector<Variant<NonnullRefPtr<Node>, String>> const& nodes) const
{
auto* node = static_cast<NodeType const*>(this);
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<NonnullRefPtr<Node>>())
continue;
auto node_in_vector = node_or_string.template get<NonnullRefPtr<Node>>();
if (node_in_vector.ptr() == previous_sibling) {
contained_in_nodes = true;
break;
}
}
if (!contained_in_nodes)
return previous_sibling;
}
return nullptr;
}
RefPtr<Node> viable_nest_sibling_for_insertion(Vector<Variant<NonnullRefPtr<Node>, String>> const& nodes) const
{
auto* node = static_cast<NodeType const*>(this);
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<NonnullRefPtr<Node>>())
continue;
auto node_in_vector = node_or_string.template get<NonnullRefPtr<Node>>();
if (node_in_vector.ptr() == next_sibling) {
contained_in_nodes = true;
break;
}
}
if (!contained_in_nodes)
return next_sibling;
}
return nullptr;
}
};
}
|