summaryrefslogtreecommitdiffstats
path: root/servo/components/style/invalidation
diff options
context:
space:
mode:
Diffstat (limited to 'servo/components/style/invalidation')
-rw-r--r--servo/components/style/invalidation/element/element_wrapper.rs24
-rw-r--r--servo/components/style/invalidation/element/invalidation_map.rs37
-rw-r--r--servo/components/style/invalidation/element/relative_selector.rs25
-rw-r--r--servo/components/style/invalidation/element/state_and_attributes.rs66
-rw-r--r--servo/components/style/invalidation/stylesheets.rs7
5 files changed, 132 insertions, 27 deletions
diff --git a/servo/components/style/invalidation/element/element_wrapper.rs b/servo/components/style/invalidation/element/element_wrapper.rs
index e17afd7774..9a39b7d5a0 100644
--- a/servo/components/style/invalidation/element/element_wrapper.rs
+++ b/servo/components/style/invalidation/element/element_wrapper.rs
@@ -73,6 +73,18 @@ pub trait ElementSnapshot: Sized {
where
F: FnMut(&AtomIdent);
+
+ /// If this snapshot contains CustomStateSet information.
+ fn has_custom_states(&self) -> bool;
+
+ /// A callback that should be called for each CustomState of the snapshot.
+ fn has_custom_state(&self, state: &AtomIdent) -> bool;
+
+ /// A callback that should be called for each CustomState of the snapshot.
+ fn each_custom_state<F>(&self, callback: F)
+ where
+ F: FnMut(&AtomIdent);
+
/// The `xml:lang=""` or `lang=""` attribute value per this snapshot.
fn lang_attr(&self) -> Option<AttrValue>;
}
@@ -213,6 +225,11 @@ where
.match_element_lang(Some(self.get_lang()), lang_arg);
},
+ // CustomStateSet should match against the snapshot before element
+ NonTSPseudoClass::CustomState(ref state) => {
+ return self.has_custom_state(&state.0)
+ },
+
_ => {},
}
@@ -357,6 +374,13 @@ where
}
}
+ fn has_custom_state(&self, state: &AtomIdent) -> bool {
+ match self.snapshot() {
+ Some(snapshot) if snapshot.has_custom_states() => snapshot.has_custom_state(state),
+ _ => self.element.has_custom_state(state),
+ }
+ }
+
fn is_empty(&self) -> bool {
self.element.is_empty()
}
diff --git a/servo/components/style/invalidation/element/invalidation_map.rs b/servo/components/style/invalidation/element/invalidation_map.rs
index cb03862740..f42f542dd2 100644
--- a/servo/components/style/invalidation/element/invalidation_map.rs
+++ b/servo/components/style/invalidation/element/invalidation_map.rs
@@ -10,6 +10,7 @@ use crate::selector_map::{
};
use crate::selector_parser::{NonTSPseudoClass, SelectorImpl};
use crate::AllocErr;
+use crate::values::AtomIdent;
use crate::{Atom, LocalName, Namespace, ShrinkIfNeeded};
use dom::{DocumentState, ElementState};
use selectors::attr::NamespaceConstraint;
@@ -259,6 +260,8 @@ pub type IdOrClassDependencyMap = MaybeCaseInsensitiveHashMap<Atom, SmallVec<[De
pub type StateDependencyMap = SelectorMap<StateDependency>;
/// Dependency mapping for local names.
pub type LocalNameDependencyMap = PrecomputedHashMap<LocalName, SmallVec<[Dependency; 1]>>;
+/// Dependency mapping for customstates
+pub type CustomStateDependencyMap = PrecomputedHashMap<AtomIdent, SmallVec<[Dependency; 1]>>;
/// A map where we store invalidations.
///
@@ -282,6 +285,8 @@ pub struct InvalidationMap {
pub document_state_selectors: Vec<DocumentStateDependency>,
/// A map of other attribute affecting selectors.
pub other_attribute_affecting_selectors: LocalNameDependencyMap,
+ /// A map of CSS custom states
+ pub custom_state_affecting_selectors: CustomStateDependencyMap,
}
/// Tree-structural pseudoclasses that we care about for (Relative selector) invalidation.
@@ -383,6 +388,7 @@ impl InvalidationMap {
state_affecting_selectors: StateDependencyMap::new(),
document_state_selectors: Vec::new(),
other_attribute_affecting_selectors: LocalNameDependencyMap::default(),
+ custom_state_affecting_selectors: CustomStateDependencyMap::default(),
}
}
@@ -398,6 +404,9 @@ impl InvalidationMap {
.fold(0, |accum, (_, ref v)| accum + v.len()) +
self.class_to_selector
.iter()
+ .fold(0, |accum, (_, ref v)| accum + v.len()) +
+ self.custom_state_affecting_selectors
+ .iter()
.fold(0, |accum, (_, ref v)| accum + v.len())
}
@@ -408,6 +417,7 @@ impl InvalidationMap {
self.state_affecting_selectors.clear();
self.document_state_selectors.clear();
self.other_attribute_affecting_selectors.clear();
+ self.custom_state_affecting_selectors.clear();
}
/// Shrink the capacity of hash maps if needed.
@@ -416,6 +426,7 @@ impl InvalidationMap {
self.id_to_selector.shrink_if_needed();
self.state_affecting_selectors.shrink_if_needed();
self.other_attribute_affecting_selectors.shrink_if_needed();
+ self.custom_state_affecting_selectors.shrink_if_needed();
}
}
@@ -489,6 +500,7 @@ trait Collector {
fn class_map(&mut self) -> &mut IdOrClassDependencyMap;
fn state_map(&mut self) -> &mut StateDependencyMap;
fn attribute_map(&mut self) -> &mut LocalNameDependencyMap;
+ fn custom_state_map(&mut self) -> &mut LocalNameDependencyMap;
fn update_states(&mut self, element_state: ElementState, document_state: DocumentState);
// In normal invalidations, type-based dependencies don't need to be explicitly tracked;
@@ -551,6 +563,16 @@ fn add_attr_dependency<C: Collector>(name: LocalName, collector: &mut C) -> Resu
add_local_name(name, dependency, map)
}
+fn add_custom_state_dependency<C: Collector>(name: AtomIdent, collector: &mut C) -> Result<(), AllocErr> {
+ let dependency = collector.dependency();
+ let map = collector.custom_state_map();
+ map.try_reserve(1)?;
+ let vec = map.entry(name).or_default();
+ vec.try_reserve(1)?;
+ vec.push(dependency);
+ Ok(())
+}
+
fn add_local_name(
name: LocalName,
dependency: Dependency,
@@ -576,6 +598,9 @@ fn on_pseudo_class<C: Collector>(pc: &NonTSPseudoClass, collector: &mut C) -> Re
return add_attr_dependency(local_name!("size"), collector);
},
NonTSPseudoClass::Lang(..) => local_name!("lang"),
+ NonTSPseudoClass::CustomState(ref name) => {
+ return add_custom_state_dependency(name.0.clone(), collector);
+ },
_ => return Ok(()),
};
@@ -702,6 +727,10 @@ impl<'a> Collector for SelectorDependencyCollector<'a> {
self.compound_state.element_state |= element_state;
*self.document_state |= document_state;
}
+
+ fn custom_state_map(&mut self) -> &mut CustomStateDependencyMap {
+ &mut self.map.custom_state_affecting_selectors
+ }
}
impl<'a> SelectorDependencyCollector<'a> {
@@ -1074,6 +1103,10 @@ impl<'a> Collector for RelativeSelectorDependencyCollector<'a> {
&mut self.map.map.other_attribute_affecting_selectors
}
+ fn custom_state_map(&mut self) -> &mut CustomStateDependencyMap {
+ &mut self.map.map.custom_state_affecting_selectors
+ }
+
fn update_states(&mut self, element_state: ElementState, document_state: DocumentState) {
self.compound_state.state.element_state |= element_state;
*self.document_state |= document_state;
@@ -1255,6 +1288,10 @@ impl<'a, 'b> Collector for RelativeSelectorInnerDependencyCollector<'a, 'b> {
&mut self.map.map.other_attribute_affecting_selectors
}
+ fn custom_state_map(&mut self) -> &mut CustomStateDependencyMap {
+ &mut self.map.map.custom_state_affecting_selectors
+ }
+
fn update_states(&mut self, element_state: ElementState, document_state: DocumentState) {
self.compound_state.state.element_state |= element_state;
*self.document_state |= document_state;
diff --git a/servo/components/style/invalidation/element/relative_selector.rs b/servo/components/style/invalidation/element/relative_selector.rs
index ccb48e349f..41222304be 100644
--- a/servo/components/style/invalidation/element/relative_selector.rs
+++ b/servo/components/style/invalidation/element/relative_selector.rs
@@ -53,12 +53,12 @@ impl DomMutationOperation {
fn accept<E: TElement>(&self, d: &Dependency, e: E) -> bool {
match self {
Self::Insert | Self::Append | Self::Remove => {
- e.relative_selector_search_direction().is_some()
+ !e.relative_selector_search_direction().is_empty()
},
// `:has(+ .a + .b)` with `.anchor + .a + .remove + .b` - `.a` would be present
// in the search path.
Self::SideEffectPrevSibling => {
- e.relative_selector_search_direction().is_some() &&
+ !e.relative_selector_search_direction().is_empty() &&
d.right_combinator_is_next_sibling()
},
// If an element is being removed and would cause next-sibling match to happen,
@@ -499,6 +499,19 @@ where
},
None => (),
});
+ element.each_custom_state(|v| {
+ match map.map.custom_state_affecting_selectors.get(v) {
+ Some(v) => {
+ for dependency in v {
+ if !operation.accept(dependency, element) {
+ continue;
+ }
+ self.add_dependency(dependency, element, scope);
+ }
+ },
+ None => (),
+ }
+ });
element.each_attr_name(
|v| match map.map.other_attribute_affecting_selectors.get(v) {
Some(v) => {
@@ -782,11 +795,7 @@ where
/// Is this element in the direction of the given relative selector search path?
fn in_search_direction(element: &E, desired: ElementSelectorFlags) -> bool {
- if let Some(direction) = element.relative_selector_search_direction() {
- direction.intersects(desired)
- } else {
- false
- }
+ element.relative_selector_search_direction().intersects(desired)
}
/// Handle a potential relative selector anchor.
@@ -1145,7 +1154,7 @@ where
dep: &'a Dependency,
) {
debug_assert!(dep.parent.is_some(), "Orphaned inners selector?");
- if element.relative_selector_search_direction().is_none() {
+ if element.relative_selector_search_direction().is_empty() {
return;
}
self.invalidations.push((
diff --git a/servo/components/style/invalidation/element/state_and_attributes.rs b/servo/components/style/invalidation/element/state_and_attributes.rs
index 1c58cddf1e..d5f0723d66 100644
--- a/servo/components/style/invalidation/element/state_and_attributes.rs
+++ b/servo/components/style/invalidation/element/state_and_attributes.rs
@@ -19,11 +19,12 @@ use crate::selector_map::SelectorMap;
use crate::selector_parser::Snapshot;
use crate::stylesheets::origin::OriginSet;
use crate::{Atom, WeakAtom};
+use crate::values::AtomIdent;
use dom::ElementState;
use selectors::attr::CaseSensitivity;
+use selectors::kleene_value::KleeneValue;
use selectors::matching::{
- matches_selector, MatchingContext, MatchingForInvalidation, MatchingMode, NeedsSelectorFlags,
- SelectorCaches, VisitedHandlingMode,
+ matches_selector_kleene, MatchingContext, MatchingForInvalidation, MatchingMode, NeedsSelectorFlags, SelectorCaches, VisitedHandlingMode
};
use smallvec::SmallVec;
@@ -41,6 +42,8 @@ where
added_id: Option<&'a WeakAtom>,
classes_removed: &'a SmallVec<[Atom; 8]>,
classes_added: &'a SmallVec<[Atom; 8]>,
+ custom_states_removed: &'a SmallVec<[AtomIdent; 8]>,
+ custom_states_added: &'a SmallVec<[AtomIdent; 8]>,
state_changes: ElementState,
descendant_invalidations: &'a mut DescendantInvalidationLists<'selectors>,
sibling_invalidations: &'a mut InvalidationVector<'selectors>,
@@ -97,23 +100,25 @@ where
E: TElement,
W: selectors::Element<Impl = E::Impl>,
{
- let matches_now = matches_selector(
- &dependency.selector,
- dependency.selector_offset,
- None,
- element,
- context,
- );
+ context.for_invalidation_comparison(|context| {
+ let matches_now = matches_selector_kleene(
+ &dependency.selector,
+ dependency.selector_offset,
+ None,
+ element,
+ context,
+ );
- let matched_then = matches_selector(
- &dependency.selector,
- dependency.selector_offset,
- None,
- wrapper,
- context,
- );
+ let matched_then = matches_selector_kleene(
+ &dependency.selector,
+ dependency.selector_offset,
+ None,
+ wrapper,
+ context,
+ );
- matched_then != matches_now
+ matched_then != matches_now || matches_now == KleeneValue::Unknown
+ })
}
/// Whether we should process the descendants of a given element for style
@@ -243,7 +248,7 @@ where
return false;
};
- if !snapshot.has_attrs() && state_changes.is_empty() {
+ if !snapshot.has_attrs() && !snapshot.has_custom_states() && state_changes.is_empty() {
return false;
}
@@ -264,6 +269,21 @@ where
})
}
+ let mut custom_states_removed = SmallVec::<[AtomIdent; 8]>::new();
+ let mut custom_states_added = SmallVec::<[AtomIdent; 8]>::new();
+ if snapshot.has_custom_states() {
+ snapshot.each_custom_state(|s| {
+ if !element.has_custom_state(s) {
+ custom_states_removed.push(s.clone())
+ }
+ });
+ element.each_custom_state(|s| {
+ if !snapshot.has_custom_state(s) {
+ custom_states_added.push(s.clone())
+ }
+ })
+ }
+
let mut id_removed = None;
let mut id_added = None;
if snapshot.id_changed() {
@@ -326,6 +346,8 @@ where
added_id: id_added,
classes_removed: &classes_removed,
classes_added: &classes_added,
+ custom_states_removed: &custom_states_removed,
+ custom_states_added: &custom_states_added,
descendant_invalidations,
sibling_invalidations,
invalidates_self: false,
@@ -443,6 +465,14 @@ where
}
}
+ for state in self.custom_states_added.iter().chain(self.custom_states_removed.iter()) {
+ if let Some(deps) = map.custom_state_affecting_selectors.get(state) {
+ for dep in deps {
+ self.scan_dependency(dep);
+ }
+ }
+ }
+
self.snapshot.each_attr_changed(|attribute| {
if let Some(deps) = map.other_attribute_affecting_selectors.get(attribute) {
for dep in deps {
diff --git a/servo/components/style/invalidation/stylesheets.rs b/servo/components/style/invalidation/stylesheets.rs
index d845897aa4..48ef8c1b5d 100644
--- a/servo/components/style/invalidation/stylesheets.rs
+++ b/servo/components/style/invalidation/stylesheets.rs
@@ -616,7 +616,7 @@ impl StylesheetInvalidationSet {
return self.invalidate_fully();
},
Document(..) | Import(..) | Media(..) | Supports(..) | Container(..) |
- LayerBlock(..) => {
+ LayerBlock(..) | StartingStyle(..) => {
// Do nothing, relevant nested rules are visited as part of rule iteration.
},
FontFace(..) => {
@@ -646,6 +646,11 @@ impl StylesheetInvalidationSet {
debug!(" > Found unsupported rule, marking the whole subtree invalid.");
self.invalidate_fully();
},
+ Scope(..) => {
+ // Addition/removal of @scope requires re-evaluation of scope proximity to properly
+ // figure out the styling order.
+ self.invalidate_fully();
+ },
}
}
}