servo: Merge #13346 - layout: Make some major improvements to incremental layout to improve CNN (from pcwalton:cnn); r=notriddle
authorPatrick Walton <pcwalton@mimiga.net>
Mon, 26 Sep 2016 20:57:59 -0500
changeset 339754 ddabd73bc67203c386c7ee301d533cd3c2fa7d77
parent 339753 ff706352fa21d6602b17ef46f0ba15404dd24bb0
child 339755 c3a894f27a880728be7df9c71ec41c40d8b2016b
push id31307
push usergszorc@mozilla.com
push dateSat, 04 Feb 2017 00:59:06 +0000
treeherdermozilla-central@94079d43835f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnotriddle
servo: Merge #13346 - layout: Make some major improvements to incremental layout to improve CNN (from pcwalton:cnn); r=notriddle CNN is still too slow to be usable, but this is a partial solution. r? @notriddle (feel free to reassign if you like) Source-Repo: https://github.com/servo/servo Source-Revision: cbe54582727e3d756aa084e94520f2f29f9d7c86
servo/components/layout/block.rs
servo/components/layout/flow.rs
servo/components/layout/inline.rs
servo/components/layout/sequential.rs
servo/components/layout/table_row.rs
servo/components/layout_thread/lib.rs
servo/components/script_layout_interface/restyle_damage.rs
--- a/servo/components/layout/block.rs
+++ b/servo/components/layout/block.rs
@@ -46,16 +46,17 @@ use fragment::SpecificFragmentInfo;
 use gfx::display_list::{ClippingRegion, StackingContext};
 use gfx_traits::{LayerId, StackingContextId};
 use gfx_traits::print_tree::PrintTree;
 use layout_debug;
 use model::{self, IntrinsicISizes, MarginCollapseInfo};
 use model::{CollapsibleMargins, MaybeAuto, specified, specified_or_none};
 use rustc_serialize::{Encodable, Encoder};
 use script_layout_interface::restyle_damage::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW};
+use script_layout_interface::restyle_damage::REPOSITION;
 use std::cmp::{max, min};
 use std::fmt;
 use std::sync::Arc;
 use style::computed_values::{border_collapse, box_sizing, display, float, overflow_x, overflow_y};
 use style::computed_values::{position, text_align, transform, transform_style};
 use style::context::{SharedStyleContext, StyleContext};
 use style::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize, WritingMode};
 use style::properties::ServoComputedValues;
@@ -1837,16 +1838,18 @@ impl Flow for BlockFlow {
                                              self.base.debug_id());
 
             // Assign block-size for fragment if it is an image fragment.
             let containing_block_block_size =
                 self.base.block_container_explicit_block_size;
             self.fragment.assign_replaced_block_size_if_necessary(containing_block_block_size);
             if !self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) {
                 self.base.position.size.block = self.fragment.border_box.size.block;
+                self.base.restyle_damage.remove(REFLOW_OUT_OF_FLOW | REFLOW);
+                self.fragment.restyle_damage.remove(REFLOW_OUT_OF_FLOW | REFLOW);
             }
             None
         } else if self.is_root() || self.formatting_context_type() != FormattingContextType::None {
             // Root element margins should never be collapsed according to CSS § 8.3.1.
             debug!("assign_block_size: assigning block_size for root flow {:?}",
                    flow::base(self).debug_id());
             self.assign_block_size_block_base(
                 layout_context,
@@ -2109,16 +2112,18 @@ impl Flow for BlockFlow {
                 clip
             } else {
                 clip.clone()
             };
             flow::mut_base(kid).clip = clip;
             flow::mut_base(kid).stacking_relative_position_of_display_port =
                 stacking_relative_position_of_display_port_for_children;
         }
+
+        self.base.restyle_damage.remove(REPOSITION)
     }
 
     fn mark_as_root(&mut self) {
         self.flags.insert(IS_ROOT)
     }
 
     fn is_root(&self) -> bool {
         self.flags.contains(IS_ROOT)
--- a/servo/components/layout/flow.rs
+++ b/servo/components/layout/flow.rs
@@ -37,17 +37,18 @@ use fragment::{Fragment, FragmentBorderB
 use gfx::display_list::{ClippingRegion, StackingContext};
 use gfx_traits::{LayerId, LayerType, StackingContextId};
 use gfx_traits::print_tree::PrintTree;
 use inline::InlineFlow;
 use model::{CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo};
 use multicol::MulticolFlow;
 use parallel::FlowParallelInfo;
 use rustc_serialize::{Encodable, Encoder};
-use script_layout_interface::restyle_damage::{RECONSTRUCT_FLOW, REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, RestyleDamage};
+use script_layout_interface::restyle_damage::{RECONSTRUCT_FLOW, REFLOW, REFLOW_OUT_OF_FLOW};
+use script_layout_interface::restyle_damage::{REPAINT, REPOSITION, RestyleDamage};
 use script_layout_interface::wrapper_traits::{PseudoElementType, ThreadSafeLayoutNode};
 use std::{fmt, mem, raw};
 use std::iter::Zip;
 use std::slice::IterMut;
 use std::sync::Arc;
 use std::sync::atomic::Ordering;
 use style::computed_values::{clear, display, empty_cells, float, overflow_x, position, text_align};
 use style::context::SharedStyleContext;
@@ -315,16 +316,17 @@ pub trait Flow: fmt::Debug + Sync + Send
             _ => {}
         }
         mut_base(self).overflow = overflow
     }
 
     /// Phase 4 of reflow: computes absolute positions.
     fn compute_absolute_position(&mut self, _: &SharedLayoutContext) {
         // The default implementation is a no-op.
+        mut_base(self).restyle_damage.remove(REPOSITION)
     }
 
     /// Phase 5 of reflow: builds display lists.
     fn build_display_list(&mut self, state: &mut DisplayListBuildState);
 
     /// Returns the union of all overflow rects of all of this flow's fragments.
     fn compute_overflow(&self) -> Overflow;
 
--- a/servo/components/layout/inline.rs
+++ b/servo/components/layout/inline.rs
@@ -20,18 +20,18 @@ use fragment::SpecificFragmentInfo;
 use gfx::display_list::{OpaqueNode, StackingContext};
 use gfx::font::FontMetrics;
 use gfx::font_context::FontContext;
 use gfx_traits::StackingContextId;
 use gfx_traits::print_tree::PrintTree;
 use layout_debug;
 use model::IntrinsicISizesContribution;
 use range::{Range, RangeIndex};
-use script_layout_interface::restyle_damage::{BUBBLE_ISIZES, REFLOW};
-use script_layout_interface::restyle_damage::{REFLOW_OUT_OF_FLOW, RESOLVE_GENERATED_CONTENT};
+use script_layout_interface::restyle_damage::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW};
+use script_layout_interface::restyle_damage::{REPOSITION, RESOLVE_GENERATED_CONTENT};
 use script_layout_interface::wrapper_traits::PseudoElementType;
 use std::{fmt, i32, isize, mem};
 use std::cmp::max;
 use std::collections::VecDeque;
 use std::sync::Arc;
 use style::arc_ptr_eq;
 use style::computed_values::{display, overflow_x, position, text_align, text_justify};
 use style::computed_values::{text_overflow, vertical_align, white_space};
@@ -1645,16 +1645,18 @@ impl Flow for InlineFlow {
                     block_flow.base.stacking_relative_position =
                         stacking_relative_border_box.origin;
                     block_flow.base.stacking_relative_position_of_display_port =
                         self.base.stacking_relative_position_of_display_port;
                 }
                 _ => {}
             }
         }
+
+        self.base.restyle_damage.remove(REPOSITION)
     }
 
     fn update_late_computed_inline_position_if_necessary(&mut self, _: Au) {}
 
     fn update_late_computed_block_position_if_necessary(&mut self, _: Au) {}
 
     fn collect_stacking_contexts(&mut self,
                                  parent_id: StackingContextId,
--- a/servo/components/layout/sequential.rs
+++ b/servo/components/layout/sequential.rs
@@ -12,17 +12,17 @@ use floats::SpeculatedFloatPlacement;
 use flow::{self, Flow, ImmutableFlowUtils, InorderFlowTraversal, MutableFlowUtils};
 use flow::{PostorderFlowTraversal, PreorderFlowTraversal};
 use flow::IS_ABSOLUTELY_POSITIONED;
 use fragment::FragmentBorderBoxIterator;
 use generated_content::ResolveGeneratedContent;
 use gfx::display_list::{DisplayItem, StackingContext};
 use script_layout_interface::restyle_damage::{REFLOW, STORE_OVERFLOW};
 use style::context::StyleContext;
-use traversal::{AssignBSizes, AssignISizes, BubbleISizes, BuildDisplayList, ComputeAbsolutePositions};
+use traversal::{AssignBSizes, AssignISizes, BubbleISizes, BuildDisplayList};
 use util::opts;
 
 pub use style::sequential::traverse_dom;
 
 pub fn resolve_generated_content(root: &mut Flow, shared_layout_context: &SharedLayoutContext) {
     fn doit(flow: &mut Flow, level: u32, traversal: &mut ResolveGeneratedContent) {
         if !traversal.should_process(flow) {
             return
@@ -73,17 +73,16 @@ pub fn traverse_flow_tree_preorder(root:
 
     doit(root, assign_inline_sizes, assign_block_sizes);
 }
 
 pub fn build_display_list_for_subtree(flow_root: &mut Flow,
                                       root_stacking_context: &mut StackingContext,
                                       shared_layout_context: &SharedLayoutContext)
                                       -> Vec<DisplayItem> {
-    flow_root.traverse_preorder(&ComputeAbsolutePositions { layout_context: shared_layout_context });
     let mut children = vec![];
     flow_root.collect_stacking_contexts(root_stacking_context.id,
                                         &mut children);
     root_stacking_context.add_children(children);
     let mut build_display_list = BuildDisplayList {
         state: DisplayListBuildState::new(shared_layout_context,
                                           flow::base(flow_root).stacking_context_id),
     };
--- a/servo/components/layout/table_row.rs
+++ b/servo/components/layout/table_row.rs
@@ -16,16 +16,17 @@ use flow::{self, EarlyAbsolutePositionIn
 use flow_list::MutFlowListIterator;
 use fragment::{Fragment, FragmentBorderBoxIterator, Overflow};
 use gfx::display_list::StackingContext;
 use gfx_traits::StackingContextId;
 use gfx_traits::print_tree::PrintTree;
 use layout_debug;
 use model::MaybeAuto;
 use rustc_serialize::{Encodable, Encoder};
+use script_layout_interface::restyle_damage::{REFLOW, REFLOW_OUT_OF_FLOW};
 use std::cmp::max;
 use std::fmt;
 use std::iter::{Enumerate, IntoIterator, Peekable};
 use std::sync::Arc;
 use style::computed_values::{border_collapse, border_spacing, border_top_style};
 use style::context::SharedStyleContext;
 use style::logical_geometry::{LogicalSize, PhysicalSide, WritingMode};
 use style::properties::ServoComputedValues;
@@ -101,83 +102,94 @@ impl TableRowFlow {
     /// Assign block-size for table-row flow.
     ///
     /// TODO(pcwalton): This doesn't handle floats and positioned elements right.
     ///
     /// inline(always) because this is only ever called by in-order or non-in-order top-level
     /// methods
     #[inline(always)]
     fn assign_block_size_table_row_base(&mut self, layout_context: &LayoutContext) {
-        // Per CSS 2.1 § 17.5.3, find max_y = max(computed `block-size`, minimum block-size of all
-        // cells).
-        let mut max_block_size = Au(0);
-        let thread_id = self.block_flow.base.thread_id;
-        for kid in self.block_flow.base.child_iter_mut() {
-            kid.place_float_if_applicable();
-            if !flow::base(kid).flags.is_float() {
-                kid.assign_block_size_for_inorder_child_if_necessary(layout_context, thread_id);
+        if self.block_flow.base.restyle_damage.contains(REFLOW) {
+            // Per CSS 2.1 § 17.5.3, find max_y = max(computed `block-size`, minimum block-size of
+            // all cells).
+            let mut max_block_size = Au(0);
+            let thread_id = self.block_flow.base.thread_id;
+            for kid in self.block_flow.base.child_iter_mut() {
+                kid.place_float_if_applicable();
+                if !flow::base(kid).flags.is_float() {
+                    kid.assign_block_size_for_inorder_child_if_necessary(layout_context,
+                                                                         thread_id);
+                }
+
+                {
+                    let child_fragment = kid.as_mut_table_cell().fragment();
+                    // TODO: Percentage block-size
+                    let child_specified_block_size =
+                        MaybeAuto::from_style(child_fragment.style().content_block_size(),
+                                              Au(0)).specified_or_zero();
+                    max_block_size =
+                        max(max_block_size,
+                            child_specified_block_size +
+                            child_fragment.border_padding.block_start_end());
+                }
+                let child_node = flow::mut_base(kid);
+                child_node.position.start.b = Au(0);
+                max_block_size = max(max_block_size, child_node.position.size.block);
             }
 
-            {
-                let child_fragment = kid.as_mut_table_cell().fragment();
-                // TODO: Percentage block-size
-                let child_specified_block_size =
-                    MaybeAuto::from_style(child_fragment.style().content_block_size(),
-                                          Au(0)).specified_or_zero();
-                max_block_size =
-                    max(max_block_size,
-                        child_specified_block_size +
-                        child_fragment.border_padding.block_start_end());
+            let mut block_size = max_block_size;
+            // TODO: Percentage block-size
+            block_size = match MaybeAuto::from_style(self.block_flow
+                                                         .fragment
+                                                         .style()
+                                                         .content_block_size(),
+                                                     Au(0)) {
+                MaybeAuto::Auto => block_size,
+                MaybeAuto::Specified(value) => max(value, block_size),
+            };
+
+            // Assign the block-size of own fragment
+            let mut position = self.block_flow.fragment.border_box;
+            position.size.block = block_size;
+            self.block_flow.fragment.border_box = position;
+            self.block_flow.base.position.size.block = block_size;
+
+            // Assign the block-size of kid fragments, which is the same value as own block-size.
+            for kid in self.block_flow.base.child_iter_mut() {
+                let child_table_cell = kid.as_mut_table_cell();
+                {
+                    let kid_fragment = child_table_cell.mut_fragment();
+                    let mut position = kid_fragment.border_box;
+                    position.size.block = block_size;
+                    kid_fragment.border_box = position;
+                }
+
+                // Assign the child's block size.
+                child_table_cell.block_flow.base.position.size.block = block_size;
+
+                // Now we know the cell height, vertical align the cell's children.
+                child_table_cell.valign_children();
+
+                // Write in the size of the relative containing block for children. (This
+                // information is also needed to handle RTL.)
+                child_table_cell.block_flow.base.early_absolute_position_info =
+                    EarlyAbsolutePositionInfo {
+                        relative_containing_block_size: self.block_flow
+                                                            .fragment
+                                                            .content_box()
+                                                            .size,
+                        relative_containing_block_mode: self.block_flow
+                                                            .fragment
+                                                            .style()
+                                                            .writing_mode,
+                    };
             }
-            let child_node = flow::mut_base(kid);
-            child_node.position.start.b = Au(0);
-            max_block_size = max(max_block_size, child_node.position.size.block);
         }
 
-        let mut block_size = max_block_size;
-        // TODO: Percentage block-size
-        block_size = match MaybeAuto::from_style(self.block_flow
-                                                     .fragment
-                                                     .style()
-                                                     .content_block_size(),
-                                                 Au(0)) {
-            MaybeAuto::Auto => block_size,
-            MaybeAuto::Specified(value) => max(value, block_size),
-        };
-
-        // Assign the block-size of own fragment
-        let mut position = self.block_flow.fragment.border_box;
-        position.size.block = block_size;
-        self.block_flow.fragment.border_box = position;
-        self.block_flow.base.position.size.block = block_size;
-
-        // Assign the block-size of kid fragments, which is the same value as own block-size.
-        for kid in self.block_flow.base.child_iter_mut() {
-            let child_table_cell = kid.as_mut_table_cell();
-            {
-                let kid_fragment = child_table_cell.mut_fragment();
-                let mut position = kid_fragment.border_box;
-                position.size.block = block_size;
-                kid_fragment.border_box = position;
-            }
-
-            // Assign the child's block size.
-            child_table_cell.block_flow.base.position.size.block = block_size;
-
-            // Now we know the cell height, vertical align the cell's children.
-            child_table_cell.valign_children();
-
-            // Write in the size of the relative containing block for children. (This information
-            // is also needed to handle RTL.)
-            child_table_cell.block_flow.base.early_absolute_position_info =
-                EarlyAbsolutePositionInfo {
-                    relative_containing_block_size: self.block_flow.fragment.content_box().size,
-                    relative_containing_block_mode: self.block_flow.fragment.style().writing_mode,
-                };
-        }
+        self.block_flow.base.restyle_damage.remove(REFLOW_OUT_OF_FLOW | REFLOW);
     }
 
     pub fn populate_collapsed_border_spacing<'a, I>(
             &mut self,
             collapsed_inline_direction_border_widths_for_table: &[Au],
             collapsed_block_direction_border_widths_for_table: &mut Peekable<I>)
             where I: Iterator<Item=&'a Au> {
         self.collapsed_border_spacing.inline.clear();
--- a/servo/components/layout_thread/lib.rs
+++ b/servo/components/layout_thread/lib.rs
@@ -60,41 +60,42 @@ use gfx::paint_thread::LayoutToPaintMsg;
 use gfx_traits::{Epoch, FragmentType, LayerId, ScrollPolicy, StackingContextId, color};
 use heapsize::HeapSizeOf;
 use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
 use ipc_channel::router::ROUTER;
 use layout::animation;
 use layout::construct::ConstructionResult;
 use layout::context::{LayoutContext, SharedLayoutContext, heap_size_of_local_context};
 use layout::display_list_builder::ToGfxColor;
-use layout::flow::{self, Flow, ImmutableFlowUtils, MutableOwnedFlowUtils};
+use layout::flow::{self, Flow, ImmutableFlowUtils, MutableFlowUtils, MutableOwnedFlowUtils};
 use layout::flow_ref::{self, FlowRef};
 use layout::incremental::{LayoutDamageComputation, REFLOW_ENTIRE_DOCUMENT};
 use layout::layout_debug;
 use layout::parallel;
 use layout::query::{LayoutRPCImpl, LayoutThreadData, process_content_box_request, process_content_boxes_request};
 use layout::query::{process_margin_style_query, process_node_overflow_request, process_resolved_style_request};
 use layout::query::{process_node_geometry_request, process_node_layer_id_request, process_node_scroll_area_request};
 use layout::query::process_offset_parent_query;
 use layout::sequential;
-use layout::traversal::RecalcStyleAndConstructFlows;
+use layout::traversal::{ComputeAbsolutePositions, RecalcStyleAndConstructFlows};
 use layout::webrender_helpers::{WebRenderDisplayListConverter, WebRenderFrameBuilder};
 use layout::wrapper::{LayoutNodeLayoutData, NonOpaqueStyleAndLayoutData};
 use layout_traits::LayoutThreadFactory;
 use msg::constellation_msg::PipelineId;
 use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheResult, ImageCacheThread};
 use net_traits::image_cache_thread::UsePlaceholder;
 use profile_traits::mem::{self, Report, ReportKind, ReportsChan};
 use profile_traits::time::{self, TimerMetadata, profile};
 use profile_traits::time::{TimerMetadataFrameType, TimerMetadataReflowType};
 use script::layout_wrapper::{ServoLayoutDocument, ServoLayoutNode};
 use script_layout_interface::{OpaqueStyleAndLayoutData, PartialStyleAndLayoutData};
 use script_layout_interface::message::{Msg, NewLayoutThreadInfo, Reflow, ReflowQueryType, ScriptReflow};
 use script_layout_interface::reporter::CSSErrorReporter;
-use script_layout_interface::restyle_damage::{REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, STORE_OVERFLOW};
+use script_layout_interface::restyle_damage::{REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, REPOSITION};
+use script_layout_interface::restyle_damage::STORE_OVERFLOW;
 use script_layout_interface::rpc::{LayoutRPC, MarginStyleResponse, NodeOverflowResponse, OffsetParentResponse};
 use script_layout_interface::wrapper_traits::LayoutNode;
 use script_traits::{ConstellationControlMsg, LayoutControlMsg, LayoutMsg as ConstellationMsg};
 use script_traits::{StackingContextScrollState, UntrustedNodeAddress};
 use std::borrow::ToOwned;
 use std::collections::HashMap;
 use std::hash::BuildHasherDefault;
 use std::ops::{Deref, DerefMut};
@@ -625,16 +626,17 @@ impl LayoutThread {
             page_clip_rect: max_rect(),
         };
         let mut layout_context = self.build_shared_layout_context(&*rw_data,
                                                                   false,
                                                                   reflow_info.goal);
 
         self.perform_post_style_recalc_layout_passes(&reflow_info,
                                                      None,
+                                                     None,
                                                      &mut *rw_data,
                                                      &mut layout_context);
 
 
         true
     }
 
     /// Receives and dispatches messages from other threads.
@@ -895,18 +897,21 @@ impl LayoutThread {
         // operation out.
         parallel::traverse_flow_tree_preorder(layout_root,
                                               profiler_metadata,
                                               time_profiler_chan,
                                               shared_layout_context,
                                               traversal);
     }
 
+    /// Computes the stacking-relative positions of all flows and, if the painting is dirty and the
+    /// reflow goal and query type need it, builds the display list.
     fn compute_abs_pos_and_build_display_list(&mut self,
                                               data: &Reflow,
+                                              query_type: Option<&ReflowQueryType>,
                                               document: Option<&ServoLayoutDocument>,
                                               layout_root: &mut Flow,
                                               shared_layout_context: &mut SharedLayoutContext,
                                               rw_data: &mut LayoutThreadData) {
         let writing_mode = flow::base(layout_root).writing_mode;
         let (metadata, sender) = (self.profiler_metadata(), self.time_profiler_chan.clone());
         profile(time::ProfilerCategory::LayoutDispListBuild,
                 metadata.clone(),
@@ -914,59 +919,74 @@ impl LayoutThread {
                 || {
             flow::mut_base(layout_root).stacking_relative_position =
                 LogicalPoint::zero(writing_mode).to_physical(writing_mode,
                                                              self.viewport_size);
 
             flow::mut_base(layout_root).clip =
                 ClippingRegion::from_rect(&data.page_clip_rect);
 
+            if flow::base(layout_root).restyle_damage.contains(REPOSITION) {
+                layout_root.traverse_preorder(&ComputeAbsolutePositions {
+                    layout_context: shared_layout_context
+                });
+            }
+
             if flow::base(layout_root).restyle_damage.contains(REPAINT) ||
                     rw_data.display_list.is_none() {
-                let mut root_stacking_context = StackingContext::new(StackingContextId::new(0),
-                                                                     StackingContextType::Real,
-                                                                     &Rect::zero(),
-                                                                     &Rect::zero(),
-                                                                     0,
-                                                                     filter::T::new(Vec::new()),
-                                                                     mix_blend_mode::T::normal,
-                                                                     Matrix4D::identity(),
-                                                                     Matrix4D::identity(),
-                                                                     true,
-                                                                     false,
-                                                                     None);
+                let display_list_needed = query_type.map(reflow_query_type_needs_display_list)
+                                                    .unwrap_or(false);
+                match (data.goal, display_list_needed) {
+                    (ReflowGoal::ForDisplay, _) | (ReflowGoal::ForScriptQuery, true) => {
+                        let mut root_stacking_context =
+                            StackingContext::new(StackingContextId::new(0),
+                                                 StackingContextType::Real,
+                                                 &Rect::zero(),
+                                                 &Rect::zero(),
+                                                 0,
+                                                 filter::T::new(Vec::new()),
+                                                 mix_blend_mode::T::normal,
+                                                 Matrix4D::identity(),
+                                                 Matrix4D::identity(),
+                                                 true,
+                                                 false,
+                                                 None);
 
-                let display_list_entries =
-                    sequential::build_display_list_for_subtree(layout_root,
-                                                               &mut root_stacking_context,
-                                                               shared_layout_context);
+                        let display_list_entries =
+                            sequential::build_display_list_for_subtree(layout_root,
+                                                                       &mut root_stacking_context,
+                                                                       shared_layout_context);
 
-                debug!("Done building display list.");
+                        debug!("Done building display list.");
 
-                let root_background_color = get_root_flow_background_color(layout_root);
-                let root_size = {
-                    let root_flow = flow::base(layout_root);
-                    if rw_data.stylist.viewport_constraints().is_some() {
-                        root_flow.position.size.to_physical(root_flow.writing_mode)
-                    } else {
-                        root_flow.overflow.scroll.size
-                    }
-                };
+                        let root_background_color = get_root_flow_background_color(layout_root);
+                        let root_size = {
+                            let root_flow = flow::base(layout_root);
+                            if rw_data.stylist.viewport_constraints().is_some() {
+                                root_flow.position.size.to_physical(root_flow.writing_mode)
+                            } else {
+                                root_flow.overflow.scroll.size
+                            }
+                        };
 
-                let origin = Rect::new(Point2D::new(Au(0), Au(0)), root_size);
-                root_stacking_context.bounds = origin;
-                root_stacking_context.overflow = origin;
-                root_stacking_context.layer_info =
-                    Some(LayerInfo::new(layout_root.layer_id(),
-                                        ScrollPolicy::Scrollable,
-                                        None,
-                                        root_background_color));
+                        let origin = Rect::new(Point2D::new(Au(0), Au(0)), root_size);
+                        root_stacking_context.bounds = origin;
+                        root_stacking_context.overflow = origin;
+                        root_stacking_context.layer_info =
+                            Some(LayerInfo::new(layout_root.layer_id(),
+                                                ScrollPolicy::Scrollable,
+                                                None,
+                                                root_background_color));
 
-                rw_data.display_list =
-                    Some(Arc::new(DisplayList::new(root_stacking_context, display_list_entries)))
+                        rw_data.display_list =
+                            Some(Arc::new(DisplayList::new(root_stacking_context,
+                                                           display_list_entries)))
+                    }
+                    (ReflowGoal::ForScriptQuery, false) => {}
+                }
             }
 
             if data.goal != ReflowGoal::ForDisplay {
                 // Defer the paint step until the next ForDisplay.
                 //
                 // We need to tell the document about this so it doesn't
                 // incorrectly suppress reflows. See #13131.
                 document.expect("No document in a non-display reflow?")
@@ -1033,16 +1053,22 @@ impl LayoutThread {
 
     /// The high-level routine that performs layout threads.
     fn handle_reflow<'a, 'b>(&mut self,
                              data: &ScriptReflow,
                              possibly_locked_rw_data: &mut RwData<'a, 'b>) {
         let document = unsafe { ServoLayoutNode::new(&data.document) };
         let document = document.as_document().unwrap();
 
+        // FIXME(pcwalton): Combine `ReflowGoal` and `ReflowQueryType`. Then remove this assert.
+        debug_assert!((data.reflow_info.goal == ReflowGoal::ForDisplay &&
+                       data.query_type == ReflowQueryType::NoQuery) ||
+                      (data.reflow_info.goal == ReflowGoal::ForScriptQuery &&
+                       data.query_type != ReflowQueryType::NoQuery));
+
         debug!("layout: received layout request for: {}", self.url);
 
         let mut rw_data = possibly_locked_rw_data.lock();
 
         let node: ServoLayoutNode = match document.root_node() {
             None => {
                 // Since we cannot compute anything, give spec-required placeholders.
                 debug!("layout: No root node: bailing");
@@ -1212,84 +1238,94 @@ impl LayoutThread {
         }
 
         if opts::get().dump_style_tree {
             node.dump_style();
         }
 
         // Perform post-style recalculation layout passes.
         self.perform_post_style_recalc_layout_passes(&data.reflow_info,
+                                                     Some(&data.query_type),
                                                      Some(&document),
                                                      &mut rw_data,
                                                      &mut shared_layout_context);
 
-        if let Some(mut root_flow) = self.root_flow.clone() {
-            let root_flow = flow_ref::deref_mut(&mut root_flow);
-            match data.query_type {
-                ReflowQueryType::ContentBoxQuery(node) => {
-                    let node = unsafe { ServoLayoutNode::new(&node) };
-                    rw_data.content_box_response = process_content_box_request(node, root_flow);
-                },
-                ReflowQueryType::ContentBoxesQuery(node) => {
-                    let node = unsafe { ServoLayoutNode::new(&node) };
-                    rw_data.content_boxes_response = process_content_boxes_request(node, root_flow);
-                },
-                ReflowQueryType::HitTestQuery(translated_point, client_point, update_cursor) => {
-                    let translated_point =
-                        Point2D::new(Au::from_f32_px(translated_point.x),
-                                     Au::from_f32_px(translated_point.y));
+        self.respond_to_query_if_necessary(&data.query_type,
+                                           &mut *rw_data,
+                                           &mut shared_layout_context);
+    }
 
-                    let client_point =
-                        Point2D::new(Au::from_f32_px(client_point.x),
-                                     Au::from_f32_px(client_point.y));
+    fn respond_to_query_if_necessary(&mut self,
+                                     query_type: &ReflowQueryType,
+                                     rw_data: &mut LayoutThreadData,
+                                     shared_layout_context: &mut SharedLayoutContext) {
+        let mut root_flow = match self.root_flow.clone() {
+            Some(root_flow) => root_flow,
+            None => return,
+        };
+        let root_flow = flow_ref::deref_mut(&mut root_flow);
+        match *query_type {
+            ReflowQueryType::ContentBoxQuery(node) => {
+                let node = unsafe { ServoLayoutNode::new(&node) };
+                rw_data.content_box_response = process_content_box_request(node, root_flow);
+            },
+            ReflowQueryType::ContentBoxesQuery(node) => {
+                let node = unsafe { ServoLayoutNode::new(&node) };
+                rw_data.content_boxes_response = process_content_boxes_request(node, root_flow);
+            },
+            ReflowQueryType::HitTestQuery(translated_point, client_point, update_cursor) => {
+                let translated_point = Point2D::new(Au::from_f32_px(translated_point.x),
+                                                    Au::from_f32_px(translated_point.y));
+
+                let client_point = Point2D::new(Au::from_f32_px(client_point.x),
+                                                Au::from_f32_px(client_point.y));
 
-                    let result = rw_data.display_list
-                                        .as_ref()
-                                        .expect("Tried to hit test with no display list")
-                                        .hit_test(&translated_point,
-                                                  &client_point,
-                                                  &rw_data.stacking_context_scroll_offsets);
-                    rw_data.hit_test_response = (result.last().cloned(), update_cursor);
-                },
-                ReflowQueryType::NodeGeometryQuery(node) => {
-                    let node = unsafe { ServoLayoutNode::new(&node) };
-                    rw_data.client_rect_response = process_node_geometry_request(node, root_flow);
-                },
-                ReflowQueryType::NodeScrollGeometryQuery(node) => {
-                    let node = unsafe { ServoLayoutNode::new(&node) };
-                    rw_data.scroll_area_response = process_node_scroll_area_request(node, root_flow);
-                },
-                ReflowQueryType::NodeOverflowQuery(node) => {
-                    let node = unsafe { ServoLayoutNode::new(&node) };
-                    rw_data.overflow_response = process_node_overflow_request(node);
-                },
-                ReflowQueryType::NodeLayerIdQuery(node) => {
-                    let node = unsafe { ServoLayoutNode::new(&node) };
-                    rw_data.layer_id_response = Some(process_node_layer_id_request(node));
-                },
-                ReflowQueryType::ResolvedStyleQuery(node, ref pseudo, ref property) => {
-                    let node = unsafe { ServoLayoutNode::new(&node) };
-                    let layout_context = LayoutContext::new(&shared_layout_context);
-                    rw_data.resolved_style_response =
-                        process_resolved_style_request(node,
-                                                       &layout_context,
-                                                       pseudo,
-                                                       property,
-                                                       root_flow);
-                },
-                ReflowQueryType::OffsetParentQuery(node) => {
-                    let node = unsafe { ServoLayoutNode::new(&node) };
-                    rw_data.offset_parent_response = process_offset_parent_query(node, root_flow);
-                },
-                ReflowQueryType::MarginStyleQuery(node) => {
-                    let node = unsafe { ServoLayoutNode::new(&node) };
-                    rw_data.margin_style_response = process_margin_style_query(node);
-                },
-                ReflowQueryType::NoQuery => {}
-            }
+                let result = rw_data.display_list
+                                    .as_ref()
+                                    .expect("Tried to hit test with no display list")
+                                    .hit_test(&translated_point,
+                                              &client_point,
+                                              &rw_data.stacking_context_scroll_offsets);
+                rw_data.hit_test_response = (result.last().cloned(), update_cursor);
+            },
+            ReflowQueryType::NodeGeometryQuery(node) => {
+                let node = unsafe { ServoLayoutNode::new(&node) };
+                rw_data.client_rect_response = process_node_geometry_request(node, root_flow);
+            },
+            ReflowQueryType::NodeScrollGeometryQuery(node) => {
+                let node = unsafe { ServoLayoutNode::new(&node) };
+                rw_data.scroll_area_response = process_node_scroll_area_request(node, root_flow);
+            },
+            ReflowQueryType::NodeOverflowQuery(node) => {
+                let node = unsafe { ServoLayoutNode::new(&node) };
+                rw_data.overflow_response = process_node_overflow_request(node);
+            },
+            ReflowQueryType::NodeLayerIdQuery(node) => {
+                let node = unsafe { ServoLayoutNode::new(&node) };
+                rw_data.layer_id_response = Some(process_node_layer_id_request(node));
+            },
+            ReflowQueryType::ResolvedStyleQuery(node, ref pseudo, ref property) => {
+                let node = unsafe { ServoLayoutNode::new(&node) };
+                let layout_context = LayoutContext::new(&shared_layout_context);
+                rw_data.resolved_style_response =
+                    process_resolved_style_request(node,
+                                                   &layout_context,
+                                                   pseudo,
+                                                   property,
+                                                   root_flow);
+            },
+            ReflowQueryType::OffsetParentQuery(node) => {
+                let node = unsafe { ServoLayoutNode::new(&node) };
+                rw_data.offset_parent_response = process_offset_parent_query(node, root_flow);
+            },
+            ReflowQueryType::MarginStyleQuery(node) => {
+                let node = unsafe { ServoLayoutNode::new(&node) };
+                rw_data.margin_style_response = process_margin_style_query(node);
+            },
+            ReflowQueryType::NoQuery => {}
         }
     }
 
     fn set_visible_rects<'a, 'b>(&mut self,
                                  new_visible_rects: Vec<(LayerId, Rect<Au>)>,
                                  possibly_locked_rw_data: &mut RwData<'a, 'b>)
                                  -> bool {
         let mut rw_data = possibly_locked_rw_data.lock();
@@ -1335,17 +1371,21 @@ impl LayoutThread {
             goal: ReflowGoal::ForDisplay,
             page_clip_rect: max_rect(),
         };
 
         let mut layout_context = self.build_shared_layout_context(&*rw_data,
                                                                   false,
                                                                   reflow_info.goal);
 
-        self.perform_post_main_layout_passes(&reflow_info, None, &mut *rw_data, &mut layout_context);
+        self.perform_post_main_layout_passes(&reflow_info,
+                                             None,
+                                             None,
+                                             &mut *rw_data,
+                                             &mut layout_context);
         true
     }
 
     fn set_stacking_context_scroll_states<'a, 'b>(
             &mut self,
             new_scroll_states: Vec<StackingContextScrollState>,
             possibly_locked_rw_data: &mut RwData<'a, 'b>) {
         let mut rw_data = possibly_locked_rw_data.lock();
@@ -1394,16 +1434,17 @@ impl LayoutThread {
                         animation::recalc_style_for_animations(&layout_context,
                                                                flow_ref::deref_mut(&mut root_flow),
                                                                &animations)
                     });
         }
 
         self.perform_post_style_recalc_layout_passes(&reflow_info,
                                                      None,
+                                                     None,
                                                      &mut *rw_data,
                                                      &mut layout_context);
     }
 
     fn reflow_with_newly_loaded_web_font<'a, 'b>(&mut self, possibly_locked_rw_data: &mut RwData<'a, 'b>) {
         let mut rw_data = possibly_locked_rw_data.lock();
         font_context::invalidate_font_caches();
 
@@ -1417,22 +1458,24 @@ impl LayoutThread {
                                                                   reflow_info.goal);
 
         // No need to do a style recalc here.
         if self.root_flow.is_none() {
             return
         }
         self.perform_post_style_recalc_layout_passes(&reflow_info,
                                                      None,
+                                                     None,
                                                      &mut *rw_data,
                                                      &mut layout_context);
     }
 
     fn perform_post_style_recalc_layout_passes(&mut self,
                                                data: &Reflow,
+                                               query_type: Option<&ReflowQueryType>,
                                                document: Option<&ServoLayoutDocument>,
                                                rw_data: &mut LayoutThreadData,
                                                layout_context: &mut SharedLayoutContext) {
         if let Some(mut root_flow) = self.root_flow.clone() {
             // Kick off animations if any were triggered, expire completed ones.
             animation::update_animation_state(&self.constellation_chan,
                                               &mut *self.running_animations.write().unwrap(),
                                               &mut *self.expired_animations.write().unwrap(),
@@ -1498,28 +1541,34 @@ impl LayoutThread {
                     self.profiler_metadata(),
                     self.time_profiler_chan.clone(),
                     || {
                 let layout_context = LayoutContext::new(&*layout_context);
                 sequential::store_overflow(&layout_context,
                                            flow_ref::deref_mut(&mut root_flow) as &mut Flow);
             });
 
-            self.perform_post_main_layout_passes(data, document, rw_data, layout_context);
+            self.perform_post_main_layout_passes(data,
+                                                 query_type,
+                                                 document,
+                                                 rw_data,
+                                                 layout_context);
         }
     }
 
     fn perform_post_main_layout_passes(&mut self,
                                        data: &Reflow,
+                                       query_type: Option<&ReflowQueryType>,
                                        document: Option<&ServoLayoutDocument>,
                                        rw_data: &mut LayoutThreadData,
                                        layout_context: &mut SharedLayoutContext) {
         // Build the display list if necessary, and send it to the painter.
         if let Some(mut root_flow) = self.root_flow.clone() {
             self.compute_abs_pos_and_build_display_list(data,
+                                                        query_type,
                                                         document,
                                                         flow_ref::deref_mut(&mut root_flow),
                                                         &mut *layout_context,
                                                         rw_data);
             self.first_reflow = false;
 
             if opts::get().trace_layout {
                 layout_debug::end_trace(self.generation);
@@ -1627,16 +1676,28 @@ fn get_ua_stylesheets() -> Result<UserAg
     let quirks_mode_stylesheet = try!(parse_ua_stylesheet("quirks-mode.css"));
 
     Ok(UserAgentStylesheets {
         user_or_user_agent_stylesheets: user_or_user_agent_stylesheets,
         quirks_mode_stylesheet: quirks_mode_stylesheet,
     })
 }
 
+/// Returns true if the given reflow query type needs a full, up-to-date display list to be present
+/// or false if it only needs stacking-relative positions.
+fn reflow_query_type_needs_display_list(query_type: &ReflowQueryType) -> bool {
+    match *query_type {
+        ReflowQueryType::HitTestQuery(..) => true,
+        ReflowQueryType::ContentBoxQuery(_) | ReflowQueryType::ContentBoxesQuery(_) |
+        ReflowQueryType::NodeGeometryQuery(_) | ReflowQueryType::NodeScrollGeometryQuery(_) |
+        ReflowQueryType::NodeOverflowQuery(_) | ReflowQueryType::NodeLayerIdQuery(_) |
+        ReflowQueryType::ResolvedStyleQuery(..) | ReflowQueryType::OffsetParentQuery(_) |
+        ReflowQueryType::MarginStyleQuery(_) | ReflowQueryType::NoQuery => false,
+    }
+}
 
 lazy_static! {
     static ref UA_STYLESHEETS: UserAgentStylesheets = {
         match get_ua_stylesheets() {
             Ok(stylesheets) => stylesheets,
             Err(filename) => {
                 error!("Failed to load UA stylesheet {}!", filename);
                 process::exit(1);
--- a/servo/components/script_layout_interface/restyle_damage.rs
+++ b/servo/components/script_layout_interface/restyle_damage.rs
@@ -10,41 +10,46 @@ use style::properties::ServoComputedValu
 
 bitflags! {
     #[doc = "Individual layout actions that may be necessary after restyling."]
     pub flags RestyleDamage: u8 {
         #[doc = "Repaint the node itself."]
         #[doc = "Currently unused; need to decide how this propagates."]
         const REPAINT = 0x01,
 
+        #[doc = "The stacking-context-relative position of this node or its descendants has \
+                 changed."]
+        #[doc = "Propagates both up and down the flow tree."]
+        const REPOSITION = 0x02,
+
         #[doc = "Recompute the overflow regions (bounding box of object and all descendants)."]
         #[doc = "Propagates down the flow tree because the computation is bottom-up."]
-        const STORE_OVERFLOW = 0x02,
+        const STORE_OVERFLOW = 0x04,
 
         #[doc = "Recompute intrinsic inline_sizes (minimum and preferred)."]
         #[doc = "Propagates down the flow tree because the computation is"]
         #[doc = "bottom-up."]
-        const BUBBLE_ISIZES = 0x04,
+        const BUBBLE_ISIZES = 0x08,
 
         #[doc = "Recompute actual inline-sizes and block-sizes, only taking out-of-flow children \
                  into account. \
                  Propagates up the flow tree because the computation is top-down."]
-        const REFLOW_OUT_OF_FLOW = 0x08,
+        const REFLOW_OUT_OF_FLOW = 0x10,
 
         #[doc = "Recompute actual inline_sizes and block_sizes."]
         #[doc = "Propagates up the flow tree because the computation is"]
         #[doc = "top-down."]
-        const REFLOW = 0x10,
+        const REFLOW = 0x20,
 
         #[doc = "Re-resolve generated content. \
                  Propagates up the flow tree because the computation is inorder."]
-        const RESOLVE_GENERATED_CONTENT = 0x20,
+        const RESOLVE_GENERATED_CONTENT = 0x40,
 
         #[doc = "The entire flow needs to be reconstructed."]
-        const RECONSTRUCT_FLOW = 0x40
+        const RECONSTRUCT_FLOW = 0x80
     }
 }
 
 impl TRestyleDamage for RestyleDamage {
     /// For Servo the style source is always the computed values.
     type PreExistingComputedValues = Arc<ServoComputedValues>;
 
     fn empty() -> Self {
@@ -58,68 +63,71 @@ impl TRestyleDamage for RestyleDamage {
 
     /// Returns a bitmask that represents a flow that needs to be rebuilt and
     /// reflowed.
     ///
     /// Use this instead of `RestyleDamage::all()` because
     /// `RestyleDamage::all()` will result in unnecessary sequential resolution
     /// of generated content.
     fn rebuild_and_reflow() -> RestyleDamage {
-        REPAINT | STORE_OVERFLOW | BUBBLE_ISIZES | REFLOW_OUT_OF_FLOW | REFLOW | RECONSTRUCT_FLOW
+        REPAINT | REPOSITION | STORE_OVERFLOW | BUBBLE_ISIZES | REFLOW_OUT_OF_FLOW | REFLOW |
+            RECONSTRUCT_FLOW
     }
 }
 
 impl RestyleDamage {
     /// Supposing a flow has the given `position` property and this damage,
     /// returns the damage that we should add to the *parent* of this flow.
     pub fn damage_for_parent(self, child_is_absolutely_positioned: bool) -> RestyleDamage {
         if child_is_absolutely_positioned {
-            self & (REPAINT | STORE_OVERFLOW | REFLOW_OUT_OF_FLOW | RESOLVE_GENERATED_CONTENT)
+            self & (REPAINT | REPOSITION | STORE_OVERFLOW | REFLOW_OUT_OF_FLOW |
+                    RESOLVE_GENERATED_CONTENT)
         } else {
-            self & (REPAINT | STORE_OVERFLOW | REFLOW | REFLOW_OUT_OF_FLOW |
+            self & (REPAINT | REPOSITION | STORE_OVERFLOW | REFLOW | REFLOW_OUT_OF_FLOW |
                     RESOLVE_GENERATED_CONTENT)
         }
     }
 
     /// Supposing the *parent* of a flow with the given `position` property has this damage,
     /// returns the damage that we should add to this flow.
     pub fn damage_for_child(self,
                             parent_is_absolutely_positioned: bool,
                             child_is_absolutely_positioned: bool)
                             -> RestyleDamage {
         match (parent_is_absolutely_positioned, child_is_absolutely_positioned) {
             (false, true) => {
                 // Absolute children are out-of-flow and therefore insulated from changes.
                 //
                 // FIXME(pcwalton): Au contraire, if the containing block dimensions change!
-                self & REPAINT
+                self & (REPAINT | REPOSITION)
             }
             (true, false) => {
                 // Changing the position of an absolutely-positioned block requires us to reflow
                 // its kids.
                 if self.contains(REFLOW_OUT_OF_FLOW) {
                     self | REFLOW
                 } else {
                     self
                 }
             }
             _ => {
                 // TODO(pcwalton): Take floatedness into account.
-                self & (REPAINT | REFLOW)
+                self & (REPAINT | REPOSITION | REFLOW)
             }
         }
     }
 }
 
 impl fmt::Display for RestyleDamage {
     fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
         let mut first_elem = true;
 
         let to_iter =
             [ (REPAINT, "Repaint")
+            , (REPOSITION, "Reposition")
             , (STORE_OVERFLOW, "StoreOverflow")
             , (BUBBLE_ISIZES, "BubbleISizes")
             , (REFLOW_OUT_OF_FLOW, "ReflowOutOfFlow")
             , (REFLOW, "Reflow")
             , (RESOLVE_GENERATED_CONTENT, "ResolveGeneratedContent")
             , (RECONSTRUCT_FLOW, "ReconstructFlow")
             ];
 
@@ -158,24 +166,18 @@ fn compute_damage(old: &ServoComputedVal
     let mut damage = RestyleDamage::empty();
 
     // This should check every CSS property, as enumerated in the fields of
     // http://doc.servo.org/style/properties/struct.ServoComputedValues.html
 
     // FIXME: Test somehow that every property is included.
 
     add_if_not_equal!(old, new, damage,
-                      [
-                          REPAINT,
-                          STORE_OVERFLOW,
-                          BUBBLE_ISIZES,
-                          REFLOW_OUT_OF_FLOW,
-                          REFLOW,
-                          RECONSTRUCT_FLOW
-                      ], [
+                      [REPAINT, REPOSITION, STORE_OVERFLOW, BUBBLE_ISIZES, REFLOW_OUT_OF_FLOW,
+                       REFLOW, RECONSTRUCT_FLOW], [
         get_box.float, get_box.display, get_box.position, get_counters.content,
         get_counters.counter_reset, get_counters.counter_increment,
         get_inheritedbox._servo_under_display_none,
         get_list.quotes, get_list.list_style_type,
 
         // If these text or font properties change, we need to reconstruct the flow so that
         // text shaping is re-run.
         get_inheritedtext.letter_spacing, get_inheritedtext.text_rendering,
@@ -186,26 +188,27 @@ fn compute_damage(old: &ServoComputedVal
         get_font.font_size, get_font.font_stretch,
         get_inheritedbox.direction, get_inheritedbox.writing_mode,
         get_inheritedbox.text_orientation,
         get_text.text_decoration, get_text.unicode_bidi,
         get_inheritedtable.empty_cells, get_inheritedtable.caption_side,
         get_column.column_width, get_column.column_count
     ]) || (new.get_box().display == display::T::inline &&
            add_if_not_equal!(old, new, damage,
-                             [REPAINT, STORE_OVERFLOW, BUBBLE_ISIZES, REFLOW_OUT_OF_FLOW, REFLOW,
-                              RECONSTRUCT_FLOW], [
+                             [REPAINT, REPOSITION, STORE_OVERFLOW, BUBBLE_ISIZES,
+                              REFLOW_OUT_OF_FLOW, REFLOW, RECONSTRUCT_FLOW], [
         // For inline boxes only, border/padding styles are used in flow construction (to decide
         // whether to create fragments for empty flows).
         get_border.border_top_width, get_border.border_right_width,
         get_border.border_bottom_width, get_border.border_left_width,
         get_padding.padding_top, get_padding.padding_right,
         get_padding.padding_bottom, get_padding.padding_left
     ])) || add_if_not_equal!(old, new, damage,
-                            [ REPAINT, STORE_OVERFLOW, BUBBLE_ISIZES, REFLOW_OUT_OF_FLOW, REFLOW ],
+                            [REPAINT, REPOSITION, STORE_OVERFLOW, BUBBLE_ISIZES,
+                             REFLOW_OUT_OF_FLOW, REFLOW],
         [get_border.border_top_width, get_border.border_right_width,
         get_border.border_bottom_width, get_border.border_left_width,
         get_margin.margin_top, get_margin.margin_right,
         get_margin.margin_bottom, get_margin.margin_left,
         get_padding.padding_top, get_padding.padding_right,
         get_padding.padding_bottom, get_padding.padding_left,
         get_position.width, get_position.height,
         get_inheritedtext.line_height,
@@ -220,39 +223,40 @@ fn compute_damage(old: &ServoComputedVal
         get_position.align_items,
         get_position.align_content,
         get_position.order,
         get_position.flex_basis,
         get_position.flex_grow,
         get_position.flex_shrink,
         get_position.align_self
     ]) || add_if_not_equal!(old, new, damage,
-                            [ REPAINT, STORE_OVERFLOW, REFLOW_OUT_OF_FLOW ], [
+                            [REPAINT, REPOSITION, STORE_OVERFLOW, REFLOW_OUT_OF_FLOW], [
         get_position.top, get_position.left,
-        get_position.right, get_position.bottom
+        get_position.right, get_position.bottom,
+        get_effects.opacity,
+        get_effects.transform, get_effects.transform_style, get_effects.transform_origin,
+        get_effects.perspective, get_effects.perspective_origin
     ]) || add_if_not_equal!(old, new, damage,
-                            [ REPAINT ], [
+                            [REPAINT], [
         get_color.color, get_background.background_color,
         get_background.background_image, get_background.background_position,
         get_background.background_repeat, get_background.background_attachment,
         get_background.background_clip, get_background.background_origin,
         get_background.background_size,
         get_border.border_top_color, get_border.border_right_color,
         get_border.border_bottom_color, get_border.border_left_color,
         get_border.border_top_style, get_border.border_right_style,
         get_border.border_bottom_style, get_border.border_left_style,
         get_border.border_top_left_radius, get_border.border_top_right_radius,
         get_border.border_bottom_left_radius, get_border.border_bottom_right_radius,
         get_position.z_index, get_box._servo_overflow_clip_box,
         get_inheritedtext._servo_text_decorations_in_effect,
         get_pointing.cursor, get_pointing.pointer_events,
         get_effects.box_shadow, get_effects.clip, get_inheritedtext.text_shadow, get_effects.filter,
-        get_effects.transform, get_effects.backface_visibility, get_effects.transform_style,
-        get_effects.transform_origin, get_effects.perspective, get_effects.perspective_origin,
-        get_effects.mix_blend_mode, get_effects.opacity, get_inheritedbox.image_rendering,
+        get_effects.mix_blend_mode, get_inheritedbox.image_rendering,
 
         // Note: May require REFLOW et al. if `visibility: collapse` is implemented.
         get_inheritedbox.visibility
     ]);
 
     // If the layer requirements of this flow have changed due to the value
     // of the transform, then reflow is required to rebuild the layers.
     if old.transform_requires_layer() != new.transform_requires_layer() {