1use std::cell::Ref;
8use std::cmp::Ordering;
9use std::fmt::Debug;
10use std::hash::Hash;
11use std::rc::Rc;
12
13use html5ever::{LocalName, Namespace, Prefix};
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::script_runtime::CanGc;
19use script_bindings::str::DOMString;
20use style::Atom;
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::node::{Node, NodeTraits, 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
54 fn is_comment(&self) -> bool {
55 self.0.is::<Comment>()
56 }
57
58 fn is_text(&self) -> bool {
59 self.0.is::<Text>()
60 }
61
62 fn text_content(&self) -> String {
63 self.0.GetTextContent().unwrap_or_default().into()
64 }
65
66 fn language(&self) -> Option<String> {
67 self.0.get_lang()
68 }
69
70 fn parent(&self) -> Option<Self> {
71 if let Some(attribute) = self.0.downcast::<Attr>() {
74 return attribute
75 .GetOwnerElement()
76 .map(DomRoot::upcast)
77 .map(XPathWrapper);
78 }
79
80 self.0.GetParentNode().map(XPathWrapper)
81 }
82
83 fn children(&self) -> impl Iterator<Item = Self> {
84 self.0.children().map(XPathWrapper)
85 }
86
87 fn compare_tree_order(&self, other: &Self) -> Ordering {
88 if self == other {
89 Ordering::Equal
90 } else if self.0.is_before(&other.0) {
91 Ordering::Less
92 } else {
93 Ordering::Greater
94 }
95 }
96
97 fn traverse_preorder(&self) -> impl Iterator<Item = Self> {
98 self.0
99 .traverse_preorder(ShadowIncluding::No)
100 .map(XPathWrapper)
101 }
102
103 fn inclusive_ancestors(&self) -> impl Iterator<Item = Self> {
104 self.0
105 .inclusive_ancestors(ShadowIncluding::No)
106 .map(XPathWrapper)
107 }
108
109 fn preceding_nodes(&self, root: &Self) -> impl Iterator<Item = Self> {
110 self.0.preceding_nodes(&root.0).map(XPathWrapper)
111 }
112
113 fn following_nodes(&self, root: &Self) -> impl Iterator<Item = Self> {
114 self.0.following_nodes(&root.0).map(XPathWrapper)
115 }
116
117 fn preceding_siblings(&self) -> impl Iterator<Item = Self> {
118 self.0.preceding_siblings().map(XPathWrapper)
119 }
120
121 fn following_siblings(&self) -> impl Iterator<Item = Self> {
122 self.0.following_siblings().map(XPathWrapper)
123 }
124
125 fn owner_document(&self) -> Self::Document {
126 XPathWrapper(self.0.owner_document())
127 }
128
129 fn to_opaque(&self) -> impl Eq + Hash {
130 self.0.to_opaque()
131 }
132
133 fn as_processing_instruction(&self) -> Option<Self::ProcessingInstruction> {
134 self.0
135 .downcast::<ProcessingInstruction>()
136 .map(DomRoot::from_ref)
137 .map(XPathWrapper)
138 }
139
140 fn as_attribute(&self) -> Option<Self::Attribute> {
141 self.0
142 .downcast::<Attr>()
143 .map(DomRoot::from_ref)
144 .map(XPathWrapper)
145 }
146
147 fn as_element(&self) -> Option<Self::Element> {
148 self.0
149 .downcast::<Element>()
150 .map(DomRoot::from_ref)
151 .map(XPathWrapper)
152 }
153
154 fn get_root_node(&self) -> Self {
155 XPathWrapper(self.0.GetRootNode(&GetRootNodeOptions::empty()))
156 }
157}
158
159impl xpath::Document for XPathWrapper<DomRoot<Document>> {
160 type Node = XPathWrapper<DomRoot<Node>>;
161
162 fn get_elements_with_id(
163 &self,
164 id: &str,
165 ) -> impl Iterator<Item = XPathWrapper<DomRoot<Element>>> {
166 struct ElementIterator<'a> {
167 elements: Ref<'a, [Dom<Element>]>,
168 position: usize,
169 }
170
171 impl<'a> Iterator for ElementIterator<'a> {
172 type Item = XPathWrapper<DomRoot<Element>>;
173
174 fn next(&mut self) -> Option<Self::Item> {
175 let element = self.elements.get(self.position)?;
176 self.position += 1;
177 Some(element.as_rooted().into())
178 }
179 }
180
181 ElementIterator {
182 elements: self.0.get_elements_with_id(&Atom::from(id)),
183 position: 0,
184 }
185 }
186}
187
188impl xpath::Element for XPathWrapper<DomRoot<Element>> {
189 type Node = XPathWrapper<DomRoot<Node>>;
190 type Attribute = XPathWrapper<DomRoot<Attr>>;
191
192 fn as_node(&self) -> Self::Node {
193 DomRoot::from_ref(self.0.upcast::<Node>()).into()
194 }
195
196 fn attributes(&self) -> impl Iterator<Item = Self::Attribute> {
197 struct AttributeIterator<'a> {
198 attributes: Ref<'a, [Dom<Attr>]>,
199 position: usize,
200 }
201
202 impl<'a> Iterator for AttributeIterator<'a> {
203 type Item = XPathWrapper<DomRoot<Attr>>;
204
205 fn next(&mut self) -> Option<Self::Item> {
206 let attribute = self.attributes.get(self.position)?;
207 self.position += 1;
208 Some(attribute.as_rooted().into())
209 }
210
211 fn size_hint(&self) -> (usize, Option<usize>) {
212 let exact_length = self.attributes.len() - self.position;
213 (exact_length, Some(exact_length))
214 }
215 }
216
217 AttributeIterator {
218 attributes: self.0.attrs(),
219 position: 0,
220 }
221 }
222
223 fn prefix(&self) -> Option<Prefix> {
224 self.0.prefix().clone()
225 }
226
227 fn namespace(&self) -> Namespace {
228 self.0.namespace().clone()
229 }
230
231 fn local_name(&self) -> LocalName {
232 self.0.local_name().clone()
233 }
234
235 fn is_html_element_in_html_document(&self) -> bool {
236 self.0.is_html_element() && self.0.owner_document().is_html_document()
237 }
238}
239
240impl xpath::Attribute for XPathWrapper<DomRoot<Attr>> {
241 type Node = XPathWrapper<DomRoot<Node>>;
242
243 fn as_node(&self) -> Self::Node {
244 XPathWrapper(DomRoot::from_ref(self.0.upcast::<Node>()))
245 }
246
247 fn prefix(&self) -> Option<Prefix> {
248 self.0.prefix().cloned()
249 }
250
251 fn namespace(&self) -> Namespace {
252 self.0.namespace().clone()
253 }
254
255 fn local_name(&self) -> LocalName {
256 self.0.local_name().clone()
257 }
258}
259
260impl xpath::NamespaceResolver for XPathWrapper<Rc<XPathNSResolver>> {
261 fn resolve_namespace_prefix(&self, prefix: &str) -> Option<String> {
262 self.0
263 .LookupNamespaceURI__(
264 Some(DOMString::from(prefix)),
265 ExceptionHandling::Report,
266 CanGc::note(),
267 )
268 .ok()
269 .flatten()
270 .map(String::from)
271 }
272}
273
274impl xpath::ProcessingInstruction for XPathWrapper<DomRoot<ProcessingInstruction>> {
275 fn target(&self) -> String {
276 self.0.target().to_owned().into()
277 }
278}
279
280impl<T> From<T> for XPathWrapper<T> {
281 fn from(value: T) -> Self {
282 Self(value)
283 }
284}
285
286impl<T> XPathWrapper<T> {
287 pub(crate) fn into_inner(self) -> T {
288 self.0
289 }
290}
291
292pub(crate) fn parse_expression(
293 expression: &str,
294 resolver: Option<Rc<XPathNSResolver>>,
295 is_in_html_document: bool,
296) -> Fallible<xpath::Expression> {
297 xpath::parse(expression, resolver.map(XPathWrapper), is_in_html_document).map_err(|error| {
298 match error {
299 xpath::ParserError::FailedToResolveNamespacePrefix => Error::Namespace,
300 _ => Error::Syntax(Some(format!("Failed to parse XPath expression: {error:?}"))),
301 }
302 })
303}