use crate::parser::{Combinator, Component, NonTSPseudoClass, SelectorImpl};
use crate::sink::Push;
use servo_arc::{Arc, HeaderWithLength, ThinArc};
use smallvec::{self, SmallVec};
use std::cmp;
use std::iter;
use std::ptr;
use std::slice;
#[derive(Debug)]
pub struct SelectorBuilder<Impl: SelectorImpl> {
simple_selectors: SmallVec<[Component<Impl>; 32]>,
combinators: SmallVec<[(Combinator, usize); 16]>,
current_len: usize,
}
impl<Impl: SelectorImpl> Default for SelectorBuilder<Impl> {
#[inline(always)]
fn default() -> Self {
SelectorBuilder {
simple_selectors: SmallVec::new(),
combinators: SmallVec::new(),
current_len: 0,
}
}
}
impl<Impl: SelectorImpl> Push<Component<Impl>> for SelectorBuilder<Impl> {
fn push(&mut self, value: Component<Impl>) {
self.push_simple_selector(value);
}
}
impl<Impl: SelectorImpl> SelectorBuilder<Impl> {
#[inline(always)]
pub fn push_simple_selector(&mut self, ss: Component<Impl>) {
assert!(!ss.is_combinator());
self.simple_selectors.push(ss);
self.current_len += 1;
}
#[inline(always)]
pub fn push_combinator(&mut self, c: Combinator) {
self.combinators.push((c, self.current_len));
self.current_len = 0;
}
#[inline(always)]
pub fn has_combinators(&self) -> bool {
!self.combinators.is_empty()
}
#[inline(always)]
pub fn build(
&mut self,
parsed_pseudo: bool,
parsed_slotted: bool,
parsed_part: bool,
) -> ThinArc<SpecificityAndFlags, Component<Impl>> {
let specificity = specificity(self.simple_selectors.iter());
let mut flags = SelectorFlags::empty();
if parsed_pseudo {
flags |= SelectorFlags::HAS_PSEUDO;
}
if parsed_slotted {
flags |= SelectorFlags::HAS_SLOTTED;
}
if parsed_part {
flags |= SelectorFlags::HAS_PART;
}
self.build_with_specificity_and_flags(SpecificityAndFlags { specificity, flags })
}
#[inline(always)]
pub fn build_with_specificity_and_flags(
&mut self,
spec: SpecificityAndFlags,
) -> ThinArc<SpecificityAndFlags, Component<Impl>> {
let full_len = self.simple_selectors.len() + self.combinators.len();
let header = HeaderWithLength::new(spec, full_len);
let raw_simple_selectors: *const [Component<Impl>] = &*self.simple_selectors;
unsafe {
self.simple_selectors.set_len(0)
}
let (rest, current) = split_from_end(unsafe { &*raw_simple_selectors }, self.current_len);
let iter = SelectorBuilderIter {
current_simple_selectors: current.iter(),
rest_of_simple_selectors: rest,
combinators: self.combinators.drain(..).rev(),
};
Arc::into_thin(Arc::from_header_and_iter(header, iter))
}
}
struct SelectorBuilderIter<'a, Impl: SelectorImpl> {
current_simple_selectors: slice::Iter<'a, Component<Impl>>,
rest_of_simple_selectors: &'a [Component<Impl>],
combinators: iter::Rev<smallvec::Drain<'a, [(Combinator, usize); 16]>>,
}
impl<'a, Impl: SelectorImpl> ExactSizeIterator for SelectorBuilderIter<'a, Impl> {
fn len(&self) -> usize {
self.current_simple_selectors.len() +
self.rest_of_simple_selectors.len() +
self.combinators.len()
}
}
impl<'a, Impl: SelectorImpl> Iterator for SelectorBuilderIter<'a, Impl> {
type Item = Component<Impl>;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
if let Some(simple_selector_ref) = self.current_simple_selectors.next() {
unsafe { Some(ptr::read(simple_selector_ref)) }
} else {
self.combinators.next().map(|(combinator, len)| {
let (rest, current) = split_from_end(self.rest_of_simple_selectors, len);
self.rest_of_simple_selectors = rest;
self.current_simple_selectors = current.iter();
Component::Combinator(combinator)
})
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.len(), Some(self.len()))
}
}
fn split_from_end<T>(s: &[T], at: usize) -> (&[T], &[T]) {
s.split_at(s.len() - at)
}
bitflags! {
#[derive(Default)]
pub (crate) struct SelectorFlags : u8 {
const HAS_PSEUDO = 1 << 0;
const HAS_SLOTTED = 1 << 1;
const HAS_PART = 1 << 2;
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct SpecificityAndFlags {
pub(crate) specificity: u32,
pub(crate) flags: SelectorFlags,
}
impl SpecificityAndFlags {
#[inline]
pub fn specificity(&self) -> u32 {
self.specificity
}
#[inline]
pub fn has_pseudo_element(&self) -> bool {
self.flags.intersects(SelectorFlags::HAS_PSEUDO)
}
#[inline]
pub fn is_slotted(&self) -> bool {
self.flags.intersects(SelectorFlags::HAS_SLOTTED)
}
#[inline]
pub fn is_part(&self) -> bool {
self.flags.intersects(SelectorFlags::HAS_PART)
}
}
const MAX_10BIT: u32 = (1u32 << 10) - 1;
#[derive(Add, AddAssign, Clone, Copy, Default, Eq, Ord, PartialEq, PartialOrd)]
struct Specificity {
id_selectors: u32,
class_like_selectors: u32,
element_selectors: u32,
}
impl From<u32> for Specificity {
#[inline]
fn from(value: u32) -> Specificity {
assert!(value <= MAX_10BIT << 20 | MAX_10BIT << 10 | MAX_10BIT);
Specificity {
id_selectors: value >> 20,
class_like_selectors: (value >> 10) & MAX_10BIT,
element_selectors: value & MAX_10BIT,
}
}
}
impl From<Specificity> for u32 {
#[inline]
fn from(specificity: Specificity) -> u32 {
cmp::min(specificity.id_selectors, MAX_10BIT) << 20 |
cmp::min(specificity.class_like_selectors, MAX_10BIT) << 10 |
cmp::min(specificity.element_selectors, MAX_10BIT)
}
}
fn specificity<Impl>(iter: slice::Iter<Component<Impl>>) -> u32
where
Impl: SelectorImpl,
{
complex_selector_specificity(iter).into()
}
fn complex_selector_specificity<Impl>(iter: slice::Iter<Component<Impl>>) -> Specificity
where
Impl: SelectorImpl,
{
fn simple_selector_specificity<Impl>(
simple_selector: &Component<Impl>,
specificity: &mut Specificity,
) where
Impl: SelectorImpl,
{
match *simple_selector {
Component::Combinator(..) => {
unreachable!("Found combinator in simple selectors vector?");
},
Component::Part(..) | Component::PseudoElement(..) | Component::LocalName(..) => {
specificity.element_selectors += 1
},
Component::Slotted(ref selector) => {
specificity.element_selectors += 1;
*specificity += Specificity::from(selector.specificity());
},
Component::Host(ref selector) => {
specificity.class_like_selectors += 1;
if let Some(ref selector) = *selector {
*specificity += Specificity::from(selector.specificity());
}
},
Component::ID(..) => {
specificity.id_selectors += 1;
},
Component::Class(..) |
Component::AttributeInNoNamespace { .. } |
Component::AttributeInNoNamespaceExists { .. } |
Component::AttributeOther(..) |
Component::FirstChild |
Component::LastChild |
Component::OnlyChild |
Component::Root |
Component::Empty |
Component::Scope |
Component::NthChild(..) |
Component::NthLastChild(..) |
Component::NthOfType(..) |
Component::NthLastOfType(..) |
Component::FirstOfType |
Component::LastOfType |
Component::OnlyOfType => {
specificity.class_like_selectors += 1;
},
Component::NonTSPseudoClass(ref pseudo) => {
if !pseudo.has_zero_specificity() {
specificity.class_like_selectors += 1;
}
},
Component::ExplicitUniversalType |
Component::ExplicitAnyNamespace |
Component::ExplicitNoNamespace |
Component::DefaultNamespace(..) |
Component::Namespace(..) => {
},
Component::Negation(ref negated) => {
for ss in negated.iter() {
simple_selector_specificity(&ss, specificity);
}
},
}
}
let mut specificity = Default::default();
for simple_selector in iter {
simple_selector_specificity(&simple_selector, &mut specificity);
}
specificity
}