script/dom/range/
abstractrange.rs1use std::cell::Cell;
6use std::cmp::{Ord, Ordering, PartialEq};
7
8use deny_public_fields::DenyPublicFields;
9use dom_struct::dom_struct;
10use js::context::NoGC;
11use script_bindings::reflector::Reflector;
12use servo_base::text::Utf16CodeUnits;
13
14use crate::dom::bindings::codegen::Bindings::AbstractRangeBinding::AbstractRangeMethods;
15use crate::dom::bindings::codegen::Bindings::NodeBinding::{NodeConstants, NodeMethods};
16use crate::dom::bindings::root::{DomRoot, MutDom};
17use crate::dom::iterators::ShadowIncluding;
18use crate::dom::node::Node;
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 start(&self) -> &BoundaryPoint {
42 &self.start
43 }
44
45 pub(crate) fn end(&self) -> &BoundaryPoint {
46 &self.end
47 }
48}
49
50impl AbstractRangeMethods<crate::DomTypeHolder> for AbstractRange {
51 fn StartContainer(&self) -> DomRoot<Node> {
53 self.start.node.get()
54 }
55
56 fn StartOffset(&self) -> u32 {
58 self.start.offset.get()
59 }
60
61 fn EndContainer(&self) -> DomRoot<Node> {
63 self.end.node.get()
64 }
65
66 fn EndOffset(&self) -> u32 {
68 self.end.offset.get()
69 }
70
71 fn Collapsed(&self) -> bool {
73 self.start == self.end
75 }
76}
77
78#[derive(DenyPublicFields, JSTraceable, MallocSizeOf)]
80#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
81pub(crate) struct BoundaryPoint {
82 node: MutDom<Node>,
84 offset: Cell<u32>,
86}
87
88impl BoundaryPoint {
89 pub(crate) fn new(node: &Node, offset: u32) -> BoundaryPoint {
90 debug_assert!(!node.is_doctype());
91 BoundaryPoint {
92 node: MutDom::new(node),
93 offset: Cell::new(offset),
94 }
95 }
96
97 pub(crate) fn set(&self, node: &Node, offset: u32) {
98 self.node.set(node);
99 self.set_offset(offset);
100 }
101
102 pub(crate) fn offset(&self) -> Utf16CodeUnits {
103 Utf16CodeUnits::from(self.offset.get())
104 }
105
106 pub(crate) fn set_offset(&self, offset: u32) {
107 self.offset.set(offset);
108 }
109
110 pub(crate) fn node(&self) -> &MutDom<Node> {
111 &self.node
112 }
113
114 pub(crate) fn partial_cmp(&self, no_gc: &NoGC, other: &Self) -> Option<Ordering> {
115 Some(bp_position(
116 no_gc,
117 &self.node.get(),
118 self.offset.get(),
119 &other.node.get(),
120 other.offset.get(),
121 ))
122 }
123}
124
125impl PartialEq for BoundaryPoint {
127 fn eq(&self, other: &Self) -> bool {
128 self.node.get() == other.node.get() && self.offset.get() == other.offset.get()
130 }
131}
132
133pub(crate) fn bp_position(
135 no_gc: &NoGC,
136 a_node: &Node,
137 a_offset: u32,
138 b_node: &Node,
139 b_offset: u32,
140) -> Ordering {
141 debug_assert!(
143 a_node.GetRootNode(&Default::default()) == b_node.GetRootNode(&Default::default())
144 );
145
146 if a_node == b_node {
149 return a_offset.cmp(&b_offset);
150 }
151
152 let position = b_node.CompareDocumentPosition(no_gc, a_node);
153 assert!(
154 position & NodeConstants::DOCUMENT_POSITION_DISCONNECTED == 0,
155 "Nodes should be in the same tree"
156 );
157 if position & NodeConstants::DOCUMENT_POSITION_FOLLOWING != 0 {
158 return match bp_position(no_gc, b_node, b_offset, a_node, a_offset) {
162 Ordering::Less => Ordering::Greater,
163 Ordering::Greater => Ordering::Less,
164 Ordering::Equal => unreachable!("Should be impossible due to Step 2."),
165 };
166 } else if position & NodeConstants::DOCUMENT_POSITION_CONTAINS != 0 {
167 let mut b_ancestors = b_node.inclusive_ancestors(ShadowIncluding::No);
171 let child = b_ancestors
172 .find(|child| &*child.GetParentNode().unwrap() == a_node)
173 .unwrap();
174
175 if child.index() < a_offset {
177 return Ordering::Greater;
178 }
179 }
180
181 Ordering::Less
183}