Bug 1344947 - Update webrender to e30fb2914928c0e596d8632ed234647c0fd1492e. r?jrmuizel draft
authorKartikaya Gupta <kgupta@mozilla.com>
Mon, 13 Mar 2017 10:25:44 -0400
changeset 497578 4a89416d544c736cefaafe33b7632c74e62a0c2a
parent 497577 df803a096e8dbf905459d3cec3a3ef4cf14b21c4
child 497579 6b6f866e73c7f84524b09da3d1b38c729057abda
push id48939
push userkgupta@mozilla.com
push dateMon, 13 Mar 2017 14:27:40 +0000
reviewersjrmuizel
bugs1344947
milestone55.0a1
Bug 1344947 - Update webrender to e30fb2914928c0e596d8632ed234647c0fd1492e. r?jrmuizel MozReview-Commit-ID: K3rIchdm3mr
gfx/doc/README.webrender
gfx/webrender/Cargo.toml
gfx/webrender/res/prim_shared.glsl
gfx/webrender/res/ps_gradient.vs.glsl
gfx/webrender/res/ps_image.fs.glsl
gfx/webrender/res/ps_yuv_image.vs.glsl
gfx/webrender/src/clip_scroll_node.rs
gfx/webrender/src/clip_scroll_tree.rs
gfx/webrender/src/device.rs
gfx/webrender/src/frame.rs
gfx/webrender/src/frame_builder.rs
gfx/webrender/src/gpu_store.rs
gfx/webrender/src/platform/macos/font.rs
gfx/webrender/src/prim_store.rs
gfx/webrender/src/profiler.rs
gfx/webrender/src/render_backend.rs
gfx/webrender/src/render_task.rs
gfx/webrender/src/renderer.rs
gfx/webrender/src/tiling.rs
gfx/webrender_traits/Cargo.toml
gfx/webrender_traits/src/api.rs
gfx/webrender_traits/src/color.rs
gfx/webrender_traits/src/display_item.rs
gfx/webrender_traits/src/display_list.rs
gfx/webrender_traits/src/font.rs
gfx/webrender_traits/src/image.rs
gfx/webrender_traits/src/lib.rs
gfx/webrender_traits/src/stacking_context.rs
gfx/webrender_traits/src/types.rs
gfx/webrender_traits/src/webgl.rs
--- a/gfx/doc/README.webrender
+++ b/gfx/doc/README.webrender
@@ -74,9 +74,9 @@ there is another crate in m-c called moz
 the same folder to store its rust dependencies. If one of the libraries that is
 required by both mozjs_sys and webrender is updated without updating the other
 project's Cargo.lock file, that results in build bustage.
 This means that any time you do this sort of manual update of packages, you need
 to make sure that mozjs_sys also has its Cargo.lock file updated if needed, hence
 the need to run the cargo update command in js/src as well. Hopefully this will
 be resolved soon.
 
-Latest Commit: 178a65d098afcda7de0298d80d423bc80c6426ba
+Latest Commit: e30fb2914928c0e596d8632ed234647c0fd1492e
--- a/gfx/webrender/Cargo.toml
+++ b/gfx/webrender/Cargo.toml
@@ -1,11 +1,11 @@
 [package]
 name = "webrender"
-version = "0.22.1"
+version = "0.24.0"
 authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
 license = "MPL-2.0"
 repository = "https://github.com/servo/webrender"
 build = "build.rs"
 
 [features]
 default = ["freetype-lib"]
 freetype-lib = ["freetype/servo-freetype-sys"]
--- a/gfx/webrender/res/prim_shared.glsl
+++ b/gfx/webrender/res/prim_shared.glsl
@@ -620,22 +620,19 @@ struct YuvImage {
     vec4 v_st_rect;
     vec2 size;
     int color_space;
 };
 
 YuvImage fetch_yuv_image(int index) {
     YuvImage image;
 
-    ivec2 uv = get_fetch_uv_4(index);
+    ivec2 uv = get_fetch_uv_1(index);
 
-    image.y_st_rect = texelFetchOffset(sData64, uv, 0, ivec2(0, 0));
-    image.u_st_rect = texelFetchOffset(sData64, uv, 0, ivec2(1, 0));
-    image.v_st_rect = texelFetchOffset(sData64, uv, 0, ivec2(2, 0));
-    vec4 size_color_space = texelFetchOffset(sData64, uv, 0, ivec2(3, 0));
+    vec4 size_color_space = texelFetchOffset(sData16, uv, 0, ivec2(0, 0));
     image.size = size_color_space.xy;
     image.color_space = int(size_color_space.z);
 
     return image;
 }
 
 struct BoxShadow {
     vec4 src_rect;
--- a/gfx/webrender/res/ps_gradient.vs.glsl
+++ b/gfx/webrender/res/ps_gradient.vs.glsl
@@ -7,35 +7,43 @@ void main(void) {
     Primitive prim = load_primitive();
     Gradient gradient = fetch_gradient(prim.prim_index);
 
     GradientStop g0 = fetch_gradient_stop(prim.sub_index + 0);
     GradientStop g1 = fetch_gradient_stop(prim.sub_index + 1);
 
     RectWithSize segment_rect;
     vec2 axis;
+    vec4 adjusted_color_g0 = g0.color;
+    vec4 adjusted_color_g1 = g1.color;
     if (gradient.start_end_point.y == gradient.start_end_point.w) {
-        float x0 = mix(gradient.start_end_point.x,
-                       gradient.start_end_point.z,
-                       g0.offset.x);
-        float x1 = mix(gradient.start_end_point.x,
-                       gradient.start_end_point.z,
-                       g1.offset.x);
-        segment_rect.p0 = vec2(x0, prim.local_rect.p0.y);
-        segment_rect.size = vec2(x1 - x0, prim.local_rect.size.y);
+        vec2 x = mix(gradient.start_end_point.xx, gradient.start_end_point.zz,
+                     vec2(g0.offset.x, g1.offset.x));
+        // The start and end point of gradient might exceed the geometry rect. So clamp
+        // it to the geometry rect.
+        x = clamp(x, prim.local_rect.p0.xx, prim.local_rect.p0.xx + prim.local_rect.size.xx);
+        vec2 adjusted_offset =
+            (x - gradient.start_end_point.xx) / (gradient.start_end_point.zz - gradient.start_end_point.xx);
+        adjusted_color_g0 = mix(g0.color, g1.color, adjusted_offset.x);
+        adjusted_color_g1 = mix(g0.color, g1.color, adjusted_offset.y);
+        segment_rect.p0 = vec2(x.x, prim.local_rect.p0.y);
+        segment_rect.size = vec2(x.y - x.x, prim.local_rect.size.y);
         axis = vec2(1.0, 0.0);
     } else {
-        float y0 = mix(gradient.start_end_point.y,
-                       gradient.start_end_point.w,
-                       g0.offset.x);
-        float y1 = mix(gradient.start_end_point.y,
-                       gradient.start_end_point.w,
-                       g1.offset.x);
-        segment_rect.p0 = vec2(prim.local_rect.p0.x, y0);
-        segment_rect.size = vec2(prim.local_rect.size.x, y1 - y0);
+        vec2 y = mix(gradient.start_end_point.yy, gradient.start_end_point.ww,
+                     vec2(g0.offset.x, g1.offset.x));
+        // The start and end point of gradient might exceed the geometry rect. So clamp
+        // it to the geometry rect.
+        y = clamp(y, prim.local_rect.p0.yy, prim.local_rect.p0.yy + prim.local_rect.size.yy);
+        vec2 adjusted_offset =
+            (y - gradient.start_end_point.yy) / (gradient.start_end_point.ww - gradient.start_end_point.yy);
+        adjusted_color_g0 = mix(g0.color, g1.color, adjusted_offset.x);
+        adjusted_color_g1 = mix(g0.color, g1.color, adjusted_offset.y);
+        segment_rect.p0 = vec2(prim.local_rect.p0.x, y.x);
+        segment_rect.size = vec2(prim.local_rect.size.x, y.y - y.x);
         axis = vec2(0.0, 1.0);
     }
 
 #ifdef WR_FEATURE_TRANSFORM
     TransformVertexInfo vi = write_transform_vertex(segment_rect,
                                                     prim.local_clip_rect,
                                                     prim.z,
                                                     prim.layer,
@@ -51,10 +59,10 @@ void main(void) {
                                  prim.task);
 
     vec2 f = (vi.local_pos - segment_rect.p0) / segment_rect.size;
     vPos = vi.local_pos;
 #endif
 
     write_clip(vi.screen_pos, prim.clip_area);
 
-    vColor = mix(g0.color, g1.color, dot(f, axis));
+    vColor = mix(adjusted_color_g0, adjusted_color_g1, dot(f, axis));
 }
--- a/gfx/webrender/res/ps_image.fs.glsl
+++ b/gfx/webrender/res/ps_image.fs.glsl
@@ -26,10 +26,10 @@ void main(void) {
     vec2 position_in_tile = mod(relative_pos_in_rect, vStretchSize + vTileSpacing);
     // We clamp the texture coordinates to the half-pixel offset from the borders
     // in order to avoid sampling outside of the texture area.
     vec2 st = vTextureOffset + ((position_in_tile / vStretchSize) * vTextureSize);
     st = clamp(st, vStRect.xy, vStRect.zw);
 
     alpha = alpha * float(all(bvec2(step(position_in_tile, vStretchSize))));
 
-    oFragColor = vec4(1.0, 1.0, 1.0, alpha) * textureLod(sColor0, st, 0.0);
+    oFragColor = vec4(alpha) * textureLod(sColor0, st, 0.0);
 }
--- a/gfx/webrender/res/ps_yuv_image.vs.glsl
+++ b/gfx/webrender/res/ps_yuv_image.vs.glsl
@@ -18,30 +18,33 @@ void main(void) {
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
                                  prim.task);
     vLocalPos = vi.local_pos - vi.local_rect.p0;
 #endif
 
     YuvImage image = fetch_yuv_image(prim.prim_index);
+    ResourceRect y_rect = fetch_resource_rect(prim.user_data.x);
+    ResourceRect u_rect = fetch_resource_rect(prim.user_data.x + 1);
+    ResourceRect v_rect = fetch_resource_rect(prim.user_data.x + 2);
 
     vec2 y_texture_size = vec2(textureSize(sColor0, 0));
-    vec2 y_st0 = image.y_st_rect.xy / y_texture_size;
-    vec2 y_st1 = image.y_st_rect.zw / y_texture_size;
+    vec2 y_st0 = y_rect.uv_rect.xy / y_texture_size;
+    vec2 y_st1 = y_rect.uv_rect.zw / y_texture_size;
 
     vTextureSizeY = y_st1 - y_st0;
     vTextureOffsetY = y_st0;
 
     vec2 uv_texture_size = vec2(textureSize(sColor1, 0));
-    vec2 u_st0 = image.u_st_rect.xy / uv_texture_size;
-    vec2 u_st1 = image.u_st_rect.zw / uv_texture_size;
+    vec2 u_st0 = u_rect.uv_rect.xy / uv_texture_size;
+    vec2 u_st1 = u_rect.uv_rect.zw / uv_texture_size;
 
-    vec2 v_st0 = image.v_st_rect.xy / uv_texture_size;
-    vec2 v_st1 = image.v_st_rect.zw / uv_texture_size;
+    vec2 v_st0 = v_rect.uv_rect.xy / uv_texture_size;
+    vec2 v_st1 = v_rect.uv_rect.zw / uv_texture_size;
 
     // This assumes the U and V surfaces have the same size.
     vTextureSizeUv = u_st1 - u_st0;
     vTextureOffsetU = u_st0;
     vTextureOffsetV = v_st0;
 
     vStretchSize = image.size;
 
--- a/gfx/webrender/src/clip_scroll_node.rs
+++ b/gfx/webrender/src/clip_scroll_node.rs
@@ -1,37 +1,92 @@
 /* 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 euclid::Point3D;
 use geometry::ray_intersects_rect;
+use mask_cache::{ClipSource, MaskCacheInfo};
+use prim_store::GpuBlock32;
+use renderer::VertexDataStore;
 use spring::{DAMPING, STIFFNESS, Spring};
-use webrender_traits::{LayerPixel, LayerPoint, LayerRect, LayerSize, LayerToScrollTransform};
-use webrender_traits::{LayerToWorldTransform, PipelineId, ScrollEventPhase, ScrollLayerId};
-use webrender_traits::{ScrollLayerRect, ScrollLocation, WorldPoint, WorldPoint4D};
+use tiling::PackedLayerIndex;
+use util::TransformedRect;
+use webrender_traits::{ClipRegion, LayerPixel, LayerPoint, LayerRect, LayerSize};
+use webrender_traits::{LayerToScrollTransform, LayerToWorldTransform, PipelineId};
+use webrender_traits::{ScrollEventPhase, ScrollLayerId, ScrollLayerRect, ScrollLocation};
+use webrender_traits::{ServoScrollRootId, WorldPoint, WorldPoint4D};
 
 #[cfg(target_os = "macos")]
 const CAN_OVERSCROLL: bool = true;
 
 #[cfg(not(target_os = "macos"))]
 const CAN_OVERSCROLL: bool = false;
 
-#[derive(Clone)]
+#[derive(Clone, Debug)]
+pub struct ClipInfo {
+    /// The ClipSource for this node, which is used to generate mask_cache_info.
+    pub clip_source: ClipSource,
+
+    /// The MaskCacheInfo for this node, which is produced by processing the
+    /// provided ClipSource.
+    pub mask_cache_info: Option<MaskCacheInfo>,
+
+    /// The packed layer index for this node, which is used to render a clip mask
+    /// for it, if necessary.
+    pub packed_layer_index: PackedLayerIndex,
+
+    /// The final transformed rectangle of this clipping region for this node,
+    /// which depends on the screen rectangle and the transformation of all of
+    /// the parents.
+    pub xf_rect: Option<TransformedRect>,
+
+    /// An external identifier that is used to scroll this clipping node
+    /// from the API.
+    pub scroll_root_id: Option<ServoScrollRootId>,
+}
+
+impl ClipInfo {
+    pub fn new(clip_region: &ClipRegion,
+               clip_store: &mut VertexDataStore<GpuBlock32>,
+               packed_layer_index: PackedLayerIndex,
+               scroll_root_id: Option<ServoScrollRootId>)
+               -> ClipInfo {
+        // We pass true here for the MaskCacheInfo because this type of
+        // mask needs an extra clip for the clip rectangle.
+        let clip_source = ClipSource::Region(clip_region.clone());
+        ClipInfo {
+            mask_cache_info: MaskCacheInfo::new(&clip_source, true, clip_store),
+            clip_source: clip_source,
+            packed_layer_index: packed_layer_index,
+            xf_rect: None,
+            scroll_root_id: scroll_root_id,
+        }
+    }
+
+    pub fn is_masking(&self) -> bool {
+        match self.mask_cache_info {
+            Some(ref info) => info.is_masking(),
+            _ => false,
+        }
+    }
+
+}
+#[derive(Clone, Debug)]
 pub enum NodeType {
     /// Transform for this layer, relative to parent reference frame. A reference
     /// frame establishes a new coordinate space in the tree.
     ReferenceFrame(LayerToScrollTransform),
 
     /// Other nodes just do clipping, but no transformation.
-    ClipRect,
+    Clip(ClipInfo),
 }
 
 /// Contains scrolling and transform information stacking contexts.
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub struct ClipScrollNode {
     /// Manages scrolling offset, overscroll state etc.
     pub scrolling: ScrollingState,
 
     /// Size of the content inside the scroll region (in logical pixels)
     pub content_size: LayerSize,
 
     /// Viewing rectangle in the coordinate system of the parent reference frame.
@@ -51,57 +106,63 @@ pub struct ClipScrollNode {
     pub world_viewport_transform: LayerToWorldTransform,
 
     /// World transform for content transformed by this node.
     pub world_content_transform: LayerToWorldTransform,
 
     /// Pipeline that this layer belongs to
     pub pipeline_id: PipelineId,
 
+    /// Parent layer. If this is None, we are the root node.
+    pub parent: Option<ScrollLayerId>,
+
     /// Child layers
     pub children: Vec<ScrollLayerId>,
 
     /// Whether or not this node is a reference frame.
     pub node_type: NodeType,
 }
 
 impl ClipScrollNode {
-    pub fn new(local_viewport_rect: &LayerRect,
-               local_clip_rect: &LayerRect,
+    pub fn new(pipeline_id: PipelineId,
+               parent_id: ScrollLayerId,
+               local_viewport_rect: &LayerRect,
                content_size: LayerSize,
-               pipeline_id: PipelineId)
+               clip_info: ClipInfo)
                -> ClipScrollNode {
         ClipScrollNode {
             scrolling: ScrollingState::new(),
             content_size: content_size,
             local_viewport_rect: *local_viewport_rect,
-            local_clip_rect: *local_clip_rect,
-            combined_local_viewport_rect: *local_clip_rect,
+            local_clip_rect: *local_viewport_rect,
+            combined_local_viewport_rect: LayerRect::zero(),
             world_viewport_transform: LayerToWorldTransform::identity(),
             world_content_transform: LayerToWorldTransform::identity(),
+            parent: Some(parent_id),
             children: Vec::new(),
             pipeline_id: pipeline_id,
-            node_type: NodeType::ClipRect,
+            node_type: NodeType::Clip(clip_info),
         }
     }
 
-    pub fn new_reference_frame(local_viewport_rect: &LayerRect,
-                               local_clip_rect: &LayerRect,
+    pub fn new_reference_frame(parent_id: Option<ScrollLayerId>,
+                               local_viewport_rect: &LayerRect,
                                content_size: LayerSize,
                                local_transform: &LayerToScrollTransform,
                                pipeline_id: PipelineId)
                                -> ClipScrollNode {
         ClipScrollNode {
             scrolling: ScrollingState::new(),
             content_size: content_size,
             local_viewport_rect: *local_viewport_rect,
-            local_clip_rect: *local_clip_rect,
-            combined_local_viewport_rect: *local_clip_rect,
+            local_clip_rect: *local_viewport_rect,
+            combined_local_viewport_rect: LayerRect::zero(),
             world_viewport_transform: LayerToWorldTransform::identity(),
             world_content_transform: LayerToWorldTransform::identity(),
+            parent: parent_id,
             children: Vec::new(),
             pipeline_id: pipeline_id,
             node_type: NodeType::ReferenceFrame(*local_transform),
         }
     }
 
     pub fn add_child(&mut self, child: ScrollLayerId) {
         self.children.push(child);
@@ -154,17 +215,17 @@ impl ClipScrollNode {
 
     pub fn update_transform(&mut self,
                             parent_reference_frame_transform: &LayerToWorldTransform,
                             parent_combined_viewport_rect: &ScrollLayerRect,
                             parent_accumulated_scroll_offset: LayerPoint) {
 
         let local_transform = match self.node_type {
             NodeType::ReferenceFrame(transform) => transform,
-            NodeType::ClipRect => LayerToScrollTransform::identity(),
+            NodeType::Clip(_) => LayerToScrollTransform::identity(),
         };
 
         let inv_transform = match local_transform.inverse() {
             Some(transform) => transform,
             None => {
                 // If a transform function causes the current transformation matrix of an object
                 // to be non-invertible, the object and its content do not get displayed.
                 self.combined_local_viewport_rect = LayerRect::zero();
@@ -325,17 +386,17 @@ impl ClipScrollNode {
 
         if self.scrollable_width() <= 0. && self.scrollable_height() <= 0. {
             return false;
         }
         ray_intersects_rect(p0, p1, self.local_viewport_rect.to_untyped())
     }
 }
 
-#[derive(Copy, Clone)]
+#[derive(Copy, Clone, Debug)]
 pub struct ScrollingState {
     pub offset: LayerPoint,
     pub spring: Spring,
     pub started_bouncing_back: bool,
     pub bouncing_back: bool,
     pub should_handoff_scroll: bool
 }
 
--- a/gfx/webrender/src/clip_scroll_tree.rs
+++ b/gfx/webrender/src/clip_scroll_tree.rs
@@ -1,20 +1,20 @@
 /* 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 clip_scroll_node::{ClipScrollNode, NodeType, ScrollingState};
 use fnv::FnvHasher;
 use std::collections::{HashMap, HashSet};
 use std::hash::BuildHasherDefault;
-use webrender_traits::{LayerPoint, LayerRect, LayerSize, LayerToScrollTransform};
-use webrender_traits::{LayerToWorldTransform, PipelineId, ScrollEventPhase, ScrollLayerId};
-use webrender_traits::{ScrollLayerInfo, ScrollLayerRect, ScrollLayerState, ScrollLocation};
-use webrender_traits::{ServoScrollRootId, WorldPoint, as_scroll_parent_rect};
+use webrender_traits::{LayerPoint, LayerRect, LayerToScrollTransform, LayerToWorldTransform};
+use webrender_traits::{PipelineId, ScrollEventPhase, ScrollLayerId, ScrollLayerInfo};
+use webrender_traits::{ScrollLayerRect, ScrollLayerState, ScrollLocation, ServoScrollRootId};
+use webrender_traits::{WorldPoint, as_scroll_parent_rect};
 
 pub type ScrollStates = HashMap<ScrollLayerId, ScrollingState, BuildHasherDefault<FnvHasher>>;
 
 pub struct ClipScrollTree {
     pub nodes: HashMap<ScrollLayerId, ClipScrollNode, BuildHasherDefault<FnvHasher>>,
     pub pending_scroll_offsets: HashMap<(PipelineId, ServoScrollRootId), LayerPoint>,
 
     /// The ScrollLayerId of the currently scrolling node. Used to allow the same
@@ -43,17 +43,17 @@ impl ClipScrollTree {
     pub fn new() -> ClipScrollTree {
         let dummy_pipeline = PipelineId(0, 0);
         ClipScrollTree {
             nodes: HashMap::with_hasher(Default::default()),
             pending_scroll_offsets: HashMap::new(),
             current_scroll_layer_id: None,
             root_reference_frame_id: ScrollLayerId::root_reference_frame(dummy_pipeline),
             topmost_scroll_layer_id: ScrollLayerId::root_scroll_layer(dummy_pipeline),
-            current_reference_frame_id: 1,
+            current_reference_frame_id: 0,
             pipelines_to_discard: HashSet::new(),
         }
     }
 
     pub fn root_reference_frame_id(&self) -> ScrollLayerId {
         // TODO(mrobinson): We should eventually make this impossible to misuse.
         debug_assert!(!self.nodes.is_empty());
         debug_assert!(self.nodes.contains_key(&self.root_reference_frame_id));
@@ -62,43 +62,16 @@ impl ClipScrollTree {
 
     pub fn topmost_scroll_layer_id(&self) -> ScrollLayerId {
         // TODO(mrobinson): We should eventually make this impossible to misuse.
         debug_assert!(!self.nodes.is_empty());
         debug_assert!(self.nodes.contains_key(&self.topmost_scroll_layer_id));
         self.topmost_scroll_layer_id
     }
 
-    pub fn establish_root(&mut self,
-                          pipeline_id: PipelineId,
-                          viewport_size: &LayerSize,
-                          viewport_offset: LayerPoint,
-                          clip_size: LayerSize,
-                          content_size: &LayerSize) {
-        debug_assert!(self.nodes.is_empty());
-
-        let transform = LayerToScrollTransform::create_translation(viewport_offset.x, viewport_offset.y, 0.0);
-        let viewport = LayerRect::new(LayerPoint::zero(), *viewport_size);
-        let clip = LayerRect::new(LayerPoint::new(-viewport_offset.x, -viewport_offset.y),
-                                  LayerSize::new(clip_size.width, clip_size.height));
-        let root_reference_frame_id = ScrollLayerId::root_reference_frame(pipeline_id);
-        self.root_reference_frame_id = root_reference_frame_id;
-        let reference_frame = ClipScrollNode::new_reference_frame(&viewport,
-                                                                  &clip,
-                                                                  viewport.size,
-                                                                  &transform,
-                                                                  pipeline_id);
-        self.nodes.insert(self.root_reference_frame_id, reference_frame);
-
-        let scroll_node = ClipScrollNode::new(&viewport, &clip, *content_size, pipeline_id);
-        let topmost_scroll_layer_id = ScrollLayerId::root_scroll_layer(pipeline_id);
-        self.topmost_scroll_layer_id = topmost_scroll_layer_id;
-        self.add_node(scroll_node, topmost_scroll_layer_id, root_reference_frame_id);
-    }
-
     pub fn collect_nodes_bouncing_back(&self)
                                        -> HashSet<ScrollLayerId, BuildHasherDefault<FnvHasher>> {
         let mut nodes_bouncing_back = HashSet::with_hasher(Default::default());
         for (scroll_layer_id, node) in self.nodes.iter() {
             if node.scrolling.bouncing_back {
                 nodes_bouncing_back.insert(*scroll_layer_id);
             }
         }
@@ -131,26 +104,26 @@ impl ClipScrollTree {
 
     pub fn find_scrolling_node_at_point(&self, cursor: &WorldPoint) -> ScrollLayerId {
         self.find_scrolling_node_at_point_in_node(cursor, self.root_reference_frame_id())
             .unwrap_or(self.topmost_scroll_layer_id())
     }
 
     pub fn get_scroll_node_state(&self) -> Vec<ScrollLayerState> {
         let mut result = vec![];
-        for (scroll_layer_id, scroll_node) in self.nodes.iter() {
-            match scroll_layer_id.info {
-                ScrollLayerInfo::Scrollable(_, servo_scroll_root_id) => {
+        for (_, node) in self.nodes.iter() {
+            match node.node_type {
+                NodeType::Clip(ref info) if info.scroll_root_id.is_some() => {
                     result.push(ScrollLayerState {
-                        pipeline_id: scroll_node.pipeline_id,
-                        scroll_root_id: servo_scroll_root_id,
-                        scroll_offset: scroll_node.scrolling.offset,
+                        pipeline_id: node.pipeline_id,
+                        scroll_root_id: info.scroll_root_id.unwrap(),
+                        scroll_offset: node.scrolling.offset,
                     })
                 }
-                ScrollLayerInfo::ReferenceFrame(..) => {}
+                _ => {},
             }
         }
         result
     }
 
     pub fn drain(&mut self) -> ScrollStates {
         self.current_reference_frame_id = 1;
 
@@ -179,20 +152,20 @@ impl ClipScrollTree {
 
         let mut scrolled_a_node = false;
         let mut found_node = false;
         for (layer_id, node) in self.nodes.iter_mut() {
             if layer_id.pipeline_id != pipeline_id {
                 continue;
             }
 
-            match layer_id.info {
-                ScrollLayerInfo::Scrollable(_, id) if id != scroll_root_id => continue,
-                ScrollLayerInfo::ReferenceFrame(..) => continue,
-                ScrollLayerInfo::Scrollable(..) => {}
+            match node.node_type {
+                NodeType::Clip(ref info) if info.scroll_root_id != Some(scroll_root_id) => continue,
+                NodeType::ReferenceFrame(..) => continue,
+                NodeType::Clip(_) => {},
             }
 
             found_node = true;
             scrolled_a_node |= node.set_scroll_origin(&origin);
         }
 
         if !found_node {
             self.pending_scroll_offsets.insert((pipeline_id, scroll_root_id), origin);
@@ -260,55 +233,64 @@ impl ClipScrollTree {
             ScrollEventPhase::End => {
                 // clean-up when gesture ends.
                 let mut current_node = self.nodes.get_mut(&scroll_layer_id).unwrap();
                 current_node.scrolling.should_handoff_scroll = false;
                 false
             },
         };
 
-        let scroll_node_info = if switch_node {
-            topmost_scroll_layer_id.info
+        let scroll_layer_id = if switch_node {
+            topmost_scroll_layer_id
         } else {
-            scroll_layer_id.info
+            scroll_layer_id
         };
 
-        let scroll_root_id = match scroll_node_info {
-             ScrollLayerInfo::Scrollable(_, scroll_root_id) => scroll_root_id,
-             _ => unreachable!("Tried to scroll a reference frame."),
+        // TODO(mrobinson): Once we remove the concept of shared scroll root ids we can remove
+        // this entirely and just scroll the node based on the ScrollLayerId.
+        let scroll_root_id = {
+            let node = self.nodes.get_mut(&scroll_layer_id).unwrap();
+            let scroll_root_id = match node.node_type {
+                NodeType::Clip(ref info) => info.scroll_root_id,
+                NodeType::ReferenceFrame(..) => unreachable!("Tried to scroll a reference frame."),
+            };
 
+            if scroll_root_id.is_none() {
+                return node.scroll(scroll_location, phase);
+            }
+
+            scroll_root_id
         };
 
         let mut scrolled_a_node = false;
         for (layer_id, node) in self.nodes.iter_mut() {
             if layer_id.pipeline_id != scroll_layer_id.pipeline_id {
                 continue;
             }
 
-            match layer_id.info {
-                ScrollLayerInfo::Scrollable(_, id) if id != scroll_root_id => continue,
-                ScrollLayerInfo::ReferenceFrame(..) => continue,
-                _ => {}
+            match node.node_type {
+                NodeType::Clip(ref info) if info.scroll_root_id == scroll_root_id => { }
+                _ => continue,
             }
 
             let scrolled_this_node = node.scroll(scroll_location, phase);
             scrolled_a_node = scrolled_a_node || scrolled_this_node;
         }
         scrolled_a_node
     }
 
-    pub fn update_all_node_transforms(&mut self) {
+    pub fn update_all_node_transforms(&mut self, pan: LayerPoint) {
         if self.nodes.is_empty() {
             return;
         }
 
         let root_reference_frame_id = self.root_reference_frame_id();
         let root_viewport = self.nodes[&root_reference_frame_id].local_clip_rect;
         self.update_node_transform(root_reference_frame_id,
-                                   &LayerToWorldTransform::identity(),
+                                   &LayerToWorldTransform::create_translation(pan.x, pan.y, 0.0),
                                    &as_scroll_parent_rect(&root_viewport),
                                    LayerPoint::zero());
     }
 
     fn update_node_transform(&mut self,
                              layer_id: ScrollLayerId,
                              parent_reference_frame_transform: &LayerToWorldTransform,
                              parent_viewport_rect: &ScrollLayerRect,
@@ -324,17 +306,17 @@ impl ClipScrollTree {
 
                     // The transformation we are passing is the transformation of the parent
                     // reference frame and the offset is the accumulated offset of all the nodes
                     // between us and the parent reference frame. If we are a reference frame,
                     // we need to reset both these values.
                     let (transform, offset) = match node.node_type {
                         NodeType::ReferenceFrame(..) =>
                             (node.world_viewport_transform, LayerPoint::zero()),
-                        NodeType::ClipRect => {
+                        NodeType::Clip(_) => {
                             (*parent_reference_frame_transform,
                              parent_accumulated_scroll_offset + node.scrolling.offset)
                         }
                     };
 
                     (transform,
                      as_scroll_parent_rect(&node.combined_local_viewport_rect),
                      offset,
@@ -364,53 +346,62 @@ impl ClipScrollTree {
         for (scroll_layer_id, node) in &mut self.nodes {
             let scrolling_state = match old_states.get(&scroll_layer_id) {
                 Some(old_scrolling_state) => *old_scrolling_state,
                 None => ScrollingState::new(),
             };
 
             node.finalize(&scrolling_state);
 
-            let scroll_root_id = match scroll_layer_id.info {
-                ScrollLayerInfo::Scrollable(_, scroll_root_id) => scroll_root_id,
+            let scroll_root_id = match node.node_type {
+                NodeType::Clip(ref info) if info.scroll_root_id.is_some() =>
+                    info.scroll_root_id.unwrap(),
                 _ => continue,
             };
 
 
             let pipeline_id = scroll_layer_id.pipeline_id;
             if let Some(pending_offset) =
                 self.pending_scroll_offsets.remove(&(pipeline_id, scroll_root_id)) {
                 node.set_scroll_origin(&pending_offset);
             }
         }
 
     }
 
     pub fn add_reference_frame(&mut self,
-                               rect: LayerRect,
-                               transform: LayerToScrollTransform,
+                               rect: &LayerRect,
+                               transform: &LayerToScrollTransform,
                                pipeline_id: PipelineId,
-                               parent_id: ScrollLayerId) -> ScrollLayerId {
+                               parent_id: Option<ScrollLayerId>)
+                               -> ScrollLayerId {
         let reference_frame_id = ScrollLayerId {
             pipeline_id: pipeline_id,
             info: ScrollLayerInfo::ReferenceFrame(self.current_reference_frame_id),
         };
         self.current_reference_frame_id += 1;
 
-        let node = ClipScrollNode::new_reference_frame(&rect, &rect, rect.size, &transform, pipeline_id);
-        self.add_node(node, reference_frame_id, parent_id);
+        let node = ClipScrollNode::new_reference_frame(parent_id,
+                                                       &rect,
+                                                       rect.size,
+                                                       &transform,
+                                                       pipeline_id);
+        self.add_node(node, reference_frame_id);
         reference_frame_id
     }
 
-    pub fn add_node(&mut self, node: ClipScrollNode, id: ScrollLayerId, parent_id: ScrollLayerId) {
+    pub fn add_node(&mut self, node: ClipScrollNode, id: ScrollLayerId) {
+        // When the parent node is None this means we are adding the root.
+        match node.parent {
+            Some(parent_id) => self.nodes.get_mut(&parent_id).unwrap().add_child(id),
+            None => self.root_reference_frame_id = id,
+        }
+
         debug_assert!(!self.nodes.contains_key(&id));
         self.nodes.insert(id, node);
-
-        debug_assert!(parent_id != id);
-        self.nodes.get_mut(&parent_id).unwrap().add_child(id);
     }
 
     pub fn discard_frame_state_for_pipeline(&mut self, pipeline_id: PipelineId) {
         self.pipelines_to_discard.insert(pipeline_id);
 
         match self.current_scroll_layer_id {
             Some(id) if id.pipeline_id == pipeline_id => self.current_scroll_layer_id = None,
             _ => {}
--- a/gfx/webrender/src/device.rs
+++ b/gfx/webrender/src/device.rs
@@ -17,16 +17,19 @@ use std::io::Read;
 use std::iter::repeat;
 use std::mem;
 use std::path::PathBuf;
 //use std::sync::mpsc::{channel, Sender};
 //use std::thread;
 use webrender_traits::{ColorF, ImageFormat};
 use webrender_traits::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceUintSize};
 
+#[derive(Debug, Copy, Clone)]
+pub struct FrameId(usize);
+
 #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
 const GL_FORMAT_A: gl::GLuint = gl::RED;
 
 #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
 const GL_FORMAT_A: gl::GLuint = gl::ALPHA;
 
 #[cfg(any(target_os = "windows", all(unix, not(target_os = "android"))))]
 const GL_FORMAT_BGRA: gl::GLuint = gl::BGRA;
@@ -492,42 +495,46 @@ pub struct GpuSample<T> {
     pub time_ns: u64,
 }
 
 pub struct GpuFrameProfile<T> {
     queries: Vec<gl::GLuint>,
     samples: Vec<GpuSample<T>>,
     next_query: usize,
     pending_query: gl::GLuint,
+    frame_id: FrameId,
 }
 
 impl<T> GpuFrameProfile<T> {
     #[cfg(not(target_os = "android"))]
     fn new() -> GpuFrameProfile<T> {
         let queries = gl::gen_queries(MAX_EVENTS_PER_FRAME as gl::GLint);
 
         GpuFrameProfile {
             queries: queries,
             samples: Vec::new(),
             next_query: 0,
             pending_query: 0,
+            frame_id: FrameId(0),
         }
     }
 
     #[cfg(target_os = "android")]
     fn new() -> GpuFrameProfile<T> {
         GpuFrameProfile {
             queries: Vec::new(),
             samples: Vec::new(),
             next_query: 0,
             pending_query: 0,
+            frame_id: FrameId(0),
         }
     }
 
-    fn begin_frame(&mut self) {
+    fn begin_frame(&mut self, frame_id: FrameId) {
+        self.frame_id = frame_id;
         self.next_query = 0;
         self.pending_query = 0;
         self.samples.clear();
     }
 
     #[cfg(not(target_os = "android"))]
     fn end_frame(&mut self) {
         if self.pending_query != 0 {
@@ -567,17 +574,17 @@ impl<T> GpuFrameProfile<T> {
     fn add_marker(&mut self, tag: T) {
         self.samples.push(GpuSample {
             tag: tag,
             time_ns: 0,
         });
     }
 
     fn is_valid(&self) -> bool {
-        self.next_query <= MAX_EVENTS_PER_FRAME
+        self.next_query > 0 && self.next_query <= MAX_EVENTS_PER_FRAME
     }
 
     #[cfg(not(target_os = "android"))]
     fn build_samples(&mut self) -> Vec<GpuSample<T>> {
         for (index, sample) in self.samples.iter_mut().enumerate() {
             sample.time_ns = gl::get_query_object_ui64v(self.queries[index], gl::QUERY_RESULT)
         }
 
@@ -614,28 +621,28 @@ impl<T> GpuProfiler<T> {
                       GpuFrameProfile::new(),
                       GpuFrameProfile::new(),
                       GpuFrameProfile::new(),
                       GpuFrameProfile::new(),
                     ],
         }
     }
 
-    pub fn build_samples(&mut self) -> Option<Vec<GpuSample<T>>> {
+    pub fn build_samples(&mut self) -> Option<(FrameId, Vec<GpuSample<T>>)> {
         let frame = &mut self.frames[self.next_frame];
         if frame.is_valid() {
-            Some(frame.build_samples())
+            Some((frame.frame_id, frame.build_samples()))
         } else {
             None
         }
     }
 
-    pub fn begin_frame(&mut self) {
+    pub fn begin_frame(&mut self, frame_id: FrameId) {
         let frame = &mut self.frames[self.next_frame];
-        frame.begin_frame();
+        frame.begin_frame(frame_id);
     }
 
     pub fn end_frame(&mut self) {
         let frame = &mut self.frames[self.next_frame];
         frame.end_frame();
         self.next_frame = (self.next_frame + 1) % MAX_PROFILE_FRAMES;
     }
 
@@ -816,16 +823,20 @@ pub struct Device {
     shader_preamble: String,
     //file_watcher: FileWatcherThread,
 
     // Used on android only
     #[allow(dead_code)]
     next_vao_id: gl::GLuint,
 
     max_texture_size: u32,
+
+    // Frame counter. This is used to map between CPU
+    // frames and GPU frames.
+    frame_id: FrameId,
 }
 
 impl Device {
     pub fn new(resource_override_path: Option<PathBuf>,
                _file_changed_handler: Box<FileWatcherHandler>) -> Device {
         //let file_watcher = FileWatcherThread::new(file_changed_handler);
 
         let shader_preamble = get_shader_source(SHADER_PREAMBLE, &resource_override_path);
@@ -855,17 +866,18 @@ impl Device {
             programs: HashMap::with_hasher(Default::default()),
             vaos: HashMap::with_hasher(Default::default()),
 
             shader_preamble: shader_preamble,
 
             next_vao_id: 1,
             //file_watcher: file_watcher,
 
-            max_texture_size: gl::get_integer_v(gl::MAX_TEXTURE_SIZE) as u32
+            max_texture_size: gl::get_integer_v(gl::MAX_TEXTURE_SIZE) as u32,
+            frame_id: FrameId(0),
         }
     }
 
     pub fn max_texture_size(&self) -> u32 {
         self.max_texture_size
     }
 
     pub fn get_capabilities(&self) -> &Capabilities {
@@ -898,17 +910,17 @@ impl Device {
         } else {
             if !log.is_empty() {
                 println!("Warnings detected on shader: {:?}\n{}", name, log);
             }
             Ok(id)
         }
     }
 
-    pub fn begin_frame(&mut self, device_pixel_ratio: f32) {
+    pub fn begin_frame(&mut self, device_pixel_ratio: f32) -> FrameId {
         debug_assert!(!self.inside_frame);
         self.inside_frame = true;
         self.device_pixel_ratio = device_pixel_ratio;
 
         // Retrive the currently set FBO.
         let default_read_fbo = gl::get_integer_v(gl::READ_FRAMEBUFFER_BINDING);
         self.default_read_fbo = default_read_fbo as gl::GLuint;
         let default_draw_fbo = gl::get_integer_v(gl::DRAW_FRAMEBUFFER_BINDING);
@@ -933,16 +945,18 @@ impl Device {
         self.bound_read_fbo = FBOId(self.default_read_fbo);
         self.bound_draw_fbo = FBOId(self.default_draw_fbo);
 
         // Pixel op state
         gl::pixel_store_i(gl::UNPACK_ALIGNMENT, 1);
 
         // Default is sampler 0, always
         gl::active_texture(gl::TEXTURE0);
+
+        self.frame_id
     }
 
     pub fn bind_texture(&mut self,
                         sampler: TextureSampler,
                         texture_id: TextureId) {
         debug_assert!(self.inside_frame);
 
         let sampler_index = sampler as usize;
@@ -1821,16 +1835,18 @@ impl Device {
         gl::use_program(0);
 
         for i in 0..self.bound_textures.len() {
             gl::active_texture(gl::TEXTURE0 + i as gl::GLuint);
             gl::bind_texture(gl::TEXTURE_2D, 0);
         }
 
         gl::active_texture(gl::TEXTURE0);
+
+        self.frame_id.0 += 1;
     }
 
     pub fn assign_ubo_binding(&self, program_id: ProgramId, name: &str, value: u32) -> u32 {
         let index = gl::get_uniform_block_index(program_id.0, name);
         gl::uniform_block_binding(program_id.0, index, value);
         index
     }
 
@@ -1942,17 +1958,17 @@ impl Device {
         if enable {
             gl::enable(gl::BLEND);
         } else {
             gl::disable(gl::BLEND);
         }
     }
 
     pub fn set_blend_mode_premultiplied_alpha(&self) {
-        gl::blend_func(gl::SRC_ALPHA, gl::ZERO);
+        gl::blend_func(gl::ONE, gl::ONE_MINUS_SRC_ALPHA);
         gl::blend_equation(gl::FUNC_ADD);
     }
 
     pub fn set_blend_mode_alpha(&self) {
         //gl::blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
         gl::blend_func_separate(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA,
                                 gl::ONE, gl::ONE);
         gl::blend_equation(gl::FUNC_ADD);
--- a/gfx/webrender/src/frame.rs
+++ b/gfx/webrender/src/frame.rs
@@ -1,41 +1,64 @@
 /* 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::rect::rect;
 use fnv::FnvHasher;
 use internal_types::{ANGLE_FLOAT_TO_FIXED, AxisDirection};
 use internal_types::{LowLevelFilterOp};
 use internal_types::{RendererFrame};
 use frame_builder::{FrameBuilder, FrameBuilderConfig};
-use clip_scroll_node::ClipScrollNode;
 use clip_scroll_tree::{ClipScrollTree, ScrollStates};
 use profiler::TextureCacheProfileCounters;
 use resource_cache::ResourceCache;
 use scene::{Scene, SceneProperties};
 use std::collections::HashMap;
 use std::hash::BuildHasherDefault;
 use tiling::{AuxiliaryListsMap, CompositeOps, PrimitiveFlags};
-use webrender_traits::{AuxiliaryLists, ClipRegion, ColorF, DisplayItem, Epoch, FilterOp};
-use webrender_traits::{LayerPoint, LayerRect, LayerSize, LayerToScrollTransform, LayoutTransform, TileOffset};
-use webrender_traits::{MixBlendMode, PipelineId, ScrollEventPhase, ScrollLayerId, ScrollLayerState};
-use webrender_traits::{ScrollLocation, ScrollPolicy, ServoScrollRootId, SpecificDisplayItem};
-use webrender_traits::{StackingContext, WorldPoint, ImageDisplayItem, DeviceUintRect, DeviceUintSize};
+use webrender_traits::{AuxiliaryLists, ClipDisplayItem, ClipRegion, ColorF, DeviceUintRect};
+use webrender_traits::{DeviceUintSize, DisplayItem, Epoch, FilterOp, ImageDisplayItem, LayerPoint};
+use webrender_traits::{LayerRect, LayerSize, LayerToScrollTransform, LayoutTransform};
+use webrender_traits::{MixBlendMode, PipelineId, ScrollEventPhase, ScrollLayerId};
+use webrender_traits::{ScrollLayerState, ScrollLocation, ScrollPolicy, ServoScrollRootId};
+use webrender_traits::{SpecificDisplayItem, StackingContext, TileOffset, WorldPoint};
 
 #[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
 pub struct FrameId(pub u32);
 
 static DEFAULT_SCROLLBAR_COLOR: ColorF = ColorF { r: 0.3, g: 0.3, b: 0.3, a: 0.6 };
 
 struct FlattenContext<'a> {
     scene: &'a Scene,
     builder: &'a mut FrameBuilder,
     resource_cache: &'a mut ResourceCache,
+    replacements: Vec<(ScrollLayerId, ScrollLayerId)>,
+}
+
+impl<'a> FlattenContext<'a> {
+    fn new(scene: &'a Scene,
+           builder: &'a mut FrameBuilder,
+           resource_cache: &'a mut ResourceCache)
+           -> FlattenContext<'a> {
+        FlattenContext {
+            scene: scene,
+            builder: builder,
+            resource_cache: resource_cache,
+            replacements: Vec::new(),
+        }
+    }
+
+    fn scroll_layer_id_with_replacement(&self, id: ScrollLayerId) -> ScrollLayerId {
+        match self.replacements.last() {
+            Some(&(to_replace, replacement)) if to_replace == id => replacement,
+            _ => id,
+        }
+    }
 }
 
 // TODO: doc
 pub struct Frame {
     pub clip_scroll_tree: ClipScrollTree,
     pub pipeline_epoch_map: HashMap<PipelineId, Epoch, BuildHasherDefault<FnvHasher>>,
     pub pipeline_auxiliary_lists: AuxiliaryListsMap,
     id: FrameId,
@@ -136,20 +159,25 @@ impl<'a> DisplayListTraversal<'a> {
     pub fn new_skipping_first(display_list: &'a Vec<DisplayItem>) -> DisplayListTraversal {
         DisplayListTraversal {
             display_list: display_list,
             next_item_index: 1,
         }
     }
 
     pub fn skip_current_stacking_context(&mut self) {
+        let mut depth = 0;
         for item in self {
-            if item.item == SpecificDisplayItem::PopStackingContext {
-                return;
+            match item.item {
+                SpecificDisplayItem::PushStackingContext(..) => depth += 1,
+                SpecificDisplayItem::PopStackingContext if depth == 0 => return,
+                SpecificDisplayItem::PopStackingContext => depth -= 1,
+                _ => {}
             }
+            debug_assert!(depth >= 0);
         }
     }
 
     pub fn current_stacking_context_empty(&self) -> bool {
         match self.peek() {
             Some(item) => item.item == SpecificDisplayItem::PopStackingContext,
             None => true,
         }
@@ -263,117 +291,80 @@ impl Frame {
         let (root_stacking_context, root_clip) = match display_list.starting_stacking_context() {
             Some(some) => some,
             None => {
                 warn!("Pipeline display list does not start with a stacking context.");
                 return;
             }
         };
 
-        let inner_origin = inner_rect.origin.to_f32();
-        let viewport_offset = LayerPoint::new((inner_origin.x / device_pixel_ratio).round(),
-                                              (inner_origin.y / device_pixel_ratio).round());
-        let outer_size = window_size.to_f32();
-        let outer_size = LayerSize::new((outer_size.width / device_pixel_ratio).round(),
-                                        (outer_size.height / device_pixel_ratio).round());
-        let clip_size = LayerSize::new(outer_size.width + 2.0 * viewport_offset.x,
-                                       outer_size.height + 2.0 * viewport_offset.y);
-
-        self.clip_scroll_tree.establish_root(root_pipeline_id,
-                                             &root_pipeline.viewport_size,
-                                             viewport_offset,
-                                             clip_size,
-                                             &root_clip.main.size);
-
         let background_color = root_pipeline.background_color.and_then(|color| {
             if color.a > 0.0 {
                 Some(color)
             } else {
                 None
             }
         });
 
         let mut frame_builder = FrameBuilder::new(window_size,
                                                   background_color,
                                                   self.frame_builder_config);
 
         {
-            let mut context = FlattenContext {
-                scene: scene,
-                builder: &mut frame_builder,
-                resource_cache: resource_cache
-            };
+            let mut context = FlattenContext::new(scene, &mut frame_builder, resource_cache);
+
+            let scroll_layer_id = context.builder.push_root(root_pipeline_id,
+                                                            &root_pipeline.viewport_size,
+                                                            &root_clip.main.size,
+                                                            &mut self.clip_scroll_tree);
+
+            context.builder.setup_viewport_offset(window_size,
+                                                  inner_rect,
+                                                  device_pixel_ratio,
+                                                  &mut self.clip_scroll_tree);
 
             let mut traversal = DisplayListTraversal::new_skipping_first(display_list);
-            let reference_frame_id = self.clip_scroll_tree.root_reference_frame_id();
-            let topmost_scroll_layer_id = self.clip_scroll_tree.topmost_scroll_layer_id();
-            debug_assert!(reference_frame_id != topmost_scroll_layer_id);
-
-            let viewport_rect = LayerRect::new(LayerPoint::zero(), root_pipeline.viewport_size);
-            let clip = ClipRegion::simple(&viewport_rect);
-            context.builder.push_clip_scroll_node(reference_frame_id, &clip);
-            context.builder.push_clip_scroll_node(topmost_scroll_layer_id, &clip);
-
             self.flatten_stacking_context(&mut traversal,
                                           root_pipeline_id,
                                           &mut context,
-                                          reference_frame_id,
-                                          topmost_scroll_layer_id,
+                                          scroll_layer_id,
                                           LayerPoint::zero(),
                                           0,
                                           &root_stacking_context,
                                           root_clip);
-
-            context.builder.pop_clip_scroll_node();
-            context.builder.pop_clip_scroll_node();
         }
 
         self.frame_builder = Some(frame_builder);
         self.clip_scroll_tree.finalize_and_apply_pending_scroll_offsets(old_scrolling_states);
     }
 
-    fn flatten_scroll_layer<'a>(&mut self,
-                                traversal: &mut DisplayListTraversal<'a>,
-                                pipeline_id: PipelineId,
-                                context: &mut FlattenContext,
-                                current_reference_frame_id: ScrollLayerId,
-                                parent_scroll_layer_id: ScrollLayerId,
-                                reference_frame_relative_offset: LayerPoint,
-                                level: i32,
-                                clip: &ClipRegion,
-                                content_size: &LayerSize,
-                                new_scroll_layer_id: ScrollLayerId) {
-        // Avoid doing unnecessary work for empty stacking contexts.
-        if traversal.current_stacking_context_empty() {
-            traversal.skip_current_stacking_context();
-            return;
-        }
+    fn flatten_clip<'a>(&mut self,
+                        context: &mut FlattenContext,
+                        pipeline_id: PipelineId,
+                        parent_id: ScrollLayerId,
+                        item: &ClipDisplayItem,
+                        reference_frame_relative_offset: LayerPoint,
+                        clip: &ClipRegion) {
+        let clip_rect = clip.main.translate(&reference_frame_relative_offset);
+        context.builder.add_clip_scroll_node(item.id,
+                                             parent_id,
+                                             pipeline_id,
+                                             &clip_rect,
+                                             &item.content_size,
+                                             item.scroll_root_id,
+                                             clip,
+                                             &mut self.clip_scroll_tree);
 
-        let clip_rect = clip.main.translate(&reference_frame_relative_offset);
-        let node = ClipScrollNode::new(&clip_rect, &clip_rect, *content_size, pipeline_id);
-        self.clip_scroll_tree.add_node(node, new_scroll_layer_id, parent_scroll_layer_id);
-        context.builder.push_clip_scroll_node(new_scroll_layer_id, clip);
-
-        self.flatten_items(traversal,
-                           pipeline_id,
-                           context,
-                           current_reference_frame_id,
-                           new_scroll_layer_id,
-                           reference_frame_relative_offset,
-                           level);
-
-        context.builder.pop_clip_scroll_node();
     }
 
     fn flatten_stacking_context<'a>(&mut self,
                                     traversal: &mut DisplayListTraversal<'a>,
                                     pipeline_id: PipelineId,
                                     context: &mut FlattenContext,
-                                    current_reference_frame_id: ScrollLayerId,
-                                    current_scroll_layer_id: ScrollLayerId,
+                                    context_scroll_layer_id: ScrollLayerId,
                                     mut reference_frame_relative_offset: LayerPoint,
                                     level: i32,
                                     stacking_context: &StackingContext,
                                     clip_region: &ClipRegion) {
         // Avoid doing unnecessary work for empty stacking contexts.
         if traversal.current_stacking_context_empty() {
             traversal.skip_current_stacking_context();
             return;
@@ -388,107 +379,111 @@ impl Frame {
                 stacking_context.mix_blend_mode_for_compositing())
         };
 
         if composition_operations.will_make_invisible() {
             traversal.skip_current_stacking_context();
             return;
         }
 
-        let mut reference_frame_id = current_reference_frame_id;
-        let mut scroll_layer_id = match stacking_context.scroll_policy {
-            ScrollPolicy::Fixed => current_reference_frame_id,
-            ScrollPolicy::Scrollable => current_scroll_layer_id,
-        };
+        let mut scroll_layer_id =
+            context.scroll_layer_id_with_replacement(context_scroll_layer_id);
+
+        if stacking_context.scroll_policy == ScrollPolicy::Fixed {
+            context.replacements.push((context_scroll_layer_id,
+                                       context.builder.current_reference_frame_id()));
+        }
 
         // If we have a transformation, we establish a new reference frame. This means
         // that fixed position stacking contexts are positioned relative to us.
-        if stacking_context.transform.is_some() || stacking_context.perspective.is_some() {
+        let is_reference_frame = stacking_context.transform.is_some() ||
+                                 stacking_context.perspective.is_some();
+        if is_reference_frame {
             let transform = stacking_context.transform.as_ref();
             let transform = context.scene.properties.resolve_layout_transform(transform);
             let perspective =
                 stacking_context.perspective.unwrap_or_else(LayoutTransform::identity);
             let transform =
                 LayerToScrollTransform::create_translation(reference_frame_relative_offset.x,
                                                            reference_frame_relative_offset.y,
                                                            0.0)
                                         .pre_translated(stacking_context.bounds.origin.x,
                                                         stacking_context.bounds.origin.y,
                                                         0.0)
                                         .pre_mul(&transform)
                                         .pre_mul(&perspective);
-            scroll_layer_id = self.clip_scroll_tree.add_reference_frame(clip_region.main,
-                                                                        transform,
-                                                                        pipeline_id,
-                                                                        scroll_layer_id);
-            reference_frame_id = scroll_layer_id;
+
+            scroll_layer_id = context.builder.push_reference_frame(Some(scroll_layer_id),
+                                                                   pipeline_id,
+                                                                   &clip_region.main,
+                                                                   &transform,
+                                                                   &mut self.clip_scroll_tree);
+            context.replacements.push((context_scroll_layer_id, scroll_layer_id));
             reference_frame_relative_offset = LayerPoint::zero();
         } else {
             reference_frame_relative_offset = LayerPoint::new(
                 reference_frame_relative_offset.x + stacking_context.bounds.origin.x,
                 reference_frame_relative_offset.y + stacking_context.bounds.origin.y);
         }
 
+        // TODO(gw): Int with overflow etc
+        context.builder.push_stacking_context(reference_frame_relative_offset,
+                                              clip_region.main,
+                                              pipeline_id,
+                                              level == 0,
+                                              composition_operations);
+
         if level == 0 {
             if let Some(pipeline) = context.scene.pipeline_map.get(&pipeline_id) {
                 if let Some(bg_color) = pipeline.background_color {
-                    // Adding a dummy layer for this rectangle in order to disable clipping.
-                    context.builder.push_stacking_context(reference_frame_relative_offset,
-                                                          clip_region.main,
-                                                          pipeline_id,
-                                                          false,
-                                                          CompositeOps::empty());
-
                     // Note: we don't use the original clip region here,
                     // it's already processed by the node we just pushed.
                     let background_rect = LayerRect::new(LayerPoint::zero(), clip_region.main.size);
                     context.builder.add_solid_rectangle(scroll_layer_id,
                                                         &clip_region.main,
                                                         &ClipRegion::simple(&background_rect),
                                                         &bg_color,
                                                         PrimitiveFlags::None);
-
-                    context.builder.pop_stacking_context();
                 }
             }
         }
 
-         // TODO(gw): Int with overflow etc
-        context.builder.push_stacking_context(reference_frame_relative_offset,
-                                              clip_region.main,
-                                              pipeline_id,
-                                              level == 0,
-                                              composition_operations);
-
         self.flatten_items(traversal,
                            pipeline_id,
                            context,
-                           reference_frame_id,
-                           scroll_layer_id,
                            reference_frame_relative_offset,
                            level);
 
         if level == 0 && self.frame_builder_config.enable_scrollbars {
             let scrollbar_rect = LayerRect::new(LayerPoint::zero(), LayerSize::new(10.0, 70.0));
             context.builder.add_solid_rectangle(
                 scroll_layer_id,
                 &scrollbar_rect,
                 &ClipRegion::simple(&scrollbar_rect),
                 &DEFAULT_SCROLLBAR_COLOR,
                 PrimitiveFlags::Scrollbar(self.clip_scroll_tree.topmost_scroll_layer_id(), 4.0));
         }
 
+        if stacking_context.scroll_policy == ScrollPolicy::Fixed {
+            context.replacements.pop();
+        }
+
+        if is_reference_frame {
+            context.replacements.pop();
+            context.builder.pop_reference_frame();
+        }
+
         context.builder.pop_stacking_context();
     }
 
     fn flatten_iframe<'a>(&mut self,
                           pipeline_id: PipelineId,
+                          parent_id: ScrollLayerId,
                           bounds: &LayerRect,
                           context: &mut FlattenContext,
-                          current_scroll_layer_id: ScrollLayerId,
                           reference_frame_relative_offset: LayerPoint) {
 
         let pipeline = match context.scene.pipeline_map.get(&pipeline_id) {
             Some(pipeline) => pipeline,
             None => return,
         };
 
         let display_list = context.scene.display_lists.get(&pipeline_id);
@@ -507,193 +502,258 @@ impl Frame {
 
         self.pipeline_epoch_map.insert(pipeline_id, pipeline.epoch);
 
         let iframe_rect = LayerRect::new(LayerPoint::zero(), bounds.size);
         let transform = LayerToScrollTransform::create_translation(
             reference_frame_relative_offset.x + bounds.origin.x,
             reference_frame_relative_offset.y + bounds.origin.y,
             0.0);
+
         let iframe_reference_frame_id =
-            self.clip_scroll_tree.add_reference_frame(iframe_rect,
-                                                      transform,
-                                                      pipeline_id,
-                                                      current_scroll_layer_id);
+            context.builder.push_reference_frame(Some(parent_id),
+                                                 pipeline_id,
+                                                 &iframe_rect,
+                                                 &transform,
+                                                 &mut self.clip_scroll_tree);
+
         let iframe_scroll_layer_id = ScrollLayerId::root_scroll_layer(pipeline_id);
-        let node = ClipScrollNode::new(&LayerRect::new(LayerPoint::zero(), iframe_rect.size),
-                                       &LayerRect::new(LayerPoint::zero(), iframe_rect.size),
-                                       iframe_clip.main.size,
-                                       pipeline_id);
-        self.clip_scroll_tree.add_node(node.clone(),
-                                       iframe_scroll_layer_id,
-                                       iframe_reference_frame_id);
-
-        context.builder.push_clip_scroll_node(iframe_reference_frame_id, iframe_clip);
-        context.builder.push_clip_scroll_node(iframe_scroll_layer_id, iframe_clip);
+        context.builder.add_clip_scroll_node(
+            iframe_scroll_layer_id,
+            iframe_reference_frame_id,
+            pipeline_id,
+            &LayerRect::new(LayerPoint::zero(), iframe_rect.size),
+            &iframe_clip.main.size,
+            Some(ServoScrollRootId(0)),
+            iframe_clip,
+            &mut self.clip_scroll_tree);
 
         let mut traversal = DisplayListTraversal::new_skipping_first(display_list);
-
         self.flatten_stacking_context(&mut traversal,
                                       pipeline_id,
                                       context,
-                                      iframe_reference_frame_id,
                                       iframe_scroll_layer_id,
                                       LayerPoint::zero(),
                                       0,
                                       &iframe_stacking_context,
                                       iframe_clip);
 
-        context.builder.pop_clip_scroll_node();
-        context.builder.pop_clip_scroll_node();
+        context.builder.pop_reference_frame();
     }
 
     fn flatten_items<'a>(&mut self,
                          traversal: &mut DisplayListTraversal<'a>,
                          pipeline_id: PipelineId,
                          context: &mut FlattenContext,
-                         current_reference_frame_id: ScrollLayerId,
-                         current_scroll_layer_id: ScrollLayerId,
                          reference_frame_relative_offset: LayerPoint,
                          level: i32) {
         while let Some(item) = traversal.next() {
+            let scroll_layer_id = context.scroll_layer_id_with_replacement(item.scroll_layer_id);
             match item.item {
                 SpecificDisplayItem::WebGL(ref info) => {
-                    context.builder.add_webgl_rectangle(current_scroll_layer_id,
+                    context.builder.add_webgl_rectangle(scroll_layer_id,
                                                         item.rect,
                                                         &item.clip,
                                                         info.context_id);
                 }
                 SpecificDisplayItem::Image(ref info) => {
                     let image = context.resource_cache.get_image_properties(info.image_key);
                     if let Some(tile_size) = image.tiling {
                         // The image resource is tiled. We have to generate an image primitive
                         // for each tile.
                         let image_size = DeviceUintSize::new(image.descriptor.width, image.descriptor.height);
-                        self.decompose_tiled_image(current_scroll_layer_id,
-                                                   context,
-                                                   &item,
-                                                   info,
-                                                   image_size,
-                                                   tile_size as u32);
+                        self.decompose_image(scroll_layer_id,
+                                             context,
+                                             &item.rect,
+                                             &item.clip,
+                                             info,
+                                             image_size,
+                                             tile_size as u32);
                     } else {
-                        context.builder.add_image(current_scroll_layer_id,
+                        context.builder.add_image(scroll_layer_id,
                                                   item.rect,
                                                   &item.clip,
                                                   &info.stretch_size,
                                                   &info.tile_spacing,
                                                   None,
                                                   info.image_key,
                                                   info.image_rendering,
                                                   None);
                     }
                 }
                 SpecificDisplayItem::YuvImage(ref info) => {
-                    context.builder.add_yuv_image(current_scroll_layer_id,
+                    context.builder.add_yuv_image(scroll_layer_id,
                                                   item.rect,
                                                   &item.clip,
                                                   info.y_image_key,
                                                   info.u_image_key,
                                                   info.v_image_key,
                                                   info.color_space);
                 }
                 SpecificDisplayItem::Text(ref text_info) => {
-                    context.builder.add_text(current_scroll_layer_id,
+                    context.builder.add_text(scroll_layer_id,
                                              item.rect,
                                              &item.clip,
                                              text_info.font_key,
                                              text_info.size,
                                              text_info.blur_radius,
                                              &text_info.color,
                                              text_info.glyphs,
                                              text_info.glyph_options);
                 }
                 SpecificDisplayItem::Rectangle(ref info) => {
-                    context.builder.add_solid_rectangle(current_scroll_layer_id,
+                    context.builder.add_solid_rectangle(scroll_layer_id,
                                                         &item.rect,
                                                         &item.clip,
                                                         &info.color,
                                                         PrimitiveFlags::None);
                 }
                 SpecificDisplayItem::Gradient(ref info) => {
-                    context.builder.add_gradient(current_scroll_layer_id,
+                    context.builder.add_gradient(scroll_layer_id,
                                                  item.rect,
                                                  &item.clip,
-                                                 info.start_point,
-                                                 info.end_point,
-                                                 info.stops,
-                                                 info.extend_mode);
+                                                 info.gradient.start_point,
+                                                 info.gradient.end_point,
+                                                 info.gradient.stops,
+                                                 info.gradient.extend_mode);
                 }
                 SpecificDisplayItem::RadialGradient(ref info) => {
-                    context.builder.add_radial_gradient(current_scroll_layer_id,
+                    context.builder.add_radial_gradient(scroll_layer_id,
                                                         item.rect,
                                                         &item.clip,
-                                                        info.start_center,
-                                                        info.start_radius,
-                                                        info.end_center,
-                                                        info.end_radius,
-                                                        info.stops,
-                                                        info.extend_mode);
+                                                        info.gradient.start_center,
+                                                        info.gradient.start_radius,
+                                                        info.gradient.end_center,
+                                                        info.gradient.end_radius,
+                                                        info.gradient.stops,
+                                                        info.gradient.extend_mode);
                 }
                 SpecificDisplayItem::BoxShadow(ref box_shadow_info) => {
-                    context.builder.add_box_shadow(current_scroll_layer_id,
+                    context.builder.add_box_shadow(scroll_layer_id,
                                                    &box_shadow_info.box_bounds,
                                                    &item.clip,
                                                    &box_shadow_info.offset,
                                                    &box_shadow_info.color,
                                                    box_shadow_info.blur_radius,
                                                    box_shadow_info.spread_radius,
                                                    box_shadow_info.border_radius,
                                                    box_shadow_info.clip_mode);
                 }
                 SpecificDisplayItem::Border(ref info) => {
-                    context.builder.add_border(current_scroll_layer_id,
+                    context.builder.add_border(scroll_layer_id,
                                                item.rect,
                                                &item.clip,
                                                info);
                 }
                 SpecificDisplayItem::PushStackingContext(ref info) => {
                     self.flatten_stacking_context(traversal,
                                                   pipeline_id,
                                                   context,
-                                                  current_reference_frame_id,
-                                                  current_scroll_layer_id,
+                                                  item.scroll_layer_id,
                                                   reference_frame_relative_offset,
                                                   level + 1,
                                                   &info.stacking_context,
                                                   &item.clip);
                 }
-                SpecificDisplayItem::PushScrollLayer(ref info) => {
-                    self.flatten_scroll_layer(traversal,
-                                              pipeline_id,
-                                              context,
-                                              current_reference_frame_id,
-                                              current_scroll_layer_id,
-                                              reference_frame_relative_offset,
-                                              level,
-                                              &item.clip,
-                                              &info.content_size,
-                                              info.id);
-                }
                 SpecificDisplayItem::Iframe(ref info) => {
                     self.flatten_iframe(info.pipeline_id,
+                                        scroll_layer_id,
                                         &item.rect,
                                         context,
-                                        current_scroll_layer_id,
                                         reference_frame_relative_offset);
                 }
-                SpecificDisplayItem::PopStackingContext |
-                SpecificDisplayItem::PopScrollLayer => return,
+                SpecificDisplayItem::Clip(ref info) => {
+                    self.flatten_clip(context,
+                                      pipeline_id,
+                                      scroll_layer_id,
+                                      &info,
+                                      reference_frame_relative_offset,
+                                      &item.clip);
+                }
+                SpecificDisplayItem::PopStackingContext => return,
+            }
+        }
+    }
+
+    /// Decomposes an image display item that is repeated into an image per individual repetition.
+    /// We need to do this when we are unable to perform the repetition in the shader,
+    /// for example if the image is tiled.
+    ///
+    /// In all of the "decompose" methods below, we independently handle horizontal and vertical
+    /// decomposition. This lets us generate the minimum amount of primitives by, for  example,
+    /// decompositing the repetition horizontally while repeating vertically in the shader (for
+    /// an image where the width is too bug but the height is not).
+    ///
+    /// decompose_image and decompose_image_row handle image repetitions while decompose_tiled_image
+    /// takes care of the decomposition required by the internal tiling of the image.
+    fn decompose_image(&mut self,
+                       scroll_layer_id: ScrollLayerId,
+                       context: &mut FlattenContext,
+                       item_rect: &LayerRect,
+                       item_clip: &ClipRegion,
+                       info: &ImageDisplayItem,
+                       image_size: DeviceUintSize,
+                       tile_size: u32) {
+        let no_vertical_tiling = image_size.height <= tile_size;
+        let no_vertical_spacing = info.tile_spacing.height == 0.0;
+        if no_vertical_tiling && no_vertical_spacing {
+            self.decompose_image_row(scroll_layer_id, context, item_rect, item_clip, info, image_size, tile_size);
+            return;
+        }
+
+        // Decompose each vertical repetition into rows.
+        let layout_stride = info.stretch_size.height + info.tile_spacing.height;
+        let num_repetitions = (item_rect.size.height / layout_stride).ceil() as u32;
+        for i in 0..num_repetitions {
+            if let Some(row_rect) = rect(
+                item_rect.origin.x,
+                item_rect.origin.y + (i as f32) * layout_stride,
+                item_rect.size.width,
+                info.stretch_size.height
+            ).intersection(&item_rect) {
+                self.decompose_image_row(scroll_layer_id, context, &row_rect, item_clip, info, image_size, tile_size);
+            }
+        }
+    }
+
+    fn decompose_image_row(&mut self,
+                           scroll_layer_id: ScrollLayerId,
+                           context: &mut FlattenContext,
+                           item_rect: &LayerRect,
+                           item_clip: &ClipRegion,
+                           info: &ImageDisplayItem,
+                           image_size: DeviceUintSize,
+                           tile_size: u32) {
+        let no_horizontal_tiling = image_size.width <= tile_size;
+        let no_horizontal_spacing = info.tile_spacing.width == 0.0;
+        if no_horizontal_tiling && no_horizontal_spacing {
+            self.decompose_tiled_image(scroll_layer_id, context, item_rect, item_clip, info, image_size, tile_size);
+            return;
+        }
+
+        // Decompose each horizontal repetition.
+        let layout_stride = info.stretch_size.width + info.tile_spacing.width;
+        let num_repetitions = (item_rect.size.width / layout_stride).ceil() as u32;
+        for i in 0..num_repetitions {
+            if let Some(decomposed_rect) = rect(
+                item_rect.origin.x + (i as f32) * layout_stride,
+                item_rect.origin.y,
+                info.stretch_size.width,
+                item_rect.size.height,
+            ).intersection(&item_rect) {
+                self.decompose_tiled_image(scroll_layer_id, context, &decomposed_rect, item_clip, info, image_size, tile_size);
             }
         }
     }
 
     fn decompose_tiled_image(&mut self,
                              scroll_layer_id: ScrollLayerId,
                              context: &mut FlattenContext,
-                             item: &DisplayItem,
+                             item_rect: &LayerRect,
+                             item_clip: &ClipRegion,
                              info: &ImageDisplayItem,
                              image_size: DeviceUintSize,
                              tile_size: u32) {
         // The image resource is tiled. We have to generate an image primitive
         // for each tile.
         // We need to do this because the image is broken up into smaller tiles in the texture
         // cache and the image shader is not able to work with this type of sparse representation.
 
@@ -711,135 +771,127 @@ impl Frame {
         //  #----+----+----+----+
         //
         // In the ascii diagram above, a large image is plit into tiles of almost regular size.
         // The tiles on the right and bottom edges (hatched in the diagram) are smaller than
         // the regular tiles and are handled separately in the code see leftover_width/height.
         // each generated image primitive corresponds to a tile in the texture cache, with the
         // assumption that the smaller tiles with leftover sizes are sized to fit their own
         // irregular size in the texture cache.
-
-        // TODO(nical) supporting tiled repeated images isn't implemented yet.
-        // One way to implement this is to have another level of decomposition on top of this one,
-        // and generate a set of image primitive per repetition just like we have a primitive
-        // per tile here.
         //
         // For the case where we don't tile along an axis, we can still perform the repetition in
         // the shader (for this particular axis), and it is worth special-casing for this to avoid
         // generating many primitives.
         // This can happen with very tall and thin images used as a repeating background.
         // Apparently web authors do that...
 
-        let mut stretch_size = info.stretch_size;
-
         let mut repeat_x = false;
         let mut repeat_y = false;
 
-        if stretch_size.width < item.rect.size.width {
-            if image_size.width < tile_size {
-                // we don't actually tile in this dimmension so repeating can be done in the shader.
-                repeat_x = true;
-            } else {
-                println!("Unimplemented! repeating a tiled image (x axis)");
-                stretch_size.width = item.rect.size.width;
-            }
+        if info.stretch_size.width < item_rect.size.width {
+            // If this assert blows up it means we haven't properly decomposed the image in decompose_image_row.
+            debug_assert!(image_size.width <= tile_size);
+            // we don't actually tile in this dimmension so repeating can be done in the shader.
+            repeat_x = true;
         }
 
-        if stretch_size.height < item.rect.size.height {
-                // we don't actually tile in this dimmension so repeating can be done in the shader.
-            if image_size.height < tile_size {
-                repeat_y = true;
-            } else {
-                println!("Unimplemented! repeating a tiled image (y axis)");
-                stretch_size.height = item.rect.size.height;
-            }
+        if info.stretch_size.height < item_rect.size.height {
+            // If this assert blows up it means we haven't properly decomposed the image in decompose_image.
+            debug_assert!(image_size.height <= tile_size);
+            // we don't actually tile in this dimmension so repeating can be done in the shader.
+            repeat_y = true;
         }
 
         let tile_size_f32 = tile_size as f32;
 
         // Note: this rounds down so it excludes the partially filled tiles on the right and
         // bottom edges (we handle them separately below).
         let num_tiles_x = (image_size.width / tile_size) as u16;
         let num_tiles_y = (image_size.height / tile_size) as u16;
 
         // Ratio between (image space) tile size and image size.
         let img_dw = tile_size_f32 / (image_size.width as f32);
         let img_dh = tile_size_f32 / (image_size.height as f32);
 
         // Strected size of the tile in layout space.
         let stretched_tile_size = LayerSize::new(
-            img_dw * stretch_size.width,
-            img_dh * stretch_size.height
+            img_dw * info.stretch_size.width,
+            img_dh * info.stretch_size.height
         );
 
         // The size in pixels of the tiles on the right and bottom edges, smaller
         // than the regular tile size if the image is not a multiple of the tile size.
         // Zero means the image size is a multiple of the tile size.
         let leftover = DeviceUintSize::new(image_size.width % tile_size, image_size.height % tile_size);
 
         for ty in 0..num_tiles_y {
             for tx in 0..num_tiles_x {
                 self.add_tile_primitive(scroll_layer_id,
                                         context,
-                                        item,
+                                        item_rect,
+                                        item_clip,
                                         info,
                                         TileOffset::new(tx, ty),
                                         stretched_tile_size,
                                         1.0, 1.0,
                                         repeat_x, repeat_y);
             }
             if leftover.width != 0 {
                 // Tiles on the right edge that are smaller than the tile size.
                 self.add_tile_primitive(scroll_layer_id,
                                         context,
-                                        item,
+                                        item_rect,
+                                        item_clip,
                                         info,
                                         TileOffset::new(num_tiles_x, ty),
                                         stretched_tile_size,
                                         (leftover.width as f32) / tile_size_f32,
                                         1.0,
                                         repeat_x, repeat_y);
             }
         }
 
         if leftover.height != 0 {
             for tx in 0..num_tiles_x {
                 // Tiles on the bottom edge that are smaller than the tile size.
                 self.add_tile_primitive(scroll_layer_id,
                                         context,
-                                        item,
+                                        item_rect,
+                                        item_clip,
                                         info,
                                         TileOffset::new(tx, num_tiles_y),
                                         stretched_tile_size,
                                         1.0,
                                         (leftover.height as f32) / tile_size_f32,
                                         repeat_x,
                                         repeat_y);
             }
 
             if leftover.width != 0 {
                 // Finally, the bottom-right tile with a "leftover" size.
                 self.add_tile_primitive(scroll_layer_id,
                                         context,
-                                        item,
+                                        item_rect,
+                                        item_clip,
                                         info,
                                         TileOffset::new(num_tiles_x, num_tiles_y),
                                         stretched_tile_size,
                                         (leftover.width as f32) / tile_size_f32,
                                         (leftover.height as f32) / tile_size_f32,
                                         repeat_x,
                                         repeat_y);
             }
         }
     }
 
     fn add_tile_primitive(&mut self,
                           scroll_layer_id: ScrollLayerId,
                           context: &mut FlattenContext,
-                          item: &DisplayItem,
+                          item_rect: &LayerRect,
+                          item_clip: &ClipRegion,
                           info: &ImageDisplayItem,
                           tile_offset: TileOffset,
                           stretched_tile_size: LayerSize,
                           tile_ratio_width: f32,
                           tile_ratio_height: f32,
                           repeat_x: bool,
                           repeat_y: bool) {
         // If the the image is tiled along a given axis, we can't have the shader compute
@@ -852,54 +904,55 @@ impl Frame {
         // See the repeat_x/y code below.
 
         let stretched_size = LayerSize::new(
             stretched_tile_size.width * tile_ratio_width,
             stretched_tile_size.height * tile_ratio_height,
         );
 
         let mut prim_rect = LayerRect::new(
-            item.rect.origin + LayerPoint::new(
+            item_rect.origin + LayerPoint::new(
                 tile_offset.x as f32 * stretched_tile_size.width,
                 tile_offset.y as f32 * stretched_tile_size.height,
             ),
             stretched_size,
         );
 
         if repeat_x {
             assert_eq!(tile_offset.x, 0);
-            prim_rect.size.width = item.rect.size.width;
+            prim_rect.size.width = item_rect.size.width;
         }
 
         if repeat_y {
             assert_eq!(tile_offset.y, 0);
-            prim_rect.size.height = item.rect.size.height;
+            prim_rect.size.height = item_rect.size.height;
         }
 
         // Fix up the primitive's rect if it overflows the original item rect.
-        if let Some(prim_rect) = prim_rect.intersection(&item.rect) {
+        if let Some(prim_rect) = prim_rect.intersection(&item_rect) {
             context.builder.add_image(scroll_layer_id,
                                       prim_rect,
-                                      &item.clip,
+                                      &item_clip,
                                       &stretched_size,
                                       &info.tile_spacing,
                                       None,
                                       info.image_key,
                                       info.image_rendering,
                                       Some(tile_offset));
         }
     }
 
     pub fn build(&mut self,
                  resource_cache: &mut ResourceCache,
                  auxiliary_lists_map: &AuxiliaryListsMap,
                  device_pixel_ratio: f32,
+                 pan: LayerPoint,
                  texture_cache_profile: &mut TextureCacheProfileCounters)
                  -> RendererFrame {
-        self.clip_scroll_tree.update_all_node_transforms();
+        self.clip_scroll_tree.update_all_node_transforms(pan);
         let frame = self.build_frame(resource_cache,
                                      auxiliary_lists_map,
                                      device_pixel_ratio,
                                      texture_cache_profile);
         resource_cache.expire_old_resources(self.id);
         frame
     }
 
@@ -908,17 +961,17 @@ impl Frame {
                    auxiliary_lists_map: &AuxiliaryListsMap,
                    device_pixel_ratio: f32,
                    texture_cache_profile: &mut TextureCacheProfileCounters)
                    -> RendererFrame {
         let mut frame_builder = self.frame_builder.take();
         let frame = frame_builder.as_mut().map(|builder|
             builder.build(resource_cache,
                           self.id,
-                          &self.clip_scroll_tree,
+                          &mut self.clip_scroll_tree,
                           auxiliary_lists_map,
                           device_pixel_ratio,
                           texture_cache_profile)
         );
         self.frame_builder = frame_builder;
 
         let nodes_bouncing_back = self.clip_scroll_tree.collect_nodes_bouncing_back();
         RendererFrame::new(self.pipeline_epoch_map.clone(), nodes_bouncing_back, frame)
--- a/gfx/webrender/src/frame_builder.rs
+++ b/gfx/webrender/src/frame_builder.rs
@@ -13,30 +13,33 @@ use prim_store::{GradientPrimitiveCpu, G
 use prim_store::{ImagePrimitiveKind, PrimitiveContainer, PrimitiveGeometry, PrimitiveIndex};
 use prim_store::{PrimitiveStore, RadialGradientPrimitiveCpu, RadialGradientPrimitiveGpu};
 use prim_store::{RectanglePrimitive, TextRunPrimitiveCpu, TextRunPrimitiveGpu};
 use prim_store::{TexelRect, YuvImagePrimitiveCpu, YuvImagePrimitiveGpu};
 use profiler::{FrameProfileCounters, TextureCacheProfileCounters};
 use render_task::{AlphaRenderItem, MaskCacheKey, MaskResult, RenderTask, RenderTaskIndex};
 use render_task::RenderTaskLocation;
 use resource_cache::ResourceCache;
+use clip_scroll_node::{ClipInfo, ClipScrollNode, NodeType};
 use clip_scroll_tree::ClipScrollTree;
 use std::{cmp, f32, i32, mem, usize};
+use euclid::SideOffsets2D;
+use tiling::StackingContextIndex;
 use tiling::{AuxiliaryListsMap, ClipScrollGroup, ClipScrollGroupIndex, CompositeOps, Frame};
 use tiling::{PackedLayer, PackedLayerIndex, PrimitiveFlags, PrimitiveRunCmd, RenderPass};
-use tiling::{RenderTargetContext, RenderTaskCollection, ScrollbarPrimitive, ScrollLayer};
-use tiling::{ScrollLayerIndex, StackingContext, StackingContextIndex};
+use tiling::{RenderTargetContext, RenderTaskCollection, ScrollbarPrimitive, StackingContext};
 use util::{self, pack_as_float, rect_from_points_f, subtract_rect};
 use util::{RectHelpers, TransformedRectKind};
 use webrender_traits::{BorderDetails, BorderDisplayItem, BorderSide, BorderStyle};
-use webrender_traits::{BoxShadowClipMode, ClipRegion, ColorF, DeviceIntPoint};
-use webrender_traits::{DeviceIntRect, DeviceIntSize, DeviceUintSize, ExtendMode, FontKey, TileOffset};
+use webrender_traits::{BoxShadowClipMode, ClipRegion, ColorF, DeviceIntPoint, DeviceIntRect};
+use webrender_traits::{DeviceIntSize, DeviceUintRect, DeviceUintSize, ExtendMode, FontKey};
 use webrender_traits::{FontRenderMode, GlyphOptions, ImageKey, ImageRendering, ItemRange};
-use webrender_traits::{LayerPoint, LayerRect, LayerSize, PipelineId, RepeatMode, ScrollLayerId};
-use webrender_traits::{WebGLContextId, YuvColorSpace};
+use webrender_traits::{LayerPoint, LayerRect, LayerSize, LayerToScrollTransform, PipelineId};
+use webrender_traits::{RepeatMode, ScrollLayerId, ServoScrollRootId, TileOffset, WebGLContextId};
+use webrender_traits::YuvColorSpace;
 
 #[derive(Debug, Clone)]
 struct ImageBorderSegment {
     geom_rect: LayerRect,
     sub_rect: TexelRect,
     stretch_size: LayerSize,
     tile_spacing: LayerSize,
 }
@@ -104,60 +107,58 @@ impl FrameBuilderConfig {
 pub struct FrameBuilder {
     screen_size: DeviceUintSize,
     background_color: Option<ColorF>,
     prim_store: PrimitiveStore,
     cmds: Vec<PrimitiveRunCmd>,
     config: FrameBuilderConfig,
 
     stacking_context_store: Vec<StackingContext>,
-    scroll_layer_store: Vec<ScrollLayer>,
     clip_scroll_group_store: Vec<ClipScrollGroup>,
     packed_layers: Vec<PackedLayer>,
 
     scrollbar_prims: Vec<ScrollbarPrimitive>,
 
     /// A stack of scroll nodes used during display list processing to properly
     /// parent new scroll nodes.
-    clip_scroll_node_stack: Vec<ScrollLayerIndex>,
+    reference_frame_stack: Vec<ScrollLayerId>,
 
     /// A stack of stacking contexts used for creating ClipScrollGroups as
     /// primitives are added to the frame.
     stacking_context_stack: Vec<StackingContextIndex>,
 }
 
 impl FrameBuilder {
     pub fn new(screen_size: DeviceUintSize,
                background_color: Option<ColorF>,
                config: FrameBuilderConfig) -> FrameBuilder {
         FrameBuilder {
             screen_size: screen_size,
             background_color: background_color,
             stacking_context_store: Vec::new(),
-            scroll_layer_store: Vec::new(),
             clip_scroll_group_store: Vec::new(),
             prim_store: PrimitiveStore::new(),
             cmds: Vec::new(),
             packed_layers: Vec::new(),
             scrollbar_prims: Vec::new(),
             config: config,
-            clip_scroll_node_stack: Vec::new(),
+            reference_frame_stack: Vec::new(),
             stacking_context_stack: Vec::new(),
         }
     }
 
     fn add_primitive(&mut self,
                      scroll_layer_id: ScrollLayerId,
                      rect: &LayerRect,
                      clip_region: &ClipRegion,
                      container: PrimitiveContainer)
                      -> PrimitiveIndex {
         let stacking_context_index = *self.stacking_context_stack.last().unwrap();
         if !self.stacking_context_store[stacking_context_index.0]
-                 .has_clip_scroll_group(scroll_layer_id) {
+                .has_clip_scroll_group(scroll_layer_id) {
             let group_index = self.create_clip_scroll_group(stacking_context_index,
                                                             scroll_layer_id);
             let stacking_context = &mut self.stacking_context_store[stacking_context_index.0];
             stacking_context.clip_scroll_groups.push(group_index);
         }
 
         let geometry = PrimitiveGeometry {
             local_rect: *rect,
@@ -173,29 +174,28 @@ impl FrameBuilder {
                                            &mut self.prim_store.gpu_data32);
 
         let prim_index = self.prim_store.add_primitive(geometry,
                                                        Box::new(clip_source),
                                                        clip_info,
                                                        container);
 
         match self.cmds.last_mut().unwrap() {
-            &mut PrimitiveRunCmd::PrimitiveRun(_run_prim_index, ref mut count, _) => {
-                debug_assert!(_run_prim_index.0 + *count == prim_index.0);
-                *count += 1;
-                return prim_index;
+            &mut PrimitiveRunCmd::PrimitiveRun(_run_prim_index, ref mut count, run_layer_id)
+                if run_layer_id == scroll_layer_id => {
+                    debug_assert!(_run_prim_index.0 + *count == prim_index.0);
+                    *count += 1;
+                    return prim_index;
             }
+            &mut PrimitiveRunCmd::PrimitiveRun(..) |
             &mut PrimitiveRunCmd::PushStackingContext(..) |
-            &mut PrimitiveRunCmd::PopStackingContext |
-            &mut PrimitiveRunCmd::PushScrollLayer(..) |
-            &mut PrimitiveRunCmd::PopScrollLayer => {}
+            &mut PrimitiveRunCmd::PopStackingContext => {}
         }
 
         self.cmds.push(PrimitiveRunCmd::PrimitiveRun(prim_index, 1, scroll_layer_id));
-
         prim_index
     }
 
     pub fn create_clip_scroll_group(&mut self,
                                     stacking_context_index: StackingContextIndex,
                                     scroll_layer_id: ScrollLayerId)
                                     -> ClipScrollGroupIndex {
         let packed_layer_index = PackedLayerIndex(self.packed_layers.len());
@@ -238,45 +238,113 @@ impl FrameBuilder {
         self.stacking_context_stack.push(stacking_context_index);
     }
 
     pub fn pop_stacking_context(&mut self) {
         self.cmds.push(PrimitiveRunCmd::PopStackingContext);
         self.stacking_context_stack.pop();
     }
 
-    pub fn push_clip_scroll_node(&mut self,
-                                 scroll_layer_id: ScrollLayerId,
-                                 clip_region: &ClipRegion) {
-        let scroll_layer_index = ScrollLayerIndex(self.scroll_layer_store.len());
-        let parent_index = *self.clip_scroll_node_stack.last().unwrap_or(&scroll_layer_index);
-        self.clip_scroll_node_stack.push(scroll_layer_index);
+    pub fn push_reference_frame(&mut self,
+                                parent_id: Option<ScrollLayerId>,
+                                pipeline_id: PipelineId,
+                                rect: &LayerRect,
+                                transform: &LayerToScrollTransform,
+                                clip_scroll_tree: &mut ClipScrollTree)
+                                -> ScrollLayerId {
+        let new_id = clip_scroll_tree.add_reference_frame(rect, transform, pipeline_id, parent_id);
+        self.reference_frame_stack.push(new_id);
+        new_id
+    }
 
-        let packed_layer_index = PackedLayerIndex(self.packed_layers.len());
+    pub fn current_reference_frame_id(&self) -> ScrollLayerId {
+        *self.reference_frame_stack.last().unwrap()
+    }
 
-        let clip_source = ClipSource::Region(clip_region.clone());
-        let clip_info = MaskCacheInfo::new(&clip_source,
-                                           true, // needs an extra clip for the clip rectangle
-                                           &mut self.prim_store.gpu_data32);
+    pub fn setup_viewport_offset(&mut self,
+                                 window_size: DeviceUintSize,
+                                 inner_rect: DeviceUintRect,
+                                 device_pixel_ratio: f32,
+                                 clip_scroll_tree: &mut ClipScrollTree) {
+        let inner_origin = inner_rect.origin.to_f32();
+        let viewport_offset = LayerPoint::new((inner_origin.x / device_pixel_ratio).round(),
+                                              (inner_origin.y / device_pixel_ratio).round());
+        let outer_size = window_size.to_f32();
+        let outer_size = LayerSize::new((outer_size.width / device_pixel_ratio).round(),
+                                        (outer_size.height / device_pixel_ratio).round());
+        let clip_size = LayerSize::new(outer_size.width + 2.0 * viewport_offset.x,
+                                       outer_size.height + 2.0 * viewport_offset.y);
 
-        self.scroll_layer_store.push(ScrollLayer {
-            scroll_layer_id: scroll_layer_id,
-            parent_index: parent_index,
-            clip_source: clip_source,
-            clip_cache_info: clip_info,
-            xf_rect: None,
-            packed_layer_index: packed_layer_index,
-        });
-        self.packed_layers.push(PackedLayer::empty());
-        self.cmds.push(PrimitiveRunCmd::PushScrollLayer(scroll_layer_index));
+        let viewport_clip = LayerRect::new(LayerPoint::new(-viewport_offset.x, -viewport_offset.y),
+                                           LayerSize::new(clip_size.width, clip_size.height));
+
+        let root_id = clip_scroll_tree.root_reference_frame_id();
+        if let Some(root_node) = clip_scroll_tree.nodes.get_mut(&root_id) {
+            if let NodeType::ReferenceFrame(ref mut transform) = root_node.node_type {
+                *transform = LayerToScrollTransform::create_translation(viewport_offset.x,
+                                                                        viewport_offset.y,
+                                                                        0.0);
+            }
+            root_node.local_clip_rect = viewport_clip;
+        }
+
+        let scroll_layer_id = clip_scroll_tree.topmost_scroll_layer_id();
+        if let Some(root_node) = clip_scroll_tree.nodes.get_mut(&scroll_layer_id) {
+            root_node.local_clip_rect = viewport_clip;
+        }
     }
 
-    pub fn pop_clip_scroll_node(&mut self) {
-        self.cmds.push(PrimitiveRunCmd::PopScrollLayer);
-        self.clip_scroll_node_stack.pop();
+    pub fn push_root(&mut self,
+                     pipeline_id: PipelineId,
+                     viewport_size: &LayerSize,
+                     content_size: &LayerSize,
+                     clip_scroll_tree: &mut ClipScrollTree)
+                     -> ScrollLayerId {
+        let viewport_rect = LayerRect::new(LayerPoint::zero(), *viewport_size);
+        let identity = &LayerToScrollTransform::identity();
+        self.push_reference_frame(None, pipeline_id, &viewport_rect, identity, clip_scroll_tree);
+
+        let topmost_scroll_layer_id = ScrollLayerId::root_scroll_layer(pipeline_id);
+        clip_scroll_tree.topmost_scroll_layer_id = topmost_scroll_layer_id;
+        self.add_clip_scroll_node(topmost_scroll_layer_id,
+                                   clip_scroll_tree.root_reference_frame_id,
+                                   pipeline_id,
+                                   &viewport_rect,
+                                   content_size,
+                                   Some(ServoScrollRootId(0)),
+                                   &ClipRegion::simple(&viewport_rect),
+                                   clip_scroll_tree);
+        topmost_scroll_layer_id
+    }
+
+    pub fn add_clip_scroll_node(&mut self,
+                                new_node_id: ScrollLayerId,
+                                parent_id: ScrollLayerId,
+                                pipeline_id: PipelineId,
+                                local_viewport_rect: &LayerRect,
+                                content_size: &LayerSize,
+                                scroll_root_id: Option<ServoScrollRootId>,
+                                clip_region: &ClipRegion,
+                                clip_scroll_tree: &mut ClipScrollTree) {
+        let clip_info = ClipInfo::new(clip_region,
+                                      &mut self.prim_store.gpu_data32,
+                                      PackedLayerIndex(self.packed_layers.len()),
+                                      scroll_root_id);
+        let node = ClipScrollNode::new(pipeline_id,
+                                       parent_id,
+                                       local_viewport_rect,
+                                       *content_size,
+                                       clip_info);
+
+        clip_scroll_tree.add_node(node, new_node_id);
+        self.packed_layers.push(PackedLayer::empty());
+    }
+
+    pub fn pop_reference_frame(&mut self) {
+        self.reference_frame_stack.pop();
     }
 
     pub fn add_solid_rectangle(&mut self,
                                scroll_layer_id: ScrollLayerId,
                                rect: &LayerRect,
                                clip_region: &ClipRegion,
                                color: &ColorF,
                                flags: PrimitiveFlags) {
@@ -325,16 +393,58 @@ impl FrameBuilder {
         }
     }
 
     pub fn add_border(&mut self,
                       scroll_layer_id: ScrollLayerId,
                       rect: LayerRect,
                       clip_region: &ClipRegion,
                       border_item: &BorderDisplayItem) {
+        let create_segments = |outset: SideOffsets2D<f32>| {
+            // Calculate the modified rect as specific by border-image-outset
+            let origin = LayerPoint::new(rect.origin.x - outset.left,
+                                         rect.origin.y - outset.top);
+            let size = LayerSize::new(rect.size.width + outset.left + outset.right,
+                                      rect.size.height + outset.top + outset.bottom);
+            let rect = LayerRect::new(origin, size);
+
+            let tl_outer = LayerPoint::new(rect.origin.x, rect.origin.y);
+            let tl_inner = tl_outer + LayerPoint::new(border_item.widths.left, border_item.widths.top);
+
+            let tr_outer = LayerPoint::new(rect.origin.x + rect.size.width, rect.origin.y);
+            let tr_inner = tr_outer + LayerPoint::new(-border_item.widths.right, border_item.widths.top);
+
+            let bl_outer = LayerPoint::new(rect.origin.x, rect.origin.y + rect.size.height);
+            let bl_inner = bl_outer + LayerPoint::new(border_item.widths.left, -border_item.widths.bottom);
+
+            let br_outer = LayerPoint::new(rect.origin.x + rect.size.width,
+                                           rect.origin.y + rect.size.height);
+            let br_inner = br_outer - LayerPoint::new(border_item.widths.right, border_item.widths.bottom);
+
+            // Build the list of gradient segments
+            vec![
+                // Top left
+                LayerRect::from_floats(tl_outer.x, tl_outer.y, tl_inner.x, tl_inner.y),
+                // Top right
+                LayerRect::from_floats(tr_inner.x, tr_outer.y, tr_outer.x, tr_inner.y),
+                // Bottom right
+                LayerRect::from_floats(br_inner.x, br_inner.y, br_outer.x, br_outer.y),
+                // Bottom left
+                LayerRect::from_floats(bl_outer.x, bl_inner.y, bl_inner.x, bl_outer.y),
+                // Top
+                LayerRect::from_floats(tl_inner.x, tl_outer.y, tr_inner.x, tl_inner.y),
+                // Bottom
+                LayerRect::from_floats(bl_inner.x, bl_inner.y, br_inner.x, bl_outer.y),
+                // Left
+                LayerRect::from_floats(tl_outer.x, tl_inner.y, tl_inner.x, bl_inner.y),
+                // Right
+                LayerRect::from_floats(tr_inner.x, tr_inner.y, br_outer.x, br_inner.y),
+            ]
+        };
+
         match border_item.details {
             BorderDetails::Image(ref border) => {
                 // Calculate the modified rect as specific by border-image-outset
                 let origin = LayerPoint::new(rect.origin.x - border.outset.left,
                                              rect.origin.y - border.outset.top);
                 let size = LayerSize::new(rect.size.width + border.outset.left + border.outset.right,
                                           rect.size.height + border.outset.top + border.outset.bottom);
                 let rect = LayerRect::new(origin, size);
@@ -544,16 +654,40 @@ impl FrameBuilder {
                     ],
                 };
 
                 self.add_primitive(scroll_layer_id,
                                    &rect,
                                    clip_region,
                                    PrimitiveContainer::Border(prim_cpu, prim_gpu));
             }
+            BorderDetails::Gradient(ref border) => {
+                for segment in create_segments(border.outset) {
+                    self.add_gradient(scroll_layer_id,
+                                      segment,
+                                      clip_region,
+                                      border.gradient.start_point,
+                                      border.gradient.end_point,
+                                      border.gradient.stops,
+                                      border.gradient.extend_mode);
+                }
+            }
+            BorderDetails::RadialGradient(ref border) => {
+                for segment in create_segments(border.outset) {
+                    self.add_radial_gradient(scroll_layer_id,
+                                             segment,
+                                             clip_region,
+                                             border.gradient.start_center,
+                                             border.gradient.start_radius,
+                                             border.gradient.end_center,
+                                             border.gradient.end_radius,
+                                             border.gradient.stops,
+                                             border.gradient.extend_mode);
+                }
+            }
         }
     }
 
     pub fn add_gradient(&mut self,
                         scroll_layer_id: ScrollLayerId,
                         rect: LayerRect,
                         clip_region: &ClipRegion,
                         start_point: LayerPoint,
@@ -892,37 +1026,34 @@ impl FrameBuilder {
                          rect: LayerRect,
                          clip_region: &ClipRegion,
                          y_image_key: ImageKey,
                          u_image_key: ImageKey,
                          v_image_key: ImageKey,
                          color_space: YuvColorSpace) {
 
         let prim_cpu = YuvImagePrimitiveCpu {
-            y_key: y_image_key,
-            u_key: u_image_key,
-            v_key: v_image_key,
-            y_texture_id: SourceTexture::Invalid,
-            u_texture_id: SourceTexture::Invalid,
-            v_texture_id: SourceTexture::Invalid,
+            yuv_key: [y_image_key, u_image_key, v_image_key],
+            yuv_texture_id: [SourceTexture::Invalid, SourceTexture::Invalid, SourceTexture::Invalid],
+            yuv_resource_address: GpuStoreAddress(0),
         };
 
         let prim_gpu = YuvImagePrimitiveGpu::new(rect.size, color_space);
 
         self.add_primitive(scroll_layer_id,
                            &rect,
                            clip_region,
                            PrimitiveContainer::YuvImage(prim_cpu, prim_gpu));
     }
 
     /// Compute the contribution (bounding rectangles, and resources) of layers and their
     /// primitives in screen space.
     fn build_layer_screen_rects_and_cull_layers(&mut self,
                                                 screen_rect: &DeviceIntRect,
-                                                clip_scroll_tree: &ClipScrollTree,
+                                                clip_scroll_tree: &mut ClipScrollTree,
                                                 auxiliary_lists_map: &AuxiliaryListsMap,
                                                 resource_cache: &mut ResourceCache,
                                                 profile_counters: &mut FrameProfileCounters,
                                                 device_pixel_ratio: f32) {
         profile_scope!("cull");
         LayerRectCalculationAndCullingPass::create_and_run(self,
                                                            screen_rect,
                                                            clip_scroll_tree,
@@ -1115,28 +1246,27 @@ impl FrameBuilder {
                             } else {
                                 &mut current_task.as_alpha_batch().opaque_items
                             };
                             items.push(AlphaRenderItem::Primitive(group_index, prim_index, next_z));
                             next_z += 1;
                         }
                     }
                 }
-                PrimitiveRunCmd::PushScrollLayer(_) | PrimitiveRunCmd::PopScrollLayer => { }
             }
         }
 
         debug_assert!(alpha_task_stack.is_empty());
         (current_task, next_task_index.0)
     }
 
     pub fn build(&mut self,
                  resource_cache: &mut ResourceCache,
                  frame_id: FrameId,
-                 clip_scroll_tree: &ClipScrollTree,
+                 clip_scroll_tree: &mut ClipScrollTree,
                  auxiliary_lists_map: &AuxiliaryListsMap,
                  device_pixel_ratio: f32,
                  texture_cache_profile: &mut TextureCacheProfileCounters)
                  -> Frame {
         profile_scope!("build");
 
         let mut profile_counters = FrameProfileCounters::new();
         profile_counters.total_primitives.set(self.prim_store.prim_count());
@@ -1167,19 +1297,21 @@ impl FrameBuilder {
         let (main_render_task, static_render_task_count) = self.build_render_task();
         let mut render_tasks = RenderTaskCollection::new(static_render_task_count);
 
         let mut required_pass_count = 0;
         main_render_task.max_depth(0, &mut required_pass_count);
 
         resource_cache.block_until_all_resources_added(texture_cache_profile);
 
-        for scroll_layer in self.scroll_layer_store.iter() {
-            if let Some(ref clip_info) = scroll_layer.clip_cache_info {
-                self.prim_store.resolve_clip_cache(clip_info, resource_cache);
+        for node in clip_scroll_tree.nodes.values() {
+            if let NodeType::Clip(ref clip_info) = node.node_type {
+                if let Some(ref mask_info) = clip_info.mask_cache_info {
+                    self.prim_store.resolve_clip_cache(mask_info, resource_cache);
+                }
             }
         }
 
         let deferred_resolves = self.prim_store.resolve_primitives(resource_cache,
                                                                    device_pixel_ratio);
 
         let mut passes = Vec::new();
 
@@ -1229,79 +1361,126 @@ impl FrameBuilder {
         }
     }
 
 }
 
 struct LayerRectCalculationAndCullingPass<'a> {
     frame_builder: &'a mut FrameBuilder,
     screen_rect: &'a DeviceIntRect,
-    clip_scroll_tree: &'a ClipScrollTree,
+    clip_scroll_tree: &'a mut ClipScrollTree,
     auxiliary_lists_map: &'a AuxiliaryListsMap,
     resource_cache: &'a mut ResourceCache,
     profile_counters: &'a mut FrameProfileCounters,
     device_pixel_ratio: f32,
     stacking_context_stack: Vec<StackingContextIndex>,
-    scroll_layer_stack: Vec<ScrollLayerIndex>,
 
     /// A cached clip info stack, which should handle the most common situation,
     /// which is that we are using the same clip info stack that we were using
     /// previously.
     current_clip_stack: Vec<(PackedLayerIndex, MaskCacheInfo)>,
 
-    /// The scroll layer that defines the previous scroll layer info stack.
-    current_clip_stack_scroll_layer: Option<ScrollLayerIndex>
+    /// Information about the cached clip stack, which is used to avoid having
+    /// to recalculate it for every primitive.
+    current_clip_info: Option<(ScrollLayerId, DeviceIntRect)>
 }
 
 impl<'a> LayerRectCalculationAndCullingPass<'a> {
     fn create_and_run(frame_builder: &'a mut FrameBuilder,
                       screen_rect: &'a DeviceIntRect,
-                      clip_scroll_tree: &'a ClipScrollTree,
+                      clip_scroll_tree: &'a mut ClipScrollTree,
                       auxiliary_lists_map: &'a AuxiliaryListsMap,
                       resource_cache: &'a mut ResourceCache,
                       profile_counters: &'a mut FrameProfileCounters,
                       device_pixel_ratio: f32) {
 
         let mut pass = LayerRectCalculationAndCullingPass {
             frame_builder: frame_builder,
             screen_rect: screen_rect,
             clip_scroll_tree: clip_scroll_tree,
             auxiliary_lists_map: auxiliary_lists_map,
             resource_cache: resource_cache,
             profile_counters: profile_counters,
             device_pixel_ratio: device_pixel_ratio,
             stacking_context_stack: Vec::new(),
-            scroll_layer_stack: Vec::new(),
             current_clip_stack: Vec::new(),
-            current_clip_stack_scroll_layer: None,
+            current_clip_info: None,
         };
         pass.run();
     }
 
     fn run(&mut self) {
         self.recalculate_clip_scroll_groups();
+        self.recalculate_clip_scroll_nodes();
         self.compute_stacking_context_visibility();
 
         let commands = mem::replace(&mut self.frame_builder.cmds, Vec::new());
         for cmd in &commands {
             match cmd {
                 &PrimitiveRunCmd::PushStackingContext(stacking_context_index) =>
                     self.handle_push_stacking_context(stacking_context_index),
-                &PrimitiveRunCmd::PushScrollLayer(scroll_layer_index) =>
-                    self.handle_push_scroll_layer(scroll_layer_index),
                 &PrimitiveRunCmd::PrimitiveRun(prim_index, prim_count, scroll_layer_id) =>
                     self.handle_primitive_run(prim_index, prim_count, scroll_layer_id),
                 &PrimitiveRunCmd::PopStackingContext => self.handle_pop_stacking_context(),
-                &PrimitiveRunCmd::PopScrollLayer => self.handle_pop_scroll_layer(),
             }
         }
 
         mem::replace(&mut self.frame_builder.cmds, commands);
     }
 
+    fn recalculate_clip_scroll_nodes(&mut self) {
+        for (_, ref mut node) in self.clip_scroll_tree.nodes.iter_mut() {
+            let node_clip_info = match node.node_type {
+                NodeType::Clip(ref mut clip_info) => clip_info,
+                NodeType::ReferenceFrame(_) => continue,
+            };
+
+            let packed_layer_index = node_clip_info.packed_layer_index;
+            let packed_layer = &mut self.frame_builder.packed_layers[packed_layer_index.0];
+
+            // The coordinates of the mask are relative to the origin of the node itself,
+            // so we need to account for that origin in the transformation we assign to
+            // the packed layer.
+            let transform = node.world_viewport_transform
+                                .pre_translated(node.local_viewport_rect.origin.x,
+                                                node.local_viewport_rect.origin.y,
+                                                0.0);
+            packed_layer.set_transform(transform);
+
+            // Meanwhile, the combined viewport rect is relative to the reference frame, so
+            // we move it into the local coordinate system of the node.
+            let local_viewport_rect =
+                node.combined_local_viewport_rect.translate(&-node.local_viewport_rect.origin);
+
+            node_clip_info.xf_rect = packed_layer.set_rect(Some(local_viewport_rect),
+                                                           self.screen_rect,
+                                                           self.device_pixel_ratio);
+
+            let mask_info = match node_clip_info.mask_cache_info {
+                Some(ref mut mask_info) => mask_info,
+                _ => continue,
+            };
+
+            let auxiliary_lists = self.auxiliary_lists_map.get(&node.pipeline_id)
+                                                          .expect("No auxiliary lists?");
+
+            mask_info.update(&node_clip_info.clip_source,
+                             &packed_layer.transform,
+                             &mut self.frame_builder.prim_store.gpu_data32,
+                             self.device_pixel_ratio,
+                             auxiliary_lists);
+
+            if let Some(mask) = node_clip_info.clip_source.image_mask() {
+                // We don't add the image mask for resolution, because
+                // layer masks are resolved later.
+                self.resource_cache.request_image(mask.image, ImageRendering::Auto, None);
+            }
+        }
+    }
+
     fn recalculate_clip_scroll_groups(&mut self) {
         for ref mut group in &mut self.frame_builder.clip_scroll_group_store {
             let stacking_context_index = group.stacking_context_index;
             let stacking_context = &mut self.frame_builder
                                             .stacking_context_store[stacking_context_index.0];
 
             let node = &self.clip_scroll_tree.nodes[&group.scroll_layer_id];
             let packed_layer = &mut self.frame_builder.packed_layers[group.packed_layer_index.0];
@@ -1364,103 +1543,62 @@ impl<'a> LayerRectCalculationAndCullingP
             parent.bounding_rect = parent.bounding_rect.union(&bounding_rect);
 
             // The previous compute_stacking_context_visibility pass did not take into
             // account visibility of children, so we do that now.
             parent.is_visible = parent.is_visible || is_visible;
         }
     }
 
-    fn handle_push_scroll_layer(&mut self, scroll_layer_index: ScrollLayerIndex) {
-        self.scroll_layer_stack.push(scroll_layer_index);
-
-        let scroll_layer = &mut self.frame_builder.scroll_layer_store[scroll_layer_index.0];
-        let node = &self.clip_scroll_tree.nodes[&scroll_layer.scroll_layer_id];
-
-        let packed_layer_index = scroll_layer.packed_layer_index;
-        let packed_layer = &mut self.frame_builder.packed_layers[packed_layer_index.0];
-
-        // The coordinates of the mask are relative to the origin of the node itself,
-        // so we need to account for that origin in the transformation we assign to
-        // the packed layer.
-        let transform = node.world_viewport_transform
-                            .pre_translated(node.local_viewport_rect.origin.x,
-                                            node.local_viewport_rect.origin.y,
-                                            0.0);
-        packed_layer.set_transform(transform);
-
-        // Meanwhile, the combined viewport rect is relative to the reference frame, so
-        // we move it into the local coordinate system of the node.
-        let local_viewport_rect =
-            node.combined_local_viewport_rect.translate(&-node.local_viewport_rect.origin);
-
-        scroll_layer.xf_rect = packed_layer.set_rect(Some(local_viewport_rect),
-                                                     self.screen_rect,
-                                                     self.device_pixel_ratio);
-
-        let clip_info = match scroll_layer.clip_cache_info {
-            Some(ref mut clip_info) => clip_info,
-            None => return,
-        };
-
-        let pipeline_id = scroll_layer.scroll_layer_id.pipeline_id;
-        let auxiliary_lists = self.auxiliary_lists_map.get(&pipeline_id)
-                                                       .expect("No auxiliary lists?");
-        clip_info.update(&scroll_layer.clip_source,
-                         &packed_layer.transform,
-                         &mut self.frame_builder.prim_store.gpu_data32,
-                         self.device_pixel_ratio,
-                         auxiliary_lists);
-
-        if let Some(mask) = scroll_layer.clip_source.image_mask() {
-            // We don't add the image mask for resolution, because layer masks are resolved later.
-            self.resource_cache.request_image(mask.image, ImageRendering::Auto, None);
-        }
-    }
-
     fn handle_push_stacking_context(&mut self, stacking_context_index: StackingContextIndex) {
         self.stacking_context_stack.push(stacking_context_index);
 
         // Reset bounding rect to zero. We will calculate it as we collect primitives
         // from various scroll layers. In handle_pop_stacking_context , we use this to
         // calculate the device bounding rect. In the future, we could cache this during
         // the initial adding of items for the common case (where there is only a single
         // scroll layer for items in a stacking context).
         let stacking_context = &mut self.frame_builder
                                         .stacking_context_store[stacking_context_index.0];
         stacking_context.bounding_rect = DeviceIntRect::zero();
     }
 
-    fn rebuild_clip_info_stack_if_necessary(&mut self, mut scroll_layer_index: ScrollLayerIndex) {
-        if let Some(previous_scroll_layer) = self.current_clip_stack_scroll_layer {
-            if previous_scroll_layer == scroll_layer_index {
-                return;
+    fn rebuild_clip_info_stack_if_necessary(&mut self, id: ScrollLayerId) -> DeviceIntRect {
+        if let Some((current_scroll_id, bounding_rect)) = self.current_clip_info {
+            if current_scroll_id == id {
+                return bounding_rect;
             }
         }
 
         // TODO(mrobinson): If we notice that this process is expensive, we can special-case
         // more common situations, such as moving from a child or a parent.
-        self.current_clip_stack_scroll_layer = Some(scroll_layer_index);
         self.current_clip_stack.clear();
-        loop {
-            let scroll_layer = &self.frame_builder.scroll_layer_store[scroll_layer_index.0];
-            match scroll_layer.clip_cache_info {
-                Some(ref clip_info) if clip_info.is_masking() =>
-                    self.current_clip_stack.push((scroll_layer.packed_layer_index,
-                                                  clip_info.clone())),
-                _ => {},
+        let mut bounding_rect = None;
+
+        let mut current_id = Some(id);
+        while let Some(id) = current_id {
+            let node = &self.clip_scroll_tree.nodes.get(&id).unwrap();
+            current_id = node.parent;
+
+            let clip_info = match node.node_type {
+                NodeType::Clip(ref clip) if clip.is_masking() => clip,
+                _ => continue,
             };
 
-            if scroll_layer.parent_index == scroll_layer_index {
-                break;
+            if bounding_rect.is_none() {
+                bounding_rect = Some(clip_info.xf_rect.as_ref().unwrap().bounding_rect);
             }
-            scroll_layer_index = scroll_layer.parent_index;
+            self.current_clip_stack.push((clip_info.packed_layer_index,
+                                          clip_info.mask_cache_info.clone().unwrap()))
         }
+        self.current_clip_stack.reverse();
 
-        self.current_clip_stack.reverse();
+        let bounding_rect = bounding_rect.unwrap_or_else(DeviceIntRect::zero);
+        self.current_clip_info = Some((id, bounding_rect));
+        bounding_rect
     }
 
     fn handle_primitive_run(&mut self,
                             prim_index: PrimitiveIndex,
                             prim_count: usize,
                             scroll_layer_id: ScrollLayerId) {
         let stacking_context_index = *self.stacking_context_stack.last().unwrap();
         let (packed_layer_index, pipeline_id) = {
@@ -1471,22 +1609,20 @@ impl<'a> LayerRectCalculationAndCullingP
                 return;
             }
 
             let group_index = stacking_context.clip_scroll_group(scroll_layer_id);
             let clip_scroll_group = &self.frame_builder.clip_scroll_group_store[group_index.0];
             (clip_scroll_group.packed_layer_index, stacking_context.pipeline_id)
         };
 
-        let scroll_layer_index = *self.scroll_layer_stack.last().unwrap();
-        self.rebuild_clip_info_stack_if_necessary(scroll_layer_index);
+        let node_clip_bounds = self.rebuild_clip_info_stack_if_necessary(scroll_layer_id);
 
         let stacking_context =
             &mut self.frame_builder.stacking_context_store[stacking_context_index.0];
-
         let packed_layer = &self.frame_builder.packed_layers[packed_layer_index.0];
         let auxiliary_lists = self.auxiliary_lists_map.get(&pipeline_id)
                                                       .expect("No auxiliary lists?");
 
         for i in 0..prim_count {
             let prim_index = PrimitiveIndex(prim_index.0 + i);
             if self.frame_builder.prim_store.build_bounding_rect(prim_index,
                                                                  self.screen_rect,
@@ -1527,22 +1663,17 @@ impl<'a> LayerRectCalculationAndCullingP
                 // Try to create a mask if we may need to.
                 if !self.current_clip_stack.is_empty() {
                     // If the primitive doesn't have a specific clip, key the task ID off the
                     // stacking context. This means that two primitives which are only clipped
                     // by the stacking context stack can share clip masks during render task
                     // assignment to targets.
                     let (mask_key, mask_rect) = match prim_clip_info {
                         Some(..) => (MaskCacheKey::Primitive(prim_index), prim_bounding_rect),
-                        None => {
-                            let scroll_layer =
-                                &self.frame_builder.scroll_layer_store[scroll_layer_index.0];
-                            (MaskCacheKey::ScrollLayer(scroll_layer_index),
-                             scroll_layer.xf_rect.as_ref().unwrap().bounding_rect)
-                        }
+                        None => (MaskCacheKey::ScrollLayer(scroll_layer_id), node_clip_bounds)
                     };
                     let mask_opt =
                         RenderTask::new_mask(mask_rect, mask_key, &self.current_clip_stack);
                     match mask_opt {
                         MaskResult::Outside => { // Primitive is completely clipped out.
                             prim_metadata.clip_task = None;
                             self.frame_builder.prim_store.cpu_bounding_rects[prim_index.0] = None;
                             visible = false;
@@ -1556,13 +1687,9 @@ impl<'a> LayerRectCalculationAndCullingP
                 }
 
                 if visible {
                     self.profile_counters.visible_primitives.inc();
                 }
             }
         }
     }
-
-    fn handle_pop_scroll_layer(&mut self) {
-        self.scroll_layer_stack.pop();
-    }
 }
--- a/gfx/webrender/src/gpu_store.rs
+++ b/gfx/webrender/src/gpu_store.rs
@@ -1,20 +1,30 @@
 /* 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 device::TextureFilter;
 use std::marker::PhantomData;
 use std::mem;
+use std::ops::Add;
 use webrender_traits::ImageFormat;
 
 #[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
 pub struct GpuStoreAddress(pub i32);
 
+
+impl Add<i32> for GpuStoreAddress {
+    type Output = GpuStoreAddress;
+
+    fn add(self, other: i32) -> GpuStoreAddress {
+        GpuStoreAddress(self.0 + other)
+    }
+}
+
 pub trait GpuStoreLayout {
     fn image_format() -> ImageFormat;
 
     fn texture_width<T>() -> usize;
 
     fn texture_filter() -> TextureFilter;
 
     fn texel_size() -> usize {
--- a/gfx/webrender/src/platform/macos/font.rs
+++ b/gfx/webrender/src/platform/macos/font.rs
@@ -144,16 +144,18 @@ impl FontContext {
             return
         }
 
         self.cg_fonts.insert((*font_key).clone(), native_font_handle);
     }
 
     pub fn delete_font(&mut self, font_key: &FontKey) {
         if let Some(cg_font) = self.cg_fonts.remove(font_key) {
+            // Unstable Rust has a retain() method on HashMap that will
+            // let us do this in-place. https://github.com/rust-lang/rust/issues/36648
             let ct_font_keys = self.ct_fonts.keys()
                                             .filter(|k| k.0 == *font_key)
                                             .cloned()
                                             .collect::<Vec<_>>();
             for ct_font_key in ct_font_keys {
                 self.ct_fonts.remove(&ct_font_key);
             }
         }
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -4,17 +4,17 @@
 
 use app_units::Au;
 use euclid::{Point2D, Size2D};
 use gpu_store::GpuStoreAddress;
 use internal_types::{SourceTexture, PackedTexel};
 use mask_cache::{ClipSource, MaskCacheInfo};
 use renderer::{VertexDataStore, GradientDataStore};
 use render_task::{RenderTask, RenderTaskLocation};
-use resource_cache::{ImageProperties, ResourceCache};
+use resource_cache::{CacheItem, ImageProperties, ResourceCache};
 use std::mem;
 use std::usize;
 use util::TransformedRect;
 use webrender_traits::{AuxiliaryLists, ColorF, ImageKey, ImageRendering, YuvColorSpace};
 use webrender_traits::{ClipRegion, ComplexClipRegion, ItemRange, GlyphKey};
 use webrender_traits::{FontKey, FontRenderMode, WebGLContextId};
 use webrender_traits::{device_length, DeviceIntRect, DeviceIntSize};
 use webrender_traits::{DeviceRect, DevicePoint, DeviceSize};
@@ -152,47 +152,35 @@ pub struct ImagePrimitiveCpu {
 #[repr(C)]
 pub struct ImagePrimitiveGpu {
     pub stretch_size: LayerSize,
     pub tile_spacing: LayerSize,
 }
 
 #[derive(Debug)]
 pub struct YuvImagePrimitiveCpu {
-    pub y_key: ImageKey,
-    pub u_key: ImageKey,
-    pub v_key: ImageKey,
-    pub y_texture_id: SourceTexture,
-    pub u_texture_id: SourceTexture,
-    pub v_texture_id: SourceTexture,
+    pub yuv_key: [ImageKey; 3],
+    pub yuv_texture_id: [SourceTexture; 3],
+
+    // The first address of yuv resource_address. Use "yuv_resource_address + N-th" to get the N-th channel data.
+    // e.g. yuv_resource_address + 0 => y channel resource_address
+    pub yuv_resource_address: GpuStoreAddress,
 }
 
 #[derive(Debug, Clone)]
 #[repr(C)]
 pub struct YuvImagePrimitiveGpu {
-    pub y_uv0: DevicePoint,
-    pub y_uv1: DevicePoint,
-    pub u_uv0: DevicePoint,
-    pub u_uv1: DevicePoint,
-    pub v_uv0: DevicePoint,
-    pub v_uv1: DevicePoint,
     pub size: LayerSize,
     pub color_space: f32,
     pub padding: f32,
 }
 
 impl YuvImagePrimitiveGpu {
     pub fn new(size: LayerSize, color_space: YuvColorSpace) -> Self {
         YuvImagePrimitiveGpu {
-            y_uv0: DevicePoint::zero(),
-            y_uv1: DevicePoint::zero(),
-            u_uv0: DevicePoint::zero(),
-            u_uv1: DevicePoint::zero(),
-            v_uv0: DevicePoint::zero(),
-            v_uv1: DevicePoint::zero(),
             size: size,
             color_space: color_space as u32 as f32,
             padding: 0.0,
         }
     }
 }
 
 #[derive(Debug, Clone)]
@@ -671,18 +659,20 @@ impl PrimitiveStore {
                     gpu_data_count: 0,
                     render_task: None,
                     clip_task: None,
                 };
 
                 self.cpu_images.push(image_cpu);
                 metadata
             }
-            PrimitiveContainer::YuvImage(image_cpu, image_gpu) => {
-                let gpu_address = self.gpu_data64.push(image_gpu);
+            PrimitiveContainer::YuvImage(mut image_cpu, image_gpu) => {
+                image_cpu.yuv_resource_address = self.gpu_resource_rects.alloc(3);
+
+                let gpu_address = self.gpu_data16.push(image_gpu);
 
                 let metadata = PrimitiveMetadata {
                     is_opaque: true,
                     clip_source: clip_source,
                     clip_cache_info: clip_info,
                     prim_kind: PrimitiveKind::YuvImage,
                     cpu_prim_index: SpecificPrimitiveIndex(self.cpu_yuv_images.len()),
                     gpu_prim_index: gpu_address,
@@ -850,16 +840,45 @@ impl PrimitiveStore {
     }
 
     pub fn resolve_clip_cache(&mut self,
                               clip_info: &MaskCacheInfo,
                               resource_cache: &ResourceCache) {
         Self::resolve_clip_cache_internal(&mut self.gpu_data32, clip_info, resource_cache)
     }
 
+    fn resolve_image(resource_cache: &ResourceCache,
+                     deferred_resolves: &mut Vec<DeferredResolve>,
+                     image_key: ImageKey,
+                     image_uv_address: GpuStoreAddress,
+                     image_rendering: ImageRendering,
+                     tile_offset: Option<TileOffset>) -> (SourceTexture, Option<CacheItem>) {
+        let image_properties = resource_cache.get_image_properties(image_key);
+
+        // Check if an external image that needs to be resolved
+        // by the render thread.
+        match image_properties.external_id {
+            Some(external_id) => {
+                // This is an external texture - we will add it to
+                // the deferred resolves list to be patched by
+                // the render thread...
+                deferred_resolves.push(DeferredResolve {
+                    image_properties: image_properties,
+                    resource_address: image_uv_address,
+                });
+
+                (SourceTexture::External(external_id), None)
+            }
+            None => {
+                let cache_item = resource_cache.get_cached_image(image_key, image_rendering, tile_offset);
+                (cache_item.texture_id, Some(cache_item))
+            }
+        }
+    }
+
     pub fn resolve_primitives(&mut self,
                               resource_cache: &ResourceCache,
                               device_pixel_ratio: f32) -> Vec<DeferredResolve> {
         profile_scope!("resolve_primitives");
         let mut deferred_resolves = Vec::new();
 
         for prim_index in self.prims_to_resolve.drain(..) {
             let metadata = &mut self.cpu_metadata[prim_index.0];
@@ -897,35 +916,22 @@ impl PrimitiveStore {
                 }
                 PrimitiveKind::Image => {
                     let image_cpu = &mut self.cpu_images[metadata.cpu_prim_index.0];
 
                     let (texture_id, cache_item) = match image_cpu.kind {
                         ImagePrimitiveKind::Image(image_key, image_rendering, tile_offset, _) => {
                             // Check if an external image that needs to be resolved
                             // by the render thread.
-                            let image_properties = resource_cache.get_image_properties(image_key);
-
-                            match image_properties.external_id {
-                                Some(external_id) => {
-                                    // This is an external texture - we will add it to
-                                    // the deferred resolves list to be patched by
-                                    // the render thread...
-                                    deferred_resolves.push(DeferredResolve {
-                                        resource_address: image_cpu.resource_address,
-                                        image_properties: image_properties,
-                                    });
-
-                                    (SourceTexture::External(external_id), None)
-                                }
-                                None => {
-                                    let cache_item = resource_cache.get_cached_image(image_key, image_rendering, tile_offset);
-                                    (cache_item.texture_id, Some(cache_item))
-                                }
-                            }
+                            PrimitiveStore::resolve_image(resource_cache,
+                                                          &mut deferred_resolves,
+                                                          image_key,
+                                                          image_cpu.resource_address,
+                                                          image_rendering,
+                                                          tile_offset)
                         }
                         ImagePrimitiveKind::WebGL(context_id) => {
                             let cache_item = resource_cache.get_webgl_texture(&context_id);
                             (cache_item.texture_id, Some(cache_item))
                         }
                     };
 
                     if let Some(cache_item) = cache_item {
@@ -942,39 +948,40 @@ impl PrimitiveStore {
                                 resource_rect.uv1 = cache_item.uv1;
                             }
                         }
                     }
                     image_cpu.color_texture_id = texture_id;
                 }
                 PrimitiveKind::YuvImage => {
                     let image_cpu = &mut self.cpu_yuv_images[metadata.cpu_prim_index.0];
-                    let image_gpu: &mut YuvImagePrimitiveGpu = unsafe {
-                        mem::transmute(self.gpu_data64.get_mut(metadata.gpu_prim_index))
-                    };
 
-                    if image_cpu.y_texture_id == SourceTexture::Invalid {
-                        let y_cache_item = resource_cache.get_cached_image(image_cpu.y_key, ImageRendering::Auto, None);
-                        image_cpu.y_texture_id = y_cache_item.texture_id;
-                        image_gpu.y_uv0 = y_cache_item.uv0;
-                        image_gpu.y_uv1 = y_cache_item.uv1;
-                    }
+                    //yuv
+                    for channel in 0..3 {
+                        if image_cpu.yuv_texture_id[channel] == SourceTexture::Invalid {
+                            // Check if an external image that needs to be resolved
+                            // by the render thread.
+                            let resource_address = image_cpu.yuv_resource_address + channel as i32;
 
-                    if image_cpu.u_texture_id == SourceTexture::Invalid {
-                        let u_cache_item = resource_cache.get_cached_image(image_cpu.u_key, ImageRendering::Auto, None);
-                        image_cpu.u_texture_id = u_cache_item.texture_id;
-                        image_gpu.u_uv0 = u_cache_item.uv0;
-                        image_gpu.u_uv1 = u_cache_item.uv1;
-                    }
-
-                    if image_cpu.v_texture_id == SourceTexture::Invalid {
-                        let v_cache_item = resource_cache.get_cached_image(image_cpu.v_key, ImageRendering::Auto, None);
-                        image_cpu.v_texture_id = v_cache_item.texture_id;
-                        image_gpu.v_uv0 = v_cache_item.uv0;
-                        image_gpu.v_uv1 = v_cache_item.uv1;
+                            let (texture_id, cache_item) =
+                                PrimitiveStore::resolve_image(resource_cache,
+                                                              &mut deferred_resolves,
+                                                              image_cpu.yuv_key[channel],
+                                                              resource_address,
+                                                              ImageRendering::Auto,
+                                                              None);
+                            // texture_id
+                            image_cpu.yuv_texture_id[channel] = texture_id;
+                            // uv coordinates
+                            if let Some(cache_item) = cache_item {
+                                let resource_rect = self.gpu_resource_rects.get_mut(image_cpu.yuv_resource_address + channel as i32);
+                                resource_rect.uv0 = cache_item.uv0;
+                                resource_rect.uv1 = cache_item.uv1;
+                            }
+                        }
                     }
                 }
             }
         }
 
         deferred_resolves
     }
 
@@ -1183,19 +1190,19 @@ impl PrimitiveStore {
                     }
                     ImagePrimitiveKind::WebGL(..) => {}
                 }
             }
             PrimitiveKind::YuvImage => {
                 let image_cpu = &mut self.cpu_yuv_images[metadata.cpu_prim_index.0];
                 prim_needs_resolve = true;
 
-                resource_cache.request_image(image_cpu.y_key, ImageRendering::Auto, None);
-                resource_cache.request_image(image_cpu.u_key, ImageRendering::Auto, None);
-                resource_cache.request_image(image_cpu.v_key, ImageRendering::Auto, None);
+                for channel in 0..3 {
+                    resource_cache.request_image(image_cpu.yuv_key[channel], ImageRendering::Auto, None);
+                }
 
                 // TODO(nical): Currently assuming no tile_spacing for yuv images.
                 metadata.is_opaque = true;
             }
             PrimitiveKind::AlignedGradient => {
                 let gradient = &mut self.cpu_gradients[metadata.cpu_prim_index.0];
                 if gradient.cache_dirty {
                     let src_stops = auxiliary_lists.gradient_stops(&gradient.stops_range);
@@ -1330,20 +1337,20 @@ impl From<GradientStopGpu> for GpuBlock3
 impl From<RadialGradientPrimitiveGpu> for GpuBlock32 {
     fn from(data: RadialGradientPrimitiveGpu) -> GpuBlock32 {
         unsafe {
             mem::transmute::<RadialGradientPrimitiveGpu, GpuBlock32>(data)
         }
     }
 }
 
-impl From<YuvImagePrimitiveGpu> for GpuBlock64 {
-    fn from(data: YuvImagePrimitiveGpu) -> GpuBlock64 {
+impl From<YuvImagePrimitiveGpu> for GpuBlock16 {
+    fn from(data: YuvImagePrimitiveGpu) -> GpuBlock16 {
         unsafe {
-            mem::transmute::<YuvImagePrimitiveGpu, GpuBlock64>(data)
+            mem::transmute::<YuvImagePrimitiveGpu, GpuBlock16>(data)
         }
     }
 }
 
 impl From<ClipRect> for GpuBlock32 {
     fn from(data: ClipRect) -> GpuBlock32 {
         unsafe {
             mem::transmute::<ClipRect, GpuBlock32>(data)
--- a/gfx/webrender/src/profiler.rs
+++ b/gfx/webrender/src/profiler.rs
@@ -63,16 +63,20 @@ impl IntProfileCounter {
     pub fn add(&mut self, amount: usize) {
         self.value += amount;
     }
 
     #[inline(always)]
     pub fn set(&mut self, amount: usize) {
         self.value = amount;
     }
+
+    pub fn get(&self) -> usize {
+        self.value
+    }
 }
 
 impl ProfileCounter for IntProfileCounter {
     fn description(&self) -> &'static str {
         self.description
     }
 
     fn value(&self) -> String {
@@ -148,16 +152,20 @@ impl TimeProfileCounter {
     pub fn profile<T, F>(&mut self, callback: F) -> T where F: FnOnce() -> T {
         let t0 = precise_time_ns();
         let val = callback();
         let t1 = precise_time_ns();
         let ns = t1 - t0;
         self.nanoseconds += ns;
         val
     }
+
+    pub fn get(&self) -> u64 {
+        self.nanoseconds
+    }
 }
 
 impl ProfileCounter for TimeProfileCounter {
     fn description(&self) -> &'static str {
         self.description
     }
 
     fn value(&self) -> String {
--- a/gfx/webrender/src/render_backend.rs
+++ b/gfx/webrender/src/render_backend.rs
@@ -13,35 +13,38 @@ use resource_cache::ResourceCache;
 use scene::Scene;
 use std::collections::HashMap;
 use std::io::{Cursor, Read};
 use std::sync::{Arc, Mutex};
 use std::sync::mpsc::Sender;
 use texture_cache::TextureCache;
 use thread_profiler::register_thread_with_profiler;
 use threadpool::ThreadPool;
-use webrender_traits::{DeviceUintPoint, DeviceUintRect, DeviceUintSize};
+use webrender_traits::{DeviceIntPoint, DeviceUintPoint, DeviceUintRect, DeviceUintSize, LayerPoint};
 use webrender_traits::{ApiMsg, AuxiliaryLists, BuiltDisplayList, IdNamespace, ImageData};
 use webrender_traits::{PipelineId, RenderNotifier, RenderDispatcher, WebGLCommand, WebGLContextId};
 use webrender_traits::channel::{PayloadHelperMethods, PayloadReceiver, PayloadSender, MsgReceiver};
 use webrender_traits::{BlobImageRenderer, VRCompositorCommand, VRCompositorHandler};
 use offscreen_gl_context::GLContextDispatcher;
 
 /// The render backend is responsible for transforming high level display lists into
 /// GPU-friendly work which is then submitted to the renderer in the form of a frame::Frame.
 ///
 /// The render backend operates on its own thread.
 pub struct RenderBackend {
     api_rx: MsgReceiver<ApiMsg>,
     payload_rx: PayloadReceiver,
     payload_tx: PayloadSender,
     result_tx: Sender<ResultMsg>,
 
-    device_pixel_ratio: f32,
+    // TODO(gw): Consider using strongly typed units here.
+    hidpi_factor: f32,
     page_zoom_factor: f32,
+    pinch_zoom_factor: f32,
+    pan: DeviceIntPoint,
     window_size: DeviceUintSize,
     inner_rect: DeviceUintRect,
     next_namespace_id: IdNamespace,
 
     resource_cache: ResourceCache,
 
     scene: Scene,
     frame: Frame,
@@ -58,17 +61,17 @@ pub struct RenderBackend {
     vr_compositor_handler: Arc<Mutex<Option<Box<VRCompositorHandler>>>>
 }
 
 impl RenderBackend {
     pub fn new(api_rx: MsgReceiver<ApiMsg>,
                payload_rx: PayloadReceiver,
                payload_tx: PayloadSender,
                result_tx: Sender<ResultMsg>,
-               device_pixel_ratio: f32,
+               hidpi_factor: f32,
                texture_cache: TextureCache,
                enable_aa: bool,
                workers: Arc<Mutex<ThreadPool>>,
                notifier: Arc<Mutex<Option<Box<RenderNotifier>>>>,
                webrender_context_handle: Option<GLContextHandleWrapper>,
                config: FrameBuilderConfig,
                recorder: Option<Box<ApiRecordingReceiver>>,
                main_thread_dispatcher: Arc<Mutex<Option<Box<RenderDispatcher>>>>,
@@ -80,18 +83,20 @@ impl RenderBackend {
 
         register_thread_with_profiler("Backend".to_string());
 
         RenderBackend {
             api_rx: api_rx,
             payload_rx: payload_rx,
             payload_tx: payload_tx,
             result_tx: result_tx,
-            device_pixel_ratio: device_pixel_ratio,
+            hidpi_factor: hidpi_factor,
             page_zoom_factor: 1.0,
+            pinch_zoom_factor: 1.0,
+            pan: DeviceIntPoint::zero(),
             resource_cache: resource_cache,
             scene: Scene::new(),
             frame: Frame::new(config),
             next_namespace_id: IdNamespace(1),
             notifier: notifier,
             webrender_context_handle: webrender_context_handle,
             webgl_contexts: HashMap::new(),
             current_bound_webgl_context_id: None,
@@ -146,16 +151,22 @@ impl RenderBackend {
                             self.resource_cache.update_image_template(id, descriptor, bytes);
                         }
                         ApiMsg::DeleteImage(id) => {
                             self.resource_cache.delete_image_template(id);
                         }
                         ApiMsg::SetPageZoom(factor) => {
                             self.page_zoom_factor = factor.get();
                         }
+                        ApiMsg::SetPinchZoom(factor) => {
+                            self.pinch_zoom_factor = factor.get();
+                        }
+                        ApiMsg::SetPan(pan) => {
+                            self.pan = pan;
+                        }
                         ApiMsg::SetWindowParameters(window_size, inner_rect) => {
                             self.window_size = window_size;
                             self.inner_rect = inner_rect;
                         }
                         ApiMsg::CloneApi(sender) => {
                             let result = self.next_namespace_id;
 
                             let IdNamespace(id_namespace) = self.next_namespace_id;
@@ -411,16 +422,20 @@ impl RenderBackend {
             }
         }
     }
 
     fn discard_frame_state_for_pipeline(&mut self, pipeline_id: PipelineId) {
         self.frame.discard_frame_state_for_pipeline(pipeline_id);
     }
 
+    fn accumulated_scale_factor(&self) -> f32 {
+        self.hidpi_factor * self.page_zoom_factor * self.pinch_zoom_factor
+    }
+
     fn build_scene(&mut self) {
         // Flatten the stacking context hierarchy
         if let Some(id) = self.current_bound_webgl_context_id {
             self.webgl_contexts[&id].unbind();
             self.current_bound_webgl_context_id = None;
         }
 
         // When running in OSMesa mode with texture sharing,
@@ -431,32 +446,34 @@ impl RenderBackend {
         // context at the start of a render frame should
         // incur minimal cost.
         for (_, webgl_context) in &self.webgl_contexts {
             webgl_context.make_current();
             webgl_context.apply_command(WebGLCommand::Flush);
             webgl_context.unbind();
         }
 
-        let device_pixel_ratio = self.device_pixel_ratio * self.page_zoom_factor;
-
+        let accumulated_scale_factor = self.accumulated_scale_factor();
         self.frame.create(&self.scene,
                           &mut self.resource_cache,
                           self.window_size,
                           self.inner_rect,
-                          device_pixel_ratio);
+                          accumulated_scale_factor);
     }
 
     fn render(&mut self,
               texture_cache_profile: &mut TextureCacheProfileCounters)
               -> RendererFrame {
-        let device_pixel_ratio = self.device_pixel_ratio * self.page_zoom_factor;
+        let accumulated_scale_factor = self.accumulated_scale_factor();
+        let pan = LayerPoint::new(self.pan.x as f32 / accumulated_scale_factor,
+                                  self.pan.y as f32 / accumulated_scale_factor);
         let frame = self.frame.build(&mut self.resource_cache,
                                      &self.scene.pipeline_auxiliary_lists,
-                                     device_pixel_ratio,
+                                     accumulated_scale_factor,
+                                     pan,
                                      texture_cache_profile);
         frame
     }
 
     fn publish_frame(&mut self,
                      frame: RendererFrame,
                      profile_counters: &mut BackendProfileCounters) {
         let pending_update = self.resource_cache.pending_updates();
--- a/gfx/webrender/src/render_task.rs
+++ b/gfx/webrender/src/render_task.rs
@@ -2,19 +2,19 @@
  * 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 internal_types::{HardwareCompositeOp, LowLevelFilterOp};
 use mask_cache::MaskCacheInfo;
 use prim_store::{PrimitiveCacheKey, PrimitiveIndex};
 use std::{cmp, f32, i32, mem, usize};
 use tiling::{ClipScrollGroupIndex, PackedLayerIndex, RenderPass, RenderTargetIndex};
-use tiling::{ScrollLayerIndex, StackingContextIndex};
+use tiling::{StackingContextIndex};
 use webrender_traits::{DeviceIntLength, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
-use webrender_traits::MixBlendMode;
+use webrender_traits::{MixBlendMode, ScrollLayerId};
 
 const FLOATS_PER_RENDER_TASK_INFO: usize = 12;
 
 #[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
 pub struct RenderTaskIndex(pub usize);
 
 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
 pub enum RenderTaskKey {
@@ -28,17 +28,17 @@ pub enum RenderTaskKey {
     HorizontalBlur(i32, PrimitiveIndex),
     /// Allocate a block of space in target for framebuffer copy.
     CopyFramebuffer(StackingContextIndex),
 }
 
 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
 pub enum MaskCacheKey {
     Primitive(PrimitiveIndex),
-    ScrollLayer(ScrollLayerIndex),
+    ScrollLayer(ScrollLayerId),
 }
 
 #[derive(Debug, Copy, Clone)]
 pub enum RenderTaskId {
     Static(RenderTaskIndex),
     Dynamic(RenderTaskKey),
 }
 
--- a/gfx/webrender/src/renderer.rs
+++ b/gfx/webrender/src/renderer.rs
@@ -6,52 +6,52 @@
 //!
 //! The `webrender::renderer` module provides the interface to webrender, which
 //! is accessible through [`Renderer`][renderer]
 //!
 //! [renderer]: struct.Renderer.html
 
 use debug_colors;
 use debug_render::DebugRenderer;
-use device::{DepthFunction, Device, ProgramId, TextureId, VertexFormat, GpuMarker, GpuProfiler};
-use device::{TextureFilter, VAOId, VertexUsageHint, FileWatcherHandler, TextureTarget, ShaderError};
+use device::{DepthFunction, Device, FrameId, ProgramId, TextureId, VertexFormat, GpuMarker, GpuProfiler};
+use device::{GpuSample, TextureFilter, VAOId, VertexUsageHint, FileWatcherHandler, TextureTarget, ShaderError};
 use euclid::Matrix4D;
 use fnv::FnvHasher;
 use frame_builder::FrameBuilderConfig;
 use gpu_store::{GpuStore, GpuStoreLayout};
 use internal_types::{CacheTextureId, RendererFrame, ResultMsg, TextureUpdateOp};
 use internal_types::{ExternalImageUpdateList, TextureUpdateList, PackedVertex, RenderTargetMode};
 use internal_types::{ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE, SourceTexture};
 use internal_types::{BatchTextures, TextureSampler, GLContextHandleWrapper};
 use prim_store::GradientData;
 use profiler::{Profiler, BackendProfileCounters};
 use profiler::{GpuProfileTag, RendererProfileTimers, RendererProfileCounters};
 use record::ApiRecordingReceiver;
 use render_backend::RenderBackend;
 use render_task::RenderTaskData;
 use std;
 use std::cmp;
-use std::collections::HashMap;
+use std::collections::{HashMap, VecDeque};
 use std::f32;
 use std::hash::BuildHasherDefault;
 use std::marker::PhantomData;
 use std::mem;
 use std::path::PathBuf;
 use std::sync::{Arc, Mutex};
 use std::sync::mpsc::{channel, Receiver, Sender};
 use std::thread;
 use texture_cache::TextureCache;
 use threadpool::ThreadPool;
 use tiling::{AlphaBatchKind, BlurCommand, Frame, PrimitiveBatch, PrimitiveBatchData};
 use tiling::{CacheClipInstance, PrimitiveInstance, RenderTarget};
 use time::precise_time_ns;
 use thread_profiler::{register_thread_with_profiler, write_profile};
 use util::TransformedRectKind;
 use webrender_traits::{ColorF, Epoch, PipelineId, RenderNotifier, RenderDispatcher};
-use webrender_traits::{ExternalImageId, ImageData, ImageFormat, RenderApiSender, RendererKind};
+use webrender_traits::{ExternalImageId, ImageData, ImageFormat, RenderApiSender};
 use webrender_traits::{DeviceIntRect, DevicePoint, DeviceIntPoint, DeviceIntSize, DeviceUintSize};
 use webrender_traits::{ImageDescriptor, BlobImageRenderer};
 use webrender_traits::channel;
 use webrender_traits::VRCompositorHandler;
 
 pub const GPU_DATA_TEXTURE_POOL: usize = 5;
 pub const MAX_VERTEX_TEXTURE_WIDTH: usize = 1024;
 
@@ -70,20 +70,65 @@ const GPU_TAG_PRIM_TEXT_RUN: GpuProfileT
 const GPU_TAG_PRIM_GRADIENT: GpuProfileTag = GpuProfileTag { label: "Gradient", color: debug_colors::YELLOW };
 const GPU_TAG_PRIM_ANGLE_GRADIENT: GpuProfileTag = GpuProfileTag { label: "AngleGradient", color: debug_colors::POWDERBLUE };
 const GPU_TAG_PRIM_RADIAL_GRADIENT: GpuProfileTag = GpuProfileTag { label: "RadialGradient", color: debug_colors::LIGHTPINK };
 const GPU_TAG_PRIM_BOX_SHADOW: GpuProfileTag = GpuProfileTag { label: "BoxShadow", color: debug_colors::CYAN };
 const GPU_TAG_PRIM_BORDER: GpuProfileTag = GpuProfileTag { label: "Border", color: debug_colors::ORANGE };
 const GPU_TAG_PRIM_CACHE_IMAGE: GpuProfileTag = GpuProfileTag { label: "CacheImage", color: debug_colors::SILVER };
 const GPU_TAG_BLUR: GpuProfileTag = GpuProfileTag { label: "Blur", color: debug_colors::VIOLET };
 
+#[derive(Debug, Copy, Clone)]
+pub enum RendererKind {
+    Native,
+    OSMesa,
+}
+
+#[derive(Debug)]
+pub struct GpuProfile {
+    pub frame_id: FrameId,
+    pub paint_time_ns: u64,
+}
+
+impl GpuProfile {
+    fn new<T>(frame_id: FrameId, samples: &[GpuSample<T>]) -> GpuProfile {
+        let mut paint_time_ns = 0;
+        for sample in samples {
+            paint_time_ns += sample.time_ns;
+        }
+        GpuProfile {
+            frame_id: frame_id,
+            paint_time_ns: paint_time_ns,
+        }
+    }
+}
+
+#[derive(Debug)]
+pub struct CpuProfile {
+    pub frame_id: FrameId,
+    pub composite_time_ns: u64,
+    pub draw_calls: usize,
+}
+
+impl CpuProfile {
+    fn new(frame_id: FrameId,
+           composite_time_ns: u64,
+           draw_calls: usize) -> CpuProfile {
+        CpuProfile {
+            frame_id: frame_id,
+            composite_time_ns: composite_time_ns,
+            draw_calls: draw_calls,
+        }
+    }
+}
+
 #[derive(Debug, Copy, Clone, PartialEq)]
 pub enum BlendMode {
     None,
     Alpha,
+    PremultipliedAlpha,
 
     // Use the color of the text itself as a constant color blend factor.
     Subpixel(ColorF),
 }
 
 struct GpuDataTexture<L> {
     id: TextureId,
     layout: PhantomData<L>,
@@ -420,16 +465,17 @@ pub struct Renderer {
 
     ps_blend: LazilyCompiledShader,
     ps_hw_composite: LazilyCompiledShader,
     ps_composite: LazilyCompiledShader,
 
     notifier: Arc<Mutex<Option<Box<RenderNotifier>>>>,
 
     enable_profiler: bool,
+    max_recorded_profiles: usize,
     clear_framebuffer: bool,
     clear_color: ColorF,
     debug: DebugRenderer,
     render_target_debug: bool,
     backend_profile_counters: BackendProfileCounters,
     profile_counters: RendererProfileCounters,
     profiler: Profiler,
     last_time: u64,
@@ -468,17 +514,22 @@ pub struct Renderer {
     /// application to provide external buffers for image data.
     external_image_handler: Option<Box<ExternalImageHandler>>,
 
     /// Map of external image IDs to native textures.
     external_images: HashMap<ExternalImageId, TextureId, BuildHasherDefault<FnvHasher>>,
 
     // Optional trait object that handles WebVR commands.
     // Some WebVR commands such as SubmitFrame must be synced with the WebGL render thread.
-    vr_compositor_handler: Arc<Mutex<Option<Box<VRCompositorHandler>>>>
+    vr_compositor_handler: Arc<Mutex<Option<Box<VRCompositorHandler>>>>,
+
+    /// List of profile results from previous frames. Can be retrieved
+    /// via get_frame_profiles().
+    cpu_profiles: VecDeque<CpuProfile>,
+    gpu_profiles: VecDeque<GpuProfile>,
 }
 
 #[derive(Debug)]
 pub enum InitError {
     Shader(ShaderError),
     Thread(std::io::Error),
 }
 
@@ -835,33 +886,36 @@ impl Renderer {
             ps_composite: ps_composite,
             notifier: notifier,
             debug: debug_renderer,
             render_target_debug: render_target_debug,
             backend_profile_counters: BackendProfileCounters::new(),
             profile_counters: RendererProfileCounters::new(),
             profiler: Profiler::new(),
             enable_profiler: options.enable_profiler,
+            max_recorded_profiles: options.max_recorded_profiles,
             clear_framebuffer: options.clear_framebuffer,
             clear_color: options.clear_color,
             last_time: 0,
             render_targets: Vec::new(),
             gpu_profile: GpuProfiler::new(),
             prim_vao_id: prim_vao_id,
             blur_vao_id: blur_vao_id,
             clip_vao_id: clip_vao_id,
             gdt_index: 0,
             gpu_data_textures: gpu_data_textures,
             pipeline_epoch_map: HashMap::with_hasher(Default::default()),
             main_thread_dispatcher: main_thread_dispatcher,
             cache_texture_id_map: Vec::new(),
             dummy_cache_texture_id: dummy_cache_texture_id,
             external_image_handler: None,
             external_images: HashMap::with_hasher(Default::default()),
-            vr_compositor_handler: vr_compositor
+            vr_compositor_handler: vr_compositor,
+            cpu_profiles: VecDeque::new(),
+            gpu_profiles: VecDeque::new(),
         };
 
         let sender = RenderApiSender::new(api_tx, payload_tx);
         Ok((renderer, sender))
     }
 
     /// Sets the new RenderNotifier.
     ///
@@ -953,56 +1007,80 @@ impl Renderer {
         }
     }
 
     /// Set a callback for handling external images.
     pub fn set_external_image_handler(&mut self, handler: Box<ExternalImageHandler>) {
         self.external_image_handler = Some(handler);
     }
 
+    /// Retrieve (and clear) the current list of recorded frame profiles.
+    pub fn get_frame_profiles(&mut self) -> (Vec<CpuProfile>, Vec<GpuProfile>) {
+        let cpu_profiles = self.cpu_profiles.drain(..).collect();
+        let gpu_profiles = self.gpu_profiles.drain(..).collect();
+        (cpu_profiles, gpu_profiles)
+    }
+
     /// Renders the current frame.
     ///
     /// A Frame is supplied by calling [set_root_stacking_context()][newframe].
     /// [newframe]: ../../webrender_traits/struct.RenderApi.html#method.set_root_stacking_context
     pub fn render(&mut self, framebuffer_size: DeviceUintSize) {
         profile_scope!("render");
 
         if let Some(mut frame) = self.current_frame.take() {
             if let Some(ref mut frame) = frame.frame {
                 let mut profile_timers = RendererProfileTimers::new();
 
                 // Block CPU waiting for last frame's GPU profiles to arrive.
                 // In general this shouldn't block unless heavily GPU limited.
-                if let Some(samples) = self.gpu_profile.build_samples() {
+                if let Some((gpu_frame_id, samples)) = self.gpu_profile.build_samples() {
+                    if self.max_recorded_profiles > 0 {
+                        while self.gpu_profiles.len() >= self.max_recorded_profiles {
+                            self.gpu_profiles.pop_front();
+                        }
+                        self.gpu_profiles.push_back(GpuProfile::new(gpu_frame_id, &samples));
+                    }
                     profile_timers.gpu_samples = samples;
                 }
 
-                profile_timers.cpu_time.profile(|| {
-                    self.device.begin_frame(frame.device_pixel_ratio);
-                    self.gpu_profile.begin_frame();
+                let cpu_frame_id = profile_timers.cpu_time.profile(|| {
+                    let cpu_frame_id = self.device.begin_frame(frame.device_pixel_ratio);
+                    self.gpu_profile.begin_frame(cpu_frame_id);
                     {
                         let _gm = self.gpu_profile.add_marker(GPU_TAG_INIT);
 
                         self.device.disable_scissor();
                         self.device.disable_depth();
                         self.device.set_blend(false);
 
                         //self.update_shaders();
                         self.update_texture_cache();
                     }
 
                     self.draw_tile_frame(frame, &framebuffer_size);
 
                     self.gpu_profile.end_frame();
+                    cpu_frame_id
                 });
 
                 let current_time = precise_time_ns();
                 let ns = current_time - self.last_time;
                 self.profile_counters.frame_time.set(ns);
 
+                if self.max_recorded_profiles > 0 {
+                    while self.cpu_profiles.len() >= self.max_recorded_profiles {
+                        self.cpu_profiles.pop_front();
+                    }
+                    let cpu_profile = CpuProfile::new(cpu_frame_id,
+                                                      profile_timers.cpu_time.get(),
+                                                      self.profile_counters.draw_calls.get());
+                    self.cpu_profiles.push_back(cpu_profile);
+                }
+
                 if self.enable_profiler {
                     self.profiler.draw_profile(&frame.profile_counters,
                                                &self.backend_profile_counters,
                                                &self.profile_counters,
                                                &mut profile_timers,
                                                &mut self.debug);
                 }
 
@@ -1173,17 +1251,19 @@ impl Renderer {
                     batch: &PrimitiveBatch,
                     projection: &Matrix4D<f32>,
                     render_task_data: &Vec<RenderTaskData>,
                     cache_texture: TextureId,
                     render_target: Option<(TextureId, i32)>,
                     target_dimensions: DeviceUintSize) {
         let transform_kind = batch.key.flags.transform_kind();
         let needs_clipping = batch.key.flags.needs_clipping();
-        debug_assert!(!needs_clipping || batch.key.blend_mode == BlendMode::Alpha);
+        debug_assert!(!needs_clipping ||
+                      batch.key.blend_mode == BlendMode::Alpha ||
+                      batch.key.blend_mode == BlendMode::PremultipliedAlpha);
 
         match batch.data {
             PrimitiveBatchData::Instances(ref data) => {
                 let (marker, shader) = match batch.key.kind {
                     AlphaBatchKind::Composite => unreachable!(),
                     AlphaBatchKind::HardwareComposite => {
                         let shader = self.ps_hw_composite.get(&mut self.device);
                         (GPU_TAG_PRIM_HW_COMPOSITE, shader)
@@ -1198,17 +1278,17 @@ impl Renderer {
                         } else {
                             self.ps_rectangle.get(&mut self.device, transform_kind)
                         };
                         (GPU_TAG_PRIM_RECT, shader)
                     }
                     AlphaBatchKind::TextRun => {
                         let shader = match batch.key.blend_mode {
                             BlendMode::Subpixel(..) => self.ps_text_run_subpixel.get(&mut self.device, transform_kind),
-                            BlendMode::Alpha | BlendMode::None => self.ps_text_run.get(&mut self.device, transform_kind),
+                            BlendMode::Alpha | BlendMode::PremultipliedAlpha | BlendMode::None => self.ps_text_run.get(&mut self.device, transform_kind),
                         };
                         (GPU_TAG_PRIM_TEXT_RUN, shader)
                     }
                     AlphaBatchKind::Image => {
                         let shader = self.ps_image.get(&mut self.device, transform_kind);
                         (GPU_TAG_PRIM_IMAGE, shader)
                     }
                     AlphaBatchKind::YuvImage => {
@@ -1500,16 +1580,20 @@ impl Renderer {
                 match batch.key.blend_mode {
                     BlendMode::None => {
                         self.device.set_blend(false);
                     }
                     BlendMode::Alpha => {
                         self.device.set_blend(true);
                         self.device.set_blend_mode_alpha();
                     }
+                    BlendMode::PremultipliedAlpha => {
+                        self.device.set_blend(true);
+                        self.device.set_blend_mode_premultiplied_alpha();
+                    }
                     BlendMode::Subpixel(color) => {
                         self.device.set_blend(true);
                         self.device.set_blend_mode_subpixel(color);
                     }
                 }
                 prev_blend_mode = batch.key.blend_mode;
             }
 
@@ -1759,16 +1843,17 @@ pub trait ExternalImageHandler {
     fn release(&mut self, key: ExternalImageId);
 }
 
 pub struct RendererOptions {
     pub device_pixel_ratio: f32,
     pub resource_override_path: Option<PathBuf>,
     pub enable_aa: bool,
     pub enable_profiler: bool,
+    pub max_recorded_profiles: usize,
     pub debug: bool,
     pub enable_scrollbars: bool,
     pub precache_shaders: bool,
     pub renderer_kind: RendererKind,
     pub enable_subpixel_aa: bool,
     pub clear_framebuffer: bool,
     pub clear_color: ColorF,
     pub render_target_debug: bool,
@@ -1780,16 +1865,17 @@ pub struct RendererOptions {
 
 impl Default for RendererOptions {
     fn default() -> RendererOptions {
         RendererOptions {
             device_pixel_ratio: 1.0,
             resource_override_path: None,
             enable_aa: true,
             enable_profiler: false,
+            max_recorded_profiles: 0,
             debug: false,
             enable_scrollbars: false,
             precache_shaders: false,
             renderer_kind: RendererKind::Native,
             enable_subpixel_aa: false,
             clear_framebuffer: true,
             clear_color: ColorF::new(1.0, 1.0, 1.0, 1.0),
             render_target_debug: false,
--- a/gfx/webrender/src/tiling.rs
+++ b/gfx/webrender/src/tiling.rs
@@ -2,17 +2,17 @@
  * 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 fnv::FnvHasher;
 use gpu_store::GpuStoreAddress;
 use internal_types::{ANGLE_FLOAT_TO_FIXED, BatchTextures, CacheTextureId, LowLevelFilterOp};
 use internal_types::SourceTexture;
-use mask_cache::{ClipSource, MaskCacheInfo};
+use mask_cache::MaskCacheInfo;
 use prim_store::{CLIP_DATA_GPU_SIZE, DeferredResolve, GpuBlock128, GpuBlock16, GpuBlock32};
 use prim_store::{GpuBlock64, GradientData, PrimitiveCacheKey, PrimitiveGeometry, PrimitiveIndex};
 use prim_store::{PrimitiveKind, PrimitiveMetadata, PrimitiveStore, TexelRect};
 use profiler::FrameProfileCounters;
 use render_task::{AlphaRenderItem, MaskGeometryKind, MaskSegment, RenderTask, RenderTaskData};
 use render_task::{RenderTaskId, RenderTaskIndex, RenderTaskKey, RenderTaskKind};
 use render_task::RenderTaskLocation;
 use renderer::BlendMode;
@@ -100,17 +100,17 @@ impl AlphaBatchHelpers for PrimitiveStor
             PrimitiveKind::AngleGradient |
             PrimitiveKind::RadialGradient => [invalid; 3],
             PrimitiveKind::Image => {
                 let image_cpu = &self.cpu_images[metadata.cpu_prim_index.0];
                 [image_cpu.color_texture_id, invalid, invalid]
             }
             PrimitiveKind::YuvImage => {
                 let image_cpu = &self.cpu_yuv_images[metadata.cpu_prim_index.0];
-                [image_cpu.y_texture_id, image_cpu.u_texture_id, image_cpu.v_texture_id]
+                image_cpu.yuv_texture_id
             }
             PrimitiveKind::TextRun => {
                 let text_run_cpu = &self.cpu_text_runs[metadata.cpu_prim_index.0];
                 [text_run_cpu.color_texture_id, invalid, invalid]
             }
         }
     }
 
@@ -123,16 +123,23 @@ impl AlphaBatchHelpers for PrimitiveStor
                         FontRenderMode::Subpixel => BlendMode::Subpixel(text_run_cpu.color),
                         FontRenderMode::Alpha | FontRenderMode::Mono => BlendMode::Alpha,
                     }
                 } else {
                     // Text runs drawn to blur never get drawn with subpixel AA.
                     BlendMode::Alpha
                 }
             }
+            PrimitiveKind::Image => {
+                if needs_blending {
+                    BlendMode::PremultipliedAlpha
+                } else {
+                    BlendMode::None
+                }
+            }
             _ => {
                 if needs_blending {
                     BlendMode::Alpha
                 } else {
                     BlendMode::None
                 }
             }
         }
@@ -272,24 +279,26 @@ impl AlphaBatchHelpers for PrimitiveStor
                             global_prim_id: global_prim_id,
                             prim_address: prim_address,
                             sub_index: 0,
                             user_data: [ image_cpu.resource_address.0, 0 ],
                             z_sort_index: z_sort_index,
                         });
                     }
                     AlphaBatchKind::YuvImage => {
+                        let image_yuv_cpu = &self.cpu_yuv_images[metadata.cpu_prim_index.0];
+
                         data.push(PrimitiveInstance {
                             task_index: task_index,
                             clip_task_index: clip_task_index,
                             layer_index: packed_layer_index,
                             global_prim_id: global_prim_id,
                             prim_address: prim_address,
                             sub_index: 0,
-                            user_data: [ 0, 0 ],
+                            user_data: [ image_yuv_cpu.yuv_resource_address.0, 0 ],
                             z_sort_index: z_sort_index,
                         });
                     }
                     AlphaBatchKind::Border => {
                         for border_segment in 0..8 {
                             data.push(PrimitiveInstance {
                                 task_index: task_index,
                                 clip_task_index: clip_task_index,
@@ -386,23 +395,20 @@ impl AlphaBatchHelpers for PrimitiveStor
 
 #[derive(Debug)]
 pub struct ScrollbarPrimitive {
     pub scroll_layer_id: ScrollLayerId,
     pub prim_index: PrimitiveIndex,
     pub border_radius: f32,
 }
 
+#[derive(Debug)]
 pub enum PrimitiveRunCmd {
     PushStackingContext(StackingContextIndex),
     PopStackingContext,
-
-    PushScrollLayer(ScrollLayerIndex),
-    PopScrollLayer,
-
     PrimitiveRun(PrimitiveIndex, usize, ScrollLayerId),
 }
 
 #[derive(Debug, Copy, Clone)]
 pub enum PrimitiveFlags {
     None,
     Scrollbar(ScrollLayerId, f32)
 }
@@ -1390,28 +1396,16 @@ pub struct ClipScrollGroup {
 }
 
 impl ClipScrollGroup {
     pub fn is_visible(&self) -> bool {
         self.xf_rect.is_some()
     }
 }
 
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
-pub struct ScrollLayerIndex(pub usize);
-
-pub struct ScrollLayer {
-    pub scroll_layer_id: ScrollLayerId,
-    pub parent_index: ScrollLayerIndex,
-    pub clip_source: ClipSource,
-    pub clip_cache_info: Option<MaskCacheInfo>,
-    pub packed_layer_index: PackedLayerIndex,
-    pub xf_rect: Option<TransformedRect>,
-}
-
 #[derive(Debug, Clone)]
 #[repr(C)]
 pub struct PackedLayer {
     pub transform: LayerToWorldTransform,
     pub inv_transform: WorldToLayerTransform,
     pub local_clip_rect: LayerRect,
     pub screen_vertices: [WorldPoint4D; 4],
 }
@@ -1470,23 +1464,16 @@ pub struct CompositeOps {
 impl CompositeOps {
     pub fn new(filters: Vec<LowLevelFilterOp>, mix_blend_mode: Option<MixBlendMode>) -> CompositeOps {
         CompositeOps {
             filters: filters,
             mix_blend_mode: mix_blend_mode
         }
     }
 
-    pub fn empty() -> CompositeOps {
-        CompositeOps {
-            filters: Vec::new(),
-            mix_blend_mode: None,
-        }
-    }
-
     pub fn count(&self) -> usize {
         self.filters.len() + if self.mix_blend_mode.is_some() { 1 } else { 0 }
     }
 
     pub fn will_make_invisible(&self) -> bool {
         for op in &self.filters {
             match op {
                 &LowLevelFilterOp::Opacity(Au(0)) => return true,
--- a/gfx/webrender_traits/Cargo.toml
+++ b/gfx/webrender_traits/Cargo.toml
@@ -1,11 +1,11 @@
 [package]
 name = "webrender_traits"
-version = "0.23.1"
+version = "0.25.0"
 authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
 license = "MPL-2.0"
 repository = "https://github.com/servo/webrender"
 
 [features]
 nightly = ["euclid/unstable", "serde/unstable"]
 ipc = ["ipc-channel"]
 
--- a/gfx/webrender_traits/src/api.rs
+++ b/gfx/webrender_traits/src/api.rs
@@ -1,27 +1,146 @@
 /* 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 byteorder::{LittleEndian, WriteBytesExt};
 use channel::{self, MsgSender, PayloadHelperMethods, PayloadSender};
 use offscreen_gl_context::{GLContextAttributes, GLLimits};
 use std::cell::Cell;
-use {ApiMsg, ColorF, Epoch, ImageDescriptor};
-use {FontKey, IdNamespace, ImageKey, NativeFontHandle, PipelineId};
-use {RenderApiSender, ResourceId, ScrollEventPhase, ScrollLayerState, ScrollLocation, ServoScrollRootId};
-use {GlyphKey, GlyphDimensions, ImageData, WebGLContextId, WebGLCommand, TileSize};
-use {DeviceIntSize, DynamicProperties, LayoutPoint, LayoutSize, WorldPoint, PropertyBindingKey, PropertyBindingId};
-use {DeviceUintRect, DeviceUintSize};
-use {BuiltDisplayList, AuxiliaryLists};
-use VRCompositorCommand;
-use {ExternalEvent, PageZoomFactor};
+use {ColorF, ImageDescriptor};
+use {FontKey, ImageKey, NativeFontHandle, ServoScrollRootId};
+use {GlyphKey, GlyphDimensions, ImageData, WebGLContextId, WebGLCommand};
+use {DeviceIntSize, LayoutPoint, LayoutSize, WorldPoint};
+use {DeviceIntPoint, DeviceUintRect, DeviceUintSize, LayoutTransform};
+use {BuiltDisplayList, BuiltDisplayListDescriptor, AuxiliaryLists, AuxiliaryListsDescriptor};
+use std::fmt;
 use std::marker::PhantomData;
 
+pub type TileSize = u16;
+
+#[derive(Clone, Deserialize, Serialize)]
+pub enum ApiMsg {
+    AddRawFont(FontKey, Vec<u8>),
+    AddNativeFont(FontKey, NativeFontHandle),
+    DeleteFont(FontKey),
+    /// Gets the glyph dimensions
+    GetGlyphDimensions(Vec<GlyphKey>, MsgSender<Vec<Option<GlyphDimensions>>>),
+    /// Adds an image from the resource cache.
+    AddImage(ImageKey, ImageDescriptor, ImageData, Option<TileSize>),
+    /// Updates the the resource cache with the new image data.
+    UpdateImage(ImageKey, ImageDescriptor, Vec<u8>),
+    /// Drops an image from the resource cache.
+    DeleteImage(ImageKey),
+    CloneApi(MsgSender<IdNamespace>),
+    /// Supplies a new frame to WebRender.
+    ///
+    /// After receiving this message, WebRender will read the display list, followed by the
+    /// auxiliary lists, from the payload channel.
+    SetRootDisplayList(Option<ColorF>,
+                       Epoch,
+                       PipelineId,
+                       LayoutSize,
+                       BuiltDisplayListDescriptor,
+                       AuxiliaryListsDescriptor,
+                       bool),
+    SetPageZoom(ZoomFactor),
+    SetPinchZoom(ZoomFactor),
+    SetPan(DeviceIntPoint),
+    SetRootPipeline(PipelineId),
+    SetWindowParameters(DeviceUintSize, DeviceUintRect),
+    Scroll(ScrollLocation, WorldPoint, ScrollEventPhase),
+    ScrollLayersWithScrollId(LayoutPoint, PipelineId, ServoScrollRootId),
+    TickScrollingBounce,
+    TranslatePointToLayerSpace(WorldPoint, MsgSender<(LayoutPoint, PipelineId)>),
+    GetScrollLayerState(MsgSender<Vec<ScrollLayerState>>),
+    RequestWebGLContext(DeviceIntSize, GLContextAttributes, MsgSender<Result<(WebGLContextId, GLLimits), String>>),
+    ResizeWebGLContext(WebGLContextId, DeviceIntSize),
+    WebGLCommand(WebGLContextId, WebGLCommand),
+    GenerateFrame(Option<DynamicProperties>),
+    // WebVR commands that must be called in the WebGL render thread.
+    VRCompositorCommand(WebGLContextId, VRCompositorCommand),
+    /// An opaque handle that must be passed to the render notifier. It is used by Gecko
+    /// to forward gecko-specific messages to the render thread preserving the ordering
+    /// within the other messages.
+    ExternalEvent(ExternalEvent),
+    ShutDown,
+}
+
+impl fmt::Debug for ApiMsg {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            &ApiMsg::AddRawFont(..) => { write!(f, "ApiMsg::AddRawFont") }
+            &ApiMsg::AddNativeFont(..) => { write!(f, "ApiMsg::AddNativeFont") }
+            &ApiMsg::DeleteFont(..) => { write!(f, "ApiMsg::DeleteFont") }
+            &ApiMsg::GetGlyphDimensions(..) => { write!(f, "ApiMsg::GetGlyphDimensions") }
+            &ApiMsg::AddImage(..) => { write!(f, "ApiMsg::AddImage") }
+            &ApiMsg::UpdateImage(..) => { write!(f, "ApiMsg::UpdateImage") }
+            &ApiMsg::DeleteImage(..) => { write!(f, "ApiMsg::DeleteImage") }
+            &ApiMsg::CloneApi(..) => { write!(f, "ApiMsg::CloneApi") }
+            &ApiMsg::SetRootDisplayList(..) => { write!(f, "ApiMsg::SetRootDisplayList") }
+            &ApiMsg::SetRootPipeline(..) => { write!(f, "ApiMsg::SetRootPipeline") }
+            &ApiMsg::Scroll(..) => { write!(f, "ApiMsg::Scroll") }
+            &ApiMsg::ScrollLayersWithScrollId(..) => { write!(f, "ApiMsg::ScrollLayersWithScrollId") }
+            &ApiMsg::TickScrollingBounce => { write!(f, "ApiMsg::TickScrollingBounce") }
+            &ApiMsg::TranslatePointToLayerSpace(..) => { write!(f, "ApiMsg::TranslatePointToLayerSpace") }
+            &ApiMsg::GetScrollLayerState(..) => { write!(f, "ApiMsg::GetScrollLayerState") }
+            &ApiMsg::RequestWebGLContext(..) => { write!(f, "ApiMsg::RequestWebGLContext") }
+            &ApiMsg::ResizeWebGLContext(..) => { write!(f, "ApiMsg::ResizeWebGLContext") }
+            &ApiMsg::WebGLCommand(..) => { write!(f, "ApiMsg::WebGLCommand") }
+            &ApiMsg::GenerateFrame(..) => { write!(f, "ApiMsg::GenerateFrame") }
+            &ApiMsg::VRCompositorCommand(..) => { write!(f, "ApiMsg::VRCompositorCommand") }
+            &ApiMsg::ExternalEvent(..) => { write!(f, "ApiMsg::ExternalEvent") }
+            &ApiMsg::ShutDown => { write!(f, "ApiMsg::ShutDown") }
+            &ApiMsg::SetPageZoom(..) => { write!(f, "ApiMsg::SetPageZoom") }
+            &ApiMsg::SetPinchZoom(..) => { write!(f, "ApiMsg::SetPinchZoom") }
+            &ApiMsg::SetPan(..) => { write!(f, "ApiMsg::SetPan") }
+            &ApiMsg::SetWindowParameters(..) => { write!(f, "ApiMsg::SetWindowParameters") }
+        }
+    }
+}
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
+pub struct Epoch(pub u32);
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
+pub struct PipelineId(pub u32, pub u32);
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
+pub struct IdNamespace(pub u32);
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
+pub struct ResourceId(pub u32);
+
+/// An opaque pointer-sized value.
+#[repr(C)]
+#[derive(Clone, Deserialize, Serialize)]
+pub struct ExternalEvent {
+    raw: usize,
+}
+
+unsafe impl Send for ExternalEvent {}
+
+impl ExternalEvent {
+    pub fn from_raw(raw: usize) -> Self { ExternalEvent { raw: raw } }
+    /// Consumes self to make it obvious that the event should be forwarded only once.
+    pub fn unwrap(self) -> usize { self.raw }
+}
+
+
+#[derive(Clone, Deserialize, Serialize)]
+pub struct RenderApiSender {
+    api_sender: MsgSender<ApiMsg>,
+    payload_sender: PayloadSender,
+}
+
 impl RenderApiSender {
     pub fn new(api_sender: MsgSender<ApiMsg>,
                payload_sender: PayloadSender)
                -> RenderApiSender {
         RenderApiSender {
             api_sender: api_sender,
             payload_sender: payload_sender,
         }
@@ -194,21 +313,31 @@ impl RenderApi {
     pub fn scroll_layers_with_scroll_root_id(&self,
                                              new_scroll_origin: LayoutPoint,
                                              pipeline_id: PipelineId,
                                              scroll_root_id: ServoScrollRootId) {
         let msg = ApiMsg::ScrollLayersWithScrollId(new_scroll_origin, pipeline_id, scroll_root_id);
         self.api_sender.send(msg).unwrap();
     }
 
-    pub fn set_page_zoom(&self, page_zoom: PageZoomFactor) {
+    pub fn set_page_zoom(&self, page_zoom: ZoomFactor) {
         let msg = ApiMsg::SetPageZoom(page_zoom);
         self.api_sender.send(msg).unwrap();
     }
 
+    pub fn set_pinch_zoom(&self, pinch_zoom: ZoomFactor) {
+        let msg = ApiMsg::SetPinchZoom(pinch_zoom);
+        self.api_sender.send(msg).unwrap();
+    }
+
+    pub fn set_pan(&self, pan: DeviceIntPoint) {
+        let msg = ApiMsg::SetPan(pan);
+        self.api_sender.send(msg).unwrap();
+    }
+
     pub fn set_window_parameters(&self,
                                  window_size: DeviceUintSize,
                                  inner_rect: DeviceUintRect) {
         let msg = ApiMsg::SetWindowParameters(window_size, inner_rect);
         self.api_sender.send(msg).unwrap();
     }
 
     pub fn tick_scrolling_bounce_animations(&self) {
@@ -288,8 +417,144 @@ impl RenderApi {
     #[inline]
     fn next_unique_id(&self) -> (u32, u32) {
         let IdNamespace(namespace) = self.id_namespace;
         let ResourceId(id) = self.next_id.get();
         self.next_id.set(ResourceId(id + 1));
         (namespace, id)
     }
 }
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub enum ScrollEventPhase {
+    /// The user started scrolling.
+    Start,
+    /// The user performed a scroll. The Boolean flag indicates whether the user's fingers are
+    /// down, if a touchpad is in use. (If false, the event is a touchpad fling.)
+    Move(bool),
+    /// The user ended scrolling.
+    End,
+}
+
+#[derive(Clone, Deserialize, Serialize)]
+pub struct ScrollLayerState {
+    pub pipeline_id: PipelineId,
+    pub scroll_root_id: ServoScrollRootId,
+    pub scroll_offset: LayoutPoint,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
+pub enum ScrollLocation {
+    /// Scroll by a certain amount.
+    Delta(LayoutPoint),
+    /// Scroll to very top of element.
+    Start,
+    /// Scroll to very bottom of element.
+    End
+}
+
+/// Represents a zoom factor.
+#[derive(Clone, Copy, Serialize, Deserialize, Debug)]
+pub struct ZoomFactor(f32);
+
+impl ZoomFactor {
+    /// Construct a new zoom factor.
+    pub fn new(scale: f32) -> ZoomFactor {
+        ZoomFactor(scale)
+    }
+
+    /// Get the zoom factor as an untyped float.
+    pub fn get(&self) -> f32 {
+        self.0
+    }
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, Eq, Hash)]
+pub struct PropertyBindingId {
+    namespace: u32,
+    uid: u32,
+}
+
+/// A unique key that is used for connecting animated property
+/// values to bindings in the display list.
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct PropertyBindingKey<T> {
+    pub id: PropertyBindingId,
+    _phantom: PhantomData<T>,
+}
+
+/// Construct a property value from a given key and value.
+impl<T: Copy> PropertyBindingKey<T> {
+    pub fn with(&self, value: T) -> PropertyValue<T> {
+        PropertyValue {
+            key: *self,
+            value: value,
+        }
+    }
+}
+
+/// A binding property can either be a specific value
+/// (the normal, non-animated case) or point to a binding location
+/// to fetch the current value from.
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub enum PropertyBinding<T> {
+    Value(T),
+    Binding(PropertyBindingKey<T>),
+}
+
+impl<T> From<T> for PropertyBinding<T> {
+    fn from(value: T) -> PropertyBinding<T> {
+        PropertyBinding::Value(value)
+    }
+}
+
+impl<T> From<PropertyBindingKey<T>> for PropertyBinding<T> {
+    fn from(key: PropertyBindingKey<T>) -> PropertyBinding<T> {
+        PropertyBinding::Binding(key)
+    }
+}
+
+/// The current value of an animated property. This is
+/// supplied by the calling code.
+#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
+pub struct PropertyValue<T> {
+    pub key: PropertyBindingKey<T>,
+    pub value: T,
+}
+
+/// When using generate_frame(), a list of PropertyValue structures
+/// can optionally be supplied to provide the current value of any
+/// animated properties.
+#[derive(Clone, Deserialize, Serialize, Debug)]
+pub struct DynamicProperties {
+    pub transforms: Vec<PropertyValue<LayoutTransform>>,
+    pub floats: Vec<PropertyValue<f32>>,
+}
+
+pub type VRCompositorId = u64;
+
+// WebVR commands that must be called in the WebGL render thread.
+#[derive(Clone, Deserialize, Serialize)]
+pub enum VRCompositorCommand {
+    Create(VRCompositorId),
+    SyncPoses(VRCompositorId, f64, f64, MsgSender<Result<Vec<u8>,()>>),
+    SubmitFrame(VRCompositorId, [f32; 4], [f32; 4]),
+    Release(VRCompositorId)
+}
+
+// Trait object that handles WebVR commands.
+// Receives the texture_id associated to the WebGLContext.
+pub trait VRCompositorHandler: Send {
+    fn handle(&mut self, command: VRCompositorCommand, texture_id: Option<u32>);
+}
+
+pub trait RenderNotifier: Send {
+    fn new_frame_ready(&mut self);
+    fn new_scroll_frame_ready(&mut self, composite_needed: bool);
+    fn external_event(&mut self, _evt: ExternalEvent) { unimplemented!() }
+    fn shut_down(&mut self) {}
+}
+
+// Trait to allow dispatching functions to a specific thread or event loop.
+pub trait RenderDispatcher: Send {
+    fn dispatch(&self, Box<Fn() + Send>);
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender_traits/src/color.rs
@@ -0,0 +1,64 @@
+/* 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/. */
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct ColorF {
+    pub r: f32,
+    pub g: f32,
+    pub b: f32,
+    pub a: f32,
+}
+known_heap_size!(0, ColorF);
+
+#[repr(C)]
+#[derive(Clone, Copy, Hash, Eq, Debug, Deserialize, PartialEq, PartialOrd, Ord, Serialize)]
+pub struct ColorU {
+    pub r: u8,
+    pub g: u8,
+    pub b: u8,
+    pub a: u8,
+}
+
+impl From<ColorF> for ColorU {
+    fn from(color: ColorF) -> ColorU {
+        ColorU {
+            r: ColorU::round_to_int(color.r),
+            g: ColorU::round_to_int(color.g),
+            b: ColorU::round_to_int(color.b),
+            a: ColorU::round_to_int(color.a),
+        }
+    }
+}
+
+impl Into<ColorF> for ColorU {
+    fn into(self) -> ColorF {
+        ColorF {
+            r: self.r as f32 / 255.0,
+            g: self.g as f32 / 255.0,
+            b: self.b as f32 / 255.0,
+            a: self.a as f32 / 255.0,
+        }
+    }
+}
+
+impl ColorU {
+    fn round_to_int(x: f32) -> u8 {
+        debug_assert!((0.0 <= x) && (x <= 1.0));
+        let f = (255.0 * x) + 0.5;
+        let val = f.floor();
+        debug_assert!(val <= 255.0);
+        val as u8
+    }
+
+    pub fn new(r: u8, g: u8, b: u8, a: u8) -> ColorU {
+        ColorU {
+            r: r,
+            g: g,
+            b: b,
+            a: a,
+        }
+    }
+}
+
--- a/gfx/webrender_traits/src/display_item.rs
+++ b/gfx/webrender_traits/src/display_item.rs
@@ -1,17 +1,385 @@
 /* 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::SideOffsets2D;
 use display_list::AuxiliaryListsBuilder;
-use {BorderRadius, ClipRegion, ColorF, ComplexClipRegion};
-use {FontKey, ImageKey, PipelineId, ScrollLayerId, ScrollLayerInfo, ServoScrollRootId};
-use {ImageMask, ItemRange};
-use {LayoutSize, LayoutPoint, LayoutRect};
+use {ColorF, FontKey, ImageKey, PipelineId, WebGLContextId};
+use {LayoutPoint, LayoutRect, LayoutSize, LayoutTransform};
+use {PropertyBinding};
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct DisplayItem {
+    pub item: SpecificDisplayItem,
+    pub rect: LayoutRect,
+    pub clip: ClipRegion,
+    pub scroll_layer_id: ScrollLayerId,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub enum SpecificDisplayItem {
+    Clip(ClipDisplayItem),
+    Rectangle(RectangleDisplayItem),
+    Text(TextDisplayItem),
+    Image(ImageDisplayItem),
+    YuvImage(YuvImageDisplayItem),
+    WebGL(WebGLDisplayItem),
+    Border(BorderDisplayItem),
+    BoxShadow(BoxShadowDisplayItem),
+    Gradient(GradientDisplayItem),
+    RadialGradient(RadialGradientDisplayItem),
+    Iframe(IframeDisplayItem),
+    PushStackingContext(PushStackingContextDisplayItem),
+    PopStackingContext,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
+pub struct ItemRange {
+    pub start: usize,
+    pub length: usize,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct ClipDisplayItem {
+    pub content_size: LayoutSize,
+    pub id: ScrollLayerId,
+    pub parent_id: ScrollLayerId,
+    pub scroll_root_id: Option<ServoScrollRootId>,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct RectangleDisplayItem {
+    pub color: ColorF,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct TextDisplayItem {
+    pub glyphs: ItemRange,
+    pub font_key: FontKey,
+    pub size: Au,
+    pub color: ColorF,
+    pub blur_radius: Au,
+    pub glyph_options: Option<GlyphOptions>,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)]
+pub struct GlyphOptions {
+    // These are currently only used on windows for dwrite fonts.
+    pub use_embedded_bitmap: bool,
+    pub force_gdi_rendering: bool,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct WebGLDisplayItem {
+    pub context_id: WebGLContextId,
+}
+
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct NormalBorder {
+    pub left: BorderSide,
+    pub right: BorderSide,
+    pub top: BorderSide,
+    pub bottom: BorderSide,
+    pub radius: BorderRadius,
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
+pub enum RepeatMode {
+    Stretch,
+    Repeat,
+    Round,
+    Space,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct NinePatchDescriptor {
+    pub width: u32,
+    pub height: u32,
+    pub slice: SideOffsets2D<u32>,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct ImageBorder {
+    pub image_key: ImageKey,
+    pub patch: NinePatchDescriptor,
+    pub outset: SideOffsets2D<f32>,
+    pub repeat_horizontal: RepeatMode,
+    pub repeat_vertical: RepeatMode,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct GradientBorder {
+    pub gradient: Gradient,
+    pub outset: SideOffsets2D<f32>,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct RadialGradientBorder {
+    pub gradient: RadialGradient,
+    pub outset: SideOffsets2D<f32>,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub enum BorderDetails {
+    Normal(NormalBorder),
+    Image(ImageBorder),
+    Gradient(GradientBorder),
+    RadialGradient(RadialGradientBorder),
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct BorderDisplayItem {
+    pub widths: BorderWidths,
+    pub details: BorderDetails,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct BorderRadius {
+    pub top_left: LayoutSize,
+    pub top_right: LayoutSize,
+    pub bottom_left: LayoutSize,
+    pub bottom_right: LayoutSize,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct BorderWidths {
+    pub left: f32,
+    pub top: f32,
+    pub right: f32,
+    pub bottom: f32,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct BorderSide {
+    pub color: ColorF,
+    pub style: BorderStyle,
+}
+
+#[repr(u32)]
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub enum BorderStyle {
+    None    = 0,
+    Solid   = 1,
+    Double  = 2,
+    Dotted  = 3,
+    Dashed  = 4,
+    Hidden  = 5,
+    Groove  = 6,
+    Ridge   = 7,
+    Inset   = 8,
+    Outset  = 9,
+}
+
+#[repr(u32)]
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub enum BoxShadowClipMode {
+    None    = 0,
+    Outset  = 1,
+    Inset   = 2,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct BoxShadowDisplayItem {
+    pub box_bounds: LayoutRect,
+    pub offset: LayoutPoint,
+    pub color: ColorF,
+    pub blur_radius: f32,
+    pub spread_radius: f32,
+    pub border_radius: f32,
+    pub clip_mode: BoxShadowClipMode,
+}
+
+#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Ord, PartialOrd)]
+pub enum ExtendMode {
+    Clamp,
+    Repeat,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct Gradient {
+    pub start_point: LayoutPoint,
+    pub end_point: LayoutPoint,
+    pub stops: ItemRange,
+    pub extend_mode: ExtendMode,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct GradientDisplayItem {
+    pub gradient: Gradient,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct GradientStop {
+    pub offset: f32,
+    pub color: ColorF,
+}
+known_heap_size!(0, GradientStop);
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct RadialGradient {
+    pub start_center: LayoutPoint,
+    pub start_radius: f32,
+    pub end_center: LayoutPoint,
+    pub end_radius: f32,
+    pub stops: ItemRange,
+    pub extend_mode: ExtendMode,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct RadialGradientDisplayItem {
+    pub gradient: RadialGradient,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct PushStackingContextDisplayItem {
+    pub stacking_context: StackingContext,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct StackingContext {
+    pub scroll_policy: ScrollPolicy,
+    pub bounds: LayoutRect,
+    pub z_index: i32,
+    pub transform: Option<PropertyBinding<LayoutTransform>>,
+    pub perspective: Option<LayoutTransform>,
+    pub mix_blend_mode: MixBlendMode,
+    pub filters: ItemRange,
+}
+
+#[repr(C)]
+#[repr(u32)]
+#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
+pub enum ScrollPolicy {
+    Scrollable  = 0,
+    Fixed       = 1,
+}
+
+known_heap_size!(0, ScrollPolicy);
+
+#[repr(u32)]
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
+pub enum MixBlendMode {
+    Normal      = 0,
+    Multiply    = 1,
+    Screen      = 2,
+    Overlay     = 3,
+    Darken      = 4,
+    Lighten     = 5,
+    ColorDodge  = 6,
+    ColorBurn   = 7,
+    HardLight   = 8,
+    SoftLight   = 9,
+    Difference  = 10,
+    Exclusion   = 11,
+    Hue         = 12,
+    Saturation  = 13,
+    Color       = 14,
+    Luminosity  = 15,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
+pub enum FilterOp {
+    Blur(Au),
+    Brightness(f32),
+    Contrast(f32),
+    Grayscale(f32),
+    HueRotate(f32),
+    Invert(f32),
+    Opacity(PropertyBinding<f32>),
+    Saturate(f32),
+    Sepia(f32),
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct IframeDisplayItem {
+    pub pipeline_id: PipelineId,
+}
+
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct ImageDisplayItem {
+    pub image_key: ImageKey,
+    pub stretch_size: LayoutSize,
+    pub tile_spacing: LayoutSize,
+    pub image_rendering: ImageRendering,
+}
+
+#[repr(u32)]
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
+pub enum ImageRendering {
+    Auto        = 0,
+    CrispEdges  = 1,
+    Pixelated   = 2,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct YuvImageDisplayItem {
+    pub y_image_key: ImageKey,
+    pub u_image_key: ImageKey,
+    pub v_image_key: ImageKey,
+    pub color_space: YuvColorSpace,
+}
+
+#[repr(u32)]
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
+pub enum YuvColorSpace {
+    Rec601 = 1, // The values must match the ones in prim_shared.glsl
+    Rec709 = 2,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct ImageMask {
+    pub image: ImageKey,
+    pub rect: LayoutRect,
+    pub repeat: bool,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct ClipRegion {
+    pub main: LayoutRect,
+    pub complex: ItemRange,
+    pub image_mask: Option<ImageMask>,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct ComplexClipRegion {
+    /// The boundaries of the rectangle.
+    pub rect: LayoutRect,
+    /// Border radii of this rectangle.
+    pub radii: BorderRadius,
+}
+
+impl StackingContext {
+    pub fn new(scroll_policy: ScrollPolicy,
+               bounds: LayoutRect,
+               z_index: i32,
+               transform: Option<PropertyBinding<LayoutTransform>>,
+               perspective: Option<LayoutTransform>,
+               mix_blend_mode: MixBlendMode,
+               filters: Vec<FilterOp>,
+               auxiliary_lists_builder: &mut AuxiliaryListsBuilder)
+               -> StackingContext {
+        StackingContext {
+            scroll_policy: scroll_policy,
+            bounds: bounds,
+            z_index: z_index,
+            transform: transform,
+            perspective: perspective,
+            mix_blend_mode: mix_blend_mode,
+            filters: auxiliary_lists_builder.add_filters(&filters),
+        }
+    }
+}
 
 impl BorderRadius {
     pub fn zero() -> BorderRadius {
         BorderRadius {
             top_left: LayoutSize::new(0.0, 0.0),
             top_right: LayoutSize::new(0.0, 0.0),
             bottom_left: LayoutSize::new(0.0, 0.0),
             bottom_right: LayoutSize::new(0.0, 0.0),
@@ -136,31 +504,53 @@ impl ComplexClipRegion {
         if xl <= xr && yt <= yb {
             Some(LayoutRect::new(LayoutPoint::new(xl, yt), LayoutSize::new(xr-xl, yb-yt)))
         } else {
             None
         }
     }
 }
 
-impl FontKey {
-    pub fn new(key0: u32, key1: u32) -> FontKey {
-        FontKey(key0, key1)
-    }
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
+pub struct ServoScrollRootId(pub usize);
+
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
+pub struct ScrollLayerId {
+    pub pipeline_id: PipelineId,
+    pub info: ScrollLayerInfo,
 }
 
-impl ImageKey {
-    pub fn new(key0: u32, key1: u32) -> ImageKey {
-        ImageKey(key0, key1)
+impl ScrollLayerId {
+    pub fn root_scroll_layer(pipeline_id: PipelineId) -> ScrollLayerId {
+        ScrollLayerId {
+            pipeline_id: pipeline_id,
+            info: ScrollLayerInfo::Scrollable(0),
+        }
+    }
+
+    pub fn root_reference_frame(pipeline_id: PipelineId) -> ScrollLayerId {
+        ScrollLayerId {
+            pipeline_id: pipeline_id,
+            info: ScrollLayerInfo::ReferenceFrame(0),
+        }
+    }
+
+    pub fn is_reference_frame(&self) -> bool {
+        match self.info {
+            ScrollLayerInfo::Scrollable(..) => false,
+            ScrollLayerInfo::ReferenceFrame(..) => true,
+        }
+    }
+
+    pub fn new(pipeline_id: PipelineId, index: usize) -> ScrollLayerId {
+        ScrollLayerId {
+            pipeline_id: pipeline_id,
+            info: ScrollLayerInfo::Scrollable(index),
+        }
     }
 }
 
-impl ScrollLayerId {
-    pub fn new(pipeline_id: PipelineId,
-               index: usize,
-               scroll_root_id: ServoScrollRootId)
-               -> ScrollLayerId {
-        ScrollLayerId {
-            pipeline_id: pipeline_id,
-            info: ScrollLayerInfo::Scrollable(index, scroll_root_id),
-        }
-    }
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
+pub enum ScrollLayerInfo {
+    Scrollable(usize),
+    ReferenceFrame(usize),
 }
--- a/gfx/webrender_traits/src/display_list.rs
+++ b/gfx/webrender_traits/src/display_list.rs
@@ -1,26 +1,63 @@
 /* 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 std::mem;
 use std::slice;
-use {AuxiliaryLists, AuxiliaryListsDescriptor, BorderDisplayItem};
-use {BoxShadowClipMode, BoxShadowDisplayItem, BuiltDisplayList};
-use {BuiltDisplayListDescriptor, ClipRegion, ComplexClipRegion, ColorF};
-use {DisplayItem, ExtendMode, FilterOp, YuvColorSpace};
-use {FontKey, GlyphInstance, GradientDisplayItem, RadialGradientDisplayItem, GradientStop, IframeDisplayItem};
-use {ImageDisplayItem, ImageKey, ImageMask, ImageRendering, ItemRange, MixBlendMode, PipelineId};
-use {PushScrollLayerItem, PushStackingContextDisplayItem, RectangleDisplayItem, ScrollLayerId};
-use {ScrollPolicy, ServoScrollRootId, SpecificDisplayItem, StackingContext, TextDisplayItem};
-use {WebGLContextId, WebGLDisplayItem, YuvImageDisplayItem};
-use {LayoutTransform, LayoutPoint, LayoutRect, LayoutSize};
-use {BorderDetails, BorderWidths, GlyphOptions, PropertyBinding};
+use {BorderDetails, BorderDisplayItem, BorderWidths, BoxShadowClipMode, BoxShadowDisplayItem};
+use {ClipDisplayItem, ClipRegion, ColorF, ComplexClipRegion, DisplayItem, ExtendMode, FilterOp};
+use {FontKey, GlyphInstance, GlyphOptions, Gradient, GradientDisplayItem, GradientStop};
+use {IframeDisplayItem, ImageDisplayItem, ImageKey, ImageMask, ImageRendering, ItemRange};
+use {LayoutPoint, LayoutRect, LayoutSize, LayoutTransform, MixBlendMode, PipelineId};
+use {PropertyBinding, PushStackingContextDisplayItem, RadialGradient, RadialGradientDisplayItem};
+use {RectangleDisplayItem, ScrollLayerId, ScrollPolicy, ServoScrollRootId, SpecificDisplayItem};
+use {StackingContext, TextDisplayItem, WebGLContextId, WebGLDisplayItem, YuvColorSpace};
+use YuvImageDisplayItem;
+
+#[derive(Clone, Deserialize, Serialize)]
+pub struct AuxiliaryLists {
+    /// The concatenation of: gradient stops, complex clip regions, filters, and glyph instances,
+    /// in that order.
+    data: Vec<u8>,
+    descriptor: AuxiliaryListsDescriptor,
+}
+
+/// Describes the memory layout of the auxiliary lists.
+///
+/// Auxiliary lists consist of some number of gradient stops, complex clip regions, filters, and
+/// glyph instances, in that order.
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
+pub struct AuxiliaryListsDescriptor {
+    gradient_stops_size: usize,
+    complex_clip_regions_size: usize,
+    filters_size: usize,
+    glyph_instances_size: usize,
+}
+
+/// A display list.
+#[derive(Clone, Deserialize, Serialize)]
+pub struct BuiltDisplayList {
+    data: Vec<u8>,
+    descriptor: BuiltDisplayListDescriptor,
+}
+
+/// Describes the memory layout of a display list.
+///
+/// A display list consists of some number of display list items, followed by a number of display
+/// items.
+#[repr(C)]
+#[derive(Copy, Clone, Deserialize, Serialize)]
+pub struct BuiltDisplayListDescriptor {
+    /// The size in bytes of the display list items in this display list.
+    display_list_items_size: usize,
+}
 
 impl BuiltDisplayListDescriptor {
     pub fn size(&self) -> usize {
         self.display_list_items_size
     }
 }
 
 impl BuiltDisplayList {
@@ -46,109 +83,109 @@ impl BuiltDisplayList {
     }
 }
 
 #[derive(Clone)]
 pub struct DisplayListBuilder {
     pub list: Vec<DisplayItem>,
     auxiliary_lists_builder: AuxiliaryListsBuilder,
     pub pipeline_id: PipelineId,
+    clip_stack: Vec<ScrollLayerId>,
     next_scroll_layer_id: usize,
 }
 
 impl DisplayListBuilder {
     pub fn new(pipeline_id: PipelineId) -> DisplayListBuilder {
         DisplayListBuilder {
             list: Vec::new(),
             auxiliary_lists_builder: AuxiliaryListsBuilder::new(),
             pipeline_id: pipeline_id,
-            next_scroll_layer_id: 0,
+            clip_stack: vec![ScrollLayerId::root_scroll_layer(pipeline_id)],
+
+            // We start at 1 here, because the root scroll id is always 0.
+            next_scroll_layer_id: 1,
         }
     }
 
     pub fn print_display_list(&mut self) {
         for item in &self.list {
             println!("{:?}", item);
         }
     }
 
+    fn push_item(&mut self, item: SpecificDisplayItem, rect: LayoutRect, clip: ClipRegion) {
+        self.list.push(DisplayItem {
+            item: item,
+            rect: rect,
+            clip: clip,
+            scroll_layer_id: *self.clip_stack.last().unwrap(),
+        });
+    }
+
+    fn push_new_empty_item(&mut self, item: SpecificDisplayItem) {
+        self.list.push(DisplayItem {
+            item: item,
+            rect: LayoutRect::zero(),
+            clip: ClipRegion::simple(&LayoutRect::zero()),
+            scroll_layer_id: *self.clip_stack.last().unwrap(),
+        });
+    }
+
     pub fn push_rect(&mut self,
                      rect: LayoutRect,
                      clip: ClipRegion,
                      color: ColorF) {
-        let item = RectangleDisplayItem {
+        let item = SpecificDisplayItem::Rectangle(RectangleDisplayItem {
             color: color,
-        };
+        });
 
-        let display_item = DisplayItem {
-            item: SpecificDisplayItem::Rectangle(item),
-            rect: rect,
-            clip: clip,
-        };
-
-        self.list.push(display_item);
+        self.push_item(item, rect, clip);
     }
 
     pub fn push_image(&mut self,
                       rect: LayoutRect,
                       clip: ClipRegion,
                       stretch_size: LayoutSize,
                       tile_spacing: LayoutSize,
                       image_rendering: ImageRendering,
                       key: ImageKey) {
-        let item = ImageDisplayItem {
+        let item = SpecificDisplayItem::Image(ImageDisplayItem {
             image_key: key,
             stretch_size: stretch_size,
             tile_spacing: tile_spacing,
             image_rendering: image_rendering,
-        };
+        });
 
-        let display_item = DisplayItem {
-            item: SpecificDisplayItem::Image(item),
-            rect: rect,
-            clip: clip,
-        };
-
-        self.list.push(display_item);
+        self.push_item(item, rect, clip);
     }
 
     pub fn push_yuv_image(&mut self,
                           rect: LayoutRect,
                           clip: ClipRegion,
                           y_key: ImageKey,
                           u_key: ImageKey,
                           v_key: ImageKey,
                           color_space: YuvColorSpace) {
-        self.list.push(DisplayItem {
-            item: SpecificDisplayItem::YuvImage(YuvImageDisplayItem {
+        let item = SpecificDisplayItem::YuvImage(YuvImageDisplayItem {
                 y_image_key: y_key,
                 u_image_key: u_key,
                 v_image_key: v_key,
                 color_space: color_space,
-            }),
-            rect: rect,
-            clip: clip,
         });
+        self.push_item(item, rect, clip);
     }
 
     pub fn push_webgl_canvas(&mut self,
                              rect: LayoutRect,
                              clip: ClipRegion,
                              context_id: WebGLContextId) {
-        let item = WebGLDisplayItem {
+        let item = SpecificDisplayItem::WebGL(WebGLDisplayItem {
             context_id: context_id,
-        };
-
-        let display_item = DisplayItem {
-            item: SpecificDisplayItem::WebGL(item),
-            rect: rect,
-            clip: clip,
-        };
-
-        self.list.push(display_item);
+        });
+        self.push_item(item, rect, clip);
     }
 
     pub fn push_text(&mut self,
                      rect: LayoutRect,
                      clip: ClipRegion,
                      glyphs: Vec<GlyphInstance>,
                      font_key: FontKey,
                      color: ColorF,
@@ -157,207 +194,199 @@ impl DisplayListBuilder {
                      glyph_options: Option<GlyphOptions>) {
         // Sanity check - anything with glyphs bigger than this
         // is probably going to consume too much memory to render
         // efficiently anyway. This is specifically to work around
         // the font_advance.html reftest, which creates a very large
         // font as a crash test - the rendering is also ignored
         // by the azure renderer.
         if size < Au::from_px(4096) {
-            let item = TextDisplayItem {
+            let item = SpecificDisplayItem::Text(TextDisplayItem {
                 color: color,
                 glyphs: self.auxiliary_lists_builder.add_glyph_instances(&glyphs),
                 font_key: font_key,
                 size: size,
                 blur_radius: blur_radius,
                 glyph_options: glyph_options,
-            };
+            });
+
+            self.push_item(item, rect, clip);
+        }
+    }
 
-            let display_item = DisplayItem {
-                item: SpecificDisplayItem::Text(item),
-                rect: rect,
-                clip: clip,
-            };
+    pub fn create_gradient(&mut self,
+                           start_point: LayoutPoint,
+                           end_point: LayoutPoint,
+                           stops: Vec<GradientStop>,
+                           extend_mode: ExtendMode) -> Gradient {
+        Gradient {
+            start_point: start_point,
+            end_point: end_point,
+            stops: self.auxiliary_lists_builder.add_gradient_stops(&stops),
+            extend_mode: extend_mode,
+        }
+    }
 
-            self.list.push(display_item);
+    pub fn create_radial_gradient(&mut self,
+                                  start_center: LayoutPoint,
+                                  start_radius: f32,
+                                  end_center: LayoutPoint,
+                                  end_radius: f32,
+                                  stops: Vec<GradientStop>,
+                                  extend_mode: ExtendMode) -> RadialGradient {
+        RadialGradient {
+            start_center: start_center,
+            start_radius: start_radius,
+            end_center: end_center,
+            end_radius: end_radius,
+            stops: self.auxiliary_lists_builder.add_gradient_stops(&stops),
+            extend_mode: extend_mode,
         }
     }
 
     pub fn push_border(&mut self,
                        rect: LayoutRect,
                        clip: ClipRegion,
                        widths: BorderWidths,
                        details: BorderDetails) {
-        let item = BorderDisplayItem {
+        let item = SpecificDisplayItem::Border(BorderDisplayItem {
             details: details,
             widths: widths,
-        };
+        });
 
-        let display_item = DisplayItem {
-            item: SpecificDisplayItem::Border(item),
-            rect: rect,
-            clip: clip,
-        };
-
-        self.list.push(display_item);
+        self.push_item(item, rect, clip);
     }
 
     pub fn push_box_shadow(&mut self,
                            rect: LayoutRect,
                            clip: ClipRegion,
                            box_bounds: LayoutRect,
                            offset: LayoutPoint,
                            color: ColorF,
                            blur_radius: f32,
                            spread_radius: f32,
                            border_radius: f32,
                            clip_mode: BoxShadowClipMode) {
-        let item = BoxShadowDisplayItem {
+        let item = SpecificDisplayItem::BoxShadow(BoxShadowDisplayItem {
             box_bounds: box_bounds,
             offset: offset,
             color: color,
             blur_radius: blur_radius,
             spread_radius: spread_radius,
             border_radius: border_radius,
             clip_mode: clip_mode,
-        };
+        });
 
-        let display_item = DisplayItem {
-            item: SpecificDisplayItem::BoxShadow(item),
-            rect: rect,
-            clip: clip,
-        };
-
-        self.list.push(display_item);
+        self.push_item(item, rect, clip);
     }
 
     pub fn push_gradient(&mut self,
                          rect: LayoutRect,
                          clip: ClipRegion,
                          start_point: LayoutPoint,
                          end_point: LayoutPoint,
                          stops: Vec<GradientStop>,
                          extend_mode: ExtendMode) {
-        let item = GradientDisplayItem {
-            start_point: start_point,
-            end_point: end_point,
-            stops: self.auxiliary_lists_builder.add_gradient_stops(&stops),
-            extend_mode: extend_mode,
-        };
+        let item = SpecificDisplayItem::Gradient(GradientDisplayItem {
+            gradient: self.create_gradient(start_point, end_point, stops, extend_mode),
+        });
 
-        let display_item = DisplayItem {
-            item: SpecificDisplayItem::Gradient(item),
-            rect: rect,
-            clip: clip,
-        };
-
-        self.list.push(display_item);
+        self.push_item(item, rect, clip);
     }
 
     pub fn push_radial_gradient(&mut self,
                                 rect: LayoutRect,
                                 clip: ClipRegion,
                                 start_center: LayoutPoint,
                                 start_radius: f32,
                                 end_center: LayoutPoint,
                                 end_radius: f32,
                                 stops: Vec<GradientStop>,
                                 extend_mode: ExtendMode) {
-        let item = RadialGradientDisplayItem {
-            start_center: start_center,
-            start_radius: start_radius,
-            end_center: end_center,
-            end_radius: end_radius,
-            stops: self.auxiliary_lists_builder.add_gradient_stops(&stops),
-            extend_mode: extend_mode,
-        };
+        let item = SpecificDisplayItem::RadialGradient(RadialGradientDisplayItem {
+            gradient: self.create_radial_gradient(start_center, start_radius,
+                                                  end_center, end_radius,
+                                                  stops, extend_mode),
+        });
 
-        let display_item = DisplayItem {
-            item: SpecificDisplayItem::RadialGradient(item),
-            rect: rect,
-            clip: clip,
-        };
-
-        self.list.push(display_item);
+        self.push_item(item, rect, clip);
     }
 
     pub fn push_stacking_context(&mut self,
                                  scroll_policy: ScrollPolicy,
                                  bounds: LayoutRect,
                                  clip: ClipRegion,
                                  z_index: i32,
                                  transform: Option<PropertyBinding<LayoutTransform>>,
                                  perspective: Option<LayoutTransform>,
                                  mix_blend_mode: MixBlendMode,
                                  filters: Vec<FilterOp>) {
-        let stacking_context = StackingContext {
-            scroll_policy: scroll_policy,
-            bounds: bounds,
-            z_index: z_index,
-            transform: transform,
-            perspective: perspective,
-            mix_blend_mode: mix_blend_mode,
-            filters: self.auxiliary_lists_builder.add_filters(&filters),
-        };
+        let item = SpecificDisplayItem::PushStackingContext(PushStackingContextDisplayItem {
+            stacking_context: StackingContext {
+                scroll_policy: scroll_policy,
+                bounds: bounds,
+                z_index: z_index,
+                transform: transform,
+                perspective: perspective,
+                mix_blend_mode: mix_blend_mode,
+                filters: self.auxiliary_lists_builder.add_filters(&filters),
+            }
+        });
 
-        let item = DisplayItem {
-            item: SpecificDisplayItem::PushStackingContext(PushStackingContextDisplayItem {
-                stacking_context: stacking_context
-            }),
-            rect: LayoutRect::zero(),
-            clip: clip,
-        };
-        self.list.push(item);
+        self.push_item(item, LayoutRect::zero(), clip);
     }
 
     pub fn pop_stacking_context(&mut self) {
-        let item = DisplayItem {
-            item: SpecificDisplayItem::PopStackingContext,
-            rect: LayoutRect::zero(),
-            clip: ClipRegion::simple(&LayoutRect::zero()),
-        };
-        self.list.push(item);
+        self.push_new_empty_item(SpecificDisplayItem::PopStackingContext);
+    }
+
+    pub fn define_clip(&mut self,
+                       clip: ClipRegion,
+                       content_size: LayoutSize,
+                       scroll_root_id: Option<ServoScrollRootId>)
+                       -> ScrollLayerId {
+        let scroll_layer_id = self.next_scroll_layer_id;
+        self.next_scroll_layer_id += 1;
+
+        let id = ScrollLayerId::new(self.pipeline_id, scroll_layer_id);
+        let item = SpecificDisplayItem::Clip(ClipDisplayItem {
+            content_size: content_size,
+            id: id,
+            parent_id: *self.clip_stack.last().unwrap(),
+            scroll_root_id: scroll_root_id,
+        });
+
+        self.push_item(item, clip.main, clip);
+        id
     }
 
     pub fn push_scroll_layer(&mut self,
                              clip: ClipRegion,
                              content_size: LayoutSize,
-                             scroll_root_id: ServoScrollRootId) {
-        let scroll_layer_id = self.next_scroll_layer_id;
-        self.next_scroll_layer_id += 1;
+                             scroll_root_id: Option<ServoScrollRootId>) {
+        let id = self.define_clip(clip, content_size, scroll_root_id);
+        self.clip_stack.push(id);
+    }
 
-        let item = PushScrollLayerItem {
-            content_size: content_size,
-            id: ScrollLayerId::new(self.pipeline_id, scroll_layer_id, scroll_root_id),
-        };
+    pub fn push_clip_id(&mut self, id: ScrollLayerId) {
+        self.clip_stack.push(id);
+    }
 
-        let item = DisplayItem {
-            item: SpecificDisplayItem::PushScrollLayer(item),
-            rect: clip.main,
-            clip: clip,
-        };
-        self.list.push(item);
+    pub fn pop_clip_id(&mut self) {
+        self.clip_stack.pop();
+        assert!(self.clip_stack.len() > 0);
     }
 
     pub fn pop_scroll_layer(&mut self) {
-        let item = DisplayItem {
-            item: SpecificDisplayItem::PopScrollLayer,
-            rect: LayoutRect::zero(),
-            clip: ClipRegion::simple(&LayoutRect::zero()),
-        };
-        self.list.push(item);
+        self.pop_clip_id();
     }
 
     pub fn push_iframe(&mut self, rect: LayoutRect, clip: ClipRegion, pipeline_id: PipelineId) {
-        let item = DisplayItem {
-            item: SpecificDisplayItem::Iframe(IframeDisplayItem { pipeline_id: pipeline_id }),
-            rect: rect,
-            clip: clip,
-        };
-        self.list.push(item);
+        let item = SpecificDisplayItem::Iframe(IframeDisplayItem { pipeline_id: pipeline_id });
+        self.push_item(item, rect, clip);
     }
 
     // Don't use this function. It will go away.
     // We're using it as a hack in Gecko to retain parts sub-parts of display lists so that
     // we can regenerate them without building Gecko display items. 
     pub fn push_built_display_list(&mut self, dl: BuiltDisplayList, aux: AuxiliaryLists) {
         use SpecificDisplayItem::*;
         // It's important for us to make sure that all of ItemRange structures are relocated
@@ -365,25 +394,25 @@ impl DisplayListBuilder {
         // derive implementation that would let ItemRanges relocate themselves.
         for i in dl.all_display_items() {
             let mut i = *i;
             match i.item {
                 Text(ref mut item) => {
                     item.glyphs = self.auxiliary_lists_builder.add_glyph_instances(aux.glyph_instances(&item.glyphs));
                 }
                 Gradient(ref mut item) => {
-                    item.stops = self.auxiliary_lists_builder.add_gradient_stops(aux.gradient_stops(&item.stops));
+                    item.gradient.stops = self.auxiliary_lists_builder.add_gradient_stops(aux.gradient_stops(&item.gradient.stops));
                 }
                 RadialGradient(ref mut item) => {
-                    item.stops = self.auxiliary_lists_builder.add_gradient_stops(aux.gradient_stops(&item.stops));
+                    item.gradient.stops = self.auxiliary_lists_builder.add_gradient_stops(aux.gradient_stops(&item.gradient.stops));
                 }
                 PushStackingContext(ref mut item) => {
                     item.stacking_context.filters = self.auxiliary_lists_builder.add_filters(aux.filters(&item.stacking_context.filters));
                 }
-                Iframe(_) | PushScrollLayer(_) => {
+                Iframe(_) | Clip(_) => {
                     // We don't support relocating these
                     panic!();
                 }
                 _ => {}
             }
             i.clip.complex = self.auxiliary_lists_builder.add_complex_clip_regions(aux.complex_clip_regions(&i.clip.complex));
             self.list.push(i);
         }
new file mode 100644
--- /dev/null
+++ b/gfx/webrender_traits/src/font.rs
@@ -0,0 +1,152 @@
+/* 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::Point2D;
+use {ColorU, ColorF};
+
+#[cfg(target_os = "macos")] use core_graphics::font::CGFont;
+#[cfg(target_os = "windows")] use dwrote::FontDescriptor;
+
+
+#[cfg(target_os = "macos")]
+pub type NativeFontHandle = CGFont;
+
+/// Native fonts are not used on Linux; all fonts are raw.
+#[cfg(not(any(target_os = "macos", target_os = "windows")))]
+#[cfg_attr(not(any(target_os = "macos", target_os = "windows")), derive(Clone, Serialize, Deserialize))]
+pub struct NativeFontHandle;
+
+#[cfg(target_os = "windows")]
+pub type NativeFontHandle = FontDescriptor;
+
+#[repr(C)]
+#[derive(Copy, Clone, Deserialize, Serialize, Debug)]
+pub struct GlyphDimensions {
+    pub left: i32,
+    pub top: i32,
+    pub width: u32,
+    pub height: u32,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Ord, PartialOrd)]
+pub struct FontKey(pub u32, pub u32);
+
+impl FontKey {
+    pub fn new(key0: u32, key1: u32) -> FontKey {
+        FontKey(key0, key1)
+    }
+}
+
+#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Ord, PartialOrd)]
+pub enum FontRenderMode {
+    Mono,
+    Alpha,
+    Subpixel,
+}
+
+impl FontRenderMode {
+    // Skia quantizes subpixel offets into 1/4 increments.
+    // Given the absolute position, return the quantized increment
+    fn subpixel_quantize_offset(&self, pos: f32) -> SubpixelOffset {
+        if *self != FontRenderMode::Subpixel {
+            return SubpixelOffset::Zero;
+        }
+
+        const SUBPIXEL_ROUNDING :f32 = 0.125; // Skia chosen value.
+        let fraction = (pos + SUBPIXEL_ROUNDING).fract();
+
+        match fraction {
+            0.0...0.25 => SubpixelOffset::Zero,
+            0.25...0.5 => SubpixelOffset::Quarter,
+            0.5...0.75 => SubpixelOffset::Half,
+            0.75...1.0 => SubpixelOffset::ThreeQuarters,
+            _ => panic!("Should only be given the fractional part"),
+        }
+    }
+}
+
+#[repr(u8)]
+#[derive(Hash, Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize, Deserialize)]
+pub enum SubpixelOffset {
+    Zero            = 0,
+    Quarter         = 1,
+    Half            = 2,
+    ThreeQuarters   = 3,
+}
+
+impl Into<f64> for SubpixelOffset {
+    fn into(self) -> f64 {
+        match self {
+            SubpixelOffset::Zero => 0.0,
+            SubpixelOffset::Quarter => 0.25,
+            SubpixelOffset::Half => 0.5,
+            SubpixelOffset::ThreeQuarters => 0.75,
+        }
+    }
+}
+
+#[derive(Clone, Hash, PartialEq, Eq, Debug, Deserialize, Serialize, Ord, PartialOrd)]
+pub struct SubpixelPoint {
+    pub x: SubpixelOffset,
+    pub y: SubpixelOffset,
+}
+
+impl SubpixelPoint {
+    pub fn new(point: Point2D<f32>,
+               render_mode: FontRenderMode) -> SubpixelPoint {
+        SubpixelPoint {
+            x: render_mode.subpixel_quantize_offset(point.x),
+            y: render_mode.subpixel_quantize_offset(point.y),
+        }
+    }
+
+    pub fn to_f64(&self) -> (f64, f64) {
+        return (self.x.into(), self.y.into());
+    }
+
+    pub fn set_offset(&mut self, point: Point2D<f32>, render_mode: FontRenderMode) {
+        self.x = render_mode.subpixel_quantize_offset(point.x);
+        self.y = render_mode.subpixel_quantize_offset(point.y);
+    }
+}
+
+#[derive(Clone, Hash, PartialEq, Eq, Debug, Deserialize, Serialize, Ord, PartialOrd)]
+pub struct GlyphKey {
+    pub font_key: FontKey,
+    // The font size is in *device* pixels, not logical pixels.
+    // It is stored as an Au since we need sub-pixel sizes, but
+    // can't store as a f32 due to use of this type as a hash key.
+    // TODO(gw): Perhaps consider having LogicalAu and DeviceAu
+    //           or something similar to that.
+    pub size: Au,
+    pub index: u32,
+    pub color: ColorU,
+    pub subpixel_point: SubpixelPoint,
+}
+
+impl GlyphKey {
+    pub fn new(font_key: FontKey,
+               size: Au,
+               color: ColorF,
+               index: u32,
+               point: Point2D<f32>,
+               render_mode: FontRenderMode) -> GlyphKey {
+        GlyphKey {
+            font_key: font_key,
+            size: size,
+            color: ColorU::from(color),
+            index: index,
+            subpixel_point: SubpixelPoint::new(point, render_mode),
+        }
+    }
+}
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct GlyphInstance {
+    pub index: u32,
+    pub point: Point2D<f32>,
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender_traits/src/image.rs
@@ -0,0 +1,131 @@
+/* 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 std::sync::Arc;
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
+pub struct ImageKey(pub u32, pub u32);
+
+impl ImageKey {
+    pub fn new(key0: u32, key1: u32) -> ImageKey {
+        ImageKey(key0, key1)
+    }
+}
+
+/// An arbitrary identifier for an external image provided by the
+/// application. It must be a unique identifier for each external
+/// image.
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
+pub struct ExternalImageId(pub u64);
+
+#[repr(C)]
+#[repr(u32)]
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
+pub enum ImageFormat {
+    Invalid  = 0,
+    A8       = 1,
+    RGB8     = 2,
+    RGBA8    = 3,
+    RGBAF32  = 4,
+}
+
+impl ImageFormat {
+    pub fn bytes_per_pixel(self) -> Option<u32> {
+        match self {
+            ImageFormat::A8 => Some(1),
+            ImageFormat::RGB8 => Some(3),
+            ImageFormat::RGBA8 => Some(4),
+            ImageFormat::RGBAF32 => Some(16),
+            ImageFormat::Invalid => None,
+        }
+    }
+}
+
+#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Serialize)]
+pub struct ImageDescriptor {
+    pub format: ImageFormat,
+    pub width: u32,
+    pub height: u32,
+    pub stride: Option<u32>,
+    pub offset: u32,
+    pub is_opaque: bool,
+}
+
+impl ImageDescriptor {
+    pub fn new(width: u32, height: u32, format: ImageFormat, is_opaque: bool) -> Self {
+        ImageDescriptor {
+            width: width,
+            height: height,
+            format: format,
+            stride: None,
+            offset: 0,
+            is_opaque: is_opaque,
+        }
+    }
+
+    pub fn compute_stride(&self) -> u32 {
+        self.stride.unwrap_or(self.width * self.format.bytes_per_pixel().unwrap())
+    }
+}
+
+#[derive(Clone, Serialize, Deserialize)]
+pub enum ImageData {
+    Raw(Arc<Vec<u8>>),
+    Blob(Arc<BlobImageData>),
+    ExternalHandle(ExternalImageId),
+    ExternalBuffer(ExternalImageId),
+}
+
+impl ImageData {
+    pub fn new(bytes: Vec<u8>) -> ImageData {
+        ImageData::Raw(Arc::new(bytes))
+    }
+
+    pub fn new_shared(bytes: Arc<Vec<u8>>) -> ImageData {
+        ImageData::Raw(bytes)
+    }
+
+    pub fn new_blob_image(commands: Vec<u8>) -> ImageData {
+        ImageData::Blob(Arc::new(commands))
+    }
+
+    pub fn new_shared_blob_image(commands: Arc<Vec<u8>>) -> ImageData {
+        ImageData::Blob(commands)
+    }
+}
+
+pub trait BlobImageRenderer: Send {
+    fn request_blob_image(&mut self,
+                            key: ImageKey,
+                            data: Arc<BlobImageData>,
+                            descriptor: &BlobImageDescriptor);
+    fn resolve_blob_image(&mut self, key: ImageKey) -> BlobImageResult;
+}
+
+pub type BlobImageData = Vec<u8>;
+
+pub type BlobImageResult = Result<RasterizedBlobImage, BlobImageError>;
+
+#[derive(Copy, Clone, Debug)]
+pub struct BlobImageDescriptor {
+    pub width: u32,
+    pub height: u32,
+    pub format: ImageFormat,
+    pub scale_factor: f32,
+}
+
+pub struct RasterizedBlobImage {
+    pub width: u32,
+    pub height: u32,
+    pub data: Vec<u8>,
+}
+
+#[derive(Clone, Debug)]
+pub enum BlobImageError {
+    Oom,
+    InvalidKey,
+    InvalidData,
+    Other(String),
+}
--- a/gfx/webrender_traits/src/lib.rs
+++ b/gfx/webrender_traits/src/lib.rs
@@ -20,21 +20,26 @@ extern crate serde;
 extern crate serde_derive;
 
 #[cfg(target_os = "macos")]
 extern crate core_graphics;
 
 #[cfg(target_os = "windows")]
 extern crate dwrote;
 
-include!("types.rs");
-
 mod units;
 mod api;
+mod color;
 pub mod channel;
 mod display_item;
 mod display_list;
-mod stacking_context;
+mod font;
+mod image;
 mod webgl;
 
-pub use api::RenderApi;
-pub use display_list::DisplayListBuilder;
+pub use api::*;
+pub use color::*;
+pub use display_item::*;
+pub use display_list::*;
+pub use font::*;
+pub use image::*;
 pub use units::*;
+pub use webgl::*;
deleted file mode 100644
--- a/gfx/webrender_traits/src/stacking_context.rs
+++ /dev/null
@@ -1,29 +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/. */
-
-use display_list::AuxiliaryListsBuilder;
-use {FilterOp, MixBlendMode, ScrollPolicy, StackingContext};
-use {LayoutTransform, LayoutRect, PropertyBinding};
-
-impl StackingContext {
-    pub fn new(scroll_policy: ScrollPolicy,
-               bounds: LayoutRect,
-               z_index: i32,
-               transform: Option<PropertyBinding<LayoutTransform>>,
-               perspective: Option<LayoutTransform>,
-               mix_blend_mode: MixBlendMode,
-               filters: Vec<FilterOp>,
-               auxiliary_lists_builder: &mut AuxiliaryListsBuilder)
-               -> StackingContext {
-        StackingContext {
-            scroll_policy: scroll_policy,
-            bounds: bounds,
-            z_index: z_index,
-            transform: transform,
-            perspective: perspective,
-            mix_blend_mode: mix_blend_mode,
-            filters: auxiliary_lists_builder.add_filters(&filters),
-        }
-    }
-}
deleted file mode 100644
--- a/gfx/webrender_traits/src/types.rs
+++ /dev/null
@@ -1,1243 +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/. */
-
-// Every serialisable type is defined in this file to only codegen one file
-// for the serde implementations.
-
-use app_units::Au;
-use euclid::{Point2D, SideOffsets2D};
-use channel::{PayloadSender, MsgSender};
-#[cfg(feature = "nightly")]
-use core::nonzero::NonZero;
-use offscreen_gl_context::{GLContextAttributes, GLLimits};
-use std::fmt;
-use std::marker::PhantomData;
-use std::sync::Arc;
-
-#[cfg(target_os = "macos")] use core_graphics::font::CGFont;
-#[cfg(target_os = "windows")] use dwrote::FontDescriptor;
-
-#[derive(Debug, Copy, Clone)]
-pub enum RendererKind {
-    Native,
-    OSMesa,
-}
-
-pub type TileSize = u16;
-
-#[derive(Clone, Deserialize, Serialize)]
-pub enum ApiMsg {
-    AddRawFont(FontKey, Vec<u8>),
-    AddNativeFont(FontKey, NativeFontHandle),
-    DeleteFont(FontKey),
-    /// Gets the glyph dimensions
-    GetGlyphDimensions(Vec<GlyphKey>, MsgSender<Vec<Option<GlyphDimensions>>>),
-    /// Adds an image from the resource cache.
-    AddImage(ImageKey, ImageDescriptor, ImageData, Option<TileSize>),
-    /// Updates the the resource cache with the new image data.
-    UpdateImage(ImageKey, ImageDescriptor, Vec<u8>),
-    /// Drops an image from the resource cache.
-    DeleteImage(ImageKey),
-    CloneApi(MsgSender<IdNamespace>),
-    /// Supplies a new frame to WebRender.
-    ///
-    /// After receiving this message, WebRender will read the display list, followed by the
-    /// auxiliary lists, from the payload channel.
-    SetRootDisplayList(Option<ColorF>,
-                       Epoch,
-                       PipelineId,
-                       LayoutSize,
-                       BuiltDisplayListDescriptor,
-                       AuxiliaryListsDescriptor,
-                       bool),
-    SetPageZoom(PageZoomFactor),
-    SetRootPipeline(PipelineId),
-    SetWindowParameters(DeviceUintSize, DeviceUintRect),
-    Scroll(ScrollLocation, WorldPoint, ScrollEventPhase),
-    ScrollLayersWithScrollId(LayoutPoint, PipelineId, ServoScrollRootId),
-    TickScrollingBounce,
-    TranslatePointToLayerSpace(WorldPoint, MsgSender<(LayoutPoint, PipelineId)>),
-    GetScrollLayerState(MsgSender<Vec<ScrollLayerState>>),
-    RequestWebGLContext(DeviceIntSize, GLContextAttributes, MsgSender<Result<(WebGLContextId, GLLimits), String>>),
-    ResizeWebGLContext(WebGLContextId, DeviceIntSize),
-    WebGLCommand(WebGLContextId, WebGLCommand),
-    GenerateFrame(Option<DynamicProperties>),
-    // WebVR commands that must be called in the WebGL render thread.
-    VRCompositorCommand(WebGLContextId, VRCompositorCommand),
-    /// An opaque handle that must be passed to the render notifier. It is used by Gecko
-    /// to forward gecko-specific messages to the render thread preserving the ordering
-    /// within the other messages.
-    ExternalEvent(ExternalEvent),
-    ShutDown,
-}
-
-/// An opaque pointer-sized value.
-#[repr(C)]
-#[derive(Clone, Deserialize, Serialize)]
-pub struct ExternalEvent {
-    raw: usize,
-}
-
-unsafe impl Send for ExternalEvent {}
-
-impl ExternalEvent {
-    pub fn from_raw(raw: usize) -> Self { ExternalEvent { raw: raw } }
-    /// Consumes self to make it obvious that the event should be forwarded only once.
-    pub fn unwrap(self) -> usize { self.raw }
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, Eq, Hash)]
-pub struct PropertyBindingId {
-    namespace: u32,
-    uid: u32,
-}
-
-/// A unique key that is used for connecting animated property
-/// values to bindings in the display list.
-#[repr(C)]
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct PropertyBindingKey<T> {
-    pub id: PropertyBindingId,
-    _phantom: PhantomData<T>,
-}
-
-/// Construct a property value from a given key and value.
-impl<T: Copy> PropertyBindingKey<T> {
-    pub fn with(&self, value: T) -> PropertyValue<T> {
-        PropertyValue {
-            key: *self,
-            value: value,
-        }
-    }
-}
-
-/// A binding property can either be a specific value
-/// (the normal, non-animated case) or point to a binding location
-/// to fetch the current value from.
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub enum PropertyBinding<T> {
-    Value(T),
-    Binding(PropertyBindingKey<T>),
-}
-
-impl<T> From<T> for PropertyBinding<T> {
-    fn from(value: T) -> PropertyBinding<T> {
-        PropertyBinding::Value(value)
-    }
-}
-
-impl<T> From<PropertyBindingKey<T>> for PropertyBinding<T> {
-    fn from(key: PropertyBindingKey<T>) -> PropertyBinding<T> {
-        PropertyBinding::Binding(key)
-    }
-}
-
-/// The current value of an animated property. This is
-/// supplied by the calling code.
-#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
-pub struct PropertyValue<T> {
-    pub key: PropertyBindingKey<T>,
-    pub value: T,
-}
-
-/// When using generate_frame(), a list of PropertyValue structures
-/// can optionally be supplied to provide the current value of any
-/// animated properties.
-#[derive(Clone, Deserialize, Serialize, Debug)]
-pub struct DynamicProperties {
-    pub transforms: Vec<PropertyValue<LayoutTransform>>,
-    pub floats: Vec<PropertyValue<f32>>,
-}
-
-#[repr(C)]
-#[derive(Copy, Clone, Deserialize, Serialize, Debug)]
-pub struct GlyphDimensions {
-    pub left: i32,
-    pub top: i32,
-    pub width: u32,
-    pub height: u32,
-}
-
-#[derive(Clone, Deserialize, Serialize)]
-pub struct AuxiliaryLists {
-    /// The concatenation of: gradient stops, complex clip regions, filters, and glyph instances,
-    /// in that order.
-    data: Vec<u8>,
-    descriptor: AuxiliaryListsDescriptor,
-}
-
-/// Describes the memory layout of the auxiliary lists.
-///
-/// Auxiliary lists consist of some number of gradient stops, complex clip regions, filters, and
-/// glyph instances, in that order.
-#[repr(C)]
-#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
-pub struct AuxiliaryListsDescriptor {
-    gradient_stops_size: usize,
-    complex_clip_regions_size: usize,
-    filters_size: usize,
-    glyph_instances_size: usize,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct NormalBorder {
-    pub left: BorderSide,
-    pub right: BorderSide,
-    pub top: BorderSide,
-    pub bottom: BorderSide,
-    pub radius: BorderRadius,
-}
-
-#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
-pub enum RepeatMode {
-    Stretch,
-    Repeat,
-    Round,
-    Space,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct NinePatchDescriptor {
-    pub width: u32,
-    pub height: u32,
-    pub slice: SideOffsets2D<u32>,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct ImageBorder {
-    pub image_key: ImageKey,
-    pub patch: NinePatchDescriptor,
-    pub outset: SideOffsets2D<f32>,
-    pub repeat_horizontal: RepeatMode,
-    pub repeat_vertical: RepeatMode,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub enum BorderDetails {
-    Normal(NormalBorder),
-    Image(ImageBorder),
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct BorderDisplayItem {
-    pub widths: BorderWidths,
-    pub details: BorderDetails,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct BorderRadius {
-    pub top_left: LayoutSize,
-    pub top_right: LayoutSize,
-    pub bottom_left: LayoutSize,
-    pub bottom_right: LayoutSize,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct BorderWidths {
-    pub left: f32,
-    pub top: f32,
-    pub right: f32,
-    pub bottom: f32,
-}
-
-#[repr(C)]
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct BorderSide {
-    pub color: ColorF,
-    pub style: BorderStyle,
-}
-
-#[repr(u32)]
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub enum BorderStyle {
-    None    = 0,
-    Solid   = 1,
-    Double  = 2,
-    Dotted  = 3,
-    Dashed  = 4,
-    Hidden  = 5,
-    Groove  = 6,
-    Ridge   = 7,
-    Inset   = 8,
-    Outset  = 9,
-}
-
-#[repr(u32)]
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub enum BoxShadowClipMode {
-    None    = 0,
-    Outset  = 1,
-    Inset   = 2,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct BoxShadowDisplayItem {
-    pub box_bounds: LayoutRect,
-    pub offset: LayoutPoint,
-    pub color: ColorF,
-    pub blur_radius: f32,
-    pub spread_radius: f32,
-    pub border_radius: f32,
-    pub clip_mode: BoxShadowClipMode,
-}
-
-/// A display list.
-#[derive(Clone, Deserialize, Serialize)]
-pub struct BuiltDisplayList {
-    data: Vec<u8>,
-    descriptor: BuiltDisplayListDescriptor,
-}
-
-/// Describes the memory layout of a display list.
-///
-/// A display list consists of some number of display list items, followed by a number of display
-/// items.
-#[repr(C)]
-#[derive(Copy, Clone, Deserialize, Serialize)]
-pub struct BuiltDisplayListDescriptor {
-    /// The size in bytes of the display list items in this display list.
-    display_list_items_size: usize,
-}
-
-#[repr(C)]
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct ColorF {
-    pub r: f32,
-    pub g: f32,
-    pub b: f32,
-    pub a: f32,
-}
-known_heap_size!(0, ColorF);
-
-#[repr(C)]
-#[derive(Clone, Copy, Hash, Eq, Debug, Deserialize, PartialEq, PartialOrd, Ord, Serialize)]
-pub struct ColorU {
-    pub r: u8,
-    pub g: u8,
-    pub b: u8,
-    pub a: u8,
-}
-
-impl From<ColorF> for ColorU {
-    fn from(color: ColorF) -> ColorU {
-        ColorU {
-            r: ColorU::round_to_int(color.r),
-            g: ColorU::round_to_int(color.g),
-            b: ColorU::round_to_int(color.b),
-            a: ColorU::round_to_int(color.a),
-        }
-    }
-}
-
-impl Into<ColorF> for ColorU {
-    fn into(self) -> ColorF {
-        ColorF {
-            r: self.r as f32 / 255.0,
-            g: self.g as f32 / 255.0,
-            b: self.b as f32 / 255.0,
-            a: self.a as f32 / 255.0,
-        }
-    }
-}
-
-impl ColorU {
-    fn round_to_int(x: f32) -> u8 {
-        debug_assert!((0.0 <= x) && (x <= 1.0));
-        let f = (255.0 * x) + 0.5;
-        let val = f.floor();
-        debug_assert!(val <= 255.0);
-        val as u8
-    }
-
-    pub fn new(r: u8, g: u8, b: u8, a: u8) -> ColorU {
-        ColorU {
-            r: r,
-            g: g,
-            b: b,
-            a: a,
-        }
-    }
-}
-
-#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Serialize)]
-pub struct ImageDescriptor {
-    pub format: ImageFormat,
-    pub width: u32,
-    pub height: u32,
-    pub stride: Option<u32>,
-    pub offset: u32,
-    pub is_opaque: bool,
-}
-
-impl ImageDescriptor {
-    pub fn new(width: u32, height: u32, format: ImageFormat, is_opaque: bool) -> Self {
-        ImageDescriptor {
-            width: width,
-            height: height,
-            format: format,
-            stride: None,
-            offset: 0,
-            is_opaque: is_opaque,
-        }
-    }
-
-    pub fn compute_stride(&self) -> u32 {
-        self.stride.unwrap_or(self.width * self.format.bytes_per_pixel().unwrap())
-    }
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct ImageMask {
-    pub image: ImageKey,
-    pub rect: LayoutRect,
-    pub repeat: bool,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct ClipRegion {
-    pub main: LayoutRect,
-    pub complex: ItemRange,
-    pub image_mask: Option<ImageMask>,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct ComplexClipRegion {
-    /// The boundaries of the rectangle.
-    pub rect: LayoutRect,
-    /// Border radii of this rectangle.
-    pub radii: BorderRadius,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct DisplayItem {
-    pub item: SpecificDisplayItem,
-    pub rect: LayoutRect,
-    pub clip: ClipRegion,
-}
-
-#[repr(C)]
-#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
-pub struct Epoch(pub u32);
-
-#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Ord, PartialOrd)]
-pub enum ExtendMode {
-    Clamp,
-    Repeat,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
-pub enum FilterOp {
-    Blur(Au),
-    Brightness(f32),
-    Contrast(f32),
-    Grayscale(f32),
-    HueRotate(f32),
-    Invert(f32),
-    Opacity(PropertyBinding<f32>),
-    Saturate(f32),
-    Sepia(f32),
-}
-
-#[repr(C)]
-#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Ord, PartialOrd)]
-pub struct FontKey(pub u32, pub u32);
-
-#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Ord, PartialOrd)]
-pub enum FontRenderMode {
-    Mono,
-    Alpha,
-    Subpixel,
-}
-
-impl FontRenderMode {
-    // Skia quantizes subpixel offets into 1/4 increments.
-    // Given the absolute position, return the quantized increment
-    fn subpixel_quantize_offset(&self, pos: f32) -> SubpixelOffset {
-        if *self != FontRenderMode::Subpixel {
-            return SubpixelOffset::Zero;
-        }
-
-        const SUBPIXEL_ROUNDING :f32 = 0.125; // Skia chosen value.
-        let fraction = (pos + SUBPIXEL_ROUNDING).fract();
-
-        match fraction {
-            0.0...0.25 => SubpixelOffset::Zero,
-            0.25...0.5 => SubpixelOffset::Quarter,
-            0.5...0.75 => SubpixelOffset::Half,
-            0.75...1.0 => SubpixelOffset::ThreeQuarters,
-            _ => panic!("Should only be given the fractional part"),
-        }
-    }
-}
-
-#[repr(u8)]
-#[derive(Hash, Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize, Deserialize)]
-pub enum SubpixelOffset {
-    Zero            = 0,
-    Quarter         = 1,
-    Half            = 2,
-    ThreeQuarters   = 3,
-}
-
-impl Into<f64> for SubpixelOffset {
-    fn into(self) -> f64 {
-        match self {
-            SubpixelOffset::Zero => 0.0,
-            SubpixelOffset::Quarter => 0.25,
-            SubpixelOffset::Half => 0.5,
-            SubpixelOffset::ThreeQuarters => 0.75,
-        }
-    }
-}
-
-#[derive(Clone, Hash, PartialEq, Eq, Debug, Deserialize, Serialize, Ord, PartialOrd)]
-pub struct SubpixelPoint {
-    pub x: SubpixelOffset,
-    pub y: SubpixelOffset,
-}
-
-impl SubpixelPoint {
-    pub fn new(point: Point2D<f32>,
-               render_mode: FontRenderMode) -> SubpixelPoint {
-        SubpixelPoint {
-            x: render_mode.subpixel_quantize_offset(point.x),
-            y: render_mode.subpixel_quantize_offset(point.y),
-        }
-    }
-
-    pub fn to_f64(&self) -> (f64, f64) {
-        return (self.x.into(), self.y.into());
-    }
-
-    pub fn set_offset(&mut self, point: Point2D<f32>, render_mode: FontRenderMode) {
-        self.x = render_mode.subpixel_quantize_offset(point.x);
-        self.y = render_mode.subpixel_quantize_offset(point.y);
-    }
-}
-
-#[derive(Clone, Hash, PartialEq, Eq, Debug, Deserialize, Serialize, Ord, PartialOrd)]
-pub struct GlyphKey {
-    pub font_key: FontKey,
-    // The font size is in *device* pixels, not logical pixels.
-    // It is stored as an Au since we need sub-pixel sizes, but
-    // can't store as a f32 due to use of this type as a hash key.
-    // TODO(gw): Perhaps consider having LogicalAu and DeviceAu
-    //           or something similar to that.
-    pub size: Au,
-    pub index: u32,
-    pub color: ColorU,
-    pub subpixel_point: SubpixelPoint,
-}
-
-impl GlyphKey {
-    pub fn new(font_key: FontKey,
-               size: Au,
-               color: ColorF,
-               index: u32,
-               point: Point2D<f32>,
-               render_mode: FontRenderMode) -> GlyphKey {
-        GlyphKey {
-            font_key: font_key,
-            size: size,
-            color: ColorU::from(color),
-            index: index,
-            subpixel_point: SubpixelPoint::new(point, render_mode),
-        }
-    }
-}
-
-#[repr(u32)]
-#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
-pub enum FragmentType {
-    FragmentBody        = 0,
-    BeforePseudoContent = 1,
-    AfterPseudoContent  = 2,
-}
-
-#[repr(C)]
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct GlyphInstance {
-    pub index: u32,
-    pub point: Point2D<f32>,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct GradientDisplayItem {
-    pub start_point: LayoutPoint,
-    pub end_point: LayoutPoint,
-    pub stops: ItemRange,
-    pub extend_mode: ExtendMode,
-}
-
-#[repr(C)]
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct GradientStop {
-    pub offset: f32,
-    pub color: ColorF,
-}
-known_heap_size!(0, GradientStop);
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct RadialGradientDisplayItem {
-    pub start_center: LayoutPoint,
-    pub start_radius: f32,
-    pub end_center: LayoutPoint,
-    pub end_radius: f32,
-    pub stops: ItemRange,
-    pub extend_mode: ExtendMode,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct PushStackingContextDisplayItem {
-    pub stacking_context: StackingContext,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct PushScrollLayerItem {
-    pub content_size: LayoutSize,
-    pub id: ScrollLayerId,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct IframeDisplayItem {
-    pub pipeline_id: PipelineId,
-}
-
-#[repr(C)]
-#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
-pub struct IdNamespace(pub u32);
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct ImageDisplayItem {
-    pub image_key: ImageKey,
-    pub stretch_size: LayoutSize,
-    pub tile_spacing: LayoutSize,
-    pub image_rendering: ImageRendering,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct YuvImageDisplayItem {
-    pub y_image_key: ImageKey,
-    pub u_image_key: ImageKey,
-    pub v_image_key: ImageKey,
-    pub color_space: YuvColorSpace,
-}
-
-#[repr(C)]
-#[repr(u32)]
-#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
-pub enum ImageFormat {
-    Invalid  = 0,
-    A8       = 1,
-    RGB8     = 2,
-    RGBA8    = 3,
-    RGBAF32  = 4,
-}
-
-impl ImageFormat {
-    pub fn bytes_per_pixel(self) -> Option<u32> {
-        match self {
-            ImageFormat::A8 => Some(1),
-            ImageFormat::RGB8 => Some(3),
-            ImageFormat::RGBA8 => Some(4),
-            ImageFormat::RGBAF32 => Some(16),
-            ImageFormat::Invalid => None,
-        }
-    }
-}
-
-#[repr(C)]
-#[repr(u32)]
-#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
-pub enum YuvColorSpace {
-    Rec601 = 1, // The values must match the ones in prim_shared.glsl
-    Rec709 = 2,
-}
-
-/// An arbitrary identifier for an external image provided by the
-/// application. It must be a unique identifier for each external
-/// image.
-#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
-pub struct ExternalImageId(pub u64);
-
-pub trait BlobImageRenderer: Send {
-    fn request_blob_image(&mut self,
-                            key: ImageKey,
-                            data: Arc<BlobImageData>,
-                            descriptor: &BlobImageDescriptor);
-    fn resolve_blob_image(&mut self, key: ImageKey) -> BlobImageResult;
-}
-
-pub type BlobImageData = Vec<u8>;
-
-#[derive(Copy, Clone, Debug)]
-pub struct BlobImageDescriptor {
-    pub width: u32,
-    pub height: u32,
-    pub format: ImageFormat,
-    pub scale_factor: f32,
-}
-
-pub struct RasterizedBlobImage {
-    pub width: u32,
-    pub height: u32,
-    pub data: Vec<u8>,
-}
-
-#[derive(Clone, Debug)]
-pub enum BlobImageError {
-    Oom,
-    InvalidKey,
-    InvalidData,
-    Other(String),
-}
-
-pub type BlobImageResult = Result<RasterizedBlobImage, BlobImageError>;
-
-#[derive(Clone, Serialize, Deserialize)]
-pub enum ImageData {
-    Raw(Arc<Vec<u8>>),
-    Blob(Arc<BlobImageData>),
-    ExternalHandle(ExternalImageId),
-    ExternalBuffer(ExternalImageId),
-}
-
-impl ImageData {
-    pub fn new(bytes: Vec<u8>) -> ImageData {
-        ImageData::Raw(Arc::new(bytes))
-    }
-
-    pub fn new_shared(bytes: Arc<Vec<u8>>) -> ImageData {
-        ImageData::Raw(bytes)
-    }
-
-    pub fn new_blob_image(commands: Vec<u8>) -> ImageData {
-        ImageData::Blob(Arc::new(commands))
-    }
-
-    pub fn new_shared_blob_image(commands: Arc<Vec<u8>>) -> ImageData {
-        ImageData::Blob(commands)
-    }
-}
-
-#[repr(C)]
-#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
-pub struct ImageKey(pub u32, pub u32);
-
-#[repr(u32)]
-#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
-pub enum ImageRendering {
-    Auto        = 0,
-    CrispEdges  = 1,
-    Pixelated   = 2,
-}
-
-#[repr(C)]
-#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
-pub struct ItemRange {
-    pub start: usize,
-    pub length: usize,
-}
-
-#[repr(C)]
-#[repr(u32)]
-#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
-pub enum MixBlendMode {
-    Normal      = 0,
-    Multiply    = 1,
-    Screen      = 2,
-    Overlay     = 3,
-    Darken      = 4,
-    Lighten     = 5,
-    ColorDodge  = 6,
-    ColorBurn   = 7,
-    HardLight   = 8,
-    SoftLight   = 9,
-    Difference  = 10,
-    Exclusion   = 11,
-    Hue         = 12,
-    Saturation  = 13,
-    Color       = 14,
-    Luminosity  = 15,
-}
-
-#[cfg(target_os = "macos")]
-pub type NativeFontHandle = CGFont;
-
-/// Native fonts are not used on Linux; all fonts are raw.
-#[cfg(not(any(target_os = "macos", target_os = "windows")))]
-#[cfg_attr(not(any(target_os = "macos", target_os = "windows")), derive(Clone, Serialize, Deserialize))]
-pub struct NativeFontHandle;
-
-#[cfg(target_os = "windows")]
-pub type NativeFontHandle = FontDescriptor;
-
-#[repr(C)]
-#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
-pub struct PipelineId(pub u32, pub u32);
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct RectangleDisplayItem {
-    pub color: ColorF,
-}
-
-#[derive(Clone, Deserialize, Serialize)]
-pub struct RenderApiSender {
-    api_sender: MsgSender<ApiMsg>,
-    payload_sender: PayloadSender,
-}
-
-pub trait RenderNotifier: Send {
-    fn new_frame_ready(&mut self);
-    fn new_scroll_frame_ready(&mut self, composite_needed: bool);
-    fn external_event(&mut self, _evt: ExternalEvent) { unimplemented!() }
-    fn shut_down(&mut self) {}
-}
-
-// Trait to allow dispatching functions to a specific thread or event loop.
-pub trait RenderDispatcher: Send {
-    fn dispatch(&self, Box<Fn() + Send>);
-}
-
-#[repr(C)]
-#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
-pub struct ResourceId(pub u32);
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub enum ScrollEventPhase {
-    /// The user started scrolling.
-    Start,
-    /// The user performed a scroll. The Boolean flag indicates whether the user's fingers are
-    /// down, if a touchpad is in use. (If false, the event is a touchpad fling.)
-    Move(bool),
-    /// The user ended scrolling.
-    End,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
-pub struct ScrollLayerId {
-    pub pipeline_id: PipelineId,
-    pub info: ScrollLayerInfo,
-}
-
-impl ScrollLayerId {
-    pub fn root_scroll_layer(pipeline_id: PipelineId) -> ScrollLayerId {
-        ScrollLayerId {
-            pipeline_id: pipeline_id,
-            info: ScrollLayerInfo::Scrollable(0, ServoScrollRootId(0)),
-        }
-    }
-
-    pub fn root_reference_frame(pipeline_id: PipelineId) -> ScrollLayerId {
-        ScrollLayerId {
-            pipeline_id: pipeline_id,
-            info: ScrollLayerInfo::ReferenceFrame(0),
-        }
-    }
-
-    pub fn scroll_root_id(&self) -> Option<ServoScrollRootId> {
-        match self.info {
-            ScrollLayerInfo::Scrollable(_, scroll_root_id) => Some(scroll_root_id),
-            ScrollLayerInfo::ReferenceFrame(..) => None,
-        }
-    }
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
-pub enum ScrollLayerInfo {
-    Scrollable(usize, ServoScrollRootId),
-    ReferenceFrame(usize),
-}
-
-#[derive(Clone, Deserialize, Serialize)]
-pub struct ScrollLayerState {
-    pub pipeline_id: PipelineId,
-    pub scroll_root_id: ServoScrollRootId,
-    pub scroll_offset: LayoutPoint,
-}
-
-#[repr(C)]
-#[repr(u32)]
-#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
-pub enum ScrollPolicy {
-    Scrollable  = 0,
-    Fixed       = 1,
-}
-known_heap_size!(0, ScrollPolicy);
-
-#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
-pub enum ScrollLocation {
-    /// Scroll by a certain amount.
-    Delta(LayoutPoint), 
-    /// Scroll to very top of element.
-    Start,
-    /// Scroll to very bottom of element. 
-    End 
-}
-
-#[repr(C)]
-#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
-pub struct ServoScrollRootId(pub usize);
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub enum SpecificDisplayItem {
-    Rectangle(RectangleDisplayItem),
-    Text(TextDisplayItem),
-    Image(ImageDisplayItem),
-    YuvImage(YuvImageDisplayItem),
-    WebGL(WebGLDisplayItem),
-    Border(BorderDisplayItem),
-    BoxShadow(BoxShadowDisplayItem),
-    Gradient(GradientDisplayItem),
-    RadialGradient(RadialGradientDisplayItem),
-    Iframe(IframeDisplayItem),
-    PushStackingContext(PushStackingContextDisplayItem),
-    PopStackingContext,
-    PushScrollLayer(PushScrollLayerItem),
-    PopScrollLayer,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct StackingContext {
-    pub scroll_policy: ScrollPolicy,
-    pub bounds: LayoutRect,
-    pub z_index: i32,
-    pub transform: Option<PropertyBinding<LayoutTransform>>,
-    pub perspective: Option<LayoutTransform>,
-    pub mix_blend_mode: MixBlendMode,
-    pub filters: ItemRange,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)]
-pub struct GlyphOptions {
-    // These are currently only used on windows for dwrite fonts.
-    pub use_embedded_bitmap: bool,
-    pub force_gdi_rendering: bool,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct TextDisplayItem {
-    pub glyphs: ItemRange,
-    pub font_key: FontKey,
-    pub size: Au,
-    pub color: ColorF,
-    pub blur_radius: Au,
-    pub glyph_options: Option<GlyphOptions>,
-}
-
-#[derive(Clone, Deserialize, Serialize)]
-pub enum WebGLCommand {
-    GetContextAttributes(MsgSender<GLContextAttributes>),
-    ActiveTexture(u32),
-    BlendColor(f32, f32, f32, f32),
-    BlendEquation(u32),
-    BlendEquationSeparate(u32, u32),
-    BlendFunc(u32, u32),
-    BlendFuncSeparate(u32, u32, u32, u32),
-    AttachShader(WebGLProgramId, WebGLShaderId),
-    DetachShader(WebGLProgramId, WebGLShaderId),
-    BindAttribLocation(WebGLProgramId, u32, String),
-    BufferData(u32, Vec<u8>, u32),
-    BufferSubData(u32, isize, Vec<u8>),
-    Clear(u32),
-    ClearColor(f32, f32, f32, f32),
-    ClearDepth(f64),
-    ClearStencil(i32),
-    ColorMask(bool, bool, bool, bool),
-    CullFace(u32),
-    FrontFace(u32),
-    DepthFunc(u32),
-    DepthMask(bool),
-    DepthRange(f64, f64),
-    Enable(u32),
-    Disable(u32),
-    CompileShader(WebGLShaderId, String),
-    CopyTexImage2D(u32, i32, u32, i32, i32, i32, i32, i32),
-    CopyTexSubImage2D(u32, i32, i32, i32, i32, i32, i32, i32),
-    CreateBuffer(MsgSender<Option<WebGLBufferId>>),
-    CreateFramebuffer(MsgSender<Option<WebGLFramebufferId>>),
-    CreateRenderbuffer(MsgSender<Option<WebGLRenderbufferId>>),
-    CreateTexture(MsgSender<Option<WebGLTextureId>>),
-    CreateProgram(MsgSender<Option<WebGLProgramId>>),
-    CreateShader(u32, MsgSender<Option<WebGLShaderId>>),
-    DeleteBuffer(WebGLBufferId),
-    DeleteFramebuffer(WebGLFramebufferId),
-    DeleteRenderbuffer(WebGLRenderbufferId),
-    DeleteTexture(WebGLTextureId),
-    DeleteProgram(WebGLProgramId),
-    DeleteShader(WebGLShaderId),
-    BindBuffer(u32, Option<WebGLBufferId>),
-    BindFramebuffer(u32, WebGLFramebufferBindingRequest),
-    BindRenderbuffer(u32, Option<WebGLRenderbufferId>),
-    BindTexture(u32, Option<WebGLTextureId>),
-    DisableVertexAttribArray(u32),
-    DrawArrays(u32, i32, i32),
-    DrawElements(u32, i32, u32, i64),
-    EnableVertexAttribArray(u32),
-    FramebufferRenderbuffer(u32, u32, u32, Option<WebGLRenderbufferId>),
-    FramebufferTexture2D(u32, u32, u32, Option<WebGLTextureId>, i32),
-    GetBufferParameter(u32, u32, MsgSender<WebGLResult<WebGLParameter>>),
-    GetParameter(u32, MsgSender<WebGLResult<WebGLParameter>>),
-    GetProgramParameter(WebGLProgramId, u32, MsgSender<WebGLResult<WebGLParameter>>),
-    GetShaderParameter(WebGLShaderId, u32, MsgSender<WebGLResult<WebGLParameter>>),
-    GetActiveAttrib(WebGLProgramId, u32, MsgSender<WebGLResult<(i32, u32, String)>>),
-    GetActiveUniform(WebGLProgramId, u32, MsgSender<WebGLResult<(i32, u32, String)>>),
-    GetAttribLocation(WebGLProgramId, String, MsgSender<Option<i32>>),
-    GetUniformLocation(WebGLProgramId, String, MsgSender<Option<i32>>),
-    GetVertexAttrib(u32, u32, MsgSender<WebGLResult<WebGLParameter>>),
-    GetShaderInfoLog(WebGLShaderId, MsgSender<String>),
-    GetProgramInfoLog(WebGLProgramId, MsgSender<String>),
-    PolygonOffset(f32, f32),
-    RenderbufferStorage(u32, u32, i32, i32),
-    ReadPixels(i32, i32, i32, i32, u32, u32, MsgSender<Vec<u8>>),
-    SampleCoverage(f32, bool),
-    Scissor(i32, i32, i32, i32),
-    StencilFunc(u32, i32, u32),
-    StencilFuncSeparate(u32, u32, i32, u32),
-    StencilMask(u32),
-    StencilMaskSeparate(u32, u32),
-    StencilOp(u32, u32, u32),
-    StencilOpSeparate(u32, u32, u32, u32),
-    Hint(u32, u32),
-    IsEnabled(u32, MsgSender<bool>),
-    LineWidth(f32),
-    PixelStorei(u32, i32),
-    LinkProgram(WebGLProgramId),
-    Uniform1f(i32, f32),
-    Uniform1fv(i32, Vec<f32>),
-    Uniform1i(i32, i32),
-    Uniform1iv(i32, Vec<i32>),
-    Uniform2f(i32, f32, f32),
-    Uniform2fv(i32, Vec<f32>),
-    Uniform2i(i32, i32, i32),
-    Uniform2iv(i32, Vec<i32>),
-    Uniform3f(i32, f32, f32, f32),
-    Uniform3fv(i32, Vec<f32>),
-    Uniform3i(i32, i32, i32, i32),
-    Uniform3iv(i32, Vec<i32>),
-    Uniform4f(i32, f32, f32, f32, f32),
-    Uniform4fv(i32, Vec<f32>),
-    Uniform4i(i32, i32, i32, i32, i32),
-    Uniform4iv(i32, Vec<i32>),
-    UniformMatrix2fv(i32, bool, Vec<f32>),
-    UniformMatrix3fv(i32, bool, Vec<f32>),
-    UniformMatrix4fv(i32, bool, Vec<f32>),
-    UseProgram(WebGLProgramId),
-    ValidateProgram(WebGLProgramId),
-    VertexAttrib(u32, f32, f32, f32, f32),
-    VertexAttribPointer(u32, i32, u32, bool, i32, u32),
-    VertexAttribPointer2f(u32, i32, bool, i32, u32),
-    Viewport(i32, i32, i32, i32),
-    TexImage2D(u32, i32, i32, i32, i32, u32, u32, Vec<u8>),
-    TexParameteri(u32, u32, i32),
-    TexParameterf(u32, u32, f32),
-    TexSubImage2D(u32, i32, i32, i32, i32, i32, u32, u32, Vec<u8>),
-    DrawingBufferWidth(MsgSender<i32>),
-    DrawingBufferHeight(MsgSender<i32>),
-    Finish(MsgSender<()>),
-    Flush,
-    GenerateMipmap(u32),
-}
-
-#[cfg(feature = "nightly")]
-macro_rules! define_resource_id_struct {
-    ($name:ident) => {
-        #[derive(Clone, Copy, PartialEq)]
-        pub struct $name(NonZero<u32>);
-
-        impl $name {
-            #[inline]
-            unsafe fn new(id: u32) -> Self {
-                $name(NonZero::new(id))
-            }
-
-            #[inline]
-            fn get(self) -> u32 {
-                *self.0
-            }
-        }
-
-    };
-}
-
-#[cfg(not(feature = "nightly"))]
-macro_rules! define_resource_id_struct {
-    ($name:ident) => {
-        #[derive(Clone, Copy, PartialEq)]
-        pub struct $name(u32);
-
-        impl $name {
-            #[inline]
-            unsafe fn new(id: u32) -> Self {
-                $name(id)
-            }
-
-            #[inline]
-            fn get(self) -> u32 {
-                self.0
-            }
-        }
-    };
-}
-
-macro_rules! define_resource_id {
-    ($name:ident) => {
-        define_resource_id_struct!($name);
-
-        impl ::serde::Deserialize for $name {
-            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
-                where D: ::serde::Deserializer
-            {
-                let id = try!(u32::deserialize(deserializer));
-                if id == 0 {
-                    Err(::serde::de::Error::custom("expected a non-zero value"))
-                } else {
-                    Ok(unsafe { $name::new(id) })
-                }
-            }
-        }
-
-        impl ::serde::Serialize for $name {
-            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
-                where S: ::serde::Serializer
-            {
-                self.get().serialize(serializer)
-            }
-        }
-
-        impl ::std::fmt::Debug for $name {
-            fn fmt(&self, fmt: &mut ::std::fmt::Formatter)
-                  -> Result<(), ::std::fmt::Error> {
-                fmt.debug_tuple(stringify!($name))
-                   .field(&self.get())
-                   .finish()
-            }
-        }
-
-        impl ::std::fmt::Display for $name {
-            fn fmt(&self, fmt: &mut ::std::fmt::Formatter)
-                  -> Result<(), ::std::fmt::Error> {
-                write!(fmt, "{}", self.get())
-            }
-        }
-
-        impl ::heapsize::HeapSizeOf for $name {
-            fn heap_size_of_children(&self) -> usize { 0 }
-        }
-    }
-}
-
-define_resource_id!(WebGLBufferId);
-define_resource_id!(WebGLFramebufferId);
-define_resource_id!(WebGLRenderbufferId);
-define_resource_id!(WebGLTextureId);
-define_resource_id!(WebGLProgramId);
-define_resource_id!(WebGLShaderId);
-
-#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
-pub struct WebGLContextId(pub usize);
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct WebGLDisplayItem {
-    pub context_id: WebGLContextId,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub enum WebGLError {
-    InvalidEnum,
-    InvalidFramebufferOperation,
-    InvalidOperation,
-    InvalidValue,
-    OutOfMemory,
-    ContextLost,
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub enum WebGLFramebufferBindingRequest {
-    Explicit(WebGLFramebufferId),
-    Default,
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub enum WebGLParameter {
-    Int(i32),
-    Bool(bool),
-    String(String),
-    Float(f32),
-    FloatArray(Vec<f32>),
-    Invalid,
-}
-
-pub type WebGLResult<T> = Result<T, WebGLError>;
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub enum WebGLShaderParameter {
-    Int(i32),
-    Bool(bool),
-    Invalid,
-}
-
-pub type VRCompositorId = u64;
-
-// WebVR commands that must be called in the WebGL render thread.
-#[derive(Clone, Deserialize, Serialize)]
-pub enum VRCompositorCommand {
-    Create(VRCompositorId),
-    SyncPoses(VRCompositorId, f64, f64, MsgSender<Result<Vec<u8>,()>>),
-    SubmitFrame(VRCompositorId, [f32; 4], [f32; 4]),
-    Release(VRCompositorId)
-}
-
-/// Represents a desktop style page zoom factor.
-#[derive(Clone, Copy, Serialize, Deserialize, Debug)]
-pub struct PageZoomFactor(f32);
-
-impl PageZoomFactor {
-    /// Construct a new page zoom factor.
-    pub fn new(scale: f32) -> PageZoomFactor {
-        PageZoomFactor(scale)
-    }
-
-    /// Get the page zoom factor as an untyped float.
-    pub fn get(&self) -> f32 {
-        self.0
-    }
-}
-
-// Trait object that handles WebVR commands.
-// Receives the texture_id associated to the WebGLContext.
-pub trait VRCompositorHandler: Send {
-    fn handle(&mut self, command: VRCompositorCommand, texture_id: Option<u32>);
-}
-
-impl fmt::Debug for ApiMsg {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        match self {
-            &ApiMsg::AddRawFont(..) => { write!(f, "ApiMsg::AddRawFont") }
-            &ApiMsg::AddNativeFont(..) => { write!(f, "ApiMsg::AddNativeFont") }
-            &ApiMsg::DeleteFont(..) => { write!(f, "ApiMsg::DeleteFont") }
-            &ApiMsg::GetGlyphDimensions(..) => { write!(f, "ApiMsg::GetGlyphDimensions") }
-            &ApiMsg::AddImage(..) => { write!(f, "ApiMsg::AddImage") }
-            &ApiMsg::UpdateImage(..) => { write!(f, "ApiMsg::UpdateImage") }
-            &ApiMsg::DeleteImage(..) => { write!(f, "ApiMsg::DeleteImage") }
-            &ApiMsg::CloneApi(..) => { write!(f, "ApiMsg::CloneApi") }
-            &ApiMsg::SetRootDisplayList(..) => { write!(f, "ApiMsg::SetRootDisplayList") }
-            &ApiMsg::SetRootPipeline(..) => { write!(f, "ApiMsg::SetRootPipeline") }
-            &ApiMsg::Scroll(..) => { write!(f, "ApiMsg::Scroll") }
-            &ApiMsg::ScrollLayersWithScrollId(..) => { write!(f, "ApiMsg::ScrollLayersWithScrollId") }
-            &ApiMsg::TickScrollingBounce => { write!(f, "ApiMsg::TickScrollingBounce") }
-            &ApiMsg::TranslatePointToLayerSpace(..) => { write!(f, "ApiMsg::TranslatePointToLayerSpace") }
-            &ApiMsg::GetScrollLayerState(..) => { write!(f, "ApiMsg::GetScrollLayerState") }
-            &ApiMsg::RequestWebGLContext(..) => { write!(f, "ApiMsg::RequestWebGLContext") }
-            &ApiMsg::ResizeWebGLContext(..) => { write!(f, "ApiMsg::ResizeWebGLContext") }
-            &ApiMsg::WebGLCommand(..) => { write!(f, "ApiMsg::WebGLCommand") }
-            &ApiMsg::GenerateFrame(..) => { write!(f, "ApiMsg::GenerateFrame") }
-            &ApiMsg::VRCompositorCommand(..) => { write!(f, "ApiMsg::VRCompositorCommand") }
-            &ApiMsg::ExternalEvent(..) => { write!(f, "ApiMsg::ExternalEvent") }
-            &ApiMsg::ShutDown => { write!(f, "ApiMsg::ShutDown") }
-            &ApiMsg::SetPageZoom(..) => { write!(f, "ApiMsg::SetPageZoom") }
-            &ApiMsg::SetWindowParameters(..) => { write!(f, "ApiMsg::SetWindowParameters") }
-        }
-    }
-}
--- a/gfx/webrender_traits/src/webgl.rs
+++ b/gfx/webrender_traits/src/webgl.rs
@@ -1,19 +1,267 @@
 /* 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 channel::MsgSender;
 use gleam::gl;
-use offscreen_gl_context::{GLContext, NativeGLContextMethods};
+
+#[cfg(feature = "nightly")]
+use core::nonzero::NonZero;
+
+use offscreen_gl_context::{GLContext, NativeGLContextMethods, GLContextAttributes};
 use std::fmt;
-use {WebGLBufferId, WebGLCommand, WebGLError, WebGLFramebufferBindingRequest};
-use {WebGLFramebufferId, WebGLParameter, WebGLProgramId, WebGLRenderbufferId};
-use {WebGLResult, WebGLShaderId, WebGLTextureId};
+
+#[derive(Clone, Deserialize, Serialize)]
+pub enum WebGLCommand {
+    GetContextAttributes(MsgSender<GLContextAttributes>),
+    ActiveTexture(u32),
+    BlendColor(f32, f32, f32, f32),
+    BlendEquation(u32),
+    BlendEquationSeparate(u32, u32),
+    BlendFunc(u32, u32),
+    BlendFuncSeparate(u32, u32, u32, u32),
+    AttachShader(WebGLProgramId, WebGLShaderId),
+    DetachShader(WebGLProgramId, WebGLShaderId),
+    BindAttribLocation(WebGLProgramId, u32, String),
+    BufferData(u32, Vec<u8>, u32),
+    BufferSubData(u32, isize, Vec<u8>),
+    Clear(u32),
+    ClearColor(f32, f32, f32, f32),
+    ClearDepth(f64),
+    ClearStencil(i32),
+    ColorMask(bool, bool, bool, bool),
+    CullFace(u32),
+    FrontFace(u32),
+    DepthFunc(u32),
+    DepthMask(bool),
+    DepthRange(f64, f64),
+    Enable(u32),
+    Disable(u32),
+    CompileShader(WebGLShaderId, String),
+    CopyTexImage2D(u32, i32, u32, i32, i32, i32, i32, i32),
+    CopyTexSubImage2D(u32, i32, i32, i32, i32, i32, i32, i32),
+    CreateBuffer(MsgSender<Option<WebGLBufferId>>),
+    CreateFramebuffer(MsgSender<Option<WebGLFramebufferId>>),
+    CreateRenderbuffer(MsgSender<Option<WebGLRenderbufferId>>),
+    CreateTexture(MsgSender<Option<WebGLTextureId>>),
+    CreateProgram(MsgSender<Option<WebGLProgramId>>),
+    CreateShader(u32, MsgSender<Option<WebGLShaderId>>),
+    DeleteBuffer(WebGLBufferId),
+    DeleteFramebuffer(WebGLFramebufferId),
+    DeleteRenderbuffer(WebGLRenderbufferId),
+    DeleteTexture(WebGLTextureId),
+    DeleteProgram(WebGLProgramId),
+    DeleteShader(WebGLShaderId),
+    BindBuffer(u32, Option<WebGLBufferId>),
+    BindFramebuffer(u32, WebGLFramebufferBindingRequest),
+    BindRenderbuffer(u32, Option<WebGLRenderbufferId>),
+    BindTexture(u32, Option<WebGLTextureId>),
+    DisableVertexAttribArray(u32),
+    DrawArrays(u32, i32, i32),
+    DrawElements(u32, i32, u32, i64),
+    EnableVertexAttribArray(u32),
+    FramebufferRenderbuffer(u32, u32, u32, Option<WebGLRenderbufferId>),
+    FramebufferTexture2D(u32, u32, u32, Option<WebGLTextureId>, i32),
+    GetBufferParameter(u32, u32, MsgSender<WebGLResult<WebGLParameter>>),
+    GetParameter(u32, MsgSender<WebGLResult<WebGLParameter>>),
+    GetProgramParameter(WebGLProgramId, u32, MsgSender<WebGLResult<WebGLParameter>>),
+    GetShaderParameter(WebGLShaderId, u32, MsgSender<WebGLResult<WebGLParameter>>),
+    GetActiveAttrib(WebGLProgramId, u32, MsgSender<WebGLResult<(i32, u32, String)>>),
+    GetActiveUniform(WebGLProgramId, u32, MsgSender<WebGLResult<(i32, u32, String)>>),
+    GetAttribLocation(WebGLProgramId, String, MsgSender<Option<i32>>),
+    GetUniformLocation(WebGLProgramId, String, MsgSender<Option<i32>>),
+    GetVertexAttrib(u32, u32, MsgSender<WebGLResult<WebGLParameter>>),
+    GetShaderInfoLog(WebGLShaderId, MsgSender<String>),
+    GetProgramInfoLog(WebGLProgramId, MsgSender<String>),
+    PolygonOffset(f32, f32),
+    RenderbufferStorage(u32, u32, i32, i32),
+    ReadPixels(i32, i32, i32, i32, u32, u32, MsgSender<Vec<u8>>),
+    SampleCoverage(f32, bool),
+    Scissor(i32, i32, i32, i32),
+    StencilFunc(u32, i32, u32),
+    StencilFuncSeparate(u32, u32, i32, u32),
+    StencilMask(u32),
+    StencilMaskSeparate(u32, u32),
+    StencilOp(u32, u32, u32),
+    StencilOpSeparate(u32, u32, u32, u32),
+    Hint(u32, u32),
+    IsEnabled(u32, MsgSender<bool>),
+    LineWidth(f32),
+    PixelStorei(u32, i32),
+    LinkProgram(WebGLProgramId),
+    Uniform1f(i32, f32),
+    Uniform1fv(i32, Vec<f32>),
+    Uniform1i(i32, i32),
+    Uniform1iv(i32, Vec<i32>),
+    Uniform2f(i32, f32, f32),
+    Uniform2fv(i32, Vec<f32>),
+    Uniform2i(i32, i32, i32),
+    Uniform2iv(i32, Vec<i32>),
+    Uniform3f(i32, f32, f32, f32),
+    Uniform3fv(i32, Vec<f32>),
+    Uniform3i(i32, i32, i32, i32),
+    Uniform3iv(i32, Vec<i32>),
+    Uniform4f(i32, f32, f32, f32, f32),
+    Uniform4fv(i32, Vec<f32>),
+    Uniform4i(i32, i32, i32, i32, i32),
+    Uniform4iv(i32, Vec<i32>),
+    UniformMatrix2fv(i32, bool, Vec<f32>),
+    UniformMatrix3fv(i32, bool, Vec<f32>),
+    UniformMatrix4fv(i32, bool, Vec<f32>),
+    UseProgram(WebGLProgramId),
+    ValidateProgram(WebGLProgramId),
+    VertexAttrib(u32, f32, f32, f32, f32),
+    VertexAttribPointer(u32, i32, u32, bool, i32, u32),
+    VertexAttribPointer2f(u32, i32, bool, i32, u32),
+    Viewport(i32, i32, i32, i32),
+    TexImage2D(u32, i32, i32, i32, i32, u32, u32, Vec<u8>),
+    TexParameteri(u32, u32, i32),
+    TexParameterf(u32, u32, f32),
+    TexSubImage2D(u32, i32, i32, i32, i32, i32, u32, u32, Vec<u8>),
+    DrawingBufferWidth(MsgSender<i32>),
+    DrawingBufferHeight(MsgSender<i32>),
+    Finish(MsgSender<()>),
+    Flush,
+    GenerateMipmap(u32),
+}
+
+#[cfg(feature = "nightly")]
+macro_rules! define_resource_id_struct {
+    ($name:ident) => {
+        #[derive(Clone, Copy, PartialEq)]
+        pub struct $name(NonZero<u32>);
+
+        impl $name {
+            #[inline]
+            unsafe fn new(id: u32) -> Self {
+                $name(NonZero::new(id))
+            }
+
+            #[inline]
+            fn get(self) -> u32 {
+                *self.0
+            }
+        }
+
+    };
+}
+
+#[cfg(not(feature = "nightly"))]
+macro_rules! define_resource_id_struct {
+    ($name:ident) => {
+        #[derive(Clone, Copy, PartialEq)]
+        pub struct $name(u32);
+
+        impl $name {
+            #[inline]
+            unsafe fn new(id: u32) -> Self {
+                $name(id)
+            }
+
+            #[inline]
+            fn get(self) -> u32 {
+                self.0
+            }
+        }
+    };
+}
+
+macro_rules! define_resource_id {
+    ($name:ident) => {
+        define_resource_id_struct!($name);
+
+        impl ::serde::Deserialize for $name {
+            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+                where D: ::serde::Deserializer
+            {
+                let id = try!(u32::deserialize(deserializer));
+                if id == 0 {
+                    Err(::serde::de::Error::custom("expected a non-zero value"))
+                } else {
+                    Ok(unsafe { $name::new(id) })
+                }
+            }
+        }
+
+        impl ::serde::Serialize for $name {
+            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+                where S: ::serde::Serializer
+            {
+                self.get().serialize(serializer)
+            }
+        }
+
+        impl ::std::fmt::Debug for $name {
+            fn fmt(&self, fmt: &mut ::std::fmt::Formatter)
+                  -> Result<(), ::std::fmt::Error> {
+                fmt.debug_tuple(stringify!($name))
+                   .field(&self.get())
+                   .finish()
+            }
+        }
+
+        impl ::std::fmt::Display for $name {
+            fn fmt(&self, fmt: &mut ::std::fmt::Formatter)
+                  -> Result<(), ::std::fmt::Error> {
+                write!(fmt, "{}", self.get())
+            }
+        }
+
+        impl ::heapsize::HeapSizeOf for $name {
+            fn heap_size_of_children(&self) -> usize { 0 }
+        }
+    }
+}
+
+define_resource_id!(WebGLBufferId);
+define_resource_id!(WebGLFramebufferId);
+define_resource_id!(WebGLRenderbufferId);
+define_resource_id!(WebGLTextureId);
+define_resource_id!(WebGLProgramId);
+define_resource_id!(WebGLShaderId);
+
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
+pub struct WebGLContextId(pub usize);
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub enum WebGLError {
+    InvalidEnum,
+    InvalidFramebufferOperation,
+    InvalidOperation,
+    InvalidValue,
+    OutOfMemory,
+    ContextLost,
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub enum WebGLFramebufferBindingRequest {
+    Explicit(WebGLFramebufferId),
+    Default,
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub enum WebGLParameter {
+    Int(i32),
+    Bool(bool),
+    String(String),
+    Float(f32),
+    FloatArray(Vec<f32>),
+    Invalid,
+}
+
+pub type WebGLResult<T> = Result<T, WebGLError>;
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub enum WebGLShaderParameter {
+    Int(i32),
+    Bool(bool),
+    Invalid,
+}
 
 impl fmt::Debug for WebGLCommand {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         use WebGLCommand::*;
         let name = match *self {
             GetContextAttributes(..) => "GetContextAttributes",
             ActiveTexture(..) => "ActiveTexture",
             BlendColor(..) => "BlendColor",