mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
style: Make a bloom filter arrive to restyle hint computation.
This commit is contained in:
parent
1cea4e7942
commit
a12996f030
4 changed files with 107 additions and 38 deletions
|
@ -4,13 +4,11 @@
|
||||||
|
|
||||||
//! Per-node data used in style calculation.
|
//! Per-node data used in style calculation.
|
||||||
|
|
||||||
#![deny(missing_docs)]
|
|
||||||
|
|
||||||
use context::SharedStyleContext;
|
use context::SharedStyleContext;
|
||||||
use dom::TElement;
|
use dom::TElement;
|
||||||
use properties::ComputedValues;
|
use properties::ComputedValues;
|
||||||
use properties::longhands::display::computed_value as display;
|
use properties::longhands::display::computed_value as display;
|
||||||
use restyle_hints::{RestyleReplacements, RestyleHint};
|
use restyle_hints::{HintComputationContext, RestyleReplacements, RestyleHint};
|
||||||
use rule_tree::StrongRuleNode;
|
use rule_tree::StrongRuleNode;
|
||||||
use selector_parser::{EAGER_PSEUDO_COUNT, PseudoElement, RestyleDamage};
|
use selector_parser::{EAGER_PSEUDO_COUNT, PseudoElement, RestyleDamage};
|
||||||
use shared_lock::StylesheetGuards;
|
use shared_lock::StylesheetGuards;
|
||||||
|
@ -372,15 +370,16 @@ impl ElementData {
|
||||||
/// explicit sibling restyle hints from the stored restyle hint.
|
/// explicit sibling restyle hints from the stored restyle hint.
|
||||||
///
|
///
|
||||||
/// Returns true if later siblings must be restyled.
|
/// Returns true if later siblings must be restyled.
|
||||||
pub fn compute_final_hint<E: TElement>(
|
pub fn compute_final_hint<'a, E: TElement>(
|
||||||
&mut self,
|
&mut self,
|
||||||
element: E,
|
element: E,
|
||||||
context: &SharedStyleContext)
|
shared_context: &SharedStyleContext,
|
||||||
|
hint_context: HintComputationContext<'a, E>)
|
||||||
-> bool
|
-> bool
|
||||||
{
|
{
|
||||||
debug!("compute_final_hint: {:?}, {:?}",
|
debug!("compute_final_hint: {:?}, {:?}",
|
||||||
element,
|
element,
|
||||||
context.traversal_flags);
|
shared_context.traversal_flags);
|
||||||
|
|
||||||
let mut hint = match self.get_restyle() {
|
let mut hint = match self.get_restyle() {
|
||||||
Some(r) => r.hint.0.clone(),
|
Some(r) => r.hint.0.clone(),
|
||||||
|
@ -395,7 +394,11 @@ impl ElementData {
|
||||||
element.implemented_pseudo_element());
|
element.implemented_pseudo_element());
|
||||||
|
|
||||||
if element.has_snapshot() && !element.handled_snapshot() {
|
if element.has_snapshot() && !element.handled_snapshot() {
|
||||||
hint.insert(context.stylist.compute_restyle_hint(&element, context.snapshot_map));
|
let snapshot_hint =
|
||||||
|
shared_context.stylist.compute_restyle_hint(&element,
|
||||||
|
shared_context,
|
||||||
|
hint_context);
|
||||||
|
hint.insert(snapshot_hint);
|
||||||
unsafe { element.set_handled_snapshot() }
|
unsafe { element.set_handled_snapshot() }
|
||||||
debug_assert!(element.handled_snapshot());
|
debug_assert!(element.handled_snapshot());
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
use Atom;
|
use Atom;
|
||||||
use LocalName;
|
use LocalName;
|
||||||
use Namespace;
|
use Namespace;
|
||||||
|
use context::{SharedStyleContext, ThreadLocalStyleContext};
|
||||||
use dom::TElement;
|
use dom::TElement;
|
||||||
use element_state::*;
|
use element_state::*;
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
|
@ -825,6 +826,35 @@ pub struct DependencySet {
|
||||||
dependencies: SelectorMap<Dependency>,
|
dependencies: SelectorMap<Dependency>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The data that we need to compute a given restyle hint.
|
||||||
|
pub enum HintComputationContext<'a, E: 'a>
|
||||||
|
where E: TElement,
|
||||||
|
{
|
||||||
|
/// The data we need to compute a restyle hint for the root of the
|
||||||
|
/// traversal.
|
||||||
|
///
|
||||||
|
/// We don't bother with the bloom filter there for multiple reasons:
|
||||||
|
///
|
||||||
|
/// * The root of the traversal uses to be the root of the document, so we
|
||||||
|
/// don't gain much using a bloom filter.
|
||||||
|
///
|
||||||
|
/// * The chances that a non-root-element root of the traversal has a
|
||||||
|
/// snapshot is quite low.
|
||||||
|
Root,
|
||||||
|
|
||||||
|
/// The data we need to compute a restyle hint for a child.
|
||||||
|
///
|
||||||
|
/// This needs a full-blown style context in order to get the selector
|
||||||
|
/// filters up-to-date, and the dom depth in order to insert into the filter
|
||||||
|
/// properly if needed.
|
||||||
|
Child {
|
||||||
|
/// The thread-local context, that holds the bloom filter alive.
|
||||||
|
local_context: &'a mut ThreadLocalStyleContext<E>,
|
||||||
|
/// The dom depth of this element.
|
||||||
|
dom_depth: usize,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl DependencySet {
|
impl DependencySet {
|
||||||
/// Adds a selector to this `DependencySet`.
|
/// Adds a selector to this `DependencySet`.
|
||||||
pub fn note_selector(&mut self, selector: &Selector<SelectorImpl>) {
|
pub fn note_selector(&mut self, selector: &Selector<SelectorImpl>) {
|
||||||
|
@ -921,16 +951,17 @@ impl DependencySet {
|
||||||
|
|
||||||
/// Compute a restyle hint given an element and a snapshot, per the rules
|
/// Compute a restyle hint given an element and a snapshot, per the rules
|
||||||
/// explained in the rest of the documentation.
|
/// explained in the rest of the documentation.
|
||||||
pub fn compute_hint<E>(
|
pub fn compute_hint<'a, E>(
|
||||||
&self,
|
&self,
|
||||||
el: &E,
|
el: &E,
|
||||||
snapshots: &SnapshotMap)
|
shared_context: &SharedStyleContext,
|
||||||
|
hint_context: HintComputationContext<'a, E>)
|
||||||
-> RestyleHint
|
-> RestyleHint
|
||||||
where E: TElement,
|
where E: TElement,
|
||||||
{
|
{
|
||||||
debug_assert!(el.has_snapshot(), "Shouldn't be here!");
|
debug_assert!(el.has_snapshot(), "Shouldn't be here!");
|
||||||
|
let snapshot_el =
|
||||||
let snapshot_el = ElementWrapper::new(el.clone(), snapshots);
|
ElementWrapper::new(*el, shared_context.snapshot_map);
|
||||||
let snapshot =
|
let snapshot =
|
||||||
snapshot_el.snapshot().expect("has_snapshot lied so badly");
|
snapshot_el.snapshot().expect("has_snapshot lied so badly");
|
||||||
|
|
||||||
|
@ -960,8 +991,30 @@ impl DependencySet {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(emilio): A bloom filter here would be neat.
|
let bloom_filter = match hint_context {
|
||||||
let mut matching_context =
|
HintComputationContext::Root => None,
|
||||||
|
HintComputationContext::Child { mut local_context, dom_depth } => {
|
||||||
|
local_context
|
||||||
|
.bloom_filter
|
||||||
|
.insert_parents_recovering(*el, dom_depth);
|
||||||
|
local_context.bloom_filter.assert_complete(*el);
|
||||||
|
Some(local_context.bloom_filter.filter())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut element_matching_context =
|
||||||
|
MatchingContext::new(MatchingMode::Normal, bloom_filter);
|
||||||
|
|
||||||
|
// NOTE(emilio): We can't use the bloom filter for snapshots, given that
|
||||||
|
// arbitrary elements in the parent chain may have mutated their
|
||||||
|
// id's/classes, which means that they won't be in the filter, and as
|
||||||
|
// such we may fast-reject selectors incorrectly.
|
||||||
|
//
|
||||||
|
// We may be able to improve this if we record as we go down the tree
|
||||||
|
// whether any parent had a snapshot, and whether those snapshots were
|
||||||
|
// taken due to an element class/id change, but it's not clear we _need_
|
||||||
|
// it right now.
|
||||||
|
let mut snapshot_matching_context =
|
||||||
MatchingContext::new(MatchingMode::Normal, None);
|
MatchingContext::new(MatchingMode::Normal, None);
|
||||||
|
|
||||||
let lookup_element = if el.implemented_pseudo_element().is_some() {
|
let lookup_element = if el.implemented_pseudo_element().is_some() {
|
||||||
|
@ -989,11 +1042,11 @@ impl DependencySet {
|
||||||
// change its matching behavior here.
|
// change its matching behavior here.
|
||||||
let matched_then =
|
let matched_then =
|
||||||
matches_selector(&dep.selector, &snapshot_el,
|
matches_selector(&dep.selector, &snapshot_el,
|
||||||
&mut matching_context,
|
&mut snapshot_matching_context,
|
||||||
&mut |_, _| {});
|
&mut |_, _| {});
|
||||||
let matches_now =
|
let matches_now =
|
||||||
matches_selector(&dep.selector, el,
|
matches_selector(&dep.selector, el,
|
||||||
&mut matching_context,
|
&mut element_matching_context,
|
||||||
&mut |_, _| {});
|
&mut |_, _| {});
|
||||||
if matched_then != matches_now {
|
if matched_then != matches_now {
|
||||||
hint.insert_from(&dep.hint);
|
hint.insert_from(&dep.hint);
|
||||||
|
|
|
@ -4,11 +4,9 @@
|
||||||
|
|
||||||
//! Selector matching.
|
//! Selector matching.
|
||||||
|
|
||||||
#![deny(missing_docs)]
|
|
||||||
|
|
||||||
use {Atom, LocalName, Namespace};
|
use {Atom, LocalName, Namespace};
|
||||||
use bit_vec::BitVec;
|
use bit_vec::BitVec;
|
||||||
use context::QuirksMode;
|
use context::{QuirksMode, SharedStyleContext};
|
||||||
use data::ComputedStyle;
|
use data::ComputedStyle;
|
||||||
use dom::{AnimationRules, TElement};
|
use dom::{AnimationRules, TElement};
|
||||||
use element_state::ElementState;
|
use element_state::ElementState;
|
||||||
|
@ -23,9 +21,9 @@ use properties::{self, CascadeFlags, ComputedValues};
|
||||||
#[cfg(feature = "servo")]
|
#[cfg(feature = "servo")]
|
||||||
use properties::INHERIT_ALL;
|
use properties::INHERIT_ALL;
|
||||||
use properties::PropertyDeclarationBlock;
|
use properties::PropertyDeclarationBlock;
|
||||||
use restyle_hints::{RestyleHint, DependencySet};
|
use restyle_hints::{HintComputationContext, DependencySet, RestyleHint};
|
||||||
use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode, StyleSource};
|
use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode, StyleSource};
|
||||||
use selector_parser::{SelectorImpl, PseudoElement, SnapshotMap};
|
use selector_parser::{SelectorImpl, PseudoElement};
|
||||||
use selectors::attr::NamespaceConstraint;
|
use selectors::attr::NamespaceConstraint;
|
||||||
use selectors::bloom::BloomFilter;
|
use selectors::bloom::BloomFilter;
|
||||||
use selectors::matching::{AFFECTED_BY_STYLE_ATTRIBUTE, AFFECTED_BY_PRESENTATIONAL_HINTS};
|
use selectors::matching::{AFFECTED_BY_STYLE_ATTRIBUTE, AFFECTED_BY_PRESENTATIONAL_HINTS};
|
||||||
|
@ -1029,13 +1027,14 @@ impl Stylist {
|
||||||
/// Given an element, and a snapshot table that represents a previous state
|
/// Given an element, and a snapshot table that represents a previous state
|
||||||
/// of the tree, compute the appropriate restyle hint, that is, the kind of
|
/// of the tree, compute the appropriate restyle hint, that is, the kind of
|
||||||
/// restyle we need to do.
|
/// restyle we need to do.
|
||||||
pub fn compute_restyle_hint<E>(&self,
|
pub fn compute_restyle_hint<'a, E>(&self,
|
||||||
element: &E,
|
element: &E,
|
||||||
snapshots: &SnapshotMap)
|
shared_context: &SharedStyleContext,
|
||||||
-> RestyleHint
|
context: HintComputationContext<'a, E>)
|
||||||
|
-> RestyleHint
|
||||||
where E: TElement,
|
where E: TElement,
|
||||||
{
|
{
|
||||||
self.dependencies.compute_hint(element, snapshots)
|
self.dependencies.compute_hint(element, shared_context, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes styles for a given declaration with parent_style.
|
/// Computes styles for a given declaration with parent_style.
|
||||||
|
|
|
@ -9,7 +9,7 @@ use context::{SharedStyleContext, StyleContext, ThreadLocalStyleContext};
|
||||||
use data::{ElementData, ElementStyles, StoredRestyleHint};
|
use data::{ElementData, ElementStyles, StoredRestyleHint};
|
||||||
use dom::{DirtyDescendants, NodeInfo, OpaqueNode, TElement, TNode};
|
use dom::{DirtyDescendants, NodeInfo, OpaqueNode, TElement, TNode};
|
||||||
use matching::{ChildCascadeRequirement, MatchMethods, StyleSharingBehavior};
|
use matching::{ChildCascadeRequirement, MatchMethods, StyleSharingBehavior};
|
||||||
use restyle_hints::RestyleHint;
|
use restyle_hints::{HintComputationContext, RestyleHint};
|
||||||
use selector_parser::RestyleDamage;
|
use selector_parser::RestyleDamage;
|
||||||
#[cfg(feature = "servo")] use servo_config::opts;
|
#[cfg(feature = "servo")] use servo_config::opts;
|
||||||
use std::borrow::BorrowMut;
|
use std::borrow::BorrowMut;
|
||||||
|
@ -123,7 +123,8 @@ pub trait DomTraversal<E: TElement> : Sync {
|
||||||
type ThreadLocalContext: Send + BorrowMut<ThreadLocalStyleContext<E>>;
|
type ThreadLocalContext: Send + BorrowMut<ThreadLocalStyleContext<E>>;
|
||||||
|
|
||||||
/// Process `node` on the way down, before its children have been processed.
|
/// Process `node` on the way down, before its children have been processed.
|
||||||
fn process_preorder(&self, data: &PerLevelTraversalData,
|
fn process_preorder(&self,
|
||||||
|
data: &PerLevelTraversalData,
|
||||||
thread_local: &mut Self::ThreadLocalContext,
|
thread_local: &mut Self::ThreadLocalContext,
|
||||||
node: E::ConcreteNode);
|
node: E::ConcreteNode);
|
||||||
|
|
||||||
|
@ -227,7 +228,10 @@ pub trait DomTraversal<E: TElement> : Sync {
|
||||||
// Expanding snapshots here may create a LATER_SIBLINGS restyle hint, which
|
// Expanding snapshots here may create a LATER_SIBLINGS restyle hint, which
|
||||||
// we propagate to the next sibling element.
|
// we propagate to the next sibling element.
|
||||||
if let Some(mut data) = root.mutate_data() {
|
if let Some(mut data) = root.mutate_data() {
|
||||||
let later_siblings = data.compute_final_hint(root, shared_context);
|
let later_siblings =
|
||||||
|
data.compute_final_hint(root,
|
||||||
|
shared_context,
|
||||||
|
HintComputationContext::Root);
|
||||||
if later_siblings {
|
if later_siblings {
|
||||||
if let Some(next) = root.next_sibling_element() {
|
if let Some(next) = root.next_sibling_element() {
|
||||||
if let Some(mut next_data) = next.mutate_data() {
|
if let Some(mut next_data) = next.mutate_data() {
|
||||||
|
@ -669,11 +673,12 @@ pub fn recalc_style_at<E, D>(traversal: &D,
|
||||||
r.damage_handled() | r.damage.handled_for_descendants()
|
r.damage_handled() | r.damage.handled_for_descendants()
|
||||||
});
|
});
|
||||||
|
|
||||||
preprocess_children(traversal,
|
preprocess_children::<E, D>(context,
|
||||||
element,
|
traversal_data,
|
||||||
propagated_hint,
|
element,
|
||||||
damage_handled,
|
propagated_hint,
|
||||||
inherited_style_changed);
|
damage_handled,
|
||||||
|
inherited_style_changed);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we are in a restyle for reconstruction, drop the existing restyle
|
// If we are in a restyle for reconstruction, drop the existing restyle
|
||||||
|
@ -740,7 +745,8 @@ fn compute_style<E, D>(_traversal: &D,
|
||||||
MatchAndCascade => {
|
MatchAndCascade => {
|
||||||
// Ensure the bloom filter is up to date.
|
// Ensure the bloom filter is up to date.
|
||||||
context.thread_local.bloom_filter
|
context.thread_local.bloom_filter
|
||||||
.insert_parents_recovering(element, traversal_data.current_dom_depth);
|
.insert_parents_recovering(element,
|
||||||
|
traversal_data.current_dom_depth);
|
||||||
|
|
||||||
context.thread_local.bloom_filter.assert_complete(element);
|
context.thread_local.bloom_filter.assert_complete(element);
|
||||||
context.thread_local.statistics.elements_matched += 1;
|
context.thread_local.statistics.elements_matched += 1;
|
||||||
|
@ -770,15 +776,17 @@ fn compute_style<E, D>(_traversal: &D,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn preprocess_children<E, D>(traversal: &D,
|
fn preprocess_children<E, D>(context: &mut StyleContext<E>,
|
||||||
|
parent_traversal_data: &PerLevelTraversalData,
|
||||||
element: E,
|
element: E,
|
||||||
mut propagated_hint: StoredRestyleHint,
|
mut propagated_hint: StoredRestyleHint,
|
||||||
damage_handled: RestyleDamage,
|
damage_handled: RestyleDamage,
|
||||||
parent_inherited_style_changed: bool)
|
parent_inherited_style_changed: bool)
|
||||||
where E: TElement,
|
where E: TElement,
|
||||||
D: DomTraversal<E>
|
D: DomTraversal<E>,
|
||||||
{
|
{
|
||||||
trace!("preprocess_children: {:?}", element);
|
trace!("preprocess_children: {:?}", element);
|
||||||
|
|
||||||
// Loop over all the children.
|
// Loop over all the children.
|
||||||
for child in element.as_node().children() {
|
for child in element.as_node().children() {
|
||||||
// FIXME(bholley): Add TElement::element_children instead of this.
|
// FIXME(bholley): Add TElement::element_children instead of this.
|
||||||
|
@ -787,7 +795,8 @@ fn preprocess_children<E, D>(traversal: &D,
|
||||||
None => continue,
|
None => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut child_data = unsafe { D::ensure_element_data(&child).borrow_mut() };
|
let mut child_data =
|
||||||
|
unsafe { D::ensure_element_data(&child).borrow_mut() };
|
||||||
|
|
||||||
// If the child is unstyled, we don't need to set up any restyling.
|
// If the child is unstyled, we don't need to set up any restyling.
|
||||||
if !child_data.has_styles() {
|
if !child_data.has_styles() {
|
||||||
|
@ -798,7 +807,12 @@ fn preprocess_children<E, D>(traversal: &D,
|
||||||
//
|
//
|
||||||
// NB: This will be a no-op if there's no restyle data and no snapshot.
|
// NB: This will be a no-op if there's no restyle data and no snapshot.
|
||||||
let later_siblings =
|
let later_siblings =
|
||||||
child_data.compute_final_hint(child, traversal.shared_context());
|
child_data.compute_final_hint(child,
|
||||||
|
&context.shared,
|
||||||
|
HintComputationContext::Child {
|
||||||
|
local_context: &mut context.thread_local,
|
||||||
|
dom_depth: parent_traversal_data.current_dom_depth + 1,
|
||||||
|
});
|
||||||
|
|
||||||
trace!(" > {:?} -> {:?} + {:?}, pseudo: {:?}, later_siblings: {:?}",
|
trace!(" > {:?} -> {:?} + {:?}, pseudo: {:?}, later_siblings: {:?}",
|
||||||
child,
|
child,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue