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
//! Helper module to build up a selector safely and efficiently.
6
//!
7
//! Our selector representation is designed to optimize matching, and has
8
//! several requirements:
9
//! * All simple selectors and combinators are stored inline in the same buffer
10
//! as Component instances.
11
//! * We store the top-level compound selectors from right to left, i.e. in
12
//! matching order.
13
//! * We store the simple selectors for each combinator from left to right, so
14
//! that we match the cheaper simple selectors first.
15
//!
16
//! Meeting all these constraints without extra memmove traffic during parsing
17
//! is non-trivial. This module encapsulates those details and presents an
18
//! easy-to-use API for the parser.
19
20
use crate::parser::{Combinator, Component, SelectorImpl};
21
use crate::sink::Push;
22
use servo_arc::{Arc, HeaderWithLength, ThinArc};
23
use smallvec::{self, SmallVec};
24
use std::cmp;
25
use std::iter;
26
use std::ptr;
27
use std::slice;
28
29
/// Top-level SelectorBuilder struct. This should be stack-allocated by the
30
/// consumer and never moved (because it contains a lot of inline data that
31
/// would be slow to memmov).
32
///
33
/// After instantation, callers may call the push_simple_selector() and
34
/// push_combinator() methods to append selector data as it is encountered
35
/// (from left to right). Once the process is complete, callers should invoke
36
/// build(), which transforms the contents of the SelectorBuilder into a heap-
37
/// allocated Selector and leaves the builder in a drained state.
38
#[derive(Debug)]
39
pub struct SelectorBuilder<Impl: SelectorImpl> {
40
/// The entire sequence of simple selectors, from left to right, without combinators.
41
///
42
/// We make this large because the result of parsing a selector is fed into a new
43
/// Arc-ed allocation, so any spilled vec would be a wasted allocation. Also,
44
/// Components are large enough that we don't have much cache locality benefit
45
/// from reserving stack space for fewer of them.
46
simple_selectors: SmallVec<[Component<Impl>; 32]>,
47
/// The combinators, and the length of the compound selector to their left.
48
combinators: SmallVec<[(Combinator, usize); 16]>,
49
/// The length of the current compount selector.
50
current_len: usize,
51
}
52
53
impl<Impl: SelectorImpl> Default for SelectorBuilder<Impl> {
54
#[inline(always)]
55
fn default() -> Self {
56
SelectorBuilder {
57
simple_selectors: SmallVec::new(),
58
combinators: SmallVec::new(),
59
current_len: 0,
60
}
61
}
62
}
63
64
impl<Impl: SelectorImpl> Push<Component<Impl>> for SelectorBuilder<Impl> {
65
fn push(&mut self, value: Component<Impl>) {
66
self.push_simple_selector(value);
67
}
68
}
69
70
impl<Impl: SelectorImpl> SelectorBuilder<Impl> {
71
/// Pushes a simple selector onto the current compound selector.
72
#[inline(always)]
73
pub fn push_simple_selector(&mut self, ss: Component<Impl>) {
74
assert!(!ss.is_combinator());
75
self.simple_selectors.push(ss);
76
self.current_len += 1;
77
}
78
79
/// Completes the current compound selector and starts a new one, delimited
80
/// by the given combinator.
81
#[inline(always)]
82
pub fn push_combinator(&mut self, c: Combinator) {
83
self.combinators.push((c, self.current_len));
84
self.current_len = 0;
85
}
86
87
/// Returns true if combinators have ever been pushed to this builder.
88
#[inline(always)]
89
pub fn has_combinators(&self) -> bool {
90
!self.combinators.is_empty()
91
}
92
93
/// Consumes the builder, producing a Selector.
94
#[inline(always)]
95
pub fn build(
96
&mut self,
97
parsed_pseudo: bool,
98
parsed_slotted: bool,
99
parsed_part: bool,
100
) -> ThinArc<SpecificityAndFlags, Component<Impl>> {
101
// Compute the specificity and flags.
102
let specificity = specificity(self.simple_selectors.iter());
103
let mut flags = SelectorFlags::empty();
104
if parsed_pseudo {
105
flags |= SelectorFlags::HAS_PSEUDO;
106
}
107
if parsed_slotted {
108
flags |= SelectorFlags::HAS_SLOTTED;
109
}
110
if parsed_part {
111
flags |= SelectorFlags::HAS_PART;
112
}
113
self.build_with_specificity_and_flags(SpecificityAndFlags { specificity, flags })
114
}
115
116
/// Builds with an explicit SpecificityAndFlags. This is separated from build() so
117
/// that unit tests can pass an explicit specificity.
118
#[inline(always)]
119
pub fn build_with_specificity_and_flags(
120
&mut self,
121
spec: SpecificityAndFlags,
122
) -> ThinArc<SpecificityAndFlags, Component<Impl>> {
123
// First, compute the total number of Components we'll need to allocate
124
// space for.
125
let full_len = self.simple_selectors.len() + self.combinators.len();
126
127
// Create the header.
128
let header = HeaderWithLength::new(spec, full_len);
129
130
// Create the Arc using an iterator that drains our buffers.
131
132
// Use a raw pointer to be able to call set_len despite "borrowing" the slice.
133
// This is similar to SmallVec::drain, but we use a slice here because
134
// we’re gonna traverse it non-linearly.
135
let raw_simple_selectors: *const [Component<Impl>] = &*self.simple_selectors;
136
unsafe {
137
// Panic-safety: if SelectorBuilderIter is not iterated to the end,
138
// some simple selectors will safely leak.
139
self.simple_selectors.set_len(0)
140
}
141
let (rest, current) = split_from_end(unsafe { &*raw_simple_selectors }, self.current_len);
142
let iter = SelectorBuilderIter {
143
current_simple_selectors: current.iter(),
144
rest_of_simple_selectors: rest,
145
combinators: self.combinators.drain().rev(),
146
};
147
148
Arc::into_thin(Arc::from_header_and_iter(header, iter))
149
}
150
}
151
152
struct SelectorBuilderIter<'a, Impl: SelectorImpl> {
153
current_simple_selectors: slice::Iter<'a, Component<Impl>>,
154
rest_of_simple_selectors: &'a [Component<Impl>],
155
combinators: iter::Rev<smallvec::Drain<'a, (Combinator, usize)>>,
156
}
157
158
impl<'a, Impl: SelectorImpl> ExactSizeIterator for SelectorBuilderIter<'a, Impl> {
159
fn len(&self) -> usize {
160
self.current_simple_selectors.len() +
161
self.rest_of_simple_selectors.len() +
162
self.combinators.len()
163
}
164
}
165
166
impl<'a, Impl: SelectorImpl> Iterator for SelectorBuilderIter<'a, Impl> {
167
type Item = Component<Impl>;
168
#[inline(always)]
169
fn next(&mut self) -> Option<Self::Item> {
170
if let Some(simple_selector_ref) = self.current_simple_selectors.next() {
171
// Move a simple selector out of this slice iterator.
172
// This is safe because we’ve called SmallVec::set_len(0) above,
173
// so SmallVec::drop won’t drop this simple selector.
174
unsafe { Some(ptr::read(simple_selector_ref)) }
175
} else {
176
self.combinators.next().map(|(combinator, len)| {
177
let (rest, current) = split_from_end(self.rest_of_simple_selectors, len);
178
self.rest_of_simple_selectors = rest;
179
self.current_simple_selectors = current.iter();
180
Component::Combinator(combinator)
181
})
182
}
183
}
184
185
fn size_hint(&self) -> (usize, Option<usize>) {
186
(self.len(), Some(self.len()))
187
}
188
}
189
190
fn split_from_end<T>(s: &[T], at: usize) -> (&[T], &[T]) {
191
s.split_at(s.len() - at)
192
}
193
194
bitflags! {
195
/// Flags that indicate at which point of parsing a selector are we.
196
#[derive(Default, ToShmem)]
197
pub (crate) struct SelectorFlags : u8 {
198
const HAS_PSEUDO = 1 << 0;
199
const HAS_SLOTTED = 1 << 1;
200
const HAS_PART = 1 << 2;
201
}
202
}
203
204
#[derive(Clone, Copy, Debug, Eq, PartialEq, ToShmem)]
205
pub struct SpecificityAndFlags {
206
/// There are two free bits here, since we use ten bits for each specificity
207
/// kind (id, class, element).
208
pub(crate) specificity: u32,
209
/// There's padding after this field due to the size of the flags.
210
pub(crate) flags: SelectorFlags,
211
}
212
213
impl SpecificityAndFlags {
214
#[inline]
215
pub fn specificity(&self) -> u32 {
216
self.specificity
217
}
218
219
#[inline]
220
pub fn has_pseudo_element(&self) -> bool {
221
self.flags.intersects(SelectorFlags::HAS_PSEUDO)
222
}
223
224
#[inline]
225
pub fn is_slotted(&self) -> bool {
226
self.flags.intersects(SelectorFlags::HAS_SLOTTED)
227
}
228
229
#[inline]
230
pub fn is_part(&self) -> bool {
231
self.flags.intersects(SelectorFlags::HAS_PART)
232
}
233
}
234
235
const MAX_10BIT: u32 = (1u32 << 10) - 1;
236
237
#[derive(Add, AddAssign, Clone, Copy, Default, Eq, Ord, PartialEq, PartialOrd)]
238
struct Specificity {
239
id_selectors: u32,
240
class_like_selectors: u32,
241
element_selectors: u32,
242
}
243
244
impl From<u32> for Specificity {
245
#[inline]
246
fn from(value: u32) -> Specificity {
247
assert!(value <= MAX_10BIT << 20 | MAX_10BIT << 10 | MAX_10BIT);
248
Specificity {
249
id_selectors: value >> 20,
250
class_like_selectors: (value >> 10) & MAX_10BIT,
251
element_selectors: value & MAX_10BIT,
252
}
253
}
254
}
255
256
impl From<Specificity> for u32 {
257
#[inline]
258
fn from(specificity: Specificity) -> u32 {
259
cmp::min(specificity.id_selectors, MAX_10BIT) << 20 |
260
cmp::min(specificity.class_like_selectors, MAX_10BIT) << 10 |
261
cmp::min(specificity.element_selectors, MAX_10BIT)
262
}
263
}
264
265
fn specificity<Impl>(iter: slice::Iter<Component<Impl>>) -> u32
266
where
267
Impl: SelectorImpl,
268
{
269
complex_selector_specificity(iter).into()
270
}
271
272
fn complex_selector_specificity<Impl>(iter: slice::Iter<Component<Impl>>) -> Specificity
273
where
274
Impl: SelectorImpl,
275
{
276
fn simple_selector_specificity<Impl>(
277
simple_selector: &Component<Impl>,
278
specificity: &mut Specificity,
279
) where
280
Impl: SelectorImpl,
281
{
282
match *simple_selector {
283
Component::Combinator(..) => {
284
unreachable!("Found combinator in simple selectors vector?");
285
},
286
Component::Part(..) | Component::PseudoElement(..) | Component::LocalName(..) => {
287
specificity.element_selectors += 1
288
},
289
Component::Slotted(ref selector) => {
290
specificity.element_selectors += 1;
291
// Note that due to the way ::slotted works we only compete with
292
// other ::slotted rules, so the above rule doesn't really
293
// matter, but we do it still for consistency with other
294
// pseudo-elements.
295
//
297
*specificity += Specificity::from(selector.specificity());
298
},
299
Component::Host(ref selector) => {
300
specificity.class_like_selectors += 1;
301
if let Some(ref selector) = *selector {
303
*specificity += Specificity::from(selector.specificity());
304
}
305
},
306
Component::ID(..) => {
307
specificity.id_selectors += 1;
308
},
309
Component::Class(..) |
310
Component::AttributeInNoNamespace { .. } |
311
Component::AttributeInNoNamespaceExists { .. } |
312
Component::AttributeOther(..) |
313
Component::FirstChild |
314
Component::LastChild |
315
Component::OnlyChild |
316
Component::Root |
317
Component::Empty |
318
Component::Scope |
319
Component::NthChild(..) |
320
Component::NthLastChild(..) |
321
Component::NthOfType(..) |
322
Component::NthLastOfType(..) |
323
Component::FirstOfType |
324
Component::LastOfType |
325
Component::OnlyOfType |
326
Component::NonTSPseudoClass(..) => {
327
specificity.class_like_selectors += 1;
328
},
329
Component::ExplicitUniversalType |
330
Component::ExplicitAnyNamespace |
331
Component::ExplicitNoNamespace |
332
Component::DefaultNamespace(..) |
333
Component::Namespace(..) => {
334
// Does not affect specificity
335
},
336
Component::Negation(ref negated) => {
337
for ss in negated.iter() {
338
simple_selector_specificity(&ss, specificity);
339
}
340
},
341
}
342
}
343
344
let mut specificity = Default::default();
345
for simple_selector in iter {
346
simple_selector_specificity(&simple_selector, &mut specificity);
347
}
348
specificity
349
}