servo: Merge #11754 - Move ServoLayoutNode and related structs to script (from Ms2ger:wrapper-traits-prep2); r=nox
authorMs2ger <Ms2ger@gmail.com>
Mon, 20 Jun 2016 12:54:20 -0500
changeset 339099 b60e2d7f53f7ace2d4c8117694a41f248efb2fc7
parent 339098 239ae4b98aa0c9752ad375b84a052605417cd461
child 339100 0e24a90fd529e68765b0904a64303d451a135474
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)
reviewersnox
servo: Merge #11754 - Move ServoLayoutNode and related structs to script (from Ms2ger:wrapper-traits-prep2); r=nox Source-Repo: https://github.com/servo/servo Source-Revision: ee8c5c5a67e1181eca163d3cd1189866d230b8f0
servo/components/gfx/text/glyph.rs
servo/components/gfx_traits/Cargo.toml
servo/components/gfx_traits/lib.rs
servo/components/layout/Cargo.toml
servo/components/layout/animation.rs
servo/components/layout/block.rs
servo/components/layout/construct.rs
servo/components/layout/data.rs
servo/components/layout/flex.rs
servo/components/layout/flow.rs
servo/components/layout/fragment.rs
servo/components/layout/generated_content.rs
servo/components/layout/incremental.rs
servo/components/layout/inline.rs
servo/components/layout/layout_thread.rs
servo/components/layout/lib.rs
servo/components/layout/list_item.rs
servo/components/layout/opaque_node.rs
servo/components/layout/query.rs
servo/components/layout/sequential.rs
servo/components/layout/table.rs
servo/components/layout/table_cell.rs
servo/components/layout/traversal.rs
servo/components/layout/wrapper.rs
servo/components/script/Cargo.toml
servo/components/script/dom/bindings/js.rs
servo/components/script/dom/bindings/trace.rs
servo/components/script/dom/document.rs
servo/components/script/dom/htmlcanvaselement.rs
servo/components/script/dom/htmliframeelement.rs
servo/components/script/dom/htmllinkelement.rs
servo/components/script/dom/htmlobjectelement.rs
servo/components/script/dom/htmlstyleelement.rs
servo/components/script/dom/node.rs
servo/components/script/dom/window.rs
servo/components/script/layout_interface.rs
servo/components/script/layout_wrapper.rs
servo/components/script/lib.rs
servo/components/script/reporter.rs
servo/components/script/script_thread.rs
servo/components/script_layout_interface/Cargo.toml
servo/components/script_layout_interface/lib.rs
servo/components/script_layout_interface/message.rs
servo/components/script_layout_interface/reporter.rs
servo/components/script_layout_interface/restyle_damage.rs
servo/components/script_layout_interface/rpc.rs
servo/components/script_layout_interface/wrapper_traits.rs
servo/components/servo/Cargo.lock
servo/components/servo/Cargo.toml
servo/components/servo/lib.rs
servo/ports/cef/Cargo.lock
servo/tests/unit/layout/size_of.rs
servo/tests/unit/script/size_of.rs
--- a/servo/components/gfx/text/glyph.rs
+++ b/servo/components/gfx/text/glyph.rs
@@ -6,16 +6,18 @@ use app_units::Au;
 use euclid::point::Point2D;
 use range::{self, EachIndex, Range, RangeIndex};
 #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
 use simd::u32x4;
 use std::cmp::{Ordering, PartialOrd};
 use std::vec::Vec;
 use std::{fmt, mem, u16};
 
+pub use gfx_traits::ByteIndex;
+
 /// GlyphEntry is a port of Gecko's CompressedGlyph scheme for storing glyph data compactly.
 ///
 /// In the common case (reasonable glyph advances, no offsets from the font em-box, and one glyph
 /// per character), we pack glyph advance, glyph id, and some flags into a single u32.
 ///
 /// In the uncommon case (multiple glyphs per unicode character, large glyph index/advance, or
 /// glyph offsets), we pack the glyph count into GlyphEntry, and store the other glyph information
 /// in DetailedGlyphStore.
@@ -421,24 +423,16 @@ pub struct GlyphStore {
     total_spaces: i32,
 
     /// Used to check if fast path should be used in glyph iteration.
     has_detailed_glyphs: bool,
     is_whitespace: bool,
     is_rtl: bool,
 }
 
-int_range_index! {
-    #[derive(Deserialize, Serialize, RustcEncodable)]
-    #[doc = "An index that refers to a byte offset in a text run. This could \
-             point to the middle of a glyph."]
-    #[derive(HeapSizeOf)]
-    struct ByteIndex(isize)
-}
-
 impl<'a> GlyphStore {
     /// Initializes the glyph store, but doesn't actually shape anything.
     ///
     /// Use the `add_*` methods to store glyph data.
     pub fn new(length: usize, is_whitespace: bool, is_rtl: bool) -> GlyphStore {
         assert!(length > 0);
 
         GlyphStore {
--- a/servo/components/gfx_traits/Cargo.toml
+++ b/servo/components/gfx_traits/Cargo.toml
@@ -8,13 +8,15 @@ publish = false
 name = "gfx_traits"
 path = "lib.rs"
 
 [dependencies]
 azure = {git = "https://github.com/servo/rust-azure", features = ["plugins"]}
 layers = {git = "https://github.com/servo/rust-layers", features = ["plugins"]}
 msg = {path = "../msg"}
 plugins = {path = "../plugins"}
+range = {path = "../range"}
+rustc-serialize = "0.3"
 euclid = {version = "0.6.5", features = ["plugins"]}
 heapsize = "0.3.0"
 heapsize_plugin = "0.1.2"
 serde = "0.7"
 serde_macros = "0.7"
--- a/servo/components/gfx_traits/lib.rs
+++ b/servo/components/gfx_traits/lib.rs
@@ -10,26 +10,30 @@
 
 #![deny(unsafe_code)]
 
 extern crate azure;
 extern crate euclid;
 extern crate heapsize;
 extern crate layers;
 extern crate msg;
+#[macro_use]
+extern crate range;
+extern crate rustc_serialize;
 extern crate serde;
 
 pub mod color;
 mod paint_listener;
 
 pub use paint_listener::PaintListener;
 use azure::azure_hl::Color;
 use euclid::Matrix4D;
 use euclid::rect::Rect;
 use msg::constellation_msg::PipelineId;
+use range::RangeIndex;
 use std::fmt::{self, Debug, Formatter};
 use std::sync::atomic::{ATOMIC_USIZE_INIT, AtomicUsize, Ordering};
 
 /// The next ID that will be used for a special stacking context.
 ///
 /// A special stacking context is a stacking context that is one of (a) the outer stacking context
 /// of an element with `overflow: scroll`; (b) generated content; (c) both (a) and (b).
 static NEXT_SPECIAL_STACKING_CONTEXT_ID: AtomicUsize = ATOMIC_USIZE_INIT;
@@ -250,8 +254,15 @@ impl FragmentType {
         match n {
             0 => FragmentType::FragmentBody,
             1 => FragmentType::BeforePseudoContent,
             _ => FragmentType::AfterPseudoContent,
         }
     }
 }
 
+int_range_index! {
+    #[derive(Deserialize, Serialize, RustcEncodable)]
+    #[doc = "An index that refers to a byte offset in a text run. This could \
+             point to the middle of a glyph."]
+    #[derive(HeapSizeOf)]
+    struct ByteIndex(isize)
+}
--- a/servo/components/layout/Cargo.toml
+++ b/servo/components/layout/Cargo.toml
@@ -26,16 +26,17 @@ libc = "0.2"
 log = "0.3.5"
 msg = {path = "../msg"}
 net_traits = {path = "../net_traits"}
 plugins = {path = "../plugins"}
 profile_traits = {path = "../profile_traits"}
 range = {path = "../range"}
 rustc-serialize = "0.3"
 script = {path = "../script"}
+script_layout_interface = {path = "../script_layout_interface"}
 script_traits = {path = "../script_traits"}
 selectors = {version = "0.6", features = ["heap_size"]}
 serde_json = "0.7"
 serde_macros = "0.7"
 smallvec = "0.1"
 string_cache = {version = "0.2.20", features = ["heap_size"]}
 style = {path = "../style"}
 style_traits = {path = "../style_traits"}
--- a/servo/components/layout/animation.rs
+++ b/servo/components/layout/animation.rs
@@ -1,19 +1,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! CSS transitions and animations.
 
 use flow::{self, Flow};
 use gfx::display_list::OpaqueNode;
-use incremental::RestyleDamage;
 use ipc_channel::ipc::IpcSender;
 use msg::constellation_msg::PipelineId;
+use script_layout_interface::restyle_damage::RestyleDamage;
 use script_traits::{AnimationState, LayoutMsg as ConstellationMsg};
 use std::collections::HashMap;
 use std::collections::hash_map::Entry;
 use std::sync::mpsc::Receiver;
 use style::animation::{Animation, update_style_for_animation};
 use time;
 
 /// Processes any new animations that were discovered after style recalculation.
--- a/servo/components/layout/block.rs
+++ b/servo/components/layout/block.rs
@@ -40,22 +40,22 @@ use flow::{ImmutableFlowUtils, LateAbsol
 use flow::{NEEDS_LAYER, PreorderFlowTraversal, FragmentationContext};
 use flow::{self, BaseFlow, EarlyAbsolutePositionInfo, Flow, FlowClass, ForceNonfloatedFlag};
 use flow_list::FlowList;
 use flow_ref::FlowRef;
 use fragment::SpecificFragmentInfo;
 use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, HAS_LAYER, Overflow};
 use gfx::display_list::{ClippingRegion, StackingContext};
 use gfx_traits::{LayerId, StackingContextId};
-use incremental::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW, REPAINT};
 use layout_debug;
 use layout_thread::DISPLAY_PORT_SIZE_FACTOR;
 use model::{CollapsibleMargins, MaybeAuto, specified, specified_or_none};
 use model::{self, IntrinsicISizes, MarginCollapseInfo};
 use rustc_serialize::{Encodable, Encoder};
+use script_layout_interface::restyle_damage::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW, REPAINT};
 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::StyleContext;
 use style::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize, WritingMode};
 use style::properties::{ComputedValues, ServoComputedValues};
--- a/servo/components/layout/construct.rs
+++ b/servo/components/layout/construct.rs
@@ -23,25 +23,24 @@ use flow::{MutableFlowUtils, MutableOwne
 use flow::{self, AbsoluteDescendants, IS_ABSOLUTELY_POSITIONED, ImmutableFlowUtils};
 use flow_ref::{self, FlowRef};
 use fragment::WhitespaceStrippingResult;
 use fragment::{CanvasFragmentInfo, ImageFragmentInfo, InlineAbsoluteFragmentInfo};
 use fragment::{Fragment, GeneratedContentInfo, IframeFragmentInfo};
 use fragment::{InlineAbsoluteHypotheticalFragmentInfo, TableColumnFragmentInfo};
 use fragment::{InlineBlockFragmentInfo, SpecificFragmentInfo, UnscannedTextFragmentInfo};
 use gfx::display_list::OpaqueNode;
-use incremental::{BUBBLE_ISIZES, RECONSTRUCT_FLOW, RestyleDamage};
 use inline::{FIRST_FRAGMENT_OF_ELEMENT, InlineFlow, InlineFragmentNodeFlags};
 use inline::{InlineFragmentNodeInfo, LAST_FRAGMENT_OF_ELEMENT};
 use list_item::{ListItemFlow, ListStyleTypeContent};
 use multicol::{MulticolFlow, MulticolColumnFlow};
 use parallel;
-use script::layout_interface::is_image_data;
-use script::layout_interface::{CharacterDataTypeId, ElementTypeId};
-use script::layout_interface::{HTMLElementTypeId, NodeTypeId};
+use script_layout_interface::restyle_damage::{BUBBLE_ISIZES, RECONSTRUCT_FLOW, RestyleDamage};
+use script_layout_interface::wrapper_traits::{ThreadSafeLayoutNode, PseudoElementType, ThreadSafeLayoutElement};
+use script_layout_interface::{LayoutNodeType, LayoutElementType, is_image_data};
 use std::borrow::ToOwned;
 use std::collections::LinkedList;
 use std::marker::PhantomData;
 use std::mem;
 use std::sync::Arc;
 use std::sync::atomic::Ordering;
 use style::computed_values::content::ContentItem;
 use style::computed_values::position;
@@ -55,17 +54,17 @@ use table_colgroup::TableColGroupFlow;
 use table_row::TableRowFlow;
 use table_rowgroup::TableRowGroupFlow;
 use table_wrapper::TableWrapperFlow;
 use text::TextRunScanner;
 use traversal::PostorderNodeMutTraversal;
 use url::Url;
 use util::linked_list;
 use util::opts;
-use wrapper::{PseudoElementType, TextContent, ThreadSafeLayoutElement, ThreadSafeLayoutNode};
+use wrapper::{TextContent, ThreadSafeLayoutNodeHelpers};
 
 /// The results of flow construction for a DOM node.
 #[derive(Clone)]
 pub enum ConstructionResult {
     /// This node contributes nothing at all (`display: none`). Alternately, this is what newly
     /// created nodes have their `ConstructionResult` set to.
     None,
 
@@ -298,54 +297,45 @@ impl<'a, ConcreteThreadSafeLayoutNode: T
                                     node: &ConcreteThreadSafeLayoutNode,
                                     result: ConstructionResult) {
         node.set_flow_construction_result(result);
     }
 
     /// Builds the fragment for the given block or subclass thereof.
     fn build_fragment_for_block(&mut self, node: &ConcreteThreadSafeLayoutNode) -> Fragment {
         let specific_fragment_info = match node.type_id() {
-            Some(NodeTypeId::Element(ElementTypeId::HTMLElement(
-                        HTMLElementTypeId::HTMLIFrameElement))) => {
+            Some(LayoutNodeType::Element(LayoutElementType::HTMLIFrameElement)) => {
                 SpecificFragmentInfo::Iframe(IframeFragmentInfo::new(node))
             }
-            Some(NodeTypeId::Element(ElementTypeId::HTMLElement(
-                        HTMLElementTypeId::HTMLImageElement))) => {
+            Some(LayoutNodeType::Element(LayoutElementType::HTMLImageElement)) => {
                 let image_info = box ImageFragmentInfo::new(node,
                                                             node.image_url(),
                                                             &self.layout_context);
                 SpecificFragmentInfo::Image(image_info)
             }
-            Some(NodeTypeId::Element(ElementTypeId::HTMLElement(
-                        HTMLElementTypeId::HTMLObjectElement))) => {
+            Some(LayoutNodeType::Element(LayoutElementType::HTMLObjectElement)) => {
                 let image_info = box ImageFragmentInfo::new(node,
                                                             node.object_data(),
                                                             &self.layout_context);
                 SpecificFragmentInfo::Image(image_info)
             }
-            Some(NodeTypeId::Element(ElementTypeId::HTMLElement(
-                        HTMLElementTypeId::HTMLTableElement))) => {
+            Some(LayoutNodeType::Element(LayoutElementType::HTMLTableElement)) => {
                 SpecificFragmentInfo::TableWrapper
             }
-            Some(NodeTypeId::Element(ElementTypeId::HTMLElement(
-                        HTMLElementTypeId::HTMLTableColElement))) => {
+            Some(LayoutNodeType::Element(LayoutElementType::HTMLTableColElement)) => {
                 SpecificFragmentInfo::TableColumn(TableColumnFragmentInfo::new(node))
             }
-            Some(NodeTypeId::Element(ElementTypeId::HTMLElement(
-                        HTMLElementTypeId::HTMLTableCellElement(_)))) => {
+            Some(LayoutNodeType::Element(LayoutElementType::HTMLTableCellElement)) => {
                 SpecificFragmentInfo::TableCell
             }
-            Some(NodeTypeId::Element(ElementTypeId::HTMLElement(
-                        HTMLElementTypeId::HTMLTableRowElement))) |
-            Some(NodeTypeId::Element(ElementTypeId::HTMLElement(
-                        HTMLElementTypeId::HTMLTableSectionElement))) => {
+            Some(LayoutNodeType::Element(LayoutElementType::HTMLTableRowElement)) |
+            Some(LayoutNodeType::Element(LayoutElementType::HTMLTableSectionElement)) => {
                 SpecificFragmentInfo::TableRow
             }
-            Some(NodeTypeId::Element(ElementTypeId::HTMLElement(
-                        HTMLElementTypeId::HTMLCanvasElement))) => {
+            Some(LayoutNodeType::Element(LayoutElementType::HTMLCanvasElement)) => {
                 let data = node.canvas_data().unwrap();
                 SpecificFragmentInfo::Canvas(box CanvasFragmentInfo::new(node, data, self.layout_context))
             }
             _ => {
                 // This includes pseudo-elements.
                 SpecificFragmentInfo::Generic
             }
         };
@@ -684,26 +674,23 @@ impl<'a, ConcreteThreadSafeLayoutNode: T
     /// * `<input>` and `<textarea>` elements get their content.
     ///
     /// FIXME(pcwalton): It is not clear to me that there isn't a cleaner way to handle
     /// `<textarea>`.
     fn build_flow_for_block_like(&mut self, flow: FlowRef, node: &ConcreteThreadSafeLayoutNode)
                                  -> ConstructionResult {
         let mut initial_fragments = IntermediateInlineFragments::new();
         let node_is_input_or_text_area =
-           node.type_id() == Some(NodeTypeId::Element(ElementTypeId::HTMLElement(
-                       HTMLElementTypeId::HTMLInputElement))) ||
-           node.type_id() == Some(NodeTypeId::Element(ElementTypeId::HTMLElement(
-                       HTMLElementTypeId::HTMLTextAreaElement)));
+           node.type_id() == Some(LayoutNodeType::Element(LayoutElementType::HTMLInputElement)) ||
+           node.type_id() == Some(LayoutNodeType::Element(LayoutElementType::HTMLTextAreaElement));
         if node.get_pseudo_element_type().is_replaced_content() ||
                 node_is_input_or_text_area {
             // A TextArea's text contents are displayed through the input text
             // box, so don't construct them.
-            if node.type_id() == Some(NodeTypeId::Element(ElementTypeId::HTMLElement(
-                        HTMLElementTypeId::HTMLTextAreaElement))) {
+            if node.type_id() == Some(LayoutNodeType::Element(LayoutElementType::HTMLTextAreaElement)) {
                 for kid in node.children() {
                     self.set_flow_construction_result(&kid, ConstructionResult::None)
                 }
             }
 
             let mut style = node.style(self.style_context()).clone();
             if node_is_input_or_text_area {
                 properties::modify_style_for_input_text(&mut style);
@@ -965,17 +952,17 @@ impl<'a, ConcreteThreadSafeLayoutNode: T
         let mut style = (*node.style(self.style_context())).clone();
         properties::modify_style_for_replaced_content(&mut style);
 
         // If this is generated content, then we need to initialize the accumulator with the
         // fragment corresponding to that content. Otherwise, just initialize with the ordinary
         // fragment that needs to be generated for this inline node.
         let mut fragments = IntermediateInlineFragments::new();
         match (node.get_pseudo_element_type(), node.type_id()) {
-            (_, Some(NodeTypeId::CharacterData(CharacterDataTypeId::Text))) => {
+            (_, Some(LayoutNodeType::Text)) => {
                 self.create_fragments_for_node_text_content(&mut fragments, node, &style)
             }
             (PseudoElementType::Normal, _) => {
                 fragments.fragments.push_back(self.build_fragment_for_block(node));
             }
             (_, _) => self.create_fragments_for_node_text_content(&mut fragments, node, &style),
         }
 
@@ -1519,32 +1506,32 @@ impl<'a, ConcreteThreadSafeLayoutNode> P
                     PseudoElementType::Before(maybe_display) |
                     PseudoElementType::After(maybe_display) |
                     PseudoElementType::DetailsContent(maybe_display) |
                     PseudoElementType::DetailsSummary(maybe_display)
                         => maybe_display.unwrap_or(style.get_box().display),
                 };
                 (display, style.get_box().float, style.get_box().position)
             }
-            Some(NodeTypeId::Element(_)) => {
+            Some(LayoutNodeType::Element(_)) => {
                 let style = node.style(self.style_context());
                 let original_display = style.get_box()._servo_display_for_hypothetical_box;
                 let munged_display = match original_display {
                     display::T::inline | display::T::inline_block => original_display,
                     _ => style.get_box().display,
                 };
                 (munged_display, style.get_box().float, style.get_box().position)
             }
-            Some(NodeTypeId::CharacterData(CharacterDataTypeId::Text)) =>
+            Some(LayoutNodeType::Text) =>
                 (display::T::inline, float::T::none, position::T::static_),
-            Some(NodeTypeId::CharacterData(CharacterDataTypeId::Comment)) |
-            Some(NodeTypeId::CharacterData(CharacterDataTypeId::ProcessingInstruction)) |
-            Some(NodeTypeId::DocumentType) |
-            Some(NodeTypeId::DocumentFragment) |
-            Some(NodeTypeId::Document(_)) => {
+            Some(LayoutNodeType::Comment) |
+            Some(LayoutNodeType::ProcessingInstruction) |
+            Some(LayoutNodeType::DocumentType) |
+            Some(LayoutNodeType::DocumentFragment) |
+            Some(LayoutNodeType::Document) => {
                 (display::T::none, float::T::none, position::T::static_)
             }
         };
 
         debug!("building flow for node: {:?} {:?} {:?} {:?}", display, float, positioning, node.type_id());
 
         // Switch on display and floatedness.
         match (display, float, positioning) {
@@ -1673,29 +1660,27 @@ trait NodeUtils {
     /// the old value.
     fn swap_out_construction_result(self) -> ConstructionResult;
 }
 
 impl<ConcreteThreadSafeLayoutNode> NodeUtils for ConcreteThreadSafeLayoutNode
                                              where ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode {
     fn is_replaced_content(&self) -> bool {
         match self.type_id() {
-            Some(NodeTypeId::CharacterData(_)) |
-            Some(NodeTypeId::DocumentType) |
-            Some(NodeTypeId::DocumentFragment) |
-            Some(NodeTypeId::Document(_)) |
-            Some(NodeTypeId::Element(ElementTypeId::HTMLElement(
-                        HTMLElementTypeId::HTMLImageElement))) |
-            Some(NodeTypeId::Element(ElementTypeId::HTMLElement(
-                        HTMLElementTypeId::HTMLIFrameElement))) |
-            Some(NodeTypeId::Element(ElementTypeId::HTMLElement(
-                        HTMLElementTypeId::HTMLCanvasElement))) => true,
-            Some(NodeTypeId::Element(ElementTypeId::HTMLElement(
-                        HTMLElementTypeId::HTMLObjectElement))) => self.has_object_data(),
-            Some(NodeTypeId::Element(_)) => false,
+            Some(LayoutNodeType::Comment) |
+            Some(LayoutNodeType::ProcessingInstruction) |
+            Some(LayoutNodeType::Text) |
+            Some(LayoutNodeType::DocumentType) |
+            Some(LayoutNodeType::DocumentFragment) |
+            Some(LayoutNodeType::Document) |
+            Some(LayoutNodeType::Element(LayoutElementType::HTMLImageElement)) |
+            Some(LayoutNodeType::Element(LayoutElementType::HTMLIFrameElement)) |
+            Some(LayoutNodeType::Element(LayoutElementType::HTMLCanvasElement)) => true,
+            Some(LayoutNodeType::Element(LayoutElementType::HTMLObjectElement)) => self.has_object_data(),
+            Some(LayoutNodeType::Element(_)) => false,
             None => self.get_pseudo_element_type().is_replaced_content(),
         }
     }
 
     fn construction_result_mut(self, data: &mut PrivateLayoutData) -> &mut ConstructionResult {
         match self.get_pseudo_element_type() {
             PseudoElementType::Before(_) => &mut data.before_flow_construction_result,
             PseudoElementType::After (_) => &mut data.after_flow_construction_result,
--- a/servo/components/layout/data.rs
+++ b/servo/components/layout/data.rs
@@ -1,14 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use construct::ConstructionResult;
-use incremental::RestyleDamage;
+use script_layout_interface::restyle_damage::RestyleDamage;
 use style::servo::PrivateStyleData;
 
 /// Data that layout associates with a node.
 pub struct PrivateLayoutData {
     /// Data that the style system associates with a node. When the
     /// style system is being used standalone, this is all that hangs
     /// off the node. This must be first to permit the various
     /// transmuations between PrivateStyleData PrivateLayoutData.
--- a/servo/components/layout/flex.rs
+++ b/servo/components/layout/flex.rs
@@ -14,19 +14,19 @@ use euclid::Point2D;
 use floats::FloatKind;
 use flow;
 use flow::{Flow, FlowClass, ImmutableFlowUtils, OpaqueFlow};
 use flow::{INLINE_POSITION_IS_STATIC, IS_ABSOLUTELY_POSITIONED};
 use flow_ref::{self, FlowRef};
 use fragment::{Fragment, FragmentBorderBoxIterator, Overflow};
 use gfx::display_list::StackingContext;
 use gfx_traits::StackingContextId;
-use incremental::{REFLOW, REFLOW_OUT_OF_FLOW};
 use layout_debug;
 use model::{IntrinsicISizes, MaybeAuto, MinMaxConstraint};
+use script_layout_interface::restyle_damage::{REFLOW, REFLOW_OUT_OF_FLOW};
 use std::cmp::max;
 use std::sync::Arc;
 use style::computed_values::flex_direction;
 use style::logical_geometry::LogicalSize;
 use style::properties::{ComputedValues, ServoComputedValues};
 use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto, LengthOrPercentageOrNone};
 
 /// The size of an axis. May be a specified size, a min/max
--- a/servo/components/layout/flow.rs
+++ b/servo/components/layout/flow.rs
@@ -31,22 +31,23 @@ use context::LayoutContext;
 use display_list_builder::DisplayListBuildState;
 use euclid::{Point2D, Rect, Size2D};
 use floats::{Floats, SpeculatedFloatPlacement};
 use flow_list::{FlowList, FlowListIterator, MutFlowListIterator};
 use flow_ref::{self, FlowRef, WeakFlowRef};
 use fragment::{Fragment, FragmentBorderBoxIterator, Overflow, SpecificFragmentInfo};
 use gfx::display_list::{ClippingRegion, StackingContext};
 use gfx_traits::{LayerId, LayerType, StackingContextId};
-use incremental::{RECONSTRUCT_FLOW, REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, RestyleDamage};
 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::wrapper_traits::{PseudoElementType, ThreadSafeLayoutNode};
 use std::iter::Zip;
 use std::slice::IterMut;
 use std::sync::Arc;
 use std::sync::atomic::Ordering;
 use std::{fmt, mem, raw};
 use style::computed_values::{clear, display, empty_cells, float, position, overflow_x, text_align};
 use style::dom::TRestyleDamage;
 use style::logical_geometry::{LogicalRect, LogicalSize, WritingMode};
@@ -55,17 +56,16 @@ use style::values::computed::LengthOrPer
 use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize, TableFlow};
 use table_caption::TableCaptionFlow;
 use table_cell::TableCellFlow;
 use table_colgroup::TableColGroupFlow;
 use table_row::TableRowFlow;
 use table_rowgroup::TableRowGroupFlow;
 use table_wrapper::TableWrapperFlow;
 use util::print_tree::PrintTree;
-use wrapper::{PseudoElementType, ThreadSafeLayoutNode};
 
 /// Virtual methods that make up a float context.
 ///
 /// Note that virtual methods have a cost; we should not overuse them in Servo. Consider adding
 /// methods to `ImmutableFlowUtils` or `MutableFlowUtils` before adding more methods here.
 pub trait Flow: fmt::Debug + Sync + Send + 'static {
     // RTTI
     //
--- a/servo/components/layout/fragment.rs
+++ b/servo/components/layout/fragment.rs
@@ -13,29 +13,30 @@ use euclid::{Point2D, Rect, Size2D};
 use floats::ClearType;
 use flow::{self, ImmutableFlowUtils};
 use flow_ref::{self, FlowRef};
 use gfx;
 use gfx::display_list::{BLUR_INFLATION_FACTOR, OpaqueNode};
 use gfx::text::glyph::ByteIndex;
 use gfx::text::text_run::{TextRun, TextRunSlice};
 use gfx_traits::{FragmentType, LayerId, LayerType, StackingContextId};
-use incremental::{RECONSTRUCT_FLOW, RestyleDamage};
 use inline::{FIRST_FRAGMENT_OF_ELEMENT, InlineFragmentContext, InlineFragmentNodeInfo};
 use inline::{InlineMetrics, LAST_FRAGMENT_OF_ELEMENT};
 use ipc_channel::ipc::IpcSender;
 #[cfg(debug_assertions)]
 use layout_debug;
 use model::{self, IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto, specified};
 use msg::constellation_msg::PipelineId;
 use net_traits::image::base::{Image, ImageMetadata};
 use net_traits::image_cache_thread::{ImageOrMetadataAvailable, UsePlaceholder};
 use range::*;
 use rustc_serialize::{Encodable, Encoder};
-use script::layout_interface::HTMLCanvasData;
+use script_layout_interface::HTMLCanvasData;
+use script_layout_interface::restyle_damage::{RECONSTRUCT_FLOW, RestyleDamage};
+use script_layout_interface::wrapper_traits::{PseudoElementType, ThreadSafeLayoutElement, ThreadSafeLayoutNode};
 use std::borrow::ToOwned;
 use std::cmp::{max, min};
 use std::collections::LinkedList;
 use std::fmt;
 use std::sync::{Arc, Mutex};
 use style::computed_values::content::ContentItem;
 use style::computed_values::{border_collapse, clear, display, mix_blend_mode, overflow_wrap};
 use style::computed_values::{overflow_x, position, text_decoration, transform_style};
@@ -44,17 +45,16 @@ use style::dom::TRestyleDamage;
 use style::logical_geometry::{LogicalMargin, LogicalRect, LogicalSize, WritingMode};
 use style::properties::{ComputedValues, ServoComputedValues};
 use style::values::computed::LengthOrPercentageOrNone;
 use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto};
 use text;
 use text::TextRunScanner;
 use url::Url;
 use util;
-use wrapper::{PseudoElementType, ThreadSafeLayoutElement, ThreadSafeLayoutNode};
 
 /// Fragments (`struct Fragment`) are the leaves of the layout tree. They cannot position
 /// themselves. In general, fragments do not have a simple correspondence with CSS fragments in the
 /// specification:
 ///
 /// * Several fragments may correspond to the same CSS box or DOM node. For example, a CSS text box
 /// broken across two lines is represented by two fragments.
 ///
--- a/servo/components/layout/generated_content.rs
+++ b/servo/components/layout/generated_content.rs
@@ -8,26 +8,26 @@
 //! done in parallel and is therefore a sequential pass that runs on as little of the flow tree
 //! as possible.
 
 use context::LayoutContext;
 use flow::InorderFlowTraversal;
 use flow::{self, AFFECTS_COUNTERS, Flow, HAS_COUNTER_AFFECTING_CHILDREN, ImmutableFlowUtils};
 use fragment::{Fragment, GeneratedContentInfo, SpecificFragmentInfo, UnscannedTextFragmentInfo};
 use gfx::display_list::OpaqueNode;
-use incremental::{RESOLVE_GENERATED_CONTENT, RestyleDamage};
+use script_layout_interface::restyle_damage::{RESOLVE_GENERATED_CONTENT, RestyleDamage};
+use script_layout_interface::wrapper_traits::PseudoElementType;
 use smallvec::SmallVec;
 use std::collections::{HashMap, LinkedList};
 use std::sync::Arc;
 use style::computed_values::content::ContentItem;
 use style::computed_values::{display, list_style_type};
 use style::dom::TRestyleDamage;
 use style::properties::{ComputedValues, ServoComputedValues};
 use text::TextRunScanner;
-use wrapper::PseudoElementType;
 
 // Decimal styles per CSS-COUNTER-STYLES § 6.1:
 static DECIMAL: [char; 10] = [ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' ];
 // TODO(pcwalton): `decimal-leading-zero`
 static ARABIC_INDIC: [char; 10] = [ '٠', '١', '٢', '٣', '٤', '٥', '٦', '٧', '٨', '٩' ];
 // TODO(pcwalton): `armenian`, `upper-armenian`, `lower-armenian`
 static BENGALI: [char; 10] = [ '০', '১', '২', '৩', '৪', '৫', '৬', '৭', '৮', '৯' ];
 static CAMBODIAN: [char; 10] = [ '០', '១', '២', '៣', '៤', '៥', '៦', '៧', '៨', '៩' ];
--- a/servo/components/layout/incremental.rs
+++ b/servo/components/layout/incremental.rs
@@ -1,277 +1,25 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use flow::{self, AFFECTS_COUNTERS, Flow, HAS_COUNTER_AFFECTING_CHILDREN, IS_ABSOLUTELY_POSITIONED};
-use std::fmt;
-use std::sync::Arc;
-use style::computed_values::{display, float};
+use script_layout_interface::restyle_damage::{RestyleDamage, REFLOW, RECONSTRUCT_FLOW};
+use style::computed_values::float;
 use style::dom::TRestyleDamage;
-use style::properties::{ComputedValues, ServoComputedValues};
-
-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 = "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,
-
-        #[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,
-
-        #[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,
-
-        #[doc = "Recompute actual inline_sizes and block_sizes."]
-        #[doc = "Propagates up the flow tree because the computation is"]
-        #[doc = "top-down."]
-        const REFLOW = 0x10,
-
-        #[doc = "Re-resolve generated content. \
-                 Propagates up the flow tree because the computation is inorder."]
-        const RESOLVE_GENERATED_CONTENT = 0x20,
-
-        #[doc = "The entire flow needs to be reconstructed."]
-        const RECONSTRUCT_FLOW = 0x40
-    }
-}
 
 bitflags! {
     pub flags SpecialRestyleDamage: u8 {
         #[doc = "If this flag is set, we need to reflow the entire document. This is more or less a \
                  temporary hack to deal with cases that we don't handle incrementally yet."]
         const REFLOW_ENTIRE_DOCUMENT = 0x01,
     }
 }
 
-impl TRestyleDamage for RestyleDamage {
-    type ConcreteComputedValues = ServoComputedValues;
-    fn compute(old: Option<&Arc<ServoComputedValues>>, new: &ServoComputedValues) ->
-        RestyleDamage { compute_damage(old, new) }
-
-    /// 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
-    }
-}
-
-
-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)
-        } else {
-            self & (REPAINT | 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
-            }
-            (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)
-            }
-        }
-    }
-}
-
-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")
-            , (STORE_OVERFLOW, "StoreOverflow")
-            , (BUBBLE_ISIZES, "BubbleISizes")
-            , (REFLOW_OUT_OF_FLOW, "ReflowOutOfFlow")
-            , (REFLOW, "Reflow")
-            , (RESOLVE_GENERATED_CONTENT, "ResolveGeneratedContent")
-            , (RECONSTRUCT_FLOW, "ReconstructFlow")
-            ];
-
-        for &(damage, damage_str) in &to_iter {
-            if self.contains(damage) {
-                if !first_elem { try!(write!(f, " | ")); }
-                try!(write!(f, "{}", damage_str));
-                first_elem = false;
-            }
-        }
-
-        if first_elem {
-            try!(write!(f, "NoDamage"));
-        }
-
-        Ok(())
-    }
-}
-
-// NB: We need the braces inside the RHS due to Rust #8012.  This particular
-// version of this macro might be safe anyway, but we want to avoid silent
-// breakage on modifications.
-macro_rules! add_if_not_equal(
-    ($old:ident, $new:ident, $damage:ident,
-     [ $($effect:ident),* ], [ $($style_struct_getter:ident.$name:ident),* ]) => ({
-        if $( ($old.$style_struct_getter().$name != $new.$style_struct_getter().$name) )||* {
-            $damage.insert($($effect)|*);
-            true
-        } else {
-            false
-        }
-    })
-);
-
-pub fn compute_damage(old: Option<&Arc<ServoComputedValues>>, new: &ServoComputedValues) -> RestyleDamage {
-    let old: &ServoComputedValues = match old {
-        None => return RestyleDamage::rebuild_and_reflow(),
-        Some(cv) => &**cv,
-    };
-
-    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
-                      ], [
-        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,
-        get_inheritedtext.text_transform, get_inheritedtext.word_spacing,
-        get_inheritedtext.overflow_wrap, get_inheritedtext.text_justify,
-        get_inheritedtext.white_space, get_inheritedtext.word_break, get_text.text_overflow,
-        get_font.font_family, get_font.font_style, get_font.font_variant, get_font.font_weight,
-        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], [
-        // 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 ],
-        [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,
-        get_inheritedtext.text_align, get_inheritedtext.text_indent,
-        get_table.table_layout,
-        get_inheritedtable.border_collapse,
-        get_inheritedtable.border_spacing,
-        get_column.column_gap,
-        get_position.flex_direction,
-        get_position.flex_wrap,
-        get_position.justify_content,
-        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 ], [
-        get_position.top, get_position.left,
-        get_position.right, get_position.bottom
-    ]) || add_if_not_equal!(old, new, damage,
-                            [ 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_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() {
-        damage.insert(RestyleDamage::rebuild_and_reflow());
-    }
-
-    damage
-}
-
 pub trait LayoutDamageComputation {
     fn compute_layout_damage(self) -> SpecialRestyleDamage;
     fn reflow_entire_document(self);
 }
 
 impl<'a> LayoutDamageComputation for &'a mut Flow {
     fn compute_layout_damage(self) -> SpecialRestyleDamage {
         let mut special_damage = SpecialRestyleDamage::empty();
--- a/servo/components/layout/inline.rs
+++ b/servo/components/layout/inline.rs
@@ -16,34 +16,35 @@ use flow::{CONTAINS_TEXT_OR_REPLACED_FRA
 use flow::{self, BaseFlow, Flow, FlowClass, ForceNonfloatedFlag, IS_ABSOLUTELY_POSITIONED};
 use flow_ref;
 use fragment::SpecificFragmentInfo;
 use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, Overflow};
 use gfx::display_list::{OpaqueNode, StackingContext};
 use gfx::font::FontMetrics;
 use gfx::font_context::FontContext;
 use gfx_traits::StackingContextId;
-use incremental::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, RESOLVE_GENERATED_CONTENT};
 use layout_debug;
 use model::IntrinsicISizesContribution;
 use range::{Range, RangeIndex};
+use script_layout_interface::restyle_damage::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW};
+use script_layout_interface::restyle_damage::{REPAINT, RESOLVE_GENERATED_CONTENT};
+use script_layout_interface::wrapper_traits::PseudoElementType;
 use std::cmp::max;
 use std::collections::VecDeque;
 use std::sync::Arc;
 use std::{fmt, i32, isize, mem};
 use style::computed_values::{display, overflow_x, position, text_align, text_justify};
 use style::computed_values::{text_overflow, vertical_align, white_space};
 use style::logical_geometry::{LogicalRect, LogicalSize, WritingMode};
 use style::properties::{ComputedValues, ServoComputedValues};
 use style::values::computed::LengthOrPercentage;
 use text;
 use unicode_bidi;
 use util;
 use util::print_tree::PrintTree;
-use wrapper::PseudoElementType;
 
 // From gfxFontConstants.h in Firefox
 static FONT_SUBSCRIPT_OFFSET_RATIO: f32 = 0.20;
 static FONT_SUPERSCRIPT_OFFSET_RATIO: f32 = 0.34;
 
 /// `Line`s are represented as offsets into the child list, rather than
 /// as an object that "owns" fragments. Choosing a different set of line
 /// breaks requires a new list of offsets, and possibly some splitting and
--- a/servo/components/layout/layout_thread.rs
+++ b/servo/components/layout/layout_thread.rs
@@ -24,43 +24,46 @@ use fnv::FnvHasher;
 use gfx::display_list::{ClippingRegion, DisplayItemMetadata, DisplayList, LayerInfo, OpaqueNode};
 use gfx::display_list::{ScrollOffsetMap, StackingContext, StackingContextType, WebRenderImageInfo};
 use gfx::font;
 use gfx::font_cache_thread::FontCacheThread;
 use gfx::font_context;
 use gfx::paint_thread::LayoutToPaintMsg;
 use gfx_traits::{color, Epoch, FragmentType, LayerId, ScrollPolicy, StackingContextId};
 use heapsize::HeapSizeOf;
-use incremental::LayoutDamageComputation;
-use incremental::{REPAINT, STORE_OVERFLOW, REFLOW_OUT_OF_FLOW, REFLOW, REFLOW_ENTIRE_DOCUMENT};
+use incremental::{LayoutDamageComputation, REFLOW_ENTIRE_DOCUMENT};
 use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
 use ipc_channel::router::ROUTER;
 use layout_debug;
 use layout_traits::LayoutThreadFactory;
 use log;
 use msg::constellation_msg::{PanicMsg, PipelineId};
 use net_traits::image_cache_thread::UsePlaceholder;
 use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheResult, ImageCacheThread};
 use parallel;
 use profile_traits::mem::{self, Report, ReportKind, ReportsChan};
 use profile_traits::time::{TimerMetadataFrameType, TimerMetadataReflowType};
 use profile_traits::time::{self, TimerMetadata, profile};
 use query::process_offset_parent_query;
 use query::{LayoutRPCImpl, process_content_box_request, process_content_boxes_request};
 use query::{process_node_geometry_request, process_node_layer_id_request, process_node_scroll_area_request};
 use query::{process_node_overflow_request, process_resolved_style_request, process_margin_style_query};
-use script::layout_interface::OpaqueStyleAndLayoutData;
-use script::layout_interface::{LayoutRPC, OffsetParentResponse, NodeOverflowResponse, MarginStyleResponse};
-use script::layout_interface::{Msg, NewLayoutThreadInfo, Reflow, ReflowQueryType, ScriptReflow};
-use script::reporter::CSSErrorReporter;
+use script::layout_wrapper::ServoLayoutNode;
+use script_layout_interface::message::{Msg, NewLayoutThreadInfo, Reflow, ReflowQueryType, ScriptReflow};
+use script_layout_interface::reporter::CSSErrorReporter;
+use script_layout_interface::restyle_damage::{REPAINT, STORE_OVERFLOW, REFLOW_OUT_OF_FLOW, REFLOW};
+use script_layout_interface::rpc::{LayoutRPC, MarginStyleResponse, NodeOverflowResponse, OffsetParentResponse};
+use script_layout_interface::wrapper_traits::LayoutNode;
+use script_layout_interface::{OpaqueStyleAndLayoutData, PartialStyleAndLayoutData};
 use script_traits::{ConstellationControlMsg, LayoutControlMsg, LayoutMsg as ConstellationMsg};
 use script_traits::{StackingContextScrollState, UntrustedNodeAddress};
 use sequential;
 use serde_json;
 use std::borrow::ToOwned;
+use std::cell::RefCell;
 use std::collections::HashMap;
 use std::hash::BuildHasherDefault;
 use std::ops::{Deref, DerefMut};
 use std::sync::atomic::{AtomicUsize, Ordering};
 use std::sync::mpsc::{channel, Sender, Receiver};
 use std::sync::{Arc, Mutex, MutexGuard, RwLock};
 use style::animation::Animation;
 use style::computed_values::{filter, mix_blend_mode};
@@ -79,17 +82,17 @@ use url::Url;
 use util::geometry::MAX_RECT;
 use util::ipc::OptionalIpcSender;
 use util::opts;
 use util::thread;
 use util::thread_state;
 use util::workqueue::WorkQueue;
 use webrender_helpers::{WebRenderDisplayListConverter, WebRenderFrameBuilder};
 use webrender_traits;
-use wrapper::{LayoutNode, NonOpaqueStyleAndLayoutData, ServoLayoutNode};
+use wrapper::{LayoutNodeLayoutData, NonOpaqueStyleAndLayoutData};
 
 /// The number of screens of data we're allowed to generate display lists for in each direction.
 pub const DISPLAY_PORT_SIZE_FACTOR: i32 = 8;
 
 /// The number of screens we have to traverse before we decide to generate new display lists.
 const DISPLAY_PORT_THRESHOLD_SIZE_FACTOR: i32 = 4;
 
 /// Mutable data belonging to the LayoutThread.
@@ -1475,17 +1478,17 @@ impl LayoutThread {
         for child in flow::child_iter_mut(flow) {
             LayoutThread::reflow_all_nodes(child);
         }
     }
 
     /// Handles a message to destroy layout data. Layout data must be destroyed on *this* thread
     /// because the struct type is transmuted to a different type on the script side.
     unsafe fn handle_reap_style_and_layout_data(&self, data: OpaqueStyleAndLayoutData) {
-        let ptr: *mut () = *data.ptr;
+        let ptr: *mut RefCell<PartialStyleAndLayoutData> = *data.ptr;
         let non_opaque: NonOpaqueStyleAndLayoutData = ptr as *mut _;
         let _ = Box::from_raw(non_opaque);
     }
 
     /// Returns profiling information which is passed to the time profiler.
     fn profiler_metadata(&self) -> Option<TimerMetadata> {
         Some(TimerMetadata {
             url: self.url.to_string(),
--- a/servo/components/layout/lib.rs
+++ b/servo/components/layout/lib.rs
@@ -42,16 +42,17 @@ extern crate net_traits;
 #[no_link]
 extern crate plugins as servo_plugins;
 #[macro_use]
 extern crate profile_traits;
 #[macro_use]
 extern crate range;
 extern crate rustc_serialize;
 extern crate script;
+extern crate script_layout_interface;
 extern crate script_traits;
 extern crate selectors;
 extern crate serde_json;
 extern crate smallvec;
 #[macro_use(atom, ns)] extern crate string_cache;
 extern crate style;
 extern crate style_traits;
 extern crate time;
@@ -97,9 +98,8 @@ mod table_rowgroup;
 mod table_wrapper;
 mod text;
 mod traversal;
 mod webrender_helpers;
 mod wrapper;
 
 // For unit tests:
 pub use fragment::Fragment;
-pub use wrapper::ServoThreadSafeLayoutNode;
--- a/servo/components/layout/list_item.rs
+++ b/servo/components/layout/list_item.rs
@@ -14,18 +14,18 @@ use display_list_builder::{DisplayListBu
 use euclid::Point2D;
 use floats::FloatKind;
 use flow::{Flow, FlowClass, OpaqueFlow};
 use fragment::Overflow;
 use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, GeneratedContentInfo};
 use generated_content;
 use gfx::display_list::StackingContext;
 use gfx_traits::StackingContextId;
-use incremental::RESOLVE_GENERATED_CONTENT;
 use inline::InlineMetrics;
+use script_layout_interface::restyle_damage::RESOLVE_GENERATED_CONTENT;
 use std::sync::Arc;
 use style::computed_values::{list_style_type, position};
 use style::logical_geometry::LogicalSize;
 use style::properties::{ComputedValues, ServoComputedValues};
 use text;
 
 /// A block with the CSS `display` property equal to `list-item`.
 #[derive(Debug)]
--- a/servo/components/layout/opaque_node.rs
+++ b/servo/components/layout/opaque_node.rs
@@ -1,43 +1,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#![allow(unsafe_code)]
-
 use gfx::display_list::OpaqueNode;
-use libc::{c_void, uintptr_t};
-use script::layout_interface::LayoutJS;
-use script::layout_interface::Node;
-use script::layout_interface::TrustedNodeAddress;
+use libc::c_void;
 use script_traits::UntrustedNodeAddress;
 
 pub trait OpaqueNodeMethods {
-    /// Converts a DOM node (script view) to an `OpaqueNode`.
-    fn from_script_node(node: TrustedNodeAddress) -> Self;
-
-    /// Converts a DOM node to an `OpaqueNode'.
-    fn from_jsmanaged(node: &LayoutJS<Node>) -> Self;
-
     /// Converts this node to an `UntrustedNodeAddress`. An `UntrustedNodeAddress` is just the type
     /// of node that script expects to receive in a hit test.
     fn to_untrusted_node_address(&self) -> UntrustedNodeAddress;
 }
 
 impl OpaqueNodeMethods for OpaqueNode {
-    fn from_script_node(node: TrustedNodeAddress) -> OpaqueNode {
-        unsafe {
-            OpaqueNodeMethods::from_jsmanaged(&LayoutJS::from_trusted_node_address(node))
-        }
-    }
-
-    fn from_jsmanaged(node: &LayoutJS<Node>) -> OpaqueNode {
-        unsafe {
-            let ptr: uintptr_t = node.get_jsobject() as uintptr_t;
-            OpaqueNode(ptr)
-        }
-    }
-
     fn to_untrusted_node_address(&self) -> UntrustedNodeAddress {
         UntrustedNodeAddress(self.0 as *const c_void)
     }
 }
--- a/servo/components/layout/query.rs
+++ b/servo/components/layout/query.rs
@@ -11,35 +11,38 @@ use euclid::rect::Rect;
 use euclid::size::Size2D;
 use flow;
 use flow_ref::FlowRef;
 use fragment::{Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo};
 use gfx::display_list::OpaqueNode;
 use gfx_traits::LayerId;
 use layout_thread::LayoutThreadData;
 use opaque_node::OpaqueNodeMethods;
-use script::layout_interface::{ContentBoxResponse, NodeOverflowResponse, ContentBoxesResponse, NodeGeometryResponse};
-use script::layout_interface::{HitTestResponse, LayoutRPC, OffsetParentResponse, NodeLayerIdResponse};
-use script::layout_interface::{ResolvedStyleResponse, MarginStyleResponse};
+use script_layout_interface::rpc::{ContentBoxResponse, ContentBoxesResponse};
+use script_layout_interface::rpc::{HitTestResponse, LayoutRPC};
+use script_layout_interface::rpc::{MarginStyleResponse, NodeGeometryResponse};
+use script_layout_interface::rpc::{NodeLayerIdResponse, NodeOverflowResponse};
+use script_layout_interface::rpc::{OffsetParentResponse, ResolvedStyleResponse};
+use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutNode};
 use script_traits::LayoutMsg as ConstellationMsg;
 use script_traits::UntrustedNodeAddress;
 use sequential;
 use std::cmp::{min, max};
 use std::ops::Deref;
 use std::sync::{Arc, Mutex};
 use string_cache::Atom;
 use style::computed_values;
 use style::logical_geometry::{WritingMode, BlockFlowDirection, InlineBaseDirection};
 use style::properties::ComputedValues;
 use style::properties::longhands::{display, position};
 use style::properties::style_structs;
 use style::selector_impl::PseudoElement;
 use style::values::AuExtensionMethods;
 use style_traits::cursor::Cursor;
-use wrapper::{LayoutNode, ThreadSafeLayoutNode};
+use wrapper::ThreadSafeLayoutNodeHelpers;
 
 pub struct LayoutRPCImpl(pub Arc<Mutex<LayoutThreadData>>);
 
 // https://drafts.csswg.org/cssom-view/#overflow-directions
 fn overflow_direction(writing_mode: &WritingMode) -> OverflowDirection {
     match (writing_mode.block_flow_direction(), writing_mode.inline_base_direction()) {
         (BlockFlowDirection::TopToBottom, InlineBaseDirection::LeftToRight) |
             (BlockFlowDirection::LeftToRight, InlineBaseDirection::LeftToRight) => OverflowDirection::RightAndDown,
--- a/servo/components/layout/sequential.rs
+++ b/servo/components/layout/sequential.rs
@@ -11,17 +11,17 @@ use euclid::point::Point2D;
 use floats::SpeculatedFloatPlacement;
 use flow::IS_ABSOLUTELY_POSITIONED;
 use flow::{PostorderFlowTraversal, PreorderFlowTraversal};
 use flow::{self, Flow, ImmutableFlowUtils, InorderFlowTraversal, MutableFlowUtils};
 use flow_ref::{self, FlowRef};
 use fragment::FragmentBorderBoxIterator;
 use generated_content::ResolveGeneratedContent;
 use gfx::display_list::{DisplayItem, StackingContext};
-use incremental::{REFLOW, STORE_OVERFLOW};
+use script_layout_interface::restyle_damage::{REFLOW, STORE_OVERFLOW};
 use traversal::{AssignBSizes, AssignISizes, BubbleISizes, BuildDisplayList, ComputeAbsolutePositions};
 use util::opts;
 
 pub use style::sequential::traverse_dom;
 
 pub fn resolve_generated_content(root: &mut FlowRef, shared_layout_context: &SharedLayoutContext) {
     fn doit(flow: &mut Flow, level: u32, traversal: &mut ResolveGeneratedContent) {
         if !traversal.should_process(flow) {
--- a/servo/components/layout/table.rs
+++ b/servo/components/layout/table.rs
@@ -13,19 +13,19 @@ use context::LayoutContext;
 use display_list_builder::{BlockFlowDisplayListBuilding, BorderPaintingMode, DisplayListBuildState};
 use euclid::Point2D;
 use flow;
 use flow::{BaseFlow, EarlyAbsolutePositionInfo, Flow, FlowClass, ImmutableFlowUtils, OpaqueFlow};
 use flow_list::MutFlowListIterator;
 use fragment::{Fragment, FragmentBorderBoxIterator, Overflow};
 use gfx::display_list::StackingContext;
 use gfx_traits::StackingContextId;
-use incremental::{REFLOW, REFLOW_OUT_OF_FLOW};
 use layout_debug;
 use model::{IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto};
+use script_layout_interface::restyle_damage::{REFLOW, REFLOW_OUT_OF_FLOW};
 use std::cmp;
 use std::fmt;
 use std::sync::Arc;
 use style::computed_values::{border_collapse, border_spacing, table_layout};
 use style::logical_geometry::LogicalSize;
 use style::properties::{ComputedValues, ServoComputedValues};
 use style::values::CSSFloat;
 use style::values::computed::LengthOrPercentageOrAuto;
--- a/servo/components/layout/table_cell.rs
+++ b/servo/components/layout/table_cell.rs
@@ -11,28 +11,28 @@ use block::{BlockFlow, ISizeAndMarginsCo
 use context::LayoutContext;
 use cssparser::Color;
 use display_list_builder::{BlockFlowDisplayListBuilding, BorderPaintingMode, DisplayListBuildState};
 use euclid::{Point2D, Rect, SideOffsets2D, Size2D};
 use flow::{self, Flow, FlowClass, OpaqueFlow};
 use fragment::{Fragment, FragmentBorderBoxIterator, Overflow};
 use gfx::display_list::StackingContext;
 use gfx_traits::StackingContextId;
-use incremental::REFLOW;
 use layout_debug;
 use model::MaybeAuto;
+use script_layout_interface::restyle_damage::REFLOW;
+use script_layout_interface::wrapper_traits::ThreadSafeLayoutNode;
 use std::fmt;
 use std::sync::Arc;
 use style::computed_values::{border_collapse, border_top_style, vertical_align};
 use style::logical_geometry::{LogicalMargin, LogicalRect, LogicalSize, WritingMode};
 use style::properties::{ComputedValues, ServoComputedValues};
 use table::InternalTable;
 use table_row::{CollapsedBorder, CollapsedBorderProvenance};
 use util::print_tree::PrintTree;
-use wrapper::ThreadSafeLayoutNode;
 
 /// A table formatting context.
 #[derive(RustcEncodable)]
 pub struct TableCellFlow {
     /// Data common to all block flows.
     pub block_flow: BlockFlow,
 
     /// Border collapse information for the cell.
--- a/servo/components/layout/traversal.rs
+++ b/servo/components/layout/traversal.rs
@@ -5,32 +5,40 @@
 //! Traversals over the DOM and flow trees, running the layout computations.
 
 use construct::FlowConstructor;
 use context::{LayoutContext, SharedLayoutContext};
 use display_list_builder::DisplayListBuildState;
 use flow::{CAN_BE_FRAGMENTED, Flow, ImmutableFlowUtils, PostorderFlowTraversal};
 use flow::{PreorderFlowTraversal, self};
 use gfx::display_list::OpaqueNode;
-use incremental::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, RestyleDamage};
+use script_layout_interface::restyle_damage::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, RestyleDamage};
+use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutNode};
 use std::mem;
 use style::context::StyleContext;
+use style::dom::TNode;
 use style::matching::MatchMethods;
+use style::properties::ServoComputedValues;
+use style::selector_impl::ServoSelectorImpl;
 use style::traversal::{DomTraversalContext, STYLE_BLOOM};
 use style::traversal::{put_thread_local_bloom_filter, recalc_style_at};
 use util::opts;
 use util::tid::tid;
-use wrapper::{LayoutNode, ServoLayoutNode, ThreadSafeLayoutNode};
+use wrapper::{LayoutNodeLayoutData, ThreadSafeLayoutNodeHelpers};
 
 pub struct RecalcStyleAndConstructFlows<'lc> {
     context: LayoutContext<'lc>,
     root: OpaqueNode,
 }
 
-impl<'lc, 'ln> DomTraversalContext<ServoLayoutNode<'ln>> for RecalcStyleAndConstructFlows<'lc> {
+impl<'lc, N> DomTraversalContext<N> for RecalcStyleAndConstructFlows<'lc>
+    where N: LayoutNode + TNode<ConcreteComputedValues=ServoComputedValues>,
+          N::ConcreteElement: ::selectors::Element<Impl=ServoSelectorImpl>
+
+{
     type SharedContext = SharedLayoutContext;
     #[allow(unsafe_code)]
     fn new<'a>(shared: &'a Self::SharedContext, root: OpaqueNode) -> Self {
         // FIXME(bholley): This transmutation from &'a to &'lc is very unfortunate, but I haven't
         // found a way to avoid it despite spending several days on it (and consulting Manishearth,
         // brson, and nmatsakis).
         //
         // The crux of the problem is that parameterizing DomTraversalContext on the lifetime of
@@ -60,25 +68,25 @@ impl<'lc, 'ln> DomTraversalContext<Servo
         // LayoutThread, generally much longer than that of a given SharedLayoutContext borrow.
         let shared_lc: &'lc SharedLayoutContext = unsafe { mem::transmute(shared) };
         RecalcStyleAndConstructFlows {
             context: LayoutContext::new(shared_lc),
             root: root,
         }
     }
 
-    fn process_preorder(&self, node: ServoLayoutNode<'ln>) {
+    fn process_preorder(&self, node: N) {
         // FIXME(pcwalton): Stop allocating here. Ideally this should just be done by the HTML
         // parser.
         node.initialize_data();
 
         recalc_style_at(&self.context, self.root, node);
     }
 
-    fn process_postorder(&self, node: ServoLayoutNode<'ln>) { construct_flows_at(&self.context, self.root, node); }
+    fn process_postorder(&self, node: N) { construct_flows_at(&self.context, self.root, node); }
 }
 
 /// A bottom-up, parallelizable traversal.
 pub trait PostorderNodeMutTraversal<ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> {
     /// The operation to perform. Return true to continue or false to stop.
     fn process(&mut self, node: &ConcreteThreadSafeLayoutNode);
 }
 
--- a/servo/components/layout/wrapper.rs
+++ b/servo/components/layout/wrapper.rs
@@ -27,1386 +27,174 @@
 //!
 //!   o Instead of `html_element_in_html_document()`, use
 //!     `html_element_in_html_document_for_layout()`.
 
 #![allow(unsafe_code)]
 
 use core::nonzero::NonZero;
 use data::{LayoutDataFlags, PrivateLayoutData};
-use gfx::display_list::OpaqueNode;
-use gfx::text::glyph::ByteIndex;
-use gfx_traits::{LayerId, LayerType};
-use incremental::RestyleDamage;
-use msg::constellation_msg::PipelineId;
-use opaque_node::OpaqueNodeMethods;
-use range::Range;
-use script::layout_interface::{CAN_BE_FRAGMENTED, HAS_CHANGED, HAS_DIRTY_DESCENDANTS, IS_DIRTY};
-use script::layout_interface::{CharacterDataTypeId, Document, Element, ElementTypeId};
-use script::layout_interface::{HTMLCanvasData, HTMLElementTypeId, LayoutCharacterDataHelpers};
-use script::layout_interface::{LayoutDocumentHelpers, LayoutElementHelpers, LayoutJS};
-use script::layout_interface::{LayoutNodeHelpers, Node, NodeTypeId, OpaqueStyleAndLayoutData};
-use script::layout_interface::{RawLayoutElementHelpers, Text, TrustedNodeAddress};
-use selectors::matching::{DeclarationBlock, ElementFlags};
-use selectors::parser::{AttrSelector, NamespaceConstraint};
-use smallvec::VecLike;
+use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutNode};
+use script_layout_interface::{OpaqueStyleAndLayoutData, PartialStyleAndLayoutData};
 use std::cell::{Ref, RefCell, RefMut};
-use std::marker::PhantomData;
-use std::mem::{transmute, transmute_copy};
-use std::sync::Arc;
-use string_cache::{Atom, BorrowedAtom, BorrowedNamespace, Namespace};
-use style::attr::AttrValue;
-use style::computed_values::content::ContentItem;
-use style::computed_values::{content, display};
-use style::dom::{PresentationalHintsSynthetizer, TDocument, TElement, TNode, UnsafeNode};
-use style::element_state::*;
-use style::properties::{ComputedValues, ServoComputedValues};
-use style::properties::{PropertyDeclaration, PropertyDeclarationBlock};
-use style::restyle_hints::ElementSnapshot;
-use style::selector_impl::{NonTSPseudoClass, PseudoElement, PseudoElementCascadeType, ServoSelectorImpl};
-use style::servo::{PrivateStyleData, SharedStyleContext};
-use url::Url;
-use util::str::is_whitespace;
+use style::computed_values::content::{self, ContentItem};
+use style::properties::ComputedValues;
 
 pub type NonOpaqueStyleAndLayoutData = *mut RefCell<PrivateLayoutData>;
 
-/// A wrapper so that layout can access only the methods that it should have access to. Layout must
-/// only ever see these and must never see instances of `LayoutJS`.
-
-pub trait LayoutNode: TNode {
-    type ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode;
-    fn to_threadsafe(&self) -> Self::ConcreteThreadSafeLayoutNode;
-
-    /// Returns the type ID of this node.
-    fn type_id(&self) -> NodeTypeId;
-
+pub trait LayoutNodeLayoutData {
     /// Similar to borrow_data*, but returns the full PrivateLayoutData rather
     /// than only the PrivateStyleData.
     unsafe fn borrow_layout_data_unchecked(&self) -> Option<*const PrivateLayoutData>;
     fn borrow_layout_data(&self) -> Option<Ref<PrivateLayoutData>>;
     fn mutate_layout_data(&self) -> Option<RefMut<PrivateLayoutData>>;
-}
-
-#[derive(Copy, Clone)]
-pub struct ServoLayoutNode<'a> {
-    /// The wrapped node.
-    node: LayoutJS<Node>,
-
-    /// Being chained to a PhantomData prevents `LayoutNode`s from escaping.
-    chain: PhantomData<&'a ()>,
-}
-
-impl<'a> PartialEq for ServoLayoutNode<'a> {
-    #[inline]
-    fn eq(&self, other: &ServoLayoutNode) -> bool {
-        self.node == other.node
-    }
-}
-
-impl<'ln> ServoLayoutNode<'ln> {
-    fn from_layout_js(n: LayoutJS<Node>) -> ServoLayoutNode<'ln> {
-        ServoLayoutNode {
-            node: n,
-            chain: PhantomData,
-        }
-    }
-
-    pub unsafe fn new(address: &TrustedNodeAddress) -> ServoLayoutNode {
-        ServoLayoutNode::from_layout_js(LayoutJS::from_trusted_node_address(*address))
-    }
-
-    /// Creates a new layout node with the same lifetime as this layout node.
-    pub unsafe fn new_with_this_lifetime(&self, node: &LayoutJS<Node>) -> ServoLayoutNode<'ln> {
-        ServoLayoutNode {
-            node: *node,
-            chain: self.chain,
-        }
-    }
-
-    pub fn initialize_data(self) {
-        if unsafe { self.borrow_data_unchecked() }.is_none() {
-            let ptr: NonOpaqueStyleAndLayoutData =
-                Box::into_raw(box RefCell::new(PrivateLayoutData::new()));
-            let opaque = OpaqueStyleAndLayoutData {
-                ptr: unsafe { NonZero::new(ptr as *mut ()) }
-            };
-            unsafe {
-                self.node.init_style_and_layout_data(opaque);
-            }
-        }
-    }
+    fn initialize_data(self);
+    fn flow_debug_id(self) -> usize;
 }
 
-impl<'ln> TNode for ServoLayoutNode<'ln> {
-    type ConcreteComputedValues = ServoComputedValues;
-    type ConcreteElement = ServoLayoutElement<'ln>;
-    type ConcreteDocument = ServoLayoutDocument<'ln>;
-    type ConcreteRestyleDamage = RestyleDamage;
-
-    fn to_unsafe(&self) -> UnsafeNode {
-        unsafe {
-            let ptr: usize = transmute_copy(self);
-            (ptr, 0)
-        }
-    }
-
-    unsafe fn from_unsafe(n: &UnsafeNode) -> Self {
-        let (node, _) = *n;
-        transmute(node)
-    }
-
-    fn is_text_node(&self) -> bool {
-        self.type_id() == NodeTypeId::CharacterData(CharacterDataTypeId::Text)
-    }
-
-    fn is_element(&self) -> bool {
-        unsafe {
-            self.node.is_element_for_layout()
-        }
-    }
-
-    fn dump(self) {
-        self.dump_indent(0);
-    }
-
-    fn opaque(&self) -> OpaqueNode {
-        OpaqueNodeMethods::from_jsmanaged(unsafe { self.get_jsmanaged() })
-    }
-
-    fn layout_parent_node(self, reflow_root: OpaqueNode) -> Option<ServoLayoutNode<'ln>> {
-        if self.opaque() == reflow_root {
-            None
-        } else {
-            self.parent_node()
-        }
-    }
-
-    fn debug_id(self) -> usize {
-        self.opaque().to_untrusted_node_address().0 as usize
-    }
-
-    fn children_count(&self) -> u32 {
-        unsafe { self.node.children_count() }
-    }
-
-    fn as_element(&self) -> Option<ServoLayoutElement<'ln>> {
-        as_element(self.node)
-    }
-
-    fn as_document(&self) -> Option<ServoLayoutDocument<'ln>> {
-        self.node.downcast().map(ServoLayoutDocument::from_layout_js)
-    }
-
-    fn has_changed(&self) -> bool {
-        unsafe { self.node.get_flag(HAS_CHANGED) }
-    }
-
-    unsafe fn set_changed(&self, value: bool) {
-        self.node.set_flag(HAS_CHANGED, value)
-    }
-
-    fn is_dirty(&self) -> bool {
-        unsafe { self.node.get_flag(IS_DIRTY) }
-    }
-
-    unsafe fn set_dirty(&self, value: bool) {
-        self.node.set_flag(IS_DIRTY, value)
-    }
-
-    fn has_dirty_descendants(&self) -> bool {
-        unsafe { self.node.get_flag(HAS_DIRTY_DESCENDANTS) }
-    }
-
-    unsafe fn set_dirty_descendants(&self, value: bool) {
-        self.node.set_flag(HAS_DIRTY_DESCENDANTS, value)
-    }
-
-    fn can_be_fragmented(&self) -> bool {
-        unsafe { self.node.get_flag(CAN_BE_FRAGMENTED) }
-    }
-
-    unsafe fn set_can_be_fragmented(&self, value: bool) {
-        self.node.set_flag(CAN_BE_FRAGMENTED, value)
-    }
-
-    unsafe fn borrow_data_unchecked(&self) -> Option<*const PrivateStyleData> {
-        self.borrow_layout_data_unchecked().map(|d| &(*d).style_data as *const PrivateStyleData)
-    }
-
-    fn borrow_data(&self) -> Option<Ref<PrivateStyleData>> {
-        self.borrow_layout_data().map(|d| Ref::map(d, |d| &d.style_data))
-    }
-
-    fn mutate_data(&self) -> Option<RefMut<PrivateStyleData>> {
-        self.mutate_layout_data().map(|d| RefMut::map(d, |d| &mut d.style_data))
-    }
-
-    fn restyle_damage(self) -> RestyleDamage {
-        self.borrow_layout_data().unwrap().restyle_damage
-    }
-
-    fn set_restyle_damage(self, damage: RestyleDamage) {
-        self.mutate_layout_data().unwrap().restyle_damage = damage;
-    }
-
-    fn parent_node(&self) -> Option<ServoLayoutNode<'ln>> {
-        unsafe {
-            self.node.parent_node_ref().map(|node| self.new_with_this_lifetime(&node))
-        }
-    }
-
-    fn first_child(&self) -> Option<ServoLayoutNode<'ln>> {
-        unsafe {
-            self.node.first_child_ref().map(|node| self.new_with_this_lifetime(&node))
-        }
-    }
-
-    fn last_child(&self) -> Option<ServoLayoutNode<'ln>> {
-        unsafe {
-            self.node.last_child_ref().map(|node| self.new_with_this_lifetime(&node))
-        }
-    }
-
-    fn prev_sibling(&self) -> Option<ServoLayoutNode<'ln>> {
-        unsafe {
-            self.node.prev_sibling_ref().map(|node| self.new_with_this_lifetime(&node))
-        }
-    }
-
-    fn next_sibling(&self) -> Option<ServoLayoutNode<'ln>> {
-        unsafe {
-            self.node.next_sibling_ref().map(|node| self.new_with_this_lifetime(&node))
-        }
-    }
-}
-
-impl<'ln> LayoutNode for ServoLayoutNode<'ln> {
-    type ConcreteThreadSafeLayoutNode = ServoThreadSafeLayoutNode<'ln>;
-
-    fn to_threadsafe(&self) -> Self::ConcreteThreadSafeLayoutNode {
-        ServoThreadSafeLayoutNode::new(self)
-    }
-
-    fn type_id(&self) -> NodeTypeId {
-        unsafe {
-            self.node.type_id_for_layout()
-        }
-    }
-
+impl<T: LayoutNode> LayoutNodeLayoutData for T {
     unsafe fn borrow_layout_data_unchecked(&self) -> Option<*const PrivateLayoutData> {
-        self.get_jsmanaged().get_style_and_layout_data().map(|opaque| {
+        self.get_style_and_layout_data().map(|opaque| {
             let container = *opaque.ptr as NonOpaqueStyleAndLayoutData;
             &(*(*container).as_unsafe_cell().get()) as *const PrivateLayoutData
         })
     }
 
     fn borrow_layout_data(&self) -> Option<Ref<PrivateLayoutData>> {
         unsafe {
-            self.get_jsmanaged().get_style_and_layout_data().map(|opaque| {
+            self.get_style_and_layout_data().map(|opaque| {
                 let container = *opaque.ptr as NonOpaqueStyleAndLayoutData;
                 (*container).borrow()
             })
         }
     }
 
     fn mutate_layout_data(&self) -> Option<RefMut<PrivateLayoutData>> {
         unsafe {
-            self.get_jsmanaged().get_style_and_layout_data().map(|opaque| {
+            self.get_style_and_layout_data().map(|opaque| {
                 let container = *opaque.ptr as NonOpaqueStyleAndLayoutData;
                 (*container).borrow_mut()
             })
         }
     }
-}
 
-
-impl<'ln> ServoLayoutNode<'ln> {
-    fn dump_indent(self, indent: u32) {
-        let mut s = String::new();
-        for _ in 0..indent {
-            s.push_str("  ");
-        }
-
-        s.push_str(&self.debug_str());
-        println!("{}", s);
-
-        for kid in self.children() {
-            kid.dump_indent(indent + 1);
-        }
-    }
-
-    fn debug_str(self) -> String {
-        format!("{:?}: changed={} dirty={} dirty_descendants={}",
-                self.type_id(), self.has_changed(), self.is_dirty(), self.has_dirty_descendants())
-    }
-
-    pub fn flow_debug_id(self) -> usize {
-        self.borrow_layout_data().map_or(0, |d| d.flow_construction_result.debug_id())
-    }
-
-    /// Returns the interior of this node as a `LayoutJS`. This is highly unsafe for layout to
-    /// call and as such is marked `unsafe`.
-    unsafe fn get_jsmanaged(&self) -> &LayoutJS<Node> {
-        &self.node
-    }
-}
-
-// A wrapper around documents that ensures ayout can only ever access safe properties.
-#[derive(Copy, Clone)]
-pub struct ServoLayoutDocument<'ld> {
-    document: LayoutJS<Document>,
-    chain: PhantomData<&'ld ()>,
-}
-
-impl<'ld> TDocument for ServoLayoutDocument<'ld> {
-    type ConcreteNode = ServoLayoutNode<'ld>;
-    type ConcreteElement = ServoLayoutElement<'ld>;
-
-    fn as_node(&self) -> ServoLayoutNode<'ld> {
-        ServoLayoutNode::from_layout_js(self.document.upcast())
-    }
-
-    fn root_node(&self) -> Option<ServoLayoutNode<'ld>> {
-        self.as_node().children().find(ServoLayoutNode::is_element)
-    }
-
-    fn drain_modified_elements(&self) -> Vec<(ServoLayoutElement<'ld>, ElementSnapshot)> {
-        let elements =  unsafe { self.document.drain_modified_elements() };
-        elements.into_iter().map(|(el, snapshot)| (ServoLayoutElement::from_layout_js(el), snapshot)).collect()
-    }
-}
-
-impl<'ld> ServoLayoutDocument<'ld> {
-    fn from_layout_js(doc: LayoutJS<Document>) -> ServoLayoutDocument<'ld> {
-        ServoLayoutDocument {
-            document: doc,
-            chain: PhantomData,
-        }
-    }
-}
-
-/// A wrapper around elements that ensures layout can only ever access safe properties.
-#[derive(Copy, Clone)]
-pub struct ServoLayoutElement<'le> {
-    element: LayoutJS<Element>,
-    chain: PhantomData<&'le ()>,
-}
-
-impl<'le> PresentationalHintsSynthetizer for ServoLayoutElement<'le> {
-    fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, hints: &mut V)
-        where V: VecLike<DeclarationBlock<Vec<PropertyDeclaration>>>
-    {
-        unsafe {
-            self.element.synthesize_presentational_hints_for_legacy_attributes(hints);
-        }
-    }
-}
-
-impl<'le> TElement for ServoLayoutElement<'le> {
-    type ConcreteNode = ServoLayoutNode<'le>;
-    type ConcreteDocument = ServoLayoutDocument<'le>;
-
-    fn as_node(&self) -> ServoLayoutNode<'le> {
-        ServoLayoutNode::from_layout_js(self.element.upcast())
-    }
-
-    fn style_attribute(&self) -> &Option<PropertyDeclarationBlock> {
-        unsafe {
-            &*self.element.style_attribute()
-        }
-    }
-
-    fn get_state(&self) -> ElementState {
-        self.element.get_state_for_layout()
-    }
-
-    #[inline]
-    fn get_attr(&self, namespace: &Namespace, name: &Atom) -> Option<&str> {
-        unsafe {
-            (*self.element.unsafe_get()).get_attr_val_for_layout(namespace, name)
-        }
-    }
-
-    #[inline]
-    fn get_attrs(&self, name: &Atom) -> Vec<&str> {
-        unsafe {
-            (*self.element.unsafe_get()).get_attr_vals_for_layout(name)
-        }
-    }
-}
-
-
-impl<'le> ServoLayoutElement<'le> {
-    fn from_layout_js(el: LayoutJS<Element>) -> ServoLayoutElement<'le> {
-        ServoLayoutElement {
-            element: el,
-            chain: PhantomData,
-        }
-    }
-}
-
-fn as_element<'le>(node: LayoutJS<Node>) -> Option<ServoLayoutElement<'le>> {
-    node.downcast().map(ServoLayoutElement::from_layout_js)
-}
-
-impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
-    type Impl = ServoSelectorImpl;
-
-    fn parent_element(&self) -> Option<ServoLayoutElement<'le>> {
-        unsafe {
-            self.element.upcast().parent_node_ref().and_then(as_element)
-        }
-    }
-
-    fn first_child_element(&self) -> Option<ServoLayoutElement<'le>> {
-        self.as_node().children().filter_map(|n| n.as_element()).next()
-    }
-
-    fn last_child_element(&self) -> Option<ServoLayoutElement<'le>> {
-        self.as_node().rev_children().filter_map(|n| n.as_element()).next()
-    }
-
-    fn prev_sibling_element(&self) -> Option<ServoLayoutElement<'le>> {
-        let mut node = self.as_node();
-        while let Some(sibling) = node.prev_sibling() {
-            if let Some(element) = sibling.as_element() {
-                return Some(element)
-            }
-            node = sibling;
-        }
-        None
-    }
-
-    fn next_sibling_element(&self) -> Option<ServoLayoutElement<'le>> {
-        let mut node = self.as_node();
-        while let Some(sibling) = node.next_sibling() {
-            if let Some(element) = sibling.as_element() {
-                return Some(element)
-            }
-            node = sibling;
-        }
-        None
-    }
-
-    fn is_root(&self) -> bool {
-        match self.as_node().parent_node() {
-            None => false,
-            Some(node) => {
-                match node.type_id() {
-                    NodeTypeId::Document(_) => true,
-                    _ => false
-                }
-            },
+    fn initialize_data(self) {
+        if unsafe { self.borrow_data_unchecked() }.is_none() {
+            let ptr: NonOpaqueStyleAndLayoutData =
+                Box::into_raw(box RefCell::new(PrivateLayoutData::new()));
+            let opaque = OpaqueStyleAndLayoutData {
+                ptr: unsafe { NonZero::new(ptr as *mut RefCell<PartialStyleAndLayoutData>) }
+            };
+            self.init_style_and_layout_data(opaque);
         }
     }
 
-    fn is_empty(&self) -> bool {
-        self.as_node().children().all(|node| match node.type_id() {
-            NodeTypeId::Element(..) => false,
-            NodeTypeId::CharacterData(CharacterDataTypeId::Text) => unsafe {
-                node.node.downcast().unwrap().data_for_layout().is_empty()
-            },
-            _ => true
-        })
-    }
-
-    #[inline]
-    fn get_local_name<'a>(&'a self) -> BorrowedAtom<'a> {
-        BorrowedAtom(self.element.local_name())
-    }
-
-    #[inline]
-    fn get_namespace<'a>(&'a self) -> BorrowedNamespace<'a> {
-        BorrowedNamespace(self.element.namespace())
-    }
-
-    fn match_non_ts_pseudo_class(&self, pseudo_class: NonTSPseudoClass) -> bool {
-        match pseudo_class {
-            // https://github.com/servo/servo/issues/8718
-            NonTSPseudoClass::Link |
-            NonTSPseudoClass::AnyLink => unsafe {
-                match self.as_node().type_id() {
-                    // https://html.spec.whatwg.org/multipage/#selector-link
-                    NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAnchorElement)) |
-                    NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAreaElement)) |
-                    NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLLinkElement)) =>
-                        (*self.element.unsafe_get()).get_attr_val_for_layout(&ns!(), &atom!("href")).is_some(),
-                    _ => false,
-                }
-            },
-            NonTSPseudoClass::Visited => false,
-
-            NonTSPseudoClass::ServoNonZeroBorder => unsafe {
-                match (*self.element.unsafe_get()).get_attr_for_layout(&ns!(), &atom!("border")) {
-                    None | Some(&AttrValue::UInt(_, 0)) => false,
-                    _ => true,
-                }
-            },
-
-            NonTSPseudoClass::ReadOnly =>
-                !self.element.get_state_for_layout().contains(pseudo_class.state_flag()),
-
-            NonTSPseudoClass::Active |
-            NonTSPseudoClass::Focus |
-            NonTSPseudoClass::Hover |
-            NonTSPseudoClass::Enabled |
-            NonTSPseudoClass::Disabled |
-            NonTSPseudoClass::Checked |
-            NonTSPseudoClass::Indeterminate |
-            NonTSPseudoClass::ReadWrite |
-            NonTSPseudoClass::PlaceholderShown =>
-                self.element.get_state_for_layout().contains(pseudo_class.state_flag())
-        }
-    }
-
-    #[inline]
-    fn get_id(&self) -> Option<Atom> {
-        unsafe {
-            (*self.element.id_attribute()).clone()
-        }
-    }
-
-    #[inline]
-    fn has_class(&self, name: &Atom) -> bool {
-        unsafe {
-            self.element.has_class_for_layout(name)
-        }
-    }
-
-    #[inline(always)]
-    fn each_class<F>(&self, mut callback: F) where F: FnMut(&Atom) {
-        unsafe {
-            if let Some(ref classes) = self.element.get_classes_for_layout() {
-                for class in *classes {
-                    callback(class)
-                }
-            }
-        }
-    }
-
-    fn match_attr<F>(&self, attr: &AttrSelector, test: F) -> bool where F: Fn(&str) -> bool {
-        let name = if self.is_html_element_in_html_document() {
-            &attr.lower_name
-        } else {
-            &attr.name
-        };
-        match attr.namespace {
-            NamespaceConstraint::Specific(ref ns) => {
-                self.get_attr(ns, name).map_or(false, |attr| test(attr))
-            },
-            NamespaceConstraint::Any => {
-                self.get_attrs(name).iter().any(|attr| test(*attr))
-            }
-        }
-    }
-
-    fn is_html_element_in_html_document(&self) -> bool {
-        unsafe {
-            self.element.html_element_in_html_document_for_layout()
-        }
-    }
-
-    fn insert_flags(&self, flags: ElementFlags) {
-        self.element.insert_atomic_flags(flags);
+    fn flow_debug_id(self) -> usize {
+        self.borrow_layout_data().map_or(0, |d| d.flow_construction_result.debug_id())
     }
 }
 
-#[derive(Copy, PartialEq, Clone)]
-pub enum PseudoElementType<T> {
-    Normal,
-    Before(T),
-    After(T),
-    DetailsSummary(T),
-    DetailsContent(T),
-}
-
-impl<T> PseudoElementType<T> {
-    pub fn is_before(&self) -> bool {
-        match *self {
-            PseudoElementType::Before(_) => true,
-            _ => false,
-        }
-    }
-
-    pub fn is_replaced_content(&self) -> bool {
-        match *self {
-            PseudoElementType::Before(_) | PseudoElementType::After(_) => true,
-            _ => false,
-        }
-    }
-
-    pub fn strip(&self) -> PseudoElementType<()> {
-        match *self {
-            PseudoElementType::Normal => PseudoElementType::Normal,
-            PseudoElementType::Before(_) => PseudoElementType::Before(()),
-            PseudoElementType::After(_) => PseudoElementType::After(()),
-            PseudoElementType::DetailsSummary(_) => PseudoElementType::DetailsSummary(()),
-            PseudoElementType::DetailsContent(_) => PseudoElementType::DetailsContent(()),
-        }
-    }
-
-    pub fn style_pseudo_element(&self) -> PseudoElement {
-        match *self {
-            PseudoElementType::Normal => unreachable!("style_pseudo_element called with PseudoElementType::Normal"),
-            PseudoElementType::Before(_) => PseudoElement::Before,
-            PseudoElementType::After(_) => PseudoElement::After,
-            PseudoElementType::DetailsSummary(_) => PseudoElement::DetailsSummary,
-            PseudoElementType::DetailsContent(_) => PseudoElement::DetailsContent,
-        }
-    }
-}
-
-/// A thread-safe version of `LayoutNode`, used during flow construction. This type of layout
-/// node does not allow any parents or siblings of nodes to be accessed, to avoid races.
-
-pub trait ThreadSafeLayoutNode: Clone + Copy + Sized + PartialEq {
-    type ConcreteThreadSafeLayoutElement:
-        ThreadSafeLayoutElement<ConcreteThreadSafeLayoutNode = Self>
-        + ::selectors::Element<Impl=ServoSelectorImpl>;
-    type ChildrenIterator: Iterator<Item = Self> + Sized;
-
-    /// Creates a new `ThreadSafeLayoutNode` for the same `LayoutNode`
-    /// with a different pseudo-element type.
-    fn with_pseudo(&self, pseudo: PseudoElementType<Option<display::T>>) -> Self;
-
-    /// Converts self into an `OpaqueNode`.
-    fn opaque(&self) -> OpaqueNode;
-
-    /// Returns the type ID of this node.
-    /// Returns `None` if this is a pseudo-element; otherwise, returns `Some`.
-    fn type_id(&self) -> Option<NodeTypeId>;
-
-    /// Returns the type ID of this node, without discarding pseudo-elements as
-    /// `type_id` does.
-    fn type_id_without_excluding_pseudo_elements(&self) -> NodeTypeId;
-
-    #[inline]
-    fn is_element_or_elements_pseudo(&self) -> bool {
-        match self.type_id_without_excluding_pseudo_elements() {
-            NodeTypeId::Element(..) => true,
-            _ => false,
-        }
-    }
-
-    fn debug_id(self) -> usize;
-
+pub trait ThreadSafeLayoutNodeHelpers {
     fn flow_debug_id(self) -> usize;
 
-    /// Returns an iterator over this node's children.
-    fn children(&self) -> Self::ChildrenIterator;
-
-    #[inline]
-    fn is_element(&self) -> bool { if let Some(NodeTypeId::Element(_)) = self.type_id() { true } else { false } }
-
-    /// If this is an element, accesses the element data. Fails if this is not an element node.
-    #[inline]
-    fn as_element(&self) -> Self::ConcreteThreadSafeLayoutElement;
-
-    #[inline]
-    fn get_pseudo_element_type(&self) -> PseudoElementType<Option<display::T>>;
-
-    #[inline]
-    fn get_before_pseudo(&self) -> Option<Self> {
-        if self.borrow_layout_data().unwrap()
-               .style_data.per_pseudo
-               .contains_key(&PseudoElement::Before) {
-            Some(self.with_pseudo(PseudoElementType::Before(None)))
-        } else {
-            None
-        }
-    }
-
-    #[inline]
-    fn get_after_pseudo(&self) -> Option<Self> {
-        if self.borrow_layout_data().unwrap()
-               .style_data.per_pseudo
-               .contains_key(&PseudoElement::After) {
-            Some(self.with_pseudo(PseudoElementType::After(None)))
-        } else {
-            None
-        }
-    }
-
-    #[inline]
-    fn get_details_summary_pseudo(&self) -> Option<Self> {
-        if self.is_element() &&
-           self.as_element().get_local_name() == atom!("details") &&
-           self.as_element().get_namespace() == ns!(html) {
-            Some(self.with_pseudo(PseudoElementType::DetailsSummary(None)))
-        } else {
-            None
-        }
-    }
-
-    #[inline]
-    fn get_details_content_pseudo(&self) -> Option<Self> {
-        if self.is_element() &&
-           self.as_element().get_local_name() == atom!("details") &&
-           self.as_element().get_namespace() == ns!(html) {
-            let display = if self.as_element().get_attr(&ns!(), &atom!("open")).is_some() {
-                None // Specified by the stylesheet
-            } else {
-                Some(display::T::none)
-            };
-            Some(self.with_pseudo(PseudoElementType::DetailsContent(display)))
-        } else {
-            None
-        }
-    }
+    unsafe fn borrow_layout_data_unchecked(&self) -> Option<*const PrivateLayoutData>;
 
     /// Borrows the layout data immutably. Fails on a conflicting borrow.
     ///
     /// TODO(pcwalton): Make this private. It will let us avoid borrow flag checks in some cases.
     #[inline(always)]
     fn borrow_layout_data(&self) -> Option<Ref<PrivateLayoutData>>;
 
     /// Borrows the layout data mutably. Fails on a conflicting borrow.
     ///
     /// TODO(pcwalton): Make this private. It will let us avoid borrow flag checks in some cases.
     #[inline(always)]
     fn mutate_layout_data(&self) -> Option<RefMut<PrivateLayoutData>>;
 
-    /// Returns the style results for the given node. If CSS selector matching
-    /// has not yet been performed, fails.
-    ///
-    /// Unlike the version on TNode, this handles pseudo-elements.
-    #[inline]
-    fn style(&self, context: &SharedStyleContext) -> Ref<Arc<ServoComputedValues>> {
-        match self.get_pseudo_element_type() {
-            PseudoElementType::Normal => {
-                Ref::map(self.borrow_layout_data().unwrap(), |data| {
-                    data.style_data.style.as_ref().unwrap()
-                })
-            },
-            other => {
-                // Precompute non-eagerly-cascaded pseudo-element styles if not
-                // cached before.
-                let style_pseudo = other.style_pseudo_element();
-                match style_pseudo.cascade_type() {
-                    // Already computed during the cascade.
-                    PseudoElementCascadeType::Eager => {},
-                    PseudoElementCascadeType::Precomputed => {
-                        if !self.borrow_layout_data()
-                                .unwrap().style_data
-                                .per_pseudo.contains_key(&style_pseudo) {
-                            let mut data = self.mutate_layout_data().unwrap();
-                            let new_style =
-                                context.stylist
-                                       .precomputed_values_for_pseudo(&style_pseudo,
-                                                                      data.style_data.style.as_ref());
-                            data.style_data.per_pseudo
-                                .insert(style_pseudo.clone(), new_style.unwrap());
-                        }
-                    }
-                    PseudoElementCascadeType::Lazy => {
-                        debug_assert!(self.is_element_or_elements_pseudo());
-                        if !self.borrow_layout_data()
-                                .unwrap().style_data
-                                .per_pseudo.contains_key(&style_pseudo) {
-                            let mut data = self.mutate_layout_data().unwrap();
-                            let new_style =
-                                context.stylist
-                                       .lazily_compute_pseudo_element_style(
-                                           &self.as_element(),
-                                           &style_pseudo,
-                                           data.style_data.style.as_ref().unwrap());
-                            data.style_data.per_pseudo
-                                .insert(style_pseudo.clone(), new_style.unwrap());
-                        }
-                    }
-                }
-
-                Ref::map(self.borrow_layout_data().unwrap(), |data| {
-                    data.style_data.per_pseudo.get(&style_pseudo).unwrap()
-                })
-            }
-        }
-    }
-
-    /// Returns the already resolved style of the node.
-    ///
-    /// This differs from `style(ctx)` in that if the pseudo-element has not yet
-    /// been computed it would panic.
-    ///
-    /// This should be used just for querying layout, or when we know the
-    /// element style is precomputed, not from general layout itself.
-    #[inline]
-    fn resolved_style(&self) -> Ref<Arc<ServoComputedValues>> {
-        Ref::map(self.borrow_layout_data().unwrap(), |data| {
-            match self.get_pseudo_element_type() {
-                PseudoElementType::Normal
-                    => data.style_data.style.as_ref().unwrap(),
-                other
-                    => data.style_data.per_pseudo.get(&other.style_pseudo_element()).unwrap(),
-            }
-        })
-    }
-
-    #[inline]
-    fn selected_style(&self, _context: &SharedStyleContext) -> Ref<Arc<ServoComputedValues>> {
-        Ref::map(self.borrow_layout_data().unwrap(), |data| {
-            data.style_data.per_pseudo
-                .get(&PseudoElement::Selection)
-                .unwrap_or(data.style_data.style.as_ref().unwrap())
-        })
-    }
-
-    /// Removes the style from this node.
-    ///
-    /// Unlike the version on TNode, this handles pseudo-elements.
-    fn unstyle(self) {
-        let mut data = self.mutate_layout_data().unwrap();
-
-        match self.get_pseudo_element_type() {
-            PseudoElementType::Normal => {
-                data.style_data.style = None;
-            }
-            other => {
-                data.style_data.per_pseudo.remove(&other.style_pseudo_element());
-            }
-        };
-    }
-
-    fn is_ignorable_whitespace(&self, context: &SharedStyleContext) -> bool;
-
-    fn restyle_damage(self) -> RestyleDamage;
-
-    fn set_restyle_damage(self, damage: RestyleDamage);
-
     /// Returns the layout data flags for this node.
     fn flags(self) -> LayoutDataFlags;
 
     /// Adds the given flags to this node.
-    fn insert_flags(self, new_flags: LayoutDataFlags) {
-        self.mutate_layout_data().unwrap().flags.insert(new_flags);
-    }
+    fn insert_flags(self, new_flags: LayoutDataFlags);
 
     /// Removes the given flags from this node.
-    fn remove_flags(self, flags: LayoutDataFlags) {
-        self.mutate_layout_data().unwrap().flags.remove(flags);
-    }
-
-    /// Returns true if this node contributes content. This is used in the implementation of
-    /// `empty_cells` per CSS 2.1 § 17.6.1.1.
-    fn is_content(&self) -> bool {
-        match self.type_id() {
-            Some(NodeTypeId::Element(..)) | Some(NodeTypeId::CharacterData(CharacterDataTypeId::Text)) => true,
-            _ => false
-        }
-    }
-
-    fn can_be_fragmented(&self) -> bool;
+    fn remove_flags(self, flags: LayoutDataFlags);
 
     /// If this is a text node, generated content, or a form element, copies out
     /// its content. Otherwise, panics.
     ///
     /// FIXME(pcwalton): This might have too much copying and/or allocation. Profile this.
     fn text_content(&self) -> TextContent;
-
-    /// If the insertion point is within this node, returns it. Otherwise, returns `None`.
-    fn selection(&self) -> Option<Range<ByteIndex>>;
-
-    /// If this is an image element, returns its URL. If this is not an image element, fails.
-    ///
-    /// FIXME(pcwalton): Don't copy URLs.
-    fn image_url(&self) -> Option<Url>;
-
-    fn canvas_data(&self) -> Option<HTMLCanvasData>;
-
-    /// If this node is an iframe element, returns its pipeline ID. If this node is
-    /// not an iframe element, fails.
-    fn iframe_pipeline_id(&self) -> PipelineId;
-
-    fn get_colspan(&self) -> u32;
-
-    fn layer_id(&self) -> LayerId {
-        let layer_type = match self.get_pseudo_element_type() {
-            PseudoElementType::Normal => LayerType::FragmentBody,
-            PseudoElementType::Before(_) => LayerType::BeforePseudoContent,
-            PseudoElementType::After(_) => LayerType::AfterPseudoContent,
-            PseudoElementType::DetailsSummary(_) => LayerType::FragmentBody,
-            PseudoElementType::DetailsContent(_) => LayerType::FragmentBody,
-        };
-        LayerId::new_of_type(layer_type, self.opaque().id() as usize)
-    }
-
-    fn layer_id_for_overflow_scroll(&self) -> LayerId {
-        LayerId::new_of_type(LayerType::OverflowScroll, self.opaque().id() as usize)
-    }
-}
-
-// This trait is only public so that it can be implemented by the gecko wrapper.
-// It can be used to violate thread-safety, so don't use it elsewhere in layout!
-pub trait DangerousThreadSafeLayoutNode: ThreadSafeLayoutNode {
-    unsafe fn dangerous_first_child(&self) -> Option<Self>;
-    unsafe fn dangerous_next_sibling(&self) -> Option<Self>;
 }
 
-pub trait ThreadSafeLayoutElement: Clone + Copy + Sized +
-                                   ::selectors::Element<Impl=ServoSelectorImpl> +
-                                   PresentationalHintsSynthetizer {
-    type ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode<ConcreteThreadSafeLayoutElement = Self>;
-
-    #[inline]
-    fn get_attr(&self, namespace: &Namespace, name: &Atom) -> Option<&str>;
-
-    #[inline]
-    fn get_local_name<'a>(&'a self) -> BorrowedAtom<'a>;
-
-    #[inline]
-    fn get_namespace<'a>(&'a self) -> BorrowedNamespace<'a>;
-}
-
-#[derive(Copy, Clone)]
-pub struct ServoThreadSafeLayoutNode<'ln> {
-    /// The wrapped node.
-    node: ServoLayoutNode<'ln>,
-
-    /// The pseudo-element type, with (optionally),
-    /// an specified display value to override the stylesheet.
-    pseudo: PseudoElementType<Option<display::T>>,
-}
-
-impl<'a> PartialEq for ServoThreadSafeLayoutNode<'a> {
-    #[inline]
-    fn eq(&self, other: &ServoThreadSafeLayoutNode<'a>) -> bool {
-        self.node == other.node
-    }
-}
-
-impl<'ln> DangerousThreadSafeLayoutNode for ServoThreadSafeLayoutNode<'ln> {
-    unsafe fn dangerous_first_child(&self) -> Option<Self> {
-            self.get_jsmanaged().first_child_ref()
-                .map(|node| self.new_with_this_lifetime(&node))
-    }
-    unsafe fn dangerous_next_sibling(&self) -> Option<Self> {
-            self.get_jsmanaged().next_sibling_ref()
-                .map(|node| self.new_with_this_lifetime(&node))
-    }
-}
-
-impl<'ln> ServoThreadSafeLayoutNode<'ln> {
-    /// Creates a new layout node with the same lifetime as this layout node.
-    pub unsafe fn new_with_this_lifetime(&self, node: &LayoutJS<Node>) -> ServoThreadSafeLayoutNode<'ln> {
-        ServoThreadSafeLayoutNode {
-            node: self.node.new_with_this_lifetime(node),
-            pseudo: PseudoElementType::Normal,
-        }
+impl<T: ThreadSafeLayoutNode> ThreadSafeLayoutNodeHelpers for T {
+    fn flow_debug_id(self) -> usize {
+        self.borrow_layout_data().map_or(0, |d| d.flow_construction_result.debug_id())
     }
 
-    /// Creates a new `ServoThreadSafeLayoutNode` from the given `ServoLayoutNode`.
-    pub fn new<'a>(node: &ServoLayoutNode<'a>) -> ServoThreadSafeLayoutNode<'a> {
-        ServoThreadSafeLayoutNode {
-            node: node.clone(),
-            pseudo: PseudoElementType::Normal,
+    unsafe fn borrow_layout_data_unchecked(&self) -> Option<*const PrivateLayoutData> {
+        self.get_style_and_layout_data().map(|opaque| {
+            let container = *opaque.ptr as NonOpaqueStyleAndLayoutData;
+            &(*(*container).as_unsafe_cell().get()) as *const PrivateLayoutData
+        })
+    }
+
+    fn borrow_layout_data(&self) -> Option<Ref<PrivateLayoutData>> {
+        unsafe {
+            self.get_style_and_layout_data().map(|opaque| {
+                let container = *opaque.ptr as NonOpaqueStyleAndLayoutData;
+                (*container).borrow()
+            })
         }
     }
 
-    /// Returns the interior of this node as a `LayoutJS`. This is highly unsafe for layout to
-    /// call and as such is marked `unsafe`.
-    unsafe fn get_jsmanaged(&self) -> &LayoutJS<Node> {
-        self.node.get_jsmanaged()
-    }
-}
-
-impl<'ln> ThreadSafeLayoutNode for ServoThreadSafeLayoutNode<'ln> {
-    type ConcreteThreadSafeLayoutElement = ServoThreadSafeLayoutElement<'ln>;
-    type ChildrenIterator = ThreadSafeLayoutNodeChildrenIterator<Self>;
-
-    fn with_pseudo(&self,
-                   pseudo: PseudoElementType<Option<display::T>>) -> ServoThreadSafeLayoutNode<'ln> {
-        ServoThreadSafeLayoutNode {
-            node: self.node.clone(),
-            pseudo: pseudo,
-        }
-    }
-
-    fn opaque(&self) -> OpaqueNode {
-        OpaqueNodeMethods::from_jsmanaged(unsafe { self.get_jsmanaged() })
-    }
-
-    fn type_id(&self) -> Option<NodeTypeId> {
-        if self.pseudo != PseudoElementType::Normal {
-            return None
-        }
-
-        Some(self.node.type_id())
-    }
-
-    #[inline]
-    fn type_id_without_excluding_pseudo_elements(&self) -> NodeTypeId {
-        self.node.type_id()
-    }
-
-    fn debug_id(self) -> usize {
-        self.node.debug_id()
-    }
-
-    fn flow_debug_id(self) -> usize {
-        self.node.flow_debug_id()
-    }
-
-    fn children(&self) -> Self::ChildrenIterator {
-        ThreadSafeLayoutNodeChildrenIterator::new(*self)
-    }
-
-    fn as_element(&self) -> ServoThreadSafeLayoutElement<'ln> {
+    fn mutate_layout_data(&self) -> Option<RefMut<PrivateLayoutData>> {
         unsafe {
-            let element = match self.get_jsmanaged().downcast() {
-                Some(e) => e.unsafe_get(),
-                None => panic!("not an element")
-            };
-            // FIXME(pcwalton): Workaround until Rust gets multiple lifetime parameters on
-            // implementations.
-            ServoThreadSafeLayoutElement {
-                element: &*element,
-            }
+            self.get_style_and_layout_data().map(|opaque| {
+                let container = *opaque.ptr as NonOpaqueStyleAndLayoutData;
+                (*container).borrow_mut()
+            })
         }
     }
 
-    fn get_pseudo_element_type(&self) -> PseudoElementType<Option<display::T>> {
-        self.pseudo
-    }
-
-    fn borrow_layout_data(&self) -> Option<Ref<PrivateLayoutData>> {
-        self.node.borrow_layout_data()
-    }
-
-    fn mutate_layout_data(&self) -> Option<RefMut<PrivateLayoutData>> {
-        self.node.mutate_layout_data()
-    }
-
-    fn is_ignorable_whitespace(&self, context: &SharedStyleContext) -> bool {
-        unsafe {
-            let text: LayoutJS<Text> = match self.get_jsmanaged().downcast() {
-                Some(text) => text,
-                None => return false
-            };
-
-            if !is_whitespace(text.upcast().data_for_layout()) {
-                return false
-            }
-
-            // NB: See the rules for `white-space` here:
-            //
-            //    http://www.w3.org/TR/CSS21/text.html#propdef-white-space
-            //
-            // If you implement other values for this property, you will almost certainly
-            // want to update this check.
-            !self.style(context).get_inheritedtext().white_space.preserve_newlines()
-        }
-    }
-
-    fn restyle_damage(self) -> RestyleDamage {
-        self.node.restyle_damage()
-    }
-
-    fn set_restyle_damage(self, damage: RestyleDamage) {
-        self.node.set_restyle_damage(damage)
-    }
-
     fn flags(self) -> LayoutDataFlags {
         unsafe {
-            (*self.node.borrow_layout_data_unchecked().unwrap()).flags
+            (*self.borrow_layout_data_unchecked().unwrap()).flags
         }
     }
 
-    fn can_be_fragmented(&self) -> bool {
-        self.node.can_be_fragmented()
+    fn insert_flags(self, new_flags: LayoutDataFlags) {
+        self.mutate_layout_data().unwrap().flags.insert(new_flags);
+    }
+
+    fn remove_flags(self, flags: LayoutDataFlags) {
+        self.mutate_layout_data().unwrap().flags.remove(flags);
     }
 
     fn text_content(&self) -> TextContent {
-        if self.pseudo.is_replaced_content() {
+        if self.get_pseudo_element_type().is_replaced_content() {
             let style = self.resolved_style();
 
             return match style.as_ref().get_counters().content {
                 content::T::Content(ref value) if !value.is_empty() => {
                     TextContent::GeneratedContent((*value).clone())
                 }
                 _ => TextContent::GeneratedContent(vec![]),
             };
         }
 
-        let this = unsafe { self.get_jsmanaged() };
-        return TextContent::Text(this.text_content());
-    }
-
-    fn selection(&self) -> Option<Range<ByteIndex>> {
-        let this = unsafe { self.get_jsmanaged() };
-
-        this.selection().map(|range| {
-            Range::new(ByteIndex(range.start as isize),
-                       ByteIndex(range.len() as isize))
-        })
-    }
-
-    fn image_url(&self) -> Option<Url> {
-        let this = unsafe { self.get_jsmanaged() };
-        this.image_url()
-    }
-
-    fn canvas_data(&self) -> Option<HTMLCanvasData> {
-        let this = unsafe { self.get_jsmanaged() };
-        this.canvas_data()
-    }
-
-    fn iframe_pipeline_id(&self) -> PipelineId {
-        let this = unsafe { self.get_jsmanaged() };
-        this.iframe_pipeline_id()
-    }
-
-    fn get_colspan(&self) -> u32 {
-        unsafe {
-            self.get_jsmanaged().downcast::<Element>().unwrap().get_colspan()
-        }
-    }
-}
-
-pub struct ThreadSafeLayoutNodeChildrenIterator<ConcreteNode: ThreadSafeLayoutNode> {
-    current_node: Option<ConcreteNode>,
-    parent_node: ConcreteNode,
-}
-
-impl<ConcreteNode> ThreadSafeLayoutNodeChildrenIterator<ConcreteNode>
-                   where ConcreteNode: DangerousThreadSafeLayoutNode {
-    pub fn new(parent: ConcreteNode) -> Self {
-        let first_child: Option<ConcreteNode> = match parent.get_pseudo_element_type() {
-            PseudoElementType::Normal => {
-                parent.get_before_pseudo().or_else(|| parent.get_details_summary_pseudo()).or_else(|| {
-                    unsafe { parent.dangerous_first_child() }
-                })
-            },
-            PseudoElementType::DetailsContent(_) | PseudoElementType::DetailsSummary(_) => {
-                unsafe { parent.dangerous_first_child() }
-            },
-            _ => None,
-        };
-        ThreadSafeLayoutNodeChildrenIterator {
-            current_node: first_child,
-            parent_node: parent,
-        }
-    }
-}
-
-impl<ConcreteNode> Iterator for ThreadSafeLayoutNodeChildrenIterator<ConcreteNode>
-                            where ConcreteNode: DangerousThreadSafeLayoutNode {
-    type Item = ConcreteNode;
-    fn next(&mut self) -> Option<ConcreteNode> {
-        match self.parent_node.get_pseudo_element_type() {
-            PseudoElementType::Before(_) | PseudoElementType::After(_) => None,
-
-            PseudoElementType::DetailsSummary(_) => {
-                let mut current_node = self.current_node.clone();
-                loop {
-                    let next_node = if let Some(ref node) = current_node {
-                        if node.is_element() &&
-                           node.as_element().get_local_name() == atom!("summary") &&
-                           node.as_element().get_namespace() == ns!(html) {
-                            self.current_node = None;
-                            return Some(node.clone());
-                        }
-                        unsafe { node.dangerous_next_sibling() }
-                    } else {
-                        self.current_node = None;
-                        return None
-                    };
-                    current_node = next_node;
-                }
-            }
-
-            PseudoElementType::DetailsContent(_) => {
-                let node = self.current_node.clone();
-                let node = node.and_then(|node| {
-                    if node.is_element() &&
-                       node.as_element().get_local_name() == atom!("summary") &&
-                       node.as_element().get_namespace() == ns!(html) {
-                        unsafe { node.dangerous_next_sibling() }
-                    } else {
-                        Some(node)
-                    }
-                });
-                self.current_node = node.and_then(|node| unsafe { node.dangerous_next_sibling() });
-                node
-            }
-
-            PseudoElementType::Normal => {
-                let node = self.current_node.clone();
-                if let Some(ref node) = node {
-                    self.current_node = match node.get_pseudo_element_type() {
-                        PseudoElementType::Before(_) => {
-                            let first = self.parent_node.get_details_summary_pseudo().or_else(|| unsafe {
-                                self.parent_node.dangerous_first_child()
-                            });
-                            match first {
-                                Some(first) => Some(first),
-                                None => self.parent_node.get_after_pseudo(),
-                            }
-                        },
-                        PseudoElementType::Normal => {
-                            match unsafe { node.dangerous_next_sibling() } {
-                                Some(next) => Some(next),
-                                None => self.parent_node.get_after_pseudo(),
-                            }
-                        },
-                        PseudoElementType::DetailsSummary(_) => self.parent_node.get_details_content_pseudo(),
-                        PseudoElementType::DetailsContent(_) => self.parent_node.get_after_pseudo(),
-                        PseudoElementType::After(_) => {
-                            None
-                        },
-                    };
-                }
-                node
-            }
-
-        }
-    }
-}
-
-/// A wrapper around elements that ensures layout can only
-/// ever access safe properties and cannot race on elements.
-#[derive(Copy, Clone)]
-pub struct ServoThreadSafeLayoutElement<'le> {
-    element: &'le Element,
-}
-
-impl<'le> ThreadSafeLayoutElement for ServoThreadSafeLayoutElement<'le> {
-    type ConcreteThreadSafeLayoutNode = ServoThreadSafeLayoutNode<'le>;
-
-    fn get_attr<'a>(&'a self, namespace: &Namespace, name: &Atom) -> Option<&'a str> {
-        unsafe {
-            self.element.get_attr_val_for_layout(namespace, name)
-        }
-    }
-
-    #[inline]
-    fn get_local_name<'a>(&'a self) -> BorrowedAtom<'a> {
-        BorrowedAtom(self.element.local_name())
-    }
-
-    #[inline]
-    fn get_namespace<'a>(&'a self) -> BorrowedNamespace<'a> {
-        BorrowedNamespace(self.element.namespace())
+        return TextContent::Text(self.node_text_content());
     }
 }
 
 pub enum TextContent {
     Text(String),
     GeneratedContent(Vec<ContentItem>),
 }
 
 impl TextContent {
     pub fn is_empty(&self) -> bool {
         match *self {
             TextContent::Text(_) => false,
             TextContent::GeneratedContent(ref content) => content.is_empty(),
         }
     }
 }
-
-/// This implementation of `::selectors::Element` is used for implementing lazy
-/// pseudo-elements.
-///
-/// Lazy pseudo-elements in Servo only allows selectors using safe properties,
-/// i.e., local_name, attributes, so they can only be used for **private**
-/// pseudo-elements (like `::-servo-details-content`).
-///
-/// Probably a few more of this functions can be implemented (like `has_class`,
-/// `each_class`, etc), but they have no use right now.
-///
-/// Note that the element implementation is needed only for selector matching,
-/// not for inheritance (styles are inherited appropiately).
-impl <'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> {
-    type Impl = ServoSelectorImpl;
-
-    fn parent_element(&self) -> Option<Self> {
-        warn!("ServoThreadSafeLayoutElement::parent_element called");
-        None
-    }
-
-    fn first_child_element(&self) -> Option<Self> {
-        warn!("ServoThreadSafeLayoutElement::first_child_element called");
-        None
-    }
-
-    // Skips non-element nodes
-    fn last_child_element(&self) -> Option<Self> {
-        warn!("ServoThreadSafeLayoutElement::last_child_element called");
-        None
-    }
-
-    // Skips non-element nodes
-    fn prev_sibling_element(&self) -> Option<Self> {
-        warn!("ServoThreadSafeLayoutElement::prev_sibling_element called");
-        None
-    }
-
-    // Skips non-element nodes
-    fn next_sibling_element(&self) -> Option<Self> {
-        warn!("ServoThreadSafeLayoutElement::next_sibling_element called");
-        None
-    }
-
-    fn is_html_element_in_html_document(&self) -> bool {
-        debug!("ServoThreadSafeLayoutElement::is_html_element_in_html_document called");
-        true
-    }
-
-    #[inline]
-    fn get_local_name<'a>(&'a self) -> BorrowedAtom<'a> {
-        ThreadSafeLayoutElement::get_local_name(self)
-    }
-
-    #[inline]
-    fn get_namespace<'a>(&'a self) -> BorrowedNamespace<'a> {
-        ThreadSafeLayoutElement::get_namespace(self)
-    }
-
-    fn match_non_ts_pseudo_class(&self, _: NonTSPseudoClass) -> bool {
-        // NB: This could maybe be implemented
-        warn!("ServoThreadSafeLayoutElement::match_non_ts_pseudo_class called");
-        false
-    }
-
-    fn get_id(&self) -> Option<Atom> {
-        debug!("ServoThreadSafeLayoutElement::get_id called");
-        None
-    }
-
-    fn has_class(&self, _name: &Atom) -> bool {
-        debug!("ServoThreadSafeLayoutElement::has_class called");
-        false
-    }
-
-    fn match_attr<F>(&self, attr: &AttrSelector, test: F) -> bool
-        where F: Fn(&str) -> bool {
-        match attr.namespace {
-            NamespaceConstraint::Specific(ref ns) => {
-                self.get_attr(ns, &attr.name).map_or(false, |attr| test(attr))
-            },
-            NamespaceConstraint::Any => {
-                unsafe {
-                    self.element.get_attr_vals_for_layout(&attr.name).iter()
-                        .any(|attr| test(*attr))
-                }
-            }
-        }
-    }
-
-    fn is_empty(&self) -> bool {
-        warn!("ServoThreadSafeLayoutElement::is_empty called");
-        false
-    }
-
-    fn is_root(&self) -> bool {
-        warn!("ServoThreadSafeLayoutElement::is_root called");
-        false
-    }
-
-    fn each_class<F>(&self, _callback: F)
-        where F: FnMut(&Atom) {
-        warn!("ServoThreadSafeLayoutElement::each_class called");
-    }
-}
-
-impl<'le> PresentationalHintsSynthetizer for ServoThreadSafeLayoutElement<'le> {
-    fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, _hints: &mut V)
-        where V: VecLike<DeclarationBlock<Vec<PropertyDeclaration>>> {}
-}
--- a/servo/components/script/Cargo.toml
+++ b/servo/components/script/Cargo.toml
@@ -44,20 +44,22 @@ net_traits = {path = "../net_traits"}
 num-traits = "0.1.32"
 offscreen_gl_context = "0.1.2"
 open = "1.1.1"
 phf = "0.7.13"
 phf_macros = "0.7.13"
 plugins = {path = "../plugins"}
 profile_traits = {path = "../profile_traits"}
 rand = "0.3"
+range = {path = "../range"}
 ref_filter_map = "1.0"
 ref_slice = "1.0"
 regex = "0.1.43"
 rustc-serialize = "0.3"
+script_layout_interface = {path = "../script_layout_interface"}
 script_traits = {path = "../script_traits"}
 selectors = {version = "0.6", features = ["heap_size"]}
 serde = "0.7"
 smallvec = "0.1"
 string_cache = {version = "0.2.20", features = ["heap_size", "unstable"]}
 style = {path = "../style"}
 time = "0.1.12"
 url = {version = "1.0.0", features = ["heap_size", "query_encoding"]}
--- a/servo/components/script/dom/bindings/js.rs
+++ b/servo/components/script/dom/bindings/js.rs
@@ -28,17 +28,17 @@ use dom::bindings::conversions::DerivedF
 use dom::bindings::inheritance::Castable;
 use dom::bindings::reflector::{Reflectable, Reflector};
 use dom::bindings::trace::JSTraceable;
 use dom::bindings::trace::trace_reflector;
 use dom::node::Node;
 use heapsize::HeapSizeOf;
 use js::jsapi::{Heap, JSObject, JSTracer};
 use js::jsval::JSVal;
-use layout_interface::TrustedNodeAddress;
+use script_layout_interface::TrustedNodeAddress;
 use script_thread::STACK_ROOTS;
 use std::cell::UnsafeCell;
 use std::default::Default;
 use std::hash::{Hash, Hasher};
 #[cfg(debug_assertions)]
 use std::intrinsics::type_name;
 use std::mem;
 use std::ops::Deref;
--- a/servo/components/script/dom/bindings/trace.rs
+++ b/servo/components/script/dom/bindings/trace.rs
@@ -50,28 +50,30 @@ use html5ever::tree_builder::QuirksMode;
 use hyper::header::Headers;
 use hyper::method::Method;
 use hyper::mime::Mime;
 use ipc_channel::ipc::{IpcReceiver, IpcSender};
 use js::glue::{CallObjectTracer, CallUnbarrieredObjectTracer, CallValueTracer};
 use js::jsapi::{GCTraceKindToAscii, Heap, TraceKind, JSObject, JSTracer};
 use js::jsval::JSVal;
 use js::rust::Runtime;
-use layout_interface::LayoutRPC;
 use libc;
 use msg::constellation_msg::{FrameType, PipelineId, SubpageId, WindowSizeData, WindowSizeType, ReferrerPolicy};
 use net_traits::filemanager_thread::SelectedFileId;
 use net_traits::image::base::{Image, ImageMetadata};
 use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheThread};
 use net_traits::response::HttpsState;
 use net_traits::storage_thread::StorageType;
 use net_traits::{Metadata, NetworkError, ResourceThreads};
 use offscreen_gl_context::GLLimits;
 use profile_traits::mem::ProfilerChan as MemProfilerChan;
 use profile_traits::time::ProfilerChan as TimeProfilerChan;
+use script_layout_interface::OpaqueStyleAndLayoutData;
+use script_layout_interface::reporter::CSSErrorReporter;
+use script_layout_interface::rpc::LayoutRPC;
 use script_runtime::ScriptChan;
 use script_traits::{TimerEventId, TimerSource, TouchpadPressurePhase, UntrustedNodeAddress};
 use serde::{Deserialize, Serialize};
 use smallvec::SmallVec;
 use std::boxed::FnBox;
 use std::cell::{Cell, UnsafeCell};
 use std::collections::{BTreeMap, HashMap, HashSet};
 use std::hash::{BuildHasher, Hash};
@@ -323,16 +325,18 @@ no_jsmanaged_fields!(ElementSnapshot);
 no_jsmanaged_fields!(HttpsState);
 no_jsmanaged_fields!(SharedRt);
 no_jsmanaged_fields!(TouchpadPressurePhase);
 no_jsmanaged_fields!(USVString);
 no_jsmanaged_fields!(ReferrerPolicy);
 no_jsmanaged_fields!(ResourceThreads);
 no_jsmanaged_fields!(SystemTime);
 no_jsmanaged_fields!(SelectedFileId);
+no_jsmanaged_fields!(OpaqueStyleAndLayoutData);
+no_jsmanaged_fields!(CSSErrorReporter);
 
 impl JSTraceable for Box<ScriptChan + Send> {
     #[inline]
     fn trace(&self, _trc: *mut JSTracer) {
         // Do nothing
     }
 }
 
--- a/servo/components/script/dom/document.rs
+++ b/servo/components/script/dom/document.rs
@@ -87,27 +87,27 @@ use dom::webglcontextevent::WebGLContext
 use dom::window::{ReflowReason, Window};
 use encoding::EncodingRef;
 use encoding::all::UTF_8;
 use euclid::point::Point2D;
 use html5ever::tree_builder::{LimitedQuirks, NoQuirks, Quirks, QuirksMode};
 use ipc_channel::ipc::{self, IpcSender};
 use js::jsapi::JS_GetRuntime;
 use js::jsapi::{JSContext, JSObject, JSRuntime};
-use layout_interface::{Msg, ReflowQueryType};
 use msg::constellation_msg::{ALT, CONTROL, SHIFT, SUPER};
 use msg::constellation_msg::{Key, KeyModifiers, KeyState};
 use msg::constellation_msg::{PipelineId, ReferrerPolicy, SubpageId};
 use net_traits::CookieSource::NonHTTP;
 use net_traits::CoreResourceMsg::{GetCookiesForUrl, SetCookiesForUrl};
 use net_traits::response::HttpsState;
 use net_traits::{AsyncResponseTarget, PendingAsyncLoad, IpcSend};
 use num_traits::ToPrimitive;
 use origin::Origin;
 use parse::{ParserRoot, ParserRef, MutNullableParserField};
+use script_layout_interface::message::{Msg, ReflowQueryType};
 use script_thread::{MainThreadScriptMsg, Runnable};
 use script_traits::UntrustedNodeAddress;
 use script_traits::{AnimationState, MouseButton, MouseEventType, MozBrowserEvent};
 use script_traits::{ScriptMsg as ConstellationMsg, TouchpadPressurePhase};
 use script_traits::{TouchEventType, TouchId};
 use std::ascii::AsciiExt;
 use std::borrow::ToOwned;
 use std::boxed::FnBox;
--- a/servo/components/script/dom/htmlcanvaselement.rs
+++ b/servo/components/script/dom/htmlcanvaselement.rs
@@ -25,16 +25,17 @@ use dom::virtualmethods::VirtualMethods;
 use dom::webglrenderingcontext::{LayoutCanvasWebGLRenderingContextHelpers, WebGLRenderingContext};
 use euclid::size::Size2D;
 use image::ColorType;
 use image::png::PNGEncoder;
 use ipc_channel::ipc::{self, IpcSender};
 use js::jsapi::{HandleValue, JSContext};
 use offscreen_gl_context::GLContextAttributes;
 use rustc_serialize::base64::{STANDARD, ToBase64};
+use script_layout_interface::HTMLCanvasData;
 use std::iter::repeat;
 use string_cache::Atom;
 use style::attr::AttrValue;
 
 const DEFAULT_WIDTH: u32 = 300;
 const DEFAULT_HEIGHT: u32 = 150;
 
 #[must_root]
@@ -87,22 +88,16 @@ impl HTMLCanvasElement {
     pub fn origin_is_clean(&self) -> bool {
         match *self.context.borrow() {
             Some(CanvasContext::Context2d(ref context)) => context.origin_is_clean(),
             _ => true,
         }
     }
 }
 
-pub struct HTMLCanvasData {
-    pub ipc_renderer: Option<IpcSender<CanvasMsg>>,
-    pub width: u32,
-    pub height: u32,
-}
-
 pub trait LayoutHTMLCanvasElementHelpers {
     fn data(&self) -> HTMLCanvasData;
 }
 
 impl LayoutHTMLCanvasElementHelpers for LayoutJS<HTMLCanvasElement> {
     #[allow(unsafe_code)]
     fn data(&self) -> HTMLCanvasData {
         unsafe {
--- a/servo/components/script/dom/htmliframeelement.rs
+++ b/servo/components/script/dom/htmliframeelement.rs
@@ -33,19 +33,19 @@ use dom::eventtarget::EventTarget;
 use dom::htmlelement::HTMLElement;
 use dom::node::{Node, NodeDamage, UnbindContext, window_from_node, document_from_node};
 use dom::urlhelper::UrlHelper;
 use dom::virtualmethods::VirtualMethods;
 use dom::window::{ReflowReason, Window};
 use ipc_channel::ipc;
 use js::jsapi::{JSAutoCompartment, RootedValue, JSContext, MutableHandleValue};
 use js::jsval::{UndefinedValue, NullValue};
-use layout_interface::ReflowQueryType;
 use msg::constellation_msg::{FrameType, LoadData, NavigationDirection, PipelineId, SubpageId};
 use net_traits::response::HttpsState;
+use script_layout_interface::message::ReflowQueryType;
 use script_traits::IFrameSandboxState::{IFrameSandboxed, IFrameUnsandboxed};
 use script_traits::{IFrameLoadInfo, MozBrowserEvent, ScriptMsg as ConstellationMsg};
 use std::cell::Cell;
 use string_cache::Atom;
 use style::attr::{AttrValue, LengthOrPercentageOrAuto};
 use style::context::ReflowGoal;
 use url::Url;
 use util::prefs::mozbrowser_enabled;
--- a/servo/components/script/dom/htmllinkelement.rs
+++ b/servo/components/script/dom/htmllinkelement.rs
@@ -20,19 +20,19 @@ use dom::htmlelement::HTMLElement;
 use dom::node::{Node, document_from_node, window_from_node};
 use dom::virtualmethods::VirtualMethods;
 use encoding::EncodingRef;
 use encoding::all::UTF_8;
 use hyper::header::ContentType;
 use hyper::mime::{Mime, TopLevel, SubLevel};
 use ipc_channel::ipc;
 use ipc_channel::router::ROUTER;
-use layout_interface::Msg;
 use net_traits::{AsyncResponseListener, AsyncResponseTarget, Metadata, NetworkError};
 use network_listener::{NetworkListener, PreInvoke};
+use script_layout_interface::message::Msg;
 use script_traits::{MozBrowserEvent, ScriptMsg as ConstellationMsg};
 use std::ascii::AsciiExt;
 use std::borrow::ToOwned;
 use std::cell::Cell;
 use std::default::Default;
 use std::mem;
 use std::sync::{Arc, Mutex};
 use string_cache::Atom;
--- a/servo/components/script/dom/htmlobjectelement.rs
+++ b/servo/components/script/dom/htmlobjectelement.rs
@@ -63,21 +63,16 @@ impl<'a> ProcessDataURL for &'a HTMLObje
             (None, Some(_uri)) => {
                 // TODO(gw): Prefetch the image here.
             }
             _ => { }
         }
     }
 }
 
-pub fn is_image_data(uri: &str) -> bool {
-    static TYPES: &'static [&'static str] = &["data:image/png", "data:image/gif", "data:image/jpeg"];
-    TYPES.iter().any(|&type_| uri.starts_with(type_))
-}
-
 impl HTMLObjectElementMethods for HTMLObjectElement {
     // https://html.spec.whatwg.org/multipage/#dom-cva-validity
     fn Validity(&self) -> Root<ValidityState> {
         let window = window_from_node(self);
         ValidityState::new(window.r(), self.upcast())
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-object-type
--- a/servo/components/script/dom/htmlstyleelement.rs
+++ b/servo/components/script/dom/htmlstyleelement.rs
@@ -9,17 +9,17 @@ use dom::bindings::codegen::Bindings::No
 use dom::bindings::inheritance::Castable;
 use dom::bindings::js::Root;
 use dom::bindings::str::DOMString;
 use dom::document::Document;
 use dom::element::Element;
 use dom::htmlelement::HTMLElement;
 use dom::node::{ChildrenMutation, Node, document_from_node, window_from_node};
 use dom::virtualmethods::VirtualMethods;
-use layout_interface::Msg;
+use script_layout_interface::message::Msg;
 use std::sync::Arc;
 use string_cache::Atom;
 use style::media_queries::parse_media_query_list;
 use style::parser::ParserContextExtraData;
 use style::servo::Stylesheet;
 use style::stylesheets::Origin;
 
 #[dom_struct]
--- a/servo/components/script/dom/node.rs
+++ b/servo/components/script/dom/node.rs
@@ -1,49 +1,48 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! The core DOM types. Defines the basic DOM hierarchy as well as all the HTML elements.
 
 use app_units::Au;
-use core::nonzero::NonZero;
 use devtools_traits::NodeInfo;
 use document_loader::DocumentLoader;
 use dom::attr::Attr;
 use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
 use dom::bindings::codegen::Bindings::CharacterDataBinding::CharacterDataMethods;
 use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
 use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
 use dom::bindings::codegen::Bindings::HTMLCollectionBinding::HTMLCollectionMethods;
 use dom::bindings::codegen::Bindings::NodeBinding::{NodeConstants, NodeMethods};
 use dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods;
 use dom::bindings::codegen::Bindings::ProcessingInstructionBinding::ProcessingInstructionMethods;
 use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
 use dom::bindings::codegen::UnionTypes::NodeOrString;
 use dom::bindings::conversions::{self, DerivedFrom};
 use dom::bindings::error::{Error, ErrorResult, Fallible};
 use dom::bindings::global::GlobalRef;
-use dom::bindings::inheritance::{Castable, CharacterDataTypeId};
-use dom::bindings::inheritance::{EventTargetTypeId, NodeTypeId};
+use dom::bindings::inheritance::{Castable, CharacterDataTypeId, ElementTypeId};
+use dom::bindings::inheritance::{EventTargetTypeId, HTMLElementTypeId, NodeTypeId};
 use dom::bindings::js::Root;
 use dom::bindings::js::RootedReference;
 use dom::bindings::js::{JS, LayoutJS, MutNullableHeap};
 use dom::bindings::reflector::{Reflectable, reflect_dom_object};
 use dom::bindings::str::{DOMString, USVString};
 use dom::bindings::trace::RootedVec;
 use dom::bindings::xmlname::namespace_from_domstring;
 use dom::characterdata::{CharacterData, LayoutCharacterDataHelpers};
 use dom::document::{Document, DocumentSource, IsHTMLDocument};
 use dom::documentfragment::DocumentFragment;
 use dom::documenttype::DocumentType;
 use dom::element::{Element, ElementCreator};
 use dom::eventtarget::EventTarget;
 use dom::htmlbodyelement::HTMLBodyElement;
-use dom::htmlcanvaselement::{LayoutHTMLCanvasElementHelpers, HTMLCanvasData};
+use dom::htmlcanvaselement::LayoutHTMLCanvasElementHelpers;
 use dom::htmlcollection::HTMLCollection;
 use dom::htmlelement::HTMLElement;
 use dom::htmliframeelement::{HTMLIFrameElement, HTMLIFrameElementLayoutMethods};
 use dom::htmlimageelement::{HTMLImageElement, LayoutHTMLImageElementHelpers};
 use dom::htmlinputelement::{HTMLInputElement, LayoutHTMLInputElementHelpers};
 use dom::htmltextareaelement::{HTMLTextAreaElement, LayoutHTMLTextAreaElementHelpers};
 use dom::nodelist::NodeList;
 use dom::processinginstruction::ProcessingInstruction;
@@ -52,33 +51,36 @@ use dom::text::Text;
 use dom::virtualmethods::{VirtualMethods, vtable_for};
 use dom::window::Window;
 use euclid::point::Point2D;
 use euclid::rect::Rect;
 use euclid::size::Size2D;
 use heapsize::{HeapSizeOf, heap_size_of};
 use html5ever::tree_builder::QuirksMode;
 use js::jsapi::{JSContext, JSObject, JSRuntime};
-use layout_interface::Msg;
 use libc::{self, c_void, uintptr_t};
 use msg::constellation_msg::PipelineId;
 use parse::html::parse_html_fragment;
 use ref_slice::ref_slice;
+use script_layout_interface::message::Msg;
+use script_layout_interface::{HTMLCanvasData, OpaqueStyleAndLayoutData};
+use script_layout_interface::{LayoutNodeType, LayoutElementType, TrustedNodeAddress};
 use script_traits::UntrustedNodeAddress;
 use selectors::matching::matches;
 use selectors::parser::Selector;
 use selectors::parser::parse_author_origin_selector_list_from_str;
 use std::borrow::ToOwned;
 use std::cell::{Cell, UnsafeCell};
 use std::cmp::max;
 use std::default::Default;
 use std::iter::{self, FilterMap, Peekable};
 use std::mem;
 use std::ops::Range;
 use string_cache::{Atom, Namespace, QualName};
+use style::dom::OpaqueNode;
 use style::selector_impl::ServoSelectorImpl;
 use url::Url;
 use util::thread_state;
 use uuid::Uuid;
 
 //
 // The basic Node structure
 //
@@ -166,52 +168,38 @@ impl NodeFlags {
     pub fn new() -> NodeFlags {
         HAS_CHANGED | IS_DIRTY | HAS_DIRTY_DESCENDANTS
     }
 }
 
 impl Drop for Node {
     #[allow(unsafe_code)]
     fn drop(&mut self) {
-        self.style_and_layout_data.get().map(|d| d.dispose(self));
+        self.style_and_layout_data.get().map(|d| self.dispose(d));
     }
 }
 
 /// suppress observers flag
 /// https://dom.spec.whatwg.org/#concept-node-insert
 /// https://dom.spec.whatwg.org/#concept-node-remove
 #[derive(Copy, Clone, HeapSizeOf)]
 enum SuppressObserver {
     Suppressed,
     Unsuppressed
 }
 
-#[derive(Copy, Clone, HeapSizeOf)]
-pub struct OpaqueStyleAndLayoutData {
-    #[ignore_heap_size_of = "TODO(#6910) Box value that should be counted but \
-                             the type lives in layout"]
-    pub ptr: NonZero<*mut ()>
-}
-
-#[allow(unsafe_code)]
-unsafe impl Send for OpaqueStyleAndLayoutData {}
-
-no_jsmanaged_fields!(OpaqueStyleAndLayoutData);
+impl Node {
+    /// Sends the style and layout data, if any, back to the layout thread to be destroyed.
+    pub fn dispose(&self, data: OpaqueStyleAndLayoutData) {
+        debug_assert!(thread_state::get().is_script());
+        let win = window_from_node(self);
+        self.style_and_layout_data.set(None);
+        win.layout_chan().send(Msg::ReapStyleAndLayoutData(data)).unwrap();
+    }
 
-impl OpaqueStyleAndLayoutData {
-    /// Sends the style and layout data, if any, back to the layout thread to be destroyed.
-    pub fn dispose(self, node: &Node) {
-        debug_assert!(thread_state::get().is_script());
-        let win = window_from_node(node);
-        node.style_and_layout_data.set(None);
-        win.layout_chan().send(Msg::ReapStyleAndLayoutData(self)).unwrap();
-    }
-}
-
-impl Node {
     /// Adds a new child to the end of this node's list of children.
     ///
     /// Fails unless `new_child` is disconnected from the tree.
     fn add_child(&self, new_child: &Node, before: Option<&Node>) {
         assert!(new_child.parent_node.get().is_none());
         assert!(new_child.prev_sibling.get().is_none());
         assert!(new_child.next_sibling.get().is_none());
         match before {
@@ -287,17 +275,17 @@ impl Node {
         child.prev_sibling.set(None);
         child.next_sibling.set(None);
         child.parent_node.set(None);
         self.children_count.set(self.children_count.get() - 1);
 
         for node in child.traverse_preorder() {
             node.set_flag(IS_IN_DOC, false);
             vtable_for(&&*node).unbind_from_tree(&context);
-            node.style_and_layout_data.get().map(|d| d.dispose(&node));
+            node.style_and_layout_data.get().map(|d| node.dispose(d));
         }
 
         self.owner_doc().content_and_heritage_changed(self, NodeDamage::OtherNodeDamage);
         child.owner_doc().content_and_heritage_changed(child, NodeDamage::OtherNodeDamage);
     }
 
     pub fn to_untrusted_node_address(&self) -> UntrustedNodeAddress {
         UntrustedNodeAddress(self.reflector().get_jsobject().get() as *const c_void)
@@ -335,17 +323,17 @@ impl<'a> Iterator for QuerySelectorItera
             None
         }).next()
     }
 }
 
 
 impl Node {
     pub fn teardown(&self) {
-        self.style_and_layout_data.get().map(|d| d.dispose(self));
+        self.style_and_layout_data.get().map(|d| self.dispose(d));
         for kid in self.children() {
             kid.teardown();
         }
     }
 
     /// Dumps the subtree rooted at this node, for debugging.
     pub fn dump(&self) {
         self.dump_indent(0);
@@ -969,16 +957,17 @@ pub trait LayoutNodeHelpers {
     unsafe fn get_style_and_layout_data(&self) -> Option<OpaqueStyleAndLayoutData>;
     unsafe fn init_style_and_layout_data(&self, OpaqueStyleAndLayoutData);
 
     fn text_content(&self) -> String;
     fn selection(&self) -> Option<Range<usize>>;
     fn image_url(&self) -> Option<Url>;
     fn canvas_data(&self) -> Option<HTMLCanvasData>;
     fn iframe_pipeline_id(&self) -> PipelineId;
+    fn opaque(&self) -> OpaqueNode;
 }
 
 impl LayoutNodeHelpers for LayoutJS<Node> {
     #[inline]
     #[allow(unsafe_code)]
     unsafe fn type_id_for_layout(&self) -> NodeTypeId {
         (*self.unsafe_get()).type_id()
     }
@@ -1109,16 +1098,23 @@ impl LayoutNodeHelpers for LayoutJS<Node
             .map(|canvas| canvas.data())
     }
 
     fn iframe_pipeline_id(&self) -> PipelineId {
         let iframe_element = self.downcast::<HTMLIFrameElement>()
             .expect("not an iframe element!");
         iframe_element.pipeline_id().unwrap()
     }
+
+    #[allow(unsafe_code)]
+    fn opaque(&self) -> OpaqueNode {
+        unsafe {
+            OpaqueNode(self.get_jsobject() as usize)
+        }
+    }
 }
 
 
 //
 // Iteration and traversal
 //
 
 pub type ChildElementIterator =
@@ -2408,27 +2404,16 @@ impl NodeMethods for Node {
     fn IsDefaultNamespace(&self, namespace: Option<DOMString>) -> bool {
         // Step 1.
         let namespace = namespace_from_domstring(namespace);
         // Steps 2 and 3.
         Node::locate_namespace(self, None) == namespace
     }
 }
 
-
-
-/// The address of a node known to be valid. These are sent from script to layout,
-/// and are also used in the HTML parser interface.
-
-#[derive(Clone, PartialEq, Eq, Copy)]
-pub struct TrustedNodeAddress(pub *const c_void);
-
-#[allow(unsafe_code)]
-unsafe impl Send for TrustedNodeAddress {}
-
 pub fn document_from_node<T: DerivedFrom<Node> + Reflectable>(derived: &T) -> Root<Document> {
     derived.upcast().owner_doc()
 }
 
 pub fn window_from_node<T: DerivedFrom<Node> + Reflectable>(derived: &T) -> Root<Window> {
     let document = document_from_node(derived);
     Root::from_ref(document.window())
 }
@@ -2643,8 +2628,61 @@ impl UniqueId {
             let ptr = self.cell.get();
             if (*ptr).is_none() {
                 *ptr = Some(box Uuid::new_v4());
             }
             &(&*ptr).as_ref().unwrap()
         }
     }
 }
+
+impl Into<LayoutNodeType> for NodeTypeId {
+    #[inline(always)]
+    fn into(self) -> LayoutNodeType {
+        match self {
+            NodeTypeId::CharacterData(CharacterDataTypeId::Comment) =>
+                LayoutNodeType::Comment,
+            NodeTypeId::Document(..) =>
+                LayoutNodeType::Document,
+            NodeTypeId::DocumentFragment =>
+                LayoutNodeType::DocumentFragment,
+            NodeTypeId::DocumentType =>
+                LayoutNodeType::DocumentType,
+            NodeTypeId::Element(e) =>
+                LayoutNodeType::Element(e.into()),
+            NodeTypeId::CharacterData(CharacterDataTypeId::ProcessingInstruction) =>
+                LayoutNodeType::ProcessingInstruction,
+            NodeTypeId::CharacterData(CharacterDataTypeId::Text) =>
+                LayoutNodeType::Text,
+        }
+    }
+}
+
+impl Into<LayoutElementType> for ElementTypeId {
+    #[inline(always)]
+    fn into(self) -> LayoutElementType {
+        match self {
+            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLCanvasElement) =>
+                LayoutElementType::HTMLCanvasElement,
+            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLIFrameElement) =>
+                LayoutElementType::HTMLIFrameElement,
+            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLImageElement) =>
+                LayoutElementType::HTMLImageElement,
+            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLInputElement) =>
+                LayoutElementType::HTMLInputElement,
+            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLObjectElement) =>
+                LayoutElementType::HTMLObjectElement,
+            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableCellElement(_)) =>
+                LayoutElementType::HTMLTableCellElement,
+            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableColElement) =>
+                LayoutElementType::HTMLTableColElement,
+            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableElement) =>
+                LayoutElementType::HTMLTableElement,
+            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableRowElement) =>
+                LayoutElementType::HTMLTableRowElement,
+            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableSectionElement) =>
+                LayoutElementType::HTMLTableSectionElement,
+            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTextAreaElement) =>
+                LayoutElementType::HTMLTextAreaElement,
+            _ => LayoutElementType::Element,
+        }
+    }
+}
--- a/servo/components/script/dom/window.rs
+++ b/servo/components/script/dom/window.rs
@@ -26,44 +26,46 @@ use dom::browsingcontext::BrowsingContex
 use dom::console::Console;
 use dom::crypto::Crypto;
 use dom::cssstyledeclaration::{CSSModificationAccess, CSSStyleDeclaration};
 use dom::document::Document;
 use dom::element::Element;
 use dom::eventtarget::EventTarget;
 use dom::location::Location;
 use dom::navigator::Navigator;
-use dom::node::{Node, TrustedNodeAddress, from_untrusted_node_address, window_from_node};
+use dom::node::{Node, from_untrusted_node_address, window_from_node};
 use dom::performance::Performance;
 use dom::screen::Screen;
 use dom::storage::Storage;
 use euclid::{Point2D, Rect, Size2D};
 use gfx_traits::LayerId;
 use ipc_channel::ipc::{self, IpcSender};
 use js::jsapi::{Evaluate2, HandleObject, HandleValue, JSAutoCompartment, JSContext};
 use js::jsapi::{JS_GetRuntime, JS_GC, MutableHandleValue, SetWindowProxy};
 use js::rust::CompileOptionsWrapper;
 use js::rust::Runtime;
-use layout_interface::{ContentBoxResponse, ContentBoxesResponse, ResolvedStyleResponse, ScriptReflow};
-use layout_interface::{LayoutRPC, Msg, Reflow, ReflowQueryType, MarginStyleResponse};
 use libc;
 use msg::constellation_msg::{FrameType, LoadData, PanicMsg, PipelineId, SubpageId};
 use msg::constellation_msg::{WindowSizeData, WindowSizeType};
 use msg::webdriver_msg::{WebDriverJSError, WebDriverJSResult};
 use net_traits::bluetooth_thread::BluetoothMethodMsg;
 use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheThread};
 use net_traits::storage_thread::StorageType;
 use net_traits::{ResourceThreads, CustomResponseSender};
 use num_traits::ToPrimitive;
 use open;
 use profile_traits::mem;
 use profile_traits::time::{ProfilerCategory, TimerMetadata, TimerMetadataFrameType};
 use profile_traits::time::{ProfilerChan, TimerMetadataReflowType, profile};
-use reporter::CSSErrorReporter;
 use rustc_serialize::base64::{FromBase64, STANDARD, ToBase64};
+use script_layout_interface::TrustedNodeAddress;
+use script_layout_interface::message::{Msg, Reflow, ReflowQueryType, ScriptReflow};
+use script_layout_interface::reporter::CSSErrorReporter;
+use script_layout_interface::rpc::{ContentBoxResponse, ContentBoxesResponse, LayoutRPC};
+use script_layout_interface::rpc::{MarginStyleResponse, ResolvedStyleResponse};
 use script_runtime::{ScriptChan, ScriptPort};
 use script_thread::SendableMainThreadScriptChan;
 use script_thread::{MainThreadScriptChan, MainThreadScriptMsg, RunnableWrapper};
 use script_traits::{ConstellationControlMsg, UntrustedNodeAddress};
 use script_traits::{DocumentState, MsDuration, TimerEvent, TimerEventId};
 use script_traits::{ScriptMsg as ConstellationMsg, TimerEventRequest, TimerSource};
 use std::ascii::AsciiExt;
 use std::borrow::ToOwned;
deleted file mode 100644
--- a/servo/components/script/layout_interface.rs
+++ /dev/null
@@ -1,249 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//! The high-level interface from script to layout. Using this abstract
-//! interface helps reduce coupling between these two components, and enables
-//! the DOM to be placed in a separate crate from layout.
-
-use app_units::Au;
-use euclid::point::Point2D;
-use euclid::rect::Rect;
-use gfx_traits::{Epoch, LayerId};
-use ipc_channel::ipc::{IpcReceiver, IpcSender};
-use msg::constellation_msg::{PanicMsg, PipelineId, WindowSizeData};
-use net_traits::image_cache_thread::ImageCacheThread;
-use profile_traits::mem::ReportsChan;
-use script_traits::{ConstellationControlMsg, LayoutControlMsg, LayoutMsg as ConstellationMsg};
-use script_traits::{StackingContextScrollState, UntrustedNodeAddress};
-use std::sync::Arc;
-use std::sync::mpsc::{Receiver, Sender};
-use string_cache::Atom;
-use style::context::ReflowGoal;
-use style::properties::longhands::{margin_top, margin_right, margin_bottom, margin_left, overflow_x};
-use style::selector_impl::PseudoElement;
-use style::servo::Stylesheet;
-use url::Url;
-use util::ipc::OptionalOpaqueIpcSender;
-
-pub use dom::bindings::inheritance::{CharacterDataTypeId, ElementTypeId};
-pub use dom::bindings::inheritance::{HTMLElementTypeId, NodeTypeId};
-pub use dom::bindings::js::LayoutJS;
-pub use dom::characterdata::LayoutCharacterDataHelpers;
-pub use dom::document::{Document, LayoutDocumentHelpers};
-pub use dom::element::{Element, LayoutElementHelpers, RawLayoutElementHelpers};
-pub use dom::htmlcanvaselement::HTMLCanvasData;
-pub use dom::htmlobjectelement::is_image_data;
-pub use dom::node::{CAN_BE_FRAGMENTED, HAS_CHANGED, HAS_DIRTY_DESCENDANTS, IS_DIRTY};
-pub use dom::node::LayoutNodeHelpers;
-pub use dom::node::Node;
-pub use dom::node::OpaqueStyleAndLayoutData;
-pub use dom::node::TrustedNodeAddress;
-pub use dom::text::Text;
-
-
-/// Asynchronous messages that script can send to layout.
-pub enum Msg {
-    /// Adds the given stylesheet to the document.
-    AddStylesheet(Arc<Stylesheet>),
-
-    /// Puts a document into quirks mode, causing the quirks mode stylesheet to be loaded.
-    SetQuirksMode,
-
-    /// Requests a reflow.
-    Reflow(ScriptReflow),
-
-    /// Get an RPC interface.
-    GetRPC(Sender<Box<LayoutRPC + Send>>),
-
-    /// Requests that the layout thread render the next frame of all animations.
-    TickAnimations,
-
-    /// Requests that the layout thread reflow with a newly-loaded Web font.
-    ReflowWithNewlyLoadedWebFont,
-
-    /// Updates the layout visible rects, affecting the area that display lists will be constructed
-    /// for.
-    SetVisibleRects(Vec<(LayerId, Rect<Au>)>),
-
-    /// Destroys layout data associated with a DOM node.
-    ///
-    /// TODO(pcwalton): Maybe think about batching to avoid message traffic.
-    ReapStyleAndLayoutData(OpaqueStyleAndLayoutData),
-
-    /// Requests that the layout thread measure its memory usage. The resulting reports are sent back
-    /// via the supplied channel.
-    CollectReports(ReportsChan),
-
-    /// Requests that the layout thread enter a quiescent state in which no more messages are
-    /// accepted except `ExitMsg`. A response message will be sent on the supplied channel when
-    /// this happens.
-    PrepareToExit(Sender<()>),
-
-    /// Requests that the layout thread immediately shut down. There must be no more nodes left after
-    /// this, or layout will crash.
-    ExitNow,
-
-    /// Get the last epoch counter for this layout thread.
-    GetCurrentEpoch(IpcSender<Epoch>),
-
-    /// Asks the layout thread whether any Web fonts have yet to load (if true, loads are pending;
-    /// false otherwise).
-    GetWebFontLoadState(IpcSender<bool>),
-
-    /// Creates a new layout thread.
-    ///
-    /// This basically exists to keep the script-layout dependency one-way.
-    CreateLayoutThread(NewLayoutThreadInfo),
-
-    /// Set the final Url.
-    SetFinalUrl(Url),
-
-    /// Tells layout about the new scrolling offsets of each scrollable stacking context.
-    SetStackingContextScrollStates(Vec<StackingContextScrollState>),
-}
-
-/// Synchronous messages that script can send to layout.
-///
-/// In general, you should use messages to talk to Layout. Use the RPC interface
-/// if and only if the work is
-///
-///   1) read-only with respect to LayoutThreadData,
-///   2) small,
-///   3) and really needs to be fast.
-pub trait LayoutRPC {
-    /// Requests the dimensions of the content box, as in the `getBoundingClientRect()` call.
-    fn content_box(&self) -> ContentBoxResponse;
-    /// Requests the dimensions of all the content boxes, as in the `getClientRects()` call.
-    fn content_boxes(&self) -> ContentBoxesResponse;
-    /// Requests the geometry of this node. Used by APIs such as `clientTop`.
-    fn node_geometry(&self) -> NodeGeometryResponse;
-    /// Requests the overflow-x and overflow-y of this node. Used by `scrollTop` etc.
-    fn node_overflow(&self) -> NodeOverflowResponse;
-    /// Requests the scroll geometry of this node. Used by APIs such as `scrollTop`.
-    fn node_scroll_area(&self) -> NodeGeometryResponse;
-    /// Requests the layer id of this node. Used by APIs such as `scrollTop`
-    fn node_layer_id(&self) -> NodeLayerIdResponse;
-    /// Requests the node containing the point of interest
-    fn hit_test(&self) -> HitTestResponse;
-    /// Query layout for the resolved value of a given CSS property
-    fn resolved_style(&self) -> ResolvedStyleResponse;
-    fn offset_parent(&self) -> OffsetParentResponse;
-    /// Query layout for the resolve values of the margin properties for an element.
-    fn margin_style(&self) -> MarginStyleResponse;
-
-    fn nodes_from_point(&self, point: Point2D<f32>) -> Vec<UntrustedNodeAddress>;
-}
-
-#[derive(Clone)]
-pub struct MarginStyleResponse {
-    pub top: margin_top::computed_value::T,
-    pub right: margin_right::computed_value::T,
-    pub bottom: margin_bottom::computed_value::T,
-    pub left: margin_left::computed_value::T,
-}
-
-impl MarginStyleResponse {
-    pub fn empty() -> MarginStyleResponse {
-        MarginStyleResponse {
-            top: margin_top::computed_value::T::Auto,
-            right: margin_right::computed_value::T::Auto,
-            bottom: margin_bottom::computed_value::T::Auto,
-            left: margin_left::computed_value::T::Auto,
-        }
-    }
-}
-
-pub struct NodeOverflowResponse(pub Option<Point2D<overflow_x::computed_value::T>>);
-
-pub struct ContentBoxResponse(pub Rect<Au>);
-pub struct ContentBoxesResponse(pub Vec<Rect<Au>>);
-pub struct HitTestResponse {
-    pub node_address: Option<UntrustedNodeAddress>,
-}
-pub struct NodeGeometryResponse {
-    pub client_rect: Rect<i32>,
-}
-
-pub struct NodeLayerIdResponse {
-    pub layer_id: LayerId,
-}
-
-pub struct ResolvedStyleResponse(pub Option<String>);
-
-#[derive(Clone)]
-pub struct OffsetParentResponse {
-    pub node_address: Option<UntrustedNodeAddress>,
-    pub rect: Rect<Au>,
-}
-
-impl OffsetParentResponse {
-    pub fn empty() -> OffsetParentResponse {
-        OffsetParentResponse {
-            node_address: None,
-            rect: Rect::zero(),
-        }
-    }
-}
-
-/// Any query to perform with this reflow.
-#[derive(PartialEq)]
-pub enum ReflowQueryType {
-    NoQuery,
-    ContentBoxQuery(TrustedNodeAddress),
-    ContentBoxesQuery(TrustedNodeAddress),
-    NodeOverflowQuery(TrustedNodeAddress),
-    HitTestQuery(Point2D<f32>, bool),
-    NodeGeometryQuery(TrustedNodeAddress),
-    NodeLayerIdQuery(TrustedNodeAddress),
-    NodeScrollGeometryQuery(TrustedNodeAddress),
-    ResolvedStyleQuery(TrustedNodeAddress, Option<PseudoElement>, Atom),
-    OffsetParentQuery(TrustedNodeAddress),
-    MarginStyleQuery(TrustedNodeAddress),
-}
-
-/// Information needed for a reflow.
-pub struct Reflow {
-    /// The goal of reflow: either to render to the screen or to flush layout info for script.
-    pub goal: ReflowGoal,
-    ///  A clipping rectangle for the page, an enlarged rectangle containing the viewport.
-    pub page_clip_rect: Rect<Au>,
-}
-
-/// Information needed for a script-initiated reflow.
-pub struct ScriptReflow {
-    /// General reflow data.
-    pub reflow_info: Reflow,
-    /// The document node.
-    pub document: TrustedNodeAddress,
-    /// The document's list of stylesheets.
-    pub document_stylesheets: Vec<Arc<Stylesheet>>,
-    /// Whether the document's stylesheets have changed since the last script reflow.
-    pub stylesheets_changed: bool,
-    /// The current window size.
-    pub window_size: WindowSizeData,
-    /// The channel that we send a notification to.
-    pub script_join_chan: Sender<()>,
-    /// The type of query if any to perform during this reflow.
-    pub query_type: ReflowQueryType,
-}
-
-impl Drop for ScriptReflow {
-    fn drop(&mut self) {
-        self.script_join_chan.send(()).unwrap();
-    }
-}
-
-pub struct NewLayoutThreadInfo {
-    pub id: PipelineId,
-    pub url: Url,
-    pub is_parent: bool,
-    pub layout_pair: (Sender<Msg>, Receiver<Msg>),
-    pub pipeline_port: IpcReceiver<LayoutControlMsg>,
-    pub constellation_chan: IpcSender<ConstellationMsg>,
-    pub panic_chan: IpcSender<PanicMsg>,
-    pub script_chan: IpcSender<ConstellationControlMsg>,
-    pub image_cache_thread: ImageCacheThread,
-    pub paint_chan: OptionalOpaqueIpcSender,
-    pub content_process_shutdown_chan: IpcSender<()>,
-}
new file mode 100644
--- /dev/null
+++ b/servo/components/script/layout_wrapper.rs
@@ -0,0 +1,999 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! A safe wrapper for DOM nodes that prevents layout from mutating the DOM, from letting DOM nodes
+//! escape, and from generally doing anything that it isn't supposed to. This is accomplished via
+//! a simple whitelist of allowed operations, along with some lifetime magic to prevent nodes from
+//! escaping.
+//!
+//! As a security wrapper is only as good as its whitelist, be careful when adding operations to
+//! this list. The cardinal rules are:
+//!
+//! 1. Layout is not allowed to mutate the DOM.
+//!
+//! 2. Layout is not allowed to see anything with `LayoutJS` in the name, because it could hang
+//!    onto these objects and cause use-after-free.
+//!
+//! When implementing wrapper functions, be careful that you do not touch the borrow flags, or you
+//! will race and cause spurious thread failure. (Note that I do not believe these races are
+//! exploitable, but they'll result in brokenness nonetheless.)
+//!
+//! Rules of the road for this file:
+//!
+//! * Do not call any methods on DOM nodes without checking to see whether they use borrow flags.
+//!
+//!   o Instead of `get_attr()`, use `.get_attr_val_for_layout()`.
+//!
+//!   o Instead of `html_element_in_html_document()`, use
+//!     `html_element_in_html_document_for_layout()`.
+
+#![allow(unsafe_code)]
+
+use dom::bindings::inheritance::{CharacterDataTypeId, ElementTypeId};
+use dom::bindings::inheritance::{HTMLElementTypeId, NodeTypeId};
+use dom::bindings::js::LayoutJS;
+use dom::characterdata::LayoutCharacterDataHelpers;
+use dom::document::{Document, LayoutDocumentHelpers};
+use dom::element::{Element, LayoutElementHelpers, RawLayoutElementHelpers};
+use dom::node::{CAN_BE_FRAGMENTED, HAS_CHANGED, HAS_DIRTY_DESCENDANTS, IS_DIRTY};
+use dom::node::{Node, LayoutNodeHelpers};
+use dom::text::Text;
+use gfx_traits::ByteIndex;
+use msg::constellation_msg::PipelineId;
+use range::Range;
+use script_layout_interface::restyle_damage::RestyleDamage;
+use script_layout_interface::wrapper_traits::{DangerousThreadSafeLayoutNode, LayoutNode, PseudoElementType};
+use script_layout_interface::wrapper_traits::{ThreadSafeLayoutNode, ThreadSafeLayoutElement};
+use script_layout_interface::{HTMLCanvasData, LayoutNodeType, TrustedNodeAddress};
+use script_layout_interface::{OpaqueStyleAndLayoutData, PartialStyleAndLayoutData};
+use selectors::matching::{DeclarationBlock, ElementFlags};
+use selectors::parser::{AttrSelector, NamespaceConstraint};
+use smallvec::VecLike;
+use std::cell::{Ref, RefCell, RefMut};
+use std::marker::PhantomData;
+use std::mem::{transmute, transmute_copy};
+use string_cache::{Atom, BorrowedAtom, BorrowedNamespace, Namespace};
+use style::attr::AttrValue;
+use style::computed_values::display;
+use style::dom::{PresentationalHintsSynthetizer, OpaqueNode, TDocument, TElement, TNode, UnsafeNode};
+use style::element_state::*;
+use style::properties::{ComputedValues, ServoComputedValues};
+use style::properties::{PropertyDeclaration, PropertyDeclarationBlock};
+use style::restyle_hints::ElementSnapshot;
+use style::selector_impl::{NonTSPseudoClass, ServoSelectorImpl};
+use style::servo::{PrivateStyleData, SharedStyleContext};
+use url::Url;
+use util::str::is_whitespace;
+
+#[derive(Copy, Clone)]
+pub struct ServoLayoutNode<'a> {
+    /// The wrapped node.
+    node: LayoutJS<Node>,
+
+    /// Being chained to a PhantomData prevents `LayoutNode`s from escaping.
+    chain: PhantomData<&'a ()>,
+}
+
+impl<'a> PartialEq for ServoLayoutNode<'a> {
+    #[inline]
+    fn eq(&self, other: &ServoLayoutNode) -> bool {
+        self.node == other.node
+    }
+}
+
+impl<'ln> ServoLayoutNode<'ln> {
+    fn from_layout_js(n: LayoutJS<Node>) -> ServoLayoutNode<'ln> {
+        ServoLayoutNode {
+            node: n,
+            chain: PhantomData,
+        }
+    }
+
+    pub unsafe fn new(address: &TrustedNodeAddress) -> ServoLayoutNode {
+        ServoLayoutNode::from_layout_js(LayoutJS::from_trusted_node_address(*address))
+    }
+
+    /// Creates a new layout node with the same lifetime as this layout node.
+    pub unsafe fn new_with_this_lifetime(&self, node: &LayoutJS<Node>) -> ServoLayoutNode<'ln> {
+        ServoLayoutNode {
+            node: *node,
+            chain: self.chain,
+        }
+    }
+
+    fn script_type_id(&self) -> NodeTypeId {
+        unsafe {
+            self.node.type_id_for_layout()
+        }
+    }
+}
+
+impl<'ln> TNode for ServoLayoutNode<'ln> {
+    type ConcreteComputedValues = ServoComputedValues;
+    type ConcreteElement = ServoLayoutElement<'ln>;
+    type ConcreteDocument = ServoLayoutDocument<'ln>;
+    type ConcreteRestyleDamage = RestyleDamage;
+
+    fn to_unsafe(&self) -> UnsafeNode {
+        unsafe {
+            let ptr: usize = transmute_copy(self);
+            (ptr, 0)
+        }
+    }
+
+    unsafe fn from_unsafe(n: &UnsafeNode) -> Self {
+        let (node, _) = *n;
+        transmute(node)
+    }
+
+    fn is_text_node(&self) -> bool {
+        self.script_type_id() == NodeTypeId::CharacterData(CharacterDataTypeId::Text)
+    }
+
+    fn is_element(&self) -> bool {
+        unsafe {
+            self.node.is_element_for_layout()
+        }
+    }
+
+    fn dump(self) {
+        self.dump_indent(0);
+    }
+
+    fn opaque(&self) -> OpaqueNode {
+        unsafe { self.get_jsmanaged().opaque() }
+    }
+
+    fn layout_parent_node(self, reflow_root: OpaqueNode) -> Option<ServoLayoutNode<'ln>> {
+        if self.opaque() == reflow_root {
+            None
+        } else {
+            self.parent_node()
+        }
+    }
+
+    fn debug_id(self) -> usize {
+        self.opaque().0
+    }
+
+    fn children_count(&self) -> u32 {
+        unsafe { self.node.children_count() }
+    }
+
+    fn as_element(&self) -> Option<ServoLayoutElement<'ln>> {
+        as_element(self.node)
+    }
+
+    fn as_document(&self) -> Option<ServoLayoutDocument<'ln>> {
+        self.node.downcast().map(ServoLayoutDocument::from_layout_js)
+    }
+
+    fn has_changed(&self) -> bool {
+        unsafe { self.node.get_flag(HAS_CHANGED) }
+    }
+
+    unsafe fn set_changed(&self, value: bool) {
+        self.node.set_flag(HAS_CHANGED, value)
+    }
+
+    fn is_dirty(&self) -> bool {
+        unsafe { self.node.get_flag(IS_DIRTY) }
+    }
+
+    unsafe fn set_dirty(&self, value: bool) {
+        self.node.set_flag(IS_DIRTY, value)
+    }
+
+    fn has_dirty_descendants(&self) -> bool {
+        unsafe { self.node.get_flag(HAS_DIRTY_DESCENDANTS) }
+    }
+
+    unsafe fn set_dirty_descendants(&self, value: bool) {
+        self.node.set_flag(HAS_DIRTY_DESCENDANTS, value)
+    }
+
+    fn can_be_fragmented(&self) -> bool {
+        unsafe { self.node.get_flag(CAN_BE_FRAGMENTED) }
+    }
+
+    unsafe fn set_can_be_fragmented(&self, value: bool) {
+        self.node.set_flag(CAN_BE_FRAGMENTED, value)
+    }
+
+    unsafe fn borrow_data_unchecked(&self) -> Option<*const PrivateStyleData> {
+        self.get_style_data().map(|d| {
+            &(*d.as_unsafe_cell().get()).style_data as *const _
+        })
+    }
+
+    fn borrow_data(&self) -> Option<Ref<PrivateStyleData>> {
+        self.get_style_data().map(|d| {
+            Ref::map(d.borrow(), |d| &d.style_data)
+        })
+    }
+
+    fn mutate_data(&self) -> Option<RefMut<PrivateStyleData>> {
+        self.get_style_data().map(|d| {
+            RefMut::map(d.borrow_mut(), |d| &mut d.style_data)
+        })
+    }
+
+    fn restyle_damage(self) -> RestyleDamage {
+        self.get_style_data().unwrap().borrow().restyle_damage
+    }
+
+    fn set_restyle_damage(self, damage: RestyleDamage) {
+        self.get_style_data().unwrap().borrow_mut().restyle_damage = damage;
+    }
+
+    fn parent_node(&self) -> Option<ServoLayoutNode<'ln>> {
+        unsafe {
+            self.node.parent_node_ref().map(|node| self.new_with_this_lifetime(&node))
+        }
+    }
+
+    fn first_child(&self) -> Option<ServoLayoutNode<'ln>> {
+        unsafe {
+            self.node.first_child_ref().map(|node| self.new_with_this_lifetime(&node))
+        }
+    }
+
+    fn last_child(&self) -> Option<ServoLayoutNode<'ln>> {
+        unsafe {
+            self.node.last_child_ref().map(|node| self.new_with_this_lifetime(&node))
+        }
+    }
+
+    fn prev_sibling(&self) -> Option<ServoLayoutNode<'ln>> {
+        unsafe {
+            self.node.prev_sibling_ref().map(|node| self.new_with_this_lifetime(&node))
+        }
+    }
+
+    fn next_sibling(&self) -> Option<ServoLayoutNode<'ln>> {
+        unsafe {
+            self.node.next_sibling_ref().map(|node| self.new_with_this_lifetime(&node))
+        }
+    }
+}
+
+impl<'ln> LayoutNode for ServoLayoutNode<'ln> {
+    type ConcreteThreadSafeLayoutNode = ServoThreadSafeLayoutNode<'ln>;
+
+    fn to_threadsafe(&self) -> Self::ConcreteThreadSafeLayoutNode {
+        ServoThreadSafeLayoutNode::new(self)
+    }
+
+    fn type_id(&self) -> LayoutNodeType {
+        self.script_type_id().into()
+    }
+
+    fn get_style_data(&self) -> Option<&RefCell<PartialStyleAndLayoutData>> {
+        unsafe {
+            self.get_jsmanaged().get_style_and_layout_data().map(|d| {
+                &**d.ptr
+            })
+        }
+    }
+
+    fn init_style_and_layout_data(&self, data: OpaqueStyleAndLayoutData) {
+        unsafe {
+            self.get_jsmanaged().init_style_and_layout_data(data);
+        }
+    }
+
+    fn get_style_and_layout_data(&self) -> Option<OpaqueStyleAndLayoutData> {
+        unsafe {
+            self.get_jsmanaged().get_style_and_layout_data()
+        }
+    }
+}
+
+impl<'ln> ServoLayoutNode<'ln> {
+    fn dump_indent(self, indent: u32) {
+        let mut s = String::new();
+        for _ in 0..indent {
+            s.push_str("  ");
+        }
+
+        s.push_str(&self.debug_str());
+        println!("{}", s);
+
+        for kid in self.children() {
+            kid.dump_indent(indent + 1);
+        }
+    }
+
+    fn debug_str(self) -> String {
+        format!("{:?}: changed={} dirty={} dirty_descendants={}",
+                self.script_type_id(), self.has_changed(), self.is_dirty(), self.has_dirty_descendants())
+    }
+
+    /// Returns the interior of this node as a `LayoutJS`. This is highly unsafe for layout to
+    /// call and as such is marked `unsafe`.
+    unsafe fn get_jsmanaged(&self) -> &LayoutJS<Node> {
+        &self.node
+    }
+}
+
+// A wrapper around documents that ensures ayout can only ever access safe properties.
+#[derive(Copy, Clone)]
+pub struct ServoLayoutDocument<'ld> {
+    document: LayoutJS<Document>,
+    chain: PhantomData<&'ld ()>,
+}
+
+impl<'ld> TDocument for ServoLayoutDocument<'ld> {
+    type ConcreteNode = ServoLayoutNode<'ld>;
+    type ConcreteElement = ServoLayoutElement<'ld>;
+
+    fn as_node(&self) -> ServoLayoutNode<'ld> {
+        ServoLayoutNode::from_layout_js(self.document.upcast())
+    }
+
+    fn root_node(&self) -> Option<ServoLayoutNode<'ld>> {
+        self.as_node().children().find(ServoLayoutNode::is_element)
+    }
+
+    fn drain_modified_elements(&self) -> Vec<(ServoLayoutElement<'ld>, ElementSnapshot)> {
+        let elements =  unsafe { self.document.drain_modified_elements() };
+        elements.into_iter().map(|(el, snapshot)| (ServoLayoutElement::from_layout_js(el), snapshot)).collect()
+    }
+}
+
+impl<'ld> ServoLayoutDocument<'ld> {
+    fn from_layout_js(doc: LayoutJS<Document>) -> ServoLayoutDocument<'ld> {
+        ServoLayoutDocument {
+            document: doc,
+            chain: PhantomData,
+        }
+    }
+}
+
+/// A wrapper around elements that ensures layout can only ever access safe properties.
+#[derive(Copy, Clone)]
+pub struct ServoLayoutElement<'le> {
+    element: LayoutJS<Element>,
+    chain: PhantomData<&'le ()>,
+}
+
+impl<'le> PresentationalHintsSynthetizer for ServoLayoutElement<'le> {
+    fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, hints: &mut V)
+        where V: VecLike<DeclarationBlock<Vec<PropertyDeclaration>>>
+    {
+        unsafe {
+            self.element.synthesize_presentational_hints_for_legacy_attributes(hints);
+        }
+    }
+}
+
+impl<'le> TElement for ServoLayoutElement<'le> {
+    type ConcreteNode = ServoLayoutNode<'le>;
+    type ConcreteDocument = ServoLayoutDocument<'le>;
+
+    fn as_node(&self) -> ServoLayoutNode<'le> {
+        ServoLayoutNode::from_layout_js(self.element.upcast())
+    }
+
+    fn style_attribute(&self) -> &Option<PropertyDeclarationBlock> {
+        unsafe {
+            &*self.element.style_attribute()
+        }
+    }
+
+    fn get_state(&self) -> ElementState {
+        self.element.get_state_for_layout()
+    }
+
+    #[inline]
+    fn get_attr(&self, namespace: &Namespace, name: &Atom) -> Option<&str> {
+        unsafe {
+            (*self.element.unsafe_get()).get_attr_val_for_layout(namespace, name)
+        }
+    }
+
+    #[inline]
+    fn get_attrs(&self, name: &Atom) -> Vec<&str> {
+        unsafe {
+            (*self.element.unsafe_get()).get_attr_vals_for_layout(name)
+        }
+    }
+}
+
+
+impl<'le> ServoLayoutElement<'le> {
+    fn from_layout_js(el: LayoutJS<Element>) -> ServoLayoutElement<'le> {
+        ServoLayoutElement {
+            element: el,
+            chain: PhantomData,
+        }
+    }
+}
+
+fn as_element<'le>(node: LayoutJS<Node>) -> Option<ServoLayoutElement<'le>> {
+    node.downcast().map(ServoLayoutElement::from_layout_js)
+}
+
+impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
+    type Impl = ServoSelectorImpl;
+
+    fn parent_element(&self) -> Option<ServoLayoutElement<'le>> {
+        unsafe {
+            self.element.upcast().parent_node_ref().and_then(as_element)
+        }
+    }
+
+    fn first_child_element(&self) -> Option<ServoLayoutElement<'le>> {
+        self.as_node().children().filter_map(|n| n.as_element()).next()
+    }
+
+    fn last_child_element(&self) -> Option<ServoLayoutElement<'le>> {
+        self.as_node().rev_children().filter_map(|n| n.as_element()).next()
+    }
+
+    fn prev_sibling_element(&self) -> Option<ServoLayoutElement<'le>> {
+        let mut node = self.as_node();
+        while let Some(sibling) = node.prev_sibling() {
+            if let Some(element) = sibling.as_element() {
+                return Some(element)
+            }
+            node = sibling;
+        }
+        None
+    }
+
+    fn next_sibling_element(&self) -> Option<ServoLayoutElement<'le>> {
+        let mut node = self.as_node();
+        while let Some(sibling) = node.next_sibling() {
+            if let Some(element) = sibling.as_element() {
+                return Some(element)
+            }
+            node = sibling;
+        }
+        None
+    }
+
+    fn is_root(&self) -> bool {
+        match self.as_node().parent_node() {
+            None => false,
+            Some(node) => {
+                match node.script_type_id() {
+                    NodeTypeId::Document(_) => true,
+                    _ => false
+                }
+            },
+        }
+    }
+
+    fn is_empty(&self) -> bool {
+        self.as_node().children().all(|node| match node.script_type_id() {
+            NodeTypeId::Element(..) => false,
+            NodeTypeId::CharacterData(CharacterDataTypeId::Text) => unsafe {
+                node.node.downcast().unwrap().data_for_layout().is_empty()
+            },
+            _ => true
+        })
+    }
+
+    #[inline]
+    fn get_local_name<'a>(&'a self) -> BorrowedAtom<'a> {
+        BorrowedAtom(self.element.local_name())
+    }
+
+    #[inline]
+    fn get_namespace<'a>(&'a self) -> BorrowedNamespace<'a> {
+        BorrowedNamespace(self.element.namespace())
+    }
+
+    fn match_non_ts_pseudo_class(&self, pseudo_class: NonTSPseudoClass) -> bool {
+        match pseudo_class {
+            // https://github.com/servo/servo/issues/8718
+            NonTSPseudoClass::Link |
+            NonTSPseudoClass::AnyLink => unsafe {
+                match self.as_node().script_type_id() {
+                    // https://html.spec.whatwg.org/multipage/#selector-link
+                    NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAnchorElement)) |
+                    NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAreaElement)) |
+                    NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLLinkElement)) =>
+                        (*self.element.unsafe_get()).get_attr_val_for_layout(&ns!(), &atom!("href")).is_some(),
+                    _ => false,
+                }
+            },
+            NonTSPseudoClass::Visited => false,
+
+            NonTSPseudoClass::ServoNonZeroBorder => unsafe {
+                match (*self.element.unsafe_get()).get_attr_for_layout(&ns!(), &atom!("border")) {
+                    None | Some(&AttrValue::UInt(_, 0)) => false,
+                    _ => true,
+                }
+            },
+
+            NonTSPseudoClass::ReadOnly =>
+                !self.element.get_state_for_layout().contains(pseudo_class.state_flag()),
+
+            NonTSPseudoClass::Active |
+            NonTSPseudoClass::Focus |
+            NonTSPseudoClass::Hover |
+            NonTSPseudoClass::Enabled |
+            NonTSPseudoClass::Disabled |
+            NonTSPseudoClass::Checked |
+            NonTSPseudoClass::Indeterminate |
+            NonTSPseudoClass::ReadWrite |
+            NonTSPseudoClass::PlaceholderShown =>
+                self.element.get_state_for_layout().contains(pseudo_class.state_flag())
+        }
+    }
+
+    #[inline]
+    fn get_id(&self) -> Option<Atom> {
+        unsafe {
+            (*self.element.id_attribute()).clone()
+        }
+    }
+
+    #[inline]
+    fn has_class(&self, name: &Atom) -> bool {
+        unsafe {
+            self.element.has_class_for_layout(name)
+        }
+    }
+
+    #[inline(always)]
+    fn each_class<F>(&self, mut callback: F) where F: FnMut(&Atom) {
+        unsafe {
+            if let Some(ref classes) = self.element.get_classes_for_layout() {
+                for class in *classes {
+                    callback(class)
+                }
+            }
+        }
+    }
+
+    fn match_attr<F>(&self, attr: &AttrSelector, test: F) -> bool where F: Fn(&str) -> bool {
+        let name = if self.is_html_element_in_html_document() {
+            &attr.lower_name
+        } else {
+            &attr.name
+        };
+        match attr.namespace {
+            NamespaceConstraint::Specific(ref ns) => {
+                self.get_attr(ns, name).map_or(false, |attr| test(attr))
+            },
+            NamespaceConstraint::Any => {
+                self.get_attrs(name).iter().any(|attr| test(*attr))
+            }
+        }
+    }
+
+    fn is_html_element_in_html_document(&self) -> bool {
+        unsafe {
+            self.element.html_element_in_html_document_for_layout()
+        }
+    }
+
+    fn insert_flags(&self, flags: ElementFlags) {
+        self.element.insert_atomic_flags(flags);
+    }
+}
+
+#[derive(Copy, Clone)]
+pub struct ServoThreadSafeLayoutNode<'ln> {
+    /// The wrapped node.
+    node: ServoLayoutNode<'ln>,
+
+    /// The pseudo-element type, with (optionally),
+    /// an specified display value to override the stylesheet.
+    pseudo: PseudoElementType<Option<display::T>>,
+}
+
+impl<'a> PartialEq for ServoThreadSafeLayoutNode<'a> {
+    #[inline]
+    fn eq(&self, other: &ServoThreadSafeLayoutNode<'a>) -> bool {
+        self.node == other.node
+    }
+}
+
+impl<'ln> DangerousThreadSafeLayoutNode for ServoThreadSafeLayoutNode<'ln> {
+    unsafe fn dangerous_first_child(&self) -> Option<Self> {
+            self.get_jsmanaged().first_child_ref()
+                .map(|node| self.new_with_this_lifetime(&node))
+    }
+    unsafe fn dangerous_next_sibling(&self) -> Option<Self> {
+            self.get_jsmanaged().next_sibling_ref()
+                .map(|node| self.new_with_this_lifetime(&node))
+    }
+}
+
+impl<'ln> ServoThreadSafeLayoutNode<'ln> {
+    /// Creates a new layout node with the same lifetime as this layout node.
+    pub unsafe fn new_with_this_lifetime(&self, node: &LayoutJS<Node>) -> ServoThreadSafeLayoutNode<'ln> {
+        ServoThreadSafeLayoutNode {
+            node: self.node.new_with_this_lifetime(node),
+            pseudo: PseudoElementType::Normal,
+        }
+    }
+
+    /// Creates a new `ServoThreadSafeLayoutNode` from the given `ServoLayoutNode`.
+    pub fn new<'a>(node: &ServoLayoutNode<'a>) -> ServoThreadSafeLayoutNode<'a> {
+        ServoThreadSafeLayoutNode {
+            node: node.clone(),
+            pseudo: PseudoElementType::Normal,
+        }
+    }
+
+    /// Returns the interior of this node as a `LayoutJS`. This is highly unsafe for layout to
+    /// call and as such is marked `unsafe`.
+    unsafe fn get_jsmanaged(&self) -> &LayoutJS<Node> {
+        self.node.get_jsmanaged()
+    }
+}
+
+impl<'ln> ThreadSafeLayoutNode for ServoThreadSafeLayoutNode<'ln> {
+    type ConcreteThreadSafeLayoutElement = ServoThreadSafeLayoutElement<'ln>;
+    type ChildrenIterator = ThreadSafeLayoutNodeChildrenIterator<Self>;
+
+    fn with_pseudo(&self,
+                   pseudo: PseudoElementType<Option<display::T>>) -> ServoThreadSafeLayoutNode<'ln> {
+        ServoThreadSafeLayoutNode {
+            node: self.node.clone(),
+            pseudo: pseudo,
+        }
+    }
+
+    fn opaque(&self) -> OpaqueNode {
+        unsafe { self.get_jsmanaged().opaque() }
+    }
+
+    fn type_id(&self) -> Option<LayoutNodeType> {
+        if self.pseudo != PseudoElementType::Normal {
+            return None
+        }
+
+        Some(self.node.type_id())
+    }
+
+    #[inline]
+    fn type_id_without_excluding_pseudo_elements(&self) -> LayoutNodeType {
+        self.node.type_id()
+    }
+
+    fn debug_id(self) -> usize {
+        self.node.debug_id()
+    }
+
+    fn children(&self) -> Self::ChildrenIterator {
+        ThreadSafeLayoutNodeChildrenIterator::new(*self)
+    }
+
+    fn as_element(&self) -> ServoThreadSafeLayoutElement<'ln> {
+        unsafe {
+            let element = match self.get_jsmanaged().downcast() {
+                Some(e) => e.unsafe_get(),
+                None => panic!("not an element")
+            };
+            // FIXME(pcwalton): Workaround until Rust gets multiple lifetime parameters on
+            // implementations.
+            ServoThreadSafeLayoutElement {
+                element: &*element,
+            }
+        }
+    }
+
+    fn get_pseudo_element_type(&self) -> PseudoElementType<Option<display::T>> {
+        self.pseudo
+    }
+
+    fn get_style_and_layout_data(&self) -> Option<OpaqueStyleAndLayoutData> {
+        self.node.get_style_and_layout_data()
+    }
+
+    fn is_ignorable_whitespace(&self, context: &SharedStyleContext) -> bool {
+        unsafe {
+            let text: LayoutJS<Text> = match self.get_jsmanaged().downcast() {
+                Some(text) => text,
+                None => return false
+            };
+
+            if !is_whitespace(text.upcast().data_for_layout()) {
+                return false
+            }
+
+            // NB: See the rules for `white-space` here:
+            //
+            //    http://www.w3.org/TR/CSS21/text.html#propdef-white-space
+            //
+            // If you implement other values for this property, you will almost certainly
+            // want to update this check.
+            !self.style(context).get_inheritedtext().white_space.preserve_newlines()
+        }
+    }
+
+    fn restyle_damage(self) -> RestyleDamage {
+        self.node.restyle_damage()
+    }
+
+    fn set_restyle_damage(self, damage: RestyleDamage) {
+        self.node.set_restyle_damage(damage)
+    }
+
+    fn can_be_fragmented(&self) -> bool {
+        self.node.can_be_fragmented()
+    }
+
+    fn node_text_content(&self) -> String {
+        let this = unsafe { self.get_jsmanaged() };
+        return this.text_content();
+    }
+
+    fn selection(&self) -> Option<Range<ByteIndex>> {
+        let this = unsafe { self.get_jsmanaged() };
+
+        this.selection().map(|range| {
+            Range::new(ByteIndex(range.start as isize),
+                       ByteIndex(range.len() as isize))
+        })
+    }
+
+    fn image_url(&self) -> Option<Url> {
+        let this = unsafe { self.get_jsmanaged() };
+        this.image_url()
+    }
+
+    fn canvas_data(&self) -> Option<HTMLCanvasData> {
+        let this = unsafe { self.get_jsmanaged() };
+        this.canvas_data()
+    }
+
+    fn iframe_pipeline_id(&self) -> PipelineId {
+        let this = unsafe { self.get_jsmanaged() };
+        this.iframe_pipeline_id()
+    }
+
+    fn get_colspan(&self) -> u32 {
+        unsafe {
+            self.get_jsmanaged().downcast::<Element>().unwrap().get_colspan()
+        }
+    }
+
+    fn get_style_data(&self) -> Option<&RefCell<PartialStyleAndLayoutData>> {
+        self.node.get_style_data()
+    }
+}
+
+pub struct ThreadSafeLayoutNodeChildrenIterator<ConcreteNode: ThreadSafeLayoutNode> {
+    current_node: Option<ConcreteNode>,
+    parent_node: ConcreteNode,
+}
+
+impl<ConcreteNode> ThreadSafeLayoutNodeChildrenIterator<ConcreteNode>
+                   where ConcreteNode: DangerousThreadSafeLayoutNode {
+    pub fn new(parent: ConcreteNode) -> Self {
+        let first_child: Option<ConcreteNode> = match parent.get_pseudo_element_type() {
+            PseudoElementType::Normal => {
+                parent.get_before_pseudo().or_else(|| parent.get_details_summary_pseudo()).or_else(|| {
+                    unsafe { parent.dangerous_first_child() }
+                })
+            },
+            PseudoElementType::DetailsContent(_) | PseudoElementType::DetailsSummary(_) => {
+                unsafe { parent.dangerous_first_child() }
+            },
+            _ => None,
+        };
+        ThreadSafeLayoutNodeChildrenIterator {
+            current_node: first_child,
+            parent_node: parent,
+        }
+    }
+}
+
+impl<ConcreteNode> Iterator for ThreadSafeLayoutNodeChildrenIterator<ConcreteNode>
+                            where ConcreteNode: DangerousThreadSafeLayoutNode {
+    type Item = ConcreteNode;
+    fn next(&mut self) -> Option<ConcreteNode> {
+        match self.parent_node.get_pseudo_element_type() {
+            PseudoElementType::Before(_) | PseudoElementType::After(_) => None,
+
+            PseudoElementType::DetailsSummary(_) => {
+                let mut current_node = self.current_node.clone();
+                loop {
+                    let next_node = if let Some(ref node) = current_node {
+                        if node.is_element() &&
+                           node.as_element().get_local_name() == atom!("summary") &&
+                           node.as_element().get_namespace() == ns!(html) {
+                            self.current_node = None;
+                            return Some(node.clone());
+                        }
+                        unsafe { node.dangerous_next_sibling() }
+                    } else {
+                        self.current_node = None;
+                        return None
+                    };
+                    current_node = next_node;
+                }
+            }
+
+            PseudoElementType::DetailsContent(_) => {
+                let node = self.current_node.clone();
+                let node = node.and_then(|node| {
+                    if node.is_element() &&
+                       node.as_element().get_local_name() == atom!("summary") &&
+                       node.as_element().get_namespace() == ns!(html) {
+                        unsafe { node.dangerous_next_sibling() }
+                    } else {
+                        Some(node)
+                    }
+                });
+                self.current_node = node.and_then(|node| unsafe { node.dangerous_next_sibling() });
+                node
+            }
+
+            PseudoElementType::Normal => {
+                let node = self.current_node.clone();
+                if let Some(ref node) = node {
+                    self.current_node = match node.get_pseudo_element_type() {
+                        PseudoElementType::Before(_) => {
+                            let first = self.parent_node.get_details_summary_pseudo().or_else(|| unsafe {
+                                self.parent_node.dangerous_first_child()
+                            });
+                            match first {
+                                Some(first) => Some(first),
+                                None => self.parent_node.get_after_pseudo(),
+                            }
+                        },
+                        PseudoElementType::Normal => {
+                            match unsafe { node.dangerous_next_sibling() } {
+                                Some(next) => Some(next),
+                                None => self.parent_node.get_after_pseudo(),
+                            }
+                        },
+                        PseudoElementType::DetailsSummary(_) => self.parent_node.get_details_content_pseudo(),
+                        PseudoElementType::DetailsContent(_) => self.parent_node.get_after_pseudo(),
+                        PseudoElementType::After(_) => {
+                            None
+                        },
+                    };
+                }
+                node
+            }
+
+        }
+    }
+}
+
+/// A wrapper around elements that ensures layout can only
+/// ever access safe properties and cannot race on elements.
+#[derive(Copy, Clone)]
+pub struct ServoThreadSafeLayoutElement<'le> {
+    element: &'le Element,
+}
+
+impl<'le> ThreadSafeLayoutElement for ServoThreadSafeLayoutElement<'le> {
+    type ConcreteThreadSafeLayoutNode = ServoThreadSafeLayoutNode<'le>;
+
+    fn get_attr<'a>(&'a self, namespace: &Namespace, name: &Atom) -> Option<&'a str> {
+        unsafe {
+            self.element.get_attr_val_for_layout(namespace, name)
+        }
+    }
+
+    #[inline]
+    fn get_local_name<'a>(&'a self) -> BorrowedAtom<'a> {
+        BorrowedAtom(self.element.local_name())
+    }
+
+    #[inline]
+    fn get_namespace<'a>(&'a self) -> BorrowedNamespace<'a> {
+        BorrowedNamespace(self.element.namespace())
+    }
+}
+
+/// This implementation of `::selectors::Element` is used for implementing lazy
+/// pseudo-elements.
+///
+/// Lazy pseudo-elements in Servo only allows selectors using safe properties,
+/// i.e., local_name, attributes, so they can only be used for **private**
+/// pseudo-elements (like `::-servo-details-content`).
+///
+/// Probably a few more of this functions can be implemented (like `has_class`,
+/// `each_class`, etc), but they have no use right now.
+///
+/// Note that the element implementation is needed only for selector matching,
+/// not for inheritance (styles are inherited appropiately).
+impl <'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> {
+    type Impl = ServoSelectorImpl;
+
+    fn parent_element(&self) -> Option<Self> {
+        warn!("ServoThreadSafeLayoutElement::parent_element called");
+        None
+    }
+
+    fn first_child_element(&self) -> Option<Self> {
+        warn!("ServoThreadSafeLayoutElement::first_child_element called");
+        None
+    }
+
+    // Skips non-element nodes
+    fn last_child_element(&self) -> Option<Self> {
+        warn!("ServoThreadSafeLayoutElement::last_child_element called");
+        None
+    }
+
+    // Skips non-element nodes
+    fn prev_sibling_element(&self) -> Option<Self> {
+        warn!("ServoThreadSafeLayoutElement::prev_sibling_element called");
+        None
+    }
+
+    // Skips non-element nodes
+    fn next_sibling_element(&self) -> Option<Self> {
+        warn!("ServoThreadSafeLayoutElement::next_sibling_element called");
+        None
+    }
+
+    fn is_html_element_in_html_document(&self) -> bool {
+        debug!("ServoThreadSafeLayoutElement::is_html_element_in_html_document called");
+        true
+    }
+
+    #[inline]
+    fn get_local_name<'a>(&'a self) -> BorrowedAtom<'a> {
+        ThreadSafeLayoutElement::get_local_name(self)
+    }
+
+    #[inline]
+    fn get_namespace<'a>(&'a self) -> BorrowedNamespace<'a> {
+        ThreadSafeLayoutElement::get_namespace(self)
+    }
+
+    fn match_non_ts_pseudo_class(&self, _: NonTSPseudoClass) -> bool {
+        // NB: This could maybe be implemented
+        warn!("ServoThreadSafeLayoutElement::match_non_ts_pseudo_class called");
+        false
+    }
+
+    fn get_id(&self) -> Option<Atom> {
+        debug!("ServoThreadSafeLayoutElement::get_id called");
+        None
+    }
+
+    fn has_class(&self, _name: &Atom) -> bool {
+        debug!("ServoThreadSafeLayoutElement::has_class called");
+        false
+    }
+
+    fn match_attr<F>(&self, attr: &AttrSelector, test: F) -> bool
+        where F: Fn(&str) -> bool {
+        match attr.namespace {
+            NamespaceConstraint::Specific(ref ns) => {
+                self.get_attr(ns, &attr.name).map_or(false, |attr| test(attr))
+            },
+            NamespaceConstraint::Any => {
+                unsafe {
+                    self.element.get_attr_vals_for_layout(&attr.name).iter()
+                        .any(|attr| test(*attr))
+                }
+            }
+        }
+    }
+
+    fn is_empty(&self) -> bool {
+        warn!("ServoThreadSafeLayoutElement::is_empty called");
+        false
+    }
+
+    fn is_root(&self) -> bool {
+        warn!("ServoThreadSafeLayoutElement::is_root called");
+        false
+    }
+
+    fn each_class<F>(&self, _callback: F)
+        where F: FnMut(&Atom) {
+        warn!("ServoThreadSafeLayoutElement::each_class called");
+    }
+}
+
+impl<'le> PresentationalHintsSynthetizer for ServoThreadSafeLayoutElement<'le> {
+    fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, _hints: &mut V)
+        where V: VecLike<DeclarationBlock<Vec<PropertyDeclaration>>> {}
+}
--- a/servo/components/script/lib.rs
+++ b/servo/components/script/lib.rs
@@ -60,20 +60,22 @@ extern crate msg;
 extern crate net_traits;
 extern crate num_traits;
 extern crate offscreen_gl_context;
 extern crate open;
 extern crate phf;
 #[macro_use]
 extern crate profile_traits;
 extern crate rand;
+extern crate range;
 extern crate ref_filter_map;
 extern crate ref_slice;
 extern crate regex;
 extern crate rustc_serialize;
+extern crate script_layout_interface;
 extern crate script_traits;
 extern crate selectors;
 extern crate serde;
 extern crate smallvec;
 #[macro_use(atom, ns)] extern crate string_cache;
 #[macro_use]
 extern crate style;
 extern crate time;
@@ -88,22 +90,21 @@ extern crate websocket;
 extern crate xml5ever;
 
 pub mod bluetooth_blacklist;
 pub mod clipboard_provider;
 mod devtools;
 pub mod document_loader;
 #[macro_use]
 pub mod dom;
-pub mod layout_interface;
+pub mod layout_wrapper;
 mod mem;
 mod network_listener;
 pub mod origin;
 pub mod parse;
-pub mod reporter;
 pub mod script_runtime;
 #[allow(unsafe_code)]
 pub mod script_thread;
 mod task_source;
 pub mod textinput;
 mod timers;
 mod unpremultiplytable;
 mod webdriver_handlers;
--- a/servo/components/script/script_thread.rs
+++ b/servo/components/script/script_thread.rs
@@ -56,32 +56,32 @@ use hyper::mime::{Mime, SubLevel, TopLev
 use ipc_channel::ipc::{self, IpcSender};
 use ipc_channel::router::ROUTER;
 use js::glue::GetWindowProxyClass;
 use js::jsapi::{DOMProxyShadowsResult, HandleId, HandleObject, RootedValue};
 use js::jsapi::{JSAutoCompartment, JSContext, JS_SetWrapObjectCallbacks};
 use js::jsapi::{JSTracer, SetWindowProxyClass};
 use js::jsval::UndefinedValue;
 use js::rust::Runtime;
-use layout_interface::{self, NewLayoutThreadInfo, ReflowQueryType};
 use mem::heap_size_of_self_and_children;
 use msg::constellation_msg::{FrameType, LoadData, PanicMsg, PipelineId, PipelineNamespace};
 use msg::constellation_msg::{SubpageId, WindowSizeData, WindowSizeType};
 use msg::webdriver_msg::WebDriverScriptCommand;
 use net_traits::LoadData as NetLoadData;
 use net_traits::bluetooth_thread::BluetoothMethodMsg;
 use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheResult, ImageCacheThread};
 use net_traits::{AsyncResponseTarget, CoreResourceMsg, LoadConsumer, LoadContext, Metadata, ResourceThreads};
 use net_traits::{RequestSource, CustomResponse, CustomResponseSender, IpcSend};
 use network_listener::NetworkListener;
 use parse::ParserRoot;
 use parse::html::{ParseContext, parse_html};
 use parse::xml::{self, parse_xml};
 use profile_traits::mem::{self, OpaqueSender, Report, ReportKind, ReportsChan};
 use profile_traits::time::{self, ProfilerCategory, profile};
+use script_layout_interface::message::{self, NewLayoutThreadInfo, ReflowQueryType};
 use script_runtime::{CommonScriptMsg, ScriptChan, ScriptThreadEventCategory};
 use script_runtime::{ScriptPort, StackRootTLS, new_rt_and_cx, get_reports};
 use script_traits::CompositorEvent::{KeyEvent, MouseButtonEvent, MouseMoveEvent, ResizeEvent};
 use script_traits::CompositorEvent::{TouchEvent, TouchpadPressureEvent};
 use script_traits::{CompositorEvent, ConstellationControlMsg, EventResult};
 use script_traits::{InitialScriptState, MouseButton, MouseEventType, MozBrowserEvent};
 use script_traits::{NewLayoutInfo, ScriptMsg as ConstellationMsg};
 use script_traits::{ScriptThreadFactory, TimerEvent, TimerEventRequest, TimerSource};
@@ -130,32 +130,32 @@ pub unsafe fn trace_thread(tr: *mut JSTr
 struct InProgressLoad {
     /// The pipeline which requested this load.
     pipeline_id: PipelineId,
     /// The parent pipeline and child subpage associated with this load, if any.
     parent_info: Option<(PipelineId, SubpageId, FrameType)>,
     /// The current window size associated with this pipeline.
     window_size: Option<WindowSizeData>,
     /// Channel to the layout thread associated with this pipeline.
-    layout_chan: Sender<layout_interface::Msg>,
+    layout_chan: Sender<message::Msg>,
     /// The current viewport clipping rectangle applying to this pipeline, if any.
     clip_rect: Option<Rect<f32>>,
     /// Window is frozen (navigated away while loading for example).
     is_frozen: bool,
     /// Window is visible.
     is_visible: bool,
     /// The requested URL of the load.
     url: Url,
 }
 
 impl InProgressLoad {
     /// Create a new InProgressLoad object.
     fn new(id: PipelineId,
            parent_info: Option<(PipelineId, SubpageId, FrameType)>,
-           layout_chan: Sender<layout_interface::Msg>,
+           layout_chan: Sender<message::Msg>,
            window_size: Option<WindowSizeData>,
            url: Url) -> InProgressLoad {
         InProgressLoad {
             pipeline_id: id,
             parent_info: parent_info,
             layout_chan: layout_chan,
             window_size: window_size,
             clip_rect: None,
@@ -433,21 +433,21 @@ impl<'a> Drop for ScriptMemoryFailsafe<'
                 }
             }
             None => (),
         }
     }
 }
 
 impl ScriptThreadFactory for ScriptThread {
-    type Message = layout_interface::Msg;
+    type Message = message::Msg;
 
     fn create(state: InitialScriptState,
               load_data: LoadData)
-              -> (Sender<layout_interface::Msg>, Receiver<layout_interface::Msg>) {
+              -> (Sender<message::Msg>, Receiver<message::Msg>) {
         let panic_chan = state.panic_chan.clone();
         let (script_chan, script_port) = channel();
 
         let (sender, receiver) = channel();
         let layout_chan = sender.clone();
         let pipeline_id = state.id;
         thread::spawn_named_with_send_on_panic(format!("ScriptThread {:?}", state.id),
                                                thread_state::SCRIPT,
@@ -1179,17 +1179,17 @@ impl ScriptThread {
         let context = self.root_browsing_context();
         let parent_context = context.find(containing_pipeline_id).expect("ScriptThread: received a layout
             whose parent has a PipelineId which does not correspond to a pipeline in the script
             thread's browsing context tree. This is a bug.");
         let parent_window = parent_context.active_window();
 
         // Tell layout to actually spawn the thread.
         parent_window.layout_chan()
-                     .send(layout_interface::Msg::CreateLayoutThread(layout_creation_info))
+                     .send(message::Msg::CreateLayoutThread(layout_creation_info))
                      .unwrap();
 
         // Kick off the fetch for the new resource.
         let new_load = InProgressLoad::new(new_pipeline_id, Some((containing_pipeline_id, subpage_id, frame_type)),
                                            layout_chan, parent_window.window_size(),
                                            load_data.url.clone());
         self.start_page_load(new_load, load_data);
     }
@@ -1457,20 +1457,20 @@ impl ScriptThread {
 
         if let Some(idx) = idx {
             let load = self.incomplete_loads.borrow_mut().remove(idx);
 
             // Tell the layout thread to begin shutting down, and wait until it
             // processed this message.
             let (response_chan, response_port) = channel();
             let chan = &load.layout_chan;
-            if chan.send(layout_interface::Msg::PrepareToExit(response_chan)).is_ok() {
+            if chan.send(message::Msg::PrepareToExit(response_chan)).is_ok() {
                 debug!("shutting down layout for page {:?}", id);
                 response_port.recv().unwrap();
-                chan.send(layout_interface::Msg::ExitNow).ok();
+                chan.send(message::Msg::ExitNow).ok();
             }
 
             let has_pending_loads = self.incomplete_loads.borrow().len() > 0;
             let has_root_context = self.root_browsing_context_exists();
 
             // Exit if no pending loads and no root context
             return !has_pending_loads && !has_root_context;
         }
@@ -1518,17 +1518,17 @@ impl ScriptThread {
 
     /// The entry point to document loading. Defines bindings, sets up the window and document
     /// objects, parses HTML and CSS, and kicks off initial layout.
     fn load(&self, metadata: Metadata, incomplete: InProgressLoad) -> ParserRoot {
         let final_url = metadata.final_url.clone();
         {
             // send the final url to the layout thread.
             incomplete.layout_chan
-                      .send(layout_interface::Msg::SetFinalUrl(final_url.clone()))
+                      .send(message::Msg::SetFinalUrl(final_url.clone()))
                       .unwrap();
 
             // update the pipeline url
             self.constellation_chan
                 .send(ConstellationMsg::SetFinalUrl(incomplete.pipeline_id, final_url.clone()))
                 .unwrap();
         }
         debug!("ScriptThread: loading {} on pipeline {:?}", incomplete.url, incomplete.pipeline_id);
@@ -2121,34 +2121,34 @@ fn shut_down_layout(context_tree: &Brows
     let mut channels = vec!();
 
     for context in context_tree.iter() {
         // Tell the layout thread to begin shutting down, and wait until it
         // processed this message.
         let (response_chan, response_port) = channel();
         let window = context.active_window();
         let chan = window.layout_chan().clone();
-        if chan.send(layout_interface::Msg::PrepareToExit(response_chan)).is_ok() {
+        if chan.send(message::Msg::PrepareToExit(response_chan)).is_ok() {
             channels.push(chan);
             response_port.recv().unwrap();
         }
     }
 
     // Drop our references to the JSContext and DOM objects.
     for context in context_tree.iter() {
         let window = context.active_window();
         window.clear_js_runtime();
 
         // Sever the connection between the global and the DOM tree
         context.clear_session_history();
     }
 
     // Destroy the layout thread. If there were node leaks, layout will now crash safely.
     for chan in channels {
-        chan.send(layout_interface::Msg::ExitNow).ok();
+        chan.send(message::Msg::ExitNow).ok();
     }
 }
 
 pub fn get_browsing_context(context: &BrowsingContext,
                             pipeline_id: PipelineId)
                             -> Root<BrowsingContext> {
     context.find(pipeline_id).expect("ScriptThread: received an event \
             message for a layout channel that is not associated with this script thread.\
new file mode 100644
--- /dev/null
+++ b/servo/components/script_layout_interface/Cargo.toml
@@ -0,0 +1,33 @@
+[package]
+name = "script_layout_interface"
+version = "0.0.1"
+authors = ["The Servo Project Developers"]
+publish = false
+
+[lib]
+name = "script_layout_interface"
+path = "lib.rs"
+
+[dependencies]
+app_units = {version = "0.2.3", features = ["plugins"]}
+bitflags = "0.7"
+canvas_traits = {path = "../canvas_traits"}
+cssparser = {version = "0.5.4", features = ["heap_size", "serde-serialization"]}
+euclid = {version = "0.6.4", features = ["plugins"]}
+gfx_traits = {path = "../gfx_traits"}
+heapsize = "0.3.0"
+heapsize_plugin = "0.1.2"
+ipc-channel = {git = "https://github.com/servo/ipc-channel"}
+libc = "0.2"
+log = "0.3.5"
+msg = {path = "../msg"}
+net_traits = {path = "../net_traits"}
+profile_traits = {path = "../profile_traits"}
+plugins = {path = "../plugins"}
+range = {path = "../range"}
+script_traits = {path = "../script_traits"}
+selectors = {version = "0.6", features = ["heap_size"]}
+string_cache = {version = "0.2.20", features = ["heap_size"]}
+style = {path = "../style"}
+url = {version = "1.0.0", features = ["heap_size"]}
+util = {path = "../util"}
new file mode 100644
--- /dev/null
+++ b/servo/components/script_layout_interface/lib.rs
@@ -0,0 +1,116 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! This module contains traits in script used generically in the rest of Servo.
+//! The traits are here instead of in script so that these modules won't have
+//! to depend on script.
+
+#![deny(unsafe_code)]
+#![feature(box_syntax)]
+#![feature(custom_attribute)]
+#![feature(custom_derive)]
+#![feature(nonzero)]
+#![feature(plugin)]
+#![plugin(heapsize_plugin)]
+#![plugin(plugins)]
+
+extern crate app_units;
+#[allow(unused_extern_crates)]
+#[macro_use]
+extern crate bitflags;
+extern crate canvas_traits;
+extern crate core;
+extern crate cssparser;
+extern crate euclid;
+extern crate gfx_traits;
+extern crate heapsize;
+extern crate ipc_channel;
+extern crate libc;
+#[macro_use]
+extern crate log;
+extern crate msg;
+extern crate net_traits;
+extern crate profile_traits;
+extern crate range;
+extern crate script_traits;
+extern crate selectors;
+#[macro_use(atom, ns)]
+extern crate string_cache;
+extern crate style;
+extern crate url;
+extern crate util;
+
+pub mod message;
+pub mod reporter;
+pub mod restyle_damage;
+pub mod rpc;
+pub mod wrapper_traits;
+
+use canvas_traits::CanvasMsg;
+use core::nonzero::NonZero;
+use ipc_channel::ipc::IpcSender;
+use libc::c_void;
+use restyle_damage::RestyleDamage;
+use std::cell::RefCell;
+use style::servo::PrivateStyleData;
+
+pub struct PartialStyleAndLayoutData {
+    pub style_data: PrivateStyleData,
+    pub restyle_damage: RestyleDamage,
+}
+
+#[derive(Copy, Clone, HeapSizeOf)]
+pub struct OpaqueStyleAndLayoutData {
+    #[ignore_heap_size_of = "TODO(#6910) Box value that should be counted but \
+                             the type lives in layout"]
+    pub ptr: NonZero<*mut RefCell<PartialStyleAndLayoutData>>
+}
+
+#[allow(unsafe_code)]
+unsafe impl Send for OpaqueStyleAndLayoutData {}
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub enum LayoutNodeType {
+    Comment,
+    Document,
+    DocumentFragment,
+    DocumentType,
+    Element(LayoutElementType),
+    ProcessingInstruction,
+    Text,
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub enum LayoutElementType {
+    Element,
+    HTMLCanvasElement,
+    HTMLIFrameElement,
+    HTMLImageElement,
+    HTMLInputElement,
+    HTMLObjectElement,
+    HTMLTableCellElement,
+    HTMLTableColElement,
+    HTMLTableElement,
+    HTMLTableRowElement,
+    HTMLTableSectionElement,
+    HTMLTextAreaElement,
+}
+
+pub struct HTMLCanvasData {
+    pub ipc_renderer: Option<IpcSender<CanvasMsg>>,
+    pub width: u32,
+    pub height: u32,
+}
+
+/// The address of a node known to be valid. These are sent from script to layout.
+#[derive(Clone, PartialEq, Eq, Copy)]
+pub struct TrustedNodeAddress(pub *const c_void);
+
+#[allow(unsafe_code)]
+unsafe impl Send for TrustedNodeAddress {}
+
+pub fn is_image_data(uri: &str) -> bool {
+    static TYPES: &'static [&'static str] = &["data:image/png", "data:image/gif", "data:image/jpeg"];
+    TYPES.iter().any(|&type_| uri.starts_with(type_))
+}
new file mode 100644
--- /dev/null
+++ b/servo/components/script_layout_interface/message.rs
@@ -0,0 +1,148 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use app_units::Au;
+use euclid::point::Point2D;
+use euclid::rect::Rect;
+use gfx_traits::{Epoch, LayerId};
+use ipc_channel::ipc::{IpcReceiver, IpcSender};
+use msg::constellation_msg::{PanicMsg, PipelineId, WindowSizeData};
+use net_traits::image_cache_thread::ImageCacheThread;
+use profile_traits::mem::ReportsChan;
+use rpc::LayoutRPC;
+use script_traits::{ConstellationControlMsg, LayoutControlMsg};
+use script_traits::{LayoutMsg as ConstellationMsg, StackingContextScrollState};
+use std::sync::Arc;
+use std::sync::mpsc::{Receiver, Sender};
+use string_cache::Atom;
+use style::context::ReflowGoal;
+use style::selector_impl::PseudoElement;
+use style::servo::Stylesheet;
+use url::Url;
+use util::ipc::OptionalOpaqueIpcSender;
+use {OpaqueStyleAndLayoutData, TrustedNodeAddress};
+
+/// Asynchronous messages that script can send to layout.
+pub enum Msg {
+    /// Adds the given stylesheet to the document.
+    AddStylesheet(Arc<Stylesheet>),
+
+    /// Puts a document into quirks mode, causing the quirks mode stylesheet to be loaded.
+    SetQuirksMode,
+
+    /// Requests a reflow.
+    Reflow(ScriptReflow),
+
+    /// Get an RPC interface.
+    GetRPC(Sender<Box<LayoutRPC + Send>>),
+
+    /// Requests that the layout thread render the next frame of all animations.
+    TickAnimations,
+
+    /// Requests that the layout thread reflow with a newly-loaded Web font.
+    ReflowWithNewlyLoadedWebFont,
+
+    /// Updates the layout visible rects, affecting the area that display lists will be constructed
+    /// for.
+    SetVisibleRects(Vec<(LayerId, Rect<Au>)>),
+
+    /// Destroys layout data associated with a DOM node.
+    ///
+    /// TODO(pcwalton): Maybe think about batching to avoid message traffic.
+    ReapStyleAndLayoutData(OpaqueStyleAndLayoutData),
+
+    /// Requests that the layout thread measure its memory usage. The resulting reports are sent back
+    /// via the supplied channel.
+    CollectReports(ReportsChan),
+
+    /// Requests that the layout thread enter a quiescent state in which no more messages are
+    /// accepted except `ExitMsg`. A response message will be sent on the supplied channel when
+    /// this happens.
+    PrepareToExit(Sender<()>),
+
+    /// Requests that the layout thread immediately shut down. There must be no more nodes left after
+    /// this, or layout will crash.
+    ExitNow,
+
+    /// Get the last epoch counter for this layout thread.
+    GetCurrentEpoch(IpcSender<Epoch>),
+
+    /// Asks the layout thread whether any Web fonts have yet to load (if true, loads are pending;
+    /// false otherwise).
+    GetWebFontLoadState(IpcSender<bool>),
+
+    /// Creates a new layout thread.
+    ///
+    /// This basically exists to keep the script-layout dependency one-way.
+    CreateLayoutThread(NewLayoutThreadInfo),
+
+    /// Set the final Url.
+    SetFinalUrl(Url),
+
+    /// Tells layout about the new scrolling offsets of each scrollable stacking context.
+    SetStackingContextScrollStates(Vec<StackingContextScrollState>),
+}
+
+
+/// Any query to perform with this reflow.
+#[derive(PartialEq)]
+pub enum ReflowQueryType {
+    NoQuery,
+    ContentBoxQuery(TrustedNodeAddress),
+    ContentBoxesQuery(TrustedNodeAddress),
+    NodeOverflowQuery(TrustedNodeAddress),
+    HitTestQuery(Point2D<f32>, bool),
+    NodeGeometryQuery(TrustedNodeAddress),
+    NodeLayerIdQuery(TrustedNodeAddress),
+    NodeScrollGeometryQuery(TrustedNodeAddress),
+    ResolvedStyleQuery(TrustedNodeAddress, Option<PseudoElement>, Atom),
+    OffsetParentQuery(TrustedNodeAddress),
+    MarginStyleQuery(TrustedNodeAddress),
+}
+
+/// Information needed for a reflow.
+pub struct Reflow {
+    /// The goal of reflow: either to render to the screen or to flush layout info for script.
+    pub goal: ReflowGoal,
+    ///  A clipping rectangle for the page, an enlarged rectangle containing the viewport.
+    pub page_clip_rect: Rect<Au>,
+}
+
+/// Information needed for a script-initiated reflow.
+pub struct ScriptReflow {
+    /// General reflow data.
+    pub reflow_info: Reflow,
+    /// The document node.
+    pub document: TrustedNodeAddress,
+    /// The document's list of stylesheets.
+    pub document_stylesheets: Vec<Arc<Stylesheet>>,
+    /// Whether the document's stylesheets have changed since the last script reflow.
+    pub stylesheets_changed: bool,
+    /// The current window size.
+    pub window_size: WindowSizeData,
+    /// The channel that we send a notification to.
+    pub script_join_chan: Sender<()>,
+    /// The type of query if any to perform during this reflow.
+    pub query_type: ReflowQueryType,
+}
+
+impl Drop for ScriptReflow {
+    fn drop(&mut self) {
+        self.script_join_chan.send(()).unwrap();
+    }
+}
+
+pub struct NewLayoutThreadInfo {
+    pub id: PipelineId,
+    pub url: Url,
+    pub is_parent: bool,
+    pub layout_pair: (Sender<Msg>, Receiver<Msg>),
+    pub pipeline_port: IpcReceiver<LayoutControlMsg>,
+    pub constellation_chan: IpcSender<ConstellationMsg>,
+    pub panic_chan: IpcSender<PanicMsg>,
+    pub script_chan: IpcSender<ConstellationControlMsg>,
+    pub image_cache_thread: ImageCacheThread,
+    pub paint_chan: OptionalOpaqueIpcSender,
+    pub content_process_shutdown_chan: IpcSender<()>,
+}
rename from servo/components/script/reporter.rs
rename to servo/components/script_layout_interface/reporter.rs
--- a/servo/components/script/reporter.rs
+++ b/servo/components/script_layout_interface/reporter.rs
@@ -5,17 +5,17 @@
 use cssparser::{Parser, SourcePosition};
 use ipc_channel::ipc::IpcSender;
 use log;
 use msg::constellation_msg::PipelineId;
 use script_traits::ConstellationControlMsg;
 use std::sync::{Mutex, Arc};
 use style::error_reporting::ParseErrorReporter;
 
-#[derive(JSTraceable, HeapSizeOf)]
+#[derive(HeapSizeOf)]
 pub struct CSSErrorReporter {
     pub pipelineid: PipelineId,
     // Arc+Mutex combo is necessary to make this struct Sync,
     // which is necessary to fulfill the bounds required by the
     // uses of the ParseErrorReporter trait.
     #[ignore_heap_size_of = "Arc is defined in libstd"]
     pub script_chan: Arc<Mutex<IpcSender<ConstellationControlMsg>>>,
 }
copy from servo/components/layout/incremental.rs
copy to servo/components/script_layout_interface/restyle_damage.rs
--- a/servo/components/layout/incremental.rs
+++ b/servo/components/script_layout_interface/restyle_damage.rs
@@ -1,16 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-use flow::{self, AFFECTS_COUNTERS, Flow, HAS_COUNTER_AFFECTING_CHILDREN, IS_ABSOLUTELY_POSITIONED};
 use std::fmt;
 use std::sync::Arc;
-use style::computed_values::{display, float};
+use style::computed_values::display;
 use style::dom::TRestyleDamage;
 use style::properties::{ComputedValues, ServoComputedValues};
 
 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."]
@@ -39,39 +38,30 @@ bitflags! {
                  Propagates up the flow tree because the computation is inorder."]
         const RESOLVE_GENERATED_CONTENT = 0x20,
 
         #[doc = "The entire flow needs to be reconstructed."]
         const RECONSTRUCT_FLOW = 0x40
     }
 }
 
-bitflags! {
-    pub flags SpecialRestyleDamage: u8 {
-        #[doc = "If this flag is set, we need to reflow the entire document. This is more or less a \
-                 temporary hack to deal with cases that we don't handle incrementally yet."]
-        const REFLOW_ENTIRE_DOCUMENT = 0x01,
-    }
-}
-
 impl TRestyleDamage for RestyleDamage {
     type ConcreteComputedValues = ServoComputedValues;
     fn compute(old: Option<&Arc<ServoComputedValues>>, new: &ServoComputedValues) ->
         RestyleDamage { compute_damage(old, new) }
 
     /// 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
     }
 }
 
-
 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)
         } else {
             self & (REPAINT | STORE_OVERFLOW | REFLOW | REFLOW_OUT_OF_FLOW |
@@ -149,17 +139,17 @@ macro_rules! add_if_not_equal(
             $damage.insert($($effect)|*);
             true
         } else {
             false
         }
     })
 );
 
-pub fn compute_damage(old: Option<&Arc<ServoComputedValues>>, new: &ServoComputedValues) -> RestyleDamage {
+fn compute_damage(old: Option<&Arc<ServoComputedValues>>, new: &ServoComputedValues) -> RestyleDamage {
     let old: &ServoComputedValues = match old {
         None => return RestyleDamage::rebuild_and_reflow(),
         Some(cv) => &**cv,
     };
 
     let mut damage = RestyleDamage::empty();
 
     // This should check every CSS property, as enumerated in the fields of
@@ -261,71 +251,8 @@ pub fn compute_damage(old: Option<&Arc<S
     // 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() {
         damage.insert(RestyleDamage::rebuild_and_reflow());
     }
 
     damage
 }
-
-pub trait LayoutDamageComputation {
-    fn compute_layout_damage(self) -> SpecialRestyleDamage;
-    fn reflow_entire_document(self);
-}
-
-impl<'a> LayoutDamageComputation for &'a mut Flow {
-    fn compute_layout_damage(self) -> SpecialRestyleDamage {
-        let mut special_damage = SpecialRestyleDamage::empty();
-        let is_absolutely_positioned = flow::base(self).flags.contains(IS_ABSOLUTELY_POSITIONED);
-
-        // In addition to damage, we use this phase to compute whether nodes affect CSS counters.
-        let mut has_counter_affecting_children = false;
-
-        {
-            let self_base = flow::mut_base(self);
-            // Take a snapshot of the parent damage before updating it with damage from children.
-            let parent_damage = self_base.restyle_damage;
-
-            for kid in self_base.children.iter_mut() {
-                let child_is_absolutely_positioned =
-                    flow::base(kid).flags.contains(IS_ABSOLUTELY_POSITIONED);
-                flow::mut_base(kid).restyle_damage.insert(
-                    parent_damage.damage_for_child(is_absolutely_positioned,
-                                                   child_is_absolutely_positioned));
-                {
-                    let kid: &mut Flow = kid;
-                    special_damage.insert(kid.compute_layout_damage());
-                }
-                self_base.restyle_damage
-                         .insert(flow::base(kid).restyle_damage.damage_for_parent(
-                                 child_is_absolutely_positioned));
-
-                has_counter_affecting_children = has_counter_affecting_children ||
-                    flow::base(kid).flags.intersects(AFFECTS_COUNTERS |
-                                                     HAS_COUNTER_AFFECTING_CHILDREN);
-            }
-        }
-
-        let self_base = flow::mut_base(self);
-        if self_base.flags.float_kind() != float::T::none &&
-                self_base.restyle_damage.intersects(REFLOW) {
-            special_damage.insert(REFLOW_ENTIRE_DOCUMENT);
-        }
-
-        if has_counter_affecting_children {
-            self_base.flags.insert(HAS_COUNTER_AFFECTING_CHILDREN)
-        } else {
-            self_base.flags.remove(HAS_COUNTER_AFFECTING_CHILDREN)
-        }
-
-        special_damage
-    }
-
-    fn reflow_entire_document(self) {
-        let self_base = flow::mut_base(self);
-        self_base.restyle_damage.insert(RestyleDamage::rebuild_and_reflow());
-        self_base.restyle_damage.remove(RECONSTRUCT_FLOW);
-        for kid in self_base.children.iter_mut() {
-            kid.reflow_entire_document();
-        }
-    }
-}
new file mode 100644
--- /dev/null
+++ b/servo/components/script_layout_interface/rpc.rs
@@ -0,0 +1,96 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use app_units::Au;
+use euclid::point::Point2D;
+use euclid::rect::Rect;
+use gfx_traits::LayerId;
+use script_traits::UntrustedNodeAddress;
+use style::properties::longhands::{margin_top, margin_right, margin_bottom, margin_left, overflow_x};
+
+/// Synchronous messages that script can send to layout.
+///
+/// In general, you should use messages to talk to Layout. Use the RPC interface
+/// if and only if the work is
+///
+///   1) read-only with respect to LayoutThreadData,
+///   2) small,
+///   3) and really needs to be fast.
+pub trait LayoutRPC {
+    /// Requests the dimensions of the content box, as in the `getBoundingClientRect()` call.
+    fn content_box(&self) -> ContentBoxResponse;
+    /// Requests the dimensions of all the content boxes, as in the `getClientRects()` call.
+    fn content_boxes(&self) -> ContentBoxesResponse;
+    /// Requests the geometry of this node. Used by APIs such as `clientTop`.
+    fn node_geometry(&self) -> NodeGeometryResponse;
+    /// Requests the overflow-x and overflow-y of this node. Used by `scrollTop` etc.
+    fn node_overflow(&self) -> NodeOverflowResponse;
+    /// Requests the scroll geometry of this node. Used by APIs such as `scrollTop`.
+    fn node_scroll_area(&self) -> NodeGeometryResponse;
+    /// Requests the layer id of this node. Used by APIs such as `scrollTop`
+    fn node_layer_id(&self) -> NodeLayerIdResponse;
+    /// Requests the node containing the point of interest
+    fn hit_test(&self) -> HitTestResponse;
+    /// Query layout for the resolved value of a given CSS property
+    fn resolved_style(&self) -> ResolvedStyleResponse;
+    fn offset_parent(&self) -> OffsetParentResponse;
+    /// Query layout for the resolve values of the margin properties for an element.
+    fn margin_style(&self) -> MarginStyleResponse;
+
+    fn nodes_from_point(&self, point: Point2D<f32>) -> Vec<UntrustedNodeAddress>;
+}
+
+pub struct ContentBoxResponse(pub Rect<Au>);
+
+pub struct ContentBoxesResponse(pub Vec<Rect<Au>>);
+
+pub struct NodeGeometryResponse {
+    pub client_rect: Rect<i32>,
+}
+
+pub struct NodeOverflowResponse(pub Option<Point2D<overflow_x::computed_value::T>>);
+
+pub struct NodeLayerIdResponse {
+    pub layer_id: LayerId,
+}
+
+pub struct HitTestResponse {
+    pub node_address: Option<UntrustedNodeAddress>,
+}
+
+pub struct ResolvedStyleResponse(pub Option<String>);
+
+#[derive(Clone)]
+pub struct OffsetParentResponse {
+    pub node_address: Option<UntrustedNodeAddress>,
+    pub rect: Rect<Au>,
+}
+
+impl OffsetParentResponse {
+    pub fn empty() -> OffsetParentResponse {
+        OffsetParentResponse {
+            node_address: None,
+            rect: Rect::zero(),
+        }
+    }
+}
+
+#[derive(Clone)]
+pub struct MarginStyleResponse {
+    pub top: margin_top::computed_value::T,
+    pub right: margin_right::computed_value::T,
+    pub bottom: margin_bottom::computed_value::T,
+    pub left: margin_left::computed_value::T,
+}
+
+impl MarginStyleResponse {
+    pub fn empty() -> MarginStyleResponse {
+        MarginStyleResponse {
+            top: margin_top::computed_value::T::Auto,
+            right: margin_right::computed_value::T::Auto,
+            bottom: margin_bottom::computed_value::T::Auto,
+            left: margin_left::computed_value::T::Auto,
+        }
+    }
+}
new file mode 100644
--- /dev/null
+++ b/servo/components/script_layout_interface/wrapper_traits.rs
@@ -0,0 +1,366 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use HTMLCanvasData;
+use LayoutNodeType;
+use OpaqueStyleAndLayoutData;
+use PartialStyleAndLayoutData;
+use gfx_traits::{ByteIndex, LayerId, LayerType};
+use msg::constellation_msg::PipelineId;
+use range::Range;
+use restyle_damage::RestyleDamage;
+use std::cell::{Ref, RefCell};
+use std::sync::Arc;
+use string_cache::{Atom, BorrowedAtom, BorrowedNamespace, Namespace};
+use style::computed_values::display;
+use style::dom::OpaqueNode;
+use style::dom::{PresentationalHintsSynthetizer, TNode};
+use style::properties::ServoComputedValues;
+use style::selector_impl::{PseudoElement, PseudoElementCascadeType, ServoSelectorImpl};
+use style::servo::SharedStyleContext;
+use url::Url;
+
+#[derive(Copy, PartialEq, Clone)]
+pub enum PseudoElementType<T> {
+    Normal,
+    Before(T),
+    After(T),
+    DetailsSummary(T),
+    DetailsContent(T),
+}
+
+impl<T> PseudoElementType<T> {
+    pub fn is_before(&self) -> bool {
+        match *self {
+            PseudoElementType::Before(_) => true,
+            _ => false,
+        }
+    }
+
+    pub fn is_replaced_content(&self) -> bool {
+        match *self {
+            PseudoElementType::Before(_) | PseudoElementType::After(_) => true,
+            _ => false,
+        }
+    }
+
+    pub fn strip(&self) -> PseudoElementType<()> {
+        match *self {
+            PseudoElementType::Normal => PseudoElementType::Normal,
+            PseudoElementType::Before(_) => PseudoElementType::Before(()),
+            PseudoElementType::After(_) => PseudoElementType::After(()),
+            PseudoElementType::DetailsSummary(_) => PseudoElementType::DetailsSummary(()),
+            PseudoElementType::DetailsContent(_) => PseudoElementType::DetailsContent(()),
+        }
+    }
+
+    pub fn style_pseudo_element(&self) -> PseudoElement {
+        match *self {
+            PseudoElementType::Normal => unreachable!("style_pseudo_element called with PseudoElementType::Normal"),
+            PseudoElementType::Before(_) => PseudoElement::Before,
+            PseudoElementType::After(_) => PseudoElement::After,
+            PseudoElementType::DetailsSummary(_) => PseudoElement::DetailsSummary,
+            PseudoElementType::DetailsContent(_) => PseudoElement::DetailsContent,
+        }
+    }
+}
+
+/// A wrapper so that layout can access only the methods that it should have access to. Layout must
+/// only ever see these and must never see instances of `LayoutJS`.
+pub trait LayoutNode: TNode {
+    type ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode;
+    fn to_threadsafe(&self) -> Self::ConcreteThreadSafeLayoutNode;
+
+    /// Returns the type ID of this node.
+    fn type_id(&self) -> LayoutNodeType;
+
+    fn get_style_data(&self) -> Option<&RefCell<PartialStyleAndLayoutData>>;
+
+    fn init_style_and_layout_data(&self, data: OpaqueStyleAndLayoutData);
+    fn get_style_and_layout_data(&self) -> Option<OpaqueStyleAndLayoutData>;
+}
+
+/// A thread-safe version of `LayoutNode`, used during flow construction. This type of layout
+/// node does not allow any parents or siblings of nodes to be accessed, to avoid races.
+pub trait ThreadSafeLayoutNode: Clone + Copy + Sized + PartialEq {
+    type ConcreteThreadSafeLayoutElement:
+        ThreadSafeLayoutElement<ConcreteThreadSafeLayoutNode = Self>
+        + ::selectors::Element<Impl=ServoSelectorImpl>;
+    type ChildrenIterator: Iterator<Item = Self> + Sized;
+
+    /// Creates a new `ThreadSafeLayoutNode` for the same `LayoutNode`
+    /// with a different pseudo-element type.
+    fn with_pseudo(&self, pseudo: PseudoElementType<Option<display::T>>) -> Self;
+
+    /// Converts self into an `OpaqueNode`.
+    fn opaque(&self) -> OpaqueNode;
+
+    /// Returns the type ID of this node.
+    /// Returns `None` if this is a pseudo-element; otherwise, returns `Some`.
+    fn type_id(&self) -> Option<LayoutNodeType>;
+
+    /// Returns the type ID of this node, without discarding pseudo-elements as
+    /// `type_id` does.
+    fn type_id_without_excluding_pseudo_elements(&self) -> LayoutNodeType;
+
+    #[inline]
+    fn is_element_or_elements_pseudo(&self) -> bool {
+        match self.type_id_without_excluding_pseudo_elements() {
+            LayoutNodeType::Element(..) => true,
+            _ => false,
+        }
+    }
+
+    fn debug_id(self) -> usize;
+
+    /// Returns an iterator over this node's children.
+    fn children(&self) -> Self::ChildrenIterator;
+
+    #[inline]
+    fn is_element(&self) -> bool { if let Some(LayoutNodeType::Element(_)) = self.type_id() { true } else { false } }
+
+    /// If this is an element, accesses the element data. Fails if this is not an element node.
+    #[inline]
+    fn as_element(&self) -> Self::ConcreteThreadSafeLayoutElement;
+
+    #[inline]
+    fn get_pseudo_element_type(&self) -> PseudoElementType<Option<display::T>>;
+
+    #[inline]
+    fn get_before_pseudo(&self) -> Option<Self> {
+        if self.get_style_data()
+               .unwrap()
+               .borrow()
+               .style_data
+               .per_pseudo
+               .contains_key(&PseudoElement::Before) {
+            Some(self.with_pseudo(PseudoElementType::Before(None)))
+        } else {
+            None
+        }
+    }
+
+    #[inline]
+    fn get_after_pseudo(&self) -> Option<Self> {
+        if self.get_style_data()
+               .unwrap()
+               .borrow()
+               .style_data
+               .per_pseudo
+               .contains_key(&PseudoElement::After) {
+            Some(self.with_pseudo(PseudoElementType::After(None)))
+        } else {
+            None
+        }
+    }
+
+    #[inline]
+    fn get_details_summary_pseudo(&self) -> Option<Self> {
+        if self.is_element() &&
+           self.as_element().get_local_name() == atom!("details") &&
+           self.as_element().get_namespace() == ns!(html) {
+            Some(self.with_pseudo(PseudoElementType::DetailsSummary(None)))
+        } else {
+            None
+        }
+    }
+
+    #[inline]
+    fn get_details_content_pseudo(&self) -> Option<Self> {
+        if self.is_element() &&
+           self.as_element().get_local_name() == atom!("details") &&
+           self.as_element().get_namespace() == ns!(html) {
+            let display = if self.as_element().get_attr(&ns!(), &atom!("open")).is_some() {
+                None // Specified by the stylesheet
+            } else {
+                Some(display::T::none)
+            };
+            Some(self.with_pseudo(PseudoElementType::DetailsContent(display)))
+        } else {
+            None
+        }
+    }
+
+    fn get_style_and_layout_data(&self) -> Option<OpaqueStyleAndLayoutData>;
+
+    /// Returns the style results for the given node. If CSS selector matching
+    /// has not yet been performed, fails.
+    ///
+    /// Unlike the version on TNode, this handles pseudo-elements.
+    #[inline]
+    fn style(&self, context: &SharedStyleContext) -> Ref<Arc<ServoComputedValues>> {
+        match self.get_pseudo_element_type() {
+            PseudoElementType::Normal => {
+                Ref::map(self.get_style_data().unwrap().borrow(), |data| {
+                    data.style_data.style.as_ref().unwrap()
+                })
+            },
+            other => {
+                // Precompute non-eagerly-cascaded pseudo-element styles if not
+                // cached before.
+                let style_pseudo = other.style_pseudo_element();
+                match style_pseudo.cascade_type() {
+                    // Already computed during the cascade.
+                    PseudoElementCascadeType::Eager => {},
+                    PseudoElementCascadeType::Precomputed => {
+                        if !self.get_style_data()
+                                .unwrap()
+                                .borrow()
+                                .style_data
+                                .per_pseudo.contains_key(&style_pseudo) {
+                            let mut data = self.get_style_data().unwrap().borrow_mut();
+                            let new_style =
+                                context.stylist
+                                       .precomputed_values_for_pseudo(&style_pseudo,
+                                                                      data.style_data.style.as_ref());
+                            data.style_data.per_pseudo
+                                .insert(style_pseudo.clone(), new_style.unwrap());
+                        }
+                    }
+                    PseudoElementCascadeType::Lazy => {
+                        debug_assert!(self.is_element_or_elements_pseudo());
+                        if !self.get_style_data()
+                                .unwrap()
+                                .borrow()
+                                .style_data
+                                .per_pseudo.contains_key(&style_pseudo) {
+                            let mut data = self.get_style_data().unwrap().borrow_mut();
+                            let new_style =
+                                context.stylist
+                                       .lazily_compute_pseudo_element_style(
+                                           &self.as_element(),
+                                           &style_pseudo,
+                                           data.style_data.style.as_ref().unwrap());
+                            data.style_data.per_pseudo
+                                .insert(style_pseudo.clone(), new_style.unwrap());
+                        }
+                    }
+                }
+
+                Ref::map(self.get_style_data().unwrap().borrow(), |data| {
+                    data.style_data.per_pseudo.get(&style_pseudo).unwrap()
+                })
+            }
+        }
+    }
+
+    /// Returns the already resolved style of the node.
+    ///
+    /// This differs from `style(ctx)` in that if the pseudo-element has not yet
+    /// been computed it would panic.
+    ///
+    /// This should be used just for querying layout, or when we know the
+    /// element style is precomputed, not from general layout itself.
+    #[inline]
+    fn resolved_style(&self) -> Ref<Arc<ServoComputedValues>> {
+        Ref::map(self.get_style_data().unwrap().borrow(), |data| {
+            match self.get_pseudo_element_type() {
+                PseudoElementType::Normal
+                    => data.style_data.style.as_ref().unwrap(),
+                other
+                    => data.style_data.per_pseudo.get(&other.style_pseudo_element()).unwrap(),
+            }
+        })
+    }
+
+    #[inline]
+    fn selected_style(&self, _context: &SharedStyleContext) -> Ref<Arc<ServoComputedValues>> {
+        Ref::map(self.get_style_data().unwrap().borrow(), |data| {
+            data.style_data.per_pseudo
+                .get(&PseudoElement::Selection)
+                .unwrap_or(data.style_data.style.as_ref().unwrap())
+        })
+    }
+
+    /// Removes the style from this node.
+    ///
+    /// Unlike the version on TNode, this handles pseudo-elements.
+    fn unstyle(self) {
+        let mut data = self.get_style_data().unwrap().borrow_mut();
+
+        match self.get_pseudo_element_type() {
+            PseudoElementType::Normal => {
+                data.style_data.style = None;
+            }
+            other => {
+                data.style_data.per_pseudo.remove(&other.style_pseudo_element());
+            }
+        };
+    }
+
+    fn is_ignorable_whitespace(&self, context: &SharedStyleContext) -> bool;
+
+    fn restyle_damage(self) -> RestyleDamage;
+
+    fn set_restyle_damage(self, damage: RestyleDamage);
+
+    /// Returns true if this node contributes content. This is used in the implementation of
+    /// `empty_cells` per CSS 2.1 § 17.6.1.1.
+    fn is_content(&self) -> bool {
+        match self.type_id() {
+            Some(LayoutNodeType::Element(..)) | Some(LayoutNodeType::Text) => true,
+            _ => false
+        }
+    }
+
+    fn can_be_fragmented(&self) -> bool;
+
+    fn node_text_content(&self) -> String;
+
+    /// If the insertion point is within this node, returns it. Otherwise, returns `None`.
+    fn selection(&self) -> Option<Range<ByteIndex>>;
+
+    /// If this is an image element, returns its URL. If this is not an image element, fails.
+    ///
+    /// FIXME(pcwalton): Don't copy URLs.
+    fn image_url(&self) -> Option<Url>;
+
+    fn canvas_data(&self) -> Option<HTMLCanvasData>;
+
+    /// If this node is an iframe element, returns its pipeline ID. If this node is
+    /// not an iframe element, fails.
+    fn iframe_pipeline_id(&self) -> PipelineId;
+
+    fn get_colspan(&self) -> u32;
+
+    fn layer_id(&self) -> LayerId {
+        let layer_type = match self.get_pseudo_element_type() {
+            PseudoElementType::Normal => LayerType::FragmentBody,
+            PseudoElementType::Before(_) => LayerType::BeforePseudoContent,
+            PseudoElementType::After(_) => LayerType::AfterPseudoContent,
+            PseudoElementType::DetailsSummary(_) => LayerType::FragmentBody,
+            PseudoElementType::DetailsContent(_) => LayerType::FragmentBody,
+        };
+        LayerId::new_of_type(layer_type, self.opaque().id() as usize)
+    }
+
+    fn layer_id_for_overflow_scroll(&self) -> LayerId {
+        LayerId::new_of_type(LayerType::OverflowScroll, self.opaque().id() as usize)
+    }
+
+    fn get_style_data(&self) -> Option<&RefCell<PartialStyleAndLayoutData>>;
+}
+
+// This trait is only public so that it can be implemented by the gecko wrapper.
+// It can be used to violate thread-safety, so don't use it elsewhere in layout!
+#[allow(unsafe_code)]
+pub trait DangerousThreadSafeLayoutNode: ThreadSafeLayoutNode {
+    unsafe fn dangerous_first_child(&self) -> Option<Self>;
+    unsafe fn dangerous_next_sibling(&self) -> Option<Self>;
+}
+
+pub trait ThreadSafeLayoutElement: Clone + Copy + Sized +
+                                   ::selectors::Element<Impl=ServoSelectorImpl> +
+                                   PresentationalHintsSynthetizer {
+    type ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode<ConcreteThreadSafeLayoutElement = Self>;
+
+    #[inline]
+    fn get_attr(&self, namespace: &Namespace, name: &Atom) -> Option<&str>;
+
+    #[inline]
+    fn get_local_name<'a>(&'a self) -> BorrowedAtom<'a>;
+
+    #[inline]
+    fn get_namespace<'a>(&'a self) -> BorrowedNamespace<'a>;
+}
--- a/servo/components/servo/Cargo.lock
+++ b/servo/components/servo/Cargo.lock
@@ -28,16 +28,17 @@ dependencies = [
  "net_tests 0.0.1",
  "net_traits 0.0.1",
  "net_traits_tests 0.0.1",
  "plugin_compiletest 0.0.1",
  "profile 0.0.1",
  "profile_tests 0.0.1",
  "profile_traits 0.0.1",
  "script 0.0.1",
+ "script_layout_interface 0.0.1",
  "script_tests 0.0.1",
  "script_traits 0.0.1",
  "style 0.0.1",
  "style_tests 0.0.1",
  "url 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "util 0.0.1",
  "util_tests 0.0.1",
  "webdriver_server 0.0.1",
@@ -809,16 +810,18 @@ version = "0.0.1"
 dependencies = [
  "azure 0.4.5 (git+https://github.com/servo/rust-azure)",
  "euclid 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize_plugin 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "layers 0.2.5 (git+https://github.com/servo/rust-layers)",
  "msg 0.0.1",
  "plugins 0.0.1",
+ "range 0.0.1",
+ "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_macros 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "gif"
 version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1150,16 +1153,17 @@ dependencies = [
  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "msg 0.0.1",
  "net_traits 0.0.1",
  "plugins 0.0.1",
  "profile_traits 0.0.1",
  "range 0.0.1",
  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
  "script 0.0.1",
+ "script_layout_interface 0.0.1",
  "script_traits 0.0.1",
  "selectors 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_macros 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "string_cache 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)",
  "style 0.0.1",
  "style_traits 0.0.1",
@@ -1898,20 +1902,22 @@ dependencies = [
  "num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
  "offscreen_gl_context 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "open 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf 0.7.15 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf_macros 0.7.15 (registry+https://github.com/rust-lang/crates.io-index)",
  "plugins 0.0.1",
  "profile_traits 0.0.1",
  "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "range 0.0.1",
  "ref_filter_map 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "ref_slice 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex 0.1.71 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
+ "script_layout_interface 0.0.1",
  "script_traits 0.0.1",
  "selectors 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "string_cache 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)",
  "style 0.0.1",
  "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
  "tinyfiledialogs 0.1.0 (git+https://github.com/jdm/tinyfiledialogs)",
@@ -1919,16 +1925,44 @@ dependencies = [
  "util 0.0.1",
  "uuid 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "webrender_traits 0.1.0 (git+https://github.com/servo/webrender_traits)",
  "websocket 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "xml5ever 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
+name = "script_layout_interface"
+version = "0.0.1"
+dependencies = [
+ "app_units 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "canvas_traits 0.0.1",
+ "cssparser 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "euclid 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gfx_traits 0.0.1",
+ "heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "heapsize_plugin 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ipc-channel 0.2.3 (git+https://github.com/servo/ipc-channel)",
+ "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "msg 0.0.1",
+ "net_traits 0.0.1",
+ "plugins 0.0.1",
+ "profile_traits 0.0.1",
+ "range 0.0.1",
+ "script_traits 0.0.1",
+ "selectors 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "string_cache 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)",
+ "style 0.0.1",
+ "url 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "util 0.0.1",
+]
+
+[[package]]
 name = "script_tests"
 version = "0.0.1"
 dependencies = [
  "msg 0.0.1",
  "plugins 0.0.1",
  "script 0.0.1",
  "url 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
--- a/servo/components/servo/Cargo.toml
+++ b/servo/components/servo/Cargo.toml
@@ -38,16 +38,17 @@ net_traits_tests = {path = "../../tests/
 profile_tests = {path = "../../tests/unit/profile"}
 script_tests = {path = "../../tests/unit/script"}
 style_tests = {path = "../../tests/unit/style"}
 util_tests = {path = "../../tests/unit/util"}
 compiletest_helper = {path = "../../tests/compiletest/helper"}
 plugin_compiletest = {path = "../../tests/compiletest/plugin"}
 
 [dependencies]
+script_layout_interface = {path = "../script_layout_interface"}
 webrender_traits = {git = "https://github.com/servo/webrender_traits"}
 webrender = {git = "https://github.com/servo/webrender"}
 compositing = {path = "../compositing"}
 constellation = {path = "../constellation"}
 net = {path = "../net"}
 net_traits = {path = "../net_traits"}
 msg = {path = "../msg"}
 profile = {path = "../profile"}
--- a/servo/components/servo/lib.rs
+++ b/servo/components/servo/lib.rs
@@ -34,16 +34,17 @@ pub extern crate ipc_channel;
 pub extern crate layout;
 pub extern crate msg;
 pub extern crate net;
 pub extern crate net_traits;
 pub extern crate profile;
 pub extern crate profile_traits;
 pub extern crate script;
 pub extern crate script_traits;
+pub extern crate script_layout_interface;
 pub extern crate style;
 pub extern crate url;
 pub extern crate util;
 
 #[cfg(feature = "webdriver")]
 extern crate webdriver_server;
 
 extern crate webrender;
@@ -224,17 +225,17 @@ fn create_constellation(opts: opts::Opts
         font_cache_thread: font_cache_thread,
         resource_threads: resource_threads,
         time_profiler_chan: time_profiler_chan,
         mem_profiler_chan: mem_profiler_chan,
         supports_clipboard: supports_clipboard,
         webrender_api_sender: webrender_api_sender,
     };
     let constellation_chan =
-        Constellation::<script::layout_interface::Msg,
+        Constellation::<script_layout_interface::message::Msg,
                         layout::layout_thread::LayoutThread,
                         script::script_thread::ScriptThread>::start(initial_state);
 
     // Send the URL command to the constellation.
     match opts.url {
         Some(url) => {
             constellation_chan.send(ConstellationMsg::InitLoadUrl(url)).unwrap();
         },
@@ -258,17 +259,17 @@ pub fn run_content_process(token: String
 
     // Enter the sandbox if necessary.
     if opts::get().sandbox {
        create_sandbox();
     }
 
     script::init();
 
-    unprivileged_content.start_all::<script::layout_interface::Msg,
+    unprivileged_content.start_all::<script_layout_interface::message::Msg,
                                      layout::layout_thread::LayoutThread,
                                      script::script_thread::ScriptThread>(true);
 }
 
 // This is a workaround for https://github.com/rust-lang/rust/pull/30175 until
 // https://github.com/lfairy/rust-errno/pull/5 lands, and should be removed once
 // we update Servo with the rust-errno crate.
 #[cfg(target_os = "android")]
--- a/servo/ports/cef/Cargo.lock
+++ b/servo/ports/cef/Cargo.lock
@@ -722,16 +722,18 @@ version = "0.0.1"
 dependencies = [
  "azure 0.4.5 (git+https://github.com/servo/rust-azure)",
  "euclid 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize_plugin 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "layers 0.2.5 (git+https://github.com/servo/rust-layers)",
  "msg 0.0.1",
  "plugins 0.0.1",
+ "range 0.0.1",
+ "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_macros 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "gif"
 version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1063,16 +1065,17 @@ dependencies = [
  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "msg 0.0.1",
  "net_traits 0.0.1",
  "plugins 0.0.1",
  "profile_traits 0.0.1",
  "range 0.0.1",
  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
  "script 0.0.1",
+ "script_layout_interface 0.0.1",
  "script_traits 0.0.1",
  "selectors 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_macros 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "string_cache 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)",
  "style 0.0.1",
  "style_traits 0.0.1",
@@ -1756,20 +1759,22 @@ dependencies = [
  "num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
  "offscreen_gl_context 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "open 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf 0.7.15 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf_macros 0.7.15 (registry+https://github.com/rust-lang/crates.io-index)",
  "plugins 0.0.1",
  "profile_traits 0.0.1",
  "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "range 0.0.1",
  "ref_filter_map 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "ref_slice 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex 0.1.71 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
+ "script_layout_interface 0.0.1",
  "script_traits 0.0.1",
  "selectors 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "string_cache 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)",
  "style 0.0.1",
  "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
  "tinyfiledialogs 0.1.0 (git+https://github.com/jdm/tinyfiledialogs)",
@@ -1777,16 +1782,44 @@ dependencies = [
  "util 0.0.1",
  "uuid 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "webrender_traits 0.1.0 (git+https://github.com/servo/webrender_traits)",
  "websocket 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "xml5ever 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
+name = "script_layout_interface"
+version = "0.0.1"
+dependencies = [
+ "app_units 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "canvas_traits 0.0.1",
+ "cssparser 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "euclid 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gfx_traits 0.0.1",
+ "heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "heapsize_plugin 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ipc-channel 0.2.3 (git+https://github.com/servo/ipc-channel)",
+ "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "msg 0.0.1",
+ "net_traits 0.0.1",
+ "plugins 0.0.1",
+ "profile_traits 0.0.1",
+ "range 0.0.1",
+ "script_traits 0.0.1",
+ "selectors 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "string_cache 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)",
+ "style 0.0.1",
+ "url 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "util 0.0.1",
+]
+
+[[package]]
 name = "script_traits"
 version = "0.0.1"
 dependencies = [
  "app_units 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "canvas_traits 0.0.1",
  "devtools_traits 0.0.1",
  "euclid 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "gfx_traits 0.0.1",
@@ -1883,16 +1916,17 @@ dependencies = [
  "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "msg 0.0.1",
  "net 0.0.1",
  "net_traits 0.0.1",
  "profile 0.0.1",
  "profile_traits 0.0.1",
  "script 0.0.1",
+ "script_layout_interface 0.0.1",
  "script_traits 0.0.1",
  "style 0.0.1",
  "url 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "util 0.0.1",
  "webdriver_server 0.0.1",
  "webrender 0.1.0 (git+https://github.com/servo/webrender)",
  "webrender_traits 0.1.0 (git+https://github.com/servo/webrender_traits)",
 ]
--- a/servo/tests/unit/layout/size_of.rs
+++ b/servo/tests/unit/layout/size_of.rs
@@ -1,14 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use layout::Fragment;
-use layout::ServoThreadSafeLayoutNode;
 use std::mem::size_of;
 
 #[test]
 fn test_size_of_fragment() {
     let expected = 160;
     let actual = size_of::<Fragment>();
 
     if actual < expected {
@@ -20,28 +19,8 @@ fn test_size_of_fragment() {
     if actual > expected {
         panic!("Your changes have increased the stack size of layout::fragment::Fragment \
                 from {} to {}.  Please consider choosing a design which avoids this increase. \
                 If you feel that the increase is necessary, update the size in \
                 tests/unit/layout/size_of.rs.",
                 expected, actual);
     }
 }
-
-#[test]
-fn test_size_of_layout_node() {
-    let expected = 16;
-    let actual = size_of::<ServoThreadSafeLayoutNode>();
-
-    if actual < expected {
-        panic!("Your changes have decreased the stack size of layout::wrapper::ServoThreadSafeLayoutNode \
-                from {} to {}. Good work! Please update the size in tests/layout/unit/size_of.rs",
-                expected, actual);
-    }
-
-    if actual > expected {
-        panic!("Your changes have increased the stack size of layout::wrapper::ServoThreadSafeLayoutNode \
-                from {} to {}.  Please consider choosing a design which avoids this increase. \
-                If you feel that the increase is necessary, update the size in \
-                tests/unit/layout/size_of.rs.",
-                expected, actual);
-    }
-}
--- a/servo/tests/unit/script/size_of.rs
+++ b/servo/tests/unit/script/size_of.rs
@@ -5,16 +5,17 @@
 use script::dom::characterdata::CharacterData;
 use script::dom::element::Element;
 use script::dom::eventtarget::EventTarget;
 use script::dom::htmldivelement::HTMLDivElement;
 use script::dom::htmlelement::HTMLElement;
 use script::dom::htmlspanelement::HTMLSpanElement;
 use script::dom::node::Node;
 use script::dom::text::Text;
+use script::layout_wrapper::ServoThreadSafeLayoutNode;
 use std::mem::size_of;
 
 // Macro so that we can stringify type names
 // I'd really prefer the tests themselves to be run at plugin time,
 // however rustc::middle doesn't have access to the full type data
 macro_rules! sizeof_checker (
     ($testname: ident, $t:ty, $known_size:expr) => (
         #[test]
@@ -40,8 +41,9 @@ macro_rules! sizeof_checker (
 sizeof_checker!(size_event_target, EventTarget, 40);
 sizeof_checker!(size_node, Node, 160);
 sizeof_checker!(size_element, Element, 312);
 sizeof_checker!(size_htmlelement, HTMLElement, 328);
 sizeof_checker!(size_div, HTMLDivElement, 328);
 sizeof_checker!(size_span, HTMLSpanElement, 328);
 sizeof_checker!(size_text, Text, 192);
 sizeof_checker!(size_characterdata, CharacterData, 192);
+sizeof_checker!(size_servothreadsafelayoutnode, ServoThreadSafeLayoutNode, 16);