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