Skip to main content

script/dom/
selection_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::cmp::Ordering;
6
7use js::context::NoGC;
8use script_bindings::root::DomRoot;
9use servo_base::text::Utf32CodeUnitsOrNodeOffset;
10
11use crate::dom::abstractrange::BoundaryPoint;
12use crate::dom::bindings::root::Dom;
13use crate::dom::comparator::compare_dom_positions;
14use crate::dom::node::Node;
15use crate::dom::range::Range;
16use crate::dom::selection::compare_shadow_including_dom_positions;
17use crate::dom::traversal::FlatTreeForSelectionNoGcTraversal;
18
19/// A rooted selection boundary. This is similar to `SelectionBoundary`, but is rooted.
20/// This means it is appropriate to use in variables stored on the stack.
21#[derive(Clone, PartialEq)]
22pub(crate) struct RootedSelectionBoundary {
23    pub container: DomRoot<Node>,
24    pub offset: u32,
25}
26
27impl RootedSelectionBoundary {
28    pub(crate) fn start_of(node: &Node) -> Self {
29        Self {
30            container: DomRoot::from_ref(node),
31            offset: 0,
32        }
33    }
34
35    pub(crate) fn end_of(node: &Node) -> Self {
36        Self {
37            container: DomRoot::from_ref(node),
38            offset: node.len(),
39        }
40    }
41
42    pub(crate) fn new_with_utf16_offset(container: DomRoot<Node>, offset: u32) -> Self {
43        Self { container, offset }
44    }
45
46    pub(crate) fn new_with_utf32_offset(
47        container: DomRoot<Node>,
48        utf32_offset: Utf32CodeUnitsOrNodeOffset,
49    ) -> Self {
50        let offset = container.to_sibling_or_utf16_offset(utf32_offset);
51        Self { container, offset }
52    }
53
54    pub(crate) fn utf32_offset(&self) -> Utf32CodeUnitsOrNodeOffset {
55        self.container.to_sibling_or_utf32_offset(self.offset)
56    }
57
58    pub(crate) fn compare_dom_positions(&self, no_gc: &NoGC, other: &Self) -> Option<Ordering> {
59        compare_dom_positions::<FlatTreeForSelectionNoGcTraversal>(
60            no_gc,
61            &self.container,
62            self.offset,
63            &other.container,
64            other.offset,
65        )
66        .0
67    }
68
69    fn traced(&self) -> SelectionBoundary {
70        SelectionBoundary {
71            container: self.container.as_traced(),
72            offset: self.offset,
73        }
74    }
75
76    pub(crate) fn cmp(&self, no_gc: &NoGC, other: &Self) -> Ordering {
77        compare_shadow_including_dom_positions(
78            no_gc,
79            &self.container,
80            self.offset,
81            &other.container,
82            other.offset,
83        )
84    }
85}
86
87/// A selection boundary. This is similar to `BoundaryPoint`, but supports
88/// positions in the composed tree.
89#[derive(Clone, JSTraceable, PartialEq, MallocSizeOf)]
90#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
91pub(crate) struct SelectionBoundary {
92    pub container: Dom<Node>,
93    pub offset: u32,
94}
95
96impl SelectionBoundary {
97    pub(crate) fn new(container: &Node, offset: u32) -> Self {
98        Self {
99            container: Dom::from_ref(container),
100            offset,
101        }
102    }
103
104    pub(crate) fn rooted(&self) -> RootedSelectionBoundary {
105        RootedSelectionBoundary {
106            container: self.container.as_rooted(),
107            offset: self.offset,
108        }
109    }
110}
111
112impl PartialEq<BoundaryPoint> for SelectionBoundary {
113    fn eq(&self, boundary_point: &BoundaryPoint) -> bool {
114        *self.container == *boundary_point.node().get() && self.offset == boundary_point.offset().0
115    }
116}
117
118#[derive(JSTraceable, PartialEq, MallocSizeOf)]
119#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
120pub(crate) struct SelectionRange {
121    pub start: SelectionBoundary,
122    pub end: SelectionBoundary,
123}
124
125impl SelectionRange {
126    #[cfg_attr(crown, expect(crown::unrooted_must_root))]
127    pub(crate) fn new(start: SelectionBoundary, end: SelectionBoundary) -> Self {
128        Self { start, end }
129    }
130
131    pub(crate) fn new_from_rooted(
132        start: RootedSelectionBoundary,
133        end: RootedSelectionBoundary,
134    ) -> Self {
135        Self {
136            start: start.traced(),
137            end: end.traced(),
138        }
139    }
140
141    #[cfg_attr(crown, expect(crown::unrooted_must_root))]
142    pub(crate) fn collapsed_at(at: SelectionBoundary) -> Self {
143        Self {
144            start: at.clone(),
145            end: at,
146        }
147    }
148
149    pub(crate) fn collapsed(&self) -> bool {
150        self.start == self.end
151    }
152}
153
154impl From<&Range> for SelectionRange {
155    fn from(range: &Range) -> Self {
156        Self::new(
157            SelectionBoundary::new(&range.start_container(), range.start_offset()),
158            SelectionBoundary::new(&range.end_container(), range.end_offset()),
159        )
160    }
161}