1use std::cell::Ref;
8use std::cmp::Ordering;
9use std::fmt::Debug;
10use std::rc::Rc;
11
12use html5ever::{LocalName, Namespace, Prefix};
13use script_bindings::callback::ExceptionHandling;
14use script_bindings::codegen::GenericBindings::AttrBinding::AttrMethods;
15use script_bindings::codegen::GenericBindings::NodeBinding::{GetRootNodeOptions, NodeMethods};
16use script_bindings::root::Dom;
17use script_bindings::str::DOMString;
18use style::Atom;
19use style::dom::OpaqueNode;
20
21use crate::dom::attr::Attr;
22use crate::dom::bindings::codegen::Bindings::XPathNSResolverBinding::XPathNSResolver;
23use crate::dom::bindings::error::{Error, Fallible};
24use crate::dom::bindings::inheritance::Castable;
25use crate::dom::bindings::root::DomRoot;
26use crate::dom::comment::Comment;
27use crate::dom::document::Document;
28use crate::dom::element::Element;
29use crate::dom::element::attributes::storage::AttributeStorage;
30use crate::dom::node::{Node, NodeTraits, PrecedingNodeIterator, ShadowIncluding};
31use crate::dom::processinginstruction::ProcessingInstruction;
32use crate::dom::text::Text;
33
34pub(crate) type Value = xpath::Value<XPathWrapper<DomRoot<Node>>>;
35
36#[derive(Clone, Debug, Eq, PartialEq)]
39pub(crate) struct XPathWrapper<T>(pub T);
40
41pub(crate) struct XPathImplementation;
42
43impl xpath::Dom for XPathImplementation {
44 type Node = XPathWrapper<DomRoot<Node>>;
45 type NamespaceResolver = XPathWrapper<Rc<XPathNSResolver>>;
46}
47
48impl xpath::Node for XPathWrapper<DomRoot<Node>> {
49 type ProcessingInstruction = XPathWrapper<DomRoot<ProcessingInstruction>>;
50 type Document = XPathWrapper<DomRoot<Document>>;
51 type Attribute = XPathWrapper<DomRoot<Attr>>;
52 type Element = XPathWrapper<DomRoot<Element>>;
53 type Opaque = OpaqueNode;
55
56 fn is_comment(&self) -> bool {
57 self.0.is::<Comment>()
58 }
59
60 fn is_text(&self) -> bool {
61 self.0.is::<Text>()
62 }
63
64 fn text_content(&self) -> String {
65 self.0.GetTextContent().unwrap_or_default().into()
66 }
67
68 #[expect(unsafe_code)]
69 fn language(&self) -> Option<String> {
70 let mut cx = unsafe { script_bindings::script_runtime::temp_cx() };
71 let cx = &mut cx;
72
73 self.0.get_lang(cx)
74 }
75
76 fn parent(&self) -> Option<Self> {
77 if let Some(attribute) = self.0.downcast::<Attr>() {
80 return attribute
81 .GetOwnerElement()
82 .map(DomRoot::upcast)
83 .map(XPathWrapper);
84 }
85
86 self.0.GetParentNode().map(XPathWrapper)
87 }
88
89 fn children(&self) -> impl Iterator<Item = Self> {
90 self.0.children().map(XPathWrapper)
91 }
92
93 fn compare_tree_order(&self, other: &Self) -> Ordering {
94 if self == other {
95 Ordering::Equal
96 } else if self.0.is_before(&other.0) {
97 Ordering::Less
98 } else {
99 Ordering::Greater
100 }
101 }
102
103 fn traverse_preorder(&self) -> impl Iterator<Item = Self> {
104 self.0
105 .traverse_preorder(ShadowIncluding::No)
106 .map(XPathWrapper)
107 }
108
109 fn inclusive_ancestors(&self) -> impl Iterator<Item = Self> {
110 self.0
111 .inclusive_ancestors(ShadowIncluding::No)
112 .map(XPathWrapper)
113 }
114
115 fn preceding_nodes(&self) -> impl Iterator<Item = Self> {
116 PrecedingNodeIteratorWithoutAncestors::new(&self.0).map(XPathWrapper)
117 }
118
119 fn following_nodes(&self) -> impl Iterator<Item = Self> {
120 let owner_document = self.0.owner_document();
121 let next_non_descendant_node = self
122 .0
123 .following_nodes(owner_document.upcast())
124 .next_skipping_children();
125 let following_nodes = next_non_descendant_node
126 .clone()
127 .map(|node| node.following_nodes(owner_document.upcast()))
128 .into_iter()
129 .flatten();
130 next_non_descendant_node
131 .into_iter()
132 .chain(following_nodes)
133 .map(XPathWrapper)
134 }
135
136 fn preceding_siblings(&self) -> impl Iterator<Item = Self> {
137 self.0.preceding_siblings().map(XPathWrapper)
138 }
139
140 fn following_siblings(&self) -> impl Iterator<Item = Self> {
141 self.0.following_siblings().map(XPathWrapper)
142 }
143
144 fn owner_document(&self) -> Self::Document {
145 XPathWrapper(self.0.owner_document())
146 }
147
148 fn to_opaque(&self) -> Self::Opaque {
149 self.0.to_opaque()
150 }
151
152 fn as_processing_instruction(&self) -> Option<Self::ProcessingInstruction> {
153 self.0
154 .downcast::<ProcessingInstruction>()
155 .map(DomRoot::from_ref)
156 .map(XPathWrapper)
157 }
158
159 fn as_attribute(&self) -> Option<Self::Attribute> {
160 self.0
161 .downcast::<Attr>()
162 .map(DomRoot::from_ref)
163 .map(XPathWrapper)
164 }
165
166 fn as_element(&self) -> Option<Self::Element> {
167 self.0
168 .downcast::<Element>()
169 .map(DomRoot::from_ref)
170 .map(XPathWrapper)
171 }
172
173 fn get_root_node(&self) -> Self {
174 XPathWrapper(self.0.GetRootNode(&GetRootNodeOptions::empty()))
175 }
176}
177
178impl xpath::Document for XPathWrapper<DomRoot<Document>> {
179 type Node = XPathWrapper<DomRoot<Node>>;
180
181 fn get_elements_with_id(
182 &self,
183 id: &str,
184 ) -> impl Iterator<Item = XPathWrapper<DomRoot<Element>>> {
185 struct ElementIterator<'a> {
186 elements: Ref<'a, [Dom<Element>]>,
187 position: usize,
188 }
189
190 impl<'a> Iterator for ElementIterator<'a> {
191 type Item = XPathWrapper<DomRoot<Element>>;
192
193 fn next(&mut self) -> Option<Self::Item> {
194 let element = self.elements.get(self.position)?;
195 self.position += 1;
196 Some(element.as_rooted().into())
197 }
198 }
199
200 ElementIterator {
201 elements: self.0.get_elements_with_id(&Atom::from(id)),
202 position: 0,
203 }
204 }
205}
206
207impl xpath::Element for XPathWrapper<DomRoot<Element>> {
208 type Node = XPathWrapper<DomRoot<Node>>;
209 type Attribute = XPathWrapper<DomRoot<Attr>>;
210
211 fn as_node(&self) -> Self::Node {
212 DomRoot::from_ref(self.0.upcast::<Node>()).into()
213 }
214
215 #[expect(unsafe_code)]
216 fn attributes(&self) -> impl Iterator<Item = Self::Attribute> {
217 let mut cx = unsafe { script_bindings::script_runtime::temp_cx() };
218 let cx = &mut cx;
219
220 struct AttributeIterator<'a> {
221 attributes: &'a AttributeStorage,
222 position: usize,
223 }
224
225 impl<'a> Iterator for AttributeIterator<'a> {
226 type Item = XPathWrapper<DomRoot<Attr>>;
227
228 fn next(&mut self) -> Option<Self::Item> {
229 let entries = self.attributes.borrow();
230 let entry = entries.get(self.position)?;
231 self.position += 1;
232 Some(DomRoot::from_ref(entry.as_attr().unwrap()).into())
233 }
234
235 fn size_hint(&self) -> (usize, Option<usize>) {
236 let exact_length = self.attributes.borrow().len() - self.position;
237 (exact_length, Some(exact_length))
238 }
239 }
240
241 AttributeIterator {
243 attributes: self.0.dom_attrs(cx),
244 position: 0,
245 }
246 }
247
248 fn prefix(&self) -> Option<Prefix> {
249 self.0.prefix().clone()
250 }
251
252 fn namespace(&self) -> Namespace {
253 self.0.namespace().clone()
254 }
255
256 fn local_name(&self) -> LocalName {
257 self.0.local_name().clone()
258 }
259
260 fn is_html_element_in_html_document(&self) -> bool {
261 self.0.is_html_element() && self.0.owner_document().is_html_document()
262 }
263}
264
265impl xpath::Attribute for XPathWrapper<DomRoot<Attr>> {
266 type Node = XPathWrapper<DomRoot<Node>>;
267
268 fn as_node(&self) -> Self::Node {
269 XPathWrapper(DomRoot::from_ref(self.0.upcast::<Node>()))
270 }
271
272 fn prefix(&self) -> Option<Prefix> {
273 self.0.prefix().cloned()
274 }
275
276 fn namespace(&self) -> Namespace {
277 self.0.namespace().clone()
278 }
279
280 fn local_name(&self) -> LocalName {
281 self.0.local_name().clone()
282 }
283}
284
285impl xpath::NamespaceResolver for XPathWrapper<Rc<XPathNSResolver>> {
286 #[expect(unsafe_code)]
287 fn resolve_namespace_prefix(&self, prefix: &str) -> Option<String> {
288 let mut cx = unsafe { script_bindings::script_runtime::temp_cx() };
289 let cx = &mut cx;
290
291 self.0
292 .LookupNamespaceURI__(cx, Some(DOMString::from(prefix)), ExceptionHandling::Report)
293 .ok()
294 .flatten()
295 .map(String::from)
296 }
297}
298
299impl xpath::ProcessingInstruction for XPathWrapper<DomRoot<ProcessingInstruction>> {
300 fn target(&self) -> String {
301 self.0.target().to_owned().into()
302 }
303}
304
305impl<T> From<T> for XPathWrapper<T> {
306 fn from(value: T) -> Self {
307 Self(value)
308 }
309}
310
311impl<T> XPathWrapper<T> {
312 pub(crate) fn into_inner(self) -> T {
313 self.0
314 }
315}
316
317pub(crate) fn parse_expression(
318 expression: &str,
319 resolver: Option<Rc<XPathNSResolver>>,
320 is_in_html_document: bool,
321) -> Fallible<xpath::Expression> {
322 xpath::parse(expression, resolver.map(XPathWrapper), is_in_html_document).map_err(|error| {
323 match error {
324 xpath::ParserError::FailedToResolveNamespacePrefix => Error::Namespace(None),
325 _ => Error::Syntax(Some(format!("Failed to parse XPath expression: {error:?}"))),
326 }
327 })
328}
329
330enum PrecedingNodeIteratorWithoutAncestors {
331 Done,
332 NotDone {
333 current: DomRoot<Node>,
334 subtree_iterator: Option<PrecedingNodeIterator>,
337 },
338}
339
340fn previous_non_ancestor_node(node: &Node) -> Option<DomRoot<Node>> {
342 let mut current = DomRoot::from_ref(node);
343 loop {
344 if let Some(previous_sibling) = current.GetPreviousSibling() {
345 return Some(previous_sibling);
346 }
347
348 current = current.GetParentNode()?;
349 }
350}
351
352impl PrecedingNodeIteratorWithoutAncestors {
353 fn new(node: &Node) -> Self {
354 let Some(current) = previous_non_ancestor_node(node) else {
355 return Self::Done;
356 };
357
358 Self::NotDone {
359 subtree_iterator: current
360 .descending_last_children()
361 .last()
362 .map(|node| node.preceding_nodes(¤t)),
363 current,
364 }
365 }
366}
367
368impl Iterator for PrecedingNodeIteratorWithoutAncestors {
369 type Item = DomRoot<Node>;
370
371 fn next(&mut self) -> Option<Self::Item> {
372 let Self::NotDone {
373 current,
374 subtree_iterator,
375 } = self
376 else {
377 return None;
378 };
379
380 if let Some(next_node) = subtree_iterator
381 .as_mut()
382 .and_then(|iterator| iterator.next())
383 {
384 return Some(next_node);
385 }
386
387 let Some(next_subtree) = previous_non_ancestor_node(current) else {
390 *self = Self::Done;
391 return None;
392 };
393
394 *current = next_subtree;
395 *subtree_iterator = current
396 .descending_last_children()
397 .last()
398 .map(|node| node.preceding_nodes(current));
399
400 self.next()
401 }
402}