Bug 1859917 - Make SelectorList cheaply cloneable and more compact. r=dshin

We have ArcSelectorList for selector-lists that are cheap to copy.

For bug 1859915 and related, what we're going to end up doing is probably
expanding into the SelectorList of the parent rule unconditionally, at least
for the "single bare &" case.

It'd be nice to unify SelectorList implementations as preparation for that, and
make them cheaper to clone in general.

My proposal is making SelectorList a tagged pointer to either a single
selector, or to a ThinArc, effectively preserving the SmallVec<> optimization
we have now. This patch implements that proposal.

Depends on D191361

Differential Revision: https://phabricator.services.mozilla.com/D191362
This commit is contained in:
Emilio Cobos Álvarez
2023-10-19 15:35:54 +00:00
parent 056ebd4273
commit 4067c993be
12 changed files with 204 additions and 139 deletions

View File

@@ -714,12 +714,12 @@ impl MallocSizeOf for selectors::parser::AncestorHashes {
}
}
impl<Impl: selectors::parser::SelectorImpl> MallocSizeOf for selectors::parser::Selector<Impl>
impl<Impl: selectors::parser::SelectorImpl> MallocUnconditionalSizeOf for selectors::parser::Selector<Impl>
where
Impl::NonTSPseudoClass: MallocSizeOf,
Impl::PseudoElement: MallocSizeOf,
{
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
fn unconditional_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
let mut n = 0;
// It's OK to measure this ThinArc directly because it's the
@@ -734,22 +734,42 @@ where
}
}
impl<Impl: selectors::parser::SelectorImpl> MallocSizeOf for selectors::parser::Component<Impl>
impl<Impl: selectors::parser::SelectorImpl> MallocUnconditionalSizeOf for selectors::parser::SelectorList<Impl>
where
Impl::NonTSPseudoClass: MallocSizeOf,
Impl::PseudoElement: MallocSizeOf,
{
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
fn unconditional_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
let mut n = 0;
// It's OK to measure this ThinArc directly because it's the "primary" reference. (The
// secondary references are on the Stylist.)
n += unsafe { ops.malloc_size_of(self.thin_arc_heap_ptr()) };
if self.len() > 1 {
for selector in self.slice().iter() {
n += selector.size_of(ops);
}
}
n
}
}
impl<Impl: selectors::parser::SelectorImpl> MallocUnconditionalSizeOf for selectors::parser::Component<Impl>
where
Impl::NonTSPseudoClass: MallocSizeOf,
Impl::PseudoElement: MallocSizeOf,
{
fn unconditional_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
use selectors::parser::Component;
match self {
Component::AttributeOther(ref attr_selector) => attr_selector.size_of(ops),
Component::Negation(ref components) => components.size_of(ops),
Component::Negation(ref components) => components.unconditional_size_of(ops),
Component::NonTSPseudoClass(ref pseudo) => (*pseudo).size_of(ops),
Component::Slotted(ref selector) | Component::Host(Some(ref selector)) => {
selector.size_of(ops)
selector.unconditional_size_of(ops)
},
Component::Is(ref list) | Component::Where(ref list) => list.size_of(ops),
Component::Is(ref list) | Component::Where(ref list) => list.unconditional_size_of(ops),
Component::Has(ref relative_selectors) => relative_selectors.size_of(ops),
Component::NthOf(ref nth_of_data) => nth_of_data.size_of(ops),
Component::PseudoElement(ref pseudo) => (*pseudo).size_of(ops),

View File

@@ -120,7 +120,7 @@ where
{
// This is pretty much any(..) but manually inlined because the compiler
// refuses to do so from querySelector / querySelectorAll.
for selector in &selector_list.0 {
for selector in selector_list.slice() {
let matches = matches_selector(selector, 0, None, element, context);
if matches {
return true;

View File

@@ -18,7 +18,7 @@ use cssparser::{BasicParseError, BasicParseErrorKind, ParseError, ParseErrorKind
use cssparser::{CowRcStr, Delimiter, SourceLocation};
use cssparser::{Parser as CssParser, ToCss, Token};
use precomputed_hash::PrecomputedHash;
use servo_arc::{Arc, ThinArc, UniqueArc};
use servo_arc::{Arc, ThinArc, UniqueArc, ThinArcUnion, ArcUnionBorrow};
use smallvec::SmallVec;
use std::borrow::{Borrow, Cow};
use std::fmt::{self, Debug};
@@ -352,12 +352,64 @@ pub trait Parser<'i> {
}
}
#[derive(Clone, Debug, Eq, PartialEq, ToShmem)]
/// A selector list is a tagged pointer with either a single selector, or a ThinArc<()> of multiple
/// selectors.
#[derive(Clone, Eq, Debug, PartialEq, ToShmem)]
#[shmem(no_bounds)]
pub struct SelectorList<Impl: SelectorImpl>(
#[shmem(field_bound)] pub SmallVec<[Selector<Impl>; 1]>,
#[shmem(field_bound)] ThinArcUnion<SpecificityAndFlags, Component<Impl>, (), Selector<Impl>>
);
impl<Impl: SelectorImpl> SelectorList<Impl> {
pub fn from_one(selector: Selector<Impl>) -> Self {
#[cfg(debug_assertions)]
let selector_repr = unsafe { *(&selector as *const _ as *const usize) };
let list = Self(ThinArcUnion::from_first(selector.into_data()));
debug_assert_eq!(
selector_repr,
unsafe { *(&list as *const _ as *const usize) },
"We rely on the same bit representation for the single selector variant"
);
list
}
pub fn from_iter(mut iter: impl ExactSizeIterator<Item = Selector<Impl>>) -> Self {
if iter.len() == 1 {
Self::from_one(iter.next().unwrap())
} else {
Self(ThinArcUnion::from_second(ThinArc::from_header_and_iter((), iter)))
}
}
#[inline]
pub fn slice(&self) -> &[Selector<Impl>] {
match self.0.borrow() {
ArcUnionBorrow::First(..) => {
// SAFETY: see from_one.
let selector: &Selector<Impl> = unsafe { std::mem::transmute(self) };
std::slice::from_ref(selector)
},
ArcUnionBorrow::Second(list) => list.get().slice(),
}
}
#[inline]
pub fn len(&self) -> usize {
match self.0.borrow() {
ArcUnionBorrow::First(..) => 1,
ArcUnionBorrow::Second(list) => list.len(),
}
}
/// Returns the address on the heap of the ThinArc for memory reporting.
pub fn thin_arc_heap_ptr(&self) -> *const ::std::os::raw::c_void {
match self.0.borrow() {
ArcUnionBorrow::First(s) => s.with_arc(|a| a.heap_ptr()),
ArcUnionBorrow::Second(s) => s.with_arc(|a| a.heap_ptr()),
}
}
}
/// Uniquely identify a selector based on its components, which is behind ThinArc and
/// is therefore stable.
#[derive(Clone, Copy, Hash, Eq, PartialEq)]
@@ -397,18 +449,7 @@ pub enum ParseRelative {
impl<Impl: SelectorImpl> SelectorList<Impl> {
/// Returns a selector list with a single `&`
pub fn ampersand() -> Self {
Self(smallvec::smallvec![Selector::ampersand()])
}
/// Copies a selector list into a reference counted list.
pub fn to_shared(&self) -> ArcSelectorList<Impl> {
ThinArc::from_header_and_iter((), self.0.iter().cloned())
}
/// Turns a selector list into a reference counted list. Drains the list. We don't use
/// `mut self` to avoid memmoving around.
pub fn into_shared(&mut self) -> ArcSelectorList<Impl> {
ThinArc::from_header_and_iter((), self.0.drain(..))
Self::from_one(Selector::ampersand())
}
/// Parse a comma-separated list of Selectors.
@@ -443,7 +484,7 @@ impl<Impl: SelectorImpl> SelectorList<Impl> {
where
P: Parser<'i, Impl = Impl>,
{
let mut values = SmallVec::new();
let mut values = SmallVec::<[_; 4]>::new();
let forgiving = recovery == ForgivingParsing::Yes && parser.allow_forgiving_selectors();
loop {
let selector = input.parse_until_before(Delimiter::Comma, |input| {
@@ -464,18 +505,18 @@ impl<Impl: SelectorImpl> SelectorList<Impl> {
Err(_) => break,
}
}
Ok(SelectorList(values))
Ok(Self::from_iter(values.into_iter()))
}
/// Replaces the parent selector in all the items of the selector list.
pub fn replace_parent_selector(&self, parent: &ArcSelectorList<Impl>) -> Self {
Self(self.0.iter().map(|selector| selector.replace_parent_selector(parent)).collect())
pub fn replace_parent_selector(&self, parent: &SelectorList<Impl>) -> Self {
Self::from_iter(self.slice().iter().map(|selector| selector.replace_parent_selector(parent)))
}
/// Creates a SelectorList from a Vec of selectors. Used in tests.
#[allow(dead_code)]
pub(crate) fn from_vec(v: Vec<Selector<Impl>>) -> Self {
SelectorList(SmallVec::from_vec(v))
SelectorList::from_iter(v.into_iter())
}
}
@@ -582,9 +623,10 @@ where
// :where and :is OR their selectors, so we can't put any hash
// in the filter if there's more than one selector, as that'd
// exclude elements that may match one of the other selectors.
if list.len() == 1 &&
let slice = list.slice();
if slice.len() == 1 &&
!collect_selector_hashes(
create_inner_iterator(&list.slice()[0]),
create_inner_iterator(&slice[0]),
quirks_mode,
hashes,
len,
@@ -653,6 +695,8 @@ pub fn namespace_empty_string<Impl: SelectorImpl>() -> Impl::NamespaceUrl {
Impl::NamespaceUrl::default()
}
type SelectorData<Impl> = ThinArc<SpecificityAndFlags, Component<Impl>>;
/// A Selector stores a sequence of simple selectors and combinators. The
/// iterator classes allow callers to iterate at either the raw sequence level or
/// at the level of sequences of simple selectors separated by combinators. Most
@@ -669,9 +713,8 @@ pub fn namespace_empty_string<Impl: SelectorImpl>() -> Impl::NamespaceUrl {
/// handle it in to_css to make it invisible to serialization.
#[derive(Clone, Eq, PartialEq, ToShmem)]
#[shmem(no_bounds)]
pub struct Selector<Impl: SelectorImpl>(
#[shmem(field_bound)] ThinArc<SpecificityAndFlags, Component<Impl>>,
);
#[repr(transparent)]
pub struct Selector<Impl: SelectorImpl>(#[shmem(field_bound)] SelectorData<Impl>);
impl<Impl: SelectorImpl> Selector<Impl> {
/// See Arc::mark_as_intentionally_leaked
@@ -905,7 +948,12 @@ impl<Impl: SelectorImpl> Selector<Impl> {
Selector(builder.build_with_specificity_and_flags(spec))
}
pub fn replace_parent_selector(&self, parent: &ArcSelectorList<Impl>) -> Self {
#[inline]
fn into_data(self) -> SelectorData<Impl> {
self.0
}
pub fn replace_parent_selector(&self, parent: &SelectorList<Impl>) -> Self {
// FIXME(emilio): Shouldn't allow replacing if parent has a pseudo-element selector
// or what not.
let flags = self.flags() - SelectorFlags::HAS_PARENT;
@@ -922,14 +970,13 @@ impl<Impl: SelectorImpl> Selector<Impl> {
fn replace_parent_on_selector_list<Impl: SelectorImpl>(
orig: &[Selector<Impl>],
parent: &ArcSelectorList<Impl>,
parent: &SelectorList<Impl>,
specificity: &mut Specificity,
with_specificity: bool,
) -> Option<ArcSelectorList<Impl>> {
) -> Option<SelectorList<Impl>> {
let mut any = false;
let result = ArcSelectorList::from_header_and_iter(
(),
let result = SelectorList::from_iter(
orig
.iter()
.map(|s| {
@@ -958,7 +1005,7 @@ impl<Impl: SelectorImpl> Selector<Impl> {
fn replace_parent_on_relative_selector_list<Impl: SelectorImpl>(
orig: &[RelativeSelector<Impl>],
parent: &ArcSelectorList<Impl>,
parent: &SelectorList<Impl>,
specificity: &mut Specificity,
) -> Vec<RelativeSelector<Impl>> {
let mut any = false;
@@ -990,7 +1037,7 @@ impl<Impl: SelectorImpl> Selector<Impl> {
fn replace_parent_on_selector<Impl: SelectorImpl>(
orig: &Selector<Impl>,
parent: &ArcSelectorList<Impl>,
parent: &SelectorList<Impl>,
specificity: &mut Specificity,
) -> Selector<Impl> {
if !orig.has_parent_selector() {
@@ -1755,8 +1802,8 @@ impl CombinatorComposition {
impl<Impl: SelectorImpl> RelativeSelector<Impl> {
fn from_selector_list(selector_list: SelectorList<Impl>) -> Box<[Self]> {
let vec: Vec<Self> = selector_list
.0
.into_iter()
.slice()
.iter()
.map(|selector| {
// It's more efficient to keep track of all this during the parse time, but that seems like a lot of special
// case handling for what it's worth.
@@ -1783,7 +1830,7 @@ impl<Impl: SelectorImpl> RelativeSelector<Impl> {
);
RelativeSelector {
match_hint,
selector,
selector: selector.clone(),
}
})
.collect();
@@ -1791,9 +1838,6 @@ impl<Impl: SelectorImpl> RelativeSelector<Impl> {
}
}
/// A reference-counted selector list.
pub type ArcSelectorList<Impl> = ThinArc<(), Selector<Impl>>;
/// A CSS simple selector or combinator. We store both in the same enum for
/// optimal packing and cache performance, see [1].
///
@@ -1833,7 +1877,7 @@ pub enum Component<Impl: SelectorImpl> {
),
/// Pseudo-classes
Negation(ArcSelectorList<Impl>),
Negation(SelectorList<Impl>),
Root,
Empty,
Scope,
@@ -1872,13 +1916,13 @@ pub enum Component<Impl: SelectorImpl> {
///
/// The inner argument is conceptually a SelectorList, but we move the
/// selectors to the heap to keep Component small.
Where(ArcSelectorList<Impl>),
Where(SelectorList<Impl>),
/// The `:is` pseudo-class.
///
/// https://drafts.csswg.org/selectors/#matches-pseudo
///
/// Same comment as above re. the argument.
Is(ArcSelectorList<Impl>),
Is(SelectorList<Impl>),
/// The `:has` pseudo-class.
///
/// https://drafts.csswg.org/selectors/#has-pseudo
@@ -2112,7 +2156,7 @@ impl<Impl: SelectorImpl> ToCss for SelectorList<Impl> {
where
W: fmt::Write,
{
serialize_selector_list(self.0.iter(), dest)
serialize_selector_list(self.slice().iter(), dest)
}
}
@@ -2961,7 +3005,7 @@ where
P: Parser<'i, Impl = Impl>,
Impl: SelectorImpl,
{
let mut list = SelectorList::parse_with_state(
let list = SelectorList::parse_with_state(
parser,
input,
state |
@@ -2971,7 +3015,7 @@ where
ParseRelative::No,
)?;
Ok(Component::Negation(list.into_shared()))
Ok(Component::Negation(list))
}
/// simple_selector_sequence
@@ -3077,7 +3121,7 @@ fn parse_is_where<'i, 't, P, Impl>(
parser: &P,
input: &mut CssParser<'i, 't>,
state: SelectorParsingState,
component: impl FnOnce(ArcSelectorList<Impl>) -> Component<Impl>,
component: impl FnOnce(SelectorList<Impl>) -> Component<Impl>,
) -> Result<Component<Impl>, ParseError<'i, P::Error>>
where
P: Parser<'i, Impl = Impl>,
@@ -3089,7 +3133,7 @@ where
// Pseudo-elements cannot be represented by the matches-any
// pseudo-class; they are not valid within :is().
//
let mut inner = SelectorList::parse_with_state(
let inner = SelectorList::parse_with_state(
parser,
input,
state |
@@ -3098,7 +3142,7 @@ where
ForgivingParsing::Yes,
ParseRelative::No,
)?;
Ok(component(inner.into_shared()))
Ok(component(inner))
}
fn parse_has<'i, 't, P, Impl>(
@@ -3202,7 +3246,7 @@ where
}
// Whitespace between "of" and the selector list is optional
// https://github.com/w3c/csswg-drafts/issues/8285
let mut selectors = SelectorList::parse_with_state(
let selectors = SelectorList::parse_with_state(
parser,
input,
state |
@@ -3213,7 +3257,7 @@ where
)?;
Ok(Component::NthOf(NthOfSelectorData::new(
&nth_data,
selectors.0.drain(..),
selectors.slice().iter().cloned(),
)))
}
@@ -4028,7 +4072,7 @@ pub mod tests {
vec![Component::Class(DummyAtom::from("cl"))],
specificity(0, 1, 0),
Default::default(),
)]).to_shared()
)])
),
],
specificity(0, 1, 0),
@@ -4048,7 +4092,7 @@ pub mod tests {
],
specificity(0, 0, 0),
Default::default(),
)]).to_shared(),
)]),
),
],
specificity(0, 0, 0),
@@ -4071,7 +4115,7 @@ pub mod tests {
],
specificity(0, 0, 1),
Default::default(),
)]).to_shared()
)])
),
],
specificity(0, 0, 1),
@@ -4177,7 +4221,7 @@ pub mod tests {
vec![Component::ExplicitUniversalType],
specificity(0, 0, 0),
Default::default(),
)]).to_shared()
)])
)],
specificity(0, 0, 0),
Default::default(),
@@ -4194,7 +4238,7 @@ pub mod tests {
],
specificity(0, 0, 0),
Default::default(),
)]).to_shared()
)])
)],
specificity(0, 0, 0),
Default::default(),
@@ -4210,7 +4254,7 @@ pub mod tests {
vec![Component::ExplicitUniversalType],
specificity(0, 0, 0),
Default::default()
)]).to_shared()
)])
)],
specificity(0, 0, 0),
Default::default(),
@@ -4263,19 +4307,19 @@ pub mod tests {
let parent = parse(".bar, div .baz").unwrap();
let child = parse("#foo &.bar").unwrap();
assert_eq!(
child.replace_parent_selector(&parent.to_shared()),
child.replace_parent_selector(&parent),
parse("#foo :is(.bar, div .baz).bar").unwrap()
);
let has_child = parse("#foo:has(&.bar)").unwrap();
assert_eq!(
has_child.replace_parent_selector(&parent.to_shared()),
has_child.replace_parent_selector(&parent),
parse("#foo:has(:is(.bar, div .baz).bar)").unwrap()
);
let child = parse("#foo").unwrap();
assert_eq!(
child.replace_parent_selector(&parent.to_shared()),
child.replace_parent_selector(&parent),
parse(":is(.bar, div .baz) #foo").unwrap()
);
@@ -4285,7 +4329,8 @@ pub mod tests {
#[test]
fn test_pseudo_iter() {
let selector = &parse("q::before").unwrap().0[0];
let list = parse("q::before").unwrap();
let selector = &list.slice()[0];
assert!(!selector.is_universal());
let mut iter = selector.iter();
assert_eq!(
@@ -4302,18 +4347,19 @@ pub mod tests {
#[test]
fn test_universal() {
let selector = &parse_ns(
let list = parse_ns(
"*|*::before",
&DummyParser::default_with_namespace(DummyAtom::from("https://mozilla.org")),
)
.unwrap()
.0[0];
.unwrap();
let selector = &list.slice()[0];
assert!(selector.is_universal());
}
#[test]
fn test_empty_pseudo_iter() {
let selector = &parse("::before").unwrap().0[0];
let list = parse("::before").unwrap();
let selector = &list.slice()[0];
assert!(selector.is_universal());
let mut iter = selector.iter();
assert_eq!(
@@ -4344,11 +4390,11 @@ pub mod tests {
#[test]
fn visitor() {
let mut test_visitor = TestVisitor { seen: vec![] };
parse(":not(:hover) ~ label").unwrap().0[0].visit(&mut test_visitor);
parse(":not(:hover) ~ label").unwrap().slice()[0].visit(&mut test_visitor);
assert!(test_visitor.seen.contains(&":hover".into()));
let mut test_visitor = TestVisitor { seen: vec![] };
parse("::before:hover").unwrap().0[0].visit(&mut test_visitor);
parse("::before:hover").unwrap().slice()[0].visit(&mut test_visitor);
assert!(test_visitor.seen.contains(&":hover".into()));
}
}

View File

@@ -879,6 +879,9 @@ impl<H, T> Arc<HeaderSlice<H, T>> {
/// allocation itself, via `HeaderSlice`.
pub type ThinArc<H, T> = Arc<HeaderSlice<H, T>>;
/// See `ArcUnion`. This is a version that works for `ThinArc`s.
pub type ThinArcUnion<H1, T1, H2, T2> = ArcUnion<HeaderSlice<H1, T1>, HeaderSlice<H2, T2>>;
impl<H, T> UniqueArc<HeaderSlice<H, T>> {
#[inline]
pub fn from_header_and_iter<I>(header: H, items: I) -> Self
@@ -1027,6 +1030,8 @@ impl<A: PartialEq, B: PartialEq> PartialEq for ArcUnion<A, B> {
}
}
impl<A: Eq, B: Eq> Eq for ArcUnion<A, B> {}
/// This represents a borrow of an `ArcUnion`.
#[derive(Debug)]
pub enum ArcUnionBorrow<'a, A: 'a, B: 'a> {

View File

@@ -532,11 +532,11 @@ where
{
// We need to return elements in document order, and reordering them
// afterwards is kinda silly.
if selector_list.0.len() > 1 {
if selector_list.len() > 1 {
return Err(());
}
let selector = &selector_list.0[0];
let selector = &selector_list.slice()[0];
let class_and_id_case_sensitivity = matching_context.classes_and_ids_case_sensitivity();
// Let's just care about the easy cases for now.
if selector.len() == 1 {
@@ -782,13 +782,13 @@ pub fn query_selector<E, Q>(
// A selector with a combinator needs to have a length of at least 3: A
// simple selector, a combinator, and another simple selector.
let invalidation_may_be_useful = may_use_invalidation == MayUseInvalidation::Yes &&
selector_list.0.iter().any(|s| s.len() > 2);
selector_list.slice().iter().any(|s| s.len() > 2);
if root_element.is_some() || !invalidation_may_be_useful {
query_selector_slow::<E, Q>(root, selector_list, results, &mut matching_context);
} else {
let dependencies = selector_list
.0
.slice()
.iter()
.map(|selector| Dependency::for_full_selector_invalidation(selector.clone()))
.collect::<SmallVec<[_; 5]>>();

View File

@@ -590,7 +590,7 @@ impl StylesheetInvalidationSet {
}
let style_rule = lock.read_with(guard);
for selector in &style_rule.selectors.0 {
for selector in style_rule.selectors.slice() {
self.collect_invalidations(selector, quirks_mode);
if self.fully_invalid {
return;

View File

@@ -477,7 +477,7 @@ impl NestedParseResult {
lazy_static! {
static ref AMPERSAND: SelectorList<SelectorImpl> = {
let list = SelectorList::ampersand();
list.0
list.slice()
.iter()
.for_each(|selector| selector.mark_as_intentionally_leaked());
list
@@ -572,7 +572,7 @@ impl<'a, 'i> NestedRuleParser<'a, 'i> {
use cssparser::ToCss;
debug_assert!(self.context.error_reporting_enabled());
self.error_reporting_state.push(selectors.clone());
'selector_loop: for selector in selectors.0.iter() {
'selector_loop: for selector in selectors.slice().iter() {
let mut current = selector.iter();
loop {
let mut found_host = false;

View File

@@ -13,9 +13,7 @@ use crate::str::CssStringWriter;
use crate::stylesheets::CssRules;
use cssparser::SourceLocation;
#[cfg(feature = "gecko")]
use malloc_size_of::MallocUnconditionalShallowSizeOf;
#[cfg(feature = "gecko")]
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use malloc_size_of::{MallocUnconditionalShallowSizeOf, MallocUnconditionalSizeOf, MallocSizeOf, MallocSizeOfOps};
use selectors::SelectorList;
use servo_arc::Arc;
use std::fmt::{self, Write};
@@ -58,7 +56,7 @@ impl StyleRule {
#[cfg(feature = "gecko")]
pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
let mut n = 0;
n += self.selectors.0.size_of(ops);
n += self.selectors.unconditional_size_of(ops);
n += self.block.unconditional_shallow_size_of(ops) +
self.block.read_with(guard).size_of(ops);
if let Some(ref rules) = self.rules {

View File

@@ -58,13 +58,12 @@ use selectors::matching::{
};
use selectors::matching::{MatchingForInvalidation, VisitedHandlingMode};
use selectors::parser::{
ArcSelectorList, AncestorHashes, Combinator, Component, Selector, SelectorIter, SelectorList,
AncestorHashes, Combinator, Component, Selector, SelectorIter, SelectorList,
};
use selectors::visitor::{SelectorListKind, SelectorVisitor};
use servo_arc::{Arc, ArcBorrow};
use smallbitvec::SmallBitVec;
use smallvec::SmallVec;
use std::borrow::Cow;
use std::cmp::Ordering;
use std::hash::{Hash, Hasher};
use std::sync::Mutex;
@@ -576,34 +575,16 @@ impl From<StyleRuleInclusion> for RuleInclusion {
}
}
enum AncestorSelectorList<'a> {
Borrowed(&'a SelectorList<SelectorImpl>),
Shared(ArcSelectorList<SelectorImpl>),
}
impl<'a> AncestorSelectorList<'a> {
fn into_shared(&mut self) -> &ArcSelectorList<SelectorImpl> {
if let Self::Borrowed(ref b) = *self {
let shared = b.to_shared();
*self = Self::Shared(shared);
}
match *self {
Self::Shared(ref shared) => return shared,
Self::Borrowed(..) => unsafe { debug_unreachable!() },
}
}
}
/// A struct containing state from ancestor rules like @layer / @import /
/// @container / nesting.
struct ContainingRuleState<'a> {
struct ContainingRuleState {
layer_name: LayerName,
layer_id: LayerId,
container_condition_id: ContainerConditionId,
ancestor_selector_lists: SmallVec<[AncestorSelectorList<'a>; 2]>,
ancestor_selector_lists: SmallVec<[SelectorList<SelectorImpl>; 2]>,
}
impl<'a> Default for ContainingRuleState<'a> {
impl Default for ContainingRuleState {
fn default() -> Self {
Self {
layer_name: LayerName::new_empty(),
@@ -621,7 +602,7 @@ struct SavedContainingRuleState {
container_condition_id: ContainerConditionId,
}
impl<'a> ContainingRuleState<'a> {
impl ContainingRuleState {
fn save(&self) -> SavedContainingRuleState {
SavedContainingRuleState {
ancestor_selector_lists_len: self.ancestor_selector_lists.len(),
@@ -2767,15 +2748,15 @@ impl CascadeData {
}
}
fn add_rule_list<'a, S>(
fn add_rule_list<S>(
&mut self,
rules: std::slice::Iter<'a, CssRule>,
device: &'a Device,
rules: std::slice::Iter<CssRule>,
device: &Device,
quirks_mode: QuirksMode,
stylesheet: &S,
guard: &'a SharedRwLockReadGuard,
guard: &SharedRwLockReadGuard,
rebuild_kind: SheetRebuildKind,
containing_rule_state: &mut ContainingRuleState<'a>,
containing_rule_state: &mut ContainingRuleState,
mut precomputed_pseudo_element_decls: Option<&mut PrecomputedPseudoElementDeclarations>,
) -> Result<(), AllocErr>
where
@@ -2785,7 +2766,7 @@ impl CascadeData {
// Handle leaf rules first, as those are by far the most common
// ones, and are always effective, so we can skip some checks.
let mut handled = true;
let mut selectors_for_nested_rules = None;
let mut list_for_nested_rules = None;
match *rule {
CssRule::Style(ref locked) => {
let style_rule = locked.read_with(guard);
@@ -2793,15 +2774,10 @@ impl CascadeData {
let has_nested_rules = style_rule.rules.is_some();
let mut ancestor_selectors = containing_rule_state.ancestor_selector_lists.last_mut();
if has_nested_rules {
selectors_for_nested_rules = Some(if ancestor_selectors.is_some() {
Cow::Owned(SelectorList(Default::default()))
} else {
Cow::Borrowed(&style_rule.selectors)
});
}
let mut replaced_selectors = SmallVec::<[Selector<SelectorImpl>; 4]>::new();
let collect_replaced_selectors = has_nested_rules && ancestor_selectors.is_some();
for selector in &style_rule.selectors.0 {
for selector in style_rule.selectors.slice() {
self.num_selectors += 1;
let pseudo_element = selector.pseudo_element();
@@ -2832,7 +2808,7 @@ impl CascadeData {
}
let selector = match ancestor_selectors {
Some(ref mut s) => selector.replace_parent_selector(&s.into_shared()),
Some(ref mut s) => selector.replace_parent_selector(&s),
None => selector.clone(),
};
@@ -2847,10 +2823,8 @@ impl CascadeData {
containing_rule_state.container_condition_id,
);
if let Some(Cow::Owned(ref mut nested_selectors)) =
selectors_for_nested_rules
{
nested_selectors.0.push(rule.selector.clone())
if collect_replaced_selectors {
replaced_selectors.push(rule.selector.clone())
}
if rebuild_kind.should_rebuild_invalidation() {
@@ -2927,7 +2901,15 @@ impl CascadeData {
}
}
self.rules_source_order += 1;
handled = !has_nested_rules;
handled = true;
if has_nested_rules {
handled = false;
list_for_nested_rules = Some(if collect_replaced_selectors {
SelectorList::from_iter(replaced_selectors.drain(..))
} else {
style_rule.selectors.clone()
});
}
},
CssRule::Keyframes(ref keyframes_rule) => {
debug!("Found valid keyframes rule: {:?}", *keyframes_rule);
@@ -3112,11 +3094,8 @@ impl CascadeData {
}
},
CssRule::Style(..) => {
if let Some(ref mut s) = selectors_for_nested_rules {
containing_rule_state.ancestor_selector_lists.push(match s {
Cow::Owned(ref mut list) => AncestorSelectorList::Shared(list.into_shared()),
Cow::Borrowed(ref b) => AncestorSelectorList::Borrowed(b),
});
if let Some(s) = list_for_nested_rules {
containing_rule_state.ancestor_selector_lists.push(s);
}
},
CssRule::Container(ref rule) => {

View File

@@ -20,7 +20,7 @@ extern crate smallvec;
extern crate string_cache;
extern crate thin_vec;
use servo_arc::{Arc, HeaderSlice};
use servo_arc::{Arc, ArcUnion, HeaderSlice, ArcUnionBorrow};
use smallbitvec::{InternalStorage, SmallBitVec};
use smallvec::{Array, SmallVec};
use std::alloc::Layout;
@@ -431,6 +431,23 @@ where
}
}
impl<A: 'static, B: 'static> ToShmem for ArcUnion<A, B>
where
Arc<A>: ToShmem,
Arc<B>: ToShmem,
{
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
Ok(ManuallyDrop::new(match self.borrow() {
ArcUnionBorrow::First(first) => Self::from_first(ManuallyDrop::into_inner(first.with_arc(|a| {
a.to_shmem(builder)
})?)),
ArcUnionBorrow::Second(second) => Self::from_second(ManuallyDrop::into_inner(second.with_arc(|a| {
a.to_shmem(builder)
})?)),
}))
}
}
impl<T: ToShmem> ToShmem for Arc<T> {
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
// Assert that we don't encounter any shared references to values we

View File

@@ -461,7 +461,7 @@ impl ErrorReporter {
_ => {
let mut desugared = selectors.last().unwrap().clone();
for parent in selectors.iter().rev().skip(1) {
desugared = desugared.replace_parent_selector(&parent.to_shared());
desugared = desugared.replace_parent_selector(&parent);
}
Some(desugared.to_css_string())
},

View File

@@ -2450,7 +2450,7 @@ fn desugared_selector_list(rules: &ThinVec<&LockedStyleRule>) -> SelectorList {
let mut selectors: Option<SelectorList> = None;
for rule in rules.iter().rev() {
selectors = Some(read_locked_arc(rule, |rule| match selectors {
Some(s) => rule.selectors.replace_parent_selector(&s.to_shared()),
Some(ref s) => rule.selectors.replace_parent_selector(s),
None => rule.selectors.clone(),
}));
}
@@ -2465,7 +2465,7 @@ pub extern "C" fn Servo_StyleRule_GetSelectorDataAtIndex(
specificity: Option<&mut u64>,
) {
let selectors = desugared_selector_list(rules);
let Some(selector) = selectors.0.get(index as usize) else { return };
let Some(selector) = selectors.slice().get(index as usize) else { return };
if let Some(text) = text {
selector.to_css(text).unwrap();
}
@@ -2476,7 +2476,7 @@ pub extern "C" fn Servo_StyleRule_GetSelectorDataAtIndex(
#[no_mangle]
pub extern "C" fn Servo_StyleRule_GetSelectorCount(rule: &LockedStyleRule) -> u32 {
read_locked_arc(rule, |rule| rule.selectors.0.len() as u32)
read_locked_arc(rule, |rule| rule.selectors.len() as u32)
}
#[no_mangle]
@@ -2492,7 +2492,7 @@ pub extern "C" fn Servo_StyleRule_SelectorMatchesElement(
matches_selector, MatchingContext, MatchingMode, NeedsSelectorFlags, VisitedHandlingMode,
};
let selectors = desugared_selector_list(rules);
let Some(selector) = selectors.0.get(index as usize) else { return false };
let Some(selector) = selectors.slice().get(index as usize) else { return false };
let mut matching_mode = MatchingMode::Normal;
match PseudoElement::from_pseudo_type(pseudo_type, None) {
Some(pseudo) => {