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::{RootedSelectionBoundary, 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<RootedSelectionBoundary> {
489 let range = self.range.borrow();
490 let range = range.as_ref()?;
491 let (container, offset) = 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 Some(RootedSelectionBoundary::new_with_utf16_offset(
496 container, offset,
497 ))
498 }
499
500 fn live_anchor_node(&self, cx: &mut JSContext) -> Option<DomRoot<Node>> {
502 self.active_range(cx)
503 .map(|range| match self.direction.get() {
504 Direction::Forwards => range.start_container(),
505 _ => range.end_container(),
506 })
507 }
508
509 fn live_anchor_offset(&self, cx: &mut JSContext) -> u32 {
511 self.active_range(cx)
512 .map_or(0, |range| match self.direction.get() {
513 Direction::Forwards => range.start_offset(),
514 _ => range.end_offset(),
515 })
516 }
517
518 fn live_focus_node(&self, cx: &mut JSContext) -> Option<DomRoot<Node>> {
520 self.active_range(cx)
521 .map(|range| match self.direction.get() {
522 Direction::Forwards => range.end_container(),
523 _ => range.start_container(),
524 })
525 }
526
527 fn live_focus_offset(&self, cx: &mut JSContext) -> u32 {
529 self.active_range(cx)
530 .map_or(0, |range| match self.direction.get() {
531 Direction::Forwards => range.end_offset(),
532 _ => range.start_offset(),
533 })
534 }
535
536 pub(crate) fn insert_steps(&self, parent: &Node, child: &Node, count: u32) {
541 let mut range_borrow = self.range.borrow_mut();
542 let Some(range) = &mut *range_borrow else {
543 return;
544 };
545 let child_index = LazyCell::new(|| child.index());
546 if range.start.container == parent && range.start.offset > *child_index {
549 range.start.offset += count;
550 self.selection_boundaries_changed();
551 }
552 if range.end.container == parent && range.end.offset > *child_index {
555 range.end.offset += count;
556 self.selection_boundaries_changed();
557 }
558 }
559
560 pub(crate) fn pre_remove_steps(
563 &self,
564 removed_node: &Node, parent_of_removed_node: &Node, index_of_removed_node: &mut dyn FnMut() -> u32, ) {
568 let mut range_borrow = self.range.borrow_mut();
569 let Some(range) = &mut *range_borrow else {
570 return;
571 };
572
573 if removed_node.is_shadow_including_inclusive_ancestor_of(&range.start.container) {
576 range.start = SelectionBoundary::new(parent_of_removed_node, index_of_removed_node());
577 self.selection_boundaries_changed();
578 }
579 if removed_node.is_shadow_including_inclusive_ancestor_of(&range.end.container) {
582 range.end = SelectionBoundary::new(parent_of_removed_node, index_of_removed_node());
583 self.selection_boundaries_changed();
584 }
585 if range.start.container == parent_of_removed_node &&
588 range.start.offset > index_of_removed_node()
589 {
590 range.start.offset -= 1;
591 self.selection_boundaries_changed();
592 }
593 if range.end.container == parent_of_removed_node &&
596 range.end.offset > index_of_removed_node()
597 {
598 range.end.offset -= 1;
599 self.selection_boundaries_changed();
600 }
601 }
602
603 pub(crate) fn normalization_steps(
612 &self,
613 parent: &Node,
614 node: &Node,
615 current_node: &Node,
616 current_node_index: &dyn Fn() -> u32,
617 length: u32,
618 ) {
619 let mut range_borrow = self.range.borrow_mut();
620 let Some(range) = &mut *range_borrow else {
621 return;
622 };
623 if range.start.container == current_node {
626 range.start.offset += length;
627 range.start.container = Dom::from_ref(node);
628 self.selection_boundaries_changed();
629 }
630 if range.end.container == current_node {
633 range.end.offset += length;
634 range.end.container = Dom::from_ref(node);
635 self.selection_boundaries_changed();
636 }
637 if range.start.container == parent && range.start.offset == current_node_index() {
641 range.start.container = Dom::from_ref(node);
642 range.start.offset = length;
643 self.selection_boundaries_changed();
644 }
645 if range.end.container == parent && range.end.offset == current_node_index() {
648 range.end.container = Dom::from_ref(node);
649 range.end.offset = length;
650 self.selection_boundaries_changed();
651 }
652 }
653
654 pub(crate) fn replace_data_steps(
657 &self,
658 node: &Node,
659 offset: u32,
660 removed_code_units: u32,
661 added_code_units: &mut dyn FnMut() -> u32,
662 ) {
663 let mut range_borrow = self.range.borrow_mut();
664 let Some(range) = &mut *range_borrow else {
665 return;
666 };
667 let start_container = &range.start.container;
671 let start_offset = range.start.offset;
672 if &**start_container == node &&
673 start_offset > offset &&
674 start_offset <= offset + removed_code_units
675 {
676 range.start.offset = offset;
677 self.selection_boundaries_changed();
678 }
679 let end_container = &range.end.container;
683 let end_offset = range.end.offset;
684 if &**end_container == node &&
685 end_offset > offset &&
686 end_offset <= offset + removed_code_units
687 {
688 range.end.offset = offset;
689 self.selection_boundaries_changed();
690 }
691 if &**start_container == node && start_offset > offset + removed_code_units {
695 range.start.offset = start_offset + added_code_units() - removed_code_units;
696 self.selection_boundaries_changed();
697 }
698 if &**end_container == node && end_offset > offset + removed_code_units {
702 range.end.offset = end_offset + added_code_units() - removed_code_units;
703 self.selection_boundaries_changed();
704 }
705 }
706
707 pub(crate) fn text_split_steps(
710 &self,
711 node: &Node,
712 offset: u32,
713 parent_node: &Node,
714 new_node: &Node,
715 ) {
716 let mut range_borrow = self.range.borrow_mut();
717 let Some(range) = &mut *range_borrow else {
718 return;
719 };
720 if range.start.container == node && range.start.offset > offset {
724 range.start.container = Dom::from_ref(new_node);
725 range.start.offset -= offset;
726 self.selection_boundaries_changed();
727 }
728 if range.end.container == node && range.end.offset > offset {
731 range.end.container = Dom::from_ref(new_node);
732 range.end.offset -= offset;
733 self.selection_boundaries_changed();
734 }
735 let node_index = LazyCell::new(|| node.index());
738 if range.start.container == parent_node && range.start.offset == *node_index + 1 {
739 range.start.offset += 1;
740 self.selection_boundaries_changed();
741 }
742 if range.end.container == parent_node && range.end.offset == *node_index + 1 {
745 range.end.offset += 1;
746 self.selection_boundaries_changed();
747 }
748 }
749
750 pub(crate) fn collapse_to_dom_position(
751 &self,
752 cx: &mut JSContext,
753 boundary: &RootedSelectionBoundary,
754 ) {
755 let _ = self.Collapse(cx, Some(&boundary.container), boundary.offset);
756 }
757
758 pub(crate) fn collapse_or_extend_to_dom_position(
759 &self,
760 cx: &mut JSContext,
761 boundary: &RootedSelectionBoundary,
762 ) {
763 let is_anchor = self
764 .composed_anchor_position()
765 .is_some_and(|anchor| anchor == *boundary);
766
767 if self.range.borrow().is_none() || is_anchor {
768 let _ = self.Collapse(cx, Some(&boundary.container), boundary.offset);
769 } else {
770 let _ = self.Extend(cx, &boundary.container, boundary.offset);
771 }
772 }
773
774 pub(crate) fn set_anchor_and_focus(
778 &self,
779 cx: &mut JSContext,
780 anchor: &RootedSelectionBoundary,
781 focus: &RootedSelectionBoundary,
782 ) {
783 let _ = self.SetBaseAndExtent(
784 cx,
785 &anchor.container,
786 anchor.offset,
787 &focus.container,
788 focus.offset,
789 );
790 }
791}
792
793impl SelectionMethods<crate::DomTypeHolder> for Selection {
794 fn GetAnchorNode(&self, cx: &mut JSContext) -> Option<DomRoot<Node>> {
796 self.live_anchor_node(cx)
802 }
803
804 fn AnchorOffset(&self, cx: &mut JSContext) -> u32 {
806 self.live_anchor_offset(cx)
812 }
813
814 fn GetFocusNode(&self, cx: &mut JSContext) -> Option<DomRoot<Node>> {
816 self.live_focus_node(cx)
822 }
823
824 fn FocusOffset(&self, cx: &mut JSContext) -> u32 {
826 self.live_focus_offset(cx)
832 }
833
834 fn IsCollapsed(&self, cx: &mut JSContext) -> bool {
836 self.active_range(cx).is_none_or(|range| range.collapsed())
839 }
840
841 fn RangeCount(&self) -> u32 {
843 if self.range.borrow().is_none() {
849 return 0;
850 }
851 1
852 }
853
854 fn Type(&self) -> DOMString {
856 let range = self.range.borrow();
863 let Some(range) = range.as_ref() else {
864 return DOMString::from_static("None");
865 };
866 if range.collapsed() {
867 DOMString::from_static("Caret")
868 } else {
869 DOMString::from_static("Range")
870 }
871 }
872
873 fn Direction(&self) -> DOMString {
875 match self.direction.get() {
879 Direction::Directionless => DOMString::from_static("none"),
880 Direction::Forwards => DOMString::from_static("forward"),
881 Direction::Backwards => DOMString::from_static("backward"),
882 }
883 }
884
885 fn GetRangeAt(&self, cx: &mut JSContext, index: u32) -> Fallible<DomRoot<Range>> {
887 if index != 0 {
894 return Err(Error::IndexSize(Some("Index must be zero".into())));
895 }
896
897 let Some(range) = self.active_range(cx) else {
898 return Err(Error::IndexSize(Some("Selection is empty".into())));
899 };
900
901 Ok(range)
902 }
903
904 fn AddRange(&self, no_gc: &NoGC, range: &Range) {
906 if !self.is_in_composed_tree_of_document_and_is_not_ua_widget(&range.start_container()) {
909 return;
910 }
911
912 if self.RangeCount() != 0 {
914 return;
915 }
916
917 self.set_live_range(no_gc, Some(range));
919
920 self.direction.set(Direction::Forwards);
922 }
923
924 fn RemoveRange(&self, no_gc: &NoGC, range: &Range) -> ErrorResult {
926 if let Some(own_range) = self.live_range.get() &&
929 &*own_range == range
930 {
931 self.set_range(no_gc, None);
932 return Ok(());
933 }
934 Err(Error::NotFound(None))
935 }
936
937 fn RemoveAllRanges(&self, no_gc: &NoGC) {
939 self.set_range(no_gc, None);
942 }
943
944 fn Empty(&self, no_gc: &NoGC) {
946 self.RemoveAllRanges(no_gc);
948 }
949
950 fn GetComposedRanges(
952 &self,
953 cx: &mut JSContext,
954 options: &GetComposedRangesOptions,
955 ) -> Vec<DomRoot<StaticRange>> {
956 let borrowed_range = self.range.borrow();
958 let Some(range) = borrowed_range.as_ref() else {
959 return Vec::new();
960 };
961
962 let mut start_node = range.start.container.as_rooted();
965 let mut start_offset = range.start.offset;
966
967 let is_ancestor_of_provided_shadow_roots = |shadow_root: &ShadowRoot| {
968 let shadow_root_node = shadow_root.upcast::<Node>();
969 options.shadowRoots.iter().any(|option_shadow_root| {
970 shadow_root_node
971 .is_shadow_including_inclusive_ancestor_of(option_shadow_root.upcast())
972 })
973 };
974
975 while let Some(containing_shadow_root) = start_node.containing_shadow_root() &&
979 !is_ancestor_of_provided_shadow_roots(&containing_shadow_root)
980 {
981 let host = DomRoot::upcast::<Node>(containing_shadow_root.Host());
983 start_offset = host.index();
984
985 let Some(new_start_node) = host.GetParentNode() else {
989 return Vec::new();
990 };
991 start_node = new_start_node;
992 }
993
994 let mut end_node = range.end.container.as_rooted();
997 let mut end_offset = range.end.offset;
998
999 while let Some(containing_shadow_root) = end_node.containing_shadow_root() &&
1003 !is_ancestor_of_provided_shadow_roots(&containing_shadow_root)
1004 {
1005 let host = DomRoot::upcast::<Node>(containing_shadow_root.Host());
1007 end_offset = host.index() + 1;
1008
1009 let Some(new_end_node) = host.GetParentNode() else {
1013 return Vec::new();
1014 };
1015 end_node = new_end_node;
1016 }
1017
1018 drop(borrowed_range);
1019
1020 vec![DomRoot::from_ref(&StaticRange::new(
1024 cx,
1025 &self.document,
1026 &start_node,
1027 start_offset,
1028 &end_node,
1029 end_offset,
1030 ))]
1031 }
1032
1033 fn Collapse(&self, cx: &mut JSContext, node: Option<&Node>, offset: u32) -> ErrorResult {
1035 let Some(node) = node else {
1038 self.set_range(cx.no_gc(), None);
1039 return Ok(());
1040 };
1041
1042 if node.is_doctype() {
1045 return Err(Error::InvalidNodeType(None));
1046 }
1047
1048 if offset > node.len() {
1051 return Err(Error::IndexSize(None));
1052 }
1053
1054 if !self.is_in_composed_tree_of_document_and_is_not_ua_widget(node) {
1057 return Ok(());
1058 }
1059
1060 self.set_range(
1064 cx.no_gc(),
1065 Some(SelectionRange::collapsed_at(SelectionBoundary::new(
1066 node, offset,
1067 ))),
1068 );
1069
1070 self.direction.set(Direction::Forwards);
1072
1073 Ok(())
1074 }
1075
1076 fn SetPosition(&self, cx: &mut JSContext, node: Option<&Node>, offset: u32) -> ErrorResult {
1078 self.Collapse(cx, node, offset)
1080 }
1081
1082 fn CollapseToStart(&self, cx: &mut JSContext) -> ErrorResult {
1084 let Some((start_container, start_offset)) = self
1089 .range
1090 .borrow()
1091 .as_ref()
1092 .map(|range| (range.start.container.as_rooted(), range.start.offset))
1093 else {
1094 return Err(Error::InvalidState(None));
1095 };
1096 self.Collapse(cx, Some(&*start_container), start_offset)
1097 }
1098
1099 fn CollapseToEnd(&self, cx: &mut JSContext) -> ErrorResult {
1101 let Some((end_container, end_offset)) = self
1105 .range
1106 .borrow()
1107 .as_ref()
1108 .map(|range| (range.end.container.as_rooted(), range.end.offset))
1109 else {
1110 return Err(Error::InvalidState(None));
1111 };
1112 self.Collapse(cx, Some(&*end_container), end_offset)
1113 }
1114
1115 fn Extend(&self, cx: &mut JSContext, node: &Node, offset: u32) -> ErrorResult {
1117 if !self.is_in_composed_tree_of_document_and_is_not_ua_widget(node) {
1120 return Ok(());
1121 }
1122
1123 let range_borrow = self.range.borrow();
1125 let Some(range) = range_borrow.as_ref() else {
1126 return Err(Error::InvalidState(None));
1127 };
1128
1129 if node.is_doctype() {
1132 return Err(Error::InvalidNodeType(None));
1133 }
1134
1135 if offset > node.len() {
1138 return Err(Error::IndexSize(None));
1139 }
1140
1141 let old_anchor = self
1146 .composed_anchor_position()
1147 .expect("has range, therefore has anchor node");
1148
1149 let direction;
1152
1153 let in_different_roots = !nodes_have_same_shadow_root(&range.start.container, node);
1156 drop(range_borrow);
1157
1158 if in_different_roots {
1159 self.set_range(
1160 cx.no_gc(),
1161 Some(SelectionRange::collapsed_at(SelectionBoundary::new(
1162 node, offset,
1163 ))),
1164 );
1165 direction = Direction::Forwards;
1166 } else {
1167 let is_old_anchor_before_or_equal = matches!(
1168 compare_shadow_including_dom_positions(
1169 cx.no_gc(),
1170 &old_anchor.container,
1171 old_anchor.offset,
1172 node,
1173 offset
1174 ),
1175 Ordering::Less | Ordering::Equal
1176 );
1177 if is_old_anchor_before_or_equal {
1178 self.set_range(
1181 cx.no_gc(),
1182 Some(SelectionRange::new(
1183 SelectionBoundary::new(&old_anchor.container, old_anchor.offset),
1184 SelectionBoundary::new(node, offset),
1185 )),
1186 );
1187 direction = Direction::Forwards;
1188 } else {
1189 self.set_range(
1192 cx.no_gc(),
1193 Some(SelectionRange::new(
1194 SelectionBoundary::new(node, offset),
1195 SelectionBoundary::new(&old_anchor.container, old_anchor.offset),
1196 )),
1197 );
1198 direction = Direction::Backwards;
1199 }
1200 }
1201
1202 self.direction.set(direction);
1208
1209 Ok(())
1210 }
1211
1212 fn SetBaseAndExtent(
1214 &self,
1215 cx: &mut JSContext,
1216 anchor_node: &Node,
1217 anchor_offset: u32,
1218 focus_node: &Node,
1219 focus_offset: u32,
1220 ) -> ErrorResult {
1221 if anchor_node.is_doctype() || focus_node.is_doctype() {
1224 return Err(Error::InvalidNodeType(None));
1225 }
1226
1227 if anchor_offset > anchor_node.len() || focus_offset > focus_node.len() {
1231 return Err(Error::IndexSize(None));
1232 }
1233
1234 if !self.is_in_composed_tree_of_document_and_is_not_ua_widget(anchor_node) ||
1237 !self.is_in_composed_tree_of_document_and_is_not_ua_widget(focus_node)
1238 {
1239 return Ok(());
1240 }
1241
1242 let ordering = compare_shadow_including_dom_positions(
1254 cx.no_gc(),
1255 anchor_node,
1256 anchor_offset,
1257 focus_node,
1258 focus_offset,
1259 );
1260 if ordering == Ordering::Less {
1261 self.set_range(
1262 cx.no_gc(),
1263 Some(SelectionRange::new(
1264 SelectionBoundary::new(anchor_node, anchor_offset),
1265 SelectionBoundary::new(focus_node, focus_offset),
1266 )),
1267 );
1268 } else {
1269 self.set_range(
1270 cx.no_gc(),
1271 Some(SelectionRange::new(
1272 SelectionBoundary::new(focus_node, focus_offset),
1273 SelectionBoundary::new(anchor_node, anchor_offset),
1274 )),
1275 );
1276 };
1277
1278 if ordering == Ordering::Greater {
1284 self.direction.set(Direction::Backwards);
1285 } else {
1286 self.direction.set(Direction::Forwards);
1287 }
1288
1289 Ok(())
1290 }
1291
1292 fn SelectAllChildren(&self, cx: &mut JSContext, node: &Node) -> ErrorResult {
1294 if node.is_doctype() {
1297 return Err(Error::InvalidNodeType(None));
1298 }
1299
1300 if !self.is_in_composed_tree_of_document_and_is_not_ua_widget(node) {
1303 return Ok(());
1304 }
1305
1306 let child_count = node.children_count();
1308
1309 self.set_range(
1313 cx.no_gc(),
1314 Some(SelectionRange::new(
1315 SelectionBoundary::new(node, 0),
1316 SelectionBoundary::new(node, child_count),
1317 )),
1318 );
1319
1320 self.direction.set(Direction::Forwards);
1322
1323 Ok(())
1324 }
1325
1326 fn DeleteFromDocument(&self, cx: &mut JSContext) -> ErrorResult {
1328 let Some(active_range) = self
1332 .active_range(cx)
1333 .filter(|range| range.start_and_end_are_in_document_tree())
1334 else {
1335 return Ok(());
1336 };
1337
1338 active_range.DeleteContents(cx)
1339 }
1340
1341 fn ContainsNode(&self, no_gc: &NoGC, node: &Node, allow_partial_containment: bool) -> bool {
1343 if !self.is_in_composed_tree_of_document_and_is_not_ua_widget(node) {
1356 return false;
1357 }
1358 let range = self.range.borrow();
1359 let Some(range) = range.as_ref() else {
1360 return false;
1361 };
1362 let start_node = &*range.start.container;
1363 if !self.is_in_composed_tree_of_document_and_is_not_ua_widget(start_node) {
1364 return false;
1365 }
1366 let end_node = &*range.end.container;
1367
1368 let first_offset = 0;
1369 let last_offset = node.len();
1370 let (compare_start_to, compare_end_to) = if allow_partial_containment {
1371 (last_offset, first_offset)
1372 } else {
1373 (first_offset, last_offset)
1374 };
1375
1376 if !node.is_in_flat_tree(no_gc) {
1381 return false;
1382 }
1383
1384 matches!(
1385 compare_shadow_including_dom_positions(
1386 no_gc,
1387 start_node,
1388 range.start.offset,
1389 node,
1390 compare_start_to
1391 ),
1392 Ordering::Less | Ordering::Equal
1393 ) && matches!(
1394 compare_shadow_including_dom_positions(
1395 no_gc,
1396 end_node,
1397 range.end.offset,
1398 node,
1399 compare_end_to
1400 ),
1401 Ordering::Greater | Ordering::Equal
1402 )
1403 }
1404
1405 fn Stringifier(&self, cx: &mut JSContext) -> DOMString {
1407 let Some(visible_selection) =
1413 FlatTreeSelection::from_selection_if_renderable(cx.no_gc(), self)
1414 else {
1415 return DOMString::new();
1416 };
1417
1418 self.document.window().layout_reflow(QueryMsg::StyleQuery);
1420
1421 let mut user_select_cache = Default::default();
1422 let mut string = DOMString::new();
1423 for node in visible_selection.traversal() {
1424 let Some(character_data) = node.downcast::<CharacterData>() else {
1425 continue;
1426 };
1427
1428 if node.used_user_select(cx.no_gc(), &mut user_select_cache) == UsedUserSelect::None {
1429 continue;
1430 }
1431
1432 let range = visible_selection.range_for_character_data(character_data);
1433 let Some(text) = character_data.rendered_text(range) else {
1434 continue;
1435 };
1436 string.push_str(&text);
1437 }
1438
1439 string
1440 }
1441}
1442
1443impl<'dom> LayoutDom<'dom, Selection> {
1444 #[expect(unsafe_code)]
1445 pub(crate) fn range_for_layout(&self) -> &Option<SelectionRange> {
1446 unsafe { self.unsafe_get().visible_range.borrow_for_layout() }
1447 }
1448}
1449
1450enum FlatTreeNodePosition {
1451 Before(DomRoot<Node>),
1452 Inside(DomRoot<Node>),
1453 After(DomRoot<Node>),
1454}
1455
1456impl FlatTreeNodePosition {
1457 fn node(&self) -> &Node {
1458 match self {
1459 FlatTreeNodePosition::Before(node) => node,
1460 FlatTreeNodePosition::Inside(node) => node,
1461 FlatTreeNodePosition::After(node) => node,
1462 }
1463 }
1464}
1465
1466fn position_in_flat_tree_for_selection(
1470 no_gc: &NoGC,
1471 boundary: &FlatTreeBoundary,
1472) -> FlatTreeNodePosition {
1473 if boundary.container.is::<CharacterData>() {
1474 return FlatTreeNodePosition::Inside(boundary.container.as_rooted());
1475 }
1476
1477 let shadow_host_or_node = |node: &Node| {
1478 boundary
1479 .container
1480 .downcast::<ShadowRoot>()
1481 .map(|shadow_root| DomRoot::upcast(shadow_root.Host()))
1482 .unwrap_or(DomRoot::from_ref(node))
1483 };
1484
1485 if let Some(child) = boundary.container.children().nth(boundary.offset as usize) {
1486 if let FlatTreeParent::Parent(_) = child.parent_in_flat_tree(no_gc) {
1487 return FlatTreeNodePosition::Before(child);
1488 }
1489 } else if let Some(last_child) = boundary.container.GetLastChild() &&
1490 let FlatTreeParent::Parent(_) = last_child.parent_in_flat_tree(no_gc)
1491 {
1492 return FlatTreeNodePosition::After(shadow_host_or_node(&boundary.container.as_rooted()));
1493 }
1494
1495 FlatTreeNodePosition::Inside(shadow_host_or_node(&boundary.container.as_rooted()))
1498}
1499
1500impl Node {
1501 pub(crate) fn to_sibling_or_utf16_offset(&self, offset: Utf32CodeUnitsOrNodeOffset) -> u32 {
1504 if let Some(character_data) = self.downcast::<CharacterData>() {
1505 offset
1507 .to_utf16_code_units_in(AssumeUnder4GB, &character_data.data())
1508 .0
1509 } else {
1510 offset.0
1511 }
1512 }
1513
1514 pub(crate) fn to_sibling_or_utf32_offset(&self, offset: u32) -> Utf32CodeUnitsOrNodeOffset {
1517 Utf32CodeUnitsOrNodeOffset(
1518 if let Some(character_data) = self.downcast::<CharacterData>() {
1519 Utf16CodeUnits(offset)
1521 .to_utf32_code_units_in(&character_data.data())
1522 .0
1523 } else {
1524 offset
1525 },
1526 )
1527 }
1528}
1529
1530bitflags! {
1531 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
1532 pub(crate) struct SelectionLiveRangeNotification: u8 {
1533 const Start = 1 << 0;
1534 const End = 1 << 1;
1535 }
1536}
1537
1538fn nodes_have_same_shadow_root(a: &Node, b: &Node) -> bool {
1539 a.is_connected() && b.is_connected() && a.containing_shadow_root() == b.containing_shadow_root()
1540}
1541
1542fn project_into_shared_tree<'a>(
1549 no_gc: &'a NoGC,
1550 container_a: &Node,
1551 mut offset_a: u32,
1552 target: Option<&Node>,
1553) -> (UnrootedDom<'a, Node>, u32) {
1554 let mut start_node = UnrootedDom::from_ref(container_a, no_gc);
1555 while let Some(containing_shadow_root) = start_node.containing_shadow_root_unrooted(no_gc) &&
1556 target.is_none_or(|target| {
1557 !containing_shadow_root
1558 .upcast::<Node>()
1559 .is_shadow_including_inclusive_ancestor_of(target)
1560 })
1561 {
1562 let host = DomRoot::upcast::<Node>(containing_shadow_root.Host());
1563 offset_a = host.index();
1564 start_node = host
1565 .get_parent_node_unrooted(no_gc)
1566 .expect("Should only be called on nodes in the same tree");
1567 }
1568
1569 (start_node, offset_a)
1570}
1571
1572pub(crate) fn compare_shadow_including_dom_positions(
1576 no_gc: &NoGC,
1577 container_a: &Node,
1578 offset_a: u32,
1579 container_b: &Node,
1580 offset_b: u32,
1581) -> Ordering {
1582 let (projected_container_a, projected_offset_a) =
1583 project_into_shared_tree(no_gc, container_a, offset_a, Some(container_b));
1584 let (projected_container_b, projected_offset_b) =
1585 project_into_shared_tree(no_gc, container_b, offset_b, Some(container_a));
1586
1587 let a_was_projected = &**projected_container_a != container_a;
1588 let b_was_projected = &**projected_container_b != container_b;
1589
1590 match bp_position(
1591 no_gc,
1592 &projected_container_a,
1593 projected_offset_a,
1594 &projected_container_b,
1595 projected_offset_b,
1596 ) {
1597 Ordering::Equal if a_was_projected && !b_was_projected => Ordering::Greater,
1598 Ordering::Equal if !a_was_projected && b_was_projected => Ordering::Less,
1599 ordering => ordering,
1600 }
1601}
1602
1603struct FlatTreeBoundary<'no_gc> {
1604 container: UnrootedDom<'no_gc, Node>,
1605 offset: u32,
1606}
1607
1608struct FlatTreeSelection<'no_gc> {
1609 no_gc: &'no_gc NoGC,
1610 start: FlatTreeBoundary<'no_gc>,
1611 end: FlatTreeBoundary<'no_gc>,
1612}
1613
1614impl<'no_gc> FlatTreeSelection<'no_gc> {
1615 fn from_selection_if_renderable(no_gc: &'no_gc NoGC, selection: &Selection) -> Option<Self> {
1619 let range = selection.range.borrow();
1620 let range = range.as_ref()?;
1621
1622 let mut start_container = range.start.container.as_unrooted(no_gc);
1623 let mut start_offset = range.start.offset;
1624 let mut end_container = range.end.container.as_unrooted(no_gc);
1625 let mut end_offset = range.end.offset;
1626
1627 if !start_container.is_in_flat_tree(no_gc) {
1628 return None;
1629 }
1630 if !end_container.is_in_flat_tree(no_gc) {
1631 return None;
1632 }
1633
1634 let ordering = compare_dom_positions::<FlatTreeForSelectionNoGcTraversal>(
1638 no_gc,
1639 &range.start.container,
1640 range.start.offset,
1641 &range.end.container,
1642 range.end.offset,
1643 )
1644 .0?;
1645
1646 if ordering == Ordering::Greater {
1649 std::mem::swap(&mut start_container, &mut end_container);
1650 std::mem::swap(&mut start_offset, &mut end_offset);
1651 }
1652
1653 Some(FlatTreeSelection {
1654 no_gc,
1655 start: FlatTreeBoundary {
1656 container: start_container,
1657 offset: start_offset,
1658 },
1659 end: FlatTreeBoundary {
1660 container: end_container,
1661 offset: end_offset,
1662 },
1663 })
1664 }
1665
1666 fn range_for_character_data(&self, character_data: &CharacterData) -> RangeAny<Utf32CodeUnits> {
1667 let text = character_data.data();
1668 let node: &Node = character_data.upcast();
1669 RangeAny::new(
1670 (node == &**self.start.container)
1671 .then(|| Utf16CodeUnits(self.start.offset).to_utf32_code_units_in(&text)),
1672 (node == &**self.end.container)
1673 .then(|| Utf16CodeUnits(self.end.offset).to_utf32_code_units_in(&text)),
1674 )
1675 }
1676
1677 fn traversal(&self) -> VisibleSelectionTraversal<'no_gc> {
1678 let start_position = position_in_flat_tree_for_selection(self.no_gc, &self.start);
1679 let end_position = position_in_flat_tree_for_selection(self.no_gc, &self.end);
1680 VisibleSelectionTraversal {
1681 following: start_position
1682 .node()
1683 .following_flat_tree_nodes_unrooted(self.no_gc),
1684 start: start_position,
1685 end: end_position,
1686 finished: false,
1687 skip_subtree: false,
1688 }
1689 }
1690}
1691
1692struct VisibleSelectionTraversal<'no_gc> {
1693 following: UnrootedFollowingFlatTreeNodesTraversal<'no_gc>,
1694 start: FlatTreeNodePosition,
1695 end: FlatTreeNodePosition,
1696 finished: bool,
1697 skip_subtree: bool,
1698}
1699
1700impl<'no_gc> Iterator for VisibleSelectionTraversal<'no_gc> {
1701 type Item = UnrootedDom<'no_gc, Node>;
1702
1703 fn next(&mut self) -> Option<Self::Item> {
1704 while !self.finished {
1705 let following = if std::mem::take(&mut self.skip_subtree) {
1706 self.following.next_skipping_subtree()?
1707 } else {
1708 self.following.next()?
1709 };
1710
1711 match following {
1712 PrePostIteration::Enter(node) => {
1713 if &**node == self.end.node() &&
1716 matches!(self.end, FlatTreeNodePosition::Before(_))
1717 {
1718 self.finished = true;
1719 break;
1720 }
1721 if &**node == self.start.node() &&
1724 matches!(self.start, FlatTreeNodePosition::After(_))
1725 {
1726 self.skip_subtree = true;
1727 }
1728 return Some(node);
1729 },
1730 PrePostIteration::Leave(node) => {
1731 self.finished = &**node == self.end.node();
1732 },
1733 }
1734 }
1735
1736 None
1737 }
1738}
1739
1740struct VisibleSelectionFlagUpdate<'no_gc> {
1741 no_gc: &'no_gc NoGC,
1742 previously_flagged_nodes: FxHashSet<UnrootedDom<'no_gc, Node>>,
1748 used_user_select_cache: FxHashMap<UnrootedDom<'no_gc, Node>, UsedUserSelect>,
1750 needs_new_display_list: bool,
1752}
1753
1754impl<'no_gc> VisibleSelectionFlagUpdate<'no_gc> {
1755 fn run(
1756 no_gc: &'no_gc NoGC,
1757 previously_flagged_nodes: FxHashSet<UnrootedDom<'no_gc, Node>>,
1758 flat_tree_selection: Option<&FlatTreeSelection<'_>>,
1759 document: &Document,
1760 ) {
1761 let mut update = Self {
1762 no_gc,
1763 previously_flagged_nodes,
1764 used_user_select_cache: Default::default(),
1765 needs_new_display_list: false,
1766 };
1767
1768 if let Some(flat_tree_selection) = flat_tree_selection {
1769 let traversal = flat_tree_selection.traversal();
1770 for ancestor in traversal
1771 .start
1772 .node()
1773 .ancestors_in_flat_tree_unrooted(no_gc)
1774 {
1775 update.set(&ancestor, flat_tree_selection);
1776 }
1777 for node in traversal {
1778 update.set(&node, flat_tree_selection);
1779 }
1780 }
1781
1782 update.finish(document);
1783 }
1784
1785 fn set(&mut self, node: &UnrootedDom<'no_gc, Node>, flat_tree_selection: &FlatTreeSelection) {
1786 if !node.get_flag(NodeFlags::OVERLAPS_DOCUMENT_SELECTION) {
1787 node.set_flag(NodeFlags::OVERLAPS_DOCUMENT_SELECTION, true);
1788 debug_assert!(!self.previously_flagged_nodes.contains(node));
1789 } else {
1790 self.previously_flagged_nodes.remove(node);
1791 }
1792
1793 let user_select = node.used_user_select(self.no_gc, &mut self.used_user_select_cache);
1798 let inhibited = user_select == UsedUserSelect::None;
1799 node.set_flag(NodeFlags::SELECTION_INHIBITED, inhibited);
1800
1801 self.set_node_selection(node, (!inhibited).then_some(flat_tree_selection));
1802 }
1803
1804 fn clear(&mut self, node: &Node) {
1805 node.set_flag(NodeFlags::OVERLAPS_DOCUMENT_SELECTION, false);
1806 node.set_flag(NodeFlags::SELECTION_INHIBITED, false);
1807 self.set_node_selection(node, None);
1808 }
1809
1810 fn set_character_data_selection(
1811 &mut self,
1812 character_data: &CharacterData,
1813 range: Option<RangeAny<Utf32CodeUnits>>,
1814 ) {
1815 if character_data.set_text_run_selection(range) {
1816 self.needs_new_display_list = true;
1817 } else {
1818 character_data
1823 .upcast::<Node>()
1824 .dirty(self.no_gc, NodeDamage::ContentOrHeritage);
1825 }
1826 }
1827
1828 fn set_node_selection(&mut self, node: &Node, flat_tree_selection: Option<&FlatTreeSelection>) {
1829 if let Some(character_data) = node.downcast::<CharacterData>() {
1830 let range = flat_tree_selection.map(|flat_tree_selection| {
1831 flat_tree_selection.range_for_character_data(character_data)
1832 });
1833 self.set_character_data_selection(character_data, range);
1834 } else if node.set_element_selection(flat_tree_selection.is_some()) {
1835 self.needs_new_display_list = true;
1836 }
1837 }
1838
1839 fn finish(mut self, document: &Document) {
1840 for node in std::mem::take(&mut self.previously_flagged_nodes) {
1841 self.clear(&node);
1842 }
1843 if self.needs_new_display_list {
1844 document.window().layout().set_needs_new_display_list();
1845 }
1846 }
1847}