зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
88c70e7340
Коммит
30adf86c52
|
@ -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(),
|
||||
}));
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче