diff --git a/Userland/Libraries/LibWeb/DOM/Range.cpp b/Userland/Libraries/LibWeb/DOM/Range.cpp index 5b2b9bb6166..1da56a99951 100644 --- a/Userland/Libraries/LibWeb/DOM/Range.cpp +++ b/Userland/Libraries/LibWeb/DOM/Range.cpp @@ -6,10 +6,12 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include #include #include #include +#include #include #include #include @@ -763,4 +765,83 @@ bool Range::partially_contains_node(Node const& node) const return false; } +// https://dom.spec.whatwg.org/#dom-range-insertnode +ExceptionOr Range::insert_node(NonnullRefPtr node) +{ + return insert(node); +} + +// https://dom.spec.whatwg.org/#concept-range-insert +ExceptionOr Range::insert(NonnullRefPtr node) +{ + // 1. If range’s start node is a ProcessingInstruction or Comment node, is a Text node whose parent is null, or is node, then throw a "HierarchyRequestError" DOMException. + if ((is(*m_start_container) || is(*m_start_container)) + || (is(*m_start_container) && !m_start_container->parent_node()) + || m_start_container == node.ptr()) { + return DOM::HierarchyRequestError::create("Range has inappropriate start node for insertion"); + } + + // 2. Let referenceNode be null. + RefPtr reference_node; + + // 3. If range’s start node is a Text node, set referenceNode to that Text node. + if (is(*m_start_container)) { + reference_node = m_start_container; + } + // 4. Otherwise, set referenceNode to the child of start node whose index is start offset, and null if there is no such child. + else { + reference_node = m_start_container->child_at_index(m_start_offset); + } + + // 5. Let parent be range’s start node if referenceNode is null, and referenceNode’s parent otherwise. + RefPtr parent; + if (!reference_node) + parent = m_start_container; + else + parent = reference_node->parent(); + + // 6. Ensure pre-insertion validity of node into parent before referenceNode. + if (auto result = parent->ensure_pre_insertion_validity(node, reference_node); result.is_exception()) + return result.exception(); + + // 7. If range’s start node is a Text node, set referenceNode to the result of splitting it with offset range’s start offset. + if (is(*m_start_container)) { + auto result = static_cast(*m_start_container).split_text(m_start_offset); + if (result.is_exception()) + return result.exception(); + reference_node = result.release_value(); + } + + // 8. If node is referenceNode, set referenceNode to its next sibling. + if (node == reference_node) + reference_node = reference_node->next_sibling(); + + // 9. If node’s parent is non-null, then remove node. + if (node->parent()) + node->remove(); + + // 10. Let newOffset be parent’s length if referenceNode is null, and referenceNode’s index otherwise. + size_t new_offset = 0; + if (!reference_node) + new_offset = parent->length(); + else + new_offset = reference_node->index(); + + // 11. Increase newOffset by node’s length if node is a DocumentFragment node, and one otherwise. + if (is(*node)) + new_offset += node->length(); + else + new_offset += 1; + + // 12. Pre-insert node into parent before referenceNode. + if (auto result = parent->pre_insert(node, reference_node); result.is_exception()) + return result.exception(); + + // 13. If range is collapsed, then set range’s end to (parent, newOffset). + if (collapsed()) + set_end(*parent, new_offset); + + return {}; +} + } diff --git a/Userland/Libraries/LibWeb/DOM/Range.h b/Userland/Libraries/LibWeb/DOM/Range.h index 96f3053247f..50d9ef5d1b4 100644 --- a/Userland/Libraries/LibWeb/DOM/Range.h +++ b/Userland/Libraries/LibWeb/DOM/Range.h @@ -63,6 +63,8 @@ public: ExceptionOr> extract_contents(); + ExceptionOr insert_node(NonnullRefPtr); + String to_string() const; private: @@ -82,6 +84,7 @@ private: ExceptionOr select(Node& node); ExceptionOr> extract(); + ExceptionOr insert(NonnullRefPtr); bool contains_node(Node const&) const; bool partially_contains_node(Node const&) const; diff --git a/Userland/Libraries/LibWeb/DOM/Range.idl b/Userland/Libraries/LibWeb/DOM/Range.idl index d6b1f2649b2..ac4bca149a5 100644 --- a/Userland/Libraries/LibWeb/DOM/Range.idl +++ b/Userland/Libraries/LibWeb/DOM/Range.idl @@ -25,6 +25,7 @@ interface Range : AbstractRange { short compareBoundaryPoints(unsigned short how, Range sourceRange); [CEReactions, NewObject] DocumentFragment extractContents(); + [CEReactions] undefined insertNode(Node node); Range cloneRange(); undefined detach();