Coverage Report

Created: 2026-11-13 08:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibWeb/DOM/ChildNode.h
Line
Count
Source
1
/*
2
 * Copyright (c) 2021-2022, Luke Wilde <lukew@serenityos.org>
3
 *
4
 * SPDX-License-Identifier: BSD-2-Clause
5
 */
6
7
#pragma once
8
9
#include <LibWeb/DOM/NodeOperations.h>
10
#include <LibWeb/WebIDL/ExceptionOr.h>
11
12
namespace Web::DOM {
13
14
// https://dom.spec.whatwg.org/#childnode
15
template<typename NodeType>
16
class ChildNode {
17
public:
18
    // https://dom.spec.whatwg.org/#dom-childnode-before
19
    WebIDL::ExceptionOr<void> before(Vector<Variant<JS::Handle<Node>, String>> const& nodes)
20
0
    {
21
0
        auto* node = static_cast<NodeType*>(this);
22
23
        // 1. Let parent be this’s parent.
24
0
        auto* parent = node->parent();
25
26
        // 2. If parent is null, then return.
27
0
        if (!parent)
28
0
            return {};
29
30
        // 3. Let viablePreviousSibling be this’s first preceding sibling not in nodes; otherwise null.
31
0
        auto viable_previous_sibling = viable_previous_sibling_for_insertion(nodes);
32
33
        // 4. Let node be the result of converting nodes into a node, given nodes and this’s node document.
34
0
        auto node_to_insert = TRY(convert_nodes_to_single_node(nodes, node->document()));
35
36
        // 5. If viablePreviousSibling is null, then set it to parent’s first child; otherwise to viablePreviousSibling’s next sibling.
37
0
        if (!viable_previous_sibling)
38
0
            viable_previous_sibling = parent->first_child();
39
0
        else
40
0
            viable_previous_sibling = viable_previous_sibling->next_sibling();
41
42
        // 6. Pre-insert node into parent before viablePreviousSibling.
43
0
        (void)TRY(parent->pre_insert(node_to_insert, viable_previous_sibling));
44
45
0
        return {};
46
0
    }
Unexecuted instantiation: Web::DOM::ChildNode<Web::DOM::CharacterData>::before(AK::Vector<AK::Variant<JS::Handle<Web::DOM::Node>, AK::String>, 0ul> const&)
Unexecuted instantiation: Web::DOM::ChildNode<Web::DOM::DocumentType>::before(AK::Vector<AK::Variant<JS::Handle<Web::DOM::Node>, AK::String>, 0ul> const&)
Unexecuted instantiation: Web::DOM::ChildNode<Web::DOM::Element>::before(AK::Vector<AK::Variant<JS::Handle<Web::DOM::Node>, AK::String>, 0ul> const&)
47
48
    // https://dom.spec.whatwg.org/#dom-childnode-after
49
    WebIDL::ExceptionOr<void> after(Vector<Variant<JS::Handle<Node>, String>> const& nodes)
50
0
    {
51
0
        auto* node = static_cast<NodeType*>(this);
52
53
        // 1. Let parent be this’s parent.
54
0
        auto* parent = node->parent();
55
56
        // 2. If parent is null, then return.
57
0
        if (!parent)
58
0
            return {};
59
60
        // 3. Let viableNextSibling be this’s first following sibling not in nodes; otherwise null.
61
0
        auto viable_next_sibling = viable_next_sibling_for_insertion(nodes);
62
63
        // 4. Let node be the result of converting nodes into a node, given nodes and this’s node document.
64
0
        auto node_to_insert = TRY(convert_nodes_to_single_node(nodes, node->document()));
65
66
        // 5. Pre-insert node into parent before viableNextSibling.
67
0
        (void)TRY(parent->pre_insert(node_to_insert, viable_next_sibling));
68
69
0
        return {};
70
0
    }
Unexecuted instantiation: Web::DOM::ChildNode<Web::DOM::CharacterData>::after(AK::Vector<AK::Variant<JS::Handle<Web::DOM::Node>, AK::String>, 0ul> const&)
Unexecuted instantiation: Web::DOM::ChildNode<Web::DOM::DocumentType>::after(AK::Vector<AK::Variant<JS::Handle<Web::DOM::Node>, AK::String>, 0ul> const&)
Unexecuted instantiation: Web::DOM::ChildNode<Web::DOM::Element>::after(AK::Vector<AK::Variant<JS::Handle<Web::DOM::Node>, AK::String>, 0ul> const&)
71
72
    // https://dom.spec.whatwg.org/#dom-childnode-replacewith
73
    WebIDL::ExceptionOr<void> replace_with(Vector<Variant<JS::Handle<Node>, String>> const& nodes)
74
0
    {
75
0
        auto* node = static_cast<NodeType*>(this);
76
77
        // 1. Let parent be this’s parent.
78
0
        auto* parent = node->parent();
79
80
        // 2. If parent is null, then return.
81
0
        if (!parent)
82
0
            return {};
83
84
        // 3. Let viableNextSibling be this’s first following sibling not in nodes; otherwise null.
85
0
        auto viable_next_sibling = viable_next_sibling_for_insertion(nodes);
86
87
        // 4. Let node be the result of converting nodes into a node, given nodes and this’s node document.
88
0
        auto node_to_insert = TRY(convert_nodes_to_single_node(nodes, node->document()));
89
90
        // 5. If this’s parent is parent, replace this with node within parent.
91
        // Note: This could have been inserted into node.
92
0
        if (node->parent() == parent) {
93
0
            (void)TRY(parent->replace_child(node_to_insert, *node));
94
0
            return {};
95
0
        }
96
97
        // 6. Otherwise, pre-insert node into parent before viableNextSibling.
98
0
        (void)TRY(parent->pre_insert(node_to_insert, viable_next_sibling));
99
100
0
        return {};
101
0
    }
Unexecuted instantiation: Web::DOM::ChildNode<Web::DOM::CharacterData>::replace_with(AK::Vector<AK::Variant<JS::Handle<Web::DOM::Node>, AK::String>, 0ul> const&)
Unexecuted instantiation: Web::DOM::ChildNode<Web::DOM::DocumentType>::replace_with(AK::Vector<AK::Variant<JS::Handle<Web::DOM::Node>, AK::String>, 0ul> const&)
Unexecuted instantiation: Web::DOM::ChildNode<Web::DOM::Element>::replace_with(AK::Vector<AK::Variant<JS::Handle<Web::DOM::Node>, AK::String>, 0ul> const&)
102
103
    // https://dom.spec.whatwg.org/#dom-childnode-remove
104
    void remove_binding()
105
0
    {
106
0
        auto* node = static_cast<NodeType*>(this);
107
108
        // 1. If this’s parent is null, then return.
109
0
        if (!node->parent())
110
0
            return;
111
112
        // 2. Remove this.
113
0
        node->remove();
114
0
    }
Unexecuted instantiation: Web::DOM::ChildNode<Web::DOM::Element>::remove_binding()
Unexecuted instantiation: Web::DOM::ChildNode<Web::DOM::CharacterData>::remove_binding()
Unexecuted instantiation: Web::DOM::ChildNode<Web::DOM::DocumentType>::remove_binding()
115
116
protected:
117
    ChildNode() = default;
118
119
private:
120
    JS::GCPtr<Node> viable_previous_sibling_for_insertion(Vector<Variant<JS::Handle<Node>, String>> const& nodes)
121
0
    {
122
0
        auto* node = static_cast<NodeType*>(this);
123
124
0
        for (auto* sibling = node->previous_sibling(); sibling; sibling = sibling->previous_sibling()) {
125
0
            bool contained_in_nodes = false;
126
127
0
            for (auto const& node_or_string : nodes) {
128
0
                if (!node_or_string.template has<JS::Handle<Node>>())
129
0
                    continue;
130
131
0
                auto const& node_in_vector = node_or_string.template get<JS::Handle<Node>>();
132
0
                if (node_in_vector.cell() == sibling) {
133
0
                    contained_in_nodes = true;
134
0
                    break;
135
0
                }
136
0
            }
137
138
0
            if (!contained_in_nodes)
139
0
                return sibling;
140
0
        }
141
142
0
        return nullptr;
143
0
    }
Unexecuted instantiation: Web::DOM::ChildNode<Web::DOM::CharacterData>::viable_previous_sibling_for_insertion(AK::Vector<AK::Variant<JS::Handle<Web::DOM::Node>, AK::String>, 0ul> const&)
Unexecuted instantiation: Web::DOM::ChildNode<Web::DOM::DocumentType>::viable_previous_sibling_for_insertion(AK::Vector<AK::Variant<JS::Handle<Web::DOM::Node>, AK::String>, 0ul> const&)
Unexecuted instantiation: Web::DOM::ChildNode<Web::DOM::Element>::viable_previous_sibling_for_insertion(AK::Vector<AK::Variant<JS::Handle<Web::DOM::Node>, AK::String>, 0ul> const&)
144
145
    JS::GCPtr<Node> viable_next_sibling_for_insertion(Vector<Variant<JS::Handle<Node>, String>> const& nodes)
146
0
    {
147
0
        auto* node = static_cast<NodeType*>(this);
148
149
0
        for (auto* sibling = node->next_sibling(); sibling; sibling = sibling->next_sibling()) {
150
0
            bool contained_in_nodes = false;
151
152
0
            for (auto const& node_or_string : nodes) {
153
0
                if (!node_or_string.template has<JS::Handle<Node>>())
154
0
                    continue;
155
156
0
                auto const& node_in_vector = node_or_string.template get<JS::Handle<Node>>();
157
0
                if (node_in_vector.cell() == sibling) {
158
0
                    contained_in_nodes = true;
159
0
                    break;
160
0
                }
161
0
            }
162
163
0
            if (!contained_in_nodes)
164
0
                return sibling;
165
0
        }
166
167
0
        return nullptr;
168
0
    }
Unexecuted instantiation: Web::DOM::ChildNode<Web::DOM::CharacterData>::viable_next_sibling_for_insertion(AK::Vector<AK::Variant<JS::Handle<Web::DOM::Node>, AK::String>, 0ul> const&)
Unexecuted instantiation: Web::DOM::ChildNode<Web::DOM::DocumentType>::viable_next_sibling_for_insertion(AK::Vector<AK::Variant<JS::Handle<Web::DOM::Node>, AK::String>, 0ul> const&)
Unexecuted instantiation: Web::DOM::ChildNode<Web::DOM::Element>::viable_next_sibling_for_insertion(AK::Vector<AK::Variant<JS::Handle<Web::DOM::Node>, AK::String>, 0ul> const&)
169
};
170
171
}