script/dom/
range.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::cell::RefCell;
6use std::cmp::{Ordering, PartialOrd};
7use std::iter;
8
9use app_units::Au;
10use dom_struct::dom_struct;
11use euclid::Rect;
12use js::jsapi::JSTracer;
13use js::rust::HandleObject;
14use style_traits::CSSPixel;
15
16use crate::dom::abstractrange::{AbstractRange, BoundaryPoint, bp_position};
17use crate::dom::bindings::cell::DomRefCell;
18use crate::dom::bindings::codegen::Bindings::AbstractRangeBinding::AbstractRangeMethods;
19use crate::dom::bindings::codegen::Bindings::CharacterDataBinding::CharacterDataMethods;
20use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
21use crate::dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods;
22use crate::dom::bindings::codegen::Bindings::RangeBinding::{RangeConstants, RangeMethods};
23use crate::dom::bindings::codegen::Bindings::TextBinding::TextMethods;
24use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
25use crate::dom::bindings::codegen::UnionTypes::TrustedHTMLOrString;
26use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
27use crate::dom::bindings::inheritance::{Castable, CharacterDataTypeId, NodeTypeId};
28use crate::dom::bindings::reflector::reflect_dom_object_with_proto;
29use crate::dom::bindings::root::{Dom, DomRoot};
30use crate::dom::bindings::str::DOMString;
31use crate::dom::bindings::trace::JSTraceable;
32use crate::dom::bindings::weakref::{WeakRef, WeakRefVec};
33use crate::dom::characterdata::CharacterData;
34use crate::dom::document::Document;
35use crate::dom::documentfragment::DocumentFragment;
36use crate::dom::domrect::DOMRect;
37use crate::dom::domrectlist::DOMRectList;
38use crate::dom::element::Element;
39use crate::dom::html::htmlscriptelement::HTMLScriptElement;
40use crate::dom::node::{Node, NodeTraits, ShadowIncluding, UnbindContext};
41use crate::dom::selection::Selection;
42use crate::dom::text::Text;
43use crate::dom::trustedhtml::TrustedHTML;
44use crate::dom::window::Window;
45use crate::script_runtime::CanGc;
46
47#[dom_struct]
48pub(crate) struct Range {
49    abstract_range: AbstractRange,
50    // A range that belongs to a Selection needs to know about it
51    // so selectionchange can fire when the range changes.
52    // A range shouldn't belong to more than one Selection at a time,
53    // but from the spec as of Feb 1 2020 I can't rule out a corner case like:
54    // * Select a range R in document A, from node X to Y
55    // * Insert everything from X to Y into document B
56    // * Set B's selection's range to R
57    // which leaves R technically, and observably, associated with A even though
58    // it will fail the same-root-node check on many of A's selection's methods.
59    associated_selections: DomRefCell<Vec<Dom<Selection>>>,
60}
61
62struct ContainedChildren {
63    first_partially_contained_child: Option<DomRoot<Node>>,
64    last_partially_contained_child: Option<DomRoot<Node>>,
65    contained_children: Vec<DomRoot<Node>>,
66}
67
68impl Range {
69    fn new_inherited(
70        start_container: &Node,
71        start_offset: u32,
72        end_container: &Node,
73        end_offset: u32,
74    ) -> Range {
75        debug_assert!(start_offset <= start_container.len());
76        debug_assert!(end_offset <= end_container.len());
77        Range {
78            abstract_range: AbstractRange::new_inherited(
79                start_container,
80                start_offset,
81                end_container,
82                end_offset,
83            ),
84            associated_selections: DomRefCell::new(vec![]),
85        }
86    }
87
88    pub(crate) fn new_with_doc(
89        document: &Document,
90        proto: Option<HandleObject>,
91        can_gc: CanGc,
92    ) -> DomRoot<Range> {
93        let root = document.upcast();
94        Range::new_with_proto(document, proto, root, 0, root, 0, can_gc)
95    }
96
97    pub(crate) fn new(
98        document: &Document,
99        start_container: &Node,
100        start_offset: u32,
101        end_container: &Node,
102        end_offset: u32,
103        can_gc: CanGc,
104    ) -> DomRoot<Range> {
105        Self::new_with_proto(
106            document,
107            None,
108            start_container,
109            start_offset,
110            end_container,
111            end_offset,
112            can_gc,
113        )
114    }
115
116    fn new_with_proto(
117        document: &Document,
118        proto: Option<HandleObject>,
119        start_container: &Node,
120        start_offset: u32,
121        end_container: &Node,
122        end_offset: u32,
123        can_gc: CanGc,
124    ) -> DomRoot<Range> {
125        let range = reflect_dom_object_with_proto(
126            Box::new(Range::new_inherited(
127                start_container,
128                start_offset,
129                end_container,
130                end_offset,
131            )),
132            document.window(),
133            proto,
134            can_gc,
135        );
136        start_container.ranges().push(WeakRef::new(&range));
137        if start_container != end_container {
138            end_container.ranges().push(WeakRef::new(&range));
139        }
140        range
141    }
142
143    /// <https://dom.spec.whatwg.org/#contained>
144    fn contains(&self, node: &Node) -> bool {
145        matches!(
146            (
147                bp_position(node, 0, &self.start_container(), self.start_offset()),
148                bp_position(node, node.len(), &self.end_container(), self.end_offset()),
149            ),
150            (Some(Ordering::Greater), Some(Ordering::Less))
151        )
152    }
153
154    /// <https://dom.spec.whatwg.org/#partially-contained>
155    fn partially_contains(&self, node: &Node) -> bool {
156        self.start_container()
157            .inclusive_ancestors(ShadowIncluding::No)
158            .any(|n| &*n == node) !=
159            self.end_container()
160                .inclusive_ancestors(ShadowIncluding::No)
161                .any(|n| &*n == node)
162    }
163
164    /// <https://dom.spec.whatwg.org/#concept-range-clone>
165    fn contained_children(&self) -> Fallible<ContainedChildren> {
166        let start_node = self.start_container();
167        let end_node = self.end_container();
168        // Steps 5-6.
169        let common_ancestor = self.CommonAncestorContainer();
170
171        let first_partially_contained_child = if start_node.is_inclusive_ancestor_of(&end_node) {
172            // Step 7.
173            None
174        } else {
175            // Step 8.
176            common_ancestor
177                .children()
178                .find(|node| Range::partially_contains(self, node))
179        };
180
181        let last_partially_contained_child = if end_node.is_inclusive_ancestor_of(&start_node) {
182            // Step 9.
183            None
184        } else {
185            // Step 10.
186            common_ancestor
187                .rev_children()
188                .find(|node| Range::partially_contains(self, node))
189        };
190
191        // Step 11.
192        let contained_children: Vec<DomRoot<Node>> = common_ancestor
193            .children()
194            .filter(|n| self.contains(n))
195            .collect();
196
197        // Step 12.
198        if contained_children.iter().any(|n| n.is_doctype()) {
199            return Err(Error::HierarchyRequest(None));
200        }
201
202        Ok(ContainedChildren {
203            first_partially_contained_child,
204            last_partially_contained_child,
205            contained_children,
206        })
207    }
208
209    /// <https://dom.spec.whatwg.org/#concept-range-bp-set>
210    fn set_start(&self, node: &Node, offset: u32) {
211        if self.start().node() != node || self.start_offset() != offset {
212            self.report_change();
213        }
214        if self.start().node() != node {
215            if self.start().node() == self.end().node() {
216                node.ranges().push(WeakRef::new(self));
217            } else if self.end().node() == node {
218                self.start_container().ranges().remove(self);
219            } else {
220                node.ranges()
221                    .push(self.start_container().ranges().remove(self));
222            }
223        }
224        self.start().set(node, offset);
225    }
226
227    /// <https://dom.spec.whatwg.org/#concept-range-bp-set>
228    fn set_end(&self, node: &Node, offset: u32) {
229        if self.end().node() != node || self.end_offset() != offset {
230            self.report_change();
231        }
232        if self.end().node() != node {
233            if self.end().node() == self.start().node() {
234                node.ranges().push(WeakRef::new(self));
235            } else if self.start().node() == node {
236                self.end_container().ranges().remove(self);
237            } else {
238                node.ranges()
239                    .push(self.end_container().ranges().remove(self));
240            }
241        }
242        self.end().set(node, offset);
243    }
244
245    /// <https://dom.spec.whatwg.org/#dom-range-comparepointnode-offset>
246    fn compare_point(&self, node: &Node, offset: u32) -> Fallible<Ordering> {
247        let start_node = self.start_container();
248        let start_node_root = start_node
249            .inclusive_ancestors(ShadowIncluding::No)
250            .last()
251            .unwrap();
252        let node_root = node
253            .inclusive_ancestors(ShadowIncluding::No)
254            .last()
255            .unwrap();
256        if start_node_root != node_root {
257            // Step 1.
258            return Err(Error::WrongDocument(None));
259        }
260        if node.is_doctype() {
261            // Step 2.
262            return Err(Error::InvalidNodeType(None));
263        }
264        if offset > node.len() {
265            // Step 3.
266            return Err(Error::IndexSize(None));
267        }
268        if let Ordering::Less = bp_position(node, offset, &start_node, self.start_offset()).unwrap()
269        {
270            // Step 4.
271            return Ok(Ordering::Less);
272        }
273        if let Ordering::Greater =
274            bp_position(node, offset, &self.end_container(), self.end_offset()).unwrap()
275        {
276            // Step 5.
277            return Ok(Ordering::Greater);
278        }
279        // Step 6.
280        Ok(Ordering::Equal)
281    }
282
283    pub(crate) fn associate_selection(&self, selection: &Selection) {
284        let mut selections = self.associated_selections.borrow_mut();
285        if !selections.iter().any(|s| &**s == selection) {
286            selections.push(Dom::from_ref(selection));
287        }
288    }
289
290    pub(crate) fn disassociate_selection(&self, selection: &Selection) {
291        self.associated_selections
292            .borrow_mut()
293            .retain(|s| &**s != selection);
294    }
295
296    fn report_change(&self) {
297        self.associated_selections
298            .borrow()
299            .iter()
300            .for_each(|s| s.queue_selectionchange_task());
301    }
302
303    fn abstract_range(&self) -> &AbstractRange {
304        &self.abstract_range
305    }
306
307    fn start(&self) -> &BoundaryPoint {
308        self.abstract_range().start()
309    }
310
311    fn end(&self) -> &BoundaryPoint {
312        self.abstract_range().end()
313    }
314
315    pub(crate) fn start_container(&self) -> DomRoot<Node> {
316        self.abstract_range().StartContainer()
317    }
318
319    pub(crate) fn start_offset(&self) -> u32 {
320        self.abstract_range().StartOffset()
321    }
322
323    pub(crate) fn end_container(&self) -> DomRoot<Node> {
324        self.abstract_range().EndContainer()
325    }
326
327    pub(crate) fn end_offset(&self) -> u32 {
328        self.abstract_range().EndOffset()
329    }
330
331    pub(crate) fn collapsed(&self) -> bool {
332        self.abstract_range().Collapsed()
333    }
334
335    fn client_rects(&self) -> impl Iterator<Item = Rect<Au, CSSPixel>> {
336        // FIXME: For text nodes that are only partially selected, this should return the client
337        // rect of the selected part, not the whole text node.
338        let start = self.start_container();
339        let end = self.end_container();
340        let document = start.owner_doc();
341        let end_clone = end.clone();
342        start
343            .following_nodes(document.upcast::<Node>())
344            .take_while(move |node| node != &end)
345            .chain(iter::once(end_clone))
346            .flat_map(move |node| node.border_boxes())
347    }
348
349    /// <https://dom.spec.whatwg.org/#concept-range-bp-set>
350    #[expect(clippy::neg_cmp_op_on_partial_ord)]
351    fn set_the_start_or_end(
352        &self,
353        node: &Node,
354        offset: u32,
355        start_or_end: StartOrEnd,
356    ) -> ErrorResult {
357        // Step 1. If node is a doctype, then throw an "InvalidNodeTypeError" DOMException.
358        if node.is_doctype() {
359            return Err(Error::InvalidNodeType(None));
360        }
361
362        // Step 2. If offset is greater than node’s length, then throw an "IndexSizeError" DOMException.
363        if offset > node.len() {
364            return Err(Error::IndexSize(None));
365        }
366
367        // Step 3. Let bp be the boundary point (node, offset).
368        // NOTE: We don't need this part.
369
370        match start_or_end {
371            // If these steps were invoked as "set the start"
372            StartOrEnd::Start => {
373                // Step 4.1  If range’s root is not equal to node’s root, or if bp is after the range’s end,
374                // set range’s end to bp.
375                // Step 4.2 Set range’s start to bp.
376                self.set_start(node, offset);
377                if !(self.start() <= self.end()) {
378                    self.set_end(node, offset);
379                }
380            },
381            // If these steps were invoked as "set the end"
382            StartOrEnd::End => {
383                // Step 4.1 If range’s root is not equal to node’s root, or if bp is before the range’s start,
384                // set range’s start to bp.
385                // Step 4.2 Set range’s end to bp.
386                self.set_end(node, offset);
387                if !(self.end() >= self.start()) {
388                    self.set_start(node, offset);
389                }
390            },
391        }
392
393        Ok(())
394    }
395}
396
397enum StartOrEnd {
398    Start,
399    End,
400}
401
402impl RangeMethods<crate::DomTypeHolder> for Range {
403    /// <https://dom.spec.whatwg.org/#dom-range>
404    fn Constructor(
405        window: &Window,
406        proto: Option<HandleObject>,
407        can_gc: CanGc,
408    ) -> Fallible<DomRoot<Range>> {
409        let document = window.Document();
410        Ok(Range::new_with_doc(&document, proto, can_gc))
411    }
412
413    /// <https://dom.spec.whatwg.org/#dom-range-commonancestorcontainer>
414    fn CommonAncestorContainer(&self) -> DomRoot<Node> {
415        self.end_container()
416            .common_ancestor(&self.start_container(), ShadowIncluding::No)
417            .expect("Couldn't find common ancestor container")
418    }
419
420    /// <https://dom.spec.whatwg.org/#dom-range-setstart>
421    fn SetStart(&self, node: &Node, offset: u32) -> ErrorResult {
422        self.set_the_start_or_end(node, offset, StartOrEnd::Start)
423    }
424
425    /// <https://dom.spec.whatwg.org/#dom-range-setend>
426    fn SetEnd(&self, node: &Node, offset: u32) -> ErrorResult {
427        self.set_the_start_or_end(node, offset, StartOrEnd::End)
428    }
429
430    /// <https://dom.spec.whatwg.org/#dom-range-setstartbefore>
431    fn SetStartBefore(&self, node: &Node) -> ErrorResult {
432        let parent = node.GetParentNode().ok_or(Error::InvalidNodeType(None))?;
433        self.SetStart(&parent, node.index())
434    }
435
436    /// <https://dom.spec.whatwg.org/#dom-range-setstartafter>
437    fn SetStartAfter(&self, node: &Node) -> ErrorResult {
438        let parent = node.GetParentNode().ok_or(Error::InvalidNodeType(None))?;
439        self.SetStart(&parent, node.index() + 1)
440    }
441
442    /// <https://dom.spec.whatwg.org/#dom-range-setendbefore>
443    fn SetEndBefore(&self, node: &Node) -> ErrorResult {
444        let parent = node.GetParentNode().ok_or(Error::InvalidNodeType(None))?;
445        self.SetEnd(&parent, node.index())
446    }
447
448    /// <https://dom.spec.whatwg.org/#dom-range-setendafter>
449    fn SetEndAfter(&self, node: &Node) -> ErrorResult {
450        let parent = node.GetParentNode().ok_or(Error::InvalidNodeType(None))?;
451        self.SetEnd(&parent, node.index() + 1)
452    }
453
454    /// <https://dom.spec.whatwg.org/#dom-range-collapse>
455    fn Collapse(&self, to_start: bool) {
456        if to_start {
457            self.set_end(&self.start_container(), self.start_offset());
458        } else {
459            self.set_start(&self.end_container(), self.end_offset());
460        }
461    }
462
463    /// <https://dom.spec.whatwg.org/#dom-range-selectnode>
464    fn SelectNode(&self, node: &Node) -> ErrorResult {
465        // Steps 1, 2.
466        let parent = node.GetParentNode().ok_or(Error::InvalidNodeType(None))?;
467        // Step 3.
468        let index = node.index();
469        // Step 4.
470        self.set_start(&parent, index);
471        // Step 5.
472        self.set_end(&parent, index + 1);
473        Ok(())
474    }
475
476    /// <https://dom.spec.whatwg.org/#dom-range-selectnodecontents>
477    fn SelectNodeContents(&self, node: &Node) -> ErrorResult {
478        if node.is_doctype() {
479            // Step 1.
480            return Err(Error::InvalidNodeType(None));
481        }
482        // Step 2.
483        let length = node.len();
484        // Step 3.
485        self.set_start(node, 0);
486        // Step 4.
487        self.set_end(node, length);
488        Ok(())
489    }
490
491    /// <https://dom.spec.whatwg.org/#dom-range-compareboundarypoints>
492    fn CompareBoundaryPoints(&self, how: u16, other: &Range) -> Fallible<i16> {
493        if how > RangeConstants::END_TO_START {
494            // Step 1.
495            return Err(Error::NotSupported(None));
496        }
497        let this_root = self
498            .start_container()
499            .inclusive_ancestors(ShadowIncluding::No)
500            .last()
501            .unwrap();
502        let other_root = other
503            .start_container()
504            .inclusive_ancestors(ShadowIncluding::No)
505            .last()
506            .unwrap();
507        if this_root != other_root {
508            // Step 2.
509            return Err(Error::WrongDocument(None));
510        }
511        // Step 3.
512        let (this_point, other_point) = match how {
513            RangeConstants::START_TO_START => (self.start(), other.start()),
514            RangeConstants::START_TO_END => (self.end(), other.start()),
515            RangeConstants::END_TO_END => (self.end(), other.end()),
516            RangeConstants::END_TO_START => (self.start(), other.end()),
517            _ => unreachable!(),
518        };
519        // step 4.
520        match this_point.partial_cmp(other_point).unwrap() {
521            Ordering::Less => Ok(-1),
522            Ordering::Equal => Ok(0),
523            Ordering::Greater => Ok(1),
524        }
525    }
526
527    /// <https://dom.spec.whatwg.org/#dom-range-clonerange>
528    fn CloneRange(&self, can_gc: CanGc) -> DomRoot<Range> {
529        let start_node = self.start_container();
530        let owner_doc = start_node.owner_doc();
531        Range::new(
532            &owner_doc,
533            &start_node,
534            self.start_offset(),
535            &self.end_container(),
536            self.end_offset(),
537            can_gc,
538        )
539    }
540
541    /// <https://dom.spec.whatwg.org/#dom-range-ispointinrange>
542    fn IsPointInRange(&self, node: &Node, offset: u32) -> Fallible<bool> {
543        match self.compare_point(node, offset) {
544            Ok(Ordering::Less) => Ok(false),
545            Ok(Ordering::Equal) => Ok(true),
546            Ok(Ordering::Greater) => Ok(false),
547            Err(Error::WrongDocument(None)) => {
548                // Step 2.
549                Ok(false)
550            },
551            Err(error) => Err(error),
552        }
553    }
554
555    /// <https://dom.spec.whatwg.org/#dom-range-comparepoint>
556    fn ComparePoint(&self, node: &Node, offset: u32) -> Fallible<i16> {
557        self.compare_point(node, offset).map(|order| match order {
558            Ordering::Less => -1,
559            Ordering::Equal => 0,
560            Ordering::Greater => 1,
561        })
562    }
563
564    /// <https://dom.spec.whatwg.org/#dom-range-intersectsnode>
565    fn IntersectsNode(&self, node: &Node) -> bool {
566        let start_node = self.start_container();
567        let start_node_root = self
568            .start_container()
569            .inclusive_ancestors(ShadowIncluding::No)
570            .last()
571            .unwrap();
572        let node_root = node
573            .inclusive_ancestors(ShadowIncluding::No)
574            .last()
575            .unwrap();
576        if start_node_root != node_root {
577            // Step 1.
578            return false;
579        }
580        let parent = match node.GetParentNode() {
581            Some(parent) => parent,
582            None => {
583                // Step 3.
584                return true;
585            },
586        };
587        // Step 4.
588        let offset = node.index();
589        // Step 5.
590        Ordering::Greater ==
591            bp_position(&parent, offset + 1, &start_node, self.start_offset()).unwrap() &&
592            Ordering::Less ==
593                bp_position(&parent, offset, &self.end_container(), self.end_offset())
594                    .unwrap()
595    }
596
597    /// <https://dom.spec.whatwg.org/#dom-range-clonecontents>
598    /// <https://dom.spec.whatwg.org/#concept-range-clone>
599    fn CloneContents(&self, can_gc: CanGc) -> Fallible<DomRoot<DocumentFragment>> {
600        // Step 3.
601        let start_node = self.start_container();
602        let start_offset = self.start_offset();
603        let end_node = self.end_container();
604        let end_offset = self.end_offset();
605
606        // Step 1.
607        let fragment = DocumentFragment::new(&start_node.owner_doc(), can_gc);
608
609        // Step 2.
610        if self.start() == self.end() {
611            return Ok(fragment);
612        }
613
614        if end_node == start_node {
615            if let Some(cdata) = start_node.downcast::<CharacterData>() {
616                // Steps 4.1-2.
617                let data = cdata
618                    .SubstringData(start_offset, end_offset - start_offset)
619                    .unwrap();
620                let clone = cdata.clone_with_data(data, &start_node.owner_doc(), can_gc);
621                // Step 4.3.
622                fragment.upcast::<Node>().AppendChild(&clone, can_gc)?;
623                // Step 4.4
624                return Ok(fragment);
625            }
626        }
627
628        // Steps 5-12.
629        let ContainedChildren {
630            first_partially_contained_child,
631            last_partially_contained_child,
632            contained_children,
633        } = self.contained_children()?;
634
635        if let Some(child) = first_partially_contained_child {
636            // Step 13.
637            if let Some(cdata) = child.downcast::<CharacterData>() {
638                assert!(child == start_node);
639                // Steps 13.1-2.
640                let data = cdata
641                    .SubstringData(start_offset, start_node.len() - start_offset)
642                    .unwrap();
643                let clone = cdata.clone_with_data(data, &start_node.owner_doc(), can_gc);
644                // Step 13.3.
645                fragment.upcast::<Node>().AppendChild(&clone, can_gc)?;
646            } else {
647                // Step 14.1.
648                let clone = child.CloneNode(/* deep */ false, can_gc)?;
649                // Step 14.2.
650                fragment.upcast::<Node>().AppendChild(&clone, can_gc)?;
651                // Step 14.3.
652                let subrange = Range::new(
653                    &clone.owner_doc(),
654                    &start_node,
655                    start_offset,
656                    &child,
657                    child.len(),
658                    can_gc,
659                );
660                // Step 14.4.
661                let subfragment = subrange.CloneContents(can_gc)?;
662                // Step 14.5.
663                clone.AppendChild(subfragment.upcast(), can_gc)?;
664            }
665        }
666
667        // Step 15.
668        for child in contained_children {
669            // Step 15.1.
670            let clone = child.CloneNode(/* deep */ true, can_gc)?;
671            // Step 15.2.
672            fragment.upcast::<Node>().AppendChild(&clone, can_gc)?;
673        }
674
675        if let Some(child) = last_partially_contained_child {
676            // Step 16.
677            if let Some(cdata) = child.downcast::<CharacterData>() {
678                assert!(child == end_node);
679                // Steps 16.1-2.
680                let data = cdata.SubstringData(0, end_offset).unwrap();
681                let clone = cdata.clone_with_data(data, &start_node.owner_doc(), can_gc);
682                // Step 16.3.
683                fragment.upcast::<Node>().AppendChild(&clone, can_gc)?;
684            } else {
685                // Step 17.1.
686                let clone = child.CloneNode(/* deep */ false, can_gc)?;
687                // Step 17.2.
688                fragment.upcast::<Node>().AppendChild(&clone, can_gc)?;
689                // Step 17.3.
690                let subrange =
691                    Range::new(&clone.owner_doc(), &child, 0, &end_node, end_offset, can_gc);
692                // Step 17.4.
693                let subfragment = subrange.CloneContents(can_gc)?;
694                // Step 17.5.
695                clone.AppendChild(subfragment.upcast(), can_gc)?;
696            }
697        }
698
699        // Step 18.
700        Ok(fragment)
701    }
702
703    /// <https://dom.spec.whatwg.org/#dom-range-extractcontents>
704    /// <https://dom.spec.whatwg.org/#concept-range-extract>
705    fn ExtractContents(&self, can_gc: CanGc) -> Fallible<DomRoot<DocumentFragment>> {
706        // Step 3.
707        let start_node = self.start_container();
708        let start_offset = self.start_offset();
709        let end_node = self.end_container();
710        let end_offset = self.end_offset();
711
712        // Step 1.
713        let fragment = DocumentFragment::new(&start_node.owner_doc(), can_gc);
714
715        // Step 2.
716        if self.collapsed() {
717            return Ok(fragment);
718        }
719
720        if end_node == start_node {
721            if let Some(end_data) = end_node.downcast::<CharacterData>() {
722                // Step 4.1.
723                let clone = end_node.CloneNode(/* deep */ true, can_gc)?;
724                // Step 4.2.
725                let text = end_data.SubstringData(start_offset, end_offset - start_offset);
726                clone
727                    .downcast::<CharacterData>()
728                    .unwrap()
729                    .SetData(text.unwrap());
730                // Step 4.3.
731                fragment.upcast::<Node>().AppendChild(&clone, can_gc)?;
732                // Step 4.4.
733                end_data.ReplaceData(start_offset, end_offset - start_offset, DOMString::new())?;
734                // Step 4.5.
735                return Ok(fragment);
736            }
737        }
738
739        // Steps 5-12.
740        let ContainedChildren {
741            first_partially_contained_child,
742            last_partially_contained_child,
743            contained_children,
744        } = self.contained_children()?;
745
746        let (new_node, new_offset) = if start_node.is_inclusive_ancestor_of(&end_node) {
747            // Step 13.
748            (DomRoot::from_ref(&*start_node), start_offset)
749        } else {
750            // Step 14.1-2.
751            let reference_node = start_node
752                .ancestors()
753                .take_while(|n| !n.is_inclusive_ancestor_of(&end_node))
754                .last()
755                .unwrap_or(DomRoot::from_ref(&start_node));
756            // Step 14.3.
757            (
758                reference_node.GetParentNode().unwrap(),
759                reference_node.index() + 1,
760            )
761        };
762
763        if let Some(child) = first_partially_contained_child {
764            if let Some(start_data) = child.downcast::<CharacterData>() {
765                assert!(child == start_node);
766                // Step 15.1.
767                let clone = start_node.CloneNode(/* deep */ true, can_gc)?;
768                // Step 15.2.
769                let text = start_data.SubstringData(start_offset, start_node.len() - start_offset);
770                clone
771                    .downcast::<CharacterData>()
772                    .unwrap()
773                    .SetData(text.unwrap());
774                // Step 15.3.
775                fragment.upcast::<Node>().AppendChild(&clone, can_gc)?;
776                // Step 15.4.
777                start_data.ReplaceData(
778                    start_offset,
779                    start_node.len() - start_offset,
780                    DOMString::new(),
781                )?;
782            } else {
783                // Step 16.1.
784                let clone = child.CloneNode(/* deep */ false, can_gc)?;
785                // Step 16.2.
786                fragment.upcast::<Node>().AppendChild(&clone, can_gc)?;
787                // Step 16.3.
788                let subrange = Range::new(
789                    &clone.owner_doc(),
790                    &start_node,
791                    start_offset,
792                    &child,
793                    child.len(),
794                    can_gc,
795                );
796                // Step 16.4.
797                let subfragment = subrange.ExtractContents(can_gc)?;
798                // Step 16.5.
799                clone.AppendChild(subfragment.upcast(), can_gc)?;
800            }
801        }
802
803        // Step 17.
804        for child in contained_children {
805            fragment.upcast::<Node>().AppendChild(&child, can_gc)?;
806        }
807
808        if let Some(child) = last_partially_contained_child {
809            if let Some(end_data) = child.downcast::<CharacterData>() {
810                assert!(child == end_node);
811                // Step 18.1.
812                let clone = end_node.CloneNode(/* deep */ true, can_gc)?;
813                // Step 18.2.
814                let text = end_data.SubstringData(0, end_offset);
815                clone
816                    .downcast::<CharacterData>()
817                    .unwrap()
818                    .SetData(text.unwrap());
819                // Step 18.3.
820                fragment.upcast::<Node>().AppendChild(&clone, can_gc)?;
821                // Step 18.4.
822                end_data.ReplaceData(0, end_offset, DOMString::new())?;
823            } else {
824                // Step 19.1.
825                let clone = child.CloneNode(/* deep */ false, can_gc)?;
826                // Step 19.2.
827                fragment.upcast::<Node>().AppendChild(&clone, can_gc)?;
828                // Step 19.3.
829                let subrange =
830                    Range::new(&clone.owner_doc(), &child, 0, &end_node, end_offset, can_gc);
831                // Step 19.4.
832                let subfragment = subrange.ExtractContents(can_gc)?;
833                // Step 19.5.
834                clone.AppendChild(subfragment.upcast(), can_gc)?;
835            }
836        }
837
838        // Step 20.
839        self.SetStart(&new_node, new_offset)?;
840        self.SetEnd(&new_node, new_offset)?;
841
842        // Step 21.
843        Ok(fragment)
844    }
845
846    /// <https://dom.spec.whatwg.org/#dom-range-detach>
847    fn Detach(&self) {
848        // This method intentionally left blank.
849    }
850
851    /// <https://dom.spec.whatwg.org/#dom-range-insertnode>
852    /// <https://dom.spec.whatwg.org/#concept-range-insert>
853    fn InsertNode(&self, node: &Node, can_gc: CanGc) -> ErrorResult {
854        let start_node = self.start_container();
855        let start_offset = self.start_offset();
856
857        // Step 1.
858        if &*start_node == node {
859            return Err(Error::HierarchyRequest(None));
860        }
861        match start_node.type_id() {
862            // Handled under step 2.
863            NodeTypeId::CharacterData(CharacterDataTypeId::Text(_)) => (),
864            NodeTypeId::CharacterData(_) => return Err(Error::HierarchyRequest(None)),
865            _ => (),
866        }
867
868        // Step 2.
869        let (reference_node, parent) = match start_node.type_id() {
870            NodeTypeId::CharacterData(CharacterDataTypeId::Text(_)) => {
871                // Step 3.
872                let parent = match start_node.GetParentNode() {
873                    Some(parent) => parent,
874                    // Step 1.
875                    None => return Err(Error::HierarchyRequest(None)),
876                };
877                // Step 5.
878                (Some(DomRoot::from_ref(&*start_node)), parent)
879            },
880            _ => {
881                // Steps 4-5.
882                let child = start_node.ChildNodes(can_gc).Item(start_offset);
883                (child, DomRoot::from_ref(&*start_node))
884            },
885        };
886
887        // Step 6.
888        Node::ensure_pre_insertion_validity(node, &parent, reference_node.as_deref())?;
889
890        // Step 7.
891        let split_text;
892        let reference_node = match start_node.downcast::<Text>() {
893            Some(text) => {
894                split_text = text.SplitText(start_offset, can_gc)?;
895                let new_reference = DomRoot::upcast::<Node>(split_text);
896                assert!(new_reference.GetParentNode().as_deref() == Some(&parent));
897                Some(new_reference)
898            },
899            _ => reference_node,
900        };
901
902        // Step 8.
903        let reference_node = if Some(node) == reference_node.as_deref() {
904            node.GetNextSibling()
905        } else {
906            reference_node
907        };
908
909        // Step 9.
910        node.remove_self(can_gc);
911
912        // Step 10.
913        let new_offset = reference_node
914            .as_ref()
915            .map_or(parent.len(), |node| node.index());
916
917        // Step 11
918        let new_offset = new_offset +
919            if let NodeTypeId::DocumentFragment(_) = node.type_id() {
920                node.len()
921            } else {
922                1
923            };
924
925        // Step 12.
926        Node::pre_insert(node, &parent, reference_node.as_deref(), can_gc)?;
927
928        // Step 13.
929        if self.collapsed() {
930            self.set_end(&parent, new_offset);
931        }
932
933        Ok(())
934    }
935
936    /// <https://dom.spec.whatwg.org/#dom-range-deletecontents>
937    fn DeleteContents(&self) -> ErrorResult {
938        // Step 1.
939        if self.collapsed() {
940            return Ok(());
941        }
942
943        // Step 2.
944        let start_node = self.start_container();
945        let end_node = self.end_container();
946        let start_offset = self.start_offset();
947        let end_offset = self.end_offset();
948
949        // Step 3.
950        if start_node == end_node {
951            if let Some(text) = start_node.downcast::<CharacterData>() {
952                if end_offset > start_offset {
953                    self.report_change();
954                }
955                return text.ReplaceData(start_offset, end_offset - start_offset, DOMString::new());
956            }
957        }
958
959        // Step 4.
960        rooted_vec!(let mut contained_children);
961        let ancestor = self.CommonAncestorContainer();
962
963        let mut iter = start_node.following_nodes(&ancestor);
964
965        let mut next = iter.next();
966        while let Some(child) = next {
967            if self.contains(&child) {
968                contained_children.push(Dom::from_ref(&*child));
969                next = iter.next_skipping_children();
970            } else {
971                next = iter.next();
972            }
973        }
974
975        let (new_node, new_offset) = if start_node.is_inclusive_ancestor_of(&end_node) {
976            // Step 5.
977            (DomRoot::from_ref(&*start_node), start_offset)
978        } else {
979            // Step 6.
980            fn compute_reference(start_node: &Node, end_node: &Node) -> (DomRoot<Node>, u32) {
981                let mut reference_node = DomRoot::from_ref(start_node);
982                while let Some(parent) = reference_node.GetParentNode() {
983                    if parent.is_inclusive_ancestor_of(end_node) {
984                        return (parent, reference_node.index() + 1);
985                    }
986                    reference_node = parent;
987                }
988                unreachable!()
989            }
990
991            compute_reference(&start_node, &end_node)
992        };
993
994        // Step 7.
995        if let Some(text) = start_node.downcast::<CharacterData>() {
996            text.ReplaceData(
997                start_offset,
998                start_node.len() - start_offset,
999                DOMString::new(),
1000            )
1001            .unwrap();
1002        }
1003
1004        // Step 8.
1005        for child in &*contained_children {
1006            child.remove_self(CanGc::note());
1007        }
1008
1009        // Step 9.
1010        if let Some(text) = end_node.downcast::<CharacterData>() {
1011            text.ReplaceData(0, end_offset, DOMString::new()).unwrap();
1012        }
1013
1014        // Step 10.
1015        self.SetStart(&new_node, new_offset).unwrap();
1016        self.SetEnd(&new_node, new_offset).unwrap();
1017        Ok(())
1018    }
1019
1020    /// <https://dom.spec.whatwg.org/#dom-range-surroundcontents>
1021    fn SurroundContents(&self, new_parent: &Node, can_gc: CanGc) -> ErrorResult {
1022        // Step 1.
1023        let start = self.start_container();
1024        let end = self.end_container();
1025
1026        if start
1027            .inclusive_ancestors(ShadowIncluding::No)
1028            .any(|n| !n.is_inclusive_ancestor_of(&end) && !n.is::<Text>()) ||
1029            end.inclusive_ancestors(ShadowIncluding::No)
1030                .any(|n| !n.is_inclusive_ancestor_of(&start) && !n.is::<Text>())
1031        {
1032            return Err(Error::InvalidState(None));
1033        }
1034
1035        // Step 2.
1036        match new_parent.type_id() {
1037            NodeTypeId::Document(_) |
1038            NodeTypeId::DocumentType |
1039            NodeTypeId::DocumentFragment(_) => {
1040                return Err(Error::InvalidNodeType(None));
1041            },
1042            _ => (),
1043        }
1044
1045        // Step 3.
1046        let fragment = self.ExtractContents(can_gc)?;
1047
1048        // Step 4.
1049        Node::replace_all(None, new_parent, can_gc);
1050
1051        // Step 5.
1052        self.InsertNode(new_parent, can_gc)?;
1053
1054        // Step 6.
1055        new_parent.AppendChild(fragment.upcast(), can_gc)?;
1056
1057        // Step 7.
1058        self.SelectNode(new_parent)
1059    }
1060
1061    /// <https://dom.spec.whatwg.org/#dom-range-stringifier>
1062    fn Stringifier(&self) -> DOMString {
1063        let start_node = self.start_container();
1064        let end_node = self.end_container();
1065
1066        // Step 1.
1067        let mut s = DOMString::new();
1068
1069        if let Some(text_node) = start_node.downcast::<Text>() {
1070            let char_data = text_node.upcast::<CharacterData>();
1071
1072            // Step 2.
1073            if start_node == end_node {
1074                return char_data
1075                    .SubstringData(self.start_offset(), self.end_offset() - self.start_offset())
1076                    .unwrap();
1077            }
1078
1079            // Step 3.
1080            s.push_str(
1081                &char_data
1082                    .SubstringData(
1083                        self.start_offset(),
1084                        char_data.Length() - self.start_offset(),
1085                    )
1086                    .unwrap()
1087                    .str(),
1088            );
1089        }
1090
1091        // Step 4.
1092        let ancestor = self.CommonAncestorContainer();
1093        let iter = start_node
1094            .following_nodes(&ancestor)
1095            .filter_map(DomRoot::downcast::<Text>);
1096
1097        for child in iter {
1098            if self.contains(child.upcast()) {
1099                s.push_str(&child.upcast::<CharacterData>().Data().str());
1100            }
1101        }
1102
1103        // Step 5.
1104        if let Some(text_node) = end_node.downcast::<Text>() {
1105            let char_data = text_node.upcast::<CharacterData>();
1106            s.push_str(&char_data.SubstringData(0, self.end_offset()).unwrap().str());
1107        }
1108
1109        // Step 6.
1110        s
1111    }
1112
1113    /// <https://html.spec.whatwg.org/multipage/#dom-range-createcontextualfragment>
1114    fn CreateContextualFragment(
1115        &self,
1116        fragment: TrustedHTMLOrString,
1117        can_gc: CanGc,
1118    ) -> Fallible<DomRoot<DocumentFragment>> {
1119        // Step 2. Let node be this's start node.
1120        //
1121        // Required to obtain the global, so we do this first. Shouldn't be an
1122        // observable difference.
1123        let node = self.start_container();
1124
1125        // Step 1. Let compliantString be the result of invoking the
1126        // Get Trusted Type compliant string algorithm with TrustedHTML,
1127        // this's relevant global object, string, "Range createContextualFragment", and "script".
1128        let fragment = TrustedHTML::get_trusted_script_compliant_string(
1129            node.owner_window().upcast(),
1130            fragment,
1131            "Range createContextualFragment",
1132            can_gc,
1133        )?;
1134
1135        let owner_doc = node.owner_doc();
1136
1137        // Step 3. Let element be null.
1138        // Step 4. If node implements Element, set element to node.
1139        // Step 5. Otherwise, if node implements Text or Comment, set element to node's parent element.
1140        let element = match node.type_id() {
1141            NodeTypeId::Element(_) => Some(DomRoot::downcast::<Element>(node).unwrap()),
1142            NodeTypeId::CharacterData(CharacterDataTypeId::Comment) |
1143            NodeTypeId::CharacterData(CharacterDataTypeId::Text(_)) => node.GetParentElement(),
1144            _ => None,
1145        };
1146
1147        // Step 6. If element is null or all of the following are true:
1148        let element = Element::fragment_parsing_context(&owner_doc, element.as_deref(), can_gc);
1149
1150        // Step 7. Let fragment node be the result of invoking the fragment parsing algorithm steps with element and compliantString.
1151        let fragment_node = element.parse_fragment(fragment, can_gc)?;
1152
1153        // Step 8. For each script of fragment node's script element descendants:
1154        for node in fragment_node
1155            .upcast::<Node>()
1156            .traverse_preorder(ShadowIncluding::No)
1157        {
1158            if let Some(script) = node.downcast::<HTMLScriptElement>() {
1159                // Step 8.1. Set script's already started to false.
1160                script.set_already_started(false);
1161                // Step 8.2. Set script's parser document to null.
1162                script.set_parser_inserted(false);
1163            }
1164        }
1165
1166        // Step 9. Return fragment node.
1167        Ok(fragment_node)
1168    }
1169
1170    /// <https://drafts.csswg.org/cssom-view/#dom-range-getclientrects>
1171    fn GetClientRects(&self, can_gc: CanGc) -> DomRoot<DOMRectList> {
1172        let start = self.start_container();
1173        let window = start.owner_window();
1174
1175        let client_rects = self
1176            .client_rects()
1177            .map(|rect| {
1178                DOMRect::new(
1179                    window.upcast(),
1180                    rect.origin.x.to_f64_px(),
1181                    rect.origin.y.to_f64_px(),
1182                    rect.size.width.to_f64_px(),
1183                    rect.size.height.to_f64_px(),
1184                    can_gc,
1185                )
1186            })
1187            .collect();
1188
1189        DOMRectList::new(&window, client_rects, can_gc)
1190    }
1191
1192    /// <https://drafts.csswg.org/cssom-view/#dom-range-getboundingclientrect>
1193    fn GetBoundingClientRect(&self, can_gc: CanGc) -> DomRoot<DOMRect> {
1194        let window = self.start_container().owner_window();
1195
1196        // Step 1. Let list be the result of invoking getClientRects() on the same range this method was invoked on.
1197        let list = self.client_rects();
1198
1199        // Step 2. If list is empty return a DOMRect object whose x, y, width and height members are zero.
1200        // Step 3. If all rectangles in list have zero width or height, return the first rectangle in list.
1201        // Step 4. Otherwise, return a DOMRect object describing the smallest rectangle that includes all
1202        // of the rectangles in list of which the height or width is not zero.
1203        let bounding_rect = list.fold(euclid::Rect::zero(), |acc, rect| acc.union(&rect));
1204
1205        DOMRect::new(
1206            window.upcast(),
1207            bounding_rect.origin.x.to_f64_px(),
1208            bounding_rect.origin.y.to_f64_px(),
1209            bounding_rect.size.width.to_f64_px(),
1210            bounding_rect.size.height.to_f64_px(),
1211            can_gc,
1212        )
1213    }
1214}
1215
1216#[derive(MallocSizeOf)]
1217pub(crate) struct WeakRangeVec {
1218    cell: RefCell<WeakRefVec<Range>>,
1219}
1220
1221impl Default for WeakRangeVec {
1222    fn default() -> Self {
1223        WeakRangeVec {
1224            cell: RefCell::new(WeakRefVec::new()),
1225        }
1226    }
1227}
1228
1229impl WeakRangeVec {
1230    /// Whether that vector of ranges is empty.
1231    pub(crate) fn is_empty(&self) -> bool {
1232        self.cell.borrow().is_empty()
1233    }
1234
1235    /// Used for steps 2.1-2. when inserting a node.
1236    /// <https://dom.spec.whatwg.org/#concept-node-insert>
1237    pub(crate) fn increase_above(&self, node: &Node, offset: u32, delta: u32) {
1238        self.map_offset_above(node, offset, |offset| offset + delta);
1239    }
1240
1241    /// Used for steps 4-5. when removing a node.
1242    /// <https://dom.spec.whatwg.org/#concept-node-remove>
1243    pub(crate) fn decrease_above(&self, node: &Node, offset: u32, delta: u32) {
1244        self.map_offset_above(node, offset, |offset| offset - delta);
1245    }
1246
1247    /// Used for steps 2-3. when removing a node.
1248    ///
1249    /// <https://dom.spec.whatwg.org/#concept-node-remove>
1250    pub(crate) fn drain_to_parent(&self, context: &UnbindContext, child: &Node) {
1251        if self.is_empty() {
1252            return;
1253        }
1254
1255        let offset = context.index();
1256        let parent = context.parent;
1257        let ranges = &mut *self.cell.borrow_mut();
1258
1259        ranges.update(|entry| {
1260            let range = entry.root().unwrap();
1261            if range.start().node() == parent || range.end().node() == parent {
1262                entry.remove();
1263            }
1264            if range.start().node() == child {
1265                range.report_change();
1266                range.start().set(context.parent, offset);
1267            }
1268            if range.end().node() == child {
1269                range.report_change();
1270                range.end().set(context.parent, offset);
1271            }
1272        });
1273
1274        context
1275            .parent
1276            .ranges()
1277            .cell
1278            .borrow_mut()
1279            .extend(ranges.drain(..));
1280    }
1281
1282    /// Used for steps 6.1-2. when normalizing a node.
1283    /// <https://dom.spec.whatwg.org/#dom-node-normalize>
1284    pub(crate) fn drain_to_preceding_text_sibling(&self, node: &Node, sibling: &Node, length: u32) {
1285        if self.is_empty() {
1286            return;
1287        }
1288
1289        let ranges = &mut *self.cell.borrow_mut();
1290
1291        ranges.update(|entry| {
1292            let range = entry.root().unwrap();
1293            if range.start().node() == sibling || range.end().node() == sibling {
1294                entry.remove();
1295            }
1296            if range.start().node() == node {
1297                range.report_change();
1298                range.start().set(sibling, range.start_offset() + length);
1299            }
1300            if range.end().node() == node {
1301                range.report_change();
1302                range.end().set(sibling, range.end_offset() + length);
1303            }
1304        });
1305
1306        sibling.ranges().cell.borrow_mut().extend(ranges.drain(..));
1307    }
1308
1309    /// Used for steps 6.3-4. when normalizing a node.
1310    /// <https://dom.spec.whatwg.org/#dom-node-normalize>
1311    pub(crate) fn move_to_text_child_at(
1312        &self,
1313        node: &Node,
1314        offset: u32,
1315        child: &Node,
1316        new_offset: u32,
1317    ) {
1318        let child_ranges = child.ranges();
1319        let mut child_ranges = child_ranges.cell.borrow_mut();
1320
1321        self.cell.borrow_mut().update(|entry| {
1322            let range = entry.root().unwrap();
1323
1324            let node_is_start = range.start().node() == node;
1325            let node_is_end = range.end().node() == node;
1326
1327            let move_start = node_is_start && range.start_offset() == offset;
1328            let move_end = node_is_end && range.end_offset() == offset;
1329
1330            let remove_from_node =
1331                move_start && (move_end || !node_is_end) || move_end && !node_is_start;
1332
1333            let already_in_child = range.start().node() == child || range.end().node() == child;
1334            let push_to_child = !already_in_child && (move_start || move_end);
1335
1336            if remove_from_node {
1337                let ref_ = entry.remove();
1338                if push_to_child {
1339                    child_ranges.push(ref_);
1340                }
1341            } else if push_to_child {
1342                child_ranges.push(WeakRef::new(&range));
1343            }
1344
1345            if move_start {
1346                range.report_change();
1347                range.start().set(child, new_offset);
1348            }
1349            if move_end {
1350                range.report_change();
1351                range.end().set(child, new_offset);
1352            }
1353        });
1354    }
1355
1356    /// Used for steps 8-11. when replacing character data.
1357    /// <https://dom.spec.whatwg.org/#concept-cd-replace>
1358    pub(crate) fn replace_code_units(
1359        &self,
1360        node: &Node,
1361        offset: u32,
1362        removed_code_units: u32,
1363        added_code_units: u32,
1364    ) {
1365        self.map_offset_above(node, offset, |range_offset| {
1366            if range_offset <= offset + removed_code_units {
1367                offset
1368            } else {
1369                range_offset + added_code_units - removed_code_units
1370            }
1371        });
1372    }
1373
1374    /// Used for steps 7.2-3. when splitting a text node.
1375    /// <https://dom.spec.whatwg.org/#concept-text-split>
1376    pub(crate) fn move_to_following_text_sibling_above(
1377        &self,
1378        node: &Node,
1379        offset: u32,
1380        sibling: &Node,
1381    ) {
1382        let sibling_ranges = sibling.ranges();
1383        let mut sibling_ranges = sibling_ranges.cell.borrow_mut();
1384
1385        self.cell.borrow_mut().update(|entry| {
1386            let range = entry.root().unwrap();
1387            let start_offset = range.start_offset();
1388            let end_offset = range.end_offset();
1389
1390            let node_is_start = range.start().node() == node;
1391            let node_is_end = range.end().node() == node;
1392
1393            let move_start = node_is_start && start_offset > offset;
1394            let move_end = node_is_end && end_offset > offset;
1395
1396            let remove_from_node =
1397                move_start && (move_end || !node_is_end) || move_end && !node_is_start;
1398
1399            let already_in_sibling =
1400                range.start().node() == sibling || range.end().node() == sibling;
1401            let push_to_sibling = !already_in_sibling && (move_start || move_end);
1402
1403            if remove_from_node {
1404                let ref_ = entry.remove();
1405                if push_to_sibling {
1406                    sibling_ranges.push(ref_);
1407                }
1408            } else if push_to_sibling {
1409                sibling_ranges.push(WeakRef::new(&range));
1410            }
1411
1412            if move_start {
1413                range.report_change();
1414                range.start().set(sibling, start_offset - offset);
1415            }
1416            if move_end {
1417                range.report_change();
1418                range.end().set(sibling, end_offset - offset);
1419            }
1420        });
1421    }
1422
1423    /// Used for steps 7.4-5. when splitting a text node.
1424    /// <https://dom.spec.whatwg.org/#concept-text-split>
1425    pub(crate) fn increment_at(&self, node: &Node, offset: u32) {
1426        self.cell.borrow_mut().update(|entry| {
1427            let range = entry.root().unwrap();
1428            if range.start().node() == node && offset == range.start_offset() {
1429                range.report_change();
1430                range.start().set_offset(offset + 1);
1431            }
1432            if range.end().node() == node && offset == range.end_offset() {
1433                range.report_change();
1434                range.end().set_offset(offset + 1);
1435            }
1436        });
1437    }
1438
1439    fn map_offset_above<F: FnMut(u32) -> u32>(&self, node: &Node, offset: u32, mut f: F) {
1440        self.cell.borrow_mut().update(|entry| {
1441            let range = entry.root().unwrap();
1442            let start_offset = range.start_offset();
1443            if range.start().node() == node && start_offset > offset {
1444                range.report_change();
1445                range.start().set_offset(f(start_offset));
1446            }
1447            let end_offset = range.end_offset();
1448            if range.end().node() == node && end_offset > offset {
1449                range.report_change();
1450                range.end().set_offset(f(end_offset));
1451            }
1452        });
1453    }
1454
1455    pub(crate) fn push(&self, ref_: WeakRef<Range>) {
1456        self.cell.borrow_mut().push(ref_);
1457    }
1458
1459    fn remove(&self, range: &Range) -> WeakRef<Range> {
1460        let mut ranges = self.cell.borrow_mut();
1461        let position = ranges.iter().position(|ref_| ref_ == range).unwrap();
1462        ranges.swap_remove(position)
1463    }
1464}
1465
1466#[expect(unsafe_code)]
1467unsafe impl JSTraceable for WeakRangeVec {
1468    unsafe fn trace(&self, _: *mut JSTracer) {
1469        self.cell.borrow_mut().retain_alive()
1470    }
1471}