mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
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:
parent
96594be65d
commit
fac050325c
2 changed files with 92 additions and 28 deletions
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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(),
|
||||
)]))
|
||||
);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue