Skip to main content

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 script_bindings::cell::DomRefCell;
16use script_bindings::reflector::reflect_dom_object_with_proto;
17use style_traits::CSSPixel;
18
19use crate::dom::abstractrange::{AbstractRange, BoundaryPoint, bp_position};
20use crate::dom::bindings::codegen::Bindings::AbstractRangeBinding::AbstractRangeMethods;
21use crate::dom::bindings::codegen::Bindings::CharacterDataBinding::CharacterDataMethods;
22use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
23use crate::dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods;
24use crate::dom::bindings::codegen::Bindings::RangeBinding::{RangeConstants, RangeMethods};
25use crate::dom::bindings::codegen::Bindings::TextBinding::TextMethods;
26use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
27use crate::dom::bindings::codegen::UnionTypes::TrustedHTMLOrString;
28use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
29use crate::dom::bindings::inheritance::{Castable, CharacterDataTypeId, NodeTypeId};
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    pub(crate) first_partially_contained_child: Option<DomRoot<Node>>,
65    pub(crate) 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    pub(crate) 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
402impl std::fmt::Debug for Range {
403    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
404        write!(
405            f,
406            "[({:?}, {}) -> ({:?}, {})]",
407            self.start_container(),
408            self.start_offset(),
409            self.end_container(),
410            self.end_offset()
411        )
412    }
413}
414
415enum StartOrEnd {
416    Start,
417    End,
418}
419
420impl RangeMethods<crate::DomTypeHolder> for Range {
421    /// <https://dom.spec.whatwg.org/#dom-range>
422    fn Constructor(
423        window: &Window,
424        proto: Option<HandleObject>,
425        can_gc: CanGc,
426    ) -> Fallible<DomRoot<Range>> {
427        let document = window.Document();
428        Ok(Range::new_with_doc(&document, proto, can_gc))
429    }
430
431    /// <https://dom.spec.whatwg.org/#dom-range-commonancestorcontainer>
432    fn CommonAncestorContainer(&self) -> DomRoot<Node> {
433        self.end_container()
434            .common_ancestor(&self.start_container(), ShadowIncluding::No)
435            .expect("Couldn't find common ancestor container")
436    }
437
438    /// <https://dom.spec.whatwg.org/#dom-range-setstart>
439    fn SetStart(&self, node: &Node, offset: u32) -> ErrorResult {
440        self.set_the_start_or_end(node, offset, StartOrEnd::Start)
441    }
442
443    /// <https://dom.spec.whatwg.org/#dom-range-setend>
444    fn SetEnd(&self, node: &Node, offset: u32) -> ErrorResult {
445        self.set_the_start_or_end(node, offset, StartOrEnd::End)
446    }
447
448    /// <https://dom.spec.whatwg.org/#dom-range-setstartbefore>
449    fn SetStartBefore(&self, node: &Node) -> ErrorResult {
450        let parent = node.GetParentNode().ok_or(Error::InvalidNodeType(None))?;
451        self.SetStart(&parent, node.index())
452    }
453
454    /// <https://dom.spec.whatwg.org/#dom-range-setstartafter>
455    fn SetStartAfter(&self, node: &Node) -> ErrorResult {
456        let parent = node.GetParentNode().ok_or(Error::InvalidNodeType(None))?;
457        self.SetStart(&parent, node.index() + 1)
458    }
459
460    /// <https://dom.spec.whatwg.org/#dom-range-setendbefore>
461    fn SetEndBefore(&self, node: &Node) -> ErrorResult {
462        let parent = node.GetParentNode().ok_or(Error::InvalidNodeType(None))?;
463        self.SetEnd(&parent, node.index())
464    }
465
466    /// <https://dom.spec.whatwg.org/#dom-range-setendafter>
467    fn SetEndAfter(&self, node: &Node) -> ErrorResult {
468        let parent = node.GetParentNode().ok_or(Error::InvalidNodeType(None))?;
469        self.SetEnd(&parent, node.index() + 1)
470    }
471
472    /// <https://dom.spec.whatwg.org/#dom-range-collapse>
473    fn Collapse(&self, to_start: bool) {
474        if to_start {
475            self.set_end(&self.start_container(), self.start_offset());
476        } else {
477            self.set_start(&self.end_container(), self.end_offset());
478        }
479    }
480
481    /// <https://dom.spec.whatwg.org/#dom-range-selectnode>
482    fn SelectNode(&self, node: &Node) -> ErrorResult {
483        // Steps 1, 2.
484        let parent = node.GetParentNode().ok_or(Error::InvalidNodeType(None))?;
485        // Step 3.
486        let index = node.index();
487        // Step 4.
488        self.set_start(&parent, index);
489        // Step 5.
490        self.set_end(&parent, index + 1);
491        Ok(())
492    }
493
494    /// <https://dom.spec.whatwg.org/#dom-range-selectnodecontents>
495    fn SelectNodeContents(&self, node: &Node) -> ErrorResult {
496        if node.is_doctype() {
497            // Step 1.
498            return Err(Error::InvalidNodeType(None));
499        }
500        // Step 2.
501        let length = node.len();
502        // Step 3.
503        self.set_start(node, 0);
504        // Step 4.
505        self.set_end(node, length);
506        Ok(())
507    }
508
509    /// <https://dom.spec.whatwg.org/#dom-range-compareboundarypoints>
510    fn CompareBoundaryPoints(&self, how: u16, other: &Range) -> Fallible<i16> {
511        if how > RangeConstants::END_TO_START {
512            // Step 1.
513            return Err(Error::NotSupported(None));
514        }
515        let this_root = self
516            .start_container()
517            .inclusive_ancestors(ShadowIncluding::No)
518            .last()
519            .unwrap();
520        let other_root = other
521            .start_container()
522            .inclusive_ancestors(ShadowIncluding::No)
523            .last()
524            .unwrap();
525        if this_root != other_root {
526            // Step 2.
527            return Err(Error::WrongDocument(None));
528        }
529        // Step 3.
530        let (this_point, other_point) = match how {
531            RangeConstants::START_TO_START => (self.start(), other.start()),
532            RangeConstants::START_TO_END => (self.end(), other.start()),
533            RangeConstants::END_TO_END => (self.end(), other.end()),
534            RangeConstants::END_TO_START => (self.start(), other.end()),
535            _ => unreachable!(),
536        };
537        // step 4.
538        match this_point.partial_cmp(other_point).unwrap() {
539            Ordering::Less => Ok(-1),
540            Ordering::Equal => Ok(0),
541            Ordering::Greater => Ok(1),
542        }
543    }
544
545    /// <https://dom.spec.whatwg.org/#dom-range-clonerange>
546    fn CloneRange(&self, cx: &mut JSContext) -> DomRoot<Range> {
547        let start_node = self.start_container();
548        let owner_doc = start_node.owner_doc();
549        Range::new(
550            &owner_doc,
551            &start_node,
552            self.start_offset(),
553            &self.end_container(),
554            self.end_offset(),
555            CanGc::from_cx(cx),
556        )
557    }
558
559    /// <https://dom.spec.whatwg.org/#dom-range-ispointinrange>
560    fn IsPointInRange(&self, node: &Node, offset: u32) -> Fallible<bool> {
561        match self.compare_point(node, offset) {
562            Ok(Ordering::Less) => Ok(false),
563            Ok(Ordering::Equal) => Ok(true),
564            Ok(Ordering::Greater) => Ok(false),
565            Err(Error::WrongDocument(None)) => {
566                // Step 2.
567                Ok(false)
568            },
569            Err(error) => Err(error),
570        }
571    }
572
573    /// <https://dom.spec.whatwg.org/#dom-range-comparepoint>
574    fn ComparePoint(&self, node: &Node, offset: u32) -> Fallible<i16> {
575        self.compare_point(node, offset).map(|order| match order {
576            Ordering::Less => -1,
577            Ordering::Equal => 0,
578            Ordering::Greater => 1,
579        })
580    }
581
582    /// <https://dom.spec.whatwg.org/#dom-range-intersectsnode>
583    fn IntersectsNode(&self, node: &Node) -> bool {
584        let start_node = self.start_container();
585        let start_node_root = self
586            .start_container()
587            .inclusive_ancestors(ShadowIncluding::No)
588            .last()
589            .unwrap();
590        let node_root = node
591            .inclusive_ancestors(ShadowIncluding::No)
592            .last()
593            .unwrap();
594        if start_node_root != node_root {
595            // Step 1.
596            return false;
597        }
598        let parent = match node.GetParentNode() {
599            Some(parent) => parent,
600            None => {
601                // Step 3.
602                return true;
603            },
604        };
605        // Step 4.
606        let offset = node.index();
607        // Step 5.
608        Ordering::Greater ==
609            bp_position(&parent, offset + 1, &start_node, self.start_offset()).unwrap() &&
610            Ordering::Less ==
611                bp_position(&parent, offset, &self.end_container(), self.end_offset())
612                    .unwrap()
613    }
614
615    /// <https://dom.spec.whatwg.org/#dom-range-clonecontents>
616    /// <https://dom.spec.whatwg.org/#concept-range-clone>
617    fn CloneContents(&self, cx: &mut JSContext) -> Fallible<DomRoot<DocumentFragment>> {
618        // Step 3.
619        let start_node = self.start_container();
620        let start_offset = self.start_offset();
621        let end_node = self.end_container();
622        let end_offset = self.end_offset();
623
624        // Step 1.
625        let fragment = DocumentFragment::new(cx, &start_node.owner_doc());
626
627        // Step 2.
628        if self.start() == self.end() {
629            return Ok(fragment);
630        }
631
632        if end_node == start_node &&
633            let Some(cdata) = start_node.downcast::<CharacterData>()
634        {
635            // Steps 4.1-2.
636            let data = cdata
637                .SubstringData(start_offset, end_offset - start_offset)
638                .unwrap();
639            let clone = cdata.clone_with_data(cx, data, &start_node.owner_doc());
640            // Step 4.3.
641            fragment.upcast::<Node>().AppendChild(cx, &clone)?;
642            // Step 4.4
643            return Ok(fragment);
644        }
645
646        // Steps 5-12.
647        let ContainedChildren {
648            first_partially_contained_child,
649            last_partially_contained_child,
650            contained_children,
651        } = self.contained_children()?;
652
653        if let Some(child) = first_partially_contained_child {
654            // Step 13.
655            if let Some(cdata) = child.downcast::<CharacterData>() {
656                assert!(child == start_node);
657                // Steps 13.1-2.
658                let data = cdata
659                    .SubstringData(start_offset, start_node.len() - start_offset)
660                    .unwrap();
661                let clone = cdata.clone_with_data(cx, data, &start_node.owner_doc());
662                // Step 13.3.
663                fragment.upcast::<Node>().AppendChild(cx, &clone)?;
664            } else {
665                // Step 14.1.
666                let clone = child.CloneNode(cx, /* deep */ false)?;
667                // Step 14.2.
668                fragment.upcast::<Node>().AppendChild(cx, &clone)?;
669                // Step 14.3.
670                let subrange = Range::new(
671                    &clone.owner_doc(),
672                    &start_node,
673                    start_offset,
674                    &child,
675                    child.len(),
676                    CanGc::from_cx(cx),
677                );
678                // Step 14.4.
679                let subfragment = subrange.CloneContents(cx)?;
680                // Step 14.5.
681                clone.AppendChild(cx, subfragment.upcast())?;
682            }
683        }
684
685        // Step 15.
686        for child in contained_children {
687            // Step 15.1.
688            let clone = child.CloneNode(cx, /* deep */ true)?;
689            // Step 15.2.
690            fragment.upcast::<Node>().AppendChild(cx, &clone)?;
691        }
692
693        if let Some(child) = last_partially_contained_child {
694            // Step 16.
695            if let Some(cdata) = child.downcast::<CharacterData>() {
696                assert!(child == end_node);
697                // Steps 16.1-2.
698                let data = cdata.SubstringData(0, end_offset).unwrap();
699                let clone = cdata.clone_with_data(cx, data, &start_node.owner_doc());
700                // Step 16.3.
701                fragment.upcast::<Node>().AppendChild(cx, &clone)?;
702            } else {
703                // Step 17.1.
704                let clone = child.CloneNode(cx, /* deep */ false)?;
705                // Step 17.2.
706                fragment.upcast::<Node>().AppendChild(cx, &clone)?;
707                // Step 17.3.
708                let subrange = Range::new(
709                    &clone.owner_doc(),
710                    &child,
711                    0,
712                    &end_node,
713                    end_offset,
714                    CanGc::from_cx(cx),
715                );
716                // Step 17.4.
717                let subfragment = subrange.CloneContents(cx)?;
718                // Step 17.5.
719                clone.AppendChild(cx, subfragment.upcast())?;
720            }
721        }
722
723        // Step 18.
724        Ok(fragment)
725    }
726
727    /// <https://dom.spec.whatwg.org/#dom-range-extractcontents>
728    /// <https://dom.spec.whatwg.org/#concept-range-extract>
729    fn ExtractContents(&self, cx: &mut JSContext) -> Fallible<DomRoot<DocumentFragment>> {
730        // Step 3.
731        let start_node = self.start_container();
732        let start_offset = self.start_offset();
733        let end_node = self.end_container();
734        let end_offset = self.end_offset();
735
736        // Step 1.
737        let fragment = DocumentFragment::new(cx, &start_node.owner_doc());
738
739        // Step 2.
740        if self.collapsed() {
741            return Ok(fragment);
742        }
743
744        if end_node == start_node &&
745            let Some(end_data) = end_node.downcast::<CharacterData>()
746        {
747            // Step 4.1.
748            let clone = end_node.CloneNode(cx, /* deep */ true)?;
749            // Step 4.2.
750            let text = end_data.SubstringData(start_offset, end_offset - start_offset);
751            clone
752                .downcast::<CharacterData>()
753                .unwrap()
754                .SetData(text.unwrap());
755            // Step 4.3.
756            fragment.upcast::<Node>().AppendChild(cx, &clone)?;
757            // Step 4.4.
758            end_data.ReplaceData(start_offset, end_offset - start_offset, DOMString::new())?;
759            // Step 4.5.
760            return Ok(fragment);
761        }
762
763        // Steps 5-12.
764        let ContainedChildren {
765            first_partially_contained_child,
766            last_partially_contained_child,
767            contained_children,
768        } = self.contained_children()?;
769
770        let (new_node, new_offset) = if start_node.is_inclusive_ancestor_of(&end_node) {
771            // Step 13.
772            (DomRoot::from_ref(&*start_node), start_offset)
773        } else {
774            // Step 14.1-2.
775            let reference_node = start_node
776                .ancestors()
777                .take_while(|n| !n.is_inclusive_ancestor_of(&end_node))
778                .last()
779                .unwrap_or(DomRoot::from_ref(&start_node));
780            // Step 14.3.
781            (
782                reference_node.GetParentNode().unwrap(),
783                reference_node.index() + 1,
784            )
785        };
786
787        if let Some(child) = first_partially_contained_child {
788            if let Some(start_data) = child.downcast::<CharacterData>() {
789                assert!(child == start_node);
790                // Step 15.1.
791                let clone = start_node.CloneNode(cx, /* deep */ true)?;
792                // Step 15.2.
793                let text = start_data.SubstringData(start_offset, start_node.len() - start_offset);
794                clone
795                    .downcast::<CharacterData>()
796                    .unwrap()
797                    .SetData(text.unwrap());
798                // Step 15.3.
799                fragment.upcast::<Node>().AppendChild(cx, &clone)?;
800                // Step 15.4.
801                start_data.ReplaceData(
802                    start_offset,
803                    start_node.len() - start_offset,
804                    DOMString::new(),
805                )?;
806            } else {
807                // Step 16.1.
808                let clone = child.CloneNode(cx, /* deep */ false)?;
809                // Step 16.2.
810                fragment.upcast::<Node>().AppendChild(cx, &clone)?;
811                // Step 16.3.
812                let subrange = Range::new(
813                    &clone.owner_doc(),
814                    &start_node,
815                    start_offset,
816                    &child,
817                    child.len(),
818                    CanGc::from_cx(cx),
819                );
820                // Step 16.4.
821                let subfragment = subrange.ExtractContents(cx)?;
822                // Step 16.5.
823                clone.AppendChild(cx, subfragment.upcast())?;
824            }
825        }
826
827        // Step 17.
828        for child in contained_children {
829            fragment.upcast::<Node>().AppendChild(cx, &child)?;
830        }
831
832        if let Some(child) = last_partially_contained_child {
833            if let Some(end_data) = child.downcast::<CharacterData>() {
834                assert!(child == end_node);
835                // Step 18.1.
836                let clone = end_node.CloneNode(cx, /* deep */ true)?;
837                // Step 18.2.
838                let text = end_data.SubstringData(0, end_offset);
839                clone
840                    .downcast::<CharacterData>()
841                    .unwrap()
842                    .SetData(text.unwrap());
843                // Step 18.3.
844                fragment.upcast::<Node>().AppendChild(cx, &clone)?;
845                // Step 18.4.
846                end_data.ReplaceData(0, end_offset, DOMString::new())?;
847            } else {
848                // Step 19.1.
849                let clone = child.CloneNode(cx, /* deep */ false)?;
850                // Step 19.2.
851                fragment.upcast::<Node>().AppendChild(cx, &clone)?;
852                // Step 19.3.
853                let subrange = Range::new(
854                    &clone.owner_doc(),
855                    &child,
856                    0,
857                    &end_node,
858                    end_offset,
859                    CanGc::from_cx(cx),
860                );
861                // Step 19.4.
862                let subfragment = subrange.ExtractContents(cx)?;
863                // Step 19.5.
864                clone.AppendChild(cx, subfragment.upcast())?;
865            }
866        }
867
868        // Step 20.
869        self.SetStart(&new_node, new_offset)?;
870        self.SetEnd(&new_node, new_offset)?;
871
872        // Step 21.
873        Ok(fragment)
874    }
875
876    /// <https://dom.spec.whatwg.org/#dom-range-detach>
877    fn Detach(&self) {
878        // This method intentionally left blank.
879    }
880
881    /// <https://dom.spec.whatwg.org/#dom-range-insertnode>
882    /// <https://dom.spec.whatwg.org/#concept-range-insert>
883    fn InsertNode(&self, cx: &mut JSContext, node: &Node) -> ErrorResult {
884        let start_node = self.start_container();
885        let start_offset = self.start_offset();
886
887        // Step 1.
888        if &*start_node == node {
889            return Err(Error::HierarchyRequest(None));
890        }
891        match start_node.type_id() {
892            // Handled under step 2.
893            NodeTypeId::CharacterData(CharacterDataTypeId::Text(_)) => (),
894            NodeTypeId::CharacterData(_) => return Err(Error::HierarchyRequest(None)),
895            _ => (),
896        }
897
898        // Step 2.
899        let (reference_node, parent) = match start_node.type_id() {
900            NodeTypeId::CharacterData(CharacterDataTypeId::Text(_)) => {
901                // Step 3.
902                let parent = match start_node.GetParentNode() {
903                    Some(parent) => parent,
904                    // Step 1.
905                    None => return Err(Error::HierarchyRequest(None)),
906                };
907                // Step 5.
908                (Some(DomRoot::from_ref(&*start_node)), parent)
909            },
910            _ => {
911                // Steps 4-5.
912                let child = start_node.ChildNodes(cx).Item(start_offset);
913                (child, DomRoot::from_ref(&*start_node))
914            },
915        };
916
917        // Step 6.
918        Node::ensure_pre_insertion_validity(cx.no_gc(), node, &parent, reference_node.as_deref())?;
919
920        // Step 7.
921        let split_text;
922        let reference_node = match start_node.downcast::<Text>() {
923            Some(text) => {
924                split_text = text.SplitText(cx, start_offset)?;
925                let new_reference = DomRoot::upcast::<Node>(split_text);
926                assert!(new_reference.GetParentNode().as_deref() == Some(&parent));
927                Some(new_reference)
928            },
929            _ => reference_node,
930        };
931
932        // Step 8.
933        let reference_node = if Some(node) == reference_node.as_deref() {
934            node.GetNextSibling()
935        } else {
936            reference_node
937        };
938
939        // Step 9.
940        node.remove_self(cx);
941
942        // Step 10.
943        let new_offset = reference_node
944            .as_ref()
945            .map_or(parent.len(), |node| node.index());
946
947        // Step 11
948        let new_offset = new_offset +
949            if let NodeTypeId::DocumentFragment(_) = node.type_id() {
950                node.len()
951            } else {
952                1
953            };
954
955        // Step 12.
956        Node::pre_insert(cx, node, &parent, reference_node.as_deref())?;
957
958        // Step 13.
959        if self.collapsed() {
960            self.set_end(&parent, new_offset);
961        }
962
963        Ok(())
964    }
965
966    /// <https://dom.spec.whatwg.org/#dom-range-deletecontents>
967    fn DeleteContents(&self, cx: &mut JSContext) -> ErrorResult {
968        // Step 1. If this is collapsed, then return.
969        if self.collapsed() {
970            return Ok(());
971        }
972
973        // Step 2. Let originalStartNode, originalStartOffset, originalEndNode,
974        // and originalEndOffset be this’s start node, start offset, end node, and end offset, respectively.
975        let start_node = self.start_container();
976        let end_node = self.end_container();
977        let start_offset = self.start_offset();
978        let end_offset = self.end_offset();
979
980        // Step 3. If originalStartNode is originalEndNode and it is a CharacterData node:
981        if start_node == end_node &&
982            let Some(text) = start_node.downcast::<CharacterData>()
983        {
984            if end_offset > start_offset {
985                self.report_change();
986            }
987
988            // Step 3.1. Replace data of originalStartNode with originalStartOffset,
989            // originalEndOffset − originalStartOffset, and the empty string.
990            // Step 3.2. Return.
991            return text.ReplaceData(start_offset, end_offset - start_offset, DOMString::new());
992        }
993
994        // Step 4. Let nodesToRemove be a list of all the nodes that are contained in this,
995        // in tree order, omitting any node whose parent is also contained in this.
996        rooted_vec!(let mut contained_children);
997        let ancestor = self.CommonAncestorContainer();
998
999        let mut iter = start_node.following_nodes(&ancestor);
1000
1001        let mut next = iter.next();
1002        while let Some(child) = next {
1003            if self.contains(&child) {
1004                contained_children.push(Dom::from_ref(&*child));
1005                next = iter.next_skipping_children();
1006            } else {
1007                next = iter.next();
1008            }
1009        }
1010
1011        // Step 5. Let newNode and newOffset be null.
1012        // Step 6. If originalStartNode is an inclusive ancestor of originalEndNode,
1013        // then set newNode to originalStartNode and newOffset to originalStartOffset.
1014        let (new_node, new_offset) = if start_node.is_inclusive_ancestor_of(&end_node) {
1015            (DomRoot::from_ref(&*start_node), start_offset)
1016        } else {
1017            // Step 7. Otherwise:
1018            fn compute_reference(start_node: &Node, end_node: &Node) -> (DomRoot<Node>, u32) {
1019                // Step 7.1. Let referenceNode be originalStartNode.
1020                let mut reference_node = DomRoot::from_ref(start_node);
1021                // Step 7.2. While referenceNode’s parent is non-null and
1022                // is not an inclusive ancestor of originalEndNode: set referenceNode to its parent.
1023                while let Some(parent) = reference_node.GetParentNode() {
1024                    if parent.is_inclusive_ancestor_of(end_node) {
1025                        // Step 7.3. Set newNode to referenceNode’s parent and newOffset to referenceNode’s index + 1.
1026                        return (parent, reference_node.index() + 1);
1027                    }
1028                    reference_node = parent;
1029                }
1030                unreachable!()
1031            }
1032
1033            compute_reference(&start_node, &end_node)
1034        };
1035
1036        // Step 8. Set this’s start and end to (newNode, newOffset).
1037        self.SetStart(&new_node, new_offset).unwrap();
1038        self.SetEnd(&new_node, new_offset).unwrap();
1039
1040        // Step 9. If originalStartNode is a CharacterData node,
1041        // then replace data of originalStartNode with originalStartOffset,
1042        // originalStartNode’s length − originalStartOffset, and the empty string.
1043        if let Some(text) = start_node.downcast::<CharacterData>() {
1044            text.ReplaceData(
1045                start_offset,
1046                start_node.len() - start_offset,
1047                DOMString::new(),
1048            )
1049            .unwrap();
1050        }
1051
1052        // Step 10. For each node of nodesToRemove, in tree order: remove node.
1053        for child in &*contained_children {
1054            child.remove_self(cx);
1055        }
1056
1057        // Step 11. If originalEndNode is a CharacterData node,
1058        // then replace data of originalEndNode with 0, originalEndOffset, and the empty string.
1059        if let Some(text) = end_node.downcast::<CharacterData>() {
1060            text.ReplaceData(0, end_offset, DOMString::new()).unwrap();
1061        }
1062
1063        Ok(())
1064    }
1065
1066    /// <https://dom.spec.whatwg.org/#dom-range-surroundcontents>
1067    fn SurroundContents(&self, cx: &mut JSContext, new_parent: &Node) -> ErrorResult {
1068        // Step 1.
1069        let start = self.start_container();
1070        let end = self.end_container();
1071
1072        if start
1073            .inclusive_ancestors(ShadowIncluding::No)
1074            .any(|n| !n.is_inclusive_ancestor_of(&end) && !n.is::<Text>()) ||
1075            end.inclusive_ancestors(ShadowIncluding::No)
1076                .any(|n| !n.is_inclusive_ancestor_of(&start) && !n.is::<Text>())
1077        {
1078            return Err(Error::InvalidState(None));
1079        }
1080
1081        // Step 2.
1082        match new_parent.type_id() {
1083            NodeTypeId::Document(_) |
1084            NodeTypeId::DocumentType |
1085            NodeTypeId::DocumentFragment(_) => {
1086                return Err(Error::InvalidNodeType(None));
1087            },
1088            _ => (),
1089        }
1090
1091        // Step 3.
1092        let fragment = self.ExtractContents(cx)?;
1093
1094        // Step 4.
1095        Node::replace_all(cx, None, new_parent);
1096
1097        // Step 5.
1098        self.InsertNode(cx, new_parent)?;
1099
1100        // Step 6.
1101        new_parent.AppendChild(cx, fragment.upcast())?;
1102
1103        // Step 7.
1104        self.SelectNode(new_parent)
1105    }
1106
1107    /// <https://dom.spec.whatwg.org/#dom-range-stringifier>
1108    fn Stringifier(&self) -> DOMString {
1109        let start_node = self.start_container();
1110        let end_node = self.end_container();
1111
1112        // Step 1. Let string be the empty string.
1113        let mut s = DOMString::new();
1114
1115        if let Some(text_node) = start_node.downcast::<Text>() {
1116            let char_data = text_node.upcast::<CharacterData>();
1117
1118            // Step 2. If this’s start node is this’s end node and it is a Text node,
1119            // then return the substring of that Text node’s data beginning at
1120            // this’s start offset and ending at this’s end offset.
1121            if start_node == end_node {
1122                return char_data
1123                    .SubstringData(self.start_offset(), self.end_offset() - self.start_offset())
1124                    .unwrap();
1125            }
1126
1127            // Step 3. If this’s start node is a Text node, then append the substring of
1128            // that node’s data from this’s start offset until the end to string.
1129            s.push_str(
1130                &char_data
1131                    .SubstringData(
1132                        self.start_offset(),
1133                        char_data.Length() - self.start_offset(),
1134                    )
1135                    .unwrap()
1136                    .str(),
1137            );
1138        }
1139
1140        // Step 4. Append the concatenation of the data of all Text nodes that are contained in this,
1141        // in tree order, to string.
1142        let ancestor = self.CommonAncestorContainer();
1143        let iter = start_node
1144            .following_nodes(&ancestor)
1145            .filter_map(DomRoot::downcast::<Text>);
1146
1147        for child in iter {
1148            if self.contains(child.upcast()) {
1149                s.push_str(&child.upcast::<CharacterData>().Data().str());
1150            }
1151        }
1152
1153        // Step 5. If this’s end node is a Text node, then append the substring of
1154        // that node’s data from its start until this’s end offset to string.
1155        if let Some(text_node) = end_node.downcast::<Text>() {
1156            let char_data = text_node.upcast::<CharacterData>();
1157            s.push_str(&char_data.SubstringData(0, self.end_offset()).unwrap().str());
1158        }
1159
1160        // Step 6. Return string.
1161        s
1162    }
1163
1164    /// <https://html.spec.whatwg.org/multipage/#dom-range-createcontextualfragment>
1165    fn CreateContextualFragment(
1166        &self,
1167        cx: &mut JSContext,
1168        fragment: TrustedHTMLOrString,
1169    ) -> Fallible<DomRoot<DocumentFragment>> {
1170        // Step 2. Let node be this's start node.
1171        //
1172        // Required to obtain the global, so we do this first. Shouldn't be an
1173        // observable difference.
1174        let node = self.start_container();
1175
1176        // Step 1. Let compliantString be the result of invoking the
1177        // Get Trusted Type compliant string algorithm with TrustedHTML,
1178        // this's relevant global object, string, "Range createContextualFragment", and "script".
1179        let fragment = TrustedHTML::get_trusted_type_compliant_string(
1180            cx,
1181            node.owner_window().upcast(),
1182            fragment,
1183            "Range createContextualFragment",
1184        )?;
1185
1186        let owner_doc = node.owner_doc();
1187
1188        // Step 3. Let element be null.
1189        // Step 4. If node implements Element, set element to node.
1190        // Step 5. Otherwise, if node implements Text or Comment, set element to node's parent element.
1191        let element = match node.type_id() {
1192            NodeTypeId::Element(_) => Some(DomRoot::downcast::<Element>(node).unwrap()),
1193            NodeTypeId::CharacterData(CharacterDataTypeId::Comment) |
1194            NodeTypeId::CharacterData(CharacterDataTypeId::Text(_)) => node.GetParentElement(),
1195            _ => None,
1196        };
1197
1198        // Step 6. If element is null or all of the following are true:
1199        let element = Element::fragment_parsing_context(cx, &owner_doc, element.as_deref());
1200
1201        // Step 7. Let fragment node be the result of invoking the fragment parsing algorithm steps with element and compliantString.
1202        let fragment_node = element.parse_fragment(fragment, cx)?;
1203
1204        // Step 8. For each script of fragment node's script element descendants:
1205        for node in fragment_node
1206            .upcast::<Node>()
1207            .traverse_preorder(ShadowIncluding::No)
1208        {
1209            if let Some(script) = node.downcast::<HTMLScriptElement>() {
1210                // Step 8.1. Set script's already started to false.
1211                script.set_already_started(false);
1212                // Step 8.2. Set script's parser document to null.
1213                script.set_parser_inserted(false);
1214            }
1215        }
1216
1217        // Step 9. Return fragment node.
1218        Ok(fragment_node)
1219    }
1220
1221    /// <https://drafts.csswg.org/cssom-view/#dom-range-getclientrects>
1222    fn GetClientRects(&self, cx: &mut JSContext) -> DomRoot<DOMRectList> {
1223        let start = self.start_container();
1224        let window = start.owner_window();
1225
1226        let client_rects = self
1227            .client_rects()
1228            .map(|rect| {
1229                DOMRect::new(
1230                    window.upcast(),
1231                    rect.origin.x.to_f64_px(),
1232                    rect.origin.y.to_f64_px(),
1233                    rect.size.width.to_f64_px(),
1234                    rect.size.height.to_f64_px(),
1235                    CanGc::from_cx(cx),
1236                )
1237            })
1238            .collect();
1239
1240        DOMRectList::new(&window, client_rects, CanGc::from_cx(cx))
1241    }
1242
1243    /// <https://drafts.csswg.org/cssom-view/#dom-range-getboundingclientrect>
1244    fn GetBoundingClientRect(&self, cx: &mut JSContext) -> DomRoot<DOMRect> {
1245        let window = self.start_container().owner_window();
1246
1247        // Step 1. Let list be the result of invoking getClientRects() on the same range this method was invoked on.
1248        let list = self.client_rects();
1249
1250        // Step 2. If list is empty return a DOMRect object whose x, y, width and height members are zero.
1251        // Step 3. If all rectangles in list have zero width or height, return the first rectangle in list.
1252        // Step 4. Otherwise, return a DOMRect object describing the smallest rectangle that includes all
1253        // of the rectangles in list of which the height or width is not zero.
1254        let bounding_rect = list.fold(euclid::Rect::zero(), |acc, rect| acc.union(&rect));
1255
1256        DOMRect::new(
1257            window.upcast(),
1258            bounding_rect.origin.x.to_f64_px(),
1259            bounding_rect.origin.y.to_f64_px(),
1260            bounding_rect.size.width.to_f64_px(),
1261            bounding_rect.size.height.to_f64_px(),
1262            CanGc::from_cx(cx),
1263        )
1264    }
1265}
1266
1267#[derive(MallocSizeOf)]
1268pub(crate) struct WeakRangeVec {
1269    cell: RefCell<WeakRefVec<Range>>,
1270}
1271
1272impl Default for WeakRangeVec {
1273    fn default() -> Self {
1274        WeakRangeVec {
1275            cell: RefCell::new(WeakRefVec::new()),
1276        }
1277    }
1278}
1279
1280impl WeakRangeVec {
1281    /// Whether that vector of ranges is empty.
1282    pub(crate) fn is_empty(&self) -> bool {
1283        self.cell.borrow().is_empty()
1284    }
1285
1286    /// Used for steps 2.1-2. when inserting a node.
1287    /// <https://dom.spec.whatwg.org/#concept-node-insert>
1288    pub(crate) fn increase_above(&self, node: &Node, offset: u32, delta: u32) {
1289        self.map_offset_above(node, offset, |offset| offset + delta);
1290    }
1291
1292    /// Used for steps 4-5. when removing a node.
1293    /// <https://dom.spec.whatwg.org/#concept-node-remove>
1294    pub(crate) fn decrease_above(&self, node: &Node, offset: u32, delta: u32) {
1295        self.map_offset_above(node, offset, |offset| offset - delta);
1296    }
1297
1298    /// Used for steps 2-3. when removing a node.
1299    ///
1300    /// <https://dom.spec.whatwg.org/#concept-node-remove>
1301    pub(crate) fn drain_to_parent(&self, parent: &Node, offset: u32, child: &Node) {
1302        if self.is_empty() {
1303            return;
1304        }
1305
1306        let ranges = &mut *self.cell.borrow_mut();
1307
1308        ranges.update(|entry| {
1309            let range = entry.root().unwrap();
1310            if range.start().node() == parent || range.end().node() == parent {
1311                entry.remove();
1312            }
1313            if range.start().node() == child {
1314                range.report_change();
1315                range.start().set(parent, offset);
1316            }
1317            if range.end().node() == child {
1318                range.report_change();
1319                range.end().set(parent, offset);
1320            }
1321        });
1322
1323        parent.ranges().cell.borrow_mut().extend(ranges.drain(..));
1324    }
1325
1326    /// Used for steps 6.1-2. when normalizing a node.
1327    /// <https://dom.spec.whatwg.org/#dom-node-normalize>
1328    pub(crate) fn drain_to_preceding_text_sibling(&self, node: &Node, sibling: &Node, length: u32) {
1329        if self.is_empty() {
1330            return;
1331        }
1332
1333        let ranges = &mut *self.cell.borrow_mut();
1334
1335        ranges.update(|entry| {
1336            let range = entry.root().unwrap();
1337            if range.start().node() == sibling || range.end().node() == sibling {
1338                entry.remove();
1339            }
1340            if range.start().node() == node {
1341                range.report_change();
1342                range.start().set(sibling, range.start_offset() + length);
1343            }
1344            if range.end().node() == node {
1345                range.report_change();
1346                range.end().set(sibling, range.end_offset() + length);
1347            }
1348        });
1349
1350        sibling.ranges().cell.borrow_mut().extend(ranges.drain(..));
1351    }
1352
1353    /// Used for steps 6.3-4. when normalizing a node.
1354    /// <https://dom.spec.whatwg.org/#dom-node-normalize>
1355    pub(crate) fn move_to_text_child_at(
1356        &self,
1357        node: &Node,
1358        offset: u32,
1359        child: &Node,
1360        new_offset: u32,
1361    ) {
1362        let child_ranges = child.ranges();
1363        let mut child_ranges = child_ranges.cell.borrow_mut();
1364
1365        self.cell.borrow_mut().update(|entry| {
1366            let range = entry.root().unwrap();
1367
1368            let node_is_start = range.start().node() == node;
1369            let node_is_end = range.end().node() == node;
1370
1371            let move_start = node_is_start && range.start_offset() == offset;
1372            let move_end = node_is_end && range.end_offset() == offset;
1373
1374            let remove_from_node =
1375                move_start && (move_end || !node_is_end) || move_end && !node_is_start;
1376
1377            let already_in_child = range.start().node() == child || range.end().node() == child;
1378            let push_to_child = !already_in_child && (move_start || move_end);
1379
1380            if remove_from_node {
1381                let ref_ = entry.remove();
1382                if push_to_child {
1383                    child_ranges.push(ref_);
1384                }
1385            } else if push_to_child {
1386                child_ranges.push(WeakRef::new(&range));
1387            }
1388
1389            if move_start {
1390                range.report_change();
1391                range.start().set(child, new_offset);
1392            }
1393            if move_end {
1394                range.report_change();
1395                range.end().set(child, new_offset);
1396            }
1397        });
1398    }
1399
1400    /// Used for steps 8-11. when replacing character data.
1401    /// <https://dom.spec.whatwg.org/#concept-cd-replace>
1402    pub(crate) fn replace_code_units(
1403        &self,
1404        node: &Node,
1405        offset: u32,
1406        removed_code_units: u32,
1407        added_code_units: u32,
1408    ) {
1409        self.map_offset_above(node, offset, |range_offset| {
1410            if range_offset <= offset + removed_code_units {
1411                offset
1412            } else {
1413                range_offset + added_code_units - removed_code_units
1414            }
1415        });
1416    }
1417
1418    /// Used for steps 7.2-3. when splitting a text node.
1419    /// <https://dom.spec.whatwg.org/#concept-text-split>
1420    pub(crate) fn move_to_following_text_sibling_above(
1421        &self,
1422        node: &Node,
1423        offset: u32,
1424        sibling: &Node,
1425    ) {
1426        let sibling_ranges = sibling.ranges();
1427        let mut sibling_ranges = sibling_ranges.cell.borrow_mut();
1428
1429        self.cell.borrow_mut().update(|entry| {
1430            let range = entry.root().unwrap();
1431            let start_offset = range.start_offset();
1432            let end_offset = range.end_offset();
1433
1434            let node_is_start = range.start().node() == node;
1435            let node_is_end = range.end().node() == node;
1436
1437            let move_start = node_is_start && start_offset > offset;
1438            let move_end = node_is_end && end_offset > offset;
1439
1440            let remove_from_node =
1441                move_start && (move_end || !node_is_end) || move_end && !node_is_start;
1442
1443            let already_in_sibling =
1444                range.start().node() == sibling || range.end().node() == sibling;
1445            let push_to_sibling = !already_in_sibling && (move_start || move_end);
1446
1447            if remove_from_node {
1448                let ref_ = entry.remove();
1449                if push_to_sibling {
1450                    sibling_ranges.push(ref_);
1451                }
1452            } else if push_to_sibling {
1453                sibling_ranges.push(WeakRef::new(&range));
1454            }
1455
1456            if move_start {
1457                range.report_change();
1458                range.start().set(sibling, start_offset - offset);
1459            }
1460            if move_end {
1461                range.report_change();
1462                range.end().set(sibling, end_offset - offset);
1463            }
1464        });
1465    }
1466
1467    /// Used for steps 7.4-5. when splitting a text node.
1468    /// <https://dom.spec.whatwg.org/#concept-text-split>
1469    pub(crate) fn increment_at(&self, node: &Node, offset: u32) {
1470        self.cell.borrow_mut().update(|entry| {
1471            let range = entry.root().unwrap();
1472            if range.start().node() == node && offset == range.start_offset() {
1473                range.report_change();
1474                range.start().set_offset(offset + 1);
1475            }
1476            if range.end().node() == node && offset == range.end_offset() {
1477                range.report_change();
1478                range.end().set_offset(offset + 1);
1479            }
1480        });
1481    }
1482
1483    fn map_offset_above<F: FnMut(u32) -> u32>(&self, node: &Node, offset: u32, mut f: F) {
1484        self.cell.borrow_mut().update(|entry| {
1485            let range = entry.root().unwrap();
1486            let start_offset = range.start_offset();
1487            if range.start().node() == node && start_offset > offset {
1488                range.report_change();
1489                range.start().set_offset(f(start_offset));
1490            }
1491            let end_offset = range.end_offset();
1492            if range.end().node() == node && end_offset > offset {
1493                range.report_change();
1494                range.end().set_offset(f(end_offset));
1495            }
1496        });
1497    }
1498
1499    pub(crate) fn push(&self, ref_: WeakRef<Range>) {
1500        self.cell.borrow_mut().push(ref_);
1501    }
1502
1503    fn remove(&self, range: &Range) -> WeakRef<Range> {
1504        let mut ranges = self.cell.borrow_mut();
1505        let position = ranges.iter().position(|ref_| ref_ == range).unwrap();
1506        ranges.swap_remove(position)
1507    }
1508}
1509
1510#[expect(unsafe_code)]
1511unsafe impl JSTraceable for WeakRangeVec {
1512    unsafe fn trace(&self, _: *mut JSTracer) {
1513        self.cell.borrow_mut().retain_alive()
1514    }
1515}