LibWeb: Fix selection when start node is inside end node
Some checks are pending
CI / macOS, arm64, Sanitizer_CI, Clang (push) Waiting to run
CI / Linux, x86_64, Fuzzers_CI, Clang (push) Waiting to run
CI / Linux, x86_64, Sanitizer_CI, GNU (push) Waiting to run
CI / Linux, x86_64, Sanitizer_CI, Clang (push) Waiting to run
Package the js repl as a binary artifact / Linux, arm64 (push) Waiting to run
Package the js repl as a binary artifact / macOS, arm64 (push) Waiting to run
Package the js repl as a binary artifact / Linux, x86_64 (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Label PRs with merge conflicts / auto-labeler (push) Waiting to run
Push notes / build (push) Waiting to run

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
commit baf2063e31
Notes: github-actions[bot] 2025-07-04 18:20:56 +00:00
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). // 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 (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); paintable->set_selection_state(SelectionState::End);
} else { else
paintable->for_each_in_inclusive_subtree([&](auto& layout_node) { paintable->set_selection_state(SelectionState::Full);
if (!layout_node.dom_node() || !layout_node.dom_node()->is_inert())
layout_node.set_selection_state(SelectionState::Full);
return TraversalDecision::Continue;
});
}
} }
// 5. Mark the nodes between start node and end node (in tree order) as 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()) if (node->is_inert())
continue; continue;
if (auto* paintable = node->paintable()) 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>