Skip to content

Commit 04824e6

Browse files
committed
Update cssparser and selectors
1 parent 4938ed3 commit 04824e6

23 files changed

+6129
-188
lines changed

Cargo.lock

Lines changed: 37 additions & 22 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
[workspace]
22
members = [
3-
"node"
3+
"node",
4+
"selectors"
45
]
56

67
[package]
@@ -21,8 +22,8 @@ crate-type = ["rlib"]
2122

2223
[dependencies]
2324
serde = { version = "1.0.123", features = ["derive"] }
24-
cssparser = "0.28.1"
25-
selectors = "0.23.0"
25+
cssparser = "0.29.1"
26+
parcel_selectors = { version = "0.24.0", path = "./selectors" }
2627
itertools = "0.10.1"
2728
smallvec = { version = "1.7.0", features = ["union"] }
2829
bitflags = "1.3.2"

selectors/Cargo.toml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
[package]
2+
name = "parcel_selectors"
3+
version = "0.24.0"
4+
authors = ["The Servo Project Developers"]
5+
documentation = "https://docs.rs/selectors/"
6+
description = "CSS Selectors matching for Rust - forked for parcel_css"
7+
repository = "https://github.com/servo/servo"
8+
readme = "README.md"
9+
keywords = ["css", "selectors"]
10+
license = "MPL-2.0"
11+
build = "build.rs"
12+
13+
[lib]
14+
name = "parcel_selectors"
15+
path = "lib.rs"
16+
17+
[features]
18+
bench = []
19+
20+
[dependencies]
21+
bitflags = "1.0"
22+
cssparser = "0.29"
23+
derive_more = "0.99"
24+
fxhash = "0.2"
25+
log = "0.4"
26+
phf = "0.8"
27+
precomputed-hash = "0.1"
28+
servo_arc = "0.1"
29+
smallvec = "1.0"
30+
31+
[build-dependencies]
32+
phf_codegen = "0.8"

selectors/README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
rust-selectors
2+
==============
3+
4+
This is a fork of the `selectors` crate, updated to use the latest version of `cssparser`.
5+
6+
* [![Build Status](https://travis-ci.com/servo/rust-selectors.svg?branch=master)](
7+
https://travis-ci.com/servo/rust-selectors)
8+
* [Documentation](https://docs.rs/selectors/)
9+
* [crates.io](https://crates.io/crates/selectors)
10+
11+
CSS Selectors library for Rust.
12+
Includes parsing and serialization of selectors,
13+
as well as matching against a generic tree of elements.
14+
Pseudo-elements and most pseudo-classes are generic as well.
15+
16+
**Warning:** breaking changes are made to this library fairly frequently
17+
(13 times in 2016, for example).
18+
However you can use this crate without updating it that often,
19+
old versions stay available on crates.io and Cargo will only automatically update
20+
to versions that are numbered as compatible.
21+
22+
To see how to use this library with your own tree representation,
23+
see [Kuchiki’s `src/select.rs`](https://github.com/kuchiki-rs/kuchiki/blob/master/src/select.rs).
24+
(Note however that Kuchiki is not always up to date with the latest rust-selectors version,
25+
so that code may need to be tweaked.)
26+
If you don’t already have a tree data structure,
27+
consider using [Kuchiki](https://github.com/kuchiki-rs/kuchiki) itself.

selectors/attr.rs

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

0 commit comments

Comments
 (0)