LibWeb: Fix selection when start node is inside end node

Fixes a regression introduced in
bc8870d019 (in a performant way this
time)
This commit is contained in:
Psychpsyo 2025-07-03 18:33:11 +02:00 committed by Alexander Kalenik
parent 202c55bf28
commit baf2063e31
6 changed files with 50 additions and 9 deletions

View File

@ -362,19 +362,23 @@ void ViewportPaintable::recompute_selection_states(DOM::Range& range)
// 4. Mark the selection end node as End (if text) or Full (if anything else).
if (auto* paintable = end_container->paintable(); paintable && !range.end().node->is_inert()) {
if (is<DOM::Text>(*end_container) || end_container->is_ancestor_of(start_container)) {
if (is<DOM::Text>(*end_container))
paintable->set_selection_state(SelectionState::End);
} else {
paintable->for_each_in_inclusive_subtree([&](auto& layout_node) {
if (!layout_node.dom_node() || !layout_node.dom_node()->is_inert())
layout_node.set_selection_state(SelectionState::Full);
return TraversalDecision::Continue;
});
}
else
paintable->set_selection_state(SelectionState::Full);
}
// 5. Mark the nodes between start node and end node (in tree order) as Full.
for (auto* node = start_container->next_in_pre_order(); node && node != end_container; node = node->next_in_pre_order()) {
// NOTE: If the start node is a descendant of the end node, we cannot traverse from it to the end node since the end node is before it in tree order.
// Instead, we need to stop traversal somewhere inside the end node, or right after it.
DOM::Node* stop_at;
if (start_container->is_descendant_of(end_container)) {
stop_at = end_container->child_at_index(range.end_offset());
} else {
stop_at = end_container;
}
for (auto* node = start_container->next_in_pre_order(); node && node != stop_at; node = node->next_in_pre_order(end_container)) {
if (node->is_inert())
continue;
if (auto* paintable = node->paintable())

View File

@ -0,0 +1,7 @@
<!DOCTYPE html>
<style>
* {
margin: 0;
}
</style>
<img src="../images/selection-start-in-end-node-ref.png">

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<link rel="match" href="../expected/selection-start-in-end-node-ref.html" />
<!-- The space after the start node, as well as "NOT SELECTED" must not be selected. -->
<span id="end">End Node <span id="start">Start Node</span> </span>NOT SELECTED
<script>
let range = document.createRange();
range.setStart(start, 0);
range.setEnd(end, 2);
window.getSelection().addRange(range);
</script>

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<link rel="match" href="../expected/selection-start-in-end-node-ref.html" />
<!-- here the end node contains the start node, as well as some further selected content and some non-selected content. -->
<span id="end">End Node <span id="start">Start</span> <span>Node</span> </span>NOT SELECTED
<script>
let range = document.createRange();
range.setStart(start, 0);
range.setEnd(end, 4);
window.getSelection().addRange(range);
</script>

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<link rel="match" href="../expected/selection-start-in-end-node-ref.html" />
<!-- The text after the end node must not be selected. -->
<span id="end">End Node <span id="start">Start Node</span></span> NOT SELECTED
<script>
let range = document.createRange();
range.setStart(start, 0);
range.setEnd(end, 2);
window.getSelection().addRange(range);
</script>