]> git.proxmox.com Git - rustc.git/blame - src/vendor/selectors/attr.rs
New upstream version 1.25.0+dfsg1
[rustc.git] / src / vendor / selectors / attr.rs
CommitLineData
ea8adc8c
XL
1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5use cssparser::ToCss;
6use parser::SelectorImpl;
7use std::ascii::AsciiExt;
8use std::fmt;
9
10#[derive(Eq, PartialEq, Clone)]
11pub struct AttrSelectorWithNamespace<Impl: SelectorImpl> {
12 pub namespace: NamespaceConstraint<(Impl::NamespacePrefix, Impl::NamespaceUrl)>,
13 pub local_name: Impl::LocalName,
14 pub local_name_lower: Impl::LocalName,
15 pub operation: ParsedAttrSelectorOperation<Impl::AttrValue>,
16 pub never_matches: bool,
17}
18
19impl<Impl: SelectorImpl> AttrSelectorWithNamespace<Impl> {
20 pub fn namespace(&self) -> NamespaceConstraint<&Impl::NamespaceUrl> {
21 match self.namespace {
22 NamespaceConstraint::Any => NamespaceConstraint::Any,
23 NamespaceConstraint::Specific((_, ref url)) => {
24 NamespaceConstraint::Specific(url)
25 }
26 }
27 }
28}
29
30#[derive(Eq, PartialEq, Clone)]
31pub enum NamespaceConstraint<NamespaceUrl> {
32 Any,
33
34 /// Empty string for no namespace
35 Specific(NamespaceUrl),
36}
37
38#[derive(Eq, PartialEq, Clone)]
39pub enum ParsedAttrSelectorOperation<AttrValue> {
40 Exists,
41 WithValue {
42 operator: AttrSelectorOperator,
43 case_sensitivity: ParsedCaseSensitivity,
44 expected_value: AttrValue,
45 }
46}
47
48#[derive(Eq, PartialEq, Clone)]
49pub enum AttrSelectorOperation<AttrValue> {
50 Exists,
51 WithValue {
52 operator: AttrSelectorOperator,
53 case_sensitivity: CaseSensitivity,
54 expected_value: AttrValue,
55 }
56}
57
58impl<AttrValue> AttrSelectorOperation<AttrValue> {
59 pub fn eval_str(&self, element_attr_value: &str) -> bool where AttrValue: AsRef<str> {
60 match *self {
61 AttrSelectorOperation::Exists => true,
62 AttrSelectorOperation::WithValue { operator, case_sensitivity, ref expected_value } => {
63 operator.eval_str(element_attr_value, expected_value.as_ref(), case_sensitivity)
64 }
65 }
66 }
67}
68
69#[derive(Eq, PartialEq, Clone, Copy)]
70pub enum AttrSelectorOperator {
71 Equal,
72 Includes,
73 DashMatch,
74 Prefix,
75 Substring,
76 Suffix,
77}
78
79impl ToCss for AttrSelectorOperator {
80 fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
81 dest.write_str(match *self {
82 AttrSelectorOperator::Equal => " = ",
83 AttrSelectorOperator::Includes => " ~= ",
84 AttrSelectorOperator::DashMatch => " |= ",
85 AttrSelectorOperator::Prefix => " ^= ",
86 AttrSelectorOperator::Substring => " *= ",
87 AttrSelectorOperator::Suffix => " $= ",
88 })
89 }
90}
91
92impl AttrSelectorOperator {
93 pub fn eval_str(self, element_attr_value: &str, attr_selector_value: &str,
94 case_sensitivity: CaseSensitivity) -> bool {
95 let e = element_attr_value.as_bytes();
96 let s = attr_selector_value.as_bytes();
97 let case = case_sensitivity;
98 match self {
99 AttrSelectorOperator::Equal => {
100 case.eq(e, s)
101 }
102 AttrSelectorOperator::Prefix => {
103 e.len() >= s.len() && case.eq(&e[..s.len()], s)
104 }
105 AttrSelectorOperator::Suffix => {
106 e.len() >= s.len() && case.eq(&e[(e.len() - s.len())..], s)
107 }
108 AttrSelectorOperator::Substring => {
109 case.contains(element_attr_value, attr_selector_value)
110 }
111 AttrSelectorOperator::Includes => {
112 element_attr_value.split(SELECTOR_WHITESPACE)
113 .any(|part| case.eq(part.as_bytes(), s))
114 }
115 AttrSelectorOperator::DashMatch => {
116 case.eq(e, s) || (
117 e.get(s.len()) == Some(&b'-') &&
118 case.eq(&e[..s.len()], s)
119 )
120 }
121 }
122 }
123}
124
125/// The definition of whitespace per CSS Selectors Level 3 ยง 4.
126pub static SELECTOR_WHITESPACE: &'static [char] = &[' ', '\t', '\n', '\r', '\x0C'];
127
128#[derive(Eq, PartialEq, Clone, Copy, Debug)]
129pub enum ParsedCaseSensitivity {
130 CaseSensitive, // Selectors spec says language-defined, but HTML says sensitive.
131 AsciiCaseInsensitive,
132 AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument,
133}
134
135impl ParsedCaseSensitivity {
136 pub fn to_unconditional(self, is_html_element_in_html_document: bool) -> CaseSensitivity {
137 match self {
138 ParsedCaseSensitivity::AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument
139 if is_html_element_in_html_document => {
140 CaseSensitivity::AsciiCaseInsensitive
141 }
142 ParsedCaseSensitivity::AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument => {
143 CaseSensitivity::CaseSensitive
144 }
145 ParsedCaseSensitivity::CaseSensitive => CaseSensitivity::CaseSensitive,
146 ParsedCaseSensitivity::AsciiCaseInsensitive => CaseSensitivity::AsciiCaseInsensitive,
147 }
148 }
149}
150
151#[derive(Eq, PartialEq, Clone, Copy, Debug)]
152pub enum CaseSensitivity {
153 CaseSensitive, // Selectors spec says language-defined, but HTML says sensitive.
154 AsciiCaseInsensitive,
155}
156
157impl CaseSensitivity {
158 pub fn eq(self, a: &[u8], b: &[u8]) -> bool {
159 match self {
160 CaseSensitivity::CaseSensitive => a == b,
161 CaseSensitivity::AsciiCaseInsensitive => a.eq_ignore_ascii_case(b),
162 }
163 }
164
165 pub fn contains(self, haystack: &str, needle: &str) -> bool {
166 match self {
167 CaseSensitivity::CaseSensitive => haystack.contains(needle),
168 CaseSensitivity::AsciiCaseInsensitive => {
169 if let Some((&n_first_byte, n_rest)) = needle.as_bytes().split_first() {
170 haystack.bytes().enumerate().any(|(i, byte)| {
171 if !byte.eq_ignore_ascii_case(&n_first_byte) {
172 return false
173 }
174 let after_this_byte = &haystack.as_bytes()[i + 1..];
175 match after_this_byte.get(..n_rest.len()) {
176 None => false,
177 Some(haystack_slice) => {
178 haystack_slice.eq_ignore_ascii_case(n_rest)
179 }
180 }
181 })
182 } else {
183 // any_str.contains("") == true,
184 // though these cases should be handled with *NeverMatches and never go here.
185 true
186 }
187 }
188 }
189 }
190}