mirror of
https://github.com/servo/servo.git
synced 2025-06-17 12:54:28 +00:00
style: Fix a no-longer valid assumption in pseudo-element matching / invalidation code.
After bug 1632647, we can have pseudo-classes inside :not / :is / :where, which the invalidation and matching code weren't handling. Add a few tests for this stuff working as expected. Differential Revision: https://phabricator.services.mozilla.com/D76160
This commit is contained in:
parent
a40b2b610a
commit
bd23e05c47
3 changed files with 65 additions and 19 deletions
|
@ -322,14 +322,13 @@ where
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// The only other parser-allowed Component in this sequence is a state
|
for component in &mut iter {
|
||||||
// class. We just don't match in that case.
|
// The only other parser-allowed Components in this sequence are
|
||||||
if let Some(s) = iter.next() {
|
// state pseudo-classes, or one of the other things that can contain
|
||||||
debug_assert!(
|
// them.
|
||||||
matches!(*s, Component::NonTSPseudoClass(..)),
|
if !component.matches_for_stateless_pseudo_element() {
|
||||||
"Someone messed up pseudo-element parsing"
|
return false;
|
||||||
);
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Advance to the non-pseudo-element part of the selector.
|
// Advance to the non-pseudo-element part of the selector.
|
||||||
|
|
|
@ -1054,6 +1054,47 @@ impl<Impl: SelectorImpl> Component<Impl> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether this component is valid after a pseudo-element. Only intended
|
||||||
|
/// for sanity-checking.
|
||||||
|
pub fn maybe_allowed_after_pseudo_element(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
Component::NonTSPseudoClass(..) => true,
|
||||||
|
Component::Negation(ref components) => components.iter().all(|c| c.maybe_allowed_after_pseudo_element()),
|
||||||
|
Component::Is(ref selectors) |
|
||||||
|
Component::Where(ref selectors) => {
|
||||||
|
selectors.iter().all(|selector| {
|
||||||
|
selector.iter_raw_match_order().all(|c| c.maybe_allowed_after_pseudo_element())
|
||||||
|
})
|
||||||
|
},
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether a given selector should match for stateless pseudo-elements.
|
||||||
|
///
|
||||||
|
/// This is a bit subtle: Only selectors that return true in
|
||||||
|
/// `maybe_allowed_after_pseudo_element` should end up here, and
|
||||||
|
/// `NonTSPseudoClass` never matches (as it is a stateless pseudo after
|
||||||
|
/// all).
|
||||||
|
pub(crate) fn matches_for_stateless_pseudo_element(&self) -> bool {
|
||||||
|
debug_assert!(
|
||||||
|
self.maybe_allowed_after_pseudo_element(),
|
||||||
|
"Someone messed up pseudo-element parsing: {:?}",
|
||||||
|
*self
|
||||||
|
);
|
||||||
|
match *self {
|
||||||
|
Component::Negation(ref components) => {
|
||||||
|
!components.iter().all(|c| c.matches_for_stateless_pseudo_element())
|
||||||
|
},
|
||||||
|
Component::Is(ref selectors) | Component::Where(ref selectors) => {
|
||||||
|
selectors.iter().any(|selector| {
|
||||||
|
selector.iter_raw_match_order().all(|c| c.matches_for_stateless_pseudo_element())
|
||||||
|
})
|
||||||
|
},
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn visit<V>(&self, visitor: &mut V) -> bool
|
pub fn visit<V>(&self, visitor: &mut V) -> bool
|
||||||
where
|
where
|
||||||
V: SelectorVisitor<Impl = Impl>,
|
V: SelectorVisitor<Impl = Impl>,
|
||||||
|
|
|
@ -871,23 +871,29 @@ where
|
||||||
// This will usually be the very next component, except for
|
// This will usually be the very next component, except for
|
||||||
// the fact that we store compound selectors the other way
|
// the fact that we store compound selectors the other way
|
||||||
// around, so there could also be state pseudo-classes.
|
// around, so there could also be state pseudo-classes.
|
||||||
let pseudo_selector = next_invalidation
|
let pseudo = next_invalidation
|
||||||
.dependency
|
.dependency
|
||||||
.selector
|
.selector
|
||||||
.iter_raw_parse_order_from(next_invalidation.offset)
|
.iter_raw_parse_order_from(next_invalidation.offset)
|
||||||
.skip_while(|c| matches!(**c, Component::NonTSPseudoClass(..)))
|
.flat_map(|c| {
|
||||||
|
if let Component::PseudoElement(ref pseudo) = *c {
|
||||||
|
return Some(pseudo);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Would be nice to make this a diagnostic_assert! of
|
||||||
|
// sorts.
|
||||||
|
debug_assert!(
|
||||||
|
c.maybe_allowed_after_pseudo_element(),
|
||||||
|
"Someone seriously messed up selector parsing: \
|
||||||
|
{:?} at offset {:?}: {:?}",
|
||||||
|
next_invalidation.dependency, next_invalidation.offset, c,
|
||||||
|
);
|
||||||
|
|
||||||
|
None
|
||||||
|
})
|
||||||
.next()
|
.next()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let pseudo = match *pseudo_selector {
|
|
||||||
Component::PseudoElement(ref pseudo) => pseudo,
|
|
||||||
_ => unreachable!(
|
|
||||||
"Someone seriously messed up selector parsing: \
|
|
||||||
{:?} at offset {:?}: {:?}",
|
|
||||||
next_invalidation.dependency, next_invalidation.offset, pseudo_selector,
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
// FIXME(emilio): This is not ideal, and could not be
|
// FIXME(emilio): This is not ideal, and could not be
|
||||||
// accurate if we ever have stateful element-backed eager
|
// accurate if we ever have stateful element-backed eager
|
||||||
// pseudos.
|
// pseudos.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue