1use std::cell::Ref;
8use std::cmp::Ordering;
9use std::fmt::Debug;
10use std::rc::Rc;
11
12use html5ever::{LocalName, Namespace, Prefix};
13use js::context::JSContext;
14use script_bindings::callback::ExceptionHandling;
15use script_bindings::codegen::GenericBindings::AttrBinding::AttrMethods;
16use script_bindings::codegen::GenericBindings::NodeBinding::{GetRootNodeOptions, NodeMethods};
17use script_bindings::root::Dom;
18use script_bindings::str::DOMString;
19use style::Atom;
20use style::dom::OpaqueNode;
21
22use crate::dom::attr::Attr;
23use crate::dom::bindings::codegen::Bindings::XPathNSResolverBinding::XPathNSResolver;
24use crate::dom::bindings::error::{Error, Fallible};
25use crate::dom::bindings::inheritance::Castable;
26use crate::dom::bindings::root::DomRoot;
27use crate::dom::comment::Comment;
28use crate::dom::document::Document;
29use crate::dom::element::Element;
30use crate::dom::element::attributes::storage::AttributeStorage;
31use crate::dom::iterators::{PrecedingNodeIterator, ShadowIncluding};
32use crate::dom::node::{Node, NodeTraits};
33use crate::dom::processinginstruction::ProcessingInstruction;
34use crate::dom::text::Text;
35
36pub(crate) type Value = xpath::Value<XPathWrapper<DomRoot<Node>>>;
37
38#[derive(Clone, Debug, Eq, PartialEq)]
41pub(crate) struct XPathWrapper<T>(pub T);
42
43pub(crate) struct XPathImplementation;
44
45impl xpath::Dom for XPathImplementation {
46 type Context = JSContext;
47 type Node = XPathWrapper<DomRoot<Node>>;
48 type NamespaceResolver = XPathWrapper<Rc<XPathNSResolver>>;
49}
50
51impl xpath::Node for XPathWrapper<DomRoot<Node>> {
52 type Context = JSContext;
53 type ProcessingInstruction = XPathWrapper<DomRoot<ProcessingInstruction>>;
54 type Document = XPathWrapper<DomRoot<Document>>;
55 type Attribute = XPathWrapper<DomRoot<Attr>>;
56 type Element = XPathWrapper<DomRoot<Element>>;
57 type Opaque = OpaqueNode;
59
60 fn is_comment(&self) -> bool {
61 self.0.is::<Comment>()
62 }
63
64 fn is_text(&self) -> bool {
65 self.0.is::<Text>()
66 }
67
68 fn text_content(&self) -> String {
69 self.0.GetTextContent().unwrap_or_default().into()
70 }
71
72 fn language(&self) -> Option<String> {
73 self.0.get_lang()
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(), ShadowIncluding::No)
124 .next_skipping_children();
125 let following_nodes = next_non_descendant_node
126 .clone()
127 .map(|node| node.following_nodes(owner_document.upcast(), ShadowIncluding::No))
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 cx: &mut JSContext,
184 id: &str,
185 ) -> impl Iterator<Item = XPathWrapper<DomRoot<Element>>> {
186 struct ElementIterator<'a> {
187 elements: Ref<'a, [Dom<Element>]>,
188 position: usize,
189 }
190
191 impl<'a> Iterator for ElementIterator<'a> {
192 type Item = XPathWrapper<DomRoot<Element>>;
193
194 fn next(&mut self) -> Option<Self::Item> {
195 let element = self.elements.get(self.position)?;
196 self.position += 1;
197 Some(element.as_rooted().into())
198 }
199 }
200
201 ElementIterator {
202 elements: self.0.get_elements_with_id(cx, &Atom::from(id)),
203 position: 0,
204 }
205 }
206}
207
208impl xpath::Element for XPathWrapper<DomRoot<Element>> {
209 type Context = JSContext;
210 type Node = XPathWrapper<DomRoot<Node>>;
211 type Attribute = XPathWrapper<DomRoot<Attr>>;
212
213 fn as_node(&self) -> Self::Node {
214 DomRoot::from_ref(self.0.upcast::<Node>()).into()
215 }
216
217 fn attributes(&self, cx: &mut JSContext) -> impl Iterator<Item = Self::Attribute> {
218 struct AttributeIterator<'a> {
219 attributes: &'a AttributeStorage,
220 position: usize,
221 }
222
223 impl<'a> Iterator for AttributeIterator<'a> {
224 type Item = XPathWrapper<DomRoot<Attr>>;
225
226 fn next(&mut self) -> Option<Self::Item> {
227 let entries = self.attributes.borrow();
228 let entry = entries.get(self.position)?;
229 self.position += 1;
230 Some(DomRoot::from_ref(entry.as_attr().unwrap()).into())
231 }
232
233 fn size_hint(&self) -> (usize, Option<usize>) {
234 let exact_length = self.attributes.borrow().len() - self.position;
235 (exact_length, Some(exact_length))
236 }
237 }
238
239 AttributeIterator {
241 attributes: self.0.dom_attrs(cx),
242 position: 0,
243 }
244 }
245
246 fn prefix(&self) -> Option<Prefix> {
247 self.0.prefix().clone()
248 }
249
250 fn namespace(&self) -> Namespace {
251 self.0.namespace().clone()
252 }
253
254 fn local_name(&self) -> LocalName {
255 self.0.local_name().clone()
256 }
257
258 fn is_html_element_in_html_document(&self) -> bool {
259 self.0.is_html_element() && self.0.owner_document().is_html_document()
260 }
261}
262
263impl xpath::Attribute for XPathWrapper<DomRoot<Attr>> {
264 type Node = XPathWrapper<DomRoot<Node>>;
265
266 fn as_node(&self) -> Self::Node {
267 XPathWrapper(DomRoot::from_ref(self.0.upcast::<Node>()))
268 }
269
270 fn prefix(&self) -> Option<Prefix> {
271 self.0.prefix().cloned()
272 }
273
274 fn namespace(&self) -> Namespace {
275 self.0.namespace().clone()
276 }
277
278 fn local_name(&self) -> LocalName {
279 self.0.local_name().clone()
280 }
281}
282
283impl xpath::NamespaceResolver for XPathWrapper<Rc<XPathNSResolver>> {
284 type Context = JSContext;
285
286 fn resolve_namespace_prefix(&self, cx: &mut JSContext, prefix: &str) -> Option<String> {
287 self.0
288 .LookupNamespaceURI__(cx, Some(DOMString::from(prefix)), ExceptionHandling::Report)
289 .ok()
290 .flatten()
291 .map(String::from)
292 }
293}
294
295impl xpath::ProcessingInstruction for XPathWrapper<DomRoot<ProcessingInstruction>> {
296 fn target(&self) -> String {
297 self.0.target().to_owned().into()
298 }
299}
300
301impl<T> From<T> for XPathWrapper<T> {
302 fn from(value: T) -> Self {
303 Self(value)
304 }
305}
306
307impl<T> XPathWrapper<T> {
308 pub(crate) fn into_inner(self) -> T {
309 self.0
310 }
311}
312
313pub(crate) fn parse_expression(
314 cx: &mut JSContext,
315 expression: &str,
316 resolver: Option<Rc<XPathNSResolver>>,
317 is_in_html_document: bool,
318) -> Fallible<xpath::Expression> {
319 xpath::parse(
320 cx,
321 expression,
322 resolver.map(XPathWrapper),
323 is_in_html_document,
324 )
325 .map_err(|error| match error {
326 xpath::ParserError::FailedToResolveNamespacePrefix => Error::Namespace(None),
327 _ => Error::Syntax(Some(format!("Failed to parse XPath expression: {error:?}"))),
328 })
329}
330
331enum PrecedingNodeIteratorWithoutAncestors {
332 Done,
333 NotDone {
334 current: DomRoot<Node>,
335 subtree_iterator: Option<PrecedingNodeIterator>,
338 },
339}
340
341fn previous_non_ancestor_node(node: &Node) -> Option<DomRoot<Node>> {
343 let mut current = DomRoot::from_ref(node);
344 loop {
345 if let Some(previous_sibling) = current.GetPreviousSibling() {
346 return Some(previous_sibling);
347 }
348
349 current = current.GetParentNode()?;
350 }
351}
352
353impl PrecedingNodeIteratorWithoutAncestors {
354 fn new(node: &Node) -> Self {
355 let Some(current) = previous_non_ancestor_node(node) else {
356 return Self::Done;
357 };
358
359 Self::NotDone {
360 subtree_iterator: current
361 .descending_last_children()
362 .last()
363 .map(|node| node.preceding_nodes(¤t)),
364 current,
365 }
366 }
367}
368
369impl Iterator for PrecedingNodeIteratorWithoutAncestors {
370 type Item = DomRoot<Node>;
371
372 fn next(&mut self) -> Option<Self::Item> {
373 let Self::NotDone {
374 current,
375 subtree_iterator,
376 } = self
377 else {
378 return None;
379 };
380
381 if let Some(next_node) = subtree_iterator
382 .as_mut()
383 .and_then(|iterator| iterator.next())
384 {
385 return Some(next_node);
386 }
387
388 let Some(next_subtree) = previous_non_ancestor_node(current) else {
391 *self = Self::Done;
392 return None;
393 };
394
395 *current = next_subtree;
396 *subtree_iterator = current
397 .descending_last_children()
398 .last()
399 .map(|node| node.preceding_nodes(current));
400
401 self.next()
402 }
403}