style: Add an extra flag to flag ::part() to selectors.

This grows the selector struct, but only in 32-bit, since in 64-bit we take
space from the alignment padding that we're paying due to having the size of the
slice as a word.

Differential Revision: https://phabricator.services.mozilla.com/D32645
This commit is contained in:
Emilio Cobos Álvarez 2019-06-11 17:42:23 +00:00
parent 96594be65d
commit fac050325c
No known key found for this signature in database
GPG key ID: E1152D0994E4BF8A
2 changed files with 92 additions and 28 deletions

View file

@ -96,18 +96,24 @@ impl<Impl: SelectorImpl> SelectorBuilder<Impl> {
&mut self,
parsed_pseudo: bool,
parsed_slotted: bool,
parsed_part: bool,
) -> ThinArc<SpecificityAndFlags, Component<Impl>> {
// Compute the specificity and flags.
let mut spec = SpecificityAndFlags(specificity(self.simple_selectors.iter()));
let specificity = specificity(self.simple_selectors.iter());
let mut flags = SelectorFlags::empty();
if parsed_pseudo {
spec.0 |= HAS_PSEUDO_BIT;
flags |= SelectorFlags::HAS_PSEUDO;
}
if parsed_slotted {
spec.0 |= HAS_SLOTTED_BIT;
flags |= SelectorFlags::HAS_SLOTTED;
}
self.build_with_specificity_and_flags(spec)
if parsed_part {
flags |= SelectorFlags::HAS_PART;
}
self.build_with_specificity_and_flags(SpecificityAndFlags {
specificity,
flags,
})
}
/// Builds with an explicit SpecificityAndFlags. This is separated from build() so
@ -188,28 +194,44 @@ fn split_from_end<T>(s: &[T], at: usize) -> (&[T], &[T]) {
s.split_at(s.len() - at)
}
pub const HAS_PSEUDO_BIT: u32 = 1 << 30;
pub const HAS_SLOTTED_BIT: u32 = 1 << 31;
bitflags! {
/// Flags that indicate at which point of parsing a selector are we.
#[derive(Default, ToShmem)]
pub (crate) struct SelectorFlags : u8 {
const HAS_PSEUDO = 1 << 0;
const HAS_SLOTTED = 1 << 1;
const HAS_PART = 1 << 2;
}
}
/// We use ten bits for each specificity kind (id, class, element), and the two
/// high bits for the pseudo and slotted flags.
#[derive(Clone, Copy, Debug, Eq, PartialEq, ToShmem)]
pub struct SpecificityAndFlags(pub u32);
pub struct SpecificityAndFlags {
/// There are two free bits here, since we use ten bits for each specificity
/// kind (id, class, element).
pub (crate) specificity: u32,
/// There's padding after this field due to the size of the flags.
pub (crate) flags: SelectorFlags,
}
impl SpecificityAndFlags {
#[inline]
pub fn specificity(&self) -> u32 {
self.0 & !(HAS_PSEUDO_BIT | HAS_SLOTTED_BIT)
self.specificity
}
#[inline]
pub fn has_pseudo_element(&self) -> bool {
(self.0 & HAS_PSEUDO_BIT) != 0
self.flags.intersects(SelectorFlags::HAS_PSEUDO)
}
#[inline]
pub fn is_slotted(&self) -> bool {
(self.0 & HAS_SLOTTED_BIT) != 0
self.flags.intersects(SelectorFlags::HAS_SLOTTED)
}
#[inline]
pub fn is_part(&self) -> bool {
self.flags.intersects(SelectorFlags::HAS_PART)
}
}

View file

@ -6,7 +6,7 @@ use crate::attr::{AttrSelectorOperator, AttrSelectorWithOptionalNamespace};
use crate::attr::{NamespaceConstraint, ParsedAttrSelectorOperation};
use crate::attr::{ParsedCaseSensitivity, SELECTOR_WHITESPACE};
use crate::bloom::BLOOM_HASH_MASK;
use crate::builder::{SelectorBuilder, SpecificityAndFlags};
use crate::builder::{SelectorBuilder, SelectorFlags, SpecificityAndFlags};
use crate::context::QuirksMode;
use crate::sink::Push;
pub use crate::visitor::{SelectorVisitor, Visit};
@ -601,6 +601,11 @@ impl<Impl: SelectorImpl> Selector<Impl> {
self.0.header.header.is_slotted()
}
#[inline]
pub fn is_part(&self) -> bool {
self.0.header.header.is_part()
}
#[inline]
pub fn pseudo_element(&self) -> Option<&Impl::PseudoElement> {
if !self.has_pseudo_element() {
@ -720,7 +725,8 @@ impl<Impl: SelectorImpl> Selector<Impl> {
}
/// Creates a Selector from a vec of Components, specified in parse order. Used in tests.
pub fn from_vec(vec: Vec<Component<Impl>>, specificity_and_flags: u32) -> Self {
#[allow(unused)]
pub (crate) fn from_vec(vec: Vec<Component<Impl>>, specificity: u32, flags: SelectorFlags) -> Self {
let mut builder = SelectorBuilder::default();
for component in vec.into_iter() {
if let Some(combinator) = component.as_combinator() {
@ -729,7 +735,10 @@ impl<Impl: SelectorImpl> Selector<Impl> {
builder.push_simple_selector(component);
}
}
let spec = SpecificityAndFlags(specificity_and_flags);
let spec = SpecificityAndFlags {
specificity,
flags,
};
Selector(builder.build_with_specificity_and_flags(spec))
}
@ -1445,6 +1454,7 @@ where
let mut has_pseudo_element = false;
let mut slotted = false;
let mut part = false;
'outer_loop: loop {
// Parse a sequence of simple selectors.
let state = match parse_compound_selector(parser, input, &mut builder)? {
@ -1461,7 +1471,7 @@ where
if state.intersects(SelectorParsingState::AFTER_PSEUDO) {
has_pseudo_element = state.intersects(SelectorParsingState::AFTER_PSEUDO_ELEMENT);
slotted = state.intersects(SelectorParsingState::AFTER_SLOTTED);
let part = state.intersects(SelectorParsingState::AFTER_PART);
part = state.intersects(SelectorParsingState::AFTER_PART);
debug_assert!(has_pseudo_element || slotted || part);
break;
}
@ -1500,9 +1510,7 @@ where
builder.push_combinator(combinator);
}
// TODO(emilio): We'll have to flag part() somehow as well, but we need more
// bits!
Ok(Selector(builder.build(has_pseudo_element, slotted)))
Ok(Selector(builder.build(has_pseudo_element, slotted, part)))
}
impl<Impl: SelectorImpl> Selector<Impl> {
@ -2249,7 +2257,7 @@ where
#[cfg(test)]
pub mod tests {
use super::*;
use crate::builder::HAS_PSEUDO_BIT;
use crate::builder::SelectorFlags;
use crate::parser;
use cssparser::{serialize_identifier, Parser as CssParser, ParserInput, ToCss};
use std::collections::HashMap;
@ -2532,6 +2540,7 @@ pub mod tests {
lower_name: DummyAtom::from("eeÉ"),
})],
specificity(0, 0, 1),
Default::default(),
)]))
);
assert_eq!(
@ -2545,6 +2554,7 @@ pub mod tests {
}),
],
specificity(0, 0, 1),
Default::default(),
)]))
);
// When the default namespace is not set, *| should be elided.
@ -2557,6 +2567,7 @@ pub mod tests {
lower_name: DummyAtom::from("e"),
})],
specificity(0, 0, 1),
Default::default(),
)]))
);
// When the default namespace is set, *| should _not_ be elided (as foo
@ -2577,13 +2588,15 @@ pub mod tests {
}),
],
specificity(0, 0, 1),
Default::default(),
)]))
);
assert_eq!(
parse("*"),
Ok(SelectorList::from_vec(vec![Selector::from_vec(
vec![Component::ExplicitUniversalType],
specificity(0, 0, 0)
specificity(0, 0, 0),
Default::default(),
)]))
);
assert_eq!(
@ -2594,13 +2607,15 @@ pub mod tests {
Component::ExplicitUniversalType,
],
specificity(0, 0, 0),
Default::default(),
)]))
);
assert_eq!(
parse_expected("*|*", Some("*")),
Ok(SelectorList::from_vec(vec![Selector::from_vec(
vec![Component::ExplicitUniversalType],
specificity(0, 0, 0)
specificity(0, 0, 0),
Default::default(),
)]))
);
assert_eq!(
@ -2614,6 +2629,7 @@ pub mod tests {
Component::ExplicitUniversalType,
],
specificity(0, 0, 0),
Default::default(),
)]))
);
assert_eq!(
@ -2624,6 +2640,7 @@ pub mod tests {
Component::NonTSPseudoClass(PseudoClass::Lang("en-US".to_owned())),
],
specificity(0, 2, 0),
Default::default(),
)]))
);
assert_eq!(
@ -2631,6 +2648,7 @@ pub mod tests {
Ok(SelectorList::from_vec(vec![Selector::from_vec(
vec![Component::ID(DummyAtom::from("bar"))],
specificity(1, 0, 0),
Default::default(),
)]))
);
assert_eq!(
@ -2645,6 +2663,7 @@ pub mod tests {
Component::ID(DummyAtom::from("bar")),
],
specificity(1, 1, 1),
Default::default(),
)]))
);
assert_eq!(
@ -2660,6 +2679,7 @@ pub mod tests {
Component::ID(DummyAtom::from("bar")),
],
specificity(1, 1, 1),
Default::default(),
)]))
);
// Default namespace does not apply to attribute selectors
@ -2673,6 +2693,7 @@ pub mod tests {
local_name_lower: DummyAtom::from("foo"),
}],
specificity(0, 1, 0),
Default::default(),
)]))
);
assert!(parse_ns("svg|circle", &parser).is_err());
@ -2690,6 +2711,7 @@ pub mod tests {
}),
],
specificity(0, 0, 1),
Default::default(),
)]))
);
assert_eq!(
@ -2700,6 +2722,7 @@ pub mod tests {
Component::ExplicitUniversalType,
],
specificity(0, 0, 0),
Default::default(),
)]))
);
// Default namespace does not apply to attribute selectors
@ -2718,6 +2741,7 @@ pub mod tests {
},
],
specificity(0, 1, 0),
Default::default(),
)]))
);
// Default namespace does apply to type selectors
@ -2732,6 +2756,7 @@ pub mod tests {
}),
],
specificity(0, 0, 1),
Default::default(),
)]))
);
assert_eq!(
@ -2742,6 +2767,7 @@ pub mod tests {
Component::ExplicitUniversalType,
],
specificity(0, 0, 0),
Default::default(),
)]))
);
assert_eq!(
@ -2752,6 +2778,7 @@ pub mod tests {
Component::ExplicitUniversalType,
],
specificity(0, 0, 0),
Default::default(),
)]))
);
// Default namespace applies to universal and type selectors inside :not and :matches,
@ -2768,6 +2795,7 @@ pub mod tests {
),
],
specificity(0, 1, 0),
Default::default(),
)]))
);
assert_eq!(
@ -2785,6 +2813,7 @@ pub mod tests {
),
],
specificity(0, 0, 0),
Default::default(),
)]))
);
assert_eq!(
@ -2805,6 +2834,7 @@ pub mod tests {
),
],
specificity(0, 0, 1),
Default::default(),
)]))
);
assert_eq!(
@ -2818,6 +2848,7 @@ pub mod tests {
case_sensitivity: ParsedCaseSensitivity::CaseSensitive,
}],
specificity(0, 1, 0),
Default::default(),
)]))
);
// https://github.com/mozilla/servo/issues/1723
@ -2828,7 +2859,8 @@ pub mod tests {
Component::Combinator(Combinator::PseudoElement),
Component::PseudoElement(PseudoElement::Before),
],
specificity(0, 0, 1) | HAS_PSEUDO_BIT,
specificity(0, 0, 1),
SelectorFlags::HAS_PSEUDO,
)]))
);
assert_eq!(
@ -2839,7 +2871,8 @@ pub mod tests {
Component::PseudoElement(PseudoElement::Before),
Component::NonTSPseudoClass(PseudoClass::Hover),
],
specificity(0, 1, 1) | HAS_PSEUDO_BIT,
specificity(0, 1, 1),
SelectorFlags::HAS_PSEUDO,
)]))
);
assert_eq!(
@ -2851,7 +2884,8 @@ pub mod tests {
Component::NonTSPseudoClass(PseudoClass::Hover),
Component::NonTSPseudoClass(PseudoClass::Hover),
],
specificity(0, 2, 1) | HAS_PSEUDO_BIT,
specificity(0, 2, 1),
SelectorFlags::HAS_PSEUDO,
)]))
);
assert!(parse("::before:hover:lang(foo)").is_err());
@ -2874,7 +2908,8 @@ pub mod tests {
Component::Combinator(Combinator::PseudoElement),
Component::PseudoElement(PseudoElement::After),
],
specificity(0, 0, 2) | HAS_PSEUDO_BIT,
specificity(0, 0, 2),
SelectorFlags::HAS_PSEUDO,
)]))
);
assert_eq!(
@ -2886,6 +2921,7 @@ pub mod tests {
Component::Class(DummyAtom::from("ok")),
],
(1 << 20) + (1 << 10) + (0 << 0),
Default::default(),
)]))
);
parser.default_ns = None;
@ -2901,6 +2937,7 @@ pub mod tests {
.into(),
)],
specificity(1, 0, 0),
Default::default(),
)]))
);
assert_eq!(
@ -2918,6 +2955,7 @@ pub mod tests {
.into(),
)],
specificity(0, 0, 1),
Default::default(),
)]))
);
// https://github.com/servo/servo/issues/16017
@ -2930,6 +2968,7 @@ pub mod tests {
.into(),
)],
specificity(0, 0, 0),
Default::default(),
)]))
);
assert_eq!(
@ -2944,6 +2983,7 @@ pub mod tests {
.into(),
)],
specificity(0, 0, 0),
Default::default(),
)]))
);
// *| should be elided if there is no default namespace.
@ -2957,6 +2997,7 @@ pub mod tests {
.into(),
)],
specificity(0, 0, 0),
Default::default(),
)]))
);
@ -2972,6 +3013,7 @@ pub mod tests {
.into(),
)],
specificity(0, 0, 0),
Default::default(),
)]))
);