script/dom/
abstractrange.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::Cell;
6use std::cmp::{Ord, Ordering, PartialEq, PartialOrd};
7
8use deny_public_fields::DenyPublicFields;
9use dom_struct::dom_struct;
10
11use crate::dom::bindings::codegen::Bindings::AbstractRangeBinding::AbstractRangeMethods;
12use crate::dom::bindings::codegen::Bindings::NodeBinding::{NodeConstants, NodeMethods};
13use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
14use crate::dom::bindings::root::{DomRoot, MutDom};
15use crate::dom::document::Document;
16use crate::dom::node::{Node, ShadowIncluding};
17use crate::script_runtime::CanGc;
18
19#[dom_struct]
20pub(crate) struct AbstractRange {
21    reflector_: Reflector,
22    start: BoundaryPoint,
23    end: BoundaryPoint,
24}
25
26impl AbstractRange {
27    pub(crate) fn new_inherited(
28        start_container: &Node,
29        start_offset: u32,
30        end_container: &Node,
31        end_offset: u32,
32    ) -> AbstractRange {
33        AbstractRange {
34            reflector_: Reflector::new(),
35            start: BoundaryPoint::new(start_container, start_offset),
36            end: BoundaryPoint::new(end_container, end_offset),
37        }
38    }
39
40    pub(crate) fn new(
41        document: &Document,
42        start_container: &Node,
43        start_offset: u32,
44        end_container: &Node,
45        end_offset: u32,
46        can_gc: CanGc,
47    ) -> DomRoot<AbstractRange> {
48        reflect_dom_object(
49            Box::new(AbstractRange::new_inherited(
50                start_container,
51                start_offset,
52                end_container,
53                end_offset,
54            )),
55            document.window(),
56            can_gc,
57        )
58    }
59
60    pub(crate) fn start(&self) -> &BoundaryPoint {
61        &self.start
62    }
63
64    pub(crate) fn end(&self) -> &BoundaryPoint {
65        &self.end
66    }
67}
68
69impl AbstractRangeMethods<crate::DomTypeHolder> for AbstractRange {
70    /// <https://dom.spec.whatwg.org/#dom-range-startcontainer>
71    fn StartContainer(&self) -> DomRoot<Node> {
72        self.start.node.get()
73    }
74
75    /// <https://dom.spec.whatwg.org/#dom-range-startoffset>
76    fn StartOffset(&self) -> u32 {
77        self.start.offset.get()
78    }
79
80    /// <https://dom.spec.whatwg.org/#dom-range-endcontainer>
81    fn EndContainer(&self) -> DomRoot<Node> {
82        self.end.node.get()
83    }
84
85    /// <https://dom.spec.whatwg.org/#dom-range-endoffset>
86    fn EndOffset(&self) -> u32 {
87        self.end.offset.get()
88    }
89
90    /// <https://dom.spec.whatwg.org/#dom-range-collapsed>
91    fn Collapsed(&self) -> bool {
92        // > The collapsed getter steps are to return true if this is collapsed; otherwise false.
93        self.start == self.end
94    }
95}
96
97/// <https://dom.spec.whatwg.org/#concept-range-bp>
98#[derive(DenyPublicFields, JSTraceable, MallocSizeOf)]
99#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
100pub(crate) struct BoundaryPoint {
101    /// <https://dom.spec.whatwg.org/#boundary-point-node>
102    node: MutDom<Node>,
103    /// <https://dom.spec.whatwg.org/#concept-range-bp-offset>
104    offset: Cell<u32>,
105}
106
107impl BoundaryPoint {
108    fn new(node: &Node, offset: u32) -> BoundaryPoint {
109        debug_assert!(!node.is_doctype());
110        BoundaryPoint {
111            node: MutDom::new(node),
112            offset: Cell::new(offset),
113        }
114    }
115
116    pub(crate) fn set(&self, node: &Node, offset: u32) {
117        self.node.set(node);
118        self.set_offset(offset);
119    }
120
121    pub(crate) fn set_offset(&self, offset: u32) {
122        self.offset.set(offset);
123    }
124
125    pub(crate) fn node(&self) -> &MutDom<Node> {
126        &self.node
127    }
128}
129
130impl PartialOrd for BoundaryPoint {
131    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
132        bp_position(
133            &self.node.get(),
134            self.offset.get(),
135            &other.node.get(),
136            other.offset.get(),
137        )
138    }
139}
140
141/// <https://dom.spec.whatwg.org/#range-collapsed>
142impl PartialEq for BoundaryPoint {
143    fn eq(&self, other: &Self) -> bool {
144        // > A range is collapsed if its start node is its end node and its start offset is its end offset.
145        self.node.get() == other.node.get() && self.offset.get() == other.offset.get()
146    }
147}
148
149/// <https://dom.spec.whatwg.org/#concept-range-bp-position>
150pub(crate) fn bp_position(
151    a_node: &Node,
152    a_offset: u32,
153    b_node: &Node,
154    b_offset: u32,
155) -> Option<Ordering> {
156    if std::ptr::eq(a_node, b_node) {
157        // Step 1.
158        return Some(a_offset.cmp(&b_offset));
159    }
160    let position = b_node.CompareDocumentPosition(a_node);
161    if position & NodeConstants::DOCUMENT_POSITION_DISCONNECTED != 0 {
162        // No order is defined for nodes not in the same tree.
163        None
164    } else if position & NodeConstants::DOCUMENT_POSITION_FOLLOWING != 0 {
165        // Step 2.
166        match bp_position(b_node, b_offset, a_node, a_offset).unwrap() {
167            Ordering::Less => Some(Ordering::Greater),
168            Ordering::Greater => Some(Ordering::Less),
169            Ordering::Equal => unreachable!(),
170        }
171    } else if position & NodeConstants::DOCUMENT_POSITION_CONTAINS != 0 {
172        // Step 3-1, 3-2.
173        let mut b_ancestors = b_node.inclusive_ancestors(ShadowIncluding::No);
174        let child = b_ancestors
175            .find(|child| &*child.GetParentNode().unwrap() == a_node)
176            .unwrap();
177        // Step 3-3.
178        if child.index() < a_offset {
179            Some(Ordering::Greater)
180        } else {
181            // Step 4.
182            Some(Ordering::Less)
183        }
184    } else {
185        // Step 4.
186        Some(Ordering::Less)
187    }
188}