mirror of
https://github.com/servo/servo.git
synced 2025-07-23 07:13:52 +01:00
Fix revalidation selectors when pseudo-elements are involved.
This commit is contained in:
parent
24e944ad94
commit
537cf52707
3 changed files with 60 additions and 9 deletions
|
@ -479,6 +479,9 @@ impl<'a, Impl: 'a + SelectorImpl> AncestorIter<'a, Impl> {
|
||||||
fn skip_until_ancestor(&mut self) {
|
fn skip_until_ancestor(&mut self) {
|
||||||
loop {
|
loop {
|
||||||
while self.0.next().is_some() {}
|
while self.0.next().is_some() {}
|
||||||
|
// If this is ever changed to stop at the "pseudo-element"
|
||||||
|
// combinator, we will need to fix the way we compute hashes for
|
||||||
|
// revalidation selectors.
|
||||||
if self.0.next_sequence().map_or(true, |x| matches!(x, Combinator::Child | Combinator::Descendant)) {
|
if self.0.next_sequence().map_or(true, |x| matches!(x, Combinator::Child | Combinator::Descendant)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -515,7 +518,9 @@ pub enum Combinator {
|
||||||
/// A dummy combinator we use to the left of pseudo-elements.
|
/// A dummy combinator we use to the left of pseudo-elements.
|
||||||
///
|
///
|
||||||
/// It serializes as the empty string, and acts effectively as a child
|
/// It serializes as the empty string, and acts effectively as a child
|
||||||
/// combinator.
|
/// combinator in most cases. If we ever actually start using a child
|
||||||
|
/// combinator for this, we will need to fix up the way hashes are computed
|
||||||
|
/// for revalidation selectors.
|
||||||
PseudoElement,
|
PseudoElement,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,10 +61,10 @@
|
||||||
//! the up-front checks but would have different matching results for the
|
//! the up-front checks but would have different matching results for the
|
||||||
//! selector in question. In this case, "descendants" includes pseudo-elements,
|
//! selector in question. In this case, "descendants" includes pseudo-elements,
|
||||||
//! so there is a single selector map of revalidation selectors that includes
|
//! so there is a single selector map of revalidation selectors that includes
|
||||||
//! both selectors targeting element and selectors targeting pseudo-elements.
|
//! both selectors targeting elements and selectors targeting pseudo-element
|
||||||
//! This relies on matching an element against a pseudo-element-targeting
|
//! originating elements. We ensure that the pseudo-element parts of all these
|
||||||
//! selector being a sensible operation that will effectively check whether that
|
//! selectors are effectively stripped off, so that matching them all against
|
||||||
//! element is a matching originating element for the selector.
|
//! elements makes sense.
|
||||||
|
|
||||||
use Atom;
|
use Atom;
|
||||||
use bit_vec::BitVec;
|
use bit_vec::BitVec;
|
||||||
|
|
|
@ -160,7 +160,7 @@ pub struct Stylist {
|
||||||
/// on state that is not otherwise visible to the cache, like attributes or
|
/// on state that is not otherwise visible to the cache, like attributes or
|
||||||
/// tree-structural state like child index and pseudos).
|
/// tree-structural state like child index and pseudos).
|
||||||
#[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
|
#[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
|
||||||
selectors_for_cache_revalidation: SelectorMap<SelectorAndHashes<SelectorImpl>>,
|
selectors_for_cache_revalidation: SelectorMap<RevalidationSelectorAndHashes>,
|
||||||
|
|
||||||
/// The total number of selectors.
|
/// The total number of selectors.
|
||||||
num_selectors: usize,
|
num_selectors: usize,
|
||||||
|
@ -474,7 +474,8 @@ impl Stylist {
|
||||||
|
|
||||||
self.dependencies.note_selector(selector_and_hashes);
|
self.dependencies.note_selector(selector_and_hashes);
|
||||||
if needs_revalidation(&selector_and_hashes.selector) {
|
if needs_revalidation(&selector_and_hashes.selector) {
|
||||||
self.selectors_for_cache_revalidation.insert(selector_and_hashes.clone());
|
self.selectors_for_cache_revalidation.insert(
|
||||||
|
RevalidationSelectorAndHashes::new(&selector_and_hashes));
|
||||||
}
|
}
|
||||||
selector_and_hashes.selector.visit(&mut AttributeAndStateDependencyVisitor {
|
selector_and_hashes.selector.visit(&mut AttributeAndStateDependencyVisitor {
|
||||||
attribute_dependencies: &mut self.attribute_dependencies,
|
attribute_dependencies: &mut self.attribute_dependencies,
|
||||||
|
@ -1134,7 +1135,7 @@ impl Stylist {
|
||||||
let mut results = BitVec::new();
|
let mut results = BitVec::new();
|
||||||
self.selectors_for_cache_revalidation.lookup(*element, &mut |selector_and_hashes| {
|
self.selectors_for_cache_revalidation.lookup(*element, &mut |selector_and_hashes| {
|
||||||
results.push(matches_selector(&selector_and_hashes.selector,
|
results.push(matches_selector(&selector_and_hashes.selector,
|
||||||
0,
|
selector_and_hashes.selector_offset,
|
||||||
&selector_and_hashes.hashes,
|
&selector_and_hashes.hashes,
|
||||||
element,
|
element,
|
||||||
&mut matching_context,
|
&mut matching_context,
|
||||||
|
@ -1288,7 +1289,52 @@ impl<'a> SelectorVisitor for MappedIdVisitor<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Visitor determine whether a selector requires cache revalidation.
|
/// SelectorMapEntry implementation for use in our revalidation selector map.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
struct RevalidationSelectorAndHashes {
|
||||||
|
selector: Selector<SelectorImpl>,
|
||||||
|
selector_offset: usize,
|
||||||
|
hashes: AncestorHashes,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RevalidationSelectorAndHashes {
|
||||||
|
fn new(selector_and_hashes: &SelectorAndHashes<SelectorImpl>) -> Self {
|
||||||
|
// We basically want to check whether the first combinator is a
|
||||||
|
// pseudo-element combinator. If it is, we want to use the offset one
|
||||||
|
// past it. Otherwise, our offset is 0.
|
||||||
|
let mut index = 0;
|
||||||
|
let mut iter = selector_and_hashes.selector.iter();
|
||||||
|
|
||||||
|
// First skip over the first ComplexSelector. We can't check what sort
|
||||||
|
// of combinator we have until we do that.
|
||||||
|
for _ in &mut iter {
|
||||||
|
index += 1; // Simple selector
|
||||||
|
}
|
||||||
|
|
||||||
|
let offset = match iter.next_sequence() {
|
||||||
|
Some(Combinator::PseudoElement) => index + 1, // +1 for the combinator
|
||||||
|
_ => 0
|
||||||
|
};
|
||||||
|
|
||||||
|
RevalidationSelectorAndHashes {
|
||||||
|
selector: selector_and_hashes.selector.clone(),
|
||||||
|
selector_offset: offset,
|
||||||
|
hashes: selector_and_hashes.hashes.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SelectorMapEntry for RevalidationSelectorAndHashes {
|
||||||
|
fn selector(&self) -> SelectorIter<SelectorImpl> {
|
||||||
|
self.selector.iter_from(self.selector_offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hashes(&self) -> &AncestorHashes {
|
||||||
|
&self.hashes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Visitor to determine whether a selector requires cache revalidation.
|
||||||
///
|
///
|
||||||
/// Note that we just check simple selectors and eagerly return when the first
|
/// Note that we just check simple selectors and eagerly return when the first
|
||||||
/// need for revalidation is found, so we don't need to store state on the
|
/// need for revalidation is found, so we don't need to store state on the
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue