ladybird/Userland/Libraries/LibWeb/DOM/ChildNode.h
2022-01-31 15:25:36 +01:00

193 lines
6.4 KiB
C++
Raw 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/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 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_or_exception = convert_nodes_to_single_node(nodes, node->document());
if (node_or_exception.is_exception())
return node_or_exception.exception();
auto node_to_insert = node_or_exception.release_value();
// 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.
auto result = parent->pre_insert(node_to_insert, viable_previous_sibling);
if (result.is_exception())
return result.exception();
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 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_or_exception = convert_nodes_to_single_node(nodes, node->document());
if (node_or_exception.is_exception())
return node_or_exception.exception();
auto node_to_insert = node_or_exception.release_value();
// 5. Pre-insert node into parent before viableNextSibling.
auto result = parent->pre_insert(node_to_insert, viable_next_sibling);
if (result.is_exception())
return result.exception();
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 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_or_exception = convert_nodes_to_single_node(nodes, node->document());
if (node_or_exception.is_exception())
return node_or_exception.exception();
auto node_to_insert = node_or_exception.release_value();
// 5. If thiss parent is parent, replace this with node within parent.
// Note: This could have been inserted into node.
if (node->parent() == parent) {
auto result = parent->replace_child(node_to_insert, *node);
if (result.is_exception())
return result.exception();
return {};
}
// 6. Otherwise, pre-insert node into parent before viableNextSibling.
auto result = parent->pre_insert(node_to_insert, viable_next_sibling);
if (result.is_exception())
return result.exception();
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:
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;
}
};
}