nipper_trunk/
matcher.rs

1use crate::dom_tree::{NodeData, NodeId, NodeRef};
2use cssparser::ParseError;
3use html5ever::{LocalName, Namespace};
4use selectors::matching;
5use selectors::parser::{self, SelectorList, SelectorParseErrorKind};
6use selectors::visitor;
7use selectors::Element;
8use std::collections::HashSet;
9use std::fmt;
10
11/// CSS selector.
12#[derive(Clone, Debug)]
13pub struct Matcher {
14    selector_list: SelectorList<InnerSelector>,
15}
16
17impl Matcher {
18    /// Greate a new CSS matcher.
19    pub fn new(sel: &str) -> Result<Self, ParseError<SelectorParseErrorKind>> {
20        let mut input = cssparser::ParserInput::new(sel);
21        let mut parser = cssparser::Parser::new(&mut input);
22        selectors::parser::SelectorList::parse(&InnerSelectorParser, &mut parser)
23            .map(|selector_list| Matcher { selector_list })
24    }
25
26    pub(crate) fn match_element<E>(&self, element: &E) -> bool
27    where
28        E: Element<Impl = InnerSelector>,
29    {
30        let mut ctx = matching::MatchingContext::new(
31            matching::MatchingMode::Normal,
32            None,
33            None,
34            matching::QuirksMode::NoQuirks,
35        );
36
37        matching::matches_selector_list(&self.selector_list, element, &mut ctx)
38    }
39}
40
41#[derive(Debug, Clone)]
42pub struct Matches<T> {
43    roots: Vec<T>,
44    nodes: Vec<T>,
45    matcher: Matcher,
46    set: HashSet<NodeId>,
47    match_scope: MatchScope,
48}
49
50/// Telling a `matches` if we want to skip the roots.
51#[derive(Debug, Clone)]
52pub enum MatchScope {
53    IncludeNode,
54    ChildrenOnly,
55}
56
57impl<T> Matches<T> {
58    pub fn from_one(node: T, matcher: Matcher, match_scope: MatchScope) -> Self {
59        Self {
60            roots: vec![node],
61            nodes: vec![],
62            matcher: matcher,
63            set: HashSet::new(),
64            match_scope,
65        }
66    }
67
68    pub fn from_list<I: Iterator<Item = T>>(
69        nodes: I,
70        matcher: Matcher,
71        match_scope: MatchScope,
72    ) -> Self {
73        Self {
74            roots: nodes.collect(),
75            nodes: vec![],
76            matcher: matcher,
77            set: HashSet::new(),
78            match_scope,
79        }
80    }
81}
82
83impl<'a> Iterator for Matches<NodeRef<'a, NodeData>> {
84    type Item = NodeRef<'a, NodeData>;
85
86    fn next(&mut self) -> Option<Self::Item> {
87        loop {
88            if self.nodes.is_empty() {
89                if self.roots.is_empty() {
90                    return None;
91                }
92
93                let root = self.roots.remove(0);
94
95                match self.match_scope {
96                    MatchScope::IncludeNode => self.nodes.insert(0, root),
97                    MatchScope::ChildrenOnly => {
98                        for child in root.children().into_iter().rev() {
99                            self.nodes.insert(0, child);
100                        }
101                    }
102                }
103            }
104
105            while !self.nodes.is_empty() {
106                let node = self.nodes.remove(0);
107
108                for node in node.children().into_iter().rev() {
109                    self.nodes.insert(0, node);
110                }
111
112                if self.matcher.match_element(&node) {
113                    if self.set.contains(&node.id) {
114                        continue;
115                    }
116
117                    self.set.insert(node.id);
118                    return Some(node);
119                }
120            }
121        }
122    }
123}
124
125pub(crate) struct InnerSelectorParser;
126
127impl<'i> parser::Parser<'i> for InnerSelectorParser {
128    type Impl = InnerSelector;
129    type Error = parser::SelectorParseErrorKind<'i>;
130}
131
132#[derive(Debug, Clone)]
133pub struct InnerSelector;
134
135impl parser::SelectorImpl for InnerSelector {
136    type ExtraMatchingData = String;
137    type AttrValue = String;
138    type Identifier = LocalName;
139    type ClassName = LocalName;
140    type PartName = LocalName;
141    type LocalName = LocalName;
142    type NamespaceUrl = Namespace;
143    type NamespacePrefix = LocalName;
144    type BorrowedLocalName = LocalName;
145    type BorrowedNamespaceUrl = Namespace;
146
147    type NonTSPseudoClass = NonTSPseudoClass;
148    type PseudoElement = PseudoElement;
149}
150
151#[derive(Clone, Eq, PartialEq)]
152pub struct NonTSPseudoClass;
153
154impl parser::NonTSPseudoClass for NonTSPseudoClass {
155    type Impl = InnerSelector;
156
157    fn is_active_or_hover(&self) -> bool {
158        false
159    }
160
161    fn is_user_action_state(&self) -> bool {
162        false
163    }
164
165    fn has_zero_specificity(&self) -> bool {
166        false
167    }
168}
169
170impl parser::Visit for NonTSPseudoClass {
171    type Impl = InnerSelector;
172
173    fn visit<V>(&self, _visitor: &mut V) -> bool
174    where
175        V: visitor::SelectorVisitor<Impl = Self::Impl>,
176    {
177        true
178    }
179}
180
181impl cssparser::ToCss for NonTSPseudoClass {
182    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
183    where
184        W: fmt::Write,
185    {
186        dest.write_str("")
187    }
188}
189
190#[derive(Clone, Eq, PartialEq)]
191pub struct PseudoElement;
192
193impl parser::PseudoElement for PseudoElement {
194    type Impl = InnerSelector;
195}
196
197impl cssparser::ToCss for PseudoElement {
198    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
199    where
200        W: fmt::Write,
201    {
202        dest.write_str("")
203    }
204}