Skip to main content

bliss_dom/
query_selector.rs

1use selectors::SelectorList;
2use smallvec::SmallVec;
3use style::dom_apis::{MayUseInvalidation, QueryAll, QueryFirst, query_selector};
4use style::selector_parser::{SelectorImpl, SelectorParser};
5use style_traits::ParseError;
6
7use crate::{BaseDocument, Node};
8
9impl BaseDocument {
10    /// Find the node with the specified id attribute (if one exists)
11    pub fn get_element_by_id(&self, id: &str) -> Option<usize> {
12        self.nodes_to_id.get(id).copied()
13    }
14
15    /// Find the first node that matches the selector specified as a string
16    /// Returns:
17    ///   - Err(_) if parsing the selector fails
18    ///   - Ok(None) if nothing matches
19    ///   - Ok(Some(node_id)) with the first node ID that matches if one is found
20    pub fn query_selector<'input>(
21        &self,
22        selector: &'input str,
23    ) -> Result<Option<usize>, ParseError<'input>> {
24        let selector_list = self.try_parse_selector_list(selector)?;
25        Ok(self.query_selector_raw(&selector_list))
26    }
27
28    /// Find the first node that matches the selector(s) specified in selector_list
29    pub fn query_selector_raw(&self, selector_list: &SelectorList<SelectorImpl>) -> Option<usize> {
30        let root_node = self.root_node();
31        let mut result = None;
32        query_selector::<&Node, QueryFirst>(
33            root_node,
34            selector_list,
35            &mut result,
36            MayUseInvalidation::Yes,
37        );
38
39        result.map(|node| node.id)
40    }
41
42    /// Find all nodes that match the selector specified as a string
43    /// Returns:
44    ///   - `Err(_)` if parsing the selector fails
45    ///   - `Ok(SmallVec<usize>)` with all matching nodes otherwise
46    pub fn query_selector_all<'input>(
47        &self,
48        selector: &'input str,
49    ) -> Result<SmallVec<[usize; 32]>, ParseError<'input>> {
50        let selector_list = self.try_parse_selector_list(selector)?;
51        Ok(self.query_selector_all_raw(&selector_list))
52    }
53
54    /// Find all nodes that match the selector(s) specified in selector_list
55    pub fn query_selector_all_raw(
56        &self,
57        selector_list: &SelectorList<SelectorImpl>,
58    ) -> SmallVec<[usize; 32]> {
59        let root_node = self.root_node();
60        let mut results = SmallVec::new();
61        query_selector::<&Node, QueryAll>(
62            root_node,
63            selector_list,
64            &mut results,
65            MayUseInvalidation::Yes,
66        );
67
68        results.iter().map(|node| node.id).collect()
69    }
70
71    /// Element-scoped query: all nodes matching `selector` within the subtree
72    /// rooted at `root_id`, EXCLUDING the root itself — DOM
73    /// `element.querySelectorAll` semantics. Unlike the document-global
74    /// [`query_selector_all`](Self::query_selector_all), this traverses from the
75    /// given node, so it also queries **detached** subtrees (e.g. a `cloneNode`
76    /// result not yet inserted) that the global query cannot reach.
77    pub fn query_selector_all_from<'input>(
78        &self,
79        root_id: usize,
80        selector: &'input str,
81    ) -> Result<SmallVec<[usize; 32]>, ParseError<'input>> {
82        let selector_list = self.try_parse_selector_list(selector)?;
83        Ok(self.query_selector_all_from_raw(root_id, &selector_list))
84    }
85
86    /// Find all nodes matching `selector_list` in the subtree rooted at
87    /// `root_id`, excluding the root. Empty if `root_id` is absent.
88    pub fn query_selector_all_from_raw(
89        &self,
90        root_id: usize,
91        selector_list: &SelectorList<SelectorImpl>,
92    ) -> SmallVec<[usize; 32]> {
93        let Some(root) = self.get_node(root_id) else {
94            return SmallVec::new();
95        };
96        let mut results = SmallVec::new();
97        query_selector::<&Node, QueryAll>(root, selector_list, &mut results, MayUseInvalidation::Yes);
98        results
99            .iter()
100            .map(|node| node.id)
101            .filter(|id| *id != root_id)
102            .collect()
103    }
104
105    pub fn try_parse_selector_list<'input>(
106        &self,
107        input: &'input str,
108    ) -> Result<SelectorList<SelectorImpl>, ParseError<'input>> {
109        let url_extra_data = self.url.url_extra_data();
110        SelectorParser::parse_author_origin_no_namespace(input, &url_extra_data)
111    }
112}