diff --git a/Userland/Libraries/LibWeb/DOM/CharacterData.idl b/Userland/Libraries/LibWeb/DOM/CharacterData.idl index 4a67a12cd3d..a577a8c9e22 100644 --- a/Userland/Libraries/LibWeb/DOM/CharacterData.idl +++ b/Userland/Libraries/LibWeb/DOM/CharacterData.idl @@ -6,7 +6,8 @@ interface CharacterData : Node { readonly attribute Element? nextElementSibling; readonly attribute Element? previousElementSibling; - // FIXME: This should come from a ChildNode mixin + // FIXME: These should come from a ChildNode mixin + [CEReactions, Unscopable] undefined before((Node or DOMString)... nodes); [CEReactions, Unscopable, ImplementedAs=remove_binding] undefined remove(); }; diff --git a/Userland/Libraries/LibWeb/DOM/ChildNode.h b/Userland/Libraries/LibWeb/DOM/ChildNode.h index 559ff33fe35..7c8414a3eef 100644 --- a/Userland/Libraries/LibWeb/DOM/ChildNode.h +++ b/Userland/Libraries/LibWeb/DOM/ChildNode.h @@ -6,12 +6,51 @@ #pragma once +#include +#include + namespace Web::DOM { // https://dom.spec.whatwg.org/#childnode template class ChildNode { public: + // https://dom.spec.whatwg.org/#dom-childnode-before + ExceptionOr before(Vector, String>> const& nodes) + { + auto* node = static_cast(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_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 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. + 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-remove void remove_binding() { @@ -27,6 +66,32 @@ public: protected: ChildNode() = default; + +private: + RefPtr viable_previous_sibling_for_insertion(Vector, String>> const& nodes) const + { + auto* node = static_cast(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>()) + continue; + + auto node_in_vector = node_or_string.template get>(); + if (node_in_vector.ptr() == previous_sibling) { + contained_in_nodes = true; + break; + } + } + + if (!contained_in_nodes) + return previous_sibling; + } + + return nullptr; + } }; } diff --git a/Userland/Libraries/LibWeb/DOM/DocumentType.idl b/Userland/Libraries/LibWeb/DOM/DocumentType.idl index 531cb3a2593..fa3ec8697e5 100644 --- a/Userland/Libraries/LibWeb/DOM/DocumentType.idl +++ b/Userland/Libraries/LibWeb/DOM/DocumentType.idl @@ -4,7 +4,8 @@ interface DocumentType : Node { readonly attribute DOMString publicId; readonly attribute DOMString systemId; - // FIXME: This should come from a ChildNode mixin + // FIXME: These should come from a ChildNode mixin + [CEReactions, Unscopable] undefined before((Node or DOMString)... nodes); [CEReactions, Unscopable, ImplementedAs=remove_binding] undefined remove(); }; diff --git a/Userland/Libraries/LibWeb/DOM/Element.idl b/Userland/Libraries/LibWeb/DOM/Element.idl index 41d664994d9..91a0e56d2b1 100644 --- a/Userland/Libraries/LibWeb/DOM/Element.idl +++ b/Userland/Libraries/LibWeb/DOM/Element.idl @@ -54,7 +54,8 @@ interface Element : Node { readonly attribute long clientWidth; readonly attribute long clientHeight; - // FIXME: This should come from a ChildNode mixin + // FIXME: These should come from a ChildNode mixin + [CEReactions, Unscopable] undefined before((Node or DOMString)... nodes); [CEReactions, Unscopable, ImplementedAs=remove_binding] undefined remove(); };