Bug 1844900 - Improve memory usage with nesting. r=dshin

The idea is to share more memory when expanding nested rules (so being
able to share the whole :is() / :where() allocation), and same for the
Dependency computation (basically, share it across all descendant
selectors).

This prevents exponential explosion in cases like the one in bug 1844446
if you replace tag selectors by class selectors (so that we build
invalidation maps for them).

Differential Revision: https://phabricator.services.mozilla.com/D184283
This commit is contained in:
Emilio Cobos Álvarez 2023-07-27 21:38:10 +00:00
Родитель 88c70e7340
Коммит 30adf86c52
8 изменённых файлов: 190 добавлений и 138 удалений

Просмотреть файл

@ -333,7 +333,7 @@ where
Component::Where(ref list) |
Component::Negation(ref list) |
Component::Is(ref list) => {
let sf = selector_list_specificity_and_flags(list.iter());
let sf = selector_list_specificity_and_flags(list.slice().iter());
if !matches!(*simple_selector, Component::Where(..)) {
*specificity += Specificity::from(sf.specificity);
}

Просмотреть файл

@ -213,7 +213,12 @@ where
selector.iter_from(offset),
element,
context,
if offset == 0 { Rightmost::Yes } else { Rightmost::No })
if offset == 0 {
Rightmost::Yes
} else {
Rightmost::No
},
)
}
/// Whether a compound selector matched, and whether it was the rightmost
@ -788,7 +793,8 @@ where
return false;
}
selector.map_or(true, |selector| {
context.nest(|context| matches_complex_selector(selector.iter(), element, context, rightmost))
context
.nest(|context| matches_complex_selector(selector.iter(), element, context, rightmost))
})
}
@ -906,7 +912,9 @@ where
matches_rare_attribute_selector(element, attr_sel)
},
Component::Part(ref parts) => matches_part(element, parts, &mut context.shared),
Component::Slotted(ref selector) => matches_slotted(element, selector, &mut context.shared, rightmost),
Component::Slotted(ref selector) => {
matches_slotted(element, selector, &mut context.shared, rightmost)
},
Component::PseudoElement(ref pseudo) => {
element.match_pseudo_element(pseudo, context.shared)
},
@ -939,8 +947,7 @@ where
Component::Host(ref selector) => {
matches_host(element, selector.as_ref(), &mut context.shared, rightmost)
},
Component::ParentSelector |
Component::Scope => match context.shared.scope_element {
Component::ParentSelector | Component::Scope => match context.shared.scope_element {
Some(ref scope_element) => element.opaque() == *scope_element,
None => element.is_root(),
},
@ -956,19 +963,17 @@ where
rightmost,
)
}),
Component::Is(ref list) | Component::Where(ref list) => context
.shared
.nest(|context| matches_complex_selector_list(list, element, context, rightmost)),
Component::Negation(ref list) => context
.shared
.nest_for_negation(|context| !matches_complex_selector_list(list, element, context, rightmost)),
Component::Has(ref relative_selectors) => {
context
Component::Is(ref list) | Component::Where(ref list) => context.shared.nest(|context| {
matches_complex_selector_list(list.slice(), element, context, rightmost)
}),
Component::Negation(ref list) => context.shared.nest_for_negation(|context| {
!matches_complex_selector_list(list.slice(), element, context, rightmost)
}),
Component::Has(ref relative_selectors) => context
.shared
.nest_for_relative_selector(element.opaque(), |context| {
matches_relative_selectors(relative_selectors, element, context, rightmost)
})
},
}),
Component::Combinator(_) => unsafe {
debug_unreachable!("Shouldn't try to selector-match combinators")
},

Просмотреть файл

@ -399,6 +399,17 @@ impl<Impl: SelectorImpl> SelectorList<Impl> {
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(..))
}
/// Parse a comma-separated list of Selectors.
/// <https://drafts.csswg.org/selectors/#grouping>
///
@ -463,7 +474,7 @@ impl<Impl: SelectorImpl> SelectorList<Impl> {
}
/// Replaces the parent selector in all the items of the selector list.
pub fn replace_parent_selector(&self, parent: &[Selector<Impl>]) -> Self {
pub fn replace_parent_selector(&self, parent: &ArcSelectorList<Impl>) -> Self {
Self(self.0.iter().map(|selector| selector.replace_parent_selector(parent)).collect())
}
@ -579,7 +590,7 @@ where
// 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 &&
!collect_ancestor_hashes(list[0].iter(), quirks_mode, hashes, len)
!collect_ancestor_hashes(list.slice()[0].iter(), quirks_mode, hashes, len)
{
return false;
}
@ -890,13 +901,13 @@ impl<Impl: SelectorImpl> Selector<Impl> {
Selector(builder.build_with_specificity_and_flags(spec))
}
pub fn replace_parent_selector(&self, parent: &[Selector<Impl>]) -> Self {
pub fn replace_parent_selector(&self, parent: &ArcSelectorList<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;
let mut specificity = Specificity::from(self.specificity());
let parent_specificity =
Specificity::from(selector_list_specificity_and_flags(parent.iter()).specificity());
Specificity::from(selector_list_specificity_and_flags(parent.slice().iter()).specificity());
// The specificity at this point will be wrong, we replace it by the correct one after the
// fact.
@ -907,37 +918,43 @@ impl<Impl: SelectorImpl> Selector<Impl> {
fn replace_parent_on_selector_list<Impl: SelectorImpl>(
orig: &[Selector<Impl>],
parent: &[Selector<Impl>],
parent: &ArcSelectorList<Impl>,
specificity: &mut Specificity,
with_specificity: bool,
) -> Vec<Selector<Impl>> {
) -> Option<ArcSelectorList<Impl>> {
let mut any = false;
let result = orig
.iter()
.map(|s| {
if !s.has_parent_selector() {
return s.clone();
}
any = true;
s.replace_parent_selector(parent)
})
.collect();
let result = ArcSelectorList::from_header_and_iter(
(),
orig
.iter()
.map(|s| {
if !s.has_parent_selector() {
return s.clone();
}
any = true;
s.replace_parent_selector(parent)
})
);
if !any || !with_specificity {
return result;
if !any {
return None;
}
if !with_specificity {
return Some(result);
}
*specificity += Specificity::from(
selector_list_specificity_and_flags(result.iter()).specificity -
selector_list_specificity_and_flags(result.slice().iter()).specificity -
selector_list_specificity_and_flags(orig.iter()).specificity,
);
result
Some(result)
}
fn replace_parent_on_relative_selector_list<Impl: SelectorImpl>(
orig: &[RelativeSelector<Impl>],
parent: &[Selector<Impl>],
parent: &ArcSelectorList<Impl>,
specificity: &mut Specificity,
) -> Vec<RelativeSelector<Impl>> {
let mut any = false;
@ -969,7 +986,7 @@ impl<Impl: SelectorImpl> Selector<Impl> {
fn replace_parent_on_selector<Impl: SelectorImpl>(
orig: &Selector<Impl>,
parent: &[Selector<Impl>],
parent: &ArcSelectorList<Impl>,
specificity: &mut Specificity,
) -> Selector<Impl> {
if !orig.has_parent_selector() {
@ -991,7 +1008,7 @@ impl<Impl: SelectorImpl> Selector<Impl> {
Combinator::Descendant,
)))
.chain(std::iter::once(Component::Is(
parent.to_vec().into_boxed_slice(),
parent.clone()
)));
UniqueArc::from_header_and_iter_with_size(specificity_and_flags, iter, len)
} else {
@ -1021,38 +1038,31 @@ impl<Impl: SelectorImpl> Selector<Impl> {
RelativeSelectorAnchor => component.clone(),
ParentSelector => {
specificity += parent_specificity;
Is(parent.to_vec().into_boxed_slice())
Is(parent.clone())
},
Negation(ref selectors) => {
Negation(
replace_parent_on_selector_list(
selectors,
parent,
&mut specificity,
/* with_specificity = */ true,
)
.into_boxed_slice(),
)
},
Is(ref selectors) => {
Is(replace_parent_on_selector_list(
selectors,
Negation(replace_parent_on_selector_list(
selectors.slice(),
parent,
&mut specificity,
/* with_specificity = */ true,
)
.into_boxed_slice())
).unwrap_or_else(|| selectors.clone()))
},
Is(ref selectors) => {
Is(replace_parent_on_selector_list(
selectors.slice(),
parent,
&mut specificity,
/* with_specificity = */ true,
).unwrap_or_else(|| selectors.clone()))
},
Where(ref selectors) => {
Where(
replace_parent_on_selector_list(
selectors,
parent,
&mut specificity,
/* with_specificity = */ false,
)
.into_boxed_slice(),
)
Where(replace_parent_on_selector_list(
selectors.slice(),
parent,
&mut specificity,
/* with_specificity = */ false,
).unwrap_or_else(|| selectors.clone()))
},
Has(ref selectors) => Has(replace_parent_on_relative_selector_list(
selectors,
@ -1073,10 +1083,13 @@ impl<Impl: SelectorImpl> Selector<Impl> {
&mut specificity,
/* with_specificity = */ true,
);
NthOf(NthOfSelectorData::new(
data.nth_data(),
selectors.into_iter(),
))
NthOf(match selectors {
Some(s) => NthOfSelectorData::new(
data.nth_data(),
s.slice().iter().cloned(),
),
None => data.clone(),
})
},
Slotted(ref selector) => Slotted(replace_parent_on_selector(
selector,
@ -1658,6 +1671,9 @@ 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].
///
@ -1697,7 +1713,7 @@ pub enum Component<Impl: SelectorImpl> {
),
/// Pseudo-classes
Negation(Box<[Selector<Impl>]>),
Negation(ArcSelectorList<Impl>),
Root,
Empty,
Scope,
@ -1736,13 +1752,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(Box<[Selector<Impl>]>),
Where(ArcSelectorList<Impl>),
/// The `:is` pseudo-class.
///
/// https://drafts.csswg.org/selectors/#matches-pseudo
///
/// Same comment as above re. the argument.
Is(Box<[Selector<Impl>]>),
Is(ArcSelectorList<Impl>),
/// The `:has` pseudo-class.
///
/// https://drafts.csswg.org/selectors/#has-pseudo
@ -1789,7 +1805,7 @@ impl<Impl: SelectorImpl> Component<Impl> {
Component::NonTSPseudoClass(..) => true,
Component::Negation(ref selectors) |
Component::Is(ref selectors) |
Component::Where(ref selectors) => selectors.iter().all(|selector| {
Component::Where(ref selectors) => selectors.slice().iter().all(|selector| {
selector
.iter_raw_match_order()
.all(|c| c.maybe_allowed_after_pseudo_element())
@ -1811,13 +1827,13 @@ impl<Impl: SelectorImpl> Component<Impl> {
*self
);
match *self {
Component::Negation(ref selectors) => !selectors.iter().all(|selector| {
Component::Negation(ref selectors) => !selectors.slice().iter().all(|selector| {
selector
.iter_raw_match_order()
.all(|c| c.matches_for_stateless_pseudo_element())
}),
Component::Is(ref selectors) | Component::Where(ref selectors) => {
selectors.iter().any(|selector| {
selectors.slice().iter().any(|selector| {
selector
.iter_raw_match_order()
.all(|c| c.matches_for_stateless_pseudo_element())
@ -1894,7 +1910,7 @@ impl<Impl: SelectorImpl> Component<Impl> {
Negation(ref list) | Is(ref list) | Where(ref list) => {
let list_kind = SelectorListKind::from_component(self);
debug_assert!(!list_kind.is_empty());
if !visitor.visit_selector_list(list_kind, &list) {
if !visitor.visit_selector_list(list_kind, list.slice()) {
return false;
}
},
@ -2268,7 +2284,7 @@ impl<Impl: SelectorImpl> ToCss for Component<Impl> {
Negation(..) => dest.write_str(":not(")?,
_ => unreachable!(),
}
serialize_selector_list(list.iter(), dest)?;
serialize_selector_list(list.slice().iter(), dest)?;
dest.write_str(")")
},
Has(ref list) => {
@ -2840,7 +2856,7 @@ where
P: Parser<'i, Impl = Impl>,
Impl: SelectorImpl,
{
let list = SelectorList::parse_with_state(
let mut list = SelectorList::parse_with_state(
parser,
input,
state |
@ -2850,7 +2866,7 @@ where
ParseRelative::No,
)?;
Ok(Component::Negation(list.0.into_vec().into_boxed_slice()))
Ok(Component::Negation(list.into_shared()))
}
/// simple_selector_sequence
@ -2956,7 +2972,7 @@ fn parse_is_where<'i, 't, P, Impl>(
parser: &P,
input: &mut CssParser<'i, 't>,
state: SelectorParsingState,
component: impl FnOnce(Box<[Selector<Impl>]>) -> Component<Impl>,
component: impl FnOnce(ArcSelectorList<Impl>) -> Component<Impl>,
) -> Result<Component<Impl>, ParseError<'i, P::Error>>
where
P: Parser<'i, Impl = Impl>,
@ -2968,7 +2984,7 @@ where
// Pseudo-elements cannot be represented by the matches-any
// pseudo-class; they are not valid within :is().
//
let inner = SelectorList::parse_with_state(
let mut inner = SelectorList::parse_with_state(
parser,
input,
state |
@ -2977,7 +2993,7 @@ where
ForgivingParsing::Yes,
ParseRelative::No,
)?;
Ok(component(inner.0.into_vec().into_boxed_slice()))
Ok(component(inner.into_shared()))
}
fn parse_has<'i, 't, P, Impl>(
@ -3895,12 +3911,11 @@ pub mod tests {
vec![
Component::DefaultNamespace(MATHML.into()),
Component::Negation(
vec![Selector::from_vec(
SelectorList::from_vec(vec![Selector::from_vec(
vec![Component::Class(DummyAtom::from("cl"))],
specificity(0, 1, 0),
Default::default(),
)]
.into_boxed_slice()
)]).to_shared()
),
],
specificity(0, 1, 0),
@ -3913,15 +3928,14 @@ pub mod tests {
vec![
Component::DefaultNamespace(MATHML.into()),
Component::Negation(
vec![Selector::from_vec(
SelectorList::from_vec(vec![Selector::from_vec(
vec![
Component::DefaultNamespace(MATHML.into()),
Component::ExplicitUniversalType,
],
specificity(0, 0, 0),
Default::default(),
)]
.into_boxed_slice(),
)]).to_shared(),
),
],
specificity(0, 0, 0),
@ -3934,7 +3948,7 @@ pub mod tests {
vec![
Component::DefaultNamespace(MATHML.into()),
Component::Negation(
vec![Selector::from_vec(
SelectorList::from_vec(vec![Selector::from_vec(
vec![
Component::DefaultNamespace(MATHML.into()),
Component::LocalName(LocalName {
@ -3944,8 +3958,7 @@ pub mod tests {
],
specificity(0, 0, 1),
Default::default(),
),]
.into_boxed_slice()
)]).to_shared()
),
],
specificity(0, 0, 1),
@ -4047,12 +4060,11 @@ pub mod tests {
parse_ns(":not(*)", &parser),
Ok(SelectorList::from_vec(vec![Selector::from_vec(
vec![Component::Negation(
vec![Selector::from_vec(
SelectorList::from_vec(vec![Selector::from_vec(
vec![Component::ExplicitUniversalType],
specificity(0, 0, 0),
Default::default(),
)]
.into_boxed_slice()
)]).to_shared()
)],
specificity(0, 0, 0),
Default::default(),
@ -4062,15 +4074,14 @@ pub mod tests {
parse_ns(":not(|*)", &parser),
Ok(SelectorList::from_vec(vec![Selector::from_vec(
vec![Component::Negation(
vec![Selector::from_vec(
SelectorList::from_vec(vec![Selector::from_vec(
vec![
Component::ExplicitNoNamespace,
Component::ExplicitUniversalType,
],
specificity(0, 0, 0),
Default::default(),
)]
.into_boxed_slice(),
)]).to_shared()
)],
specificity(0, 0, 0),
Default::default(),
@ -4082,12 +4093,11 @@ pub mod tests {
parse_ns_expected(":not(*|*)", &parser, Some(":not(*)")),
Ok(SelectorList::from_vec(vec![Selector::from_vec(
vec![Component::Negation(
vec![Selector::from_vec(
SelectorList::from_vec(vec![Selector::from_vec(
vec![Component::ExplicitUniversalType],
specificity(0, 0, 0),
Default::default()
)]
.into_boxed_slice()
)]).to_shared()
)],
specificity(0, 0, 0),
Default::default(),
@ -4140,19 +4150,19 @@ pub mod tests {
let parent = parse(".bar, div .baz").unwrap();
let child = parse("#foo &.bar").unwrap();
assert_eq!(
child.replace_parent_selector(&parent.0),
child.replace_parent_selector(&parent.to_shared()),
parse("#foo :is(.bar, div .baz).bar").unwrap()
);
let has_child = parse("#foo:has(&.bar)").unwrap();
assert_eq!(
has_child.replace_parent_selector(&parent.0),
has_child.replace_parent_selector(&parent.to_shared()),
parse("#foo:has(:is(.bar, div .baz).bar)").unwrap()
);
let child = parse("#foo").unwrap();
assert_eq!(
child.replace_parent_selector(&parent.0),
child.replace_parent_selector(&parent.to_shared()),
parse(":is(.bar, div .baz) #foo").unwrap()
);

Просмотреть файл

@ -16,6 +16,7 @@ use selectors::attr::NamespaceConstraint;
use selectors::parser::{Combinator, Component};
use selectors::parser::{Selector, SelectorIter};
use selectors::visitor::{SelectorListKind, SelectorVisitor};
use servo_arc::Arc;
use smallvec::SmallVec;
/// Mapping between (partial) CompoundSelectors (and the combinator to their
@ -39,11 +40,7 @@ use smallvec::SmallVec;
#[derive(Clone, Debug, MallocSizeOf)]
pub struct Dependency {
/// The dependency selector.
#[cfg_attr(
feature = "gecko",
ignore_malloc_size_of = "CssRules have primary refs, we measure there"
)]
#[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")]
#[ignore_malloc_size_of = "CssRules have primary refs, we measure there"]
pub selector: Selector<SelectorImpl>,
/// The offset into the selector that we should match on.
@ -64,7 +61,8 @@ pub struct Dependency {
/// * One dependency from .bar pointing to C (parent: None)
/// * One dependency from .foo pointing to A (parent: None)
///
pub parent: Option<Box<Dependency>>,
#[ignore_malloc_size_of = "Arc"]
pub parent: Option<Arc<Dependency>>,
}
/// The kind of elements down the tree this dependency may affect.
@ -306,6 +304,12 @@ impl PerCompoundState {
}
}
struct ParentDependencyEntry {
selector: Selector<SelectorImpl>,
offset: usize,
cached_dependency: Option<Arc<Dependency>>,
}
/// A struct that collects invalidations for a given compound selector.
struct SelectorDependencyCollector<'a> {
map: &'a mut InvalidationMap,
@ -323,8 +327,8 @@ struct SelectorDependencyCollector<'a> {
/// sequence.
///
/// This starts empty. It grows when we find nested :is and :where selector
/// lists.
parent_selectors: &'a mut SmallVec<[(Selector<SelectorImpl>, usize); 5]>,
/// lists. The dependency field is cached and reference counted.
parent_selectors: &'a mut SmallVec<[ParentDependencyEntry; 5]>,
/// The quirks mode of the document where we're inserting dependencies.
quirks_mode: QuirksMode,
@ -399,24 +403,35 @@ impl<'a> SelectorDependencyCollector<'a> {
true
}
fn dependency(&self) -> Dependency {
let mut parent = None;
// TODO(emilio): Maybe we should refcount the parent dependencies, or
// cache them or something.
for &(ref selector, ref selector_offset) in self.parent_selectors.iter() {
debug_assert_ne!(
self.compound_state.offset, 0,
"Shouldn't bother creating nested dependencies for the rightmost compound",
);
let new_parent = Dependency {
selector: selector.clone(),
selector_offset: *selector_offset,
parent,
};
parent = Some(Box::new(new_parent));
fn parent_dependency(&mut self) -> Option<Arc<Dependency>> {
if self.parent_selectors.is_empty() {
return None;
}
fn dependencies_from(entries: &mut [ParentDependencyEntry]) -> Option<Arc<Dependency>> {
if entries.is_empty() {
return None
}
let last_index = entries.len() - 1;
let (previous, last) = entries.split_at_mut(last_index);
let last = &mut last[0];
let selector = &last.selector;
let selector_offset = last.offset;
Some(last.cached_dependency.get_or_insert_with(|| {
Arc::new(Dependency {
selector: selector.clone(),
selector_offset,
parent: dependencies_from(previous),
})
}).clone())
}
dependencies_from(&mut self.parent_selectors)
}
fn dependency(&mut self) -> Dependency {
let parent = self.parent_dependency();
Dependency {
selector: self.selector.clone(),
selector_offset: self.compound_state.offset,
@ -456,8 +471,11 @@ impl<'a> SelectorVisitor for SelectorDependencyCollector<'a> {
index += 1; // account for the combinator.
self.parent_selectors
.push((self.selector.clone(), self.compound_state.offset));
self.parent_selectors.push(ParentDependencyEntry {
selector: self.selector.clone(),
offset: self.compound_state.offset,
cached_dependency: None,
});
let mut nested = SelectorDependencyCollector {
map: &mut *self.map,
document_state: &mut *self.document_state,

Просмотреть файл

@ -753,9 +753,9 @@ fn specific_bucket_for<'a>(
},
Component::Is(ref list) | Component::Where(ref list) => {
if list.len() == 1 {
find_bucket(list[0].iter(), disjoint_buckets, bucket_attributes)
find_bucket(list.slice()[0].iter(), disjoint_buckets, bucket_attributes)
} else {
for selector in &**list {
for selector in list.slice() {
let bucket = find_bucket(selector.iter(), disjoint_buckets, bucket_attributes);
disjoint_buckets.push(bucket);
}

Просмотреть файл

@ -55,7 +55,7 @@ use selectors::matching::{
};
use selectors::matching::{IgnoreNthChildForInvalidation, VisitedHandlingMode};
use selectors::parser::{
AncestorHashes, Combinator, Component, Selector, SelectorIter, SelectorList,
ArcSelectorList, AncestorHashes, Combinator, Component, Selector, SelectorIter, SelectorList,
};
use selectors::visitor::{SelectorListKind, SelectorVisitor};
use servo_arc::{Arc, ArcBorrow};
@ -570,7 +570,23 @@ impl From<StyleRuleInclusion> for RuleInclusion {
}
}
type AncestorSelectorList<'a> = Cow<'a, SelectorList<SelectorImpl>>;
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.
@ -2677,7 +2693,7 @@ impl CascadeData {
self.num_declarations += style_rule.block.read_with(&guard).len();
let has_nested_rules = style_rule.rules.is_some();
let ancestor_selectors = containing_rule_state.ancestor_selector_lists.last();
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()))
@ -2717,7 +2733,7 @@ impl CascadeData {
}
let selector = match ancestor_selectors {
Some(s) => selector.replace_parent_selector(&s.0),
Some(ref mut s) => selector.replace_parent_selector(&s.into_shared()),
None => selector.clone(),
};
@ -2996,8 +3012,11 @@ impl CascadeData {
}
},
CssRule::Style(..) => {
if let Some(s) = selectors_for_nested_rules {
containing_rule_state.ancestor_selector_lists.push(s);
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),
});
}
},
CssRule::Container(ref rule) => {

Просмотреть файл

@ -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.0);
desugared = desugared.replace_parent_selector(&parent.to_shared());
}
Some(desugared.to_css_string())
},

Просмотреть файл

@ -2426,7 +2426,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.0),
Some(s) => rule.selectors.replace_parent_selector(&s.to_shared()),
None => rule.selectors.clone(),
}));
}