Source code

Revision control

Other Tools

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