Skip to main content

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