From 947134949adc908d6a0414f423914025355ad931 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Wed, 30 Dec 2015 17:02:38 -0800 Subject: [PATCH] Refactor parallel dom traversal to be agnostic to the processing steps themselves. --- components/layout/layout_task.rs | 7 +- components/layout/parallel.rs | 231 +++++++++------------- components/layout/sequential.rs | 32 ++-- components/layout/traversal.rs | 319 +++++++++++++++---------------- 4 files changed, 262 insertions(+), 327 deletions(-) diff --git a/components/layout/layout_task.rs b/components/layout/layout_task.rs index 88425fde26b..0015cd3fc33 100644 --- a/components/layout/layout_task.rs +++ b/components/layout/layout_task.rs @@ -69,6 +69,7 @@ use style::dom::{TDocument, TElement, TNode}; use style::media_queries::{Device, MediaType}; use style::selector_matching::{Stylist, USER_OR_USER_AGENT_STYLESHEETS}; use style::stylesheets::{CSSRuleIteratorExt, Stylesheet}; +use traversal::RecalcStyleAndConstructFlows; use url::Url; use util::geometry::MAX_RECT; use util::ipc::OptionalIpcSender; @@ -1024,10 +1025,12 @@ impl LayoutTask { // Perform CSS selector matching and flow construction. match self.parallel_traversal { None => { - sequential::traverse_dom_preorder(node, &shared_layout_context); + sequential::traverse_dom_preorder::( + node, &shared_layout_context); } Some(ref mut traversal) => { - parallel::traverse_dom_preorder(node, &shared_layout_context, traversal); + parallel::traverse_dom_preorder::( + node, &shared_layout_context, traversal); } } }); diff --git a/components/layout/parallel.rs b/components/layout/parallel.rs index 35c9b7f7955..fa52a7efdb2 100644 --- a/components/layout/parallel.rs +++ b/components/layout/parallel.rs @@ -19,8 +19,7 @@ use style::dom::UnsafeNode; use traversal::PostorderNodeMutTraversal; use traversal::{AssignBSizesAndStoreOverflow, AssignISizes, BubbleISizes}; use traversal::{BuildDisplayList, ComputeAbsolutePositions}; -use traversal::{ConstructFlows, RecalcStyleForNode}; -use traversal::{PostorderDomTraversal, PreorderDomTraversal}; +use traversal::{DomTraversal, DomTraversalContext}; use util::opts; use util::workqueue::{WorkQueue, WorkUnit, WorkerProxy}; use wrapper::LayoutNode; @@ -73,105 +72,6 @@ pub type ChunkedFlowTraversalFunction = pub type FlowTraversalFunction = extern "Rust" fn(UnsafeFlow, &SharedLayoutContext); -/// A parallel top-down DOM traversal. -pub trait ParallelPreorderDomTraversal<'ln, ConcreteLayoutNode> - : PreorderDomTraversal<'ln, ConcreteLayoutNode> - where ConcreteLayoutNode: LayoutNode<'ln> { - fn run_parallel(&self, - nodes: UnsafeNodeList, - proxy: &mut WorkerProxy); - - #[inline(always)] - fn run_parallel_helper( - &self, - unsafe_nodes: UnsafeNodeList, - proxy: &mut WorkerProxy, - top_down_func: ChunkedDomTraversalFunction, - bottom_up_func: DomTraversalFunction) { - let mut discovered_child_nodes = Vec::new(); - for unsafe_node in *unsafe_nodes.0 { - // Get a real layout node. - let node = unsafe { ConcreteLayoutNode::from_unsafe(&unsafe_node) }; - - // Perform the appropriate traversal. - self.process(node); - - let child_count = node.children_count(); - - // Reset the count of children. - { - let data = node.mutate_data().unwrap(); - data.parallel.children_count.store(child_count as isize, - Ordering::Relaxed); - } - - // Possibly enqueue the children. - if child_count != 0 { - for kid in node.children() { - discovered_child_nodes.push(kid.to_unsafe()) - } - } else { - // If there were no more children, start walking back up. - bottom_up_func(unsafe_nodes.1, unsafe_node, proxy) - } - } - - for chunk in discovered_child_nodes.chunks(CHUNK_SIZE) { - proxy.push(WorkUnit { - fun: top_down_func, - data: (box chunk.iter().cloned().collect(), unsafe_nodes.1), - }); - } - } -} - -/// A parallel bottom-up DOM traversal. -trait ParallelPostorderDomTraversal<'ln, ConcreteLayoutNode> - : PostorderDomTraversal<'ln, ConcreteLayoutNode> - where ConcreteLayoutNode: LayoutNode<'ln> { - fn root(&self) -> OpaqueNode; - - /// Process current node and potentially traverse its ancestors. - /// - /// If we are the last child that finished processing, recursively process - /// our parent. Else, stop. Also, stop at the root. - /// - /// Thus, if we start with all the leaves of a tree, we end up traversing - /// the whole tree bottom-up because each parent will be processed exactly - /// once (by the last child that finishes processing). - /// - /// The only communication between siblings is that they both - /// fetch-and-subtract the parent's children count. - fn run_parallel(&self, unsafe_node: UnsafeNode) { - // Get a real layout node. - let mut node = unsafe { ConcreteLayoutNode::from_unsafe(&unsafe_node) }; - loop { - // Perform the appropriate operation. - self.process(node); - - let parent = match node.layout_parent_node(self.root()) { - None => break, - Some(parent) => parent, - }; - - let parent_data = unsafe { - &*parent.borrow_data_unchecked().unwrap() - }; - - if parent_data - .parallel - .children_count - .fetch_sub(1, Ordering::Relaxed) != 1 { - // Get out of here and find another node to work on. - break - } - - // We were the last child of our parent. Construct flows for our parent. - node = parent; - } - } -} - /// Information that we need stored in each flow. pub struct FlowParallelInfo { /// The number of children that still need work done. @@ -332,59 +232,102 @@ impl<'a> ParallelPreorderFlowTraversal for ComputeAbsolutePositions<'a> { impl<'a> ParallelPostorderFlowTraversal for BuildDisplayList<'a> {} -impl<'a, 'ln, ConcreteLayoutNode> ParallelPostorderDomTraversal<'ln, ConcreteLayoutNode> - for ConstructFlows<'a> - where ConcreteLayoutNode: LayoutNode<'ln> { - fn root(&self) -> OpaqueNode { - self.root - } -} - -impl<'a, 'ln, ConcreteLayoutNode> ParallelPreorderDomTraversal<'ln, ConcreteLayoutNode> - for RecalcStyleForNode<'a> - where ConcreteLayoutNode: LayoutNode<'ln> { - fn run_parallel(&self, - unsafe_nodes: UnsafeNodeList, - proxy: &mut WorkerProxy) { - // Not exactly sure why we need UFCS here, but we seem to. - as ParallelPreorderDomTraversal<'ln, ConcreteLayoutNode>> - ::run_parallel_helper(self, unsafe_nodes, proxy, - recalc_style::<'ln, ConcreteLayoutNode>, - construct_flows::<'ln, ConcreteLayoutNode>) - } -} - -fn recalc_style<'ln, ConcreteLayoutNode>(unsafe_nodes: UnsafeNodeList, - proxy: &mut WorkerProxy) - where ConcreteLayoutNode: LayoutNode<'ln> { +/// A parallel top-down DOM traversal. +#[inline(always)] +fn top_down_dom<'ln, N, T>(unsafe_nodes: UnsafeNodeList, + proxy: &mut WorkerProxy) + where N: LayoutNode<'ln>, T: DomTraversal<'ln, N> { let shared_layout_context = proxy.user_data(); let layout_context = LayoutContext::new(shared_layout_context); - let recalc_style_for_node_traversal = RecalcStyleForNode { + let traversal_context = DomTraversalContext { layout_context: &layout_context, root: unsafe_nodes.1, }; - // The UFCS is necessary here to select the proper set of generic routines which - // will eventually cast the UnsafeNode to a concrete LayoutNode implementation. - > - ::run_parallel(&recalc_style_for_node_traversal, unsafe_nodes, proxy) + let mut discovered_child_nodes = Vec::new(); + for unsafe_node in *unsafe_nodes.0 { + // Get a real layout node. + let node = unsafe { N::from_unsafe(&unsafe_node) }; + + // Perform the appropriate traversal. + T::process_preorder(&traversal_context, node); + + let child_count = node.children_count(); + + // Reset the count of children. + { + let data = node.mutate_data().unwrap(); + data.parallel.children_count.store(child_count as isize, + Ordering::Relaxed); + } + + // Possibly enqueue the children. + if child_count != 0 { + for kid in node.children() { + discovered_child_nodes.push(kid.to_unsafe()) + } + } else { + // If there were no more children, start walking back up. + bottom_up_dom::(unsafe_nodes.1, unsafe_node, proxy) + } + } + + for chunk in discovered_child_nodes.chunks(CHUNK_SIZE) { + proxy.push(WorkUnit { + fun: top_down_dom::, + data: (box chunk.iter().cloned().collect(), unsafe_nodes.1), + }); + } } -fn construct_flows<'ln, ConcreteLayoutNode: LayoutNode<'ln>>( - root: OpaqueNode, - unsafe_node: UnsafeNode, - proxy: &mut WorkerProxy) { +/// Process current node and potentially traverse its ancestors. +/// +/// If we are the last child that finished processing, recursively process +/// our parent. Else, stop. Also, stop at the root. +/// +/// Thus, if we start with all the leaves of a tree, we end up traversing +/// the whole tree bottom-up because each parent will be processed exactly +/// once (by the last child that finishes processing). +/// +/// The only communication between siblings is that they both +/// fetch-and-subtract the parent's children count. +fn bottom_up_dom<'ln, N, T>(root: OpaqueNode, + unsafe_node: UnsafeNode, + proxy: &mut WorkerProxy) + where N: LayoutNode<'ln>, T: DomTraversal<'ln, N> { let shared_layout_context = proxy.user_data(); let layout_context = LayoutContext::new(shared_layout_context); - let construct_flows_traversal = ConstructFlows { + let traversal_context = DomTraversalContext { layout_context: &layout_context, root: root, }; - // The UFCS is necessary here to select the proper set of generic routines which - // will eventually cast the UnsafeNode to a concrete LayoutNode implementation. - > - ::run_parallel(&construct_flows_traversal, unsafe_node) + // Get a real layout node. + let mut node = unsafe { N::from_unsafe(&unsafe_node) }; + loop { + // Perform the appropriate operation. + T::process_postorder(&traversal_context, node); + + let parent = match node.layout_parent_node(traversal_context.root) { + None => break, + Some(parent) => parent, + }; + + let parent_data = unsafe { + &*parent.borrow_data_unchecked().unwrap() + }; + + if parent_data + .parallel + .children_count + .fetch_sub(1, Ordering::Relaxed) != 1 { + // Get out of here and find another node to work on. + break + } + + // We were the last child of our parent. Construct flows for our parent. + node = parent; + } } fn assign_inline_sizes(unsafe_flows: UnsafeFlowList, @@ -441,12 +384,14 @@ fn run_queue_with_custom_work_data_type( queue.run(shared_layout_context); } -pub fn traverse_dom_preorder<'ln, ConcreteLayoutNode: LayoutNode<'ln>>(root: ConcreteLayoutNode, +pub fn traverse_dom_preorder<'ln, N, T>( + root: N, shared_layout_context: &SharedLayoutContext, - queue: &mut WorkQueue) { + queue: &mut WorkQueue) + where N: LayoutNode<'ln>, T: DomTraversal<'ln, N> { run_queue_with_custom_work_data_type(queue, |queue| { queue.push(WorkUnit { - fun: recalc_style::<'ln, ConcreteLayoutNode>, + fun: top_down_dom::, data: (box vec![root.to_unsafe()], root.opaque()), }); }, shared_layout_context); diff --git a/components/layout/sequential.rs b/components/layout/sequential.rs index 5f33e5f352c..310560e2fa7 100644 --- a/components/layout/sequential.rs +++ b/components/layout/sequential.rs @@ -14,39 +14,33 @@ use fragment::FragmentBorderBoxIterator; use generated_content::ResolveGeneratedContent; use traversal::PostorderNodeMutTraversal; use traversal::{AssignBSizesAndStoreOverflow, AssignISizes}; -use traversal::{BubbleISizes, ConstructFlows, RecalcStyleForNode}; -use traversal::{BuildDisplayList, ComputeAbsolutePositions}; -use traversal::{PostorderDomTraversal, PreorderDomTraversal}; +use traversal::{BubbleISizes, BuildDisplayList, ComputeAbsolutePositions}; +use traversal::{DomTraversal, DomTraversalContext}; use util::opts; use wrapper::LayoutNode; -pub fn traverse_dom_preorder<'le, N>(root: N, - shared_layout_context: &SharedLayoutContext) - where N: LayoutNode<'le> { - fn doit<'le, N>(node: N, - recalc_style: RecalcStyleForNode, - construct_flows: ConstructFlows) - where N: LayoutNode<'le> { - recalc_style.process(node); +pub fn traverse_dom_preorder<'ln, N, T>(root: N, + shared_layout_context: &SharedLayoutContext) + where N: LayoutNode<'ln>, + T: DomTraversal<'ln, N> { + fn doit<'a, 'ln, N, T>(context: &'a DomTraversalContext<'a>, node: N) + where N: LayoutNode<'ln>, T: DomTraversal<'ln, N> { + T::process_preorder(context, node); for kid in node.children() { - doit(kid, recalc_style, construct_flows); + doit::(context, kid); } - construct_flows.process(node); + T::process_postorder(context, node); } let layout_context = LayoutContext::new(shared_layout_context); - let recalc_style = RecalcStyleForNode { - layout_context: &layout_context, - root: root.opaque(), - }; - let construct_flows = ConstructFlows { + let traversal_context = DomTraversalContext { layout_context: &layout_context, root: root.opaque(), }; - doit::<'le, N>(root, recalc_style, construct_flows); + doit::(&traversal_context, root); } pub fn resolve_generated_content(root: &mut FlowRef, shared_layout_context: &SharedLayoutContext) { diff --git a/components/layout/traversal.rs b/components/layout/traversal.rs index c5bf7d482f3..6a0038053cb 100644 --- a/components/layout/traversal.rs +++ b/components/layout/traversal.rs @@ -118,17 +118,30 @@ fn insert_ancestors_into_bloom_filter<'ln, N>(bf: &mut Box, debug!("[{}] Inserted {} ancestors.", tid(), ancestors); } - -/// A top-down traversal. -pub trait PreorderDomTraversal<'ln, ConcreteLayoutNode: LayoutNode<'ln>> { - /// The operation to perform. Return true to continue or false to stop. - fn process(&self, node: ConcreteLayoutNode); +#[derive(Copy, Clone)] +pub struct DomTraversalContext<'a> { + pub layout_context: &'a LayoutContext<'a>, + pub root: OpaqueNode, } -/// A bottom-up traversal, with a optional in-order pass. -pub trait PostorderDomTraversal<'ln, ConcreteLayoutNode: LayoutNode<'ln>> { - /// The operation to perform. Return true to continue or false to stop. - fn process(&self, node: ConcreteLayoutNode); +pub trait DomTraversal<'ln, N: LayoutNode<'ln>> { + fn process_preorder<'a>(context: &'a DomTraversalContext<'a>, node: N); + fn process_postorder<'a>(context: &'a DomTraversalContext<'a>, node: N); +} + +/// FIXME(bholley): I added this now to demonstrate the usefulness of the new design. +/// This is currently unused, but will be used shortly. +#[allow(dead_code)] +pub struct RecalcStyleOnly; +impl<'ln, N: LayoutNode<'ln>> DomTraversal<'ln, N> for RecalcStyleOnly { + fn process_preorder<'a>(context: &'a DomTraversalContext<'a>, node: N) { recalc_style_at(context, node); } + fn process_postorder<'a>(_: &'a DomTraversalContext<'a>, _: N) {} +} + +pub struct RecalcStyleAndConstructFlows; +impl<'ln, N: LayoutNode<'ln>> DomTraversal<'ln, N> for RecalcStyleAndConstructFlows { + fn process_preorder<'a>(context: &'a DomTraversalContext<'a>, node: N) { recalc_style_at(context, node); } + fn process_postorder<'a>(context: &'a DomTraversalContext<'a>, node: N) { construct_flows_at(context, node); } } /// A bottom-up, parallelizable traversal. @@ -139,176 +152,156 @@ pub trait PostorderNodeMutTraversal<'ln, ConcreteThreadSafeLayoutNode: ThreadSaf /// The recalc-style-for-node traversal, which styles each node and must run before /// layout computation. This computes the styles applied to each node. -#[derive(Copy, Clone)] -pub struct RecalcStyleForNode<'a> { - pub layout_context: &'a LayoutContext<'a>, - pub root: OpaqueNode, -} +#[inline] +#[allow(unsafe_code)] +fn recalc_style_at<'a, 'ln, N: LayoutNode<'ln>> (context: &'a DomTraversalContext<'a>, node: N) { + // Initialize layout data. + // + // FIXME(pcwalton): Stop allocating here. Ideally this should just be done by the HTML + // parser. + node.initialize_data(); -impl<'a, 'ln, ConcreteLayoutNode> PreorderDomTraversal<'ln, ConcreteLayoutNode> - for RecalcStyleForNode<'a> - where ConcreteLayoutNode: LayoutNode<'ln> { - #[inline] - #[allow(unsafe_code)] - fn process(&self, node: ConcreteLayoutNode) { - // Initialize layout data. - // - // FIXME(pcwalton): Stop allocating here. Ideally this should just be done by the HTML - // parser. - node.initialize_data(); + // Get the parent node. + let parent_opt = node.layout_parent_node(context.root); - // Get the parent node. - let parent_opt = node.layout_parent_node(self.root); + // Get the style bloom filter. + let mut bf = take_task_local_bloom_filter(parent_opt, context.root, context.layout_context); - // Get the style bloom filter. - let mut bf = take_task_local_bloom_filter(parent_opt, self.root, self.layout_context); - - let nonincremental_layout = opts::get().nonincremental_layout; - if nonincremental_layout || node.is_dirty() { - // Remove existing CSS styles from nodes whose content has changed (e.g. text changed), - // to force non-incremental reflow. - if node.has_changed() { - let node = node.to_threadsafe(); - node.unstyle(); - } - - // Check to see whether we can share a style with someone. - let style_sharing_candidate_cache = - &mut self.layout_context.style_sharing_candidate_cache(); - - let sharing_result = match node.as_element() { - Some(element) => { - unsafe { - element.share_style_if_possible(style_sharing_candidate_cache, - parent_opt.clone()) - } - }, - None => StyleSharingResult::CannotShare, - }; - - // Otherwise, match and cascade selectors. - match sharing_result { - StyleSharingResult::CannotShare => { - let mut applicable_declarations = ApplicableDeclarations::new(); - - let shareable_element = match node.as_element() { - Some(element) => { - // Perform the CSS selector matching. - let stylist = unsafe { &*self.layout_context.shared_context().stylist.0 }; - if element.match_element(stylist, - Some(&*bf), - &mut applicable_declarations) { - Some(element) - } else { - None - } - }, - None => { - if node.has_changed() { - node.to_threadsafe().set_restyle_damage( - incremental::rebuild_and_reflow()) - } - None - }, - }; - - // Perform the CSS cascade. - unsafe { - node.cascade_node(self.layout_context.shared, - parent_opt, - &applicable_declarations, - &mut self.layout_context.applicable_declarations_cache(), - &self.layout_context.shared_context().new_animations_sender); - } - - // Add ourselves to the LRU cache. - if let Some(element) = shareable_element { - style_sharing_candidate_cache.insert_if_possible(&element); - } - } - StyleSharingResult::StyleWasShared(index, damage) => { - style_sharing_candidate_cache.touch(index); - node.to_threadsafe().set_restyle_damage(damage); - } - } + let nonincremental_layout = opts::get().nonincremental_layout; + if nonincremental_layout || node.is_dirty() { + // Remove existing CSS styles from nodes whose content has changed (e.g. text changed), + // to force non-incremental reflow. + if node.has_changed() { + let node = node.to_threadsafe(); + node.unstyle(); } - let unsafe_layout_node = node.to_unsafe(); + // Check to see whether we can share a style with someone. + let style_sharing_candidate_cache = + &mut context.layout_context.style_sharing_candidate_cache(); - // Before running the children, we need to insert our nodes into the bloom - // filter. - debug!("[{}] + {:X}", tid(), unsafe_layout_node.0); - node.insert_into_bloom_filter(&mut *bf); + let sharing_result = match node.as_element() { + Some(element) => { + unsafe { + element.share_style_if_possible(style_sharing_candidate_cache, + parent_opt.clone()) + } + }, + None => StyleSharingResult::CannotShare, + }; - // NB: flow construction updates the bloom filter on the way up. - put_task_local_bloom_filter(bf, &unsafe_layout_node, self.layout_context); + // Otherwise, match and cascade selectors. + match sharing_result { + StyleSharingResult::CannotShare => { + let mut applicable_declarations = ApplicableDeclarations::new(); + + let shareable_element = match node.as_element() { + Some(element) => { + // Perform the CSS selector matching. + let stylist = unsafe { &*context.layout_context.shared_context().stylist.0 }; + if element.match_element(stylist, + Some(&*bf), + &mut applicable_declarations) { + Some(element) + } else { + None + } + }, + None => { + if node.has_changed() { + node.to_threadsafe().set_restyle_damage( + incremental::rebuild_and_reflow()) + } + None + }, + }; + + // Perform the CSS cascade. + unsafe { + node.cascade_node(context.layout_context.shared, + parent_opt, + &applicable_declarations, + &mut context.layout_context.applicable_declarations_cache(), + &context.layout_context.shared_context().new_animations_sender); + } + + // Add ourselves to the LRU cache. + if let Some(element) = shareable_element { + style_sharing_candidate_cache.insert_if_possible(&element); + } + } + StyleSharingResult::StyleWasShared(index, damage) => { + style_sharing_candidate_cache.touch(index); + node.to_threadsafe().set_restyle_damage(damage); + } + } } + + let unsafe_layout_node = node.to_unsafe(); + + // Before running the children, we need to insert our nodes into the bloom + // filter. + debug!("[{}] + {:X}", tid(), unsafe_layout_node.0); + node.insert_into_bloom_filter(&mut *bf); + + // NB: flow construction updates the bloom filter on the way up. + put_task_local_bloom_filter(bf, &unsafe_layout_node, context.layout_context); } /// The flow construction traversal, which builds flows for styled nodes. -#[derive(Copy, Clone)] -pub struct ConstructFlows<'a> { - pub layout_context: &'a LayoutContext<'a>, - pub root: OpaqueNode, -} +#[inline] +#[allow(unsafe_code)] +fn construct_flows_at<'a, 'ln, N: LayoutNode<'ln>>(context: &'a DomTraversalContext<'a>, node: N) { + // Construct flows for this node. + { + let tnode = node.to_threadsafe(); -impl<'a, 'ln, ConcreteLayoutNode> PostorderDomTraversal<'ln, ConcreteLayoutNode> - for ConstructFlows<'a> - where ConcreteLayoutNode: LayoutNode<'ln> { - #[inline] - #[allow(unsafe_code)] - fn process(&self, node: ConcreteLayoutNode) { - // Construct flows for this node. - { - let tnode = node.to_threadsafe(); - - // Always reconstruct if incremental layout is turned off. - let nonincremental_layout = opts::get().nonincremental_layout; - if nonincremental_layout || node.has_dirty_descendants() { - let mut flow_constructor = FlowConstructor::new(self.layout_context); - if nonincremental_layout || !flow_constructor.repair_if_possible(&tnode) { - flow_constructor.process(&tnode); - debug!("Constructed flow for {:x}: {:x}", - tnode.debug_id(), - tnode.flow_debug_id()); - } + // Always reconstruct if incremental layout is turned off. + let nonincremental_layout = opts::get().nonincremental_layout; + if nonincremental_layout || node.has_dirty_descendants() { + let mut flow_constructor = FlowConstructor::new(context.layout_context); + if nonincremental_layout || !flow_constructor.repair_if_possible(&tnode) { + flow_constructor.process(&tnode); + debug!("Constructed flow for {:x}: {:x}", + tnode.debug_id(), + tnode.flow_debug_id()); } - - // Reset the layout damage in this node. It's been propagated to the - // flow by the flow constructor. - tnode.set_restyle_damage(RestyleDamage::empty()); } - unsafe { - node.set_changed(false); - node.set_dirty(false); - node.set_dirty_descendants(false); - } - - let unsafe_layout_node = node.to_unsafe(); - - let (mut bf, old_node, old_generation) = - STYLE_BLOOM.with(|style_bloom| { - mem::replace(&mut *style_bloom.borrow_mut(), None) - .expect("The bloom filter should have been set by style recalc.") - }); - - assert_eq!(old_node, unsafe_layout_node); - assert_eq!(old_generation, self.layout_context.shared_context().generation); - - match node.layout_parent_node(self.root) { - None => { - debug!("[{}] - {:X}, and deleting BF.", tid(), unsafe_layout_node.0); - // If this is the reflow root, eat the task-local bloom filter. - } - Some(parent) => { - // Otherwise, put it back, but remove this node. - node.remove_from_bloom_filter(&mut *bf); - let unsafe_parent = parent.to_unsafe(); - put_task_local_bloom_filter(bf, &unsafe_parent, self.layout_context); - }, - }; + // Reset the layout damage in this node. It's been propagated to the + // flow by the flow constructor. + tnode.set_restyle_damage(RestyleDamage::empty()); } + + unsafe { + node.set_changed(false); + node.set_dirty(false); + node.set_dirty_descendants(false); + } + + let unsafe_layout_node = node.to_unsafe(); + + let (mut bf, old_node, old_generation) = + STYLE_BLOOM.with(|style_bloom| { + mem::replace(&mut *style_bloom.borrow_mut(), None) + .expect("The bloom filter should have been set by style recalc.") + }); + + assert_eq!(old_node, unsafe_layout_node); + assert_eq!(old_generation, context.layout_context.shared_context().generation); + + match node.layout_parent_node(context.root) { + None => { + debug!("[{}] - {:X}, and deleting BF.", tid(), unsafe_layout_node.0); + // If this is the reflow root, eat the task-local bloom filter. + } + Some(parent) => { + // Otherwise, put it back, but remove this node. + node.remove_from_bloom_filter(&mut *bf); + let unsafe_parent = parent.to_unsafe(); + put_task_local_bloom_filter(bf, &unsafe_parent, context.layout_context); + }, + }; } /// The bubble-inline-sizes traversal, the first part of layout computation. This computes