1use crate::element_ref::ElementRef;
4use crate::error::SelectorErrorKind;
5use cssparser::ToCss;
6use fast_html5ever::{LocalName, Namespace};
7use selectors::parser::SelectorParseErrorKind;
8use selectors::{matching, parser, NthIndexCache};
9use smallvec::SmallVec;
10use std::convert::TryFrom;
11use std::fmt;
12
13#[derive(Debug, Clone, PartialEq, Eq)]
17#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
18pub struct Selector {
19 selectors: SmallVec<[parser::Selector<Simple>; 1]>,
21}
22
23impl Selector {
24 pub fn raw_query(&self) -> String {
26 self.selectors
27 .iter()
28 .filter_map(|s| {
29 let mut ss = String::new();
30 if s.to_css(&mut ss).is_ok() {
31 Some(ss)
32 } else {
33 None
34 }
35 })
36 .collect()
37 }
38
39 pub fn parse(selectors: &'_ str) -> Result<Self, SelectorErrorKind> {
41 let mut parser_input = cssparser::ParserInput::new(selectors);
42 let mut parser = cssparser::Parser::new(&mut parser_input);
43
44 parser::SelectorList::parse(&Parser, &mut parser, parser::ParseRelative::No)
45 .map(|list| Selector { selectors: list.0 })
46 .map_err(SelectorErrorKind::from)
47 }
48
49 pub fn matches(&self, element: &ElementRef) -> bool {
51 self.matches_with_scope(element, None)
52 }
53
54 pub fn matches_with_scope(&self, element: &ElementRef, scope: Option<ElementRef>) -> bool {
58 let mut binding = NthIndexCache::default();
59 let mut context = matching::MatchingContext::new(
60 matching::MatchingMode::Normal,
61 None,
62 &mut binding,
63 matching::QuirksMode::NoQuirks,
64 matching::NeedsSelectorFlags::No,
65 matching::IgnoreNthChildForInvalidation::No,
66 );
67 context.scope_element = scope.map(|x| selectors::Element::opaque(&x));
68 self.selectors
69 .iter()
70 .any(|s| matching::matches_selector(s, 0, None, element, &mut context))
71 }
72}
73
74struct Parser;
76impl<'i> parser::Parser<'i> for Parser {
77 type Impl = Simple;
78 type Error = SelectorParseErrorKind<'i>;
79}
80
81#[derive(Debug, Clone, Copy, PartialEq, Eq)]
83pub struct Simple;
84
85impl parser::SelectorImpl for Simple {
86 type AttrValue = CssString;
87 type Identifier = CssLocalName;
88 type LocalName = CssLocalName;
89 type NamespacePrefix = CssLocalName;
90 type NamespaceUrl = Namespace;
91 type BorrowedNamespaceUrl = Namespace;
92 type BorrowedLocalName = CssLocalName;
93
94 type NonTSPseudoClass = NonTSPseudoClass;
95 type PseudoElement = PseudoElement;
96
97 type ExtraMatchingData<'a> = std::marker::PhantomData<&'a ()>;
99}
100
101#[derive(Debug, Clone, PartialEq, Eq)]
103pub struct CssString(pub String);
104
105impl<'a> From<&'a str> for CssString {
106 fn from(val: &'a str) -> Self {
107 Self(val.to_owned())
108 }
109}
110
111impl AsRef<str> for CssString {
112 fn as_ref(&self) -> &str {
113 &self.0
114 }
115}
116
117impl ToCss for CssString {
118 fn to_css<W>(&self, dest: &mut W) -> fmt::Result
119 where
120 W: fmt::Write,
121 {
122 cssparser::serialize_string(&self.0, dest)
123 }
124}
125
126#[derive(Debug, Default, Clone, PartialEq, Eq)]
128pub struct CssLocalName(pub LocalName);
129
130impl<'a> From<&'a str> for CssLocalName {
131 fn from(val: &'a str) -> Self {
132 Self(val.into())
133 }
134}
135
136impl ToCss for CssLocalName {
137 fn to_css<W>(&self, dest: &mut W) -> fmt::Result
138 where
139 W: fmt::Write,
140 {
141 dest.write_str(&self.0)
142 }
143}
144
145#[derive(Debug, Clone, Copy, PartialEq, Eq)]
147pub enum NonTSPseudoClass {}
148
149impl parser::NonTSPseudoClass for NonTSPseudoClass {
150 type Impl = Simple;
151
152 fn is_active_or_hover(&self) -> bool {
153 false
154 }
155
156 fn is_user_action_state(&self) -> bool {
157 false
158 }
159}
160
161impl ToCss for NonTSPseudoClass {
162 fn to_css<W>(&self, dest: &mut W) -> fmt::Result
163 where
164 W: fmt::Write,
165 {
166 dest.write_str("")
167 }
168}
169
170#[derive(Debug, Clone, Copy, PartialEq, Eq)]
172pub enum PseudoElement {}
173
174impl parser::PseudoElement for PseudoElement {
175 type Impl = Simple;
176}
177
178impl ToCss for PseudoElement {
179 fn to_css<W>(&self, dest: &mut W) -> fmt::Result
180 where
181 W: fmt::Write,
182 {
183 dest.write_str("")
184 }
185}
186
187impl<'i> TryFrom<&'i str> for Selector {
188 type Error = SelectorErrorKind<'i>;
189
190 fn try_from(s: &'i str) -> Result<Self, Self::Error> {
191 Selector::parse(s)
192 }
193}
194
195#[cfg(test)]
196mod tests {
197 use super::*;
198 use std::convert::TryInto;
199
200 #[test]
201 fn selector_conversions() {
202 let s = "#testid.testclass";
203 let _sel: Selector = s.try_into().unwrap();
204
205 let s = s.to_owned();
206 let _sel: Selector = (*s).try_into().unwrap();
207 }
208
209 #[test]
210 #[should_panic]
211 fn invalid_selector_conversions() {
212 let s = "<failing selector>";
213 let _sel: Selector = s.try_into().unwrap();
214 }
215}