LibWeb: Avoid cross-root Selection extend comparisons

Selection.extend() can keep an old anchor that is no longer orderable
with the new focus after a selected shadow host is detached. Collapse
to the new focus in that case and avoid comparing boundary points from
different shadow-including roots.

Add reduced crash coverage for extending a selection after detaching
the shadow host that held the old anchor.
This commit is contained in:
Andreas Kling 2026-06-06 23:47:10 +02:00 committed by Andreas Kling
parent ecb3080ac6
commit 3fcd95327e
2 changed files with 17 additions and 2 deletions

View file

@ -301,8 +301,10 @@ WebIDL::ExceptionOr<void> Selection::extend(GC::Ref<DOM::Node> node, unsigned of
// 4. Let newRange be a new range.
auto new_range = DOM::Range::create(*m_document);
auto old_anchor_and_new_focus_have_the_same_shadow_including_root = &old_anchor_node.shadow_including_root() == &new_focus_node->shadow_including_root();
// 5. If node's root is not the same as the this's range's root, set the start newRange's start and end to newFocus.
if (&node->root() != &m_range->start_container()->root()) {
if (&node->root() != &m_range->start_container()->root() || !old_anchor_and_new_focus_have_the_same_shadow_including_root) {
TRY(new_range->set_start(new_focus_node, new_focus_offset));
TRY(new_range->set_end(new_focus_node, new_focus_offset));
}
@ -321,7 +323,7 @@ WebIDL::ExceptionOr<void> Selection::extend(GC::Ref<DOM::Node> node, unsigned of
set_range(new_range);
// 9. If newFocus is before oldAnchor, set this's direction to backwards. Otherwise, set it to forwards.
if (DOM::position_of_boundary_point_relative_to_other_boundary_point({ new_focus_node, new_focus_offset }, { old_anchor_node, old_anchor_offset }) == DOM::RelativeBoundaryPointPosition::Before) {
if (old_anchor_and_new_focus_have_the_same_shadow_including_root && DOM::position_of_boundary_point_relative_to_other_boundary_point({ new_focus_node, new_focus_offset }, { old_anchor_node, old_anchor_offset }) == DOM::RelativeBoundaryPointPosition::Before) {
m_direction = Direction::Backwards;
} else {
m_direction = Direction::Forwards;

View file

@ -0,0 +1,13 @@
<!doctype html>
<div id="host"></div>
<div id="target"></div>
<script>
const shadowRoot = host.attachShadow({ mode: "open" });
shadowRoot.innerHTML = "<span id=anchor>hello</span>";
const anchor = shadowRoot.getElementById("anchor");
const selection = getSelection();
selection.collapse(anchor, 0);
host.remove();
selection.extend(target, 0);
</script>