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        self.start == self.end
93    }
94}
95
96/// <https://dom.spec.whatwg.org/#concept-range-bp>
97#[derive(DenyPublicFields, JSTraceable, MallocSizeOf)]
98#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
99pub(crate) struct BoundaryPoint {
100    /// <https://dom.spec.whatwg.org/#boundary-point-node>
101    node: MutDom<Node>,
102    /// <https://dom.spec.whatwg.org/#concept-range-bp-offset>
103    offset: Cell<u32>,
104}
105
106impl BoundaryPoint {
107    fn new(node: &Node, offset: u32) -> BoundaryPoint {
108        debug_assert!(!node.is_doctype());
109        BoundaryPoint {
110            node: MutDom::new(node),
111            offset: Cell::new(offset),
112        }
113    }
114
115    pub(crate) fn set(&self, node: &Node, offset: u32) {
116        self.node.set(node);
117        self.set_offset(offset);
118    }
119
120    pub(crate) fn set_offset(&self, offset: u32) {
121        self.offset.set(offset);
122    }
123
124    pub(crate) fn node(&self) -> &MutDom<Node> {
125        &self.node
126    }
127}
128
129#[cfg_attr(crown, allow(crown::unrooted_must_root))]
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#[cfg_attr(crown, allow(crown::unrooted_must_root))]
142impl PartialEq for BoundaryPoint {
143    fn eq(&self, other: &Self) -> bool {
144        self.node.get() == other.node.get() && self.offset.get() == other.offset.get()
145    }
146}
147
148/// <https://dom.spec.whatwg.org/#concept-range-bp-position>
149pub(crate) fn bp_position(
150    a_node: &Node,
151    a_offset: u32,
152    b_node: &Node,
153    b_offset: u32,
154) -> Option<Ordering> {
155    if std::ptr::eq(a_node, b_node) {
156        // Step 1.
157        return Some(a_offset.cmp(&b_offset));
158    }
159    let position = b_node.CompareDocumentPosition(a_node);
160    if position & NodeConstants::DOCUMENT_POSITION_DISCONNECTED != 0 {
161        // No order is defined for nodes not in the same tree.
162        None
163    } else if position & NodeConstants::DOCUMENT_POSITION_FOLLOWING != 0 {
164        // Step 2.
165        match bp_position(b_node, b_offset, a_node, a_offset).unwrap() {
166            Ordering::Less => Some(Ordering::Greater),
167            Ordering::Greater => Some(Ordering::Less),
168            Ordering::Equal => unreachable!(),
169        }
170    } else if position & NodeConstants::DOCUMENT_POSITION_CONTAINS != 0 {
171        // Step 3-1, 3-2.
172        let mut b_ancestors = b_node.inclusive_ancestors(ShadowIncluding::No);
173        let child = b_ancestors
174            .find(|child| &*child.GetParentNode().unwrap() == a_node)
175            .unwrap();
176        // Step 3-3.
177        if child.index() < a_offset {
178            Some(Ordering::Greater)
179        } else {
180            // Step 4.
181            Some(Ordering::Less)
182        }
183    } else {
184        // Step 4.
185        Some(Ordering::Less)
186    }
187}