diff --git a/Libraries/LibWeb/DOM/CharacterData.cpp b/Libraries/LibWeb/DOM/CharacterData.cpp index d8a89a149ef..3ad01cbc0a8 100644 --- a/Libraries/LibWeb/DOM/CharacterData.cpp +++ b/Libraries/LibWeb/DOM/CharacterData.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2018-2022, Andreas Kling + * Copyright (c) 2025, Jelle Raaijmakers * * SPDX-License-Identifier: BSD-2-Clause */ @@ -10,7 +11,6 @@ #include #include #include -#include #include namespace Web::DOM { @@ -105,31 +105,32 @@ WebIDL::ExceptionOr CharacterData::replace_data(size_t offset, size_t coun // NOTE: We do this later so that the mutation observer may notify UI clients of this node's new value. queue_mutation_record(MutationType::characterData, {}, {}, old_data, {}, {}, nullptr, nullptr); - // 8. For each live range whose start node is node and start offset is greater than offset but less than or equal to offset plus count, set its start offset to offset. - for (auto& range : Range::live_ranges()) { + // 8. For each live range whose start node is node and start offset is greater than offset but less than or equal to + // offset plus count, set its start offset to offset. + for (auto* range : Range::live_ranges()) { if (range->start_container() == this && range->start_offset() > offset && range->start_offset() <= (offset + count)) - TRY(range->set_start(*range->start_container(), offset)); + range->set_start_offset(offset); } - // 9. For each live range whose end node is node and end offset is greater than offset but less than or equal to offset plus count, set its end offset to offset. - for (auto& range : Range::live_ranges()) { + // 9. For each live range whose end node is node and end offset is greater than offset but less than or equal to + // offset plus count, set its end offset to offset. + for (auto* range : Range::live_ranges()) { if (range->end_container() == this && range->end_offset() > offset && range->end_offset() <= (offset + count)) - TRY(range->set_end(*range->end_container(), offset)); + range->set_end_offset(offset); } - // 10. For each live range whose start node is node and start offset is greater than offset plus count, increase its start offset by data’s length and decrease it by count. - for (auto& range : Range::live_ranges()) { + // 10. For each live range whose start node is node and start offset is greater than offset plus count, increase its + // start offset by data’s length and decrease it by count. + for (auto* range : Range::live_ranges()) { if (range->start_container() == this && range->start_offset() > (offset + count)) - TRY(range->set_start(*range->start_container(), range->start_offset() + inserted_data_result.data.size() - count)); + range->set_start_offset(range->start_offset() + inserted_data_result.data.size() - count); } - // 11. For each live range whose end node is node and end offset is greater than offset plus count, increase its end offset by data’s length and decrease it by count. - for (auto& range : Range::live_ranges()) { - if (range->end_container() == this && range->end_offset() > (offset + count)) { - // AD-HOC: Clamp offset to the end of the data if it's too large. - auto new_offset = min(range->end_offset() + inserted_data_result.data.size() - count, length_in_utf16_code_units()); - TRY(range->set_end(*range->end_container(), new_offset)); - } + // 11. For each live range whose end node is node and end offset is greater than offset plus count, increase its end + // offset by data’s length and decrease it by count. + for (auto* range : Range::live_ranges()) { + if (range->end_container() == this && range->end_offset() > (offset + count)) + range->set_end_offset(range->end_offset() + inserted_data_result.data.size() - count); } // 12. If node’s parent is non-null, then run the children changed steps for node’s parent. diff --git a/Libraries/LibWeb/DOM/Node.cpp b/Libraries/LibWeb/DOM/Node.cpp index 74fdaab75ae..2b8c4f708eb 100644 --- a/Libraries/LibWeb/DOM/Node.cpp +++ b/Libraries/LibWeb/DOM/Node.cpp @@ -2,7 +2,7 @@ * Copyright (c) 2018-2024, Andreas Kling * Copyright (c) 2021-2022, Linus Groh * Copyright (c) 2021, Luke Wilde - * Copyright (c) 2024, Jelle Raaijmakers + * Copyright (c) 2024-2025, Jelle Raaijmakers * Copyright (c) 2025, Shannon Booth * * SPDX-License-Identifier: BSD-2-Clause @@ -299,28 +299,40 @@ WebIDL::ExceptionOr Node::normalize() // 6. While currentNode is an exclusive Text node: while (current_node && current_node->is_exclusive_text()) { - // 1. For each live range whose start node is currentNode, add length to its start offset and set its start node to node. + // 1. For each live range whose start node is currentNode, add length to its start offset and set its start + // node to node. for (auto& range : Range::live_ranges()) { - if (range->start_container() == current_node) - TRY(range->set_start(node, range->start_offset() + length)); + if (range->start_container() == current_node) { + range->increase_start_offset(length); + range->set_start_node(node); + } } - // 2. For each live range whose end node is currentNode, add length to its end offset and set its end node to node. + // 2. For each live range whose end node is currentNode, add length to its end offset and set its end node + // to node. for (auto& range : Range::live_ranges()) { - if (range->end_container() == current_node) - TRY(range->set_end(node, range->end_offset() + length)); + if (range->end_container() == current_node) { + range->increase_end_offset(length); + range->set_end_node(node); + } } - // 3. For each live range whose start node is currentNode’s parent and start offset is currentNode’s index, set its start node to node and its start offset to length. + // 3. For each live range whose start node is currentNode’s parent and start offset is currentNode’s index, + // set its start node to node and its start offset to length. for (auto& range : Range::live_ranges()) { - if (range->start_container() == current_node->parent() && range->start_offset() == current_node->index()) - TRY(range->set_start(node, length)); + if (range->start_container() == current_node->parent() && range->start_offset() == current_node->index()) { + range->set_start_node(node); + range->set_start_offset(length); + } } - // 4. For each live range whose end node is currentNode’s parent and end offset is currentNode’s index, set its end node to node and its end offset to length. + // 4. For each live range whose end node is currentNode’s parent and end offset is currentNode’s index, set + // its end node to node and its end offset to length. for (auto& range : Range::live_ranges()) { - if (range->end_container() == current_node->parent() && range->end_offset() == current_node->index()) - TRY(range->set_end(node, length)); + if (range->end_container() == current_node->parent() && range->end_offset() == current_node->index()) { + range->set_end_node(node); + range->set_end_offset(length); + } } // 5. Add currentNode’s length to length. @@ -681,16 +693,18 @@ void Node::insert_before(GC::Ref node, GC::Ptr child, bool suppress_ // 5. If child is non-null, then: if (child) { - // 1. For each live range whose start node is parent and start offset is greater than child’s index, increase its start offset by count. + // 1. For each live range whose start node is parent and start offset is greater than child’s index, increase + // its start offset by count. for (auto& range : Range::live_ranges()) { if (range->start_container() == this && range->start_offset() > child->index()) - range->increase_start_offset({}, count); + range->increase_start_offset(count); } - // 2. For each live range whose end node is parent and end offset is greater than child’s index, increase its end offset by count. + // 2. For each live range whose end node is parent and end offset is greater than child’s index, increase its + // end offset by count. for (auto& range : Range::live_ranges()) { if (range->end_container() == this && range->end_offset() > child->index()) - range->increase_end_offset({}, count); + range->increase_end_offset(count); } } @@ -866,27 +880,28 @@ void Node::live_range_pre_remove() auto index = this->index(); // 4. For each live range whose start node is an inclusive descendant of node, set its start to (parent, index). - for (auto& range : Range::live_ranges()) { + for (auto* range : Range::live_ranges()) { if (range->start_container()->is_inclusive_descendant_of(*this)) MUST(range->set_start(*parent, index)); } // 5. For each live range whose end node is an inclusive descendant of node, set its end to (parent, index). - for (auto& range : Range::live_ranges()) { + for (auto* range : Range::live_ranges()) { if (range->end_container()->is_inclusive_descendant_of(*this)) MUST(range->set_end(*parent, index)); } - // 6. For each live range whose start node is parent and start offset is greater than index, decrease its start offset by 1. - for (auto& range : Range::live_ranges()) { + // 6. For each live range whose start node is parent and start offset is greater than index, decrease its start + // offset by 1. + for (auto* range : Range::live_ranges()) { if (range->start_container() == parent && range->start_offset() > index) - range->decrease_start_offset({}, 1); + range->decrease_start_offset(1); } // 7. For each live range whose end node is parent and end offset is greater than index, decrease its end offset by 1. - for (auto& range : Range::live_ranges()) { + for (auto* range : Range::live_ranges()) { if (range->end_container() == parent && range->end_offset() > index) - range->decrease_end_offset({}, 1); + range->decrease_end_offset(1); } } @@ -1259,16 +1274,18 @@ WebIDL::ExceptionOr Node::move_node(Node& new_parent, Node* child) // 17. If child is non-null: if (child) { - // 1. For each live range whose start node is newParent and start offset is greater than child’s index, increase its start offset by 1. + // 1. For each live range whose start node is newParent and start offset is greater than child’s index, increase + // its start offset by 1. for (auto& range : Range::live_ranges()) { if (range->start_container() == &new_parent && range->start_offset() > child->index()) - range->increase_start_offset({}, 1); + range->increase_start_offset(1); } - // 2. For each live range whose end node is newParent and end offset is greater than child’s index, increase its end offset by 1. + // 2. For each live range whose end node is newParent and end offset is greater than child’s index, increase its + // end offset by 1. for (auto& range : Range::live_ranges()) { if (range->end_container() == &new_parent && range->end_offset() > child->index()) - range->increase_end_offset({}, 1); + range->increase_end_offset(1); } } diff --git a/Libraries/LibWeb/DOM/Range.cpp b/Libraries/LibWeb/DOM/Range.cpp index 6b96daa49f2..97c23d3a4d6 100644 --- a/Libraries/LibWeb/DOM/Range.cpp +++ b/Libraries/LibWeb/DOM/Range.cpp @@ -39,7 +39,7 @@ HashTable& Range::live_ranges() GC::Ref Range::create(HTML::Window& window) { - return Range::create(window.associated_document()); + return create(window.associated_document()); } GC::Ref Range::create(Document& document) @@ -57,7 +57,7 @@ GC::Ref Range::create(GC::Ref start_container, WebIDL::UnsignedLong WebIDL::ExceptionOr> Range::construct_impl(JS::Realm& realm) { auto& window = as(realm.global_object()); - return Range::create(window); + return create(window); } Range::Range(Document& document) @@ -167,6 +167,7 @@ RelativeBoundaryPointPosition position_of_boundary_point_relative_to_other_bound return RelativeBoundaryPointPosition::Before; } +// https://dom.spec.whatwg.org/#concept-range-bp-set WebIDL::ExceptionOr Range::set_start_or_end(GC::Ref node, u32 offset, StartOrEnd start_or_end) { // To set the start or end of a range to a boundary point (node, offset), run these steps: @@ -212,13 +213,14 @@ WebIDL::ExceptionOr Range::set_start_or_end(GC::Ref node, u32 offset return {}; } -// https://dom.spec.whatwg.org/#concept-range-bp-set +// https://dom.spec.whatwg.org/#dom-range-setstart WebIDL::ExceptionOr Range::set_start(GC::Ref node, WebIDL::UnsignedLong offset) { // The setStart(node, offset) method steps are to set the start of this to boundary point (node, offset). return set_start_or_end(node, offset, StartOrEnd::Start); } +// https://dom.spec.whatwg.org/#dom-range-setend WebIDL::ExceptionOr Range::set_end(GC::Ref node, WebIDL::UnsignedLong offset) { // The setEnd(node, offset) method steps are to set the end of this to boundary point (node, offset). @@ -1291,24 +1293,4 @@ WebIDL::ExceptionOr> Range::create_contextual_fragment return fragment_node; } -void Range::increase_start_offset(Badge, WebIDL::UnsignedLong count) -{ - m_start_offset += count; -} - -void Range::increase_end_offset(Badge, WebIDL::UnsignedLong count) -{ - m_end_offset += count; -} - -void Range::decrease_start_offset(Badge, WebIDL::UnsignedLong count) -{ - m_start_offset -= count; -} - -void Range::decrease_end_offset(Badge, WebIDL::UnsignedLong count) -{ - m_end_offset -= count; -} - } diff --git a/Libraries/LibWeb/DOM/Range.h b/Libraries/LibWeb/DOM/Range.h index 571405157ad..ae3d25dd905 100644 --- a/Libraries/LibWeb/DOM/Range.h +++ b/Libraries/LibWeb/DOM/Range.h @@ -47,11 +47,6 @@ public: void collapse(bool to_start); WebIDL::ExceptionOr select_node_contents(GC::Ref); - void increase_start_offset(Badge, WebIDL::UnsignedLong); - void increase_end_offset(Badge, WebIDL::UnsignedLong); - void decrease_start_offset(Badge, WebIDL::UnsignedLong); - void decrease_end_offset(Badge, WebIDL::UnsignedLong); - // https://dom.spec.whatwg.org/#dom-range-start_to_start enum HowToCompareBoundaryPoints : WebIDL::UnsignedShort { START_TO_START = 0, @@ -123,6 +118,10 @@ public: } private: + friend class CharacterData; + friend class Node; + friend class Text; + explicit Range(Document&); Range(GC::Ref start_container, WebIDL::UnsignedLong start_offset, GC::Ref end_container, WebIDL::UnsignedLong end_offset); @@ -138,6 +137,16 @@ private: End, }; + void set_start_node(GC::Ref node) { m_start_container = node; } + void set_start_offset(WebIDL::UnsignedLong offset) { m_start_offset = offset; } + void set_end_node(GC::Ref node) { m_end_container = node; } + void set_end_offset(WebIDL::UnsignedLong offset) { m_end_offset = offset; } + + void increase_start_offset(WebIDL::UnsignedLong count) { m_start_offset += count; } + void increase_end_offset(WebIDL::UnsignedLong count) { m_end_offset += count; } + void decrease_start_offset(WebIDL::UnsignedLong count) { m_start_offset -= count; } + void decrease_end_offset(WebIDL::UnsignedLong count) { m_end_offset -= count; } + WebIDL::ExceptionOr set_start_or_end(GC::Ref node, u32 offset, StartOrEnd start_or_end); WebIDL::ExceptionOr select(GC::Ref node); diff --git a/Libraries/LibWeb/DOM/Text.cpp b/Libraries/LibWeb/DOM/Text.cpp index 5c3412fa7fc..b09164da6d1 100644 --- a/Libraries/LibWeb/DOM/Text.cpp +++ b/Libraries/LibWeb/DOM/Text.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2018-2022, Andreas Kling + * Copyright (c) 2025, Jelle Raaijmakers * * SPDX-License-Identifier: BSD-2-Clause */ @@ -75,30 +76,37 @@ WebIDL::ExceptionOr> Text::split_text(size_t offset) // 1. Insert new node into parent before node’s next sibling. parent->insert_before(*new_node, next_sibling()); - // 2. For each live range whose start node is node and start offset is greater than offset, set its start node to new node and decrease its start offset by offset. - for (auto& range : Range::live_ranges()) { - if (range->start_container() == this && range->start_offset() > offset) - TRY(range->set_start(*new_node, range->start_offset() - offset)); - } - - // 3. For each live range whose end node is node and end offset is greater than offset, set its end node to new node and decrease its end offset by offset. - for (auto& range : Range::live_ranges()) { - if (range->end_container() == this && range->end_offset() > offset) - TRY(range->set_end(*new_node, range->end_offset() - offset)); - } - - // 4. For each live range whose start node is parent and start offset is equal to the index of node plus 1, increase its start offset by 1. - for (auto& range : Range::live_ranges()) { - if (range->start_container() == parent.ptr() && range->start_offset() == index() + 1) - TRY(range->set_start(*range->start_container(), range->start_offset() + 1)); - } - - // 5. For each live range whose end node is parent and end offset is equal to the index of node plus 1, increase its end offset by 1. - for (auto& range : Range::live_ranges()) { - if (range->end_container() == parent.ptr() && range->end_offset() == index() + 1) { - TRY(range->set_end(*range->end_container(), range->end_offset() + 1)); + // 2. For each live range whose start node is node and start offset is greater than offset, set its start node + // to new node and decrease its start offset by offset. + for (auto* range : Range::live_ranges()) { + if (range->start_container() == this && range->start_offset() > offset) { + range->set_start_node(*new_node); + range->decrease_start_offset(offset); } } + + // 3. For each live range whose end node is node and end offset is greater than offset, set its end node to new + // node and decrease its end offset by offset. + for (auto* range : Range::live_ranges()) { + if (range->end_container() == this && range->end_offset() > offset) { + range->set_end_node(*new_node); + range->decrease_end_offset(offset); + } + } + + // 4. For each live range whose start node is parent and start offset is equal to the index of node plus 1, + // increase its start offset by 1. + for (auto* range : Range::live_ranges()) { + if (range->start_container() == parent.ptr() && range->start_offset() == index() + 1) + range->increase_start_offset(1); + } + + // 5. For each live range whose end node is parent and end offset is equal to the index of node plus 1, increase + // its end offset by 1. + for (auto* range : Range::live_ranges()) { + if (range->end_container() == parent.ptr() && range->end_offset() == index() + 1) + range->increase_end_offset(1); + } } // 8. Replace data with node node, offset offset, count count, and data the empty string.