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