mirror of
https://github.com/servo/servo.git
synced 2025-08-06 06:00:15 +01:00
style: Record attribute dependencies within the selector list of :nth-child(... of <selector list>)
There are separate filters for IDs, classes, attribute local names, and element state. Also, we invalidate siblings of elements matched against the selector list of :nth-child(... of <selector list>) by marking matched elements with NODE_HAS_SLOW_SELECTOR_NTH_OF. The only remaining invalidation case invalidation case is `:nth-child(An+B of :has())` (bug 1818155), which should not block shipping `layout.css.nth-child-of.enabled`, because :has(...) is still being implemented (bug 418039). Depends on D172352 Differential Revision: https://phabricator.services.mozilla.com/D171936
This commit is contained in:
parent
c7f8845665
commit
356e886d26
7 changed files with 261 additions and 28 deletions
|
@ -38,14 +38,24 @@ bitflags! {
|
|||
/// :first-of-type, or :nth-of-type.
|
||||
const HAS_SLOW_SELECTOR_LATER_SIBLINGS = 1 << 1;
|
||||
|
||||
/// When a DOM mutation occurs on a child that might be matched by
|
||||
/// :nth-last-child(.. of <selector list>), earlier children must be
|
||||
/// restyled, and HAS_SLOW_SELECTOR will be set (which normally
|
||||
/// indicates that all children will be restyled).
|
||||
///
|
||||
/// Similarly, when a DOM mutation occurs on a child that might be
|
||||
/// matched by :nth-child(.. of <selector list>), later children must be
|
||||
/// restyled, and HAS_SLOW_SELECTOR_LATER_SIBLINGS will be set.
|
||||
const HAS_SLOW_SELECTOR_NTH_OF = 1 << 2;
|
||||
|
||||
/// When a child is added or removed from the parent, the first and
|
||||
/// last children must be restyled, because they may match :first-child,
|
||||
/// :last-child, or :only-child.
|
||||
const HAS_EDGE_CHILD_SELECTOR = 1 << 2;
|
||||
const HAS_EDGE_CHILD_SELECTOR = 1 << 3;
|
||||
|
||||
/// The element has an empty selector, so when a child is appended we
|
||||
/// might need to restyle the parent completely.
|
||||
const HAS_EMPTY_SELECTOR = 1 << 3;
|
||||
const HAS_EMPTY_SELECTOR = 1 << 4;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,6 +69,7 @@ impl ElementSelectorFlags {
|
|||
pub fn for_parent(self) -> ElementSelectorFlags {
|
||||
self & (ElementSelectorFlags::HAS_SLOW_SELECTOR |
|
||||
ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS |
|
||||
ElementSelectorFlags::HAS_SLOW_SELECTOR_NTH_OF |
|
||||
ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR)
|
||||
}
|
||||
}
|
||||
|
@ -939,13 +950,17 @@ where
|
|||
let is_edge_child_selector = a == 0 && b == 1 && !is_of_type && selectors.is_empty();
|
||||
|
||||
if context.needs_selector_flags() {
|
||||
element.apply_selector_flags(if is_edge_child_selector {
|
||||
let mut flags = if is_edge_child_selector {
|
||||
ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR
|
||||
} else if is_from_end {
|
||||
ElementSelectorFlags::HAS_SLOW_SELECTOR
|
||||
} else {
|
||||
ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS
|
||||
});
|
||||
};
|
||||
if !selectors.is_empty() {
|
||||
flags |= ElementSelectorFlags::HAS_SLOW_SELECTOR_NTH_OF;
|
||||
}
|
||||
element.apply_selector_flags(flags);
|
||||
}
|
||||
|
||||
if !selectors.is_empty() && !list_matches_complex_selector(selectors, element, context) {
|
||||
|
|
|
@ -12,6 +12,7 @@ use crate::builder::{
|
|||
};
|
||||
use crate::context::QuirksMode;
|
||||
use crate::sink::Push;
|
||||
use crate::visitor::SelectorListKind;
|
||||
pub use crate::visitor::SelectorVisitor;
|
||||
use bitflags::bitflags;
|
||||
use cssparser::{match_ignore_ascii_case, parse_nth, *};
|
||||
|
@ -1625,14 +1626,15 @@ impl<Impl: SelectorImpl> Component<Impl> {
|
|||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
Negation(ref list) | Is(ref list) | Where(ref list) => {
|
||||
if !visitor.visit_selector_list(&list) {
|
||||
let list_kind = SelectorListKind::from_component(self);
|
||||
debug_assert!(!list_kind.is_empty());
|
||||
if !visitor.visit_selector_list(list_kind, &list) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
NthOf(ref nth_of_data) => {
|
||||
if !visitor.visit_selector_list(nth_of_data.selectors()) {
|
||||
if !visitor.visit_selector_list(SelectorListKind::NTH_OF, nth_of_data.selectors()) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
|
|
@ -38,7 +38,11 @@ pub trait SelectorVisitor: Sized {
|
|||
/// into the internal selectors if / as needed.
|
||||
///
|
||||
/// The default implementation does this.
|
||||
fn visit_selector_list(&mut self, list: &[Selector<Self::Impl>]) -> bool {
|
||||
fn visit_selector_list(
|
||||
&mut self,
|
||||
_list_kind: SelectorListKind,
|
||||
list: &[Selector<Self::Impl>],
|
||||
) -> bool {
|
||||
for nested in list {
|
||||
if !nested.visit(self) {
|
||||
return false;
|
||||
|
@ -55,3 +59,53 @@ pub trait SelectorVisitor: Sized {
|
|||
true
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// The kinds of components the visitor is visiting the selector list of, if any
|
||||
#[derive(Default)]
|
||||
pub struct SelectorListKind: u8 {
|
||||
/// The visitor is inside :not(..)
|
||||
const NEGATION = 1 << 0;
|
||||
/// The visitor is inside :is(..)
|
||||
const IS = 1 << 1;
|
||||
/// The visitor is inside :where(..)
|
||||
const WHERE = 1 << 2;
|
||||
/// The visitor is inside :nth-child(.. of <selector list>) or
|
||||
/// :nth-last-child(.. of <selector list>)
|
||||
const NTH_OF = 1 << 3;
|
||||
}
|
||||
}
|
||||
|
||||
impl SelectorListKind {
|
||||
/// Construct a SelectorListKind for the corresponding component.
|
||||
pub fn from_component<Impl: SelectorImpl>(component: &Component<Impl>) -> Self {
|
||||
match component {
|
||||
Component::Negation(_) => SelectorListKind::NEGATION,
|
||||
Component::Is(_) => SelectorListKind::IS,
|
||||
Component::Where(_) => SelectorListKind::WHERE,
|
||||
Component::NthOf(_) => SelectorListKind::NTH_OF,
|
||||
_ => SelectorListKind::empty(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether the visitor is inside :not(..)
|
||||
pub fn in_negation(&self) -> bool {
|
||||
self.intersects(SelectorListKind::NEGATION)
|
||||
}
|
||||
|
||||
/// Whether the visitor is inside :is(..)
|
||||
pub fn in_is(&self) -> bool {
|
||||
self.intersects(SelectorListKind::IS)
|
||||
}
|
||||
|
||||
/// Whether the visitor is inside :where(..)
|
||||
pub fn in_where(&self) -> bool {
|
||||
self.intersects(SelectorListKind::WHERE)
|
||||
}
|
||||
|
||||
/// Whether the visitor is inside :nth-child(.. of <selector list>) or
|
||||
/// :nth-last-child(.. of <selector list>)
|
||||
pub fn in_nth_of(&self) -> bool {
|
||||
self.intersects(SelectorListKind::NTH_OF)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue