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#[derive(Clone, Debug)]
13pub struct Matcher {
14 selector_list: SelectorList<InnerSelector>,
15}
16
17impl Matcher {
18 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#[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}