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