tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 7ab4d7f6dba998ac72e13394dd4ff8c0054674dc
parent 7ece99560bbf651a4b77e740ccc1bb6affd659c6
Author: Diego Escalante <descalante@mozilla.com>
Date:   Wed, 22 Oct 2025 18:49:44 +0000

Bug 1966704 - Add support for @scope invalidations with relative selectors. r=dshin,firefox-style-system-reviewers

Also implement drive-by fix for NegationScopeVisitor.

Differential Revision: https://phabricator.services.mozilla.com/D268952

Diffstat:
Mservo/components/style/invalidation/element/invalidator.rs | 3++-
Mservo/components/style/invalidation/element/relative_selector.rs | 66+++++++++++++++++++++++++++++++++++++++++++++++++++---------------
2 files changed, 53 insertions(+), 16 deletions(-)

diff --git a/servo/components/style/invalidation/element/invalidator.rs b/servo/components/style/invalidation/element/invalidator.rs @@ -440,7 +440,8 @@ impl NegationScopeVisitor { DependencyInvalidationKind::Normal(..) ) { - return dependency.selector.visit(&mut self); + dependency.selector.visit(&mut self); + return self.found_scope_in_negation; } let nested_visitor = Self { diff --git a/servo/components/style/invalidation/element/relative_selector.rs b/servo/components/style/invalidation/element/relative_selector.rs @@ -12,11 +12,12 @@ use crate::invalidation::element::element_wrapper::ElementWrapper; use crate::invalidation::element::invalidation_map::{ AdditionalRelativeSelectorInvalidationMap, Dependency, DependencyInvalidationKind, InvalidationMap, NormalDependencyInvalidationKind, RelativeDependencyInvalidationKind, - TSStateForInvalidation, + ScopeDependencyInvalidationKind, TSStateForInvalidation, }; use crate::invalidation::element::invalidator::{ - DescendantInvalidationLists, Invalidation, InvalidationProcessor, InvalidationResult, - InvalidationVector, SiblingTraversalMap, TreeStyleInvalidator, + note_scope_dependency_force_at_subject, DescendantInvalidationLists, Invalidation, + InvalidationProcessor, InvalidationResult, InvalidationVector, SiblingTraversalMap, + TreeStyleInvalidator, }; use crate::invalidation::element::restyle_hints::RestyleHint; use crate::invalidation::element::state_and_attributes::{ @@ -35,7 +36,7 @@ use selectors::matching::{ }; use selectors::parser::SelectorKey; use selectors::OpaqueElement; -use smallvec::SmallVec; +use smallvec::{smallvec, SmallVec}; use std::ops::DerefMut; /// Kind of DOM mutation this relative selector invalidation is being carried out in. @@ -1010,9 +1011,17 @@ where ); if let Some(x) = outer_dependency.next.as_ref() { - if !Self::is_subject(&x.as_ref().slice()[0]) { - // Not subject in outer selector. - return false; + // We only care to ensure that we're the subject in the outermost selector of the current + // selector - Crossing over a scope invalidation would mean moving into a selector inside + // the current scope block + if matches!( + outer_dependency.invalidation_kind(), + DependencyInvalidationKind::Normal(..) + ) { + if !Self::is_subject(&x.as_ref().slice()[0]) { + // Not subject in outer selector. + return false; + } } } outer_dependency @@ -1086,8 +1095,10 @@ where // e.g. Element under consideration can only be the anchor to `:has` in // `.foo .bar ~ .baz:has()`, iff it matches `.foo .bar ~ .baz`. let invalidated_self = { - let mut d = self.dependency; - loop { + let mut invalidated = false; + let mut dependencies_to_invalidate: SmallVec<[&Dependency; 1]> = + smallvec![self.dependency]; + while let Some(d) = dependencies_to_invalidate.pop() { debug_assert!( matches!( d.invalidation_kind(), @@ -1097,7 +1108,7 @@ where "Unexpected dependency kind" ); if !dependency_may_be_relevant(d, &element, false) { - break false; + continue; } if !matches_selector( &d.selector, @@ -1106,30 +1117,55 @@ where &element, self.matching_context(), ) { - break false; + continue; } + let invalidation_kind = d.invalidation_kind(); + + if let DependencyInvalidationKind::Scope(scope_kind) = invalidation_kind { + if d.selector_offset == 0 { + if scope_kind == ScopeDependencyInvalidationKind::ScopeEnd { + let invalidations = note_scope_dependency_force_at_subject( + d, + self.matching_context.current_host.clone(), + self.matching_context.scope_element, + false, + ); + descendant_invalidations + .dom_descendants + .extend(invalidations); + + invalidated |= true; + } else if let Some(ref next) = d.next { + dependencies_to_invalidate.extend(next.as_ref().slice()); + } + continue; + } + } + if matches!( invalidation_kind, DependencyInvalidationKind::Normal(NormalDependencyInvalidationKind::Element) - | DependencyInvalidationKind::Scope(_) ) { if let Some(ref deps) = d.next { - d = &deps.as_ref().slice()[0]; + // Normal dependencies should only have one next + dependencies_to_invalidate.push(&deps.as_ref().slice()[0]); continue; } - break true; + invalidated |= true; + continue; } debug_assert_ne!(d.selector_offset, 0); debug_assert_ne!(d.selector_offset, d.selector.len()); let invalidation = Invalidation::new(&d, self.host, None); - break push_invalidation( + invalidated |= push_invalidation( invalidation, d.invalidation_kind(), descendant_invalidations, sibling_invalidations, ); } + invalidated }; if invalidated_self {