Merge graphics to m-c a=merge
authorWes Kocher <wkocher@mozilla.com>
Thu, 23 Feb 2017 11:04:48 -0800
changeset 373510 5069348353f8fc1121e632e3208da33900627214
parent 373501 22ec1dab9e821676f4204d36ce9801803032f504 (current diff)
parent 373509 2fcef8b1c4374728d95c3d72cf6e4fba76d69169 (diff)
child 373511 7d50c8e3086d4cd1f05895b3dceac091f8825f1a
push id10863
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 23:02:23 +0000
treeherdermozilla-aurora@0931190cd725 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone54.0a1
Merge graphics to m-c a=merge MozReview-Commit-ID: 6qomiUNluVr
gfx/webrender/src/layer.rs
gfx/webrender/src/scroll_tree.rs
third_party/rust/app_units-0.3.0/.cargo-checksum.json
third_party/rust/app_units-0.3.0/.cargo-ok
third_party/rust/app_units-0.3.0/.gitignore
third_party/rust/app_units-0.3.0/.travis.yml
third_party/rust/app_units-0.3.0/Cargo.toml
third_party/rust/app_units-0.3.0/README.md
third_party/rust/app_units-0.3.0/src/app_unit.rs
third_party/rust/app_units-0.3.0/src/lib.rs
third_party/rust/bincode/src/rustc_serialize/mod.rs
third_party/rust/bincode/src/rustc_serialize/reader.rs
third_party/rust/bincode/src/rustc_serialize/writer.rs
third_party/rust/dwrote/.cargo-checksum.json
third_party/rust/dwrote/.cargo-ok
third_party/rust/dwrote/.gitignore
third_party/rust/dwrote/Cargo.toml
third_party/rust/dwrote/README.md
third_party/rust/dwrote/build.rs
third_party/rust/dwrote/src/bitmap_render_target.rs
third_party/rust/dwrote/src/com_helpers.rs
third_party/rust/dwrote/src/comptr.rs
third_party/rust/dwrote/src/font.rs
third_party/rust/dwrote/src/font_collection.rs
third_party/rust/dwrote/src/font_face.rs
third_party/rust/dwrote/src/font_family.rs
third_party/rust/dwrote/src/font_file.rs
third_party/rust/dwrote/src/font_file_loader_impl.rs
third_party/rust/dwrote/src/gdi_interop.rs
third_party/rust/dwrote/src/glyph_run_analysis.rs
third_party/rust/dwrote/src/helpers.rs
third_party/rust/dwrote/src/lib.rs
third_party/rust/dwrote/src/rendering_params.rs
third_party/rust/dwrote/src/test.rs
third_party/rust/dwrote/src/types.rs
third_party/rust/euclid-0.10.5/.cargo-checksum.json
third_party/rust/euclid-0.10.5/.cargo-ok
third_party/rust/euclid-0.10.5/.gitignore
third_party/rust/euclid-0.10.5/.travis.yml
third_party/rust/euclid-0.10.5/COPYRIGHT
third_party/rust/euclid-0.10.5/Cargo.toml
third_party/rust/euclid-0.10.5/LICENSE-APACHE
third_party/rust/euclid-0.10.5/LICENSE-MIT
third_party/rust/euclid-0.10.5/README.md
third_party/rust/euclid-0.10.5/src/approxeq.rs
third_party/rust/euclid-0.10.5/src/length.rs
third_party/rust/euclid-0.10.5/src/lib.rs
third_party/rust/euclid-0.10.5/src/macros.rs
third_party/rust/euclid-0.10.5/src/matrix2d.rs
third_party/rust/euclid-0.10.5/src/matrix4d.rs
third_party/rust/euclid-0.10.5/src/num.rs
third_party/rust/euclid-0.10.5/src/point.rs
third_party/rust/euclid-0.10.5/src/rect.rs
third_party/rust/euclid-0.10.5/src/scale_factor.rs
third_party/rust/euclid-0.10.5/src/side_offsets.rs
third_party/rust/euclid-0.10.5/src/size.rs
third_party/rust/euclid-0.10.5/src/trig.rs
third_party/rust/serde-0.8.23/.cargo-checksum.json
third_party/rust/serde-0.8.23/.cargo-ok
third_party/rust/serde-0.8.23/Cargo.toml
third_party/rust/serde-0.8.23/src/bytes.rs
third_party/rust/serde-0.8.23/src/de/from_primitive.rs
third_party/rust/serde-0.8.23/src/de/impls.rs
third_party/rust/serde-0.8.23/src/de/mod.rs
third_party/rust/serde-0.8.23/src/de/value.rs
third_party/rust/serde-0.8.23/src/error.rs
third_party/rust/serde-0.8.23/src/iter.rs
third_party/rust/serde-0.8.23/src/lib.rs
third_party/rust/serde-0.8.23/src/macros.rs
third_party/rust/serde-0.8.23/src/ser/impls.rs
third_party/rust/serde-0.8.23/src/ser/mod.rs
third_party/rust/serde-0.8.23/src/utils.rs
--- a/gfx/doc/README.webrender
+++ b/gfx/doc/README.webrender
@@ -74,9 +74,9 @@ there is another crate in m-c called moz
 the same folder to store its rust dependencies. If one of the libraries that is
 required by both mozjs_sys and webrender is updated without updating the other
 project's Cargo.lock file, that results in build bustage.
 This means that any time you do this sort of manual update of packages, you need
 to make sure that mozjs_sys also has its Cargo.lock file updated if needed, hence
 the need to run the cargo update command in js/src as well. Hopefully this will
 be resolved soon.
 
-Latest Commit: 938b32ca93bf5e878422ac4bafcdd53f8058f880
+Latest Commit: edc74274d28b1fa1229a1d1ea05027f57172b992
--- a/gfx/ipc/GfxMessageUtils.h
+++ b/gfx/ipc/GfxMessageUtils.h
@@ -917,27 +917,29 @@ struct ParamTraits<mozilla::layers::Text
 {
   typedef mozilla::layers::TextureFactoryIdentifier paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg, aParam.mParentBackend);
     WriteParam(aMsg, aParam.mParentProcessType);
     WriteParam(aMsg, aParam.mMaxTextureSize);
+    WriteParam(aMsg, aParam.mCompositorUseANGLE);
     WriteParam(aMsg, aParam.mSupportsTextureBlitting);
     WriteParam(aMsg, aParam.mSupportsPartialUploads);
     WriteParam(aMsg, aParam.mSupportsComponentAlpha);
     WriteParam(aMsg, aParam.mSyncHandle);
   }
 
   static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
   {
     bool result = ReadParam(aMsg, aIter, &aResult->mParentBackend) &&
                   ReadParam(aMsg, aIter, &aResult->mParentProcessType) &&
                   ReadParam(aMsg, aIter, &aResult->mMaxTextureSize) &&
+                  ReadParam(aMsg, aIter, &aResult->mCompositorUseANGLE) &&
                   ReadParam(aMsg, aIter, &aResult->mSupportsTextureBlitting) &&
                   ReadParam(aMsg, aIter, &aResult->mSupportsPartialUploads) &&
                   ReadParam(aMsg, aIter, &aResult->mSupportsComponentAlpha) &&
                   ReadParam(aMsg, aIter, &aResult->mSyncHandle);
     return result;
   }
 };
 
--- a/gfx/layers/CompositorTypes.h
+++ b/gfx/layers/CompositorTypes.h
@@ -163,31 +163,34 @@ typedef uintptr_t SyncHandle;
  * of the compositor and should (in the future) include information about what
  * kinds of buffer and texture clients to create.
  */
 struct TextureFactoryIdentifier
 {
   LayersBackend mParentBackend;
   GeckoProcessType mParentProcessType;
   int32_t mMaxTextureSize;
+  bool mCompositorUseANGLE;
   bool mSupportsTextureBlitting;
   bool mSupportsPartialUploads;
   bool mSupportsComponentAlpha;
   SyncHandle mSyncHandle;
 
   explicit TextureFactoryIdentifier(LayersBackend aLayersBackend = LayersBackend::LAYERS_NONE,
                                     GeckoProcessType aParentProcessType = GeckoProcessType_Default,
                                     int32_t aMaxTextureSize = 4096,
+                                    bool aCompositorUseANGLE = false,
                                     bool aSupportsTextureBlitting = false,
                                     bool aSupportsPartialUploads = false,
                                     bool aSupportsComponentAlpha = true,
                                     SyncHandle aSyncHandle = 0)
     : mParentBackend(aLayersBackend)
     , mParentProcessType(aParentProcessType)
     , mMaxTextureSize(aMaxTextureSize)
+    , mCompositorUseANGLE(aCompositorUseANGLE)
     , mSupportsTextureBlitting(aSupportsTextureBlitting)
     , mSupportsPartialUploads(aSupportsPartialUploads)
     , mSupportsComponentAlpha(aSupportsComponentAlpha)
     , mSyncHandle(aSyncHandle)
   {}
 };
 
 /**
--- a/gfx/layers/ipc/KnowsCompositor.h
+++ b/gfx/layers/ipc/KnowsCompositor.h
@@ -57,16 +57,21 @@ public:
     return mTextureFactoryIdentifier.mSupportsPartialUploads;
   }
 
   bool SupportsComponentAlpha() const
   {
     return mTextureFactoryIdentifier.mSupportsComponentAlpha;
   }
 
+  bool GetCompositorUseANGLE() const
+  {
+    return mTextureFactoryIdentifier.mCompositorUseANGLE;
+  }
+
   const TextureFactoryIdentifier& GetTextureFactoryIdentifier() const
   {
     return mTextureFactoryIdentifier;
   }
 
   bool DeviceCanReset() const {
     return GetCompositorBackendType() != LayersBackend::LAYERS_BASIC;
   }
--- a/gfx/layers/opengl/CompositorOGL.h
+++ b/gfx/layers/opengl/CompositorOGL.h
@@ -136,16 +136,17 @@ public:
   virtual void Destroy() override;
 
   virtual TextureFactoryIdentifier GetTextureFactoryIdentifier() override
   {
     TextureFactoryIdentifier result =
       TextureFactoryIdentifier(LayersBackend::LAYERS_OPENGL,
                                XRE_GetProcessType(),
                                GetMaxTextureSize(),
+                               false,
                                mFBOTextureTarget == LOCAL_GL_TEXTURE_2D,
                                SupportsPartialTextureUpdate());
     return result;
   }
 
   virtual already_AddRefed<CompositingRenderTarget>
   CreateRenderTarget(const gfx::IntRect &aRect, SurfaceInitMode aInit) override;
 
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -691,13 +691,14 @@ WebRenderBridgeParent::SetWebRenderProfi
 
 TextureFactoryIdentifier
 WebRenderBridgeParent::GetTextureFactoryIdentifier()
 {
   MOZ_ASSERT(mApi);
 
   return TextureFactoryIdentifier(LayersBackend::LAYERS_WR,
                                   XRE_GetProcessType(),
-                                  mApi->GetMaxTextureSize());
+                                  mApi->GetMaxTextureSize(),
+                                  mApi->GetUseANGLE());
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/webrender/Cargo.toml
+++ b/gfx/webrender/Cargo.toml
@@ -1,46 +1,46 @@
 [package]
 name = "webrender"
-version = "0.15.0"
+version = "0.19.0"
 authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
 license = "MPL-2.0"
 repository = "https://github.com/servo/webrender"
 build = "build.rs"
 
 [features]
 default = ["codegen", "freetype-lib"]
 codegen = ["webrender_traits/codegen"]
 freetype-lib = ["freetype/servo-freetype-sys"]
 serde_derive = ["webrender_traits/serde_derive"]
 profiler = ["thread_profiler/thread_profiler"]
 
 [dependencies]
-app_units = "0.3"
-bincode = "0.6"
+app_units = "0.4"
+bincode = "1.0.0-alpha2"
 bit-set = "0.4"
 byteorder = "1.0"
-euclid = "0.10.3"
+euclid = "0.11"
 fnv="1.0"
 gleam = "0.2.30"
 lazy_static = "0.2"
 log = "0.3"
 num-traits = "0.1.32"
-offscreen_gl_context = {version = "0.5", features = ["serde_serialization", "osmesa"]}
+offscreen_gl_context = {version = "0.6", features = ["serde", "osmesa"]}
 time = "0.1"
 threadpool = "1.3.2"
 webrender_traits = {path = "../webrender_traits", default-features = false}
 bitflags = "0.7"
 gamma-lut = "0.1"
 thread_profiler = "0.1.1"
 
 [dev-dependencies]
 angle = {git = "https://github.com/servo/angle", branch = "servo"}
 
 [target.'cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))'.dependencies]
 freetype = { version = "0.2", default-features = false }
 
 [target.'cfg(target_os = "windows")'.dependencies]
-dwrote = "0.1.7"
+servo-dwrote = "0.2"
 
 [target.'cfg(target_os = "macos")'.dependencies]
-core-graphics = "0.6.0"
-core-text = "3.0"
+core-graphics = "0.7.0"
+core-text = "4.0"
--- a/gfx/webrender/res/cs_text_run.vs.glsl
+++ b/gfx/webrender/res/cs_text_run.vs.glsl
@@ -14,17 +14,17 @@ void main(void) {
     Glyph glyph = fetch_glyph(cpi.sub_index);
     PrimitiveGeometry pg = fetch_prim_geometry(cpi.global_prim_index);
     ResourceRect res = fetch_resource_rect(cpi.user_data.x);
 
     // Glyphs size is already in device-pixels.
     // The render task origin is in device-pixels. Offset that by
     // the glyph offset, relative to its primitive bounding rect.
     vec2 size = res.uv_rect.zw - res.uv_rect.xy;
-    vec2 origin = task.data0.xy + uDevicePixelRatio * (glyph.offset.xy - pg.local_rect.xy);
+    vec2 origin = task.data0.xy + uDevicePixelRatio * (glyph.offset.xy - pg.local_rect.p0);
     vec4 local_rect = vec4(origin, size);
 
     vec2 texture_size = vec2(textureSize(sColor0, 0));
     vec2 st0 = res.uv_rect.xy / texture_size;
     vec2 st1 = res.uv_rect.zw / texture_size;
 
     vec2 pos = mix(local_rect.xy,
                    local_rect.xy + local_rect.zw,
--- a/gfx/webrender/res/prim_shared.glsl
+++ b/gfx/webrender/res/prim_shared.glsl
@@ -81,20 +81,63 @@ ivec2 get_fetch_uv_2(int index) {
 ivec2 get_fetch_uv_4(int index) {
     return get_fetch_uv(index, 4);
 }
 
 ivec2 get_fetch_uv_8(int index) {
     return get_fetch_uv(index, 8);
 }
 
+struct RectWithSize {
+    vec2 p0;
+    vec2 size;
+};
+
+struct RectWithEndpoint {
+    vec2 p0;
+    vec2 p1;
+};
+
+RectWithEndpoint to_rect_with_endpoint(RectWithSize rect) {
+    RectWithEndpoint result;
+    result.p0 = rect.p0;
+    result.p1 = rect.p0 + rect.size;
+
+    return result;
+}
+
+RectWithSize to_rect_with_size(RectWithEndpoint rect) {
+    RectWithSize result;
+    result.p0 = rect.p0;
+    result.size = rect.p1 - rect.p0;
+
+    return result;
+}
+
+vec2 clamp_rect(vec2 point, RectWithSize rect) {
+    return clamp(point, rect.p0, rect.p0 + rect.size);
+}
+
+vec2 clamp_rect(vec2 point, RectWithEndpoint rect) {
+    return clamp(point, rect.p0, rect.p1);
+}
+
+// Clamp 2 points at once.
+vec4 clamp_rect(vec4 points, RectWithSize rect) {
+    return clamp(points, rect.p0.xyxy, rect.p0.xyxy + rect.size.xyxy);
+}
+
+vec4 clamp_rect(vec4 points, RectWithEndpoint rect) {
+    return clamp(points, rect.p0.xyxy, rect.p1.xyxy);
+}
+
 struct Layer {
     mat4 transform;
     mat4 inv_transform;
-    vec4 local_clip_rect;
+    RectWithSize local_clip_rect;
     vec4 screen_vertices[4];
 };
 
 Layer fetch_layer(int index) {
     Layer layer;
 
     // Create a UV base coord for each 8 texels.
     // This is required because trying to use an offset
@@ -109,17 +152,18 @@ Layer fetch_layer(int index) {
     layer.transform[2] = texelFetchOffset(sLayers, uv0, 0, ivec2(2, 0));
     layer.transform[3] = texelFetchOffset(sLayers, uv0, 0, ivec2(3, 0));
 
     layer.inv_transform[0] = texelFetchOffset(sLayers, uv0, 0, ivec2(4, 0));
     layer.inv_transform[1] = texelFetchOffset(sLayers, uv0, 0, ivec2(5, 0));
     layer.inv_transform[2] = texelFetchOffset(sLayers, uv0, 0, ivec2(6, 0));
     layer.inv_transform[3] = texelFetchOffset(sLayers, uv0, 0, ivec2(7, 0));
 
-    layer.local_clip_rect = texelFetchOffset(sLayers, uv1, 0, ivec2(0, 0));
+    vec4 clip_rect = texelFetchOffset(sLayers, uv1, 0, ivec2(0, 0));
+    layer.local_clip_rect = RectWithSize(clip_rect.xy, clip_rect.zw);
 
     layer.screen_vertices[0] = texelFetchOffset(sLayers, uv1, 0, ivec2(1, 0));
     layer.screen_vertices[1] = texelFetchOffset(sLayers, uv1, 0, ivec2(2, 0));
     layer.screen_vertices[2] = texelFetchOffset(sLayers, uv1, 0, ivec2(3, 0));
     layer.screen_vertices[3] = texelFetchOffset(sLayers, uv1, 0, ivec2(4, 0));
 
     return layer;
 }
@@ -241,36 +285,38 @@ Glyph fetch_glyph(int index) {
 
     ivec2 uv = get_fetch_uv_1(index);
 
     glyph.offset = texelFetchOffset(sData16, uv, 0, ivec2(0, 0));
 
     return glyph;
 }
 
-vec4 fetch_instance_geometry(int index) {
+RectWithSize fetch_instance_geometry(int index) {
     ivec2 uv = get_fetch_uv_1(index);
 
     vec4 rect = texelFetchOffset(sData16, uv, 0, ivec2(0, 0));
 
-    return rect;
+    return RectWithSize(rect.xy, rect.zw);
 }
 
 struct PrimitiveGeometry {
-    vec4 local_rect;
-    vec4 local_clip_rect;
+    RectWithSize local_rect;
+    RectWithSize local_clip_rect;
 };
 
 PrimitiveGeometry fetch_prim_geometry(int index) {
     PrimitiveGeometry pg;
 
     ivec2 uv = get_fetch_uv(index, VECS_PER_PRIM_GEOM);
 
-    pg.local_rect = texelFetchOffset(sPrimGeometry, uv, 0, ivec2(0, 0));
-    pg.local_clip_rect = texelFetchOffset(sPrimGeometry, uv, 0, ivec2(1, 0));
+    vec4 local_rect = texelFetchOffset(sPrimGeometry, uv, 0, ivec2(0, 0));
+    pg.local_rect = RectWithSize(local_rect.xy, local_rect.zw);
+    vec4 local_clip_rect = texelFetchOffset(sPrimGeometry, uv, 0, ivec2(1, 0));
+    pg.local_clip_rect = RectWithSize(local_clip_rect.xy, local_clip_rect.zw);
 
     return pg;
 }
 
 struct PrimitiveInstance {
     int global_prim_index;
     int specific_prim_index;
     int render_task_index;
@@ -317,18 +363,18 @@ CachePrimitiveInstance fetch_cache_insta
 
     return cpi;
 }
 
 struct Primitive {
     Layer layer;
     ClipArea clip_area;
     AlphaBatchTask task;
-    vec4 local_rect;
-    vec4 local_clip_rect;
+    RectWithSize local_rect;
+    RectWithSize local_clip_rect;
     int prim_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;
     float z;
 };
 
@@ -397,57 +443,38 @@ vec4 get_layer_pos(vec2 pos, Layer layer
     vec3 a = layer.screen_vertices[0].xyz / layer.screen_vertices[0].w;
     vec3 b = layer.screen_vertices[3].xyz / layer.screen_vertices[3].w;
     vec3 c = layer.screen_vertices[2].xyz / layer.screen_vertices[2].w;
     // get the normal to the layer plane
     vec3 n = normalize(cross(b-a, c-a));
     return untransform(pos, n, a, layer.inv_transform);
 }
 
-vec2 clamp_rect(vec2 point, vec4 rect) {
-    return clamp(point, rect.xy, rect.xy + rect.zw);
-}
-
-struct Rect {
-    vec2 p0;
-    vec2 p1;
-};
-
 struct VertexInfo {
-    Rect local_rect;
+    RectWithEndpoint local_rect;
     vec2 local_pos;
     vec2 screen_pos;
 };
 
-VertexInfo write_vertex(vec4 instance_rect,
-                        vec4 local_clip_rect,
+VertexInfo write_vertex(RectWithSize instance_rect,
+                        RectWithSize local_clip_rect,
                         float z,
                         Layer layer,
                         AlphaBatchTask task) {
-    // Get the min/max local space coords of the rectangle.
-    vec2 local_p0 = instance_rect.xy;
-    vec2 local_p1 = instance_rect.xy + instance_rect.zw;
-
-    // Get the min/max coords of the local space clip rect.
-    vec2 local_clip_p0 = local_clip_rect.xy;
-    vec2 local_clip_p1 = local_clip_rect.xy + local_clip_rect.zw;
-
-    // Get the min/max coords of the layer clip rect.
-    vec2 layer_clip_p0 = layer.local_clip_rect.xy;
-    vec2 layer_clip_p1 = layer.local_clip_rect.xy + layer.local_clip_rect.zw;
+    RectWithEndpoint local_rect = to_rect_with_endpoint(instance_rect);
 
     // Select the corner of the local rect that we are processing.
-    vec2 local_pos = mix(local_p0, local_p1, aPosition.xy);
+    vec2 local_pos = mix(local_rect.p0, local_rect.p1, aPosition.xy);
 
     // xy = top left corner of the local rect, zw = position of current vertex.
-    vec4 local_p0_pos = vec4(local_p0, local_pos);
+    vec4 local_p0_pos = vec4(local_rect.p0, local_pos);
 
     // Clamp to the two local clip rects.
-    local_p0_pos = clamp(local_p0_pos, local_clip_p0.xyxy, local_clip_p1.xyxy);
-    local_p0_pos = clamp(local_p0_pos, layer_clip_p0.xyxy, layer_clip_p1.xyxy);
+    local_p0_pos = clamp_rect(local_p0_pos, local_clip_rect);
+    local_p0_pos = clamp_rect(local_p0_pos, layer.local_clip_rect);
 
     // Transform the top corner and current vertex to world space.
     vec4 world_p0 = layer.transform * vec4(local_p0_pos.xy, 0.0, 1.0);
     world_p0.xyz /= world_p0.w;
     vec4 world_pos = layer.transform * vec4(local_p0_pos.zw, 0.0, 1.0);
     world_pos.xyz /= world_pos.w;
 
     // Convert the world positions to device pixel space. xy=top left corner. zw=current vertex.
@@ -459,35 +486,35 @@ VertexInfo write_vertex(vec4 instance_re
     // Apply offsets for the render task to get correct screen location.
     vec2 final_pos = device_p0_pos.zw -
                      snap_delta -
                      task.screen_space_origin +
                      task.render_target_origin;
 
     gl_Position = uTransform * vec4(final_pos, z, 1.0);
 
-    VertexInfo vi = VertexInfo(Rect(local_p0, local_p1), local_p0_pos.zw, device_p0_pos.zw);
+    VertexInfo vi = VertexInfo(local_rect, local_p0_pos.zw, device_p0_pos.zw);
     return vi;
 }
 
 #ifdef WR_FEATURE_TRANSFORM
 
 struct TransformVertexInfo {
     vec3 local_pos;
     vec2 screen_pos;
     vec4 clipped_local_rect;
 };
 
-TransformVertexInfo write_transform_vertex(vec4 instance_rect,
-                                           vec4 local_clip_rect,
+TransformVertexInfo write_transform_vertex(RectWithSize instance_rect,
+                                           RectWithSize local_clip_rect,
                                            float z,
                                            Layer layer,
                                            AlphaBatchTask task) {
-    vec2 lp0_base = instance_rect.xy;
-    vec2 lp1_base = instance_rect.xy + instance_rect.zw;
+    vec2 lp0_base = instance_rect.p0;
+    vec2 lp1_base = instance_rect.p0 + instance_rect.size;
 
     vec2 lp0 = clamp_rect(clamp_rect(lp0_base, local_clip_rect),
                           layer.local_clip_rect);
     vec2 lp1 = clamp_rect(clamp_rect(lp1_base, local_clip_rect),
                           layer.local_clip_rect);
 
     vec4 clipped_local_rect = vec4(lp0, lp1 - lp0);
 
--- a/gfx/webrender/res/ps_border.vs.glsl
+++ b/gfx/webrender/res/ps_border.vs.glsl
@@ -27,110 +27,100 @@ Border fetch_border(int index) {
     return border;
 }
 
 void main(void) {
     Primitive prim = load_primitive();
     Border border = fetch_border(prim.prim_index);
     int sub_part = prim.sub_index;
 
-    vec2 tl_outer = prim.local_rect.xy;
+    vec2 tl_outer = prim.local_rect.p0;
     vec2 tl_inner = tl_outer + vec2(max(border.radii[0].x, border.widths.x),
                                     max(border.radii[0].y, border.widths.y));
 
-    vec2 tr_outer = vec2(prim.local_rect.x + prim.local_rect.z,
-                         prim.local_rect.y);
+    vec2 tr_outer = vec2(prim.local_rect.p0.x + prim.local_rect.size.x,
+                         prim.local_rect.p0.y);
     vec2 tr_inner = tr_outer + vec2(-max(border.radii[0].z, border.widths.z),
                                     max(border.radii[0].w, border.widths.y));
 
-    vec2 br_outer = vec2(prim.local_rect.x + prim.local_rect.z,
-                         prim.local_rect.y + prim.local_rect.w);
+    vec2 br_outer = vec2(prim.local_rect.p0.x + prim.local_rect.size.x,
+                         prim.local_rect.p0.y + prim.local_rect.size.y);
     vec2 br_inner = br_outer - vec2(max(border.radii[1].x, border.widths.z),
                                     max(border.radii[1].y, border.widths.w));
 
-    vec2 bl_outer = vec2(prim.local_rect.x,
-                         prim.local_rect.y + prim.local_rect.w);
+    vec2 bl_outer = vec2(prim.local_rect.p0.x,
+                         prim.local_rect.p0.y + prim.local_rect.size.y);
     vec2 bl_inner = bl_outer + vec2(max(border.radii[1].z, border.widths.x),
                                     -max(border.radii[1].w, border.widths.w));
 
-    vec4 segment_rect;
+    RectWithSize segment_rect;
     switch (sub_part) {
         case PST_TOP_LEFT:
-            segment_rect = vec4(tl_outer, tl_inner - tl_outer);
+            segment_rect.p0 = tl_outer;
+            segment_rect.size = tl_inner - tl_outer;
             vBorderStyle = int(border.style.x);
             vHorizontalColor = border.colors[BORDER_LEFT];
             vVerticalColor = border.colors[BORDER_TOP];
             vRadii = vec4(border.radii[0].xy,
                           border.radii[0].xy - border.widths.xy);
             break;
         case PST_TOP_RIGHT:
-            segment_rect = vec4(tr_inner.x,
-                                tr_outer.y,
-                                tr_outer.x - tr_inner.x,
-                                tr_inner.y - tr_outer.y);
+            segment_rect.p0 = vec2(tr_inner.x, tr_outer.y);
+            segment_rect.size = vec2(tr_outer.x - tr_inner.x, tr_inner.y - tr_outer.y);
             vBorderStyle = int(border.style.y);
             vHorizontalColor = border.colors[BORDER_TOP];
             vVerticalColor = border.colors[BORDER_RIGHT];
             vRadii = vec4(border.radii[0].zw,
                           border.radii[0].zw - border.widths.zy);
             break;
         case PST_BOTTOM_RIGHT:
-            segment_rect = vec4(br_inner, br_outer - br_inner);
+            segment_rect.p0 = br_inner;
+            segment_rect.size = br_outer - br_inner;
             vBorderStyle = int(border.style.z);
             vHorizontalColor = border.colors[BORDER_BOTTOM];
             vVerticalColor = border.colors[BORDER_RIGHT];
             vRadii = vec4(border.radii[1].xy,
                           border.radii[1].xy - border.widths.zw);
             break;
         case PST_BOTTOM_LEFT:
-            segment_rect = vec4(bl_outer.x,
-                                bl_inner.y,
-                                bl_inner.x - bl_outer.x,
-                                bl_outer.y - bl_inner.y);
+            segment_rect.p0 = vec2(bl_outer.x, bl_inner.y);
+            segment_rect.size = vec2(bl_inner.x - bl_outer.x, bl_outer.y - bl_inner.y);
             vBorderStyle = int(border.style.w);
             vHorizontalColor = border.colors[BORDER_BOTTOM];
             vVerticalColor = border.colors[BORDER_LEFT];
             vRadii = vec4(border.radii[1].zw,
                           border.radii[1].zw - border.widths.xw);
             break;
         case PST_LEFT:
-            segment_rect = vec4(tl_outer.x,
-                                tl_inner.y,
-                                border.widths.x,
-                                bl_inner.y - tl_inner.y);
+            segment_rect.p0 = vec2(tl_outer.x, tl_inner.y);
+            segment_rect.size = vec2(border.widths.x, bl_inner.y - tl_inner.y);
             vBorderStyle = int(border.style.x);
             vHorizontalColor = border.colors[BORDER_LEFT];
             vVerticalColor = border.colors[BORDER_LEFT];
             vRadii = vec4(0.0);
             break;
         case PST_RIGHT:
-            segment_rect = vec4(tr_outer.x - border.widths.z,
-                                tr_inner.y,
-                                border.widths.z,
-                                br_inner.y - tr_inner.y);
+            segment_rect.p0 = vec2(tr_outer.x - border.widths.z, tr_inner.y);
+            segment_rect.size = vec2(border.widths.z, br_inner.y - tr_inner.y);
             vBorderStyle = int(border.style.z);
             vHorizontalColor = border.colors[BORDER_RIGHT];
             vVerticalColor = border.colors[BORDER_RIGHT];
             vRadii = vec4(0.0);
             break;
         case PST_BOTTOM:
-            segment_rect = vec4(bl_inner.x,
-                                bl_outer.y - border.widths.w,
-                                br_inner.x - bl_inner.x,
-                                border.widths.w);
+            segment_rect.p0 = vec2(bl_inner.x, bl_outer.y - border.widths.w);
+            segment_rect.size = vec2(br_inner.x - bl_inner.x, border.widths.w);
             vBorderStyle = int(border.style.w);
             vHorizontalColor = border.colors[BORDER_BOTTOM];
             vVerticalColor = border.colors[BORDER_BOTTOM];
             vRadii = vec4(0.0);
             break;
         case PST_TOP:
-            segment_rect = vec4(tl_inner.x,
-                                tl_outer.y,
-                                tr_inner.x - tl_inner.x,
-                                border.widths.y);
+            segment_rect.p0 = vec2(tl_inner.x, tl_outer.y);
+            segment_rect.size = vec2(tr_inner.x - tl_inner.x, border.widths.y);
             vBorderStyle = int(border.style.y);
             vHorizontalColor = border.colors[BORDER_TOP];
             vVerticalColor = border.colors[BORDER_TOP];
             vRadii = vec4(0.0);
             break;
     }
 
 #ifdef WR_FEATURE_TRANSFORM
@@ -147,63 +137,63 @@ void main(void) {
     VertexInfo vi = write_vertex(segment_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
                                  prim.task);
     vLocalPos = vi.local_pos.xy;
 
     // Local space
-    vLocalRect = prim.local_rect;
+    vLocalRect = vec4(prim.local_rect.p0, prim.local_rect.size);
 #endif
 
     float x0, y0, x1, y1;
     switch (sub_part) {
         // These are the layer tile part PrimitivePart as uploaded by the tiling.rs
         case PST_TOP_LEFT:
-            x0 = segment_rect.x;
-            y0 = segment_rect.y;
+            x0 = segment_rect.p0.x;
+            y0 = segment_rect.p0.y;
             // These are width / heights
-            x1 = segment_rect.x + segment_rect.z;
-            y1 = segment_rect.y + segment_rect.w;
+            x1 = segment_rect.p0.x + segment_rect.size.x;
+            y1 = segment_rect.p0.y + segment_rect.size.y;
 
             // The radius here is the border-radius. This is 0, so vRefPoint will
             // just be the top left (x,y) corner.
             vRefPoint = vec2(x0, y0) + vRadii.xy;
             break;
         case PST_TOP_RIGHT:
-            x0 = segment_rect.x + segment_rect.z;
-            y0 = segment_rect.y;
-            x1 = segment_rect.x;
-            y1 = segment_rect.y + segment_rect.w;
+            x0 = segment_rect.p0.x + segment_rect.size.x;
+            y0 = segment_rect.p0.y;
+            x1 = segment_rect.p0.x;
+            y1 = segment_rect.p0.y + segment_rect.size.y;
             vRefPoint = vec2(x0, y0) + vec2(-vRadii.x, vRadii.y);
             break;
         case PST_BOTTOM_LEFT:
-            x0 = segment_rect.x;
-            y0 = segment_rect.y + segment_rect.w;
-            x1 = segment_rect.x + segment_rect.z;
-            y1 = segment_rect.y;
+            x0 = segment_rect.p0.x;
+            y0 = segment_rect.p0.y + segment_rect.size.y;
+            x1 = segment_rect.p0.x + segment_rect.size.x;
+            y1 = segment_rect.p0.y;
             vRefPoint = vec2(x0, y0) + vec2(vRadii.x, -vRadii.y);
             break;
         case PST_BOTTOM_RIGHT:
-            x0 = segment_rect.x;
-            y0 = segment_rect.y;
-            x1 = segment_rect.x + segment_rect.z;
-            y1 = segment_rect.y + segment_rect.w;
+            x0 = segment_rect.p0.x;
+            y0 = segment_rect.p0.y;
+            x1 = segment_rect.p0.x + segment_rect.size.x;
+            y1 = segment_rect.p0.y + segment_rect.size.y;
             vRefPoint = vec2(x1, y1) + vec2(-vRadii.x, -vRadii.y);
             break;
         case PST_TOP:
         case PST_LEFT:
         case PST_BOTTOM:
         case PST_RIGHT:
-            vRefPoint = segment_rect.xy;
-            x0 = segment_rect.x;
-            y0 = segment_rect.y;
-            x1 = segment_rect.x + segment_rect.z;
-            y1 = segment_rect.y + segment_rect.w;
+            vRefPoint = segment_rect.p0.xy;
+            x0 = segment_rect.p0.x;
+            y0 = segment_rect.p0.y;
+            x1 = segment_rect.p0.x + segment_rect.size.x;
+            y1 = segment_rect.p0.y + segment_rect.size.y;
             break;
     }
 
     // y1 - y0 is the height of the corner / line
     // x1 - x0 is the width of the corner / line.
     float width = x1 - x0;
     float height = y1 - y0;
 
--- a/gfx/webrender/res/ps_box_shadow.vs.glsl
+++ b/gfx/webrender/res/ps_box_shadow.vs.glsl
@@ -1,32 +1,32 @@
 #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/. */
 
 void main(void) {
     Primitive prim = load_primitive();
     BoxShadow bs = fetch_boxshadow(prim.prim_index);
-    vec4 segment_rect = fetch_instance_geometry(prim.sub_index);
+    RectWithSize segment_rect = fetch_instance_geometry(prim.sub_index);
 
     VertexInfo vi = write_vertex(segment_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
                                  prim.task);
 
     RenderTaskData child_task = fetch_render_task(prim.user_data.x);
     vUv.z = child_task.data1.x;
 
     // Constant offsets to inset from bilinear filtering border.
     vec2 patch_origin = child_task.data0.xy + vec2(1.0);
     vec2 patch_size_device_pixels = child_task.data0.zw - vec2(2.0);
     vec2 patch_size = patch_size_device_pixels / uDevicePixelRatio;
 
-    vUv.xy = (vi.local_pos - prim.local_rect.xy) / patch_size;
-    vMirrorPoint = 0.5 * prim.local_rect.zw / patch_size;
+    vUv.xy = (vi.local_pos - prim.local_rect.p0) / patch_size;
+    vMirrorPoint = 0.5 * prim.local_rect.size / patch_size;
 
     vec2 texture_size = vec2(textureSize(sCache, 0));
     vCacheUvRectCoords = vec4(patch_origin, patch_origin + patch_size_device_pixels) / texture_size.xyxy;
 
     vColor = bs.color;
 }
--- a/gfx/webrender/res/ps_cache_image.vs.glsl
+++ b/gfx/webrender/res/ps_cache_image.vs.glsl
@@ -17,12 +17,12 @@ void main(void) {
 
     RenderTaskData child_task = fetch_render_task(prim.user_data.x);
     vUv.z = child_task.data1.x;
 
     vec2 texture_size = vec2(textureSize(sCache, 0));
     vec2 uv0 = child_task.data0.xy / texture_size;
     vec2 uv1 = (child_task.data0.xy + child_task.data0.zw) / texture_size;
 
-    vec2 f = (vi.local_pos - prim.local_rect.xy) / prim.local_rect.zw;
+    vec2 f = (vi.local_pos - prim.local_rect.p0) / prim.local_rect.size;
 
     vUv.xy = mix(uv0, uv1, f);
 }
--- a/gfx/webrender/res/ps_gradient.vs.glsl
+++ b/gfx/webrender/res/ps_gradient.vs.glsl
@@ -5,58 +5,56 @@
 
 void main(void) {
     Primitive prim = load_primitive();
     Gradient gradient = fetch_gradient(prim.prim_index);
 
     GradientStop g0 = fetch_gradient_stop(prim.sub_index + 0);
     GradientStop g1 = fetch_gradient_stop(prim.sub_index + 1);
 
-    vec4 segment_rect;
+    RectWithSize segment_rect;
     vec2 axis;
     if (gradient.start_end_point.y == gradient.start_end_point.w) {
         float x0 = mix(gradient.start_end_point.x,
                        gradient.start_end_point.z,
                        g0.offset.x);
         float x1 = mix(gradient.start_end_point.x,
                        gradient.start_end_point.z,
                        g1.offset.x);
-        segment_rect.yw = prim.local_rect.yw;
-        segment_rect.x = x0;
-        segment_rect.z = x1 - x0;
+        segment_rect.p0 = vec2(x0, prim.local_rect.p0.y);
+        segment_rect.size = vec2(x1 - x0, prim.local_rect.size.y);
         axis = vec2(1.0, 0.0);
     } else {
         float y0 = mix(gradient.start_end_point.y,
                        gradient.start_end_point.w,
                        g0.offset.x);
         float y1 = mix(gradient.start_end_point.y,
                        gradient.start_end_point.w,
                        g1.offset.x);
-        segment_rect.xz = prim.local_rect.xz;
-        segment_rect.y = y0;
-        segment_rect.w = y1 - y0;
+        segment_rect.p0 = vec2(prim.local_rect.p0.x, y0);
+        segment_rect.size = vec2(prim.local_rect.size.x, y1 - y0);
         axis = vec2(0.0, 1.0);
     }
 
 #ifdef WR_FEATURE_TRANSFORM
     TransformVertexInfo vi = write_transform_vertex(segment_rect,
                                                     prim.local_clip_rect,
                                                     prim.z,
                                                     prim.layer,
                                                     prim.task);
     vLocalRect = vi.clipped_local_rect;
     vLocalPos = vi.local_pos;
-    vec2 f = (vi.local_pos.xy - prim.local_rect.xy) / prim.local_rect.zw;
+    vec2 f = (vi.local_pos.xy - prim.local_rect.p0) / prim.local_rect.size;
 #else
     VertexInfo vi = write_vertex(segment_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
                                  prim.task);
 
-    vec2 f = (vi.local_pos - segment_rect.xy) / segment_rect.zw;
+    vec2 f = (vi.local_pos - segment_rect.p0) / segment_rect.size;
     vPos = vi.local_pos;
 #endif
 
     write_clip(vi.screen_pos, prim.clip_area);
 
     vColor = mix(g0.color, g1.color, dot(f, axis));
 }
--- a/gfx/webrender/res/ps_text_run.vs.glsl
+++ b/gfx/webrender/res/ps_text_run.vs.glsl
@@ -4,27 +4,28 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 void main(void) {
     Primitive prim = load_primitive();
     TextRun text = fetch_text_run(prim.prim_index);
     Glyph glyph = fetch_glyph(prim.sub_index);
     ResourceRect res = fetch_resource_rect(prim.user_data.x);
 
-    vec4 local_rect = vec4(glyph.offset.xy, (res.uv_rect.zw - res.uv_rect.xy) / uDevicePixelRatio);
+    RectWithSize local_rect = RectWithSize(glyph.offset.xy,
+                                           (res.uv_rect.zw - res.uv_rect.xy) / uDevicePixelRatio);
 
 #ifdef WR_FEATURE_TRANSFORM
     TransformVertexInfo vi = write_transform_vertex(local_rect,
                                                     prim.local_clip_rect,
                                                     prim.z,
                                                     prim.layer,
                                                     prim.task);
     vLocalRect = vi.clipped_local_rect;
     vLocalPos = vi.local_pos;
-    vec2 f = (vi.local_pos.xy / vi.local_pos.z - local_rect.xy) / local_rect.zw;
+    vec2 f = (vi.local_pos.xy / vi.local_pos.z - local_rect.p0) / local_rect.size;
 #else
     VertexInfo vi = write_vertex(local_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
                                  prim.task);
     vec2 f = (vi.local_pos - vi.local_rect.p0) / (vi.local_rect.p1 - vi.local_rect.p0);
 #endif
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/src/clip_scroll_node.rs
@@ -0,0 +1,288 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use euclid::Point3D;
+use geometry::ray_intersects_rect;
+use spring::{DAMPING, STIFFNESS, Spring};
+use webrender_traits::{LayerPoint, LayerRect, LayerSize, LayerToScrollTransform};
+use webrender_traits::{LayerToWorldTransform, PipelineId, ScrollEventPhase, ScrollLayerId};
+use webrender_traits::{ScrollLayerRect, ScrollLocation, ScrollToWorldTransform, WorldPoint};
+use webrender_traits::{WorldPoint4D};
+
+#[cfg(target_os = "macos")]
+const CAN_OVERSCROLL: bool = true;
+
+#[cfg(not(target_os = "macos"))]
+const CAN_OVERSCROLL: bool = false;
+
+
+/// Contains scrolling and transform information stacking contexts.
+#[derive(Clone)]
+pub struct ClipScrollNode {
+    /// Manages scrolling offset, overscroll state etc.
+    pub scrolling: ScrollingState,
+
+    /// Size of the content inside the scroll region (in logical pixels)
+    pub content_size: LayerSize,
+
+    /// Viewing rectangle
+    pub local_viewport_rect: LayerRect,
+
+    /// Viewing rectangle clipped against parent layer(s)
+    pub combined_local_viewport_rect: LayerRect,
+
+    /// World transform for the viewport rect itself.
+    pub world_viewport_transform: LayerToWorldTransform,
+
+    /// World transform for content within this layer
+    pub world_content_transform: LayerToWorldTransform,
+
+    /// Transform for this layer, relative to parent scrollable layer.
+    pub local_transform: LayerToScrollTransform,
+
+    /// Pipeline that this layer belongs to
+    pub pipeline_id: PipelineId,
+
+    /// Child layers
+    pub children: Vec<ScrollLayerId>,
+}
+
+impl ClipScrollNode {
+    pub fn new(local_viewport_rect: &LayerRect,
+               content_size: LayerSize,
+               local_transform: &LayerToScrollTransform,
+               pipeline_id: PipelineId)
+               -> ClipScrollNode {
+        ClipScrollNode {
+            scrolling: ScrollingState::new(),
+            content_size: content_size,
+            local_viewport_rect: *local_viewport_rect,
+            combined_local_viewport_rect: *local_viewport_rect,
+            world_viewport_transform: LayerToWorldTransform::identity(),
+            world_content_transform: LayerToWorldTransform::identity(),
+            local_transform: *local_transform,
+            children: Vec::new(),
+            pipeline_id: pipeline_id,
+        }
+    }
+
+    pub fn add_child(&mut self, child: ScrollLayerId) {
+        self.children.push(child);
+    }
+
+    pub fn finalize(&mut self, scrolling: &ScrollingState) {
+        self.scrolling = *scrolling;
+    }
+
+    pub fn overscroll_amount(&self) -> LayerSize {
+        let scrollable_width = self.scrollable_width();
+        let overscroll_x = if self.scrolling.offset.x > 0.0 {
+            -self.scrolling.offset.x
+        } else if self.scrolling.offset.x < -scrollable_width {
+            -scrollable_width - self.scrolling.offset.x
+        } else {
+            0.0
+        };
+
+        let scrollable_height = self.scrollable_height();
+        let overscroll_y = if self.scrolling.offset.y > 0.0 {
+            -self.scrolling.offset.y
+        } else if self.scrolling.offset.y < -scrollable_height {
+            -scrollable_height - self.scrolling.offset.y
+        } else {
+            0.0
+        };
+
+        LayerSize::new(overscroll_x, overscroll_y)
+    }
+
+    pub fn set_scroll_origin(&mut self, origin: &LayerPoint) -> bool {
+        let scrollable_height = self.scrollable_height();
+        let scrollable_width = self.scrollable_width();
+        if scrollable_height <= 0. && scrollable_width <= 0. {
+            return false;
+        }
+
+        let new_offset = LayerPoint::new((-origin.x).max(-scrollable_width).min(0.0).round(),
+                                         (-origin.y).max(-scrollable_height).min(0.0).round());
+        if new_offset == self.scrolling.offset {
+            return false;
+        }
+
+        self.scrolling.offset = new_offset;
+        self.scrolling.bouncing_back = false;
+        self.scrolling.started_bouncing_back = false;
+        return true;
+    }
+
+    pub fn update_transform(&mut self,
+                            parent_world_transform: &ScrollToWorldTransform,
+                            parent_viewport_rect: &ScrollLayerRect) {
+        let inv_transform = match self.local_transform.inverse() {
+            Some(transform) => transform,
+            None => {
+                // If a transform function causes the current transformation matrix of an object
+                // to be non-invertible, the object and its content do not get displayed.
+                self.combined_local_viewport_rect = LayerRect::zero();
+                return;
+            }
+        };
+
+        let parent_viewport_rect_in_local_space = inv_transform.transform_rect(parent_viewport_rect)
+                                                               .translate(&-self.scrolling.offset);
+        let local_viewport_rect = self.local_viewport_rect.translate(&-self.scrolling.offset);
+        let viewport_rect = parent_viewport_rect_in_local_space.intersection(&local_viewport_rect)
+                                                               .unwrap_or(LayerRect::zero());
+
+        self.combined_local_viewport_rect = viewport_rect;
+        self.world_viewport_transform = parent_world_transform.pre_mul(&self.local_transform);
+        self.world_content_transform = self.world_viewport_transform
+                                                     .pre_translated(self.scrolling.offset.x,
+                                                                     self.scrolling.offset.y,
+                                                                     0.0);
+    }
+
+    pub fn scrollable_height(&self) -> f32 {
+        self.content_size.height - self.local_viewport_rect.size.height
+    }
+
+    pub fn scrollable_width(&self) -> f32 {
+        self.content_size.width - self.local_viewport_rect.size.width
+    }
+
+    pub fn scroll(&mut self, scroll_location: ScrollLocation, phase: ScrollEventPhase) -> bool {
+        if self.scrolling.started_bouncing_back && phase == ScrollEventPhase::Move(false) {
+            return false;
+        }
+
+        let mut delta = match scroll_location {
+            ScrollLocation::Delta(delta) => delta,
+            ScrollLocation::Start => {
+                if self.scrolling.offset.y.round() >= 0.0 {
+                    // Nothing to do on this layer.
+                    return false;
+                }
+
+                self.scrolling.offset.y = 0.0;
+                return true;
+            },
+            ScrollLocation::End => {
+                let end_pos = self.local_viewport_rect.size.height - self.content_size.height;
+
+                if self.scrolling.offset.y.round() <= end_pos {
+                    // Nothing to do on this layer.
+                    return false;
+                }
+
+                self.scrolling.offset.y = end_pos;
+                return true;
+            }
+        };
+
+        let overscroll_amount = self.overscroll_amount();
+        let overscrolling = CAN_OVERSCROLL && (overscroll_amount.width != 0.0 ||
+                                               overscroll_amount.height != 0.0);
+        if overscrolling {
+            if overscroll_amount.width != 0.0 {
+                delta.x /= overscroll_amount.width.abs()
+            }
+            if overscroll_amount.height != 0.0 {
+                delta.y /= overscroll_amount.height.abs()
+            }
+        }
+
+        let scrollable_width = self.scrollable_width();
+        let scrollable_height = self.scrollable_height();
+        let is_unscrollable = scrollable_width <= 0. && scrollable_height <= 0.;
+        let original_layer_scroll_offset = self.scrolling.offset;
+
+        if scrollable_width > 0. {
+            self.scrolling.offset.x = self.scrolling.offset.x + delta.x;
+            if is_unscrollable || !CAN_OVERSCROLL {
+                self.scrolling.offset.x =
+                    self.scrolling.offset.x.min(0.0).max(-scrollable_width).round();
+            }
+        }
+
+        if scrollable_height > 0. {
+            self.scrolling.offset.y = self.scrolling.offset.y + delta.y;
+            if is_unscrollable || !CAN_OVERSCROLL {
+                self.scrolling.offset.y =
+                    self.scrolling.offset.y.min(0.0).max(-scrollable_height).round();
+            }
+        }
+
+        if phase == ScrollEventPhase::Start || phase == ScrollEventPhase::Move(true) {
+            self.scrolling.started_bouncing_back = false
+        } else if overscrolling &&
+                ((delta.x < 1.0 && delta.y < 1.0) || phase == ScrollEventPhase::End) {
+            self.scrolling.started_bouncing_back = true;
+            self.scrolling.bouncing_back = true
+        }
+
+        if CAN_OVERSCROLL {
+            self.stretch_overscroll_spring();
+        }
+
+        self.scrolling.offset != original_layer_scroll_offset ||
+            self.scrolling.started_bouncing_back
+    }
+
+    pub fn stretch_overscroll_spring(&mut self) {
+        let overscroll_amount = self.overscroll_amount();
+        self.scrolling.spring.coords(self.scrolling.offset,
+                                     self.scrolling.offset,
+                                     self.scrolling.offset + overscroll_amount);
+    }
+
+    pub fn tick_scrolling_bounce_animation(&mut self) {
+        let finished = self.scrolling.spring.animate();
+        self.scrolling.offset = self.scrolling.spring.current();
+        if finished {
+            self.scrolling.bouncing_back = false
+        }
+    }
+
+    pub fn ray_intersects_node(&self, cursor: &WorldPoint) -> bool {
+        let inv = self.world_viewport_transform.inverse().unwrap();
+        let z0 = -10000.0;
+        let z1 =  10000.0;
+
+        let p0 = inv.transform_point4d(&WorldPoint4D::new(cursor.x, cursor.y, z0, 1.0));
+        let p0 = Point3D::new(p0.x / p0.w,
+                              p0.y / p0.w,
+                              p0.z / p0.w);
+        let p1 = inv.transform_point4d(&WorldPoint4D::new(cursor.x, cursor.y, z1, 1.0));
+        let p1 = Point3D::new(p1.x / p1.w,
+                              p1.y / p1.w,
+                              p1.z / p1.w);
+
+        if self.scrollable_width() <= 0. && self.scrollable_height() <= 0. {
+            return false;
+        }
+        ray_intersects_rect(p0, p1, self.local_viewport_rect.to_untyped())
+    }
+}
+
+#[derive(Copy, Clone)]
+pub struct ScrollingState {
+    pub offset: LayerPoint,
+    pub spring: Spring,
+    pub started_bouncing_back: bool,
+    pub bouncing_back: bool,
+    pub should_handoff_scroll: bool
+}
+
+impl ScrollingState {
+    pub fn new() -> ScrollingState {
+        ScrollingState {
+            offset: LayerPoint::zero(),
+            spring: Spring::at(LayerPoint::zero(), STIFFNESS, DAMPING),
+            started_bouncing_back: false,
+            bouncing_back: false,
+            should_handoff_scroll: false
+        }
+    }
+}
+
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/src/clip_scroll_tree.rs
@@ -0,0 +1,394 @@
+/* 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 fnv::FnvHasher;
+use clip_scroll_node::{ClipScrollNode, ScrollingState};
+use std::collections::{HashMap, HashSet};
+use std::hash::BuildHasherDefault;
+use webrender_traits::{LayerPoint, LayerRect, LayerSize, LayerToScrollTransform, PipelineId};
+use webrender_traits::{ScrollEventPhase, ScrollLayerId, ScrollLayerInfo, ScrollLayerPixel};
+use webrender_traits::{ScrollLayerRect, ScrollLayerState, ScrollLocation, ScrollToWorldTransform};
+use webrender_traits::{ServoScrollRootId, WorldPoint, as_scroll_parent_rect};
+
+pub type ScrollStates = HashMap<ScrollLayerId, ScrollingState, BuildHasherDefault<FnvHasher>>;
+
+pub struct ClipScrollTree {
+    pub nodes: HashMap<ScrollLayerId, ClipScrollNode, BuildHasherDefault<FnvHasher>>,
+    pub pending_scroll_offsets: HashMap<(PipelineId, ServoScrollRootId), LayerPoint>,
+
+    /// The ScrollLayerId of the currently scrolling node. Used to allow the same
+    /// node to scroll even if a touch operation leaves the boundaries of that node.
+    pub current_scroll_layer_id: Option<ScrollLayerId>,
+
+    /// The current reference frame id, used for giving a unique id to all new
+    /// reference frames. The ClipScrollTree increments this by one every time a
+    /// reference frame is created.
+    current_reference_frame_id: usize,
+
+    /// The root reference frame, which is the true root of the ClipScrollTree. Initially
+    /// this ID is not valid, which is indicated by ```node``` being empty.
+    pub root_reference_frame_id: ScrollLayerId,
+
+    /// The root scroll node which is the first child of the root reference frame.
+    /// Initially this ID is not valid, which is indicated by ```nodes``` being empty.
+    pub topmost_scroll_layer_id: ScrollLayerId,
+
+    /// A set of pipelines which should be discarded the next time this
+    /// tree is drained.
+    pub pipelines_to_discard: HashSet<PipelineId>,
+}
+
+impl ClipScrollTree {
+    pub fn new() -> ClipScrollTree {
+        let dummy_pipeline = PipelineId(0, 0);
+        ClipScrollTree {
+            nodes: HashMap::with_hasher(Default::default()),
+            pending_scroll_offsets: HashMap::new(),
+            current_scroll_layer_id: None,
+            root_reference_frame_id: ScrollLayerId::root_reference_frame(dummy_pipeline),
+            topmost_scroll_layer_id: ScrollLayerId::root_scroll_layer(dummy_pipeline),
+            current_reference_frame_id: 1,
+            pipelines_to_discard: HashSet::new(),
+        }
+    }
+
+    pub fn root_reference_frame_id(&self) -> ScrollLayerId {
+        // TODO(mrobinson): We should eventually make this impossible to misuse.
+        debug_assert!(!self.nodes.is_empty());
+        debug_assert!(self.nodes.contains_key(&self.root_reference_frame_id));
+        self.root_reference_frame_id
+    }
+
+    pub fn topmost_scroll_layer_id(&self) -> ScrollLayerId {
+        // TODO(mrobinson): We should eventually make this impossible to misuse.
+        debug_assert!(!self.nodes.is_empty());
+        debug_assert!(self.nodes.contains_key(&self.topmost_scroll_layer_id));
+        self.topmost_scroll_layer_id
+    }
+
+    pub fn establish_root(&mut self,
+                          pipeline_id: PipelineId,
+                          viewport_size: &LayerSize,
+                          content_size: &LayerSize) {
+        debug_assert!(self.nodes.is_empty());
+
+        let identity = LayerToScrollTransform::identity();
+        let viewport = LayerRect::new(LayerPoint::zero(), *viewport_size);
+
+        let root_reference_frame_id = ScrollLayerId::root_reference_frame(pipeline_id);
+        self.root_reference_frame_id = root_reference_frame_id;
+        let reference_frame = ClipScrollNode::new(&viewport, viewport.size, &identity, pipeline_id);
+        self.nodes.insert(self.root_reference_frame_id, reference_frame);
+
+        let scroll_node = ClipScrollNode::new(&viewport, *content_size, &identity, pipeline_id);
+        let topmost_scroll_layer_id = ScrollLayerId::root_scroll_layer(pipeline_id);
+        self.topmost_scroll_layer_id = topmost_scroll_layer_id;
+        self.add_node(scroll_node, topmost_scroll_layer_id, root_reference_frame_id);
+    }
+
+    pub fn collect_nodes_bouncing_back(&self)
+                                       -> HashSet<ScrollLayerId, BuildHasherDefault<FnvHasher>> {
+        let mut nodes_bouncing_back = HashSet::with_hasher(Default::default());
+        for (scroll_layer_id, node) in self.nodes.iter() {
+            if node.scrolling.bouncing_back {
+                nodes_bouncing_back.insert(*scroll_layer_id);
+            }
+        }
+        nodes_bouncing_back
+    }
+
+    fn find_scrolling_node_at_point_in_node(&self,
+                                            cursor: &WorldPoint,
+                                            scroll_layer_id: ScrollLayerId)
+                                            -> Option<ScrollLayerId> {
+        self.nodes.get(&scroll_layer_id).and_then(|node| {
+            for child_layer_id in node.children.iter().rev() {
+            if let Some(layer_id) =
+                self.find_scrolling_node_at_point_in_node(cursor, *child_layer_id) {
+                    return Some(layer_id);
+                }
+            }
+
+            if let ScrollLayerInfo::ReferenceFrame(_) = scroll_layer_id.info {
+                return None;
+            }
+
+            if node.ray_intersects_node(cursor) {
+                Some(scroll_layer_id)
+            } else {
+                None
+            }
+        })
+    }
+
+    pub fn find_scrolling_node_at_point(&self, cursor: &WorldPoint) -> ScrollLayerId {
+        self.find_scrolling_node_at_point_in_node(cursor, self.root_reference_frame_id())
+            .unwrap_or(self.topmost_scroll_layer_id())
+    }
+
+    pub fn get_scroll_node_state(&self) -> Vec<ScrollLayerState> {
+        let mut result = vec![];
+        for (scroll_layer_id, scroll_node) in self.nodes.iter() {
+            match scroll_layer_id.info {
+                ScrollLayerInfo::Scrollable(_, servo_scroll_root_id) => {
+                    result.push(ScrollLayerState {
+                        pipeline_id: scroll_node.pipeline_id,
+                        scroll_root_id: servo_scroll_root_id,
+                        scroll_offset: scroll_node.scrolling.offset,
+                    })
+                }
+                ScrollLayerInfo::ReferenceFrame(..) => {}
+            }
+        }
+        result
+    }
+
+    pub fn drain(&mut self) -> ScrollStates {
+        self.current_reference_frame_id = 1;
+
+        let mut scroll_states = HashMap::with_hasher(Default::default());
+        for (layer_id, old_node) in &mut self.nodes.drain() {
+            if !self.pipelines_to_discard.contains(&layer_id.pipeline_id) {
+                scroll_states.insert(layer_id, old_node.scrolling);
+            }
+        }
+
+        self.pipelines_to_discard.clear();
+        scroll_states
+    }
+
+    pub fn scroll_nodes(&mut self,
+                        origin: LayerPoint,
+                        pipeline_id: PipelineId,
+                        scroll_root_id: ServoScrollRootId)
+                        -> bool {
+        if self.nodes.is_empty() {
+            self.pending_scroll_offsets.insert((pipeline_id, scroll_root_id), origin);
+            return false;
+        }
+
+        let origin = LayerPoint::new(origin.x.max(0.0), origin.y.max(0.0));
+
+        let mut scrolled_a_node = false;
+        let mut found_node = false;
+        for (layer_id, node) in self.nodes.iter_mut() {
+            if layer_id.pipeline_id != pipeline_id {
+                continue;
+            }
+
+            match layer_id.info {
+                ScrollLayerInfo::Scrollable(_, id) if id != scroll_root_id => continue,
+                ScrollLayerInfo::ReferenceFrame(..) => continue,
+                ScrollLayerInfo::Scrollable(..) => {}
+            }
+
+            found_node = true;
+            scrolled_a_node |= node.set_scroll_origin(&origin);
+        }
+
+        if !found_node {
+            self.pending_scroll_offsets.insert((pipeline_id, scroll_root_id), origin);
+        }
+
+        scrolled_a_node
+    }
+
+    pub fn scroll(&mut self,
+                  scroll_location: ScrollLocation,
+                  cursor: WorldPoint,
+                  phase: ScrollEventPhase)
+                  -> bool {
+        if self.nodes.is_empty() {
+            return false;
+        }
+
+        let scroll_layer_id = match (
+            phase,
+            self.find_scrolling_node_at_point(&cursor),
+            self.current_scroll_layer_id) {
+            (ScrollEventPhase::Start, scroll_node_at_point_id, _) => {
+                self.current_scroll_layer_id = Some(scroll_node_at_point_id);
+                scroll_node_at_point_id
+            },
+            (_, scroll_node_at_point_id, Some(cached_scroll_layer_id)) => {
+                let scroll_layer_id = match self.nodes.get(&cached_scroll_layer_id) {
+                    Some(_) => cached_scroll_layer_id,
+                    None => {
+                        self.current_scroll_layer_id = Some(scroll_node_at_point_id);
+                        scroll_node_at_point_id
+                    },
+                };
+                scroll_layer_id
+            },
+            (_, _, None) => return false,
+        };
+
+        let topmost_scroll_layer_id = self.topmost_scroll_layer_id();
+        let non_root_overscroll = if scroll_layer_id != topmost_scroll_layer_id {
+            // true if the current node is overscrolling,
+            // and it is not the root scroll node.
+            let child_node = self.nodes.get(&scroll_layer_id).unwrap();
+            let overscroll_amount = child_node.overscroll_amount();
+            overscroll_amount.width != 0.0 || overscroll_amount.height != 0.0
+        } else {
+            false
+        };
+
+        let switch_node = match phase {
+            ScrollEventPhase::Start => {
+                // if this is a new gesture, we do not switch node,
+                // however we do save the state of non_root_overscroll,
+                // for use in the subsequent Move phase.
+                let mut current_node = self.nodes.get_mut(&scroll_layer_id).unwrap();
+                current_node.scrolling.should_handoff_scroll = non_root_overscroll;
+                false
+            },
+            ScrollEventPhase::Move(_) => {
+                // Switch node if movement originated in a new gesture,
+                // from a non root node in overscroll.
+                let current_node = self.nodes.get_mut(&scroll_layer_id).unwrap();
+                current_node.scrolling.should_handoff_scroll && non_root_overscroll
+            },
+            ScrollEventPhase::End => {
+                // clean-up when gesture ends.
+                let mut current_node = self.nodes.get_mut(&scroll_layer_id).unwrap();
+                current_node.scrolling.should_handoff_scroll = false;
+                false
+            },
+        };
+
+        let scroll_node_info = if switch_node {
+            topmost_scroll_layer_id.info
+        } else {
+            scroll_layer_id.info
+        };
+
+        let scroll_root_id = match scroll_node_info {
+             ScrollLayerInfo::Scrollable(_, scroll_root_id) => scroll_root_id,
+             _ => unreachable!("Tried to scroll a reference frame."),
+
+        };
+
+        let mut scrolled_a_node = false;
+        for (layer_id, node) in self.nodes.iter_mut() {
+            if layer_id.pipeline_id != scroll_layer_id.pipeline_id {
+                continue;
+            }
+
+            match layer_id.info {
+                ScrollLayerInfo::Scrollable(_, id) if id != scroll_root_id => continue,
+                ScrollLayerInfo::ReferenceFrame(..) => continue,
+                _ => {}
+            }
+
+            let scrolled_this_node = node.scroll(scroll_location, phase);
+            scrolled_a_node = scrolled_a_node || scrolled_this_node;
+        }
+        scrolled_a_node
+    }
+
+    pub fn update_all_node_transforms(&mut self) {
+        if self.nodes.is_empty() {
+            return;
+        }
+
+        let root_reference_frame_id = self.root_reference_frame_id();
+        let root_viewport = self.nodes[&root_reference_frame_id].local_viewport_rect;
+        self.update_node_transform(root_reference_frame_id,
+                                    &ScrollToWorldTransform::identity(),
+                                    &as_scroll_parent_rect(&root_viewport));
+    }
+
+    fn update_node_transform(&mut self,
+                             layer_id: ScrollLayerId,
+                             parent_world_transform: &ScrollToWorldTransform,
+                             parent_viewport_rect: &ScrollLayerRect) {
+        // TODO(gw): This is an ugly borrow check workaround to clone these.
+        //           Restructure this to avoid the clones!
+        let (node_transform_for_children, viewport_rect, node_children) = {
+            match self.nodes.get_mut(&layer_id) {
+                Some(node) => {
+                    node.update_transform(parent_world_transform, parent_viewport_rect);
+
+                    (node.world_content_transform.with_source::<ScrollLayerPixel>(),
+                     as_scroll_parent_rect(&node.combined_local_viewport_rect),
+                     node.children.clone())
+                }
+                None => return,
+            }
+        };
+
+        for child_layer_id in node_children {
+            self.update_node_transform(child_layer_id,
+                                       &node_transform_for_children,
+                                       &viewport_rect);
+        }
+    }
+
+    pub fn tick_scrolling_bounce_animations(&mut self) {
+        for (_, node) in &mut self.nodes {
+            node.tick_scrolling_bounce_animation()
+        }
+    }
+
+    pub fn finalize_and_apply_pending_scroll_offsets(&mut self, old_states: ScrollStates) {
+        // TODO(gw): These are all independent - can be run through thread pool if it shows up
+        // in the profile!
+        for (scroll_layer_id, node) in &mut self.nodes {
+            let scrolling_state = match old_states.get(&scroll_layer_id) {
+                Some(old_scrolling_state) => *old_scrolling_state,
+                None => ScrollingState::new(),
+            };
+
+            node.finalize(&scrolling_state);
+
+            let scroll_root_id = match scroll_layer_id.info {
+                ScrollLayerInfo::Scrollable(_, scroll_root_id) => scroll_root_id,
+                _ => continue,
+            };
+
+
+            let pipeline_id = scroll_layer_id.pipeline_id;
+            if let Some(pending_offset) =
+                self.pending_scroll_offsets.remove(&(pipeline_id, scroll_root_id)) {
+                node.set_scroll_origin(&pending_offset);
+            }
+        }
+
+    }
+
+    pub fn add_reference_frame(&mut self,
+                               rect: LayerRect,
+                               transform: LayerToScrollTransform,
+                               pipeline_id: PipelineId,
+                               parent_id: ScrollLayerId) -> ScrollLayerId {
+        let reference_frame_id = ScrollLayerId {
+            pipeline_id: pipeline_id,
+            info: ScrollLayerInfo::ReferenceFrame(self.current_reference_frame_id),
+        };
+        self.current_reference_frame_id += 1;
+
+        let node = ClipScrollNode::new(&rect, rect.size, &transform, pipeline_id);
+        self.add_node(node, reference_frame_id, parent_id);
+        reference_frame_id
+    }
+
+    pub fn add_node(&mut self, node: ClipScrollNode, id: ScrollLayerId, parent_id: ScrollLayerId) {
+        debug_assert!(!self.nodes.contains_key(&id));
+        self.nodes.insert(id, node);
+
+        debug_assert!(parent_id != id);
+        self.nodes.get_mut(&parent_id).unwrap().add_child(id);
+    }
+
+    pub fn discard_frame_state_for_pipeline(&mut self, pipeline_id: PipelineId) {
+        self.pipelines_to_discard.insert(pipeline_id);
+
+        match self.current_scroll_layer_id {
+            Some(id) if id.pipeline_id == pipeline_id => self.current_scroll_layer_id = None,
+            _ => {}
+        }
+    }
+}
+
--- a/gfx/webrender/src/device.rs
+++ b/gfx/webrender/src/device.rs
@@ -37,22 +37,16 @@ const GL_FORMAT_BGRA: gl::GLuint = gl::B
 #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
 const SHADER_VERSION: &'static str = "#version 150\n";
 
 #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
 const SHADER_VERSION: &'static str = "#version 300 es\n";
 
 static SHADER_PREAMBLE: &'static str = "shared";
 
-lazy_static! {
-    pub static ref MAX_TEXTURE_SIZE: gl::GLint = {
-        gl::get_integer_v(gl::MAX_TEXTURE_SIZE)
-    };
-}
-
 #[repr(u32)]
 pub enum DepthFunction {
     Less = gl::LESS,
 }
 
 #[derive(Copy, Clone, Debug, PartialEq)]
 pub enum TextureTarget {
     Default,
@@ -820,16 +814,18 @@ pub struct Device {
 
     // misc.
     shader_preamble: String,
     //file_watcher: FileWatcherThread,
 
     // Used on android only
     #[allow(dead_code)]
     next_vao_id: gl::GLuint,
+
+    max_texture_size: u32,
 }
 
 impl Device {
     pub fn new(resource_override_path: Option<PathBuf>,
                _file_changed_handler: Box<FileWatcherHandler>) -> Device {
         //let file_watcher = FileWatcherThread::new(file_changed_handler);
 
         let shader_preamble = get_shader_source(SHADER_PREAMBLE, &resource_override_path);
@@ -858,19 +854,25 @@ impl Device {
             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,
+
+            max_texture_size: gl::get_integer_v(gl::MAX_TEXTURE_SIZE) as u32
         }
     }
 
+    pub fn max_texture_size(&self) -> u32 {
+        self.max_texture_size
+    }
+
     pub fn get_capabilities(&self) -> &Capabilities {
         &self.capabilities
     }
 
     pub fn compile_shader(name: &str,
                           source_str: &str,
                           shader_type: gl::GLenum,
                           shader_preamble: &[String])
--- a/gfx/webrender/src/frame.rs
+++ b/gfx/webrender/src/frame.rs
@@ -3,20 +3,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use app_units::Au;
 use fnv::FnvHasher;
 use internal_types::{ANGLE_FLOAT_TO_FIXED, AxisDirection};
 use internal_types::{LowLevelFilterOp};
 use internal_types::{RendererFrame};
 use frame_builder::{FrameBuilder, FrameBuilderConfig};
-use layer::Layer;
+use clip_scroll_node::ClipScrollNode;
 use resource_cache::ResourceCache;
 use scene::{Scene, SceneProperties};
-use scroll_tree::{ScrollTree, ScrollStates};
+use clip_scroll_tree::{ClipScrollTree, ScrollStates};
 use std::collections::HashMap;
 use std::hash::BuildHasherDefault;
 use tiling::{AuxiliaryListsMap, CompositeOps, PrimitiveFlags};
 use webrender_traits::{AuxiliaryLists, ClipRegion, ColorF, DisplayItem, Epoch, FilterOp};
 use webrender_traits::{LayerPoint, LayerRect, LayerSize, LayerToScrollTransform, LayoutTransform};
 use webrender_traits::{MixBlendMode, PipelineId, ScrollEventPhase, ScrollLayerId, ScrollLayerState};
 use webrender_traits::{ScrollLocation, ScrollPolicy, ServoScrollRootId, SpecificDisplayItem};
 use webrender_traits::{StackingContext, WorldPoint};
@@ -28,17 +28,17 @@ static DEFAULT_SCROLLBAR_COLOR: ColorF =
 
 struct FlattenContext<'a> {
     scene: &'a Scene,
     builder: &'a mut FrameBuilder,
 }
 
 // TODO: doc
 pub struct Frame {
-    pub scroll_tree: ScrollTree,
+    pub clip_scroll_tree: ClipScrollTree,
     pub pipeline_epoch_map: HashMap<PipelineId, Epoch, BuildHasherDefault<FnvHasher>>,
     pub pipeline_auxiliary_lists: AuxiliaryListsMap,
     id: FrameId,
     frame_builder_config: FrameBuilderConfig,
     frame_builder: Option<FrameBuilder>,
 }
 
 trait DisplayListHelpers {
@@ -175,60 +175,60 @@ impl<'a> Iterator for DisplayListTravers
     }
 }
 
 impl Frame {
     pub fn new(config: FrameBuilderConfig) -> Frame {
         Frame {
             pipeline_epoch_map: HashMap::with_hasher(Default::default()),
             pipeline_auxiliary_lists: HashMap::with_hasher(Default::default()),
-            scroll_tree: ScrollTree::new(),
+            clip_scroll_tree: ClipScrollTree::new(),
             id: FrameId(0),
             frame_builder: None,
             frame_builder_config: config,
         }
     }
 
     pub fn reset(&mut self) -> ScrollStates {
         self.pipeline_epoch_map.clear();
 
         // Advance to the next frame.
         self.id.0 += 1;
 
-        self.scroll_tree.drain()
+        self.clip_scroll_tree.drain()
     }
 
-    pub fn get_scroll_layer_state(&self) -> Vec<ScrollLayerState> {
-        self.scroll_tree.get_scroll_layer_state()
+    pub fn get_scroll_node_state(&self) -> Vec<ScrollLayerState> {
+        self.clip_scroll_tree.get_scroll_node_state()
     }
 
-    /// Returns true if any layers actually changed position or false otherwise.
-    pub fn scroll_layers(&mut self,
-                         origin: LayerPoint,
-                         pipeline_id: PipelineId,
-                         scroll_root_id: ServoScrollRootId)
-                          -> bool {
-        self.scroll_tree.scroll_layers(origin, pipeline_id, scroll_root_id)
+    /// Returns true if any nodes actually changed position or false otherwise.
+    pub fn scroll_nodes(&mut self,
+                        origin: LayerPoint,
+                        pipeline_id: PipelineId,
+                        scroll_root_id: ServoScrollRootId)
+                         -> bool {
+        self.clip_scroll_tree.scroll_nodes(origin, pipeline_id, scroll_root_id)
     }
 
-    /// Returns true if any layers actually changed position or false otherwise.
+    /// Returns true if any nodes actually changed position or false otherwise.
     pub fn scroll(&mut self,
                   scroll_location: ScrollLocation,
                   cursor: WorldPoint,
                   phase: ScrollEventPhase)
                   -> bool {
-        self.scroll_tree.scroll(scroll_location, cursor, phase,)
+        self.clip_scroll_tree.scroll(scroll_location, cursor, phase,)
     }
 
     pub fn tick_scrolling_bounce_animations(&mut self) {
-        self.scroll_tree.tick_scrolling_bounce_animations();
+        self.clip_scroll_tree.tick_scrolling_bounce_animations();
     }
 
     pub fn discard_frame_state_for_pipeline(&mut self, pipeline_id: PipelineId) {
-        self.scroll_tree.discard_frame_state_for_pipeline(pipeline_id);
+        self.clip_scroll_tree.discard_frame_state_for_pipeline(pipeline_id);
     }
 
     pub fn create(&mut self, scene: &Scene) {
         let root_pipeline_id = match scene.root_pipeline_id {
             Some(root_pipeline_id) => root_pipeline_id,
             None => return,
         };
 
@@ -251,19 +251,19 @@ impl Frame {
         let (root_stacking_context, root_clip) = match display_list.starting_stacking_context() {
             Some(some) => some,
             None => {
                 warn!("Pipeline display list does not start with a stacking context.");
                 return;
             }
         };
 
-        self.scroll_tree.establish_root(root_pipeline_id,
-                                        &root_pipeline.viewport_size,
-                                        &root_clip.main.size);
+        self.clip_scroll_tree.establish_root(root_pipeline_id,
+                                             &root_pipeline.viewport_size,
+                                             &root_clip.main.size);
 
         let background_color = root_pipeline.background_color.and_then(|color| {
             if color.a > 0.0 {
                 Some(color)
             } else {
                 None
             }
         });
@@ -274,47 +274,47 @@ impl Frame {
 
         {
             let mut context = FlattenContext {
                 scene: scene,
                 builder: &mut frame_builder,
             };
 
             let mut traversal = DisplayListTraversal::new_skipping_first(display_list);
-            let reference_frame_id = self.scroll_tree.root_reference_frame_id();
-            let topmost_scroll_layer_id = self.scroll_tree.topmost_scroll_layer_id();
+            let reference_frame_id = self.clip_scroll_tree.root_reference_frame_id();
+            let topmost_scroll_layer_id = self.clip_scroll_tree.topmost_scroll_layer_id();
             debug_assert!(reference_frame_id != topmost_scroll_layer_id);
 
             let viewport_rect = LayerRect::new(LayerPoint::zero(), root_pipeline.viewport_size);
             let clip = ClipRegion::simple(&viewport_rect);
-            context.builder.push_scroll_layer(reference_frame_id,
-                                              &clip,
-                                              &LayerPoint::zero(),
-                                              &root_pipeline.viewport_size);
-            context.builder.push_scroll_layer(topmost_scroll_layer_id,
-                                              &clip,
-                                              &LayerPoint::zero(),
-                                              &root_clip.main.size);
+            context.builder.push_clip_scroll_node(reference_frame_id,
+                                                  &clip,
+                                                  &LayerPoint::zero(),
+                                                  &root_pipeline.viewport_size);
+            context.builder.push_clip_scroll_node(topmost_scroll_layer_id,
+                                                  &clip,
+                                                  &LayerPoint::zero(),
+                                                  &root_clip.main.size);
 
             self.flatten_stacking_context(&mut traversal,
                                           root_pipeline_id,
                                           &mut context,
                                           reference_frame_id,
                                           topmost_scroll_layer_id,
                                           LayerToScrollTransform::identity(),
                                           0,
                                           &root_stacking_context,
                                           root_clip);
 
-            context.builder.pop_scroll_layer();
-            context.builder.pop_scroll_layer();
+            context.builder.pop_clip_scroll_node();
+            context.builder.pop_clip_scroll_node();
         }
 
         self.frame_builder = Some(frame_builder);
-        self.scroll_tree.finalize_and_apply_pending_scroll_offsets(old_scrolling_states);
+        self.clip_scroll_tree.finalize_and_apply_pending_scroll_offsets(old_scrolling_states);
     }
 
     fn flatten_scroll_layer<'a>(&mut self,
                                 traversal: &mut DisplayListTraversal<'a>,
                                 pipeline_id: PipelineId,
                                 context: &mut FlattenContext,
                                 current_reference_frame_id: ScrollLayerId,
                                 parent_scroll_layer_id: ScrollLayerId,
@@ -325,32 +325,35 @@ impl Frame {
                                 new_scroll_layer_id: ScrollLayerId) {
         // Avoid doing unnecessary work for empty stacking contexts.
         if traversal.current_stacking_context_empty() {
             traversal.skip_current_stacking_context();
             return;
         }
 
         let clip_rect = clip.main;
-        let layer = Layer::new(&clip_rect, *content_size, &layer_relative_transform, pipeline_id);
-        self.scroll_tree.add_layer(layer, new_scroll_layer_id, parent_scroll_layer_id);
-        context.builder.push_scroll_layer(new_scroll_layer_id,
-                                          clip,
-                                          &clip_rect.origin,
-                                          &content_size);
+        let node = ClipScrollNode::new(&clip_rect,
+                                       *content_size,
+                                       &layer_relative_transform,
+                                       pipeline_id);
+        self.clip_scroll_tree.add_node(node, new_scroll_layer_id, parent_scroll_layer_id);
+        context.builder.push_clip_scroll_node(new_scroll_layer_id,
+                                              clip,
+                                              &clip_rect.origin,
+                                              &content_size);
 
         self.flatten_items(traversal,
                            pipeline_id,
                            context,
                            current_reference_frame_id,
                            new_scroll_layer_id,
                            LayerToScrollTransform::identity(),
                            level);
 
-        context.builder.pop_scroll_layer();
+        context.builder.pop_clip_scroll_node();
     }
 
     fn flatten_stacking_context<'a>(&mut self,
                                     traversal: &mut DisplayListTraversal<'a>,
                                     pipeline_id: PipelineId,
                                     context: &mut FlattenContext,
                                     current_reference_frame_id: ScrollLayerId,
                                     current_scroll_layer_id: ScrollLayerId,
@@ -394,20 +397,20 @@ impl Frame {
             ScrollPolicy::Fixed => current_reference_frame_id,
             ScrollPolicy::Scrollable => current_scroll_layer_id,
         };
 
         // If we have a transformation, we establish a new reference frame. This means
         // that fixed position stacking contexts are positioned relative to us.
         if stacking_context_transform != LayoutTransform::identity() ||
            stacking_context.perspective != LayoutTransform::identity() {
-            scroll_layer_id = self.scroll_tree.add_reference_frame(clip_region.main,
-                                                                   transform,
-                                                                   pipeline_id,
-                                                                   scroll_layer_id);
+            scroll_layer_id = self.clip_scroll_tree.add_reference_frame(clip_region.main,
+                                                                        transform,
+                                                                        pipeline_id,
+                                                                        scroll_layer_id);
             reference_frame_id = scroll_layer_id;
             transform = LayerToScrollTransform::identity();
         }
 
         if level == 0 {
             if let Some(pipeline) = context.scene.pipeline_map.get(&pipeline_id) {
                 if let Some(bg_color) = pipeline.background_color {
 
@@ -415,17 +418,17 @@ impl Frame {
                     let no_clip = ClipRegion::simple(&clip_region.main);
                     context.builder.push_stacking_context(clip_region.main,
                                                           transform,
                                                           pipeline_id,
                                                           scroll_layer_id,
                                                           CompositeOps::empty());
 
                     //Note: we don't use the original clip region here,
-                    // it's already processed by the layer we just pushed.
+                    // it's already processed by the node we just pushed.
                     context.builder.add_solid_rectangle(&clip_region.main,
                                                         &no_clip,
                                                         &bg_color,
                                                         PrimitiveFlags::None);
 
                     context.builder.pop_stacking_context();
                 }
             }
@@ -447,17 +450,17 @@ impl Frame {
                            level);
 
         if level == 0 && self.frame_builder_config.enable_scrollbars {
             let scrollbar_rect = LayerRect::new(LayerPoint::zero(), LayerSize::new(10.0, 70.0));
             context.builder.add_solid_rectangle(
                 &scrollbar_rect,
                 &ClipRegion::simple(&scrollbar_rect),
                 &DEFAULT_SCROLLBAR_COLOR,
-                PrimitiveFlags::Scrollbar(self.scroll_tree.topmost_scroll_layer_id, 4.0));
+                PrimitiveFlags::Scrollbar(self.clip_scroll_tree.topmost_scroll_layer_id(), 4.0));
         }
 
         context.builder.pop_stacking_context();
     }
 
     fn flatten_iframe<'a>(&mut self,
                           pipeline_id: PipelineId,
                           bounds: &LayerRect,
@@ -486,52 +489,52 @@ impl Frame {
 
         self.pipeline_epoch_map.insert(pipeline_id, pipeline.epoch);
 
         let iframe_rect = LayerRect::new(LayerPoint::zero(), bounds.size);
         let transform = layer_relative_transform.pre_translated(bounds.origin.x,
                                                                 bounds.origin.y,
                                                                 0.0);
         let iframe_reference_frame_id =
-            self.scroll_tree.add_reference_frame(iframe_rect,
-                                                 transform,
-                                                 pipeline_id,
-                                                 current_scroll_layer_id);
+            self.clip_scroll_tree.add_reference_frame(iframe_rect,
+                                                      transform,
+                                                      pipeline_id,
+                                                      current_scroll_layer_id);
         let iframe_scroll_layer_id = ScrollLayerId::root_scroll_layer(pipeline_id);
-        let layer = Layer::new(&LayerRect::new(LayerPoint::zero(), iframe_rect.size),
-                               iframe_clip.main.size,
-                               &LayerToScrollTransform::identity(),
-                               pipeline_id);
-        self.scroll_tree.add_layer(layer.clone(),
-                                   iframe_scroll_layer_id,
-                                   iframe_reference_frame_id);
+        let node = ClipScrollNode::new(&LayerRect::new(LayerPoint::zero(), iframe_rect.size),
+                                       iframe_clip.main.size,
+                                       &LayerToScrollTransform::identity(),
+                                       pipeline_id);
+        self.clip_scroll_tree.add_node(node.clone(),
+                                       iframe_scroll_layer_id,
+                                       iframe_reference_frame_id);
 
-        context.builder.push_scroll_layer(iframe_reference_frame_id,
-                                          iframe_clip,
-                                          &LayerPoint::zero(),
-                                          &iframe_rect.size);
-        context.builder.push_scroll_layer(iframe_scroll_layer_id,
-                                          iframe_clip,
-                                          &LayerPoint::zero(),
-                                          &iframe_clip.main.size);
+        context.builder.push_clip_scroll_node(iframe_reference_frame_id,
+                                              iframe_clip,
+                                              &LayerPoint::zero(),
+                                              &iframe_rect.size);
+        context.builder.push_clip_scroll_node(iframe_scroll_layer_id,
+                                              iframe_clip,
+                                              &LayerPoint::zero(),
+                                              &iframe_clip.main.size);
 
         let mut traversal = DisplayListTraversal::new_skipping_first(display_list);
 
         self.flatten_stacking_context(&mut traversal,
                                       pipeline_id,
                                       context,
                                       iframe_reference_frame_id,
                                       iframe_scroll_layer_id,
                                       LayerToScrollTransform::identity(),
                                       0,
                                       &iframe_stacking_context,
                                       iframe_clip);
 
-        context.builder.pop_scroll_layer();
-        context.builder.pop_scroll_layer();
+        context.builder.pop_clip_scroll_node();
+        context.builder.pop_clip_scroll_node();
     }
 
     fn flatten_items<'a>(&mut self,
                          traversal: &mut DisplayListTraversal<'a>,
                          pipeline_id: PipelineId,
                          context: &mut FlattenContext,
                          current_reference_frame_id: ScrollLayerId,
                          current_scroll_layer_id: ScrollLayerId,
@@ -543,16 +546,17 @@ impl Frame {
                     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,
+                                              None,
                                               info.image_key,
                                               info.image_rendering);
                 }
                 SpecificDisplayItem::YuvImage(ref info) => {
                     context.builder.add_yuv_image(item.rect,
                                                   &item.clip,
                                                   info.y_image_key,
                                                   info.u_image_key,
@@ -642,34 +646,34 @@ impl Frame {
         }
     }
 
     pub fn build(&mut self,
                  resource_cache: &mut ResourceCache,
                  auxiliary_lists_map: &AuxiliaryListsMap,
                  device_pixel_ratio: f32)
                  -> RendererFrame {
-        self.scroll_tree.update_all_layer_transforms();
+        self.clip_scroll_tree.update_all_node_transforms();
         let frame = self.build_frame(resource_cache,
                                      auxiliary_lists_map,
                                      device_pixel_ratio);
         resource_cache.expire_old_resources(self.id);
         frame
     }
 
     fn build_frame(&mut self,
                    resource_cache: &mut ResourceCache,
                    auxiliary_lists_map: &AuxiliaryListsMap,
                    device_pixel_ratio: f32) -> RendererFrame {
         let mut frame_builder = self.frame_builder.take();
         let frame = frame_builder.as_mut().map(|builder|
             builder.build(resource_cache,
                           self.id,
-                          &self.scroll_tree,
+                          &self.clip_scroll_tree,
                           auxiliary_lists_map,
                           device_pixel_ratio)
         );
         self.frame_builder = frame_builder;
 
-        let layers_bouncing_back = self.scroll_tree.collect_layers_bouncing_back();
-        RendererFrame::new(self.pipeline_epoch_map.clone(), layers_bouncing_back, frame)
+        let nodes_bouncing_back = self.clip_scroll_tree.collect_nodes_bouncing_back();
+        RendererFrame::new(self.pipeline_epoch_map.clone(), nodes_bouncing_back, frame)
     }
 }
--- a/gfx/webrender/src/frame_builder.rs
+++ b/gfx/webrender/src/frame_builder.rs
@@ -8,35 +8,83 @@ use frame::FrameId;
 use gpu_store::GpuStoreAddress;
 use internal_types::{HardwareCompositeOp, SourceTexture};
 use mask_cache::{ClipSource, MaskCacheInfo};
 use prim_store::{BorderPrimitiveCpu, BorderPrimitiveGpu, BoxShadowPrimitiveGpu};
 use prim_store::{GradientPrimitiveCpu, GradientPrimitiveGpu, ImagePrimitiveCpu, ImagePrimitiveGpu};
 use prim_store::{ImagePrimitiveKind, PrimitiveContainer, PrimitiveGeometry, PrimitiveIndex};
 use prim_store::{PrimitiveStore, RadialGradientPrimitiveCpu, RadialGradientPrimitiveGpu};
 use prim_store::{RectanglePrimitive, TextRunPrimitiveCpu, TextRunPrimitiveGpu};
-use prim_store::{YuvImagePrimitiveCpu, YuvImagePrimitiveGpu};
+use prim_store::{TexelRect, YuvImagePrimitiveCpu, YuvImagePrimitiveGpu};
 use profiler::FrameProfileCounters;
 use render_task::{AlphaRenderItem, MaskCacheKey, MaskResult, RenderTask, RenderTaskIndex};
 use render_task::RenderTaskLocation;
 use resource_cache::ResourceCache;
-use scroll_tree::ScrollTree;
+use clip_scroll_tree::ClipScrollTree;
 use std::{cmp, f32, i32, mem, usize};
-use tiling::{AuxiliaryListsMap, CompositeOps, Frame, PackedLayer, PackedLayerIndex};
-use tiling::{PrimitiveFlags, PrimitiveRunCmd, RenderPass, RenderTargetContext};
-use tiling::{RenderTaskCollection, ScrollbarPrimitive, ScrollLayer, ScrollLayerIndex};
-use tiling::{StackingContext, StackingContextIndex};
+use tiling::{AuxiliaryListsMap, ClipScrollGroup, ClipScrollGroupIndex, CompositeOps, Frame};
+use tiling::{PackedLayer, PackedLayerIndex, PrimitiveFlags, PrimitiveRunCmd, RenderPass};
+use tiling::{RenderTargetContext, RenderTaskCollection, ScrollbarPrimitive, ScrollLayer};
+use tiling::{ScrollLayerIndex, StackingContext, StackingContextIndex};
 use util::{self, pack_as_float, rect_from_points_f, subtract_rect, TransformedRect};
-use util::TransformedRectKind;
-use webrender_traits::{as_scroll_parent_rect, BorderDisplayItem, BorderSide, BorderStyle};
+use util::{RectHelpers, TransformedRectKind};
+use webrender_traits::{as_scroll_parent_rect, BorderDetails, BorderDisplayItem, BorderSide, BorderStyle};
 use webrender_traits::{BoxShadowClipMode, ClipRegion, ColorF, device_length, DeviceIntPoint};
 use webrender_traits::{DeviceIntRect, DeviceIntSize, DeviceUintSize, ExtendMode, FontKey};
 use webrender_traits::{FontRenderMode, GlyphOptions, ImageKey, ImageRendering, ItemRange};
 use webrender_traits::{LayerPoint, LayerRect, LayerSize, LayerToScrollTransform, PipelineId};
-use webrender_traits::{ScrollLayerId, ScrollLayerPixel, WebGLContextId, YuvColorSpace};
+use webrender_traits::{RepeatMode, ScrollLayerId, ScrollLayerPixel, WebGLContextId, YuvColorSpace};
+
+#[derive(Debug, Clone)]
+struct ImageBorderSegment {
+    geom_rect: LayerRect,
+    sub_rect: TexelRect,
+    stretch_size: LayerSize,
+    tile_spacing: LayerSize,
+}
+
+impl ImageBorderSegment {
+    fn new(rect: LayerRect,
+           sub_rect: TexelRect,
+           repeat_horizontal: RepeatMode,
+           repeat_vertical: RepeatMode) -> ImageBorderSegment {
+        let tile_spacing = LayerSize::zero();
+
+        debug_assert!(sub_rect.uv1.x >= sub_rect.uv0.x);
+        debug_assert!(sub_rect.uv1.y >= sub_rect.uv0.y);
+
+        let image_size = LayerSize::new(sub_rect.uv1.x - sub_rect.uv0.x,
+                                        sub_rect.uv1.y - sub_rect.uv0.y);
+
+        let stretch_size_x = match repeat_horizontal {
+            RepeatMode::Stretch => rect.size.width,
+            RepeatMode::Repeat => image_size.width,
+            RepeatMode::Round | RepeatMode::Space => {
+                println!("Round/Space not supported yet!");
+                rect.size.width
+            }
+        };
+
+        let stretch_size_y = match repeat_vertical {
+            RepeatMode::Stretch => rect.size.height,
+            RepeatMode::Repeat => image_size.height,
+            RepeatMode::Round | RepeatMode::Space => {
+                println!("Round/Space not supported yet!");
+                rect.size.height
+            }
+        };
+
+        ImageBorderSegment {
+            geom_rect: rect,
+            sub_rect: sub_rect,
+            stretch_size: LayerSize::new(stretch_size_x, stretch_size_y),
+            tile_spacing: tile_spacing,
+        }
+    }
+}
 
 #[derive(Clone, Copy)]
 pub struct FrameBuilderConfig {
     pub enable_scrollbars: bool,
     pub enable_subpixel_aa: bool,
     pub debug: bool,
 }
 
@@ -57,39 +105,42 @@ pub struct FrameBuilder {
     screen_rect: LayerRect,
     background_color: Option<ColorF>,
     prim_store: PrimitiveStore,
     cmds: Vec<PrimitiveRunCmd>,
     config: FrameBuilderConfig,
 
     stacking_context_store: Vec<StackingContext>,
     scroll_layer_store: Vec<ScrollLayer>,
+    clip_scroll_group_store: Vec<ClipScrollGroup>,
     packed_layers: Vec<PackedLayer>,
 
     scrollbar_prims: Vec<ScrollbarPrimitive>,
 
-    /// A stack of scroll layers used during building to properly parent new scroll layers.
-    scroll_layer_stack: Vec<ScrollLayerIndex>,
+    /// A stack of scroll nodes used during display list processing to properly
+    /// parent new scroll nodes.
+    clip_scroll_node_stack: Vec<ScrollLayerIndex>,
 }
 
 impl FrameBuilder {
     pub fn new(viewport_size: LayerSize,
                background_color: Option<ColorF>,
                config: FrameBuilderConfig) -> FrameBuilder {
         FrameBuilder {
             screen_rect: LayerRect::new(LayerPoint::zero(), viewport_size),
             background_color: background_color,
             stacking_context_store: Vec::new(),
             scroll_layer_store: Vec::new(),
+            clip_scroll_group_store: Vec::new(),
             prim_store: PrimitiveStore::new(),
             cmds: Vec::new(),
             packed_layers: Vec::new(),
             scrollbar_prims: Vec::new(),
             config: config,
-            scroll_layer_stack: Vec::new(),
+            clip_scroll_node_stack: Vec::new(),
         }
     }
 
     fn add_primitive(&mut self,
                      rect: &LayerRect,
                      clip_region: &ClipRegion,
                      container: PrimitiveContainer) -> PrimitiveIndex {
 
@@ -123,88 +174,103 @@ impl FrameBuilder {
             &mut PrimitiveRunCmd::PopScrollLayer => {}
         }
 
         self.cmds.push(PrimitiveRunCmd::PrimitiveRun(prim_index, 1));
 
         prim_index
     }
 
+    pub fn create_clip_scroll_group(&mut self,
+                                    stacking_context_index: StackingContextIndex,
+                                    scroll_layer_id: ScrollLayerId,
+                                    pipeline_id: PipelineId)
+                                    -> ClipScrollGroupIndex {
+        let packed_layer_index = PackedLayerIndex(self.packed_layers.len());
+        self.packed_layers.push(PackedLayer::empty());
+
+        self.clip_scroll_group_store.push(ClipScrollGroup {
+            stacking_context_index: stacking_context_index,
+            scroll_layer_id: scroll_layer_id,
+            packed_layer_index: packed_layer_index,
+            pipeline_id: pipeline_id,
+            xf_rect: None,
+         });
+
+        ClipScrollGroupIndex(self.clip_scroll_group_store.len() - 1)
+    }
+
     pub fn push_stacking_context(&mut self,
                                  rect: LayerRect,
                                  transform: LayerToScrollTransform,
                                  pipeline_id: PipelineId,
                                  scroll_layer_id: ScrollLayerId,
                                  composite_ops: CompositeOps) {
         let stacking_context_index = StackingContextIndex(self.stacking_context_store.len());
-        let packed_layer_index = PackedLayerIndex(self.packed_layers.len());
-
-        self.stacking_context_store.push(StackingContext {
-            local_rect: rect,
-            local_transform: transform,
-            scroll_layer_id: scroll_layer_id,
-            pipeline_id: pipeline_id,
-            xf_rect: None,
-            composite_ops: composite_ops,
-            packed_layer_index: packed_layer_index,
-        });
-        self.packed_layers.push(PackedLayer::empty());
+        let group_index = self.create_clip_scroll_group(stacking_context_index,
+                                                        scroll_layer_id,
+                                                        pipeline_id);
+        self.stacking_context_store.push(StackingContext::new(pipeline_id,
+                                                              transform,
+                                                              rect,
+                                                              composite_ops,
+                                                              group_index));
         self.cmds.push(PrimitiveRunCmd::PushStackingContext(stacking_context_index));
-
     }
 
     pub fn pop_stacking_context(&mut self) {
         self.cmds.push(PrimitiveRunCmd::PopStackingContext);
     }
 
-    pub fn push_scroll_layer(&mut self,
-                             scroll_layer_id: ScrollLayerId,
-                             clip_region: &ClipRegion,
-                             iframe_origin: &LayerPoint,
-                             content_size: &LayerSize) {
+    pub fn push_clip_scroll_node(&mut self,
+                                 scroll_layer_id: ScrollLayerId,
+                                 clip_region: &ClipRegion,
+                                 node_origin: &LayerPoint,
+                                 content_size: &LayerSize) {
         let scroll_layer_index = ScrollLayerIndex(self.scroll_layer_store.len());
+        let parent_index = *self.clip_scroll_node_stack.last().unwrap_or(&scroll_layer_index);
+        self.clip_scroll_node_stack.push(scroll_layer_index);
+
         let packed_layer_index = PackedLayerIndex(self.packed_layers.len());
 
         let clip_source = ClipSource::Region(clip_region.clone());
         let clip_info = MaskCacheInfo::new(&clip_source,
                                            true, // needs an extra clip for the clip rectangle
                                            &mut self.prim_store.gpu_data32);
 
-        let parent_index = *self.scroll_layer_stack.last().unwrap_or(&scroll_layer_index);
         self.scroll_layer_store.push(ScrollLayer {
             scroll_layer_id: scroll_layer_id,
             parent_index: parent_index,
             clip_source: clip_source,
             clip_cache_info: clip_info,
             xf_rect: None,
             packed_layer_index: packed_layer_index,
         });
         self.packed_layers.push(PackedLayer::empty());
         self.cmds.push(PrimitiveRunCmd::PushScrollLayer(scroll_layer_index));
 
 
         // We need to push a fake stacking context here, because primitives that are
         // direct children of this stacking context, need to be adjusted by the scroll
         // offset of this layer. Eventually we should be able to remove this.
         let rect = LayerRect::new(LayerPoint::zero(),
-                                  LayerSize::new(content_size.width + iframe_origin.x,
-                                                 content_size.height + iframe_origin.y));
+                                  LayerSize::new(content_size.width + node_origin.x,
+                                                 content_size.height + node_origin.y));
         self.push_stacking_context(rect,
                                    LayerToScrollTransform::identity(),
                                    scroll_layer_id.pipeline_id,
                                    scroll_layer_id,
                                    CompositeOps::empty());
 
-        self.scroll_layer_stack.push(scroll_layer_index);
     }
 
-    pub fn pop_scroll_layer(&mut self) {
+    pub fn pop_clip_scroll_node(&mut self) {
         self.pop_stacking_context();
         self.cmds.push(PrimitiveRunCmd::PopScrollLayer);
-        self.scroll_layer_stack.pop();
+        self.clip_scroll_node_stack.pop();
     }
 
     pub fn add_solid_rectangle(&mut self,
                                rect: &LayerRect,
                                clip_region: &ClipRegion,
                                color: &ColorF,
                                flags: PrimitiveFlags) {
         if color.a == 0.0 {
@@ -249,128 +315,233 @@ impl FrameBuilder {
                 return false;
             }
         }
     }
 
     pub fn add_border(&mut self,
                       rect: LayerRect,
                       clip_region: &ClipRegion,
-                      border: &BorderDisplayItem) {
-        let radius = &border.radius;
-        let left = &border.left;
-        let right = &border.right;
-        let top = &border.top;
-        let bottom = &border.bottom;
-
-        if !self.supported_style(left) || !self.supported_style(right) ||
-           !self.supported_style(top) || !self.supported_style(bottom) {
-            println!("Unsupported border style, not rendering border");
-            return;
-        }
+                      border_item: &BorderDisplayItem) {
+        match border_item.details {
+            BorderDetails::Image(ref border) => {
+                // Calculate the modified rect as specific by border-image-outset
+                let origin = LayerPoint::new(rect.origin.x - border.outset.left,
+                                             rect.origin.y - border.outset.top);
+                let size = LayerSize::new(rect.size.width + border.outset.left + border.outset.right,
+                                          rect.size.height + border.outset.top + border.outset.bottom);
+                let rect = LayerRect::new(origin, size);
 
-        // These colors are used during inset/outset scaling.
-        let left_color      = left.border_color(1.0, 2.0/3.0, 0.3, 0.7);
-        let top_color       = top.border_color(1.0, 2.0/3.0, 0.3, 0.7);
-        let right_color     = right.border_color(2.0/3.0, 1.0, 0.7, 0.3);
-        let bottom_color    = bottom.border_color(2.0/3.0, 1.0, 0.7, 0.3);
+                // Calculate the local texel coords of the slices.
+                let px0 = 0;
+                let px1 = border.patch.slice.left;
+                let px2 = border.patch.width - border.patch.slice.right;
+                let px3 = border.patch.width;
 
-        let tl_outer = LayerPoint::new(rect.origin.x, rect.origin.y);
-        let tl_inner = tl_outer + LayerPoint::new(radius.top_left.width.max(left.width),
-                                                  radius.top_left.height.max(top.width));
+                let py0 = 0;
+                let py1 = border.patch.slice.top;
+                let py2 = border.patch.height - border.patch.slice.bottom;
+                let py3 = border.patch.height;
 
-        let tr_outer = LayerPoint::new(rect.origin.x + rect.size.width, rect.origin.y);
-        let tr_inner = tr_outer + LayerPoint::new(-radius.top_right.width.max(right.width),
-                                                  radius.top_right.height.max(top.width));
+                let tl_outer = LayerPoint::new(rect.origin.x, rect.origin.y);
+                let tl_inner = tl_outer + LayerPoint::new(border_item.widths.left, border_item.widths.top);
 
-        let bl_outer = LayerPoint::new(rect.origin.x, rect.origin.y + rect.size.height);
-        let bl_inner = bl_outer + LayerPoint::new(radius.bottom_left.width.max(left.width),
-                                                  -radius.bottom_left.height.max(bottom.width));
+                let tr_outer = LayerPoint::new(rect.origin.x + rect.size.width, rect.origin.y);
+                let tr_inner = tr_outer + LayerPoint::new(-border_item.widths.right, border_item.widths.top);
+
+                let bl_outer = LayerPoint::new(rect.origin.x, rect.origin.y + rect.size.height);
+                let bl_inner = bl_outer + LayerPoint::new(border_item.widths.left, -border_item.widths.bottom);
 
-        let br_outer = LayerPoint::new(rect.origin.x + rect.size.width,
-                                       rect.origin.y + rect.size.height);
-        let br_inner = br_outer - LayerPoint::new(radius.bottom_right.width.max(right.width),
-                                                  radius.bottom_right.height.max(bottom.width));
+                let br_outer = LayerPoint::new(rect.origin.x + rect.size.width,
+                                               rect.origin.y + rect.size.height);
+                let br_inner = br_outer - LayerPoint::new(border_item.widths.right, border_item.widths.bottom);
+
+                // Build the list of image segments
+                let mut segments = vec![
+                    // Top left
+                    ImageBorderSegment::new(LayerRect::from_floats(tl_outer.x, tl_outer.y, tl_inner.x, tl_inner.y),
+                                            TexelRect::new(px0, py0, px1, py1),
+                                            RepeatMode::Stretch,
+                                            RepeatMode::Stretch),
 
-        // The border shader is quite expensive. For simple borders, we can just draw
-        // the border with a few rectangles. This generally gives better batching, and
-        // a GPU win in fragment shader time.
-        // More importantly, the software (OSMesa) implementation we run tests on is
-        // particularly slow at running our complex border shader, compared to the
-        // rectangle shader. This has the effect of making some of our tests time
-        // out more often on CI (the actual cause is simply too many Servo processes and
-        // threads being run on CI at once).
-        // TODO(gw): Detect some more simple cases and handle those with simpler shaders too.
-        // TODO(gw): Consider whether it's only worth doing this for large rectangles (since
-        //           it takes a little more CPU time to handle multiple rectangles compared
-        //           to a single border primitive).
-        if left.style == BorderStyle::Solid {
-            let same_color = left_color == top_color &&
-                             left_color == right_color &&
-                             left_color == bottom_color;
-            let same_style = left.style == top.style &&
-                             left.style == right.style &&
-                             left.style == bottom.style;
+                    // Top right
+                    ImageBorderSegment::new(LayerRect::from_floats(tr_inner.x, tr_outer.y, tr_outer.x, tr_inner.y),
+                                            TexelRect::new(px2, py0, px3, py1),
+                                            RepeatMode::Stretch,
+                                            RepeatMode::Stretch),
 
-            if same_color && same_style && radius.is_zero() {
-                let rects = [
-                    LayerRect::new(rect.origin,
-                                   LayerSize::new(rect.size.width, top.width)),
-                    LayerRect::new(LayerPoint::new(tl_outer.x, tl_inner.y),
-                                   LayerSize::new(left.width,
-                                                  rect.size.height - top.width - bottom.width)),
-                    LayerRect::new(tr_inner,
-                                   LayerSize::new(right.width,
-                                                  rect.size.height - top.width - bottom.width)),
-                    LayerRect::new(LayerPoint::new(bl_outer.x, bl_inner.y),
-                                   LayerSize::new(rect.size.width, bottom.width))
+                    // Bottom right
+                    ImageBorderSegment::new(LayerRect::from_floats(br_inner.x, br_inner.y, br_outer.x, br_outer.y),
+                                            TexelRect::new(px2, py2, px3, py3),
+                                            RepeatMode::Stretch,
+                                            RepeatMode::Stretch),
+
+                    // Bottom left
+                    ImageBorderSegment::new(LayerRect::from_floats(bl_outer.x, bl_inner.y, bl_inner.x, bl_outer.y),
+                                            TexelRect::new(px0, py2, px1, py3),
+                                            RepeatMode::Stretch,
+                                            RepeatMode::Stretch),
                 ];
 
-                for rect in &rects {
-                    self.add_solid_rectangle(rect,
-                                             clip_region,
-                                             &top_color,
-                                             PrimitiveFlags::None);
+                // Add edge segments if valid size.
+                if px1 < px2 && py1 < py2 {
+                    segments.extend_from_slice(&[
+                        // Top
+                        ImageBorderSegment::new(LayerRect::from_floats(tl_inner.x, tl_outer.y, tr_inner.x, tl_inner.y),
+                                                TexelRect::new(px1, py0, px2, py1),
+                                                border.repeat_horizontal,
+                                                RepeatMode::Stretch),
+
+                        // Bottom
+                        ImageBorderSegment::new(LayerRect::from_floats(bl_inner.x, bl_inner.y, br_inner.x, bl_outer.y),
+                                                TexelRect::new(px1, py2, px2, py3),
+                                                border.repeat_horizontal,
+                                                RepeatMode::Stretch),
+
+                        // Left
+                        ImageBorderSegment::new(LayerRect::from_floats(tl_outer.x, tl_inner.y, tl_inner.x, bl_inner.y),
+                                                TexelRect::new(px0, py1, px1, py2),
+                                                RepeatMode::Stretch,
+                                                border.repeat_vertical),
+
+                        // Right
+                        ImageBorderSegment::new(LayerRect::from_floats(tr_inner.x, tr_inner.y, br_outer.x, br_inner.y),
+                                                TexelRect::new(px2, py1, px3, py2),
+                                                RepeatMode::Stretch,
+                                                border.repeat_vertical),
+                    ]);
+                }
+
+                for segment in segments {
+                    self.add_image(segment.geom_rect,
+                                   clip_region,
+                                   &segment.stretch_size,
+                                   &segment.tile_spacing,
+                                   Some(segment.sub_rect),
+                                   border.image_key,
+                                   ImageRendering::Auto);
+                }
+            }
+            BorderDetails::Normal(ref border) => {
+                let radius = &border.radius;
+                let left = &border.left;
+                let right = &border.right;
+                let top = &border.top;
+                let bottom = &border.bottom;
+
+                if !self.supported_style(left) || !self.supported_style(right) ||
+                   !self.supported_style(top) || !self.supported_style(bottom) {
+                    println!("Unsupported border style, not rendering border");
+                    return;
                 }
 
-                return;
+                // These colors are used during inset/outset scaling.
+                let left_color      = left.border_color(1.0, 2.0/3.0, 0.3, 0.7);
+                let top_color       = top.border_color(1.0, 2.0/3.0, 0.3, 0.7);
+                let right_color     = right.border_color(2.0/3.0, 1.0, 0.7, 0.3);
+                let bottom_color    = bottom.border_color(2.0/3.0, 1.0, 0.7, 0.3);
+
+                let tl_outer = LayerPoint::new(rect.origin.x, rect.origin.y);
+                let tl_inner = tl_outer + LayerPoint::new(radius.top_left.width.max(border_item.widths.left),
+                                                          radius.top_left.height.max(border_item.widths.top));
+
+                let tr_outer = LayerPoint::new(rect.origin.x + rect.size.width, rect.origin.y);
+                let tr_inner = tr_outer + LayerPoint::new(-radius.top_right.width.max(border_item.widths.right),
+                                                          radius.top_right.height.max(border_item.widths.top));
+
+                let bl_outer = LayerPoint::new(rect.origin.x, rect.origin.y + rect.size.height);
+                let bl_inner = bl_outer + LayerPoint::new(radius.bottom_left.width.max(border_item.widths.left),
+                                                          -radius.bottom_left.height.max(border_item.widths.bottom));
+
+                let br_outer = LayerPoint::new(rect.origin.x + rect.size.width,
+                                               rect.origin.y + rect.size.height);
+                let br_inner = br_outer - LayerPoint::new(radius.bottom_right.width.max(border_item.widths.right),
+                                                          radius.bottom_right.height.max(border_item.widths.bottom));
+
+                // The border shader is quite expensive. For simple borders, we can just draw
+                // the border with a few rectangles. This generally gives better batching, and
+                // a GPU win in fragment shader time.
+                // More importantly, the software (OSMesa) implementation we run tests on is
+                // particularly slow at running our complex border shader, compared to the
+                // rectangle shader. This has the effect of making some of our tests time
+                // out more often on CI (the actual cause is simply too many Servo processes and
+                // threads being run on CI at once).
+                // TODO(gw): Detect some more simple cases and handle those with simpler shaders too.
+                // TODO(gw): Consider whether it's only worth doing this for large rectangles (since
+                //           it takes a little more CPU time to handle multiple rectangles compared
+                //           to a single border primitive).
+                if left.style == BorderStyle::Solid {
+                    let same_color = left_color == top_color &&
+                                     left_color == right_color &&
+                                     left_color == bottom_color;
+                    let same_style = left.style == top.style &&
+                                     left.style == right.style &&
+                                     left.style == bottom.style;
+
+                    if same_color && same_style && radius.is_zero() {
+                        let rects = [
+                            LayerRect::new(rect.origin,
+                                           LayerSize::new(rect.size.width, border_item.widths.top)),
+                            LayerRect::new(LayerPoint::new(tl_outer.x, tl_inner.y),
+                                           LayerSize::new(border_item.widths.left,
+                                                          rect.size.height - border_item.widths.top - border_item.widths.bottom)),
+                            LayerRect::new(tr_inner,
+                                           LayerSize::new(border_item.widths.right,
+                                                          rect.size.height - border_item.widths.top - border_item.widths.bottom)),
+                            LayerRect::new(LayerPoint::new(bl_outer.x, bl_inner.y),
+                                           LayerSize::new(rect.size.width, border_item.widths.bottom))
+                        ];
+
+                        for rect in &rects {
+                            self.add_solid_rectangle(rect,
+                                                     clip_region,
+                                                     &top_color,
+                                                     PrimitiveFlags::None);
+                        }
+
+                        return;
+                    }
+                }
+
+                //Note: while similar to `ComplexClipRegion::get_inner_rect()` in spirit,
+                // this code is a bit more complex and can not there for be merged.
+                let inner_rect = rect_from_points_f(tl_inner.x.max(bl_inner.x),
+                                                    tl_inner.y.max(tr_inner.y),
+                                                    tr_inner.x.min(br_inner.x),
+                                                    bl_inner.y.min(br_inner.y));
+
+                let prim_cpu = BorderPrimitiveCpu {
+                    inner_rect: LayerRect::from_untyped(&inner_rect),
+                };
+
+                let prim_gpu = BorderPrimitiveGpu {
+                    colors: [ left_color, top_color, right_color, bottom_color ],
+                    widths: [ border_item.widths.left,
+                              border_item.widths.top,
+                              border_item.widths.right,
+                              border_item.widths.bottom ],
+                    style: [
+                        pack_as_float(left.style as u32),
+                        pack_as_float(top.style as u32),
+                        pack_as_float(right.style as u32),
+                        pack_as_float(bottom.style as u32),
+                    ],
+                    radii: [
+                        radius.top_left,
+                        radius.top_right,
+                        radius.bottom_right,
+                        radius.bottom_left,
+                    ],
+                };
+
+                self.add_primitive(&rect,
+                                   clip_region,
+                                   PrimitiveContainer::Border(prim_cpu, prim_gpu));
             }
         }
-
-        //Note: while similar to `ComplexClipRegion::get_inner_rect()` in spirit,
-        // this code is a bit more complex and can not there for be merged.
-        let inner_rect = rect_from_points_f(tl_inner.x.max(bl_inner.x),
-                                            tl_inner.y.max(tr_inner.y),
-                                            tr_inner.x.min(br_inner.x),
-                                            bl_inner.y.min(br_inner.y));
-
-        let prim_cpu = BorderPrimitiveCpu {
-            inner_rect: LayerRect::from_untyped(&inner_rect),
-        };
-
-        let prim_gpu = BorderPrimitiveGpu {
-            colors: [ left_color, top_color, right_color, bottom_color ],
-            widths: [ left.width, top.width, right.width, bottom.width ],
-            style: [
-                pack_as_float(left.style as u32),
-                pack_as_float(top.style as u32),
-                pack_as_float(right.style as u32),
-                pack_as_float(bottom.style as u32),
-            ],
-            radii: [
-                radius.top_left,
-                radius.top_right,
-                radius.bottom_right,
-                radius.bottom_left,
-            ],
-        };
-
-        self.add_primitive(&rect,
-                           clip_region,
-                           PrimitiveContainer::Border(prim_cpu, prim_gpu));
     }
 
     pub fn add_gradient(&mut self,
                         rect: LayerRect,
                         clip_region: &ClipRegion,
                         start_point: LayerPoint,
                         end_point: LayerPoint,
                         stops: ItemRange,
@@ -536,67 +707,118 @@ impl FrameBuilder {
         if blur_radius == 0.0 && spread_radius == 0.0 && clip_mode == BoxShadowClipMode::None {
             self.add_solid_rectangle(&box_bounds,
                                      clip_region,
                                      color,
                                      PrimitiveFlags::None);
             return;
         }
 
+        // The local space box shadow rect. It is the element rect
+        // translated by the box shadow offset and inflated by the
+        // box shadow spread.
         let bs_rect = box_bounds.translate(box_offset)
                                 .inflate(spread_radius, spread_radius);
 
+        // Get the outer rectangle, based on the blur radius.
         let outside_edge_size = 2.0 * blur_radius;
         let inside_edge_size = outside_edge_size.max(border_radius);
         let edge_size = outside_edge_size + inside_edge_size;
         let outer_rect = bs_rect.inflate(outside_edge_size, outside_edge_size);
-        let mut instance_rects = Vec::new();
-        let (prim_rect, inverted) = match clip_mode {
+
+        // Box shadows are often used for things like text underline and other
+        // simple primitives, so we want to draw these simple cases with the
+        // solid rectangle shader wherever possible, to avoid invoking the
+        // expensive box-shadow shader.
+        enum BoxShadowKind {
+            Simple(Vec<LayerRect>),     // Can be drawn via simple rectangles only
+            Shadow(Vec<LayerRect>),     // Requires the full box-shadow code path
+        }
+
+        let shadow_kind = match clip_mode {
             BoxShadowClipMode::Outset | BoxShadowClipMode::None => {
-                subtract_rect(&outer_rect, box_bounds, &mut instance_rects);
-                (outer_rect, 0.0)
+                // For outset shadows, subtracting the element rectangle
+                // from the outer rectangle gives the rectangles we need
+                // to draw. In the simple case (no blur radius), we can
+                // just draw these as solid colors.
+                let mut rects = Vec::new();
+                subtract_rect(&outer_rect, box_bounds, &mut rects);
+                if edge_size == 0.0 {
+                    BoxShadowKind::Simple(rects)
+                } else {
+                    BoxShadowKind::Shadow(rects)
+                }
             }
             BoxShadowClipMode::Inset => {
-                subtract_rect(box_bounds, &bs_rect, &mut instance_rects);
-                (*box_bounds, 1.0)
+                // For inset shadows, in the simple case (no blur) we
+                // can draw the shadow area by subtracting the box
+                // shadow rect from the element rect (since inset box
+                // shadows never extend past the element rect). However,
+                // in the case of an inset box shadow with blur, we
+                // currently just draw the box shadow over the entire
+                // rect. The opaque parts of the shadow (past the outside
+                // edge of the box-shadow) are handled by the shadow
+                // shader.
+                // TODO(gw): We should be able to optimize the complex
+                //           inset shadow case to touch fewer pixels. We
+                //           can probably calculate the inner rect that
+                //           can't be affected, and subtract that from
+                //           the element rect?
+                let mut rects = Vec::new();
+                if edge_size == 0.0 {
+                    subtract_rect(box_bounds, &bs_rect, &mut rects);
+                    BoxShadowKind::Simple(rects)
+                } else {
+                    rects.push(*box_bounds);
+                    BoxShadowKind::Shadow(rects)
+                }
             }
         };
 
-        if edge_size == 0.0 {
-            for rect in &instance_rects {
-                self.add_solid_rectangle(rect,
-                                         clip_region,
-                                         color,
-                                         PrimitiveFlags::None)
+        match shadow_kind {
+            BoxShadowKind::Simple(rects) => {
+                for rect in &rects {
+                    self.add_solid_rectangle(rect,
+                                             clip_region,
+                                             color,
+                                             PrimitiveFlags::None)
+                }
             }
-        } else {
-            let prim_gpu = BoxShadowPrimitiveGpu {
-                src_rect: *box_bounds,
-                bs_rect: bs_rect,
-                color: *color,
-                blur_radius: blur_radius,
-                border_radius: border_radius,
-                edge_size: edge_size,
-                inverted: inverted,
-            };
+            BoxShadowKind::Shadow(rects) => {
+                let inverted = match clip_mode {
+                    BoxShadowClipMode::Outset | BoxShadowClipMode::None => 0.0,
+                    BoxShadowClipMode::Inset => 1.0,
+                };
 
-            self.add_primitive(&prim_rect,
-                               clip_region,
-                               PrimitiveContainer::BoxShadow(prim_gpu, instance_rects));
+                let prim_gpu = BoxShadowPrimitiveGpu {
+                    src_rect: *box_bounds,
+                    bs_rect: bs_rect,
+                    color: *color,
+                    blur_radius: blur_radius,
+                    border_radius: border_radius,
+                    edge_size: edge_size,
+                    inverted: inverted,
+                };
+
+                self.add_primitive(&outer_rect,
+                                   clip_region,
+                                   PrimitiveContainer::BoxShadow(prim_gpu, rects));
+            }
         }
     }
 
     pub fn add_webgl_rectangle(&mut self,
                                rect: LayerRect,
                                clip_region: &ClipRegion,
                                context_id: WebGLContextId) {
         let prim_cpu = ImagePrimitiveCpu {
             kind: ImagePrimitiveKind::WebGL(context_id),
             color_texture_id: SourceTexture::Invalid,
             resource_address: GpuStoreAddress(0),
+            sub_rect: None,
         };
 
         let prim_gpu = ImagePrimitiveGpu {
             stretch_size: rect.size,
             tile_spacing: LayerSize::zero(),
         };
 
         self.add_primitive(&rect,
@@ -604,24 +826,26 @@ impl FrameBuilder {
                            PrimitiveContainer::Image(prim_cpu, prim_gpu));
     }
 
     pub fn add_image(&mut self,
                      rect: LayerRect,
                      clip_region: &ClipRegion,
                      stretch_size: &LayerSize,
                      tile_spacing: &LayerSize,
+                     sub_rect: Option<TexelRect>,
                      image_key: ImageKey,
                      image_rendering: ImageRendering) {
         let prim_cpu = ImagePrimitiveCpu {
             kind: ImagePrimitiveKind::Image(image_key,
                                             image_rendering,
                                             *tile_spacing),
             color_texture_id: SourceTexture::Invalid,
             resource_address: GpuStoreAddress(0),
+            sub_rect: sub_rect,
         };
 
         let prim_gpu = ImagePrimitiveGpu {
             stretch_size: *stretch_size,
             tile_spacing: *tile_spacing,
         };
 
         self.add_primitive(&rect,
@@ -652,60 +876,60 @@ impl FrameBuilder {
                            clip_region,
                            PrimitiveContainer::YuvImage(prim_cpu, prim_gpu));
     }
 
     /// Compute the contribution (bounding rectangles, and resources) of layers and their
     /// primitives in screen space.
     fn build_layer_screen_rects_and_cull_layers(&mut self,
                                                 screen_rect: &DeviceIntRect,
-                                                scroll_tree: &ScrollTree,
+                                                clip_scroll_tree: &ClipScrollTree,
                                                 auxiliary_lists_map: &AuxiliaryListsMap,
                                                 resource_cache: &mut ResourceCache,
                                                 profile_counters: &mut FrameProfileCounters,
                                                 device_pixel_ratio: f32) {
         profile_scope!("cull");
         LayerRectCalculationAndCullingPass::create_and_run(self,
                                                            screen_rect,
-                                                           scroll_tree,
+                                                           clip_scroll_tree,
                                                            auxiliary_lists_map,
                                                            resource_cache,
                                                            profile_counters,
                                                            device_pixel_ratio);
     }
 
-    fn update_scroll_bars(&mut self, scroll_tree: &ScrollTree) {
+    fn update_scroll_bars(&mut self, clip_scroll_tree: &ClipScrollTree) {
         let distance_from_edge = 8.0;
 
         for scrollbar_prim in &self.scrollbar_prims {
             let mut geom = (*self.prim_store.gpu_geometry.get(GpuStoreAddress(scrollbar_prim.prim_index.0 as i32))).clone();
-            let scroll_layer = &scroll_tree.layers[&scrollbar_prim.scroll_layer_id];
+            let clip_scroll_node = &clip_scroll_tree.nodes[&scrollbar_prim.scroll_layer_id];
 
-            let scrollable_distance = scroll_layer.scrollable_height();
+            let scrollable_distance = clip_scroll_node.scrollable_height();
 
             if scrollable_distance <= 0.0 {
                 geom.local_clip_rect.size = LayerSize::zero();
                 *self.prim_store.gpu_geometry.get_mut(GpuStoreAddress(scrollbar_prim.prim_index.0 as i32)) = geom;
                 continue;
             }
 
-            let f = -scroll_layer.scrolling.offset.y / scrollable_distance;
+            let f = -clip_scroll_node.scrolling.offset.y / scrollable_distance;
 
-            let min_y = scroll_layer.local_viewport_rect.origin.y -
-                        scroll_layer.scrolling.offset.y +
+            let min_y = clip_scroll_node.local_viewport_rect.origin.y -
+                        clip_scroll_node.scrolling.offset.y +
                         distance_from_edge;
 
-            let max_y = scroll_layer.local_viewport_rect.origin.y +
-                        scroll_layer.local_viewport_rect.size.height -
-                        scroll_layer.scrolling.offset.y -
+            let max_y = clip_scroll_node.local_viewport_rect.origin.y +
+                        clip_scroll_node.local_viewport_rect.size.height -
+                        clip_scroll_node.scrolling.offset.y -
                         geom.local_rect.size.height -
                         distance_from_edge;
 
-            geom.local_rect.origin.x = scroll_layer.local_viewport_rect.origin.x +
-                                       scroll_layer.local_viewport_rect.size.width -
+            geom.local_rect.origin.x = clip_scroll_node.local_viewport_rect.origin.x +
+                                       clip_scroll_node.local_viewport_rect.size.width -
                                        geom.local_rect.size.width -
                                        distance_from_edge;
 
             geom.local_rect.origin.y = util::lerp(min_y, max_y, f);
             geom.local_clip_rect = geom.local_rect;
 
             let clip_source = if scrollbar_prim.border_radius == 0.0 {
                 ClipSource::NoClip
@@ -731,38 +955,37 @@ impl FrameBuilder {
         let mut alpha_task_stack = Vec::new();
 
         for cmd in &self.cmds {
             match *cmd {
                 PrimitiveRunCmd::PushStackingContext(stacking_context_index) => {
                     let stacking_context = &self.stacking_context_store[stacking_context_index.0];
                     sc_stack.push(stacking_context_index);
 
-                    if !stacking_context.is_visible() {
+                    if !stacking_context.is_visible {
                         continue;
                     }
 
+                    let stacking_context_rect = &stacking_context.bounding_rect;
                     let composite_count = stacking_context.composite_ops.count();
                     for _ in 0..composite_count {
-                        let stacking_context_rect =
-                            stacking_context.xf_rect.as_ref().unwrap().bounding_rect;
                         let location = RenderTaskLocation::Dynamic(None, stacking_context_rect.size);
                         let new_task = RenderTask::new_alpha_batch(next_task_index,
                                                                    stacking_context_rect.origin,
                                                                    location);
                         next_task_index.0 += 1;
                         let prev_task = mem::replace(&mut current_task, new_task);
                         alpha_task_stack.push(prev_task);
                     }
                 }
                 PrimitiveRunCmd::PopStackingContext => {
                     let stacking_context_index = sc_stack.pop().unwrap();
                     let stacking_context = &self.stacking_context_store[stacking_context_index.0];
 
-                    if !stacking_context.is_visible() {
+                    if !stacking_context.is_visible {
                         continue;
                     }
 
                     for filter in &stacking_context.composite_ops.filters {
                         let mut prev_task = alpha_task_stack.pop().unwrap();
                         let item = AlphaRenderItem::Blend(stacking_context_index,
                                                           current_task.id,
                                                           *filter,
@@ -781,20 +1004,19 @@ impl FrameBuilder {
                                                                               op,
                                                                               next_z);
                                 next_z += 1;
                                 prev_task.as_alpha_batch().alpha_items.push(item);
                                 prev_task.children.push(current_task);
                                 current_task = prev_task;
                             }
                             None => {
-                                let stacking_context_rect =
-                                    stacking_context.xf_rect.as_ref().unwrap().bounding_rect;
                                 let readback_task =
-                                    RenderTask::new_readback(stacking_context_index, stacking_context_rect);
+                                    RenderTask::new_readback(stacking_context_index,
+                                                             stacking_context.bounding_rect);
 
                                 let mut prev_task = alpha_task_stack.pop().unwrap();
                                 let item = AlphaRenderItem::Composite(stacking_context_index,
                                                                       readback_task.id,
                                                                       current_task.id,
                                                                       mix_blend_mode,
                                                                       next_z);
                                 next_z += 1;
@@ -805,64 +1027,67 @@ impl FrameBuilder {
                             }
                         }
                     }
                 }
                 PrimitiveRunCmd::PrimitiveRun(first_prim_index, prim_count) => {
                     let stacking_context_index = *sc_stack.last().unwrap();
                     let stacking_context = &self.stacking_context_store[stacking_context_index.0];
 
-                    if !stacking_context.is_visible() {
+                    if !stacking_context.is_visible {
                         continue;
                     }
 
+                    let stacking_context_index = *sc_stack.last().unwrap();
+                    let group_index =
+                        self.stacking_context_store[stacking_context_index.0].clip_scroll_group();
+                    let clip_scroll_group = &self.clip_scroll_group_store[group_index.0];
+
                     for i in 0..prim_count {
                         let prim_index = PrimitiveIndex(first_prim_index.0 + i);
 
                         if self.prim_store.cpu_bounding_rects[prim_index.0].is_some() {
                             let prim_metadata = self.prim_store.get_metadata(prim_index);
 
                             // 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());
                             }
                             if let Some(ref clip_task) = prim_metadata.clip_task {
                                 current_task.children.push(clip_task.clone());
                             }
 
-                            let transform_kind = stacking_context.xf_rect.as_ref().unwrap().kind;
+                            let transform_kind = clip_scroll_group.xf_rect.as_ref().unwrap().kind;
                             let needs_clipping = prim_metadata.clip_task.is_some();
                             let needs_blending = transform_kind == TransformedRectKind::Complex ||
                                                  !prim_metadata.is_opaque ||
                                                  needs_clipping;
 
                             let items = if needs_blending {
                                 &mut current_task.as_alpha_batch().alpha_items
                             } else {
                                 &mut current_task.as_alpha_batch().opaque_items
                             };
-                            items.push(AlphaRenderItem::Primitive(stacking_context_index,
-                                                                  prim_index,
-                                                                  next_z));
+                            items.push(AlphaRenderItem::Primitive(group_index, prim_index, next_z));
                             next_z += 1;
                         }
                     }
                 }
                 PrimitiveRunCmd::PushScrollLayer(_) | PrimitiveRunCmd::PopScrollLayer => { }
             }
         }
 
         debug_assert!(alpha_task_stack.is_empty());
         (current_task, next_task_index.0)
     }
 
     pub fn build(&mut self,
                  resource_cache: &mut ResourceCache,
                  frame_id: FrameId,
-                 scroll_tree: &ScrollTree,
+                 clip_scroll_tree: &ClipScrollTree,
                  auxiliary_lists_map: &AuxiliaryListsMap,
                  device_pixel_ratio: f32) -> Frame {
         profile_scope!("build");
 
         let mut profile_counters = FrameProfileCounters::new();
         profile_counters.total_primitives.set(self.prim_store.prim_count());
 
         resource_cache.begin_frame(frame_id);
@@ -876,20 +1101,20 @@ impl FrameBuilder {
 
         // Pick a size for the cache render targets to be. The main requirement is that it
         // has to be at least as large as the framebuffer size. This ensures that it will
         // always be able to allocate the worst case render task (such as a clip mask that
         // covers the entire screen).
         let cache_size = DeviceUintSize::new(cmp::max(1024, screen_rect.size.width as u32),
                                              cmp::max(1024, screen_rect.size.height as u32));
 
-        self.update_scroll_bars(scroll_tree);
+        self.update_scroll_bars(clip_scroll_tree);
 
         self.build_layer_screen_rects_and_cull_layers(&screen_rect,
-                                                      scroll_tree,
+                                                      clip_scroll_tree,
                                                       auxiliary_lists_map,
                                                       resource_cache,
                                                       &mut profile_counters,
                                                       device_pixel_ratio);
 
         let (main_render_task, static_render_task_count) = self.build_render_task();
         let mut render_tasks = RenderTaskCollection::new(static_render_task_count);
 
@@ -917,16 +1142,17 @@ impl FrameBuilder {
                                         cache_size));
         }
 
         main_render_task.assign_to_passes(passes.len() - 1, &mut passes);
 
         for pass in &mut passes {
             let ctx = RenderTargetContext {
                 stacking_context_store: &self.stacking_context_store,
+                clip_scroll_group_store: &self.clip_scroll_group_store,
                 prim_store: &self.prim_store,
                 resource_cache: resource_cache,
             };
 
             pass.build(&ctx, &mut render_tasks);
 
             profile_counters.passes.inc();
             profile_counters.targets.add(pass.targets.len());
@@ -948,22 +1174,23 @@ impl FrameBuilder {
             gpu_data64: self.prim_store.gpu_data64.build(),
             gpu_data128: self.prim_store.gpu_data128.build(),
             gpu_geometry: self.prim_store.gpu_geometry.build(),
             gpu_gradient_data: self.prim_store.gpu_gradient_data.build(),
             gpu_resource_rects: self.prim_store.gpu_resource_rects.build(),
             deferred_resolves: deferred_resolves,
         }
     }
+
 }
 
 struct LayerRectCalculationAndCullingPass<'a> {
     frame_builder: &'a mut FrameBuilder,
     screen_rect: &'a DeviceIntRect,
-    scroll_tree: &'a ScrollTree,
+    clip_scroll_tree: &'a ClipScrollTree,
     auxiliary_lists_map: &'a AuxiliaryListsMap,
     resource_cache: &'a mut ResourceCache,
     profile_counters: &'a mut FrameProfileCounters,
     device_pixel_ratio: f32,
     stacking_context_stack: Vec<StackingContextIndex>,
     scroll_layer_stack: Vec<ScrollLayerIndex>,
 
     /// A cached clip info stack, which should handle the most common situation,
@@ -973,64 +1200,137 @@ struct LayerRectCalculationAndCullingPas
 
     /// The scroll layer that defines the previous scroll layer info stack.
     current_clip_stack_scroll_layer: Option<ScrollLayerIndex>
 }
 
 impl<'a> LayerRectCalculationAndCullingPass<'a> {
     fn create_and_run(frame_builder: &'a mut FrameBuilder,
                       screen_rect: &'a DeviceIntRect,
-                      scroll_tree: &'a ScrollTree,
+                      clip_scroll_tree: &'a ClipScrollTree,
                       auxiliary_lists_map: &'a AuxiliaryListsMap,
                       resource_cache: &'a mut ResourceCache,
                       profile_counters: &'a mut FrameProfileCounters,
                       device_pixel_ratio: f32) {
 
         let mut pass = LayerRectCalculationAndCullingPass {
             frame_builder: frame_builder,
             screen_rect: screen_rect,
-            scroll_tree: scroll_tree,
+            clip_scroll_tree: clip_scroll_tree,
             auxiliary_lists_map: auxiliary_lists_map,
             resource_cache: resource_cache,
             profile_counters: profile_counters,
             device_pixel_ratio: device_pixel_ratio,
             stacking_context_stack: Vec::new(),
             scroll_layer_stack: Vec::new(),
             current_clip_stack: Vec::new(),
             current_clip_stack_scroll_layer: None,
         };
         pass.run();
     }
 
     fn run(&mut self) {
+        self.recalculate_clip_scroll_groups();
+        self.compute_stacking_context_visibility();
+
         let commands = mem::replace(&mut self.frame_builder.cmds, Vec::new());
         for cmd in &commands {
             match cmd {
                 &PrimitiveRunCmd::PushStackingContext(stacking_context_index) =>
                     self.handle_push_stacking_context(stacking_context_index),
                 &PrimitiveRunCmd::PushScrollLayer(scroll_layer_index) =>
                     self.handle_push_scroll_layer(scroll_layer_index),
                 &PrimitiveRunCmd::PrimitiveRun(prim_index, prim_count) =>
                     self.handle_primitive_run(prim_index, prim_count),
-                &PrimitiveRunCmd::PopStackingContext => {
-                    self.stacking_context_stack.pop();
-                }
+                &PrimitiveRunCmd::PopStackingContext => self.handle_pop_stacking_context(),
                 &PrimitiveRunCmd::PopScrollLayer => self.handle_pop_scroll_layer(),
             }
         }
 
         mem::replace(&mut self.frame_builder.cmds, commands);
     }
 
+    fn recalculate_clip_scroll_groups(&mut self) {
+        for ref mut group in &mut self.frame_builder.clip_scroll_group_store {
+            let stacking_context_index = group.stacking_context_index;
+            let stacking_context = &mut self.frame_builder
+                                            .stacking_context_store[stacking_context_index.0];
+
+            let scroll_tree_layer = &self.clip_scroll_tree.nodes[&group.scroll_layer_id];
+            let packed_layer = &mut self.frame_builder.packed_layers[group.packed_layer_index.0];
+            packed_layer.transform = scroll_tree_layer.world_content_transform
+                                                      .with_source::<ScrollLayerPixel>()
+                                                      .pre_mul(&stacking_context.local_transform);
+            packed_layer.inv_transform = packed_layer.transform.inverse().unwrap();
+
+            if !stacking_context.can_contribute_to_scene() {
+                return;
+            }
+
+            let inv_layer_transform = stacking_context.local_transform.inverse().unwrap();
+            let local_viewport_rect =
+                as_scroll_parent_rect(&scroll_tree_layer.combined_local_viewport_rect);
+            let viewport_rect = inv_layer_transform.transform_rect(&local_viewport_rect);
+            let layer_local_rect = stacking_context.local_rect.intersection(&viewport_rect);
+
+            group.xf_rect = None;
+
+            let layer_local_rect = match layer_local_rect {
+                Some(layer_local_rect) if !layer_local_rect.is_empty() => layer_local_rect,
+                _ => continue,
+            };
+
+            let layer_xf_rect = TransformedRect::new(&layer_local_rect,
+                                                     &packed_layer.transform,
+                                                     self.device_pixel_ratio);
+
+            if layer_xf_rect.bounding_rect.intersects(&self.screen_rect) {
+                packed_layer.screen_vertices = layer_xf_rect.vertices.clone();
+                packed_layer.local_clip_rect = layer_local_rect;
+                group.xf_rect = Some(layer_xf_rect);
+            }
+        }
+    }
+
+    fn compute_stacking_context_visibility(&mut self) {
+        for context_index in 0..self.frame_builder.stacking_context_store.len() {
+            let is_visible = {
+                let stacking_context = &self.frame_builder.stacking_context_store[context_index];
+                stacking_context.clip_scroll_groups.iter().any(|group_index| {
+                    self.frame_builder.clip_scroll_group_store[group_index.0].is_visible()
+                })
+            };
+            self.frame_builder.stacking_context_store[context_index].is_visible = is_visible;
+        }
+    }
+
+    fn handle_pop_stacking_context(&mut self) {
+        let stacking_context_index = self.stacking_context_stack.pop().unwrap();
+
+        let bounding_rect = {
+            let stacking_context =
+                &mut self.frame_builder.stacking_context_store[stacking_context_index.0];
+            stacking_context.bounding_rect = stacking_context.bounding_rect
+                                                             .intersection(self.screen_rect)
+                                                             .unwrap_or(DeviceIntRect::zero());
+            stacking_context.bounding_rect.clone()
+        };
+
+        if let Some(ref mut parent_index) = self.stacking_context_stack.last_mut() {
+            let parent = &mut self.frame_builder.stacking_context_store[parent_index.0];
+            parent.bounding_rect = parent.bounding_rect.union(&bounding_rect);
+        }
+    }
+
     fn handle_push_scroll_layer(&mut self, scroll_layer_index: ScrollLayerIndex) {
         self.scroll_layer_stack.push(scroll_layer_index);
 
         let scroll_layer = &mut self.frame_builder.scroll_layer_store[scroll_layer_index.0];
         let packed_layer_index = scroll_layer.packed_layer_index;
-        let scroll_tree_layer = &self.scroll_tree.layers[&scroll_layer.scroll_layer_id];
+        let scroll_tree_layer = &self.clip_scroll_tree.nodes[&scroll_layer.scroll_layer_id];
         let packed_layer = &mut self.frame_builder.packed_layers[packed_layer_index.0];
 
         packed_layer.transform = scroll_tree_layer.world_viewport_transform;
         packed_layer.inv_transform = packed_layer.transform.inverse().unwrap();
 
         let local_rect = &scroll_tree_layer.combined_local_viewport_rect
                                            .translate(&scroll_tree_layer.scrolling.offset);
         if !local_rect.is_empty() {
@@ -1063,46 +1363,24 @@ impl<'a> LayerRectCalculationAndCullingP
             // We don't add the image mask for resolution, because layer masks are resolved later.
             self.resource_cache.request_image(mask.image, ImageRendering::Auto);
         }
     }
 
     fn handle_push_stacking_context(&mut self, stacking_context_index: StackingContextIndex) {
         self.stacking_context_stack.push(stacking_context_index);
 
+        // Reset bounding rect to zero. We will calculate it as we collect primitives
+        // from various scroll layers. In handle_pop_stacking_context , we use this to
+        // calculate the device bounding rect. In the future, we could cache this during
+        // the initial adding of items for the common case (where there is only a single
+        // scroll layer for items in a stacking context).
         let stacking_context = &mut self.frame_builder
                                         .stacking_context_store[stacking_context_index.0];
-        let packed_layer = &mut self.frame_builder
-                                    .packed_layers[stacking_context.packed_layer_index.0];
-        let scroll_layer = &self.scroll_tree.layers[&stacking_context.scroll_layer_id];
-        packed_layer.transform = scroll_layer.world_content_transform
-                                             .with_source::<ScrollLayerPixel>()
-                                             .pre_mul(&stacking_context.local_transform);
-        packed_layer.inv_transform = packed_layer.transform.inverse().unwrap();
-
-        if !stacking_context.can_contribute_to_scene() {
-            return;
-        }
-
-        let inv_layer_transform = stacking_context.local_transform.inverse().unwrap();
-        let local_viewport_rect = as_scroll_parent_rect(&scroll_layer.combined_local_viewport_rect);
-        let viewport_rect = inv_layer_transform.transform_rect(&local_viewport_rect);
-        let layer_local_rect = stacking_context.local_rect.intersection(&viewport_rect);
-
-        if let Some(layer_local_rect) = layer_local_rect {
-            let layer_xf_rect = TransformedRect::new(&layer_local_rect,
-                                                     &packed_layer.transform,
-                                                     self.device_pixel_ratio);
-
-            if layer_xf_rect.bounding_rect.intersects(&self.screen_rect) {
-                packed_layer.screen_vertices = layer_xf_rect.vertices.clone();
-                packed_layer.local_clip_rect = layer_local_rect;
-                stacking_context.xf_rect = Some(layer_xf_rect);
-            }
-        }
+        stacking_context.bounding_rect = DeviceIntRect::zero();
     }
 
     fn rebuild_clip_info_stack_if_necessary(&mut self, mut scroll_layer_index: ScrollLayerIndex) {
         if let Some(previous_scroll_layer) = self.current_clip_stack_scroll_layer {
             if previous_scroll_layer == scroll_layer_index {
                 return;
             }
         }
@@ -1125,28 +1403,38 @@ impl<'a> LayerRectCalculationAndCullingP
             }
             scroll_layer_index = scroll_layer.parent_index;
         }
 
         self.current_clip_stack.reverse();
     }
 
     fn handle_primitive_run(&mut self, prim_index: PrimitiveIndex, prim_count: usize) {
+        let stacking_context_index = *self.stacking_context_stack.last().unwrap();
+        let (packed_layer_index, pipeline_id) = {
+            let stacking_context =
+                &self.frame_builder.stacking_context_store[stacking_context_index.0];
+
+            if !stacking_context.is_visible {
+                return;
+            }
+
+            let group_index = stacking_context.clip_scroll_group();
+            let clip_scroll_group = &self.frame_builder.clip_scroll_group_store[group_index.0];
+            (clip_scroll_group.packed_layer_index, stacking_context.pipeline_id)
+        };
+
         let scroll_layer_index = *self.scroll_layer_stack.last().unwrap();
         self.rebuild_clip_info_stack_if_necessary(scroll_layer_index);
 
-        let stacking_context_index = self.stacking_context_stack.last().unwrap();
-        let stacking_context = &self.frame_builder.stacking_context_store[stacking_context_index.0];
-        if !stacking_context.is_visible() {
-            return;
-        }
+        let stacking_context =
+            &mut self.frame_builder.stacking_context_store[stacking_context_index.0];
 
-        let packed_layer_index = stacking_context.packed_layer_index;
         let packed_layer = &self.frame_builder.packed_layers[packed_layer_index.0];
-        let auxiliary_lists = self.auxiliary_lists_map.get(&stacking_context.pipeline_id)
+        let auxiliary_lists = self.auxiliary_lists_map.get(&pipeline_id)
                                                       .expect("No auxiliary lists?");
 
         for i in 0..prim_count {
             let prim_index = PrimitiveIndex(prim_index.0 + i);
             if self.frame_builder.prim_store.build_bounding_rect(prim_index,
                                                                  self.screen_rect,
                                                                  &packed_layer.transform,
                                                                  &packed_layer.local_clip_rect,
@@ -1170,16 +1458,19 @@ impl<'a> LayerRectCalculationAndCullingP
                     Some(rect) => rect,
                     _ => continue,
                 };
 
                 let prim_metadata = &mut self.frame_builder.prim_store.cpu_metadata[prim_index.0];
                 let prim_clip_info = prim_metadata.clip_cache_info.as_ref();
                 let mut visible = true;
 
+                stacking_context.bounding_rect =
+                    stacking_context.bounding_rect.union(&prim_bounding_rect);
+
                 if let Some(info) = prim_clip_info {
                     self.current_clip_stack.push((packed_layer_index, info.clone()));
                 }
 
                 // Try to create a mask if we may need to.
                 if !self.current_clip_stack.is_empty() {
                     // If the primitive doesn't have a specific clip, key the task ID off the
                     // stacking context. This means that two primitives which are only clipped
deleted file mode 100644
--- a/gfx/webrender/src/layer.rs
+++ /dev/null
@@ -1,288 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-use euclid::Point3D;
-use geometry::ray_intersects_rect;
-use spring::{DAMPING, STIFFNESS, Spring};
-use webrender_traits::{LayerPoint, LayerRect, LayerSize, LayerToScrollTransform};
-use webrender_traits::{LayerToWorldTransform, PipelineId, ScrollEventPhase, ScrollLayerId};
-use webrender_traits::{ScrollLayerRect, ScrollLocation, ScrollToWorldTransform, WorldPoint};
-use webrender_traits::{WorldPoint4D};
-
-#[cfg(target_os = "macos")]
-const CAN_OVERSCROLL: bool = true;
-
-#[cfg(not(target_os = "macos"))]
-const CAN_OVERSCROLL: bool = false;
-
-
-/// Contains scrolling and transform information 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: LayerSize,
-
-    /// Viewing rectangle
-    pub local_viewport_rect: LayerRect,
-
-    /// Viewing rectangle clipped against parent layer(s)
-    pub combined_local_viewport_rect: LayerRect,
-
-    /// World transform for the viewport rect itself.
-    pub world_viewport_transform: LayerToWorldTransform,
-
-    /// World transform for content within this layer
-    pub world_content_transform: LayerToWorldTransform,
-
-    /// Transform for this layer, relative to parent scrollable layer.
-    pub local_transform: LayerToScrollTransform,
-
-    /// Pipeline that this layer belongs to
-    pub pipeline_id: PipelineId,
-
-    /// Child layers
-    pub children: Vec<ScrollLayerId>,
-}
-
-impl Layer {
-    pub fn new(local_viewport_rect: &LayerRect,
-               content_size: LayerSize,
-               local_transform: &LayerToScrollTransform,
-               pipeline_id: PipelineId)
-               -> Layer {
-        Layer {
-            scrolling: ScrollingState::new(),
-            content_size: content_size,
-            local_viewport_rect: *local_viewport_rect,
-            combined_local_viewport_rect: *local_viewport_rect,
-            world_viewport_transform: LayerToWorldTransform::identity(),
-            world_content_transform: LayerToWorldTransform::identity(),
-            local_transform: *local_transform,
-            children: Vec::new(),
-            pipeline_id: pipeline_id,
-        }
-    }
-
-    pub fn add_child(&mut self, child: ScrollLayerId) {
-        self.children.push(child);
-    }
-
-    pub fn finalize(&mut self, scrolling: &ScrollingState) {
-        self.scrolling = *scrolling;
-    }
-
-    pub fn overscroll_amount(&self) -> LayerSize {
-        let scrollable_width = self.scrollable_width();
-        let overscroll_x = if self.scrolling.offset.x > 0.0 {
-            -self.scrolling.offset.x
-        } else if self.scrolling.offset.x < -scrollable_width {
-            -scrollable_width - self.scrolling.offset.x
-        } else {
-            0.0
-        };
-
-        let scrollable_height = self.scrollable_height();
-        let overscroll_y = if self.scrolling.offset.y > 0.0 {
-            -self.scrolling.offset.y
-        } else if self.scrolling.offset.y < -scrollable_height {
-            -scrollable_height - self.scrolling.offset.y
-        } else {
-            0.0
-        };
-
-        LayerSize::new(overscroll_x, overscroll_y)
-    }
-
-    pub fn set_scroll_origin(&mut self, origin: &LayerPoint) -> bool {
-        let scrollable_height = self.scrollable_height();
-        let scrollable_width = self.scrollable_width();
-        if scrollable_height <= 0. && scrollable_width <= 0. {
-            return false;
-        }
-
-        let new_offset = LayerPoint::new((-origin.x).max(-scrollable_width).min(0.0).round(),
-                                         (-origin.y).max(-scrollable_height).min(0.0).round());
-        if new_offset == self.scrolling.offset {
-            return false;
-        }
-
-        self.scrolling.offset = new_offset;
-        self.scrolling.bouncing_back = false;
-        self.scrolling.started_bouncing_back = false;
-        return true;
-    }
-
-    pub fn update_transform(&mut self,
-                            parent_world_transform: &ScrollToWorldTransform,
-                            parent_viewport_rect: &ScrollLayerRect) {
-        let inv_transform = match self.local_transform.inverse() {
-            Some(transform) => transform,
-            None => {
-                // If a transform function causes the current transformation matrix of an object
-                // to be non-invertible, the object and its content do not get displayed.
-                self.combined_local_viewport_rect = LayerRect::zero();
-                return;
-            }
-        };
-
-        let parent_viewport_rect_in_local_space = inv_transform.transform_rect(parent_viewport_rect)
-                                                               .translate(&-self.scrolling.offset);
-        let local_viewport_rect = self.local_viewport_rect.translate(&-self.scrolling.offset);
-        let viewport_rect = parent_viewport_rect_in_local_space.intersection(&local_viewport_rect)
-                                                               .unwrap_or(LayerRect::zero());
-
-        self.combined_local_viewport_rect = viewport_rect;
-        self.world_viewport_transform = parent_world_transform.pre_mul(&self.local_transform);
-        self.world_content_transform = self.world_viewport_transform
-                                                     .pre_translated(self.scrolling.offset.x,
-                                                                     self.scrolling.offset.y,
-                                                                     0.0);
-    }
-
-    pub fn scrollable_height(&self) -> f32 {
-        self.content_size.height - self.local_viewport_rect.size.height
-    }
-
-    pub fn scrollable_width(&self) -> f32 {
-        self.content_size.width - self.local_viewport_rect.size.width
-    }
-
-    pub fn scroll(&mut self, scroll_location: ScrollLocation, phase: ScrollEventPhase) -> bool {
-        if self.scrolling.started_bouncing_back && phase == ScrollEventPhase::Move(false) {
-            return false;
-        }
-
-        let mut delta = match scroll_location {
-            ScrollLocation::Delta(delta) => delta,
-            ScrollLocation::Start => {
-                if self.scrolling.offset.y.round() >= 0.0 {
-                    // Nothing to do on this layer.
-                    return false;
-                }
-
-                self.scrolling.offset.y = 0.0;
-                return true;
-            },
-            ScrollLocation::End => {
-                let end_pos = self.local_viewport_rect.size.height - self.content_size.height;
-
-                if self.scrolling.offset.y.round() <= end_pos {
-                    // Nothing to do on this layer.
-                    return false;
-                }
-
-                self.scrolling.offset.y = end_pos;
-                return true;
-            }
-        };
-
-        let overscroll_amount = self.overscroll_amount();
-        let overscrolling = CAN_OVERSCROLL && (overscroll_amount.width != 0.0 ||
-                                               overscroll_amount.height != 0.0);
-        if overscrolling {
-            if overscroll_amount.width != 0.0 {
-                delta.x /= overscroll_amount.width.abs()
-            }
-            if overscroll_amount.height != 0.0 {
-                delta.y /= overscroll_amount.height.abs()
-            }
-        }
-
-        let scrollable_width = self.scrollable_width();
-        let scrollable_height = self.scrollable_height();
-        let is_unscrollable = scrollable_width <= 0. && scrollable_height <= 0.;
-        let original_layer_scroll_offset = self.scrolling.offset;
-
-        if scrollable_width > 0. {
-            self.scrolling.offset.x = self.scrolling.offset.x + delta.x;
-            if is_unscrollable || !CAN_OVERSCROLL {
-                self.scrolling.offset.x =
-                    self.scrolling.offset.x.min(0.0).max(-scrollable_width).round();
-            }
-        }
-
-        if scrollable_height > 0. {
-            self.scrolling.offset.y = self.scrolling.offset.y + delta.y;
-            if is_unscrollable || !CAN_OVERSCROLL {
-                self.scrolling.offset.y =
-                    self.scrolling.offset.y.min(0.0).max(-scrollable_height).round();
-            }
-        }
-
-        if phase == ScrollEventPhase::Start || phase == ScrollEventPhase::Move(true) {
-            self.scrolling.started_bouncing_back = false
-        } else if overscrolling &&
-                ((delta.x < 1.0 && delta.y < 1.0) || phase == ScrollEventPhase::End) {
-            self.scrolling.started_bouncing_back = true;
-            self.scrolling.bouncing_back = true
-        }
-
-        if CAN_OVERSCROLL {
-            self.stretch_overscroll_spring();
-        }
-
-        self.scrolling.offset != original_layer_scroll_offset ||
-            self.scrolling.started_bouncing_back
-    }
-
-    pub fn stretch_overscroll_spring(&mut self) {
-        let overscroll_amount = self.overscroll_amount();
-        self.scrolling.spring.coords(self.scrolling.offset,
-                                     self.scrolling.offset,
-                                     self.scrolling.offset + overscroll_amount);
-    }
-
-    pub fn tick_scrolling_bounce_animation(&mut self) {
-        let finished = self.scrolling.spring.animate();
-        self.scrolling.offset = self.scrolling.spring.current();
-        if finished {
-            self.scrolling.bouncing_back = false
-        }
-    }
-
-    pub fn ray_intersects_layer(&self, cursor: &WorldPoint) -> bool {
-        let inv = self.world_viewport_transform.inverse().unwrap();
-        let z0 = -10000.0;
-        let z1 =  10000.0;
-
-        let p0 = inv.transform_point4d(&WorldPoint4D::new(cursor.x, cursor.y, z0, 1.0));
-        let p0 = Point3D::new(p0.x / p0.w,
-                              p0.y / p0.w,
-                              p0.z / p0.w);
-        let p1 = inv.transform_point4d(&WorldPoint4D::new(cursor.x, cursor.y, z1, 1.0));
-        let p1 = Point3D::new(p1.x / p1.w,
-                              p1.y / p1.w,
-                              p1.z / p1.w);
-
-        if self.scrollable_width() <= 0. && self.scrollable_height() <= 0. {
-            return false;
-        }
-        ray_intersects_rect(p0, p1, self.local_viewport_rect.to_untyped())
-    }
-}
-
-#[derive(Copy, Clone)]
-pub struct ScrollingState {
-    pub offset: LayerPoint,
-    pub spring: Spring,
-    pub started_bouncing_back: bool,
-    pub bouncing_back: bool,
-    pub should_handoff_scroll: bool
-}
-
-impl ScrollingState {
-    pub fn new() -> ScrollingState {
-        ScrollingState {
-            offset: LayerPoint::zero(),
-            spring: Spring::at(LayerPoint::zero(), STIFFNESS, DAMPING),
-            started_bouncing_back: false,
-            bouncing_back: false,
-            should_handoff_scroll: false
-        }
-    }
-}
-
--- a/gfx/webrender/src/lib.rs
+++ b/gfx/webrender/src/lib.rs
@@ -47,36 +47,36 @@ extern crate lazy_static;
 #[macro_use]
 extern crate log;
 #[macro_use]
 extern crate bitflags;
 #[macro_use]
 extern crate thread_profiler;
 
 mod batch_builder;
+mod clip_scroll_node;
+mod clip_scroll_tree;
 mod debug_colors;
 mod debug_font_data;
 mod debug_render;
 mod device;
 mod frame;
 mod frame_builder;
 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 render_task;
 mod resource_cache;
 mod scene;
-mod scroll_tree;
 mod spring;
 mod texture_cache;
 mod tiling;
 mod util;
 
 mod shader_source {
     include!(concat!(env!("OUT_DIR"), "/shaders.rs"));
 }
--- a/gfx/webrender/src/platform/windows/font.rs
+++ b/gfx/webrender/src/platform/windows/font.rs
@@ -77,28 +77,34 @@ fn dwrite_render_mode(font_face: &dwrote
         return dwrote::DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
     }
 
     dwrite_render_mode
 }
 
 fn get_glyph_dimensions_with_analysis(analysis: dwrote::GlyphRunAnalysis,
                                       texture_type: dwrote::DWRITE_TEXTURE_TYPE)
-                                      -> GlyphDimensions {
+                                      -> Option<GlyphDimensions> {
     let bounds = analysis.get_alpha_texture_bounds(texture_type);
 
     let width = (bounds.right - bounds.left) as u32;
     let height = (bounds.bottom - bounds.top) as u32;
-    assert!(width > 0 && height > 0);
-    GlyphDimensions {
+
+    // Alpha texture bounds can sometimes return an empty rect
+    // Such as for spaces
+    if width == 0 || height == 0 {
+        return None
+    }
+
+    Some(GlyphDimensions {
         left: bounds.left,
         top: -bounds.top,
         width: width,
         height: height,
-    }
+    })
 }
 
 impl FontContext {
     pub fn new() -> FontContext {
         // These are the default values we use in Gecko.
         // We use a gamma value of 2.3 for gdi fonts
         // TODO: Fetch this data from Gecko itself.
         let contrast = 1.0;
@@ -198,17 +204,17 @@ impl FontContext {
     pub fn get_glyph_dimensions(&self,
                                 key: &GlyphKey)
                                 -> Option<GlyphDimensions> {
         // Probably have to default to something else here.
         let render_mode = FontRenderMode::Subpixel;
         let analysis = self.create_glyph_analysis(key, render_mode, None);
 
         let texture_type = dwrite_texture_type(render_mode);
-        Some(get_glyph_dimensions_with_analysis(analysis, texture_type))
+        get_glyph_dimensions_with_analysis(analysis, texture_type)
     }
 
     // DWRITE gives us values in RGB. WR doesn't really touch it after. Note, CG returns in BGR
     // TODO: Decide whether all fonts should return RGB or BGR
     fn convert_to_rgba(&self, pixels: &Vec<u8>, render_mode: FontRenderMode) -> Vec<u8> {
         match render_mode {
             FontRenderMode::Mono => {
                 let mut rgba_pixels: Vec<u8> = vec![0; pixels.len() * 4];
@@ -261,16 +267,20 @@ impl FontContext {
                                                   render_mode,
                                                   glyph_options);
         let texture_type = dwrite_texture_type(render_mode);
 
         let bounds = analysis.get_alpha_texture_bounds(texture_type);
         let width = (bounds.right - bounds.left) as usize;
         let height = (bounds.bottom - bounds.top) as usize;
 
+        // We should not get here since glyph_dimensions would return
+        // None for empty glyphs.
+        assert!(width > 0 && height > 0);
+
         let mut pixels = analysis.create_alpha_texture(texture_type, bounds);
 
         let lut_correction = match glyph_options {
             Some(option) => {
                 if option.force_gdi_rendering {
                     &self.gdi_gamma_lut
                 } else {
                     &self.gamma_lut
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -25,31 +25,40 @@ use webrender_traits::{ExtendMode, Gradi
 pub const CLIP_DATA_GPU_SIZE: usize = 5;
 pub const MASK_DATA_GPU_SIZE: usize = 1;
 
 /// Stores two coordinates in texel space. The coordinates
 /// are stored in texel coordinates because the texture atlas
 /// may grow. Storing them as texel coords and normalizing
 /// the UVs in the vertex shader means nothing needs to be
 /// updated on the CPU when the texture size changes.
-#[derive(Clone)]
+#[derive(Copy, Clone, Debug)]
 pub struct TexelRect {
     pub uv0: DevicePoint,
     pub uv1: DevicePoint,
 }
 
 impl Default for TexelRect {
     fn default() -> TexelRect {
         TexelRect {
             uv0: DevicePoint::zero(),
             uv1: DevicePoint::zero(),
         }
     }
 }
 
+impl TexelRect {
+    pub fn new(u0: u32, v0: u32, u1: u32, v1: u32) -> TexelRect {
+        TexelRect {
+            uv0: DevicePoint::new(u0 as f32, v0 as f32),
+            uv1: DevicePoint::new(u1 as f32, v1 as f32),
+        }
+    }
+}
+
 /// For external images, it's not possible to know the
 /// UV coords of the image (or the image data itself)
 /// until the render thread receives the frame and issues
 /// callbacks to the client application. For external
 /// images that are visible, a DeferredResolve is created
 /// that is stored in the frame. This allows the render
 /// thread to iterate this list and update any changed
 /// texture data and update the UV rect.
@@ -131,16 +140,17 @@ pub enum ImagePrimitiveKind {
     WebGL(WebGLContextId),
 }
 
 #[derive(Debug)]
 pub struct ImagePrimitiveCpu {
     pub kind: ImagePrimitiveKind,
     pub color_texture_id: SourceTexture,
     pub resource_address: GpuStoreAddress,
+    pub sub_rect: Option<TexelRect>,
 }
 
 #[derive(Debug, Clone)]
 #[repr(C)]
 pub struct ImagePrimitiveGpu {
     pub stretch_size: LayerSize,
     pub tile_spacing: LayerSize,
 }
@@ -915,18 +925,28 @@ impl PrimitiveStore {
                         ImagePrimitiveKind::WebGL(context_id) => {
                             let cache_item = resource_cache.get_webgl_texture(&context_id);
                             (cache_item.texture_id, Some(cache_item))
                         }
                     };
 
                     if let Some(cache_item) = cache_item {
                         let resource_rect = self.gpu_resource_rects.get_mut(image_cpu.resource_address);
-                        resource_rect.uv0 = cache_item.uv0;
-                        resource_rect.uv1 = cache_item.uv1;
+                        match image_cpu.sub_rect {
+                            Some(sub_rect) => {
+                                resource_rect.uv0.x = cache_item.uv0.x + sub_rect.uv0.x;
+                                resource_rect.uv0.y = cache_item.uv0.y + sub_rect.uv0.y;
+                                resource_rect.uv1.x = cache_item.uv0.x + sub_rect.uv1.x;
+                                resource_rect.uv1.y = cache_item.uv0.y + sub_rect.uv1.y;
+                            }
+                            None => {
+                                resource_rect.uv0 = cache_item.uv0;
+                                resource_rect.uv1 = cache_item.uv1;
+                            }
+                        }
                     }
                     image_cpu.color_texture_id = texture_id;
                 }
                 PrimitiveKind::YuvImage => {
                     let image_cpu = &mut self.cpu_yuv_images[metadata.cpu_prim_index.0];
                     let image_gpu: &mut YuvImagePrimitiveGpu = unsafe {
                         mem::transmute(self.gpu_data64.get_mut(metadata.gpu_prim_index))
                     };
--- a/gfx/webrender/src/record.rs
+++ b/gfx/webrender/src/record.rs
@@ -1,14 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-use bincode::serde::serialize;
-use bincode;
+use bincode::{SizeLimit, serialize};
 use std::fmt::Debug;
 use std::mem;
 use std::any::TypeId;
 use std::fs::File;
 use std::io::Write;
 use std::path::PathBuf;
 use webrender_traits::ApiMsg;
 use byteorder::{LittleEndian, WriteBytesExt};
@@ -46,17 +45,17 @@ impl BinaryRecorder {
         self.file.write_u32::<LittleEndian>(data.len() as u32).ok();
         self.file.write(data).ok();
     }
 }
 
 impl ApiRecordingReceiver for BinaryRecorder {
     fn write_msg(&mut self, _: u32, msg: &ApiMsg) {
         if should_record_msg(msg) {
-            let buf = serialize(msg, bincode::SizeLimit::Infinite).unwrap();
+            let buf = serialize(msg, SizeLimit::Infinite).unwrap();
             self.write_length_and_data(&buf);
         }
     }
 
     fn write_payload(&mut self, _: u32, data: &[u8]) {
         // signal payload with a 0 length
         self.file.write_u32::<LittleEndian>(0).ok();
         self.write_length_and_data(data);
--- a/gfx/webrender/src/render_backend.rs
+++ b/gfx/webrender/src/render_backend.rs
@@ -16,17 +16,17 @@ use std::io::{Cursor, Read};
 use std::sync::{Arc, Mutex};
 use std::sync::mpsc::Sender;
 use texture_cache::TextureCache;
 use thread_profiler::register_thread_with_profiler;
 use threadpool::ThreadPool;
 use webrender_traits::{ApiMsg, AuxiliaryLists, BuiltDisplayList, IdNamespace, ImageData};
 use webrender_traits::{PipelineId, RenderNotifier, RenderDispatcher, WebGLCommand, WebGLContextId};
 use webrender_traits::channel::{PayloadHelperMethods, PayloadReceiver, PayloadSender, MsgReceiver};
-use webrender_traits::{VRCompositorCommand, VRCompositorHandler};
+use webrender_traits::{BlobImageRenderer, VRCompositorCommand, VRCompositorHandler};
 use offscreen_gl_context::GLContextDispatcher;
 
 /// The render backend is responsible for transforming high level display lists into
 /// GPU-friendly work which is then submitted to the renderer in the form of a frame::Frame.
 ///
 /// The render backend operates on its own thread.
 pub struct RenderBackend {
     api_rx: MsgReceiver<ApiMsg>,
@@ -63,19 +63,20 @@ impl RenderBackend {
                texture_cache: TextureCache,
                enable_aa: bool,
                workers: Arc<Mutex<ThreadPool>>,
                notifier: Arc<Mutex<Option<Box<RenderNotifier>>>>,
                webrender_context_handle: Option<GLContextHandleWrapper>,
                config: FrameBuilderConfig,
                recorder: Option<Box<ApiRecordingReceiver>>,
                main_thread_dispatcher: Arc<Mutex<Option<Box<RenderDispatcher>>>>,
+               blob_image_renderer: Option<Box<BlobImageRenderer>>,
                vr_compositor_handler: Arc<Mutex<Option<Box<VRCompositorHandler>>>>) -> RenderBackend {
 
-        let resource_cache = ResourceCache::new(texture_cache, workers, enable_aa);
+        let resource_cache = ResourceCache::new(texture_cache, workers, blob_image_renderer, enable_aa);
 
         register_thread_with_profiler("Backend".to_string());
 
         RenderBackend {
             api_rx: api_rx,
             payload_rx: payload_rx,
             payload_tx: payload_tx,
             result_tx: result_tx,
@@ -226,17 +227,17 @@ impl RenderBackend {
                                     self.notify_compositor_of_new_scroll_frame(true)
                                 }
                                 None => self.notify_compositor_of_new_scroll_frame(false),
                             }
                         }
                         ApiMsg::ScrollLayersWithScrollId(origin, pipeline_id, scroll_root_id) => {
                             profile_scope!("ScrollLayersWithScrollId");
                             let frame = profile_counters.total_time.profile(|| {
-                                if self.frame.scroll_layers(origin, pipeline_id, scroll_root_id) {
+                                if self.frame.scroll_nodes(origin, pipeline_id, scroll_root_id) {
                                     Some(self.render())
                                 } else {
                                     None
                                 }
                             });
 
                             match frame {
                                 Some(frame) => {
@@ -256,17 +257,17 @@ impl RenderBackend {
 
                             self.publish_frame_and_notify_compositor(frame, &mut profile_counters);
                         }
                         ApiMsg::TranslatePointToLayerSpace(..) => {
                             panic!("unused api - remove from webrender_traits");
                         }
                         ApiMsg::GetScrollLayerState(tx) => {
                             profile_scope!("GetScrollLayerState");
-                            tx.send(self.frame.get_scroll_layer_state())
+                            tx.send(self.frame.get_scroll_node_state())
                               .unwrap()
                         }
                         ApiMsg::RequestWebGLContext(size, attributes, tx) => {
                             if let Some(ref wrapper) = self.webrender_context_handle {
                                 let dispatcher: Option<Box<GLContextDispatcher>> = if cfg!(target_os = "windows") {
                                     Some(Box::new(WebRenderGLDispatcher {
                                         dispatcher: self.main_thread_dispatcher.clone()
                                     }))
--- a/gfx/webrender/src/render_task.rs
+++ b/gfx/webrender/src/render_task.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 internal_types::{HardwareCompositeOp, LowLevelFilterOp};
 use mask_cache::MaskCacheInfo;
 use prim_store::{PrimitiveCacheKey, PrimitiveIndex};
 use std::{cmp, f32, i32, mem, usize};
-use tiling::{PackedLayerIndex, RenderPass, RenderTargetIndex, ScrollLayerIndex};
-use tiling::StackingContextIndex;
+use tiling::{ClipScrollGroupIndex, PackedLayerIndex, RenderPass, RenderTargetIndex};
+use tiling::{ScrollLayerIndex, StackingContextIndex};
 use webrender_traits::{DeviceIntLength, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
 use webrender_traits::MixBlendMode;
 
 const FLOATS_PER_RENDER_TASK_INFO: usize = 12;
 
 #[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
 pub struct RenderTaskIndex(pub usize);
 
@@ -46,17 +46,17 @@ pub enum RenderTaskId {
 #[derive(Debug, Clone)]
 pub enum RenderTaskLocation {
     Fixed,
     Dynamic(Option<(DeviceIntPoint, RenderTargetIndex)>, DeviceIntSize),
 }
 
 #[derive(Debug, Clone)]
 pub enum AlphaRenderItem {
-    Primitive(StackingContextIndex, PrimitiveIndex, i32),
+    Primitive(ClipScrollGroupIndex, PrimitiveIndex, i32),
     Blend(StackingContextIndex, RenderTaskId, LowLevelFilterOp, i32),
     Composite(StackingContextIndex, RenderTaskId, RenderTaskId, MixBlendMode, i32),
     HardwareComposite(StackingContextIndex, RenderTaskId, HardwareCompositeOp, i32),
 }
 
 #[derive(Debug, Clone)]
 pub struct AlphaRenderTask {
     screen_origin: DeviceIntPoint,
--- a/gfx/webrender/src/renderer.rs
+++ b/gfx/webrender/src/renderer.rs
@@ -43,17 +43,17 @@ use threadpool::ThreadPool;
 use tiling::{AlphaBatchKind, BlurCommand, Frame, PrimitiveBatch, PrimitiveBatchData};
 use tiling::{CacheClipInstance, PrimitiveInstance, RenderTarget};
 use time::precise_time_ns;
 use thread_profiler::{register_thread_with_profiler, write_profile};
 use util::TransformedRectKind;
 use webrender_traits::{ColorF, Epoch, PipelineId, RenderNotifier, RenderDispatcher};
 use webrender_traits::{ExternalImageId, ImageData, ImageFormat, RenderApiSender, RendererKind};
 use webrender_traits::{DeviceIntRect, DevicePoint, DeviceIntPoint, DeviceIntSize, DeviceUintSize};
-use webrender_traits::ImageDescriptor;
+use webrender_traits::{ImageDescriptor, BlobImageRenderer};
 use webrender_traits::channel;
 use webrender_traits::VRCompositorHandler;
 
 pub const GPU_DATA_TEXTURE_POOL: usize = 5;
 pub const MAX_VERTEX_TEXTURE_WIDTH: usize = 1024;
 
 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 };
@@ -458,16 +458,21 @@ pub struct Renderer {
     /// by the backend thread / texture cache. We free the
     /// texture memory associated with a TextureId when its
     /// texture cache ID is freed by the texture cache, but
     /// reuse the TextureId when the texture caches's free
     /// list reuses the texture cache ID. This saves having to
     /// use a hashmap, and allows a flat vector for performance.
     cache_texture_id_map: Vec<TextureId>,
 
+    /// A special 1x1 dummy cache texture used for shaders that expect to work
+    /// with the cache but are actually running in the first pass
+    /// when no target is yet provided as a cache texture input.
+    dummy_cache_texture_id: TextureId,
+
     /// Optional trait object that allows the client
     /// application to provide external buffers for image data.
     external_image_handler: Option<Box<ExternalImageHandler>>,
 
     /// Map of external image IDs to native textures.
     external_images: HashMap<ExternalImageId, TextureId, BuildHasherDefault<FnvHasher>>,
 
     // Optional trait object that handles WebVR commands.
@@ -670,17 +675,20 @@ impl Renderer {
         let ps_hw_composite = try!{
             LazilyCompiledShader::new(ShaderKind::Primitive,
                                      "ps_hardware_composite",
                                      &[],
                                      &mut device,
                                      options.precache_shaders)
         };
 
-        let mut texture_cache = TextureCache::new();
+        let device_max_size = device.max_texture_size();
+        let max_texture_size = cmp::min(device_max_size, options.max_texture_size.unwrap_or(device_max_size));
+
+        let mut texture_cache = TextureCache::new(max_texture_size);
 
         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![
@@ -707,16 +715,25 @@ impl Renderer {
                                 height: 2,
                                 stride: None,
                                 format: ImageFormat::A8,
                                 is_opaque: false,
                              },
                              TextureFilter::Linear,
                              ImageData::Raw(Arc::new(mask_pixels)));
 
+        let dummy_cache_texture_id = device.create_texture_ids(1, TextureTarget::Array)[0];
+        device.init_texture(dummy_cache_texture_id,
+                            1,
+                            1,
+                            ImageFormat::RGBA8,
+                            TextureFilter::Linear,
+                            RenderTargetMode::LayerRenderTarget(1),
+                            None);
+
         let debug_renderer = DebugRenderer::new(&mut device);
 
         let gpu_data_textures = [
             GpuDataTextures::new(&mut device),
             GpuDataTextures::new(&mut device),
             GpuDataTextures::new(&mut device),
             GpuDataTextures::new(&mut device),
             GpuDataTextures::new(&mut device),
@@ -775,30 +792,33 @@ impl Renderer {
         let (device_pixel_ratio, enable_aa) = (options.device_pixel_ratio, options.enable_aa);
         let render_target_debug = options.render_target_debug;
         let payload_tx_for_backend = payload_tx.clone();
         let recorder = options.recorder;
         let workers = options.workers.take().unwrap_or_else(||{
             // TODO(gw): Use a heuristic to select best # of worker threads.
             Arc::new(Mutex::new(ThreadPool::new_with_name("WebRender:Worker".to_string(), 4)))
         });
+
+        let blob_image_renderer = options.blob_image_renderer.take();
         try!{ thread::Builder::new().name("RenderBackend".to_string()).spawn(move || {
             let mut backend = RenderBackend::new(api_rx,
                                                  payload_rx,
                                                  payload_tx_for_backend,
                                                  result_tx,
                                                  device_pixel_ratio,
                                                  texture_cache,
                                                  enable_aa,
                                                  workers,
                                                  backend_notifier,
                                                  context_handle,
                                                  config,
                                                  recorder,
                                                  backend_main_thread_dispatcher,
+                                                 blob_image_renderer,
                                                  backend_vr_compositor);
             backend.run();
         })};
 
         let renderer = Renderer {
             result_rx: result_rx,
             device: device,
             current_frame: None,
@@ -839,16 +859,17 @@ impl Renderer {
             prim_vao_id: prim_vao_id,
             blur_vao_id: blur_vao_id,
             clip_vao_id: clip_vao_id,
             gdt_index: 0,
             gpu_data_textures: gpu_data_textures,
             pipeline_epoch_map: HashMap::with_hasher(Default::default()),
             main_thread_dispatcher: main_thread_dispatcher,
             cache_texture_id_map: Vec::new(),
+            dummy_cache_texture_id: dummy_cache_texture_id,
             external_image_handler: None,
             external_images: HashMap::with_hasher(Default::default()),
             vr_compositor_handler: vr_compositor
         };
 
         let sender = RenderApiSender::new(api_tx, payload_tx);
         Ok((renderer, sender))
     }
@@ -1158,17 +1179,17 @@ impl Renderer {
         self.profile_counters.vertices.add(6 * data.len());
         self.profile_counters.draw_calls.inc();
     }
 
     fn submit_batch(&mut self,
                     batch: &PrimitiveBatch,
                     projection: &Matrix4D<f32>,
                     render_task_data: &Vec<RenderTaskData>,
-                    cache_texture: Option<TextureId>,
+                    cache_texture: TextureId,
                     render_target: Option<(TextureId, i32)>,
                     target_dimensions: DeviceUintSize) {
         let transform_kind = batch.key.flags.transform_kind();
         let needs_clipping = batch.key.flags.needs_clipping();
         debug_assert!(!needs_clipping || batch.key.blend_mode == BlendMode::Alpha);
 
         match batch.data {
             PrimitiveBatchData::Instances(ref data) => {
@@ -1251,28 +1272,27 @@ impl Renderer {
                 // on pulling specific values from the render target data
                 // and also cloning the single primitive instance to be
                 // able to pass to draw_instanced_batch(). We should
                 // think about a cleaner way to achieve this!
 
                 // Before submitting the composite batch, do the
                 // framebuffer readbacks that are needed for each
                 // composite operation in this batch.
-                let cache_texture_id = cache_texture.unwrap();
-                let cache_texture_dimensions = self.device.get_texture_dimensions(cache_texture_id);
+                let cache_texture_dimensions = self.device.get_texture_dimensions(cache_texture);
 
                 let backdrop = &render_task_data[instance.task_index as usize];
                 let readback = &render_task_data[instance.user_data[0] as usize];
                 let source = &render_task_data[instance.user_data[1] as usize];
 
                 // Bind the FBO to blit the backdrop to.
                 // Called per-instance in case the layer (and therefore FBO)
                 // changes. The device will skip the GL call if the requested
                 // target is already bound.
-                let cache_draw_target = (cache_texture_id, readback.data[4] as i32);
+                let cache_draw_target = (cache_texture, readback.data[4] as i32);
                 self.device.bind_draw_target(Some(cache_draw_target), Some(cache_texture_dimensions));
 
                 let src_x = backdrop.data[0] - backdrop.data[4] + source.data[4];
                 let src_y = backdrop.data[1] - backdrop.data[5] + source.data[5];
 
                 let dest_x = readback.data[0];
                 let dest_y = readback.data[1];
 
@@ -1309,32 +1329,30 @@ impl Renderer {
             }
         }
     }
 
     fn draw_target(&mut self,
                    render_target: Option<(TextureId, i32)>,
                    target: &RenderTarget,
                    target_size: DeviceUintSize,
-                   cache_texture: Option<TextureId>,
+                   cache_texture: TextureId,
                    should_clear: bool,
                    background_color: Option<ColorF>,
                    render_task_data: &Vec<RenderTaskData>) {
         self.device.disable_depth();
         self.device.enable_depth_write();
 
         let projection = {
             let _gm = self.gpu_profile.add_marker(GPU_TAG_SETUP_TARGET);
             self.device.bind_draw_target(render_target, Some(target_size));
 
             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(..) => (
                     // The clear color here is chosen specifically such that:
                     // - The red channel is cleared to 1, so that the clip mask
                     //   generation (which reads/writes the red channel) can
                     //   assume that each allocated rect is opaque / non-clipped
                     //   initially.
@@ -1627,17 +1645,17 @@ impl Renderer {
 
             // TODO(gw): This is a hack / workaround for #728.
             // We should find a better way to implement these updates rather
             // than wasting this extra memory, but for now it removes a large
             // number of driver stalls.
             self.gpu_data_textures[self.gdt_index].init_frame(&mut self.device, frame);
             self.gdt_index = (self.gdt_index + 1) % GPU_DATA_TEXTURE_POOL;
 
-            let mut src_id = None;
+            let mut src_id = self.dummy_cache_texture_id;
 
             for (pass_index, pass) in frame.passes.iter().enumerate() {
                 let (do_clear, size, target_id) = if pass.is_framebuffer {
                     (self.clear_framebuffer || needs_clear,
                      framebuffer_size,
                      None)
                 } else {
                     (true, &frame.cache_size, Some(self.render_targets[pass_index]))
@@ -1652,17 +1670,17 @@ impl Renderer {
                                      *size,
                                      src_id,
                                      do_clear,
                                      frame.background_color,
                                      &frame.render_task_data);
 
                 }
 
-                src_id = target_id;
+                src_id = target_id.unwrap_or(self.dummy_cache_texture_id);
             }
 
             self.draw_render_target_debug(framebuffer_size);
         }
 
         self.unlock_external_images();
     }
 
@@ -1711,16 +1729,24 @@ impl Renderer {
                                                    None,
                                                    dest_rect);
 
                     current_target += 1;
                 }
             }
         }
     }
+
+    // De-initialize the Renderer safely, assuming the GL is still alive and active.
+    pub fn deinit(mut self) {
+        //Note: this is a fake frame, only needed because texture deletion is require to happen inside a frame
+        self.device.begin_frame(1.0);
+        self.device.deinit_texture(self.dummy_cache_texture_id);
+        self.device.end_frame();
+    }
 }
 
 pub enum ExternalImageSource<'a> {
     RawData(&'a [u8]),      // raw buffers.
     NativeTexture(u32),     // Is a gl::GLuint texture handle
 }
 
 /// The data that an external client should provide about
@@ -1751,31 +1777,32 @@ pub trait ExternalImageHandler {
     fn lock(&mut self, key: ExternalImageId) -> ExternalImage;
     /// Unlock the external image. The WR should not read the image content
     /// after this call.
     fn unlock(&mut self, key: ExternalImageId);
     /// Tell the WR client that it could start to release this external image.
     fn release(&mut self, key: ExternalImageId);
 }
 
-#[derive(Debug)]
 pub struct RendererOptions {
     pub device_pixel_ratio: f32,
     pub resource_override_path: Option<PathBuf>,
     pub enable_aa: bool,
     pub enable_profiler: bool,
     pub debug: bool,
     pub enable_scrollbars: bool,
     pub precache_shaders: bool,
     pub renderer_kind: RendererKind,
     pub enable_subpixel_aa: bool,
     pub clear_framebuffer: bool,
     pub clear_color: ColorF,
     pub render_target_debug: bool,
+    pub max_texture_size: Option<u32>,
     pub workers: Option<Arc<Mutex<ThreadPool>>>,
+    pub blob_image_renderer: Option<Box<BlobImageRenderer>>,
     pub recorder: Option<Box<ApiRecordingReceiver>>,
 }
 
 impl Default for RendererOptions {
     fn default() -> RendererOptions {
         RendererOptions {
             device_pixel_ratio: 1.0,
             resource_override_path: None,
@@ -1784,13 +1811,15 @@ impl Default for RendererOptions {
             debug: false,
             enable_scrollbars: false,
             precache_shaders: false,
             renderer_kind: RendererKind::Native,
             enable_subpixel_aa: false,
             clear_framebuffer: true,
             clear_color: ColorF::new(1.0, 1.0, 1.0, 1.0),
             render_target_debug: false,
+            max_texture_size: None,
             workers: None,
+            blob_image_renderer: None,
             recorder: None,
         }
     }
 }
--- a/gfx/webrender/src/resource_cache.rs
+++ b/gfx/webrender/src/resource_cache.rs
@@ -19,16 +19,17 @@ use std::sync::{Arc, Barrier, Mutex};
 use std::sync::mpsc::{channel, Receiver, Sender};
 use std::thread;
 use texture_cache::{TextureCache, TextureCacheItemId};
 use thread_profiler::register_thread_with_profiler;
 use webrender_traits::{Epoch, FontKey, GlyphKey, ImageKey, ImageFormat, ImageRendering};
 use webrender_traits::{FontRenderMode, ImageData, GlyphDimensions, WebGLContextId};
 use webrender_traits::{DevicePoint, DeviceIntSize, ImageDescriptor, ColorF};
 use webrender_traits::{ExternalImageId, GlyphOptions, GlyphInstance};
+use webrender_traits::{BlobImageRenderer, BlobImageDescriptor, BlobImageError};
 use threadpool::ThreadPool;
 use euclid::Point2D;
 
 thread_local!(pub static FONT_CONTEXT: RefCell<FontContext> = RefCell::new(FontContext::new()));
 
 type GlyphCache = ResourceClassCache<RenderedGlyphKey, Option<TextureCacheItemId>>;
 
 /// Message sent from the resource cache to the glyph cache thread.
@@ -204,21 +205,25 @@ pub struct ResourceCache {
     texture_cache: TextureCache,
 
     // TODO(gw): We should expire (parts of) this cache semi-regularly!
     cached_glyph_dimensions: HashMap<GlyphKey, Option<GlyphDimensions>, BuildHasherDefault<FnvHasher>>,
     pending_image_requests: Vec<ImageRequest>,
     glyph_cache_tx: Sender<GlyphCacheMsg>,
     glyph_cache_result_queue: Receiver<GlyphCacheResultMsg>,
     pending_external_image_update_list: ExternalImageUpdateList,
+
+    blob_image_renderer: Option<Box<BlobImageRenderer>>,
+    blob_image_requests: HashSet<ImageRequest>,
 }
 
 impl ResourceCache {
     pub fn new(texture_cache: TextureCache,
                workers: Arc<Mutex<ThreadPool>>,
+               blob_image_renderer: Option<Box<BlobImageRenderer>>,
                enable_aa: bool) -> ResourceCache {
         let (glyph_cache_tx, glyph_cache_result_queue) = spawn_glyph_cache_thread(workers);
 
         ResourceCache {
             cached_glyphs: Some(ResourceClassCache::new()),
             cached_images: ResourceClassCache::new(),
             webgl_textures: HashMap::with_hasher(Default::default()),
             font_templates: HashMap::with_hasher(Default::default()),
@@ -227,32 +232,45 @@ impl ResourceCache {
             texture_cache: texture_cache,
             state: State::Idle,
             enable_aa: enable_aa,
             current_frame_id: FrameId(0),
             pending_image_requests: Vec::new(),
             glyph_cache_tx: glyph_cache_tx,
             glyph_cache_result_queue: glyph_cache_result_queue,
             pending_external_image_update_list: ExternalImageUpdateList::new(),
+
+            blob_image_renderer: blob_image_renderer,
+            blob_image_requests: HashSet::new(),
         }
     }
 
+    pub fn max_texture_size(&self) -> u32 {
+        self.texture_cache.max_texture_size()
+    }
+
     pub fn add_font_template(&mut self, font_key: FontKey, template: FontTemplate) {
         // Push the new font to the glyph cache thread, and also store
         // it locally for glyph metric requests.
         self.glyph_cache_tx
             .send(GlyphCacheMsg::AddFont(font_key, template.clone()))
             .unwrap();
         self.font_templates.insert(font_key, template);
     }
 
     pub fn add_image_template(&mut self,
                               image_key: ImageKey,
                               descriptor: ImageDescriptor,
                               data: ImageData) {
+        if descriptor.width > self.max_texture_size() || descriptor.height > self.max_texture_size() {
+            // TODO: we need to support handle this case gracefully, cf. issue #620.
+            println!("Warning: texture size ({} {}) larger than the maximum size",
+                     descriptor.width, descriptor.height);
+        }
+
         let resource = ImageResource {
             descriptor: descriptor,
             data: data,
             epoch: Epoch(0),
         };
 
         self.image_templates.insert(image_key, resource);
     }
@@ -316,24 +334,48 @@ impl ResourceCache {
     pub fn update_webgl_texture(&mut self, id: WebGLContextId, texture_id: SourceTexture, size: DeviceIntSize) {
         let webgl_texture = self.webgl_textures.get_mut(&id).unwrap();
 
         // Update new texture id and size
         webgl_texture.id = texture_id;
         webgl_texture.size = size;
     }
 
-    pub fn request_image(&mut self,
-                         key: ImageKey,
-                         rendering: ImageRendering) {
+    pub fn request_image(&mut self, key: ImageKey, rendering: ImageRendering) {
         debug_assert!(self.state == State::AddResources);
-        self.pending_image_requests.push(ImageRequest {
+        let request = ImageRequest {
             key: key,
             rendering: rendering,
-        });
+        };
+
+        let template = self.image_templates.get(&key).unwrap();
+        if let ImageData::Blob(ref data) = template.data {
+            if let Some(ref mut renderer) = self.blob_image_renderer {
+                let same_epoch = match self.cached_images.resources.get(&request) {
+                    Some(entry) => entry.epoch == template.epoch,
+                    None => false,
+                };
+
+                if !same_epoch && self.blob_image_requests.insert(request) {
+                    renderer.request_blob_image(
+                        key,
+                        data.clone(),
+                        &BlobImageDescriptor {
+                            width: template.descriptor.width,
+                            height: template.descriptor.height,
+                            format: template.descriptor.format,
+                            // TODO(nical): figure out the scale factor (should change with zoom).
+                            scale_factor: 1.0,
+                        },
+                    );
+                }
+            }
+        } else {
+            self.pending_image_requests.push(request);
+        }
     }
 
     pub fn request_glyphs(&mut self,
                           key: FontKey,
                           size: Au,
                           color: ColorF,
                           glyph_instances: &[GlyphInstance],
                           render_mode: FontRenderMode,
@@ -448,17 +490,17 @@ impl ResourceCache {
     }
 
     pub fn get_image_properties(&self, image_key: ImageKey) -> ImageProperties {
         let image_template = &self.image_templates[&image_key];
 
         let external_id = match image_template.data {
             ImageData::ExternalHandle(id) => Some(id),
             // raw and externalBuffer are all use resource_cache.
-            ImageData::Raw(..) | ImageData::ExternalBuffer(..) => None,
+            ImageData::Raw(..) | ImageData::ExternalBuffer(..) | ImageData::Blob(..) => None,
         };
 
         ImageProperties {
             descriptor: image_template.descriptor,
             external_id: external_id,
         }
     }
 
@@ -534,61 +576,95 @@ impl ResourceCache {
                     }
 
                     self.cached_glyphs = Some(cache);
                     break;
                 }
             }
         }
 
-        for request in self.pending_image_requests.drain(..) {
-            let cached_images = &mut self.cached_images;
-            let image_template = &self.image_templates[&request.key];
-            let image_data = image_template.data.clone();
-
-            match image_template.data {
-                ImageData::ExternalHandle(..) => {
-                    // external handle doesn't need to update the texture_cache.
-                }
-                ImageData::Raw(..) | ImageData::ExternalBuffer(..) => {
-                    match cached_images.entry(request.clone(), self.current_frame_id) {
-                        Occupied(entry) => {
-                            let image_id = entry.get().texture_cache_id;
-
-                            if entry.get().epoch != image_template.epoch {
-                                self.texture_cache.update(image_id,
-                                                          image_template.descriptor,
-                                                          image_data);
+        let mut image_requests = mem::replace(&mut self.pending_image_requests, Vec::new());
+        for request in image_requests.drain(..) {
+            self.finalize_image_request(request, None);
+        }
 
-                                // Update the cached epoch
-                                *entry.into_mut() = CachedImageInfo {
-                                    texture_cache_id: image_id,
-                                    epoch: image_template.epoch,
-                                };
-                            }
-                        }
-                        Vacant(entry) => {
-                            let image_id = self.texture_cache.new_item_id();
+        let mut blob_image_requests = mem::replace(&mut self.blob_image_requests, HashSet::new());
+        if self.blob_image_renderer.is_some() {
+            for request in blob_image_requests.drain() {
+                match self.blob_image_renderer.as_mut().unwrap()
+                                                .resolve_blob_image(request.key) {
+                    Ok(image) => {
+                        self.finalize_image_request(request, Some(ImageData::new(image.data)));
+                    }
+                    // TODO(nical): I think that we should handle these somewhat gracefully,
+                    // at least in the out-of-memory scenario.
+                    Err(BlobImageError::Oom) => {
+                        // This one should be recoverable-ish.
+                        panic!("Failed to render a vector image (OOM)");
+                    }
+                    Err(BlobImageError::InvalidKey) => {
+                        panic!("Invalid vector image key");
+                    }
+                    Err(BlobImageError::InvalidData) => {
+                        // TODO(nical): If we run into this we should kill the content process.
+                        panic!("Invalid vector image data");
+                    }
+                    Err(BlobImageError::Other(msg)) => {
+                        panic!("Vector image error {}", msg);
+                    }
+                }
+            }
+        }
+    }
 
-                            let filter = match request.rendering {
-                                ImageRendering::Pixelated => TextureFilter::Nearest,
-                                ImageRendering::Auto | ImageRendering::CrispEdges => TextureFilter::Linear,
-                            };
+    fn finalize_image_request(&mut self, request: ImageRequest, image_data: Option<ImageData>) {
+        let image_template = &self.image_templates[&request.key];
+        let image_data = image_data.unwrap_or_else(||{
+            image_template.data.clone()
+        });
 
-                            self.texture_cache.insert(image_id,
+        match image_template.data {
+            ImageData::ExternalHandle(..) => {
+                // external handle doesn't need to update the texture_cache.
+            }
+            ImageData::Raw(..) | ImageData::ExternalBuffer(..) | ImageData::Blob(..) => {
+                match self.cached_images.entry(request.clone(), self.current_frame_id) {
+                    Occupied(entry) => {
+                        let image_id = entry.get().texture_cache_id;
+
+                        if entry.get().epoch != image_template.epoch {
+                            self.texture_cache.update(image_id,
                                                       image_template.descriptor,
-                                                      filter,
                                                       image_data);
 
-                            entry.insert(CachedImageInfo {
+                            // Update the cached epoch
+                            *entry.into_mut() = CachedImageInfo {
                                 texture_cache_id: image_id,
                                 epoch: image_template.epoch,
-                            });
+                            };
                         }
                     }
+                    Vacant(entry) => {
+                        let image_id = self.texture_cache.new_item_id();
+
+                        let filter = match request.rendering {
+                            ImageRendering::Pixelated => TextureFilter::Nearest,
+                            ImageRendering::Auto | ImageRendering::CrispEdges => TextureFilter::Linear,
+                        };
+
+                        self.texture_cache.insert(image_id,
+                                                  image_template.descriptor,
+                                                  filter,
+                                                  image_data);
+
+                        entry.insert(CachedImageInfo {
+                            texture_cache_id: image_id,
+                            epoch: image_template.epoch,
+                        });
+                    }
                 }
             }
         }
     }
 
     pub fn end_frame(&mut self) {
         debug_assert!(self.state == State::QueryResources);
         self.state = State::Idle;
deleted file mode 100644
--- a/gfx/webrender/src/scroll_tree.rs
+++ /dev/null
@@ -1,394 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-use fnv::FnvHasher;
-use layer::{Layer, ScrollingState};
-use std::collections::{HashMap, HashSet};
-use std::hash::BuildHasherDefault;
-use webrender_traits::{LayerPoint, LayerRect, LayerSize, LayerToScrollTransform, PipelineId};
-use webrender_traits::{ScrollEventPhase, ScrollLayerId, ScrollLayerInfo, ScrollLayerPixel};
-use webrender_traits::{ScrollLayerRect, ScrollLayerState, ScrollLocation, ScrollToWorldTransform};
-use webrender_traits::{ServoScrollRootId, WorldPoint, as_scroll_parent_rect};
-
-pub type ScrollStates = HashMap<ScrollLayerId, ScrollingState, BuildHasherDefault<FnvHasher>>;
-
-pub struct ScrollTree {
-    pub layers: HashMap<ScrollLayerId, Layer, BuildHasherDefault<FnvHasher>>,
-    pub pending_scroll_offsets: HashMap<(PipelineId, ServoScrollRootId), LayerPoint>,
-
-    /// The ScrollLayerId of the currently scrolling layer. Used to allow the same
-    /// layer to scroll even if a touch operation leaves the boundaries of that layer.
-    pub current_scroll_layer_id: Option<ScrollLayerId>,
-
-    /// The current reference frame id, used for giving a unique id to all new
-    /// reference frames. The ScrollTree increments this by one every time a
-    /// reference frame is created.
-    current_reference_frame_id: usize,
-
-    /// The root reference frame, which is the true root of the ScrollTree. Initially
-    /// this ID is not valid, which is indicated by ```layers``` being empty.
-    pub root_reference_frame_id: ScrollLayerId,
-
-    /// The root scroll layer, which is the first child of the root reference frame.
-    /// Initially this ID is not valid, which is indicated by ```layers``` being empty.
-    pub topmost_scroll_layer_id: ScrollLayerId,
-
-    /// A set of pipelines which should be discarded the next time this
-    /// tree is drained.
-    pub pipelines_to_discard: HashSet<PipelineId>,
-}
-
-impl ScrollTree {
-    pub fn new() -> ScrollTree {
-        let dummy_pipeline = PipelineId(0, 0);
-        ScrollTree {
-            layers: HashMap::with_hasher(Default::default()),
-            pending_scroll_offsets: HashMap::new(),
-            current_scroll_layer_id: None,
-            root_reference_frame_id: ScrollLayerId::root_reference_frame(dummy_pipeline),
-            topmost_scroll_layer_id: ScrollLayerId::root_scroll_layer(dummy_pipeline),
-            current_reference_frame_id: 1,
-            pipelines_to_discard: HashSet::new(),
-        }
-    }
-
-    pub fn root_reference_frame_id(&self) -> ScrollLayerId {
-        // TODO(mrobinson): We should eventually make this impossible to misuse.
-        debug_assert!(!self.layers.is_empty());
-        debug_assert!(self.layers.contains_key(&self.root_reference_frame_id));
-        self.root_reference_frame_id
-    }
-
-    pub fn topmost_scroll_layer_id(&self) -> ScrollLayerId {
-        // TODO(mrobinson): We should eventually make this impossible to misuse.
-        debug_assert!(!self.layers.is_empty());
-        debug_assert!(self.layers.contains_key(&self.topmost_scroll_layer_id));
-        self.topmost_scroll_layer_id
-    }
-
-    pub fn establish_root(&mut self,
-                          pipeline_id: PipelineId,
-                          viewport_size: &LayerSize,
-                          content_size: &LayerSize) {
-        debug_assert!(self.layers.is_empty());
-
-        let identity = LayerToScrollTransform::identity();
-        let viewport = LayerRect::new(LayerPoint::zero(), *viewport_size);
-
-        let root_reference_frame_id = ScrollLayerId::root_reference_frame(pipeline_id);
-        self.root_reference_frame_id = root_reference_frame_id;
-        let reference_frame = Layer::new(&viewport, viewport.size, &identity, pipeline_id);
-        self.layers.insert(self.root_reference_frame_id, reference_frame);
-
-        let scroll_layer = Layer::new(&viewport, *content_size, &identity, pipeline_id);
-        let topmost_scroll_layer_id = ScrollLayerId::root_scroll_layer(pipeline_id);
-        self.topmost_scroll_layer_id = topmost_scroll_layer_id;
-        self.add_layer(scroll_layer, topmost_scroll_layer_id, root_reference_frame_id);
-    }
-
-    pub fn collect_layers_bouncing_back(&self)
-                                        -> HashSet<ScrollLayerId, BuildHasherDefault<FnvHasher>> {
-        let mut layers_bouncing_back = HashSet::with_hasher(Default::default());
-        for (scroll_layer_id, layer) in self.layers.iter() {
-            if layer.scrolling.bouncing_back {
-                layers_bouncing_back.insert(*scroll_layer_id);
-            }
-        }
-        layers_bouncing_back
-    }
-
-    fn find_scrolling_layer_at_point_in_layer(&self,
-                                              cursor: &WorldPoint,
-                                              scroll_layer_id: ScrollLayerId)
-                                              -> Option<ScrollLayerId> {
-        self.layers.get(&scroll_layer_id).and_then(|layer| {
-            for child_layer_id in layer.children.iter().rev() {
-            if let Some(layer_id) =
-                self.find_scrolling_layer_at_point_in_layer(cursor, *child_layer_id) {
-                    return Some(layer_id);
-                }
-            }
-
-            if let ScrollLayerInfo::ReferenceFrame(_) = scroll_layer_id.info {
-                return None;
-            }
-
-            if layer.ray_intersects_layer(cursor) {
-                Some(scroll_layer_id)
-            } else {
-                None
-            }
-        })
-    }
-
-    pub fn find_scrolling_layer_at_point(&self, cursor: &WorldPoint) -> ScrollLayerId {
-        self.find_scrolling_layer_at_point_in_layer(cursor, self.root_reference_frame_id())
-            .unwrap_or(self.topmost_scroll_layer_id())
-    }
-
-    pub fn get_scroll_layer_state(&self) -> Vec<ScrollLayerState> {
-        let mut result = vec![];
-        for (scroll_layer_id, scroll_layer) in self.layers.iter() {
-            match scroll_layer_id.info {
-                ScrollLayerInfo::Scrollable(_, servo_scroll_root_id) => {
-                    result.push(ScrollLayerState {
-                        pipeline_id: scroll_layer.pipeline_id,
-                        scroll_root_id: servo_scroll_root_id,
-                        scroll_offset: scroll_layer.scrolling.offset,
-                    })
-                }
-                ScrollLayerInfo::ReferenceFrame(..) => {}
-            }
-        }
-        result
-    }
-
-    pub fn drain(&mut self) -> ScrollStates {
-        self.current_reference_frame_id = 1;
-
-        let mut scroll_states = HashMap::with_hasher(Default::default());
-        for (layer_id, old_layer) in &mut self.layers.drain() {
-            if !self.pipelines_to_discard.contains(&layer_id.pipeline_id) {
-                scroll_states.insert(layer_id, old_layer.scrolling);
-            }
-        }
-
-        self.pipelines_to_discard.clear();
-        scroll_states
-    }
-
-    pub fn scroll_layers(&mut self,
-                         origin: LayerPoint,
-                         pipeline_id: PipelineId,
-                         scroll_root_id: ServoScrollRootId)
-                         -> bool {
-        if self.layers.is_empty() {
-            self.pending_scroll_offsets.insert((pipeline_id, scroll_root_id), origin);
-            return false;
-        }
-
-        let origin = LayerPoint::new(origin.x.max(0.0), origin.y.max(0.0));
-
-        let mut scrolled_a_layer = false;
-        let mut found_layer = false;
-        for (layer_id, layer) in self.layers.iter_mut() {
-            if layer_id.pipeline_id != pipeline_id {
-                continue;
-            }
-
-            match layer_id.info {
-                ScrollLayerInfo::Scrollable(_, id) if id != scroll_root_id => continue,
-                ScrollLayerInfo::ReferenceFrame(..) => continue,
-                ScrollLayerInfo::Scrollable(..) => {}
-            }
-
-            found_layer = true;
-            scrolled_a_layer |= layer.set_scroll_origin(&origin);
-        }
-
-        if !found_layer {
-            self.pending_scroll_offsets.insert((pipeline_id, scroll_root_id), origin);
-        }
-
-        scrolled_a_layer
-    }
-
-    pub fn scroll(&mut self,
-                  scroll_location: ScrollLocation,
-                  cursor: WorldPoint,
-                  phase: ScrollEventPhase)
-                  -> bool {
-        if self.layers.is_empty() {
-            return false;
-        }
-
-        let scroll_layer_id = match (
-            phase,
-            self.find_scrolling_layer_at_point(&cursor),
-            self.current_scroll_layer_id) {
-            (ScrollEventPhase::Start, scroll_layer_at_point_id, _) => {
-                self.current_scroll_layer_id = Some(scroll_layer_at_point_id);
-                scroll_layer_at_point_id
-            },
-            (_, scroll_layer_at_point_id, Some(cached_scroll_layer_id)) => {
-                let scroll_layer_id = match self.layers.get(&cached_scroll_layer_id) {
-                    Some(_) => cached_scroll_layer_id,
-                    None => {
-                        self.current_scroll_layer_id = Some(scroll_layer_at_point_id);
-                        scroll_layer_at_point_id
-                    },
-                };
-                scroll_layer_id
-            },
-            (_, _, None) => return false,
-        };
-
-        let topmost_scroll_layer_id = self.topmost_scroll_layer_id();
-        let non_root_overscroll = if scroll_layer_id != topmost_scroll_layer_id {
-            // true if the current layer is overscrolling,
-            // and it is not the root scroll layer.
-            let child_layer = self.layers.get(&scroll_layer_id).unwrap();
-            let overscroll_amount = child_layer.overscroll_amount();
-            overscroll_amount.width != 0.0 || overscroll_amount.height != 0.0
-        } else {
-            false
-        };
-
-        let switch_layer = match phase {
-            ScrollEventPhase::Start => {
-                // if this is a new gesture, we do not switch layer,
-                // however we do save the state of non_root_overscroll,
-                // for use in the subsequent Move phase.
-                let mut current_layer = self.layers.get_mut(&scroll_layer_id).unwrap();
-                current_layer.scrolling.should_handoff_scroll = non_root_overscroll;
-                false
-            },
-            ScrollEventPhase::Move(_) => {
-                // Switch layer if movement originated in a new gesture,
-                // from a non root layer in overscroll.
-                let current_layer = self.layers.get_mut(&scroll_layer_id).unwrap();
-                current_layer.scrolling.should_handoff_scroll && non_root_overscroll
-            },
-            ScrollEventPhase::End => {
-                // clean-up when gesture ends.
-                let mut current_layer = self.layers.get_mut(&scroll_layer_id).unwrap();
-                current_layer.scrolling.should_handoff_scroll = false;
-                false
-            },
-        };
-
-        let scroll_layer_info = if switch_layer {
-            topmost_scroll_layer_id.info
-        } else {
-            scroll_layer_id.info
-        };
-
-        let scroll_root_id = match scroll_layer_info {
-             ScrollLayerInfo::Scrollable(_, scroll_root_id) => scroll_root_id,
-             _ => unreachable!("Tried to scroll a reference frame."),
-
-        };
-
-        let mut scrolled_a_layer = false;
-        for (layer_id, layer) in self.layers.iter_mut() {
-            if layer_id.pipeline_id != scroll_layer_id.pipeline_id {
-                continue;
-            }
-
-            match layer_id.info {
-                ScrollLayerInfo::Scrollable(_, id) if id != scroll_root_id => continue,
-                ScrollLayerInfo::ReferenceFrame(..) => continue,
-                _ => {}
-            }
-
-            let scrolled_this_layer = layer.scroll(scroll_location, phase);
-            scrolled_a_layer = scrolled_a_layer || scrolled_this_layer;
-        }
-        scrolled_a_layer
-    }
-
-    pub fn update_all_layer_transforms(&mut self) {
-        if self.layers.is_empty() {
-            return;
-        }
-
-        let root_reference_frame_id = self.root_reference_frame_id();
-        let root_viewport = self.layers[&root_reference_frame_id].local_viewport_rect;
-        self.update_layer_transform(root_reference_frame_id,
-                                    &ScrollToWorldTransform::identity(),
-                                    &as_scroll_parent_rect(&root_viewport));
-    }
-
-    fn update_layer_transform(&mut self,
-                              layer_id: ScrollLayerId,
-                              parent_world_transform: &ScrollToWorldTransform,
-                              parent_viewport_rect: &ScrollLayerRect) {
-        // TODO(gw): This is an ugly borrow check workaround to clone these.
-        //           Restructure this to avoid the clones!
-        let (layer_transform_for_children, viewport_rect, layer_children) = {
-            match self.layers.get_mut(&layer_id) {
-                Some(layer) => {
-                    layer.update_transform(parent_world_transform, parent_viewport_rect);
-
-                    (layer.world_content_transform.with_source::<ScrollLayerPixel>(),
-                     as_scroll_parent_rect(&layer.combined_local_viewport_rect),
-                     layer.children.clone())
-                }
-                None => return,
-            }
-        };
-
-        for child_layer_id in layer_children {
-            self.update_layer_transform(child_layer_id,
-                                        &layer_transform_for_children,
-                                        &viewport_rect);
-        }
-    }
-
-    pub fn tick_scrolling_bounce_animations(&mut self) {
-        for (_, layer) in &mut self.layers {
-            layer.tick_scrolling_bounce_animation()
-        }
-    }
-
-    pub fn finalize_and_apply_pending_scroll_offsets(&mut self, old_states: ScrollStates) {
-        // 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_states.get(&scroll_layer_id) {
-                Some(old_scrolling_state) => *old_scrolling_state,
-                None => ScrollingState::new(),
-            };
-
-            layer.finalize(&scrolling_state);
-
-            let scroll_root_id = match scroll_layer_id.info {
-                ScrollLayerInfo::Scrollable(_, scroll_root_id) => scroll_root_id,
-                _ => continue,
-            };
-
-
-            let pipeline_id = scroll_layer_id.pipeline_id;
-            if let Some(pending_offset) =
-                self.pending_scroll_offsets.remove(&(pipeline_id, scroll_root_id)) {
-                layer.set_scroll_origin(&pending_offset);
-            }
-        }
-
-    }
-
-    pub fn add_reference_frame(&mut self,
-                               rect: LayerRect,
-                               transform: LayerToScrollTransform,
-                               pipeline_id: PipelineId,
-                               parent_id: ScrollLayerId) -> ScrollLayerId {
-        let reference_frame_id = ScrollLayerId {
-            pipeline_id: pipeline_id,
-            info: ScrollLayerInfo::ReferenceFrame(self.current_reference_frame_id),
-        };
-        self.current_reference_frame_id += 1;
-
-        let layer = Layer::new(&rect, rect.size, &transform, pipeline_id);
-        self.add_layer(layer, reference_frame_id, parent_id);
-        reference_frame_id
-    }
-
-    pub fn add_layer(&mut self, layer: Layer, id: ScrollLayerId, parent_id: ScrollLayerId) {
-        debug_assert!(!self.layers.contains_key(&id));
-        self.layers.insert(id, layer);
-
-        debug_assert!(parent_id != id);
-        self.layers.get_mut(&parent_id).unwrap().add_child(id);
-    }
-
-    pub fn discard_frame_state_for_pipeline(&mut self, pipeline_id: PipelineId) {
-        self.pipelines_to_discard.insert(pipeline_id);
-
-        match self.current_scroll_layer_id {
-            Some(id) if id.pipeline_id == pipeline_id => self.current_scroll_layer_id = None,
-            _ => {}
-        }
-    }
-}
-
--- a/gfx/webrender/src/texture_cache.rs
+++ b/gfx/webrender/src/texture_cache.rs
@@ -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/. */
 
-use device::{MAX_TEXTURE_SIZE, TextureFilter};
+use device::TextureFilter;
 use fnv::FnvHasher;
 use freelist::{FreeList, FreeListItem, FreeListItemId};
 use internal_types::{TextureUpdate, TextureUpdateOp};
 use internal_types::{CacheTextureId, RenderTargetMode, TextureUpdateList, RectUv};
 use std::cmp::{self, Ordering};
 use std::collections::HashMap;
 use std::collections::hash_map::Entry;
 use std::hash::BuildHasherDefault;
@@ -23,19 +23,16 @@ use webrender_traits::ImageDescriptor;
 const MAX_BYTES_PER_TEXTURE: u32 = 1024 * 1024 * 256;  // 256MB
 
 /// The number of RGBA pixels we're allowed to use for a texture.
 const MAX_RGBA_PIXELS_PER_TEXTURE: u32 = MAX_BYTES_PER_TEXTURE / 4;
 
 /// The desired initial size of each texture, in pixels.
 const INITIAL_TEXTURE_SIZE: u32 = 1024;
 
-/// The desired initial area of each texture, in pixels squared.
-const INITIAL_TEXTURE_AREA: u32 = INITIAL_TEXTURE_SIZE * INITIAL_TEXTURE_SIZE;
-
 /// The square root of the number of RGBA pixels we're allowed to use for a texture, rounded down.
 /// to the next power of two.
 const SQRT_MAX_RGBA_PIXELS_PER_TEXTURE: u32 = 8192;
 
 /// The minimum number of pixels on each side that we require for rects to be classified as
 /// "medium" within the free list.
 const MINIMUM_MEDIUM_RECT_SIZE: u32 = 16;
 
@@ -325,19 +322,18 @@ impl TexturePage {
             if rect.size.width > 0 && rect.size.height > 0 {
                 self.free_list.push(rect);
             }
         }
 
         self.texture_size = new_texture_size
     }
 
-    fn can_grow(&self) -> bool {
-        self.texture_size.width < max_texture_size() ||
-        self.texture_size.height < max_texture_size()
+    fn can_grow(&self, max_size: u32) -> bool {
+        self.texture_size.width < max_size || self.texture_size.height < max_size
     }
 }
 
 /// A binning free list. Binning is important to avoid sifting through lots of small strips when
 /// allocating many texture items.
 struct FreeRectList {
     small: Vec<DeviceUintRect>,
     medium: Vec<DeviceUintRect>,
@@ -536,41 +532,51 @@ impl CacheTextureIdList {
 }
 
 pub struct TextureCache {
     cache_id_list: CacheTextureIdList,
     free_texture_levels: HashMap<ImageFormat, Vec<FreeTextureLevel>, BuildHasherDefault<FnvHasher>>,
     items: FreeList<TextureCacheItem>,
     arena: TextureCacheArena,
     pending_updates: TextureUpdateList,
+    max_texture_size: u32,
 }
 
 #[derive(PartialEq, Eq, Debug)]
 pub enum AllocationKind {
     TexturePage,
     Standalone,
 }
 
 #[derive(Debug)]
 pub struct AllocationResult {
     kind: AllocationKind,
     item: TextureCacheItem,
 }
 
 impl TextureCache {
-    pub fn new() -> TextureCache {
+    pub fn new(mut max_texture_size: u32) -> TextureCache {
+        if max_texture_size * max_texture_size > MAX_RGBA_PIXELS_PER_TEXTURE {
+            max_texture_size = SQRT_MAX_RGBA_PIXELS_PER_TEXTURE;
+        }
+
         TextureCache {
             cache_id_list: CacheTextureIdList::new(),
             free_texture_levels: HashMap::with_hasher(Default::default()),
             items: FreeList::new(),
             pending_updates: TextureUpdateList::new(),
             arena: TextureCacheArena::new(),
+            max_texture_size: max_texture_size,
         }
     }
 
+    pub fn max_texture_size(&self) -> u32 {
+        self.max_texture_size
+    }
+
     pub fn pending_updates(&mut self) -> TextureUpdateList {
         mem::replace(&mut self.pending_updates, TextureUpdateList::new())
     }
 
     // TODO(gw): This API is a bit ugly (having to allocate an ID and
     //           then use it). But it has to be that way for now due to
     //           how the raster_jobs code works.
     pub fn new_item_id(&mut self) -> TextureCacheItemId {
@@ -621,18 +627,18 @@ impl TextureCache {
         let page_list = match format {
             ImageFormat::A8 => &mut self.arena.pages_a8,
             ImageFormat::RGBA8 => &mut self.arena.pages_rgba8,
             ImageFormat::RGB8 => &mut self.arena.pages_rgb8,
             ImageFormat::Invalid | ImageFormat::RGBAF32 => unreachable!(),
         };
 
         // TODO(gw): Handle this sensibly (support failing to render items that can't fit?)
-        assert!(requested_size.width < max_texture_size());
-        assert!(requested_size.height < max_texture_size());
+        assert!(requested_size.width < self.max_texture_size);
+        assert!(requested_size.height < self.max_texture_size);
 
         // Loop until an allocation succeeds, growing or adding new
         // texture pages as required.
         loop {
             let location = page_list.last_mut().and_then(|last_page| {
                 last_page.allocate(&requested_size)
             });
 
@@ -646,21 +652,21 @@ impl TextureCache {
                 *self.items.get_mut(image_id) = cache_item;
 
                 return AllocationResult {
                     item: self.items.get(image_id).clone(),
                     kind: AllocationKind::TexturePage,
                 }
             }
 
-            if !page_list.is_empty() && page_list.last().unwrap().can_grow() {
+            if !page_list.is_empty() && page_list.last().unwrap().can_grow(self.max_texture_size) {
                 let last_page = page_list.last_mut().unwrap();
                 // Grow the texture.
-                let new_width = cmp::min(last_page.texture_size.width * 2, max_texture_size());
-                let new_height = cmp::min(last_page.texture_size.height * 2, max_texture_size());
+                let new_width = cmp::min(last_page.texture_size.width * 2, self.max_texture_size);
+                let new_height = cmp::min(last_page.texture_size.height * 2, self.max_texture_size);
                 let texture_size = DeviceUintSize::new(new_width, new_height);
                 self.pending_updates.push(TextureUpdate {
                     id: last_page.texture_id,
                     op: texture_grow_op(texture_size, format, mode),
                 });
                 last_page.grow(texture_size);
 
                 self.items.for_each_item(|item| {
@@ -668,17 +674,17 @@ impl TextureCache {
                         item.texture_size = texture_size;
                     }
                 });
 
                 continue;
             }
 
             // We need a new page.
-            let texture_size = initial_texture_size();
+            let texture_size = initial_texture_size(self.max_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() {
                 let texture_id = self.cache_id_list.allocate();
 
@@ -709,16 +715,19 @@ impl TextureCache {
         // TODO(gw): Handle updates to size/format!
         debug_assert_eq!(existing_item.allocated_rect.size.width, descriptor.width);
         debug_assert_eq!(existing_item.allocated_rect.size.height, descriptor.height);
 
         let op = match data {
             ImageData::ExternalHandle(..) | ImageData::ExternalBuffer(..)=> {
                 panic!("Doesn't support Update() for external image.");
             }
+            ImageData::Blob(..) => {
+                panic!("The vector image should have been rasterized into a raw image.");
+            }
             ImageData::Raw(bytes) => {
                 TextureUpdateOp::Update {
                     page_pos_x: existing_item.allocated_rect.origin.x,
                     page_pos_y: existing_item.allocated_rect.origin.y,
                     width: descriptor.width,
                     height: descriptor.height,
                     data: bytes,
                     stride: descriptor.stride,
@@ -734,16 +743,20 @@ impl TextureCache {
         self.pending_updates.push(update_op);
     }
 
     pub fn insert(&mut self,
                   image_id: TextureCacheItemId,
                   descriptor: ImageDescriptor,
                   filter: TextureFilter,
                   data: ImageData) {
+        if let ImageData::Blob(..) = data {
+            panic!("must rasterize the vector image before adding to the cache");
+        }
+
         let width = descriptor.width;
         let height = descriptor.height;
         let format = descriptor.format;
         let stride = descriptor.stride;
 
         let result = self.allocate(image_id,
                                    width,
                                    height,
@@ -751,16 +764,19 @@ impl TextureCache {
                                    filter);
 
         match result.kind {
             AllocationKind::TexturePage => {
                 match data {
                     ImageData::ExternalHandle(..) => {
                         panic!("External handle should not go through texture_cache.");
                     }
+                    ImageData::Blob(..) => {
+                        panic!("The vector image should have been rasterized.");
+                    }
                     ImageData::Raw(bytes) => {
                         let update_op = TextureUpdate {
                             id: result.item.texture_id,
                             op: TextureUpdateOp::Update {
                                 page_pos_x: result.item.allocated_rect.origin.x,
                                 page_pos_y: result.item.allocated_rect.origin.y,
                                 width: result.item.allocated_rect.size.width,
                                 height: result.item.allocated_rect.size.height,
@@ -872,27 +888,12 @@ impl FitsInside for DeviceUintSize {
 
 /// FIXME(pcwalton): Would probably be more efficient as a bit vector.
 #[derive(Clone, Copy)]
 pub struct FreeTextureLevel {
     texture_id: CacheTextureId,
 }
 
 /// Returns the number of pixels on a side we start out with for our texture atlases.
-fn initial_texture_size() -> DeviceUintSize {
-    let max_hardware_texture_size = *MAX_TEXTURE_SIZE as u32;
-    let initial_size = if max_hardware_texture_size * max_hardware_texture_size > INITIAL_TEXTURE_AREA {
-        INITIAL_TEXTURE_SIZE
-    } else {
-        max_hardware_texture_size
-    };
+fn initial_texture_size(max_texture_size: u32) -> DeviceUintSize {
+    let initial_size = cmp::min(max_texture_size, INITIAL_TEXTURE_SIZE);
     DeviceUintSize::new(initial_size, initial_size)
 }
-
-/// Returns the number of pixels on a side we're allowed to use for our texture atlases.
-fn max_texture_size() -> u32 {
-    let max_hardware_texture_size = *MAX_TEXTURE_SIZE as u32;
-    if max_hardware_texture_size * max_hardware_texture_size > MAX_RGBA_PIXELS_PER_TEXTURE {
-        SQRT_MAX_RGBA_PIXELS_PER_TEXTURE
-    } else {
-        max_hardware_texture_size
-    }
-}
--- a/gfx/webrender/src/tiling.rs
+++ b/gfx/webrender/src/tiling.rs
@@ -529,30 +529,31 @@ impl AlphaBatcher {
 
         for task in &mut self.tasks {
             let task_index = render_tasks.get_static_task_index(&task.task_id);
             let mut existing_opaque_batch_index = 0;
 
             for item in &task.alpha_items {
                 let (batch_key, item_bounding_rect) = match item {
                     &AlphaRenderItem::Blend(stacking_context_index, ..) => {
-                        let stacking_context = &ctx.stacking_context_store[stacking_context_index.0];
+                        let stacking_context =
+                            &ctx.stacking_context_store[stacking_context_index.0];
                         (AlphaBatchKey::new(AlphaBatchKind::Blend,
                                             AlphaBatchKeyFlags::empty(),
                                             BlendMode::Alpha,
                                             BatchTextures::no_texture()),
-                         &stacking_context.xf_rect.as_ref().unwrap().bounding_rect)
+                         &stacking_context.bounding_rect)
                     }
                     &AlphaRenderItem::HardwareComposite(stacking_context_index, _, composite_op, ..) => {
                         let stacking_context = &ctx.stacking_context_store[stacking_context_index.0];
                         (AlphaBatchKey::new(AlphaBatchKind::HardwareComposite,
                                             AlphaBatchKeyFlags::empty(),
                                             composite_op.to_blend_mode(),
                                             BatchTextures::no_texture()),
-                         &stacking_context.xf_rect.as_ref().unwrap().bounding_rect)
+                         &stacking_context.bounding_rect)
                     }
                     &AlphaRenderItem::Composite(stacking_context_index,
                                                 backdrop_id,
                                                 src_id,
                                                 info,
                                                 z) => {
                         // Composites always get added to their own batch.
                         // This is because the result of a composite can affect
@@ -562,21 +563,20 @@ impl AlphaBatcher {
                                                                   task_index,
                                                                   render_tasks.get_task_index(&backdrop_id, child_pass_index),
                                                                   render_tasks.get_static_task_index(&src_id),
                                                                   info,
                                                                   z);
                         alpha_batches.push(batch);
                         continue;
                     }
-                    &AlphaRenderItem::Primitive(stacking_context_index, prim_index, _) => {
-                        let stacking_context =
-                            &ctx.stacking_context_store[stacking_context_index.0];
+                    &AlphaRenderItem::Primitive(clip_scroll_group_index, prim_index, _) => {
+                        let group = &ctx.clip_scroll_group_store[clip_scroll_group_index.0];
                         let prim_metadata = ctx.prim_store.get_metadata(prim_index);
-                        let transform_kind = stacking_context.xf_rect.as_ref().unwrap().kind;
+                        let transform_kind = group.xf_rect.as_ref().unwrap().kind;
                         let needs_clipping = prim_metadata.clip_task.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 {
@@ -611,21 +611,17 @@ impl AlphaBatcher {
                     }
 
                     // check for intersections
                     for item in &batch.items {
                         let intersects = match *item {
                             PrimitiveBatchItem::StackingContext(stacking_context_index) => {
                                 let stacking_context =
                                     &ctx.stacking_context_store[stacking_context_index.0];
-                                stacking_context.xf_rect
-                                                .as_ref()
-                                                .unwrap()
-                                                .bounding_rect
-                                                .intersects(item_bounding_rect)
+                                stacking_context.bounding_rect.intersects(item_bounding_rect)
                             }
                             PrimitiveBatchItem::Primitive(prim_index) => {
                                 let bounding_rect = &ctx.prim_store.cpu_bounding_rects[prim_index.0];
                                 bounding_rect.as_ref().unwrap().intersects(item_bounding_rect)
                             }
                         };
 
                         if intersects {
@@ -660,45 +656,46 @@ impl AlphaBatcher {
                         ctx.prim_store.add_blend_to_batch(stacking_context_index,
                                                           batch,
                                                           task_index,
                                                           render_tasks.get_static_task_index(&src_id),
                                                           info,
                                                           z);
                     }
                     &AlphaRenderItem::HardwareComposite(stacking_context_index, src_id, _, z) => {
-                        ctx.prim_store.add_hardware_composite_to_batch(stacking_context_index,
-                                                                       batch,
-                                                                       task_index,
-                                                                       render_tasks.get_static_task_index(&src_id),
-                                                                       z);
+                        ctx.prim_store.add_hardware_composite_to_batch(
+                            stacking_context_index,
+                            batch,
+                            task_index,
+                            render_tasks.get_static_task_index(&src_id),
+                            z);
                     }
-                    &AlphaRenderItem::Primitive(stacking_context_index, prim_index, z) => {
-                        let stacking_context =
-                            &ctx.stacking_context_store[stacking_context_index.0];
+                    &AlphaRenderItem::Primitive(clip_scroll_group_index, prim_index, z) => {
+                        let packed_layer = ctx.clip_scroll_group_store[clip_scroll_group_index.0]
+                                              .packed_layer_index;
                         ctx.prim_store.add_prim_to_batch(prim_index,
                                                          batch,
-                                                         stacking_context.packed_layer_index,
+                                                         packed_layer,
                                                          task_index,
                                                          render_tasks,
                                                          child_pass_index,
                                                          z);
                     }
                 }
             }
 
             for item in task.opaque_items.iter().rev() {
                 let batch_key = match item {
                     &AlphaRenderItem::Composite(..) => unreachable!(),
                     &AlphaRenderItem::Blend(..) => unreachable!(),
                     &AlphaRenderItem::HardwareComposite(..) => unreachable!(),
-                    &AlphaRenderItem::Primitive(stacking_context_index, prim_index, _) => {
-                        let stacking_context = &ctx.stacking_context_store[stacking_context_index.0];
+                    &AlphaRenderItem::Primitive(clip_scroll_group_index, prim_index, _) => {
+                        let group = &ctx.clip_scroll_group_store[clip_scroll_group_index.0];
+                        let transform_kind = group.xf_rect.as_ref().unwrap().kind;
                         let prim_metadata = ctx.prim_store.get_metadata(prim_index);
-                        let transform_kind = stacking_context.xf_rect.as_ref().unwrap().kind;
                         let needs_clipping = prim_metadata.clip_task.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 {
@@ -740,22 +737,23 @@ impl AlphaBatcher {
                     opaque_batches.push(new_batch)
                 }
 
                 let batch = &mut opaque_batches[existing_opaque_batch_index];
                 match item {
                     &AlphaRenderItem::Composite(..) => unreachable!(),
                     &AlphaRenderItem::Blend(..) => unreachable!(),
                     &AlphaRenderItem::HardwareComposite(..) => unreachable!(),
-                    &AlphaRenderItem::Primitive(stacking_context_index, prim_index, z) => {
-                        let stacking_context =
-                            &ctx.stacking_context_store[stacking_context_index.0];
+                    &AlphaRenderItem::Primitive(clip_scroll_group_index, prim_index, z) => {
+                        let packed_layer_index =
+                            ctx.clip_scroll_group_store[clip_scroll_group_index.0]
+                               .packed_layer_index;
                         ctx.prim_store.add_prim_to_batch(prim_index,
                                                          batch,
-                                                         stacking_context.packed_layer_index,
+                                                         packed_layer_index,
                                                          task_index,
                                                          render_tasks,
                                                          child_pass_index,
                                                          z);
                     }
                 }
             }
         }
@@ -843,16 +841,17 @@ impl ClipBatcher {
                 })
             }
         }
     }
 }
 
 pub struct RenderTargetContext<'a> {
     pub stacking_context_store: &'a [StackingContext],
+    pub clip_scroll_group_store: &'a [ClipScrollGroup],
     pub prim_store: &'a PrimitiveStore,
     pub 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,
@@ -1294,24 +1293,73 @@ impl PrimitiveBatch {
 }
 
 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
 pub struct PackedLayerIndex(pub usize);
 
 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
 pub struct StackingContextIndex(pub usize);
 
+#[derive(Debug)]
 pub struct StackingContext {
     pub pipeline_id: PipelineId,
     pub local_transform: LayerToScrollTransform,
     pub local_rect: LayerRect,
+    pub bounding_rect: DeviceIntRect,
+    pub composite_ops: CompositeOps,
+    pub clip_scroll_groups: Vec<ClipScrollGroupIndex>,
+    pub is_visible: bool,
+}
+
+impl StackingContext {
+    pub fn new(pipeline_id: PipelineId,
+               local_transform: LayerToScrollTransform,
+               local_rect: LayerRect,
+               composite_ops: CompositeOps,
+               clip_scroll_group_index: ClipScrollGroupIndex)
+               -> StackingContext {
+        StackingContext {
+            pipeline_id: pipeline_id,
+            local_transform: local_transform,
+            local_rect: local_rect,
+            bounding_rect: DeviceIntRect::zero(),
+            composite_ops: composite_ops,
+            clip_scroll_groups: vec![clip_scroll_group_index],
+            is_visible: false,
+        }
+    }
+
+    pub fn clip_scroll_group(&self) -> ClipScrollGroupIndex {
+        // Currently there is only one scrolled stacking context per context,
+        // but eventually this will be selected from the vector based on the
+        // scroll layer of this primitive.
+        self.clip_scroll_groups[0]
+    }
+
+    pub fn can_contribute_to_scene(&self) -> bool {
+        !self.composite_ops.will_make_invisible()
+    }
+}
+
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
+pub struct ClipScrollGroupIndex(pub usize);
+
+#[derive(Debug)]
+pub struct ClipScrollGroup {
+    pub stacking_context_index: StackingContextIndex,
     pub scroll_layer_id: ScrollLayerId,
+    pub packed_layer_index: PackedLayerIndex,
+    pub pipeline_id: PipelineId,
     pub xf_rect: Option<TransformedRect>,
-    pub composite_ops: CompositeOps,
-    pub packed_layer_index: PackedLayerIndex,
+}
+
+impl ClipScrollGroup {
+    pub fn is_visible(&self) -> bool {
+        self.xf_rect.is_some()
+    }
 }
 
 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
 pub struct ScrollLayerIndex(pub usize);
 
 pub struct ScrollLayer {
     pub scroll_layer_id: ScrollLayerId,
     pub parent_index: ScrollLayerIndex,
@@ -1381,26 +1429,16 @@ impl CompositeOps {
                 &LowLevelFilterOp::Opacity(Au(0)) => return true,
                 _ => {}
             }
         }
         false
     }
 }
 
-impl StackingContext {
-    pub fn is_visible(&self) -> bool {
-        self.xf_rect.is_some()
-    }
-
-    pub fn can_contribute_to_scene(&self) -> bool {
-        !self.composite_ops.will_make_invisible()
-    }
-}
-
 /// A rendering-oriented representation of frame::Frame built by the render backend
 /// and presented to the renderer.
 pub struct Frame {
     pub viewport_size: LayerSize,
     pub background_color: Option<ColorF>,
     pub device_pixel_ratio: f32,
     pub cache_size: DeviceUintSize,
     pub passes: Vec<RenderPass>,
--- a/gfx/webrender_bindings/Cargo.toml
+++ b/gfx/webrender_bindings/Cargo.toml
@@ -1,24 +1,24 @@
 [package]
 name = "webrender_bindings"
 version = "0.1.0"
 authors = ["The Mozilla Project Developers"]
 license = "MPL-2.0"
 
 [dependencies]
-webrender_traits = {path = "../webrender_traits", version = "0.14"}
-euclid = "0.10"
-app_units = "0.3"
+webrender_traits = {path = "../webrender_traits", version = "0.19"}
+euclid = "0.11"
+app_units = "0.4"
 gleam = "0.2"
 fnv="1.0"
 
 [dependencies.webrender]
 path = "../webrender"
-version = "0.15"
+version = "0.19"
 default-features = false
 features = ["codegen"]
 
 [target.'cfg(target_os = "macos")'.dependencies]
 core-foundation = "0.2.2"
 
 [target.'cfg(target_os = "windows")'.dependencies]
 kernel32-sys = "0.2"
--- a/gfx/webrender_bindings/WebRenderAPI.cpp
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
@@ -13,21 +13,23 @@
 namespace mozilla {
 namespace wr {
 
 class NewRenderer : public RendererEvent
 {
 public:
   NewRenderer(WrAPI** aApi, layers::CompositorBridgeParentBase* aBridge,
               GLint* aMaxTextureSize,
+              bool* aUseANGLE,
               RefPtr<widget::CompositorWidget>&& aWidget,
               layers::SynchronousTask* aTask,
               bool aEnableProfiler)
     : mWrApi(aApi)
     , mMaxTextureSize(aMaxTextureSize)
+    , mUseANGLE(aUseANGLE)
     , mBridge(aBridge)
     , mCompositorWidget(Move(aWidget))
     , mTask(aTask)
     , mEnableProfiler(aEnableProfiler)
   {
     MOZ_COUNT_CTOR(NewRenderer);
   }
 
@@ -41,16 +43,17 @@ public:
     layers::AutoCompleteTask complete(mTask);
 
     RefPtr<gl::GLContext> gl = gl::GLContextProvider::CreateForCompositorWidget(mCompositorWidget, true);
     if (!gl || !gl->MakeCurrent()) {
       return;
     }
 
     gl->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, mMaxTextureSize);
+    *mUseANGLE = gl->IsANGLE();
 
     WrRenderer* wrRenderer = nullptr;
     if (!wr_window_new(aWindowId, gl.get(), this->mEnableProfiler, nullptr, mWrApi, &wrRenderer)) {
       return;
     }
     MOZ_ASSERT(wrRenderer);
 
     RefPtr<RenderThread> thread = &aRenderThread;
@@ -62,16 +65,17 @@ public:
                                             mBridge);
 
     aRenderThread.AddRenderer(aWindowId, Move(renderer));
   }
 
 private:
   WrAPI** mWrApi;
   GLint* mMaxTextureSize;
+  bool* mUseANGLE;
   layers::CompositorBridgeParentBase* mBridge;
   RefPtr<widget::CompositorWidget> mCompositorWidget;
   layers::SynchronousTask* mTask;
   bool mEnableProfiler;
 };
 
 class RemoveRenderer : public RendererEvent
 {
@@ -107,32 +111,33 @@ WebRenderAPI::Create(bool aEnableProfile
   MOZ_ASSERT(aBridge);
   MOZ_ASSERT(aWidget);
 
   static uint64_t sNextId = 1;
   auto id = NewWindowId(sNextId++);
 
   WrAPI* wrApi = nullptr;
   GLint maxTextureSize = 0;
+  bool useANGLE = false;
 
   // Dispatch a synchronous task because the WrApi object needs to be created
   // on the render thread. If need be we could delay waiting on this task until
   // the next time we need to access the WrApi object.
   layers::SynchronousTask task("Create Renderer");
-  auto event = MakeUnique<NewRenderer>(&wrApi, aBridge, &maxTextureSize,
+  auto event = MakeUnique<NewRenderer>(&wrApi, aBridge, &maxTextureSize, &useANGLE,
                                        Move(aWidget), &task, aEnableProfiler);
   RenderThread::Get()->RunEvent(id, Move(event));
 
   task.Wait();
 
   if (!wrApi) {
     return nullptr;
   }
 
-  return RefPtr<WebRenderAPI>(new WebRenderAPI(wrApi, id, maxTextureSize)).forget();
+  return RefPtr<WebRenderAPI>(new WebRenderAPI(wrApi, id, maxTextureSize, useANGLE)).forget();
 }
 
 WebRenderAPI::~WebRenderAPI()
 {
   layers::SynchronousTask task("Destroy WebRenderAPI");
   auto event = MakeUnique<RemoveRenderer>(&task);
   RunOnRenderThread(Move(event));
   task.Wait();
--- a/gfx/webrender_bindings/WebRenderAPI.h
+++ b/gfx/webrender_bindings/WebRenderAPI.h
@@ -66,29 +66,32 @@ public:
   void DeleteFont(wr::FontKey aKey);
 
   void SetProfilerEnabled(bool aEnabled);
 
   void RunOnRenderThread(UniquePtr<RendererEvent> aEvent);
   void Readback(gfx::IntSize aSize, uint8_t *aBuffer, uint32_t aBufferSize);
 
   GLint GetMaxTextureSize() const { return mMaxTextureSize; }
+  bool GetUseANGLE() const { return mUseANGLE; }
 
 protected:
-  WebRenderAPI(WrAPI* aRawApi, wr::WindowId aId, GLint aMaxTextureSize)
+  WebRenderAPI(WrAPI* aRawApi, wr::WindowId aId, GLint aMaxTextureSize, bool aUseANGLE)
     : mWrApi(aRawApi)
     , mId(aId)
     , mMaxTextureSize(aMaxTextureSize)
+    , mUseANGLE(aUseANGLE)
   {}
 
   ~WebRenderAPI();
 
   WrAPI* mWrApi;
   wr::WindowId mId;
   GLint mMaxTextureSize;
+  bool mUseANGLE;
 
   friend class DisplayListBuilder;
 };
 
 /// This is a simple C++ wrapper around WrState defined in the rust bindings.
 /// We may want to turn this into a direct wrapper on top of WebRenderFrameBuilder
 /// instead, so the interface may change a bit.
 class DisplayListBuilder {
--- a/gfx/webrender_bindings/WebRenderTypes.h
+++ b/gfx/webrender_bindings/WebRenderTypes.h
@@ -222,16 +222,24 @@ static inline WrRect ToWrRect(const gfx:
 }
 
 template<class T>
 static inline WrRect ToWrRect(const gfx::IntRectTyped<T>& rect)
 {
   return ToWrRect(IntRectToRect(rect));
 }
 
+static inline WrPoint ToWrPoint(const gfx::Point& point)
+{
+  WrPoint p;
+  p.x = point.x;
+  p.y = point.y;
+  return p;
+}
+
 struct ByteBuffer
 {
   ByteBuffer(size_t aLength, uint8_t* aData)
     : mLength(aLength)
     , mData(aData)
     , mOwned(false)
   {}
 
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -1,14 +1,14 @@
 use std::ffi::CString;
 use std::{mem, slice};
 use std::path::PathBuf;
 use std::os::raw::{c_void, c_char};
 use gleam::gl;
-use webrender_traits::{BorderSide, BorderStyle, BorderRadius};
+use webrender_traits::{BorderSide, BorderStyle, BorderRadius, BorderWidths, BorderDetails, NormalBorder};
 use webrender_traits::{PipelineId, ClipRegion, PropertyBinding};
 use webrender_traits::{Epoch, ExtendMode, ColorF, GlyphInstance, GradientStop, ImageDescriptor};
 use webrender_traits::{FilterOp, ImageData, ImageFormat, ImageKey, ImageMask, ImageRendering, RendererKind, MixBlendMode};
 use webrender_traits::{ExternalImageId, RenderApi, FontKey};
 use webrender_traits::{DeviceUintSize, ExternalEvent};
 use webrender_traits::{LayoutPoint, LayoutRect, LayoutSize, LayoutTransform};
 use webrender_traits::{BoxShadowClipMode, LayerPixel, ServoScrollRootId};
 use webrender::renderer::{Renderer, RendererOptions};
@@ -184,30 +184,20 @@ pub extern fn wr_window_new(window_id: W
     gl::load_with(|symbol| get_proc_address(gl_context, symbol));
     gl::clear_color(0.3, 0.0, 0.0, 1.0);
 
     let version = gl::get_string(gl::VERSION);
 
     println!("WebRender - OpenGL version new {}", version);
 
     let opts = RendererOptions {
-        device_pixel_ratio: 1.0,
-        resource_override_path: None,
         enable_aa: false,
-        enable_subpixel_aa: false,
         enable_profiler: enable_profiler,
-        enable_scrollbars: false,
-        precache_shaders: false,
-        renderer_kind: RendererKind::Native,
-        debug: false,
-        clear_framebuffer: true,
-        render_target_debug: false,
-        clear_color: ColorF::new(1.0, 1.0, 1.0, 1.0),
         recorder: recorder,
-        workers: None,
+        .. Default::default()
     };
 
     let (mut renderer, sender) = match Renderer::new(opts) {
         Ok((renderer, sender)) => { (renderer, sender) }
         Err(e) => {
             println!(" Failed to create a Renderer: {:?}", e);
             return false;
         }
@@ -600,26 +590,29 @@ pub extern fn wr_api_set_root_pipeline(a
     api.set_root_pipeline(pipeline_id);
     api.generate_frame(None);
 }
 
 #[no_mangle]
 pub extern fn wr_api_add_image(api: &mut RenderApi, descriptor: &WrImageDescriptor, bytes: * const u8, size: usize) -> ImageKey {
     assert!( unsafe { is_in_compositor_thread() });
     let bytes = unsafe { slice::from_raw_parts(bytes, size).to_owned() };
-    return api.add_image(
+    let image_key = api.generate_image_key();
+    api.add_image(
+        image_key,
         ImageDescriptor {
             width: descriptor.width,
             height: descriptor.height,
             stride: if descriptor.stride != 0 { Some(descriptor.stride) } else { None },
             format: descriptor.format,
             is_opaque: descriptor.is_opaque,
         },
         ImageData::new(bytes)
     );
+    image_key
 }
 
 #[no_mangle]
 pub extern fn wr_api_add_external_image_texture(api: &mut RenderApi, width: u32, height: u32, format: ImageFormat, external_image_id: u64) -> ImageKey {
     assert!( unsafe { is_in_compositor_thread() });
     unimplemented!(); // TODO
     //api.add_image(ImageDescriptor{width:width, height:height, stride:None, format: format, is_opaque: false}, ImageData::External(ExternalImageId(external_image_id)))
 }
@@ -685,24 +678,34 @@ pub extern fn wr_dp_push_box_shadow(stat
 }
 
 #[no_mangle]
 pub extern fn wr_dp_push_border(state: &mut WrState, rect: WrRect, clip: WrRect,
                                 top: WrBorderSide, right: WrBorderSide, bottom: WrBorderSide, left: WrBorderSide,
                                 radius: WrBorderRadius) {
     assert!( unsafe { is_in_compositor_thread() });
     let clip_region = state.frame_builder.dl_builder.new_clip_region(&clip.to_rect(), Vec::new(), None);
+    let border_widths = BorderWidths {
+        left: left.width,
+        top: top.width,
+        right: right.width,
+        bottom: bottom.width
+    };
+    let border_details = BorderDetails::Normal(NormalBorder {
+        left: left.to_border_side(),
+        right: right.to_border_side(),
+        top: top.to_border_side(),
+        bottom: bottom.to_border_side(),
+        radius: radius.to_border_radius(),
+    });
     state.frame_builder.dl_builder.push_border(
                                     rect.to_rect(),
                                     clip_region,
-                                    left.to_border_side(),
-                                    top.to_border_side(),
-                                    right.to_border_side(),
-                                    bottom.to_border_side(),
-                                    radius.to_border_radius());
+                                    border_widths,
+                                    border_details);
 }
 
 #[no_mangle]
 pub extern fn wr_dp_push_linear_gradient(state: &mut WrState, rect: WrRect, clip: WrRect,
                                          start_point: WrPoint, end_point: WrPoint,
                                          stops: * const WrGradientStop, stops_count: usize,
                                          extend_mode: WrGradientExtendMode) {
     assert!( unsafe { is_in_compositor_thread() });
@@ -798,17 +801,17 @@ pub struct WrBorderSide
     color: WrColor,
     style: BorderStyle
 }
 
 impl WrBorderSide
 {
     pub fn to_border_side(&self) -> BorderSide
     {
-        BorderSide { width: self.width, color: self.color.to_color(), style: self.style }
+        BorderSide { color: self.color.to_color(), style: self.style }
     }
 }
 
 #[repr(C)]
 pub struct WrGradientStop
 {
     offset: f32,
     color: WrColor,
@@ -924,17 +927,19 @@ pub extern fn wr_api_add_raw_font(api: &
     assert!( unsafe { is_in_compositor_thread() });
 
     let font_slice = unsafe {
         slice::from_raw_parts(font_buffer, buffer_size as usize)
     };
     let mut font_vector = Vec::new();
     font_vector.extend_from_slice(font_slice);
 
-    return api.add_raw_font(font_vector);
+    let font_key = api.generate_font_key();
+    api.add_raw_font(font_key, font_vector);
+    font_key
 }
 
 #[no_mangle]
 pub extern fn wr_dp_push_text(state: &mut WrState,
                               bounds: WrRect,
                               clip: WrRect,
                               color: WrColor,
                               font_key: FontKey,
--- a/gfx/webrender_bindings/webrender_ffi.h
+++ b/gfx/webrender_bindings/webrender_ffi.h
@@ -278,16 +278,17 @@ struct WrPoint
 {
   float x;
   float y;
 
   bool operator==(const WrPoint& aRhs) const {
     return x == aRhs.x && y == aRhs.y;
   }
 
+  operator mozilla::gfx::Point() const { return mozilla::gfx::Point(x, y); }
 };
 
 struct WrImageMask
 {
   WrImageKey image;
   WrRect rect;
   bool repeat;
 
--- a/gfx/webrender_traits/Cargo.toml
+++ b/gfx/webrender_traits/Cargo.toml
@@ -1,35 +1,35 @@
 [package]
 name = "webrender_traits"
-version = "0.14.0"
+version = "0.19.0"
 authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
 license = "MPL-2.0"
 repository = "https://github.com/servo/webrender"
 build = "build.rs"
 
 [features]
 default = ["codegen"]
 nightly = ["euclid/unstable", "serde/unstable"]
 codegen = ["serde_codegen", "serde_codegen/with-syntex"]
 ipc = ["ipc-channel"]
 
 [dependencies]
-app_units = "0.3.0"
+app_units = "0.4"
 byteorder = "1.0"
-euclid = "0.10.3"
+euclid = "0.11"
 gleam = "0.2"
 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 = { version = "0.5.0", optional = true }
+ipc-channel = {version = "0.7", optional = true}
+offscreen_gl_context = {version = "0.6", features = ["serde"]}
+serde = "0.9"
+serde_derive = {version = "0.9", optional = true}
 
 [target.'cfg(target_os = "macos")'.dependencies]
-core-graphics = "0.6"
+core-graphics = "0.7"
 
 [target.'cfg(target_os = "windows")'.dependencies]
-dwrote = "0.1.1"
+servo-dwrote = "0.2"
 
 [build-dependencies.serde_codegen]
-version = "0.8"
+version = "0.9"
 default_features = false
 optional = true
--- a/gfx/webrender_traits/src/api.rs
+++ b/gfx/webrender_traits/src/api.rs
@@ -49,59 +49,57 @@ pub struct RenderApi {
     pub next_id: Cell<ResourceId>,
 }
 
 impl RenderApi {
     pub fn clone_sender(&self) -> RenderApiSender {
         RenderApiSender::new(self.api_sender.clone(), self.payload_sender.clone())
     }
 
-    pub fn add_raw_font(&self, bytes: Vec<u8>) -> FontKey {
+    pub fn generate_font_key(&self) -> FontKey {
         let new_id = self.next_unique_id();
-        let key = FontKey::new(new_id.0, new_id.1);
+        FontKey::new(new_id.0, new_id.1)
+    }
+
+    pub fn add_raw_font(&self, key: FontKey, bytes: Vec<u8>) {
         let msg = ApiMsg::AddRawFont(key, bytes);
         self.api_sender.send(msg).unwrap();
-        key
     }
 
-    pub fn add_native_font(&self, native_font_handle: NativeFontHandle) -> FontKey {
-        let new_id = self.next_unique_id();
-        let key = FontKey::new(new_id.0, new_id.1);
+    pub fn add_native_font(&self, key: FontKey, native_font_handle: NativeFontHandle) {
         let msg = ApiMsg::AddNativeFont(key, native_font_handle);
         self.api_sender.send(msg).unwrap();
-        key
     }
 
     /// Gets the dimensions for the supplied glyph keys
     ///
     /// Note: Internally, the internal texture cache doesn't store
     /// 'empty' textures (height or width = 0)
     /// This means that glyph dimensions e.g. for spaces (' ') will mostly be None.
     pub fn get_glyph_dimensions(&self, glyph_keys: Vec<GlyphKey>)
                                 -> Vec<Option<GlyphDimensions>> {
         let (tx, rx) = channel::msg_channel().unwrap();
         let msg = ApiMsg::GetGlyphDimensions(glyph_keys, tx);
         self.api_sender.send(msg).unwrap();
         rx.recv().unwrap()
     }
 
     /// Creates an `ImageKey`.
-    pub fn alloc_image(&self) -> ImageKey {
+    pub fn generate_image_key(&self) -> ImageKey {
         let new_id = self.next_unique_id();
         ImageKey::new(new_id.0, new_id.1)
     }
 
-    /// Adds an image and returns the corresponding `ImageKey`.
+    /// Adds an image identified by the `ImageKey`.
     pub fn add_image(&self,
+                     key: ImageKey,
                      descriptor: ImageDescriptor,
-                     data: ImageData) -> ImageKey {
-        let key = self.alloc_image();
+                     data: ImageData) {
         let msg = ApiMsg::AddImage(key, descriptor, data);
         self.api_sender.send(msg).unwrap();
-        key
     }
 
     /// 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,
--- a/gfx/webrender_traits/src/channel_mpsc.rs
+++ b/gfx/webrender_traits/src/channel_mpsc.rs
@@ -62,32 +62,32 @@ pub fn msg_channel<T>() -> Result<(MsgSe
 /// recording tool. The recording tool only outputs messages
 /// that don't contain Senders or Receivers, so in theory
 /// these should never be called in the in-process config.
 /// If they are called, there may be a bug in the messages
 /// that the replay tool is writing.
 ///
 
 impl<T> Serialize for MsgReceiver<T> {
-    fn serialize<S: Serializer>(&self, _: &mut S) -> Result<(), S::Error> {
+    fn serialize<S: Serializer>(&self, _: S) -> Result<S::Ok, S::Error> {
         unreachable!();
     }
 }
 
 impl<T> Serialize for MsgSender<T> {
-    fn serialize<S: Serializer>(&self, _: &mut S) -> Result<(), S::Error> {
+    fn serialize<S: Serializer>(&self, _: S) -> Result<S::Ok, S::Error> {
         unreachable!();
     }
 }
 
 impl<T> Deserialize for MsgReceiver<T> {
-    fn deserialize<D>(_: &mut D) -> Result<MsgReceiver<T>, D::Error>
+    fn deserialize<D>(_: D) -> Result<MsgReceiver<T>, D::Error>
                       where D: Deserializer {
         unreachable!();
     }
 }
 
 impl<T> Deserialize for MsgSender<T> {
-    fn deserialize<D>(_: &mut D) -> Result<MsgSender<T>, D::Error>
+    fn deserialize<D>(_: D) -> Result<MsgSender<T>, D::Error>
                       where D: Deserializer {
         unreachable!();
     }
 }
--- a/gfx/webrender_traits/src/display_item.rs
+++ b/gfx/webrender_traits/src/display_item.rs
@@ -1,40 +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 display_list::AuxiliaryListsBuilder;
-use {BorderRadius, BorderDisplayItem, ClipRegion, ColorF, ComplexClipRegion};
+use {BorderRadius, ClipRegion, ColorF, ComplexClipRegion};
 use {FontKey, ImageKey, PipelineId, ScrollLayerId, ScrollLayerInfo, ServoScrollRootId};
 use {ImageMask, ItemRange};
 use {LayoutSize, LayoutPoint, LayoutRect};
 
-impl BorderDisplayItem {
-    pub fn top_left_inner_radius(&self) -> LayoutSize {
-        LayoutSize::new((self.radius.top_left.width - self.left.width).max(0.0),
-                     (self.radius.top_left.height - self.top.width).max(0.0))
-    }
-
-    pub fn top_right_inner_radius(&self) -> LayoutSize {
-        LayoutSize::new((self.radius.top_right.width - self.right.width).max(0.0),
-                     (self.radius.top_right.height - self.top.width).max(0.0))
-    }
-
-    pub fn bottom_left_inner_radius(&self) -> LayoutSize {
-        LayoutSize::new((self.radius.bottom_left.width - self.left.width).max(0.0),
-                     (self.radius.bottom_left.height - self.bottom.width).max(0.0))
-    }
-
-    pub fn bottom_right_inner_radius(&self) -> LayoutSize {
-        LayoutSize::new((self.radius.bottom_right.width - self.right.width).max(0.0),
-                     (self.radius.bottom_right.height - self.bottom.width).max(0.0))
-    }
-}
-
 impl BorderRadius {
     pub fn zero() -> BorderRadius {
         BorderRadius {
             top_left: LayoutSize::new(0.0, 0.0),
             top_right: LayoutSize::new(0.0, 0.0),
             bottom_left: LayoutSize::new(0.0, 0.0),
             bottom_right: LayoutSize::new(0.0, 0.0),
         }
@@ -44,30 +22,46 @@ impl BorderRadius {
         BorderRadius {
             top_left: LayoutSize::new(radius, radius),
             top_right: LayoutSize::new(radius, radius),
             bottom_left: LayoutSize::new(radius, radius),
             bottom_right: LayoutSize::new(radius, radius),
         }
     }
 
-    pub fn is_uniform(&self) -> Option<LayoutSize> {
+    pub fn uniform_size(radius: LayoutSize) -> BorderRadius {
+        BorderRadius {
+            top_left: radius,
+            top_right: radius,
+            bottom_left: radius,
+            bottom_right: radius,
+        }
+    }
+
+    pub fn is_uniform(&self) -> Option<f32> {
+        match self.is_uniform_size() {
+            Some(radius) if radius.width == radius.height => Some(radius.width),
+            _ => None
+        }
+    }
+
+    pub fn is_uniform_size(&self) -> Option<LayoutSize> {
         let uniform_radius = self.top_left;
         if self.top_right == uniform_radius &&
            self.bottom_left == uniform_radius &&
            self.bottom_right == uniform_radius {
             Some(uniform_radius)
         } else {
             None
         }
     }
 
     pub fn is_zero(&self) -> bool {
         if let Some(radius) = self.is_uniform() {
-            radius.width == 0.0 && radius.height == 0.0
+            radius == 0.0
         } else {
             false
         }
     }
 }
 
 impl ClipRegion {
     pub fn new(rect: &LayoutRect,
--- a/gfx/webrender_traits/src/display_list.rs
+++ b/gfx/webrender_traits/src/display_list.rs
@@ -1,26 +1,26 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use app_units::Au;
 use std::mem;
 use std::slice;
-use {AuxiliaryLists, AuxiliaryListsDescriptor, BorderDisplayItem, BorderRadius};
-use {BorderSide, BoxShadowClipMode, BoxShadowDisplayItem, BuiltDisplayList};
+use {AuxiliaryLists, AuxiliaryListsDescriptor, BorderDisplayItem};
+use {BoxShadowClipMode, BoxShadowDisplayItem, BuiltDisplayList};
 use {BuiltDisplayListDescriptor, ClipRegion, ComplexClipRegion, ColorF};
 use {DisplayItem, DisplayListMode, ExtendMode, FilterOp, YuvColorSpace};
 use {FontKey, GlyphInstance, GradientDisplayItem, RadialGradientDisplayItem, GradientStop, IframeDisplayItem};
 use {ImageDisplayItem, ImageKey, ImageMask, ImageRendering, ItemRange, MixBlendMode, PipelineId};
 use {PushScrollLayerItem, PushStackingContextDisplayItem, RectangleDisplayItem, ScrollLayerId};
 use {ScrollPolicy, ServoScrollRootId, SpecificDisplayItem, StackingContext, TextDisplayItem};
 use {WebGLContextId, WebGLDisplayItem, YuvImageDisplayItem};
 use {LayoutTransform, LayoutPoint, LayoutRect, LayoutSize};
-use {GlyphOptions, PropertyBinding};
+use {BorderDetails, BorderWidths, GlyphOptions, PropertyBinding};
 
 impl BuiltDisplayListDescriptor {
     pub fn size(&self) -> usize {
         self.display_list_items_size + self.display_items_size
     }
 }
 
 impl BuiltDisplayList {
@@ -181,27 +181,21 @@ impl DisplayListBuilder {
 
             self.list.push(display_item);
         }
     }
 
     pub fn push_border(&mut self,
                        rect: LayoutRect,
                        clip: ClipRegion,
-                       left: BorderSide,
-                       top: BorderSide,
-                       right: BorderSide,
-                       bottom: BorderSide,
-                       radius: BorderRadius) {
+                       widths: BorderWidths,
+                       details: BorderDetails) {
         let item = BorderDisplayItem {
-            left: left,
-            top: top,
-            right: right,
-            bottom: bottom,
-            radius: radius,
+            details: details,
+            widths: widths,
         };
 
         let display_item = DisplayItem {
             item: SpecificDisplayItem::Border(item),
             rect: rect,
             clip: clip,
         };
 
--- a/gfx/webrender_traits/src/types.rs
+++ b/gfx/webrender_traits/src/types.rs
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // Every serialisable type is defined in this file to only codegen one file
 // for the serde implementations.
 
 use app_units::Au;
-use euclid::Point2D;
+use euclid::{Point2D, SideOffsets2D};
 use channel::{PayloadSender, MsgSender};
 #[cfg(feature = "nightly")]
 use core::nonzero::NonZero;
 use offscreen_gl_context::{GLContextAttributes, GLLimits};
 use std::fmt;
 use std::marker::PhantomData;
 use std::sync::Arc;
 
@@ -170,36 +170,79 @@ pub struct AuxiliaryLists {
 pub struct AuxiliaryListsDescriptor {
     gradient_stops_size: usize,
     complex_clip_regions_size: usize,
     filters_size: usize,
     glyph_instances_size: usize,
 }
 
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct BorderDisplayItem {
+pub struct NormalBorder {
     pub left: BorderSide,
     pub right: BorderSide,
     pub top: BorderSide,
     pub bottom: BorderSide,
     pub radius: BorderRadius,
 }
 
+#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
+pub enum RepeatMode {
+    Stretch,
+    Repeat,
+    Round,
+    Space,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct NinePatchDescriptor {
+    pub width: u32,
+    pub height: u32,
+    pub slice: SideOffsets2D<u32>,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct ImageBorder {
+    pub image_key: ImageKey,
+    pub patch: NinePatchDescriptor,
+    pub outset: SideOffsets2D<f32>,
+    pub repeat_horizontal: RepeatMode,
+    pub repeat_vertical: RepeatMode,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub enum BorderDetails {
+    Normal(NormalBorder),
+    Image(ImageBorder),
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct BorderDisplayItem {
+    pub widths: BorderWidths,
+    pub details: BorderDetails,
+}
+
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct BorderRadius {
     pub top_left: LayoutSize,
     pub top_right: LayoutSize,
     pub bottom_left: LayoutSize,
     pub bottom_right: LayoutSize,
 }
 
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct BorderWidths {
+    pub left: f32,
+    pub top: f32,
+    pub right: f32,
+    pub bottom: f32,
+}
+
 #[repr(C)]
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct BorderSide {
-    pub width: f32,
     pub color: ColorF,
     pub style: BorderStyle,
 }
 
 #[repr(u32)]
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub enum BorderStyle {
     None    = 0,
@@ -600,31 +643,74 @@ pub enum YuvColorSpace {
 }
 
 /// An arbitrary identifier for an external image provided by the
 /// application. It must be a unique identifier for each external
 /// image.
 #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
 pub struct ExternalImageId(pub u64);
 
+pub trait BlobImageRenderer: Send {
+    fn request_blob_image(&mut self,
+                            key: ImageKey,
+                            data: Arc<BlobImageData>,
+                            descriptor: &BlobImageDescriptor);
+    fn resolve_blob_image(&mut self, key: ImageKey) -> BlobImageResult;
+}
+
+pub type BlobImageData = Vec<u8>;
+
+#[derive(Copy, Clone, Debug)]
+pub struct BlobImageDescriptor {
+    pub width: u32,
+    pub height: u32,
+    pub format: ImageFormat,
+    pub scale_factor: f32,
+}
+
+pub struct RasterizedBlobImage {
+    pub width: u32,
+    pub height: u32,
+    pub data: Vec<u8>,
+}
+
+#[derive(Clone, Debug)]
+pub enum BlobImageError {
+    Oom,
+    InvalidKey,
+    InvalidData,
+    Other(String),
+}
+
+pub type BlobImageResult = Result<RasterizedBlobImage, BlobImageError>;
+
 #[derive(Clone, Serialize, Deserialize)]
 pub enum ImageData {
     Raw(Arc<Vec<u8>>),
+    Blob(Arc<BlobImageData>),
     ExternalHandle(ExternalImageId),
     ExternalBuffer(ExternalImageId),
 }
 
 impl ImageData {
     pub fn new(bytes: Vec<u8>) -> ImageData {
         ImageData::Raw(Arc::new(bytes))
     }
 
     pub fn new_shared(bytes: Arc<Vec<u8>>) -> ImageData {
         ImageData::Raw(bytes)
     }
+
+    pub fn new_blob_image(commands: Vec<u8>) -> ImageData {
+        ImageData::Blob(Arc::new(commands))
+    }
+
+    pub fn new_shared_blob_image(commands: Arc<Vec<u8>>) -> ImageData {
+        ImageData::Blob(commands)
+    }
 }
 
 #[repr(C)]
 #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
 pub struct ImageKey(pub u32, pub u32);
 
 #[repr(u32)]
 #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
@@ -982,30 +1068,30 @@ macro_rules! define_resource_id_struct {
     };
 }
 
 macro_rules! define_resource_id {
     ($name:ident) => {
         define_resource_id_struct!($name);
 
         impl ::serde::Deserialize for $name {
-            fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
+            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
                 where D: ::serde::Deserializer
             {
                 let id = try!(u32::deserialize(deserializer));
                 if id == 0 {
-                    Err(::serde::Error::invalid_value("expected a non-zero value"))
+                    Err(::serde::de::Error::custom("expected a non-zero value"))
                 } else {
                     Ok(unsafe { $name::new(id) })
                 }
             }
         }
 
         impl ::serde::Serialize for $name {
-            fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
+            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
                 where S: ::serde::Serializer
             {
                 self.get().serialize(serializer)
             }
         }
 
         impl ::std::fmt::Debug for $name {
             fn fmt(&self, fmt: &mut ::std::fmt::Formatter)
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -4712,20 +4712,20 @@ PresShell::RenderDocument(const nsRect& 
     flags |= PaintFrameFlags::PAINT_SYNC_DECODE_IMAGES;
   }
   if (aFlags & RENDER_USE_WIDGET_LAYERS) {
     // We only support using widget layers on display root's with widgets.
     nsView* view = rootFrame->GetView();
     if (view && view->GetWidget() &&
         nsLayoutUtils::GetDisplayRootFrame(rootFrame) == rootFrame) {
       LayerManager* layerManager = view->GetWidget()->GetLayerManager();
-      // ClientLayerManagers in content processes don't support
-      // taking snapshots.
+      // ClientLayerManagers or WebRenderLayerManagers in content processes
+      // don't support taking snapshots.
       if (layerManager &&
-          (!layerManager->AsClientLayerManager() ||
+          (!layerManager->AsKnowsCompositor() ||
            XRE_IsParentProcess())) {
         flags |= PaintFrameFlags::PAINT_WIDGET_LAYERS;
       }
     }
   }
   if (!(aFlags & RENDER_CARET)) {
     wouldFlushRetainedLayers = true;
     flags |= PaintFrameFlags::PAINT_HIDE_CARET;
--- a/layout/painting/nsCSSRendering.cpp
+++ b/layout/painting/nsCSSRendering.cpp
@@ -1364,49 +1364,85 @@ nsCSSRendering::EndFrameTreesLocked()
 {
   NS_ASSERTION(gFrameTreeLockCount > 0, "Unbalanced EndFrameTreeLocked");
   --gFrameTreeLockCount;
   if (gFrameTreeLockCount == 0) {
     gInlineBGData->Reset();
   }
 }
 
+bool
+nsCSSRendering::HasBoxShadowNativeTheme(nsIFrame* aFrame,
+                                        bool& aMaybeHasBorderRadius)
+{
+  const nsStyleDisplay* styleDisplay = aFrame->StyleDisplay();
+  nsITheme::Transparency transparency;
+  if (aFrame->IsThemed(styleDisplay, &transparency)) {
+    aMaybeHasBorderRadius = false;
+    // For opaque (rectangular) theme widgets we can take the generic
+    // border-box path with border-radius disabled.
+    return transparency != nsITheme::eOpaque;
+  }
+
+  aMaybeHasBorderRadius = true;
+  return false;
+}
+
+gfx::Color
+nsCSSRendering::GetShadowColor(nsCSSShadowItem* aShadow,
+                               nsIFrame* aFrame,
+                               float aOpacity)
+{
+  // Get the shadow color; if not specified, use the foreground color
+  nscolor shadowColor;
+  if (aShadow->mHasColor)
+    shadowColor = aShadow->mColor;
+  else
+    shadowColor = aFrame->StyleColor()->mColor;
+
+  Color color = Color::FromABGR(shadowColor);
+  color.a *= aOpacity;
+  return color;
+}
+
+nsRect
+nsCSSRendering::GetShadowRect(const nsRect aFrameArea,
+                              bool aNativeTheme,
+                              nsIFrame* aForFrame)
+{
+  nsRect frameRect = aNativeTheme ?
+    aForFrame->GetVisualOverflowRectRelativeToSelf() + aFrameArea.TopLeft() :
+    aFrameArea;
+  Sides skipSides = aForFrame->GetSkipSides();
+  frameRect = ::BoxDecorationRectForBorder(aForFrame, frameRect, skipSides);
+
+  // Explicitly do not need to account for the spread radius here
+  // Webrender does it for us or PaintBoxShadow will for non-WR
+  return frameRect;
+}
+
 void
 nsCSSRendering::PaintBoxShadowOuter(nsPresContext* aPresContext,
                                     nsRenderingContext& aRenderingContext,
                                     nsIFrame* aForFrame,
                                     const nsRect& aFrameArea,
                                     const nsRect& aDirtyRect,
                                     float aOpacity)
 {
   DrawTarget& aDrawTarget = *aRenderingContext.GetDrawTarget();
   nsCSSShadowArray* shadows = aForFrame->StyleEffects()->mBoxShadow;
   if (!shadows)
     return;
 
   bool hasBorderRadius;
-  bool nativeTheme; // mutually exclusive with hasBorderRadius
+  // mutually exclusive with hasBorderRadius
+  bool nativeTheme = HasBoxShadowNativeTheme(aForFrame, hasBorderRadius);
   const nsStyleDisplay* styleDisplay = aForFrame->StyleDisplay();
-  nsITheme::Transparency transparency;
-  if (aForFrame->IsThemed(styleDisplay, &transparency)) {
-    // We don't respect border-radius for native-themed widgets
-    hasBorderRadius = false;
-    // For opaque (rectangular) theme widgets we can take the generic
-    // border-box path with border-radius disabled.
-    nativeTheme = transparency != nsITheme::eOpaque;
-  } else {
-    nativeTheme = false;
-    hasBorderRadius = true; // we'll update this below
-  }
-
-  nsRect frameRect = nativeTheme ?
-    aForFrame->GetVisualOverflowRectRelativeToSelf() + aFrameArea.TopLeft() :
-    aFrameArea;
-  Sides skipSides = aForFrame->GetSkipSides();
-  frameRect = ::BoxDecorationRectForBorder(aForFrame, frameRect, skipSides);
+
+  nsRect frameRect = GetShadowRect(aFrameArea, nativeTheme, aForFrame);
 
   // Get any border radius, since box-shadow must also have rounded corners if
   // the frame does.
   RectCornerRadii borderRadii;
   const nscoord twipsPerPixel = aPresContext->DevPixelsToAppUnits(1);
   if (hasBorderRadius) {
     nscoord twipsRadii[8];
     NS_ASSERTION(aFrameArea.Size() == aForFrame->VisualBorderRectRelativeToSelf().Size(),
@@ -1460,25 +1496,17 @@ nsCSSRendering::PaintBoxShadowOuter(nsPr
     shadowRectPlusBlur.Inflate(
       nsContextBoxBlur::GetBlurRadiusMargin(blurRadius, twipsPerPixel));
 
     Rect shadowGfxRectPlusBlur =
       NSRectToRect(shadowRectPlusBlur, twipsPerPixel);
     shadowGfxRectPlusBlur.RoundOut();
     MaybeSnapToDevicePixels(shadowGfxRectPlusBlur, aDrawTarget, true);
 
-    // Set the shadow color; if not specified, use the foreground color
-    nscolor shadowColor;
-    if (shadowItem->mHasColor)
-      shadowColor = shadowItem->mColor;
-    else
-      shadowColor = aForFrame->StyleColor()->mColor;
-
-    Color gfxShadowColor(Color::FromABGR(shadowColor));
-    gfxShadowColor.a *= aOpacity;
+    Color gfxShadowColor = GetShadowColor(shadowItem, aForFrame, aOpacity);
 
     if (nativeTheme) {
       nsContextBoxBlur blurringArea;
 
       // When getting the widget shape from the native theme, we're going
       // to draw the widget into the shadow surface to create a mask.
       // We need to ensure that there actually *is* a shadow surface
       // and that we're not going to draw directly into renderContext.
@@ -1542,16 +1570,17 @@ nsCSSRendering::PaintBoxShadowOuter(nsPr
           AppendRectToPath(builder, innerClipRect);
         }
         RefPtr<Path> path = builder->Finish();
         renderContext->Clip(path);
       }
 
       // Clip the shadow so that we only get the part that applies to aForFrame.
       nsRect fragmentClip = shadowRectPlusBlur;
+      Sides skipSides = aForFrame->GetSkipSides();
       if (!skipSides.IsEmpty()) {
         if (skipSides.Left()) {
           nscoord xmost = fragmentClip.XMost();
           fragmentClip.x = aFrameArea.x;
           fragmentClip.width = xmost - fragmentClip.x;
         }
         if (skipSides.Right()) {
           nscoord xmost = fragmentClip.XMost();
@@ -1735,21 +1764,17 @@ nsCSSRendering::PaintBoxShadowInner(nsPr
     DrawTarget* drawTarget = renderContext->GetDrawTarget();
 
     // Clip the context to the area of the frame's padding rect, so no part of the
     // shadow is painted outside. Also cut out anything beyond where the inset shadow
     // will be.
     Rect shadowGfxRect = NSRectToRect(paddingRect, twipsPerPixel);
     shadowGfxRect.Round();
 
-    // Set the shadow color; if not specified, use the foreground color
-    Color shadowColor = Color::FromABGR(shadowItem->mHasColor ?
-                                          shadowItem->mColor :
-                                          aForFrame->StyleColor()->mColor);
-
+    Color shadowColor = GetShadowColor(shadowItem, aForFrame, 1.0);
     renderContext->Save();
 
     // This clips the outside border radius.
     // clipRectRadii is the border radius inside the inset shadow.
     if (hasBorderRadius) {
       RefPtr<Path> roundedRect =
         MakePathForRoundedRect(*drawTarget, shadowGfxRect, innerRadii);
       renderContext->Clip(roundedRect);
--- a/layout/painting/nsCSSRendering.h
+++ b/layout/painting/nsCSSRendering.h
@@ -349,16 +349,17 @@ struct nsBackgroundLayerState {
   /**
    * The background-repeat property space keyword computes the
    * repeat size which is image size plus spacing.
    */
   nsSize mRepeatSize;
 };
 
 struct nsCSSRendering {
+  typedef mozilla::gfx::Color Color;
   typedef mozilla::gfx::CompositionOp CompositionOp;
   typedef mozilla::gfx::DrawTarget DrawTarget;
   typedef mozilla::gfx::Float Float;
   typedef mozilla::gfx::Point Point;
   typedef mozilla::gfx::Rect Rect;
   typedef mozilla::gfx::Size Size;
   typedef mozilla::gfx::RectCornerRadii RectCornerRadii;
   typedef mozilla::image::DrawResult DrawResult;
@@ -374,16 +375,27 @@ struct nsCSSRendering {
    */
   static void Shutdown();
   
   static void PaintBoxShadowInner(nsPresContext* aPresContext,
                                   nsRenderingContext& aRenderingContext,
                                   nsIFrame* aForFrame,
                                   const nsRect& aFrameArea);
 
+  static nsRect GetShadowRect(const nsRect aFrameArea,
+                              bool aNativeTheme,
+                              nsIFrame* aForFrame);
+  static mozilla::gfx::Color GetShadowColor(nsCSSShadowItem* aShadow,
+                                   nsIFrame* aFrame,
+                                   float aOpacity);
+  // Returns if the frame has a themed frame.
+  // aMaybeHasBorderRadius will return false if we can early detect
+  // that we don't have a border radius.
+  static bool HasBoxShadowNativeTheme(nsIFrame* aFrame,
+                                      bool& aMaybeHasBorderRadius);
   static void PaintBoxShadowOuter(nsPresContext* aPresContext,
                                   nsRenderingContext& aRenderingContext,
                                   nsIFrame* aForFrame,
                                   const nsRect& aFrameArea,
                                   const nsRect& aDirtyRect,
                                   float aOpacity = 1.0);
 
   static void ComputePixelRadii(const nscoord *aAppUnitsRadii,
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -4766,55 +4766,64 @@ nsDisplayBoxShadowOuter::CreateWebRender
   nsRegion visible = aLayer->GetVisibleRegion().ToAppUnits(appUnitsPerDevPixel);
 
   ComputeDisjointRectangles(visible, &rects);
 
   nsCSSShadowArray* shadows = mFrame->StyleEffects()->mBoxShadow;
   if (!shadows)
     return;
 
+  bool hasBorderRadius;
+  bool nativeTheme = nsCSSRendering::HasBoxShadowNativeTheme(mFrame,
+                                                             hasBorderRadius);
+
   // Everything here is in app units, change to device units.
   for (uint32_t i = 0; i < rects.Length(); ++i) {
     Rect clipRect = NSRectToRect(rects[i], appUnitsPerDevPixel);
-    Rect gfxBorderRect = NSRectToRect(borderRect, appUnitsPerDevPixel);
-
-    Rect deviceClipRect = aLayer->RelativeToParent(clipRect);
-    Rect deviceBoxRect = aLayer->RelativeToParent(gfxBorderRect);
-
-    for (uint32_t j = shadows->Length(); j > 0; --j) {
-      nsCSSShadowItem* shadowItem = shadows->ShadowAt(j - 1);
-      nscoord blurRadius = shadowItem->mRadius;
-      float gfxBlurRadius = blurRadius / appUnitsPerDevPixel;
-
-      // TODO: Have to refactor our nsCSSRendering
-      // to get the acual rects correct.
-      nscolor shadowColor;
-      if (shadowItem->mHasColor)
-        shadowColor = shadowItem->mColor;
-      else
-        shadowColor = mFrame->StyleColor()->mColor;
-
-      Color gfxShadowColor(Color::FromABGR(shadowColor));
-      gfxShadowColor.a *= mOpacity;
-
-      WrPoint offset;
-      offset.x = shadowItem->mXOffset;
-      offset.y = shadowItem->mYOffset;
+    nsCSSShadowArray* shadows = mFrame->StyleEffects()->mBoxShadow;
+
+    for (uint32_t j = shadows->Length(); j  > 0; j--) {
+      nsCSSShadowItem* shadow = shadows->ShadowAt(j - 1);
+      // Don't need the full size of the shadow rect like we do in
+      // nsCSSRendering since WR takes care of calculations for blur
+      // and spread radius.
+      nsRect shadowRect = nsCSSRendering::GetShadowRect(borderRect,
+                                                        nativeTheme,
+                                                        mFrame);
+      gfx::Color shadowColor = nsCSSRendering::GetShadowColor(shadow,
+                                                              mFrame,
+                                                              mOpacity);
+      shadowRect.MoveBy(shadow->mXOffset, shadow->mYOffset);
+
+      // Now translate everything to device pixels.
+      Point shadowOffset;
+      shadowOffset.x = (shadow->mXOffset / appUnitsPerDevPixel);
+      shadowOffset.y = (shadow->mYOffset / appUnitsPerDevPixel);
+
+      Rect deviceBoxRect = NSRectToRect(shadowRect, appUnitsPerDevPixel);
+      deviceBoxRect = aLayer->RelativeToParent(deviceBoxRect);
+
+      Rect deviceClipRect = aLayer->RelativeToParent(clipRect + shadowOffset);
+
+      float blurRadius = shadow->mRadius / appUnitsPerDevPixel;
+      // TODO: Calculate the border radius here.
+      float borderRadius = 0.0;
+      float spreadRadius = shadow->mSpread / appUnitsPerDevPixel;
 
       aCommands.AppendElement(OpDPPushBoxShadow(
                               wr::ToWrRect(deviceBoxRect),
                               wr::ToWrRect(deviceClipRect),
                               wr::ToWrRect(deviceBoxRect),
-                              offset,
-                              wr::ToWrColor(gfxShadowColor),
-                              gfxBlurRadius,
-                              0,
-                              0,
+                              wr::ToWrPoint(shadowOffset),
+                              wr::ToWrColor(shadowColor),
+                              blurRadius,
+                              spreadRadius,
+                              borderRadius,
                               WrBoxShadowClipMode::Outset
-      ));
+                              ));
     }
   }
 }
 
 void
 nsDisplayBoxShadowOuter::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                                    const nsDisplayItemGeometry* aGeometry,
                                                    nsRegion* aInvalidRegion)
deleted file mode 100644
--- a/third_party/rust/app_units-0.3.0/.cargo-checksum.json
+++ /dev/null
@@ -1,1 +0,0 @@
-{"files":{".cargo-ok":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",".gitignore":"12cc0f91b51fedf41ae1670d1624ee1d78a284bdb101645b60a06a12de16c069",".travis.yml":"6b96b2c6bfd7e1acef4b825a2813fc4277859eb9400a16800db8835c25e4087d","Cargo.toml":"789b93a48ce76901375209d5462408469c31809e09a98e71370c57187a4b0923","README.md":"9f048d969f9f8333cdcdb892744cd0816e4f2922c8817fa5e9e07f9472fe1050","src/app_unit.rs":"71b0ac2fa378427883649def1a03008ac9d4eb45addd084b7d9885867049551e","src/lib.rs":"2df7d863c47d8b22f9af66caeafa87e6a206ee713a8aeaa55c5a80a42a92513b"},"package":"636ee56f12e31dbc11dc0a1ac6004f08b04e6e6595963716fc8130e90d4e04cf"}
\ No newline at end of file
deleted file mode 100644
deleted file mode 100644
--- a/third_party/rust/app_units-0.3.0/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-target/
-Cargo.lock
deleted file mode 100644
--- a/third_party/rust/app_units-0.3.0/.travis.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-language: rust
-notifications:
-  webhooks: http://build.servo.org:54856/travis
-
-rust:
-  - stable
-  - beta
-  - nightly
deleted file mode 100644
--- a/third_party/rust/app_units-0.3.0/Cargo.toml
+++ /dev/null
@@ -1,18 +0,0 @@
-[package]
-name = "app_units"
-version = "0.3.0"
-authors = ["The Servo Project Developers"]
-description = "Servo app units type (Au)"
-documentation = "http://doc.servo.org/app_units/"
-repository = "https://github.com/servo/app_units"
-license = "MPL-2.0"
-
-[features]
-default = []
-plugins = []
-
-[dependencies]
-heapsize = "0.3"
-num-traits = "0.1.32"
-rustc-serialize = "0.3"
-serde = "0.8"
deleted file mode 100644
--- a/third_party/rust/app_units-0.3.0/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# app-units
-
-[Documentation](http://doc.servo.org/app_units/index.html)
deleted file mode 100644
--- a/third_party/rust/app_units-0.3.0/src/app_unit.rs
+++ /dev/null
@@ -1,315 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-use heapsize::HeapSizeOf;
-use num_traits::Zero;
-use rustc_serialize::{Encodable, Encoder};
-use serde::de::{Deserialize, Deserializer};
-use serde::ser::{Serialize, Serializer};
-use std::default::Default;
-use std::fmt;
-use std::i32;
-use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, Sub, SubAssign};
-
-/// The number of app units in a pixel.
-pub const AU_PER_PX: i32 = 60;
-
-#[derive(Clone, Copy, Hash, PartialEq, PartialOrd, Eq, Ord)]
-pub struct Au(pub i32);
-
-impl HeapSizeOf for Au {
-    fn heap_size_of_children(&self) -> usize { 0 }
-}
-
-impl Deserialize for Au {
-    fn deserialize<D: Deserializer>(deserializer: &mut D) -> Result<Au, D::Error> {
-        Ok(Au(try!(i32::deserialize(deserializer))))
-    }
-}
-
-impl Serialize for Au {
-    fn serialize<S: Serializer>(&self, serializer: &mut S) -> Result<(), S::Error> {
-        self.0.serialize(serializer)
-    }
-}
-
-impl Default for Au {
-    #[inline]
-    fn default() -> Au {
-        Au(0)
-    }
-}
-
-impl Zero for Au {
-    #[inline]
-    fn zero() -> Au {
-        Au(0)
-    }
-
-    #[inline]
-    fn is_zero(&self) -> bool {
-        self.0 == 0
-    }
-}
-
-pub const MIN_AU: Au = Au(i32::MIN);
-pub const MAX_AU: Au = Au(i32::MAX);
-
-impl Encodable for Au {
-    fn encode<S: Encoder>(&self, e: &mut S) -> Result<(), S::Error> {
-        e.emit_f64(self.to_f64_px())
-    }
-}
-
-impl fmt::Debug for Au {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "{}px", self.to_f64_px())
-    }
-}
-
-impl Add for Au {
-    type Output = Au;
-
-    #[inline]
-    fn add(self, other: Au) -> Au {
-        Au(self.0.wrapping_add(other.0))
-    }
-}
-
-impl Sub for Au {
-    type Output = Au;
-
-    #[inline]
-    fn sub(self, other: Au) -> Au {
-        Au(self.0.wrapping_sub(other.0))
-    }
-
-}
-
-impl Mul<i32> for Au {
-    type Output = Au;
-
-    #[inline]
-    fn mul(self, other: i32) -> Au {
-        Au(self.0.wrapping_mul(other))
-    }
-}
-
-impl Div<i32> for Au {
-    type Output = Au;
-
-    #[inline]
-    fn div(self, other: i32) -> Au {
-        Au(self.0 / other)
-    }
-}
-
-impl Rem<i32> for Au {
-    type Output = Au;
-
-    #[inline]
-    fn rem(self, other: i32) -> Au {
-        Au(self.0 % other)
-    }
-}
-
-impl Neg for Au {
-    type Output = Au;
-
-    #[inline]
-    fn neg(self) -> Au {
-        Au(-self.0)
-    }
-}
-
-impl AddAssign for Au {
-    #[inline]
-    fn add_assign(&mut self, other: Au) {
-        *self = *self + other;
-    }
-}
-
-impl SubAssign for Au {
-    #[inline]
-    fn sub_assign(&mut self, other: Au) {
-        *self = *self - other;
-    }
-}
-
-impl MulAssign<i32> for Au {
-    #[inline]
-    fn mul_assign(&mut self, other: i32) {
-        *self = *self * other;
-    }
-}
-
-impl DivAssign<i32> for Au {
-    #[inline]
-    fn div_assign(&mut self, other: i32) {
-        *self = *self / other;
-    }
-}
-
-impl Au {
-    /// FIXME(pcwalton): Workaround for lack of cross crate inlining of newtype structs!
-    #[inline]
-    pub fn new(value: i32) -> Au {
-        Au(value)
-    }
-
-    #[inline]
-    pub fn scale_by(self, factor: f32) -> Au {
-        Au(((self.0 as f32) * factor) as i32)
-    }
-
-    #[inline]
-    pub fn from_px(px: i32) -> Au {
-        Au((px * AU_PER_PX) as i32)
-    }
-
-    /// Rounds this app unit down to the pixel towards zero and returns it.
-    #[inline]
-    pub fn to_px(self) -> i32 {
-        self.0 / AU_PER_PX
-    }
-
-    /// Ceil this app unit to the appropriate pixel boundary and return it.
-    #[inline]
-    pub fn ceil_to_px(self) -> i32 {
-        ((self.0 as f64) / (AU_PER_PX as f64)).ceil() as i32
-    }
-
-    #[inline]
-    pub fn to_nearest_px(self) -> i32 {
-        ((self.0 as f64) / (AU_PER_PX as f64)).round() as i32
-    }
-
-    #[inline]
-    pub fn to_nearest_pixel(self, pixels_per_px: f32) -> f32 {
-        ((self.0 as f32) / (AU_PER_PX as f32) * pixels_per_px).round() / pixels_per_px
-    }
-
-    #[inline]
-    pub fn to_f32_px(self) -> f32 {
-        (self.0 as f32) / (AU_PER_PX as f32)
-    }
-
-    #[inline]
-    pub fn to_f64_px(self) -> f64 {
-        (self.0 as f64) / (AU_PER_PX as f64)
-    }
-
-    #[inline]
-    pub fn from_f32_px(px: f32) -> Au {
-        Au((px * (AU_PER_PX as f32)) as i32)
-    }
-
-    #[inline]
-    pub fn from_f64_px(px: f64) -> Au {
-        Au((px * (AU_PER_PX as f64)) as i32)
-    }
-}
-
-#[test]
-fn create() {
-    assert_eq!(Au::zero(), Au(0));
-    assert_eq!(Au::default(), Au(0));
-    assert_eq!(Au::new(7), Au(7));
-}
-
-#[test]
-fn operations() {
-    assert_eq!(Au(7) + Au(5), Au(12));
-    assert_eq!(MAX_AU + Au(1), MIN_AU);
-
-    assert_eq!(Au(7) - Au(5), Au(2));
-    assert_eq!(MIN_AU - Au(1), MAX_AU);
-
-    assert_eq!(Au(7) * 5, Au(35));
-    assert_eq!(MAX_AU * -1, MIN_AU + Au(1));
-    assert_eq!(MIN_AU * -1, MIN_AU);
-
-    assert_eq!(Au(35) / 5, Au(7));
-    assert_eq!(Au(35) % 6, Au(5));
-
-    assert_eq!(-Au(7), Au(-7));
-}
-
-#[test]
-#[should_panic]
-fn overflowing_div() {
-    MIN_AU / -1;
-}
-
-#[test]
-#[should_panic]
-fn overflowing_rem() {
-    MIN_AU % -1;
-}
-
-#[test]
-fn scale() {
-    assert_eq!(Au(12).scale_by(1.5), Au(18));
-}
-
-#[test]
-fn convert() {
-    assert_eq!(Au::from_px(5), Au(300));
-
-    assert_eq!(Au(300).to_px(), 5);
-    assert_eq!(Au(330).to_px(), 5);
-    assert_eq!(Au(350).to_px(), 5);
-    assert_eq!(Au(360).to_px(), 6);
-
-    assert_eq!(Au(300).ceil_to_px(), 5);
-    assert_eq!(Au(310).ceil_to_px(), 6);
-    assert_eq!(Au(330).ceil_to_px(), 6);
-    assert_eq!(Au(350).ceil_to_px(), 6);
-    assert_eq!(Au(360).ceil_to_px(), 6);
-
-    assert_eq!(Au(300).to_nearest_px(), 5);
-    assert_eq!(Au(310).to_nearest_px(), 5);
-    assert_eq!(Au(330).to_nearest_px(), 6);
-    assert_eq!(Au(350).to_nearest_px(), 6);
-    assert_eq!(Au(360).to_nearest_px(), 6);
-
-    assert_eq!(Au(60).to_nearest_pixel(2.), 1.);
-    assert_eq!(Au(70).to_nearest_pixel(2.), 1.);
-    assert_eq!(Au(80).to_nearest_pixel(2.), 1.5);
-    assert_eq!(Au(90).to_nearest_pixel(2.), 1.5);
-    assert_eq!(Au(100).to_nearest_pixel(2.), 1.5);
-    assert_eq!(Au(110).to_nearest_pixel(2.), 2.);
-    assert_eq!(Au(120).to_nearest_pixel(2.), 2.);
-
-    assert_eq!(Au(300).to_f32_px(), 5.);
-    assert_eq!(Au(312).to_f32_px(), 5.2);
-    assert_eq!(Au(330).to_f32_px(), 5.5);
-    assert_eq!(Au(348).to_f32_px(), 5.8);
-    assert_eq!(Au(360).to_f32_px(), 6.);
-
-    assert_eq!(Au(300).to_f64_px(), 5.);
-    assert_eq!(Au(312).to_f64_px(), 5.2);
-    assert_eq!(Au(330).to_f64_px(), 5.5);
-    assert_eq!(Au(348).to_f64_px(), 5.8);
-    assert_eq!(Au(360).to_f64_px(), 6.);
-
-    assert_eq!(Au::from_f32_px(5.), Au(300));
-    assert_eq!(Au::from_f32_px(5.2), Au(312));
-    assert_eq!(Au::from_f32_px(5.5), Au(330));
-    assert_eq!(Au::from_f32_px(5.8), Au(348));
-    assert_eq!(Au::from_f32_px(6.), Au(360));
-
-    assert_eq!(Au::from_f64_px(5.), Au(300));
-    assert_eq!(Au::from_f64_px(5.2), Au(312));
-    assert_eq!(Au::from_f64_px(5.5), Au(330));
-    assert_eq!(Au::from_f64_px(5.8), Au(348));
-    assert_eq!(Au::from_f64_px(6.), Au(360));
-}
-
-#[test]
-fn heapsize() {
-    use heapsize::HeapSizeOf;
-    fn f<T: HeapSizeOf>(_: T) {}
-    f(Au::new(0));
-}
deleted file mode 100644
--- a/third_party/rust/app_units-0.3.0/src/lib.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//! An Au is an "App Unit" and represents 1/60th of a CSS pixel. It was
-//! originally proposed in 2002 as a standard unit of measure in Gecko.
-//! See https://bugzilla.mozilla.org/show_bug.cgi?id=177805 for more info.
-
-extern crate heapsize;
-extern crate num_traits;
-extern crate rustc_serialize;
-extern crate serde;
-
-mod app_unit;
-
-pub use app_unit::{Au, MIN_AU, MAX_AU, AU_PER_PX};
--- a/third_party/rust/bincode/.cargo-checksum.json
+++ b/third_party/rust/bincode/.cargo-checksum.json
@@ -1,1 +1,1 @@
-{"files":{".cargo-ok":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",".gitignore":"e084df3ce631ce22082bd63f9e421e7f4d7a2408d6520de532f6a649e4d320dd",".travis.yml":"cb3f687453522852cb74371892a77d5e6eb61d771b8ef27f6cc6628e556de3d6","Cargo.toml":"d631ecb2eef5a18307a68e795080ed073851c9bea0800405cad98642ed3cc053","LICENSE.md":"90d7e062634054e6866d3c81e6a2b3058a840e6af733e98e80bdfe1a7dec6912","examples/basic.rs":"cdf97f2c4facbc202bf9e1496030d09bef3b7cd5538407325a38f0fe2e49415e","logo.png":"ebc5305aae938c1f834cf35302faa8be0f1b7b8c3c3beef5cf6b2f68b9628c35","readme.dev.md":"43bad3bcc13a5c057344d3ba7f64bd2b313f8c133d6afa068108df73e8e8facd","readme.md":"1fe1bda36327400cfedfcf103d58091c8465067b62706b0a368d287ca0312cd9","src/lib.rs":"1a85a12afad0b6150b8dbede093d19f4a32a3cd6976ee018a625fbc05051bf80","src/refbox.rs":"f0470baabbf0f9852df939c2535865793dc31c9d9d35eecf9c237a9df431a9fc","src/rustc_serialize/mod.rs":"188f5ff7fc9c5e0ac1404b919ceafac5ce4385950d22ae470ddc1775d2a0643b","src/rustc_serialize/reader.rs":"7983c37556fdef552bfeba386d557863fb5113c8fada55d4cf6a605f13214253","src/rustc_serialize/writer.rs":"684844799673fce3c54f1aca42430b6730da13473d732ee2954ebc56994ebd95","src/serde/mod.rs":"7818bbe5c320af2a15762c421d5471865a7364e1c9754c57960402fdcf09c595","src/serde/reader.rs":"1f88a55923dfc3ad82ec32571c9c7ca42818d996897966dea08a595f804d117f","src/serde/writer.rs":"d987134b3a00eb17a25e601757ad20607dd1de8989452266e9e4e7955fcd87f1","tests/test.rs":"b72a5902be11c3210dd56814276ff036155eba10d5f0aa566c86e7a1ce463adf"},"package":"55eb0b7fd108527b0c77860f75eca70214e11a8b4c6ef05148c54c05a25d48ad"}
\ No newline at end of file
+{"files":{".cargo-ok":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",".gitignore":"e084df3ce631ce22082bd63f9e421e7f4d7a2408d6520de532f6a649e4d320dd",".travis.yml":"f705a11b487bf71c41ebd8223cc1f3cbde0dfdfeea96a100af55e06e93397a1b","Cargo.toml":"c1d0f68b42bff71b04c8e763f13b0141f30dc849bee5b0ab5b9008e3627aac99","LICENSE.md":"90d7e062634054e6866d3c81e6a2b3058a840e6af733e98e80bdfe1a7dec6912","changelist.org":"90bb4036f90c3792c8294de2e3d52a54cc6230c3e5dc78013a781a9aa468f5f3","examples/basic.rs":"57aeca11d5cc5c3d5bb613e78b2ea43a2e80d66c15a2fffae303b165aa4ab41d","logo.png":"ebc5305aae938c1f834cf35302faa8be0f1b7b8c3c3beef5cf6b2f68b9628c35","readme.dev.md":"43bad3bcc13a5c057344d3ba7f64bd2b313f8c133d6afa068108df73e8e8facd","readme.md":"1fe1bda36327400cfedfcf103d58091c8465067b62706b0a368d287ca0312cd9","src/lib.rs":"04d6e4533f4bbb2ce2126bca414f95610075642b223f4e0c0b8f7a573792d7fd","src/refbox.rs":"fe266cec4f9f36942a1a9a9ad094a4bb1003d0c0f3c070cfb6214790d0f21b69","src/serde/mod.rs":"ef0c0a55936d835ae756d84a6ac38de312687d7c0f2cfc6810ec994413464516","src/serde/reader.rs":"6bfde2e2df9b450f6c07576198e47fdc837bbc4ddc74f447c72875c188c72ddc","src/serde/writer.rs":"eb3b439e8822871d715464ef6aca4b93a73b2b57625f9c586b68007f7386ab12","tests/test.rs":"f009e979fda892ad531ddd0f2003f0a7df607b19bd453a53f87c9041dfd9c745"},"package":"62650bb5651ba8f0580cebf4ef255d791b8b0ef53800322661e1bb5791d42966"}
\ No newline at end of file
--- a/third_party/rust/bincode/.travis.yml
+++ b/third_party/rust/bincode/.travis.yml
@@ -1,29 +1,5 @@
-lang: c
-after_success: |
-  [ $TRAVIS_BRANCH = master ] &&
-  [ $TRAVIS_PULL_REQUEST = false ] &&
-  cargo doc &&
-  echo "<meta http-equiv=refresh content=0;url=`echo $TRAVIS_REPO_SLUG | cut -d '/' -f 2`/index.html>" > target/doc/index.html &&
-  sudo pip install ghp-import &&
-  ghp-import -n target/doc &&
-  git push -fq https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages
-
-env:
-  matrix:
-    - CHANNEL='stable'
-    - CHANNEL='beta'
-    - CHANNEL='nightly'
-  global:
-  - secure: SZSxNqg9wiGx8EnJhifJ2kb/aCRcLim9TzTQyfurPqd8qVGkDOeVjTtbs+VTxLVXYtMJAz+YYnrQDwsu8kc/uYpQajU+gRMqNGEP5gNj3Ha5iNGDasAS6piIHQSMROayZ+D9g22nlGnjk8t9eZtLHC/Z8IWMCnjcIHvqMFY6cgI=
-
-install:
-    - curl -sf https://raw.githubusercontent.com/brson/multirust/master/blastoff.sh > ./rustup.sh
-    - chmod +x ./rustup.sh
-    - ./rustup.sh --yes
-
-script:
-    - multirust default $CHANNEL
-    - cargo build
-    - cargo build --no-default-features --features "rustc-serialize"
-    - cargo build --no-default-features --features "serde"
-    - if [ $CHANNEL = 'nightly' ] ; then cargo test ; fi
+language: rust
+rust:
+    - stable
+    - beta
+    - nightly
--- a/third_party/rust/bincode/Cargo.toml
+++ b/third_party/rust/bincode/Cargo.toml
@@ -1,29 +1,21 @@
 [package]
 name = "bincode"
-version = "0.6.1"
-authors = ["Ty Overby <ty@pre-alpha.com>", "Francesco Mazzoli <f@mazzo.li>"]
+version = "1.0.0-alpha2"
+authors = ["Ty Overby <ty@pre-alpha.com>", "Francesco Mazzoli <f@mazzo.li>", "David Tolnay <dtolnay@gmail.com>", "Daniel Griffen"]
 
 repository = "https://github.com/TyOverby/bincode"
-documentation = "http://tyoverby.github.io/bincode/bincode/"
+documentation = "https://docs.rs/bincode"
 keywords = ["binary", "encode", "decode", "serialize", "deserialize"]
 
 license = "MIT"
-description = "A binary serialization / deserialization strategy and implementation with serde and rustc-serialize backends."
+description = "A binary serialization / deserialization strategy that uses Serde for transforming structs into bytes and vice versa!"
 
 [dependencies]
 byteorder = "1.0.0"
 num-traits = "0.1.32"
 
-[dependencies.rustc-serialize]
-version = "0.3.*"
-optional = true
-
 [dependencies.serde]
-version = "0.8.*"
-optional = true
+version = "0.9.*"
 
 [dev-dependencies]
-serde_derive = "0.8.*"
-
-[features]
-default = ["rustc-serialize", "serde"]
+serde_derive = "0.9.*"
new file mode 100644
--- /dev/null
+++ b/third_party/rust/bincode/changelist.org
@@ -0,0 +1,18 @@
+* 1.0.0
+** Removed depricated rustc-serialize support
+ Rustc-serialize was a stopgap until projects like Serde were able to catch up.
+ With macros stabilization on its way, we are able to switch to serde without any
+ big user-friendliness issues.  Major congratulations to Serde for coming this far!
+
+** Moved Refbox, Strbox and Slicebox into a "refbox" module
+ Refbox, Strbox and Slicebox are still an integral piece of bincode, but since
+ they are mainly used by power-users, this move will make the crate API more organized
+ and easier for new users to understand.
+
+** Upgraded to Serde 0.9.*
+ Serde 0.9.* gives us a better API surface area and allows use of procedural macros for
+ deriving serialize and deserialize implemenetations.
+
+** Moved serde functions into global module
+ Since serde is the only supported serialization mechanism, it makes sense to have these
+ functions available at the top level.
--- a/third_party/rust/bincode/examples/basic.rs
+++ b/third_party/rust/bincode/examples/basic.rs
@@ -1,10 +1,11 @@
+/*
 extern crate bincode;
-extern crate rustc_serialize;
+extern crate
 
 use bincode::SizeLimit;
 use bincode::rustc_serialize::{encode, decode};
 
 #[derive(RustcEncodable, RustcDecodable, PartialEq)]
 struct Entity {
     x: f32,
     y: f32,
@@ -24,8 +25,11 @@ fn main() {
 
     // 8 bytes for the length of the vector, 4 bytes per float.
     assert_eq!(encoded.len(), 8 + 4 * 4);
 
     let decoded: World = decode(&encoded[..]).unwrap();
 
     assert!(world == decoded);
 }
+ */
+
+fn main() {}
--- a/third_party/rust/bincode/src/lib.rs
+++ b/third_party/rust/bincode/src/lib.rs
@@ -11,52 +11,44 @@
 //! or the `serde` crate.  `rustc_serialize` and `serde` are crates and and also the names of their
 //! corresponding modules inside of `bincode`.  Both modules have exactly equivalant functions, and
 //! and the only difference is whether or not the library user wants to use `rustc_serialize` or
 //! `serde`.
 //!
 //! ### Using Basic Functions
 //!
 //! ```rust
-//! #![allow(unstable)]
 //! extern crate bincode;
-//! use bincode::rustc_serialize::{encode, decode};
+//! use bincode::{serialize, deserialize};
 //! fn main() {
 //!     // The object that we will serialize.
 //!     let target = Some("hello world".to_string());
 //!     // The maximum size of the encoded message.
 //!     let limit = bincode::SizeLimit::Bounded(20);
 //!
-//!     let encoded: Vec<u8>        = encode(&target, limit).unwrap();
-//!     let decoded: Option<String> = decode(&encoded[..]).unwrap();
+//!     let encoded: Vec<u8>        = serialize(&target, limit).unwrap();
+//!     let decoded: Option<String> = deserialize(&encoded[..]).unwrap();
 //!     assert_eq!(target, decoded);
 //! }
 //! ```
 
 #![crate_name = "bincode"]
 #![crate_type = "rlib"]
 #![crate_type = "dylib"]
 
 #![doc(html_logo_url = "./icon.png")]
 
-#[cfg(feature = "rustc-serialize")]
-extern crate rustc_serialize as rustc_serialize_crate;
 extern crate byteorder;
 extern crate num_traits;
-#[cfg(feature = "serde")]
 extern crate serde as serde_crate;
 
-
-pub use refbox::{RefBox, StrBox, SliceBox};
+pub mod refbox;
+mod serde;
 
-mod refbox;
-#[cfg(feature = "rustc-serialize")]
-pub mod rustc_serialize;
-#[cfg(feature = "serde")]
-pub mod serde;
+pub use serde::*;
 
 /// A limit on the amount of bytes that can be read or written.
 ///
 /// Size limits are an incredibly important part of both encoding and decoding.
 ///
 /// In order to prevent DOS attacks on a decoder, it is important to limit the
 /// amount of bytes that a single encoded message can be; otherwise, if you
 /// are decoding bytes right off of a TCP stream for example, it would be
@@ -71,9 +63,8 @@ pub mod serde;
 /// encoding function, the encoder will verify that the structure can be encoded
 /// within that limit.  This verification occurs before any bytes are written to
 /// the Writer, so recovering from an error is easy.
 #[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
 pub enum SizeLimit {
     Infinite,
     Bounded(u64)
 }
-
--- a/third_party/rust/bincode/src/refbox.rs
+++ b/third_party/rust/bincode/src/refbox.rs
@@ -1,15 +1,11 @@
 use std::boxed::Box;
 use std::ops::Deref;
 
-#[cfg(feature = "rustc-serialize")]
-use rustc_serialize_crate::{Encodable, Encoder, Decodable, Decoder};
-
-#[cfg(feature = "serde")]
 use serde_crate as serde;
 
 /// A struct for encoding nested reference types.
 ///
 /// Encoding large objects by reference is really handy.  For example,
 /// `encode(&large_hashmap, ...)` encodes the large structure without having to
 /// own the hashmap.  However, it is impossible to serialize a reference if that
 /// reference is inside of a struct.
@@ -136,45 +132,28 @@ impl <T> RefBox<'static, T>  {
     pub fn try_take(self) -> Result<Box<T>, RefBox<'static, T>> {
         match self.inner {
             RefBoxInner::Box(b) => Ok(b),
             o => Err(RefBox{ inner: o})
         }
     }
 }
 
-#[cfg(feature = "rustc-serialize")]
-impl <'a, T: Encodable> Encodable for RefBox<'a, T> {
-    fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
-        self.inner.encode(s)
-    }
-}
-
-#[cfg(feature = "rustc-serialize")]
-impl <T: Decodable> Decodable for RefBox<'static, T> {
-    fn decode<D: Decoder>(d: &mut D) -> Result<RefBox<'static, T>, D::Error> {
-        let inner = try!(Decodable::decode(d));
-        Ok(RefBox{inner: inner})
-    }
-}
-
-#[cfg(feature = "serde")]
 impl<'a, T> serde::Serialize for RefBox<'a, T>
     where T: serde::Serialize,
 {
-    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
         where S: serde::Serializer
     {
         serde::Serialize::serialize(&self.inner, serializer)
     }
 }
 
-#[cfg(feature = "serde")]
 impl<'a, T: serde::Deserialize> serde::Deserialize for RefBox<'a, T> {
-    fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
         where D: serde::Deserializer
     {
         let inner = try!(serde::Deserialize::deserialize(deserializer));
         Ok(RefBox{ inner: inner })
     }
 }
 
 impl<'a> StrBox<'a> {
@@ -234,43 +213,27 @@ impl StrBox<'static>  {
     pub fn try_take(self) -> Result<String, StrBox<'static>> {
         match self.inner {
             RefBoxInner::Box(b) => Ok(b),
             o => Err(StrBox{ inner: o})
         }
     }
 }
 
-#[cfg(feature = "rustc-serialize")]
-impl <'a> Encodable for StrBox<'a> {
-    fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
-        self.inner.encode(s)
-    }
-}
 
-#[cfg(feature = "rustc-serialize")]
-impl Decodable for StrBox<'static> {
-    fn decode<D: Decoder>(d: &mut D) -> Result<StrBox<'static>, D::Error> {
-        let inner: RefBoxInner<'static, str, String> = try!(Decodable::decode(d));
-        Ok(StrBox{inner: inner})
-    }
-}
-
-#[cfg(feature = "serde")]
 impl<'a> serde::Serialize for StrBox<'a> {
-    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
         where S: serde::Serializer
     {
         serde::Serialize::serialize(&self.inner, serializer)
     }
 }
 
-#[cfg(feature = "serde")]
 impl serde::Deserialize for StrBox<'static> {
-    fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
         where D: serde::Deserializer
     {
         let inner = try!(serde::Deserialize::deserialize(deserializer));
         Ok(StrBox{ inner: inner })
     }
 }
 
 //
@@ -325,90 +288,56 @@ impl <T> SliceBox<'static, T>  {
     pub fn try_take(self) -> Result<Vec<T>, SliceBox<'static, T>> {
         match self.inner {
             RefBoxInner::Box(b) => Ok(b),
             o => Err(SliceBox{ inner: o})
         }
     }
 }
 
-#[cfg(feature = "rustc-serialize")]
-impl <'a, T: Encodable> Encodable for SliceBox<'a, T> {
-    fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
-        self.inner.encode(s)
-    }
-}
 
-#[cfg(feature = "rustc-serialize")]
-impl <T: Decodable> Decodable for SliceBox<'static, T> {
-    fn decode<D: Decoder>(d: &mut D) -> Result<SliceBox<'static, T>, D::Error> {
-        let inner: RefBoxInner<'static, [T], Vec<T>> = try!(Decodable::decode(d));
-        Ok(SliceBox{inner: inner})
-    }
-}
-
-#[cfg(feature = "serde")]
 impl<'a, T> serde::Serialize for SliceBox<'a, T>
     where T: serde::Serialize,
 {
-    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
         where S: serde::Serializer
     {
         serde::Serialize::serialize(&self.inner, serializer)
     }
 }
 
-#[cfg(feature = "serde")]
 impl<'a, T: serde::Deserialize> serde::Deserialize for SliceBox<'a, T> {
-    fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
         where D: serde::Deserializer
     {
         let inner = try!(serde::Deserialize::deserialize(deserializer));
         Ok(SliceBox{ inner: inner })
     }
 }
 
-#[cfg(feature = "rustc-serialize")]
-impl <'a, A: Encodable + ?Sized, B: Encodable> Encodable for RefBoxInner<'a, A, B> {
-    fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
-        match self {
-            &RefBoxInner::Ref(ref r) => r.encode(s),
-            &RefBoxInner::Box(ref b) => b.encode(s)
-        }
-    }
-}
 
-#[cfg(feature = "serde")]
 impl<'a, A: ?Sized, B> serde::Serialize for RefBoxInner<'a, A, B>
     where A: serde::Serialize,
           B: serde::Serialize,
 {
-    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
         where S: serde::Serializer
     {
         match self {
             &RefBoxInner::Ref(ref r) => serde::Serialize::serialize(r, serializer),
             &RefBoxInner::Box(ref b) => serde::Serialize::serialize(b, serializer),
         }
     }
 }
 
-#[cfg(feature = "rustc-serialize")]
-impl <A: ?Sized, B: Decodable> Decodable for RefBoxInner<'static, A, B> {
-    fn decode<D: Decoder>(d: &mut D) -> Result<RefBoxInner<'static, A, B>, D::Error> {
-        let decoded = try!(Decodable::decode(d));
-        Ok(RefBoxInner::Box(decoded))
-    }
-}
 
-#[cfg(feature = "serde")]
 impl<'a, A: ?Sized, B> serde::Deserialize for RefBoxInner<'a, A, B>
     where B: serde::Deserialize,
 {
-    fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
         where D: serde::Deserializer
     {
         let deserialized = try!(serde::Deserialize::deserialize(deserializer));
         Ok(RefBoxInner::Box(deserialized))
     }
 }
 
 impl <'a, T> Deref for RefBox<'a, T> {
deleted file mode 100644
--- a/third_party/rust/bincode/src/rustc_serialize/mod.rs
+++ /dev/null
@@ -1,102 +0,0 @@
-//! A collection of serialization and deserialization functions
-//! that use the `rustc_serialize` crate for the encodable and decodable
-//! implementation.
-
-use rustc_serialize_crate::{Encodable, Decodable};
-use std::io::{Write, Read};
-use ::SizeLimit;
-
-pub use self::writer::{SizeChecker, EncoderWriter, EncodingResult, EncodingError};
-pub use self::reader::{DecoderReader, DecodingResult, DecodingError, InvalidEncoding};
-
-mod reader;
-mod writer;
-
-/// Encodes an encodable object into a `Vec` of bytes.
-///
-/// If the encoding would take more bytes than allowed by `size_limit`,
-/// an error is returned.
-pub fn encode<T: Encodable>(t: &T, size_limit: SizeLimit) -> EncodingResult<Vec<u8>> {
-    // Since we are putting values directly into a vector, we can do size
-    // computation out here and pre-allocate a buffer of *exactly*
-    // the right size.
-    let mut w = if let SizeLimit::Bounded(l) = size_limit {
-        let actual_size = encoded_size_bounded(t, l);
-        let actual_size = try!(actual_size.ok_or(EncodingError::SizeLimit));
-        Vec::with_capacity(actual_size as usize)
-    } else {
-        vec![]
-    };
-
-    match encode_into(t, &mut w, SizeLimit::Infinite) {
-        Ok(()) => Ok(w),
-        Err(e) => Err(e)
-    }
-}
-
-/// Decodes a slice of bytes into an object.
-///
-/// This method does not have a size-limit because if you already have the bytes
-/// in memory, then you don't gain anything by having a limiter.
-pub fn decode<T: Decodable>(b: &[u8]) -> DecodingResult<T> {
-    let mut b = b;
-    decode_from(&mut b, SizeLimit::Infinite)
-}
-
-/// Encodes an object directly into a `Writer`.
-///
-/// If the encoding would take more bytes than allowed by `size_limit`, an error
-/// is returned and *no bytes* will be written into the `Writer`.
-///
-/// If this returns an `EncodingError` (other than SizeLimit), assume that the
-/// writer is in an invalid state, as writing could bail out in the middle of
-/// encoding.
-pub fn encode_into<T: Encodable, W: Write>(t: &T,
-                                           w: &mut W,
-                                           size_limit: SizeLimit)
-                                           -> EncodingResult<()> {
-    try!(match size_limit {
-        SizeLimit::Infinite => Ok(()),
-        SizeLimit::Bounded(x) => {
-            let mut size_checker = SizeChecker::new(x);
-            t.encode(&mut size_checker)
-        }
-    });
-
-    t.encode(&mut writer::EncoderWriter::new(w))
-}
-
-/// Decoes an object directly from a `Buffer`ed Reader.
-///
-/// If the provided `SizeLimit` is reached, the decode will bail immediately.
-/// A SizeLimit can help prevent an attacker from flooding your server with
-/// a neverending stream of values that runs your server out of memory.
-///
-/// If this returns an `DecodingError`, assume that the buffer that you passed
-/// in is in an invalid state, as the error could be returned during any point
-/// in the reading.
-pub fn decode_from<R: Read, T: Decodable>(r: &mut R, size_limit: SizeLimit) -> DecodingResult<T> {
-    Decodable::decode(&mut reader::DecoderReader::new(r, size_limit))
-}
-
-
-/// Returns the size that an object would be if encoded using bincode.
-///
-/// This is used internally as part of the check for encode_into, but it can
-/// be useful for preallocating buffers if thats your style.
-pub fn encoded_size<T: Encodable>(t: &T) -> u64 {
-    use std::u64::MAX;
-    let mut size_checker = SizeChecker::new(MAX);
-    t.encode(&mut size_checker).ok();
-    size_checker.written
-}
-
-/// Given a maximum size limit, check how large an object would be if it
-/// were to be encoded.
-///
-/// If it can be encoded in `max` or fewer bytes, that number will be returned
-/// inside `Some`.  If it goes over bounds, then None is returned.
-pub fn encoded_size_bounded<T: Encodable>(t: &T, max: u64) -> Option<u64> {
-    let mut size_checker = SizeChecker::new(max);
-    t.encode(&mut size_checker).ok().map(|_| size_checker.written)
-}
deleted file mode 100644
--- a/third_party/rust/bincode/src/rustc_serialize/reader.rs
+++ /dev/null
@@ -1,392 +0,0 @@
-use std::io::Read;
-use std::io::Error as IoError;
-use std::error::Error;
-use std::fmt;
-use std::convert::From;
-
-use rustc_serialize_crate::Decoder;
-
-use byteorder::{BigEndian, ReadBytesExt};
-use ::SizeLimit;
-
-#[derive(Eq, PartialEq, Clone, Debug)]
-pub struct InvalidEncoding {
-    pub desc: &'static str,
-    pub detail: Option<String>,
-}
-
-impl fmt::Display for InvalidEncoding {
-    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
-        match *self {
-            InvalidEncoding { detail: None, desc } =>
-                write!(fmt, "{}", desc),
-            InvalidEncoding { detail: Some(ref detail), desc } =>
-                write!(fmt, "{} ({})", desc, detail)
-        }
-    }
-}
-
-/// An error that can be produced during decoding.
-///
-/// If decoding from a Buffer, assume that the buffer has been left
-/// in an invalid state.
-#[derive(Debug)]
-pub enum DecodingError {
-    /// If the error stems from the reader that is being used
-    /// during decoding, that error will be stored and returned here.
-    IoError(IoError),
-    /// If the bytes in the reader are not decodable because of an invalid
-    /// encoding, this error will be returned.  This error is only possible
-    /// if a stream is corrupted.  A stream produced from `encode` or `encode_into`
-    /// should **never** produce an InvalidEncoding error.
-    InvalidEncoding(InvalidEncoding),
-    /// If decoding a message takes more than the provided size limit, this
-    /// error is returned.
-    SizeLimit
-}
-
-impl fmt::Display for DecodingError {
-    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
-        match *self {
-            DecodingError::IoError(ref ioerr) =>
-                write!(fmt, "IoError: {}", ioerr),
-            DecodingError::InvalidEncoding(ref ib) =>
-                write!(fmt, "InvalidEncoding: {}", ib),
-            DecodingError::SizeLimit =>
-                write!(fmt, "SizeLimit")
-        }
-    }
-}
-
-pub type DecodingResult<T> = Result<T, DecodingError>;
-
-fn wrap_io(err: IoError) -> DecodingError {
-    DecodingError::IoError(err)
-}
-
-impl Error for DecodingError {
-    fn description(&self) -> &str {
-        match *self {
-            DecodingError::IoError(ref err) => Error::description(err),
-            DecodingError::InvalidEncoding(ref ib) => ib.desc,
-            DecodingError::SizeLimit => "the size limit for decoding has been reached"
-        }
-    }
-
-    fn cause(&self) -> Option<&Error> {
-        match *self {
-            DecodingError::IoError(ref err)     => err.cause(),
-            DecodingError::InvalidEncoding(_) => None,
-            DecodingError::SizeLimit => None
-        }
-    }
-}
-
-impl From<IoError> for DecodingError {
-    fn from(err: IoError) -> DecodingError {
-        DecodingError::IoError(err)
-    }
-}
-
-/// A Decoder that reads bytes from a buffer.
-///
-/// This struct should rarely be used.
-/// In most cases, prefer the `decode_from` function.
-///
-/// ```rust,ignore
-/// let dr = bincode::rustc_serialize::DecoderReader::new(&mut some_reader, SizeLimit::Infinite);
-/// let result: T = Decodable::decode(&mut dr);
-/// let bytes_read = dr.bytes_read();
-/// ```
-pub struct DecoderReader<'a, R: 'a> {
-    reader: &'a mut R,
-    size_limit: SizeLimit,
-    read: u64
-}
-
-impl<'a, R: Read> DecoderReader<'a, R> {
-    pub fn new(r: &'a mut R, size_limit: SizeLimit) -> DecoderReader<'a, R> {
-        DecoderReader {
-            reader: r,
-            size_limit: size_limit,
-            read: 0
-        }
-    }
-
-    /// Returns the number of bytes read from the contained Reader.
-    pub fn bytes_read(&self) -> u64 {
-        self.read
-    }
-}
-
-impl <'a, A> DecoderReader<'a, A> {
-    fn read_bytes(&mut self, count: u64) -> Result<(), DecodingError> {
-        self.read = match self.read.checked_add(count) {
-            Some(read) => read,
-            None => return Err(DecodingError::SizeLimit),
-        };
-        match self.size_limit {
-            SizeLimit::Infinite => Ok(()),
-            SizeLimit::Bounded(x) if self.read <= x => Ok(()),
-            SizeLimit::Bounded(_) => Err(DecodingError::SizeLimit)
-        }
-    }
-
-    fn read_type<T>(&mut self) -> Result<(), DecodingError> {
-        use std::mem::size_of;
-        self.read_bytes(size_of::<T>() as u64)
-    }
-}
-
-impl<'a, R: Read> Decoder for DecoderReader<'a, R> {
-    type Error = DecodingError;
-
-    fn read_nil(&mut self) -> DecodingResult<()> {
-        Ok(())
-    }
-    fn read_usize(&mut self) -> DecodingResult<usize> {
-        Ok(try!(self.read_u64().map(|x| x as usize)))
-    }
-    fn read_u64(&mut self) -> DecodingResult<u64> {
-        try!(self.read_type::<u64>());
-        self.reader.read_u64::<BigEndian>().map_err(wrap_io)
-    }
-    fn read_u32(&mut self) -> DecodingResult<u32> {
-        try!(self.read_type::<u32>());
-        self.reader.read_u32::<BigEndian>().map_err(wrap_io)
-    }
-    fn read_u16(&mut self) -> DecodingResult<u16> {
-        try!(self.read_type::<u16>());
-        self.reader.read_u16::<BigEndian>().map_err(wrap_io)
-    }
-    fn read_u8(&mut self) -> DecodingResult<u8> {
-        try!(self.read_type::<u8>());
-        self.reader.read_u8().map_err(wrap_io)
-    }
-    fn read_isize(&mut self) -> DecodingResult<isize> {
-        self.read_i64().map(|x| x as isize)
-    }
-    fn read_i64(&mut self) -> DecodingResult<i64> {
-        try!(self.read_type::<i64>());
-        self.reader.read_i64::<BigEndian>().map_err(wrap_io)
-    }
-    fn read_i32(&mut self) -> DecodingResult<i32> {
-        try!(self.read_type::<i32>());
-        self.reader.read_i32::<BigEndian>().map_err(wrap_io)
-    }
-    fn read_i16(&mut self) -> DecodingResult<i16> {
-        try!(self.read_type::<i16>());
-        self.reader.read_i16::<BigEndian>().map_err(wrap_io)
-    }
-    fn read_i8(&mut self) -> DecodingResult<i8> {
-        try!(self.read_type::<i8>());
-        self.reader.read_i8().map_err(wrap_io)
-    }
-    fn read_bool(&mut self) -> DecodingResult<bool> {
-        let x = try!(self.read_i8());
-        match x {
-            1 => Ok(true),
-            0 => Ok(false),
-            _ => Err(DecodingError::InvalidEncoding(InvalidEncoding{
-                desc: "invalid u8 when decoding bool",
-                detail: Some(format!("Expected 0 or 1, got {}", x))
-            })),
-        }
-    }
-    fn read_f64(&mut self) -> DecodingResult<f64> {
-        try!(self.read_type::<f64>());
-        self.reader.read_f64::<BigEndian>().map_err(wrap_io)
-    }
-    fn read_f32(&mut self) -> DecodingResult<f32> {
-        try!(self.read_type::<f32>());
-        self.reader.read_f32::<BigEndian>().map_err(wrap_io)
-    }
-    fn read_char(&mut self) -> DecodingResult<char> {
-        use std::str;
-
-        let error = DecodingError::InvalidEncoding(InvalidEncoding {
-            desc: "Invalid char encoding",
-            detail: None
-        });
-
-        let mut buf = [0];
-
-        let _ = try!(self.reader.read(&mut buf[..]));
-        let first_byte = buf[0];
-        let width = utf8_char_width(first_byte);
-        if width == 1 { return Ok(first_byte as char) }
-        if width == 0 { return Err(error)}
-
-        let mut buf = [first_byte, 0, 0, 0];
-        {
-            let mut start = 1;
-            while start < width {
-                match try!(self.reader.read(&mut buf[start .. width])) {
-                    n if n == width - start => break,
-                    n if n < width - start => { start += n; }
-                    _ => return Err(error)
-                }
-            }
-        }
-
-        let res = try!(match str::from_utf8(&buf[..width]).ok() {
-            Some(s) => Ok(s.chars().next().unwrap()),
-            None => Err(error)
-        });
-
-        try!(self.read_bytes(res.len_utf8() as u64));
-        Ok(res)
-    }
-
-    fn read_str(&mut self) -> DecodingResult<String> {
-        let len = try!(self.read_usize());
-        try!(self.read_bytes(len as u64));
-
-        let mut buff = Vec::new();
-        try!(self.reader.by_ref().take(len as u64).read_to_end(&mut buff));
-        match String::from_utf8(buff) {
-            Ok(s) => Ok(s),
-            Err(err) => Err(DecodingError::InvalidEncoding(InvalidEncoding {
-                desc: "error while decoding utf8 string",
-                detail: Some(format!("Decoding error: {}", err))
-            })),
-        }
-    }
-    fn read_enum<T, F>(&mut self, _: &str, f: F) -> DecodingResult<T>
-        where F: FnOnce(&mut DecoderReader<'a, R>) -> DecodingResult<T>
-    {
-        f(self)
-    }
-    fn read_enum_variant<T, F>(&mut self, names: &[&str], mut f: F) -> DecodingResult<T>
-        where F: FnMut(&mut DecoderReader<'a, R>, usize) -> DecodingResult<T>
-    {
-        let id = try!(self.read_u32());
-        let id = id as usize;
-        if id >= names.len() {
-                Err(DecodingError::InvalidEncoding(InvalidEncoding {
-                    desc: "out of bounds tag when reading enum variant",
-                    detail: Some(format!("Expected tag < {}, got {}", names.len(), id))
-                }))
-            } else {
-                f(self, id)
-            }
-    }
-    fn read_enum_variant_arg<T, F>(&mut self, _: usize, f: F) -> DecodingResult<T>
-        where F: FnOnce(&mut DecoderReader<'a, R>) -> DecodingResult<T>
-    {
-        f(self)
-    }
-    fn read_enum_struct_variant<T, F>(&mut self, names: &[&str], f: F) -> DecodingResult<T>
-        where F: FnMut(&mut DecoderReader<'a, R>, usize) -> DecodingResult<T>
-    {
-        self.read_enum_variant(names, f)
-    }
-    fn read_enum_struct_variant_field<T, F>(&mut self,
-                                            _: &str,
-                                            f_idx: usize,
-                                            f: F)
-                                            -> DecodingResult<T>
-        where F: FnOnce(&mut DecoderReader<'a, R>) -> DecodingResult<T>
-    {
-        self.read_enum_variant_arg(f_idx, f)
-    }
-    fn read_struct<T, F>(&mut self, _: &str, _: usize, f: F) -> DecodingResult<T>
-        where F: FnOnce(&mut DecoderReader<'a, R>) -> DecodingResult<T>
-    {
-        f(self)
-    }
-    fn read_struct_field<T, F>(&mut self, _: &str, _: usize, f: F) -> DecodingResult<T>
-        where F: FnOnce(&mut DecoderReader<'a, R>) -> DecodingResult<T>
-    {
-        f(self)
-    }
-    fn read_tuple<T, F>(&mut self, _: usize, f: F) -> DecodingResult<T>
-        where F: FnOnce(&mut DecoderReader<'a, R>) -> DecodingResult<T>
-    {
-        f(self)
-    }
-    fn read_tuple_arg<T, F>(&mut self, _: usize, f: F) -> DecodingResult<T>
-        where F: FnOnce(&mut DecoderReader<'a, R>) -> DecodingResult<T>
-    {
-        f(self)
-    }
-    fn read_tuple_struct<T, F>(&mut self, _: &str, len: usize, f: F) -> DecodingResult<T>
-        where F: FnOnce(&mut DecoderReader<'a, R>) -> DecodingResult<T>
-    {
-        self.read_tuple(len, f)
-    }
-    fn read_tuple_struct_arg<T, F>(&mut self, a_idx: usize, f: F) -> DecodingResult<T>
-        where F: FnOnce(&mut DecoderReader<'a, R>) -> DecodingResult<T>
-    {
-        self.read_tuple_arg(a_idx, f)
-    }
-    fn read_option<T, F>(&mut self, mut f: F) -> DecodingResult<T>
-        where F: FnMut(&mut DecoderReader<'a, R>, bool) -> DecodingResult<T>
-    {
-        let x = try!(self.read_u8());
-        match x {
-                1 => f(self, true),
-                0 => f(self, false),
-                _ => Err(DecodingError::InvalidEncoding(InvalidEncoding {
-                    desc: "invalid tag when decoding Option",
-                    detail: Some(format!("Expected 0 or 1, got {}", x))
-                })),
-            }
-    }
-    fn read_seq<T, F>(&mut self, f: F) -> DecodingResult<T>
-        where F: FnOnce(&mut DecoderReader<'a, R>, usize) -> DecodingResult<T>
-    {
-        let len = try!(self.read_usize());
-        f(self, len)
-    }
-    fn read_seq_elt<T, F>(&mut self, _: usize, f: F) -> DecodingResult<T>
-        where F: FnOnce(&mut DecoderReader<'a, R>) -> DecodingResult<T>
-    {
-        f(self)
-    }
-    fn read_map<T, F>(&mut self, f: F) -> DecodingResult<T>
-        where F: FnOnce(&mut DecoderReader<'a, R>, usize) -> DecodingResult<T>
-    {
-        let len = try!(self.read_usize());
-        f(self, len)
-    }
-    fn read_map_elt_key<T, F>(&mut self, _: usize, f: F) -> DecodingResult<T>
-        where F: FnOnce(&mut DecoderReader<'a, R>) -> DecodingResult<T>
-    {
-        f(self)
-    }
-    fn read_map_elt_val<T, F>(&mut self, _: usize, f: F) -> DecodingResult<T>
-        where F: FnOnce(&mut DecoderReader<'a, R>) -> DecodingResult<T>
-    {
-        f(self)
-    }
-    fn error(&mut self, err: &str) -> DecodingError {
-        DecodingError::InvalidEncoding(InvalidEncoding {
-            desc: "user-induced error",
-            detail: Some(err.to_string()),
-        })
-    }
-}
-
-static UTF8_CHAR_WIDTH: [u8; 256] = [
-1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x1F
-1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x3F
-1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x5F
-1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x7F
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x9F
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0xBF
-0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // 0xDF
-3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, // 0xEF
-4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0, // 0xFF
-];
-
-fn utf8_char_width(b: u8) -> usize {
-    UTF8_CHAR_WIDTH[b as usize] as usize
-}
deleted file mode 100644
--- a/third_party/rust/bincode/src/rustc_serialize/writer.rs
+++ /dev/null
@@ -1,422 +0,0 @@
-use std::io::Write;
-use std::io::Error as IoError;
-use std::error::Error;
-use std::fmt;
-
-use rustc_serialize_crate::Encoder;
-
-use byteorder::{BigEndian, WriteBytesExt};
-
-pub type EncodingResult<T> = Result<T, EncodingError>;
-
-
-/// An error that can be produced during encoding.
-#[derive(Debug)]
-pub enum EncodingError {
-    /// An error originating from the underlying `Writer`.
-    IoError(IoError),
-    /// An object could not be encoded with the given size limit.
-    ///
-    /// This error is returned before any bytes are written to the
-    /// output `Writer`.
-    SizeLimit,
-}
-
-/// An Encoder that encodes values directly into a Writer.
-///
-/// This struct should not be used often.
-/// For most cases, prefer the `encode_into` function.
-pub struct EncoderWriter<'a, W: 'a> {
-    writer: &'a mut W,
-}
-
-pub struct SizeChecker {
-    pub size_limit: u64,
-    pub written: u64
-}
-
-fn wrap_io(err: IoError) -> EncodingError {
-    EncodingError::IoError(err)
-}
-
-impl fmt::Display for EncodingError {
-    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
-        match *self {
-            EncodingError::IoError(ref err) => write!(f, "IoError: {}", err),
-            EncodingError::SizeLimit => write!(f, "SizeLimit")
-        }
-    }
-}
-
-impl Error for EncodingError {
-    fn description(&self) -> &str {
-        match *self {
-            EncodingError::IoError(ref err) => Error::description(err),
-            EncodingError::SizeLimit => "the size limit for decoding has been reached"
-        }
-    }
-
-    fn cause(&self) -> Option<&Error> {
-        match *self {
-            EncodingError::IoError(ref err)     => err.cause(),
-            EncodingError::SizeLimit => None
-        }
-    }
-}
-
-impl <'a, W: Write> EncoderWriter<'a, W> {
-    pub fn new(w: &'a mut W) -> EncoderWriter<'a, W> {
-        EncoderWriter {
-            writer: w,
-        }
-    }
-}
-
-impl SizeChecker {
-    pub fn new(limit: u64) -> SizeChecker {
-        SizeChecker {
-            size_limit: limit,
-            written: 0
-        }
-    }
-
-    fn add_raw(&mut self, size: usize) -> EncodingResult<()> {
-        self.written += size as u64;
-        if self.written <= self.size_limit {
-            Ok(())
-        } else {
-            Err(EncodingError::SizeLimit)
-        }
-    }
-
-    fn add_value<T>(&mut self, t: T) -> EncodingResult<()> {
-        use std::mem::size_of_val;
-        self.add_raw(size_of_val(&t))
-    }
-}
-
-impl<'a, W: Write> Encoder for EncoderWriter<'a, W> {
-    type Error = EncodingError;
-
-    fn emit_nil(&mut self) -> EncodingResult<()> {
-        Ok(())
-    }
-    fn emit_usize(&mut self, v: usize) -> EncodingResult<()> {
-        self.emit_u64(v as u64)
-    }
-    fn emit_u64(&mut self, v: u64) -> EncodingResult<()> {
-        self.writer.write_u64::<BigEndian>(v).map_err(wrap_io)
-    }
-    fn emit_u32(&mut self, v: u32) -> EncodingResult<()> {
-        self.writer.write_u32::<BigEndian>(v).map_err(wrap_io)
-    }
-    fn emit_u16(&mut self, v: u16) -> EncodingResult<()> {
-        self.writer.write_u16::<BigEndian>(v).map_err(wrap_io)
-    }
-    fn emit_u8(&mut self, v: u8) -> EncodingResult<()> {
-        self.writer.write_u8(v).map_err(wrap_io)
-    }
-    fn emit_isize(&mut self, v: isize) -> EncodingResult<()> {
-        self.emit_i64(v as i64)
-    }
-    fn emit_i64(&mut self, v: i64) -> EncodingResult<()> {
-        self.writer.write_i64::<BigEndian>(v).map_err(wrap_io)
-    }
-    fn emit_i32(&mut self, v: i32) -> EncodingResult<()> {
-        self.writer.write_i32::<BigEndian>(v).map_err(wrap_io)
-    }
-    fn emit_i16(&mut self, v: i16) -> EncodingResult<()> {
-        self.writer.write_i16::<BigEndian>(v).map_err(wrap_io)
-    }
-    fn emit_i8(&mut self, v: i8) -> EncodingResult<()> {
-        self.writer.write_i8(v).map_err(wrap_io)
-    }
-    fn emit_bool(&mut self, v: bool) -> EncodingResult<()> {
-        self.writer.write_u8(if v {1} else {0}).map_err(wrap_io)
-    }
-    fn emit_f64(&mut self, v: f64) -> EncodingResult<()> {
-        self.writer.write_f64::<BigEndian>(v).map_err(wrap_io)
-    }
-    fn emit_f32(&mut self, v: f32) -> EncodingResult<()> {
-        self.writer.write_f32::<BigEndian>(v).map_err(wrap_io)
-    }
-    fn emit_char(&mut self, v: char) -> EncodingResult<()> {
-        // TODO: change this back once unicode works
-        //let mut cbuf = [0; 4];
-        //let sz = v.encode_utf8(&mut cbuf[..]).unwrap_or(0);
-        //let ptr = &cbuf[..sz];
-        //self.writer.write_all(ptr).map_err(EncodingError::IoError)
-
-        let mut inter = String::with_capacity(1);
-        inter.push(v);
-        self.writer.write_all(inter.as_bytes()).map_err(EncodingError::IoError)
-    }
-    fn emit_str(&mut self, v: &str) -> EncodingResult<()> {
-        try!(self.emit_usize(v.len()));
-        self.writer.write_all(v.as_bytes()).map_err(EncodingError::IoError)
-    }
-    fn emit_enum<F>(&mut self, __: &str, f: F) -> EncodingResult<()>
-        where F: FnOnce(&mut EncoderWriter<'a, W>) -> EncodingResult<()>
-    {
-        f(self)
-    }
-    fn emit_enum_variant<F>(&mut self, _: &str, v_id: usize, _: usize, f: F) -> EncodingResult<()>
-        where F: FnOnce(&mut EncoderWriter<'a, W>) -> EncodingResult<()>
-    {
-        let max_u32: u32 = ::std::u32::MAX;
-        if v_id > (max_u32 as usize) {
-                panic!("Variant tag doesn't fit in a u32")
-            }
-        try!(self.emit_u32(v_id as u32));
-        f(self)
-    }
-    fn emit_enum_variant_arg<F>(&mut self, _: usize, f: F) -> EncodingResult<()>
-        where F: FnOnce(&mut EncoderWriter<'a, W>) -> EncodingResult<()>
-    {
-        f(self)
-    }
-    fn emit_enum_struct_variant<F>(&mut self,
-                                   v_name: &str,
-                                   v_id: usize,
-                                   len: usize,
-                                   f: F)
-                                   -> EncodingResult<()>
-        where F: FnOnce(&mut EncoderWriter<'a, W>) -> EncodingResult<()>
-    {
-        self.emit_enum_variant(v_name, v_id, len, f)
-    }
-    fn emit_enum_struct_variant_field<F>(&mut self, _: &str, _: usize, f: F) -> EncodingResult<()>
-        where F: FnOnce(&mut EncoderWriter<'a, W>) -> EncodingResult<()>
-    {
-        f(self)
-    }
-    fn emit_struct<F>(&mut self, _: &str, _: usize, f: F) -> EncodingResult<()>
-        where F: FnOnce(&mut EncoderWriter<'a, W>) -> EncodingResult<()>
-    {
-        f(self)
-    }
-    fn emit_struct_field<F>(&mut self, _: &str, _: usize, f: F) -> EncodingResult<()>
-        where F: FnOnce(&mut EncoderWriter<'a, W>) -> EncodingResult<()>
-    {
-        f(self)
-    }
-    fn emit_tuple<F>(&mut self, _: usize, f: F) -> EncodingResult<()>
-        where F: FnOnce(&mut EncoderWriter<'a, W>) -> EncodingResult<()>
-    {
-        f(self)
-    }
-    fn emit_tuple_arg<F>(&mut self, _: usize, f: F) -> EncodingResult<()>
-        where F: FnOnce(&mut EncoderWriter<'a, W>) -> EncodingResult<()>
-    {
-        f(self)
-    }
-    fn emit_tuple_struct<F>(&mut self, _: &str, len: usize, f: F) -> EncodingResult<()>
-        where F: FnOnce(&mut EncoderWriter<'a, W>) -> EncodingResult<()>
-    {
-        self.emit_tuple(len, f)
-    }
-    fn emit_tuple_struct_arg<F>(&mut self, f_idx: usize, f: F) -> EncodingResult<()>
-        where F: FnOnce(&mut EncoderWriter<'a, W>) -> EncodingResult<()>
-    {
-        self.emit_tuple_arg(f_idx, f)
-    }
-    fn emit_option<F>(&mut self, f: F) -> EncodingResult<()>
-        where F: FnOnce(&mut EncoderWriter<'a, W>) -> EncodingResult<()>
-    {
-        f(self)
-    }
-    fn emit_option_none(&mut self) -> EncodingResult<()> {
-        self.writer.write_u8(0).map_err(wrap_io)
-    }
-    fn emit_option_some<F>(&mut self, f: F) -> EncodingResult<()>
-        where F: FnOnce(&mut EncoderWriter<'a, W>) -> EncodingResult<()>
-    {
-        try!(self.writer.write_u8(1).map_err(wrap_io));
-        f(self)
-    }
-    fn emit_seq<F>(&mut self, len: usize, f: F) -> EncodingResult<()>
-        where F: FnOnce(&mut EncoderWriter<'a, W>) -> EncodingResult<()>
-    {
-        try!(self.emit_usize(len));
-        f(self)
-    }
-    fn emit_seq_elt<F>(&mut self, _: usize, f: F) -> EncodingResult<()>
-        where F: FnOnce(&mut EncoderWriter<'a, W>) -> EncodingResult<()>
-    {
-        f(self)
-    }
-    fn emit_map<F>(&mut self, len: usize, f: F) -> EncodingResult<()>
-        where F: FnOnce(&mut EncoderWriter<'a, W>) -> EncodingResult<()>
-    {
-        try!(self.emit_usize(len));
-        f(self)
-    }
-    fn emit_map_elt_key<F>(&mut self, _: usize, f: F) -> EncodingResult<()>
-        where F: FnOnce(&mut EncoderWriter<'a, W>) -> EncodingResult<()>
-    {
-        f(self)
-    }
-    fn emit_map_elt_val<F>(&mut self, _: usize, f: F) -> EncodingResult<()>
-        where F: FnOnce(&mut EncoderWriter<'a, W>) -> EncodingResult<()>
-    {
-        f(self)
-    }
-
-}
-
-impl Encoder for SizeChecker {
-    type Error = EncodingError;
-
-    fn emit_nil(&mut self) -> EncodingResult<()> {
-        Ok(())
-    }
-    fn emit_usize(&mut self, v: usize) -> EncodingResult<()> {
-        self.add_value(v as u64)
-    }
-    fn emit_u64(&mut self, v: u64) -> EncodingResult<()> {
-        self.add_value(v)
-    }
-    fn emit_u32(&mut self, v: u32) -> EncodingResult<()> {
-        self.add_value(v)
-    }
-    fn emit_u16(&mut self, v: u16) -> EncodingResult<()> {
-        self.add_value(v)
-    }
-    fn emit_u8(&mut self, v: u8) -> EncodingResult<()> {
-        self.add_value(v)
-    }
-    fn emit_isize(&mut self, v: isize) -> EncodingResult<()> {
-        self.add_value(v as i64)
-    }
-    fn emit_i64(&mut self, v: i64) -> EncodingResult<()> {
-        self.add_value(v)
-    }
-    fn emit_i32(&mut self, v: i32) -> EncodingResult<()> {
-        self.add_value(v)
-    }
-    fn emit_i16(&mut self, v: i16) -> EncodingResult<()> {
-        self.add_value(v)
-    }
-    fn emit_i8(&mut self, v: i8) -> EncodingResult<()> {
-        self.add_value(v)
-    }
-    fn emit_bool(&mut self, _: bool) -> EncodingResult<()> {
-        self.add_value(0 as u8)
-    }
-    fn emit_f64(&mut self, v: f64) -> EncodingResult<()> {
-        self.add_value(v)
-    }
-    fn emit_f32(&mut self, v: f32) -> EncodingResult<()> {
-        self.add_value(v)
-    }
-    fn emit_char(&mut self, v: char) -> EncodingResult<()> {
-        self.add_raw(v.len_utf8())
-    }
-    fn emit_str(&mut self, v: &str) -> EncodingResult<()> {
-        try!(self.add_value(0 as u64));
-        self.add_raw(v.len())
-    }
-    fn emit_enum<F>(&mut self, __: &str, f: F) -> EncodingResult<()>
-        where F: FnOnce(&mut SizeChecker) -> EncodingResult<()>
-    {
-        f(self)
-    }
-    fn emit_enum_variant<F>(&mut self, _: &str, v_id: usize, _: usize, f: F) -> EncodingResult<()>
-        where F: FnOnce(&mut SizeChecker) -> EncodingResult<()>
-    {
-        try!(self.add_value(v_id as u32));
-        f(self)
-    }
-    fn emit_enum_variant_arg<F>(&mut self, _: usize, f: F) -> EncodingResult<()>
-        where F: FnOnce(&mut SizeChecker) -> EncodingResult<()>
-    {
-        f(self)
-    }
-    fn emit_enum_struct_variant<F>(&mut self,
-                                   _: &str,
-                                   _: usize,
-                                   _: usize,
-                                   f: F)
-                                   -> EncodingResult<()>
-        where F: FnOnce(&mut SizeChecker) -> EncodingResult<()>
-    {
-        f(self)
-    }
-    fn emit_enum_struct_variant_field<F>(&mut self, _: &str, _: usize, f: F) -> EncodingResult<()>
-        where F: FnOnce(&mut SizeChecker) -> EncodingResult<()>
-    {
-        f(self)
-    }
-    fn emit_struct<F>(&mut self, _: &str, _: usize, f: F) -> EncodingResult<()>
-        where F: FnOnce(&mut SizeChecker) -> EncodingResult<()>
-    {
-        f(self)
-    }
-    fn emit_struct_field<F>(&mut self, _: &str, _: usize, f: F) -> EncodingResult<()>
-        where F: FnOnce(&mut SizeChecker) -> EncodingResult<()>
-    {
-        f(self)
-    }
-    fn emit_tuple<F>(&mut self, _: usize, f: F) -> EncodingResult<()>
-        where F: FnOnce(&mut SizeChecker) -> EncodingResult<()>
-    {
-        f(self)
-    }
-    fn emit_tuple_arg<F>(&mut self, _: usize, f: F) -> EncodingResult<()>
-        where F: FnOnce(&mut SizeChecker) -> EncodingResult<()>
-    {
-        f(self)
-    }
-    fn emit_tuple_struct<F>(&mut self, _: &str, len: usize, f: F) -> EncodingResult<()>
-        where F: FnOnce(&mut SizeChecker) -> EncodingResult<()>
-    {
-        self.emit_tuple(len, f)
-    }
-    fn emit_tuple_struct_arg<F>(&mut self, f_idx: usize, f: F) -> EncodingResult<()>
-        where F: FnOnce(&mut SizeChecker) -> EncodingResult<()>
-    {
-        self.emit_tuple_arg(f_idx, f)
-    }
-    fn emit_option<F>(&mut self, f: F) -> EncodingResult<()>
-        where F: FnOnce(&mut SizeChecker) -> EncodingResult<()>
-    {
-        f(self)
-    }
-    fn emit_option_none(&mut self) -> EncodingResult<()> {
-        self.add_value(0 as u8)
-    }
-    fn emit_option_some<F>(&mut self, f: F) -> EncodingResult<()>
-        where F: FnOnce(&mut SizeChecker) -> EncodingResult<()>
-    {
-        try!(self.add_value(1 as u8));
-        f(self)
-    }
-    fn emit_seq<F>(&mut self, len: usize, f: F) -> EncodingResult<()>
-        where F: FnOnce(&mut SizeChecker) -> EncodingResult<()>
-    {
-        try!(self.emit_usize(len));
-        f(self)
-    }
-    fn emit_seq_elt<F>(&mut self, _: usize, f: F) -> EncodingResult<()>
-        where F: FnOnce(&mut SizeChecker) -> EncodingResult<()>
-    {
-        f(self)
-    }
-    fn emit_map<F>(&mut self, len: usize, f: F) -> EncodingResult<()>
-        where F: FnOnce(&mut SizeChecker) -> EncodingResult<()>
-    {
-        try!(self.emit_usize(len));
-        f(self)
-    }
-    fn emit_map_elt_key<F>(&mut self, _: usize, f: F) -> EncodingResult<()>
-        where F: FnOnce(&mut SizeChecker) -> EncodingResult<()>
-    {
-        f(self)
-    }
-    fn emit_map_elt_val<F>(&mut self, _: usize, f: F) -> EncodingResult<()>
-        where F: FnOnce(&mut SizeChecker) -> EncodingResult<()>
-    {
-        f(self)
-    }
-
-}
--- a/third_party/rust/bincode/src/serde/mod.rs
+++ b/third_party/rust/bincode/src/serde/mod.rs
@@ -1,45 +1,131 @@
 //! A collection of serialization and deserialization functions
 //! that use the `serde` crate for the serializable and deserializable
 //! implementation.
 
 use std::io::{Write, Read};
+use std::io::Error as IoError;
+use std::{error, fmt, result};
 use ::SizeLimit;
 
 pub use self::reader::{
     Deserializer,
-    DeserializeResult,
-    DeserializeError,
-    InvalidEncoding
 };
 
 pub use self::writer::{
     Serializer,
-    SerializeResult,
-    SerializeError,
 };
 
 use self::writer::SizeChecker;
 
 use serde_crate as serde;
 
 mod reader;
 mod writer;
 
+pub type Result<T> = result::Result<T, Error>;
+
+/// An error that can be produced during (de)serializing.
+///
+/// If decoding from a Buffer, assume that the buffer has been left
+/// in an invalid state.
+pub type Error = Box<ErrorKind>;
+
+#[derive(Debug)]
+pub enum ErrorKind {
+    /// If the error stems from the reader/writer that is being used
+    /// during (de)serialization, that error will be stored and returned here.
+    IoError(IoError),
+    /// If the bytes in the reader are not decodable because of an invalid
+    /// encoding, this error will be returned.  This error is only possible
+    /// if a stream is corrupted.  A stream produced from `encode` or `encode_into`
+    /// should **never** produce an InvalidEncoding error.
+    InvalidEncoding{
+        desc: &'static str, 
+        detail: Option<String>
+    },
+    /// If (de)serializing a message takes more than the provided size limit, this
+    /// error is returned.
+    SizeLimit,
+    SequenceMustHaveLength,
+    Custom(String)
+}
+
+impl error::Error for ErrorKind {
+    fn description(&self) -> &str {
+        match *self {
+            ErrorKind::IoError(ref err) => error::Error::description(err),
+            ErrorKind::InvalidEncoding{desc, ..} => desc,
+            ErrorKind::SequenceMustHaveLength => "bincode can't encode infinite sequences",
+            ErrorKind::SizeLimit => "the size limit for decoding has been reached",
+            ErrorKind::Custom(ref msg) => msg,
+
+        }
+    }
+
+    fn cause(&self) -> Option<&error::Error> {
+        match *self {
+            ErrorKind::IoError(ref err) => err.cause(),
+            ErrorKind::InvalidEncoding{..} => None,
+            ErrorKind::SequenceMustHaveLength => None,
+            ErrorKind::SizeLimit => None,
+            ErrorKind::Custom(_) => None,
+        }
+    }
+}
+
+impl From<IoError> for Error {
+    fn from(err: IoError) -> Error {
+        ErrorKind::IoError(err).into()
+    }
+}
+
+impl fmt::Display for ErrorKind {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        match *self {
+            ErrorKind::IoError(ref ioerr) =>
+                write!(fmt, "IoError: {}", ioerr),
+            ErrorKind::InvalidEncoding{desc, detail: None}=>
+                write!(fmt, "InvalidEncoding: {}", desc),
+            ErrorKind::InvalidEncoding{desc, detail: Some(ref detail)}=>
+                write!(fmt, "InvalidEncoding: {} ({})", desc, detail),
+            ErrorKind::SequenceMustHaveLength =>
+                write!(fmt, "Bincode can only encode sequences and maps that have a knowable size ahead of time."),
+            ErrorKind::SizeLimit =>
+                write!(fmt, "SizeLimit"),
+            ErrorKind::Custom(ref s) =>
+                s.fmt(fmt),
+        }
+    }
+}
+
+impl serde::de::Error for Error {
+    fn custom<T: fmt::Display>(desc: T) -> Error {
+        ErrorKind::Custom(desc.to_string()).into()
+    }
+}
+
+impl serde::ser::Error for Error {
+    fn custom<T: fmt::Display>(msg: T) -> Self {
+        ErrorKind::Custom(msg.to_string()).into()
+    }
+} 
+
 /// Serializes an object directly into a `Writer`.
 ///
 /// If the serialization would take more bytes than allowed by `size_limit`, an error
 /// is returned and *no bytes* will be written into the `Writer`.
 ///
-/// If this returns an `SerializeError` (other than SizeLimit), assume that the
+/// If this returns an `Error` (other than SizeLimit), assume that the
 /// writer is in an invalid state, as writing could bail out in the middle of
 /// serializing.
-pub fn serialize_into<W, T>(writer: &mut W, value: &T, size_limit: SizeLimit) -> SerializeResult<()>
-    where W: Write, T: serde::Serialize,
+pub fn serialize_into<W: ?Sized, T: ?Sized>(writer: &mut W, value: &T, size_limit: SizeLimit) -> Result<()>
+    where W: Write, 
+          T: serde::Serialize,
 {
     match size_limit {
         SizeLimit::Infinite => { }
         SizeLimit::Bounded(x) => {
             let mut size_checker = SizeChecker::new(x);
             try!(value.serialize(&mut size_checker))
         }
     }
@@ -47,77 +133,78 @@ pub fn serialize_into<W, T>(writer: &mut
     let mut serializer = Serializer::new(writer);
     serde::Serialize::serialize(value, &mut serializer)
 }
 
 /// Serializes a serializable object into a `Vec` of bytes.
 ///
 /// If the serialization would take more bytes than allowed by `size_limit`,
 /// an error is returned.
-pub fn serialize<T>(value: &T, size_limit: SizeLimit) -> SerializeResult<Vec<u8>>
-    where T: serde::Serialize,
+pub fn serialize<T: ?Sized>(value: &T, size_limit: SizeLimit) -> Result<Vec<u8>>
+    where T: serde::Serialize
 {
     // Since we are putting values directly into a vector, we can do size
     // computation out here and pre-allocate a buffer of *exactly*
     // the right size.
     let mut writer = match size_limit {
         SizeLimit::Bounded(size_limit) => {
-            let actual_size = match serialized_size_bounded(value, size_limit) {
-                Some(actual_size) => actual_size,
-                None => { return Err(SerializeError::SizeLimit); }
-            };
+            let actual_size = try!(serialized_size_bounded(value, size_limit).ok_or(ErrorKind::SizeLimit));
             Vec::with_capacity(actual_size as usize)
         }
         SizeLimit::Infinite => Vec::new()
     };
 
     try!(serialize_into(&mut writer, value, SizeLimit::Infinite));
     Ok(writer)
 }
 
 /// Returns the size that an object would be if serialized using bincode.
 ///
 /// This is used internally as part of the check for encode_into, but it can
 /// be useful for preallocating buffers if thats your style.
-pub fn serialized_size<T: serde::Serialize>(value: &T) -> u64 {
+pub fn serialized_size<T: ?Sized>(value: &T) -> u64 
+    where T: serde::Serialize
+{
     use std::u64::MAX;
     let mut size_checker = SizeChecker::new(MAX);
     value.serialize(&mut size_checker).ok();
     size_checker.written
 }
 
 /// Given a maximum size limit, check how large an object would be if it
 /// were to be serialized.
 ///
 /// If it can be serialized in `max` or fewer bytes, that number will be returned
 /// inside `Some`.  If it goes over bounds, then None is returned.
-pub fn serialized_size_bounded<T: serde::Serialize>(value: &T, max: u64) -> Option<u64> {
+pub fn serialized_size_bounded<T: ?Sized>(value: &T, max: u64) -> Option<u64> 
+    where T: serde::Serialize
+{
     let mut size_checker = SizeChecker::new(max);
     value.serialize(&mut size_checker).ok().map(|_| size_checker.written)
 }
 
 /// Deserializes an object directly from a `Buffer`ed Reader.
 ///
 /// If the provided `SizeLimit` is reached, the deserialization will bail immediately.
 /// A SizeLimit can help prevent an attacker from flooding your server with
 /// a neverending stream of values that runs your server out of memory.
 ///
-/// If this returns an `DeserializeError`, assume that the buffer that you passed
+/// If this returns an `Error`, assume that the buffer that you passed
 /// in is in an invalid state, as the error could be returned during any point
 /// in the reading.
-pub fn deserialize_from<R, T>(reader: &mut R, size_limit: SizeLimit) -> DeserializeResult<T>
+pub fn deserialize_from<R: ?Sized, T>(reader: &mut R, size_limit: SizeLimit) -> Result<T>
     where R: Read,
           T: serde::Deserialize,
 {
     let mut deserializer = Deserializer::new(reader, size_limit);
     serde::Deserialize::deserialize(&mut deserializer)
 }
 
 /// Deserializes a slice of bytes into an object.
 ///
 /// This method does not have a size-limit because if you already have the bytes
 /// in memory, then you don't gain anything by having a limiter.
-pub fn deserialize<T>(bytes: &[u8]) -> DeserializeResult<T>
+pub fn deserialize<T>(bytes: &[u8]) -> Result<T>
     where T: serde::Deserialize,
 {
     let mut reader = bytes;
     deserialize_from(&mut reader, SizeLimit::Infinite)
 }
--- a/third_party/rust/bincode/src/serde/reader.rs
+++ b/third_party/rust/bincode/src/serde/reader.rs
@@ -1,284 +1,158 @@
 use std::io::Read;
-use std::io::Error as IoError;
-use std::error::Error;
-use std::fmt;
-use std::convert::From;
 
 use byteorder::{BigEndian, ReadBytesExt};
-use num_traits;
 use serde_crate as serde;
 use serde_crate::de::value::ValueDeserializer;
-
+use serde_crate::de::Error as DeError;
 use ::SizeLimit;
-
-#[derive(Eq, PartialEq, Clone, Debug)]
-pub struct InvalidEncoding {
-    pub desc: &'static str,
-    pub detail: Option<String>,
-}
-
-impl fmt::Display for InvalidEncoding {
-    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
-        match *self {
-            InvalidEncoding { detail: None, desc } =>
-                write!(fmt, "{}", desc),
-            InvalidEncoding { detail: Some(ref detail), desc } =>
-                write!(fmt, "{} ({})", desc, detail)
-        }
-    }
-}
-
-/// An error that can be produced during decoding.
-///
-/// If decoding from a Buffer, assume that the buffer has been left
-/// in an invalid state.
-#[derive(Debug)]
-pub enum DeserializeError {
-    /// If the error stems from the reader that is being used
-    /// during decoding, that error will be stored and returned here.
-    IoError(IoError),
-    /// If the bytes in the reader are not decodable because of an invalid
-    /// encoding, this error will be returned.  This error is only possible
-    /// if a stream is corrupted.  A stream produced from `encode` or `encode_into`
-    /// should **never** produce an InvalidEncoding error.
-    InvalidEncoding(InvalidEncoding),
-    /// If decoding a message takes more than the provided size limit, this
-    /// error is returned.
-    SizeLimit,
-    Serde(serde::de::value::Error)
-}
-
-impl Error for DeserializeError {
-    fn description(&self) -> &str {
-        match *self {
-            DeserializeError::IoError(ref err) => Error::description(err),
-            DeserializeError::InvalidEncoding(ref ib) => ib.desc,
-            DeserializeError::SizeLimit => "the size limit for decoding has been reached",
-            DeserializeError::Serde(ref s) => s.description(),
-
-        }
-    }
-
-    fn cause(&self) -> Option<&Error> {
-        match *self {
-            DeserializeError::IoError(ref err) => err.cause(),
-            DeserializeError::InvalidEncoding(_) => None,
-            DeserializeError::SizeLimit => None,
-            DeserializeError::Serde(ref s) => s.cause(),
-        }
-    }
-}
-
-impl From<IoError> for DeserializeError {
-    fn from(err: IoError) -> DeserializeError {
-        DeserializeError::IoError(err)
-    }
-}
-
-impl From<serde::de::value::Error> for DeserializeError {
-    fn from(err: serde::de::value::Error) -> DeserializeError {
-        DeserializeError::Serde(err)
-    }
-}
-
-impl fmt::Display for DeserializeError {
-    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
-        match *self {
-            DeserializeError::IoError(ref ioerr) =>
-                write!(fmt, "IoError: {}", ioerr),
-            DeserializeError::InvalidEncoding(ref ib) =>
-                write!(fmt, "InvalidEncoding: {}", ib),
-            DeserializeError::SizeLimit =>
-                write!(fmt, "SizeLimit"),
-            DeserializeError::Serde(ref s) =>
-                s.fmt(fmt),
-        }
-    }
-}
-
-impl serde::de::Error for DeserializeError {
-    fn custom<T: Into<String>>(desc: T) -> DeserializeError {
-        DeserializeError::Serde(serde::de::value::Error::Custom(desc.into()))
-    }
-
-    fn end_of_stream() -> DeserializeError {
-        DeserializeError::Serde(serde::de::value::Error::EndOfStream)
-    }
-}
-
-pub type DeserializeResult<T> = Result<T, DeserializeError>;
-
+use super::{Result, Error, ErrorKind};
 
 /// A Deserializer that reads bytes from a buffer.
 ///
 /// This struct should rarely be used.
 /// In most cases, prefer the `decode_from` function.
 ///
 /// ```rust,ignore
 /// let d = Deserializer::new(&mut some_reader, SizeLimit::new());
 /// serde::Deserialize::deserialize(&mut deserializer);
 /// let bytes_read = d.bytes_read();
 /// ```
-pub struct Deserializer<'a, R: 'a> {
-    reader: &'a mut R,
+pub struct Deserializer<R> {
+    reader: R,
     size_limit: SizeLimit,
     read: u64
 }
 
-impl<'a, R: Read> Deserializer<'a, R> {
-    pub fn new(r: &'a mut R, size_limit: SizeLimit) -> Deserializer<'a, R> {
+impl<R: Read> Deserializer<R> {
+    pub fn new(r: R, size_limit: SizeLimit) -> Deserializer<R> {
         Deserializer {
             reader: r,
             size_limit: size_limit,
             read: 0
         }
     }
 
     /// Returns the number of bytes read from the contained Reader.
     pub fn bytes_read(&self) -> u64 {
         self.read
     }
 
-    fn read_bytes(&mut self, count: u64) -> Result<(), DeserializeError> {
+    fn read_bytes(&mut self, count: u64) -> Result<()> {
         self.read += count;
         match self.size_limit {
             SizeLimit::Infinite => Ok(()),
             SizeLimit::Bounded(x) if self.read <= x => Ok(()),
-            SizeLimit::Bounded(_) => Err(DeserializeError::SizeLimit)
+            SizeLimit::Bounded(_) => Err(ErrorKind::SizeLimit.into())
         }
     }
 
-    fn read_type<T>(&mut self) -> Result<(), DeserializeError> {
+    fn read_type<T>(&mut self) -> Result<()> {
         use std::mem::size_of;
         self.read_bytes(size_of::<T>() as u64)
     }
 
-    fn read_string(&mut self) -> DeserializeResult<String> {
-        let len = try!(serde::Deserialize::deserialize(self));
+    fn read_string(&mut self) -> Result<String> {
+        let len = try!(serde::Deserialize::deserialize(&mut *self));
         try!(self.read_bytes(len));
 
         let mut buffer = Vec::new();
         try!(self.reader.by_ref().take(len as u64).read_to_end(&mut buffer));
 
         String::from_utf8(buffer).map_err(|err|
-            DeserializeError::InvalidEncoding(InvalidEncoding {
+            ErrorKind::InvalidEncoding{
                 desc: "error while decoding utf8 string",
                 detail: Some(format!("Deserialize error: {}", err))
-            }))
+            }.into())
     }
 }
 
 macro_rules! impl_nums {
     ($ty:ty, $dser_method:ident, $visitor_method:ident, $reader_method:ident) => {
         #[inline]
-        fn $dser_method<V>(&mut self, mut visitor: V) -> DeserializeResult<V::Value>
+        fn $dser_method<V>(self, visitor: V) -> Result<V::Value>
             where V: serde::de::Visitor,
         {
             try!(self.read_type::<$ty>());
             let value = try!(self.reader.$reader_method::<BigEndian>());
             visitor.$visitor_method(value)
         }
     }
 }
 
 
-impl<'a, R: Read> serde::Deserializer for Deserializer<'a, R> {
-    type Error = DeserializeError;
+impl<'a, R: Read> serde::Deserializer for &'a mut Deserializer<R> {
+    type Error = Error;
 
     #[inline]
-    fn deserialize<V>(&mut self, _visitor: V) -> DeserializeResult<V::Value>
+    fn deserialize<V>(self, _visitor: V) -> Result<V::Value>
         where V: serde::de::Visitor,
     {
         let message = "bincode does not support Deserializer::deserialize";
-        Err(DeserializeError::Serde(serde::de::value::Error::Custom(message.into())))
+        Err(Error::custom(message))
     }
 
-    fn deserialize_bool<V>(&mut self, mut visitor: V) -> DeserializeResult<V::Value>
+    fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value>
         where V: serde::de::Visitor,
     {
         let value: u8 = try!(serde::Deserialize::deserialize(self));
         match value {
             1 => visitor.visit_bool(true),
             0 => visitor.visit_bool(false),
             value => {
-                Err(DeserializeError::InvalidEncoding(InvalidEncoding {
+                Err(ErrorKind::InvalidEncoding{
                     desc: "invalid u8 when decoding bool",
                     detail: Some(format!("Expected 0 or 1, got {}", value))
-                }))
+                }.into())
             }
         }
     }
 
     impl_nums!(u16, deserialize_u16, visit_u16, read_u16);
     impl_nums!(u32, deserialize_u32, visit_u32, read_u32);
     impl_nums!(u64, deserialize_u64, visit_u64, read_u64);
     impl_nums!(i16, deserialize_i16, visit_i16, read_i16);
     impl_nums!(i32, deserialize_i32, visit_i32, read_i32);
     impl_nums!(i64, deserialize_i64, visit_i64, read_i64);
     impl_nums!(f32, deserialize_f32, visit_f32, read_f32);
     impl_nums!(f64, deserialize_f64, visit_f64, read_f64);
 
 
     #[inline]
-    fn deserialize_u8<V>(&mut self, mut visitor: V) -> DeserializeResult<V::Value>
+    fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value>
         where V: serde::de::Visitor,
     {
         try!(self.read_type::<u8>());
         visitor.visit_u8(try!(self.reader.read_u8()))
     }
 
     #[inline]
-    fn deserialize_usize<V>(&mut self, mut visitor: V) -> DeserializeResult<V::Value>
-        where V: serde::de::Visitor,
-    {
-        try!(self.read_type::<u64>());
-        let value = try!(self.reader.read_u64::<BigEndian>());
-        match num_traits::cast(value) {
-            Some(value) => visitor.visit_usize(value),
-            None => Err(DeserializeError::Serde(serde::de::value::Error::Custom("expected usize".into())))
-        }
-    }
-
-    #[inline]
-    fn deserialize_i8<V>(&mut self, mut visitor: V) -> DeserializeResult<V::Value>
+    fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value>
         where V: serde::de::Visitor,
     {
         try!(self.read_type::<i8>());
         visitor.visit_i8(try!(self.reader.read_i8()))
     }
 
-    #[inline]
-    fn deserialize_isize<V>(&mut self, mut visitor: V) -> DeserializeResult<V::Value>
-        where V: serde::de::Visitor,
-    {
-        try!(self.read_type::<i64>());
-        let value = try!(self.reader.read_i64::<BigEndian>());
-        match num_traits::cast(value) {
-            Some(value) => visitor.visit_isize(value),
-            None => Err(DeserializeError::Serde(serde::de::value::Error::Custom("expected isize".into()))),
-        }
-    }
-
-    fn deserialize_unit<V>(&mut self, mut visitor: V) -> DeserializeResult<V::Value>
+    fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value>
         where V: serde::de::Visitor,
     {
         visitor.visit_unit()
     }
 
-    fn deserialize_char<V>(&mut self, mut visitor: V) -> DeserializeResult<V::Value>
+    fn deserialize_char<V>(self, visitor: V) -> Result<V::Value>
         where V: serde::de::Visitor,
     {
         use std::str;
 
-        let error = DeserializeError::InvalidEncoding(InvalidEncoding {
+        let error = ErrorKind::InvalidEncoding{
             desc: "Invalid char encoding",
             detail: None
-        });
+        }.into();
 
         let mut buf = [0];
 
         let _ = try!(self.reader.read(&mut buf[..]));
         let first_byte = buf[0];
         let width = utf8_char_width(first_byte);
         if width == 1 { return visitor.visit_char(first_byte as char) }
         if width == 0 { return Err(error)}
@@ -298,255 +172,245 @@ impl<'a, R: Read> serde::Deserializer fo
         let res = try!(match str::from_utf8(&buf[..width]).ok() {
             Some(s) => Ok(s.chars().next().unwrap()),
             None => Err(error)
         });
 
         visitor.visit_char(res)
     }
 
-    fn deserialize_str<V>(&mut self, mut visitor: V) -> DeserializeResult<V::Value>
+    fn deserialize_str<V>(self, visitor: V) -> Result<V::Value>
         where V: serde::de::Visitor,
     {
         visitor.visit_str(&try!(self.read_string()))
     }
 
-    fn deserialize_string<V>(&mut self, mut visitor: V) -> DeserializeResult<V::Value>
+    fn deserialize_string<V>(self, visitor: V) -> Result<V::Value>
         where V: serde::de::Visitor,
     {
         visitor.visit_string(try!(self.read_string()))
     }
 
-    fn deserialize_bytes<V>(&mut self, visitor: V) -> DeserializeResult<V::Value>
+    fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value>
+        where V: serde::de::Visitor,
+    {
+        self.deserialize_seq(visitor)
+    }
+
+    fn deserialize_byte_buf<V>(self, visitor: V) -> Result<V::Value>
         where V: serde::de::Visitor,
     {
         self.deserialize_seq(visitor)
     }
 
-    fn deserialize_enum<V>(&mut self,
+    fn deserialize_enum<V>(self,
                      _enum: &'static str,
                      _variants: &'static [&'static str],
-                     mut visitor: V) -> Result<V::Value, Self::Error>
-        where V: serde::de::EnumVisitor,
-    {
-        visitor.visit(self)
-    }
-
-    fn deserialize_tuple<V>(&mut self,
-                      _len: usize,
-                      mut visitor: V) -> DeserializeResult<V::Value>
+                     visitor: V) -> Result<V::Value>
         where V: serde::de::Visitor,
     {
-        struct TupleVisitor<'a, 'b: 'a, R: Read + 'b>(&'a mut Deserializer<'b, R>);
+        impl<'a, R: Read + 'a> serde::de::EnumVisitor for &'a mut Deserializer<R> {
+            type Error = Error;
+            type Variant = Self;
 
-        impl<'a, 'b: 'a, R: Read + 'b> serde::de::SeqVisitor for TupleVisitor<'a, 'b, R> {
-            type Error = DeserializeError;
+            fn visit_variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant)>
+                where V: serde::de::DeserializeSeed,
+            {
+                let idx: u32 = try!(serde::de::Deserialize::deserialize(&mut *self));
+                let val: Result<_> = seed.deserialize(idx.into_deserializer());
+                Ok((try!(val), self))
+            }
+        }
 
-            fn visit<T>(&mut self) -> Result<Option<T>, Self::Error>
-                where T: serde::de::Deserialize,
+        visitor.visit_enum(self)
+    }
+    
+    fn deserialize_tuple<V>(self,
+                      _len: usize,
+                      visitor: V) -> Result<V::Value>
+        where V: serde::de::Visitor,
+    {
+        struct TupleVisitor<'a, R: Read + 'a>(&'a mut Deserializer<R>);
+
+        impl<'a, 'b: 'a, R: Read + 'b> serde::de::SeqVisitor for TupleVisitor<'a, R> {
+            type Error = Error;
+
+            fn visit_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
+                where T: serde::de::DeserializeSeed,
             {
-                let value = try!(serde::Deserialize::deserialize(self.0));
+                let value = try!(serde::de::DeserializeSeed::deserialize(seed, &mut *self.0));
                 Ok(Some(value))
             }
-
-            fn end(&mut self) -> Result<(), Self::Error> {
-                Ok(())
-            }
         }
 
         visitor.visit_seq(TupleVisitor(self))
     }
 
-    fn deserialize_seq_fixed_size<V>(&mut self,
-                            _: usize,
-                            visitor: V) -> DeserializeResult<V::Value>
-        where V: serde::de::Visitor,
-    {
-        self.deserialize_seq(visitor)
-    }
-
-    fn deserialize_option<V>(&mut self, mut visitor: V) -> DeserializeResult<V::Value>
+    fn deserialize_seq_fixed_size<V>(self,
+                            len: usize,
+                            visitor: V) -> Result<V::Value>
         where V: serde::de::Visitor,
     {
-        let value: u8 = try!(serde::de::Deserialize::deserialize(self));
-        match value {
-            0 => visitor.visit_none(),
-            1 => visitor.visit_some(self),
-            _ => Err(DeserializeError::InvalidEncoding(InvalidEncoding {
-                desc: "invalid tag when decoding Option",
-                detail: Some(format!("Expected 0 or 1, got {}", value))
-            })),
-        }
-    }
-
-    fn deserialize_seq<V>(&mut self, mut visitor: V) -> DeserializeResult<V::Value>
-        where V: serde::de::Visitor,
-    {
-        struct SeqVisitor<'a, 'b: 'a, R: Read + 'b> {
-            deserializer: &'a mut Deserializer<'b, R>,
+        struct SeqVisitor<'a, R: Read + 'a> {
+            deserializer: &'a mut Deserializer<R>,
             len: usize,
         }
 
-        impl<'a, 'b: 'a, R: Read + 'b> serde::de::SeqVisitor for SeqVisitor<'a, 'b, R> {
-            type Error = DeserializeError;
+        impl<'a, 'b: 'a, R: Read + 'b> serde::de::SeqVisitor for SeqVisitor<'a, R> {
+            type Error = Error;
 
-            fn visit<T>(&mut self) -> Result<Option<T>, Self::Error>
-                where T: serde::de::Deserialize,
+            fn visit_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
+                where T: serde::de::DeserializeSeed,
             {
                 if self.len > 0 {
                     self.len -= 1;
-                    let value = try!(serde::Deserialize::deserialize(self.deserializer));
+                    let value = try!(serde::de::DeserializeSeed::deserialize(seed, &mut *self.deserializer));
                     Ok(Some(value))
                 } else {
                     Ok(None)
                 }
             }
-
-            fn end(&mut self) -> Result<(), Self::Error> {
-                if self.len == 0 {
-                    Ok(())
-                } else {
-                    Err(DeserializeError::Serde(serde::de::value::Error::Custom("expected end".into())))
-                }
-            }
         }
 
-        let len = try!(serde::Deserialize::deserialize(self));
-
         visitor.visit_seq(SeqVisitor { deserializer: self, len: len })
     }
 
-    fn deserialize_map<V>(&mut self, mut visitor: V) -> DeserializeResult<V::Value>
+    fn deserialize_option<V>(self, visitor: V) -> Result<V::Value>
         where V: serde::de::Visitor,
     {
-        struct MapVisitor<'a, 'b: 'a, R: Read + 'b> {
-            deserializer: &'a mut Deserializer<'b, R>,
+        let value: u8 = try!(serde::de::Deserialize::deserialize(&mut *self));
+        match value {
+            0 => visitor.visit_none(),
+            1 => visitor.visit_some(&mut *self),
+            _ => Err(ErrorKind::InvalidEncoding{
+                desc: "invalid tag when decoding Option",
+                detail: Some(format!("Expected 0 or 1, got {}", value))
+            }.into()),
+        }
+    }
+
+    fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value>
+        where V: serde::de::Visitor,
+    {
+        let len = try!(serde::Deserialize::deserialize(&mut *self));
+
+        self.deserialize_seq_fixed_size(len, visitor)
+    }
+
+    fn deserialize_map<V>(self, visitor: V) -> Result<V::Value>
+        where V: serde::de::Visitor,
+    {
+        struct MapVisitor<'a, R: Read + 'a> {
+            deserializer: &'a mut Deserializer<R>,
             len: usize,
         }
 
-        impl<'a, 'b: 'a, R: Read + 'b> serde::de::MapVisitor for MapVisitor<'a, 'b, R> {
-            type Error = DeserializeError;
+        impl<'a, 'b: 'a, R: Read + 'b> serde::de::MapVisitor for MapVisitor<'a, R> {
+            type Error = Error;
 
-            fn visit_key<K>(&mut self) -> Result<Option<K>, Self::Error>
-                where K: serde::de::Deserialize,
+            fn visit_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>>
+                where K: serde::de::DeserializeSeed,
             {
                 if self.len > 0 {
                     self.len -= 1;
-                    let key = try!(serde::Deserialize::deserialize(self.deserializer));
+                    let key = try!(serde::de::DeserializeSeed::deserialize(seed, &mut *self.deserializer));
                     Ok(Some(key))
                 } else {
                     Ok(None)
                 }
             }
 
-            fn visit_value<V>(&mut self) -> Result<V, Self::Error>
-                where V: serde::de::Deserialize,
+            fn visit_value_seed<V>(&mut self, seed: V) -> Result<V::Value>
+                where V: serde::de::DeserializeSeed,
             {
-                let value = try!(serde::Deserialize::deserialize(self.deserializer));
+                let value = try!(serde::de::DeserializeSeed::deserialize(seed, &mut *self.deserializer));
                 Ok(value)
             }
-
-            fn end(&mut self) -> Result<(), Self::Error> {
-                if self.len == 0 {
-                    Ok(())
-                } else {
-                    Err(DeserializeError::Serde(serde::de::value::Error::Custom("expected end".into())))
-                }
-            }
         }
 
-        let len = try!(serde::Deserialize::deserialize(self));
+        let len = try!(serde::Deserialize::deserialize(&mut *self));
 
         visitor.visit_map(MapVisitor { deserializer: self, len: len })
     }
 
-    fn deserialize_struct<V>(&mut self,
+    fn deserialize_struct<V>(self,
                        _name: &str,
                        fields: &'static [&'static str],
-                       visitor: V) -> DeserializeResult<V::Value>
+                       visitor: V) -> Result<V::Value>
         where V: serde::de::Visitor,
     {
         self.deserialize_tuple(fields.len(), visitor)
     }
 
-    fn deserialize_struct_field<V>(&mut self,
-                                   _visitor: V) -> DeserializeResult<V::Value>
+    fn deserialize_struct_field<V>(self,
+                                   _visitor: V) -> Result<V::Value>
         where V: serde::de::Visitor,
     {
         let message = "bincode does not support Deserializer::deserialize_struct_field";
-        Err(DeserializeError::Serde(serde::de::value::Error::Custom(message.into())))
+        Err(Error::custom(message))
     }
 
-    fn deserialize_newtype_struct<V>(&mut self,
+    fn deserialize_newtype_struct<V>(self,
                                _name: &str,
-                               mut visitor: V) -> DeserializeResult<V::Value>
+                               visitor: V) -> Result<V::Value>
         where V: serde::de::Visitor,
     {
         visitor.visit_newtype_struct(self)
     }
 
-    fn deserialize_unit_struct<V>(&mut self,
+    fn deserialize_unit_struct<V>(self,
                                   _name: &'static str,
-                                  mut visitor: V) -> DeserializeResult<V::Value>
+                                  visitor: V) -> Result<V::Value>
         where V: serde::de::Visitor,
     {
         visitor.visit_unit()
     }
 
-    fn deserialize_tuple_struct<V>(&mut self,
+    fn deserialize_tuple_struct<V>(self,
                                    _name: &'static str,
                                    len: usize,
-                                   visitor: V) -> DeserializeResult<V::Value>
+                                   visitor: V) -> Result<V::Value>
         where V: serde::de::Visitor,
     {
         self.deserialize_tuple(len, visitor)
     }
 
-    fn deserialize_ignored_any<V>(&mut self,
-                                  _visitor: V) -> DeserializeResult<V::Value>
+    fn deserialize_ignored_any<V>(self,
+                                  _visitor: V) -> Result<V::Value>
         where V: serde::de::Visitor,
     {
         let message = "bincode does not support Deserializer::deserialize_ignored_any";
-        Err(DeserializeError::Serde(serde::de::value::Error::Custom(message.into())))
+        Err(Error::custom(message))
     }
 }
 
-impl<'a, R: Read> serde::de::VariantVisitor for Deserializer<'a, R> {
-    type Error = DeserializeError;
+impl<'a, R: Read> serde::de::VariantVisitor for &'a mut Deserializer<R> {
+    type Error = Error;
 
-    fn visit_variant<V>(&mut self) -> Result<V, Self::Error>
-        where V: serde::Deserialize,
-    {
-        let index: u32 = try!(serde::Deserialize::deserialize(self));
-        let mut deserializer = (index as usize).into_deserializer();
-        let attempt: Result<V, serde::de::value::Error> = serde::Deserialize::deserialize(&mut deserializer);
-        Ok(try!(attempt))
-    }
-
-    fn visit_unit(&mut self) -> Result<(), Self::Error> {
+    fn visit_unit(self) -> Result<()> {
         Ok(())
     }
 
-    fn visit_newtype<T>(&mut self) -> Result<T, Self::Error>
-        where T: serde::de::Deserialize,
+    fn visit_newtype_seed<T>(self, seed: T) -> Result<T::Value>
+        where T: serde::de::DeserializeSeed,
     {
-        serde::de::Deserialize::deserialize(self)
+        serde::de::DeserializeSeed::deserialize(seed, self)
     }
 
-    fn visit_tuple<V>(&mut self,
+    fn visit_tuple<V>(self,
                       len: usize,
-                      visitor: V) -> Result<V::Value, Self::Error>
+                      visitor: V) -> Result<V::Value>
         where V: serde::de::Visitor,
     {
         serde::de::Deserializer::deserialize_tuple(self, len, visitor)
     }
 
-    fn visit_struct<V>(&mut self,
+    fn visit_struct<V>(self,
                        fields: &'static [&'static str],
-                       visitor: V) -> Result<V::Value, Self::Error>
+                       visitor: V) -> Result<V::Value>
         where V: serde::de::Visitor,
     {
         serde::de::Deserializer::deserialize_tuple(self, fields.len(), visitor)
     }
 }
 static UTF8_CHAR_WIDTH: [u8; 256] = [
 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x1F
--- a/third_party/rust/bincode/src/serde/writer.rs
+++ b/third_party/rust/bincode/src/serde/writer.rs
@@ -1,331 +1,196 @@
-use std::error::Error;
-use std::fmt;
-use std::io::Error as IoError;
 use std::io::Write;
 use std::u32;
 
 use serde_crate as serde;
 
 use byteorder::{BigEndian, WriteBytesExt};
 
-pub type SerializeResult<T> = Result<T, SerializeError>;
-
-
-/// An error that can be produced during encoding.