lewp_selectors/
builder.rs

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
20use crate::parser::{Combinator, Component, SelectorImpl};
21use crate::sink::Push;
22use servo_arc::{Arc, HeaderWithLength, ThinArc};
23use smallvec::{self, SmallVec};
24use std::cmp;
25use std::iter;
26use std::ptr;
27use 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)]
39pub 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
53impl<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
64impl<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
70impl<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 {
114            specificity,
115            flags,
116        })
117    }
118
119    /// Builds with an explicit SpecificityAndFlags. This is separated from build() so
120    /// that unit tests can pass an explicit specificity.
121    #[inline(always)]
122    pub fn build_with_specificity_and_flags(
123        &mut self,
124        spec: SpecificityAndFlags,
125    ) -> ThinArc<SpecificityAndFlags, Component<Impl>> {
126        // First, compute the total number of Components we'll need to allocate
127        // space for.
128        let full_len = self.simple_selectors.len() + self.combinators.len();
129
130        // Create the header.
131        let header = HeaderWithLength::new(spec, full_len);
132
133        // Create the Arc using an iterator that drains our buffers.
134
135        // Use a raw pointer to be able to call set_len despite "borrowing" the slice.
136        // This is similar to SmallVec::drain, but we use a slice here because
137        // we’re gonna traverse it non-linearly.
138        let raw_simple_selectors: *const [Component<Impl>] =
139            &*self.simple_selectors;
140        unsafe {
141            // Panic-safety: if SelectorBuilderIter is not iterated to the end,
142            // some simple selectors will safely leak.
143            self.simple_selectors.set_len(0)
144        }
145        let (rest, current) =
146            split_from_end(unsafe { &*raw_simple_selectors }, self.current_len);
147        let iter = SelectorBuilderIter {
148            current_simple_selectors: current.iter(),
149            rest_of_simple_selectors: rest,
150            combinators: self.combinators.drain(..).rev(),
151        };
152
153        Arc::into_thin(Arc::from_header_and_iter(header, iter))
154    }
155}
156
157struct SelectorBuilderIter<'a, Impl: SelectorImpl> {
158    current_simple_selectors: slice::Iter<'a, Component<Impl>>,
159    rest_of_simple_selectors: &'a [Component<Impl>],
160    combinators: iter::Rev<smallvec::Drain<'a, [(Combinator, usize); 16]>>,
161}
162
163impl<'a, Impl: SelectorImpl> ExactSizeIterator
164    for SelectorBuilderIter<'a, Impl>
165{
166    fn len(&self) -> usize {
167        self.current_simple_selectors.len()
168            + self.rest_of_simple_selectors.len()
169            + self.combinators.len()
170    }
171}
172
173impl<'a, Impl: SelectorImpl> Iterator for SelectorBuilderIter<'a, Impl> {
174    type Item = Component<Impl>;
175    #[inline(always)]
176    fn next(&mut self) -> Option<Self::Item> {
177        if let Some(simple_selector_ref) = self.current_simple_selectors.next()
178        {
179            // Move a simple selector out of this slice iterator.
180            // This is safe because we’ve called SmallVec::set_len(0) above,
181            // so SmallVec::drop won’t drop this simple selector.
182            unsafe { Some(ptr::read(simple_selector_ref)) }
183        } else {
184            self.combinators.next().map(|(combinator, len)| {
185                let (rest, current) =
186                    split_from_end(self.rest_of_simple_selectors, len);
187                self.rest_of_simple_selectors = rest;
188                self.current_simple_selectors = current.iter();
189                Component::Combinator(combinator)
190            })
191        }
192    }
193
194    fn size_hint(&self) -> (usize, Option<usize>) {
195        (self.len(), Some(self.len()))
196    }
197}
198
199fn split_from_end<T>(s: &[T], at: usize) -> (&[T], &[T]) {
200    s.split_at(s.len() - at)
201}
202
203bitflags! {
204    /// Flags that indicate at which point of parsing a selector are we.
205    #[derive(Default)]
206    pub (crate) struct SelectorFlags : u8 {
207        const HAS_PSEUDO = 1 << 0;
208        const HAS_SLOTTED = 1 << 1;
209        const HAS_PART = 1 << 2;
210    }
211}
212
213#[derive(Clone, Copy, Debug, Eq, PartialEq)]
214pub struct SpecificityAndFlags {
215    /// There are two free bits here, since we use ten bits for each specificity
216    /// kind (id, class, element).
217    pub(crate) specificity: u32,
218    /// There's padding after this field due to the size of the flags.
219    pub(crate) flags: SelectorFlags,
220}
221
222impl SpecificityAndFlags {
223    #[inline]
224    pub fn specificity(&self) -> u32 {
225        self.specificity
226    }
227
228    #[inline]
229    pub fn has_pseudo_element(&self) -> bool {
230        self.flags.intersects(SelectorFlags::HAS_PSEUDO)
231    }
232
233    #[inline]
234    pub fn is_slotted(&self) -> bool {
235        self.flags.intersects(SelectorFlags::HAS_SLOTTED)
236    }
237
238    #[inline]
239    pub fn is_part(&self) -> bool {
240        self.flags.intersects(SelectorFlags::HAS_PART)
241    }
242}
243
244const MAX_10BIT: u32 = (1u32 << 10) - 1;
245
246#[derive(
247    Add, AddAssign, Clone, Copy, Default, Eq, Ord, PartialEq, PartialOrd,
248)]
249struct Specificity {
250    id_selectors: u32,
251    class_like_selectors: u32,
252    element_selectors: u32,
253}
254
255impl From<u32> for Specificity {
256    #[inline]
257    fn from(value: u32) -> Specificity {
258        assert!(value <= MAX_10BIT << 20 | MAX_10BIT << 10 | MAX_10BIT);
259        Specificity {
260            id_selectors: value >> 20,
261            class_like_selectors: (value >> 10) & MAX_10BIT,
262            element_selectors: value & MAX_10BIT,
263        }
264    }
265}
266
267impl From<Specificity> for u32 {
268    #[inline]
269    fn from(specificity: Specificity) -> u32 {
270        cmp::min(specificity.id_selectors, MAX_10BIT) << 20
271            | cmp::min(specificity.class_like_selectors, MAX_10BIT) << 10
272            | cmp::min(specificity.element_selectors, MAX_10BIT)
273    }
274}
275
276fn specificity<Impl>(iter: slice::Iter<Component<Impl>>) -> u32
277where
278    Impl: SelectorImpl,
279{
280    complex_selector_specificity(iter).into()
281}
282
283fn complex_selector_specificity<Impl>(
284    iter: slice::Iter<Component<Impl>>,
285) -> Specificity
286where
287    Impl: SelectorImpl,
288{
289    fn simple_selector_specificity<Impl>(
290        simple_selector: &Component<Impl>,
291        specificity: &mut Specificity,
292    ) where
293        Impl: SelectorImpl,
294    {
295        match *simple_selector {
296            Component::Combinator(..) => {
297                unreachable!("Found combinator in simple selectors vector?");
298            }
299            Component::Part(..)
300            | Component::PseudoElement(..)
301            | Component::LocalName(..) => specificity.element_selectors += 1,
302            Component::Slotted(ref selector) => {
303                specificity.element_selectors += 1;
304                // Note that due to the way ::slotted works we only compete with
305                // other ::slotted rules, so the above rule doesn't really
306                // matter, but we do it still for consistency with other
307                // pseudo-elements.
308                //
309                // See: https://github.com/w3c/csswg-drafts/issues/1915
310                *specificity += Specificity::from(selector.specificity());
311            }
312            Component::Host(ref selector) => {
313                specificity.class_like_selectors += 1;
314                if let Some(ref selector) = *selector {
315                    // See: https://github.com/w3c/csswg-drafts/issues/1915
316                    *specificity += Specificity::from(selector.specificity());
317                }
318            }
319            Component::ID(..) => {
320                specificity.id_selectors += 1;
321            }
322            Component::Class(..)
323            | Component::AttributeInNoNamespace { .. }
324            | Component::AttributeInNoNamespaceExists { .. }
325            | Component::AttributeOther(..)
326            | Component::FirstChild
327            | Component::LastChild
328            | Component::OnlyChild
329            | Component::Root
330            | Component::Empty
331            | Component::Scope
332            | Component::NthChild(..)
333            | Component::NthLastChild(..)
334            | Component::NthOfType(..)
335            | Component::NthLastOfType(..)
336            | Component::FirstOfType
337            | Component::LastOfType
338            | Component::OnlyOfType
339            | Component::NonTSPseudoClass(..) => {
340                specificity.class_like_selectors += 1;
341            }
342            Component::Negation(ref list) | Component::Is(ref list) => {
343                // https://drafts.csswg.org/selectors/#specificity-rules:
344                //
345                //     The specificity of an :is() pseudo-class is replaced by the
346                //     specificity of the most specific complex selector in its
347                //     selector list argument.
348                let mut max = 0;
349                for selector in &**list {
350                    max = std::cmp::max(selector.specificity(), max);
351                }
352                *specificity += Specificity::from(max);
353            }
354            Component::Where(..)
355            | Component::ExplicitUniversalType
356            | Component::ExplicitAnyNamespace
357            | Component::ExplicitNoNamespace
358            | Component::DefaultNamespace(..)
359            | Component::Namespace(..) => {
360                // Does not affect specificity
361            }
362        }
363    }
364
365    let mut specificity = Default::default();
366    for simple_selector in iter {
367        simple_selector_specificity(&simple_selector, &mut specificity);
368    }
369    specificity
370}