1use blitz_traits::node_id::NodeId;
2use selectors::SelectorList;
3use smallvec::SmallVec;
4use style::dom::{TDocument, TNode};
5use style::dom_apis::{
6 MayUseInvalidation, QueryAll, QueryFirst, element_closest, element_matches, query_selector,
7};
8use style::selector_parser::{SelectorImpl, SelectorParser};
9use style_traits::ParseError;
10
11use crate::{BaseDocument, Node};
12
13impl BaseDocument {
14 pub fn get_element_by_id(&self, id: &str) -> Option<NodeId> {
17 match self.nodes_to_id.get(id)?.as_slice() {
18 [] => None,
19 [node_id] => Some(*node_id),
20 candidates => self.first_in_tree_order(candidates),
21 }
22 }
23
24 fn first_in_tree_order(&self, candidates: &[NodeId]) -> Option<NodeId> {
26 let mut stack = vec![self.root_node_id];
27 while let Some(node_id) = stack.pop() {
28 if candidates.contains(&node_id) {
29 return Some(node_id);
30 }
31 stack.extend(self.nodes[node_id].children.iter().rev().copied());
32 }
33 None
34 }
35
36 pub(crate) fn add_to_id_map(&mut self, id: &str, node_id: NodeId) {
38 if id.is_empty() {
39 return;
40 }
41 #[cfg(feature = "shadow-dom")]
42 if self.containing_shadow_root(node_id).is_some() {
43 return;
44 }
45 let node_ids = self.nodes_to_id.entry(id.to_string()).or_default();
46 if !node_ids.contains(&node_id) {
47 node_ids.push(node_id);
48 }
49 }
50
51 pub(crate) fn remove_from_id_map(&mut self, id: &str, node_id: NodeId) {
53 if let Some(node_ids) = self.nodes_to_id.get_mut(id) {
54 node_ids.retain(|nid| *nid != node_id);
55 if node_ids.is_empty() {
56 self.nodes_to_id.remove(id);
57 }
58 }
59 }
60
61 pub fn query_selector<'input>(
67 &self,
68 selector: &'input str,
69 ) -> Result<Option<NodeId>, ParseError<'input>> {
70 self.query_selector_in(self.root_node_id, selector)
71 }
72
73 pub fn query_selector_in<'input>(
84 &self,
85 scope: NodeId,
86 selector: &'input str,
87 ) -> Result<Option<NodeId>, ParseError<'input>> {
88 let selector_list = self.try_parse_selector_list(selector)?;
89 Ok(self.query_selector_in_raw(scope, &selector_list))
90 }
91
92 pub fn query_selector_raw(&self, selector_list: &SelectorList<SelectorImpl>) -> Option<NodeId> {
99 self.query_selector_in_raw(self.root_node_id, selector_list)
100 }
101
102 pub fn query_selector_in_raw(
108 &self,
109 scope: NodeId,
110 selector_list: &SelectorList<SelectorImpl>,
111 ) -> Option<NodeId> {
112 #[cfg(feature = "shadow-dom")]
113 if self.has_shadow_roots() {
114 return query_dom_descendants(&self.nodes[scope], selector_list, true)
115 .first()
116 .copied();
117 }
118
119 let root_node = &self.nodes[scope];
120 let mut result = None;
121 query_selector::<&Node, QueryFirst>(
122 root_node,
123 selector_list,
124 &mut result,
125 self.may_use_invalidation_for(scope),
126 );
127
128 result.map(|node| node.id)
129 }
130
131 pub fn query_selector_all<'input>(
136 &self,
137 selector: &'input str,
138 ) -> Result<SmallVec<[NodeId; 32]>, ParseError<'input>> {
139 self.query_selector_all_in(self.root_node_id, selector)
140 }
141
142 pub fn query_selector_all_in<'input>(
152 &self,
153 scope: NodeId,
154 selector: &'input str,
155 ) -> Result<SmallVec<[NodeId; 32]>, ParseError<'input>> {
156 let selector_list = self.try_parse_selector_list(selector)?;
157 Ok(self.query_selector_all_in_raw(scope, &selector_list))
158 }
159
160 pub fn query_selector_all_raw(
167 &self,
168 selector_list: &SelectorList<SelectorImpl>,
169 ) -> SmallVec<[NodeId; 32]> {
170 self.query_selector_all_in_raw(self.root_node_id, selector_list)
171 }
172
173 pub fn query_selector_all_in_raw(
179 &self,
180 scope: NodeId,
181 selector_list: &SelectorList<SelectorImpl>,
182 ) -> SmallVec<[NodeId; 32]> {
183 #[cfg(feature = "shadow-dom")]
184 if self.has_shadow_roots() {
185 return query_dom_descendants(&self.nodes[scope], selector_list, false);
186 }
187
188 let root_node = &self.nodes[scope];
189 let mut results = SmallVec::new();
190 query_selector::<&Node, QueryAll>(
191 root_node,
192 selector_list,
193 &mut results,
194 self.may_use_invalidation_for(scope),
195 );
196
197 results.iter().map(|node| node.id).collect()
198 }
199
200 fn may_use_invalidation_for(&self, scope: NodeId) -> MayUseInvalidation {
201 if scope == self.root_node_id {
202 MayUseInvalidation::Yes
203 } else {
204 MayUseInvalidation::No
205 }
206 }
207
208 pub fn matches_selector<'input>(
213 &self,
214 node_id: NodeId,
215 selector: &'input str,
216 ) -> Result<bool, ParseError<'input>> {
217 let selector_list = self.try_parse_selector_list(selector)?;
218 Ok(self.nodes[node_id].matches_selector_raw(&selector_list))
219 }
220
221 pub fn closest<'input>(
226 &self,
227 node_id: NodeId,
228 selector: &'input str,
229 ) -> Result<Option<NodeId>, ParseError<'input>> {
230 let selector_list = self.try_parse_selector_list(selector)?;
231 Ok(self.nodes[node_id].closest_raw(&selector_list))
232 }
233
234 pub fn try_parse_selector_list<'input>(
235 &self,
236 input: &'input str,
237 ) -> Result<SelectorList<SelectorImpl>, ParseError<'input>> {
238 let url_extra_data = self.url.url_extra_data();
239 SelectorParser::parse_author_origin_no_namespace(input, &url_extra_data)
240 }
241}
242
243#[cfg(feature = "shadow-dom")]
247fn query_dom_descendants(
248 scope: &Node,
249 selector_list: &SelectorList<SelectorImpl>,
250 first: bool,
251) -> SmallVec<[NodeId; 32]> {
252 use selectors::context::{
253 MatchingContext, MatchingForInvalidation, MatchingMode, NeedsSelectorFlags, SelectorCaches,
254 };
255 use selectors::matching::matches_selector_list;
256
257 let mut caches = SelectorCaches::default();
258 let mut context = MatchingContext::new(
259 MatchingMode::Normal,
260 None,
261 &mut caches,
262 scope.owner_doc().quirks_mode(),
263 NeedsSelectorFlags::No,
264 MatchingForInvalidation::No,
265 );
266 context.scope_element = scope
267 .as_element()
268 .map(|element| selectors::Element::opaque(&element));
269 let mut results = SmallVec::new();
270 let mut stack: Vec<_> = scope.children.iter().rev().copied().collect();
271 while let Some(id) = stack.pop() {
272 let node = scope.with(id);
273 if node.is_element() && matches_selector_list(selector_list, &node, &mut context) {
274 results.push(id);
275 if first {
276 break;
277 }
278 }
279 stack.extend(node.children.iter().rev().copied());
280 }
281 results
282}
283
284impl Node {
285 pub fn query_selector_raw(&self, selector_list: &SelectorList<SelectorImpl>) -> Option<NodeId> {
293 #[cfg(feature = "shadow-dom")]
294 {
295 query_dom_descendants(self, selector_list, true)
296 .first()
297 .copied()
298 }
299 #[cfg(not(feature = "shadow-dom"))]
300 {
301 let mut result = None;
302 query_selector::<&Node, QueryFirst>(
303 self,
304 selector_list,
305 &mut result,
306 MayUseInvalidation::No,
307 );
308 result.map(|node| node.id)
309 }
310 }
311
312 pub fn query_selector_all_raw(
320 &self,
321 selector_list: &SelectorList<SelectorImpl>,
322 ) -> SmallVec<[NodeId; 32]> {
323 #[cfg(feature = "shadow-dom")]
324 {
325 query_dom_descendants(self, selector_list, false)
326 }
327 #[cfg(not(feature = "shadow-dom"))]
328 {
329 let mut results = SmallVec::new();
330 query_selector::<&Node, QueryAll>(
331 self,
332 selector_list,
333 &mut results,
334 MayUseInvalidation::No,
335 );
336 results.iter().map(|node| node.id).collect()
337 }
338 }
339
340 pub fn matches_selector_raw(&self, selector_list: &SelectorList<SelectorImpl>) -> bool {
345 if !self.is_element() {
346 return false;
347 }
348
349 element_matches(&self, selector_list, self.owner_doc().quirks_mode())
350 }
351
352 pub fn closest_raw(&self, selector_list: &SelectorList<SelectorImpl>) -> Option<NodeId> {
356 if !self.is_element() {
357 return None;
358 }
359
360 element_closest(self, selector_list, self.owner_doc().quirks_mode()).map(|node| node.id)
361 }
362}