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