Skip to main content

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