Skip to main content

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