script/dom/
selection_range.rs1use 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#[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#[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}