mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 05:41:12 +00:00
servo: Merge #17741 - Trim some fat from the traversal (from bholley:trim_traversal_fat); r=emilio
https://bugzilla.mozilla.org/show_bug.cgi?id=1380877 Source-Repo: https://github.com/servo/servo Source-Revision: 17f99e2a7bb0d7eec167eac952f12a7feafd647d --HG-- extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear extra : subtree_revision : 4e5e24b864f4a1f80808930aa629917de1b44c8d
This commit is contained in:
parent
13b843b6ce
commit
497e5345cc
@ -12,6 +12,7 @@ use flow::{CAN_BE_FRAGMENTED, Flow, ImmutableFlowUtils, PostorderFlowTraversal};
|
||||
use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutNode};
|
||||
use servo_config::opts;
|
||||
use style::context::{SharedStyleContext, StyleContext};
|
||||
use style::data::ElementData;
|
||||
use style::dom::{NodeInfo, TElement, TNode};
|
||||
use style::selector_parser::RestyleDamage;
|
||||
use style::servo::restyle_damage::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW, REPAINT};
|
||||
@ -53,8 +54,11 @@ impl<'a, E> DomTraversal<E> for RecalcStyleAndConstructFlows<'a>
|
||||
E::ConcreteNode: LayoutNode,
|
||||
E::FontMetricsProvider: Send,
|
||||
{
|
||||
fn process_preorder(&self, traversal_data: &PerLevelTraversalData,
|
||||
context: &mut StyleContext<E>, node: E::ConcreteNode) {
|
||||
fn process_preorder<F>(&self, traversal_data: &PerLevelTraversalData,
|
||||
context: &mut StyleContext<E>, node: E::ConcreteNode,
|
||||
note_child: F)
|
||||
where F: FnMut(E::ConcreteNode)
|
||||
{
|
||||
// FIXME(pcwalton): Stop allocating here. Ideally this should just be
|
||||
// done by the HTML parser.
|
||||
unsafe { node.initialize_data() };
|
||||
@ -62,7 +66,7 @@ impl<'a, E> DomTraversal<E> for RecalcStyleAndConstructFlows<'a>
|
||||
if !node.is_text_node() {
|
||||
let el = node.as_element().unwrap();
|
||||
let mut data = el.mutate_data().unwrap();
|
||||
recalc_style_at(self, traversal_data, context, el, &mut data);
|
||||
recalc_style_at(self, traversal_data, context, el, &mut data, note_child);
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,13 +74,13 @@ impl<'a, E> DomTraversal<E> for RecalcStyleAndConstructFlows<'a>
|
||||
construct_flows_at(&self.context, node);
|
||||
}
|
||||
|
||||
fn text_node_needs_traversal(node: E::ConcreteNode) -> bool {
|
||||
fn text_node_needs_traversal(node: E::ConcreteNode, parent_data: &ElementData) -> bool {
|
||||
// Text nodes never need styling. However, there are two cases they may need
|
||||
// flow construction:
|
||||
// (1) They child doesn't yet have layout data (preorder traversal initializes it).
|
||||
// (2) The parent element has restyle damage (so the text flow also needs fixup).
|
||||
node.get_raw_data().is_none() ||
|
||||
node.parent_node().unwrap().to_threadsafe().restyle_damage() != RestyleDamage::empty()
|
||||
parent_data.restyle.damage != RestyleDamage::empty()
|
||||
}
|
||||
|
||||
fn shared_context(&self) -> &SharedStyleContext {
|
||||
|
@ -70,11 +70,16 @@ impl RestyleData {
|
||||
/// Returns whether this element or any ancestor is going to be
|
||||
/// reconstructed.
|
||||
pub fn reconstructed_self_or_ancestor(&self) -> bool {
|
||||
self.reconstructed_ancestor() ||
|
||||
self.reconstructed_ancestor() || self.reconstructed_self()
|
||||
}
|
||||
|
||||
/// Returns whether this element is going to be reconstructed.
|
||||
pub fn reconstructed_self(&self) -> bool {
|
||||
self.damage.contains(RestyleDamage::reconstruct())
|
||||
}
|
||||
|
||||
/// Returns whether any ancestor of this element was restyled.
|
||||
/// Returns whether any ancestor of this element is going to be
|
||||
/// reconstructed.
|
||||
fn reconstructed_ancestor(&self) -> bool {
|
||||
self.flags.contains(ANCESTOR_WAS_RECONSTRUCTED)
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
//! Gecko-specific bits for the styling DOM traversal.
|
||||
|
||||
use context::{SharedStyleContext, StyleContext};
|
||||
use dom::{NodeInfo, TNode, TElement};
|
||||
use dom::{TNode, TElement};
|
||||
use gecko::wrapper::{GeckoElement, GeckoNode};
|
||||
use traversal::{DomTraversal, PerLevelTraversalData, TraversalDriver, recalc_style_at};
|
||||
|
||||
@ -27,15 +27,16 @@ impl<'a> RecalcStyleOnly<'a> {
|
||||
}
|
||||
|
||||
impl<'recalc, 'le> DomTraversal<GeckoElement<'le>> for RecalcStyleOnly<'recalc> {
|
||||
fn process_preorder(&self,
|
||||
traversal_data: &PerLevelTraversalData,
|
||||
context: &mut StyleContext<GeckoElement<'le>>,
|
||||
node: GeckoNode<'le>)
|
||||
fn process_preorder<F>(&self,
|
||||
traversal_data: &PerLevelTraversalData,
|
||||
context: &mut StyleContext<GeckoElement<'le>>,
|
||||
node: GeckoNode<'le>,
|
||||
note_child: F)
|
||||
where F: FnMut(GeckoNode<'le>),
|
||||
{
|
||||
if node.is_element() {
|
||||
let el = node.as_element().unwrap();
|
||||
if let Some(el) = node.as_element() {
|
||||
let mut data = unsafe { el.ensure_data() };
|
||||
recalc_style_at(self, traversal_data, context, el, &mut data);
|
||||
recalc_style_at(self, traversal_data, context, el, &mut data, note_child);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,6 @@ use rayon;
|
||||
use scoped_tls::ScopedTLS;
|
||||
use smallvec::SmallVec;
|
||||
use std::borrow::Borrow;
|
||||
use std::mem;
|
||||
use time;
|
||||
use traversal::{DomTraversal, PerLevelTraversalData, PreTraverseToken};
|
||||
|
||||
@ -187,10 +186,9 @@ fn top_down_dom<'a, 'scope, E, D>(nodes: &'a [SendNode<E::ConcreteNode>],
|
||||
//
|
||||
// Which are not at all uncommon.
|
||||
if !discovered_child_nodes.is_empty() {
|
||||
let children = mem::replace(&mut discovered_child_nodes, Default::default());
|
||||
let mut traversal_data_copy = traversal_data.clone();
|
||||
traversal_data_copy.current_dom_depth += 1;
|
||||
traverse_nodes(&*children,
|
||||
traverse_nodes(&*discovered_child_nodes,
|
||||
DispatchMode::NotTailCall,
|
||||
recursion_depth,
|
||||
root,
|
||||
@ -199,17 +197,16 @@ fn top_down_dom<'a, 'scope, E, D>(nodes: &'a [SendNode<E::ConcreteNode>],
|
||||
pool,
|
||||
traversal,
|
||||
tls);
|
||||
discovered_child_nodes.clear();
|
||||
}
|
||||
|
||||
let node = **n;
|
||||
let mut children_to_process = 0isize;
|
||||
traversal.process_preorder(&traversal_data, &mut context, node);
|
||||
if let Some(el) = node.as_element() {
|
||||
traversal.traverse_children(&mut context, el, |_context, kid| {
|
||||
children_to_process += 1;
|
||||
discovered_child_nodes.push(unsafe { SendNode::new(kid) })
|
||||
});
|
||||
}
|
||||
traversal.process_preorder(&traversal_data, &mut context, node, |n| {
|
||||
children_to_process += 1;
|
||||
let send_n = unsafe { SendNode::new(n) };
|
||||
discovered_child_nodes.push(send_n);
|
||||
});
|
||||
|
||||
traversal.handle_postorder_traversal(&mut context, root, node,
|
||||
children_to_process);
|
||||
|
@ -52,14 +52,10 @@ pub fn traverse_dom<E, D>(traversal: &D,
|
||||
while let Some(WorkItem(node, depth)) = discovered.pop_front() {
|
||||
let mut children_to_process = 0isize;
|
||||
let traversal_data = PerLevelTraversalData { current_dom_depth: depth };
|
||||
traversal.process_preorder(&traversal_data, &mut context, node);
|
||||
|
||||
if let Some(el) = node.as_element() {
|
||||
traversal.traverse_children(&mut context, el, |_context, kid| {
|
||||
children_to_process += 1;
|
||||
discovered.push_back(WorkItem(kid, depth + 1))
|
||||
});
|
||||
}
|
||||
traversal.process_preorder(&traversal_data, &mut context, node, |n| {
|
||||
children_to_process += 1;
|
||||
discovered.push_back(WorkItem(n, depth + 1));
|
||||
});
|
||||
|
||||
traversal.handle_postorder_traversal(&mut context, root.as_node().opaque(),
|
||||
node, children_to_process);
|
||||
|
@ -85,20 +85,6 @@ impl PreTraverseToken {
|
||||
}
|
||||
}
|
||||
|
||||
/// Enum to prevent duplicate logging.
|
||||
pub enum LogBehavior {
|
||||
/// We should log.
|
||||
MayLog,
|
||||
/// We shouldn't log.
|
||||
DontLog,
|
||||
}
|
||||
use self::LogBehavior::*;
|
||||
impl LogBehavior {
|
||||
fn allow(&self) -> bool {
|
||||
matches!(*self, MayLog)
|
||||
}
|
||||
}
|
||||
|
||||
/// The kind of traversals we could perform.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum TraversalDriver {
|
||||
@ -132,10 +118,15 @@ fn is_servo_nonincremental_layout() -> bool {
|
||||
/// Gecko and Servo.
|
||||
pub trait DomTraversal<E: TElement> : Sync {
|
||||
/// Process `node` on the way down, before its children have been processed.
|
||||
fn process_preorder(&self,
|
||||
data: &PerLevelTraversalData,
|
||||
context: &mut StyleContext<E>,
|
||||
node: E::ConcreteNode);
|
||||
///
|
||||
/// The callback is invoked for each child node that should be processed by
|
||||
/// the traversal.
|
||||
fn process_preorder<F>(&self,
|
||||
data: &PerLevelTraversalData,
|
||||
context: &mut StyleContext<E>,
|
||||
node: E::ConcreteNode,
|
||||
note_child: F)
|
||||
where F: FnMut(E::ConcreteNode);
|
||||
|
||||
/// Process `node` on the way up, after its children have been processed.
|
||||
///
|
||||
@ -235,15 +226,28 @@ pub trait DomTraversal<E: TElement> : Sync {
|
||||
};
|
||||
}
|
||||
|
||||
// Look at whether there has been any attribute or state change, and
|
||||
// invalidate our style, and the one of our siblings and descendants as
|
||||
// needed.
|
||||
if let Some(mut data) = root.mutate_data() {
|
||||
let flags = shared_context.traversal_flags;
|
||||
let data = root.mutate_data();
|
||||
let should_traverse = if data.as_ref().map_or(true, |d| !d.has_styles()) {
|
||||
!flags.for_animation_only()
|
||||
} else {
|
||||
let mut data = data.unwrap();
|
||||
// Look at whether there has been any attribute or state change, and
|
||||
// invalidate our style, and the one of our siblings and descendants as
|
||||
// needed.
|
||||
data.invalidate_style_if_needed(root, shared_context);
|
||||
}
|
||||
|
||||
let parent = root.traversal_parent();
|
||||
let parent_data = match parent {
|
||||
None => None,
|
||||
Some(ref x) => Some(x.borrow_data().unwrap())
|
||||
};
|
||||
let parent_data_borrow = parent_data.as_ref().map(|x| &**x);
|
||||
Self::element_needs_traversal(root, flags, &*data, parent_data_borrow)
|
||||
};
|
||||
|
||||
PreTraverseToken {
|
||||
traverse: Self::node_needs_traversal(root.as_node(), traversal_flags),
|
||||
traverse: should_traverse,
|
||||
unstyled_children_only: false,
|
||||
}
|
||||
}
|
||||
@ -251,16 +255,23 @@ pub trait DomTraversal<E: TElement> : Sync {
|
||||
/// Returns true if traversal should visit a text node. The style system
|
||||
/// never processes text nodes, but Servo overrides this to visit them for
|
||||
/// flow construction when necessary.
|
||||
fn text_node_needs_traversal(node: E::ConcreteNode) -> bool {
|
||||
fn text_node_needs_traversal(node: E::ConcreteNode, _parent_data: &ElementData) -> bool {
|
||||
debug_assert!(node.is_text_node());
|
||||
false
|
||||
}
|
||||
|
||||
/// Returns true if traversal is needed for the given node and subtree.
|
||||
fn node_needs_traversal(
|
||||
node: E::ConcreteNode,
|
||||
traversal_flags: TraversalFlags
|
||||
/// Returns true if traversal is needed for the given element and subtree.
|
||||
///
|
||||
/// The caller passes |parent_data|, which is only null if there is no
|
||||
/// parent.
|
||||
fn element_needs_traversal(
|
||||
el: E,
|
||||
traversal_flags: TraversalFlags,
|
||||
data: &ElementData,
|
||||
parent_data: Option<&ElementData>,
|
||||
) -> bool {
|
||||
debug_assert!(data.has_styles(), "Caller should check this");
|
||||
|
||||
// Non-incremental layout visits every node.
|
||||
if is_servo_nonincremental_layout() {
|
||||
return true;
|
||||
@ -270,11 +281,6 @@ pub trait DomTraversal<E: TElement> : Sync {
|
||||
return true;
|
||||
}
|
||||
|
||||
let el = match node.as_element() {
|
||||
None => return Self::text_node_needs_traversal(node),
|
||||
Some(el) => el,
|
||||
};
|
||||
|
||||
// If the element is native-anonymous and an ancestor frame will be
|
||||
// reconstructed, the child and all its descendants will be destroyed.
|
||||
// In that case, we wouldn't need to traverse the subtree...
|
||||
@ -289,8 +295,7 @@ pub trait DomTraversal<E: TElement> : Sync {
|
||||
// But it may be that we no longer match, so detect that case and act
|
||||
// appropriately here.
|
||||
if el.is_native_anonymous() {
|
||||
if let Some(parent) = el.traversal_parent() {
|
||||
let parent_data = parent.borrow_data().unwrap();
|
||||
if let Some(parent_data) = parent_data {
|
||||
let going_to_reframe =
|
||||
parent_data.restyle.reconstructed_self_or_ancestor();
|
||||
|
||||
@ -322,22 +327,8 @@ pub trait DomTraversal<E: TElement> : Sync {
|
||||
// if the element has animation only dirty descendants bit,
|
||||
// animation-only restyle hint or recascade.
|
||||
if traversal_flags.for_animation_only() {
|
||||
// Skip elements that have no style data since animation-only
|
||||
// restyle is not necessary for the elements.
|
||||
let data = match el.borrow_data() {
|
||||
Some(d) => d,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
if !data.has_styles() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if el.has_animation_only_dirty_descendants() {
|
||||
return true;
|
||||
}
|
||||
|
||||
return data.restyle.hint.has_animation_hint() ||
|
||||
return el.has_animation_only_dirty_descendants() ||
|
||||
data.restyle.hint.has_animation_hint() ||
|
||||
data.restyle.hint.has_recascade_self();
|
||||
}
|
||||
|
||||
@ -347,18 +338,6 @@ pub trait DomTraversal<E: TElement> : Sync {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check the element data. If it doesn't exist, we need to visit the
|
||||
// element.
|
||||
let data = match el.borrow_data() {
|
||||
Some(d) => d,
|
||||
None => return true,
|
||||
};
|
||||
|
||||
// If we don't have any style data, we need to visit the element.
|
||||
if !data.has_styles() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we have a restyle hint or need to recascade, we need to visit the
|
||||
// element.
|
||||
//
|
||||
@ -385,17 +364,12 @@ pub trait DomTraversal<E: TElement> : Sync {
|
||||
false
|
||||
}
|
||||
|
||||
/// Returns true if traversal of this element's children is allowed. We use
|
||||
/// this to cull traversal of various subtrees.
|
||||
///
|
||||
/// This may be called multiple times when processing an element, so we pass
|
||||
/// a parameter to keep the logs tidy.
|
||||
fn should_traverse_children(
|
||||
/// Returns true if we want to cull this subtree from the travesal.
|
||||
fn should_cull_subtree(
|
||||
&self,
|
||||
context: &mut StyleContext<E>,
|
||||
parent: E,
|
||||
parent_data: &ElementData,
|
||||
log: LogBehavior
|
||||
) -> bool {
|
||||
// See the comment on `cascade_node` for why we allow this on Gecko.
|
||||
debug_assert!(cfg!(feature = "gecko") ||
|
||||
@ -403,11 +377,8 @@ pub trait DomTraversal<E: TElement> : Sync {
|
||||
|
||||
// If the parent computed display:none, we don't style the subtree.
|
||||
if parent_data.styles.is_display_none() {
|
||||
if log.allow() {
|
||||
debug!("Parent {:?} is display:none, culling traversal",
|
||||
parent);
|
||||
}
|
||||
return false;
|
||||
debug!("Parent {:?} is display:none, culling traversal", parent);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Gecko-only XBL handling.
|
||||
@ -431,61 +402,13 @@ pub trait DomTraversal<E: TElement> : Sync {
|
||||
// recursively drops Servo ElementData when the XBL insertion parent of
|
||||
// an Element is changed.
|
||||
if cfg!(feature = "gecko") && context.thread_local.is_initial_style() &&
|
||||
parent_data.styles.primary().has_moz_binding() {
|
||||
if log.allow() {
|
||||
debug!("Parent {:?} has XBL binding, deferring traversal",
|
||||
parent);
|
||||
}
|
||||
return false;
|
||||
parent_data.styles.primary().has_moz_binding()
|
||||
{
|
||||
debug!("Parent {:?} has XBL binding, deferring traversal", parent);
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Helper for the traversal implementations to select the children that
|
||||
/// should be enqueued for processing.
|
||||
fn traverse_children<F>(
|
||||
&self,
|
||||
context: &mut StyleContext<E>,
|
||||
parent: E,
|
||||
mut f: F
|
||||
)
|
||||
where
|
||||
F: FnMut(&mut StyleContext<E>, E::ConcreteNode)
|
||||
{
|
||||
// Check if we're allowed to traverse past this element.
|
||||
let should_traverse =
|
||||
self.should_traverse_children(
|
||||
context,
|
||||
parent,
|
||||
&parent.borrow_data().unwrap(),
|
||||
MayLog
|
||||
);
|
||||
|
||||
context.thread_local.end_element(parent);
|
||||
if !should_traverse {
|
||||
return;
|
||||
}
|
||||
|
||||
for kid in parent.as_node().traversal_children() {
|
||||
if Self::node_needs_traversal(kid, self.shared_context().traversal_flags) {
|
||||
// If we are in a restyle for reconstruction, there is no need to
|
||||
// perform a post-traversal, so we don't need to set the dirty
|
||||
// descendants bit on the parent.
|
||||
if !self.shared_context().traversal_flags.for_reconstruct() {
|
||||
let el = kid.as_element();
|
||||
if el.as_ref().and_then(|el| el.borrow_data())
|
||||
.map_or(false, |d| d.has_styles()) {
|
||||
if self.shared_context().traversal_flags.for_animation_only() {
|
||||
unsafe { parent.set_animation_only_dirty_descendants(); }
|
||||
} else {
|
||||
unsafe { parent.set_dirty_descendants(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
f(context, kid);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Return the shared style context common to all worker threads.
|
||||
@ -586,16 +509,18 @@ where
|
||||
/// Calculates the style for a single node.
|
||||
#[inline]
|
||||
#[allow(unsafe_code)]
|
||||
pub fn recalc_style_at<E, D>(
|
||||
pub fn recalc_style_at<E, D, F>(
|
||||
traversal: &D,
|
||||
traversal_data: &PerLevelTraversalData,
|
||||
context: &mut StyleContext<E>,
|
||||
element: E,
|
||||
data: &mut ElementData
|
||||
data: &mut ElementData,
|
||||
note_child: F,
|
||||
)
|
||||
where
|
||||
E: TElement,
|
||||
D: DomTraversal<E>,
|
||||
F: FnMut(E::ConcreteNode),
|
||||
{
|
||||
context.thread_local.begin_element(element, data);
|
||||
context.thread_local.statistics.elements_traversed += 1;
|
||||
@ -660,32 +585,52 @@ where
|
||||
"Should have computed style or haven't yet valid computed \
|
||||
style in case of animation-only restyle");
|
||||
|
||||
let flags = context.shared.traversal_flags;
|
||||
let has_dirty_descendants_for_this_restyle =
|
||||
if context.shared.traversal_flags.for_animation_only() {
|
||||
if flags.for_animation_only() {
|
||||
element.has_animation_only_dirty_descendants()
|
||||
} else {
|
||||
element.has_dirty_descendants()
|
||||
};
|
||||
if flags.for_animation_only() {
|
||||
unsafe { element.unset_animation_only_dirty_descendants(); }
|
||||
}
|
||||
|
||||
// Preprocess children, propagating restyle hints and handling sibling
|
||||
// relationships.
|
||||
let should_traverse_children = traversal.should_traverse_children(
|
||||
context,
|
||||
element,
|
||||
&data,
|
||||
DontLog
|
||||
);
|
||||
if should_traverse_children &&
|
||||
(has_dirty_descendants_for_this_restyle || !propagated_hint.is_empty()) {
|
||||
let reconstructed_ancestor =
|
||||
data.restyle.reconstructed_self_or_ancestor();
|
||||
// Before examining each child individually, try to prove that our children
|
||||
// don't need style processing. They need processing if any of the following
|
||||
// conditions hold:
|
||||
// * We have the dirty descendants bit.
|
||||
// * We're propagating a hint.
|
||||
// * This is the initial style.
|
||||
// * We generated a reconstruct hint on self (which could mean that we
|
||||
// switched from display:none to something else, which means the children
|
||||
// need initial styling).
|
||||
// * This is a reconstruct traversal.
|
||||
// * This is a servo non-incremental traversal.
|
||||
//
|
||||
// Additionally, there are a few scenarios where we avoid traversing the
|
||||
// subtree even if descendant styles are out of date. These cases are
|
||||
// enumerated in should_cull_subtree().
|
||||
let mut traverse_children = has_dirty_descendants_for_this_restyle ||
|
||||
!propagated_hint.is_empty() ||
|
||||
context.thread_local.is_initial_style() ||
|
||||
data.restyle.reconstructed_self() ||
|
||||
flags.for_reconstruct() ||
|
||||
is_servo_nonincremental_layout();
|
||||
|
||||
preprocess_children::<E>(
|
||||
traverse_children = traverse_children &&
|
||||
!traversal.should_cull_subtree(context, element, &data);
|
||||
|
||||
// Examine our children, and enqueue the appropriate ones for traversal.
|
||||
if traverse_children {
|
||||
note_children::<E, D, F>(
|
||||
context,
|
||||
element,
|
||||
data,
|
||||
propagated_hint,
|
||||
reconstructed_ancestor,
|
||||
)
|
||||
data.restyle.reconstructed_self_or_ancestor(),
|
||||
note_child
|
||||
);
|
||||
}
|
||||
|
||||
// If we are in a restyle for reconstruction, drop the existing restyle
|
||||
@ -695,10 +640,6 @@ where
|
||||
data.clear_restyle_state();
|
||||
}
|
||||
|
||||
if context.shared.traversal_flags.for_animation_only() {
|
||||
unsafe { element.unset_animation_only_dirty_descendants(); }
|
||||
}
|
||||
|
||||
// There are two cases when we want to clear the dity descendants bit here
|
||||
// after styling this element.
|
||||
//
|
||||
@ -718,6 +659,8 @@ where
|
||||
context.shared.traversal_flags.for_reconstruct() {
|
||||
unsafe { element.unset_dirty_descendants(); }
|
||||
}
|
||||
|
||||
context.thread_local.end_element(element);
|
||||
}
|
||||
|
||||
fn compute_style<E>(
|
||||
@ -813,31 +756,43 @@ where
|
||||
)
|
||||
}
|
||||
|
||||
fn preprocess_children<E>(
|
||||
fn note_children<E, D, F>(
|
||||
context: &mut StyleContext<E>,
|
||||
element: E,
|
||||
data: &ElementData,
|
||||
propagated_hint: RestyleHint,
|
||||
reconstructed_ancestor: bool,
|
||||
mut note_child: F,
|
||||
)
|
||||
where
|
||||
E: TElement,
|
||||
D: DomTraversal<E>,
|
||||
F: FnMut(E::ConcreteNode),
|
||||
{
|
||||
trace!("preprocess_children: {:?}", element);
|
||||
trace!("note_children: {:?}", element);
|
||||
let flags = context.shared.traversal_flags;
|
||||
|
||||
// Loop over all the traversal children.
|
||||
for child in element.as_node().traversal_children() {
|
||||
// FIXME(bholley): Add TElement::element_children instead of this.
|
||||
let child = match child.as_element() {
|
||||
for child_node in element.as_node().traversal_children() {
|
||||
let child = match child_node.as_element() {
|
||||
Some(el) => el,
|
||||
None => continue,
|
||||
None => {
|
||||
if is_servo_nonincremental_layout() ||
|
||||
D::text_node_needs_traversal(child_node, data) {
|
||||
note_child(child_node);
|
||||
}
|
||||
continue;
|
||||
},
|
||||
};
|
||||
|
||||
// If the child is unstyled, we don't need to set up any restyling.
|
||||
if child.borrow_data().map_or(true, |d| !d.has_styles()) {
|
||||
let child_data = child.mutate_data();
|
||||
if child_data.as_ref().map_or(true, |d| !d.has_styles()) {
|
||||
if !flags.for_animation_only() {
|
||||
note_child(child_node);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut child_data = unsafe { child.ensure_data() };
|
||||
let mut child_data = child_data.unwrap();
|
||||
|
||||
trace!(" > {:?} -> {:?} + {:?}, pseudo: {:?}",
|
||||
child,
|
||||
@ -857,6 +812,24 @@ where
|
||||
//
|
||||
// NB: This will be a no-op if there's no snapshot.
|
||||
child_data.invalidate_style_if_needed(child, &context.shared);
|
||||
|
||||
if D::element_needs_traversal(child, flags, &*child_data, Some(data)) {
|
||||
note_child(child_node);
|
||||
|
||||
// Set the dirty descendants bit on the parent as needed, so that we
|
||||
// can find elements during the post-traversal.
|
||||
//
|
||||
// If we are in a restyle for reconstruction, there is no need to
|
||||
// perform a post-traversal, so we don't need to set the dirty
|
||||
// descendants bit on the parent.
|
||||
if !context.shared.traversal_flags.for_reconstruct() {
|
||||
if context.shared.traversal_flags.for_animation_only() {
|
||||
unsafe { element.set_animation_only_dirty_descendants(); }
|
||||
} else {
|
||||
unsafe { element.set_dirty_descendants(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user