1use std::cell::{Cell, LazyCell};
6use std::cmp::Ordering;
7
8use bitflags::bitflags;
9use dom_struct::dom_struct;
10use js::context::{JSContext, NoGC};
11use layout_api::QueryMsg;
12use rustc_hash::{FxHashMap, FxHashSet};
13use script_bindings::cell::DomRefCell;
14use script_bindings::codegen::GenericBindings::ShadowRootBinding::ShadowRootMethods;
15use script_bindings::dom::UnrootedDom;
16use script_bindings::reflector::{Reflector, reflect_dom_object};
17use servo_base::text::{
18 AssumeUnder4GB, RangeAny, Utf16CodeUnits, Utf32CodeUnits, Utf32CodeUnitsOrNodeOffset,
19};
20
21use crate::dom::abstractrange::bp_position;
22use crate::dom::bindings::codegen::Bindings::NodeBinding::{GetRootNodeOptions, NodeMethods};
23use crate::dom::bindings::codegen::Bindings::RangeBinding::RangeMethods;
24use crate::dom::bindings::codegen::Bindings::SelectionBinding::{
25 GetComposedRangesOptions, SelectionMethods,
26};
27use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
28use crate::dom::bindings::inheritance::Castable;
29use crate::dom::bindings::refcounted::Trusted;
30use crate::dom::bindings::reflector::DomGlobal;
31use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom};
32use crate::dom::bindings::str::DOMString;
33use crate::dom::comparator::compare_dom_positions;
34use crate::dom::document::Document;
35use crate::dom::eventtarget::EventTarget;
36use crate::dom::iterators::{PrePostIteration, UnrootedFollowingFlatTreeNodesTraversal};
37use crate::dom::node::{Node, NodeTraits};
38use crate::dom::range::Range;
39use crate::dom::selection_range::{SelectionBoundary, SelectionRange};
40use crate::dom::staticrange::StaticRange;
41use crate::dom::traversal::FlatTreeForSelectionNoGcTraversal;
42use crate::dom::types::ShadowRoot;
43use crate::dom::{CharacterData, FlatTreeParent, NodeDamage, NodeFlags, StartOrEnd};
44
45#[derive(Copy, Clone, PartialEq)]
48pub(crate) enum UsedUserSelect {
49 Text,
50 None,
51 Contain,
52 All,
53}
54
55#[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
56pub(crate) enum Direction {
57 Forwards,
58 Backwards,
59 Directionless,
60}
61
62#[dom_struct]
64pub(crate) struct Selection {
65 reflector_: Reflector,
66 document: Dom<Document>,
67 range: DomRefCell<Option<SelectionRange>>,
70 live_range: MutNullableDom<Range>,
72 visible_range: DomRefCell<Option<SelectionRange>>,
77 direction: Cell<Direction>,
80 has_scheduled_selectionchange_event: Cell<bool>,
82 visible_selection_dirty: Cell<bool>,
85}
86
87impl Selection {
88 fn new_inherited(document: &Document) -> Selection {
89 Selection {
90 reflector_: Reflector::new(),
91 document: Dom::from_ref(document),
92 range: Default::default(),
93 live_range: MutNullableDom::new(None),
94 visible_range: Default::default(),
95 direction: Cell::new(Direction::Directionless),
96 has_scheduled_selectionchange_event: Cell::new(false),
97 visible_selection_dirty: Cell::new(false),
98 }
99 }
100
101 pub(crate) fn new(cx: &mut JSContext, document: &Document) -> DomRoot<Selection> {
102 reflect_dom_object(
103 cx,
104 Box::new(Selection::new_inherited(document)),
105 &*document.global(),
106 )
107 }
108
109 pub(crate) fn visible_selection_dirty(&self) -> bool {
110 self.visible_selection_dirty.get()
111 }
112
113 pub(crate) fn collapsed(&self) -> bool {
114 self.range
115 .borrow()
116 .as_ref()
117 .is_none_or(|range| range.collapsed())
118 }
119
120 fn clear_cached_live_range(&self) {
121 if let Some(old_range) = self.live_range.take() {
122 old_range.disassociate_selection(self);
123 }
124 }
125
126 pub(crate) fn clear_selection_if_live_range_document_changed(
130 &self,
131 no_gc: &NoGC,
132 range: &Range,
133 ) -> bool {
134 let range_start_container = range.start_container();
137 let range_end_container = range.end_container();
138 if self.is_in_composed_tree_of_document_and_is_not_ua_widget(&range_start_container) &&
139 self.is_in_composed_tree_of_document_and_is_not_ua_widget(&range_end_container)
140 {
141 return false;
142 }
143
144 self.set_range(no_gc, None);
145 true
146 }
147
148 pub(crate) fn set_start_or_end_from_live_range(
153 &self,
154 no_gc: &NoGC,
155 start_or_end: StartOrEnd,
156 live_range: &Range,
157 ) -> bool {
158 let mut range = self.range.borrow_mut();
159 let range = range
160 .as_mut()
161 .expect("A live range implies a selection range");
162
163 let (container, offset) = match start_or_end {
164 StartOrEnd::Start if range.start != *live_range.start() => {
165 (live_range.start_container(), live_range.start_offset())
166 },
167 StartOrEnd::End if range.end != *live_range.end() => {
168 (live_range.end_container(), live_range.end_offset())
169 },
170 _ => return false,
171 };
172
173 let would_invert = match start_or_end {
174 StartOrEnd::Start => {
175 compare_shadow_including_dom_positions(
176 no_gc,
177 &container,
178 offset,
179 &range.end.container,
180 range.end.offset,
181 ) == Ordering::Greater
182 },
183 StartOrEnd::End => {
184 compare_shadow_including_dom_positions(
185 no_gc,
186 &range.start.container,
187 range.start.offset,
188 &container,
189 offset,
190 ) == Ordering::Greater
191 },
192 };
193
194 if would_invert {
195 *range = SelectionRange::collapsed_at(SelectionBoundary::new(&container, offset));
196 } else {
197 match start_or_end {
198 StartOrEnd::Start => range.start = SelectionBoundary::new(&container, offset),
199 StartOrEnd::End => range.end = SelectionBoundary::new(&container, offset),
200 }
201 }
202 true
203 }
204
205 pub(crate) fn update_from_live_range(
206 &self,
207 no_gc: &NoGC,
208 live_range: &Range,
209 notification: SelectionLiveRangeNotification,
210 ) {
211 debug_assert!(Some(live_range) == self.live_range.get().as_deref());
212 let start_changed = notification.contains(SelectionLiveRangeNotification::Start) &&
213 self.set_start_or_end_from_live_range(no_gc, StartOrEnd::Start, live_range);
214 let end_changed = notification.contains(SelectionLiveRangeNotification::End) &&
215 self.set_start_or_end_from_live_range(no_gc, StartOrEnd::End, live_range);
216 if start_changed || end_changed {
217 self.selection_boundaries_changed();
218 }
219 }
220
221 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
222 fn set_range(&self, _no_gc: &NoGC, new_range: Option<SelectionRange>) -> bool {
223 let changed;
224 {
225 let mut range = self.range.borrow_mut();
226 changed = *range != new_range;
227 *range = new_range;
228
229 if range.is_none() {
230 self.direction.set(Direction::Directionless);
231 }
232 }
233
234 self.clear_cached_live_range();
236
237 if changed {
238 self.selection_boundaries_changed();
239 #[cfg(debug_assertions)]
240 self.assert_valid_selection(_no_gc);
241 }
242
243 changed
244 }
245
246 pub(crate) fn set_live_range(&self, no_gc: &NoGC, new_range: Option<&Range>) {
247 if new_range == self.live_range.get().as_deref() {
248 return;
249 }
250
251 let boundaries_changed = self.set_range(no_gc, new_range.map(|new_range| new_range.into()));
252
253 if let Some(old_range) = self.live_range.take() {
256 old_range.disassociate_selection(self);
257 }
258 if let Some(new_range) = new_range {
259 self.live_range.set(Some(new_range));
260 new_range.associate_selection(self);
261 }
262
263 if !boundaries_changed {
274 self.queue_selectionchange_task();
275 }
276 }
277
278 fn selection_boundaries_changed(&self) {
279 self.set_visible_selection_dirty();
280 self.queue_selectionchange_task();
281
282 self.document.clear_command_overrides();
291 }
292
293 fn iter_nodes_with_overlaps_document_selection_flag<'no_gc>(
294 &self,
295 no_gc: &'no_gc NoGC,
296 ) -> impl Iterator<Item = UnrootedDom<'no_gc, Node>> {
297 let mut traversal = self
298 .document
299 .upcast::<Node>()
300 .following_flat_tree_nodes_unrooted(no_gc);
301 let mut next = traversal.next();
302 std::iter::from_fn(move || {
303 while let Some(node) = next.take() {
304 match node {
305 PrePostIteration::Enter(node) => {
306 if node.get_flag(NodeFlags::OVERLAPS_DOCUMENT_SELECTION) {
307 next = traversal.next();
308 return Some(node);
309 } else {
310 next = traversal.next_skipping_subtree();
315 }
316 },
317 PrePostIteration::Leave(_) => next = traversal.next(),
318 }
319 }
320 None
321 })
322 }
323
324 fn set_visible_range(&self, flat_tree_selection: Option<FlatTreeSelection>) {
325 *self.visible_range.borrow_mut() = flat_tree_selection.map(|selection| {
326 SelectionRange::new(
327 SelectionBoundary::new(&selection.start.container, selection.start.offset),
328 SelectionBoundary::new(&selection.end.container, selection.end.offset),
329 )
330 });
331 }
332
333 pub(crate) fn update_overlaps_document_selection_flags(&self, no_gc: &NoGC) {
334 if !self.visible_selection_dirty.take() {
335 return;
336 }
337
338 let previously_flagged_nodes = self
339 .iter_nodes_with_overlaps_document_selection_flag(no_gc)
340 .collect();
341 let flat_tree_selection = FlatTreeSelection::from_selection_if_renderable(no_gc, self);
342 VisibleSelectionFlagUpdate::run(
343 no_gc,
344 previously_flagged_nodes,
345 flat_tree_selection.as_ref(),
346 &self.document,
347 );
348 self.set_visible_range(flat_tree_selection);
349 }
350
351 pub(crate) fn queue_selectionchange_task(&self) {
353 if self.has_scheduled_selectionchange_event.get() {
355 return;
356 }
357 self.has_scheduled_selectionchange_event.set(true);
359 let this = Trusted::new(self);
362 self.document
363 .owner_global()
364 .task_manager()
365 .user_interaction_task_source() .queue(
367 task!(selectionchange_task_steps: move |cx| {
369 let this = this.root();
370 this.has_scheduled_selectionchange_event.set(false);
372 this.document.upcast::<EventTarget>().fire_event(cx, atom!("selectionchange"));
381 }),
382 );
383 }
384
385 fn is_in_composed_tree_of_document_and_is_not_ua_widget(&self, node: &Node) -> bool {
386 &*node.GetRootNode(&GetRootNodeOptions { composed: true }) == self.document.upcast::<Node>() &&
387 !node.is_in_ua_widget()
388 }
389
390 pub(crate) fn start_boundary(&self, cx: &mut JSContext) -> (DomRoot<Node>, u32) {
391 let range = self.expect_active_range(cx);
392 (range.start_container(), range.start_offset())
393 }
394
395 pub(crate) fn end_boundary(&self, cx: &mut JSContext) -> (DomRoot<Node>, u32) {
396 let range = self.expect_active_range(cx);
397 (range.end_container(), range.end_offset())
398 }
399
400 #[cfg(debug_assertions)]
401 fn assert_valid_selection(&self, no_gc: &NoGC) {
402 let range_borrow = self.range.borrow();
403 let Some(range) = range_borrow.as_ref() else {
404 return;
405 };
406 debug_assert_eq!(
407 range
408 .start
409 .container
410 .GetRootNode(&GetRootNodeOptions { composed: true }),
411 range
412 .end
413 .container
414 .GetRootNode(&GetRootNodeOptions { composed: true })
415 );
416 debug_assert!(
417 compare_shadow_including_dom_positions(
418 no_gc,
419 &range.start.container,
420 range.start.offset,
421 &range.end.container,
422 range.end.offset
423 ) != Ordering::Greater
424 );
425 }
426
427 #[cfg(debug_assertions)]
428 fn assert_valid_selection_and_live_range(&self, no_gc: &NoGC) {
429 self.assert_valid_selection(no_gc);
430
431 let Some(active_range) = self.live_range.get() else {
432 return;
433 };
434 debug_assert!(
435 bp_position(
436 no_gc,
437 &active_range.start_container(),
438 active_range.start_offset(),
439 &active_range.end_container(),
440 active_range.end_offset()
441 ) != Ordering::Greater
442 );
443 }
444
445 pub(crate) fn active_range(&self, cx: &mut JSContext) -> Option<DomRoot<Range>> {
450 #[cfg(debug_assertions)]
451 self.assert_valid_selection_and_live_range(cx.no_gc());
452
453 if let Some(active_range) = self.live_range.get() {
454 return Some(active_range);
455 }
456
457 let live_range = {
458 let range = self.range.borrow();
459 let range = range.as_ref()?;
460 let live_range = Range::new(
461 cx,
462 &self.document,
463 &range.start.container,
464 range.start.offset,
465 &range.start.container,
466 range.start.offset,
467 );
468 live_range
469 .SetEnd(cx.no_gc(), &range.end.container, range.end.offset)
470 .expect("New end boundary should always be valid");
471 live_range
472 };
473
474 self.live_range.set(Some(&live_range));
475 live_range.associate_selection(self);
476 Some(live_range)
477 }
478
479 pub(crate) fn expect_active_range(&self, cx: &mut JSContext) -> DomRoot<Range> {
480 self.active_range(cx)
481 .expect("Should always have an active range")
482 }
483
484 pub(crate) fn set_visible_selection_dirty(&self) {
485 self.visible_selection_dirty.set(true);
486 }
487
488 pub(crate) fn composed_anchor_position(&self) -> Option<(DomRoot<Node>, u32)> {
489 let range = self.range.borrow();
490 let range = range.as_ref()?;
491 Some(match self.direction.get() {
492 Direction::Forwards => (range.start.container.as_rooted(), range.start.offset),
493 _ => (range.end.container.as_rooted(), range.end.offset),
494 })
495 }
496
497 fn live_anchor_node(&self, cx: &mut JSContext) -> Option<DomRoot<Node>> {
499 self.active_range(cx)
500 .map(|range| match self.direction.get() {
501 Direction::Forwards => range.start_container(),
502 _ => range.end_container(),
503 })
504 }
505
506 fn live_anchor_offset(&self, cx: &mut JSContext) -> u32 {
508 self.active_range(cx)
509 .map_or(0, |range| match self.direction.get() {
510 Direction::Forwards => range.start_offset(),
511 _ => range.end_offset(),
512 })
513 }
514
515 fn live_focus_node(&self, cx: &mut JSContext) -> Option<DomRoot<Node>> {
517 self.active_range(cx)
518 .map(|range| match self.direction.get() {
519 Direction::Forwards => range.end_container(),
520 _ => range.start_container(),
521 })
522 }
523
524 fn live_focus_offset(&self, cx: &mut JSContext) -> u32 {
526 self.active_range(cx)
527 .map_or(0, |range| match self.direction.get() {
528 Direction::Forwards => range.end_offset(),
529 _ => range.start_offset(),
530 })
531 }
532
533 pub(crate) fn insert_steps(&self, parent: &Node, child: &Node, count: u32) {
538 let mut range_borrow = self.range.borrow_mut();
539 let Some(range) = &mut *range_borrow else {
540 return;
541 };
542 let child_index = LazyCell::new(|| child.index());
543 if range.start.container == parent && range.start.offset > *child_index {
546 range.start.offset += count;
547 self.selection_boundaries_changed();
548 }
549 if range.end.container == parent && range.end.offset > *child_index {
552 range.end.offset += count;
553 self.selection_boundaries_changed();
554 }
555 }
556
557 pub(crate) fn pre_remove_steps(
560 &self,
561 removed_node: &Node, parent_of_removed_node: &Node, index_of_removed_node: &mut dyn FnMut() -> u32, ) {
565 let mut range_borrow = self.range.borrow_mut();
566 let Some(range) = &mut *range_borrow else {
567 return;
568 };
569
570 if removed_node.is_shadow_including_inclusive_ancestor_of(&range.start.container) {
573 range.start = SelectionBoundary::new(parent_of_removed_node, index_of_removed_node());
574 self.selection_boundaries_changed();
575 }
576 if removed_node.is_shadow_including_inclusive_ancestor_of(&range.end.container) {
579 range.end = SelectionBoundary::new(parent_of_removed_node, index_of_removed_node());
580 self.selection_boundaries_changed();
581 }
582 if range.start.container == parent_of_removed_node &&
585 range.start.offset > index_of_removed_node()
586 {
587 range.start.offset -= 1;
588 self.selection_boundaries_changed();
589 }
590 if range.end.container == parent_of_removed_node &&
593 range.end.offset > index_of_removed_node()
594 {
595 range.end.offset -= 1;
596 self.selection_boundaries_changed();
597 }
598 }
599
600 pub(crate) fn normalization_steps(
609 &self,
610 parent: &Node,
611 node: &Node,
612 current_node: &Node,
613 current_node_index: &dyn Fn() -> u32,
614 length: u32,
615 ) {
616 let mut range_borrow = self.range.borrow_mut();
617 let Some(range) = &mut *range_borrow else {
618 return;
619 };
620 if range.start.container == current_node {
623 range.start.offset += length;
624 range.start.container = Dom::from_ref(node);
625 self.selection_boundaries_changed();
626 }
627 if range.end.container == current_node {
630 range.end.offset += length;
631 range.end.container = Dom::from_ref(node);
632 self.selection_boundaries_changed();
633 }
634 if range.start.container == parent && range.start.offset == current_node_index() {
638 range.start.container = Dom::from_ref(node);
639 range.start.offset = length;
640 self.selection_boundaries_changed();
641 }
642 if range.end.container == parent && range.end.offset == current_node_index() {
645 range.end.container = Dom::from_ref(node);
646 range.end.offset = length;
647 self.selection_boundaries_changed();
648 }
649 }
650
651 pub(crate) fn replace_data_steps(
654 &self,
655 node: &Node,
656 offset: u32,
657 removed_code_units: u32,
658 added_code_units: &mut dyn FnMut() -> u32,
659 ) {
660 let mut range_borrow = self.range.borrow_mut();
661 let Some(range) = &mut *range_borrow else {
662 return;
663 };
664 let start_container = &range.start.container;
668 let start_offset = range.start.offset;
669 if &**start_container == node &&
670 start_offset > offset &&
671 start_offset <= offset + removed_code_units
672 {
673 range.start.offset = offset;
674 self.selection_boundaries_changed();
675 }
676 let end_container = &range.end.container;
680 let end_offset = range.end.offset;
681 if &**end_container == node &&
682 end_offset > offset &&
683 end_offset <= offset + removed_code_units
684 {
685 range.end.offset = offset;
686 self.selection_boundaries_changed();
687 }
688 if &**start_container == node && start_offset > offset + removed_code_units {
692 range.start.offset = start_offset + added_code_units() - removed_code_units;
693 self.selection_boundaries_changed();
694 }
695 if &**end_container == node && end_offset > offset + removed_code_units {
699 range.end.offset = end_offset + added_code_units() - removed_code_units;
700 self.selection_boundaries_changed();
701 }
702 }
703
704 pub(crate) fn text_split_steps(
707 &self,
708 node: &Node,
709 offset: u32,
710 parent_node: &Node,
711 new_node: &Node,
712 ) {
713 let mut range_borrow = self.range.borrow_mut();
714 let Some(range) = &mut *range_borrow else {
715 return;
716 };
717 if range.start.container == node && range.start.offset > offset {
721 range.start.container = Dom::from_ref(new_node);
722 range.start.offset -= offset;
723 self.selection_boundaries_changed();
724 }
725 if range.end.container == node && range.end.offset > offset {
728 range.end.container = Dom::from_ref(new_node);
729 range.end.offset -= offset;
730 self.selection_boundaries_changed();
731 }
732 let node_index = LazyCell::new(|| node.index());
735 if range.start.container == parent_node && range.start.offset == *node_index + 1 {
736 range.start.offset += 1;
737 self.selection_boundaries_changed();
738 }
739 if range.end.container == parent_node && range.end.offset == *node_index + 1 {
742 range.end.offset += 1;
743 self.selection_boundaries_changed();
744 }
745 }
746
747 pub(crate) fn collapse_to_dom_position(
748 &self,
749 cx: &mut JSContext,
750 container: &Node,
751 offset: Utf32CodeUnitsOrNodeOffset,
752 ) {
753 let _ = self.Collapse(
754 cx,
755 Some(container),
756 container.to_sibling_or_utf16_offset(offset),
757 );
758 }
759
760 pub(crate) fn collapse_or_extend_to_dom_position(
761 &self,
762 cx: &mut JSContext,
763 container: &Node,
764 offset: Utf32CodeUnitsOrNodeOffset,
765 ) {
766 let offset = container.to_sibling_or_utf16_offset(offset);
767 let is_anchor =
768 self.composed_anchor_position()
769 .is_some_and(|(anchor_node, anchor_offset)| {
770 &*anchor_node == container && anchor_offset == offset
771 });
772
773 if self.range.borrow().is_none() || is_anchor {
774 let _ = self.Collapse(cx, Some(container), offset);
775 } else {
776 let _ = self.Extend(cx, container, offset);
777 }
778 }
779}
780
781impl SelectionMethods<crate::DomTypeHolder> for Selection {
782 fn GetAnchorNode(&self, cx: &mut JSContext) -> Option<DomRoot<Node>> {
784 self.live_anchor_node(cx)
790 }
791
792 fn AnchorOffset(&self, cx: &mut JSContext) -> u32 {
794 self.live_anchor_offset(cx)
800 }
801
802 fn GetFocusNode(&self, cx: &mut JSContext) -> Option<DomRoot<Node>> {
804 self.live_focus_node(cx)
810 }
811
812 fn FocusOffset(&self, cx: &mut JSContext) -> u32 {
814 self.live_focus_offset(cx)
820 }
821
822 fn IsCollapsed(&self, cx: &mut JSContext) -> bool {
824 self.active_range(cx).is_none_or(|range| range.collapsed())
827 }
828
829 fn RangeCount(&self) -> u32 {
831 if self.range.borrow().is_none() {
837 return 0;
838 }
839 1
840 }
841
842 fn Type(&self) -> DOMString {
844 let range = self.range.borrow();
851 let Some(range) = range.as_ref() else {
852 return DOMString::from_static("None");
853 };
854 if range.collapsed() {
855 DOMString::from_static("Caret")
856 } else {
857 DOMString::from_static("Range")
858 }
859 }
860
861 fn Direction(&self) -> DOMString {
863 match self.direction.get() {
867 Direction::Directionless => DOMString::from_static("none"),
868 Direction::Forwards => DOMString::from_static("forward"),
869 Direction::Backwards => DOMString::from_static("backward"),
870 }
871 }
872
873 fn GetRangeAt(&self, cx: &mut JSContext, index: u32) -> Fallible<DomRoot<Range>> {
875 if index != 0 {
882 return Err(Error::IndexSize(Some("Index must be zero".into())));
883 }
884
885 let Some(range) = self.active_range(cx) else {
886 return Err(Error::IndexSize(Some("Selection is empty".into())));
887 };
888
889 Ok(range)
890 }
891
892 fn AddRange(&self, no_gc: &NoGC, range: &Range) {
894 if !self.is_in_composed_tree_of_document_and_is_not_ua_widget(&range.start_container()) {
897 return;
898 }
899
900 if self.RangeCount() != 0 {
902 return;
903 }
904
905 self.set_live_range(no_gc, Some(range));
907
908 self.direction.set(Direction::Forwards);
910 }
911
912 fn RemoveRange(&self, no_gc: &NoGC, range: &Range) -> ErrorResult {
914 if let Some(own_range) = self.live_range.get() &&
917 &*own_range == range
918 {
919 self.set_range(no_gc, None);
920 return Ok(());
921 }
922 Err(Error::NotFound(None))
923 }
924
925 fn RemoveAllRanges(&self, no_gc: &NoGC) {
927 self.set_range(no_gc, None);
930 }
931
932 fn Empty(&self, no_gc: &NoGC) {
934 self.RemoveAllRanges(no_gc);
936 }
937
938 fn GetComposedRanges(
940 &self,
941 cx: &mut JSContext,
942 options: &GetComposedRangesOptions,
943 ) -> Vec<DomRoot<StaticRange>> {
944 let borrowed_range = self.range.borrow();
946 let Some(range) = borrowed_range.as_ref() else {
947 return Vec::new();
948 };
949
950 let mut start_node = range.start.container.as_rooted();
953 let mut start_offset = range.start.offset;
954
955 let is_ancestor_of_provided_shadow_roots = |shadow_root: &ShadowRoot| {
956 let shadow_root_node = shadow_root.upcast::<Node>();
957 options.shadowRoots.iter().any(|option_shadow_root| {
958 shadow_root_node
959 .is_shadow_including_inclusive_ancestor_of(option_shadow_root.upcast())
960 })
961 };
962
963 while let Some(containing_shadow_root) = start_node.containing_shadow_root() &&
967 !is_ancestor_of_provided_shadow_roots(&containing_shadow_root)
968 {
969 let host = DomRoot::upcast::<Node>(containing_shadow_root.Host());
971 start_offset = host.index();
972
973 let Some(new_start_node) = host.GetParentNode() else {
977 return Vec::new();
978 };
979 start_node = new_start_node;
980 }
981
982 let mut end_node = range.end.container.as_rooted();
985 let mut end_offset = range.end.offset;
986
987 while let Some(containing_shadow_root) = end_node.containing_shadow_root() &&
991 !is_ancestor_of_provided_shadow_roots(&containing_shadow_root)
992 {
993 let host = DomRoot::upcast::<Node>(containing_shadow_root.Host());
995 end_offset = host.index() + 1;
996
997 let Some(new_end_node) = host.GetParentNode() else {
1001 return Vec::new();
1002 };
1003 end_node = new_end_node;
1004 }
1005
1006 drop(borrowed_range);
1007
1008 vec![DomRoot::from_ref(&StaticRange::new(
1012 cx,
1013 &self.document,
1014 &start_node,
1015 start_offset,
1016 &end_node,
1017 end_offset,
1018 ))]
1019 }
1020
1021 fn Collapse(&self, cx: &mut JSContext, node: Option<&Node>, offset: u32) -> ErrorResult {
1023 let Some(node) = node else {
1026 self.set_range(cx.no_gc(), None);
1027 return Ok(());
1028 };
1029
1030 if node.is_doctype() {
1033 return Err(Error::InvalidNodeType(None));
1034 }
1035
1036 if offset > node.len() {
1039 return Err(Error::IndexSize(None));
1040 }
1041
1042 if !self.is_in_composed_tree_of_document_and_is_not_ua_widget(node) {
1045 return Ok(());
1046 }
1047
1048 self.set_range(
1052 cx.no_gc(),
1053 Some(SelectionRange::collapsed_at(SelectionBoundary::new(
1054 node, offset,
1055 ))),
1056 );
1057
1058 self.direction.set(Direction::Forwards);
1060
1061 Ok(())
1062 }
1063
1064 fn SetPosition(&self, cx: &mut JSContext, node: Option<&Node>, offset: u32) -> ErrorResult {
1066 self.Collapse(cx, node, offset)
1068 }
1069
1070 fn CollapseToStart(&self, cx: &mut JSContext) -> ErrorResult {
1072 let Some((start_container, start_offset)) = self
1077 .range
1078 .borrow()
1079 .as_ref()
1080 .map(|range| (range.start.container.as_rooted(), range.start.offset))
1081 else {
1082 return Err(Error::InvalidState(None));
1083 };
1084 self.Collapse(cx, Some(&*start_container), start_offset)
1085 }
1086
1087 fn CollapseToEnd(&self, cx: &mut JSContext) -> ErrorResult {
1089 let Some((end_container, end_offset)) = self
1093 .range
1094 .borrow()
1095 .as_ref()
1096 .map(|range| (range.end.container.as_rooted(), range.end.offset))
1097 else {
1098 return Err(Error::InvalidState(None));
1099 };
1100 self.Collapse(cx, Some(&*end_container), end_offset)
1101 }
1102
1103 fn Extend(&self, cx: &mut JSContext, node: &Node, offset: u32) -> ErrorResult {
1105 if !self.is_in_composed_tree_of_document_and_is_not_ua_widget(node) {
1108 return Ok(());
1109 }
1110
1111 let range_borrow = self.range.borrow();
1113 let Some(range) = range_borrow.as_ref() else {
1114 return Err(Error::InvalidState(None));
1115 };
1116
1117 if node.is_doctype() {
1120 return Err(Error::InvalidNodeType(None));
1121 }
1122
1123 if offset > node.len() {
1126 return Err(Error::IndexSize(None));
1127 }
1128
1129 let (old_anchor_node, old_anchor_offset) = self
1134 .composed_anchor_position()
1135 .expect("has range, therefore has anchor node");
1136
1137 let direction;
1140
1141 let in_different_roots = !nodes_have_same_shadow_root(&range.start.container, node);
1144 drop(range_borrow);
1145
1146 if in_different_roots {
1147 self.set_range(
1148 cx.no_gc(),
1149 Some(SelectionRange::collapsed_at(SelectionBoundary::new(
1150 node, offset,
1151 ))),
1152 );
1153 direction = Direction::Forwards;
1154 } else {
1155 let is_old_anchor_before_or_equal = matches!(
1156 compare_shadow_including_dom_positions(
1157 cx.no_gc(),
1158 &old_anchor_node,
1159 old_anchor_offset,
1160 node,
1161 offset
1162 ),
1163 Ordering::Less | Ordering::Equal
1164 );
1165 if is_old_anchor_before_or_equal {
1166 self.set_range(
1169 cx.no_gc(),
1170 Some(SelectionRange::new(
1171 SelectionBoundary::new(&old_anchor_node, old_anchor_offset),
1172 SelectionBoundary::new(node, offset),
1173 )),
1174 );
1175 direction = Direction::Forwards;
1176 } else {
1177 self.set_range(
1180 cx.no_gc(),
1181 Some(SelectionRange::new(
1182 SelectionBoundary::new(node, offset),
1183 SelectionBoundary::new(&old_anchor_node, old_anchor_offset),
1184 )),
1185 );
1186 direction = Direction::Backwards;
1187 }
1188 }
1189
1190 self.direction.set(direction);
1196
1197 Ok(())
1198 }
1199
1200 fn SetBaseAndExtent(
1202 &self,
1203 cx: &mut JSContext,
1204 anchor_node: &Node,
1205 anchor_offset: u32,
1206 focus_node: &Node,
1207 focus_offset: u32,
1208 ) -> ErrorResult {
1209 if anchor_node.is_doctype() || focus_node.is_doctype() {
1212 return Err(Error::InvalidNodeType(None));
1213 }
1214
1215 if anchor_offset > anchor_node.len() || focus_offset > focus_node.len() {
1219 return Err(Error::IndexSize(None));
1220 }
1221
1222 if !self.is_in_composed_tree_of_document_and_is_not_ua_widget(anchor_node) ||
1225 !self.is_in_composed_tree_of_document_and_is_not_ua_widget(focus_node)
1226 {
1227 return Ok(());
1228 }
1229
1230 let ordering = compare_shadow_including_dom_positions(
1242 cx.no_gc(),
1243 anchor_node,
1244 anchor_offset,
1245 focus_node,
1246 focus_offset,
1247 );
1248 if ordering == Ordering::Less {
1249 self.set_range(
1250 cx.no_gc(),
1251 Some(SelectionRange::new(
1252 SelectionBoundary::new(anchor_node, anchor_offset),
1253 SelectionBoundary::new(focus_node, focus_offset),
1254 )),
1255 );
1256 } else {
1257 self.set_range(
1258 cx.no_gc(),
1259 Some(SelectionRange::new(
1260 SelectionBoundary::new(focus_node, focus_offset),
1261 SelectionBoundary::new(anchor_node, anchor_offset),
1262 )),
1263 );
1264 };
1265
1266 if ordering == Ordering::Greater {
1272 self.direction.set(Direction::Backwards);
1273 } else {
1274 self.direction.set(Direction::Forwards);
1275 }
1276
1277 Ok(())
1278 }
1279
1280 fn SelectAllChildren(&self, cx: &mut JSContext, node: &Node) -> ErrorResult {
1282 if node.is_doctype() {
1285 return Err(Error::InvalidNodeType(None));
1286 }
1287
1288 if !self.is_in_composed_tree_of_document_and_is_not_ua_widget(node) {
1291 return Ok(());
1292 }
1293
1294 let child_count = node.children_count();
1296
1297 self.set_range(
1301 cx.no_gc(),
1302 Some(SelectionRange::new(
1303 SelectionBoundary::new(node, 0),
1304 SelectionBoundary::new(node, child_count),
1305 )),
1306 );
1307
1308 self.direction.set(Direction::Forwards);
1310
1311 Ok(())
1312 }
1313
1314 fn DeleteFromDocument(&self, cx: &mut JSContext) -> ErrorResult {
1316 let Some(active_range) = self
1320 .active_range(cx)
1321 .filter(|range| range.start_and_end_are_in_document_tree())
1322 else {
1323 return Ok(());
1324 };
1325
1326 active_range.DeleteContents(cx)
1327 }
1328
1329 fn ContainsNode(&self, no_gc: &NoGC, node: &Node, allow_partial_containment: bool) -> bool {
1331 if !self.is_in_composed_tree_of_document_and_is_not_ua_widget(node) {
1344 return false;
1345 }
1346 let range = self.range.borrow();
1347 let Some(range) = range.as_ref() else {
1348 return false;
1349 };
1350 let start_node = &*range.start.container;
1351 if !self.is_in_composed_tree_of_document_and_is_not_ua_widget(start_node) {
1352 return false;
1353 }
1354 let end_node = &*range.end.container;
1355
1356 let first_offset = 0;
1357 let last_offset = node.len();
1358 let (compare_start_to, compare_end_to) = if allow_partial_containment {
1359 (last_offset, first_offset)
1360 } else {
1361 (first_offset, last_offset)
1362 };
1363
1364 if !node.is_in_flat_tree(no_gc) {
1369 return false;
1370 }
1371
1372 matches!(
1373 compare_shadow_including_dom_positions(
1374 no_gc,
1375 start_node,
1376 range.start.offset,
1377 node,
1378 compare_start_to
1379 ),
1380 Ordering::Less | Ordering::Equal
1381 ) && matches!(
1382 compare_shadow_including_dom_positions(
1383 no_gc,
1384 end_node,
1385 range.end.offset,
1386 node,
1387 compare_end_to
1388 ),
1389 Ordering::Greater | Ordering::Equal
1390 )
1391 }
1392
1393 fn Stringifier(&self, cx: &mut JSContext) -> DOMString {
1395 let Some(visible_selection) =
1401 FlatTreeSelection::from_selection_if_renderable(cx.no_gc(), self)
1402 else {
1403 return DOMString::new();
1404 };
1405
1406 self.document.window().layout_reflow(QueryMsg::StyleQuery);
1408
1409 let mut user_select_cache = Default::default();
1410 let mut string = DOMString::new();
1411 for node in visible_selection.traversal() {
1412 let Some(character_data) = node.downcast::<CharacterData>() else {
1413 continue;
1414 };
1415
1416 if node.used_user_select(cx.no_gc(), &mut user_select_cache) == UsedUserSelect::None {
1417 continue;
1418 }
1419
1420 let range = visible_selection.range_for_character_data(character_data);
1421 let Some(text) = character_data.rendered_text(range) else {
1422 continue;
1423 };
1424 string.push_str(&text);
1425 }
1426
1427 string
1428 }
1429}
1430
1431impl<'dom> LayoutDom<'dom, Selection> {
1432 #[expect(unsafe_code)]
1433 pub(crate) fn range_for_layout(&self) -> &Option<SelectionRange> {
1434 unsafe { self.unsafe_get().visible_range.borrow_for_layout() }
1435 }
1436}
1437
1438enum FlatTreeNodePosition {
1439 Before(DomRoot<Node>),
1440 Inside(DomRoot<Node>),
1441 After(DomRoot<Node>),
1442}
1443
1444impl FlatTreeNodePosition {
1445 fn node(&self) -> &Node {
1446 match self {
1447 FlatTreeNodePosition::Before(node) => node,
1448 FlatTreeNodePosition::Inside(node) => node,
1449 FlatTreeNodePosition::After(node) => node,
1450 }
1451 }
1452}
1453
1454fn position_in_flat_tree_for_selection(
1458 no_gc: &NoGC,
1459 boundary: &FlatTreeBoundary,
1460) -> FlatTreeNodePosition {
1461 if boundary.container.is::<CharacterData>() {
1462 return FlatTreeNodePosition::Inside(boundary.container.as_rooted());
1463 }
1464
1465 let shadow_host_or_node = |node: &Node| {
1466 boundary
1467 .container
1468 .downcast::<ShadowRoot>()
1469 .map(|shadow_root| DomRoot::upcast(shadow_root.Host()))
1470 .unwrap_or(DomRoot::from_ref(node))
1471 };
1472
1473 if let Some(child) = boundary.container.children().nth(boundary.offset as usize) {
1474 if let FlatTreeParent::Parent(_) = child.parent_in_flat_tree(no_gc) {
1475 return FlatTreeNodePosition::Before(child);
1476 }
1477 } else if let Some(last_child) = boundary.container.GetLastChild() &&
1478 let FlatTreeParent::Parent(_) = last_child.parent_in_flat_tree(no_gc)
1479 {
1480 return FlatTreeNodePosition::After(shadow_host_or_node(&boundary.container.as_rooted()));
1481 }
1482
1483 FlatTreeNodePosition::Inside(shadow_host_or_node(&boundary.container.as_rooted()))
1486}
1487
1488impl Node {
1489 pub(crate) fn to_sibling_or_utf16_offset(&self, offset: Utf32CodeUnitsOrNodeOffset) -> u32 {
1492 if let Some(character_data) = self.downcast::<CharacterData>() {
1493 offset
1495 .to_utf16_code_units_in(AssumeUnder4GB, &character_data.data())
1496 .0
1497 } else {
1498 offset.0
1499 }
1500 }
1501}
1502
1503bitflags! {
1504 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
1505 pub(crate) struct SelectionLiveRangeNotification: u8 {
1506 const Start = 1 << 0;
1507 const End = 1 << 1;
1508 }
1509}
1510
1511fn nodes_have_same_shadow_root(a: &Node, b: &Node) -> bool {
1512 a.is_connected() && b.is_connected() && a.containing_shadow_root() == b.containing_shadow_root()
1513}
1514
1515fn project_into_shared_tree<'a>(
1522 no_gc: &'a NoGC,
1523 container_a: &Node,
1524 mut offset_a: u32,
1525 target: Option<&Node>,
1526) -> (UnrootedDom<'a, Node>, u32) {
1527 let mut start_node = UnrootedDom::from_ref(container_a, no_gc);
1528 while let Some(containing_shadow_root) = start_node.containing_shadow_root_unrooted(no_gc) &&
1529 target.is_none_or(|target| {
1530 !containing_shadow_root
1531 .upcast::<Node>()
1532 .is_shadow_including_inclusive_ancestor_of(target)
1533 })
1534 {
1535 let host = DomRoot::upcast::<Node>(containing_shadow_root.Host());
1536 offset_a = host.index();
1537 start_node = host
1538 .get_parent_node_unrooted(no_gc)
1539 .expect("Should only be called on nodes in the same tree");
1540 }
1541
1542 (start_node, offset_a)
1543}
1544
1545fn compare_shadow_including_dom_positions(
1549 no_gc: &NoGC,
1550 container_a: &Node,
1551 offset_a: u32,
1552 container_b: &Node,
1553 offset_b: u32,
1554) -> Ordering {
1555 let (projected_container_a, projected_offset_a) =
1556 project_into_shared_tree(no_gc, container_a, offset_a, Some(container_b));
1557 let (projected_container_b, projected_offset_b) =
1558 project_into_shared_tree(no_gc, container_b, offset_b, Some(container_a));
1559
1560 let a_was_projected = &**projected_container_a != container_a;
1561 let b_was_projected = &**projected_container_b != container_b;
1562
1563 match bp_position(
1564 no_gc,
1565 &projected_container_a,
1566 projected_offset_a,
1567 &projected_container_b,
1568 projected_offset_b,
1569 ) {
1570 Ordering::Equal if a_was_projected && !b_was_projected => Ordering::Greater,
1571 Ordering::Equal if !a_was_projected && b_was_projected => Ordering::Less,
1572 ordering => ordering,
1573 }
1574}
1575
1576struct FlatTreeBoundary<'no_gc> {
1577 container: UnrootedDom<'no_gc, Node>,
1578 offset: u32,
1579}
1580
1581struct FlatTreeSelection<'no_gc> {
1582 no_gc: &'no_gc NoGC,
1583 start: FlatTreeBoundary<'no_gc>,
1584 end: FlatTreeBoundary<'no_gc>,
1585}
1586
1587impl<'no_gc> FlatTreeSelection<'no_gc> {
1588 fn from_selection_if_renderable(no_gc: &'no_gc NoGC, selection: &Selection) -> Option<Self> {
1592 let range = selection.range.borrow();
1593 let range = range.as_ref()?;
1594
1595 let mut start_container = range.start.container.as_unrooted(no_gc);
1596 let mut start_offset = range.start.offset;
1597 let mut end_container = range.end.container.as_unrooted(no_gc);
1598 let mut end_offset = range.end.offset;
1599
1600 if !start_container.is_in_flat_tree(no_gc) {
1601 return None;
1602 }
1603 if !end_container.is_in_flat_tree(no_gc) {
1604 return None;
1605 }
1606
1607 let ordering = compare_dom_positions::<FlatTreeForSelectionNoGcTraversal>(
1611 no_gc,
1612 &range.start.container,
1613 range.start.offset,
1614 &range.end.container,
1615 range.end.offset,
1616 )
1617 .0?;
1618
1619 if ordering == Ordering::Greater {
1622 std::mem::swap(&mut start_container, &mut end_container);
1623 std::mem::swap(&mut start_offset, &mut end_offset);
1624 }
1625
1626 Some(FlatTreeSelection {
1627 no_gc,
1628 start: FlatTreeBoundary {
1629 container: start_container,
1630 offset: start_offset,
1631 },
1632 end: FlatTreeBoundary {
1633 container: end_container,
1634 offset: end_offset,
1635 },
1636 })
1637 }
1638
1639 fn range_for_character_data(&self, character_data: &CharacterData) -> RangeAny<Utf32CodeUnits> {
1640 let text = character_data.data();
1641 let node: &Node = character_data.upcast();
1642 RangeAny::new(
1643 (node == &**self.start.container)
1644 .then(|| Utf16CodeUnits(self.start.offset).to_utf32_code_units_in(&text)),
1645 (node == &**self.end.container)
1646 .then(|| Utf16CodeUnits(self.end.offset).to_utf32_code_units_in(&text)),
1647 )
1648 }
1649
1650 fn traversal(&self) -> VisibleSelectionTraversal<'no_gc> {
1651 let start_position = position_in_flat_tree_for_selection(self.no_gc, &self.start);
1652 let end_position = position_in_flat_tree_for_selection(self.no_gc, &self.end);
1653 VisibleSelectionTraversal {
1654 following: start_position
1655 .node()
1656 .following_flat_tree_nodes_unrooted(self.no_gc),
1657 start: start_position,
1658 end: end_position,
1659 finished: false,
1660 skip_subtree: false,
1661 }
1662 }
1663}
1664
1665struct VisibleSelectionTraversal<'no_gc> {
1666 following: UnrootedFollowingFlatTreeNodesTraversal<'no_gc>,
1667 start: FlatTreeNodePosition,
1668 end: FlatTreeNodePosition,
1669 finished: bool,
1670 skip_subtree: bool,
1671}
1672
1673impl<'no_gc> Iterator for VisibleSelectionTraversal<'no_gc> {
1674 type Item = UnrootedDom<'no_gc, Node>;
1675
1676 fn next(&mut self) -> Option<Self::Item> {
1677 while !self.finished {
1678 let following = if std::mem::take(&mut self.skip_subtree) {
1679 self.following.next_skipping_subtree()?
1680 } else {
1681 self.following.next()?
1682 };
1683
1684 match following {
1685 PrePostIteration::Enter(node) => {
1686 if &**node == self.end.node() &&
1689 matches!(self.end, FlatTreeNodePosition::Before(_))
1690 {
1691 self.finished = true;
1692 break;
1693 }
1694 if &**node == self.start.node() &&
1697 matches!(self.start, FlatTreeNodePosition::After(_))
1698 {
1699 self.skip_subtree = true;
1700 }
1701 return Some(node);
1702 },
1703 PrePostIteration::Leave(node) => {
1704 self.finished = &**node == self.end.node();
1705 },
1706 }
1707 }
1708
1709 None
1710 }
1711}
1712
1713struct VisibleSelectionFlagUpdate<'no_gc> {
1714 no_gc: &'no_gc NoGC,
1715 previously_flagged_nodes: FxHashSet<UnrootedDom<'no_gc, Node>>,
1721 used_user_select_cache: FxHashMap<UnrootedDom<'no_gc, Node>, UsedUserSelect>,
1723 needs_new_display_list: bool,
1725}
1726
1727impl<'no_gc> VisibleSelectionFlagUpdate<'no_gc> {
1728 fn run(
1729 no_gc: &'no_gc NoGC,
1730 previously_flagged_nodes: FxHashSet<UnrootedDom<'no_gc, Node>>,
1731 flat_tree_selection: Option<&FlatTreeSelection<'_>>,
1732 document: &Document,
1733 ) {
1734 let mut update = Self {
1735 no_gc,
1736 previously_flagged_nodes,
1737 used_user_select_cache: Default::default(),
1738 needs_new_display_list: false,
1739 };
1740
1741 if let Some(flat_tree_selection) = flat_tree_selection {
1742 let traversal = flat_tree_selection.traversal();
1743 for ancestor in traversal
1744 .start
1745 .node()
1746 .ancestors_in_flat_tree_unrooted(no_gc)
1747 {
1748 update.set(&ancestor, flat_tree_selection);
1749 }
1750 for node in traversal {
1751 update.set(&node, flat_tree_selection);
1752 }
1753 }
1754
1755 update.finish(document);
1756 }
1757
1758 fn set(&mut self, node: &UnrootedDom<'no_gc, Node>, flat_tree_selection: &FlatTreeSelection) {
1759 if !node.get_flag(NodeFlags::OVERLAPS_DOCUMENT_SELECTION) {
1760 node.set_flag(NodeFlags::OVERLAPS_DOCUMENT_SELECTION, true);
1761 debug_assert!(!self.previously_flagged_nodes.contains(node));
1762 } else {
1763 self.previously_flagged_nodes.remove(node);
1764 }
1765
1766 let user_select = node.used_user_select(self.no_gc, &mut self.used_user_select_cache);
1771 let inhibited = user_select == UsedUserSelect::None;
1772 node.set_flag(NodeFlags::SELECTION_INHIBITED, inhibited);
1773
1774 self.set_node_selection(node, (!inhibited).then_some(flat_tree_selection));
1775 }
1776
1777 fn clear(&mut self, node: &Node) {
1778 node.set_flag(NodeFlags::OVERLAPS_DOCUMENT_SELECTION, false);
1779 node.set_flag(NodeFlags::SELECTION_INHIBITED, false);
1780 self.set_node_selection(node, None);
1781 }
1782
1783 fn set_character_data_selection(
1784 &mut self,
1785 character_data: &CharacterData,
1786 range: Option<RangeAny<Utf32CodeUnits>>,
1787 ) {
1788 if character_data.set_text_run_selection(range) {
1789 self.needs_new_display_list = true;
1790 } else {
1791 character_data
1796 .upcast::<Node>()
1797 .dirty(self.no_gc, NodeDamage::ContentOrHeritage);
1798 }
1799 }
1800
1801 fn set_node_selection(&mut self, node: &Node, flat_tree_selection: Option<&FlatTreeSelection>) {
1802 if let Some(character_data) = node.downcast::<CharacterData>() {
1803 let range = flat_tree_selection.map(|flat_tree_selection| {
1804 flat_tree_selection.range_for_character_data(character_data)
1805 });
1806 self.set_character_data_selection(character_data, range);
1807 } else if node.set_element_selection(flat_tree_selection.is_some()) {
1808 self.needs_new_display_list = true;
1809 }
1810 }
1811
1812 fn finish(mut self, document: &Document) {
1813 for node in std::mem::take(&mut self.previously_flagged_nodes) {
1814 self.clear(&node);
1815 }
1816 if self.needs_new_display_list {
1817 document.window().layout().set_needs_new_display_list();
1818 }
1819 }
1820}