Bug 1318440. Update webrender to 3f6da4476ecfa5f8130fa3d4d5e4450958d4ccf7. r=kats?
authorMason Chang <mchang@mozilla.com>
Thu, 17 Nov 2016 14:58:35 -0800
changeset 341842 e61d8807230203c99cce230186bd990cb8605d82
parent 341841 3427f77e5e3c344bb4e83cc3262246e43d23ec8f
child 341843 1acc749a9a21755aeb6ae59cd7fd5333578b2cb5
push id31345
push userkwierso@gmail.com
push dateFri, 10 Feb 2017 20:35:09 +0000
treeherdermozilla-central@a288fe35e494 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskats
bugs1318440
milestone53.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1318440. Update webrender to 3f6da4476ecfa5f8130fa3d4d5e4450958d4ccf7. r=kats?
README.webrender
gfx/webrender/Cargo.toml
gfx/webrender/res/clip_shared.glsl
gfx/webrender/res/debug_font.fs.glsl
gfx/webrender/res/prim_shared.glsl
gfx/webrender/res/ps_clear.fs.glsl
gfx/webrender/res/ps_gradient.fs.glsl
gfx/webrender/res/ps_gradient.vs.glsl
gfx/webrender/res/ps_image.fs.glsl
gfx/webrender/res/ps_image.vs.glsl
gfx/webrender/res/ps_rectangle.fs.glsl
gfx/webrender/res/ps_rectangle.vs.glsl
gfx/webrender/res/ps_text_run.fs.glsl
gfx/webrender/res/ps_text_run.vs.glsl
gfx/webrender/res/shared.glsl
gfx/webrender/src/bindings.rs
gfx/webrender/src/debug_render.rs
gfx/webrender/src/device.rs
gfx/webrender/src/frame.rs
gfx/webrender/src/gpu_store.rs
gfx/webrender/src/internal_types.rs
gfx/webrender/src/layer.rs
gfx/webrender/src/lib.rs
gfx/webrender/src/prim_store.rs
gfx/webrender/src/record.rs
gfx/webrender/src/render_backend.rs
gfx/webrender/src/renderer.rs
gfx/webrender/src/resource_cache.rs
gfx/webrender/src/scene.rs
gfx/webrender/src/texture_cache.rs
gfx/webrender/src/tiling.rs
gfx/webrender_traits/Cargo.toml
gfx/webrender_traits/src/api.rs
gfx/webrender_traits/src/display_list.rs
gfx/webrender_traits/src/lib.rs
gfx/webrender_traits/src/stacking_context.rs
gfx/webrender_traits/src/types.rs
third_party/rust/dwrite-sys/.cargo-checksum.json
third_party/rust/dwrite-sys/.cargo-ok
third_party/rust/dwrite-sys/Cargo.toml
third_party/rust/dwrite-sys/README.md
third_party/rust/dwrite-sys/build.rs
third_party/rust/dwrite-sys/i686/libdwrite.a
third_party/rust/dwrite-sys/src/lib.rs
third_party/rust/dwrite-sys/x86_64/libdwrite.a
third_party/rust/ipc-channel/.cargo-checksum.json
third_party/rust/ipc-channel/.travis.yml
third_party/rust/ipc-channel/Cargo.toml
third_party/rust/ipc-channel/appveyor.yml
third_party/rust/ipc-channel/src/lib.rs
third_party/rust/ipc-channel/src/platform/inprocess/mod.rs
third_party/rust/ipc-channel/src/platform/linux/mod.rs
third_party/rust/ipc-channel/src/platform/macos/mod.rs
third_party/rust/ipc-channel/src/platform/mod.rs
third_party/rust/ipc-channel/src/platform/test.rs
third_party/rust/ipc-channel/src/platform/unix/mod.rs
third_party/rust/ipc-channel/src/test.rs
toolkit/library/gtest/rust/Cargo.lock
toolkit/library/rust/Cargo.lock
--- a/README.webrender
+++ b/README.webrender
@@ -36,33 +36,34 @@ For a debug webrender build:
 
 
 What if you have to make changes to webrender itself?
 
 1) Update your graphics branch checkout to the latest code on the
 graphics branch
 2) Separately, check out the webrender repo to the last version we
 used in-tree (right now it's
-git:b91db5452e57a9fe5c444d57b0021cd2507723f5)
+git:3f6da4476ecfa5f8130fa3d4d5e4450958d4ccf7)
 3) Do a diff between the webrender repo and the stuff we have in-tree
 to see what the differences are and make a note
 4) Update the webrender repo to the version you want
 5) Copy over the webrender files into gfx/webrender (and
 gfx/webrender_traits) but make sure you keep the changes from step 3.
 You're basically doing a rebase-by-hand here which can be tricky.
 6) Commit your changes to the graphics branch locally
 7) Run |mach vendor rust| to update the rust dependencies in third_party/rust
 8) Commit the vendored changes locally
 9) Build and test. You may need to make changes in bindings.rs or on
 the C++ side depending on what changed in webrender. This can
 potentially be quite tricky if you don't fully understand the API
 changes on the webrender side. In this step, try to not use your new
 features yet, just get the build working with the minimal changes.
-10) Commit the changes locally from step 9, and push everything to the
+10) Update the git revision in (2)
+11) Commit the changes locally from step 9, and push everything to the
 graphics branch.
-11) Now you have an update webrender with the new features you wanted,
+12) Now you have an update webrender with the new features you wanted,
 so you can write gecko code against them.
 
 Yes, this is very painful. Once we split the bindings into a separate
 crate (bug 1316223) this will become simpler because it will remove
 step 2-3, and simplify step 5 to a copy. Step 9 is likely going to
 remain tricky if there are incompatible API changes on the WR side,
 hopefully those will be kept to a minimum.
--- a/gfx/webrender/Cargo.toml
+++ b/gfx/webrender/Cargo.toml
@@ -1,11 +1,11 @@
 [package]
 name = "webrender"
-version = "0.8.0"
+version = "0.9.0"
 authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
 license = "MPL-2.0"
 repository = "https://github.com/servo/webrender"
 workspace = ".."
 
 [features]
 default = ["webrender_traits/codegen"]
 serde_derive = ["webrender_traits/serde_derive"]
@@ -13,36 +13,37 @@ serde_derive = ["webrender_traits/serde_
 [dependencies]
 app_units = "0.3"
 bincode = "0.6"
 bit-set = "0.4"
 byteorder = "0.5"
 euclid = "0.10"
 fnv="1.0"
 gleam = "0.2"
-ipc-channel = "0.6"
+ipc-channel = "0.5"
 lazy_static = "0.2"
 log = "0.3"
 num-traits = "0.1.32"
 offscreen_gl_context = {version = "0.5", features = ["serde_serialization", "osmesa"]}
 rayon = "0.5"
 time = "0.1"
 webrender_traits = {path = "../webrender_traits", default-features = false}
 bitflags = "0.7"
 
-[target.'cfg(any(target_os = "android", target_os = "windows"))'.dependencies]
+[target.'cfg(target_os = "android")'.dependencies]
 freetype = "0.1.2"
 
 [target.'cfg(all(unix, not(target_os = "macos")))'.dependencies]
 freetype = {version = "0.1.2", default-features = false}
 
+[target.'cfg(target_os = "windows")'.dependencies]
+dwrote = {git = "https://github.com/vvuk/dwrote-rs"}
+kernel32-sys = "0.2"
+winapi = "0.2.8"
+
 [target.'cfg(target_os = "macos")'.dependencies]
 core-graphics = "0.4.1"
 core-text = "2.0"
 core-foundation = "0.2.2"
 
-[target.'cfg(target_os = "windows")'.dependencies]
-kernel32-sys = "0.2"
-winapi = "0.2.8"
-
 [profile.release]
 panic = "abort"
 debug = true
--- a/gfx/webrender/res/clip_shared.glsl
+++ b/gfx/webrender/res/clip_shared.glsl
@@ -1,65 +1,51 @@
 #line 1
 /* 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/. */
 
-flat varying vec4 vClipRect;
-flat varying vec4 vClipRadius;
-flat varying vec4 vClipMaskUvRect;
-flat varying vec4 vClipMaskLocalRect;
+#ifdef WR_VERTEX_SHADER
+
+struct CacheClipInstance {
+    int render_task_index;
+    int layer_index;
+    int data_index;
+};
+
+CacheClipInstance fetch_clip_item(int index) {
+    CacheClipInstance cci;
 
-#ifdef WR_VERTEX_SHADER
-void write_clip(ClipData clip) {
-    vClipRect = vec4(clip.rect.rect.xy, clip.rect.rect.xy + clip.rect.rect.zw);
-    vClipRadius = vec4(clip.top_left.outer_inner_radius.x,
-                       clip.top_right.outer_inner_radius.x,
-                       clip.bottom_right.outer_inner_radius.x,
-                       clip.bottom_left.outer_inner_radius.x);
-    //TODO: interpolate the final mask UV
-    vec2 texture_size = textureSize(sMask, 0);
-    vClipMaskUvRect = clip.mask_data.uv_rect / texture_size.xyxy;
-    vClipMaskLocalRect = clip.mask_data.local_rect; //TODO: transform
+    int offset = index * 1;
+
+    ivec4 data0 = int_data[offset + 0];
+
+    cci.render_task_index = data0.x;
+    cci.layer_index = data0.y;
+    cci.data_index = data0.z;
+
+    return cci;
 }
-#endif
-
-#ifdef WR_FRAGMENT_SHADER
-float do_clip(vec2 pos) {
-    vec2 ref_tl = vClipRect.xy + vec2( vClipRadius.x,  vClipRadius.x);
-    vec2 ref_tr = vClipRect.zy + vec2(-vClipRadius.y,  vClipRadius.y);
-    vec2 ref_br = vClipRect.zw + vec2(-vClipRadius.z, -vClipRadius.z);
-    vec2 ref_bl = vClipRect.xw + vec2( vClipRadius.w, -vClipRadius.w);
 
-    float d_tl = distance(pos, ref_tl);
-    float d_tr = distance(pos, ref_tr);
-    float d_br = distance(pos, ref_br);
-    float d_bl = distance(pos, ref_bl);
+// The transformed vertex function that always covers the whole whole clip area,
+// which is the intersection of all clip instances of a given primitive
+TransformVertexInfo write_clip_tile_vertex(vec4 local_clip_rect,
+                                           Layer layer,
+                                           ClipArea area) {
+    vec2 lp0_base = local_clip_rect.xy;
+    vec2 lp1_base = local_clip_rect.xy + local_clip_rect.zw;
 
-    float pixels_per_fragment = length(fwidth(pos.xy));
-    float nudge = 0.5 * pixels_per_fragment;
-    vec4 distances = vec4(d_tl, d_tr, d_br, d_bl) - vClipRadius + nudge;
-
-    bvec4 is_out = bvec4(pos.x < ref_tl.x && pos.y < ref_tl.y,
-                         pos.x > ref_tr.x && pos.y < ref_tr.y,
-                         pos.x > ref_br.x && pos.y > ref_br.y,
-                         pos.x < ref_bl.x && pos.y > ref_bl.y);
-
-    float distance_from_border = dot(vec4(is_out),
-                                     max(vec4(0.0, 0.0, 0.0, 0.0), distances));
+    vec2 lp0 = clamp_rect(lp0_base, layer.local_clip_rect);
+    vec2 lp1 = clamp_rect(lp1_base, layer.local_clip_rect);
+    vec4 clipped_local_rect = vec4(lp0, lp1 - lp0);
 
-    // Move the distance back into pixels.
-    distance_from_border /= pixels_per_fragment;
-    // Apply a more gradual fade out to transparent.
-    //distance_from_border -= 0.5;
+    vec2 final_pos = mix(area.task_bounds.xy, area.task_bounds.zw, aPosition.xy);
 
-    float border_alpha = 1.0 - smoothstep(0.0, 1.0, distance_from_border);
+    // compute the point position in side the layer, in CSS space
+    vec2 clamped_pos = final_pos + area.screen_origin_target_index.xy - area.task_bounds.xy;
+    vec4 layer_pos = get_layer_pos(clamped_pos / uDevicePixelRatio, layer);
 
-    bool repeat_mask = false; //TODO
-    vec2 vMaskUv = (pos - vClipMaskLocalRect.xy) / vClipMaskLocalRect.zw;
-    vec2 clamped_mask_uv = repeat_mask ? fract(vMaskUv) :
-        clamp(vMaskUv, vec2(0.0, 0.0), vec2(1.0, 1.0));
-    vec2 source_uv = clamped_mask_uv * vClipMaskUvRect.zw + vClipMaskUvRect.xy;
-    float mask_alpha = texture(sMask, source_uv).r; //careful: texture has type A8
+    gl_Position = uTransform * vec4(final_pos, 0.0, 1);
 
-    return border_alpha * mask_alpha;
+    return TransformVertexInfo(layer_pos.xyw, clamped_pos, clipped_local_rect);
 }
-#endif
+
+#endif //WR_VERTEX_SHADER
--- a/gfx/webrender/res/debug_font.fs.glsl
+++ b/gfx/webrender/res/debug_font.fs.glsl
@@ -1,13 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 void main(void)
 {
 #ifdef SERVO_ES2
-    float alpha = texture(sDiffuse, vColorTexCoord.xy).a;
+    float alpha = texture(sColor0, vColorTexCoord.xy).a;
 #else
-    float alpha = texture(sDiffuse, vColorTexCoord.xy).r;
+    float alpha = texture(sColor0, vColorTexCoord.xy).r;
 #endif
     oFragColor = vec4(vColor.xyz, vColor.w * alpha);
 }
--- a/gfx/webrender/res/prim_shared.glsl
+++ b/gfx/webrender/res/prim_shared.glsl
@@ -31,30 +31,32 @@
 #define BORDER_STYLE_RIDGE        7
 #define BORDER_STYLE_INSET        8
 #define BORDER_STYLE_OUTSET       9
 
 #define MAX_STOPS_PER_ANGLE_GRADIENT 8
 
 uniform sampler2DArray sCache;
 
+flat varying vec4 vClipMaskUvBounds;
+varying vec3 vClipMaskUv;
+
 #ifdef WR_VERTEX_SHADER
 
 #define VECS_PER_LAYER             13
 #define VECS_PER_RENDER_TASK        2
 #define VECS_PER_PRIM_GEOM          2
 
 #define GRADIENT_HORIZONTAL     0
 #define GRADIENT_VERTICAL       1
 #define GRADIENT_ROTATED        2
 
 uniform sampler2D sLayers;
 uniform sampler2D sRenderTasks;
 uniform sampler2D sPrimGeometry;
-uniform sampler2D sClips;
 
 uniform sampler2D sData16;
 uniform sampler2D sData32;
 uniform sampler2D sData64;
 uniform sampler2D sData128;
 
 ivec2 get_fetch_uv(int index, int vecs_per_item) {
     int items_per_row = WR_MAX_VERTEX_TEXTURE_WIDTH / vecs_per_item;
@@ -147,16 +149,36 @@ Tile fetch_tile(int index) {
 
     Tile tile;
     tile.screen_origin_task_origin = task.data0;
     tile.size_target_index = task.data1;
 
     return tile;
 }
 
+struct ClipArea {
+    vec4 task_bounds;
+    vec4 screen_origin_target_index;
+};
+
+ClipArea fetch_clip_area(int index) {
+    ClipArea area;
+
+    if (index == 0x7FFFFFFF) { //special sentinel task index
+        area.task_bounds = vec4(0.0, 0.0, 0.0, 0.0);
+        area.screen_origin_target_index = vec4(0.0, 0.0, 0.0, 0.0);
+    } else {
+        RenderTaskData task = fetch_render_task(index);
+        area.task_bounds = task.data0;
+        area.screen_origin_target_index = task.data1;
+    }
+
+    return area;
+}
+
 struct Gradient {
     vec4 start_end_point;
     vec4 kind;
 };
 
 Gradient fetch_gradient(int index) {
     Gradient gradient;
 
@@ -247,35 +269,35 @@ PrimitiveGeometry fetch_prim_geometry(in
 
     return pg;
 }
 
 struct PrimitiveInstance {
     int global_prim_index;
     int specific_prim_index;
     int render_task_index;
+    int clip_task_index;
     int layer_index;
-    int clip_address;
     int sub_index;
     ivec2 user_data;
 };
 
 PrimitiveInstance fetch_instance(int index) {
     PrimitiveInstance pi;
 
     int offset = index * 2;
 
     ivec4 data0 = int_data[offset + 0];
     ivec4 data1 = int_data[offset + 1];
 
     pi.global_prim_index = data0.x;
     pi.specific_prim_index = data0.y;
     pi.render_task_index = data0.z;
-    pi.layer_index = data0.w;
-    pi.clip_address = data1.x;
+    pi.clip_task_index = data0.w;
+    pi.layer_index = data1.x;
     pi.sub_index = data1.y;
     pi.user_data = data1.zw;
 
     return pi;
 }
 
 struct BlurCommand {
     int task_id;
@@ -317,117 +339,46 @@ CachePrimitiveInstance fetch_cache_insta
     cpi.sub_index = data0.w;
 
     return cpi;
 }
 
 struct Primitive {
     Layer layer;
     Tile tile;
+    ClipArea clip_area;
     vec4 local_rect;
     vec4 local_clip_rect;
     int prim_index;
-    int clip_index;
     // when sending multiple primitives of the same type (e.g. border segments)
     // this index allows the vertex shader to recognize the difference
     int sub_index;
     ivec2 user_data;
 };
 
 Primitive load_primitive(int index) {
     Primitive prim;
 
     PrimitiveInstance pi = fetch_instance(index);
 
     prim.layer = fetch_layer(pi.layer_index);
     prim.tile = fetch_tile(pi.render_task_index);
+    prim.clip_area = fetch_clip_area(pi.clip_task_index);
 
     PrimitiveGeometry pg = fetch_prim_geometry(pi.global_prim_index);
     prim.local_rect = pg.local_rect;
     prim.local_clip_rect = pg.local_clip_rect;
 
     prim.prim_index = pi.specific_prim_index;
-    prim.clip_index = pi.clip_address;
     prim.sub_index = pi.sub_index;
     prim.user_data = pi.user_data;
 
     return prim;
 }
 
-struct ClipRect {
-    vec4 rect;
-    vec4 dummy;
-};
-
-ClipRect fetch_clip_rect(int index) {
-    ClipRect rect;
-
-    ivec2 uv = get_fetch_uv_2(index);
-
-    rect.rect = texelFetchOffset(sData32, uv, 0, ivec2(0, 0));
-    //rect.dummy = texelFetchOffset(sData32, uv, 0, ivec2(1, 0));
-    rect.dummy = vec4(0.0, 0.0, 0.0, 0.0);
-
-    return rect;
-}
-
-struct ImageMaskData {
-    vec4 uv_rect;
-    vec4 local_rect;
-};
-
-ImageMaskData fetch_mask_data(int index) {
-    ImageMaskData info;
-
-    ivec2 uv = get_fetch_uv_2(index);
-
-    info.uv_rect = texelFetchOffset(sData32, uv, 0, ivec2(0, 0));
-    info.local_rect = texelFetchOffset(sData32, uv, 0, ivec2(1, 0));
-
-    return info;
-}
-
-struct ClipCorner {
-    vec4 rect;
-    vec4 outer_inner_radius;
-};
-
-ClipCorner fetch_clip_corner(int index) {
-    ClipCorner corner;
-
-    ivec2 uv = get_fetch_uv_2(index);
-
-    corner.rect = texelFetchOffset(sData32, uv, 0, ivec2(0, 0));
-    corner.outer_inner_radius = texelFetchOffset(sData32, uv, 0, ivec2(1, 0));
-
-    return corner;
-}
-
-struct ClipData {
-    ClipRect rect;
-    ClipCorner top_left;
-    ClipCorner top_right;
-    ClipCorner bottom_left;
-    ClipCorner bottom_right;
-    ImageMaskData mask_data;
-};
-
-ClipData fetch_clip(int index) {
-    ClipData clip;
-
-    clip.rect = fetch_clip_rect(index + 0);
-    clip.top_left = fetch_clip_corner(index + 1);
-    clip.top_right = fetch_clip_corner(index + 2);
-    clip.bottom_left = fetch_clip_corner(index + 3);
-    clip.bottom_right = fetch_clip_corner(index + 4);
-    clip.mask_data = fetch_mask_data(index + 5);
-
-    return clip;
-}
-
 // Return the intersection of the plane (set up by "normal" and "point")
 // with the ray (set up by "ray_origin" and "ray_dir"),
 // writing the resulting scaler into "t".
 bool ray_plane(vec3 normal, vec3 point, vec3 ray_origin, vec3 ray_dir, out float t)
 {
     float denom = dot(normal, ray_dir);
     if (denom > 1e-6) {
         vec3 d = point - ray_origin;
@@ -492,17 +443,17 @@ VertexInfo write_vertex(vec4 instance_re
     vec2 local_pos = mix(p0, p1, aPosition.xy);
 
     vec2 cp0 = floor(0.5 + local_clip_rect.xy * uDevicePixelRatio) / uDevicePixelRatio;
     vec2 cp1 = floor(0.5 + (local_clip_rect.xy + local_clip_rect.zw) * uDevicePixelRatio) / uDevicePixelRatio;
     local_pos = clamp(local_pos, cp0, cp1);
 
     local_pos = clamp_rect(local_pos, layer.local_clip_rect);
 
-    vec4 world_pos = layer.transform * vec4(local_pos, 0, 1);
+    vec4 world_pos = layer.transform * vec4(local_pos, 0.0, 1.0);
     world_pos.xyz /= world_pos.w;
 
     vec2 device_pos = world_pos.xy * uDevicePixelRatio;
 
     vec2 clamped_pos = clamp(device_pos,
                              tile.screen_origin_task_origin.xy,
                              tile.screen_origin_task_origin.xy + tile.size_target_index.xy);
 
@@ -516,16 +467,17 @@ VertexInfo write_vertex(vec4 instance_re
     VertexInfo vi = VertexInfo(Rect(p0, p1), local_clamped_pos.xy, clamped_pos.xy);
     return vi;
 }
 
 #ifdef WR_FEATURE_TRANSFORM
 
 struct TransformVertexInfo {
     vec3 local_pos;
+    vec2 global_clamped_pos;
     vec4 clipped_local_rect;
 };
 
 TransformVertexInfo write_transform_vertex(vec4 instance_rect,
                                            vec4 local_clip_rect,
                                            Layer layer,
                                            Tile tile) {
     vec2 lp0_base = instance_rect.xy;
@@ -572,19 +524,19 @@ TransformVertexInfo write_transform_vert
                            aPosition.xy);
 
     // compute the point position in side the layer, in CSS space
     vec4 layer_pos = get_layer_pos(clamped_pos / uDevicePixelRatio, layer);
 
     // apply the task offset
     vec2 final_pos = clamped_pos + tile.screen_origin_task_origin.zw - tile.screen_origin_task_origin.xy;
 
-    gl_Position = uTransform * vec4(final_pos, 0, 1);
+    gl_Position = uTransform * vec4(final_pos, 0.0, 1.0);
 
-    return TransformVertexInfo(layer_pos.xyw, clipped_local_rect);
+    return TransformVertexInfo(layer_pos.xyw, clamped_pos, clipped_local_rect);
 }
 
 #endif //WR_FEATURE_TRANSFORM
 
 struct Rectangle {
     vec4 color;
 };
 
@@ -670,17 +622,24 @@ Composite fetch_composite(int index) {
     Composite composite;
 
     int offset = index * 1;
 
     composite.src0_src1_target_id_op = int_data[offset + 0];
 
     return composite;
 }
-#endif
+
+void write_clip(vec2 global_pos, ClipArea area) {
+    vec2 texture_size = textureSize(sCache, 0).xy;
+    vec2 uv = global_pos + area.task_bounds.xy - area.screen_origin_target_index.xy;
+    vClipMaskUvBounds = area.task_bounds / texture_size.xyxy;
+    vClipMaskUv = vec3(uv / texture_size, area.screen_origin_target_index.z);
+}
+#endif //WR_VERTEX_SHADER
 
 #ifdef WR_FRAGMENT_SHADER
 float distance_from_rect(vec2 p, vec2 origin, vec2 size) {
     vec2 clamped = clamp(p, origin, origin + size);
     return distance(clamped, p);
 }
 
 vec2 init_transform_fs(vec3 local_pos, vec4 local_rect, out float fragment_alpha) {
@@ -690,9 +649,19 @@ vec2 init_transform_fs(vec3 local_pos, v
     float border_distance = distance_from_rect(pos, local_rect.xy, local_rect.zw);
     if (border_distance != 0.0) {
         float delta = length(fwidth(local_pos.xy));
         fragment_alpha = 1.0 - smoothstep(0.0, 1.0, border_distance / delta * 2.0);
     }
 
     return pos;
 }
-#endif
+
+float do_clip() {
+    // anything outside of the mask is considered transparent
+    bvec4 inside = lessThanEqual(
+        vec4(vClipMaskUvBounds.xy, vClipMaskUv.xy),
+        vec4(vClipMaskUv.xy, vClipMaskUvBounds.zw));
+    // check for the dummy bounds, which are given to the opaque objects
+    return vClipMaskUvBounds.xy == vClipMaskUvBounds.zw ? 1.0:
+        all(inside) ? textureLod(sCache, vClipMaskUv, 0).a : 0.0;
+}
+#endif //WR_FRAGMENT_SHADER
--- a/gfx/webrender/res/ps_clear.fs.glsl
+++ b/gfx/webrender/res/ps_clear.fs.glsl
@@ -1,7 +1,7 @@
 /* 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/. */
 
 void main(void) {
-    oFragColor = vec4(1, 1, 1, 1);
+    oFragColor = vec4(1.0, 1.0, 1.0, 1.0);
 }
--- a/gfx/webrender/res/ps_gradient.fs.glsl
+++ b/gfx/webrender/res/ps_gradient.fs.glsl
@@ -6,10 +6,11 @@ void main(void) {
 #ifdef WR_FEATURE_TRANSFORM
     float alpha = 0.0;
     vec2 local_pos = init_transform_fs(vLocalPos, vLocalRect, alpha);
 #else
     float alpha = 1.0;
     vec2 local_pos = vPos;
 #endif
 
-    oFragColor = vColor * vec4(1, 1, 1, alpha);
+    alpha = min(alpha, do_clip());
+    oFragColor = vColor * vec4(1.0, 1.0, 1.0, alpha);
 }
--- a/gfx/webrender/res/ps_gradient.vs.glsl
+++ b/gfx/webrender/res/ps_gradient.vs.glsl
@@ -49,16 +49,18 @@ void main(void) {
                                  prim.local_clip_rect,
                                  prim.layer,
                                  prim.tile);
 
     vec2 f = (vi.local_clamped_pos - segment_rect.xy) / segment_rect.zw;
     vPos = vi.local_clamped_pos;
 #endif
 
+    write_clip(vi.global_clamped_pos, prim.clip_area);
+
     switch (int(gradient.kind.x)) {
         case GRADIENT_HORIZONTAL:
             vColor = mix(g0.color, g1.color, f.x);
             break;
         case GRADIENT_VERTICAL:
             vColor = mix(g0.color, g1.color, f.y);
             break;
         case GRADIENT_ROTATED:
--- a/gfx/webrender/res/ps_image.fs.glsl
+++ b/gfx/webrender/res/ps_image.fs.glsl
@@ -9,21 +9,23 @@ void main(void) {
     float alpha = 0.0;
     vec2 pos = init_transform_fs(vLocalPos, vLocalRect, alpha);
 
     // We clamp the texture coordinate calculation here to the local rectangle boundaries,
     // which makes the edge of the texture stretch instead of repeat.
     vec2 relative_pos_in_rect =
          clamp(pos, vLocalRect.xy, vLocalRect.xy + vLocalRect.zw) - vLocalRect.xy;
 #else
-    float alpha = 1.0;;
+    float alpha = 1.0;
     vec2 relative_pos_in_rect = vLocalPos;
 #endif
 
+    alpha = min(alpha, do_clip());
+
     // We calculate the particular tile this fragment belongs to, taking into
     // account the spacing in between tiles. We only paint if our fragment does
     // not fall into that spacing.
     vec2 position_in_tile = mod(relative_pos_in_rect, vStretchSize + vTileSpacing);
     vec2 st = vTextureOffset + ((position_in_tile / vStretchSize) * vTextureSize);
     alpha = alpha * float(all(bvec2(step(position_in_tile, vStretchSize))));
 
-    oFragColor = vec4(1, 1, 1, alpha) * texture(sDiffuse, st);
+    oFragColor = vec4(1.0, 1.0, 1.0, alpha) * texture(sColor0, st);
 }
--- a/gfx/webrender/res/ps_image.vs.glsl
+++ b/gfx/webrender/res/ps_image.vs.glsl
@@ -17,18 +17,20 @@ void main(void) {
 #else
     VertexInfo vi = write_vertex(prim.local_rect,
                                  prim.local_clip_rect,
                                  prim.layer,
                                  prim.tile);
     vLocalPos = vi.local_clamped_pos - vi.local_rect.p0;
 #endif
 
+    write_clip(vi.global_clamped_pos, prim.clip_area);
+
     // vUv will contain how many times this image has wrapped around the image size.
-    vec2 texture_size = vec2(textureSize(sDiffuse, 0));
+    vec2 texture_size = vec2(textureSize(sColor0, 0));
     vec2 st0 = image.st_rect.xy / texture_size;
     vec2 st1 = image.st_rect.zw / texture_size;
 
     vTextureSize = st1 - st0;
     vTextureOffset = st0;
     vTileSpacing = image.stretch_size_and_tile_spacing.zw;
     vStretchSize = image.stretch_size_and_tile_spacing.xy;
 }
--- a/gfx/webrender/res/ps_rectangle.fs.glsl
+++ b/gfx/webrender/res/ps_rectangle.fs.glsl
@@ -1,13 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 void main(void) {
+    float alpha = 1.0;
 #ifdef WR_FEATURE_TRANSFORM
-    float alpha = 0.0;
+    alpha = 0.0;
     init_transform_fs(vLocalPos, vLocalRect, alpha);
-    oFragColor = vec4(1, 1, 1, alpha) * vColor;
-#else
-    oFragColor = vColor;
 #endif
+
+    alpha = min(alpha, do_clip());
+    oFragColor = vColor * vec4(1.0, 1.0, 1.0, alpha);
 }
--- a/gfx/webrender/res/ps_rectangle.vs.glsl
+++ b/gfx/webrender/res/ps_rectangle.vs.glsl
@@ -10,14 +10,16 @@ void main(void) {
 #ifdef WR_FEATURE_TRANSFORM
     TransformVertexInfo vi = write_transform_vertex(prim.local_rect,
                                                     prim.local_clip_rect,
                                                     prim.layer,
                                                     prim.tile);
     vLocalRect = vi.clipped_local_rect;
     vLocalPos = vi.local_pos;
 #else
-    write_vertex(prim.local_rect,
-                 prim.local_clip_rect,
-                 prim.layer,
-                 prim.tile);
+    VertexInfo vi = write_vertex(prim.local_rect,
+                                 prim.local_clip_rect,
+                                 prim.layer,
+                                 prim.tile);
 #endif
+
+    write_clip(vi.global_clamped_pos, prim.clip_area);
 }
--- a/gfx/webrender/res/ps_text_run.fs.glsl
+++ b/gfx/webrender/res/ps_text_run.fs.glsl
@@ -1,17 +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/. */
 
 void main(void) {
 #ifdef WR_FEATURE_SUBPIXEL_AA
-    oFragColor = texture(sDiffuse, vUv);
+    //note: the blend mode is not compatible with clipping
+    oFragColor = texture(sColor0, vUv);
 #else
-    float a = texture(sDiffuse, vUv).a;
+    float alpha = texture(sColor0, vUv).a;
 #ifdef WR_FEATURE_TRANSFORM
-    float alpha = 0.0;
-    init_transform_fs(vLocalPos, vLocalRect, alpha);
-    a *= alpha;
+    float a = 0.0;
+    init_transform_fs(vLocalPos, vLocalRect, a);
+    alpha *= a;
 #endif
-    oFragColor = vec4(vColor.rgb, vColor.a * a);
+    vec4 color = vColor;
+    alpha = min(alpha, do_clip());
+    oFragColor = vec4(vColor.rgb, vColor.a * alpha);
 #endif
 }
--- a/gfx/webrender/res/ps_text_run.vs.glsl
+++ b/gfx/webrender/res/ps_text_run.vs.glsl
@@ -20,15 +20,17 @@ void main(void) {
 #else
     VertexInfo vi = write_vertex(local_rect,
                                  prim.local_clip_rect,
                                  prim.layer,
                                  prim.tile);
     vec2 f = (vi.local_clamped_pos - vi.local_rect.p0) / (vi.local_rect.p1 - vi.local_rect.p0);
 #endif
 
-    vec2 texture_size = vec2(textureSize(sDiffuse, 0));
+    write_clip(vi.global_clamped_pos, prim.clip_area);
+
+    vec2 texture_size = vec2(textureSize(sColor0, 0));
     vec2 st0 = glyph.uv_rect.xy / texture_size;
     vec2 st1 = glyph.uv_rect.zw / texture_size;
 
     vColor = text.color;
     vUv = mix(st0, st1, f);
 }
--- a/gfx/webrender/res/shared.glsl
+++ b/gfx/webrender/res/shared.glsl
@@ -28,17 +28,19 @@
 
     // Fragment shader outputs
     out vec4 oFragColor;
 #endif
 
 //======================================================================================
 // Shared shader uniforms
 //======================================================================================
-uniform sampler2D sDiffuse;
+uniform sampler2D sColor0;
+uniform sampler2D sColor1;
+uniform sampler2D sColor2;
 uniform sampler2D sMask;
 
 //======================================================================================
 // Interpolator definitions
 //======================================================================================
 
 //======================================================================================
 // VS only types and UBOs
--- a/gfx/webrender/src/bindings.rs
+++ b/gfx/webrender/src/bindings.rs
@@ -1,10 +1,10 @@
 use std::path::PathBuf;
-use webrender_traits::{PipelineId, AuxiliaryListsBuilder, StackingContextId, DisplayListId};
+use webrender_traits::{PipelineId, AuxiliaryListsBuilder};
 use renderer::{Renderer, RendererOptions};
 extern crate webrender_traits;
 
 use euclid::{Size2D, Point2D, Rect, Matrix4D};
 use gleam::gl;
 use std::ffi::CStr;
 use webrender_traits::{ServoScrollRootId};
 use webrender_traits::{Epoch, ColorF};
@@ -162,53 +162,31 @@ mod win {
 #[cfg(target_os = "linux")]
 use self::linux::Library as GlLibrary;
 #[cfg(target_os = "macos")]
 use self::macos::Library as GlLibrary;
 #[cfg(target_os = "windows")]
 use self::win::Library as GlLibrary;
 
 pub struct WebRenderFrameBuilder {
-    pub stacking_contexts: Vec<(StackingContextId, webrender_traits::StackingContext)>,
-    pub display_lists: Vec<(DisplayListId, webrender_traits::BuiltDisplayList)>,
     pub auxiliary_lists_builder: AuxiliaryListsBuilder,
     pub root_pipeline_id: PipelineId,
+    pub root_dl_builder: webrender_traits::DisplayListBuilder,
+    pub temp_dl_builder: webrender_traits::DisplayListBuilder,
 }
 
 impl WebRenderFrameBuilder {
     pub fn new(root_pipeline_id: PipelineId) -> WebRenderFrameBuilder {
         WebRenderFrameBuilder {
-            stacking_contexts: vec![],
-            display_lists: vec![],
             auxiliary_lists_builder: AuxiliaryListsBuilder::new(),
             root_pipeline_id: root_pipeline_id,
+            root_dl_builder: webrender_traits::DisplayListBuilder::new(),
+            temp_dl_builder: webrender_traits::DisplayListBuilder::new(),
         }
     }
-
-    pub fn add_stacking_context(&mut self,
-                                api: &mut webrender_traits::RenderApi,
-                                pipeline_id: PipelineId,
-                                stacking_context: webrender_traits::StackingContext)
-                                -> StackingContextId {
-        assert!(pipeline_id == self.root_pipeline_id);
-        let id = api.next_stacking_context_id();
-        self.stacking_contexts.push((id, stacking_context));
-        id
-    }
-
-    pub fn add_display_list(&mut self,
-                            api: &mut webrender_traits::RenderApi,
-                            display_list: webrender_traits::BuiltDisplayList,
-                            stacking_context: &mut webrender_traits::StackingContext)
-                            -> DisplayListId {
-        let id = api.next_display_list_id();
-        stacking_context.display_lists.push(id);
-        self.display_lists.push((id, display_list));
-        id
-    }
 }
 
 struct Notifier {
 }
 
 impl webrender_traits::RenderNotifier for Notifier {
     fn new_frame_ready(&mut self) {
     }
@@ -229,17 +207,16 @@ pub struct WrWindowState {
     size: Size2D<u32>,
 }
 
 pub struct WrState {
     size: (u32, u32),
     pipeline_id: PipelineId,
     z_index: i32,
     frame_builder: WebRenderFrameBuilder,
-    dl_builder: Vec<webrender_traits::DisplayListBuilder>,
 }
 
 #[no_mangle]
 pub extern fn wr_init_window(root_pipeline_id: u64, path_utf8: *const c_char) -> *mut WrWindowState {
     let res_path = unsafe {
         CStr::from_ptr(path_utf8).to_string_lossy().into_owned()
     };
 
@@ -294,42 +271,58 @@ pub extern fn wr_create(window: &mut WrW
 
     let builder = WebRenderFrameBuilder::new(pipeline_id);
 
     let state = Box::new(WrState {
         size: (width, height),
         pipeline_id: pipeline_id,
         z_index: 0,
         frame_builder: builder,
-        dl_builder: Vec::new(),
     });
 
     if pipeline_id == window.root_pipeline_id {
         window.size = Size2D::new(width, height);
     }
 
     Box::into_raw(state)
 }
 
 #[no_mangle]
 pub extern fn wr_dp_begin(window: &mut WrWindowState, state: &mut WrState, width: u32, height: u32) {
     state.size = (width, height);
-    state.dl_builder.clear();
+    state.frame_builder.root_dl_builder.list.clear();
+    state.frame_builder.temp_dl_builder.list.clear();
     state.z_index = 0;
-    state.dl_builder.push(webrender_traits::DisplayListBuilder::new());
 
     if state.pipeline_id == window.root_pipeline_id {
         window.size = Size2D::new(width, height);
     }
+
+    let bounds = Rect::new(Point2D::new(0.0, 0.0), Size2D::new(width as f32, height as f32));
+
+    let root_stacking_context =
+        webrender_traits::StackingContext::new(Some(webrender_traits::ScrollLayerId::new(
+                                                    state.pipeline_id, 0, ServoScrollRootId(0))),
+                                               webrender_traits::ScrollPolicy::Scrollable,
+                                               bounds,
+                                               bounds,
+                                               0,
+                                               &Matrix4D::identity(),
+                                               &Matrix4D::identity(),
+                                               webrender_traits::MixBlendMode::Normal,
+                                               Vec::new(),
+                                               &mut state.frame_builder.auxiliary_lists_builder);
+
+    state.frame_builder.root_dl_builder.push_stacking_context(root_stacking_context);
 }
 
 #[no_mangle]
 pub extern fn wr_push_dl_builder(state:&mut WrState)
 {
-    state.dl_builder.push(webrender_traits::DisplayListBuilder::new());
+    state.frame_builder.temp_dl_builder.list.clear();
 }
 
 #[no_mangle]
 pub extern fn wr_pop_dl_builder(window: &mut WrWindowState, state: &mut WrState, bounds: WrRect, overflow: WrRect, transform: &Matrix4D<f32>, scroll_id: u64)
 {
     // 
     state.z_index += 1;
 
@@ -337,74 +330,58 @@ pub extern fn wr_pop_dl_builder(window: 
     let scroll_layer_id = if scroll_id == 0 {
         None
     } else {
         Some(webrender_traits::ScrollLayerId::new(pipeline_id,
                                                   scroll_id as usize, // WR issue 489
                                                   ServoScrollRootId(0)))
     };
 
-    let mut sc =
+    let sc =
         webrender_traits::StackingContext::new(scroll_layer_id,
                                                webrender_traits::ScrollPolicy::Scrollable,
                                                bounds.to_rect(),
                                                overflow.to_rect(),
                                                state.z_index,
                                                transform,
                                                &Matrix4D::identity(),
-                                               false,
                                                webrender_traits::MixBlendMode::Normal,
                                                Vec::new(),
                                                &mut state.frame_builder.auxiliary_lists_builder);
-    let dl = state.dl_builder.pop().unwrap();
-    state.frame_builder.add_display_list(&mut window.api, dl.finalize(), &mut sc);
-    let stacking_context_id = state.frame_builder.add_stacking_context(&mut window.api, pipeline_id, sc);
+
+    state.frame_builder.root_dl_builder.push_stacking_context(sc);
+    assert!(state.frame_builder.root_dl_builder.list.len() != 0);
 
-    state.dl_builder.last_mut().unwrap().push_stacking_context(stacking_context_id);
+    state.frame_builder.root_dl_builder.list.append(&mut state.frame_builder.temp_dl_builder.list);
 
+    assert!(state.frame_builder.temp_dl_builder.list.len() == 0);
+    // Not sure we have to pop? Before we just had a stack of both display items
+    // and stacking contexts
+    state.frame_builder.root_dl_builder.pop_stacking_context();
 }
 
 #[no_mangle]
 pub extern fn wr_dp_end(window: &mut WrWindowState, state: &mut WrState) {
     let epoch = Epoch(0);
     let root_background_color = ColorF::new(0.3, 0.0, 0.0, 1.0);
     let pipeline_id = state.pipeline_id;
     let (width, height) = state.size;
-    let bounds = Rect::new(Point2D::new(0.0, 0.0), Size2D::new(width as f32, height as f32));
 
-    let mut sc =
-        webrender_traits::StackingContext::new(Some(webrender_traits::ScrollLayerId::new(
-                                                        pipeline_id, 0, ServoScrollRootId(0))),
-                                               webrender_traits::ScrollPolicy::Scrollable,
-                                               bounds,
-                                               bounds,
-                                               0,
-                                               &Matrix4D::identity(),
-                                               &Matrix4D::identity(),
-                                               true,
-                                               webrender_traits::MixBlendMode::Normal,
-                                               Vec::new(),
-                                               &mut state.frame_builder.auxiliary_lists_builder);
-
-    assert!(state.dl_builder.len() == 1);
-    let dl = state.dl_builder.pop().unwrap();
-    state.frame_builder.add_display_list(&mut window.api, dl.finalize(), &mut sc);
-    let sc_id = state.frame_builder.add_stacking_context(&mut window.api, pipeline_id, sc);
+    // Should be the root one
+    state.frame_builder.root_dl_builder.pop_stacking_context();
 
     let fb = mem::replace(&mut state.frame_builder, WebRenderFrameBuilder::new(pipeline_id));
 
-    window.api.set_root_stacking_context(sc_id,
-                                  root_background_color,
-                                  epoch,
-                                  pipeline_id,
-                                  Size2D::new(width as f32, height as f32),
-                                  fb.stacking_contexts,
-                                  fb.display_lists,
-                                  fb.auxiliary_lists_builder
-                                               .finalize());
+    window.api.set_root_display_list(root_background_color,
+                                     epoch,
+                                     pipeline_id,
+                                     Size2D::new(width as f32, height as f32),
+                                     fb.root_dl_builder.finalize(),
+                                     fb.auxiliary_lists_builder.finalize()
+                                     );
 
     gl::clear(gl::COLOR_BUFFER_BIT);
     window.renderer.update();
 
     window.renderer.render(window.size);
 }
 
 #[no_mangle]
@@ -433,42 +410,37 @@ pub extern fn wr_update_image(window: &m
 
 #[no_mangle]
 pub extern fn wr_delete_image(window: &mut WrWindowState, key: ImageKey) {
     window.api.delete_image(key)
 }
 
 #[no_mangle]
 pub extern fn wr_dp_push_rect(state:&mut WrState, rect: WrRect, clip: WrRect, r: f32, g: f32, b: f32, a: f32) {
-    if state.dl_builder.len() == 0 {
-      return;
-    }
     //let (width, height) = state.size;
     let clip_region = webrender_traits::ClipRegion::new(&clip.to_rect(),
                                                         Vec::new(),
                                                         None,
                                                         &mut state.frame_builder.auxiliary_lists_builder);
-    state.dl_builder.last_mut().unwrap().push_rect(rect.to_rect(),
-                               clip_region,
-                               ColorF::new(r, g, b, a));
+
+    state.frame_builder.temp_dl_builder.push_rect(rect.to_rect(),
+                                    clip_region,
+                                    ColorF::new(r, g, b, a));
 }
 
 #[no_mangle]
 pub extern fn wr_dp_push_iframe(state: &mut WrState, rect: WrRect, clip: WrRect, layers_id: u64) {
-    if state.dl_builder.len() == 0 {
-        return;
-    }
-
     let clip_region = webrender_traits::ClipRegion::new(&clip.to_rect(),
                                                         Vec::new(),
                                                         None,
                                                         &mut state.frame_builder.auxiliary_lists_builder);
     let pipeline_id = PipelineId((layers_id >> 32) as u32, layers_id as u32);
-    state.dl_builder.last_mut().unwrap().push_iframe(rect.to_rect(),
-                                clip_region, pipeline_id);
+    state.frame_builder.temp_dl_builder.push_iframe(rect.to_rect(),
+                                      clip_region,
+                                      pipeline_id);
 }
 
 #[repr(C)]
 pub struct WrRect
 {
     x: f32,
     y: f32,
     width: f32,
@@ -488,37 +460,34 @@ impl WrRect
     pub fn to_rect(&self) -> Rect<f32>
     {
         Rect::new(Point2D::new(self.x, self.y), Size2D::new(self.width, self.height))
     }
 }
 
 #[no_mangle]
 pub extern fn wr_dp_push_image(state:&mut WrState, bounds: WrRect, clip : WrRect, mask: *const WrImageMask, key: ImageKey) {
-    if state.dl_builder.len() == 0 {
-      return;
-    }
     //let (width, height) = state.size;
     let bounds = bounds.to_rect();
     let clip = clip.to_rect();
 
     // convert from the C type to the Rust type
     let mask = unsafe { mask.as_ref().map(|&WrImageMask{image, ref rect,repeat}| ImageMask{image: image, rect: rect.to_rect(), repeat: repeat}) };
 
     let clip_region = webrender_traits::ClipRegion::new(&clip,
                                                         Vec::new(),
                                                         mask,
                                                         &mut state.frame_builder.auxiliary_lists_builder);
     let rect = bounds;
-    state.dl_builder.last_mut().unwrap().push_image(rect,
-                               clip_region,
-                               rect.size,
-                               rect.size,
-                               ImageRendering::Auto,
-                               key);
+    state.frame_builder.temp_dl_builder.push_image(rect,
+                                                   clip_region,
+                                                   rect.size,
+                                                   rect.size,
+                                                   ImageRendering::Auto,
+                                                   key);
 }
 
 #[no_mangle]
 pub extern fn wr_destroy(state:*mut WrState) {
   unsafe {
     Box::from_raw(state);
   }
 }
--- a/gfx/webrender/src/debug_render.rs
+++ b/gfx/webrender/src/debug_render.rs
@@ -156,52 +156,54 @@ impl DebugRenderer {
         let color1 = PackedColor::from_color(color1);
         self.line_vertices.push(DebugColorVertex::new(x0 as f32, y0 as f32, color0));
         self.line_vertices.push(DebugColorVertex::new(x1 as f32, y1 as f32, color1));
     }
 
     pub fn render(&mut self,
                   device: &mut Device,
                   viewport_size: &Size2D<u32>) {
-        if !self.font_indices.is_empty() ||
-           !self.line_vertices.is_empty() ||
-           !self.tri_vertices.is_empty() {
-
-            device.disable_depth();
-            device.set_blend(true);
-            device.set_blend_mode_alpha();
+        device.disable_depth();
+        device.set_blend(true);
+        device.set_blend_mode_alpha();
 
-            let projection = Matrix4D::ortho(0.0,
-                                             viewport_size.width as f32,
-                                             viewport_size.height as f32,
-                                             0.0,
-                                             ORTHO_NEAR_PLANE,
-                                             ORTHO_FAR_PLANE);
+        let projection = Matrix4D::ortho(0.0,
+                                         viewport_size.width as f32,
+                                         viewport_size.height as f32,
+                                         0.0,
+                                         ORTHO_NEAR_PLANE,
+                                         ORTHO_FAR_PLANE);
 
-            // Triangles
+        // Triangles
+        if !self.tri_vertices.is_empty() {
             device.bind_program(self.color_program_id, &projection);
             device.bind_vao(self.tri_vao);
             device.update_vao_indices(self.tri_vao,
                                       &self.tri_indices,
                                       VertexUsageHint::Dynamic);
             device.update_vao_main_vertices(self.tri_vao,
                                             &self.tri_vertices,
                                             VertexUsageHint::Dynamic);
             device.draw_triangles_u32(0, self.tri_indices.len() as i32);
+        }
 
-            // Lines
+        // Lines
+        if !self.line_vertices.is_empty() {
+            device.bind_program(self.color_program_id, &projection);
             device.bind_vao(self.line_vao);
             device.update_vao_main_vertices(self.line_vao,
                                             &self.line_vertices,
                                             VertexUsageHint::Dynamic);
             device.draw_nonindexed_lines(0, self.line_vertices.len() as i32);
+        }
 
-            // Glyphs
+        // Glyph
+        if !self.font_indices.is_empty() {
             device.bind_program(self.font_program_id, &projection);
-            device.bind_texture(TextureSampler::Color, self.font_texture_id);
+            device.bind_texture(TextureSampler::Color0, self.font_texture_id);
             device.bind_vao(self.font_vao);
             device.update_vao_indices(self.font_vao,
                                       &self.font_indices,
                                       VertexUsageHint::Dynamic);
             device.update_vao_main_vertices(self.font_vao,
                                             &self.font_vertices,
                                             VertexUsageHint::Dynamic);
             device.draw_triangles_u32(0, self.font_indices.len() as i32);
--- a/gfx/webrender/src/device.rs
+++ b/gfx/webrender/src/device.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 euclid::Matrix4D;
 use fnv::FnvHasher;
 use gleam::gl;
 use internal_types::{PackedVertex, PackedVertexForQuad};
 use internal_types::{RenderTargetMode, TextureSampler};
-use internal_types::{VertexAttribute, DebugFontVertex, DebugColorVertex};
+use internal_types::{VertexAttribute, DebugFontVertex, DebugColorVertex, DEFAULT_TEXTURE};
 //use notify::{self, Watcher};
 use std::collections::HashMap;
 use std::fs::File;
 use std::hash::BuildHasherDefault;
 use std::io::Read;
 use std::path::PathBuf;
 use std::mem;
 //use std::sync::mpsc::{channel, Sender};
@@ -263,16 +263,18 @@ impl TextureId {
     }
 
     pub fn invalid() -> TextureId {
         TextureId {
             name: 0,
             target: gl::TEXTURE_2D,
         }
     }
+
+    pub fn is_valid(&self) -> bool { *self != TextureId::invalid() }
 }
 
 impl ProgramId {
     fn bind(&self) {
         gl::use_program(self.0);
     }
 }
 
@@ -728,17 +730,16 @@ pub struct Device {
     capabilities: Capabilities,
 
     // debug
     inside_frame: bool,
 
     // resources
     resource_path: PathBuf,
     textures: HashMap<TextureId, Texture, BuildHasherDefault<FnvHasher>>,
-    raw_textures: HashMap<TextureId, (u32, u32, u32, u32), BuildHasherDefault<FnvHasher>>,
     programs: HashMap<ProgramId, Program, BuildHasherDefault<FnvHasher>>,
     vaos: HashMap<VAOId, VAO, BuildHasherDefault<FnvHasher>>,
 
     // misc.
     shader_preamble: String,
     //file_watcher: FileWatcherThread,
 
     // Used on android only
@@ -771,17 +772,16 @@ impl Device {
 
             bound_textures: [ TextureId::invalid(); 16 ],
             bound_program: ProgramId(0),
             bound_vao: VAOId(0),
             bound_fbo: FBOId(0),
             default_fbo: 0,
 
             textures: HashMap::with_hasher(Default::default()),
-            raw_textures: HashMap::with_hasher(Default::default()),
             programs: HashMap::with_hasher(Default::default()),
             vaos: HashMap::with_hasher(Default::default()),
 
             shader_preamble: shader_preamble,
 
             next_vao_id: 1,
             //file_watcher: file_watcher,
         }
@@ -939,43 +939,18 @@ impl Device {
 
             texture_ids.push(texture_id);
         }
 
         texture_ids
     }
 
     pub fn get_texture_dimensions(&self, texture_id: TextureId) -> (u32, u32) {
-        if let Some(texture) = self.textures.get(&texture_id) {
-            (texture.width, texture.height)
-        } else {
-            let dimensions = self.raw_textures.get(&texture_id).unwrap();
-            (dimensions.2, dimensions.3)
-        }
-    }
-
-    pub fn texture_has_alpha(&self, texture_id: TextureId) -> bool {
-        if let Some(texture) = self.textures.get(&texture_id) {
-            texture.format == ImageFormat::RGBA8
-        } else {
-            true
-        }
-    }
-
-    pub fn update_raw_texture(&mut self,
-                              texture_id: TextureId,
-                              x0: u32,
-                              y0: u32,
-                              width: u32,
-                              height: u32) {
-        self.raw_textures.insert(texture_id, (x0, y0, width, height));
-    }
-
-    pub fn remove_raw_texture(&mut self, texture_id: TextureId) {
-        self.raw_textures.remove(&texture_id);
+        let texture = &self.textures[&texture_id];
+        (texture.width, texture.height)
     }
 
     fn set_texture_parameters(&mut self, target: gl::GLuint, filter: TextureFilter) {
         let filter = match filter {
             TextureFilter::Nearest => {
                 gl::NEAREST
             }
             TextureFilter::Linear => {
@@ -1027,34 +1002,34 @@ impl Device {
             texture.mode = mode;
         }
 
         let (internal_format, gl_format) = gl_texture_formats_for_image_format(format);
         let type_ = gl_type_for_texture_format(format);
 
         match mode {
             RenderTargetMode::SimpleRenderTarget => {
-                self.bind_texture(TextureSampler::Color, texture_id);
+                self.bind_texture(DEFAULT_TEXTURE, texture_id);
                 self.set_texture_parameters(texture_id.target, filter);
                 self.upload_texture_image(texture_id.target,
                                           width,
                                           height,
                                           internal_format as u32,
                                           gl_format,
                                           type_,
                                           None);
                 self.create_fbo_for_texture_if_necessary(texture_id, None);
             }
             RenderTargetMode::LayerRenderTarget(layer_count) => {
-                self.bind_texture(TextureSampler::Color, texture_id);
+                self.bind_texture(DEFAULT_TEXTURE, texture_id);
                 self.set_texture_parameters(texture_id.target, filter);
                 self.create_fbo_for_texture_if_necessary(texture_id, Some(layer_count));
             }
             RenderTargetMode::None => {
-                self.bind_texture(TextureSampler::Color, texture_id);
+                self.bind_texture(DEFAULT_TEXTURE, texture_id);
                 self.set_texture_parameters(texture_id.target, filter);
                 self.upload_texture_image(texture_id.target,
                                           width,
                                           height,
                                           internal_format as u32,
                                           gl_format,
                                           type_,
                                           pixels);
@@ -1138,32 +1113,32 @@ impl Device {
 
         let (old_width, old_height) = self.get_texture_dimensions(texture_id);
 
         let temp_texture_id = self.create_texture_ids(1, TextureTarget::Default)[0];
         self.init_texture(temp_texture_id, old_width, old_height, format, filter, mode, None);
         self.create_fbo_for_texture_if_necessary(temp_texture_id, None);
 
         self.bind_render_target(Some((texture_id, 0)), None);
-        self.bind_texture(TextureSampler::Color, temp_texture_id);
+        self.bind_texture(DEFAULT_TEXTURE, temp_texture_id);
 
         gl::copy_tex_sub_image_2d(temp_texture_id.target,
                                   0,
                                   0,
                                   0,
                                   0,
                                   0,
                                   old_width as i32,
                                   old_height as i32);
 
         self.deinit_texture(texture_id);
         self.init_texture(texture_id, new_width, new_height, format, filter, mode, None);
         self.create_fbo_for_texture_if_necessary(texture_id, None);
         self.bind_render_target(Some((temp_texture_id, 0)), None);
-        self.bind_texture(TextureSampler::Color, texture_id);
+        self.bind_texture(DEFAULT_TEXTURE, texture_id);
 
         gl::copy_tex_sub_image_2d(texture_id.target,
                                   0,
                                   0,
                                   0,
                                   0,
                                   0,
                                   old_width as i32,
@@ -1171,17 +1146,17 @@ impl Device {
 
         self.bind_render_target(None, None);
         self.deinit_texture(temp_texture_id);
     }
 
     pub fn deinit_texture(&mut self, texture_id: TextureId) {
         debug_assert!(self.inside_frame);
 
-        self.bind_texture(TextureSampler::Color, texture_id);
+        self.bind_texture(DEFAULT_TEXTURE, texture_id);
 
         let texture = self.textures.get_mut(&texture_id).unwrap();
         let (internal_format, gl_format) = gl_texture_formats_for_image_format(texture.format);
         let type_ = gl_type_for_texture_format(texture.format);
 
         gl::tex_image_2d(texture_id.target,
                          0,
                          internal_format,
@@ -1326,19 +1301,27 @@ impl Device {
                     let vs_id = program.vs_id.unwrap();
                     let fs_id = program.fs_id.unwrap();
                     program.attach_and_bind_shaders(vs_id, fs_id, true);
                 }
 
                 program.u_transform = gl::get_uniform_location(program.id, "uTransform");
 
                 program_id.bind();
-                let u_diffuse = gl::get_uniform_location(program.id, "sDiffuse");
-                if u_diffuse != -1 {
-                    gl::uniform_1i(u_diffuse, TextureSampler::Color as i32);
+                let u_color_0 = gl::get_uniform_location(program.id, "sColor0");
+                if u_color_0 != -1 {
+                    gl::uniform_1i(u_color_0, TextureSampler::Color0 as i32);
+                }
+                let u_color1 = gl::get_uniform_location(program.id, "sColor1");
+                if u_color1 != -1 {
+                    gl::uniform_1i(u_color1, TextureSampler::Color1 as i32);
+                }
+                let u_color_2 = gl::get_uniform_location(program.id, "sColor2");
+                if u_color_2 != -1 {
+                    gl::uniform_1i(u_color_2, TextureSampler::Color2 as i32);
                 }
                 let u_mask = gl::get_uniform_location(program.id, "sMask");
                 if u_mask != -1 {
                     gl::uniform_1i(u_mask, TextureSampler::Mask as i32);
                 }
                 let u_device_pixel_ratio = gl::get_uniform_location(program.id, "uDevicePixelRatio");
                 if u_device_pixel_ratio != -1 {
                     gl::uniform_1f(u_device_pixel_ratio, self.device_pixel_ratio);
@@ -1496,50 +1479,31 @@ impl Device {
         };
 
         assert!(data.len() as u32 == bpp * row_length * height);
 
         if let Some(..) = stride {
             gl::pixel_store_i(gl::UNPACK_ROW_LENGTH, row_length as gl::GLint);
         }
 
-        self.bind_texture(TextureSampler::Color, texture_id);
+        self.bind_texture(DEFAULT_TEXTURE, texture_id);
         self.update_image_for_2d_texture(texture_id.target,
                                          x0 as gl::GLint,
                                          y0 as gl::GLint,
                                          width as gl::GLint,
                                          height as gl::GLint,
                                          gl_format,
                                          data);
 
         // Reset row length to 0, otherwise the stride would apply to all texture uploads.
         if let Some(..) = stride {
             gl::pixel_store_i(gl::UNPACK_ROW_LENGTH, 0 as gl::GLint);
         }
     }
 
-    pub fn read_framebuffer_rect(&mut self,
-                                 texture_id: TextureId,
-                                 dest_x: i32,
-                                 dest_y: i32,
-                                 src_x: i32,
-                                 src_y: i32,
-                                 width: i32,
-                                 height: i32) {
-        self.bind_texture(TextureSampler::Color, texture_id);
-        gl::copy_tex_sub_image_2d(texture_id.target,
-                                  0,
-                                  dest_x,
-                                  dest_y,
-                                  src_x as gl::GLint,
-                                  src_y as gl::GLint,
-                                  width as gl::GLint,
-                                  height as gl::GLint);
-    }
-
     fn clear_vertex_array(&mut self) {
         debug_assert!(self.inside_frame);
         gl::bind_vertex_array(0);
     }
 
     pub fn bind_vao(&mut self, vao_id: VAOId) {
         debug_assert!(self.inside_frame);
 
@@ -1755,16 +1719,22 @@ impl Device {
                                 gl::ONE, gl::ONE);
         gl::blend_equation(gl::FUNC_ADD);
     }
 
     pub fn set_blend_mode_subpixel(&self, color: ColorF) {
         gl::blend_color(color.r, color.g, color.b, color.a);
         gl::blend_func(gl::CONSTANT_COLOR, gl::ONE_MINUS_SRC_COLOR);
     }
+
+    pub fn set_blend_mode_multiply(&self) {
+        gl::blend_func_separate(gl::ZERO, gl::SRC_COLOR,
+                                gl::ZERO, gl::SRC_ALPHA);
+        gl::blend_equation(gl::FUNC_ADD);
+    }
 }
 
 impl Drop for Device {
     fn drop(&mut self) {
         //self.file_watcher.exit();
     }
 }
 
--- a/gfx/webrender/src/frame.rs
+++ b/gfx/webrender/src/frame.rs
@@ -6,110 +6,69 @@ use app_units::Au;
 use euclid::{Matrix4D, Point2D, Point3D, Point4D, Rect, Size2D};
 use fnv::FnvHasher;
 use geometry::ray_intersects_rect;
 use internal_types::{ANGLE_FLOAT_TO_FIXED, AxisDirection};
 use internal_types::{CompositionOp};
 use internal_types::{LowLevelFilterOp};
 use internal_types::{RendererFrame};
 use layer::{Layer, ScrollingState};
-use resource_cache::{DummyResources, ResourceCache};
-use scene::{SceneStackingContext, ScenePipeline, Scene, SceneItem, SpecificSceneItem};
+use resource_cache::ResourceCache;
+use scene::Scene;
 use std::collections::{HashMap, HashSet};
 use std::hash::BuildHasherDefault;
 use tiling::{AuxiliaryListsMap, FrameBuilder, FrameBuilderConfig, PrimitiveFlags};
 use util::MatrixHelpers;
 use webrender_traits::{AuxiliaryLists, PipelineId, Epoch, ScrollPolicy, ScrollLayerId};
-use webrender_traits::{ClipRegion, ColorF, StackingContext, FilterOp, MixBlendMode};
+use webrender_traits::{ClipRegion, ColorF, DisplayItem, StackingContext, FilterOp, MixBlendMode};
 use webrender_traits::{ScrollEventPhase, ScrollLayerInfo, SpecificDisplayItem, ScrollLayerState};
 
 #[cfg(target_os = "macos")]
 const CAN_OVERSCROLL: bool = true;
 
 #[cfg(not(target_os = "macos"))]
 const CAN_OVERSCROLL: bool = false;
 
 #[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> {
-    resource_cache: &'a mut ResourceCache,
     scene: &'a Scene,
     pipeline_sizes: &'a mut HashMap<PipelineId, Size2D<f32>>,
     builder: &'a mut FrameBuilder,
 }
 
-#[derive(Debug)]
-struct FlattenInfo {
-    // Pipeline this part of the tree belongs to.
-    pipeline_id: PipelineId,
-
-    current_scroll_layer_id: ScrollLayerId,
-    current_fixed_layer_id: ScrollLayerId,
-
-    layer_relative_transform: Matrix4D<f32>,
-}
-
 pub type LayerMap = HashMap<ScrollLayerId, Layer, BuildHasherDefault<FnvHasher>>;
 
 // TODO: doc
 pub struct Frame {
     pub layers: LayerMap,
     pub pipeline_epoch_map: HashMap<PipelineId, Epoch, BuildHasherDefault<FnvHasher>>,
     pub pipeline_auxiliary_lists: HashMap<PipelineId,
                                           AuxiliaryLists,
                                           BuildHasherDefault<FnvHasher>>,
     pub root_scroll_layer_id: Option<ScrollLayerId>,
     id: FrameId,
     debug: bool,
     frame_builder_config: FrameBuilderConfig,
     frame_builder: Option<FrameBuilder>,
 }
 
-enum SceneItemKind<'a> {
-    StackingContext(&'a SceneStackingContext, PipelineId),
-    Pipeline(&'a ScenePipeline)
-}
-
-#[derive(Clone)]
-struct SceneItemWithZOrder {
-    item: SceneItem,
-    z_index: i32,
+trait DisplayListHelpers {
+    fn starting_stacking_context<'a>(&'a self) -> Option<&'a StackingContext>;
 }
 
-impl<'a> SceneItemKind<'a> {
-    fn collect_scene_items(&self, scene: &Scene) -> Vec<SceneItem> {
-        let mut result = Vec::new();
-        let stacking_context = match *self {
-            SceneItemKind::StackingContext(stacking_context, _) => {
-                &stacking_context.stacking_context
-            }
-            SceneItemKind::Pipeline(pipeline) => {
-                if let Some(background_draw_list) = pipeline.background_draw_list {
-                    result.push(SceneItem {
-                        specific: SpecificSceneItem::DrawList(background_draw_list),
-                    });
-                }
-
-                &scene.stacking_context_map
-                      .get(&pipeline.root_stacking_context_id)
-                      .unwrap()
-                      .stacking_context
-            }
-        };
-
-        for display_list_id in &stacking_context.display_lists {
-            let display_list = &scene.display_list_map[display_list_id];
-            for item in &display_list.items {
-                result.push(item.clone());
-            }
-        }
-        result
+impl DisplayListHelpers for Vec<DisplayItem> {
+    fn starting_stacking_context<'a>(&'a self) -> Option<&'a StackingContext> {
+        self.first().and_then(|item| match item.item {
+            SpecificDisplayItem::PushStackingContext(ref item) => Some(&item.stacking_context),
+            _ => None,
+        })
     }
 }
 
 trait StackingContextHelpers {
     fn needs_composition_operation_for_mix_blend_mode(&self) -> bool;
     fn composition_operations(&self, auxiliary_lists: &AuxiliaryLists) -> Vec<CompositionOp>;
 }
 
@@ -185,16 +144,66 @@ impl StackingContextHelpers for Stacking
                 }
             }
         }
 
         composition_operations
     }
 }
 
+struct DisplayListTraversal<'a> {
+    pub display_list: &'a [DisplayItem],
+    pub next_item_index: usize,
+}
+
+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) {
+        for item in self {
+            if item.item == SpecificDisplayItem::PopStackingContext {
+                return;
+            }
+        }
+    }
+
+    pub fn current_stacking_context_empty(&self) -> bool {
+        match self.peek() {
+            Some(item) => item.item == SpecificDisplayItem::PopStackingContext,
+            None => true,
+        }
+    }
+
+    fn peek(&self) -> Option<&'a DisplayItem> {
+        if self.next_item_index >= self.display_list.len() {
+            return None
+        }
+        Some(&self.display_list[self.next_item_index])
+    }
+}
+
+impl<'a> Iterator for DisplayListTraversal<'a> {
+    type Item = &'a DisplayItem;
+
+    fn next(&mut self) -> Option<&'a DisplayItem> {
+        if self.next_item_index >= self.display_list.len() {
+            return None
+        }
+
+        let item = &self.display_list[self.next_item_index];
+        self.next_item_index += 1;
+        Some(item)
+    }
+}
+
 impl Frame {
     pub fn new(debug: bool, config: FrameBuilderConfig) -> Frame {
         Frame {
             pipeline_epoch_map: HashMap::with_hasher(Default::default()),
             pipeline_auxiliary_lists: HashMap::with_hasher(Default::default()),
             layers: HashMap::with_hasher(Default::default()),
             root_scroll_layer_id: None,
             id: FrameId(0),
@@ -383,385 +392,372 @@ impl Frame {
     pub fn tick_scrolling_bounce_animations(&mut self) {
         for (_, layer) in &mut self.layers {
             layer.tick_scrolling_bounce_animation()
         }
     }
 
     pub fn create(&mut self,
                   scene: &Scene,
-                  resource_cache: &mut ResourceCache,
-                  dummy_resources: &DummyResources,
                   pipeline_sizes: &mut HashMap<PipelineId, Size2D<f32>>,
                   device_pixel_ratio: f32) {
-        if let Some(root_pipeline_id) = scene.root_pipeline_id {
-            if let Some(root_pipeline) = scene.pipeline_map.get(&root_pipeline_id) {
-                let old_layer_scrolling_states = self.reset();
-
-                self.pipeline_auxiliary_lists = scene.pipeline_auxiliary_lists.clone();
+        let root_pipeline_id = match scene.root_pipeline_id {
+            Some(root_pipeline_id) => root_pipeline_id,
+            None => return,
+        };
 
-                let root_stacking_context = scene.stacking_context_map
-                                                 .get(&root_pipeline.root_stacking_context_id)
-                                                 .unwrap();
+        let root_pipeline = match scene.pipeline_map.get(&root_pipeline_id) {
+            Some(root_pipeline) => root_pipeline,
+            None => return,
+        };
 
-                let root_scroll_layer_id = root_stacking_context.stacking_context
-                                                                .scroll_layer_id
-                                                                .expect("root layer must be a scroll layer!");
-                self.root_scroll_layer_id = Some(root_scroll_layer_id);
+        let display_list = scene.display_lists.get(&root_pipeline_id);
+        let display_list = match display_list {
+            Some(display_list) => display_list,
+            None => return,
+        };
 
-                // Insert global position: fixed elements layer
-                debug_assert!(self.layers.is_empty());
-                let root_fixed_layer_id = ScrollLayerId::create_fixed(root_pipeline_id);
-                let root_viewport = Rect::new(Point2D::zero(), root_pipeline.viewport_size);
-                self.layers.insert(
-                    root_fixed_layer_id,
-                    Layer::new(&root_viewport,
-                               root_stacking_context.stacking_context.overflow.size,
-                               &Matrix4D::identity(),
-                               root_pipeline_id));
+        let old_layer_scrolling_states = self.reset();
+        self.pipeline_auxiliary_lists = scene.pipeline_auxiliary_lists.clone();
+
+        self.pipeline_epoch_map.insert(root_pipeline_id, root_pipeline.epoch);
 
-                self.layers.insert(
-                    root_scroll_layer_id,
-                    Layer::new(&root_viewport,
-                               root_stacking_context.stacking_context.overflow.size,
-                               &Matrix4D::identity(),
-                               root_pipeline_id));
+        let root_stacking_context = match display_list.starting_stacking_context() {
+            Some(stacking_context) => stacking_context,
+            None => {
+                warn!("Pipeline display list does not start with a stacking context.");
+                return;
+            }
+        };
+
+        let root_scroll_layer_id =
+            root_stacking_context.scroll_layer_id
+                                 .expect("root layer must be a scroll layer!");
+        self.root_scroll_layer_id = Some(root_scroll_layer_id);
 
-                // Work around borrow check on resource cache
-                {
-                    let mut frame_builder = FrameBuilder::new(root_pipeline.viewport_size,
-                                                              device_pixel_ratio,
-                                                              dummy_resources.clone(),
-                                                              self.debug,
-                                                              self.frame_builder_config);
+        // Insert global position: fixed elements layer
+        debug_assert!(self.layers.is_empty());
+        let root_fixed_layer_id = ScrollLayerId::create_fixed(root_pipeline_id);
+        let root_viewport = Rect::new(Point2D::zero(), root_pipeline.viewport_size);
+        let layer = Layer::new(&root_viewport,
+                               root_stacking_context.overflow.size,
+                               &Matrix4D::identity(),
+                               root_pipeline_id);
+        self.layers.insert(root_fixed_layer_id, layer.clone());
+        self.layers.insert(root_scroll_layer_id, layer);
 
-                    {
-                        let mut context = FlattenContext {
-                            resource_cache: resource_cache,
-                            scene: scene,
-                            pipeline_sizes: pipeline_sizes,
-                            builder: &mut frame_builder,
-                        };
+        let mut frame_builder = FrameBuilder::new(root_pipeline.viewport_size,
+                                                  device_pixel_ratio,
+                                                  self.debug,
+                                                  self.frame_builder_config);
+
+        {
+            let mut context = FlattenContext {
+                scene: scene,
+                pipeline_sizes: pipeline_sizes,
+                builder: &mut frame_builder,
+            };
 
-                        let parent_info = FlattenInfo {
-                            pipeline_id: root_pipeline_id,
-                            current_fixed_layer_id: root_fixed_layer_id,
-                            current_scroll_layer_id: root_scroll_layer_id,
-                            layer_relative_transform: Matrix4D::identity(),
-                        };
-
-                        let root_pipeline = SceneItemKind::Pipeline(root_pipeline);
-                        self.flatten(root_pipeline,
-                                     &parent_info,
-                                     &mut context,
-                                     0);
-                    }
+            let mut traversal = DisplayListTraversal::new_skipping_first(display_list);
+            self.flatten_stacking_context(&mut traversal,
+                                          root_pipeline_id,
+                                          &mut context,
+                                          root_fixed_layer_id,
+                                          root_scroll_layer_id,
+                                          Matrix4D::identity(),
+                                          0,
+                                          &root_stacking_context);
+        }
 
-                    self.frame_builder = Some(frame_builder);
-                }
+        self.frame_builder = Some(frame_builder);
 
-                // TODO(gw): These are all independent - can be run through thread pool if it shows up in the profile!
-                for (scroll_layer_id, layer) in &mut self.layers {
-                    let scrolling_state = match old_layer_scrolling_states.get(&scroll_layer_id) {
-                        Some(old_scrolling_state) => *old_scrolling_state,
-                        None => ScrollingState::new(),
-                    };
+        // TODO(gw): These are all independent - can be run through thread pool if it shows up
+        // in the profile!
+        for (scroll_layer_id, layer) in &mut self.layers {
+            let scrolling_state = match old_layer_scrolling_states.get(&scroll_layer_id) {
+                Some(old_scrolling_state) => *old_scrolling_state,
+                None => ScrollingState::new(),
+            };
 
-                    layer.finalize(&scrolling_state);
-                }
-            }
+            layer.finalize(&scrolling_state);
         }
     }
 
-    fn flatten(&mut self,
-               scene_item: SceneItemKind,
-               parent_info: &FlattenInfo,
-               context: &mut FlattenContext,
-               level: i32) {
-        //let _pf = util::ProfileScope::new("  flatten");
+    fn flatten_stacking_context<'a>(&mut self,
+                                    traversal: &mut DisplayListTraversal<'a>,
+                                    pipeline_id: PipelineId,
+                                    context: &mut FlattenContext,
+                                    current_fixed_layer_id: ScrollLayerId,
+                                    mut current_scroll_layer_id: ScrollLayerId,
+                                    mut layer_relative_transform: Matrix4D<f32>,
+                                    level: i32,
+                                    stacking_context: &StackingContext) {
+        // Avoid doing unnecessary work for empty stacking contexts.
+        if traversal.current_stacking_context_empty() {
+            traversal.skip_current_stacking_context();
+            return;
+        }
 
-        let (stacking_context, pipeline_id) = match scene_item {
-            SceneItemKind::StackingContext(stacking_context, pipeline_id) => {
-                (&stacking_context.stacking_context, pipeline_id)
+        match (stacking_context.scroll_policy, stacking_context.scroll_layer_id) {
+            (ScrollPolicy::Scrollable, Some(inner_scroll_layer_id)) if level != 0 => {
+                debug_assert!(!self.layers.contains_key(&inner_scroll_layer_id));
+
+                let layer = Layer::new(&stacking_context.bounds,
+                                       stacking_context.overflow.size,
+                                       &layer_relative_transform,
+                                       pipeline_id);
+
+                debug_assert!(current_scroll_layer_id != inner_scroll_layer_id);
+                self.layers
+                    .get_mut(&current_scroll_layer_id)
+                    .unwrap()
+                    .add_child(inner_scroll_layer_id);
+
+                self.layers.insert(inner_scroll_layer_id, layer);
+                current_scroll_layer_id = inner_scroll_layer_id;
+                layer_relative_transform = Matrix4D::identity();
             }
-            SceneItemKind::Pipeline(pipeline) => {
-                self.pipeline_epoch_map.insert(pipeline.pipeline_id, pipeline.epoch);
-
-                let stacking_context = &context.scene.stacking_context_map
-                                               .get(&pipeline.root_stacking_context_id)
-                                               .unwrap()
-                                               .stacking_context;
-
-                (stacking_context, pipeline.pipeline_id)
-            }
-        };
-
-        let scene_items = scene_item.collect_scene_items(&context.scene);
-        if scene_items.is_empty() {
-            return;
+            _ => {}
         }
 
         let composition_operations = {
             let auxiliary_lists = self.pipeline_auxiliary_lists
                                       .get(&pipeline_id)
                                       .expect("No auxiliary lists?!");
             stacking_context.composition_operations(auxiliary_lists)
         };
 
         // Detect composition operations that will make us invisible.
         for composition_operation in &composition_operations {
             match *composition_operation {
-                CompositionOp::Filter(LowLevelFilterOp::Opacity(Au(0))) => return,
+                CompositionOp::Filter(LowLevelFilterOp::Opacity(Au(0))) => {
+                    traversal.skip_current_stacking_context();
+                    return;
+                }
                 _ => {}
             }
         }
 
         // Stacking contexts with scroll roots are currently not "real" stacking contexts,
         // but are currently represented as stacking contexts in the display list until they
         // can get their own display item to represent them. Thus we do not adjust the transform
         // to account for them and we expand the overflow region to include the area above
         // their origin in the parent context.
         let (transform, overflow) = if stacking_context.scroll_layer_id.is_none() {
-            (parent_info.layer_relative_transform.pre_translated(stacking_context.bounds.origin.x,
-                                                                stacking_context.bounds.origin.y,
-                                                                0.0)
-                                                 .pre_mul(&stacking_context.transform)
-                                                 .pre_mul(&stacking_context.perspective),
+            (layer_relative_transform.pre_translated(stacking_context.bounds.origin.x,
+                                                     stacking_context.bounds.origin.y,
+                                                     0.0)
+                                     .pre_mul(&stacking_context.transform)
+                                     .pre_mul(&stacking_context.perspective),
              stacking_context.overflow)
         } else {
             let mut overflow = stacking_context.overflow;
             overflow.size.width += stacking_context.bounds.origin.x;
             overflow.size.height += stacking_context.bounds.origin.y;
-            (parent_info.layer_relative_transform, overflow)
+            (layer_relative_transform, overflow)
         };
 
         // Build world space transform
-        let scroll_layer_id =  match (stacking_context.scroll_policy, stacking_context.scroll_layer_id) {
+        let scroll_layer_id = match (stacking_context.scroll_policy,
+                                     stacking_context.scroll_layer_id) {
             (ScrollPolicy::Fixed, _scroll_layer_id) => {
                 debug_assert!(_scroll_layer_id.is_none());
-                parent_info.current_fixed_layer_id
+                current_fixed_layer_id
             }
-            (ScrollPolicy::Scrollable, Some(scroll_layer_id)) => {
-                debug_assert!(self.layers.contains_key(&scroll_layer_id));
-                scroll_layer_id
-            }
-            (ScrollPolicy::Scrollable, None) => {
-                parent_info.current_scroll_layer_id
-            }
+            (ScrollPolicy::Scrollable, _) => current_scroll_layer_id
         };
 
         // TODO(gw): Int with overflow etc
         context.builder.push_layer(overflow,
                                    overflow,
                                    transform,
                                    pipeline_id,
                                    scroll_layer_id,
                                    &composition_operations);
 
         if level == 0 {
-            // Add a large white rectangle as the root display item. This is removed
-            // by the occlusion culling for most tiles, and means that it's no longer
-            // necessary to clear the framebuffer.
+            // Add a large white rectangle as the root display item if there is no root stacking
+            // context background color. This is removed by the occlusion culling for most tiles,
+            // and means that it's no longer necessary to clear the framebuffer.
             //
-            // TODO(nical) Should this be optional?
-            // on deferred GPUs we probably still want to clear the framebuffer and
-            // Gecko currently supports semit-transparent windows.
-            // Also, this is not needed if the root stacking context has an opaque
-            // background (specified in set_root_stacking_context).
+            // TODO(nical) Should painting a white background be optional if there is no stacking
+            // context background color? On deferred GPUs we probably still want to clear the
+            // framebuffer and Gecko currently supports semi-transparent windows.
             //
             // If we do need this, does it make sense to keep Frame::clear_tiles?
+            let mut root_background_color = match context.scene.pipeline_map.get(&pipeline_id) {
+                Some(pipeline) => pipeline.background_color,
+                None => ColorF::new(1.0, 1.0, 1.0, 1.0),
+            };
+
+            if root_background_color.a == 0.0 {
+                root_background_color = ColorF::new(1.0, 1.0, 1.0, 1.0);
+            }
+
             context.builder.add_solid_rectangle(&stacking_context.bounds,
                                                 &ClipRegion::simple(&stacking_context.bounds),
-                                                &ColorF::new(1.0, 1.0, 1.0, 1.0),
+                                                &root_background_color,
                                                 PrimitiveFlags::None);
         }
 
-        for item in scene_items {
-            match item.specific {
-                SpecificSceneItem::DrawList(draw_list_id) => {
-                    let draw_list = context.resource_cache.get_draw_list(draw_list_id);
-                    let builder = &mut context.builder;
-
-                    for item in &draw_list.items {
-                        match item.item {
-                            SpecificDisplayItem::WebGL(ref info) => {
-                                builder.add_webgl_rectangle(item.rect,
-                                                            &item.clip,
-                                                            info.context_id);
-                            }
-                            SpecificDisplayItem::Image(ref info) => {
-                                builder.add_image(item.rect,
-                                                  &item.clip,
-                                                  &info.stretch_size,
-                                                  &info.tile_spacing,
-                                                  info.image_key,
-                                                  info.image_rendering);
-                            }
-                            SpecificDisplayItem::Text(ref text_info) => {
-                                builder.add_text(item.rect,
-                                                 &item.clip,
-                                                 text_info.font_key,
-                                                 text_info.size,
-                                                 text_info.blur_radius,
-                                                 &text_info.color,
-                                                 text_info.glyphs);
-                            }
-                            SpecificDisplayItem::Rectangle(ref info) => {
-                                builder.add_solid_rectangle(&item.rect,
-                                                            &item.clip,
-                                                            &info.color,
-                                                            PrimitiveFlags::None);
-                            }
-                            SpecificDisplayItem::Gradient(ref info) => {
-                                builder.add_gradient(item.rect,
-                                                     &item.clip,
-                                                     info.start_point,
-                                                     info.end_point,
-                                                     info.stops);
-                            }
-                            SpecificDisplayItem::BoxShadow(ref box_shadow_info) => {
-                                builder.add_box_shadow(&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) => {
-                                builder.add_border(item.rect,
-                                                   &item.clip,
-                                                   info);
-                            }
-                        }
-                    }
-                }
-                SpecificSceneItem::StackingContext(id, pipeline_id) => {
-                    let child_stacking_context = context.scene
-                                                        .stacking_context_map
-                                                        .get(&id)
-                                                        .unwrap();
-
-                    let inner_stacking_context = &child_stacking_context.stacking_context;
-
-                    let mut next_scroll_layer_id = parent_info.current_scroll_layer_id;
-                    let mut layer_relative_transform = transform;
-
-                    match (inner_stacking_context.scroll_policy, inner_stacking_context.scroll_layer_id) {
-                        (ScrollPolicy::Scrollable, Some(inner_scroll_layer_id)) => {
-                            debug_assert!(!self.layers.contains_key(&inner_scroll_layer_id));
-
-                            let layer = Layer::new(&inner_stacking_context.bounds,
-                                                   inner_stacking_context.overflow.size,
-                                                   &transform,
-                                                   parent_info.pipeline_id);
-
-                            debug_assert!(parent_info.current_scroll_layer_id != inner_scroll_layer_id);
-                            self.layers
-                                .get_mut(&scroll_layer_id)
-                                .unwrap()
-                                .add_child(inner_scroll_layer_id);
-
-                            self.layers.insert(inner_scroll_layer_id, layer);
-                            next_scroll_layer_id = inner_scroll_layer_id;
-                            layer_relative_transform = Matrix4D::identity();
-                        }
-                        (ScrollPolicy::Fixed, _) |
-                        (ScrollPolicy::Scrollable, None) => {}
-                    }
-
-                    let child_info = FlattenInfo {
-                        pipeline_id: parent_info.pipeline_id,
-                        current_scroll_layer_id: next_scroll_layer_id,
-                        current_fixed_layer_id: parent_info.current_fixed_layer_id,
-                        layer_relative_transform: layer_relative_transform,
-                    };
-
-                    let child = SceneItemKind::StackingContext(child_stacking_context,
-                                                               pipeline_id);
-
-                    self.flatten(child,
-                                 &child_info,
-                                 context,
-                                 level+1);
-                }
-                SpecificSceneItem::Iframe(ref iframe_info) => {
-                    let pipeline = context.scene
-                                          .pipeline_map
-                                          .get(&iframe_info.id);
-
-                    context.pipeline_sizes.insert(iframe_info.id,
-                                                  iframe_info.bounds.size);
-
-                    if let Some(pipeline) = pipeline {
-                        let iframe = SceneItemKind::Pipeline(pipeline);
-
-                        let transform = transform.pre_translated(iframe_info.bounds.origin.x,
-                                                                 iframe_info.bounds.origin.y,
-                                                                 0.0);
-
-                        let iframe_stacking_context = context.scene
-                                                             .stacking_context_map
-                                                             .get(&pipeline.root_stacking_context_id)
-                                                             .unwrap();
-                        let iframe_stacking_context = &iframe_stacking_context.stacking_context;
-                        let iframe_fixed_layer_id = ScrollLayerId::create_fixed(pipeline.pipeline_id);
-                        let iframe_rect = &Rect::new(Point2D::zero(), iframe_info.bounds.size);
-
-                        self.layers
-                            .insert(iframe_fixed_layer_id,
-                                    Layer::new(&iframe_rect,
-                                               iframe_stacking_context.overflow.size,
-                                               &transform,
-                                               pipeline.pipeline_id));
-
-                        let iframe_scroll_layer_id = iframe_stacking_context.scroll_layer_id.unwrap();
-
-                        self.layers
-                            .insert(iframe_scroll_layer_id,
-                                    Layer::new(&iframe_rect,
-                                               iframe_stacking_context.overflow.size,
-                                               &transform,
-                                               pipeline.pipeline_id));
-
-                        self.layers
-                            .get_mut(&scroll_layer_id)
-                            .unwrap()
-                            .add_child(iframe_scroll_layer_id);
-
-                        let child_info = FlattenInfo {
-                            pipeline_id: pipeline.pipeline_id,
-                            current_scroll_layer_id: iframe_scroll_layer_id,
-                            current_fixed_layer_id: iframe_fixed_layer_id,
-                            layer_relative_transform: Matrix4D::identity(),
-                        };
-
-                        self.flatten(iframe,
-                                     &child_info,
-                                     context,
-                                     level+1);
-                    }
-                }
-            }
-        }
+        self.flatten_items(traversal,
+                           pipeline_id,
+                           context,
+                           current_fixed_layer_id,
+                           current_scroll_layer_id,
+                           transform,
+                           level);
 
         if level == 0 && self.frame_builder_config.enable_scrollbars {
-            let scrollbar_rect = Rect::new(Point2D::zero(),
-                                           Size2D::new(10.0, 70.0));
+            let scrollbar_rect = Rect::new(Point2D::zero(), Size2D::new(10.0, 70.0));
             context.builder.add_solid_rectangle(&scrollbar_rect,
                                                 &ClipRegion::simple(&scrollbar_rect),
                                                 &DEFAULT_SCROLLBAR_COLOR,
                                                 PrimitiveFlags::Scrollbar(self.root_scroll_layer_id.unwrap(),
                                                                           4.0));
         }
 
         context.builder.pop_layer();
     }
 
+    fn flatten_iframe<'a>(&mut self,
+                          pipeline_id: PipelineId,
+                          bounds: &Rect<f32>,
+                          context: &mut FlattenContext,
+                          current_scroll_layer_id: ScrollLayerId,
+                          layer_relative_transform: Matrix4D<f32>) {
+        context.pipeline_sizes.insert(pipeline_id, bounds.size);
+
+        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);
+        let display_list = match display_list {
+            Some(display_list) => display_list,
+            None => return,
+        };
+
+        let iframe_stacking_context = match display_list.starting_stacking_context() {
+            Some(stacking_context) => stacking_context,
+            None => {
+                warn!("Pipeline display list does not start with a stacking context.");
+                return;
+            }
+        };
+
+        self.pipeline_epoch_map.insert(pipeline_id, pipeline.epoch);
+
+        let iframe_rect = &Rect::new(Point2D::zero(), bounds.size);
+        let transform = layer_relative_transform.pre_translated(bounds.origin.x,
+                                                                bounds.origin.y,
+                                                                0.0);
+
+        let iframe_fixed_layer_id = ScrollLayerId::create_fixed(pipeline_id);
+        let iframe_scroll_layer_id = iframe_stacking_context.scroll_layer_id.unwrap();
+
+        let layer = Layer::new(&iframe_rect,
+                               iframe_stacking_context.overflow.size,
+                               &transform,
+                               pipeline_id);
+        self.layers.insert(iframe_fixed_layer_id, layer.clone());
+        self.layers.insert(iframe_scroll_layer_id, layer);
+        self.layers.get_mut(&current_scroll_layer_id).unwrap().add_child(iframe_scroll_layer_id);
+
+        let mut traversal = DisplayListTraversal::new_skipping_first(display_list);
+
+        self.flatten_stacking_context(&mut traversal,
+                                      pipeline_id,
+                                      context,
+                                      iframe_fixed_layer_id,
+                                      iframe_scroll_layer_id,
+                                      Matrix4D::identity(),
+                                      0,
+                                      &iframe_stacking_context);
+    }
+
+    fn flatten_items<'a>(&mut self,
+                         traversal: &mut DisplayListTraversal<'a>,
+                         pipeline_id: PipelineId,
+                         context: &mut FlattenContext,
+                         current_fixed_layer_id: ScrollLayerId,
+                         current_scroll_layer_id: ScrollLayerId,
+                         layer_relative_transform: Matrix4D<f32>,
+                         level: i32) {
+        while let Some(item) = traversal.next() {
+            match item.item {
+                SpecificDisplayItem::WebGL(ref info) => {
+                    context.builder.add_webgl_rectangle(item.rect, &item.clip, info.context_id);
+                }
+                SpecificDisplayItem::Image(ref info) => {
+                    context.builder.add_image(item.rect,
+                                              &item.clip,
+                                              &info.stretch_size,
+                                              &info.tile_spacing,
+                                              info.image_key,
+                                              info.image_rendering);
+                }
+                SpecificDisplayItem::Text(ref text_info) => {
+                    context.builder.add_text(item.rect,
+                                             &item.clip,
+                                             text_info.font_key,
+                                             text_info.size,
+                                             text_info.blur_radius,
+                                             &text_info.color,
+                                             text_info.glyphs);
+                }
+                SpecificDisplayItem::Rectangle(ref info) => {
+                    context.builder.add_solid_rectangle(&item.rect,
+                                                        &item.clip,
+                                                        &info.color,
+                                                        PrimitiveFlags::None);
+                }
+                SpecificDisplayItem::Gradient(ref info) => {
+                    context.builder.add_gradient(item.rect,
+                                                 &item.clip,
+                                                 info.start_point,
+                                                 info.end_point,
+                                                 info.stops);
+                }
+                SpecificDisplayItem::BoxShadow(ref box_shadow_info) => {
+                    context.builder.add_box_shadow(&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(item.rect, &item.clip, info);
+                }
+                SpecificDisplayItem::PushStackingContext(ref info) => {
+                    self.flatten_stacking_context(traversal,
+                                                  pipeline_id,
+                                                  context,
+                                                  current_fixed_layer_id,
+                                                  current_scroll_layer_id,
+                                                  layer_relative_transform,
+                                                  level + 1,
+                                                  &info.stacking_context);
+                }
+                SpecificDisplayItem::Iframe(ref info) => {
+                    self.flatten_iframe(info.pipeline_id,
+                                        &item.rect,
+                                        context,
+                                        current_scroll_layer_id,
+                                        layer_relative_transform);
+                }
+                SpecificDisplayItem::PopStackingContext => return,
+            }
+        }
+    }
+
     pub fn build(&mut self,
                  resource_cache: &mut ResourceCache,
                  auxiliary_lists_map: &AuxiliaryListsMap,
                  device_pixel_ratio: f32)
                  -> RendererFrame {
         self.update_layer_transforms(device_pixel_ratio);
         let frame = self.build_frame(resource_cache, auxiliary_lists_map);
         resource_cache.expire_old_resources(self.id);
--- a/gfx/webrender/src/gpu_store.rs
+++ b/gfx/webrender/src/gpu_store.rs
@@ -1,16 +1,16 @@
 /* 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 renderer::MAX_VERTEX_TEXTURE_WIDTH;
 use std::mem;
 
-#[derive(Debug, Copy, Clone)]
+#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
 pub struct GpuStoreAddress(pub i32);
 
 /// A CPU-side buffer storing content to be uploaded to the GPU.
 pub struct GpuStore<T> {
     data: Vec<T>,
     // TODO(gw): Could store this intrusively inside
     // the data array free slots.
     //free_list: Vec<GpuStoreAddress>,
--- a/gfx/webrender/src/internal_types.rs
+++ b/gfx/webrender/src/internal_types.rs
@@ -1,33 +1,60 @@
 /* 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 device::{TextureId, TextureFilter};
-use euclid::{Point2D, Rect, Size2D, TypedRect, TypedPoint2D, TypedSize2D, Length, UnknownUnit};
+use device::TextureFilter;
+use euclid::{Size2D, TypedRect, TypedPoint2D, TypedSize2D, Length, UnknownUnit};
 use fnv::FnvHasher;
-use freelist::{FreeListItem, FreeListItemId};
 use offscreen_gl_context::{NativeGLContext, NativeGLContextHandle};
 use offscreen_gl_context::{GLContext, NativeGLContextMethods, GLContextDispatcher};
 use offscreen_gl_context::{OSMesaContext, OSMesaContextHandle};
 use offscreen_gl_context::{ColorAttachmentType, GLContextAttributes, GLLimits};
 use profiler::BackendProfileCounters;
 use std::collections::{HashMap, HashSet};
 use std::f32;
 use std::hash::BuildHasherDefault;
-use std::i32;
+use std::{i32, usize};
 use std::path::PathBuf;
 use std::sync::Arc;
 use tiling;
 use webrender_traits::{Epoch, ColorF, PipelineId};
-use webrender_traits::{ImageFormat, MixBlendMode, NativeFontHandle, DisplayItem};
+use webrender_traits::{ImageFormat, MixBlendMode, NativeFontHandle};
 use webrender_traits::{ScrollLayerId, WebGLCommand};
 
+// An ID for a texture that is owned by the
+// texture cache module. This can include atlases
+// or standalone textures allocated via the
+// texture cache (e.g. if an image is too large
+// to be added to an atlas). The texture cache
+// manages the allocation and freeing of these
+// IDs, and the rendering thread maintains a
+// map from cache texture ID to native texture.
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
+pub struct CacheTextureId(pub usize);
+
+// Represents the source for a texture.
+// These are passed from throughout the
+// pipeline until they reach the rendering
+// thread, where they are resolved to a
+// native texture ID.
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
+pub enum SourceTexture {
+    Invalid,
+    TextureCache(CacheTextureId),
+    WebGL(u32),                         // Is actually a gl::GLuint
+
+    // TODO(gw): Implement external image support via callback
+    //External(i32),
+}
+
 pub enum GLContextHandleWrapper {
     Native(NativeGLContextHandle),
     OSMesa(OSMesaContextHandle),
 }
 
 impl GLContextHandleWrapper {
     pub fn current_native_handle() -> Option<GLContextHandleWrapper> {
         NativeGLContext::current_handle().map(GLContextHandleWrapper::Native)
@@ -157,32 +184,63 @@ pub const ORTHO_NEAR_PLANE: f32 = -10000
 pub const ORTHO_FAR_PLANE: f32 = 1000000.0;
 
 
 pub enum FontTemplate {
     Raw(Arc<Vec<u8>>),
     Native(NativeFontHandle),
 }
 
-pub type DrawListId = FreeListItemId;
-
 #[derive(Debug, PartialEq, Eq)]
 pub enum TextureSampler {
-    Color,
+    Color0,
+    Color1,
+    Color2,
     Mask,
     Cache,
     Data16,
     Data32,
     Data64,
     Data128,
     Layers,
     RenderTasks,
     Geometry,
 }
 
+impl TextureSampler {
+    pub fn color(n: usize) -> TextureSampler {
+        match n {
+            0 => TextureSampler::Color0,
+            1 => TextureSampler::Color1,
+            2 => TextureSampler::Color2,
+            _ => {
+                panic!("There are only 3 color samplers.");
+            }
+        }
+    }
+}
+
+/// Optional textures that can be used as a source in the shaders.
+/// Textures that are not used by the batch are equal to TextureId::invalid().
+#[derive(Copy, Clone, Debug)]
+pub struct BatchTextures {
+    pub colors: [SourceTexture; 3],
+}
+
+impl BatchTextures {
+    pub fn no_texture() -> Self {
+        BatchTextures {
+            colors: [SourceTexture::Invalid; 3],
+        }
+    }
+}
+
+// In some places we need to temporarily bind a texture to any slot.
+pub const DEFAULT_TEXTURE: TextureSampler = TextureSampler::Color0;
+
 pub enum VertexAttribute {
     Position,
     PositionRect,
     ColorRectTL,
     ColorRectTR,
     ColorRectBR,
     ColorRectBL,
     ColorTexCoordRectTop,
@@ -295,38 +353,24 @@ impl DebugColorVertex {
 
 #[derive(Copy, Clone, Debug, PartialEq)]
 pub enum RenderTargetMode {
     None,
     SimpleRenderTarget,
     LayerRenderTarget(i32),      // Number of texture layers
 }
 
-#[derive(Debug)]
-pub enum TextureUpdateDetails {
-    Raw,
-    Blit(Vec<u8>, Option<u32>),
-}
-
-#[derive(Clone, Copy, Debug)]
-pub struct TextureImage {
-    pub texture_id: TextureId,
-    pub texel_uv: Rect<f32>,
-    pub pixel_uv: Point2D<u32>,
-}
-
 pub enum TextureUpdateOp {
     Create(u32, u32, ImageFormat, TextureFilter, RenderTargetMode, Option<Vec<u8>>),
-    Update(u32, u32, u32, u32, TextureUpdateDetails),
+    Update(u32, u32, u32, u32, Vec<u8>, Option<u32>),
     Grow(u32, u32, ImageFormat, TextureFilter, RenderTargetMode),
-    Remove
 }
 
 pub struct TextureUpdate {
-    pub id: TextureId,
+    pub id: CacheTextureId,
     pub op: TextureUpdateOp,
 }
 
 pub struct TextureUpdateList {
     pub updates: Vec<TextureUpdate>,
 }
 
 impl TextureUpdateList {
@@ -375,49 +419,19 @@ pub enum ResultMsg {
 
 #[repr(u32)]
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub enum AxisDirection {
     Horizontal,
     Vertical,
 }
 
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
 pub struct StackingContextIndex(pub usize);
 
-#[derive(Debug)]
-pub struct DrawList {
-    pub items: Vec<DisplayItem>,
-    pub stacking_context_index: Option<StackingContextIndex>,
-    pub pipeline_id: PipelineId,
-    // TODO(gw): Structure squat to remove this field.
-    next_free_id: Option<FreeListItemId>,
-}
-
-impl DrawList {
-    pub fn new(items: Vec<DisplayItem>, pipeline_id: PipelineId) -> DrawList {
-        DrawList {
-            items: items,
-            stacking_context_index: None,
-            pipeline_id: pipeline_id,
-            next_free_id: None,
-        }
-    }
-}
-
-impl FreeListItem for DrawList {
-    fn next_free_id(&self) -> Option<FreeListItemId> {
-        self.next_free_id
-    }
-
-    fn set_next_free_id(&mut self, id: Option<FreeListItemId>) {
-        self.next_free_id = id;
-    }
-}
-
 #[derive(Clone, Copy, Debug)]
 pub struct RectUv<T, U = UnknownUnit> {
     pub top_left: TypedPoint2D<T, U>,
     pub top_right: TypedPoint2D<T, U>,
     pub bottom_left: TypedPoint2D<T, U>,
     pub bottom_right: TypedPoint2D<T, U>,
 }
 
--- a/gfx/webrender/src/layer.rs
+++ b/gfx/webrender/src/layer.rs
@@ -2,16 +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 euclid::{Matrix4D, Point2D, Rect, Size2D};
 use spring::{DAMPING, STIFFNESS, Spring};
 use webrender_traits::{PipelineId, ScrollLayerId};
 
 /// Contains scroll and transform information for scrollable and root stacking contexts.
+#[derive(Clone)]
 pub struct Layer {
     /// Manages scrolling offset, overscroll state etc.
     pub scrolling: ScrollingState,
 
     /// Size of the content inside the scroll region (in logical pixels)
     pub content_size: Size2D<f32>,
 
     /// Viewing rectangle
--- a/gfx/webrender/src/lib.rs
+++ b/gfx/webrender/src/lib.rs
@@ -55,50 +55,64 @@ mod debug_font_data;
 mod debug_render;
 mod device;
 mod frame;
 mod freelist;
 mod geometry;
 mod gpu_store;
 mod internal_types;
 mod layer;
+mod mask_cache;
 mod prim_store;
 mod profiler;
 mod record;
 mod render_backend;
 mod resource_cache;
 mod scene;
 mod spring;
 mod texture_cache;
 mod tiling;
 mod util;
 pub mod bindings;
 
 mod platform {
     #[cfg(target_os="macos")]
     pub use platform::macos::font;
-    #[cfg(any(target_os = "android", target_os = "windows", all(unix, not(target_os = "macos"))))]
+    #[cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))]
     pub use platform::unix::font;
+    #[cfg(target_os = "windows")]
+    pub use platform::windows::font;
 
     #[cfg(target_os="macos")]
     pub mod macos {
         pub mod font;
     }
-    #[cfg(any(target_os = "android", target_os = "windows", all(unix, not(target_os = "macos"))))]
+    #[cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))]
     pub mod unix {
         pub mod font;
     }
+    #[cfg(target_os = "windows")]
+    pub mod windows {
+        pub mod font;
+    }
 }
 
 pub mod renderer;
 
 #[cfg(target_os="macos")]
 extern crate core_graphics;
 #[cfg(target_os="macos")]
 extern crate core_text;
+
+#[cfg(all(unix, not(target_os="macos")))]
+extern crate freetype;
+
+#[cfg(target_os = "windows")]
+extern crate dwrote;
+
 #[cfg(target_os="macos")]
 extern crate core_foundation;
 #[cfg(not(target_os="macos"))]
 extern crate freetype;
 
 #[cfg(target_os="windows")]
 extern crate kernel32;
 #[cfg(target_os="windows")]
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -1,26 +1,28 @@
 /* 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 device::TextureId;
 use euclid::{Point2D, Matrix4D, Rect, Size2D};
 use gpu_store::{GpuStore, GpuStoreAddress};
-use internal_types::{device_pixel, DeviceRect, DeviceSize};
+use internal_types::{device_pixel, DeviceRect, DeviceSize, SourceTexture};
+use mask_cache::{MaskCacheInfo, MaskCacheKey};
 use resource_cache::ResourceCache;
 use std::mem;
 use std::usize;
-use texture_cache::TextureCacheItem;
 use tiling::RenderTask;
 use util::TransformedRect;
 use webrender_traits::{AuxiliaryLists, ColorF, ImageKey, ImageRendering};
-use webrender_traits::{FontRenderMode, WebGLContextId};
-use webrender_traits::{ClipRegion, FontKey, ItemRange, ComplexClipRegion, GlyphKey};
+use webrender_traits::{ClipRegion, ComplexClipRegion, ItemRange, GlyphKey};
+use webrender_traits::{FontKey, FontRenderMode, WebGLContextId};
+
+pub const CLIP_DATA_GPU_SIZE: usize = 5;
+pub const MASK_DATA_GPU_SIZE: usize = 1;
 
 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
 pub struct SpecificPrimitiveIndex(pub usize);
 
 impl SpecificPrimitiveIndex {
     pub fn invalid() -> SpecificPrimitiveIndex {
         SpecificPrimitiveIndex(usize::MAX)
     }
@@ -58,19 +60,18 @@ pub enum PrimitiveClipSource {
     Complex(Rect<f32>, f32),
     Region(ClipRegion),
 }
 
 // TODO(gw): Pack the fields here better!
 #[derive(Debug)]
 pub struct PrimitiveMetadata {
     pub is_opaque: bool,
-    pub mask_texture_id: TextureId,
-    pub clip_index: Option<GpuStoreAddress>,
     pub clip_source: Box<PrimitiveClipSource>,
+    pub clip_cache_info: Option<MaskCacheInfo>,
     pub prim_kind: PrimitiveKind,
     pub cpu_prim_index: SpecificPrimitiveIndex,
     pub gpu_prim_index: GpuStoreAddress,
     pub gpu_data_address: GpuStoreAddress,
     pub gpu_data_count: i32,
     // An optional render task that is a dependency of
     // drawing this primitive. For instance, box shadows
     // use this to draw a portion of the box shadow to
@@ -91,17 +92,17 @@ pub struct RectanglePrimitive {
 pub enum ImagePrimitiveKind {
     Image(ImageKey, ImageRendering, Size2D<f32>),
     WebGL(WebGLContextId),
 }
 
 #[derive(Debug)]
 pub struct ImagePrimitiveCpu {
     pub kind: ImagePrimitiveKind,
-    pub color_texture_id: TextureId,
+    pub color_texture_id: SourceTexture,
 }
 
 #[derive(Debug, Clone)]
 pub struct ImagePrimitiveGpu {
     pub uv0: Point2D<f32>,
     pub uv1: Point2D<f32>,
     pub stretch_size: Size2D<f32>,
     pub tile_spacing: Size2D<f32>,
@@ -184,17 +185,17 @@ pub struct TextRunPrimitiveGpu {
 pub struct TextRunPrimitiveCpu {
     pub font_key: FontKey,
     pub font_size: Au,
     pub blur_radius: Au,
     pub glyph_range: ItemRange,
     pub cache_dirty: bool,
     // TODO(gw): Maybe make this an Arc for sharing with resource cache
     pub glyph_indices: Vec<u32>,
-    pub color_texture_id: TextureId,
+    pub color_texture_id: SourceTexture,
     pub color: ColorF,
     pub render_mode: FontRenderMode,
 }
 
 #[derive(Debug, Clone)]
 struct GlyphPrimitive {
     offset: Point2D<f32>,
     padding: Point2D<f32>,
@@ -225,29 +226,28 @@ impl ClipCorner {
             outer_radius_y: outer_radius,
             inner_radius_x: inner_radius,
             inner_radius_y: inner_radius,
         }
     }
 }
 
 #[derive(Debug, Clone)]
-struct ImageMaskData {
+pub struct ImageMaskData {
     uv_rect: Rect<f32>,
     local_rect: Rect<f32>,
 }
 
 #[derive(Debug, Clone)]
 pub struct ClipData {
     rect: ClipRect,
     top_left: ClipCorner,
     top_right: ClipCorner,
     bottom_left: ClipCorner,
     bottom_right: ClipCorner,
-    mask_data: ImageMaskData,
 }
 
 impl ClipData {
     pub fn from_clip_region(clip: &ComplexClipRegion) -> ClipData {
         ClipData {
             rect: ClipRect {
                 rect: clip.rect,
                 padding: [0.0, 0.0, 0.0, 0.0],
@@ -282,20 +282,16 @@ impl ClipData {
                 rect: Rect::new(Point2D::new(clip.rect.origin.x + clip.rect.size.width - clip.radii.bottom_right.width,
                                              clip.rect.origin.y + clip.rect.size.height - clip.radii.bottom_right.height),
                                 Size2D::new(clip.radii.bottom_right.width, clip.radii.bottom_right.height)),
                 outer_radius_x: clip.radii.bottom_right.width,
                 outer_radius_y: clip.radii.bottom_right.height,
                 inner_radius_x: 0.0,
                 inner_radius_y: 0.0,
             },
-            mask_data: ImageMaskData {
-                uv_rect: Rect::zero(),
-                local_rect: Rect::zero(),
-            },
         }
     }
 
     pub fn uniform(rect: Rect<f32>, radius: f32) -> ClipData {
         ClipData {
             rect: ClipRect {
                 rect: rect,
                 padding: [0.0; 4],
@@ -315,20 +311,16 @@ impl ClipData {
                                                        Size2D::new(radius, radius)),
                                              radius,
                                              0.0),
             bottom_right: ClipCorner::uniform(Rect::new(Point2D::new(rect.origin.x + rect.size.width - radius,
                                                                      rect.origin.y + rect.size.height - radius),
                                                         Size2D::new(radius, radius)),
                                               radius,
                                               0.0),
-            mask_data: ImageMaskData {
-                uv_rect: Rect::zero(),
-                local_rect: Rect::zero(),
-            },
         }
     }
 }
 
 #[derive(Debug)]
 pub enum PrimitiveContainer {
     Rectangle(RectanglePrimitive),
     TextRun(TextRunPrimitiveCpu, TextRunPrimitiveGpu),
@@ -373,111 +365,96 @@ impl PrimitiveStore {
             gpu_data32: GpuStore::new(),
             gpu_data64: GpuStore::new(),
             gpu_data128: GpuStore::new(),
             device_pixel_ratio: device_pixel_ratio,
             prims_to_resolve: Vec::new(),
         }
     }
 
-    fn populate_clip_data(data: &mut [GpuBlock32], clip: ClipData) {
+    pub fn populate_clip_data(data: &mut [GpuBlock32], clip: ClipData) {
         data[0] = GpuBlock32::from(clip.rect);
         data[1] = GpuBlock32::from(clip.top_left);
         data[2] = GpuBlock32::from(clip.top_right);
         data[3] = GpuBlock32::from(clip.bottom_left);
         data[4] = GpuBlock32::from(clip.bottom_right);
-        data[5] = GpuBlock32::from(clip.mask_data);
     }
 
     pub fn add_primitive(&mut self,
-                         rect: &Rect<f32>,
-                         clip: &ClipRegion,
+                         geometry: PrimitiveGeometry,
+                         clip_source: Box<PrimitiveClipSource>,
+                         clip_info: Option<MaskCacheInfo>,
                          container: PrimitiveContainer) -> PrimitiveIndex {
         let prim_index = self.cpu_metadata.len();
-
         self.cpu_bounding_rects.push(None);
-
-        self.gpu_geometry.push(PrimitiveGeometry {
-            local_rect: *rect,
-            local_clip_rect: clip.main.clone(),
-        });
-
-        let clip_source = Box::new(if clip.is_complex() {
-            PrimitiveClipSource::Region(clip.clone())
-        } else {
-            PrimitiveClipSource::NoClip
-        });
+        self.gpu_geometry.push(geometry);
 
         let metadata = match container {
             PrimitiveContainer::Rectangle(rect) => {
                 let is_opaque = rect.color.a == 1.0;
                 let gpu_address = self.gpu_data16.push(rect);
 
                 let metadata = PrimitiveMetadata {
                     is_opaque: is_opaque,
-                    mask_texture_id: TextureId::invalid(),
-                    clip_index: None,
                     clip_source: clip_source,
+                    clip_cache_info: clip_info,
                     prim_kind: PrimitiveKind::Rectangle,
                     cpu_prim_index: SpecificPrimitiveIndex::invalid(),
                     gpu_prim_index: gpu_address,
                     gpu_data_address: GpuStoreAddress(0),
                     gpu_data_count: 0,
                     render_task: None,
                 };
 
                 metadata
             }
             PrimitiveContainer::TextRun(text_cpu, text_gpu) => {
                 let gpu_address = self.gpu_data16.push(text_gpu);
                 let gpu_glyphs_address = self.gpu_data32.alloc(text_cpu.glyph_range.length);
 
                 let metadata = PrimitiveMetadata {
                     is_opaque: false,
-                    mask_texture_id: TextureId::invalid(),
-                    clip_index: None,
                     clip_source: clip_source,
+                    clip_cache_info: clip_info,
                     prim_kind: PrimitiveKind::TextRun,
                     cpu_prim_index: SpecificPrimitiveIndex(self.cpu_text_runs.len()),
                     gpu_prim_index: gpu_address,
                     gpu_data_address: gpu_glyphs_address,
                     gpu_data_count: text_cpu.glyph_range.length as i32,
                     render_task: None,
                 };
 
                 self.cpu_text_runs.push(text_cpu);
                 metadata
             }
             PrimitiveContainer::Image(image_cpu, image_gpu) => {
                 let gpu_address = self.gpu_data32.push(image_gpu);
 
                 let metadata = PrimitiveMetadata {
                     is_opaque: false,
-                    mask_texture_id: TextureId::invalid(),
-                    clip_index: None,
                     clip_source: clip_source,
+                    clip_cache_info: clip_info,
                     prim_kind: PrimitiveKind::Image,
                     cpu_prim_index: SpecificPrimitiveIndex(self.cpu_images.len()),
                     gpu_prim_index: gpu_address,
                     gpu_data_address: GpuStoreAddress(0),
                     gpu_data_count: 0,
                     render_task: None,
                 };
 
                 self.cpu_images.push(image_cpu);
                 metadata
             }
             PrimitiveContainer::Border(border_cpu, border_gpu) => {
                 let gpu_address = self.gpu_data128.push(border_gpu);
 
                 let metadata = PrimitiveMetadata {
                     is_opaque: false,
-                    mask_texture_id: TextureId::invalid(),
-                    clip_index: None,
                     clip_source: clip_source,
+                    clip_cache_info: clip_info,
                     prim_kind: PrimitiveKind::Border,
                     cpu_prim_index: SpecificPrimitiveIndex(self.cpu_borders.len()),
                     gpu_prim_index: gpu_address,
                     gpu_data_address: GpuStoreAddress(0),
                     gpu_data_count: 0,
                     render_task: None,
                 };
 
@@ -485,19 +462,18 @@ impl PrimitiveStore {
                 metadata
             }
             PrimitiveContainer::Gradient(gradient_cpu, gradient_gpu) => {
                 let gpu_address = self.gpu_data32.push(gradient_gpu);
                 let gpu_stops_address = self.gpu_data32.alloc(gradient_cpu.stops_range.length);
 
                 let metadata = PrimitiveMetadata {
                     is_opaque: false,
-                    mask_texture_id: TextureId::invalid(),
-                    clip_index: None,
                     clip_source: clip_source,
+                    clip_cache_info: clip_info,
                     prim_kind: PrimitiveKind::Gradient,
                     cpu_prim_index: SpecificPrimitiveIndex(self.cpu_gradients.len()),
                     gpu_prim_index: gpu_address,
                     gpu_data_address: gpu_stops_address,
                     gpu_data_count: gradient_cpu.stops_range.length as i32,
                     render_task: None,
                 };
 
@@ -534,19 +510,18 @@ impl PrimitiveStore {
                                                              cache_size,
                                                              PrimitiveIndex(prim_index));
 
                 let gpu_prim_address = self.gpu_data64.push(box_shadow_gpu);
                 let gpu_data_address = self.gpu_data16.get_next_address();
 
                 let metadata = PrimitiveMetadata {
                     is_opaque: false,
-                    mask_texture_id: TextureId::invalid(),
-                    clip_index: None,
                     clip_source: clip_source,
+                    clip_cache_info: None,
                     prim_kind: PrimitiveKind::BoxShadow,
                     cpu_prim_index: SpecificPrimitiveIndex::invalid(),
                     gpu_prim_index: gpu_prim_address,
                     gpu_data_address: gpu_data_address,
                     gpu_data_count: instance_rects.len() as i32,
                     render_task: Some(render_task),
                 };
 
@@ -564,28 +539,25 @@ impl PrimitiveStore {
 
         PrimitiveIndex(prim_index)
     }
 
     pub fn resolve_primitives(&mut self, resource_cache: &ResourceCache) {
         for prim_index in self.prims_to_resolve.drain(..) {
             let metadata = &mut self.cpu_metadata[prim_index.0];
 
-            if let &PrimitiveClipSource::Region(ClipRegion { image_mask: Some(mask), .. }) = metadata.clip_source.as_ref() {
-                let tex_cache = resource_cache.get_image(mask.image, ImageRendering::Auto);
-                metadata.mask_texture_id = tex_cache.texture_id;
-                if let Some(address) = metadata.clip_index {
-                    let clip_data = self.gpu_data32.get_slice_mut(address, 6);
-                    clip_data[5] = GpuBlock32::from(ImageMaskData {
-                        uv_rect: Rect::new(tex_cache.uv0,
-                                           Size2D::new(tex_cache.uv1.x - tex_cache.uv0.x,
-                                                       tex_cache.uv1.y - tex_cache.uv0.y)),
-                        local_rect: mask.rect,
-                    });
-                }
+            if let Some(MaskCacheInfo{ key: MaskCacheKey { image: Some(gpu_address), .. }, image: Some(ref mask), .. }) = metadata.clip_cache_info {
+                let cache_item = resource_cache.get_image(mask.image, ImageRendering::Auto);
+                let mask_data = self.gpu_data32.get_slice_mut(gpu_address, MASK_DATA_GPU_SIZE);
+                mask_data[0] = GpuBlock32::from(ImageMaskData {
+                    uv_rect: Rect::new(cache_item.uv0,
+                                       Size2D::new(cache_item.uv1.x - cache_item.uv0.x,
+                                                   cache_item.uv1.y - cache_item.uv0.y)),
+                    local_rect: mask.rect,
+                });
             }
 
             match metadata.prim_kind {
                 PrimitiveKind::Rectangle |
                 PrimitiveKind::Border |
                 PrimitiveKind::BoxShadow |
                 PrimitiveKind::Gradient => {}
                 PrimitiveKind::TextRun => {
@@ -640,18 +612,18 @@ impl PrimitiveStore {
         let (rect, is_complex) = match source {
             PrimitiveClipSource::NoClip => (None, false),
             PrimitiveClipSource::Complex(rect, radius) => (Some(rect), radius > 0.0),
             PrimitiveClipSource::Region(ref region) => (Some(region.main), region.is_complex()),
         };
         if let Some(rect) = rect {
             self.gpu_geometry.get_mut(GpuStoreAddress(index.0 as i32))
                 .local_clip_rect = rect;
-            if is_complex && metadata.clip_index.is_none() {
-                metadata.clip_index = Some(self.gpu_data32.alloc(6))
+            if is_complex {
+                metadata.clip_cache_info = None; //CLIP TODO: re-use the existing GPU allocation
             }
         }
         *metadata.clip_source.as_mut() = source;
     }
 
     pub fn get_metadata(&self, index: PrimitiveIndex) -> &PrimitiveMetadata {
         &self.cpu_metadata[index.0]
     }
@@ -681,60 +653,35 @@ impl PrimitiveStore {
         self.cpu_bounding_rects[prim_index.0] = bounding_rect;
         bounding_rect.is_some()
     }
 
     /// Returns true if the bounding box needs to be updated.
     pub fn prepare_prim_for_render(&mut self,
                                    prim_index: PrimitiveIndex,
                                    resource_cache: &mut ResourceCache,
+                                   layer_transform: &Matrix4D<f32>,
+                                   layer_combined_local_clip_rect: &Rect<f32>,
                                    device_pixel_ratio: f32,
-                                   dummy_mask_cache_item: &TextureCacheItem,
                                    auxiliary_lists: &AuxiliaryLists) -> bool {
+
         let metadata = &mut self.cpu_metadata[prim_index.0];
         let mut prim_needs_resolve = false;
         let mut rebuild_bounding_rect = false;
 
-        if metadata.clip_index.is_none() {
-            // if the `clip_index` already exist, we consider the contents up to date
-            let clip_data = match metadata.clip_source.as_ref() {
-                &PrimitiveClipSource::NoClip => None,
-                &PrimitiveClipSource::Complex(rect, radius) => {
-                    Some(ClipData::uniform(rect, radius))
-                }
-                &PrimitiveClipSource::Region(ref clip_region) => {
-                    if let Some(mask) = clip_region.image_mask {
-                        resource_cache.request_image(mask.image, ImageRendering::Auto);
-                    }
-                    let clips = auxiliary_lists.complex_clip_regions(&clip_region.complex);
-                    //TODO: proper solution to multiple complex clips
-                    match clips.len() {
-                        0 if clip_region.image_mask.is_none() => None,
-                        0 => Some(ClipData::uniform(clip_region.main, 0.0)),
-                        1 => Some(ClipData::from_clip_region(&clips[0])),
-                        _ => {
-                            let internal_clip = clips.last().unwrap();
-                            let region = if clips.iter().all(|current_clip| current_clip.might_contain(internal_clip)) {
-                                internal_clip
-                            } else {
-                                &clips[0]
-                            };
-                            Some(ClipData::from_clip_region(region))
-                        },
-                    }
-                }
-            };
-
-            if let Some(data) = clip_data {
+        if let Some(ref mut clip_info) = metadata.clip_cache_info {
+            clip_info.update(&metadata.clip_source,
+                             layer_transform,
+                             layer_combined_local_clip_rect,
+                             &mut self.gpu_data32,
+                             device_pixel_ratio,
+                             auxiliary_lists);
+            if let &PrimitiveClipSource::Region(ClipRegion{ image_mask: Some(ref mask), .. }) = metadata.clip_source.as_ref() {
+                resource_cache.request_image(mask.image, ImageRendering::Auto);
                 prim_needs_resolve = true;
-                let gpu_address = self.gpu_data32.alloc(6);
-                let gpu_data = self.gpu_data32.get_slice_mut(gpu_address, 6);
-                Self::populate_clip_data(gpu_data, data);
-                metadata.clip_index = Some(gpu_address);
-                metadata.mask_texture_id = dummy_mask_cache_item.texture_id;
             }
         }
 
         match metadata.prim_kind {
             PrimitiveKind::Rectangle |
             PrimitiveKind::Border |
             PrimitiveKind::BoxShadow => {}
             PrimitiveKind::TextRun => {
@@ -819,18 +766,18 @@ impl PrimitiveStore {
 
                 resource_cache.request_glyphs(text.font_key,
                                               text.font_size,
                                               &text.glyph_indices,
                                               text.render_mode);
             }
             PrimitiveKind::Image => {
                 let image_cpu = &mut self.cpu_images[metadata.cpu_prim_index.0];
+
                 prim_needs_resolve = true;
-
                 match image_cpu.kind {
                     ImagePrimitiveKind::Image(image_key, image_rendering, tile_spacing) => {
                         resource_cache.request_image(image_key, image_rendering);
 
                         // TODO(gw): This doesn't actually need to be calculated each frame.
                         // It's cheap enough that it's not worth introducing a cache for images
                         // right now, but if we introduce a cache for images for some other
                         // reason then we might as well cache this with it.
--- a/gfx/webrender/src/record.rs
+++ b/gfx/webrender/src/record.rs
@@ -14,19 +14,19 @@ fn write_data(frame: u32, data: &[u8]) {
 
 pub fn write_msg(frame: u32, msg: &ApiMsg) {
     match msg {
         &ApiMsg::AddRawFont(..) |
         &ApiMsg::AddNativeFont(..) |
         &ApiMsg::AddImage(..) |
         &ApiMsg::UpdateImage(..) |
         &ApiMsg::DeleteImage(..)|
-        &ApiMsg::SetRootStackingContext(..) |
+        &ApiMsg::SetRootDisplayList(..) |
         &ApiMsg::SetRootPipeline(..) |
-        &ApiMsg::Scroll(..)|
+        &ApiMsg::Scroll(..) |
         &ApiMsg::TickScrollingBounce |
         &ApiMsg::WebGLCommand(..) => {
             let buff = serialize(msg, bincode::SizeLimit::Infinite).unwrap();
             write_data(frame, &buff);
        }
        _ => {}
     }
 }
--- a/gfx/webrender/src/render_backend.rs
+++ b/gfx/webrender/src/render_backend.rs
@@ -1,28 +1,28 @@
 /* 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, ReadBytesExt};
 use frame::Frame;
-use internal_types::{FontTemplate, GLContextHandleWrapper, GLContextWrapper, ResultMsg, RendererFrame};
+use internal_types::{FontTemplate, GLContextHandleWrapper, GLContextWrapper};
+use internal_types::{SourceTexture, ResultMsg, RendererFrame};
 use ipc_channel::ipc::{IpcBytesReceiver, IpcBytesSender, IpcReceiver};
 use profiler::BackendProfileCounters;
-use resource_cache::{DummyResources, ResourceCache};
+use resource_cache::ResourceCache;
 use scene::Scene;
 use std::collections::HashMap;
 use std::fs;
 use std::io::{Cursor, Read};
 use std::sync::{Arc, Mutex};
 use std::sync::mpsc::Sender;
 use texture_cache::TextureCache;
 use webrender_traits::{ApiMsg, AuxiliaryLists, BuiltDisplayList, IdNamespace};
-use webrender_traits::{RenderNotifier, RenderDispatcher, WebGLCommand, WebGLContextId};
-use device::TextureId;
+use webrender_traits::{FlushNotifier, RenderNotifier, RenderDispatcher, WebGLCommand, WebGLContextId};
 use record;
 use tiling::FrameBuilderConfig;
 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.
@@ -31,41 +31,41 @@ pub struct RenderBackend {
     payload_rx: IpcBytesReceiver,
     payload_tx: IpcBytesSender,
     result_tx: Sender<ResultMsg>,
 
     device_pixel_ratio: f32,
     next_namespace_id: IdNamespace,
 
     resource_cache: ResourceCache,
-    dummy_resources: DummyResources,
 
     scene: Scene,
     frame: Frame,
 
     notifier: Arc<Mutex<Option<Box<RenderNotifier>>>>,
+    flush_notifier: Arc<Mutex<Option<Box<FlushNotifier>>>>,
     webrender_context_handle: Option<GLContextHandleWrapper>,
     webgl_contexts: HashMap<WebGLContextId, GLContextWrapper>,
     current_bound_webgl_context_id: Option<WebGLContextId>,
     enable_recording: bool,
     main_thread_dispatcher: Arc<Mutex<Option<Box<RenderDispatcher>>>>,
 
     next_webgl_id: usize,
 }
 
 impl RenderBackend {
     pub fn new(api_rx: IpcReceiver<ApiMsg>,
                payload_rx: IpcBytesReceiver,
                payload_tx: IpcBytesSender,
                result_tx: Sender<ResultMsg>,
                device_pixel_ratio: f32,
                texture_cache: TextureCache,
-               dummy_resources: DummyResources,
                enable_aa: bool,
                notifier: Arc<Mutex<Option<Box<RenderNotifier>>>>,
+               flush_notifier: Arc<Mutex<Option<Box<FlushNotifier>>>>,
                webrender_context_handle: Option<GLContextHandleWrapper>,
                config: FrameBuilderConfig,
                debug: bool,
                enable_recording:bool,
                main_thread_dispatcher:  Arc<Mutex<Option<Box<RenderDispatcher>>>>) -> RenderBackend {
 
         let resource_cache = ResourceCache::new(texture_cache,
                                                 device_pixel_ratio,
@@ -73,36 +73,37 @@ impl RenderBackend {
 
         RenderBackend {
             api_rx: api_rx,
             payload_rx: payload_rx,
             payload_tx: payload_tx,
             result_tx: result_tx,
             device_pixel_ratio: device_pixel_ratio,
             resource_cache: resource_cache,
-            dummy_resources: dummy_resources,
             scene: Scene::new(),
             frame: Frame::new(debug, config),
             next_namespace_id: IdNamespace(1),
             notifier: notifier,
+            flush_notifier: flush_notifier,
             webrender_context_handle: webrender_context_handle,
             webgl_contexts: HashMap::new(),
             current_bound_webgl_context_id: None,
             enable_recording:enable_recording,
             main_thread_dispatcher: main_thread_dispatcher,
             next_webgl_id: 0,
         }
     }
 
     pub fn run(&mut self) {
         let mut profile_counters = BackendProfileCounters::new();
         let mut frame_counter: u32 = 0;
         if self.enable_recording {
             fs::create_dir("record").ok();
         }
+
         loop {
             let msg = self.api_rx.recv();
             match msg {
                 Ok(msg) => {
                     if self.enable_recording {
                         record::write_msg(frame_counter, &msg);
                     }
                     match msg {
@@ -127,16 +128,20 @@ impl RenderBackend {
                             profile_counters.image_templates.inc(bytes.len());
                             self.resource_cache.add_image_template(id,
                                                                    width,
                                                                    height,
                                                                    stride,
                                                                    format,
                                                                    bytes);
                         }
+                        ApiMsg::Flush => {
+                            let mut flush_notifier = self.flush_notifier.lock();
+                            flush_notifier.as_mut().unwrap().as_mut().unwrap().all_messages_flushed();
+                        }
                         ApiMsg::UpdateImage(id, width, height, format, bytes) => {
                             self.resource_cache.update_image_template(id,
                                                                       width,
                                                                       height,
                                                                       format,
                                                                       bytes);
                         }
                         ApiMsg::DeleteImage(id) => {
@@ -145,85 +150,65 @@ impl RenderBackend {
                         ApiMsg::CloneApi(sender) => {
                             let result = self.next_namespace_id;
 
                             let IdNamespace(id_namespace) = self.next_namespace_id;
                             self.next_namespace_id = IdNamespace(id_namespace + 1);
 
                             sender.send(result).unwrap();
                         }
-                        ApiMsg::SetRootStackingContext(stacking_context_id,
-                                                       background_color,
-                                                       epoch,
-                                                       pipeline_id,
-                                                       viewport_size,
-                                                       stacking_contexts,
-                                                       display_lists,
-                                                       auxiliary_lists_descriptor) => {
-                            for (id, stacking_context) in stacking_contexts.into_iter() {
-                                self.scene.add_stacking_context(id,
-                                                                pipeline_id,
-                                                                epoch,
-                                                                stacking_context);
-                            }
-
+                        ApiMsg::SetRootDisplayList(background_color,
+                                                   epoch,
+                                                   pipeline_id,
+                                                   viewport_size,
+                                                   display_list_descriptor,
+                                                   auxiliary_lists_descriptor) => {
                             let mut leftover_auxiliary_data = vec![];
                             let mut auxiliary_data;
                             loop {
                                 auxiliary_data = self.payload_rx.recv().unwrap();
                                 {
                                     let mut payload_reader = Cursor::new(&auxiliary_data[..]);
-                                    let payload_stacking_context_id =
-                                        payload_reader.read_u32::<LittleEndian>().unwrap();
                                     let payload_epoch =
                                         payload_reader.read_u32::<LittleEndian>().unwrap();
-                                    if payload_epoch == epoch.0 &&
-                                            payload_stacking_context_id == stacking_context_id.0 {
+                                    if payload_epoch == epoch.0 {
                                         break
                                     }
                                 }
                                 leftover_auxiliary_data.push(auxiliary_data)
                             }
                             for leftover_auxiliary_data in leftover_auxiliary_data {
                                 self.payload_tx.send(&leftover_auxiliary_data[..]).unwrap()
                             }
                             if self.enable_recording {
                                 record::write_payload(frame_counter, &auxiliary_data);
                             }
-                            let mut auxiliary_data = Cursor::new(&mut auxiliary_data[8..]);
-                            for (display_list_id,
-                                 display_list_descriptor) in display_lists.into_iter() {
-                                let mut built_display_list_data =
-                                    vec![0; display_list_descriptor.size()];
-                                auxiliary_data.read_exact(&mut built_display_list_data[..])
-                                              .unwrap();
-                                let built_display_list =
-                                    BuiltDisplayList::from_data(built_display_list_data,
-                                                                display_list_descriptor);
-                                self.scene.add_display_list(display_list_id,
-                                                            pipeline_id,
-                                                            epoch,
-                                                            built_display_list,
-                                                            &mut self.resource_cache);
-                            }
+
+                            let mut auxiliary_data = Cursor::new(&mut auxiliary_data[4..]);
+                            let mut built_display_list_data =
+                                vec![0; display_list_descriptor.size()];
+                            auxiliary_data.read_exact(&mut built_display_list_data[..]).unwrap();
+                            let built_display_list =
+                                BuiltDisplayList::from_data(built_display_list_data,
+                                                            display_list_descriptor);
 
                             let mut auxiliary_lists_data =
                                 vec![0; auxiliary_lists_descriptor.size()];
                             auxiliary_data.read_exact(&mut auxiliary_lists_data[..]).unwrap();
                             let auxiliary_lists =
                                 AuxiliaryLists::from_data(auxiliary_lists_data,
                                                           auxiliary_lists_descriptor);
+
                             let frame = profile_counters.total_time.profile(|| {
-                                self.scene.set_root_stacking_context(pipeline_id,
-                                                                     epoch,
-                                                                     stacking_context_id,
-                                                                     background_color,
-                                                                     viewport_size,
-                                                                     &mut self.resource_cache,
-                                                                     auxiliary_lists);
+                                self.scene.set_root_display_list(pipeline_id,
+                                                                 epoch,
+                                                                 built_display_list,
+                                                                 background_color,
+                                                                 viewport_size,
+                                                                 auxiliary_lists);
 
                                 self.build_scene();
                                 self.render()
                             });
 
                             if self.scene.root_pipeline_id.is_some() {
                                 self.publish_frame_and_notify_compositor(frame, &mut profile_counters);
                                 frame_counter += 1;
@@ -296,17 +281,17 @@ impl RenderBackend {
                                         let id = WebGLContextId(self.next_webgl_id);
                                         self.next_webgl_id += 1;
 
                                         let (real_size, texture_id, limits) = ctx.get_info();
 
                                         self.webgl_contexts.insert(id, ctx);
 
                                         self.resource_cache
-                                            .add_webgl_texture(id, TextureId::new(texture_id), real_size);
+                                            .add_webgl_texture(id, SourceTexture::WebGL(texture_id), real_size);
 
                                         tx.send(Ok((id, limits))).unwrap();
                                     },
                                     Err(msg) => {
                                         tx.send(Err(msg.to_owned())).unwrap();
                                     }
                                 }
                             } else {
@@ -316,17 +301,17 @@ impl RenderBackend {
                         ApiMsg::ResizeWebGLContext(context_id, size) => {
                             let ctx = self.webgl_contexts.get_mut(&context_id).unwrap();
                             ctx.make_current();
                             match ctx.resize(&size) {
                                 Ok(_) => {
                                     // Update webgl texture size. Texture id may change too.
                                     let (real_size, texture_id, _) = ctx.get_info();
                                     self.resource_cache
-                                        .update_webgl_texture(context_id, TextureId::new(texture_id), real_size);
+                                        .update_webgl_texture(context_id, SourceTexture::WebGL(texture_id), real_size);
                                 },
                                 Err(msg) => {
                                     error!("Error resizing WebGLContext: {}", msg);
                                 }
                             }
                         }
                         ApiMsg::WebGLCommand(context_id, command) => {
                             // TODO: Buffer the commands and only apply them here if they need to
@@ -363,18 +348,16 @@ impl RenderBackend {
         // incur minimal cost.
         for (_, webgl_context) in &self.webgl_contexts {
             webgl_context.make_current();
             webgl_context.apply_command(WebGLCommand::Flush);
             webgl_context.unbind();
         }
 
         self.frame.create(&self.scene,
-                          &mut self.resource_cache,
-                          &self.dummy_resources,
                           &mut new_pipeline_sizes,
                           self.device_pixel_ratio);
 
         let mut updated_pipeline_sizes = HashMap::new();
 
         for (pipeline_id, old_size) in self.scene.pipeline_sizes.drain() {
             let new_size = new_pipeline_sizes.remove(&pipeline_id);
 
--- a/gfx/webrender/src/renderer.rs
+++ b/gfx/webrender/src/renderer.rs
@@ -10,60 +10,57 @@
 //! [renderer]: struct.Renderer.html
 
 use debug_colors;
 use debug_render::DebugRenderer;
 use device::{Device, ProgramId, TextureId, VertexFormat, GpuProfiler};
 use device::{TextureFilter, VAOId, VertexUsageHint, FileWatcherHandler, TextureTarget};
 use euclid::{Matrix4D, Size2D};
 use fnv::FnvHasher;
-use internal_types::{RendererFrame, ResultMsg, TextureUpdateOp};
-use internal_types::{TextureUpdateDetails, TextureUpdateList, PackedVertex, RenderTargetMode};
-use internal_types::{ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE, DevicePoint};
-use internal_types::{TextureSampler, GLContextHandleWrapper};
+use internal_types::{CacheTextureId, RendererFrame, ResultMsg, TextureUpdateOp};
+use internal_types::{TextureUpdateList, PackedVertex, RenderTargetMode};
+use internal_types::{ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE, DevicePoint, SourceTexture};
+use internal_types::{BatchTextures, TextureSampler, GLContextHandleWrapper};
 use ipc_channel::ipc;
 use profiler::{Profiler, BackendProfileCounters};
 use profiler::{GpuProfileTag, RendererProfileTimers, RendererProfileCounters};
 use render_backend::RenderBackend;
-use resource_cache::DummyResources;
 use std::cmp;
 use std::collections::HashMap;
 use std::f32;
 use std::hash::BuildHasherDefault;
 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 tiling::{self, Frame, FrameBuilderConfig, PrimitiveBatchData};
 use tiling::{RenderTarget, ClearTile};
 use time::precise_time_ns;
 use util::TransformedRectKind;
-use webrender_traits::{ColorF, Epoch, PipelineId, RenderNotifier, RenderDispatcher};
+use webrender_traits::{ColorF, Epoch, FlushNotifier, PipelineId, RenderNotifier, RenderDispatcher};
 use webrender_traits::{ImageFormat, RenderApiSender, RendererKind};
 
 pub const MAX_VERTEX_TEXTURE_WIDTH: usize = 1024;
 
 const UBO_BIND_DATA: u32 = 1;
 
 const GPU_TAG_CACHE_BOX_SHADOW: GpuProfileTag = GpuProfileTag { label: "C_BoxShadow", color: debug_colors::BLACK };
+const GPU_TAG_CACHE_CLIP: GpuProfileTag = GpuProfileTag { label: "C_Clip", color: debug_colors::PURPLE };
 const GPU_TAG_CACHE_TEXT_RUN: GpuProfileTag = GpuProfileTag { label: "C_TextRun", color: debug_colors::MISTYROSE };
 const GPU_TAG_INIT: GpuProfileTag = GpuProfileTag { label: "Init", color: debug_colors::WHITE };
 const GPU_TAG_SETUP_TARGET: GpuProfileTag = GpuProfileTag { label: "Target", color: debug_colors::SLATEGREY };
 const GPU_TAG_CLEAR_TILES: GpuProfileTag = GpuProfileTag { label: "Clear Tiles", color: debug_colors::BROWN };
 const GPU_TAG_PRIM_RECT: GpuProfileTag = GpuProfileTag { label: "Rect", color: debug_colors::RED };
-const GPU_TAG_PRIM_RECT_CLIP: GpuProfileTag = GpuProfileTag { label: "RectClip", color: debug_colors::DARKRED };
 const GPU_TAG_PRIM_IMAGE: GpuProfileTag = GpuProfileTag { label: "Image", color: debug_colors::GREEN };
-const GPU_TAG_PRIM_IMAGE_CLIP: GpuProfileTag = GpuProfileTag { label: "ImageClip", color: debug_colors::DARKGREEN };
 const GPU_TAG_PRIM_BLEND: GpuProfileTag = GpuProfileTag { label: "Blend", color: debug_colors::LIGHTBLUE };
 const GPU_TAG_PRIM_COMPOSITE: GpuProfileTag = GpuProfileTag { label: "Composite", color: debug_colors::MAGENTA };
 const GPU_TAG_PRIM_TEXT_RUN: GpuProfileTag = GpuProfileTag { label: "TextRun", color: debug_colors::BLUE };
 const GPU_TAG_PRIM_GRADIENT: GpuProfileTag = GpuProfileTag { label: "Gradient", color: debug_colors::YELLOW };
-const GPU_TAG_PRIM_GRADIENT_CLIP: GpuProfileTag = GpuProfileTag { label: "GradientClip", color: debug_colors::YELLOWGREEN };
 const GPU_TAG_PRIM_ANGLE_GRADIENT: GpuProfileTag = GpuProfileTag { label: "AngleGradient", color: debug_colors::POWDERBLUE };
 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, PartialEq)]
 pub enum BlendMode {
@@ -121,16 +118,17 @@ impl VertexDataTexture {
 
 const TRANSFORM_FEATURE: &'static str = "TRANSFORM";
 const SUBPIXEL_AA_FEATURE: &'static str = "SUBPIXEL_AA";
 
 enum ShaderKind {
     Primitive,
     Clear,
     Cache,
+    ClipCache,
 }
 
 struct LazilyCompiledShader {
     id: Option<ProgramId>,
     name: &'static str,
     kind: ShaderKind,
     max_ubo_vectors: usize,
     features: Vec<&'static str>,
@@ -167,16 +165,21 @@ impl LazilyCompiledShader {
                                         self.max_ubo_vectors)
                 }
                 ShaderKind::Primitive | ShaderKind::Cache => {
                     create_prim_shader(self.name,
                                        device,
                                        self.max_ubo_vectors,
                                        &self.features)
                 }
+                ShaderKind::ClipCache => {
+                    create_clip_shader(self.name,
+                                       device,
+                                       self.max_ubo_vectors)
+                }
             };
             self.id = Some(id);
         }
 
         self.id.unwrap()
     }
 }
 
@@ -261,33 +264,47 @@ fn create_prim_shader(name: &'static str
                               #define WR_MAX_VERTEX_TEXTURE_WIDTH {}\n",
                               max_ubo_vectors,
                               MAX_VERTEX_TEXTURE_WIDTH);
 
     for feature in features {
         prefix.push_str(&format!("#define WR_FEATURE_{}\n", feature));
     }
 
-    let includes_base = ["prim_shared"];
-    let includes_clip = ["prim_shared", "clip_shared"];
-    let includes: &[&str] = if name.ends_with("_clip") {
-        &includes_clip
-    } else {
-        &includes_base
-    };
+    let includes = &["prim_shared"];
     let program_id = device.create_program_with_prefix(name,
                                                        includes,
                                                        Some(prefix));
     let data_index = device.assign_ubo_binding(program_id, "Data", UBO_BIND_DATA);
 
     debug!("PrimShader {}: data={} max={}", name, data_index, max_ubo_vectors);
 
     program_id
 }
 
+fn create_clip_shader(name: &'static str,
+                      device: &mut Device,
+                      max_ubo_vectors: usize) -> ProgramId {
+    let prefix = format!("#define WR_MAX_UBO_VECTORS {}\n\
+                          #define WR_MAX_VERTEX_TEXTURE_WIDTH {}\n
+                          #define WR_FEATURE_TRANSFORM",
+                          max_ubo_vectors,
+                          MAX_VERTEX_TEXTURE_WIDTH);
+
+    let includes = &["prim_shared", "clip_shared"];
+    let program_id = device.create_program_with_prefix(name,
+                                                       includes,
+                                                       Some(prefix));
+    let data_index = device.assign_ubo_binding(program_id, "Data", UBO_BIND_DATA);
+
+    debug!("ClipShader {}: data={} max={}", name, data_index, max_ubo_vectors);
+
+    program_id
+}
+
 fn create_clear_shader(name: &'static str,
                        device: &mut Device,
                        max_ubo_vectors: usize) -> ProgramId {
     let prefix = format!("#define WR_MAX_UBO_VECTORS {}", max_ubo_vectors);
 
     let includes = &["shared_other"];
     let program_id = device.create_program_with_prefix(name,
                                                        includes,
@@ -309,51 +326,53 @@ pub struct Renderer {
     pending_shader_updates: Vec<PathBuf>,
     current_frame: Option<RendererFrame>,
     device_pixel_ratio: f32,
 
     // These are "cache shaders". These shaders are used to
     // draw intermediate results to cache targets. The results
     // of these shaders are then used by the primitive shaders.
     cs_box_shadow: LazilyCompiledShader,
+    cs_clip_clear: LazilyCompiledShader,
+    cs_clip_rectangle: LazilyCompiledShader,
+    cs_clip_image: LazilyCompiledShader,
     cs_text_run: LazilyCompiledShader,
     cs_blur: LazilyCompiledShader,
 
     // The are "primitive shaders". These shaders draw and blend
     // final results on screen. They are aware of tile boundaries.
     // Most draw directly to the framebuffer, but some use inputs
     // from the cache shaders to draw. Specifically, the box
     // shadow primitive shader stretches the box shadow cache
     // output, and the cache_image shader blits the results of
     // a cache shader (e.g. blur) to the screen.
     ps_rectangle: PrimitiveShader,
     ps_text_run: PrimitiveShader,
     ps_text_run_subpixel: PrimitiveShader,
     ps_image: PrimitiveShader,
     ps_border: PrimitiveShader,
     ps_gradient: PrimitiveShader,
-    ps_gradient_clip: PrimitiveShader,
     ps_angle_gradient: PrimitiveShader,
     ps_box_shadow: PrimitiveShader,
-    ps_rectangle_clip: PrimitiveShader,
-    ps_image_clip: PrimitiveShader,
     ps_cache_image: PrimitiveShader,
 
     ps_blend: LazilyCompiledShader,
     ps_composite: LazilyCompiledShader,
 
     tile_clear_shader: LazilyCompiledShader,
 
     max_clear_tiles: usize,
     max_prim_blends: usize,
     max_prim_composites: usize,
     max_cache_instances: usize,
     max_blurs: usize,
 
     notifier: Arc<Mutex<Option<Box<RenderNotifier>>>>,
+    // Used to notify users that all pending work on render backend thread is done.
+    flush_notifier: Arc<Mutex<Option<Box<FlushNotifier>>>>,
 
     enable_profiler: bool,
     debug: DebugRenderer,
     backend_profile_counters: BackendProfileCounters,
     profile_counters: RendererProfileCounters,
     profiler: Profiler,
     last_time: u64,
 
@@ -368,16 +387,24 @@ pub struct Renderer {
     data16_texture: VertexDataTexture,
     data32_texture: VertexDataTexture,
     data64_texture: VertexDataTexture,
     data128_texture: VertexDataTexture,
     pipeline_epoch_map: HashMap<PipelineId, Epoch, BuildHasherDefault<FnvHasher>>,
     /// Used to dispatch functions to the main thread's event loop.
     /// Required to allow GLContext sharing in some implementations like WGL.
     main_thread_dispatcher: Arc<Mutex<Option<Box<RenderDispatcher>>>>,
+
+    /// A vector for fast resolves of texture cache IDs to
+    /// native texture IDs. This maps to a free-list managed
+    /// by the backend thread / texture cache. Because of this,
+    /// items in this array may be None if they have been
+    /// freed by the backend thread. This saves having to
+    /// use a hashmap, and allows a flat vector for performance.
+    cache_texture_id_map: Vec<Option<TextureId>>,
 }
 
 impl Renderer {
     /// Initializes webrender and creates a Renderer and RenderApiSender.
     ///
     /// # Examples
     /// Initializes a Renderer with some reasonable values. For more information see
     /// [RendererOptions][rendereroptions].
@@ -396,42 +423,62 @@ impl Renderer {
     /// let (renderer, sender) = Renderer::new(opts);
     /// ```
     pub fn new(options: RendererOptions) -> (Renderer, RenderApiSender) {
         let (api_tx, api_rx) = ipc::channel().unwrap();
         let (payload_tx, payload_rx) = ipc::bytes_channel().unwrap();
         let (result_tx, result_rx) = channel();
 
         let notifier = Arc::new(Mutex::new(None));
+        let flush_notifier = Arc::new(Mutex::new(None));
 
         let file_watch_handler = FileWatcher {
             result_tx: result_tx.clone(),
             notifier: notifier.clone(),
         };
 
         let mut device = Device::new(options.resource_path.clone(),
                                      options.device_pixel_ratio,
                                      Box::new(file_watch_handler));
         device.begin_frame();
 
         let max_ubo_size = device.get_capabilities().max_ubo_size;
         let max_ubo_vectors = max_ubo_size / 16;
 
         let max_prim_instances = get_ubo_max_len::<tiling::PrimitiveInstance>(max_ubo_size);
         let max_cache_instances = get_ubo_max_len::<tiling::CachePrimitiveInstance>(max_ubo_size);
+        let max_clip_instances = get_ubo_max_len::<tiling::CacheClipInstance>(max_ubo_size);
         let max_prim_blends = get_ubo_max_len::<tiling::PackedBlendPrimitive>(max_ubo_size);
         let max_prim_composites = get_ubo_max_len::<tiling::PackedCompositePrimitive>(max_ubo_size);
         let max_blurs = get_ubo_max_len::<tiling::BlurCommand>(max_ubo_size);
 
         let cs_box_shadow = LazilyCompiledShader::new(ShaderKind::Cache,
                                                       "cs_box_shadow",
                                                       max_cache_instances,
                                                       &[],
                                                       &mut device,
                                                       options.precache_shaders);
+        let cs_clip_clear = LazilyCompiledShader::new(ShaderKind::ClipCache,
+                                                      "cs_clip_clear",
+                                                      max_clip_instances,
+                                                      &[],
+                                                      &mut device,
+                                                      options.precache_shaders);
+        let cs_clip_rectangle = LazilyCompiledShader::new(ShaderKind::ClipCache,
+                                                          "cs_clip_rectangle",
+                                                          max_clip_instances,
+                                                          &[],
+                                                          &mut device,
+                                                          options.precache_shaders);
+        let cs_clip_image = LazilyCompiledShader::new(ShaderKind::ClipCache,
+                                                      "cs_clip_image",
+                                                      max_clip_instances,
+                                                      &[],
+                                                      &mut device,
+                                                      options.precache_shaders);
         let cs_text_run = LazilyCompiledShader::new(ShaderKind::Cache,
                                                     "cs_text_run",
                                                     max_cache_instances,
                                                     &[],
                                                     &mut device,
                                                     options.precache_shaders);
         let cs_blur = LazilyCompiledShader::new(ShaderKind::Cache,
                                                 "cs_blur",
@@ -465,48 +512,30 @@ impl Renderer {
                                             &[],
                                             options.precache_shaders);
         let ps_border = PrimitiveShader::new("ps_border",
                                              max_ubo_vectors,
                                              max_prim_instances,
                                              &mut device,
                                              &[],
                                              options.precache_shaders);
-        let ps_rectangle_clip = PrimitiveShader::new("ps_rectangle_clip",
-                                                     max_ubo_vectors,
-                                                     max_prim_instances,
-                                                     &mut device,
-                                                     &[],
-                                                     options.precache_shaders);
-        let ps_image_clip = PrimitiveShader::new("ps_image_clip",
-                                                 max_ubo_vectors,
-                                                 max_prim_instances,
-                                                 &mut device,
-                                                 &[],
-                                                 options.precache_shaders);
 
         let ps_box_shadow = PrimitiveShader::new("ps_box_shadow",
                                                  max_ubo_vectors,
                                                  max_prim_instances,
                                                  &mut device,
                                                  &[],
                                                  options.precache_shaders);
 
         let ps_gradient = PrimitiveShader::new("ps_gradient",
                                                max_ubo_vectors,
                                                max_prim_instances,
                                                &mut device,
                                                &[],
                                                options.precache_shaders);
-        let ps_gradient_clip = PrimitiveShader::new("ps_gradient_clip",
-                                                    max_ubo_vectors,
-                                                    max_prim_instances,
-                                                    &mut device,
-                                                    &[],
-                                                    options.precache_shaders);
         let ps_angle_gradient = PrimitiveShader::new("ps_angle_gradient",
                                                      max_ubo_vectors,
                                                      max_prim_instances,
                                                      &mut device,
                                                      &[],
                                                      options.precache_shaders);
         let ps_cache_image = PrimitiveShader::new("ps_cache_image",
                                                   max_ubo_vectors,
@@ -531,18 +560,17 @@ impl Renderer {
         let max_clear_tiles = get_ubo_max_len::<ClearTile>(max_ubo_size);
         let tile_clear_shader = LazilyCompiledShader::new(ShaderKind::Clear,
                                                           "ps_clear",
                                                            max_ubo_vectors,
                                                            &[],
                                                            &mut device,
                                                            options.precache_shaders);
 
-        let texture_ids = device.create_texture_ids(1024, TextureTarget::Default);
-        let mut texture_cache = TextureCache::new(texture_ids);
+        let mut texture_cache = TextureCache::new();
 
         let white_pixels: Vec<u8> = vec![
             0xff, 0xff, 0xff, 0xff,
             0xff, 0xff, 0xff, 0xff,
             0xff, 0xff, 0xff, 0xff,
             0xff, 0xff, 0xff, 0xff,
         ];
         let mask_pixels: Vec<u8> = vec![
@@ -563,21 +591,16 @@ impl Renderer {
         texture_cache.insert(dummy_mask_image_id,
                              2,
                              2,
                              None,
                              ImageFormat::A8,
                              TextureFilter::Linear,
                              mask_pixels);
 
-        let dummy_resources = DummyResources {
-            white_image_id: white_image_id,
-            opaque_mask_image_id: dummy_mask_image_id,
-        };
-
         let debug_renderer = DebugRenderer::new(&mut device);
 
         let layer_texture = VertexDataTexture::new(&mut device);
         let render_task_texture = VertexDataTexture::new(&mut device);
         let prim_geom_texture = VertexDataTexture::new(&mut device);
 
         let data16_texture = VertexDataTexture::new(&mut device);
         let data32_texture = VertexDataTexture::new(&mut device);
@@ -611,16 +634,17 @@ impl Renderer {
         device.update_vao_indices(quad_vao_id, &quad_indices, VertexUsageHint::Static);
         device.update_vao_main_vertices(quad_vao_id, &quad_vertices, VertexUsageHint::Static);
 
         device.end_frame();
 
         let main_thread_dispatcher = Arc::new(Mutex::new(None));
         let backend_notifier = notifier.clone();
         let backend_main_thread_dispatcher = main_thread_dispatcher.clone();
+        let backend_flush_notifier = flush_notifier.clone();
 
         // We need a reference to the webrender context from the render backend in order to share
         // texture ids
         let context_handle = match options.renderer_kind {
             RendererKind::Native => GLContextHandleWrapper::current_native_handle(),
             RendererKind::OSMesa => GLContextHandleWrapper::current_osmesa_handle(),
         };
 
@@ -633,19 +657,19 @@ impl Renderer {
         let enable_recording = options.enable_recording;
         thread::spawn(move || {
             let mut backend = RenderBackend::new(api_rx,
                                                  payload_rx,
                                                  payload_tx_for_backend,
                                                  result_tx,
                                                  device_pixel_ratio,
                                                  texture_cache,
-                                                 dummy_resources,
                                                  enable_aa,
                                                  backend_notifier,
+                                                 backend_flush_notifier,
                                                  context_handle,
                                                  config,
                                                  debug,
                                                  enable_recording,
                                                  backend_main_thread_dispatcher);
             backend.run();
         });
 
@@ -653,38 +677,39 @@ impl Renderer {
             result_rx: result_rx,
             device: device,
             current_frame: None,
             pending_texture_updates: Vec::new(),
             pending_shader_updates: Vec::new(),
             device_pixel_ratio: options.device_pixel_ratio,
             tile_clear_shader: tile_clear_shader,
             cs_box_shadow: cs_box_shadow,
+            cs_clip_clear: cs_clip_clear,
+            cs_clip_rectangle: cs_clip_rectangle,
+            cs_clip_image: cs_clip_image,
             cs_text_run: cs_text_run,
             cs_blur: cs_blur,
             ps_rectangle: ps_rectangle,
             ps_text_run: ps_text_run,
             ps_text_run_subpixel: ps_text_run_subpixel,
             ps_image: ps_image,
             ps_border: ps_border,
-            ps_rectangle_clip: ps_rectangle_clip,
-            ps_image_clip: ps_image_clip,
             ps_box_shadow: ps_box_shadow,
             ps_gradient: ps_gradient,
-            ps_gradient_clip: ps_gradient_clip,
             ps_angle_gradient: ps_angle_gradient,
             ps_cache_image: ps_cache_image,
             ps_blend: ps_blend,
             ps_composite: ps_composite,
             max_clear_tiles: max_clear_tiles,
             max_prim_blends: max_prim_blends,
             max_prim_composites: max_prim_composites,
             max_cache_instances: max_cache_instances,
             max_blurs: max_blurs,
             notifier: notifier,
+            flush_notifier: flush_notifier,
             debug: debug_renderer,
             backend_profile_counters: BackendProfileCounters::new(),
             profile_counters: RendererProfileCounters::new(),
             profiler: Profiler::new(),
             enable_profiler: options.enable_profiler,
             last_time: 0,
             render_targets: Vec::new(),
             gpu_profile: GpuProfiler::new(),
@@ -693,31 +718,42 @@ impl Renderer {
             render_task_texture: render_task_texture,
             prim_geom_texture: prim_geom_texture,
             data16_texture: data16_texture,
             data32_texture: data32_texture,
             data64_texture: data64_texture,
             data128_texture: data128_texture,
             pipeline_epoch_map: HashMap::with_hasher(Default::default()),
             main_thread_dispatcher: main_thread_dispatcher,
+            cache_texture_id_map: Vec::new(),
         };
 
         let sender = RenderApiSender::new(api_tx, payload_tx);
         (renderer, sender)
     }
 
     /// Sets the new RenderNotifier.
     ///
     /// The RenderNotifier will be called when processing e.g. of a (scrolling) frame is done,
     /// and therefore the screen should be updated.
     pub fn set_render_notifier(&self, notifier: Box<RenderNotifier>) {
         let mut notifier_arc = self.notifier.lock().unwrap();
         *notifier_arc = Some(notifier);
     }
 
+    /// Sets the new FlushNotifier
+    ///
+    /// The FlushNotifier is called after all messages to the backend thread
+    /// have been processed. This should be used when we need to sync wait
+    /// for rendering to happen, such as with reftests.
+    pub fn set_flush_notifier(&self, flush_notifier: Box<FlushNotifier>) {
+        let mut flush_notifier_arc = self.flush_notifier.lock().unwrap();
+        *flush_notifier_arc = Some(flush_notifier);
+    }
+
     /// Sets the new MainThreadDispatcher.
     ///
     /// Allows to dispatch functions to the main thread's event loop.
     pub fn set_main_thread_dispatcher(&self, dispatcher: Box<RenderDispatcher>) {
         let mut dispatcher_arc = self.main_thread_dispatcher.lock().unwrap();
         *dispatcher_arc = Some(dispatcher);
     }
 
@@ -749,16 +785,33 @@ impl Renderer {
                 }
                 ResultMsg::RefreshShader(path) => {
                     self.pending_shader_updates.push(path);
                 }
             }
         }
     }
 
+    // Get the real (OpenGL) texture ID for a given source texture.
+    // For a texture cache texture, the IDs are stored in a vector
+    // map for fast access. For WebGL textures, the native texture ID
+    // is stored inline. When we add support for external textures,
+    // we will add a callback here that is able to ask the caller
+    // for the image data.
+    fn resolve_source_texture(&mut self, texture_id: &SourceTexture) -> TextureId {
+        match texture_id {
+            &SourceTexture::Invalid => TextureId::invalid(),
+            &SourceTexture::WebGL(id) => TextureId::new(id),
+            &SourceTexture::TextureCache(index) => {
+                self.cache_texture_id_map[index.0]
+                    .expect("BUG: Texture should exist in texture cache map!")
+            }
+        }
+    }
+
     /// 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: Size2D<u32>) {
         if let Some(mut frame) = self.current_frame.take() {
             if let Some(ref mut frame) = frame.frame {
                 let mut profile_timers = RendererProfileTimers::new();
@@ -836,67 +889,73 @@ impl Renderer {
 */
 
     fn update_texture_cache(&mut self) {
         let mut pending_texture_updates = mem::replace(&mut self.pending_texture_updates, vec![]);
         for update_list in pending_texture_updates.drain(..) {
             for update in update_list.updates {
                 match update.op {
                     TextureUpdateOp::Create(width, height, format, filter, mode, maybe_bytes) => {
+                        // Create a new native texture, as requested by the texture cache.
+                        let texture_id = self.device
+                                             .create_texture_ids(1, TextureTarget::Default)[0];
+
+                        let CacheTextureId(cache_texture_index) = update.id;
+                        if self.cache_texture_id_map.len() == cache_texture_index {
+                            // It was a new texture, so add to end of the map.
+                            self.cache_texture_id_map.push(Some(texture_id));
+                        } else {
+                            // It was re-using an item from the free-list, so store
+                            // the new ID there.
+                            debug_assert!(self.cache_texture_id_map[cache_texture_index].is_none());
+                            self.cache_texture_id_map[cache_texture_index] = Some(texture_id);
+                        }
+
                         // TODO: clean up match
                         match maybe_bytes {
                             Some(bytes) => {
-                                self.device.init_texture(update.id,
+                                self.device.init_texture(texture_id,
                                                          width,
                                                          height,
                                                          format,
                                                          filter,
                                                          mode,
                                                          Some(bytes.as_slice()));
                             }
                             None => {
-                                self.device.init_texture(update.id,
+                                self.device.init_texture(texture_id,
                                                          width,
                                                          height,
                                                          format,
                                                          filter,
                                                          mode,
                                                          None);
                             }
                         }
                     }
                     TextureUpdateOp::Grow(new_width,
                                           new_height,
                                           format,
                                           filter,
                                           mode) => {
-                        self.device.resize_texture(update.id,
+                        let texture_id = self.cache_texture_id_map[update.id.0].unwrap();
+                        self.device.resize_texture(texture_id,
                                                    new_width,
                                                    new_height,
                                                    format,
                                                    filter,
                                                    mode);
                     }
-                    TextureUpdateOp::Update(x, y, width, height, details) => {
-                        match details {
-                            TextureUpdateDetails::Raw => {
-                                self.device.update_raw_texture(update.id, x, y, width, height);
-                            }
-                            TextureUpdateDetails::Blit(bytes, stride) => {
-                                self.device.update_texture(
-                                    update.id,
-                                    x,
-                                    y,
-                                    width, height, stride,
-                                    bytes.as_slice());
-                            }
-                        }
-                    }
-                    TextureUpdateOp::Remove => {
-                        self.device.remove_raw_texture(update.id);
+                    TextureUpdateOp::Update(x, y, width, height, bytes, stride) => {
+                        let texture_id = self.cache_texture_id_map[update.id.0].unwrap();
+                        self.device.update_texture(texture_id,
+                                                   x,
+                                                   y,
+                                                   width, height, stride,
+                                                   bytes.as_slice());
                     }
                 }
             }
         }
     }
 
     fn add_debug_rect(&mut self,
                       p0: DevicePoint,
@@ -939,24 +998,26 @@ impl Renderer {
                                 c);
         }
     }
 
     fn draw_ubo_batch<T>(&mut self,
                          ubo_data: &[T],
                          shader: ProgramId,
                          quads_per_item: usize,
-                         color_texture_id: TextureId,
-                         mask_texture_id: TextureId,
+                         textures: &BatchTextures,
                          max_prim_items: usize,
                          projection: &Matrix4D<f32>) {
         self.device.bind_program(shader, &projection);
         self.device.bind_vao(self.quad_vao_id);
-        self.device.bind_texture(TextureSampler::Color, color_texture_id);
-        self.device.bind_texture(TextureSampler::Mask, mask_texture_id);
+
+        for i in 0..textures.colors.len() {
+            let texture_id = self.resolve_source_texture(&textures.colors[i]);
+            self.device.bind_texture(TextureSampler::color(i), texture_id);
+        }
 
         for chunk in ubo_data.chunks(max_prim_items) {
             let ubo = self.device.create_ubo(&chunk, UBO_BIND_DATA);
 
             let quad_count = chunk.len() * quads_per_item;
             self.device.draw_indexed_triangles_instanced_u16(6, quad_count as i32);
             self.profile_counters.vertices.add(6 * (quad_count as usize));
             self.profile_counters.draw_calls.inc();
@@ -974,34 +1035,34 @@ impl Renderer {
         self.gpu_profile.add_marker(GPU_TAG_SETUP_TARGET);
 
         let dimensions = [target_size.width as u32, target_size.height as u32];
         self.device.bind_render_target(render_target, Some(dimensions));
 
         self.device.set_blend(false);
         self.device.set_blend_mode_alpha();
         if let Some(cache_texture) = cache_texture {
-	        self.device.bind_texture(TextureSampler::Cache, cache_texture);
-	    }
+            self.device.bind_texture(TextureSampler::Cache, cache_texture);
+        }
 
         let (color, projection) = match render_target {
             Some(..) => (
                 [0.0, 0.0, 0.0, 0.0],
                 Matrix4D::ortho(0.0,
-                               target_size.width as f32,
+                               target_size.width,
                                0.0,
-                               target_size.height as f32,
+                               target_size.height,
                                ORTHO_NEAR_PLANE,
                                ORTHO_FAR_PLANE)
             ),
             None => (
                 [1.0, 1.0, 1.0, 1.0],
                 Matrix4D::ortho(0.0,
-                               target_size.width as f32,
-                               target_size.height as f32,
+                               target_size.width,
+                               target_size.height,
                                0.0,
                                ORTHO_NEAR_PLANE,
                                ORTHO_FAR_PLANE)
             ),
         };
 
         // todo(gw): remove me!
         if should_clear {
@@ -1018,50 +1079,88 @@ impl Renderer {
             self.device.set_blend(false);
 
             self.gpu_profile.add_marker(GPU_TAG_BLUR);
             let shader = self.cs_blur.get(&mut self.device);
             let max_blurs = self.max_blurs;
             self.draw_ubo_batch(&target.vertical_blurs,
                                 shader,
                                 1,
-                                TextureId::invalid(),
-                                TextureId::invalid(),
+                                &BatchTextures::no_texture(),
                                 max_blurs,
                                 &projection);
         }
 
         if !target.horizontal_blurs.is_empty() {
             self.device.set_blend(false);
 
             self.gpu_profile.add_marker(GPU_TAG_BLUR);
             let shader = self.cs_blur.get(&mut self.device);
             let max_blurs = self.max_blurs;
             self.draw_ubo_batch(&target.horizontal_blurs,
                                 shader,
                                 1,
-                                TextureId::invalid(),
-                                TextureId::invalid(),
+                                &BatchTextures::no_texture(),
                                 max_blurs,
                                 &projection);
         }
 
         // Draw any box-shadow caches for this target.
         if !target.box_shadow_cache_prims.is_empty() {
             self.device.set_blend(false);
-
             self.gpu_profile.add_marker(GPU_TAG_CACHE_BOX_SHADOW);
             let shader = self.cs_box_shadow.get(&mut self.device);
-            let max_cache_instances = self.max_cache_instances;
+            let max_prim_items = self.max_cache_instances;
             self.draw_ubo_batch(&target.box_shadow_cache_prims,
                                 shader,
                                 1,
-                                TextureId::invalid(),
-                                TextureId::invalid(),
-                                max_cache_instances,
+                                &BatchTextures::no_texture(),
+                                max_prim_items,
+                                &projection);
+        }
+
+        // Draw the clip items into the tiled alpha mask.
+        self.gpu_profile.add_marker(GPU_TAG_CACHE_CLIP);
+        // first, mark the target area as opaque
+        //Note: not needed if we know the target is cleared with opaque
+        self.device.set_blend(false);
+        if !target.clip_batcher.clears.is_empty() {
+            let shader = self.cs_clip_clear.get(&mut self.device);
+            let max_prim_items = self.max_clear_tiles;
+            self.draw_ubo_batch(&target.clip_batcher.clears,
+                                shader,
+                                1,
+                                &BatchTextures::no_texture(),
+                                max_prim_items,
+                                &projection);
+        }
+        // now switch to multiplicative blending
+        self.device.set_blend(true);
+        self.device.set_blend_mode_multiply();
+        let max_prim_items = self.max_cache_instances;
+        // draw rounded cornered rectangles
+        if !target.clip_batcher.rectangles.is_empty() {
+            let shader = self.cs_clip_rectangle.get(&mut self.device);
+            self.draw_ubo_batch(&target.clip_batcher.rectangles,
+                                shader,
+                                1,
+                                &BatchTextures::no_texture(),
+                                max_prim_items,
+                                &projection);
+        }
+        // draw image masks
+        for (mask_texture_id, items) in target.clip_batcher.images.iter() {
+            let texture_id = self.resolve_source_texture(mask_texture_id);
+            self.device.bind_texture(TextureSampler::Mask, texture_id);
+            let shader = self.cs_clip_image.get(&mut self.device);
+            self.draw_ubo_batch(items,
+                                shader,
+                                1,
+                                &BatchTextures::no_texture(),
+                                max_prim_items,
                                 &projection);
         }
 
         // Draw any textrun caches for this target. For now, this
         // is only used to cache text runs that are to be blurred
         // for text-shadow support. In the future it may be worth
         // considering using this for (some) other text runs, since
         // it removes the overhead of submitting many small glyphs
@@ -1071,34 +1170,36 @@ impl Renderer {
             self.device.set_blend_mode_alpha();
 
             self.gpu_profile.add_marker(GPU_TAG_CACHE_TEXT_RUN);
             let shader = self.cs_text_run.get(&mut self.device);
             let max_cache_instances = self.max_cache_instances;
             self.draw_ubo_batch(&target.text_run_cache_prims,
                                 shader,
                                 1,
-                                target.text_run_color_texture_id,
-                                TextureId::invalid(),
+                                &target.text_run_textures,
                                 max_cache_instances,
                                 &projection);
         }
 
+        self.device.set_blend(false);
         let mut prev_blend_mode = BlendMode::None;
 
         for batch in &target.alpha_batcher.batches {
-            let color_texture_id = batch.key.color_texture_id;
-            let mask_texture_id = batch.key.mask_texture_id;
             let transform_kind = batch.key.flags.transform_kind();
-            let has_complex_clip = batch.key.flags.needs_clipping();
+            let needs_clipping = batch.key.flags.needs_clipping();
+            assert!(!needs_clipping || batch.key.blend_mode == BlendMode::Alpha);
 
             if batch.key.blend_mode != prev_blend_mode {
                 match batch.key.blend_mode {
-                    BlendMode::None | BlendMode::Alpha => {
-                        self.device.set_blend(batch.key.blend_mode == BlendMode::Alpha);
+                    BlendMode::None => {
+                        self.device.set_blend(false);
+                    }
+                    BlendMode::Alpha => {
+                        self.device.set_blend(true);
                         self.device.set_blend_mode_alpha();
                     }
                     BlendMode::Subpixel(color) => {
                         self.device.set_blend(true);
                         self.device.set_blend_mode_subpixel(color);
                     }
                 }
                 prev_blend_mode = batch.key.blend_mode;
@@ -1107,145 +1208,115 @@ impl Renderer {
             match &batch.data {
                 &PrimitiveBatchData::CacheImage(ref ubo_data) => {
                     self.gpu_profile.add_marker(GPU_TAG_PRIM_CACHE_IMAGE);
                     let (shader, max_prim_items) = self.ps_cache_image.get(&mut self.device,
                                                                            transform_kind);
                     self.draw_ubo_batch(ubo_data,
                                         shader,
                                         1,
-                                        color_texture_id,
-                                        mask_texture_id,
+                                        &batch.key.textures,
                                         max_prim_items,
                                         &projection);
                 }
                 &PrimitiveBatchData::Blend(ref ubo_data) => {
                     self.gpu_profile.add_marker(GPU_TAG_PRIM_BLEND);
                     let shader = self.ps_blend.get(&mut self.device);
-                    self.device.bind_program(shader, &projection);
-                    self.device.bind_vao(self.quad_vao_id);
-
-                    for chunk in ubo_data.chunks(self.max_prim_blends) {
-                        let ubo = self.device.create_ubo(&chunk, UBO_BIND_DATA);
-
-                        self.device.draw_indexed_triangles_instanced_u16(6, chunk.len() as i32);
-                        self.profile_counters.vertices.add(6 * chunk.len());
-                        self.profile_counters.draw_calls.inc();
-
-                        self.device.delete_buffer(ubo);
-                    }
+                    let max_prim_items = self.max_prim_blends;
+                    self.draw_ubo_batch(ubo_data, shader,
+                                        1,
+                                        &batch.key.textures,
+                                        max_prim_items,
+                                        &projection);
                 }
                 &PrimitiveBatchData::Composite(ref ubo_data) => {
                     self.gpu_profile.add_marker(GPU_TAG_PRIM_COMPOSITE);
                     let shader = self.ps_composite.get(&mut self.device);
-                    self.device.bind_program(shader, &projection);
-                    self.device.bind_vao(self.quad_vao_id);
+                    let max_prim_items = self.max_prim_composites;
 
-                    for chunk in ubo_data.chunks(self.max_prim_composites) {
-                        let ubo = self.device.create_ubo(&chunk, UBO_BIND_DATA);
+                    // The composite shader only samples from sCache.
+                    debug_assert!(cache_texture.is_some());
 
-                        self.device.draw_indexed_triangles_instanced_u16(6, chunk.len() as i32);
-                        self.profile_counters.vertices.add(6 * chunk.len());
-                        self.profile_counters.draw_calls.inc();
+                    self.draw_ubo_batch(ubo_data, shader,
+                                        1,
+                                        &batch.key.textures,
+                                        max_prim_items,
+                                        &projection);
 
-                        self.device.delete_buffer(ubo);
-                    }
                 }
                 &PrimitiveBatchData::Rectangles(ref ubo_data) => {
-                    let (shader, max_prim_items) = if has_complex_clip {
-                        self.gpu_profile.add_marker(GPU_TAG_PRIM_RECT_CLIP);
-                        self.ps_rectangle_clip.get(&mut self.device, transform_kind)
-                    } else {
-                        self.gpu_profile.add_marker(GPU_TAG_PRIM_RECT);
-                        self.ps_rectangle.get(&mut self.device, transform_kind)
-                    };
+                    self.gpu_profile.add_marker(GPU_TAG_PRIM_RECT);
+                    let (shader, max_prim_items) = self.ps_rectangle.get(&mut self.device, transform_kind);
                     self.draw_ubo_batch(ubo_data,
                                         shader,
                                         1,
-                                        color_texture_id,
-                                        mask_texture_id,
+                                        &batch.key.textures,
                                         max_prim_items,
                                         &projection);
                 }
                 &PrimitiveBatchData::Image(ref ubo_data) => {
-                    let (shader, max_prim_items) = if has_complex_clip {
-                        self.gpu_profile.add_marker(GPU_TAG_PRIM_IMAGE_CLIP);
-                        self.ps_image_clip.get(&mut self.device, transform_kind)
-                    } else {
-                        self.gpu_profile.add_marker(GPU_TAG_PRIM_IMAGE);
-                        self.ps_image.get(&mut self.device, transform_kind)
-                    };
+                    self.gpu_profile.add_marker(GPU_TAG_PRIM_IMAGE);
+                    let (shader, max_prim_items) = self.ps_image.get(&mut self.device, transform_kind);
                     self.draw_ubo_batch(ubo_data,
                                         shader,
                                         1,
-                                        color_texture_id,
-                                        mask_texture_id,
+                                        &batch.key.textures,
                                         max_prim_items,
                                         &projection);
                 }
                 &PrimitiveBatchData::Borders(ref ubo_data) => {
                     self.gpu_profile.add_marker(GPU_TAG_PRIM_BORDER);
                     let (shader, max_prim_items) = self.ps_border.get(&mut self.device, transform_kind);
                     self.draw_ubo_batch(ubo_data,
                                         shader,
                                         1,
-                                        color_texture_id,
-                                        mask_texture_id,
+                                        &batch.key.textures,
                                         max_prim_items,
                                         &projection);
                 }
                 &PrimitiveBatchData::BoxShadow(ref ubo_data) => {
                     self.gpu_profile.add_marker(GPU_TAG_PRIM_BOX_SHADOW);
                     let (shader, max_prim_items) = self.ps_box_shadow.get(&mut self.device, transform_kind);
                     self.draw_ubo_batch(ubo_data,
                                         shader,
                                         1,
-                                        color_texture_id,
-                                        mask_texture_id,
+                                        &batch.key.textures,
                                         max_prim_items,
                                         &projection);
                 }
                 &PrimitiveBatchData::TextRun(ref ubo_data) => {
                     self.gpu_profile.add_marker(GPU_TAG_PRIM_TEXT_RUN);
                     let (shader, max_prim_items) = 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),
                     };
                     self.draw_ubo_batch(ubo_data,
                                         shader,
                                         1,
-                                        color_texture_id,
-                                        mask_texture_id,
+                                        &batch.key.textures,
                                         max_prim_items,
                                         &projection);
                 }
                 &PrimitiveBatchData::AlignedGradient(ref ubo_data) => {
-                    let (shader, max_prim_items) = if has_complex_clip {
-                        self.gpu_profile.add_marker(GPU_TAG_PRIM_GRADIENT_CLIP);
-                        self.ps_gradient_clip.get(&mut self.device, transform_kind)
-                    } else {
-                        self.gpu_profile.add_marker(GPU_TAG_PRIM_GRADIENT);
-                        self.ps_gradient.get(&mut self.device, transform_kind)
-                    };
+                    self.gpu_profile.add_marker(GPU_TAG_PRIM_GRADIENT);
+                    let (shader, max_prim_items) = self.ps_gradient.get(&mut self.device, transform_kind);
                     self.draw_ubo_batch(ubo_data,
                                         shader,
                                         1,
-                                        color_texture_id,
-                                        mask_texture_id,
+                                        &batch.key.textures,
                                         max_prim_items,
                                         &projection);
                 }
                 &PrimitiveBatchData::AngleGradient(ref ubo_data) => {
                     self.gpu_profile.add_marker(GPU_TAG_PRIM_ANGLE_GRADIENT);
                     let (shader, max_prim_items) = self.ps_angle_gradient.get(&mut self.device, transform_kind);
                     self.draw_ubo_batch(ubo_data,
                                         shader,
                                         1,
-                                        color_texture_id,
-                                        mask_texture_id,
+                                        &batch.key.textures,
                                         max_prim_items,
                                         &projection);
                 }
             }
         }
 
         self.device.set_blend(false);
     }
@@ -1346,29 +1417,25 @@ impl Renderer {
                 src_id = target_id;
             }
         }
 
         self.gpu_profile.add_marker(GPU_TAG_CLEAR_TILES);
 
         // Clear tiles with no items
         if !frame.clear_tiles.is_empty() {
-            let tile_clear_shader = self.tile_clear_shader.get(&mut self.device);
-            self.device.bind_program(tile_clear_shader, &projection);
-            self.device.bind_vao(self.quad_vao_id);
-
-            for chunk in frame.clear_tiles.chunks(self.max_clear_tiles) {
-                let ubo = self.device.create_ubo(&chunk, UBO_BIND_DATA);
-
-                self.device.draw_indexed_triangles_instanced_u16(6, chunk.len() as i32);
-                self.profile_counters.vertices.add(6 * chunk.len());
-                self.profile_counters.draw_calls.inc();
-
-                self.device.delete_buffer(ubo);
-            }
+            self.device.set_blend(false);
+            let shader = self.tile_clear_shader.get(&mut self.device);
+            let max_prim_items = self.max_clear_tiles;
+            self.draw_ubo_batch(&frame.clear_tiles,
+                                shader,
+                                1,
+                                &BatchTextures::no_texture(),
+                                max_prim_items,
+                                &projection);
         }
     }
 
     pub fn debug_renderer<'a>(&'a mut self) -> &'a mut DebugRenderer {
         &mut self.debug
     }
 
     pub fn get_profiler_enabled(&mut self) -> bool {
--- a/gfx/webrender/src/resource_cache.rs
+++ b/gfx/webrender/src/resource_cache.rs
@@ -1,45 +1,43 @@
 /* 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 device::{TextureFilter, TextureId};
+use device::TextureFilter;
 use euclid::{Point2D, Size2D};
 use fnv::FnvHasher;
 use frame::FrameId;
-use freelist::FreeList;
-use internal_types::FontTemplate;
-use internal_types::{TextureUpdateList, DrawListId, DrawList};
+use internal_types::{FontTemplate, SourceTexture, TextureUpdateList};
 use platform::font::{FontContext, RasterizedGlyph};
 use rayon::prelude::*;
 use std::cell::RefCell;
 use std::collections::HashMap;
 use std::collections::hash_map::Entry::{self, Occupied, Vacant};
 use std::fmt::Debug;
 use std::hash::BuildHasherDefault;
 use std::hash::Hash;
-use texture_cache::{TextureCache, TextureCacheItem, TextureCacheItemId};
-use webrender_traits::{Epoch, FontKey, GlyphKey, ImageKey, ImageFormat, DisplayItem, ImageRendering};
-use webrender_traits::{FontRenderMode, GlyphDimensions, PipelineId, WebGLContextId};
+use texture_cache::{TextureCache, TextureCacheItemId};
+use webrender_traits::{Epoch, FontKey, GlyphKey, ImageKey, ImageFormat, ImageRendering};
+use webrender_traits::{FontRenderMode, GlyphDimensions, WebGLContextId};
 
 thread_local!(pub static FONT_CONTEXT: RefCell<FontContext> = RefCell::new(FontContext::new()));
 
 // These coordinates are always in texels.
 // They are converted to normalized ST
 // values in the vertex shader. The reason
 // for this is that the texture may change
 // dimensions (e.g. the pages in a texture
 // atlas can grow). When this happens, by
 // storing the coordinates as texel values
 // we don't need to go through and update
 // various CPU-side structures.
 pub struct CacheItem {
-    pub texture_id: TextureId,
+    pub texture_id: SourceTexture,
     pub uv0: Point2D<f32>,
     pub uv1: Point2D<f32>,
 }
 
 #[derive(Clone, Hash, PartialEq, Eq, Debug)]
 pub struct RenderedGlyphKey {
     pub key: GlyphKey,
     pub render_mode: FontRenderMode,
@@ -64,22 +62,16 @@ pub struct ImageProperties {
 
 #[derive(Debug, Copy, Clone, PartialEq)]
 enum State {
     Idle,
     AddResources,
     QueryResources,
 }
 
-#[derive(Clone, Debug)]
-pub struct DummyResources {
-    pub white_image_id: TextureCacheItemId,
-    pub opaque_mask_image_id: TextureCacheItemId,
-}
-
 struct ImageResource {
     bytes: Vec<u8>,
     width: u32,
     height: u32,
     stride: Option<u32>,
     format: ImageFormat,
     epoch: Epoch,
     is_opaque: bool,
@@ -164,28 +156,27 @@ struct TextRunResourceRequest {
 }
 
 enum ResourceRequest {
     Image(ImageKey, ImageRendering),
     TextRun(TextRunResourceRequest),
 }
 
 struct WebGLTexture {
-    id: TextureId,
+    id: SourceTexture,
     size: Size2D<i32>,
 }
 
 pub struct ResourceCache {
     cached_glyphs: ResourceClassCache<RenderedGlyphKey, Option<TextureCacheItemId>>,
     cached_images: ResourceClassCache<(ImageKey, ImageRendering), CachedImageInfo>,
 
     // TODO(pcwalton): Figure out the lifecycle of these.
     webgl_textures: HashMap<WebGLContextId, WebGLTexture, BuildHasherDefault<FnvHasher>>,
 
-    draw_lists: FreeList<DrawList>,
     font_templates: HashMap<FontKey, FontTemplate, BuildHasherDefault<FnvHasher>>,
     image_templates: HashMap<ImageKey, ImageResource, BuildHasherDefault<FnvHasher>>,
     device_pixel_ratio: f32,
     enable_aa: bool,
     state: State,
     current_frame_id: FrameId,
 
     texture_cache: TextureCache,
@@ -199,17 +190,16 @@ pub struct ResourceCache {
 impl ResourceCache {
     pub fn new(texture_cache: TextureCache,
                device_pixel_ratio: f32,
                enable_aa: bool) -> ResourceCache {
         ResourceCache {
             cached_glyphs: ResourceClassCache::new(),
             cached_images: ResourceClassCache::new(),
             webgl_textures: HashMap::with_hasher(Default::default()),
-            draw_lists: FreeList::new(),
             font_templates: HashMap::with_hasher(Default::default()),
             image_templates: HashMap::with_hasher(Default::default()),
             cached_glyph_dimensions: HashMap::with_hasher(Default::default()),
             texture_cache: texture_cache,
             state: State::Idle,
             device_pixel_ratio: device_pixel_ratio,
             enable_aa: enable_aa,
             current_frame_id: FrameId(0),
@@ -270,36 +260,29 @@ impl ResourceCache {
 
         self.image_templates.insert(image_key, resource);
     }
 
     pub fn delete_image_template(&mut self, image_key: ImageKey) {
         self.image_templates.remove(&image_key);
     }
 
-    pub fn add_webgl_texture(&mut self, id: WebGLContextId, texture_id: TextureId, size: Size2D<i32>) {
+    pub fn add_webgl_texture(&mut self, id: WebGLContextId, texture_id: SourceTexture, size: Size2D<i32>) {
         self.webgl_textures.insert(id, WebGLTexture {
             id: texture_id,
             size: size,
         });
-        self.texture_cache.add_raw_update(texture_id, size);
     }
 
-    pub fn update_webgl_texture(&mut self, id: WebGLContextId, texture_id: TextureId, size: Size2D<i32>) {
+    pub fn update_webgl_texture(&mut self, id: WebGLContextId, texture_id: SourceTexture, size: Size2D<i32>) {
         let webgl_texture = self.webgl_textures.get_mut(&id).unwrap();
 
-        // Remove existing cache if texture id has changed
-        if webgl_texture.id != texture_id {
-            self.texture_cache.add_raw_remove(webgl_texture.id);
-        }
         // Update new texture id and size
         webgl_texture.id = texture_id;
         webgl_texture.size = size;
-
-        self.texture_cache.add_raw_update(texture_id, size);
     }
 
     pub fn request_image(&mut self,
                          key: ImageKey,
                          rendering: ImageRendering) {
         debug_assert!(self.state == State::AddResources);
         self.pending_requests.push(ResourceRequest::Image(key, rendering));
     }
@@ -342,61 +325,49 @@ impl ResourceCache {
                 Some(image_id)
             } else {
                 None
             };
             self.cached_glyphs.insert(job.glyph_key, image_id, self.current_frame_id);
         }
     }
 
-    pub fn add_draw_list(&mut self, items: Vec<DisplayItem>, pipeline_id: PipelineId)
-                         -> DrawListId {
-        self.draw_lists.insert(DrawList::new(items, pipeline_id))
-    }
-
-    pub fn get_draw_list(&self, draw_list_id: DrawListId) -> &DrawList {
-        self.draw_lists.get(draw_list_id)
-    }
-
-    pub fn remove_draw_list(&mut self, draw_list_id: DrawListId) {
-        self.draw_lists.free(draw_list_id);
-    }
-
     pub fn pending_updates(&mut self) -> TextureUpdateList {
         self.texture_cache.pending_updates()
     }
 
     pub fn get_glyphs<F>(&self,
                          font_key: FontKey,
                          size: Au,
                          glyph_indices: &[u32],
                          render_mode: FontRenderMode,
-                         mut f: F) -> TextureId where F: FnMut(usize, Point2D<f32>, Point2D<f32>) {
+                         mut f: F) -> SourceTexture where F: FnMut(usize, Point2D<f32>, Point2D<f32>) {
         debug_assert!(self.state == State::QueryResources);
         let mut glyph_key = RenderedGlyphKey::new(font_key,
                                                   size,
                                                   0,
                                                   render_mode);
-        let mut texture_id = TextureId::invalid();
+        let mut texture_id = None;
         for (loop_index, glyph_index) in glyph_indices.iter().enumerate() {
             glyph_key.key.index = *glyph_index;
             let image_id = self.cached_glyphs.get(&glyph_key, self.current_frame_id);
             let cache_item = image_id.map(|image_id| self.texture_cache.get(image_id));
             if let Some(cache_item) = cache_item {
                 let uv0 = Point2D::new(cache_item.pixel_rect.top_left.x as f32,
                                        cache_item.pixel_rect.top_left.y as f32);
                 let uv1 = Point2D::new(cache_item.pixel_rect.bottom_right.x as f32,
                                        cache_item.pixel_rect.bottom_right.y as f32);
                 f(loop_index, uv0, uv1);
-                debug_assert!(texture_id == TextureId::invalid() ||
-                              texture_id == cache_item.texture_id);
-                texture_id = cache_item.texture_id;
+                debug_assert!(texture_id == None ||
+                              texture_id == Some(cache_item.texture_id));
+                texture_id = Some(cache_item.texture_id);
             }
         }
-        texture_id
+
+        texture_id.map_or(SourceTexture::Invalid, SourceTexture::TextureCache)
     }
 
     pub fn get_glyph_dimensions(&mut self, glyph_key: &GlyphKey) -> Option<GlyphDimensions> {
         match self.cached_glyph_dimensions.entry(glyph_key.clone()) {
             Occupied(entry) => *entry.get(),
             Vacant(entry) => {
                 let mut dimensions = None;
                 let device_pixel_ratio = self.device_pixel_ratio;
@@ -429,17 +400,17 @@ impl ResourceCache {
     pub fn get_image(&self,
                      image_key: ImageKey,
                      image_rendering: ImageRendering) -> CacheItem {
         debug_assert!(self.state == State::QueryResources);
         let image_info = &self.cached_images.get(&(image_key, image_rendering),
                                                  self.current_frame_id);
         let item = self.texture_cache.get(image_info.texture_cache_id);
         CacheItem {
-            texture_id: item.texture_id,
+            texture_id: SourceTexture::TextureCache(item.texture_id),
             uv0: Point2D::new(item.pixel_rect.top_left.x as f32,
                               item.pixel_rect.top_left.y as f32),
             uv1: Point2D::new(item.pixel_rect.bottom_right.x as f32,
                               item.pixel_rect.bottom_right.y as f32),
         }
     }
 
     pub fn get_image_properties(&self, image_key: ImageKey) -> ImageProperties {
@@ -447,22 +418,16 @@ impl ResourceCache {
 
         ImageProperties {
             format: image_template.format,
             is_opaque: image_template.is_opaque,
         }
     }
 
     #[inline]
-    pub fn get_image_by_cache_id(&self, texture_cache_id: TextureCacheItemId)
-                                 -> &TextureCacheItem {
-        self.texture_cache.get(texture_cache_id)
-    }
-
-    #[inline]
     pub fn get_webgl_texture(&self, context_id: &WebGLContextId) -> CacheItem {
         let webgl_texture = &self.webgl_textures[context_id];
         CacheItem {
             texture_id: webgl_texture.id,
             uv0: Point2D::new(0.0, webgl_texture.size.height as f32),
             uv1: Point2D::new(webgl_texture.size.width as f32, 0.0),
         }
     }
--- a/gfx/webrender/src/scene.rs
+++ b/gfx/webrender/src/scene.rs
@@ -1,213 +1,78 @@
 /* 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::Size2D;
 use fnv::FnvHasher;
-use internal_types::DrawListId;
-use resource_cache::ResourceCache;
 use std::collections::HashMap;
 use std::hash::BuildHasherDefault;
 use tiling::AuxiliaryListsMap;
-use webrender_traits::{AuxiliaryLists, BuiltDisplayList, PipelineId, Epoch};
-use webrender_traits::{ColorF, DisplayListId, StackingContext, StackingContextId};
-use webrender_traits::{SpecificDisplayListItem};
-use webrender_traits::{IframeInfo};
-use webrender_traits::{RectangleDisplayItem, ClipRegion, DisplayItem, SpecificDisplayItem};
+use webrender_traits::{AuxiliaryLists, BuiltDisplayList, PipelineId, Epoch, ColorF};
+use webrender_traits::{DisplayItem, SpecificDisplayItem, StackingContext};
+
+trait DisplayListHelpers {
+    fn starting_stacking_context<'a>(&'a self) -> Option<&'a StackingContext>;
+}
+
+impl DisplayListHelpers for Vec<DisplayItem> {
+    fn starting_stacking_context<'a>(&'a self) -> Option<&'a StackingContext> {
+        self.first().and_then(|item| match item.item {
+            SpecificDisplayItem::PushStackingContext(ref item) => Some(&item.stacking_context),
+            _ => None,
+        })
+    }
+}
 
 /// A representation of the layout within the display port for a given document or iframe.
 #[derive(Debug)]
 pub struct ScenePipeline {
     pub pipeline_id: PipelineId,
     pub epoch: Epoch,
-    pub background_draw_list: Option<DrawListId>,
-    pub root_stacking_context_id: StackingContextId,
     pub viewport_size: Size2D<f32>,
+    pub background_color: ColorF,
 }
 
 /// A complete representation of the layout bundling visible pipelines together.
 pub struct Scene {
     pub root_pipeline_id: Option<PipelineId>,
     pub pipeline_map: HashMap<PipelineId, ScenePipeline, BuildHasherDefault<FnvHasher>>,
     pub pipeline_sizes: HashMap<PipelineId, Size2D<f32>>,
     pub pipeline_auxiliary_lists: AuxiliaryListsMap,
-    pub display_list_map: HashMap<DisplayListId, SceneDisplayList, BuildHasherDefault<FnvHasher>>,
-    pub stacking_context_map: HashMap<StackingContextId, SceneStackingContext, BuildHasherDefault<FnvHasher>>,
-}
-
-#[derive(Clone, Debug)]
-pub enum SpecificSceneItem {
-    DrawList(DrawListId),
-    StackingContext(StackingContextId, PipelineId),
-    Iframe(IframeInfo),
-}
-
-#[derive(Clone, Debug)]
-pub struct SceneItem {
-    pub specific: SpecificSceneItem,
-}
-
-/// Similar to webrender_traits::DisplayList internal to WebRender.
-pub struct SceneDisplayList {
-    pub pipeline_id: PipelineId,
-    pub epoch: Epoch,
-    pub items: Vec<SceneItem>,
-}
-
-pub struct SceneStackingContext {
-    pub pipeline_id: PipelineId,
-    pub epoch: Epoch,
-    pub stacking_context: StackingContext,
-    pub stacking_context_id: StackingContextId,
+    pub display_lists: HashMap<PipelineId, Vec<DisplayItem>, BuildHasherDefault<FnvHasher>>,
 }
 
 impl Scene {
     pub fn new() -> Scene {
         Scene {
             root_pipeline_id: None,
             pipeline_sizes: HashMap::new(),
             pipeline_map: HashMap::with_hasher(Default::default()),
             pipeline_auxiliary_lists: HashMap::with_hasher(Default::default()),
-            display_list_map: HashMap::with_hasher(Default::default()),
-            stacking_context_map: HashMap::with_hasher(Default::default()),
+            display_lists: HashMap::with_hasher(Default::default()),
         }
     }
 
-    pub fn add_display_list(&mut self,
-                            id: DisplayListId,
-                            pipeline_id: PipelineId,
-                            epoch: Epoch,
-                            built_display_list: BuiltDisplayList,
-                            resource_cache: &mut ResourceCache) {
-        let items = built_display_list.display_list_items().iter().map(|item| {
-            match item.specific {
-                SpecificDisplayListItem::DrawList(ref info) => {
-                    let draw_list_id = resource_cache.add_draw_list(
-                        built_display_list.display_items(&info.items).to_vec(),
-                        pipeline_id);
-                    SceneItem {
-                        specific: SpecificSceneItem::DrawList(draw_list_id)
-                    }
-                }
-                SpecificDisplayListItem::StackingContext(ref info) => {
-                    SceneItem {
-                        specific: SpecificSceneItem::StackingContext(info.id, pipeline_id)
-                    }
-                }
-                SpecificDisplayListItem::Iframe(ref info) => {
-                    SceneItem {
-                        specific: SpecificSceneItem::Iframe(*info)
-                    }
-                }
-            }
-        }).collect();
-
-        let display_list = SceneDisplayList {
-            pipeline_id: pipeline_id,
-            epoch: epoch,
-            items: items,
-        };
-
-        self.display_list_map.insert(id, display_list);
-    }
-
-    pub fn add_stacking_context(&mut self,
-                                id: StackingContextId,
-                                pipeline_id: PipelineId,
-                                epoch: Epoch,
-                                stacking_context: StackingContext) {
-        let stacking_context = SceneStackingContext {
-            pipeline_id: pipeline_id,
-            epoch: epoch,
-            stacking_context: stacking_context,
-            stacking_context_id: id,
-        };
-
-        self.stacking_context_map.insert(id, stacking_context);
-    }
-
     pub fn set_root_pipeline_id(&mut self, pipeline_id: PipelineId) {
         self.root_pipeline_id = Some(pipeline_id);
     }
 
-    pub fn set_root_stacking_context(&mut self,
-                                     pipeline_id: PipelineId,
-                                     epoch: Epoch,
-                                     stacking_context_id: StackingContextId,
-                                     background_color: ColorF,
-                                     viewport_size: Size2D<f32>,
-                                     resource_cache: &mut ResourceCache,
-                                     auxiliary_lists: AuxiliaryLists) {
+    pub fn set_root_display_list(&mut self,
+                                 pipeline_id: PipelineId,
+                                 epoch: Epoch,
+                                 built_display_list: BuiltDisplayList,
+                                 background_color: ColorF,
+                                 viewport_size: Size2D<f32>,
+                                 auxiliary_lists: AuxiliaryLists) {
         self.pipeline_auxiliary_lists.insert(pipeline_id, auxiliary_lists);
-
-        let old_display_list_keys: Vec<_> = self.display_list_map.iter()
-                                                .filter(|&(_, ref v)| {
-                                                    v.pipeline_id == pipeline_id &&
-                                                    v.epoch < epoch
-                                                })
-                                                .map(|(k, _)| k.clone())
-                                                .collect();
-
-        // Remove any old draw lists and display lists for this pipeline
-        for key in old_display_list_keys {
-            let display_list = self.display_list_map.remove(&key).unwrap();
-            for item in display_list.items {
-                match item.specific {
-                    SpecificSceneItem::DrawList(draw_list_id) => {
-                        resource_cache.remove_draw_list(draw_list_id);
-                    }
-                    SpecificSceneItem::StackingContext(..) |
-                    SpecificSceneItem::Iframe(..) => {}
-                }
-            }
-        }
-
-        let old_stacking_context_keys: Vec<_> = self.stacking_context_map.iter()
-                                                                         .filter(|&(_, ref v)| {
-                                                                             v.pipeline_id == pipeline_id &&
-                                                                             v.epoch < epoch
-                                                                         })
-                                                                         .map(|(k, _)| k.clone())
-                                                                         .collect();
-
-        // Remove any old draw lists and display lists for this pipeline
-        for key in old_stacking_context_keys {
-            self.stacking_context_map.remove(&key).unwrap();
-
-            // TODO: Could remove all associated DLs here,
-            //       and then the above code could just be a debug assert check...
-        }
-
-        let background_draw_list = if background_color.a > 0.0 {
-            let overflow = self.stacking_context_map[&stacking_context_id].stacking_context.overflow;
-
-            let rectangle_item = RectangleDisplayItem {
-                color: background_color,
-            };
-            let root_bg_color_item = DisplayItem {
-                item: SpecificDisplayItem::Rectangle(rectangle_item),
-                rect: overflow,
-                clip: ClipRegion::simple(&overflow),
-            };
-
-            let draw_list_id = resource_cache.add_draw_list(vec![root_bg_color_item], pipeline_id);
-            Some(draw_list_id)
-        } else {
-            None
-        };
+        self.display_lists.insert(pipeline_id, built_display_list.all_display_items().to_vec());
 
         let new_pipeline = ScenePipeline {
             pipeline_id: pipeline_id,
             epoch: epoch,
-            background_draw_list: background_draw_list,
-            root_stacking_context_id: stacking_context_id,
             viewport_size: viewport_size,
+            background_color: background_color,
         };
 
-        if let Some(old_pipeline) = self.pipeline_map.insert(pipeline_id, new_pipeline) {
-            if let Some(background_draw_list) = old_pipeline.background_draw_list {
-                resource_cache.remove_draw_list(background_draw_list);
-            }
-        }
+        self.pipeline_map.insert(pipeline_id, new_pipeline);
     }
 }
--- a/gfx/webrender/src/texture_cache.rs
+++ b/gfx/webrender/src/texture_cache.rs
@@ -1,18 +1,18 @@
 /* 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::{MAX_TEXTURE_SIZE, TextureId, TextureFilter};
+use device::{MAX_TEXTURE_SIZE, TextureFilter};
 use euclid::{Point2D, Rect, Size2D};
 use fnv::FnvHasher;
 use freelist::{FreeList, FreeListItem, FreeListItemId};
-use internal_types::{TextureUpdate, TextureUpdateOp, TextureUpdateDetails};
-use internal_types::{RenderTargetMode, TextureUpdateList};
+use internal_types::{TextureUpdate, TextureUpdateOp};
+use internal_types::{CacheTextureId, RenderTargetMode, TextureUpdateList};
 use internal_types::{RectUv, DevicePixel, DevicePoint};
 use std::cmp::{self, Ordering};
 use std::collections::HashMap;
 use std::collections::hash_map::Entry;
 use std::hash::BuildHasherDefault;
 use std::mem;
 use std::slice::Iter;
 use time;
@@ -76,25 +76,25 @@ fn copy_pixels(src: &[u8],
 /// sections 2.2 and 2.2.5 in "A Thousand Ways to Pack the Bin - A Practical Approach to Two-
 /// Dimensional Rectangle Bin Packing":
 ///
 ///    http://clb.demon.fi/files/RectangleBinPack.pdf
 ///
 /// This approach was chosen because of its simplicity, good performance, and easy support for
 /// dynamic texture deallocation.
 pub struct TexturePage {
-    texture_id: TextureId,
+    texture_id: CacheTextureId,
     texture_size: u32,
     free_list: FreeRectList,
     allocations: u32,
     dirty: bool,
 }
 
 impl TexturePage {
-    pub fn new(texture_id: TextureId, texture_size: u32) -> TexturePage {
+    pub fn new(texture_id: CacheTextureId, texture_size: u32) -> TexturePage {
         let mut page = TexturePage {
             texture_id: texture_id,
             texture_size: texture_size,
             free_list: FreeRectList::new(),
             allocations: 0,
             dirty: false,
         };
         page.clear();
@@ -419,17 +419,17 @@ impl FreeListBin {
             FreeListBin::Small
         }
     }
 }
 
 #[derive(Debug, Clone)]
 pub struct TextureCacheItem {
     // Identifies the texture and array slice
-    pub texture_id: TextureId,
+    pub texture_id: CacheTextureId,
 
     // The texture coordinates for this item
     pub pixel_rect: RectUv<i32, DevicePixel>,
 
     // The size of the entire texture (not just the allocated rectangle)
     pub texture_size: Size2D<u32>,
 
     // The size of the actual allocated rectangle,
@@ -463,17 +463,17 @@ impl FreeListItem for TextureCacheItem {
                 self.requested_rect.size.width = 0;
                 self.requested_rect.size.height = 0;
             }
         }
     }
 }
 
 impl TextureCacheItem {
-    fn new(texture_id: TextureId,
+    fn new(texture_id: CacheTextureId,
            allocated_rect: Rect<u32>,
            requested_rect: Rect<u32>,
            texture_size: &Size2D<u32>)
            -> TextureCacheItem {
         TextureCacheItem {
             texture_id: texture_id,
             texture_size: *texture_size,
             pixel_rect: RectUv {
@@ -491,47 +491,70 @@ impl TextureCacheItem {
         }
     }
 }
 
 struct TextureCacheArena {
     pages_a8: Vec<TexturePage>,
     pages_rgb8: Vec<TexturePage>,
     pages_rgba8: Vec<TexturePage>,
-    alternate_pages_a8: Vec<TexturePage>,
-    alternate_pages_rgba8: Vec<TexturePage>,
 }
 
 impl TextureCacheArena {
     fn new() -> TextureCacheArena {
         TextureCacheArena {
             pages_a8: Vec::new(),
             pages_rgb8: Vec::new(),
             pages_rgba8: Vec::new(),
-            alternate_pages_a8: Vec::new(),
-            alternate_pages_rgba8: Vec::new(),
-            //render_target_pages: Vec::new(),
         }
     }
 
-    fn texture_page_for_id(&mut self, id: TextureId) -> Option<&mut TexturePage> {
+    fn texture_page_for_id(&mut self, id: CacheTextureId) -> Option<&mut TexturePage> {
         for page in self.pages_a8.iter_mut().chain(self.pages_rgb8.iter_mut())
-                                            .chain(self.pages_rgba8.iter_mut())
-                                            .chain(self.alternate_pages_a8.iter_mut())
-                                            .chain(self.alternate_pages_rgba8.iter_mut()) {
+                                            .chain(self.pages_rgba8.iter_mut()) {
             if page.texture_id == id {
                 return Some(page)
             }
         }
         None
     }
 }
 
+pub struct CacheTextureIdList {
+    next_id: usize,
+    free_list: Vec<usize>,
+}
+
+impl CacheTextureIdList {
+    fn new() -> CacheTextureIdList {
+        CacheTextureIdList {
+            next_id: 0,
+            free_list: Vec::new(),
+        }
+    }
+
+    fn allocate(&mut self) -> CacheTextureId {
+        // If nothing on the free list of texture IDs,
+        // allocate a new one.
+        if self.free_list.is_empty() {
+            self.free_list.push(self.next_id);
+            self.next_id += 1;
+        }
+
+        let id = self.free_list.pop().unwrap();
+        CacheTextureId(id)
+    }
+
+    fn free(&mut self, id: CacheTextureId) {
+        self.free_list.push(id.0);
+    }
+}
+
 pub struct TextureCache {
-    free_texture_ids: Vec<TextureId>,
+    cache_id_list: CacheTextureIdList,
     free_texture_levels: HashMap<ImageFormat, Vec<FreeTextureLevel>, BuildHasherDefault<FnvHasher>>,
     items: FreeList<TextureCacheItem>,
     arena: TextureCacheArena,
     pending_updates: TextureUpdateList,
 }
 
 #[derive(PartialEq, Eq, Debug)]
 pub enum AllocationKind {
@@ -541,19 +564,19 @@ pub enum AllocationKind {
 
 #[derive(Debug)]
 pub struct AllocationResult {
     kind: AllocationKind,
     item: TextureCacheItem,
 }
 
 impl TextureCache {
-    pub fn new(free_texture_ids: Vec<TextureId>) -> TextureCache {
+    pub fn new() -> TextureCache {
         TextureCache {
-            free_texture_ids: free_texture_ids,
+            cache_id_list: CacheTextureIdList::new(),
             free_texture_levels: HashMap::with_hasher(Default::default()),
             items: FreeList::new(),
             pending_updates: TextureUpdateList::new(),
             arena: TextureCacheArena::new(),
         }
     }
 
     pub fn pending_updates(&mut self) -> TextureUpdateList {
@@ -569,17 +592,17 @@ impl TextureCache {
                 top_left: DevicePoint::zero(),
                 top_right: DevicePoint::zero(),
                 bottom_left: DevicePoint::zero(),
                 bottom_right: DevicePoint::zero(),
             },
             allocated_rect: Rect::zero(),
             requested_rect: Rect::zero(),
             texture_size: Size2D::zero(),
-            texture_id: TextureId::invalid(),
+            texture_id: CacheTextureId(0),
         };
         self.items.insert(new_item)
     }
 
     pub fn allocate(&mut self,
                     image_id: TextureCacheItemId,
                     requested_width: u32,
                     requested_height: u32,
@@ -590,19 +613,17 @@ impl TextureCache {
 
         // TODO(gw): For now, anything that requests nearest filtering
         //           just fails to allocate in a texture page, and gets a standalone
         //           texture. This isn't ideal, as it causes lots of batch breaks,
         //           but is probably rare enough that it can be fixed up later (it's also
         //           fairly trivial to implement, just tedious).
         if filter == TextureFilter::Nearest {
             // Fall back to standalone texture allocation.
-            let texture_id = self.free_texture_ids
-                                 .pop()
-                                 .expect("TODO: Handle running out of texture ids!");
+            let texture_id = self.cache_id_list.allocate();
             let cache_item = TextureCacheItem::new(
                 texture_id,
                 Rect::new(Point2D::zero(), requested_size),
                 Rect::new(Point2D::zero(), requested_size),
                 &requested_size);
             *self.items.get_mut(image_id) = cache_item;
 
             return AllocationResult {
@@ -677,48 +698,36 @@ impl TextureCache {
             // We need a new page.
             let texture_size = initial_texture_size();
             let free_texture_levels_entry = self.free_texture_levels.entry(format);
             let mut free_texture_levels = match free_texture_levels_entry {
                 Entry::Vacant(entry) => entry.insert(Vec::new()),
                 Entry::Occupied(entry) => entry.into_mut(),
             };
             if free_texture_levels.is_empty() {
-                create_new_texture_page(&mut self.pending_updates,
-                                        &mut self.free_texture_ids,
-                                        &mut free_texture_levels,
-                                        texture_size,
-                                        format,
-                                        mode);
+                let texture_id = self.cache_id_list.allocate();
+
+                let update_op = TextureUpdate {
+                    id: texture_id,
+                    op: texture_create_op(texture_size, format, mode),
+                };
+                self.pending_updates.push(update_op);
+
+                free_texture_levels.push(FreeTextureLevel {
+                    texture_id: texture_id,
+                });
             }
             let free_texture_level = free_texture_levels.pop().unwrap();
             let texture_id = free_texture_level.texture_id;
 
             let page = TexturePage::new(texture_id, texture_size);
             page_list.push(page);
         }
     }
 
-    pub fn add_raw_update(&mut self, id: TextureId, size: Size2D<i32>) {
-        self.pending_updates.push(TextureUpdate {
-            id: id,
-            op: TextureUpdateOp::Update(
-                0, 0,
-                size.width as u32, size.height as u32,
-                TextureUpdateDetails::Raw),
-        })
-    }
-
-    pub fn add_raw_remove(&mut self, id: TextureId) {
-        self.pending_updates.push(TextureUpdate {
-            id: id,
-            op: TextureUpdateOp::Remove
-        });
-    }
-
     pub fn update(&mut self,
                   image_id: TextureCacheItemId,
                   width: u32,
                   height: u32,
                   stride: Option<u32>,
                   _format: ImageFormat,
                   bytes: Vec<u8>) {
         let existing_item = self.items.get(image_id);
@@ -726,17 +735,18 @@ impl TextureCache {
         // TODO(gw): Handle updates to size/format!
         debug_assert!(existing_item.requested_rect.size.width == width);
         debug_assert!(existing_item.requested_rect.size.height == height);
 
         let op = TextureUpdateOp::Update(existing_item.requested_rect.origin.x,
                                          existing_item.requested_rect.origin.y,
                                          width,
                                          height,
-                                         TextureUpdateDetails::Blit(bytes, stride));
+                                         bytes,
+                                         stride);
 
         let update_op = TextureUpdate {
             id: existing_item.texture_id,
             op: op,
         };
 
         self.pending_updates.push(update_op);
     }
@@ -783,59 +793,64 @@ impl TextureCache {
                 }
 
                 let border_update_op_top = TextureUpdate {
                     id: result.item.texture_id,
                     op: TextureUpdateOp::Update(result.item.allocated_rect.origin.x,
                                                 result.item.allocated_rect.origin.y,
                                                 result.item.allocated_rect.size.width,
                                                 1,
-                                                TextureUpdateDetails::Blit(top_row_bytes, None))
+                                                top_row_bytes,
+                                                None)
                 };
 
                 let border_update_op_bottom = TextureUpdate {
                     id: result.item.texture_id,
                     op: TextureUpdateOp::Update(
                         result.item.allocated_rect.origin.x,
                         result.item.allocated_rect.origin.y +
                             result.item.requested_rect.size.height + 1,
                         result.item.allocated_rect.size.width,
                         1,
-                        TextureUpdateDetails::Blit(bottom_row_bytes, None))
+                        bottom_row_bytes,
+                        None)
                 };
 
                 let border_update_op_left = TextureUpdate {
                     id: result.item.texture_id,
                     op: TextureUpdateOp::Update(
                         result.item.allocated_rect.origin.x,
                         result.item.requested_rect.origin.y,
                         1,
                         result.item.requested_rect.size.height,
-                        TextureUpdateDetails::Blit(left_column_bytes, None))
+                        left_column_bytes,
+                        None)
                 };
 
                 let border_update_op_right = TextureUpdate {
                     id: result.item.texture_id,
                     op: TextureUpdateOp::Update(result.item.allocated_rect.origin.x + result.item.requested_rect.size.width + 1,
                                                 result.item.requested_rect.origin.y,
                                                 1,
                                                 result.item.requested_rect.size.height,
-                                                TextureUpdateDetails::Blit(right_column_bytes, None))
+                                                right_column_bytes,
+                                                None)
                 };
 
                 self.pending_updates.push(border_update_op_top);
                 self.pending_updates.push(border_update_op_bottom);
                 self.pending_updates.push(border_update_op_left);
                 self.pending_updates.push(border_update_op_right);
 
                 TextureUpdateOp::Update(result.item.requested_rect.origin.x,
                                         result.item.requested_rect.origin.y,
                                         width,
                                         height,
-                                        TextureUpdateDetails::Blit(bytes,stride))
+                                        bytes,
+                                        stride)
             }
             AllocationKind::Standalone => {
                 TextureUpdateOp::Create(width,
                                         height,
                                         format,
                                         filter,
                                         RenderTargetMode::None,
                                         Some(bytes))
@@ -857,17 +872,17 @@ impl TextureCache {
     pub fn free(&mut self, id: TextureCacheItemId) {
         {
             let item = self.items.get(id);
             match self.arena.texture_page_for_id(item.texture_id) {
                 Some(texture_page) => texture_page.free(&item.allocated_rect),
                 None => {
                     // This is a standalone texture allocation. Just push it back onto the free
                     // list.
-                    self.free_texture_ids.push(item.texture_id);
+                    self.cache_id_list.free(item.texture_id);
                 }
             }
         }
 
         self.items.free(id)
     }
 }
 
@@ -895,35 +910,17 @@ impl FitsInside for Size2D<u32> {
     fn fits_inside(&self, other: &Size2D<u32>) -> bool {
         self.width <= other.width && self.height <= other.height
     }
 }
 
 /// FIXME(pcwalton): Would probably be more efficient as a bit vector.
 #[derive(Clone, Copy)]
 pub struct FreeTextureLevel {
-    texture_id: TextureId,
-}
-
-fn create_new_texture_page(pending_updates: &mut TextureUpdateList,
-                           free_texture_ids: &mut Vec<TextureId>,
-                           free_texture_levels: &mut Vec<FreeTextureLevel>,
-                           texture_size: u32,
-                           format: ImageFormat,
-                           mode: RenderTargetMode) {
-    let texture_id = free_texture_ids.pop().expect("TODO: Handle running out of texture IDs!");
-    let update_op = TextureUpdate {
-        id: texture_id,
-        op: texture_create_op(texture_size, format, mode),
-    };
-    pending_updates.push(update_op);
-
-    free_texture_levels.push(FreeTextureLevel {
-        texture_id: texture_id,
-    })
+    texture_id: CacheTextureId,
 }
 
 /// Returns the number of pixels on a side we start out with for our texture atlases.
 fn initial_texture_size() -> u32 {
     let max_hardware_texture_size = *MAX_TEXTURE_SIZE as u32;
     if max_hardware_texture_size * max_hardware_texture_size > INITIAL_TEXTURE_AREA {
         INITIAL_TEXTURE_SIZE
     } else {
--- a/gfx/webrender/src/tiling.rs
+++ b/gfx/webrender/src/tiling.rs
@@ -1,36 +1,37 @@
 /* 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 batch_builder::BorderSideHelpers;
-use device::{TextureId};
 use euclid::{Point2D, Point4D, Rect, Matrix4D, Size2D};
 use fnv::FnvHasher;
 use frame::FrameId;
 use gpu_store::GpuStoreAddress;
 use internal_types::{DeviceRect, DevicePoint, DeviceSize, DeviceLength, device_pixel, CompositionOp};
 use internal_types::{ANGLE_FLOAT_TO_FIXED, LowLevelFilterOp};
+use internal_types::{BatchTextures, CacheTextureId, SourceTexture};
 use layer::Layer;
+use mask_cache::{MaskCacheKey, MaskCacheInfo};
 use prim_store::{PrimitiveGeometry, RectanglePrimitive, PrimitiveContainer};
 use prim_store::{BorderPrimitiveCpu, BorderPrimitiveGpu, BoxShadowPrimitiveGpu};
 use prim_store::{ImagePrimitiveCpu, ImagePrimitiveGpu, ImagePrimitiveKind};
 use prim_store::{PrimitiveKind, PrimitiveIndex, PrimitiveMetadata};
-use prim_store::PrimitiveClipSource;
+use prim_store::{CLIP_DATA_GPU_SIZE, PrimitiveClipSource};
 use prim_store::{GradientPrimitiveCpu, GradientPrimitiveGpu, GradientType};
 use prim_store::{PrimitiveCacheKey, TextRunPrimitiveGpu, TextRunPrimitiveCpu};
 use prim_store::{PrimitiveStore, GpuBlock16, GpuBlock32, GpuBlock64, GpuBlock128};
 use profiler::FrameProfileCounters;
 use renderer::BlendMode;
-use resource_cache::{DummyResources, ResourceCache};
+use resource_cache::ResourceCache;
 use std::cmp;
 use std::collections::{HashMap};
-use std::f32;
+use std::{i32, f32};
 use std::mem;
 use std::hash::{BuildHasherDefault};
 use std::sync::atomic::{AtomicUsize, Ordering};
 use std::usize;
 use texture_cache::TexturePage;
 use util::{self, rect_from_points, MatrixHelpers, rect_from_points_f};
 use util::{TransformedRect, TransformedRectKind, subtract_rect, pack_as_float};
 use webrender_traits::{ColorF, FontKey, ImageKey, ImageRendering, MixBlendMode};
@@ -44,28 +45,28 @@ pub type LayerMap = HashMap<ScrollLayerI
                             Layer,
                             BuildHasherDefault<FnvHasher>>;
 pub type AuxiliaryListsMap = HashMap<PipelineId,
                                      AuxiliaryLists,
                                      BuildHasherDefault<FnvHasher>>;
 
 trait AlphaBatchHelpers {
     fn get_batch_kind(&self, metadata: &PrimitiveMetadata) -> AlphaBatchKind;
-    fn get_texture_id(&self, metadata: &PrimitiveMetadata) -> TextureId;
+    fn get_color_textures(&self, metadata: &PrimitiveMetadata) -> [SourceTexture; 3];
     fn get_blend_mode(&self, needs_blending: bool, metadata: &PrimitiveMetadata) -> BlendMode;
     fn prim_affects_tile(&self,
                          prim_index: PrimitiveIndex,
                          tile_rect: &DeviceRect,
                          transform: &Matrix4D<f32>,
                          device_pixel_ratio: f32) -> bool;
     fn add_prim_to_batch(&self,
                          prim_index: PrimitiveIndex,
                          batch: &mut PrimitiveBatch,
                          layer_index: StackingContextIndex,
-                         task_id: i32,
+                         task_index: i32,
                          render_tasks: &RenderTaskCollection,
                          pass_index: RenderPassIndex);
 }
 
 impl AlphaBatchHelpers for PrimitiveStore {
     fn get_batch_kind(&self, metadata: &PrimitiveMetadata) -> AlphaBatchKind {
         let batch_kind = match metadata.prim_kind {
             PrimitiveKind::Border => AlphaBatchKind::Border,
@@ -94,30 +95,32 @@ impl AlphaBatchHelpers for PrimitiveStor
                     }
                 }
             }
         };
 
         batch_kind
     }
 
-    fn get_texture_id(&self, metadata: &PrimitiveMetadata) -> TextureId {
+    fn get_color_textures(&self, metadata: &PrimitiveMetadata) -> [SourceTexture; 3] {
+        let invalid = SourceTexture::Invalid;
         match metadata.prim_kind {
             PrimitiveKind::Border |
             PrimitiveKind::BoxShadow |
             PrimitiveKind::Rectangle |
-            PrimitiveKind::Gradient => TextureId::invalid(),
+            PrimitiveKind::Gradient => [invalid; 3],
             PrimitiveKind::Image => {
                 let image_cpu = &self.cpu_images[metadata.cpu_prim_index.0];
-                image_cpu.color_texture_id
+                [image_cpu.color_texture_id, invalid, invalid]
             }
             PrimitiveKind::TextRun => {
                 let text_run_cpu = &self.cpu_text_runs[metadata.cpu_prim_index.0];
-                text_run_cpu.color_texture_id
+                [text_run_cpu.color_texture_id, invalid, invalid]
             }
+            // TODO(nical): YuvImage will return 3 textures.
         }
     }
 
     fn get_blend_mode(&self, needs_blending: bool, metadata: &PrimitiveMetadata) -> BlendMode {
         match metadata.prim_kind {
             PrimitiveKind::TextRun => {
                 let text_run_cpu = &self.cpu_text_runs[metadata.cpu_prim_index.0];
                 if text_run_cpu.blur_radius.0 == 0 {
@@ -143,154 +146,167 @@ impl AlphaBatchHelpers for PrimitiveStor
     // Optional narrow phase intersection test, depending on primitive type.
     fn prim_affects_tile(&self,
                          prim_index: PrimitiveIndex,
                          tile_rect: &DeviceRect,
                          transform: &Matrix4D<f32>,
                          device_pixel_ratio: f32) -> bool {
         let metadata = self.get_metadata(prim_index);
 
+        // bail out if the clip rectangle is outside of the tile
+        if let Some(ref clip_info) = metadata.clip_cache_info {
+            if !clip_info.device_rect.intersects(tile_rect) {
+                return false;
+            }
+        }
+
         match metadata.prim_kind {
             PrimitiveKind::Rectangle |
             PrimitiveKind::TextRun |
             PrimitiveKind::Image |
             PrimitiveKind::Gradient |
             PrimitiveKind::BoxShadow => true,
-
             PrimitiveKind::Border => {
                 let border = &self.cpu_borders[metadata.cpu_prim_index.0];
                 let inner_rect = TransformedRect::new(&border.inner_rect,
                                                       transform,
                                                       device_pixel_ratio);
 
                 !inner_rect.bounding_rect.contains_rect(tile_rect)
             }
         }
     }
 
     fn add_prim_to_batch(&self,
                          prim_index: PrimitiveIndex,
                          batch: &mut PrimitiveBatch,
                          layer_index: StackingContextIndex,
-                         task_id: i32,
+                         task_index: i32,
                          render_tasks: &RenderTaskCollection,
                          child_pass_index: RenderPassIndex) {
         let metadata = self.get_metadata(prim_index);
         let layer_index = layer_index.0 as i32;
         let global_prim_id = prim_index.0 as i32;
         let prim_address = metadata.gpu_prim_index;
-        let clip_address = metadata.clip_index.unwrap_or(GpuStoreAddress(0));
+        let clip_task_index = match metadata.clip_cache_info {
+            Some(ref clip_info) => {
+                let cache_task_id = RenderTaskId::Dynamic(RenderTaskKey::CacheMask(clip_info.key));
+                let cache_task_index = render_tasks.get_task_index(&cache_task_id, child_pass_index);
+                cache_task_index.0 as i32
+            },
+            None => i32::MAX, //sentinel value for the dummy mask
+        };
 
         match &mut batch.data {
             &mut PrimitiveBatchData::Blend(..) |
             &mut PrimitiveBatchData::Composite(..) => unreachable!(),
 
             &mut PrimitiveBatchData::Rectangles(ref mut data) => {
                 data.push(PrimitiveInstance {
-                    task_id: task_id,
+                    task_index: task_index,
+                    clip_task_index: clip_task_index,
                     layer_index: layer_index,
                     global_prim_id: global_prim_id,
                     prim_address: prim_address,
-                    clip_address: clip_address,
                     sub_index: 0,
                     user_data: [0, 0],
                 });
             }
             &mut PrimitiveBatchData::TextRun(ref mut data) => {
                 for glyph_index in 0..metadata.gpu_data_count {
                     data.push(PrimitiveInstance {
-                        task_id: task_id,
+                        task_index: task_index,
+                        clip_task_index: clip_task_index,
                         layer_index: layer_index,
                         global_prim_id: global_prim_id,
                         prim_address: prim_address,
-                        clip_address: clip_address,
                         sub_index: metadata.gpu_data_address.0 + glyph_index,
                         user_data: [ 0, 0 ],
                     });
                 }
             }
             &mut PrimitiveBatchData::Image(ref mut data) => {
                 data.push(PrimitiveInstance {
-                    task_id: task_id,
+                    task_index: task_index,
+                    clip_task_index: clip_task_index,
                     layer_index: layer_index,
                     global_prim_id: global_prim_id,
                     prim_address: prim_address,
-                    clip_address: clip_address,
                     sub_index: 0,
                     user_data: [ 0, 0 ],
                 });
             }
             &mut PrimitiveBatchData::Borders(ref mut data) => {
                 for border_segment in 0..8 {
                     data.push(PrimitiveInstance {
-                        task_id: task_id,
+                        task_index: task_index,
+                        clip_task_index: clip_task_index,
                         layer_index: layer_index,
                         global_prim_id: global_prim_id,
                         prim_address: prim_address,
-                        clip_address: clip_address,
                         sub_index: border_segment,
                         user_data: [ 0, 0 ],
                     });
                 }
             }
             &mut PrimitiveBatchData::AlignedGradient(ref mut data) => {
                 for part_index in 0..(metadata.gpu_data_count - 1) {
                     data.push(PrimitiveInstance {
-                        task_id: task_id,
+                        task_index: task_index,
+                        clip_task_index: clip_task_index,
                         layer_index: layer_index,
                         global_prim_id: global_prim_id,
                         prim_address: prim_address,
-                        clip_address: clip_address,
                         sub_index: metadata.gpu_data_address.0 + part_index,
                         user_data: [ 0, 0 ],
                     });
                 }
             }
             &mut PrimitiveBatchData::AngleGradient(ref mut data) => {
                 data.push(PrimitiveInstance {
-                    task_id: task_id,
+                    task_index: task_index,
+                    clip_task_index: clip_task_index,
                     layer_index: layer_index,
                     global_prim_id: global_prim_id,
                     prim_address: prim_address,
-                    clip_address: clip_address,
                     sub_index: metadata.gpu_data_address.0,
                     user_data: [ metadata.gpu_data_count, 0 ],
                 });
             }
             &mut PrimitiveBatchData::CacheImage(ref mut data) => {
                 // Find the render task index for the render task
                 // that this primitive depends on. Pass it to the
                 // shader so that it can sample from the cache texture
                 // at the correct location.
                 let cache_task_id = &metadata.render_task.as_ref().unwrap().id;
                 let cache_task_index = render_tasks.get_task_index(cache_task_id,
                                                                    child_pass_index);
 
                 data.push(PrimitiveInstance {
-                    task_id: task_id,
+                    task_index: task_index,
+                    clip_task_index: clip_task_index,
                     layer_index: layer_index,
                     global_prim_id: global_prim_id,
                     prim_address: prim_address,
-                    clip_address: clip_address,
                     sub_index: 0,
                     user_data: [ cache_task_index.0 as i32, 0 ],
                 });
             }
             &mut PrimitiveBatchData::BoxShadow(ref mut data) => {
                 let cache_task_id = &metadata.render_task.as_ref().unwrap().id;
                 let cache_task_index = render_tasks.get_task_index(cache_task_id,
                                                                    child_pass_index);
 
                 for rect_index in 0..metadata.gpu_data_count {
                     data.push(PrimitiveInstance {
-                        task_id: task_id,
+                        task_index: task_index,
+                        clip_task_index: clip_task_index,
                         layer_index: layer_index,
                         global_prim_id: global_prim_id,
                         prim_address: prim_address,
-                        clip_address: clip_address,
                         sub_index: metadata.gpu_data_address.0 + rect_index,
                         user_data: [ cache_task_index.0 as i32, 0 ],
                     });
                 }
             }
         }
     }
 }
@@ -327,75 +343,92 @@ pub struct RenderTargetIndex(usize);
 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
 struct RenderPassIndex(isize);
 
 #[derive(Debug, Copy, Clone)]
 pub struct RenderTaskIndex(usize);
 
 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
 pub enum RenderTaskKey {
-    // Draw this primitive to a cache target.
+    /// Draw this primitive to a cache target.
     CachePrimitive(PrimitiveCacheKey),
-    // Apply a vertical blur pass of given radius for this primitive.
+    /// Draw the tile alpha mask for a primitive.
+    CacheMask(MaskCacheKey),
+    /// Apply a vertical blur pass of given radius for this primitive.
     VerticalBlur(i32, PrimitiveIndex),
-    // Apply a horizontal blur pass of given radius for this primitive.
+    /// Apply a horizontal blur pass of given radius for this primitive.
     HorizontalBlur(i32, PrimitiveIndex),
 }
 
 #[derive(Debug, Copy, Clone)]
 pub enum RenderTaskId {
     Static(RenderTaskIndex),
     Dynamic(RenderTaskKey),
 }
 
+struct DynamicTaskInfo {
+    index: RenderTaskIndex,
+    rect: DeviceRect,
+}
+
 struct RenderTaskCollection {
     render_task_data: Vec<RenderTaskData>,
-    dynamic_tasks: HashMap<(RenderTaskKey, RenderPassIndex), RenderTaskIndex, BuildHasherDefault<FnvHasher>>,
+    dynamic_tasks: HashMap<(RenderTaskKey, RenderPassIndex), DynamicTaskInfo, BuildHasherDefault<FnvHasher>>,
 }
 
 impl RenderTaskCollection {
     fn new(static_render_task_count: usize) -> RenderTaskCollection {
         RenderTaskCollection {
             render_task_data: vec![RenderTaskData::empty(); static_render_task_count],
             dynamic_tasks: HashMap::with_hasher(Default::default()),
         }
     }
 
-    fn add(&mut self, task: &RenderTask, pass: RenderPassIndex) {
+    fn add(&mut self, task: &RenderTask, pass: RenderPassIndex) -> RenderTaskIndex {
         match task.id {
             RenderTaskId::Static(index) => {
                 self.render_task_data[index.0] = task.write_task_data();
+                index
             }
             RenderTaskId::Dynamic(key) => {
                 let index = RenderTaskIndex(self.render_task_data.len());
                 let key = (key, pass);
                 debug_assert!(self.dynamic_tasks.contains_key(&key) == false);
-                self.dynamic_tasks.insert(key, index);
+                self.dynamic_tasks.insert(key, DynamicTaskInfo {
+                    index: index,
+                    rect: match task.location {
+                        RenderTaskLocation::Fixed(rect) => rect,
+                        RenderTaskLocation::Dynamic(Some((origin, _)), size) => DeviceRect::new(origin, size),
+                        RenderTaskLocation::Dynamic(None, _) => panic!("Expect the task to be already allocated here"),
+                    },
+                });
                 self.render_task_data.push(task.write_task_data());
+                index
             }
         }
     }
 
-    fn task_exists(&self, pass_index: RenderPassIndex, key: RenderTaskKey) -> bool {
+    fn get_dynamic_allocation(&self, pass_index: RenderPassIndex, key: RenderTaskKey) -> Option<&DeviceRect> {
         let key = (key, pass_index);
-        self.dynamic_tasks.contains_key(&key)
+        self.dynamic_tasks.get(&key)
+                          .map(|task| &task.rect)
     }
 
     fn get_static_task_index(&self, id: &RenderTaskId) -> RenderTaskIndex {
         match id {
             &RenderTaskId::Static(index) => index,
             &RenderTaskId::Dynamic(..) => panic!("This is a bug - expected a static render task!"),
         }
     }
 
     fn get_task_index(&self, id: &RenderTaskId, pass_index: RenderPassIndex) -> RenderTaskIndex {
         match id {
             &RenderTaskId::Static(index) => index,
             &RenderTaskId::Dynamic(key) => {
-                self.dynamic_tasks[&(key, pass_index)]
+                self.dynamic_tasks[&(key, pass_index)].index
             }
         }
     }
 }
 
 #[derive(Debug, Clone)]
 pub struct RenderTaskData {
     data: [f32; FLOATS_PER_RENDER_TASK_INFO],
@@ -450,57 +483,58 @@ impl AlphaBatcher {
     }
 
     fn build(&mut self,
              ctx: &RenderTargetContext,
              render_tasks: &RenderTaskCollection,
              child_pass_index: RenderPassIndex) {
         let mut batches: Vec<PrimitiveBatch> = vec![];
         for task in &mut self.tasks {
-            let task_index = render_tasks.get_static_task_index(&task.task_id);
-            let task_index = task_index.0 as i32;
+            let task_index = render_tasks.get_static_task_index(&task.task_id).0 as i32;
 
             let mut existing_batch_index = 0;
             for item in task.items.drain(..) {
-                let batch_key;
-                match item {
+                let batch_key = match item {
                     AlphaRenderItem::Composite(..) => {
-                        batch_key = AlphaBatchKey::composite();
+                        AlphaBatchKey::composite()
                     }
                     AlphaRenderItem::Blend(..) => {
-                        batch_key = AlphaBatchKey::blend();
+                        AlphaBatchKey::blend()
                     }
                     AlphaRenderItem::Primitive(sc_index, prim_index) => {
                         // See if this task fits into the tile UBO
                         let layer = &ctx.layer_store[sc_index.0];
                         let prim_metadata = ctx.prim_store.get_metadata(prim_index);
                         let transform_kind = layer.xf_rect.as_ref().unwrap().kind;
-                        let needs_clipping = prim_metadata.clip_index.is_some();
+                        let needs_clipping = prim_metadata.clip_cache_info.is_some();
                         let needs_blending = transform_kind == TransformedRectKind::Complex ||
                                              !prim_metadata.is_opaque ||
                                              needs_clipping;
                         let blend_mode = ctx.prim_store.get_blend_mode(needs_blending, prim_metadata);
                         let needs_clipping_flag = if needs_clipping {
                             NEEDS_CLIPPING
                         } else {
                             AlphaBatchKeyFlags::empty()
                         };
                         let flags = match transform_kind {
                             TransformedRectKind::AxisAligned => AXIS_ALIGNED | needs_clipping_flag,
                             _ => needs_clipping_flag,
                         };
                         let batch_kind = ctx.prim_store.get_batch_kind(prim_metadata);
-                        let color_texture_id = ctx.prim_store.get_texture_id(prim_metadata);
-                        batch_key = AlphaBatchKey::primitive(batch_kind,
-                                                             flags,
-                                                             blend_mode,
-                                                             color_texture_id,
-                                                             prim_metadata.mask_texture_id);
+
+                        let textures = BatchTextures {
+                            colors: ctx.prim_store.get_color_textures(prim_metadata),
+                        };
+
+                        AlphaBatchKey::primitive(batch_kind,
+                                                 flags,
+                                                 blend_mode,
+                                                 textures)
                     }
-                }
+                };
 
                 while existing_batch_index < batches.len() &&
                         !batches[existing_batch_index].key.is_compatible_with(&batch_key) {
                     existing_batch_index += 1
                 }
 
                 if existing_batch_index == batches.len() {
                     let new_batch = match item {
@@ -546,57 +580,113 @@ impl AlphaBatcher {
                 }
             }
         }
 
         self.batches.extend(batches.into_iter())
     }
 }
 
+#[derive(Debug)]
+pub struct ClipBatcher {
+    pub clears: Vec<CacheClipInstance>,
+    pub rectangles: Vec<CacheClipInstance>,
+    pub images: HashMap<SourceTexture, Vec<CacheClipInstance>>,
+}
+
+impl ClipBatcher {
+    fn new() -> ClipBatcher {
+        ClipBatcher {
+            clears: Vec::new(),
+            rectangles: Vec::new(),
+            images: HashMap::new(),
+        }
+    }
+
+    fn add(&mut self,
+           task_index: i32,
+           key: &MaskCacheKey,
+           task_info: &CacheMaskTask,
+           resource_cache: &ResourceCache) {
+        // TODO: don't draw clipping instances covering the whole tile
+        self.clears.push(CacheClipInstance {
+            task_id: task_index,
+            layer_index: key.layer_id.0 as i32,
+            address: GpuStoreAddress(0),
+            pad: 0,
+        });
+        self.rectangles.extend((0 .. key.clip_range.item_count as usize)
+                       .map(|region_id| {
+            CacheClipInstance {
+                task_id: task_index,
+                layer_index: key.layer_id.0 as i32,
+                address: GpuStoreAddress(key.clip_range.start.0 + ((CLIP_DATA_GPU_SIZE * region_id) as i32)),
+                pad: 0,
+            }
+        }));
+        if let (Some(address), Some(mask_key)) = (key.image, task_info.image) {
+            let cache_item = resource_cache.get_image(mask_key, ImageRendering::Auto);
+            self.images.entry(cache_item.texture_id)
+                        .or_insert(Vec::new())
+                        .push(CacheClipInstance {
+                task_id: task_index,
+                layer_index: key.layer_id.0 as i32,
+                address: address,
+                pad: 0,
+            })
+        }
+    }
+}
+
+
 struct CompileTileContext<'a> {
     layer_store: &'a [StackingContext],
     prim_store: &'a PrimitiveStore,
     render_task_id_counter: AtomicUsize,
 }
 
 struct RenderTargetContext<'a> {
     layer_store: &'a [StackingContext],
     prim_store: &'a PrimitiveStore,
+    resource_cache: &'a ResourceCache,
 }
 
 /// A render target represents a number of rendering operations on a surface.
 pub struct RenderTarget {
     pub alpha_batcher: AlphaBatcher,
+    pub clip_batcher: ClipBatcher,
     pub box_shadow_cache_prims: Vec<CachePrimitiveInstance>,
     // List of text runs to be cached to this render target.
     // TODO(gw): For now, assume that these all come from
     //           the same source texture id. This is almost
     //           always true except for pathological test
     //           cases with more than 4k x 4k of unique
     //           glyphs visible. Once the future glyph / texture
     //           cache changes land, this restriction will
     //           be removed anyway.
     pub text_run_cache_prims: Vec<CachePrimitiveInstance>,
-    pub text_run_color_texture_id: TextureId,
+    pub text_run_textures: BatchTextures,
     // List of blur operations to apply for this render target.
     pub vertical_blurs: Vec<BlurCommand>,
     pub horizontal_blurs: Vec<BlurCommand>,
     page_allocator: TexturePage,
 }
 
 impl RenderTarget {
     fn new() -> RenderTarget {
         RenderTarget {
             alpha_batcher: AlphaBatcher::new(),
+            clip_batcher: ClipBatcher::new(),
             box_shadow_cache_prims: Vec::new(),
             text_run_cache_prims: Vec::new(),
-            text_run_color_texture_id: TextureId::invalid(),
+            text_run_textures: BatchTextures::no_texture(),
             vertical_blurs: Vec::new(),
             horizontal_blurs: Vec::new(),
-            page_allocator: TexturePage::new(TextureId::invalid(), RENDERABLE_CACHE_SIZE as u32),
+            page_allocator: TexturePage::new(CacheTextureId(0),
+                                             RENDERABLE_CACHE_SIZE as u32),
         }
     }
 
     fn build(&mut self,
              ctx: &RenderTargetContext,
              render_tasks: &mut RenderTaskCollection,
              child_pass_index: RenderPassIndex) {
         self.alpha_batcher.build(ctx,
@@ -659,37 +749,48 @@ impl RenderTarget {
                     PrimitiveKind::TextRun => {
                         let text = &ctx.prim_store.cpu_text_runs[prim_metadata.cpu_prim_index.0];
                         // We only cache text runs with a text-shadow (for now).
                         debug_assert!(text.blur_radius.0 != 0);
 
                         // TODO(gw): This should always be fine for now, since the texture
                         // atlas grows to 4k. However, it won't be a problem soon, once
                         // we switch the texture atlas to use texture layers!
-                        let color_texture_id = ctx.prim_store.get_texture_id(prim_metadata);
-                        debug_assert!(color_texture_id != TextureId::invalid());
-                        debug_assert!(self.text_run_color_texture_id == TextureId::invalid() ||
-                                      self.text_run_color_texture_id == color_texture_id);
-                        self.text_run_color_texture_id = color_texture_id;
+                        let textures = BatchTextures {
+                            colors: ctx.prim_store.get_color_textures(prim_metadata),
+                        };
+
+                        debug_assert!(textures.colors[0] != SourceTexture::Invalid);
+                        debug_assert!(self.text_run_textures.colors[0] == SourceTexture::Invalid ||
+                                      self.text_run_textures.colors[0] == textures.colors[0]);
+                        self.text_run_textures = textures;
 
                         for glyph_index in 0..prim_metadata.gpu_data_count {
                             self.text_run_cache_prims.push(CachePrimitiveInstance {
                                 task_id: render_tasks.get_task_index(&task.id, pass_index).0 as i32,
                                 global_prim_id: prim_index.0 as i32,
                                 prim_address: prim_metadata.gpu_prim_index,
                                 sub_index: prim_metadata.gpu_data_address.0 + glyph_index,
                             });
                         }
                     }
                     _ => {
                         // No other primitives make use of primitive caching yet!
                         unreachable!()
                     }
                 }
             }
+            RenderTaskKind::CacheMask(ref task_info) => {
+                let key = match task.id {
+                    RenderTaskId::Dynamic(RenderTaskKey::CacheMask(ref key)) => key,
+                    _ => unreachable!()
+                };
+                let task_index = render_tasks.get_task_index(&task.id, pass_index).0 as i32;
+                self.clip_batcher.add(task_index, key, task_info, ctx.resource_cache);
+            }
         }
     }
 }
 
 /// A render pass represents a set of rendering operations that don't depend on one
 /// another.
 ///
 /// A render pass can have several render targets if there wasn't enough space in one
@@ -702,17 +803,17 @@ pub struct RenderPass {
 }
 
 impl RenderPass {
     fn new(pass_index: isize, is_framebuffer: bool) -> RenderPass {
         RenderPass {
             pass_index: RenderPassIndex(pass_index),
             is_framebuffer: is_framebuffer,
             targets: vec![ RenderTarget::new() ],
-            tasks: Vec::new(),
+            tasks: vec![],
         }
     }
 
     fn add_render_task(&mut self, task: RenderTask) {
         self.tasks.push(task);
     }
 
     fn allocate_target(targets: &mut Vec<RenderTarget>, size: Size2D<u32>) -> Point2D<u32> {
@@ -738,27 +839,27 @@ impl RenderPass {
              render_tasks: &mut RenderTaskCollection) {
         // Step through each task, adding to batches as appropriate.
         for mut task in self.tasks.drain(..) {
             // Find a target to assign this task to, or create a new
             // one if required.
             match task.location {
                 RenderTaskLocation::Fixed(..) => {}
                 RenderTaskLocation::Dynamic(ref mut origin, ref size) => {
-
                     // See if this task is a duplicate from another tile.
                     // If so, just skip adding it!
                     match task.id {
                         RenderTaskId::Static(..) => {}
                         RenderTaskId::Dynamic(key) => {
                             // Look up cache primitive key in the render
                             // task data array. If a matching key exists
                             // (that is in this pass) there is no need
                             // to draw it again!
-                            if render_tasks.task_exists(self.pass_index, key) {
+                            if let Some(rect) = render_tasks.get_dynamic_allocation(self.pass_index, key) {
+                                debug_assert_eq!(rect.size, *size);
                                 continue;
                             }
                         }
                     }
 
                     let alloc_size = Size2D::new(size.width as u32,
                                                  size.height as u32);
                     let alloc_origin = Self::allocate_target(&mut self.targets, alloc_size);
@@ -798,19 +899,26 @@ enum AlphaRenderItem {
 
 #[derive(Debug, Clone)]
 pub struct AlphaRenderTask {
     actual_rect: DeviceRect,
     items: Vec<AlphaRenderItem>,
 }
 
 #[derive(Debug, Clone)]
+pub struct CacheMaskTask {
+    actual_rect: DeviceRect,
+    image: Option<ImageKey>,
+}
+
+#[derive(Debug, Clone)]
 pub enum RenderTaskKind {
     Alpha(AlphaRenderTask),
     CachePrimitive(PrimitiveIndex),
+    CacheMask(CacheMaskTask),
     VerticalBlur(DeviceLength, PrimitiveIndex),
     HorizontalBlur(DeviceLength, PrimitiveIndex),
 }
 
 // TODO(gw): Consider storing these in a separate array and having
 //           primitives hold indices - this could avoid cloning
 //           when adding them as child tasks to tiles.
 #[derive(Debug, Clone)]
@@ -832,26 +940,43 @@ impl RenderTask {
             kind: RenderTaskKind::Alpha(AlphaRenderTask {
                 actual_rect: actual_rect,
                 items: Vec::new(),
             }),
         }
     }
 
     pub fn new_prim_cache(key: PrimitiveCacheKey,
-                      size: DeviceSize,
-                      prim_index: PrimitiveIndex) -> RenderTask {
+                          size: DeviceSize,
+                          prim_index: PrimitiveIndex) -> RenderTask {
         RenderTask {
             id: RenderTaskId::Dynamic(RenderTaskKey::CachePrimitive(key)),
             children: Vec::new(),
             location: RenderTaskLocation::Dynamic(None, size),
             kind: RenderTaskKind::CachePrimitive(prim_index),
         }
     }
 
+    fn new_mask(actual_rect: DeviceRect, cache_info: &MaskCacheInfo) -> Option<RenderTask> {
+        //CLIP TODO: handle a case where the tile is completely inside the intersection
+        if !actual_rect.intersects(&cache_info.device_rect) {
+            return None
+        };
+        let task_rect = cache_info.device_rect;
+        Some(RenderTask {
+            id: RenderTaskId::Dynamic(RenderTaskKey::CacheMask(cache_info.key)),
+            children: Vec::new(),
+            location: RenderTaskLocation::Dynamic(None, task_rect.size),
+            kind: RenderTaskKind::CacheMask(CacheMaskTask {
+                actual_rect: task_rect,
+                image: cache_info.image.map(|mask| mask.image),
+            }),
+        })
+    }
+
     // Construct a render task to apply a blur to a primitive. For now,
     // this is only used for text runs, but we can probably extend this
     // to handle general blurs to any render task in the future.
     // The render task chain that is constructed looks like:
     //
     //    PrimitiveCacheTask: Draw the text run.
     //           ^
     //           |
@@ -889,64 +1014,74 @@ impl RenderTask {
 
         blur_task_h
     }
 
     fn as_alpha_batch<'a>(&'a mut self) -> &'a mut AlphaRenderTask {
         match self.kind {
             RenderTaskKind::Alpha(ref mut task) => task,
             RenderTaskKind::CachePrimitive(..) |
+            RenderTaskKind::CacheMask(..) |
             RenderTaskKind::VerticalBlur(..) |
             RenderTaskKind::HorizontalBlur(..) => unreachable!(),
         }
     }
 
     // Write (up to) 8 floats of data specific to the type
     // of render task that is provided to the GPU shaders
     // via a vertex texture.
     fn write_task_data(&self) -> RenderTaskData {
+        let (target_rect, target_index) = self.get_target_rect();
         match self.kind {
             RenderTaskKind::Alpha(ref task) => {
-                let (target_rect, target_index) = self.get_target_rect();
-                debug_assert!(target_rect.size.width == task.actual_rect.size.width);
-                debug_assert!(target_rect.size.height == task.actual_rect.size.height);
-
+                debug_assert_eq!(target_rect.size, task.actual_rect.size);
                 RenderTaskData {
                     data: [
                         task.actual_rect.origin.x as f32,
                         task.actual_rect.origin.y as f32,
                         target_rect.origin.x as f32,
                         target_rect.origin.y as f32,
                         task.actual_rect.size.width as f32,
                         task.actual_rect.size.height as f32,
                         target_index.0 as f32,
                         0.0,
                     ],
                 }
             }
             RenderTaskKind::CachePrimitive(..) => {
-                let (target_rect, target_index) = self.get_target_rect();
-
                 RenderTaskData {
                     data: [
                         target_rect.origin.x as f32,
                         target_rect.origin.y as f32,
                         target_rect.size.width as f32,
                         target_rect.size.height as f32,
                         target_index.0 as f32,
                         0.0,
                         0.0,
                         0.0,
                     ],
                 }
             }
+            RenderTaskKind::CacheMask(ref task) => {
+                debug_assert_eq!(target_rect.size, task.actual_rect.size);
+                RenderTaskData {
+                    data: [
+                        target_rect.origin.x as f32,
+                        target_rect.origin.y as f32,
+                        (target_rect.origin.x + target_rect.size.width) as f32,
+                        (target_rect.origin.y + target_rect.size.height) as f32,
+                        task.actual_rect.origin.x as f32,
+                        task.actual_rect.origin.y as f32,
+                        target_index.0 as f32,
+                        0.0,
+                    ],
+                }
+            }
             RenderTaskKind::VerticalBlur(blur_radius, _) |
             RenderTaskKind::HorizontalBlur(blur_radius, _) => {
-                let (target_rect, target_index) = self.get_target_rect();
-
                 RenderTaskData {
                     data: [
                         target_rect.origin.x as f32,
                         target_rect.origin.y as f32,
                         target_rect.size.width as f32,
                         target_rect.size.height as f32,
                         target_index.0 as f32,
                         blur_radius.0 as f32,
@@ -1047,74 +1182,73 @@ impl AlphaBatchKeyFlags {
     }
 }
 
 #[derive(Copy, Clone, Debug)]
 pub struct AlphaBatchKey {
     kind: AlphaBatchKind,
     pub flags: AlphaBatchKeyFlags,
     pub blend_mode: BlendMode,
-    pub color_texture_id: TextureId,
-    pub mask_texture_id: TextureId,
+    pub textures: BatchTextures,
 }
 
 impl AlphaBatchKey {
     fn blend() -> AlphaBatchKey {
         AlphaBatchKey {
             kind: AlphaBatchKind::Blend,
             flags: AXIS_ALIGNED,
             blend_mode: BlendMode::Alpha,
-            color_texture_id: TextureId::invalid(),
-            mask_texture_id: TextureId::invalid(),
+            textures: BatchTextures::no_texture(),
         }
     }
 
     fn composite() -> AlphaBatchKey {
         AlphaBatchKey {
             kind: AlphaBatchKind::Composite,
             flags: AXIS_ALIGNED,
             blend_mode: BlendMode::Alpha,
-            color_texture_id: TextureId::invalid(),
-            mask_texture_id: TextureId::invalid(),
+            textures: BatchTextures::no_texture(),
         }
     }
 
     fn primitive(kind: AlphaBatchKind,
                  flags: AlphaBatchKeyFlags,
                  blend_mode: BlendMode,
-                 color_texture_id: TextureId,
-                 mask_texture_id: TextureId)
+                 textures: BatchTextures)
                  -> AlphaBatchKey {
         AlphaBatchKey {
             kind: kind,
             flags: flags,
             blend_mode: blend_mode,
-            color_texture_id: color_texture_id,
-            mask_texture_id: mask_texture_id,
+            textures: textures,
         }
     }
 
     fn is_compatible_with(&self, other: &AlphaBatchKey) -> bool {
         self.kind == other.kind &&
             self.flags == other.flags &&
             self.blend_mode == other.blend_mode &&
-        (self.color_texture_id == TextureId::invalid() || other.color_texture_id == TextureId::invalid() ||
-             self.color_texture_id == other.color_texture_id) &&
-            (self.mask_texture_id == TextureId::invalid() || other.mask_texture_id == TextureId::invalid() ||
-             self.mask_texture_id == other.mask_texture_id)
+            textures_compatible(self.textures.colors[0], other.textures.colors[0]) &&
+            textures_compatible(self.textures.colors[1], other.textures.colors[1]) &&
+            textures_compatible(self.textures.colors[2], other.textures.colors[2])
     }
 }
 
 #[repr(C)]
 #[derive(Debug)]
 pub enum BlurDirection {
     Horizontal = 0,
     Vertical,
 }
 
+#[inline]
+fn textures_compatible(t1: SourceTexture, t2: SourceTexture) -> bool {
+    t1 == SourceTexture::Invalid || t2 == SourceTexture::Invalid || t1 == t2
+}
+
 // All Packed Primitives below must be 16 byte aligned.
 #[derive(Debug)]
 pub struct BlurCommand {
     task_id: i32,
     src_task_id: i32,
     blur_direction: i32,
     padding: i32,
 }
@@ -1122,23 +1256,34 @@ pub struct BlurCommand {
 #[derive(Debug)]
 pub struct CachePrimitiveInstance {
     global_prim_id: i32,
     prim_address: GpuStoreAddress,
     task_id: i32,
     sub_index: i32,
 }
 
+/// A clipping primitive drawn into the clipping mask.
+/// Could be an image or a rectangle, which defines the
+/// way `address` is treated.
+#[derive(Debug)]
+pub struct CacheClipInstance {
+    task_id: i32,
+    layer_index: i32,
+    address: GpuStoreAddress,
+    pad: i32,
+}
+
 #[derive(Debug, Clone)]
 pub struct PrimitiveInstance {
     global_prim_id: i32,
     prim_address: GpuStoreAddress,
-    task_id: i32,
+    task_index: i32,
+    clip_task_index: i32,
     layer_index: i32,
-    clip_address: GpuStoreAddress,
     sub_index: i32,
     user_data: [i32; 2],
 }
 
 #[derive(Debug, Clone)]
 pub struct PackedBlendPrimitive {
     src_task_id: i32,
     target_task_id: i32,
@@ -1376,17 +1521,16 @@ impl FrameBuilderConfig {
     }
 }
 
 pub struct FrameBuilder {
     screen_rect: Rect<i32>,
     prim_store: PrimitiveStore,
     cmds: Vec<PrimitiveRunCmd>,
     device_pixel_ratio: f32,
-    dummy_resources: DummyResources,
     debug: bool,
     config: FrameBuilderConfig,
 
     layer_store: Vec<StackingContext>,
     packed_layers: Vec<PackedStackingContext>,
 
     scrollbar_prims: Vec<ScrollbarPrimitive>,
 }
@@ -1571,23 +1715,30 @@ impl ScreenTile {
                     if self.is_simple {
                         let layer = &ctx.layer_store[sc_index.0];
 
                         let prim_bounding_rect = ctx.prim_store.get_bounding_rect(prim_index);
 
                         // If an opaque primitive covers a tile entirely, we can discard
                         // all primitives underneath it.
                         if layer.xf_rect.as_ref().unwrap().kind == TransformedRectKind::AxisAligned &&
-                           prim_metadata.clip_index.is_none() &&
+                           prim_metadata.clip_cache_info.is_none() &&
                            prim_metadata.is_opaque &&
                            prim_bounding_rect.as_ref().unwrap().contains_rect(&self.rect) {
                             current_task.as_alpha_batch().items.clear();
                         }
                     }
 
+                    // Add a task to render the updated image mask
+                    if let Some(ref clip_info) = prim_metadata.clip_cache_info {
+                        let mask_task = RenderTask::new_mask(self.rect, clip_info)
+                                                   .expect("Primitive be culled by `prim_affects_tile` already");
+                        current_task.children.push(mask_task);
+                    }
+
                     // Add any dynamic render tasks needed to render this primitive
                     if let Some(ref render_task) = prim_metadata.render_task {
                         current_task.children.push(render_task.clone());
                     }
 
                     actual_prim_count += 1;
                     current_task.as_alpha_batch().items.push(AlphaRenderItem::Primitive(sc_index, prim_index));
                 }
@@ -1605,40 +1756,53 @@ impl ScreenTile {
         current_task.location = RenderTaskLocation::Fixed(self.rect);
         Some(CompiledScreenTile::new(current_task, info))
     }
 }
 
 impl FrameBuilder {
     pub fn new(viewport_size: Size2D<f32>,
                device_pixel_ratio: f32,
-               dummy_resources: DummyResources,
                debug: bool,
                config: FrameBuilderConfig) -> FrameBuilder {
         let viewport_size = Size2D::new(viewport_size.width as i32, viewport_size.height as i32);
         FrameBuilder {
             screen_rect: Rect::new(Point2D::zero(), viewport_size),
             layer_store: Vec::new(),
             prim_store: PrimitiveStore::new(device_pixel_ratio),
             cmds: Vec::new(),
             device_pixel_ratio: device_pixel_ratio,
-            dummy_resources: dummy_resources,
             debug: debug,
             packed_layers: Vec::new(),
             scrollbar_prims: Vec::new(),
             config: config,
         }
     }
 
     fn add_primitive(&mut self,
                      rect: &Rect<f32>,
                      clip_region: &ClipRegion,
                      container: PrimitiveContainer) -> PrimitiveIndex {
-        let prim_index = self.prim_store.add_primitive(rect,
-                                                       clip_region,
+
+        let geometry = PrimitiveGeometry {
+            local_rect: *rect,
+            local_clip_rect: clip_region.main,
+        };
+        let clip_source = if clip_region.is_complex() {
+            PrimitiveClipSource::Region(clip_region.clone())
+        } else {
+            PrimitiveClipSource::NoClip
+        };
+        let clip_info = MaskCacheInfo::new(&clip_source,
+                                           StackingContextIndex(self.layer_store.len() - 1),
+                                           &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;
             }
@@ -1893,17 +2057,17 @@ impl FrameBuilder {
 
             let prim_cpu = TextRunPrimitiveCpu {
                 font_key: font_key,
                 font_size: size,
                 blur_radius: blur_radius,
                 glyph_range: sub_range,
                 cache_dirty: true,
                 glyph_indices: Vec::new(),
-                color_texture_id: TextureId::invalid(),
+                color_texture_id: SourceTexture::Invalid,
                 color: *color,
                 render_mode: render_mode,
             };
 
             let prim_gpu = TextRunPrimitiveGpu {
                 color: *color,
             };
 
@@ -1979,17 +2143,17 @@ impl FrameBuilder {
     }
 
     pub fn add_webgl_rectangle(&mut self,
                                rect: Rect<f32>,
                                clip_region: &ClipRegion,
                                context_id: WebGLContextId) {
         let prim_cpu = ImagePrimitiveCpu {
             kind: ImagePrimitiveKind::WebGL(context_id),
-            color_texture_id: TextureId::invalid(),
+            color_texture_id: SourceTexture::Invalid,
         };
 
         let prim_gpu = ImagePrimitiveGpu {
             uv0: Point2D::zero(),
             uv1: Point2D::zero(),
             stretch_size: rect.size,
             tile_spacing: Size2D::zero(),
         };
@@ -2005,17 +2169,17 @@ impl FrameBuilder {
                      stretch_size: &Size2D<f32>,
                      tile_spacing: &Size2D<f32>,
                      image_key: ImageKey,
                      image_rendering: ImageRendering) {
         let prim_cpu = ImagePrimitiveCpu {
             kind: ImagePrimitiveKind::Image(image_key,
                                             image_rendering,
                                             *tile_spacing),
-            color_texture_id: TextureId::invalid(),
+            color_texture_id: SourceTexture::Invalid,
         };
 
         let prim_gpu = ImagePrimitiveGpu {
             uv0: Point2D::zero(),
             uv1: Point2D::zero(),
             stretch_size: *stretch_size,
             tile_spacing: *tile_spacing,
         };
@@ -2035,40 +2199,36 @@ impl FrameBuilder {
                    y_tile_count: i32,
                    resource_cache: &mut ResourceCache,
                    profile_counters: &mut FrameProfileCounters) {
         // Build layer screen rects.
         // TODO(gw): This can be done earlier once update_layer_transforms() is fixed.
 
         // TODO(gw): Remove this stack once the layers refactor is done!
         let mut layer_stack: Vec<StackingContextIndex> = Vec::new();
-        let dummy_mask_cache_item = {
-            let opaque_mask_id = self.dummy_resources.opaque_mask_image_id;
-            resource_cache.get_image_by_cache_id(opaque_mask_id).clone()
-        };
 
         for cmd in &self.cmds {
             match cmd {
                 &PrimitiveRunCmd::PushStackingContext(sc_index) => {
                     layer_stack.push(sc_index);
                     let layer = &mut self.layer_store[sc_index.0];
                     let packed_layer = &mut self.packed_layers[sc_index.0];
 
                     layer.xf_rect = None;
                     layer.tile_range = None;
 
-                    if !layer.can_contribute_to_scene() {
-                        continue;
-                    }
-
                     let scroll_layer = &layer_map[&layer.scroll_layer_id];
                     packed_layer.transform = scroll_layer.world_content_transform
                                                          .pre_mul(&layer.local_transform);
                     packed_layer.inv_transform = packed_layer.transform.inverse().unwrap();
 
+                    if !layer.can_contribute_to_scene() {
+                        continue;
+                    }
+
                     let inv_layer_transform = layer.local_transform.inverse().unwrap();
                     let local_viewport_rect = scroll_layer.combined_local_viewport_rect;
                     let viewport_rect = inv_layer_transform.transform_rect(&local_viewport_rect);
                     let layer_local_rect = layer.local_rect
                                                 .intersection(&viewport_rect)
                                                 .and_then(|rect| rect.intersection(&layer.local_clip_rect));
 
                     if let Some(layer_local_rect) = layer_local_rect {
@@ -2124,18 +2284,19 @@ impl FrameBuilder {
                                                                screen_rect,
                                                                &packed_layer.transform,
                                                                &packed_layer.local_clip_rect,
                                                                self.device_pixel_ratio) {
                             profile_counters.visible_primitives.inc();
 
                             if self.prim_store.prepare_prim_for_render(prim_index,
                                                                        resource_cache,
+                                                                       &packed_layer.transform,
+                                                                       &packed_layer.local_clip_rect,
                                                                        self.device_pixel_ratio,
-                                                                       &dummy_mask_cache_item,
                                                                        auxiliary_lists) {
                                 self.prim_store.build_bounding_rect(prim_index,
                                                                     screen_rect,
                                                                     &packed_layer.transform,
                                                                     &packed_layer.local_clip_rect,
                                                                     self.device_pixel_ratio);
                             }
                         }
@@ -2404,16 +2565,17 @@ impl FrameBuilder {
         self.prim_store.resolve_primitives(resource_cache);
 
         let mut passes = Vec::new();
 
         if !compiled_screen_tiles.is_empty() {
             let ctx = RenderTargetContext {
                 layer_store: &self.layer_store,
                 prim_store: &self.prim_store,
+                resource_cache: resource_cache,
             };
 
             // Do the allocations now, assigning each tile's tasks to a render
             // pass and target as required.
             for index in 0..max_passes_needed {
                 passes.push(RenderPass::new(index as isize,
                                             index == max_passes_needed-1));
             }
--- a/gfx/webrender_traits/Cargo.toml
+++ b/gfx/webrender_traits/Cargo.toml
@@ -1,11 +1,11 @@
 [package]
 name = "webrender_traits"
-version = "0.8.0"
+version = "0.9.0"
 authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
 license = "MPL-2.0"
 repository = "https://github.com/servo/webrender"
 build = "build.rs"
 workspace = ".."
 
 [features]
 default = ["codegen"]
@@ -16,17 +16,20 @@ codegen = ["serde_codegen", "serde_codeg
 app_units = "0.3.0"
 byteorder = "0.5"
 euclid = "0.10"
 gleam = "0.2.22"
 heapsize = "0.3.6"
 offscreen_gl_context = {version = "0.5.0", features = ["serde_serialization"]}
 serde = "0.8"
 serde_derive = {version = "0.8", optional = true}
-ipc-channel = "0.6"
+ipc-channel = "0.5.0"
 
-[target.x86_64-apple-darwin.dependencies]
+[target.'cfg(target_os = "macos")'.dependencies]
 core-graphics = "0.4"
 
+[target.'cfg(target_os = "windows")'.dependencies]
+dwrote = {git = "https://github.com/vvuk/dwrote-rs"}
+
 [build-dependencies.serde_codegen]
 version = "0.8"
 default_features = false
 optional = true
--- a/gfx/webrender_traits/src/api.rs
+++ b/gfx/webrender_traits/src/api.rs
@@ -2,20 +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 byteorder::{LittleEndian, WriteBytesExt};
 use euclid::{Point2D, Size2D};
 use ipc_channel::ipc::{self, IpcBytesSender, IpcSender};
 use offscreen_gl_context::{GLContextAttributes, GLLimits};
 use std::cell::Cell;
-use {ApiMsg, AuxiliaryLists, BuiltDisplayList, ColorF, DisplayListId, Epoch};
+use {ApiMsg, AuxiliaryLists, BuiltDisplayList, ColorF, Epoch};
 use {FontKey, IdNamespace, ImageFormat, ImageKey, NativeFontHandle, PipelineId};
-use {RenderApiSender, ResourceId, ScrollEventPhase, ScrollLayerState};
-use {StackingContext, StackingContextId, WebGLContextId, WebGLCommand};
+use {RenderApiSender, ResourceId, ScrollEventPhase, ScrollLayerState, WebGLContextId, WebGLCommand};
 use {GlyphKey, GlyphDimensions};
 
 impl RenderApiSender {
     pub fn new(api_sender: IpcSender<ApiMsg>,
                payload_sender: IpcBytesSender)
                -> RenderApiSender {
         RenderApiSender {
             api_sender: api_sender,
@@ -96,16 +95,25 @@ impl RenderApi {
                      bytes: Vec<u8>) -> ImageKey {
         let new_id = self.next_unique_id();
         let key = ImageKey::new(new_id.0, new_id.1);
         let msg = ApiMsg::AddImage(key, width, height, stride, format, bytes);
         self.api_sender.send(msg).unwrap();
         key
     }
 
+    /// Flushes all messages and will send a notification when the render
+    /// backend thread has finished processing all previous messages.
+    /// This is a temporary API And users should not expect this to be API
+    /// to exist in the future.
+    pub fn flush(&self) {
+        let msg = ApiMsg::Flush;
+        self.api_sender.send(msg).unwrap();
+    }
+
     /// Updates a specific image.
     ///
     /// Currently doesn't support changing dimensions or format by updating.
     // TODO: Support changing dimensions (and format) during image update?
     pub fn update_image(&self,
                         key: ImageKey,
                         width: u32,
                         height: u32,
@@ -152,48 +160,35 @@ impl RenderApi {
     /// * `epoch`: The unique Frame ID, monotonically increasing.
     /// * `pipeline_id`: The ID of the pipeline that is supplying this display list.
     /// * `viewport_size`: The size of the viewport for this frame.
     /// * `stacking_contexts`: Stacking contexts used in this frame.
     /// * `display_lists`: Display lists used in this frame.
     /// * `auxiliary_lists`: Various items that the display lists and stacking contexts reference.
     ///
     /// [notifier]: trait.RenderNotifier.html#tymethod.new_frame_ready
-    pub fn set_root_stacking_context(&self,
-                                     stacking_context_id: StackingContextId,
-                                     background_color: ColorF,
-                                     epoch: Epoch,
-                                     pipeline_id: PipelineId,
-                                     viewport_size: Size2D<f32>,
-                                     stacking_contexts: Vec<(StackingContextId, StackingContext)>,
-                                     display_lists: Vec<(DisplayListId, BuiltDisplayList)>,
-                                     auxiliary_lists: AuxiliaryLists) {
-        let display_list_descriptors = display_lists.iter().map(|&(display_list_id,
-                                                                   ref built_display_list)| {
-            (display_list_id, (*built_display_list.descriptor()).clone())
-        }).collect();
-        let msg = ApiMsg::SetRootStackingContext(stacking_context_id,
-                                                 background_color,
-                                                 epoch,
-                                                 pipeline_id,
-                                                 viewport_size,
-                                                 stacking_contexts,
-                                                 display_list_descriptors,
-                                                 *auxiliary_lists.descriptor());
+    pub fn set_root_display_list(&self,
+                                 background_color: ColorF,
+                                 epoch: Epoch,
+                                 pipeline_id: PipelineId,
+                                 viewport_size: Size2D<f32>,
+                                 display_list: BuiltDisplayList,
+                                 auxiliary_lists: AuxiliaryLists) {
+        let msg = ApiMsg::SetRootDisplayList(background_color,
+                                             epoch,
+                                             pipeline_id,
+                                             viewport_size,
+                                             display_list.descriptor().clone(),
+                                             *auxiliary_lists.descriptor());
         self.api_sender.send(msg).unwrap();
 
         let mut payload = vec![];
-        payload.write_u32::<LittleEndian>(stacking_context_id.0).unwrap();
         payload.write_u32::<LittleEndian>(epoch.0).unwrap();
-
-        for &(_, ref built_display_list) in &display_lists {
-            payload.extend_from_slice(built_display_list.data());
-        }
+        payload.extend_from_slice(display_list.data());
         payload.extend_from_slice(auxiliary_lists.data());
-
         self.payload_sender.send(&payload[..]).unwrap();
     }
 
     /// Scrolls the scrolling layer under the `cursor`
     ///
     /// Webrender looks for the layer closest to the user
     /// which has `ScrollPolicy::Scrollable` set.
     pub fn scroll(&self, delta: Point2D<f32>, cursor: Point2D<f32>, phase: ScrollEventPhase) {
@@ -241,28 +236,16 @@ impl RenderApi {
     }
 
     pub fn send_webgl_command(&self, context_id: WebGLContextId, command: WebGLCommand) {
         let msg = ApiMsg::WebGLCommand(context_id, command);
         self.api_sender.send(msg).unwrap();
     }
 
     #[inline]
-    pub fn next_stacking_context_id(&self) -> StackingContextId {
-        let new_id = self.next_unique_id();
-        StackingContextId(new_id.0, new_id.1)
-    }
-
-    #[inline]
-    pub fn next_display_list_id(&self) -> DisplayListId {
-        let new_id = self.next_unique_id();
-        DisplayListId(new_id.0, new_id.1)
-    }
-
-    #[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)
     }
 }
 
--- a/gfx/webrender_traits/src/display_list.rs
+++ b/gfx/webrender_traits/src/display_list.rs
@@ -4,22 +4,21 @@
 
 use app_units::Au;
 use euclid::{Point2D, Rect, Size2D};
 use std::mem;
 use std::slice;
 use {AuxiliaryLists, AuxiliaryListsDescriptor, BorderDisplayItem, BorderRadius};
 use {BorderSide, BoxShadowClipMode, BoxShadowDisplayItem, BuiltDisplayList};
 use {BuiltDisplayListDescriptor, ClipRegion, ComplexClipRegion, ColorF};
-use {DisplayItem, DisplayListItem, DisplayListMode, DrawListInfo, FilterOp};
-use {FontKey, GlyphInstance, GradientDisplayItem, GradientStop, IframeInfo};
-use {ImageDisplayItem, ImageKey, ImageRendering, ItemRange, PipelineId};
-use {RectangleDisplayItem, SpecificDisplayItem, SpecificDisplayListItem};
-use {StackingContextId, StackingContextInfo, TextDisplayItem};
-use {WebGLContextId, WebGLDisplayItem};
+use {DisplayItem, DisplayListMode, FilterOp};
+use {FontKey, GlyphInstance, GradientDisplayItem, GradientStop, IframeDisplayItem};
+use {ImageDisplayItem, ImageKey, ImageRendering, ItemRange, PipelineId,};
+use {PushStackingContextDisplayItem, RectangleDisplayItem, SpecificDisplayItem};
+use {StackingContext, TextDisplayItem, WebGLContextId, WebGLDisplayItem};
 
 impl BuiltDisplayListDescriptor {
     pub fn size(&self) -> usize {
         self.display_list_items_size + self.display_items_size
     }
 }
 
 impl BuiltDisplayList {
@@ -33,45 +32,33 @@ impl BuiltDisplayList {
     pub fn data(&self) -> &[u8] {
         &self.data[..]
     }
 
     pub fn descriptor(&self) -> &BuiltDisplayListDescriptor {
         &self.descriptor
     }
 
-    pub fn display_list_items<'a>(&'a self) -> &'a [DisplayListItem] {
+    pub fn all_display_items<'a>(&'a self) -> &'a [DisplayItem] {
         unsafe {
             convert_blob_to_pod(&self.data[0..self.descriptor.display_list_items_size])
         }
     }
-
-    pub fn display_items<'a>(&'a self, range: &ItemRange) -> &'a [DisplayItem] {
-        unsafe {
-            range.get(convert_blob_to_pod(&self.data[self.descriptor.display_list_items_size..]))
-        }
-    }
 }
 
 pub struct DisplayListBuilder {
     pub mode: DisplayListMode,
-
-    pub work_list: Vec<DisplayItem>,
-
-    pub display_list_items: Vec<DisplayListItem>,
-    pub display_items: Vec<DisplayItem>,
+    pub list: Vec<DisplayItem>,
 }
 
 impl DisplayListBuilder {
     pub fn new() -> DisplayListBuilder {
         DisplayListBuilder {
             mode: DisplayListMode::Default,
-            work_list: Vec::new(),
-            display_list_items: Vec::new(),
-            display_items: Vec::new(),
+            list: Vec::new(),
         }
     }
 
     pub fn push_rect(&mut self,
                      rect: Rect<f32>,
                      clip: ClipRegion,
                      color: ColorF) {
         let item = RectangleDisplayItem {
@@ -79,17 +66,17 @@ impl DisplayListBuilder {
         };
 
         let display_item = DisplayItem {
             item: SpecificDisplayItem::Rectangle(item),
             rect: rect,
             clip: clip,
         };
 
-        self.push_item(display_item);
+        self.list.push(display_item);
     }
 
     pub fn push_image(&mut self,
                       rect: Rect<f32>,
                       clip: ClipRegion,
                       stretch_size: Size2D<f32>,
                       tile_spacing: Size2D<f32>,
                       image_rendering: ImageRendering,
@@ -102,34 +89,34 @@ impl DisplayListBuilder {
         };
 
         let display_item = DisplayItem {
             item: SpecificDisplayItem::Image(item),
             rect: rect,
             clip: clip,
         };
 
-        self.push_item(display_item);
+        self.list.push(display_item);
     }
 
     pub fn push_webgl_canvas(&mut self,
                              rect: Rect<f32>,
                              clip: ClipRegion,
                              context_id: WebGLContextId) {
         let item = WebGLDisplayItem {
             context_id: context_id,
         };
 
         let display_item = DisplayItem {
             item: SpecificDisplayItem::WebGL(item),
             rect: rect,
             clip: clip,
         };
 
-        self.push_item(display_item);
+        self.list.push(display_item);
     }
 
     pub fn push_text(&mut self,
                      rect: Rect<f32>,
                      clip: ClipRegion,
                      glyphs: Vec<GlyphInstance>,
                      font_key: FontKey,
                      color: ColorF,
@@ -152,17 +139,17 @@ impl DisplayListBuilder {
             };
 
             let display_item = DisplayItem {
                 item: SpecificDisplayItem::Text(item),
                 rect: rect,
                 clip: clip,
             };
 
-            self.push_item(display_item);
+            self.list.push(display_item);
         }
     }
 
     pub fn push_border(&mut self,
                        rect: Rect<f32>,
                        clip: ClipRegion,
                        left: BorderSide,
                        top: BorderSide,
@@ -178,17 +165,17 @@ impl DisplayListBuilder {
         };
 
         let display_item = DisplayItem {
             item: SpecificDisplayItem::Border(item),
             rect: rect,
             clip: clip,
         };
 
-        self.push_item(display_item);
+        self.list.push(display_item);
     }
 
     pub fn push_box_shadow(&mut self,
                            rect: Rect<f32>,
                            clip: ClipRegion,
                            box_bounds: Rect<f32>,
                            offset: Point2D<f32>,
                            color: ColorF,
@@ -207,28 +194,17 @@ impl DisplayListBuilder {
         };
 
         let display_item = DisplayItem {
             item: SpecificDisplayItem::BoxShadow(item),
             rect: rect,
             clip: clip,
         };
 
-        self.push_item(display_item);
-    }
-
-    pub fn push_stacking_context(&mut self, stacking_context_id: StackingContextId) {
-        self.flush();
-        let info = StackingContextInfo {
-            id: stacking_context_id,
-        };
-        let item = DisplayListItem {
-            specific: SpecificDisplayListItem::StackingContext(info),
-        };
-        self.display_list_items.push(item);
+        self.list.push(display_item);
     }
 
     pub fn push_gradient(&mut self,
                          rect: Rect<f32>,
                          clip: ClipRegion,
                          start_point: Point2D<f32>,
                          end_point: Point2D<f32>,
                          stops: Vec<GradientStop>,
@@ -240,79 +216,63 @@ impl DisplayListBuilder {
         };
 
         let display_item = DisplayItem {
             item: SpecificDisplayItem::Gradient(item),
             rect: rect,
             clip: clip,
         };
 
-        self.push_item(display_item);
+        self.list.push(display_item);
     }
 
-    pub fn push_iframe(&mut self,
-                       rect: Rect<f32>,
-                       clip: ClipRegion,
-                       iframe: PipelineId) {
-        self.flush();
-        let info = IframeInfo {
-            id: iframe,
-            bounds: rect,
-            clip: clip,
+    pub fn push_stacking_context(&mut self, stacking_context: StackingContext) {
+        let item = DisplayItem {
+            item: SpecificDisplayItem::PushStackingContext(PushStackingContextDisplayItem {
+                stacking_context: stacking_context
+            }),
+            rect: Rect::zero(),
+            clip: ClipRegion::simple(&Rect::zero()),
         };
-        let item = DisplayListItem {
-            specific: SpecificDisplayListItem::Iframe(info),
-        };
-        self.display_list_items.push(item);
-    }
-
-    fn push_item(&mut self, item: DisplayItem) {
-        self.work_list.push(item);
+        self.list.push(item);
     }
 
-    fn flush(&mut self) {
-        let items = mem::replace(&mut self.work_list, Vec::new());
-        if items.is_empty() {
-            return
-        }
-
-        let draw_list = DrawListInfo {
-            items: ItemRange::new(&mut self.display_items, &items),
+    pub fn pop_stacking_context(&mut self) {
+        let item = DisplayItem {
+            item: SpecificDisplayItem::PopStackingContext,
+            rect: Rect::zero(),
+            clip: ClipRegion::simple(&Rect::zero()),
         };
-        self.display_list_items.push(DisplayListItem {
-            specific: SpecificDisplayListItem::DrawList(draw_list),
-        });
+        self.list.push(item);
     }
 
-    pub fn finalize(mut self) -> BuiltDisplayList {
-        self.flush();
+    pub fn push_iframe(&mut self, rect: Rect<f32>, clip: ClipRegion, pipeline_id: PipelineId) {
+        let item = DisplayItem {
+            item: SpecificDisplayItem::Iframe(IframeDisplayItem { pipeline_id: pipeline_id }),
+            rect: rect,
+            clip: clip,
+        };
+        self.list.push(item);
+    }
 
+    pub fn finalize(self) -> BuiltDisplayList {
         unsafe {
-            let mut blob = convert_pod_to_blob(&self.display_list_items).to_vec();
+            let blob = convert_pod_to_blob(&self.list).to_vec();
             let display_list_items_size = blob.len();
-            blob.extend_from_slice(convert_pod_to_blob(&self.display_items));
-            let display_items_size = blob.len() - display_list_items_size;
+
             BuiltDisplayList {
                 descriptor: BuiltDisplayListDescriptor {
                     mode: self.mode,
                     display_list_items_size: display_list_items_size,
-                    display_items_size: display_items_size,
+                    display_items_size: 0,
                 },
                 data: blob,
             }
         }
     }
-
-    pub fn display_items<'a>(&'a self, range: &ItemRange) -> &'a [DisplayItem] {
-        range.get(&self.display_items)
-    }
-
-    pub fn display_items_mut<'a>(&'a mut self, range: &ItemRange) -> &'a mut [DisplayItem] {
-        range.get_mut(&mut self.display_items)
-    }
 }
 
 impl ItemRange {
     pub fn new<T>(backing_list: &mut Vec<T>, items: &[T]) -> ItemRange where T: Copy + Clone {
         let start = backing_list.len();
         backing_list.extend_from_slice(items);
         ItemRange {
             start: start,
--- a/gfx/webrender_traits/src/lib.rs
+++ b/gfx/webrender_traits/src/lib.rs
@@ -15,17 +15,21 @@ extern crate gleam;
 extern crate heapsize;
 extern crate ipc_channel;
 extern crate offscreen_gl_context;
 extern crate serde;
 #[cfg(feature = "serde_derive")]
 #[macro_use]
 extern crate serde_derive;
 
-#[cfg(target_os = "macos")] extern crate core_graphics;
+#[cfg(target_os = "macos")]
+extern crate core_graphics;
+
+#[cfg(target_os = "windows")]
+extern crate dwrote;
 
 #[cfg(feature = "serde_codegen")]
 include!(concat!(env!("OUT_DIR"), "/types.rs"));
 
 #[cfg(feature = "serde_derive")]
 include!("types.rs");
 
 mod api;
--- a/gfx/webrender_traits/src/stacking_context.rs
+++ b/gfx/webrender_traits/src/stacking_context.rs
@@ -9,28 +9,25 @@ use {FilterOp, MixBlendMode, ScrollLayer
 impl StackingContext {
     pub fn new(scroll_layer_id: Option<ScrollLayerId>,
                scroll_policy: ScrollPolicy,
                bounds: Rect<f32>,
                overflow: Rect<f32>,
                z_index: i32,
                transform: &Matrix4D<f32>,
                perspective: &Matrix4D<f32>,
-               establishes_3d_context: bool,
                mix_blend_mode: MixBlendMode,
                filters: Vec<FilterOp>,
                auxiliary_lists_builder: &mut AuxiliaryListsBuilder)
                -> StackingContext {
         StackingContext {
             scroll_layer_id: scroll_layer_id,
             scroll_policy: scroll_policy,
             bounds: bounds,
             overflow: overflow,
             z_index: z_index,
-            display_lists: Vec::new(),
             transform: transform.clone(),
             perspective: perspective.clone(),
-            establishes_3d_context: establishes_3d_context,
             mix_blend_mode: mix_blend_mode,
             filters: auxiliary_lists_builder.add_filters(&filters),
         }
     }
 }
--- a/gfx/webrender_traits/src/types.rs
+++ b/gfx/webrender_traits/src/types.rs
@@ -8,16 +8,17 @@
 use app_units::Au;
 #[cfg(feature = "nightly")]
 use core::nonzero::NonZero;
 use euclid::{Matrix4D, Point2D, Rect, Size2D};
 use ipc_channel::ipc::{IpcBytesSender, IpcSender};
 use offscreen_gl_context::{GLContextAttributes, GLLimits};
 
 #[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,
 }
 
 #[derive(Deserialize, Serialize)]
@@ -28,32 +29,28 @@ pub enum ApiMsg {
     GetGlyphDimensions(Vec<GlyphKey>, IpcSender<Vec<Option<GlyphDimensions>>>),
     /// Adds an image from the resource cache.
     AddImage(ImageKey, u32, u32, Option<u32>, ImageFormat, Vec<u8>),
     /// Updates the the resource cache with the new image data.
     UpdateImage(ImageKey, u32, u32, ImageFormat, Vec<u8>),
     /// Drops an image from the resource cache.
     DeleteImage(ImageKey),
     CloneApi(IpcSender<IdNamespace>),
+    // Flushes all messages
+    Flush,
     /// Supplies a new frame to WebRender.
     ///
-    /// The first `StackingContextId` describes the root stacking context. The actual stacking
-    /// contexts are supplied as the sixth parameter, while the display lists that make up those
-    /// stacking contexts are supplied as the seventh parameter.
-    ///
-    /// After receiving this message, WebRender will read the display lists, followed by the
+    /// After receiving this message, WebRender will read the display list, followed by the
     /// auxiliary lists, from the payload channel.
-    SetRootStackingContext(StackingContextId,
-                           ColorF,
-                           Epoch,
-                           PipelineId,
-                           Size2D<f32>,
-                           Vec<(StackingContextId, StackingContext)>,
-                           Vec<(DisplayListId, BuiltDisplayListDescriptor)>,
-                           AuxiliaryListsDescriptor),
+    SetRootDisplayList(ColorF,
+                       Epoch,
+                       PipelineId,
+                       Size2D<f32>,
+                       BuiltDisplayListDescriptor,
+                       AuxiliaryListsDescriptor),
     SetRootPipeline(PipelineId),
     Scroll(Point2D<f32>, Point2D<f32>, ScrollEventPhase),
     TickScrollingBounce,
     GenerateFrame,
     TranslatePointToLayerSpace(Point2D<f32>, IpcSender<(Point2D<f32>, PipelineId)>),
     GetScrollLayerState(IpcSender<Vec<ScrollLayerState>>),
     RequestWebGLContext(Size2D<i32>, GLContextAttributes, IpcSender<Result<(WebGLContextId, GLLimits), String>>),
     ResizeWebGLContext(WebGLContextId, Size2D<i32>),
@@ -205,35 +202,22 @@ pub struct ComplexClipRegion {
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct DisplayItem {
     pub item: SpecificDisplayItem,
     pub rect: Rect<f32>,
     pub clip: ClipRegion,
 }
 
 #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
-pub struct DisplayListId(pub u32, pub u32);
-
-#[derive(Clone, Copy, Deserialize, Serialize)]
-pub struct DisplayListItem {
-    pub specific: SpecificDisplayListItem,
-}
-
-#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
 pub enum DisplayListMode {
     Default,
     PseudoFloat,
     PseudoPositionedContent,
 }
 
-#[derive(Clone, Copy, Deserialize, Serialize)]
-pub struct DrawListInfo {
-    pub items: ItemRange,
-}
-
 #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
 pub struct Epoch(pub u32);
 
 #[derive(Clone, Copy, Debug, Deserialize, Serialize)]
 pub enum FilterOp {
     Blur(Au),
     Brightness(f32),
     Contrast(f32),
@@ -297,25 +281,29 @@ pub struct GradientDisplayItem {
 
 #[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, Serialize)]
-pub struct IdNamespace(pub u32);
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct PushStackingContextDisplayItem {
+    pub stacking_context: StackingContext,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct IframeDisplayItem {
+    pub pipeline_id: PipelineId,
+}
 
 #[derive(Clone, Copy, Debug, Deserialize, Serialize)]
-pub struct IframeInfo {
-    pub id: PipelineId,
-    pub bounds: Rect<f32>,
-    pub clip: ClipRegion,
-}
+pub struct IdNamespace(pub u32);
 
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct ImageDisplayItem {
     pub image_key: ImageKey,
     pub stretch_size: Size2D<f32>,
     pub tile_spacing: Size2D<f32>,
     pub image_rendering: ImageRendering,
 }
@@ -334,17 +322,17 @@ pub struct ImageKey(u32, u32);
 
 #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
 pub enum ImageRendering {
     Auto,
     CrispEdges,
     Pixelated,
 }
 
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
 pub struct ItemRange {
     pub start: usize,
     pub length: usize,
 }
 
 #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
 pub enum MixBlendMode {
     Normal,
@@ -364,20 +352,23 @@ pub enum MixBlendMode {
     Color,
     Luminosity,
 }
 
 #[cfg(target_os = "macos")]
 pub type NativeFontHandle = CGFont;
 
 /// Native fonts are not used on Linux; all fonts are raw.
-#[cfg(not(target_os = "macos"))]
-#[cfg_attr(not(target_os = "macos"), derive(Clone, Serialize, Deserialize))]
+#[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;
+
 #[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,
 }
 
@@ -388,16 +379,20 @@ pub struct RenderApiSender {
 }
 
 pub trait RenderNotifier: Send {
     fn new_frame_ready(&mut self);
     fn new_scroll_frame_ready(&mut self, composite_needed: bool);
     fn pipeline_size_changed(&mut self, pipeline_id: PipelineId, size: Option<Size2D<f32>>);
 }
 
+pub trait FlushNotifier: Send {
+    fn all_messages_flushed(&mut self);
+}
+
 // Trait to allow dispatching functions to a specific thread or event loop.
 pub trait RenderDispatcher: Send {
     fn dispatch(&self, Box<Fn() + Send>);
 }
 
 #[derive(Clone, Copy, Debug, Deserialize, Serialize)]
 pub struct ResourceId(pub u32);
 
@@ -444,48 +439,34 @@ pub struct ServoScrollRootId(pub usize);
 pub enum SpecificDisplayItem {
     Rectangle(RectangleDisplayItem),
     Text(TextDisplayItem),
     Image(ImageDisplayItem),
     WebGL(WebGLDisplayItem),
     Border(BorderDisplayItem),
     BoxShadow(BoxShadowDisplayItem),
     Gradient(GradientDisplayItem),
+    Iframe(IframeDisplayItem),
+    PushStackingContext(PushStackingContextDisplayItem),
+    PopStackingContext,
 }
 
-#[derive(Clone, Copy, Deserialize, Serialize)]
-pub enum SpecificDisplayListItem {
-    DrawList(DrawListInfo),
-    StackingContext(StackingContextInfo),
-    Iframe(IframeInfo),
-}
-
-#[derive(Clone, Deserialize, Serialize)]
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct StackingContext {
     pub scroll_layer_id: Option<ScrollLayerId>,
     pub scroll_policy: ScrollPolicy,
     pub bounds: Rect<f32>,
     pub overflow: Rect<f32>,
     pub z_index: i32,
-    pub display_lists: Vec<DisplayListId>,
     pub transform: Matrix4D<f32>,
     pub perspective: Matrix4D<f32>,
-    pub establishes_3d_context: bool,
     pub mix_blend_mode: MixBlendMode,
     pub filters: ItemRange,
 }
 
-#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
-pub struct StackingContextId(pub u32, pub u32);
-
-#[derive(Clone, Copy, Deserialize, Serialize)]
-pub struct StackingContextInfo {
-    pub id: StackingContextId,
-}
-
 #[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,
 }
new file mode 100644
--- /dev/null
+++ b/third_party/rust/dwrite-sys/.cargo-checksum.json
@@ -0,0 +1,1 @@
+{"files":{".cargo-ok":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","Cargo.toml":"91f94a08dce352957b67533cfca64a353e7b8cd25891feb9d9a3bd7831cda0cb","README.md":"ecd0c1aad1ce04f5e46b41b9d4e8d3c633902ba47f88c2e685c376c3560ad296","build.rs":"cbd9e2d8d2c67e665501f8d4680ab693937385013b01fbfc089a5ff9275fe583","i686/libdwrite.a":"5a6b57d0e5073cca5f131115b83ace06c106329ea164b35c9aa88938529efca7","src/lib.rs":"476ae54c2ab330e0d76665d10c7872ca478fc271139c2fe59903701a232d64f0","x86_64/libdwrite.a":"cfad4321c149bd9e8f20456914f5cb58a225bb1a365c42e82c269bfa53bf6bd4"},"package":"a7918280f33862bc8542212d74f2149b1a87ab402fd15f4ce9a1c56582958d6e"}
\ No newline at end of file
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/third_party/rust/dwrite-sys/Cargo.toml
@@ -0,0 +1,17 @@
+[package]
+name = "dwrite-sys"
+version = "0.2.0"
+authors = ["Peter Atashian <retep998@gmail.com>"]
+description = "Contains function definitions for the Windows API library dwrite. See winapi for types and constants."
+documentation = "https://retep998.github.io/doc/dwrite/"
+repository = "https://github.com/retep998/winapi-rs"
+readme = "README.md"
+keywords = ["windows", "ffi", "win32", "directx"]
+license = "MIT"
+build = "build.rs"
+[lib]
+name = "dwrite"
+[dependencies]
+winapi = { version = "0.2.5", path = "../.." }
+[build-dependencies]
+winapi-build = { version = "0.1.1", path = "../../build" }
new file mode 100644
--- /dev/null
+++ b/third_party/rust/dwrite-sys/README.md
@@ -0,0 +1,13 @@
+# dwrite #
+Contains function definitions for the Windows API library dwrite. See winapi for types and constants.
+
+```toml
+[dependencies]
+dwrite-sys = "0.2.0"
+```
+
+```rust
+extern crate dwrite;
+```
+
+[Documentation](https://retep998.github.io/doc/dwrite/)
new file mode 100644
--- /dev/null
+++ b/third_party/rust/dwrite-sys/build.rs
@@ -0,0 +1,6 @@
+// Copyright © 2015, Peter Atashian
+// Licensed under the MIT License <LICENSE.md>
+extern crate build;
+fn main() {
+    build::link("dwrite", true)
+}
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/third_party/rust/dwrite-sys/src/lib.rs
@@ -0,0 +1,11 @@
+// Copyright © 2015, Peter Atashian
+// Licensed under the MIT License <LICENSE.md>
+//! FFI bindings to dwrite.
+#![cfg(windows)]
+extern crate winapi;
+use winapi::*;
+extern "system" {
+    pub fn DWriteCreateFactory(
+        factoryType: DWRITE_FACTORY_TYPE, iid: REFIID, factory: *mut *mut IUnknown,
+    ) -> HRESULT;
+}
new file mode 100644
--- a/third_party/rust/ipc-channel/.cargo-checksum.json
+++ b/third_party/rust/ipc-channel/.cargo-checksum.json
@@ -1,1 +1,1 @@
-{"files":{".cargo-ok":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",".gitignore":"6d00fdf3626ce8a028c55b7bab26468bdc5b26ae47e9fa67c5bd73df4eb77b5c",".travis.yml":"a2c0d2b50d55d0bcaab4fdd93adc18a19e8e7ed814e01cf761aa146db443dc5e","Cargo.toml":"86c623d7c2b4bbf4e7ca98fb0299402e31a0c25ff1e13d4e233574a750943c58","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"62065228e42caebca7e7d7db1204cbb867033de5982ca4009928915e4095f3a3","README.md":"d427db47c1a114f0cd4495661435c7f258f2e6e6ac91d80cc26192b8d2ecef4c","appveyor.yml":"a6e69899364ea6a9ccb6f1c6d393848772140cb20e9798f7313cb22cf2414d37","benches/bench.rs":"eadd7d0d8a153668f2ce6edac2bc5bc56cebcf49f868b1ff23c9fdafd24030eb","src/ipc.rs":"246c820fbbbc60ef49d252a48c191a98a13e84a1e1eac351290e3ee225cb1557","src/lib.rs":"b8af363043c5ced9758e46ebc7b4e263611ddcc4fa346ca7823c5f6177a2250f","src/platform/inprocess/mod.rs":"8bb5b396d285516389008595fb343d96dc31e9088427070820b4de3a89434560","src/platform/macos/mach_sys.rs":"927805a6d2705af35c908a63fedf93c4357c1f02138fb16a4586a1b11025ee26","src/platform/macos/mod.rs":"af9f83cded753638ad8449fb0031593224b18cca7545c13e16868c9c17411389","src/platform/mod.rs":"0c92c9f18082fc407a07067c9fa1c5035a65386a7f8ce3ba36a899e97bec6a88","src/platform/test.rs":"3ac3fe7078ea1cf91237021a811cee70694b493330da212ebda2b9aa870ed97d","src/platform/unix/mod.rs":"583f9be7ed74e6b012e27d6e3cbf15ec0014324fa553597912d3b227589813f0","src/refcell.rs":"0315c37f74841e0f9aee5401d4fb1ad624980b9410ce245a19d2f02bbcb3341f","src/router.rs":"cb408ff4e50ad958d4e19a393e1d12363ab3564d93a2b51fd5bfc0b03f803db8","src/test.rs":"97e30e773c5d0135f525e71fad9964fe5f01de9dfc17ace6b881bfb8e1032f19"},"package":"f379ec6400f5cb93edbbb0ac56de3bf38331e22cbae9e27a10f04c1a0b4f8e90"}
\ No newline at end of file
+{"files":{".cargo-ok":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",".gitignore":"6d00fdf3626ce8a028c55b7bab26468bdc5b26ae47e9fa67c5bd73df4eb77b5c",".travis.yml":"264e530badb9801ff1f430f72ef5d21374e27e00d62ae50bba46de7c5e410ab3","Cargo.toml":"a49ea20eeecdc1d41e61d024f59521c9bdc1f41ae4c0a8202b88e2c4fc6016cb","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"62065228e42caebca7e7d7db1204cbb867033de5982ca4009928915e4095f3a3","README.md":"d427db47c1a114f0cd4495661435c7f258f2e6e6ac91d80cc26192b8d2ecef4c","appveyor.yml":"52c6aaab8eafe26d57036e7947e08db9a69fa592bbf7b5ba0a4731ee4cd71520","benches/bench.rs":"eadd7d0d8a153668f2ce6edac2bc5bc56cebcf49f868b1ff23c9fdafd24030eb","src/ipc.rs":"246c820fbbbc60ef49d252a48c191a98a13e84a1e1eac351290e3ee225cb1557","src/lib.rs":"2adf1e9e20d2df7b847a4d171b25e3a14ec2bb5408ea354d3d3ca278992bceaf","src/platform/inprocess/mod.rs":"f355d59c24453593d9cf74e1043c8067ba370d1d0cfca37ab7c615f35de01918","src/platform/linux/mod.rs":"bee6a908da64ec988c1a24739f414b12102d554cb287a3230a1686ef80c02de6","src/platform/macos/mach_sys.rs":"927805a6d2705af35c908a63fedf93c4357c1f02138fb16a4586a1b11025ee26","src/platform/macos/mod.rs":"0a4914dd07224254432348c10b59bf774ea665b525529b670c23c931e13cb268","src/platform/mod.rs":"2ee8102478eb1a5ce25d3ab1ac68b129d241d872c215401a552ea19cb37b4bf9","src/platform/test.rs":"d424099d2379d174554a05055e32f84a0c5ef1a28c2d5235b04bb0dd0e0cf28c","src/refcell.rs":"0315c37f74841e0f9aee5401d4fb1ad624980b9410ce245a19d2f02bbcb3341f","src/router.rs":"cb408ff4e50ad958d4e19a393e1d12363ab3564d93a2b51fd5bfc0b03f803db8","src/test.rs":"88d1e9964126639bba16011f34ac56e08cd8f5ec5ec338ad6ae125100c973f3d"},"package":"675587430ede6756dd03fdfdf9888f22f83855fd131c8451d842a710b059e571"}
\ No newline at end of file
--- a/third_party/rust/ipc-channel/.travis.yml
+++ b/third_party/rust/ipc-channel/.travis.yml
@@ -2,18 +2,10 @@ language: rust
 sudo: false
 rust:
   - nightly
 
 os:
   - linux
   - osx
 
-env:
-  - FEATURES="unstable"
-  - FEATURES="unstable force-inprocess"
-
 notifications:
   webhooks: http://build.servo.org:54856/travis
-
-script:
-  - cargo build --features "$FEATURES"
-  - cargo test --features "$FEATURES"
--- a/third_party/rust/ipc-channel/Cargo.toml
+++ b/third_party/rust/ipc-channel/Cargo.toml
@@ -1,22 +1,20 @@
 [package]
 name = "ipc-channel"
-version = "0.6.0"
+version = "0.5.1"
 description = "A multiprocess drop-in replacement for Rust channels"
 authors = ["The Servo Project Developers"]
 license = "MIT/Apache-2.0"
 repository = "https://github.com/servo/ipc-channel"
 
-[features]
-force-inprocess = []
-unstable = []
-
 [dependencies]
 bincode = "0.6"
 lazy_static = "0.2"
 libc = "0.2.12"
 rand = "0.3"
 serde = "0.8"
+
+[target.'cfg(any(target_os = "windows", target_os = "android"))'.dependencies]
 uuid = {version = "0.3", features = ["v4"]}
 
 [dev-dependencies]
 crossbeam = "0.2"
--- a/third_party/rust/ipc-channel/appveyor.yml
+++ b/third_party/rust/ipc-channel/appveyor.yml
@@ -8,9 +8,9 @@ install:
   - ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-nightly-${env:TARGET}.exe"
   - rust-nightly-%TARGET%.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust"
   - rustc -V
   - cargo -V
 
 build: false
 
 test_script:
-  - 'cargo test --verbose --features "unstable"'
+  - cargo test --verbose
--- a/third_party/rust/ipc-channel/src/lib.rs
+++ b/third_party/rust/ipc-channel/src/lib.rs
@@ -2,29 +2,26 @@
 // file at the top-level directory of this distribution.
 //
 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![cfg_attr(any(feature = "force-inprocess", target_os = "windows", target_os = "android"),
-			feature(mpsc_select))]
-#![cfg_attr(all(feature = "unstable", test), feature(specialization))]
+#![cfg_attr(any(target_os="windows", target_os="android"), feature(mpsc_select))]
 
 #[macro_use]
 extern crate lazy_static;
 
 extern crate bincode;
 extern crate libc;
 extern crate rand;
 extern crate serde;
-#[cfg(any(feature = "force-inprocess", target_os = "windows", target_os = "android"))]
-extern crate uuid;
+#[cfg(any(target_os = "windows", target_os = "android"))] extern crate uuid;
 
 pub mod ipc;
 pub mod platform;
 mod refcell;
 pub mod router;
 
 #[cfg(test)]
 mod test;
--- a/third_party/rust/ipc-channel/src/platform/inprocess/mod.rs
+++ b/third_party/rust/ipc-channel/src/platform/inprocess/mod.rs
@@ -17,30 +17,29 @@ use std::slice;
 use std::fmt::{self, Debug, Formatter};
 use std::cmp::{PartialEq};
 use std::ops::Deref;
 use std::mem;
 use std::usize;
 
 use uuid::Uuid;
 
-#[derive(Clone)]
 struct ServerRecord {
     sender: OsIpcSender,
     conn_sender: mpsc::Sender<bool>,
-    conn_receiver: Arc<Mutex<mpsc::Receiver<bool>>>,
+    conn_receiver: Mutex<mpsc::Receiver<bool>>,
 }
 
 impl ServerRecord {
     fn new(sender: OsIpcSender) -> ServerRecord {
         let (tx, rx) = mpsc::channel::<bool>();
         ServerRecord {
             sender: sender,
             conn_sender: tx,
-            conn_receiver: Arc::new(Mutex::new(rx)),
+            conn_receiver: Mutex::new(rx),
         }
     }
 
     fn accept(&self) {
         self.conn_receiver.lock().unwrap().recv().unwrap();
     }
 
     fn connect(&self) {
@@ -54,97 +53,112 @@ lazy_static! {
 
 struct MpscChannelMessage(Vec<u8>, Vec<OsIpcChannel>, Vec<OsIpcSharedMemory>);
 
 pub fn channel() -> Result<(OsIpcSender, OsIpcReceiver),MpscError> {
     let (base_sender, base_receiver) = mpsc::channel::<MpscChannelMessage>();
     Ok((OsIpcSender::new(base_sender), OsIpcReceiver::new(base_receiver)))
 }
 
-#[derive(Debug)]
 pub struct OsIpcReceiver {
-    receiver: RefCell<Option<mpsc::Receiver<MpscChannelMessage>>>,
+    receiver: Arc<Mutex<Option<mpsc::Receiver<MpscChannelMessage>>>>,
 }
 
 impl PartialEq for OsIpcReceiver {
     fn eq(&self, other: &OsIpcReceiver) -> bool {
-        self.receiver.borrow().as_ref().map(|rx| rx as *const _) ==
-            other.receiver.borrow().as_ref().map(|rx| rx as *const _)
+        self.receiver.lock().unwrap().as_ref().map(|rx| rx as *const _) ==
+            other.receiver.lock().unwrap().as_ref().map(|rx| rx as *const _)
+    }
+}
+
+// Can't derive, as mpsc::Receiver doesn't implement Debug.
+impl fmt::Debug for OsIpcReceiver {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        // Not sure there is anything useful we could print here.
+        write!(f, "OsIpcReceiver {{ .. }}")
     }
 }
 
 impl OsIpcReceiver {
     fn new(receiver: mpsc::Receiver<MpscChannelMessage>) -> OsIpcReceiver {
         OsIpcReceiver {
-            receiver: RefCell::new(Some(receiver)),
+            receiver: Arc::new(Mutex::new(Some(receiver))),
         }
     }
 
     pub fn consume(&self) -> OsIpcReceiver {
-        let receiver = self.receiver.borrow_mut().take();
+        let receiver = self.receiver.lock().unwrap().take();
         OsIpcReceiver::new(receiver.unwrap())
     }
 
     pub fn recv(&self) -> Result<(Vec<u8>, Vec<OsOpaqueIpcChannel>, Vec<OsIpcSharedMemory>),MpscError> {
-        let r = self.receiver.borrow();
+        let r = self.receiver.lock().unwrap();
         match r.as_ref().unwrap().recv() {
             Ok(MpscChannelMessage(d,c,s)) => Ok((d,
                                                  c.into_iter().map(OsOpaqueIpcChannel::new).collect(),
                                                  s)),
             Err(_) => Err(MpscError::ChannelClosedError),
         }
     }
 
     pub fn try_recv(&self) -> Result<(Vec<u8>, Vec<OsOpaqueIpcChannel>, Vec<OsIpcSharedMemory>),MpscError> {
-        let r = self.receiver.borrow();
+        let r = self.receiver.lock().unwrap();
         match r.as_ref().unwrap().try_recv() {
             Ok(MpscChannelMessage(d,c,s)) => Ok((d,
                                                  c.into_iter().map(OsOpaqueIpcChannel::new).collect(),
                                                  s)),
             Err(_) => Err(MpscError::ChannelClosedError),
         }
     }
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone)]
 pub struct OsIpcSender {
-    sender: RefCell<mpsc::Sender<MpscChannelMessage>>,
+    sender: Arc<Mutex<mpsc::Sender<MpscChannelMessage>>>,
 }
 
 impl PartialEq for OsIpcSender {
     fn eq(&self, other: &OsIpcSender) -> bool {
-        &*self.sender.borrow() as *const _ ==
-            &*other.sender.borrow() as *const _
+        &*self.sender.lock().unwrap() as *const _ ==
+            &*other.sender.lock().unwrap() as *const _
+    }
+}
+
+// Can't derive, as mpsc::Sender doesn't implement Debug.
+impl fmt::Debug for OsIpcSender {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        // Not sure there is anything useful we could print here.
+        write!(f, "OsIpcSender {{ .. }}")
     }
 }
 
 impl OsIpcSender {
     fn new(sender: mpsc::Sender<MpscChannelMessage>) -> OsIpcSender {
         OsIpcSender {
-            sender: RefCell::new(sender),
+            sender: Arc::new(Mutex::new(sender)),
         }
     }
 
     pub fn connect(name: String) -> Result<OsIpcSender,MpscError> {
-        let record = ONE_SHOT_SERVERS.lock().unwrap().get(&name).unwrap().clone();
+        let record = ONE_SHOT_SERVERS.lock().unwrap().remove(&name).unwrap();
         record.connect();
         Ok(record.sender)
     }
 
     pub fn get_max_fragment_size() -> usize {
         usize::MAX
     }
 
     pub fn send(&self,
                 data: &[u8],
                 ports: Vec<OsIpcChannel>,
                 shared_memory_regions: Vec<OsIpcSharedMemory>)
                 -> Result<(),MpscError>
     {
-        match self.sender.borrow().send(MpscChannelMessage(data.to_vec(), ports, shared_memory_regions)) {
+        match self.sender.lock().unwrap().send(MpscChannelMessage(data.to_vec(), ports, shared_memory_regions)) {
             Err(_) => Err(MpscError::ChannelClosedError),
             Ok(_) => Ok(()),
         }
     }
 }
 
 pub struct OsIpcReceiverSet {
     last_index: usize,
@@ -174,17 +188,17 @@ impl OsIpcReceiverSet {
         let mut r_index: usize = 0;
 
         {
             let select = mpsc::Select::new();
             // we *must* allocate exact capacity for this, because the Handles *can't move*
             let mut handles: Vec<mpsc::Handle<MpscChannelMessage>> = Vec::with_capacity(self.receivers.len());
 
             for r in &self.receivers {
-                let inner_r = mem::replace(&mut *r.receiver.borrow_mut(), None);
+                let inner_r = mem::replace(&mut *r.receiver.lock().unwrap(), None);
                 receivers.push(inner_r);
             }
             
             for r in &receivers {
                 unsafe {
                     handles.push(select.handle(r.as_ref().unwrap()));
                     handles.last_mut().unwrap().add();
                 }
@@ -198,17 +212,17 @@ impl OsIpcReceiverSet {
                     r_id = self.receiver_ids[index] as i64;
                     break;
                 }
             }
         }
 
         // put the receivers back
         for (index,r) in self.receivers.iter().enumerate() {
-            mem::replace(&mut *r.receiver.borrow_mut(), mem::replace(&mut receivers[index], None));
+            mem::replace(&mut *r.receiver.lock().unwrap(), mem::replace(&mut receivers[index], None));
         }
 
         if r_id == -1 {
             return Err(MpscError::UnknownError);
         }
 
         let receivers = &mut self.receivers;
         match receivers[r_index].recv() {
@@ -238,43 +252,45 @@ impl OsIpcSelectionResult {
             OsIpcSelectionResult::ChannelClosed(id) => {
                 panic!("OsIpcSelectionResult::unwrap(): receiver ID {} was closed!", id)
             }
         }
     }
 }
 
 pub struct OsIpcOneShotServer {
-    receiver: OsIpcReceiver,
+    receiver: RefCell<Option<OsIpcReceiver>>,
     name: String,
 }
 
 impl OsIpcOneShotServer {
     pub fn new() -> Result<(OsIpcOneShotServer, String),MpscError> {
-        let (sender, receiver) = try!(channel());
+        let (sender, receiver) = match channel() {
+            Ok((s,r)) => (s,r),
+            Err(err) => return Err(err),
+        };
 
         let name = Uuid::new_v4().to_string();
         let record = ServerRecord::new(sender);
         ONE_SHOT_SERVERS.lock().unwrap().insert(name.clone(), record);
         Ok((OsIpcOneShotServer {
-            receiver: receiver,
+            receiver: RefCell::new(Some(receiver)),
             name: name.clone(),
         },name.clone()))
     }
 
-    pub fn accept(self) -> Result<(OsIpcReceiver,
+    pub fn accept(&self) -> Result<(OsIpcReceiver,
                                     Vec<u8>,
                                     Vec<OsOpaqueIpcChannel>,
                                     Vec<OsIpcSharedMemory>),MpscError>
     {
-        let record = ONE_SHOT_SERVERS.lock().unwrap().get(&self.name).unwrap().clone();
-        record.accept();
-        ONE_SHOT_SERVERS.lock().unwrap().remove(&self.name).unwrap();
-        let (data, channels, shmems) = try!(self.receiver.recv());
-        Ok((self.receiver, data, channels, shmems))
+        ONE_SHOT_SERVERS.lock().unwrap().get(&self.name).unwrap().accept();
+        let receiver = self.receiver.borrow_mut().take().unwrap();
+        let (data, channels, shmems) = receiver.recv().unwrap();
+        Ok((receiver, data, channels, shmems))
     }
 }
 
 #[derive(PartialEq, Debug)]
 pub enum OsIpcChannel {
     Sender(OsIpcSender),
     Receiver(OsIpcReceiver),
 }
@@ -293,17 +309,17 @@ impl OsOpaqueIpcChannel {
 
     pub fn to_receiver(&self) -> OsIpcReceiver {
         match self.channel.borrow_mut().take().unwrap() {
             OsIpcChannel::Sender(_) => panic!("Opaque channel is not a receiver!"),
             OsIpcChannel::Receiver(r) => r
         }
     }
     
-    pub fn to_sender(&mut self) -> OsIpcSender {
+    pub fn to_sender(&self) -> OsIpcSender {
         match self.channel.borrow_mut().take().unwrap() {
             OsIpcChannel::Sender(s) => s,
             OsIpcChannel::Receiver(_) => panic!("Opaque channel is not a sender!"),
         }
     }
 }
 
 pub struct OsIpcSharedMemory {
rename from third_party/rust/ipc-channel/src/platform/unix/mod.rs
rename to third_party/rust/ipc-channel/src/platform/linux/mod.rs
--- a/third_party/rust/ipc-channel/src/platform/unix/mod.rs
+++ b/third_party/rust/ipc-channel/src/platform/linux/mod.rs
@@ -4,73 +4,45 @@
 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
 use bincode::serde::DeserializeError;
 use libc::{self, MAP_FAILED, MAP_SHARED, POLLIN, PROT_READ, PROT_WRITE, SOCK_SEQPACKET, SOL_SOCKET};
-use libc::{SO_LINGER, S_IFMT, S_IFSOCK, c_char, c_int, c_void, getsockopt};
+use libc::{SO_LINGER, S_IFMT, S_IFSOCK, c_char, c_int, c_short, c_ushort, c_void, getsockopt};
 use libc::{iovec, mkstemp, mode_t, msghdr, nfds_t, off_t, poll, pollfd, recvmsg, sendmsg};
-use libc::{setsockopt, size_t, sockaddr, sockaddr_un, socketpair, socklen_t, sa_family_t};
-use std::cell::Cell;
+use libc::{setsockopt, size_t, sockaddr, sockaddr_un, socketpair, socklen_t};
 use std::cmp;
 use std::collections::HashSet;
 use std::ffi::{CStr, CString};
 use std::fmt::{self, Debug, Formatter};
 use std::io::Error;
-use std::marker::PhantomData;
 use std::mem;
 use std::ops::Deref;
 use std::ptr;
 use std::slice;
 use std::sync::Arc;
 use std::thread;
 
 const MAX_FDS_IN_CMSG: u32 = 64;
 
-const SCM_RIGHTS: c_int = 0x01;
-
-// The value Linux returns for SO_SNDBUF
-// is not the size we are actually allowed to use...
-// Empirically, we have to deduct 32 bytes from that.
-const RESERVED_SIZE: usize = 32;
-
-#[cfg(target_os="android")]
-const TEMP_FILE_TEMPLATE: &'static str = "/sdcard/servo/ipc-channel-shared-memory.XXXXXX";
-
-#[cfg(not(target_os="android"))]
-const TEMP_FILE_TEMPLATE: &'static str = "/tmp/ipc-channel-shared-memory.XXXXXX";
-
-#[cfg(target_os = "linux")]
-type IovLen = usize;
-#[cfg(target_os = "linux")]
-type MsgControlLen = size_t;
-
-#[cfg(target_os = "freebsd")]
-type IovLen = i32;
-#[cfg(target_os = "freebsd")]
-type MsgControlLen = socklen_t;
-
-unsafe fn new_sockaddr_un(path: *const c_char) -> (sockaddr_un, usize) {
-    let mut sockaddr: sockaddr_un = mem::zeroed();
-    libc::strncpy(sockaddr.sun_path.as_mut_ptr(),
-                  path, sockaddr.sun_path.len() - 1);
-    sockaddr.sun_family = libc::AF_UNIX as sa_family_t;
-    (sockaddr, mem::size_of::<sockaddr_un>())
-}
-
 lazy_static! {
     static ref SYSTEM_SENDBUF_SIZE: usize = {
         let (tx, _) = channel().expect("Failed to obtain a socket for checking maximum send size");
         tx.get_system_sendbuf_size().expect("Failed to obtain maximum send size for socket")
     };
 }
 
+// The value Linux returns for SO_SNDBUF
+// is not the size we are actually allowed to use...
+// Empirically, we have to deduct 32 bytes from that.
+const RESERVED_SIZE: usize = 32;
+
 pub fn channel() -> Result<(OsIpcSender, OsIpcReceiver),UnixError> {
     let mut results = [0, 0];
     unsafe {
         if socketpair(libc::AF_UNIX, SOCK_SEQPACKET, 0, &mut results[0]) >= 0 {
             Ok((OsIpcSender::from_fd(results[0]), OsIpcReceiver::from_fd(results[1])))
         } else {
             Err(UnixError::last())
         }
@@ -121,37 +93,31 @@ impl OsIpcReceiver {
 
 #[derive(PartialEq, Debug, Clone)]
 pub struct SharedFileDescriptor(c_int);
 
 
 #[derive(PartialEq, Debug, Clone)]
 pub struct OsIpcSender {
     fd: Arc<SharedFileDescriptor>,
-    // Make sure this is `!Sync`, to match `mpsc::Sender`; and to discourage sharing references.
-    //
-    // (Rather, senders should just be cloned, as they are shared internally anyway --
-    // another layer of sharing only adds unnecessary overhead...)
-    nosync_marker: PhantomData<Cell<()>>,
 }
 
 impl Drop for SharedFileDescriptor {
     fn drop(&mut self) {
         unsafe {
             let result = libc::close(self.0);
             assert!(thread::panicking() || result == 0);
         }
     }
 }
 
 impl OsIpcSender {
     fn from_fd(fd: c_int) -> OsIpcSender {
         OsIpcSender {
             fd: Arc::new(SharedFileDescriptor(fd)),
-            nosync_marker: PhantomData,
         }
     }
 
     /// Maximum size of the kernel buffer used for transfers over this channel.
     ///
     /// Note: This is *not* the actual maximal packet size we are allowed to use...
     /// Some of it is reserved by the kernel for bookkeeping.
     fn get_system_sendbuf_size(&self) -> Result<usize,UnixError> {
@@ -222,22 +188,22 @@ impl OsIpcSender {
         // (i.e. the length of the data buffer passed in),
         // which in a fragmented send will be smaller than the total message length.
         fn send_first_fragment(sender_fd: c_int, fds: &[c_int], data_buffer: &[u8], len: usize)
                                -> Result<(),UnixError> {
             let result = unsafe {
                 let cmsg_length = mem::size_of_val(fds);
                 let (cmsg_buffer, cmsg_space) = if cmsg_length > 0 {
                     let cmsg_buffer = libc::malloc(CMSG_SPACE(cmsg_length)) as *mut cmsghdr;
-                    (*cmsg_buffer).cmsg_len = CMSG_LEN(cmsg_length) as MsgControlLen;
+                    (*cmsg_buffer).cmsg_len = CMSG_LEN(cmsg_length);
                     (*cmsg_buffer).cmsg_level = libc::SOL_SOCKET;
                     (*cmsg_buffer).cmsg_type = SCM_RIGHTS;
 
                     ptr::copy_nonoverlapping(fds.as_ptr(),
-                                             CMSG_DATA(cmsg_buffer) as *mut c_int,
+                                             cmsg_buffer.offset(1) as *mut c_int,
                                              fds.len());
                     (cmsg_buffer, CMSG_SPACE(cmsg_length))
                 } else {
                     (ptr::null_mut(), 0)
                 };
 
                 let mut iovec = [
                     // First fragment begins with a header recording the total data length.
@@ -254,19 +220,19 @@ impl OsIpcSender {
                         iov_len: data_buffer.len(),
                     },
                 ];
 
                 let msghdr = msghdr {
                     msg_name: ptr::null_mut(),
                     msg_namelen: 0,
                     msg_iov: iovec.as_mut_ptr(),
-                    msg_iovlen: iovec.len() as IovLen,
+                    msg_iovlen: iovec.len(),
                     msg_control: cmsg_buffer as *mut c_void,
-                    msg_controllen: cmsg_space as MsgControlLen,
+                    msg_controllen: cmsg_space,
                     msg_flags: 0,
                 };
 
                 let result = sendmsg(sender_fd, &msghdr, 0);
                 libc::free(cmsg_buffer as *mut c_void);
                 result
             };
 
@@ -381,17 +347,25 @@ impl OsIpcSender {
 
         Ok(())
     }
 
     pub fn connect(name: String) -> Result<OsIpcSender,UnixError> {
         let name = CString::new(name).unwrap();
         unsafe {
             let fd = libc::socket(libc::AF_UNIX, SOCK_SEQPACKET, 0);
-            let (sockaddr, len) = new_sockaddr_un(name.as_ptr());
+            let mut sockaddr = sockaddr_un {
+                sun_family: libc::AF_UNIX as u16,
+                sun_path: [ 0; 108 ],
+            };
+            libc::strncpy(sockaddr.sun_path.as_mut_ptr(),
+                          name.as_ptr(),
+                          sockaddr.sun_path.len() - 1);
+
+            let len = mem::size_of::<c_short>() + libc::strlen(sockaddr.sun_path.as_ptr());
             if libc::connect(fd, &sockaddr as *const _ as *const sockaddr, len as socklen_t) < 0 {
                 return Err(UnixError::last())
             }
 
             Ok(OsIpcSender::from_fd(fd))
         }
     }
 }
@@ -553,17 +527,26 @@ impl OsIpcOneShotServer {
             let mut path: Vec<u8>;
             loop {
                 let path_string = CString::new(&b"/tmp/rust-ipc-socket.XXXXXX"[..]).unwrap();
                 path = path_string.as_bytes_with_nul().iter().cloned().collect();
                 if *mktemp(path.as_mut_ptr() as *mut c_char) == 0 {
                     return Err(UnixError::last())
                 }
 
-                let (sockaddr, len) = new_sockaddr_un(path.as_ptr() as *const c_char);
+                let mut sockaddr = sockaddr_un {
+                    sun_family: libc::AF_UNIX as c_ushort,
+                    sun_path: [ 0; 108 ],
+                };
+                libc::strncpy(sockaddr.sun_path.as_mut_ptr(),
+                              path.as_ptr() as *const c_char,
+                              sockaddr.sun_path.len() - 1);
+
+                let len = mem::size_of::<c_short>() + (libc::strlen(sockaddr.sun_path.as_ptr()) as
+                                                       usize);
                 if libc::bind(fd, &sockaddr as *const _ as *const sockaddr, len as socklen_t) == 0 {
                     break
                 }
 
                 let errno = UnixError::last();
                 if errno.0 != libc::EINVAL {
                     return Err(errno)
                 }
@@ -838,16 +821,22 @@ fn recv(fd: c_int, blocking_mode: Blocki
             return Err(UnixError::last())
         };
     }
 
     Ok((main_data_buffer, channels, shared_memory_regions))
 }
 
 #[cfg(target_os="android")]
+const TEMP_FILE_TEMPLATE: &'static str = "/sdcard/servo/ipc-channel-shared-memory.XXXXXX";
+
+#[cfg(not(target_os="android"))]
+const TEMP_FILE_TEMPLATE: &'static str = "/tmp/ipc-channel-shared-memory.XXXXXX";
+
+#[cfg(target_os="android")]
 fn maybe_unlink(_: *const c_char) -> c_int {
     // Calling `unlink` on a file stored on an sdcard immediately deletes it.
     // FIXME: use a better temporary directory than the sdcard via the Java APIs
     // and threading that value into Servo.
     // https://code.google.com/p/android/issues/detail?id=19017
     0
 }
 
@@ -909,19 +898,19 @@ impl UnixCmsg {
             mem::size_of::<c_int>();
         let cmsg_buffer = libc::malloc(cmsg_length) as *mut cmsghdr;
         UnixCmsg {
             cmsg_buffer: cmsg_buffer,
             msghdr: msghdr {
                 msg_name: ptr::null_mut(),
                 msg_namelen: 0,
                 msg_iov: iovec.as_mut_ptr(),
-                msg_iovlen: iovec.len() as IovLen,
+                msg_iovlen: iovec.len(),
                 msg_control: cmsg_buffer as *mut c_void,
-                msg_controllen: cmsg_length as MsgControlLen,
+                msg_controllen: cmsg_length,
                 msg_flags: 0,
             },
         }
     }
 
     unsafe fn recv(&mut self, fd: c_int, blocking_mode: BlockingMode)
                    -> Result<usize, UnixError> {
         if let BlockingMode::Nonblocking = blocking_mode {
@@ -943,44 +932,40 @@ impl UnixCmsg {
             if libc::fcntl(fd, libc::F_SETFL, 0) < 0 {
                 return Err(UnixError::last())
             }
         }
         result
     }
 
     unsafe fn cmsg_len(&self) -> size_t {
-        (*(self.msghdr.msg_control as *const cmsghdr)).cmsg_len as size_t
+        (*(self.msghdr.msg_control as *const cmsghdr)).cmsg_len
     }
 }
 
 fn is_socket(fd: c_int) -> bool {
     unsafe {
         let mut st = mem::uninitialized();
         if libc::fstat(fd, &mut st) != 0 {
             return false
         }
         S_ISSOCK(st.st_mode as mode_t)
     }
 }
 
 // FFI stuff follows:
 
+const SCM_RIGHTS: c_int = 0x01;
+
 #[allow(non_snake_case)]
 fn CMSG_LEN(length: size_t) -> size_t {
     CMSG_ALIGN(mem::size_of::<cmsghdr>()) + length
 }
 
 #[allow(non_snake_case)]
-unsafe fn CMSG_DATA(cmsg: *mut cmsghdr) -> *mut c_void {
-    (cmsg as *mut libc::c_uchar).offset(CMSG_ALIGN(
-            mem::size_of::<cmsghdr>()) as isize) as *mut c_void
-}
-
-#[allow(non_snake_case)]
 fn CMSG_ALIGN(length: size_t) -> size_t {
     (length + mem::size_of::<size_t>() - 1) & !(mem::size_of::<size_t>() - 1)
 }
 
 #[allow(non_snake_case)]
 fn CMSG_SPACE(length: size_t) -> size_t {
     CMSG_ALIGN(length) + CMSG_ALIGN(mem::size_of::<cmsghdr>())
 }
@@ -992,17 +977,17 @@ fn S_ISSOCK(mode: mode_t) -> bool {
 
 extern {
     fn mktemp(template: *mut c_char) -> *mut c_char;
     fn strdup(string: *const c_char) -> *mut c_char;
 }
 
 #[repr(C)]
 struct cmsghdr {
-    cmsg_len: MsgControlLen,
+    cmsg_len: size_t,
     cmsg_level: c_int,
     cmsg_type: c_int,
 }
 
 #[repr(C)]
 struct linger {
     l_onoff: c_int,
     l_linger: c_int,
--- a/third_party/rust/ipc-channel/src/platform/macos/mod.rs
+++ b/third_party/rust/ipc-channel/src/platform/macos/mod.rs
@@ -14,17 +14,16 @@ use self::mach_sys::{mach_port_right_t, 
 
 use bincode::serde::DeserializeError;
 use libc::{self, c_char, c_uint, c_void, size_t};
 use rand::{self, Rng};
 use std::cell::Cell;
 use std::ffi::CString;
 use std::fmt::{self, Debug, Formatter};
 use std::io::{Error, ErrorKind};
-use std::marker::PhantomData;
 use std::mem;
 use std::ops::Deref;
 use std::ptr;
 use std::slice;
 use std::usize;
 
 mod mach_sys;
 
@@ -302,21 +301,16 @@ impl OsIpcReceiver {
                     -> Result<(Vec<u8>, Vec<OsOpaqueIpcChannel>, Vec<OsIpcSharedMemory>),MachError> {
         self.recv_with_blocking_mode(BlockingMode::Nonblocking)
     }
 }
 
 #[derive(PartialEq, Debug)]
 pub struct OsIpcSender {
     port: mach_port_t,
-    // Make sure this is `!Sync`, to match `mpsc::Sender`; and to discourage sharing references.
-    //
-    // (Rather, senders should just be cloned, as they are shared internally anyway --
-    // another layer of sharing only adds unnecessary overhead...)
-    nosync_marker: PhantomData<Cell<()>>,
 }
 
 impl Drop for OsIpcSender {
     fn drop(&mut self) {
         unsafe {
             let error = mach_sys::mach_port_mod_refs(mach_task_self(),
                                                      self.port,
                                                      MACH_PORT_RIGHT_SEND,
@@ -335,26 +329,24 @@ impl Clone for OsIpcSender {
         unsafe {
             assert!(mach_sys::mach_port_mod_refs(mach_task_self(),
                                                  self.port,
                                                  MACH_PORT_RIGHT_SEND,
                                                  1) == KERN_SUCCESS);
         }
         OsIpcSender {
             port: self.port,
-            nosync_marker: PhantomData,
         }
     }
 }
 
 impl OsIpcSender {
     fn from_name(port: mach_port_t) -> OsIpcSender {
         OsIpcSender {
             port: port,
-            nosync_marker: PhantomData,
         }
     }
 
     pub fn connect(name: String) -> Result<OsIpcSender,MachError> {
         unsafe {
             let mut bootstrap_port = 0;
             let os_result = mach_sys::task_get_special_port(mach_task_self(),
                                                             TASK_BOOTSTRAP_PORT,
@@ -483,17 +475,16 @@ impl OsOpaqueIpcChannel {
         OsOpaqueIpcChannel {
             port: name,
         }
     }
 
     pub fn to_sender(&mut self) -> OsIpcSender {
         OsIpcSender {
             port: mem::replace(&mut self.port, MACH_PORT_NULL),
-            nosync_marker: PhantomData,
         }
     }
 
     pub fn to_receiver(&mut self) -> OsIpcReceiver {
         OsIpcReceiver::from_name(mem::replace(&mut self.port, MACH_PORT_NULL))
     }
 }
 
@@ -524,17 +515,22 @@ impl OsIpcReceiverSet {
         if os_result == KERN_SUCCESS {
             Ok(receiver_port as i64)
         } else {
             Err(MachError(os_result))
         }
     }
 
     pub fn select(&mut self) -> Result<Vec<OsIpcSelectionResult>,MachError> {
-        select(self.port.get(), BlockingMode::Blocking).map(|result| vec![result])
+        match select(self.port.get(), BlockingMode::Blocking).map(|result| vec![result]) {
+            Ok(results) => Ok(results),
+            Err(error) => {
+                Err(error)
+            }
+        }
     }
 }
 
 pub enum OsIpcSelectionResult {
     DataReceived(i64, Vec<u8>, Vec<OsOpaqueIpcChannel>, Vec<OsIpcSharedMemory>),
     ChannelClosed(i64),
 }
 
@@ -572,39 +568,43 @@ fn select(port: mach_port_t, blocking_mo
         match mach_sys::mach_msg(message as *mut _,
                                  flags,
                                  0,
                                  (*message).header.msgh_size,
                                  port,
                                  timeout,
                                  MACH_PORT_NULL) {
             MACH_RCV_TOO_LARGE => {
-                // the actual size gets written into msgh_size by the kernel
-                let max_trailer_size = mem::size_of::<mach_sys::mach_msg_max_trailer_t>() as mach_sys::mach_msg_size_t;
-                let actual_size = (*message).header.msgh_size + max_trailer_size;
-                allocated_buffer = Some(libc::malloc(actual_size as size_t));
-                setup_receive_buffer(slice::from_raw_parts_mut(
-                                        allocated_buffer.unwrap() as *mut u8,
-                                        actual_size as usize),
-                                     port);
-                message = allocated_buffer.unwrap() as *mut Message;
-                match mach_sys::mach_msg(message as *mut _,
-                                         flags,
-                                         0,
-                                         actual_size,
-                                         port,
-                                         timeout,
-                                         MACH_PORT_NULL) {
-                    MACH_MSG_SUCCESS => {},
-                    MACH_RCV_TOO_LARGE => {
-                        panic!("message was bigger than we were told");
-                    }
-                    os_result => {
-                        libc::free(allocated_buffer.unwrap() as *mut _);
-                        return Err(MachError(os_result))
+                // Do a loop. There's no way I know of to figure out precisely in advance how big
+                // the message actually is!
+                let mut extra_size = 8;
+                loop {
+                    let actual_size = (*message).header.msgh_size + extra_size;
+                    allocated_buffer = Some(libc::malloc(actual_size as size_t));
+                    setup_receive_buffer(slice::from_raw_parts_mut(
+                                            allocated_buffer.unwrap() as *mut u8,
+                                            actual_size as usize),
+                                         port);
+                    message = allocated_buffer.unwrap() as *mut Message;
+                    match mach_sys::mach_msg(message as *mut _,
+                                             flags,
+                                             0,
+                                             actual_size,
+                                             port,
+                                             timeout,
+                                             MACH_PORT_NULL) {
+                        MACH_MSG_SUCCESS => break,
+                        MACH_RCV_TOO_LARGE => {
+                            libc::free(allocated_buffer.unwrap() as *mut _);
+                            extra_size *= 2;
+                        }
+                        os_result => {
+                            libc::free(allocated_buffer.unwrap() as *mut _);
+                            return Err(MachError(os_result))
+                        }
                     }
                 }
             }
             MACH_MSG_SUCCESS => {}
             os_result => return Err(MachError(os_result)),
         }
 
         let local_port = (*message).header.msgh_local_port;
@@ -646,42 +646,46 @@ fn select(port: mach_port_t, blocking_mo
         Ok(OsIpcSelectionResult::DataReceived(local_port as i64,
                                              payload,
                                              ports,
                                              shared_memory_regions))
     }
 }
 
 pub struct OsIpcOneShotServer {
-    receiver: OsIpcReceiver,
+    receiver: Option<OsIpcReceiver>,
     name: String,
 }
 
 impl Drop for OsIpcOneShotServer {
     fn drop(&mut self) {
         drop(OsIpcReceiver::unregister_global_name(mem::replace(&mut self.name, String::new())));
     }
 }
 
 impl OsIpcOneShotServer {
     pub fn new() -> Result<(OsIpcOneShotServer, String),MachError> {
         let receiver = try!(OsIpcReceiver::new());
         let name = try!(receiver.register_bootstrap_name());
         Ok((OsIpcOneShotServer {
-            receiver: receiver,
+            receiver: Some(receiver),
             name: name.clone(),
         }, name))
     }
 
-    pub fn accept(self) -> Result<(OsIpcReceiver,
-                                   Vec<u8>,
-                                   Vec<OsOpaqueIpcChannel>,
-                                   Vec<OsIpcSharedMemory>),MachError> {
-        let (bytes, channels, shared_memory_regions) = try!(self.receiver.recv());
-        Ok((self.receiver.consume(), bytes, channels, shared_memory_regions))
+    pub fn accept(mut self) -> Result<(OsIpcReceiver,
+                                       Vec<u8>,
+                                       Vec<OsOpaqueIpcChannel>,
+                                       Vec<OsIpcSharedMemory>),MachError> {
+        let (bytes, channels, shared_memory_regions) =
+            try!(self.receiver.as_mut().unwrap().recv());
+        Ok((mem::replace(&mut self.receiver, None).unwrap(),
+            bytes,
+            channels,
+            shared_memory_regions))
     }
 }
 
 pub struct OsIpcSharedMemory {
     ptr: *mut u8,
     length: usize,
 }
 
--- a/third_party/rust/ipc-channel/src/platform/mod.rs
+++ b/third_party/rust/ipc-channel/src/platform/mod.rs
@@ -3,24 +3,23 @@
 //
 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
 mod os {
-    #[cfg(all(not(feature = "force-inprocess"), any(target_os = "linux",
-                                                    target_os = "freebsd")))]
-    include!("unix/mod.rs");
+    #[cfg(target_os = "linux")]
+    include!("linux/mod.rs");
 
-    #[cfg(all(not(feature = "force-inprocess"), target_os = "macos"))]
+    #[cfg(target_os = "macos")]
     include!("macos/mod.rs");
 
-    #[cfg(any(feature = "force-inprocess", target_os = "windows", target_os = "android"))]
+    #[cfg(any(target_os = "windows", target_os = "android"))]
     include!("inprocess/mod.rs");
 }
 
 pub use self::os::{OsIpcChannel, OsIpcOneShotServer, OsIpcReceiver, OsIpcReceiverSet};
 pub use self::os::{OsIpcSelectionResult, OsIpcSender, OsIpcSharedMemory};
 pub use self::os::{OsOpaqueIpcChannel, channel};
 
 #[cfg(test)]
--- a/third_party/rust/ipc-channel/src/platform/test.rs
+++ b/third_party/rust/ipc-channel/src/platform/test.rs
@@ -2,26 +2,24 @@
 // file at the top-level directory of this distribution.
 //
 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use platform::{self, OsIpcChannel, OsIpcReceiverSet};
+use libc;
+use platform::{self, OsIpcChannel, OsIpcReceiverSet, OsIpcSender, OsIpcOneShotServer};
 use platform::{OsIpcSharedMemory};
 use std::sync::Arc;
 use std::time::{Duration, Instant};
 use std::thread;
 
-#[cfg(not(any(feature = "force-inprocess", target_os = "windows", target_os = "android")))]
-use libc;
-use platform::{OsIpcSender, OsIpcOneShotServer};
-#[cfg(not(any(feature = "force-inprocess", target_os = "windows", target_os = "android")))]
+#[cfg(not(windows))]
 use test::{fork, Wait};
 
 #[test]
 fn simple() {
     let (tx, rx) = platform::channel().unwrap();
     let data: &[u8] = b"1234567";
     tx.send(data, Vec::new(), Vec::new()).unwrap();
     let (mut received_data, received_channels, received_shared_memory) = rx.recv().unwrap();
@@ -170,18 +168,16 @@ fn big_data_with_sender_transfer() {
         sub_rx.recv().unwrap();
     received_data.truncate(65536);
     assert_eq!(received_data.len(), data.len());
     assert_eq!((&received_data[..], received_channels, received_shared_memory_regions),
                (&data[..], vec![], vec![]));
     thread.join().unwrap();
 }
 
-#[cfg(all(not(feature = "force-inprocess"), any(target_os = "linux",
-                                                target_os = "freebsd")))]
 fn with_n_fds(n: usize, size: usize) {
     let (sender_fds, receivers): (Vec<_>, Vec<_>) = (0..n).map(|_| platform::channel().unwrap())
                                                     .map(|(tx, rx)| (OsIpcChannel::Sender(tx), rx))
                                                     .unzip();
     let (super_tx, super_rx) = platform::channel().unwrap();
 
     let data: Vec<u8> = (0..size).map(|i| (i % 251) as u8).collect();
     super_tx.send(&data[..], sender_fds, vec![]).unwrap();
@@ -203,18 +199,17 @@ fn with_n_fds(n: usize, size: usize) {
         received_data.truncate(65536);
         assert_eq!(received_data.len(), data.len());
         assert_eq!((&received_data[..], received_channels, received_shared_memory_regions),
                    (&data[..], vec![], vec![]));
     }
 }
 
 // These tests only apply to platforms that need fragmentation.
-#[cfg(all(not(feature = "force-inprocess"), any(target_os = "linux",
-                                                target_os = "freebsd")))]
+#[cfg(target_os="linux")]
 mod fragment_tests {
     use platform;
     use super::with_n_fds;
 
     lazy_static! {
         static ref FRAGMENT_SIZE: usize = {
             platform::OsIpcSender::get_max_fragment_size()
         };
@@ -392,54 +387,37 @@ fn receiver_set() {
             } else if received_id == rx1_id {
                 assert!(!received1);
                 received1 = true;
             }
         }
     }
 }
 
-#[cfg(not(any(feature = "force-inprocess", target_os = "android")))]
 #[test]
-fn server_accept_first() {
+//XXXjdm This hangs indefinitely on appveyor and warrants further investigation.
+#[cfg(not(windows))]
+fn server() {
     let (server, name) = OsIpcOneShotServer::new().unwrap();
     let data: &[u8] = b"1234567";
 
     thread::spawn(move || {
-        thread::sleep(Duration::from_millis(30));
         let tx = OsIpcSender::connect(name).unwrap();
         tx.send(data, vec![], vec![]).unwrap();
     });
 
     let (_, mut received_data, received_channels, received_shared_memory_regions) =
         server.accept().unwrap();
     received_data.truncate(7);
     assert_eq!((&received_data[..], received_channels, received_shared_memory_regions),
                (data, vec![], vec![]));
 }
 
-#[test]
-fn server_connect_first() {
-    let (server, name) = OsIpcOneShotServer::new().unwrap();
-    let data: &[u8] = b"1234567";
-
-    thread::spawn(move || {
-        let tx = OsIpcSender::connect(name).unwrap();
-        tx.send(data, vec![], vec![]).unwrap();
-    });
-
-    thread::sleep(Duration::from_millis(30));
-    let (_, mut received_data, received_channels, received_shared_memory_regions) =
-        server.accept().unwrap();
-    received_data.truncate(7);
-    assert_eq!((&received_data[..], received_channels, received_shared_memory_regions),
-               (data, vec![], vec![]));
-}
-
-#[cfg(not(any(feature = "force-inprocess", target_os = "windows", target_os = "android")))]
+///XXXjdm Windows' libc doesn't include fork.
+#[cfg(not(windows))]
 #[test]
 fn cross_process() {
     let (server, name) = OsIpcOneShotServer::new().unwrap();
     let data: &[u8] = b"1234567";
 
     let child_pid = unsafe { fork(|| {
         let tx = OsIpcSender::connect(name).unwrap();
         tx.send(data, vec![], vec![]).unwrap();
@@ -449,17 +427,18 @@ fn cross_process() {
     let (_, mut received_data, received_channels, received_shared_memory_regions) =
         server.accept().unwrap();
     child_pid.wait();
     received_data.truncate(7);
     assert_eq!((&received_data[..], received_channels, received_shared_memory_regions),
                (data, vec![], vec![]));
 }
 
-#[cfg(not(any(feature = "force-inprocess", target_os = "windows", target_os = "android")))]
+///XXXjdm Windows' libc doesn't include fork.
+#[cfg(not(windows))]
 #[test]
 fn cross_process_sender_transfer() {
     let (server, name) = OsIpcOneShotServer::new().unwrap();
 
     let child_pid = unsafe { fork(|| {
         let super_tx = OsIpcSender::connect(name).unwrap();
         let (sub_tx, sub_rx) = platform::channel().unwrap();
         let data: &[u8] = b"foo";
@@ -624,32 +603,8 @@ fn try_recv_large_delayed() {
     assert!(rx.try_recv().is_err()); // There should be no further messages pending.
     received_vals.sort();
     assert_eq!(received_vals, (0..num_senders).collect::<Vec<_>>()); // Got exactly the values we sent.
 
     for thread in threads {
         thread.join().unwrap();
     }
 }
-
-#[cfg(feature = "unstable")]
-mod sync_test {
-    use platform;
-
-    trait SyncTest {
-        fn test_not_sync();
-    }
-
-    impl<T> SyncTest for T {
-        default fn test_not_sync() {}
-    }
-
-    impl<T: Sync> SyncTest for T {
-        fn test_not_sync() {
-            panic!("`OsIpcSender` should not be `Sync`");
-        }
-    }
-
-    #[test]
-    fn receiver_not_sync() {
-        platform::OsIpcSender::test_not_sync();
-    }
-}
--- a/third_party/rust/ipc-channel/src/test.rs
+++ b/third_party/rust/ipc-channel/src/test.rs
@@ -2,35 +2,29 @@
 // file at the top-level directory of this distribution.
 //
 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use ipc::{self, IpcReceiver, IpcReceiverSet, IpcSender, IpcSharedMemory};
+use ipc::{self, IpcOneShotServer, IpcReceiver, IpcReceiverSet, IpcSender, IpcSharedMemory};
 use ipc::{OpaqueIpcSender};
 use router::ROUTER;
 use libc;
-use serde::{Deserialize, Deserializer, Serialize, Serializer};
-use std::cell::RefCell;
+use std::io::Error;
 use std::iter;
 use std::ptr;
 use std::sync::Arc;
 use std::sync::mpsc::{self, Sender};
 use std::thread;
 
-#[cfg(not(any(feature = "force-inprocess", target_os = "windows", target_os = "android")))]
-use ipc::IpcOneShotServer;
-
-#[cfg(not(any(feature = "force-inprocess", target_os = "windows", target_os = "android")))]
-use std::io::Error;
-
-#[cfg(not(any(feature = "force-inprocess", target_os = "windows", target_os = "android")))]
+///XXXjdm Windows' libc doesn't include fork.
+#[cfg(not(windows))]
 // I'm not actually sure invoking this is indeed unsafe -- but better safe than sorry...
 pub unsafe fn fork<F: FnOnce()>(child_func: F) -> libc::pid_t {
     match libc::fork() {
         -1 => panic!("Fork failed: {}", Error::last_os_error()),
         0 => { child_func(); unreachable!() },
         pid => pid,
     }
 }
@@ -130,18 +124,19 @@ fn select() {
             } else if received_id == rx1_id {
                 assert!(!received1);
                 received1 = true;
             }
         }
     }
 }
 
-#[cfg(not(any(feature = "force-inprocess", target_os = "windows", target_os = "android")))]
 #[test]
+///XXXjdm Windows' libc doesn't include fork.
+#[cfg(not(windows))]
 fn cross_process_embedded_senders() {
     let person = ("Patrick Walton".to_owned(), 29);
     let (server0, server0_name) = IpcOneShotServer::new().unwrap();
     let (server2, server2_name) = IpcOneShotServer::new().unwrap();
     let child_pid = unsafe { fork(|| {
         let (tx1, rx1): (IpcSender<Person>, IpcReceiver<Person>) = ipc::channel().unwrap();
         let tx0 = IpcSender::connect(server0_name).unwrap();
         tx0.send(tx1).unwrap();
@@ -384,45 +379,8 @@ fn test_so_linger() {
     sender.send(42).unwrap();
     drop(sender);
     let val = match receiver.recv() {
         Ok(val) => val,
         Err(e) => { panic!("err: `{}`", e); }
     };
     assert_eq!(val, 42);
 }
-
-#[derive(Clone, Debug, Eq, PartialEq)]
-struct HasWeirdSerializer (Option<String>);
-
-thread_local! { static WEIRD_CHANNEL: RefCell<Option<IpcSender<HasWeirdSerializer>>> = RefCell::new(None) }
-
-impl Serialize for HasWeirdSerializer {
-    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
-        where S: Serializer
-    {
-        if self.0.is_some() {
-            WEIRD_CHANNEL.with(|chan| { chan.borrow().as_ref().unwrap().send(HasWeirdSerializer(None)).unwrap(); });
-        }
-        self.0.serialize(serializer)
-    }
-}
-
-impl Deserialize for HasWeirdSerializer {
-    fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
-        where D: Deserializer
-    {
-        Ok(HasWeirdSerializer(try!(Deserialize::deserialize(deserializer))))
-    }
-}
-
-#[test]
-fn test_reentrant() {
-    let null = HasWeirdSerializer(None);
-    let hello = HasWeirdSerializer(Some(String::from("hello")));
-    let (sender, receiver) = ipc::channel().unwrap();
-    WEIRD_CHANNEL.with(|chan| { *chan.borrow_mut() = Some(sender.clone()); });
-    sender.send(hello.clone()).unwrap();
-    assert_eq!(null, receiver.recv().unwrap());
-    assert_eq!(hello, receiver.recv().unwrap());
-    sender.send(null.clone()).unwrap();
-    assert_eq!(null, receiver.recv().unwrap());
-}
--- a/toolkit/library/gtest/rust/Cargo.lock
+++ b/toolkit/library/gtest/rust/Cargo.lock
@@ -110,16 +110,40 @@ dependencies = [
 name = "deque"
 version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
+name = "dwrite-sys"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "dwrote"
+version = "0.1.0"
+source = "git+https://github.com/vvuk/dwrote-rs#7112cf6e4bb9f645217dacb5d7470178da13a544"
+dependencies = [
+ "dwrite-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 0.8.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_codegen 0.8.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "euclid"
 version = "0.10.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "heapsize 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-serialize 0.3.21 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -156,17 +180,17 @@ dependencies = [
 
 [[package]]
 name = "gkrust-shared"
 version = "0.1.0"
 dependencies = [
  "mp4parse_capi 0.6.0",
  "nsstring 0.1.0",
  "rust_url_capi 0.0.1",
- "webrender 0.8.0",
+ "webrender 0.9.0",
 ]
 
 [[package]]
 name = "gl_generator"
 version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "khronos_api 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -198,17 +222,17 @@ source = "registry+https://github.com/ru
 dependencies = [
  "matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-bidi 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "ipc-channel"
-version = "0.6.0"
+version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "bincode 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
  "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 0.8.17 (registry+https://github.com/rust-lang/crates.io-index)",
  "uuid 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -529,53 +553,55 @@ name = "uuid"
 version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "webrender"
-version = "0.8.0"
+version = "0.9.0"
 dependencies = [
  "app_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bincode 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-foundation 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-text 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dwrote 0.1.0 (git+https://github.com/vvuk/dwrote-rs)",
  "euclid 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "freetype 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "gleam 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
- "ipc-channel 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ipc-channel 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)",
  "offscreen_gl_context 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "rayon 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
- "webrender_traits 0.8.0",
+ "webrender_traits 0.9.0",
  "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "webrender_traits"
-version = "0.8.0"
+version = "0.9.0"
 dependencies = [
  "app_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dwrote 0.1.0 (git+https://github.com/vvuk/dwrote-rs)",
  "euclid 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "gleam 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
- "ipc-channel 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ipc-channel 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "offscreen_gl_context 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 0.8.17 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_codegen 0.8.17 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "winapi"
 version = "0.2.8"
@@ -612,26 +638,28 @@ dependencies = [
 "checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855"
 "checksum cgl 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8bdd78cca65a739cb5475dbf6b6bbb49373e327f4a6f2b499c0f98632df38c10"
 "checksum cmake 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "0e5bcf27e097a184c1df4437654ed98df3d7a516e8508a6ba45d8b092bbdf283"
 "checksum core-foundation 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "20a6d0448d3a99d977ae4a2aa5a98d886a923e863e81ad9ff814645b6feb3bbd"
 "checksum core-foundation-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "05eed248dc504a5391c63794fe4fb64f46f071280afaa1b73308f3c0ce4574c5"
 "checksum core-graphics 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "66e998abb8823fecd2a8a7205429b17a340d447d8c69b3bce86846dcdea3e33b"
 "checksum core-text 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2debbf22a8358e5e270e958b6d65694667be7a2ef9c3a2bf05a0872a3124dc98"
 "checksum deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1614659040e711785ed8ea24219140654da1729f3ec8a47a9719d041112fe7bf"
+"checksum dwrite-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a7918280f33862bc8542212d74f2149b1a87ab402fd15f4ce9a1c56582958d6e"
+"checksum dwrote 0.1.0 (git+https://github.com/vvuk/dwrote-rs)" = "<none>"
 "checksum euclid 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "44ef2a3e4a621518e488db36820a12b49a9d5004764b8daf1458bbe5d7c9b626"
 "checksum fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc484842f1e2884faf56f529f960cc12ad8c71ce96cc7abba0a067c98fee344"
 "checksum freetype 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a89563eaf185762cf495c56cb16277549d2aaa7b1240d93338e8429fa33acd1"
 "checksum gcc 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)" = "553f11439bdefe755bf366b264820f1da70f3aaf3924e594b886beb9c831bcf5"
 "checksum gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0912515a8ff24ba900422ecda800b52f4016a56251922d397c576bf92c690518"
 "checksum gl_generator 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f1d8edc81c5ae84605a62f5dac661a2313003b26d59839f81d47d46cf0f16a55"
 "checksum gleam 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b04d6c8a1df841e48dfe99ed67829c9d1d17b1bb3e44c5f3283992010e20359b"
 "checksum heapsize 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8c80e194758495a9109566134dc06e42ea0423987d6ceca016edaa90381b3549"
 "checksum idna 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1053236e00ce4f668aeca4a769a09b3bf5a682d802abd6f3cb39374f6b162c11"
-"checksum ipc-channel 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f379ec6400f5cb93edbbb0ac56de3bf38331e22cbae9e27a10f04c1a0b4f8e90"
+"checksum ipc-channel 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "675587430ede6756dd03fdfdf9888f22f83855fd131c8451d842a710b059e571"
 "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
 "checksum khronos_api 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "09c9d3760673c427d46f91a0350f0a84a52e6bc5a84adf26dc610b6c52436630"
 "checksum lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6abe0ee2e758cd6bc8a2cd56726359007748fbf4128da998b65d0b70f881e19b"
 "checksum libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)" = "044d1360593a78f5c8e5e710beccdc24ab71d1f01bc19a29bcacdba22e8475d8"
 "checksum log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054"
 "checksum matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "efd7622e3022e1a6eaa602c4cea8912254e5582c9c692e9167714182244801b1"
 "checksum num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "a16a42856a256b39c6d3484f097f6713e14feacd9bfb02290917904fae46c81c"
 "checksum num_cpus 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8890e6084723d57d0df8d2720b0d60c6ee67d6c93e7169630e4371e88765dcad"
--- a/toolkit/library/rust/Cargo.lock
+++ b/toolkit/library/rust/Cargo.lock
@@ -108,16 +108,40 @@ dependencies = [
 name = "deque"
 version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
+name = "dwrite-sys"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "dwrote"
+version = "0.1.0"
+source = "git+https://github.com/vvuk/dwrote-rs#7112cf6e4bb9f645217dacb5d7470178da13a544"
+dependencies = [
+ "dwrite-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 0.8.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_codegen 0.8.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "euclid"
 version = "0.10.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "heapsize 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-serialize 0.3.21 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -154,17 +178,17 @@ dependencies = [
 
 [[package]]
 name = "gkrust-shared"
 version = "0.1.0"
 dependencies = [
  "mp4parse_capi 0.6.0",
  "nsstring 0.1.0",
  "rust_url_capi 0.0.1",
- "webrender 0.8.0",
+ "webrender 0.9.0",
 ]
 
 [[package]]
 name = "gl_generator"
 version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "khronos_api 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -196,17 +220,17 @@ source = "registry+https://github.com/ru
 dependencies = [
  "matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-bidi 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "ipc-channel"
-version = "0.6.0"
+version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "bincode 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
  "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 0.8.17 (registry+https://github.com/rust-lang/crates.io-index)",
  "uuid 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -516,53 +540,55 @@ name = "uuid"
 version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "webrender"
-version = "0.8.0"
+version = "0.9.0"
 dependencies = [
  "app_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bincode 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-foundation 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-text 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dwrote 0.1.0 (git+https://github.com/vvuk/dwrote-rs)",
  "euclid 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "freetype 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "gleam 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
- "ipc-channel 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ipc-channel 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)",
  "offscreen_gl_context 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "rayon 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
- "webrender_traits 0.8.0",
+ "webrender_traits 0.9.0",
  "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "webrender_traits"
-version = "0.8.0"
+version = "0.9.0"
 dependencies = [
  "app_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dwrote 0.1.0 (git+https://github.com/vvuk/dwrote-rs)",
  "euclid 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "gleam 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
- "ipc-channel 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ipc-channel 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "offscreen_gl_context 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 0.8.17 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_codegen 0.8.17 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "winapi"
 version = "0.2.8"
@@ -599,26 +625,28 @@ dependencies = [
 "checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855"
 "checksum cgl 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8bdd78cca65a739cb5475dbf6b6bbb49373e327f4a6f2b499c0f98632df38c10"
 "checksum cmake 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "0e5bcf27e097a184c1df4437654ed98df3d7a516e8508a6ba45d8b092bbdf283"
 "checksum core-foundation 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "20a6d0448d3a99d977ae4a2aa5a98d886a923e863e81ad9ff814645b6feb3bbd"
 "checksum core-foundation-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "05eed248dc504a5391c63794fe4fb64f46f071280afaa1b73308f3c0ce4574c5"
 "checksum core-graphics 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "66e998abb8823fecd2a8a7205429b17a340d447d8c69b3bce86846dcdea3e33b"
 "checksum core-text 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2debbf22a8358e5e270e958b6d65694667be7a2ef9c3a2bf05a0872a3124dc98"
 "checksum deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1614659040e711785ed8ea24219140654da1729f3ec8a47a9719d041112fe7bf"
+"checksum dwrite-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a7918280f33862bc8542212d74f2149b1a87ab402fd15f4ce9a1c56582958d6e"
+"checksum dwrote 0.1.0 (git+https://github.com/vvuk/dwrote-rs)" = "<none>"
 "checksum euclid 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "44ef2a3e4a621518e488db36820a12b49a9d5004764b8daf1458bbe5d7c9b626"
 "checksum fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc484842f1e2884faf56f529f960cc12ad8c71ce96cc7abba0a067c98fee344"
 "checksum freetype 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a89563eaf185762cf495c56cb16277549d2aaa7b1240d93338e8429fa33acd1"
 "checksum gcc 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)" = "553f11439bdefe755bf366b264820f1da70f3aaf3924e594b886beb9c831bcf5"
 "checksum gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0912515a8ff24ba900422ecda800b52f4016a56251922d397c576bf92c690518"
 "checksum gl_generator 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f1d8edc81c5ae84605a62f5dac661a2313003b26d59839f81d47d46cf0f16a55"
 "checksum gleam 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b04d6c8a1df841e48dfe99ed67829c9d1d17b1bb3e44c5f3283992010e20359b"
 "checksum heapsize 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8c80e194758495a9109566134dc06e42ea0423987d6ceca016edaa90381b3549"
 "checksum idna 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1053236e00ce4f668aeca4a769a09b3bf5a682d802abd6f3cb39374f6b162c11"
-"checksum ipc-channel 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f379ec6400f5cb93edbbb0ac56de3bf38331e22cbae9e27a10f04c1a0b4f8e90"
+"checksum ipc-channel 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "675587430ede6756dd03fdfdf9888f22f83855fd131c8451d842a710b059e571"
 "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
 "checksum khronos_api 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "09c9d3760673c427d46f91a0350f0a84a52e6bc5a84adf26dc610b6c52436630"
 "checksum lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6abe0ee2e758cd6bc8a2cd56726359007748fbf4128da998b65d0b70f881e19b"
 "checksum libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)" = "044d1360593a78f5c8e5e710beccdc24ab71d1f01bc19a29bcacdba22e8475d8"
 "checksum log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054"
 "checksum matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "efd7622e3022e1a6eaa602c4cea8912254e5582c9c692e9167714182244801b1"
 "checksum num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "a16a42856a256b39c6d3484f097f6713e14feacd9bfb02290917904fae46c81c"
 "checksum num_cpus 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8890e6084723d57d0df8d2720b0d60c6ee67d6c93e7169630e4371e88765dcad"