use crate::context::StackLimitChecker;
use crate::dom::{TElement, TNode, TShadowRoot};
use crate::invalidation::element::invalidation_map::{
Dependency, DependencyInvalidationKind, NormalDependencyInvalidationKind,
RelativeDependencyInvalidationKind, ScopeDependencyInvalidationKind,
};
use selectors::matching::matches_compound_selector_from;
use selectors::matching::{CompoundSelectorMatchingResult, MatchingContext};
use selectors::parser::{Combinator, Component, Selector, SelectorVisitor};
use selectors::{OpaqueElement, SelectorImpl};
use smallvec::{smallvec, SmallVec};
use std::fmt;
use std::fmt::Write;
struct SiblingInfo<E>
where
E: TElement,
{
affected: E,
prev_sibling: Option<E>,
next_sibling: Option<E>,
}
pub struct SiblingTraversalMap<E>
where
E: TElement,
{
info: Option<SiblingInfo<E>>,
}
impl<E> Default for SiblingTraversalMap<E>
where
E: TElement,
{
fn default() -> Self {
Self { info: None }
}
}
impl<E> SiblingTraversalMap<E>
where
E: TElement,
{
pub fn new(affected: E, prev_sibling: Option<E>, next_sibling: Option<E>) -> Self {
Self {
info: Some(SiblingInfo {
affected,
prev_sibling,
next_sibling,
}),
}
}
pub fn next_sibling_for(&self, element: &E) -> Option<E> {
if let Some(ref info) = self.info {
if *element == info.affected {
return info.next_sibling;
}
}
element.next_sibling_element()
}
pub fn prev_sibling_for(&self, element: &E) -> Option<E> {
if let Some(ref info) = self.info {
if *element == info.affected {
return info.prev_sibling;
}
}
element.prev_sibling_element()
}
}
pub trait InvalidationProcessor<'a, 'b, E>
where
E: TElement,
{
fn invalidates_on_pseudo_element(&self) -> bool {
false
}
fn light_tree_only(&self) -> bool {
false
}
fn check_outer_dependency(
&mut self,
dependency: &Dependency,
element: E,
scope: Option<OpaqueElement>,
) -> bool;
fn matching_context(&mut self) -> &mut MatchingContext<'b, E::Impl>;
fn sibling_traversal_map(&self) -> &SiblingTraversalMap<E>;
fn collect_invalidations(
&mut self,
element: E,
self_invalidations: &mut InvalidationVector<'a>,
descendant_invalidations: &mut DescendantInvalidationLists<'a>,
sibling_invalidations: &mut InvalidationVector<'a>,
) -> bool;
fn should_process_descendants(&mut self, element: E) -> bool;
fn recursion_limit_exceeded(&mut self, element: E);
fn invalidated_self(&mut self, element: E);
fn invalidated_sibling(&mut self, sibling: E, of: E);
fn invalidated_highlight_pseudo(&mut self, _element: E) {}
fn invalidated_descendants(&mut self, element: E, child: E);
fn found_relative_selector_invalidation(
&mut self,
_element: E,
_kind: RelativeDependencyInvalidationKind,
_relative_dependency: &'a Dependency,
) {
debug_assert!(false, "Reached relative selector dependency");
}
}
#[derive(Debug, Default)]
pub struct DescendantInvalidationLists<'a> {
pub dom_descendants: InvalidationVector<'a>,
pub slotted_descendants: InvalidationVector<'a>,
pub parts: InvalidationVector<'a>,
}
impl<'a> DescendantInvalidationLists<'a> {
fn is_empty(&self) -> bool {
self.dom_descendants.is_empty()
&& self.slotted_descendants.is_empty()
&& self.parts.is_empty()
}
}
pub struct TreeStyleInvalidator<'a, 'b, 'c, E, P: 'a>
where
'b: 'a,
E: TElement,
P: InvalidationProcessor<'b, 'c, E>,
{
element: E,
stack_limit_checker: Option<&'a StackLimitChecker>,
processor: &'a mut P,
_marker: std::marker::PhantomData<(&'b (), &'c ())>,
}
pub type InvalidationVector<'a> = SmallVec<[Invalidation<'a>; 10]>;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum DescendantInvalidationKind {
Dom,
Slotted,
Part,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum InvalidationKind {
Descendant(DescendantInvalidationKind),
Sibling,
}
pub enum InvalidationAddOverride {
Descendant,
Sibling,
}
#[derive(Clone)]
pub struct Invalidation<'a> {
dependency: &'a Dependency,
host: Option<OpaqueElement>,
scope: Option<OpaqueElement>,
offset: usize,
matched_by_any_previous: bool,
always_effective_for_next_descendant: bool,
}
impl<'a> Invalidation<'a> {
pub fn new(
dependency: &'a Dependency,
host: Option<OpaqueElement>,
scope: Option<OpaqueElement>,
) -> Self {
debug_assert!(
dependency.selector_offset == dependency.selector.len() + 1
|| dependency.invalidation_kind()
!= DependencyInvalidationKind::Normal(
NormalDependencyInvalidationKind::Element
),
"No point to this, if the dependency matched the element we should just invalidate it"
);
Self {
dependency,
host,
scope,
offset: dependency.selector.len() + 1 - dependency.selector_offset,
matched_by_any_previous: false,
always_effective_for_next_descendant: false,
}
}
pub fn new_subject_invalidation(
dependency: &'a Dependency,
host: Option<OpaqueElement>,
scope: Option<OpaqueElement>,
) -> Self {
let mut compound_offset = 0;
for s in dependency.selector.iter_raw_match_order() {
if s.is_combinator() {
break;
}
compound_offset += 1;
}
Self {
dependency,
host,
scope,
offset: dependency.selector.len() - compound_offset,
matched_by_any_previous: false,
always_effective_for_next_descendant: true,
}
}
pub fn new_always_effective_for_next_descendant(
dependency: &'a Dependency,
host: Option<OpaqueElement>,
scope: Option<OpaqueElement>,
) -> Self {
if dependency.selector.is_rightmost(dependency.selector_offset) {
return Self::new_subject_invalidation(dependency, host, scope);
}
Self {
dependency,
host,
scope,
offset: dependency.selector.len() + 1 - dependency.selector_offset,
matched_by_any_previous: false,
always_effective_for_next_descendant: true,
}
}
pub fn combinator_to_right(&self) -> Combinator {
debug_assert_ne!(self.dependency.selector_offset, 0);
self.dependency
.selector
.combinator_at_match_order(self.dependency.selector.len() - self.offset)
}
fn effective_for_next(&self) -> bool {
if self.offset == 0 || self.always_effective_for_next_descendant {
return true;
}
match self
.dependency
.selector
.combinator_at_parse_order(self.offset - 1)
{
Combinator::Descendant | Combinator::LaterSibling | Combinator::PseudoElement => true,
Combinator::Part
| Combinator::SlotAssignment
| Combinator::NextSibling
| Combinator::Child => false,
}
}
fn kind(&self) -> InvalidationKind {
if self.offset == 0 {
return InvalidationKind::Descendant(DescendantInvalidationKind::Dom);
}
match self
.dependency
.selector
.combinator_at_parse_order(self.offset - 1)
{
Combinator::Child | Combinator::Descendant | Combinator::PseudoElement => {
InvalidationKind::Descendant(DescendantInvalidationKind::Dom)
},
Combinator::Part => InvalidationKind::Descendant(DescendantInvalidationKind::Part),
Combinator::SlotAssignment => {
InvalidationKind::Descendant(DescendantInvalidationKind::Slotted)
},
Combinator::NextSibling | Combinator::LaterSibling => InvalidationKind::Sibling,
}
}
}
struct NegationScopeVisitor {
in_negation: bool,
found_scope_in_negation: bool,
}
impl NegationScopeVisitor {
fn new() -> Self {
Self {
in_negation: false,
found_scope_in_negation: false,
}
}
fn traverse_selector(
mut self,
selector: &Selector<<NegationScopeVisitor as SelectorVisitor>::Impl>,
) -> bool {
selector.visit(&mut self);
self.found_scope_in_negation
}
fn traverse_dependency(mut self, dependency: &Dependency) -> bool {
if dependency.next.is_none()
|| !matches!(
dependency.invalidation_kind(),
DependencyInvalidationKind::Normal(..)
)
{
dependency.selector.visit(&mut self);
return self.found_scope_in_negation;
}
let nested_visitor = Self {
in_negation: self.in_negation,
found_scope_in_negation: false,
};
dependency.selector.visit(&mut self);
nested_visitor.traverse_dependency(&dependency.next.as_ref().unwrap().slice()[0])
}
}
impl SelectorVisitor for NegationScopeVisitor {
type Impl = crate::selector_parser::SelectorImpl;
fn visit_attribute_selector(
&mut self,
_namespace: &selectors::attr::NamespaceConstraint<
&<Self::Impl as SelectorImpl>::NamespaceUrl,
>,
_local_name: &<Self::Impl as SelectorImpl>::LocalName,
_local_name_lower: &<Self::Impl as SelectorImpl>::LocalName,
) -> bool {
true
}
fn visit_simple_selector(&mut self, component: &Component<Self::Impl>) -> bool {
if self.in_negation {
match component {
Component::Scope => {
self.found_scope_in_negation = true;
},
_ => {},
}
}
true
}
fn visit_relative_selector_list(
&mut self,
_list: &[selectors::parser::RelativeSelector<Self::Impl>],
) -> bool {
true
}
fn visit_selector_list(
&mut self,
list_kind: selectors::visitor::SelectorListKind,
list: &[selectors::parser::Selector<Self::Impl>],
) -> bool {
for nested in list {
let nested_visitor = Self {
in_negation: list_kind.in_negation(),
found_scope_in_negation: false,
};
self.found_scope_in_negation |= nested_visitor.traverse_selector(nested);
}
true
}
fn visit_complex_selector(&mut self, _combinator_to_right: Option<Combinator>) -> bool {
true
}
}
pub fn any_next_has_scope_in_negation(dependency: &Dependency) -> bool {
let next = match dependency.next.as_ref() {
None => return false,
Some(l) => l,
};
next.slice().iter().any(|dep| {
let visitor = NegationScopeVisitor::new();
visitor.traverse_dependency(dep)
})
}
impl<'a> fmt::Debug for Invalidation<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use cssparser::ToCss;
f.write_str("Invalidation(")?;
for component in self
.dependency
.selector
.iter_raw_parse_order_from(self.offset)
{
if matches!(*component, Component::Combinator(..)) {
break;
}
component.to_css(f)?;
}
f.write_char(')')
}
}
struct ProcessInvalidationResult {
invalidated_self: bool,
matched: bool,
}
pub struct InvalidationResult {
invalidated_self: bool,
invalidated_descendants: bool,
invalidated_siblings: bool,
}
impl InvalidationResult {
pub fn empty() -> Self {
Self {
invalidated_self: false,
invalidated_descendants: false,
invalidated_siblings: false,
}
}
pub fn has_invalidated_self(&self) -> bool {
self.invalidated_self
}
pub fn has_invalidated_descendants(&self) -> bool {
self.invalidated_descendants
}
pub fn has_invalidated_siblings(&self) -> bool {
self.invalidated_siblings
}
}
impl<'a, 'b, 'c, E, P: 'a> TreeStyleInvalidator<'a, 'b, 'c, E, P>
where
'b: 'a,
E: TElement,
P: InvalidationProcessor<'b, 'c, E>,
{
pub fn new(
element: E,
stack_limit_checker: Option<&'a StackLimitChecker>,
processor: &'a mut P,
) -> Self {
Self {
element,
stack_limit_checker,
processor,
_marker: std::marker::PhantomData,
}
}
pub fn invalidate(mut self) -> InvalidationResult {
debug!("StyleTreeInvalidator::invalidate({:?})", self.element);
let mut self_invalidations = InvalidationVector::new();
let mut descendant_invalidations = DescendantInvalidationLists::default();
let mut sibling_invalidations = InvalidationVector::new();
let mut invalidated_self = self.processor.collect_invalidations(
self.element,
&mut self_invalidations,
&mut descendant_invalidations,
&mut sibling_invalidations,
);
debug!("Collected invalidations (self: {}): ", invalidated_self);
debug!(
" > self: {}, {:?}",
self_invalidations.len(),
self_invalidations
);
debug!(" > descendants: {:?}", descendant_invalidations);
debug!(
" > siblings: {}, {:?}",
sibling_invalidations.len(),
sibling_invalidations
);
let invalidated_self_from_collection = invalidated_self;
invalidated_self |= self.process_descendant_invalidations(
&self_invalidations,
&mut descendant_invalidations,
&mut sibling_invalidations,
DescendantInvalidationKind::Dom,
);
if invalidated_self && !invalidated_self_from_collection {
self.processor.invalidated_self(self.element);
}
let invalidated_descendants = self.invalidate_descendants(&descendant_invalidations);
let invalidated_siblings = self.invalidate_siblings(&mut sibling_invalidations);
InvalidationResult {
invalidated_self,
invalidated_descendants,
invalidated_siblings,
}
}
fn invalidate_siblings(&mut self, sibling_invalidations: &mut InvalidationVector<'b>) -> bool {
if sibling_invalidations.is_empty() {
return false;
}
let mut current = self
.processor
.sibling_traversal_map()
.next_sibling_for(&self.element);
let mut any_invalidated = false;
while let Some(sibling) = current {
let mut sibling_invalidator =
TreeStyleInvalidator::new(sibling, self.stack_limit_checker, self.processor);
let mut invalidations_for_descendants = DescendantInvalidationLists::default();
let invalidated_sibling = sibling_invalidator.process_sibling_invalidations(
&mut invalidations_for_descendants,
sibling_invalidations,
);
if invalidated_sibling {
sibling_invalidator
.processor
.invalidated_sibling(sibling, self.element);
}
any_invalidated |= invalidated_sibling;
any_invalidated |=
sibling_invalidator.invalidate_descendants(&invalidations_for_descendants);
if sibling_invalidations.is_empty() {
break;
}
current = self
.processor
.sibling_traversal_map()
.next_sibling_for(&sibling);
}
any_invalidated
}
fn invalidate_pseudo_element_or_nac(
&mut self,
child: E,
invalidations: &[Invalidation<'b>],
) -> bool {
let mut sibling_invalidations = InvalidationVector::new();
let result = self.invalidate_child(
child,
invalidations,
&mut sibling_invalidations,
DescendantInvalidationKind::Dom,
);
result
}
fn invalidate_child(
&mut self,
child: E,
invalidations: &[Invalidation<'b>],
sibling_invalidations: &mut InvalidationVector<'b>,
descendant_invalidation_kind: DescendantInvalidationKind,
) -> bool {
let mut invalidations_for_descendants = DescendantInvalidationLists::default();
let mut invalidated_child = false;
let invalidated_descendants = {
let mut child_invalidator =
TreeStyleInvalidator::new(child, self.stack_limit_checker, self.processor);
invalidated_child |= child_invalidator.process_sibling_invalidations(
&mut invalidations_for_descendants,
sibling_invalidations,
);
invalidated_child |= child_invalidator.process_descendant_invalidations(
invalidations,
&mut invalidations_for_descendants,
sibling_invalidations,
descendant_invalidation_kind,
);
if invalidated_child {
child_invalidator.processor.invalidated_self(child);
}
child_invalidator.invalidate_descendants(&invalidations_for_descendants)
};
if invalidated_child || invalidated_descendants {
self.processor.invalidated_descendants(self.element, child);
}
invalidated_child || invalidated_descendants
}
fn invalidate_nac(&mut self, invalidations: &[Invalidation<'b>]) -> bool {
let mut any_nac_root = false;
let element = self.element;
element.each_anonymous_content_child(|nac| {
any_nac_root |= self.invalidate_pseudo_element_or_nac(nac, invalidations);
});
any_nac_root
}
fn invalidate_dom_descendants_of(
&mut self,
parent: E::ConcreteNode,
invalidations: &[Invalidation<'b>],
) -> bool {
let mut any_descendant = false;
let mut sibling_invalidations = InvalidationVector::new();
for child in parent.dom_children() {
let child = match child.as_element() {
Some(e) => e,
None => continue,
};
any_descendant |= self.invalidate_child(
child,
invalidations,
&mut sibling_invalidations,
DescendantInvalidationKind::Dom,
);
}
any_descendant
}
fn invalidate_parts_in_shadow_tree(
&mut self,
shadow: <E::ConcreteNode as TNode>::ConcreteShadowRoot,
invalidations: &[Invalidation<'b>],
) -> bool {
debug_assert!(!invalidations.is_empty());
let mut any = false;
let mut sibling_invalidations = InvalidationVector::new();
for node in shadow.as_node().dom_descendants() {
let element = match node.as_element() {
Some(e) => e,
None => continue,
};
if element.has_part_attr() {
any |= self.invalidate_child(
element,
invalidations,
&mut sibling_invalidations,
DescendantInvalidationKind::Part,
);
debug_assert!(
sibling_invalidations.is_empty(),
"::part() shouldn't have sibling combinators to the right, \
this makes no sense! {:?}",
sibling_invalidations
);
}
if let Some(shadow) = element.shadow_root() {
if element.exports_any_part() {
any |= self.invalidate_parts_in_shadow_tree(shadow, invalidations)
}
}
}
any
}
fn invalidate_parts(&mut self, invalidations: &[Invalidation<'b>]) -> bool {
if invalidations.is_empty() {
return false;
}
let shadow = match self.element.shadow_root() {
Some(s) => s,
None => return false,
};
self.invalidate_parts_in_shadow_tree(shadow, invalidations)
}
fn invalidate_slotted_elements(&mut self, invalidations: &[Invalidation<'b>]) -> bool {
if invalidations.is_empty() {
return false;
}
let slot = self.element;
self.invalidate_slotted_elements_in_slot(slot, invalidations)
}
fn invalidate_slotted_elements_in_slot(
&mut self,
slot: E,
invalidations: &[Invalidation<'b>],
) -> bool {
let mut any = false;
let mut sibling_invalidations = InvalidationVector::new();
for node in slot.slotted_nodes() {
let element = match node.as_element() {
Some(e) => e,
None => continue,
};
if element.is_html_slot_element() {
any |= self.invalidate_slotted_elements_in_slot(element, invalidations);
} else {
any |= self.invalidate_child(
element,
invalidations,
&mut sibling_invalidations,
DescendantInvalidationKind::Slotted,
);
}
debug_assert!(
sibling_invalidations.is_empty(),
"::slotted() shouldn't have sibling combinators to the right, \
this makes no sense! {:?}",
sibling_invalidations
);
}
any
}
fn invalidate_non_slotted_descendants(&mut self, invalidations: &[Invalidation<'b>]) -> bool {
if invalidations.is_empty() {
return false;
}
if self.processor.light_tree_only() {
let node = self.element.as_node();
return self.invalidate_dom_descendants_of(node, invalidations);
}
let mut any_descendant = false;
if let Some(root) = self.element.shadow_root() {
any_descendant |= self.invalidate_dom_descendants_of(root.as_node(), invalidations);
}
any_descendant |= self.invalidate_dom_descendants_of(self.element.as_node(), invalidations);
any_descendant |= self.invalidate_nac(invalidations);
any_descendant
}
fn invalidate_descendants(&mut self, invalidations: &DescendantInvalidationLists<'b>) -> bool {
if invalidations.is_empty() {
return false;
}
debug!(
"StyleTreeInvalidator::invalidate_descendants({:?})",
self.element
);
debug!(" > {:?}", invalidations);
let should_process = self.processor.should_process_descendants(self.element);
if !should_process {
return false;
}
if let Some(checker) = self.stack_limit_checker {
if checker.limit_exceeded() {
self.processor.recursion_limit_exceeded(self.element);
return true;
}
}
let mut any_descendant = false;
any_descendant |= self.invalidate_non_slotted_descendants(&invalidations.dom_descendants);
any_descendant |= self.invalidate_slotted_elements(&invalidations.slotted_descendants);
any_descendant |= self.invalidate_parts(&invalidations.parts);
any_descendant
}
fn process_sibling_invalidations(
&mut self,
descendant_invalidations: &mut DescendantInvalidationLists<'b>,
sibling_invalidations: &mut InvalidationVector<'b>,
) -> bool {
let mut i = 0;
let mut new_sibling_invalidations = InvalidationVector::new();
let mut invalidated_self = false;
while i < sibling_invalidations.len() {
let result = self.process_invalidation(
&sibling_invalidations[i],
descendant_invalidations,
&mut new_sibling_invalidations,
InvalidationKind::Sibling,
);
invalidated_self |= result.invalidated_self;
sibling_invalidations[i].matched_by_any_previous |= result.matched;
if sibling_invalidations[i].effective_for_next() {
i += 1;
} else {
sibling_invalidations.remove(i);
}
}
sibling_invalidations.extend(new_sibling_invalidations.drain(..));
invalidated_self
}
fn process_descendant_invalidations(
&mut self,
invalidations: &[Invalidation<'b>],
descendant_invalidations: &mut DescendantInvalidationLists<'b>,
sibling_invalidations: &mut InvalidationVector<'b>,
descendant_invalidation_kind: DescendantInvalidationKind,
) -> bool {
let mut invalidated = false;
for invalidation in invalidations {
let result = self.process_invalidation(
invalidation,
descendant_invalidations,
sibling_invalidations,
InvalidationKind::Descendant(descendant_invalidation_kind),
);
invalidated |= result.invalidated_self;
if invalidation.effective_for_next() {
let mut invalidation = invalidation.clone();
invalidation.matched_by_any_previous |= result.matched;
debug_assert_eq!(
descendant_invalidation_kind,
DescendantInvalidationKind::Dom,
"Slotted or part invalidations don't propagate."
);
descendant_invalidations.dom_descendants.push(invalidation);
}
}
invalidated
}
#[inline(always)]
fn handle_fully_matched(
&mut self,
invalidation: &Invalidation<'b>,
) -> (ProcessInvalidationResult, SmallVec<[Invalidation<'b>; 1]>) {
debug!(" > Invalidation matched completely");
let mut to_process: SmallVec<[&Dependency; 1]> = SmallVec::from([invalidation.dependency]);
let mut next_invalidations: SmallVec<[Invalidation; 1]> = SmallVec::new();
let mut result = ProcessInvalidationResult {
invalidated_self: false,
matched: false,
};
while !to_process.is_empty() {
let mut next_dependencies: SmallVec<[&Dependency; 1]> = SmallVec::new();
while let Some(dependency) = to_process.pop() {
if let DependencyInvalidationKind::Scope(scope_kind) =
dependency.invalidation_kind()
{
if scope_kind == ScopeDependencyInvalidationKind::ImplicitScope {
if let Some(ref deps) = dependency.next {
for dep in deps.as_ref().slice() {
let invalidation =
Invalidation::new_always_effective_for_next_descendant(
dep,
invalidation.host,
invalidation.scope,
);
next_invalidations.push(invalidation);
}
}
continue;
}
let force_add = any_next_has_scope_in_negation(dependency);
if scope_kind == ScopeDependencyInvalidationKind::ScopeEnd || force_add {
let invalidations = note_scope_dependency_force_at_subject(
dependency,
invalidation.host,
invalidation.scope,
force_add,
);
next_invalidations.extend(invalidations);
continue;
}
}
match dependency.next {
None => {
result.invalidated_self = true;
result.matched = true;
},
Some(ref deps) => {
for n in deps.as_ref().slice() {
let invalidation_kind = n.invalidation_kind();
match invalidation_kind {
DependencyInvalidationKind::FullSelector => unreachable!(),
DependencyInvalidationKind::Normal(_) => next_dependencies.push(n),
DependencyInvalidationKind::Scope(_) => {
next_dependencies.push(n);
},
DependencyInvalidationKind::Relative(kind) => {
self.processor.found_relative_selector_invalidation(
self.element,
kind,
n,
);
result.matched = true;
},
}
}
},
};
}
for cur_dependency in next_dependencies.as_ref() {
let scope = matches!(
invalidation.dependency.invalidation_kind(),
DependencyInvalidationKind::Scope(_)
)
.then(|| self.element.opaque());
debug!(" > Checking outer dependency {:?}", cur_dependency);
if !self
.processor
.check_outer_dependency(cur_dependency, self.element, scope)
{
continue;
}
let invalidation_kind = cur_dependency.invalidation_kind();
if matches!(
invalidation_kind,
DependencyInvalidationKind::Normal(NormalDependencyInvalidationKind::Element)
) || (matches!(invalidation_kind, DependencyInvalidationKind::Scope(_))
&& cur_dependency
.selector
.is_rightmost(cur_dependency.selector_offset))
{
to_process.push(cur_dependency);
continue;
}
debug!(" > Generating invalidation");
next_invalidations.push(Invalidation::new(
cur_dependency,
invalidation.host,
scope,
));
}
}
return (result, next_invalidations);
}
fn process_invalidation(
&mut self,
invalidation: &Invalidation<'b>,
descendant_invalidations: &mut DescendantInvalidationLists<'b>,
sibling_invalidations: &mut InvalidationVector<'b>,
invalidation_kind: InvalidationKind,
) -> ProcessInvalidationResult {
debug!(
"TreeStyleInvalidator::process_invalidation({:?}, {:?}, {:?})",
self.element, invalidation, invalidation_kind
);
let matching_result = {
let context = self.processor.matching_context();
context.current_host = invalidation.host;
context.nest_for_scope_condition(invalidation.scope, |ctx| {
matches_compound_selector_from(
&invalidation.dependency.selector,
invalidation.offset,
ctx,
&self.element,
)
})
};
let (mut result, next_invalidations) = match matching_result {
CompoundSelectorMatchingResult::NotMatched => {
return ProcessInvalidationResult {
invalidated_self: false,
matched: false,
}
},
CompoundSelectorMatchingResult::FullyMatched => self.handle_fully_matched(invalidation),
CompoundSelectorMatchingResult::Matched {
next_combinator_offset,
} => (
ProcessInvalidationResult {
invalidated_self: false,
matched: true,
},
smallvec![Invalidation {
dependency: invalidation.dependency,
host: invalidation.host,
scope: invalidation.scope,
offset: next_combinator_offset + 1,
matched_by_any_previous: false,
always_effective_for_next_descendant: invalidation
.always_effective_for_next_descendant,
}],
),
};
for next_invalidation in next_invalidations {
let next_invalidation_kind = if next_invalidation.always_effective_for_next_descendant {
InvalidationKind::Descendant(DescendantInvalidationKind::Dom)
} else {
debug_assert_ne!(
next_invalidation.offset, 0,
"Rightmost selectors shouldn't generate more invalidations",
);
let next_combinator = next_invalidation
.dependency
.selector
.combinator_at_parse_order(next_invalidation.offset - 1);
if matches!(next_combinator, Combinator::PseudoElement)
&& self.processor.invalidates_on_pseudo_element()
{
result.invalidated_self = true;
if next_invalidation
.dependency
.selector
.pseudo_element()
.is_some_and(|p| p.is_lazy_painted_highlight_pseudo())
{
self.processor.invalidated_highlight_pseudo(self.element);
}
}
debug!(
" > Invalidation matched, next: {:?}, ({:?})",
next_invalidation, next_combinator
);
next_invalidation.kind()
};
let can_skip_pushing = next_invalidation_kind == invalidation_kind
&& invalidation.matched_by_any_previous
&& next_invalidation.effective_for_next();
if can_skip_pushing {
debug!(
" > Can avoid push, since the invalidation had \
already been matched before"
);
} else {
match next_invalidation_kind {
InvalidationKind::Descendant(DescendantInvalidationKind::Dom) => {
descendant_invalidations
.dom_descendants
.push(next_invalidation);
},
InvalidationKind::Descendant(DescendantInvalidationKind::Part) => {
descendant_invalidations.parts.push(next_invalidation);
},
InvalidationKind::Descendant(DescendantInvalidationKind::Slotted) => {
descendant_invalidations
.slotted_descendants
.push(next_invalidation);
},
InvalidationKind::Sibling => {
sibling_invalidations.push(next_invalidation);
},
}
}
}
result
}
}
pub fn note_scope_dependency_force_at_subject<'selectors>(
dependency: &'selectors Dependency,
current_host: Option<OpaqueElement>,
scope: Option<OpaqueElement>,
traversed_non_subject: bool,
) -> Vec<Invalidation<'selectors>> {
let mut invalidations: Vec<Invalidation> = Vec::new();
if let Some(next) = dependency.next.as_ref() {
for dep in next.slice() {
if dep.selector.is_rightmost(dep.selector_offset) && !traversed_non_subject {
continue;
}
if dep.next.is_some()
&& matches!(
dep.invalidation_kind(),
DependencyInvalidationKind::Normal(_)
)
{
invalidations.extend(note_scope_dependency_force_at_subject(
dep,
current_host,
scope,
true,
));
} else {
let invalidation = Invalidation::new_subject_invalidation(dep, current_host, scope);
invalidations.push(invalidation);
}
}
}
invalidations
}