style: Record ElementState bits that selectors depend on.
draft
style: Record ElementState bits that selectors depend on.
MozReview-Commit-ID: CNXKuk8fCeK
--- a/servo/components/style/stylist.rs
+++ b/servo/components/style/stylist.rs
@@ -6,16 +6,17 @@
#![deny(missing_docs)]
use {Atom, LocalName};
use bit_vec::BitVec;
use context::QuirksMode;
use data::ComputedStyle;
use dom::{AnimationRules, PresentationalHintsSynthetizer, TElement};
+use element_state::ElementState;
use error_reporting::RustLogReporter;
use font_metrics::FontMetricsProvider;
use keyframes::KeyframesAnimation;
use media_queries::Device;
use pdqsort::sort_by;
use properties::{self, CascadeFlags, ComputedValues};
#[cfg(feature = "servo")]
use properties::INHERIT_ALL;
@@ -124,16 +125,21 @@ pub struct Stylist {
/// Whether `"style"` appears in an attribute selector. This is not common,
/// and by tracking this explicitly, we can avoid taking an element snapshot
/// in the common case of style=""` changing due to modifying
/// `element.style`. (We could track this in `attribute_dependencies`, like
/// all other attributes, but we should probably not risk incorrectly
/// returning `true` for `"style"` just due to a hash collision.)
style_attribute_dependency: bool,
+ /// The element state bits that are relied on by selectors. Like
+ /// `attribute_dependencies`, this is used to avoid taking element snapshots
+ /// when an irrelevant element state bit changes.
+ state_dependencies: ElementState,
+
/// Selectors that require explicit cache revalidation (i.e. which depend
/// on state that is not otherwise visible to the cache, like attributes or
/// tree-structural state like child index and pseudos).
#[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
selectors_for_cache_revalidation: SelectorMap<SelectorInner<SelectorImpl>>,
/// The total number of selectors.
num_selectors: usize,
@@ -190,16 +196,17 @@ impl Stylist {
pseudos_map: Default::default(),
animations: Default::default(),
precomputed_pseudo_element_decls: Default::default(),
rules_source_order: 0,
rule_tree: RuleTree::new(),
dependencies: DependencySet::new(),
attribute_dependencies: BloomFilter::new(),
style_attribute_dependency: false,
+ state_dependencies: ElementState::empty(),
selectors_for_cache_revalidation: SelectorMap::new(),
num_selectors: 0,
num_declarations: 0,
num_rebuilds: 0,
};
SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
stylist.pseudos_map.insert(pseudo, PerPseudoElementSelectorMap::new());
@@ -274,16 +281,17 @@ impl Stylist {
self.pseudos_map.insert(pseudo, PerPseudoElementSelectorMap::new());
});
self.precomputed_pseudo_element_decls = Default::default();
self.rules_source_order = 0;
self.dependencies.clear();
self.attribute_dependencies.clear();
self.style_attribute_dependency = false;
+ self.state_dependencies = ElementState::empty();
self.animations.clear();
self.selectors_for_cache_revalidation = SelectorMap::new();
self.num_selectors = 0;
self.num_declarations = 0;
extra_data.clear_font_faces();
if let Some(ua_stylesheets) = ua_stylesheets {
@@ -334,17 +342,17 @@ impl Stylist {
CssRule::Style(ref locked) => {
let style_rule = locked.read_with(&guard);
self.num_declarations += style_rule.block.read_with(&guard).len();
for selector in &style_rule.selectors.0 {
self.num_selectors += 1;
self.add_rule_to_map(guard, selector, locked, stylesheet);
self.dependencies.note_selector(selector);
self.note_for_revalidation(selector);
- self.note_attribute_dependencies(selector);
+ self.note_attribute_and_state_dependencies(selector);
}
self.rules_source_order += 1;
}
CssRule::Import(ref import) => {
let import = import.read_with(guard);
self.add_stylesheet(&import.stylesheet, guard, extra_data)
}
CssRule::Keyframes(ref keyframes_rule) => {
@@ -406,19 +414,25 @@ impl Stylist {
pub fn might_have_attribute_dependency(&self, local_name: &Atom) -> bool {
if *local_name == atom!("style") {
self.style_attribute_dependency
} else {
self.attribute_dependencies.might_contain(local_name)
}
}
+ /// Returns whether the given ElementState bit is relied upon by a selector
+ /// of some rule in the stylist.
+ pub fn has_state_dependency(&self, state: ElementState) -> bool {
+ self.state_dependencies.intersects(state)
+ }
+
#[inline]
- fn note_attribute_dependencies(&mut self, selector: &Selector<SelectorImpl>) {
- selector.visit(&mut AttributeDependencyVisitor(self));
+ fn note_attribute_and_state_dependencies(&mut self, selector: &Selector<SelectorImpl>) {
+ selector.visit(&mut AttributeAndStateDependencyVisitor(self));
}
/// Computes the style for a given "precomputed" pseudo-element, taking the
/// universal rules and applying them.
///
/// If `inherit_all` is true, then all properties are inherited from the
/// parent; otherwise, non-inherited properties are reset to their initial
/// values. The flow constructor uses this flag when constructing anonymous
@@ -952,33 +966,41 @@ impl Drop for Stylist {
// after this point.
//
// TODO(emilio): We can at least assert all the elements in the free
// list are indeed free.
unsafe { self.rule_tree.gc(); }
}
}
-/// Visitor to collect names that appear in attribute selectors.
-struct AttributeDependencyVisitor<'a>(&'a mut Stylist);
+/// Visitor to collect names that appear in attribute selectors and any
+/// dependencies on ElementState bits.
+struct AttributeAndStateDependencyVisitor<'a>(&'a mut Stylist);
-impl<'a> SelectorVisitor for AttributeDependencyVisitor<'a> {
+impl<'a> SelectorVisitor for AttributeAndStateDependencyVisitor<'a> {
type Impl = SelectorImpl;
fn visit_attribute_selector(&mut self, selector: &AttrSelector<Self::Impl>) -> bool {
use precomputed_hash::PrecomputedHash;
if selector.lower_name == atom!("style") {
self.0.style_attribute_dependency = true;
} else {
self.0.attribute_dependencies.insert_hash(selector.name.precomputed_hash());
self.0.attribute_dependencies.insert_hash(selector.lower_name.precomputed_hash());
}
true
}
+
+ fn visit_simple_selector(&mut self, s: &Component<SelectorImpl>) -> bool {
+ if let Component::NonTSPseudoClass(ref p) = *s {
+ self.0.state_dependencies.insert(SelectorImpl::pseudo_class_state_flag(p));
+ }
+ true
+ }
}
/// Visitor determine whether a selector requires cache revalidation.
///
/// Note that we just check simple selectors and eagerly return when the first
/// need for revalidation is found, so we don't need to store state on the
/// visitor.
///
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -13,16 +13,17 @@ use std::fmt::Write;
use std::ptr;
use std::sync::Mutex;
use style::context::{QuirksMode, SharedStyleContext, StyleContext};
use style::context::{ThreadLocalStyleContext, ThreadLocalStyleContextCreationInfo};
use style::data::{ElementData, ElementStyles, RestyleData};
use style::dom::{AnimationOnlyDirtyDescendants, DirtyDescendants};
use style::dom::{ShowSubtreeData, TElement, TNode};
use style::error_reporting::RustLogReporter;
+use style::element_state::ElementState;
use style::font_metrics::get_metrics_provider_for_product;
use style::gecko::data::{PerDocumentStyleData, PerDocumentStyleDataImpl};
use style::gecko::global_style_data::{GLOBAL_STYLE_DATA, GlobalStyleData};
use style::gecko::restyle_damage::GeckoRestyleDamage;
use style::gecko::selector_parser::PseudoElement;
use style::gecko::traversal::RecalcStyleOnly;
use style::gecko::wrapper::GeckoElement;
use style::gecko_bindings::bindings;
@@ -2268,8 +2269,15 @@ pub extern "C" fn Servo_StyleSet_Resolve
}
#[no_mangle]
pub extern "C" fn Servo_StyleSet_MightHaveAttributeDependency(raw_data: RawServoStyleSetBorrowed,
local_name: *mut nsIAtom) -> bool {
let data = PerDocumentStyleData::from_ffi(raw_data).borrow();
unsafe { Atom::with(local_name, &mut |atom| data.stylist.might_have_attribute_dependency(atom)) }
}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSet_HasStateDependency(raw_data: RawServoStyleSetBorrowed,
+ state: u64) -> bool {
+ let data = PerDocumentStyleData::from_ffi(raw_data).borrow();
+ data.stylist.has_state_dependency(ElementState::from_bits(state).unwrap())
+}