Bug 1416258 - Update webrender to commit 8a39cf24f493e894a66c2465dd310a2b2923e558. r?jrmuizel draft
authorKartikaya Gupta <kgupta@mozilla.com>
Tue, 14 Nov 2017 09:19:11 -0500
changeset 697749 cddc344524f3e3e58d72bfb884e69901274dca55
parent 697748 077422bf7047cbfc8490aaa193bef3f0af72fb6a
child 740186 adfeb72b2accbb6b24bc97046981281ecce01195
push id89075
push userkgupta@mozilla.com
push dateTue, 14 Nov 2017 14:21:46 +0000
reviewersjrmuizel
bugs1416258
milestone59.0a1
Bug 1416258 - Update webrender to commit 8a39cf24f493e894a66c2465dd310a2b2923e558. r?jrmuizel MozReview-Commit-ID: IjTJa62Op9b
gfx/doc/README.webrender
gfx/webrender/Cargo.toml
gfx/webrender/res/cs_text_run.glsl
gfx/webrender/res/debug_color.glsl
gfx/webrender/res/debug_font.glsl
gfx/webrender/res/ps_blend.glsl
gfx/webrender/res/ps_composite.glsl
gfx/webrender/res/ps_line.glsl
gfx/webrender/res/ps_yuv_image.glsl
gfx/webrender/src/border.rs
gfx/webrender/src/clip.rs
gfx/webrender/src/clip_scroll_node.rs
gfx/webrender/src/clip_scroll_tree.rs
gfx/webrender/src/debug_render.rs
gfx/webrender/src/device.rs
gfx/webrender/src/frame.rs
gfx/webrender/src/frame_builder.rs
gfx/webrender/src/glyph_cache.rs
gfx/webrender/src/glyph_rasterizer.rs
gfx/webrender/src/gpu_cache.rs
gfx/webrender/src/gpu_types.rs
gfx/webrender/src/picture.rs
gfx/webrender/src/platform/macos/font.rs
gfx/webrender/src/platform/unix/font.rs
gfx/webrender/src/platform/windows/font.rs
gfx/webrender/src/prim_store.rs
gfx/webrender/src/profiler.rs
gfx/webrender/src/render_backend.rs
gfx/webrender/src/render_task.rs
gfx/webrender/src/renderer.rs
gfx/webrender/src/resource_cache.rs
gfx/webrender/src/tiling.rs
gfx/webrender_api/Cargo.toml
gfx/webrender_api/src/api.rs
gfx/webrender_api/src/color.rs
gfx/webrender_api/src/display_list.rs
gfx/webrender_api/src/font.rs
gfx/webrender_bindings/Cargo.toml
toolkit/library/gtest/rust/Cargo.lock
toolkit/library/rust/Cargo.lock
--- a/gfx/doc/README.webrender
+++ b/gfx/doc/README.webrender
@@ -170,9 +170,9 @@ 2. Sometimes autoland tip has changed en
    has an env var you can set to do this). In theory you can get the same
    result by resolving the conflict manually but Cargo.lock files are usually not
    trivial to merge by hand. If it's just the third_party/rust dir that has conflicts
    you can delete it and run |mach vendor rust| again to repopulate it.
 
 -------------------------------------------------------------------------------
 
 The version of WebRender currently in the tree is:
-f58ed651b47f47382b63dd2bce6e4ed10ee18c78
+8a39cf24f493e894a66c2465dd310a2b2923e558
--- a/gfx/webrender/Cargo.toml
+++ b/gfx/webrender/Cargo.toml
@@ -1,11 +1,11 @@
 [package]
 name = "webrender"
-version = "0.53.1"
+version = "0.53.2"
 authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
 license = "MPL-2.0"
 repository = "https://github.com/servo/webrender"
 build = "build.rs"
 
 [features]
 default = ["freetype-lib"]
 freetype-lib = ["freetype/servo-freetype-sys"]
--- a/gfx/webrender/res/cs_text_run.glsl
+++ b/gfx/webrender/res/cs_text_run.glsl
@@ -49,11 +49,11 @@ void main(void) {
 
     gl_Position = uTransform * vec4(pos, 0.0, 1.0);
 }
 #endif
 
 #ifdef WR_FRAGMENT_SHADER
 void main(void) {
     float a = texture(sColor0, vUv).a;
-    oFragColor = vec4(vColor.rgb, vColor.a * a);
+    oFragColor = vColor * a;
 }
 #endif
--- a/gfx/webrender/res/debug_color.glsl
+++ b/gfx/webrender/res/debug_color.glsl
@@ -5,17 +5,17 @@
 #include shared,shared_other
 
 varying vec4 vColor;
 
 #ifdef WR_VERTEX_SHADER
 in vec4 aColor;
 
 void main(void) {
-    vColor = aColor;
+    vColor = vec4(aColor.rgb * aColor.a, aColor.a);
     vec4 pos = vec4(aPosition, 1.0);
     pos.xy = floor(pos.xy * uDevicePixelRatio + 0.5) / uDevicePixelRatio;
     gl_Position = uTransform * pos;
 }
 #endif
 
 #ifdef WR_FRAGMENT_SHADER
 void main(void) {
--- a/gfx/webrender/res/debug_font.glsl
+++ b/gfx/webrender/res/debug_font.glsl
@@ -18,11 +18,11 @@ void main(void) {
     pos.xy = floor(pos.xy * uDevicePixelRatio + 0.5) / uDevicePixelRatio;
     gl_Position = uTransform * pos;
 }
 #endif
 
 #ifdef WR_FRAGMENT_SHADER
 void main(void) {
     float alpha = texture(sColor0, vec3(vColorTexCoord.xy, 0.0)).r;
-    oFragColor = vec4(vColor.xyz, vColor.w * alpha);
+    oFragColor = vColor * alpha;
 }
 #endif
--- a/gfx/webrender/res/ps_blend.glsl
+++ b/gfx/webrender/res/ps_blend.glsl
@@ -3,16 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include shared,prim_shared
 
 varying vec3 vUv;
 flat varying vec4 vUvBounds;
 flat varying float vAmount;
 flat varying int vOp;
+flat varying mat4 vColorMat;
 
 #ifdef WR_VERTEX_SHADER
 void main(void) {
     CompositeInstance ci = fetch_composite_instance();
     AlphaBatchTask dest_task = fetch_alpha_batch_task(ci.render_task_index);
     AlphaBatchTask src_task = fetch_alpha_batch_task(ci.src_task_index);
 
     vec2 dest_origin = dest_task.render_target_origin -
@@ -29,118 +30,89 @@ void main(void) {
 
     vec2 uv = src_task.render_target_origin + aPosition.xy * src_task.size;
     vUv = vec3(uv / texture_size, src_task.render_target_layer_index);
     vUvBounds = vec4(st0 + 0.5, st1 - 0.5) / texture_size.xyxy;
 
     vOp = ci.user_data0;
     vAmount = float(ci.user_data1) / 65535.0;
 
+    float lumR = 0.2126;
+    float lumG = 0.7152;
+    float lumB = 0.0722;
+    float oneMinusLumR = 1.0 - lumR;
+    float oneMinusLumG = 1.0 - lumG;
+    float oneMinusLumB = 1.0 - lumB;
+    float oneMinusAmount = 1.0 - vAmount;
+
+    switch (vOp) {
+        case 2: {
+            // Grayscale
+            vColorMat = mat4(vec4(lumR + oneMinusLumR * oneMinusAmount, lumR - lumR * oneMinusAmount, lumR - lumR * oneMinusAmount, 0.0),
+                             vec4(lumG - lumG * oneMinusAmount, lumG + oneMinusLumG * oneMinusAmount, lumG - lumG * oneMinusAmount, 0.0),
+                             vec4(lumB - lumB * oneMinusAmount, lumB - lumB * oneMinusAmount, lumB + oneMinusLumB * oneMinusAmount, 0.0),
+                             vec4(0.0, 0.0, 0.0, 1.0));
+            break;
+        }
+        case 3: {
+            // HueRotate
+            float c = cos(vAmount * 0.01745329251);
+            float s = sin(vAmount * 0.01745329251);
+            vColorMat = mat4(vec4(lumR + oneMinusLumR * c - lumR * s, lumR - lumR * c + 0.143 * s, lumR - lumR * c - oneMinusLumR * s, 0.0),
+                            vec4(lumG - lumG * c - lumG * s, lumG + oneMinusLumG * c + 0.140 * s, lumG - lumG * c + lumG * s, 0.0),
+                            vec4(lumB - lumB * c + oneMinusLumB * s, lumB - lumB * c - 0.283 * s, lumB + oneMinusLumB * c + lumB * s, 0.0),
+                            vec4(0.0, 0.0, 0.0, 1.0));
+            break;
+        }
+        case 5: {
+            // Saturate
+            vColorMat = mat4(vec4(oneMinusAmount * lumR + vAmount, oneMinusAmount * lumR, oneMinusAmount * lumR, 0.0),
+                             vec4(oneMinusAmount * lumG, oneMinusAmount * lumG + vAmount, oneMinusAmount * lumG, 0.0),
+                             vec4(oneMinusAmount * lumB, oneMinusAmount * lumB, oneMinusAmount * lumB + vAmount, 0.0),
+                             vec4(0.0, 0.0, 0.0, 1.0));
+            break;
+        }
+        case 6: {
+            // Sepia
+            vColorMat = mat4(vec4(0.393 + 0.607 * oneMinusAmount, 0.349 - 0.349 * oneMinusAmount, 0.272 - 0.272 * oneMinusAmount, 0.0),
+                             vec4(0.769 - 0.769 * oneMinusAmount, 0.686 + 0.314 * oneMinusAmount, 0.534 - 0.534 * oneMinusAmount, 0.0),
+                             vec4(0.189 - 0.189 * oneMinusAmount, 0.168 - 0.168 * oneMinusAmount, 0.131 + 0.869 * oneMinusAmount, 0.0),
+                             vec4(0.0, 0.0, 0.0, 1.0));
+            break;
+        }
+    }
+
+
     gl_Position = uTransform * vec4(local_pos, ci.z, 1.0);
 }
 #endif
 
 #ifdef WR_FRAGMENT_SHADER
 /* 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/. */
 
-vec3 rgbToHsv(vec3 c) {
-    float value = max(max(c.r, c.g), c.b);
-
-    float chroma = value - min(min(c.r, c.g), c.b);
-    if (chroma == 0.0) {
-        return vec3(0.0);
-    }
-    float saturation = chroma / value;
-
-    float hue;
-    if (c.r == value)
-        hue = (c.g - c.b) / chroma;
-    else if (c.g == value)
-        hue = 2.0 + (c.b - c.r) / chroma;
-    else // if (c.b == value)
-        hue = 4.0 + (c.r - c.g) / chroma;
-
-    hue *= 1.0/6.0;
-    if (hue < 0.0)
-        hue += 1.0;
-    return vec3(hue, saturation, value);
-}
-
-vec3 hsvToRgb(vec3 c) {
-    if (c.s == 0.0) {
-        return vec3(c.z);
-    }
-
-    float hue = c.x * 6.0;
-    int sector = int(hue);
-    float residualHue = hue - float(sector);
-
-    vec3 pqt = c.z * vec3(1.0 - c.y, 1.0 - c.y * residualHue, 1.0 - c.y * (1.0 - residualHue));
-    switch (sector) {
-        case 0:
-            return vec3(c.z, pqt.z, pqt.x);
-        case 1:
-            return vec3(pqt.y, c.z, pqt.x);
-        case 2:
-            return vec3(pqt.x, c.z, pqt.z);
-        case 3:
-            return vec3(pqt.x, pqt.y, c.z);
-        case 4:
-            return vec3(pqt.z, pqt.x, c.z);
-        default:
-            return vec3(c.z, pqt.x, pqt.y);
-    }
-}
-
 vec4 Blur(float radius, vec2 direction) {
     // TODO(gw): Support blur in WR2!
     return vec4(1.0);
 }
 
 vec4 Contrast(vec4 Cs, float amount) {
     return vec4(Cs.rgb * amount - 0.5 * amount + 0.5, 1.0);
 }
 
-vec4 Grayscale(vec4 Cs, float amount) {
-    float ia = 1.0 - amount;
-    return mat4(vec4(0.2126 + 0.7874 * ia, 0.2126 - 0.2126 * ia, 0.2126 - 0.2126 * ia, 0.0),
-                vec4(0.7152 - 0.7152 * ia, 0.7152 + 0.2848 * ia, 0.7152 - 0.7152 * ia, 0.0),
-                vec4(0.0722 - 0.0722 * ia, 0.0722 - 0.0722 * ia, 0.0722 + 0.9278 * ia, 0.0),
-                vec4(0.0, 0.0, 0.0, 1.0)) * Cs;
-}
-
-vec4 HueRotate(vec4 Cs, float amount) {
-    vec3 CsHsv = rgbToHsv(Cs.rgb);
-    CsHsv.x = mod(CsHsv.x + amount / 6.283185307179586, 1.0);
-    return vec4(hsvToRgb(CsHsv), Cs.a);
-}
-
 vec4 Invert(vec4 Cs, float amount) {
     Cs.rgb /= Cs.a;
 
     vec3 color = mix(Cs.rgb, vec3(1.0) - Cs.rgb, amount);
 
     // Pre-multiply the alpha into the output value.
     return vec4(color.rgb * Cs.a, Cs.a);
 }
 
-vec4 Saturate(vec4 Cs, float amount) {
-    return vec4(hsvToRgb(min(vec3(1.0, amount, 1.0) * rgbToHsv(Cs.rgb), vec3(1.0))), Cs.a);
-}
-
-vec4 Sepia(vec4 Cs, float amount) {
-    float ia = 1.0 - amount;
-    return mat4(vec4(0.393 + 0.607 * ia, 0.349 - 0.349 * ia, 0.272 - 0.272 * ia, 0.0),
-                vec4(0.769 - 0.769 * ia, 0.686 + 0.314 * ia, 0.534 - 0.534 * ia, 0.0),
-                vec4(0.189 - 0.189 * ia, 0.168 - 0.168 * ia, 0.131 + 0.869 * ia, 0.0),
-                vec4(0.0, 0.0, 0.0, 1.0)) * Cs;
-}
-
 vec4 Brightness(vec4 Cs, float amount) {
     // Un-premultiply the input.
     Cs.rgb /= Cs.a;
 
     // Apply the brightness factor.
     // Resulting color needs to be clamped to output range
     // since we are pre-multiplying alpha in the shader.
     vec3 color = clamp(Cs.rgb * amount, vec3(0.0), vec3(1.0));
@@ -164,32 +136,22 @@ void main(void) {
     switch (vOp) {
         case 0:
             // Gaussian blur is specially handled:
             oFragColor = Cs;// Blur(vAmount, vec2(0,0));
             break;
         case 1:
             oFragColor = Contrast(Cs, vAmount);
             break;
-        case 2:
-            oFragColor = Grayscale(Cs, vAmount);
-            break;
-        case 3:
-            oFragColor = HueRotate(Cs, vAmount);
-            break;
         case 4:
             oFragColor = Invert(Cs, vAmount);
             break;
-        case 5:
-            oFragColor = Saturate(Cs, vAmount);
-            break;
-        case 6:
-            oFragColor = Sepia(Cs, vAmount);
-            break;
         case 7:
             oFragColor = Brightness(Cs, vAmount);
             break;
         case 8:
             oFragColor = Opacity(Cs, vAmount);
             break;
+        default:
+            oFragColor = vColorMat * Cs;
     }
 }
 #endif
--- a/gfx/webrender/res/ps_composite.glsl
+++ b/gfx/webrender/res/ps_composite.glsl
@@ -293,11 +293,13 @@ void main(void) {
         case MixBlendMode_Luminosity:
             result.rgb = Luminosity(Cb.rgb, Cs.rgb);
             break;
     }
 
     result.rgb = (1.0 - Cb.a) * Cs.rgb + Cb.a * result.rgb;
     result.a = Cs.a;
 
+    result.rgb *= result.a;
+
     oFragColor = result;
 }
 #endif
--- a/gfx/webrender/res/ps_line.glsl
+++ b/gfx/webrender/res/ps_line.glsl
@@ -176,17 +176,17 @@ void main(void) {
 #else
     #ifdef WR_FEATURE_TRANSFORM
         alpha = 0.0;
         vec2 local_pos = init_transform_fs(vLocalPos, alpha);
     #else
         vec2 local_pos = vLocalPos;
     #endif
 
-        alpha = min(alpha, do_clip());
+        alpha *= do_clip();
 #endif
 
     // Find the appropriate distance to apply the step over.
     float aa_range = compute_aa_range(local_pos);
 
     // Select the x/y coord, depending on which axis this edge is.
     vec2 pos = mix(local_pos.xy, local_pos.yx, vAxisSelect);
 
@@ -254,11 +254,11 @@ void main(void) {
             if (half_line_thickness <= 1.0) {
                 alpha = 1.0 - step(alpha, MAGIC_WAVY_LINE_AA_SNAP);
             }
 
             break;
         }
     }
 
-    oFragColor = vColor * vec4(1.0, 1.0, 1.0, alpha);
+    oFragColor = vColor * alpha;
 }
 #endif
--- a/gfx/webrender/res/ps_yuv_image.glsl
+++ b/gfx/webrender/res/ps_yuv_image.glsl
@@ -189,11 +189,11 @@ void main(void) {
 
     yuv_value.x = TEX_SAMPLE(sColor0, vec3(st_y, vLayers.x)).r;
     yuv_value.y = TEX_SAMPLE(sColor1, vec3(st_u, vLayers.y)).r;
     yuv_value.z = TEX_SAMPLE(sColor2, vec3(st_v, vLayers.z)).r;
 #endif
 
     // See the YuvColorMatrix definition for an explanation of where the constants come from.
     vec3 rgb = YuvColorMatrix * (yuv_value - vec3(0.06275, 0.50196, 0.50196));
-    oFragColor = vec4(rgb, alpha);
+    oFragColor = vec4(rgb * alpha, alpha);
 }
 #endif
--- a/gfx/webrender/src/border.rs
+++ b/gfx/webrender/src/border.rs
@@ -5,17 +5,16 @@
 use api::{BorderSide, BorderStyle, BorderWidths, ClipAndScrollInfo, ColorF};
 use api::{EdgeAaSegmentMask, LayerPoint, LayerRect};
 use api::{LayerPrimitiveInfo, LayerSize, NormalBorder, RepeatMode};
 use clip::ClipSource;
 use ellipse::Ellipse;
 use frame_builder::FrameBuilder;
 use gpu_cache::GpuDataRequest;
 use prim_store::{BorderPrimitiveCpu, RectangleContent, PrimitiveContainer, TexelRect};
-use tiling::PrimitiveFlags;
 use util::{lerp, pack_as_float};
 
 #[repr(u8)]
 #[derive(Debug, Copy, Clone, PartialEq)]
 pub enum BorderCornerInstance {
     None,
     Single, // Single instance needed - corner styles are same or similar.
     Double, // Different corner styles. Draw two instances, one per style.
@@ -383,56 +382,56 @@ impl FrameBuilder {
             // Add a solid rectangle for each visible edge/corner combination.
             if top_edge == BorderEdgeKind::Solid {
                 info.rect = LayerRect::new(p0, LayerSize::new(rect_width, top_len));
                 info.edge_aa_segment_mask = EdgeAaSegmentMask::BOTTOM;
                 self.add_solid_rectangle(
                     clip_and_scroll,
                     &info,
                     RectangleContent::Fill(border.top.color),
-                    PrimitiveFlags::None,
+                    None,
                 );
             }
             if left_edge == BorderEdgeKind::Solid {
                 info.rect = LayerRect::new(
                     LayerPoint::new(p0.x, p0.y + top_len),
                     LayerSize::new(left_len, rect_height - top_len - bottom_len),
                 );
                 info.edge_aa_segment_mask = EdgeAaSegmentMask::RIGHT;
                 self.add_solid_rectangle(
                     clip_and_scroll,
                     &info,
                     RectangleContent::Fill(border.left.color),
-                    PrimitiveFlags::None,
+                    None,
                 );
             }
             if right_edge == BorderEdgeKind::Solid {
                 info.rect = LayerRect::new(
                     LayerPoint::new(p1.x - right_len, p0.y + top_len),
                     LayerSize::new(right_len, rect_height - top_len - bottom_len),
                 );
                 info.edge_aa_segment_mask = EdgeAaSegmentMask::LEFT;
                 self.add_solid_rectangle(
                     clip_and_scroll,
                     &info,
                     RectangleContent::Fill(border.right.color),
-                    PrimitiveFlags::None,
+                    None,
                 );
             }
             if bottom_edge == BorderEdgeKind::Solid {
                 info.rect = LayerRect::new(
                     LayerPoint::new(p0.x, p1.y - bottom_len),
                     LayerSize::new(rect_width, bottom_len),
                 );
                 info.edge_aa_segment_mask = EdgeAaSegmentMask::TOP;
                 self.add_solid_rectangle(
                     clip_and_scroll,
                     &info,
                     RectangleContent::Fill(border.bottom.color),
-                    PrimitiveFlags::None,
+                    None,
                 );
             }
         } else {
             // Create clip masks for border corners, if required.
             let mut extra_clips = Vec::new();
             let mut corner_instances = [BorderCornerInstance::Single; 4];
 
             for (i, corner) in corners.iter().enumerate() {
--- a/gfx/webrender/src/clip.rs
+++ b/gfx/webrender/src/clip.rs
@@ -240,17 +240,18 @@ impl ClipSources {
 
         for &(ref clip, _) in &self.clips {
             if let ClipSource::Image(ref mask) = *clip {
                 resource_cache.request_image(mask.image, ImageRendering::Auto, None, gpu_cache);
             }
         }
     }
 
-    pub fn is_masking(&self) -> bool {
+    /// Whether or not this ClipSources has any clips (does any clipping).
+    pub fn has_clips(&self) -> bool {
         !self.clips.is_empty()
     }
 }
 
 /// Represents a local rect and a device space
 /// rectangles that are either outside or inside bounds.
 #[derive(Clone, Debug, PartialEq)]
 pub struct Geometry {
--- a/gfx/webrender/src/clip_scroll_node.rs
+++ b/gfx/webrender/src/clip_scroll_node.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/. */
 
 use api::{ClipId, DeviceIntRect, LayerPixel, LayerPoint, LayerRect, LayerSize};
 use api::{LayerToScrollTransform, LayerToWorldTransform, LayerVector2D, LayoutVector2D, PipelineId};
 use api::{ScrollClamping, ScrollEventPhase, ScrollLocation, ScrollSensitivity};
 use api::{StickyOffsetBounds, WorldPoint};
-use clip::{ClipRegion, ClipSources, ClipSourcesHandle, ClipStore};
+use clip::{ClipSourcesHandle, ClipStore};
 use clip_scroll_tree::{CoordinateSystemId, TransformUpdateState};
 use euclid::SideOffsets2D;
 use geometry::ray_intersects_rect;
 use gpu_cache::GpuCache;
 use gpu_types::{ClipScrollNodeIndex, ClipScrollNodeData};
 use render_task::{ClipChain, ClipChainNode, ClipWorkItem};
 use resource_cache::ResourceCache;
 use spring::{DAMPING, STIFFNESS, Spring};
@@ -22,40 +22,16 @@ use util::{MatrixHelpers, MaxRect};
 const CAN_OVERSCROLL: bool = true;
 
 #[cfg(not(target_os = "macos"))]
 const CAN_OVERSCROLL: bool = false;
 
 const MAX_LOCAL_VIEWPORT: f32 = 1000000.0;
 
 #[derive(Debug)]
-pub struct ClipInfo {
-    /// The clips for this node.
-    pub clip_sources: ClipSourcesHandle,
-
-    /// Whether or not this clip node automatically creates a mask.
-    pub is_masking: bool,
-}
-
-impl ClipInfo {
-    pub fn new(
-        clip_region: ClipRegion,
-        clip_store: &mut ClipStore,
-    ) -> ClipInfo {
-        let clip_sources = ClipSources::from(clip_region);
-        let is_masking = clip_sources.is_masking();
-
-        ClipInfo {
-            clip_sources: clip_store.insert(clip_sources),
-            is_masking,
-        }
-    }
-}
-
-#[derive(Debug)]
 pub struct StickyFrameInfo {
     pub margins: SideOffsets2D<Option<f32>>,
     pub vertical_offset_bounds: StickyOffsetBounds,
     pub horizontal_offset_bounds: StickyOffsetBounds,
     pub previously_applied_offset: LayoutVector2D,
     pub current_offset: LayerVector2D,
 }
 
@@ -77,17 +53,17 @@ impl StickyFrameInfo {
 }
 
 #[derive(Debug)]
 pub enum NodeType {
     /// A reference frame establishes a new coordinate space in the tree.
     ReferenceFrame(ReferenceFrameInfo),
 
     /// Other nodes just do clipping, but no transformation.
-    Clip(ClipInfo),
+    Clip(ClipSourcesHandle),
 
     /// Transforms it's content, but doesn't clip it. Can also be adjusted
     /// by scroll events or setting scroll offsets.
     ScrollFrame(ScrollingState),
 
     /// A special kind of node that adjusts its position based on the position
     /// of its parent node and a given set of sticky positioning offset bounds.
     /// Sticky positioned is described in the CSS Positioned Layout Module Level 3 here:
@@ -146,17 +122,17 @@ pub struct ClipScrollNode {
     /// The intersected outer bounds of the clips for this node.
     pub combined_clip_outer_bounds: DeviceIntRect,
 
     /// The axis-aligned coordinate system id of this node.
     pub coordinate_system_id: CoordinateSystemId,
 
     /// A linear ID / index of this clip-scroll node. Used as a reference to
     /// pass to shaders, to allow them to fetch a given clip-scroll node.
-    pub id: ClipScrollNodeIndex,
+    pub node_data_index: ClipScrollNodeIndex,
 }
 
 impl ClipScrollNode {
     fn new(
         pipeline_id: PipelineId,
         parent_id: Option<ClipId>,
         rect: &LayerRect,
         node_type: NodeType
@@ -170,17 +146,17 @@ impl ClipScrollNode {
             reference_frame_relative_scroll_offset: LayerVector2D::zero(),
             parent: parent_id,
             children: Vec::new(),
             pipeline_id,
             node_type: node_type,
             clip_chain_node: None,
             combined_clip_outer_bounds: DeviceIntRect::max_rect(),
             coordinate_system_id: CoordinateSystemId(0),
-            id: ClipScrollNodeIndex(0),
+            node_data_index: ClipScrollNodeIndex(0),
         }
     }
 
     pub fn new_scroll_frame(
         pipeline_id: PipelineId,
         parent_id: ClipId,
         frame_rect: &LayerRect,
         content_size: &LayerSize,
@@ -195,20 +171,20 @@ impl ClipScrollNode {
         ));
 
         Self::new(pipeline_id, Some(parent_id), frame_rect, node_type)
     }
 
     pub fn new_clip_node(
         pipeline_id: PipelineId,
         parent_id: ClipId,
-        clip_info: ClipInfo,
+        handle: ClipSourcesHandle,
         clip_rect: LayerRect,
     ) -> Self {
-        Self::new(pipeline_id, Some(parent_id), &clip_rect, NodeType::Clip(clip_info))
+        Self::new(pipeline_id, Some(parent_id), &clip_rect, NodeType::Clip(handle))
     }
 
     pub fn new_reference_frame(
         parent_id: Option<ClipId>,
         frame_rect: &LayerRect,
         transform: &LayerToScrollTransform,
         origin_in_parent_reference_frame: LayerVector2D,
         pipeline_id: PipelineId,
@@ -284,35 +260,87 @@ impl ClipScrollNode {
         }
 
         scrolling.offset = new_offset;
         scrolling.bouncing_back = false;
         scrolling.started_bouncing_back = false;
         true
     }
 
+    pub fn update(
+        &mut self,
+        state: &mut TransformUpdateState,
+        node_data: &mut Vec<ClipScrollNodeData>,
+        device_pixel_ratio: f32,
+        clip_store: &mut ClipStore,
+        resource_cache: &mut ResourceCache,
+        gpu_cache: &mut GpuCache,
+    ) {
+        // We set this earlier so that we can use it before we have all the data necessary
+        // to populate the ClipScrollNodeData.
+        self.node_data_index = ClipScrollNodeIndex(node_data.len() as u32);
+
+        self.update_transform(state);
+        self.update_clip_work_item(
+            state,
+            device_pixel_ratio,
+            clip_store,
+            resource_cache,
+            gpu_cache,
+        );
+
+        let local_clip_rect = if self.world_content_transform.has_perspective_component() {
+            LayerRect::new(
+                LayerPoint::new(-MAX_LOCAL_VIEWPORT, -MAX_LOCAL_VIEWPORT),
+                LayerSize::new(2.0 * MAX_LOCAL_VIEWPORT, 2.0 * MAX_LOCAL_VIEWPORT)
+            )
+        } else {
+            self.combined_local_viewport_rect
+        };
+
+        let data = match self.world_content_transform.inverse() {
+            Some(inverse) => {
+                ClipScrollNodeData {
+                    transform: self.world_content_transform,
+                    inv_transform: inverse,
+                    local_clip_rect,
+                    reference_frame_relative_scroll_offset:
+                        self.reference_frame_relative_scroll_offset,
+                    scroll_offset: self.scroll_offset(),
+                }
+            }
+            None => {
+                state.combined_outer_clip_bounds = DeviceIntRect::zero();
+                ClipScrollNodeData::invalid()
+            }
+        };
+
+        // Write the data that will be made available to the GPU for this node.
+        node_data.push(data);
+    }
+
     pub fn update_clip_work_item(
         &mut self,
         state: &mut TransformUpdateState,
         device_pixel_ratio: f32,
         clip_store: &mut ClipStore,
         resource_cache: &mut ResourceCache,
         gpu_cache: &mut GpuCache,
     ) {
         let current_clip_chain = state.parent_clip_chain.clone();
-        let clip_info = match self.node_type {
-            NodeType::Clip(ref mut info) if info.is_masking => info,
+        let clip_sources_handle = match self.node_type {
+            NodeType::Clip(ref handle) => handle,
             _ => {
                 self.clip_chain_node = current_clip_chain;
                 self.combined_clip_outer_bounds = state.combined_outer_clip_bounds;
                 return;
             }
         };
 
-        let clip_sources = clip_store.get_mut(&clip_info.clip_sources);
+        let clip_sources = clip_store.get_mut(clip_sources_handle);
         clip_sources.update(
             &self.world_viewport_transform,
             gpu_cache,
             resource_cache,
             device_pixel_ratio,
         );
 
         let outer_bounds = clip_sources.bounds.outer.as_ref().map_or_else(
@@ -321,32 +349,28 @@ impl ClipScrollNode {
         );
 
         self.combined_clip_outer_bounds = outer_bounds.intersection(
             &state.combined_outer_clip_bounds).unwrap_or_else(DeviceIntRect::zero);
 
         // TODO: Combine rectangles in the same axis-aligned clip space here?
         self.clip_chain_node = Some(Rc::new(ClipChainNode {
             work_item: ClipWorkItem {
-                scroll_node_id: self.id,
-                clip_sources: clip_info.clip_sources.weak(),
+                scroll_node_data_index: self.node_data_index,
+                clip_sources: clip_sources_handle.weak(),
                 coordinate_system_id: state.current_coordinate_system_id,
             },
             prev: current_clip_chain,
         }));
 
         state.combined_outer_clip_bounds = self.combined_clip_outer_bounds;
         state.parent_clip_chain = self.clip_chain_node.clone();
     }
 
-    pub fn update_transform(
-        &mut self,
-        state: &mut TransformUpdateState,
-        node_data: &mut Vec<ClipScrollNodeData>,
-    ) {
+    pub fn update_transform(&mut self, state: &mut TransformUpdateState) {
         // We calculate this here to avoid a double-borrow later.
         let sticky_offset = self.calculate_sticky_offset(
             &state.nearest_scrolling_ancestor_offset,
             &state.nearest_scrolling_ancestor_viewport,
         );
 
         let (local_transform, accumulated_scroll_offset) = match self.node_type {
             NodeType::ReferenceFrame(ref info) => {
@@ -435,46 +459,16 @@ impl ClipScrollNode {
                 state.parent_combined_viewport_rect = self.combined_local_viewport_rect;
                 state.parent_accumulated_scroll_offset =
                     info.current_offset + state.parent_accumulated_scroll_offset;
             }
         }
 
         // Store coord system ID, and also the ID used for shaders to reference this node.
         self.coordinate_system_id = state.current_coordinate_system_id;
-        self.id = ClipScrollNodeIndex(node_data.len() as u32);
-
-        let local_clip_rect = if self.world_content_transform.has_perspective_component() {
-            LayerRect::new(
-                LayerPoint::new(-MAX_LOCAL_VIEWPORT, -MAX_LOCAL_VIEWPORT),
-                LayerSize::new(2.0 * MAX_LOCAL_VIEWPORT, 2.0 * MAX_LOCAL_VIEWPORT)
-            )
-        } else {
-            self.combined_local_viewport_rect
-        };
-
-        let data = match self.world_content_transform.inverse() {
-            Some(inverse) => {
-                ClipScrollNodeData {
-                    transform: self.world_content_transform,
-                    inv_transform: inverse,
-                    local_clip_rect,
-                    reference_frame_relative_scroll_offset: self.reference_frame_relative_scroll_offset,
-                    scroll_offset: self.scroll_offset(),
-                }
-            }
-            None => {
-                state.combined_outer_clip_bounds = DeviceIntRect::zero();
-
-                ClipScrollNodeData::invalid()
-            }
-        };
-
-        // Write the data that will be made available to the GPU for this node.
-        node_data.push(data);
     }
 
     fn calculate_sticky_offset(
         &self,
         viewport_scroll_offset: &LayerVector2D,
         viewport_rect: &LayerRect,
     ) -> LayerVector2D {
         let info = match self.node_type {
--- a/gfx/webrender/src/clip_scroll_tree.rs
+++ b/gfx/webrender/src/clip_scroll_tree.rs
@@ -175,31 +175,31 @@ impl ClipScrollTree {
             Some(inverted) => inverted.transform_point2d(&point),
             None => {
                 cache.insert(*node_id, None);
                 return false;
             }
         };
 
         let point_in_layer = transformed_point - node.local_viewport_rect.origin.to_vector();
-        let clip_info = match node.node_type {
-            NodeType::Clip(ref info) => info,
+        let clip_sources_handle = match node.node_type {
+            NodeType::Clip(ref clip_sources_handle) => clip_sources_handle,
             _ => {
                 cache.insert(*node_id, Some(point_in_layer));
                 return true;
             }
         };
 
         if !node.local_clip_rect.contains(&transformed_point) {
             cache.insert(*node_id, None);
             return false;
         }
 
         let point_in_clips = transformed_point - node.local_clip_rect.origin.to_vector();
-        for &(ref clip, _) in clip_store.get(&clip_info.clip_sources).clips() {
+        for &(ref clip, _) in clip_store.get(&clip_sources_handle).clips() {
             if !clip.contains(&point_in_clips) {
                 cache.insert(*node_id, None);
                 return false;
             }
         }
 
         cache.insert(*node_id, Some(point_in_layer));
 
@@ -320,17 +320,17 @@ impl ClipScrollTree {
         };
 
         self.nodes
             .get_mut(&clip_id)
             .unwrap()
             .scroll(scroll_location, phase)
     }
 
-    pub fn update_all_node_transforms(
+    pub fn update_tree(
         &mut self,
         screen_rect: &DeviceIntRect,
         device_pixel_ratio: f32,
         clip_store: &mut ClipStore,
         resource_cache: &mut ResourceCache,
         gpu_cache: &mut GpuCache,
         pan: LayerPoint,
         node_data: &mut Vec<ClipScrollNodeData>,
@@ -352,28 +352,28 @@ impl ClipScrollTree {
             parent_accumulated_scroll_offset: LayerVector2D::zero(),
             nearest_scrolling_ancestor_offset: LayerVector2D::zero(),
             nearest_scrolling_ancestor_viewport: LayerRect::zero(),
             parent_clip_chain: None,
             combined_outer_clip_bounds: *screen_rect,
             current_coordinate_system_id: CoordinateSystemId(0),
             next_coordinate_system_id: CoordinateSystemId(0).next(),
         };
-        self.update_node_transform(
+        self.update_node(
             root_reference_frame_id,
             &mut state,
             device_pixel_ratio,
             clip_store,
             resource_cache,
             gpu_cache,
             node_data,
         );
     }
 
-    fn update_node_transform(
+    fn update_node(
         &mut self,
         layer_id: ClipId,
         state: &mut TransformUpdateState,
         device_pixel_ratio: f32,
         clip_store: &mut ClipStore,
         resource_cache: &mut ResourceCache,
         gpu_cache: &mut GpuCache,
         node_data: &mut Vec<ClipScrollNodeData>,
@@ -382,33 +382,30 @@ impl ClipScrollTree {
         //           Restructure this to avoid the clones!
         let mut state = state.clone();
         let node_children = {
             let node = match self.nodes.get_mut(&layer_id) {
                 Some(node) => node,
                 None => return,
             };
 
-            node.update_transform(
+            node.update(
                 &mut state,
-                node_data
-            );
-            node.update_clip_work_item(
-                &mut state,
+                node_data,
                 device_pixel_ratio,
                 clip_store,
                 resource_cache,
                 gpu_cache,
             );
 
             node.children.clone()
         };
 
         for child_layer_id in node_children {
-            self.update_node_transform(
+            self.update_node(
                 child_layer_id,
                 &mut state,
                 device_pixel_ratio,
                 clip_store,
                 resource_cache,
                 gpu_cache,
                 node_data,
             );
@@ -502,21 +499,21 @@ impl ClipScrollTree {
             _ => {}
         }
     }
 
     fn print_node<T: PrintTreePrinter>(&self, id: &ClipId, pt: &mut T, clip_store: &ClipStore) {
         let node = self.nodes.get(id).unwrap();
 
         match node.node_type {
-            NodeType::Clip(ref info) => {
+            NodeType::Clip(ref clip_sources_handle) => {
                 pt.new_level("Clip".to_owned());
 
                 pt.add_item(format!("id: {:?}", id));
-                let clips = clip_store.get(&info.clip_sources).clips();
+                let clips = clip_store.get(&clip_sources_handle).clips();
                 pt.new_level(format!("Clip Sources [{}]", clips.len()));
                 for source in clips {
                     pt.add_item(format!("{:?}", source));
                 }
                 pt.end_level();
             }
             NodeType::ReferenceFrame(ref info) => {
                 pt.new_level(format!("ReferenceFrame {:?}", info.transform));
--- a/gfx/webrender/src/debug_render.rs
+++ b/gfx/webrender/src/debug_render.rs
@@ -261,17 +261,17 @@ impl DebugRenderer {
         self.add_line(p1.x, p1.y, color, p0.x, p1.y, color);
         self.add_line(p0.x, p1.y, color, p0.x, p0.y, color);
     }
 
     pub fn render(&mut self, device: &mut Device, viewport_size: &DeviceUintSize) {
         let _gm = GpuMarker::new(device.rc_gl(), "debug");
         device.disable_depth();
         device.set_blend(true);
-        device.set_blend_mode_alpha();
+        device.set_blend_mode_premultiplied_alpha();
 
         let projection = Transform3D::ortho(
             0.0,
             viewport_size.width as f32,
             viewport_size.height as f32,
             0.0,
             ORTHO_NEAR_PLANE,
             ORTHO_FAR_PLANE,
--- a/gfx/webrender/src/device.rs
+++ b/gfx/webrender/src/device.rs
@@ -1877,26 +1877,16 @@ impl Device {
         self.gl.blend_equation(gl::FUNC_ADD);
     }
 
     pub fn set_blend_mode_premultiplied_dest_out(&self) {
         self.gl.blend_func(gl::ZERO, gl::ONE_MINUS_SRC_ALPHA);
         self.gl.blend_equation(gl::FUNC_ADD);
     }
 
-    pub fn set_blend_mode_alpha(&self) {
-        self.gl.blend_func_separate(
-            gl::SRC_ALPHA,
-            gl::ONE_MINUS_SRC_ALPHA,
-            gl::ONE,
-            gl::ONE_MINUS_SRC_ALPHA,
-        );
-        self.gl.blend_equation(gl::FUNC_ADD);
-    }
-
     pub fn set_blend_mode_multiply(&self) {
         self.gl
             .blend_func_separate(gl::ZERO, gl::SRC_COLOR, gl::ZERO, gl::SRC_ALPHA);
         self.gl.blend_equation(gl::FUNC_ADD);
     }
     pub fn set_blend_mode_max(&self) {
         self.gl
             .blend_func_separate(gl::ONE, gl::ONE, gl::ONE, gl::ONE);
--- a/gfx/webrender/src/frame.rs
+++ b/gfx/webrender/src/frame.rs
@@ -10,24 +10,24 @@ use api::{LayerSize, LayerToScrollTransf
 use api::{LayoutRect, LayoutSize, LayoutTransform};
 use api::{LocalClip, PipelineId, ScrollClamping, ScrollEventPhase, ScrollLayerState};
 use api::{ScrollLocation, ScrollPolicy, ScrollSensitivity, SpecificDisplayItem, StackingContext};
 use api::{ClipMode, TileOffset, TransformStyle, WorldPoint};
 use clip::ClipRegion;
 use clip_scroll_node::StickyFrameInfo;
 use clip_scroll_tree::{ClipScrollTree, ScrollStates};
 use euclid::rect;
-use frame_builder::{FrameBuilder, FrameBuilderConfig};
+use frame_builder::{FrameBuilder, FrameBuilderConfig, ScrollbarInfo};
 use gpu_cache::GpuCache;
 use internal_types::{FastHashMap, FastHashSet, RendererFrame};
 use prim_store::RectangleContent;
 use profiler::{GpuCacheProfileCounters, TextureCacheProfileCounters};
 use resource_cache::{FontInstanceMap,ResourceCache, TiledImageMap};
 use scene::{Scene, StackingContextHelpers, ScenePipeline};
-use tiling::{CompositeOps, Frame, PrimitiveFlags};
+use tiling::{CompositeOps, Frame};
 use util::ComplexClipRegionHelpers;
 
 #[derive(Copy, Clone, PartialEq, PartialOrd, Debug, Eq, Ord)]
 pub struct FrameId(pub u32);
 
 static DEFAULT_SCROLLBAR_COLOR: ColorF = ColorF {
     r: 0.3,
     g: 0.3,
@@ -79,17 +79,19 @@ impl<'a> FlattenContext<'a> {
             .get(complex_clips)
             .collect()
     }
 
     fn flatten_root(
         &mut self,
         traversal: &mut BuiltDisplayListIter<'a>,
         pipeline_id: PipelineId,
-        content_size: &LayoutSize,
+        frame_size: &LayoutSize,
+        root_reference_frame_id: ClipId,
+        root_scroll_frame_id: ClipId,
     ) {
         self.builder.push_stacking_context(
             &LayerVector2D::zero(),
             pipeline_id,
             CompositeOps::default(),
             TransformStyle::Flat,
             true,
             true,
@@ -98,44 +100,42 @@ impl<'a> FlattenContext<'a> {
         // We do this here, rather than above because we want any of the top-level
         // stacking contexts in the display list to be treated like root stacking contexts.
         // FIXME(mrobinson): Currently only the first one will, which for the moment is
         // sufficient for all our use cases.
         self.builder.notify_waiting_for_root_stacking_context();
 
         // For the root pipeline, there's no need to add a full screen rectangle
         // here, as it's handled by the framebuffer clear.
-        let clip_id = ClipId::root_scroll_node(pipeline_id);
         if self.scene.root_pipeline_id != Some(pipeline_id) {
             if let Some(pipeline) = self.scene.pipelines.get(&pipeline_id) {
                 if let Some(bg_color) = pipeline.background_color {
-                    let root_bounds = LayerRect::new(LayerPoint::zero(), *content_size);
+                    let root_bounds = LayerRect::new(LayerPoint::zero(), *frame_size);
                     let info = LayerPrimitiveInfo::new(root_bounds);
                     self.builder.add_solid_rectangle(
-                        ClipAndScrollInfo::simple(clip_id),
+                        ClipAndScrollInfo::simple(root_reference_frame_id),
                         &info,
                         RectangleContent::Fill(bg_color),
-                        PrimitiveFlags::None,
+                        None,
                     );
                 }
             }
         }
 
 
         self.flatten_items(traversal, pipeline_id, LayerVector2D::zero());
 
         if self.builder.config.enable_scrollbars {
             let scrollbar_rect = LayerRect::new(LayerPoint::zero(), LayerSize::new(10.0, 70.0));
-            let info = LayerPrimitiveInfo::new(scrollbar_rect);
-
+            let container_rect = LayerRect::new(LayerPoint::zero(), *frame_size);
             self.builder.add_solid_rectangle(
-                ClipAndScrollInfo::simple(clip_id),
-                &info,
+                ClipAndScrollInfo::simple(root_reference_frame_id),
+                &LayerPrimitiveInfo::new(scrollbar_rect),
                 RectangleContent::Fill(DEFAULT_SCROLLBAR_COLOR),
-                PrimitiveFlags::Scrollbar(self.clip_scroll_tree.topmost_scrolling_node_id(), 4.0),
+                Some(ScrollbarInfo(root_scroll_frame_id, container_rect)),
             );
         }
 
         self.builder.pop_stacking_context();
     }
 
     fn flatten_items(
         &mut self,
@@ -366,17 +366,19 @@ impl<'a> FlattenContext<'a> {
             &pipeline.content_size,
             ScrollSensitivity::ScriptAndInputEvents,
             self.clip_scroll_tree,
         );
 
         self.flatten_root(
             &mut pipeline.display_list.iter(),
             pipeline_id,
-            &pipeline.content_size,
+            &iframe_rect.size,
+            iframe_reference_frame_id,
+            ClipId::root_scroll_node(pipeline_id),
         );
 
         self.builder.pop_reference_frame();
     }
 
     fn flatten_item<'b>(
         &'b mut self,
         item: DisplayItemRef<'a, 'b>,
@@ -454,26 +456,26 @@ impl<'a> FlattenContext<'a> {
                     &prim_info,
                     RectangleContent::Fill(info.color),
                     &clip_and_scroll,
                 ) {
                     self.builder.add_solid_rectangle(
                         clip_and_scroll,
                         &prim_info,
                         RectangleContent::Fill(info.color),
-                        PrimitiveFlags::None,
+                        None,
                     );
                 }
             }
             SpecificDisplayItem::ClearRectangle => {
                 self.builder.add_solid_rectangle(
                     clip_and_scroll,
                     &prim_info,
                     RectangleContent::Clear,
-                    PrimitiveFlags::None,
+                    None,
                 );
             }
             SpecificDisplayItem::Line(ref info) => {
                 self.builder.add_line(
                     clip_and_scroll,
                     &prim_info,
                     info.wavy_line_thickness,
                     info.orientation,
@@ -684,17 +686,17 @@ impl<'a> FlattenContext<'a> {
                 },
                 local_clip,
                 .. info.clone()
             };
             self.builder.add_solid_rectangle(
                 *clip_and_scroll,
                 &prim_info,
                 content,
-                PrimitiveFlags::None,
+                None,
             );
             has_opaque = true;
         }
 
         if !has_opaque {
             return false
         }
 
@@ -705,17 +707,17 @@ impl<'a> FlattenContext<'a> {
                     None => continue,
                 },
                 .. info.clone()
             };
             self.builder.add_solid_rectangle(
                 *clip_and_scroll,
                 &prim_info,
                 content,
-                PrimitiveFlags::None,
+                None,
             );
         }
         true
     }
 
     /// Decomposes an image display item that is repeated into an image per individual repetition.
     /// We need to do this when we are unable to perform the repetition in the shader,
     /// for example if the image is tiled.
@@ -1137,20 +1139,24 @@ impl FrameContext {
 
             roller.builder.setup_viewport_offset(
                 window_size,
                 inner_rect,
                 device_pixel_ratio,
                 roller.clip_scroll_tree,
             );
 
+            let reference_frame_id = roller.clip_scroll_tree.root_reference_frame_id;
+            let scroll_frame_id = roller.clip_scroll_tree.topmost_scrolling_node_id;
             roller.flatten_root(
                 &mut root_pipeline.display_list.iter(),
                 root_pipeline_id,
-                &root_pipeline.content_size,
+                &root_pipeline.viewport_size,
+                reference_frame_id,
+                scroll_frame_id,
             );
 
             self.pipeline_epoch_map.extend(roller.pipeline_epochs.drain(..));
             roller.builder
         };
 
         self.clip_scroll_tree
             .finalize_and_apply_pending_scroll_offsets(old_scrolling_states);
--- a/gfx/webrender/src/frame_builder.rs
+++ b/gfx/webrender/src/frame_builder.rs
@@ -1,51 +1,54 @@
 /* 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 api::{BorderDetails, BorderDisplayItem, BuiltDisplayList};
-use api::{ClipAndScrollInfo, ClipId, ColorF};
+use api::{ClipAndScrollInfo, ClipId, ColorF, PremultipliedColorF};
 use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceUintRect, DeviceUintSize};
-use api::{ExtendMode, FilterOp, FontInstance, FontRenderMode};
+use api::{ExtendMode, FilterOp, FontRenderMode};
 use api::{GlyphInstance, GlyphOptions, GradientStop, HitTestFlags, HitTestItem, HitTestResult};
 use api::{ImageKey, ImageRendering, ItemRange, ItemTag, LayerPoint, LayerPrimitiveInfo, LayerRect};
 use api::{LayerPixel, LayerSize, LayerToScrollTransform, LayerVector2D, LayoutVector2D, LineOrientation};
 use api::{LineStyle, LocalClip, PipelineId, RepeatMode};
 use api::{ScrollSensitivity, Shadow, TileOffset, TransformStyle};
 use api::{WorldPixel, WorldPoint, YuvColorSpace, YuvData, device_length};
 use app_units::Au;
 use border::ImageBorderSegment;
 use clip::{ClipRegion, ClipSource, ClipSources, ClipStore, Contains};
-use clip_scroll_node::{ClipInfo, ClipScrollNode, NodeType};
-use clip_scroll_tree::{ClipScrollTree};
+use clip_scroll_node::{ClipScrollNode, NodeType};
+use clip_scroll_tree::ClipScrollTree;
 use euclid::{SideOffsets2D, TypedTransform3D, vec2, vec3};
 use frame::FrameId;
+use glyph_rasterizer::FontInstance;
 use gpu_cache::GpuCache;
 use internal_types::{FastHashMap, FastHashSet, HardwareCompositeOp};
-use picture::{PicturePrimitive};
+use picture::{PictureKind, PicturePrimitive};
 use plane_split::{BspSplitter, Polygon, Splitter};
 use prim_store::{TexelRect, YuvImagePrimitiveCpu};
 use prim_store::{GradientPrimitiveCpu, ImagePrimitiveCpu, LinePrimitive, PrimitiveKind};
 use prim_store::{PrimitiveContainer, PrimitiveIndex, PrimitiveRun};
 use prim_store::{PrimitiveStore, RadialGradientPrimitiveCpu};
 use prim_store::{RectangleContent, RectanglePrimitive, TextRunPrimitiveCpu};
 use profiler::{FrameProfileCounters, GpuCacheProfileCounters, TextureCacheProfileCounters};
 use render_task::{AlphaRenderItem, ClearMode, RenderTask, RenderTaskId, RenderTaskLocation};
 use render_task::RenderTaskTree;
 use resource_cache::ResourceCache;
 use scene::ScenePipeline;
 use std::{mem, usize, f32, i32};
-use tiling::{CompositeOps, Frame};
-use tiling::{ContextIsolation, RenderTargetKind, StackingContextIndex};
-use tiling::{PrimitiveFlags, PrimitiveRunCmd, RenderPass};
-use tiling::{RenderTargetContext, ScrollbarPrimitive, StackingContext};
+use tiling::{CompositeOps, ContextIsolation, Frame, PrimitiveRunCmd, RenderPass};
+use tiling::{RenderTargetContext, RenderTargetKind, ScrollbarPrimitive, StackingContext};
+use tiling::StackingContextIndex;
 use util::{self, pack_as_float, RectHelpers, recycle_vec};
 use box_shadow::BLUR_SAMPLE_SCALE;
 
+#[derive(Debug)]
+pub struct ScrollbarInfo(pub ClipId, pub LayerRect);
+
 /// Construct a polygon from stacking context boundaries.
 /// `anchor` here is an index that's going to be preserved in all the
 /// splits of the polygon.
 fn make_polygon(
     stacking_context: &StackingContext,
     node: &ClipScrollNode,
     anchor: usize,
 ) -> Polygon<f64, WorldPixel> {
@@ -468,21 +471,22 @@ impl FrameBuilder {
         &mut self,
         new_node_id: ClipId,
         parent_id: ClipId,
         pipeline_id: PipelineId,
         clip_region: ClipRegion,
         clip_scroll_tree: &mut ClipScrollTree,
     ) {
         let clip_rect = clip_region.main;
-        let clip_info = ClipInfo::new(
-            clip_region,
-            &mut self.clip_store,
-        );
-        let node = ClipScrollNode::new_clip_node(pipeline_id, parent_id, clip_info, clip_rect);
+        let clip_sources = ClipSources::from(clip_region);
+        debug_assert!(clip_sources.has_clips());
+
+        let handle = self.clip_store.insert(clip_sources);
+
+        let node = ClipScrollNode::new_clip_node(pipeline_id, parent_id, handle, clip_rect);
         clip_scroll_tree.add_node(node, new_node_id);
     }
 
     pub fn add_scroll_frame(
         &mut self,
         new_node_id: ClipId,
         parent_id: ClipId,
         pipeline_id: PipelineId,
@@ -560,17 +564,17 @@ impl FrameBuilder {
         mem::replace(&mut self.shadow_prim_stack, shadows);
     }
 
     pub fn add_solid_rectangle(
         &mut self,
         clip_and_scroll: ClipAndScrollInfo,
         info: &LayerPrimitiveInfo,
         content: RectangleContent,
-        flags: PrimitiveFlags,
+        scrollbar_info: Option<ScrollbarInfo>,
     ) {
         if let RectangleContent::Fill(ColorF{a, ..}) = content {
             if a == 0.0 {
                 // Don't add transparent rectangles to the draw list, but do consider them for hit
                 // testing. This allows specifying invisible hit testing areas.
                 self.add_primitive_to_hit_testing_list(info, clip_and_scroll);
                 return;
             }
@@ -582,96 +586,97 @@ impl FrameBuilder {
 
         let prim_index = self.add_primitive(
             clip_and_scroll,
             info,
             Vec::new(),
             PrimitiveContainer::Rectangle(prim),
         );
 
-        match flags {
-            PrimitiveFlags::None => {}
-            PrimitiveFlags::Scrollbar(clip_id, border_radius) => {
-                self.scrollbar_prims.push(ScrollbarPrimitive {
-                    prim_index,
-                    clip_id,
-                    border_radius,
-                });
-            }
+        if let Some(ScrollbarInfo(clip_id, frame_rect)) = scrollbar_info {
+            self.scrollbar_prims.push(ScrollbarPrimitive {
+                prim_index,
+                clip_id,
+                frame_rect,
+            });
         }
     }
 
     pub fn add_line(
         &mut self,
         clip_and_scroll: ClipAndScrollInfo,
         info: &LayerPrimitiveInfo,
         wavy_line_thickness: f32,
         orientation: LineOrientation,
-        color: &ColorF,
+        line_color: &ColorF,
         style: LineStyle,
     ) {
         let line = LinePrimitive {
             wavy_line_thickness,
-            color: *color,
-            style: style,
-            orientation: orientation,
+            color: line_color.premultiplied(),
+            style,
+            orientation,
         };
 
         let mut fast_shadow_prims = Vec::new();
         for (idx, &(shadow_prim_index, _)) in self.shadow_prim_stack.iter().enumerate() {
             let shadow_metadata = &self.prim_store.cpu_metadata[shadow_prim_index.0];
             let picture = &self.prim_store.cpu_pictures[shadow_metadata.cpu_prim_index.0];
-            let shadow = picture.as_text_shadow();
-            if shadow.blur_radius == 0.0 {
-                fast_shadow_prims.push((idx, shadow.clone()));
+            match picture.kind {
+                PictureKind::TextShadow { offset, color, blur_radius } if blur_radius == 0.0 => {
+                    fast_shadow_prims.push((idx, offset, color));
+                }
+                _ => {}
             }
         }
 
-        for (idx, shadow) in fast_shadow_prims {
+        for (idx, shadow_offset, shadow_color) in fast_shadow_prims {
             let mut line = line.clone();
-            line.color = shadow.color;
+            line.color = shadow_color.premultiplied();
             let mut info = info.clone();
-            info.rect = info.rect.translate(&shadow.offset);
+            info.rect = info.rect.translate(&shadow_offset);
             let prim_index = self.create_primitive(
                 &info,
                 Vec::new(),
                 PrimitiveContainer::Line(line),
             );
             self.shadow_prim_stack[idx].1.push((prim_index, clip_and_scroll));
         }
 
         let prim_index = self.create_primitive(
             &info,
             Vec::new(),
             PrimitiveContainer::Line(line),
         );
 
-        if color.a > 0.0 {
+        if line_color.a > 0.0 {
             if self.shadow_prim_stack.is_empty() {
                 self.add_primitive_to_hit_testing_list(&info, clip_and_scroll);
                 self.add_primitive_to_draw_list(prim_index, clip_and_scroll);
             } else {
                 self.pending_shadow_contents.push((prim_index, clip_and_scroll, *info));
             }
         }
 
         for &(shadow_prim_index, _) in &self.shadow_prim_stack {
             let shadow_metadata = &mut self.prim_store.cpu_metadata[shadow_prim_index.0];
             debug_assert_eq!(shadow_metadata.prim_kind, PrimitiveKind::Picture);
             let picture =
                 &mut self.prim_store.cpu_pictures[shadow_metadata.cpu_prim_index.0];
-            let blur_radius = picture.as_text_shadow().blur_radius;
 
-            // Only run real blurs here (fast path zero blurs are handled above).
-            if blur_radius > 0.0 {
-                picture.add_primitive(
-                    prim_index,
-                    &info.rect,
-                    clip_and_scroll,
-                );
+            match picture.kind {
+                // Only run real blurs here (fast path zero blurs are handled above).
+                PictureKind::TextShadow { blur_radius, .. } if blur_radius > 0.0 => {
+                    picture.add_primitive(
+                        prim_index,
+                        &info.rect,
+                        clip_and_scroll,
+                    );
+                }
+                _ => {}
             }
         }
     }
 
     pub fn add_border(
         &mut self,
         clip_and_scroll: ClipAndScrollInfo,
         info: &LayerPrimitiveInfo,
@@ -1038,22 +1043,22 @@ impl FrameBuilder {
     }
 
     pub fn add_text(
         &mut self,
         clip_and_scroll: ClipAndScrollInfo,
         run_offset: LayoutVector2D,
         info: &LayerPrimitiveInfo,
         font: &FontInstance,
-        color: &ColorF,
+        text_color: &ColorF,
         glyph_range: ItemRange<GlyphInstance>,
         glyph_count: usize,
         glyph_options: Option<GlyphOptions>,
     ) {
-        let rect = info.rect;
+        let original_rect = info.rect;
         // Trivial early out checks
         if font.size.0 <= 0 {
             return;
         }
 
         // Sanity check - anything with glyphs bigger than this
         // is probably going to consume too much memory to render
         // efficiently anyway. This is specifically to work around
@@ -1088,17 +1093,17 @@ impl FrameBuilder {
                     render_mode = FontRenderMode::Alpha;
                 }
             }
         }
 
         let prim_font = FontInstance::new(
             font.font_key,
             font.size,
-            *color,
+            *text_color,
             font.bg_color,
             render_mode,
             font.subpx_dir,
             font.platform_options,
             font.variations.clone(),
             font.synthetic_italics,
         );
         let prim = TextRunPrimitiveCpu {
@@ -1117,22 +1122,24 @@ impl FrameBuilder {
         // primitive with the shadow's color and offset. These need to be added
         // *before* the visual text primitive in order to get the correct paint
         // order. Store them in a Vec first to work around borrowck issues.
         // TODO(gw): Refactor to avoid having to store them in a Vec first.
         let mut fast_shadow_prims = Vec::new();
         for (idx, &(shadow_prim_index, _)) in self.shadow_prim_stack.iter().enumerate() {
             let shadow_metadata = &self.prim_store.cpu_metadata[shadow_prim_index.0];
             let picture_prim = &self.prim_store.cpu_pictures[shadow_metadata.cpu_prim_index.0];
-            let shadow = picture_prim.as_text_shadow();
-            if shadow.blur_radius == 0.0 {
-                let mut text_prim = prim.clone();
-                text_prim.font.color = shadow.color.into();
-                text_prim.offset += shadow.offset;
-                fast_shadow_prims.push((idx, text_prim));
+            match picture_prim.kind {
+                PictureKind::TextShadow { offset, color, blur_radius } if blur_radius == 0.0 => {
+                    let mut text_prim = prim.clone();
+                    text_prim.font.color = color.into();
+                    text_prim.offset += offset;
+                    fast_shadow_prims.push((idx, text_prim));
+                }
+                _ => {}
             }
         }
 
         for (idx, text_prim) in fast_shadow_prims {
             let rect = info.rect;
             let mut info = info.clone();
             info.rect = rect.translate(&text_prim.offset);
             let prim_index = self.create_primitive(
@@ -1147,17 +1154,17 @@ impl FrameBuilder {
         // used for both the visual element and also the shadow(s).
         let prim_index = self.create_primitive(
             info,
             Vec::new(),
             PrimitiveContainer::TextRun(prim),
         );
 
         // Only add a visual element if it can contribute to the scene.
-        if color.a > 0.0 {
+        if text_color.a > 0.0 {
             if self.shadow_prim_stack.is_empty() {
                 self.add_primitive_to_hit_testing_list(info, clip_and_scroll);
                 self.add_primitive_to_draw_list(prim_index, clip_and_scroll);
             } else {
                 self.pending_shadow_contents.push((prim_index, clip_and_scroll, *info));
             }
         }
 
@@ -1166,27 +1173,29 @@ impl FrameBuilder {
         // primitive here, they will still draw before the visual text, since
         // the shadow primitive itself has been added to the draw cmd
         // list *before* the visual element, during push_shadow. We need
         // the primitive index of the visual element here before we can add
         // the indices as sub-primitives to the shadow primitives.
         for &(shadow_prim_index, _) in &self.shadow_prim_stack {
             let shadow_metadata = &mut self.prim_store.cpu_metadata[shadow_prim_index.0];
             debug_assert_eq!(shadow_metadata.prim_kind, PrimitiveKind::Picture);
-            let picture_prim =
+            let picture =
                 &mut self.prim_store.cpu_pictures[shadow_metadata.cpu_prim_index.0];
 
-            // Only run real blurs here (fast path zero blurs are handled above).
-            let blur_radius = picture_prim.as_text_shadow().blur_radius;
-            if blur_radius > 0.0 {
-                picture_prim.add_primitive(
-                    prim_index,
-                    &rect,
-                    clip_and_scroll,
-                );
+            match picture.kind {
+                // Only run real blurs here (fast path zero blurs are handled above).
+                PictureKind::TextShadow { blur_radius, .. } if blur_radius > 0.0 => {
+                    picture.add_primitive(
+                        prim_index,
+                        &original_rect,
+                        clip_and_scroll,
+                    );
+                }
+                _ => {}
             }
         }
     }
 
     pub fn add_image(
         &mut self,
         clip_and_scroll: ClipAndScrollInfo,
         info: &LayerPrimitiveInfo,
@@ -1510,55 +1519,41 @@ impl FrameBuilder {
                 }
             }
         }
 
         mem::replace(&mut self.cmds, commands);
     }
 
     fn update_scroll_bars(&mut self, clip_scroll_tree: &ClipScrollTree, gpu_cache: &mut GpuCache) {
-        let distance_from_edge = 8.0;
+        static SCROLLBAR_PADDING: f32 = 8.0;
 
         for scrollbar_prim in &self.scrollbar_prims {
             let metadata = &mut self.prim_store.cpu_metadata[scrollbar_prim.prim_index.0];
-            let clip_scroll_node = &clip_scroll_tree.nodes[&scrollbar_prim.clip_id];
+            let scroll_frame = &clip_scroll_tree.nodes[&scrollbar_prim.clip_id];
 
             // Invalidate what's in the cache so it will get rebuilt.
             gpu_cache.invalidate(&metadata.gpu_location);
 
-            let scrollable_distance = clip_scroll_node.scrollable_size().height;
-
+            let scrollable_distance = scroll_frame.scrollable_size().height;
             if scrollable_distance <= 0.0 {
                 metadata.local_clip_rect.size = LayerSize::zero();
                 continue;
             }
-
-            let scroll_offset = clip_scroll_node.scroll_offset();
-            let f = -scroll_offset.y / scrollable_distance;
-
-            let min_y = clip_scroll_node.local_viewport_rect.origin.y - scroll_offset.y +
-                distance_from_edge;
-
-            let max_y = clip_scroll_node.local_viewport_rect.origin.y +
-                clip_scroll_node.local_viewport_rect.size.height -
-                scroll_offset.y - metadata.local_rect.size.height -
-                distance_from_edge;
+            let amount_scrolled = -scroll_frame.scroll_offset().y / scrollable_distance;
 
-            metadata.local_rect.origin.x = clip_scroll_node.local_viewport_rect.origin.x +
-                clip_scroll_node.local_viewport_rect.size.width -
-                metadata.local_rect.size.width -
-                distance_from_edge;
+            let frame_rect = scrollbar_prim.frame_rect;
+            let min_y = frame_rect.origin.y + SCROLLBAR_PADDING;
+            let max_y = frame_rect.origin.y + frame_rect.size.height -
+                (SCROLLBAR_PADDING + metadata.local_rect.size.height);
 
-            metadata.local_rect.origin.y = util::lerp(min_y, max_y, f);
+            metadata.local_rect.origin.x = frame_rect.origin.x + frame_rect.size.width -
+                (metadata.local_rect.size.width + SCROLLBAR_PADDING);
+            metadata.local_rect.origin.y = util::lerp(min_y, max_y, amount_scrolled);
             metadata.local_clip_rect = metadata.local_rect;
-
-            // TODO(gw): The code to set / update border clips on scroll bars
-            //           has been broken for a long time, so I've removed it
-            //           for now. We can re-add that code once the clips
-            //           data is moved over to the GPU cache!
         }
     }
 
     fn build_render_task(
         &mut self,
         clip_scroll_tree: &ClipScrollTree,
         gpu_cache: &mut GpuCache,
         render_tasks: &mut RenderTaskTree,
@@ -1711,17 +1706,17 @@ impl FrameBuilder {
                                             .inflate(inflate_size as i32);
                                 let blur_render_task = RenderTask::new_blur(
                                     blur_std_deviation,
                                     current_task_id,
                                     render_tasks,
                                     RenderTargetKind::Color,
                                     &[],
                                     ClearMode::Transparent,
-                                    ColorF::new(0.0, 0.0, 0.0, 0.0),
+                                    PremultipliedColorF::TRANSPARENT,
                                 );
                                 let blur_render_task_id = render_tasks.add(blur_render_task);
                                 let item = AlphaRenderItem::HardwareComposite(
                                     stacking_context_index,
                                     blur_render_task_id,
                                     HardwareCompositeOp::PremultipliedAlpha,
                                     DeviceIntPoint::new(
                                         screen_origin.x - inflate_size as i32,
@@ -1851,17 +1846,22 @@ impl FrameBuilder {
 
                     for i in 0 .. run.count {
                         let prim_index = PrimitiveIndex(run.base_prim_index.0 + i);
 
                         if self.prim_store.cpu_metadata[prim_index.0].screen_rect.is_some() {
                             self.prim_store
                                 .add_render_tasks_for_prim(prim_index, &mut current_task);
                             let item =
-                                AlphaRenderItem::Primitive(clip_node.id, scroll_node.id, prim_index, next_z);
+                                AlphaRenderItem::Primitive(
+                                    clip_node.node_data_index,
+                                    scroll_node.node_data_index,
+                                    prim_index,
+                                    next_z
+                                );
                             current_task.as_alpha_batch_mut().items.push(item);
                             next_z += 1;
                         }
                     }
                 }
             }
         }
 
@@ -1898,17 +1898,17 @@ impl FrameBuilder {
             DeviceIntSize::new(
                 self.screen_size.width as i32,
                 self.screen_size.height as i32,
             ),
         );
 
         let mut node_data = Vec::new();
 
-        clip_scroll_tree.update_all_node_transforms(
+        clip_scroll_tree.update_tree(
             &screen_rect,
             device_pixel_ratio,
             &mut self.clip_store,
             resource_cache,
             gpu_cache,
             pan,
             &mut node_data,
         );
--- a/gfx/webrender/src/glyph_cache.rs
+++ b/gfx/webrender/src/glyph_cache.rs
@@ -1,14 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-use api::{DevicePoint, DeviceUintSize, FontInstance, GlyphKey};
-use glyph_rasterizer::GlyphFormat;
+use api::{DevicePoint, DeviceUintSize, GlyphKey};
+use glyph_rasterizer::{FontInstance, GlyphFormat};
 use internal_types::FastHashMap;
 use resource_cache::ResourceClassCache;
 use std::sync::Arc;
 use texture_cache::TextureCacheHandle;
 
 pub struct CachedGlyphInfo {
     pub texture_cache_handle: TextureCacheHandle,
     pub glyph_bytes: Arc<Vec<u8>>,
--- a/gfx/webrender/src/glyph_rasterizer.rs
+++ b/gfx/webrender/src/glyph_rasterizer.rs
@@ -1,33 +1,85 @@
 /* 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/. */
 
 #[cfg(test)]
-use api::{ColorF, ColorU, IdNamespace, LayoutPoint, SubpixelDirection};
-use api::{DevicePoint, DeviceUintSize, FontInstance, FontRenderMode};
-use api::{FontKey, FontTemplate, GlyphDimensions, GlyphKey};
+use api::{IdNamespace, LayoutPoint};
+use api::{ColorF, ColorU, DevicePoint, DeviceUintSize};
+use api::{FontInstancePlatformOptions, FontRenderMode, FontVariation};
+use api::{FontKey, FontTemplate, GlyphDimensions, GlyphKey, SubpixelDirection};
 use api::{ImageData, ImageDescriptor, ImageFormat};
-#[cfg(test)]
 use app_units::Au;
 use device::TextureFilter;
 use glyph_cache::{CachedGlyphInfo, GlyphCache};
 use gpu_cache::GpuCache;
 use internal_types::FastHashSet;
 use platform::font::FontContext;
 use profiler::TextureCacheProfileCounters;
 use rayon::ThreadPool;
 use rayon::prelude::*;
 use std::collections::hash_map::Entry;
 use std::mem;
 use std::sync::{Arc, Mutex, MutexGuard};
 use std::sync::mpsc::{channel, Receiver, Sender};
 use texture_cache::{TextureCache, TextureCacheHandle};
 
+#[derive(Clone, Hash, PartialEq, Eq, Debug, Ord, PartialOrd)]
+pub struct FontInstance {
+    pub font_key: FontKey,
+    // The font size is in *device* pixels, not logical pixels.
+    // It is stored as an Au since we need sub-pixel sizes, but
+    // can't store as a f32 due to use of this type as a hash key.
+    // TODO(gw): Perhaps consider having LogicalAu and DeviceAu
+    //           or something similar to that.
+    pub size: Au,
+    pub color: ColorU,
+    pub bg_color: ColorU,
+    pub render_mode: FontRenderMode,
+    pub subpx_dir: SubpixelDirection,
+    pub platform_options: Option<FontInstancePlatformOptions>,
+    pub variations: Vec<FontVariation>,
+    pub synthetic_italics: bool,
+}
+
+impl FontInstance {
+    pub fn new(
+        font_key: FontKey,
+        size: Au,
+        color: ColorF,
+        bg_color: ColorU,
+        render_mode: FontRenderMode,
+        subpx_dir: SubpixelDirection,
+        platform_options: Option<FontInstancePlatformOptions>,
+        variations: Vec<FontVariation>,
+        synthetic_italics: bool,
+    ) -> Self {
+        FontInstance {
+            font_key,
+            size,
+            color: color.into(),
+            bg_color,
+            render_mode,
+            subpx_dir,
+            platform_options,
+            variations,
+            synthetic_italics,
+        }
+    }
+
+    pub fn get_subpx_offset(&self, glyph: &GlyphKey) -> (f64, f64) {
+        match self.subpx_dir {
+            SubpixelDirection::None => (0.0, 0.0),
+            SubpixelDirection::Horizontal => (glyph.subpixel_offset.into(), 0.0),
+            SubpixelDirection::Vertical => (0.0, glyph.subpixel_offset.into()),
+        }
+    }
+}
+
 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
 pub enum GlyphFormat {
     Mono,
     Alpha,
     Subpixel,
     ColorBitmap,
 }
 
--- a/gfx/webrender/src/gpu_cache.rs
+++ b/gfx/webrender/src/gpu_cache.rs
@@ -19,17 +19,17 @@
 //! data is not in the cache, the user provided closure
 //! will be invoked to build the data.
 //!
 //! After ```end_frame``` has occurred, callers can
 //! use the ```get_address``` API to get the allocated
 //! address in the GPU cache of a given resource slot
 //! for this frame.
 
-use api::{ColorF, LayerRect};
+use api::{LayerRect, PremultipliedColorF};
 use device::FrameId;
 use internal_types::UvRect;
 use profiler::GpuCacheProfileCounters;
 use renderer::MAX_VERTEX_TEXTURE_WIDTH;
 use std::{mem, u16, u32};
 use std::ops::Add;
 
 pub const GPU_CACHE_INITIAL_HEIGHT: u32 = 512;
@@ -59,17 +59,17 @@ pub struct GpuBlockData {
 
 impl GpuBlockData {
     pub fn empty() -> GpuBlockData {
         GpuBlockData { data: [0.0; 4] }
     }
 }
 
 /// Conversion helpers for GpuBlockData
-impl Into<GpuBlockData> for ColorF {
+impl Into<GpuBlockData> for PremultipliedColorF {
     fn into(self) -> GpuBlockData {
         GpuBlockData {
             data: [self.r, self.g, self.b, self.a],
         }
     }
 }
 
 impl Into<GpuBlockData> for [f32; 4] {
--- a/gfx/webrender/src/gpu_types.rs
+++ b/gfx/webrender/src/gpu_types.rs
@@ -26,17 +26,17 @@ pub struct BlurInstance {
 
 /// A clipping primitive drawn into the clipping mask.
 /// Could be an image or a rectangle, which defines the
 /// way `address` is treated.
 #[derive(Debug, Copy, Clone)]
 #[repr(C)]
 pub struct ClipMaskInstance {
     pub render_task_address: RenderTaskAddress,
-    pub scroll_node_id: ClipScrollNodeIndex,
+    pub scroll_node_data_index: ClipScrollNodeIndex,
     pub segment: i32,
     pub clip_data_address: GpuCacheAddress,
     pub resource_address: GpuCacheAddress,
 }
 
 // 32 bytes per instance should be enough for anyone!
 #[derive(Debug, Clone)]
 pub struct PrimitiveInstance {
--- a/gfx/webrender/src/picture.rs
+++ b/gfx/webrender/src/picture.rs
@@ -1,14 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-use api::{BorderRadiusKind, ColorF, ClipAndScrollInfo, device_length, DeviceIntSize};
-use api::{BoxShadowClipMode, LayerPoint, LayerRect, LayerSize, Shadow};
+use api::{BorderRadiusKind, ColorF, ClipAndScrollInfo};
+use api::{device_length, DeviceIntSize};
+use api::{BoxShadowClipMode, LayerPoint, LayerRect, LayerSize, LayerVector2D, Shadow};
 use box_shadow::BLUR_SAMPLE_SCALE;
 use frame_builder::PrimitiveContext;
 use gpu_cache::GpuDataRequest;
 use prim_store::{PrimitiveIndex, PrimitiveRun};
 use render_task::{ClearMode, RenderTask, RenderTaskId, RenderTaskTree};
 use tiling::RenderTargetKind;
 
 /*
@@ -19,17 +20,19 @@ use tiling::RenderTargetKind;
    picture into its parent.
  * A configuration describing how to draw the primitives on
    this picture (e.g. in screen space or local space).
  */
 
 #[derive(Debug)]
 pub enum PictureKind {
     TextShadow {
-        shadow: Shadow,
+        offset: LayerVector2D,
+        color: ColorF,
+        blur_radius: f32,
     },
     BoxShadow {
         blur_radius: f32,
         color: ColorF,
         blur_regions: Vec<LayerRect>,
         clip_mode: BoxShadowClipMode,
         radii_kind: BorderRadiusKind,
     },
@@ -43,55 +46,50 @@ pub struct PicturePrimitive {
     pub content_rect: LayerRect,
 
     // TODO(gw): Add a mode that specifies if this
     //           picture should be rasterized in
     //           screen-space or local-space.
 }
 
 impl PicturePrimitive {
-    pub fn new_text_shadow(shadow: Shadow) -> PicturePrimitive {
+    pub fn new_text_shadow(shadow: Shadow) -> Self {
         PicturePrimitive {
             prim_runs: Vec::new(),
             render_task_id: None,
             content_rect: LayerRect::zero(),
             kind: PictureKind::TextShadow {
-                shadow,
+                offset: shadow.offset,
+                color: shadow.color,
+                blur_radius: shadow.blur_radius,
             },
         }
     }
 
     pub fn new_box_shadow(
         blur_radius: f32,
         color: ColorF,
         blur_regions: Vec<LayerRect>,
         clip_mode: BoxShadowClipMode,
         radii_kind: BorderRadiusKind,
-    ) -> PicturePrimitive {
+    ) -> Self {
         PicturePrimitive {
             prim_runs: Vec::new(),
             render_task_id: None,
             content_rect: LayerRect::zero(),
             kind: PictureKind::BoxShadow {
                 blur_radius,
-                color: color.premultiplied(),
+                color,
                 blur_regions,
                 clip_mode,
                 radii_kind,
             },
         }
     }
 
-    pub fn as_text_shadow(&self) -> &Shadow {
-        match self.kind {
-            PictureKind::TextShadow { ref shadow } => shadow,
-            PictureKind::BoxShadow { .. } => panic!("bug: not a text shadow")
-        }
-    }
-
     pub fn add_primitive(
         &mut self,
         prim_index: PrimitiveIndex,
         local_rect: &LayerRect,
         clip_and_scroll: ClipAndScrollInfo
     ) {
         // TODO(gw): Accumulating the primitive local rect
         //           into the content rect here is fine, for now.
@@ -115,25 +113,25 @@ impl PicturePrimitive {
             base_prim_index: prim_index,
             count: 1,
             clip_and_scroll,
         });
     }
 
     pub fn build(&mut self) -> LayerRect {
         match self.kind {
-            PictureKind::TextShadow { ref shadow } => {
-                let blur_offset = shadow.blur_radius * BLUR_SAMPLE_SCALE;
+            PictureKind::TextShadow { offset, blur_radius, .. } => {
+                let blur_offset = blur_radius * BLUR_SAMPLE_SCALE;
 
                 self.content_rect = self.content_rect.inflate(
                     blur_offset,
                     blur_offset,
                 );
 
-                self.content_rect.translate(&shadow.offset)
+                self.content_rect.translate(&offset)
             }
             PictureKind::BoxShadow { blur_radius, clip_mode, radii_kind, .. } => {
                 // We need to inflate the content rect if outset.
                 match clip_mode {
                     BoxShadowClipMode::Outset => {
                         let blur_offset = blur_radius * BLUR_SAMPLE_SCALE;
 
                         // If the radii are uniform, we can render just the top
@@ -188,43 +186,43 @@ impl PicturePrimitive {
         // Gecko tests.
         let cache_width =
             (self.content_rect.size.width * prim_context.device_pixel_ratio).round() as i32;
         let cache_height =
             (self.content_rect.size.height * prim_context.device_pixel_ratio).round() as i32;
         let cache_size = DeviceIntSize::new(cache_width, cache_height);
 
         match self.kind {
-            PictureKind::TextShadow { ref shadow } => {
-                let blur_radius = device_length(shadow.blur_radius, prim_context.device_pixel_ratio);
+            PictureKind::TextShadow { blur_radius, color, .. } => {
+                let blur_radius = device_length(blur_radius, prim_context.device_pixel_ratio);
 
                 // Quote from https://drafts.csswg.org/css-backgrounds-3/#shadow-blur
                 // "the image that would be generated by applying to the shadow a
                 // Gaussian blur with a standard deviation equal to half the blur radius."
                 let blur_std_deviation = blur_radius.0 as f32 * 0.5;
 
                 let picture_task = RenderTask::new_picture(
                     cache_size,
                     prim_index,
                     RenderTargetKind::Color,
                     self.content_rect.origin,
-                    shadow.color,
+                    color.premultiplied(),
                     ClearMode::Transparent,
                 );
 
                 let picture_task_id = render_tasks.add(picture_task);
 
                 let render_task = RenderTask::new_blur(
                     blur_std_deviation,
                     picture_task_id,
                     render_tasks,
                     RenderTargetKind::Color,
                     &[],
                     ClearMode::Transparent,
-                    shadow.color,
+                    color.premultiplied(),
                 );
 
                 self.render_task_id = Some(render_tasks.add(render_task));
             }
             PictureKind::BoxShadow { blur_radius, clip_mode, ref blur_regions, color, .. } => {
                 let blur_radius = device_length(blur_radius, prim_context.device_pixel_ratio);
 
                 // Quote from https://drafts.csswg.org/css-backgrounds-3/#shadow-blur
@@ -241,30 +239,30 @@ impl PicturePrimitive {
                     }
                 };
 
                 let picture_task = RenderTask::new_picture(
                     cache_size,
                     prim_index,
                     RenderTargetKind::Alpha,
                     self.content_rect.origin,
-                    color,
+                    color.premultiplied(),
                     ClearMode::Zero,
                 );
 
                 let picture_task_id = render_tasks.add(picture_task);
 
                 let render_task = RenderTask::new_blur(
                     blur_std_deviation,
                     picture_task_id,
                     render_tasks,
                     RenderTargetKind::Alpha,
                     blur_regions,
                     blur_clear_mode,
-                    color,
+                    color.premultiplied(),
                 );
 
                 self.render_task_id = Some(render_tasks.add(render_task));
             }
         }
     }
 
     pub fn write_gpu_blocks(&self, mut _request: GpuDataRequest) {
--- a/gfx/webrender/src/platform/macos/font.rs
+++ b/gfx/webrender/src/platform/macos/font.rs
@@ -1,14 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{ColorU, FontKey, FontRenderMode, GlyphDimensions};
-use api::{FontInstance, FontVariation, NativeFontHandle};
+use api::{FontVariation, NativeFontHandle};
 use api::{GlyphKey, SubpixelDirection};
 use app_units::Au;
 use core_foundation::array::{CFArray, CFArrayRef};
 use core_foundation::base::TCFType;
 use core_foundation::dictionary::{CFDictionary, CFDictionaryRef};
 use core_foundation::number::{CFNumber, CFNumberRef};
 use core_foundation::string::{CFString, CFStringRef};
 use core_graphics::base::{kCGImageAlphaNoneSkipFirst, kCGImageAlphaPremultipliedFirst};
@@ -17,17 +17,17 @@ use core_graphics::color_space::CGColorS
 use core_graphics::context::{CGContext, CGTextDrawingMode};
 use core_graphics::data_provider::CGDataProvider;
 use core_graphics::font::{CGFont, CGFontRef, CGGlyph};
 use core_graphics::geometry::{CGPoint, CGRect, CGSize};
 use core_text;
 use core_text::font::{CTFont, CTFontRef};
 use core_text::font_descriptor::{kCTFontDefaultOrientation, kCTFontColorGlyphsTrait};
 use gamma_lut::{ColorLut, GammaLut};
-use glyph_rasterizer::{GlyphFormat, RasterizedGlyph};
+use glyph_rasterizer::{FontInstance, GlyphFormat, RasterizedGlyph};
 use internal_types::FastHashMap;
 use std::collections::hash_map::Entry;
 use std::ptr;
 use std::sync::Arc;
 
 pub struct FontContext {
     cg_fonts: FastHashMap<FontKey, CGFont>,
     ct_fonts: FastHashMap<(FontKey, Au, Vec<FontVariation>), CTFont>,
--- a/gfx/webrender/src/platform/unix/font.rs
+++ b/gfx/webrender/src/platform/unix/font.rs
@@ -1,29 +1,29 @@
 /* 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 api::{FontInstance, FontKey, FontRenderMode, GlyphDimensions};
+use api::{ColorU, GlyphDimensions, GlyphKey, FontKey, FontRenderMode};
 use api::{FontInstancePlatformOptions, FontLCDFilter, FontHinting};
-use api::{NativeFontHandle, SubpixelDirection, GlyphKey, ColorU};
+use api::{NativeFontHandle, SubpixelDirection};
 use api::{FONT_FORCE_AUTOHINT, FONT_NO_AUTOHINT, FONT_EMBEDDED_BITMAP};
 use api::{FONT_EMBOLDEN, FONT_VERTICAL_LAYOUT, FONT_SUBPIXEL_BGR};
 use freetype::freetype::{FT_BBox, FT_Outline_Translate, FT_Pixel_Mode, FT_Render_Mode};
 use freetype::freetype::{FT_Done_Face, FT_Error, FT_Get_Char_Index, FT_Int32};
 use freetype::freetype::{FT_Done_FreeType, FT_Library_SetLcdFilter, FT_Pos};
 use freetype::freetype::{FT_F26Dot6, FT_Face, FT_Glyph_Format, FT_Long, FT_UInt};
 use freetype::freetype::{FT_GlyphSlot, FT_LcdFilter, FT_New_Face, FT_New_Memory_Face};
 use freetype::freetype::{FT_Init_FreeType, FT_Load_Glyph, FT_Render_Glyph};
 use freetype::freetype::{FT_Library, FT_Outline_Get_CBox, FT_Set_Char_Size, FT_Select_Size};
 use freetype::freetype::{FT_LOAD_COLOR, FT_LOAD_DEFAULT, FT_LOAD_FORCE_AUTOHINT};
 use freetype::freetype::{FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH, FT_LOAD_NO_AUTOHINT};
 use freetype::freetype::{FT_LOAD_NO_BITMAP, FT_LOAD_NO_HINTING, FT_LOAD_VERTICAL_LAYOUT};
 use freetype::freetype::{FT_FACE_FLAG_SCALABLE, FT_FACE_FLAG_FIXED_SIZES, FT_Err_Cannot_Render_Glyph};
-use glyph_rasterizer::{GlyphFormat, RasterizedGlyph};
+use glyph_rasterizer::{FontInstance, GlyphFormat, RasterizedGlyph};
 use internal_types::FastHashMap;
 use std::{cmp, mem, ptr, slice};
 use std::cmp::max;
 use std::ffi::CString;
 use std::sync::Arc;
 
 // These constants are not present in the freetype
 // bindings due to bindgen not handling the way
@@ -393,23 +393,23 @@ impl FontContext {
         }
         unsafe { FT_Select_Size(face, best_size) }
     }
 
     pub fn prepare_font(font: &mut FontInstance) {
         match font.render_mode {
             FontRenderMode::Mono | FontRenderMode::Bitmap => {
                 // In mono/bitmap modes the color of the font is irrelevant.
-                font.color = ColorU::new(255, 255, 255, 255);
+                font.color = ColorU::new(0xFF, 0xFF, 0xFF, 0xFF);
                 // Subpixel positioning is disabled in mono and bitmap modes.
                 font.subpx_dir = SubpixelDirection::None;
             }
             FontRenderMode::Alpha | FontRenderMode::Subpixel => {
                 // We don't do any preblending with FreeType currently, so the color is not used.
-                font.color = ColorU::new(255, 255, 255, 255);
+                font.color = ColorU::new(0xFF, 0xFF, 0xFF, 0xFF);
             }
         }
     }
 
     fn rasterize_glyph_outline(
         &mut self,
         slot: FT_GlyphSlot,
         font: &FontInstance,
--- a/gfx/webrender/src/platform/windows/font.rs
+++ b/gfx/webrender/src/platform/windows/font.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/. */
 
-use api::{FontInstance, FontInstancePlatformOptions, FontKey, FontRenderMode};
+use api::{FontInstancePlatformOptions, FontKey, FontRenderMode};
 use api::{ColorU, GlyphDimensions, GlyphKey, SubpixelDirection};
 use dwrote;
 use gamma_lut::{ColorLut, GammaLut};
-use glyph_rasterizer::{GlyphFormat, RasterizedGlyph};
+use glyph_rasterizer::{FontInstance, GlyphFormat, RasterizedGlyph};
 use internal_types::FastHashMap;
 use std::sync::Arc;
 
 lazy_static! {
     static ref DEFAULT_FONT_DESCRIPTOR: dwrote::FontDescriptor = dwrote::FontDescriptor {
         family_name: "Arial".to_owned(),
         weight: dwrote::FontWeight::Regular,
         stretch: dwrote::FontStretch::Normal,
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -1,20 +1,22 @@
 /* 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 api::{BorderRadius, BuiltDisplayList, ColorF, ComplexClipRegion, DeviceIntRect};
-use api::{DevicePoint, ExtendMode, FontInstance, GlyphInstance, GlyphKey};
+use api::{DevicePoint, ExtendMode, GlyphInstance, GlyphKey};
 use api::{GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag, LayerPoint, LayerRect};
 use api::{ClipMode, LayerSize, LayerVector2D, LineOrientation, LineStyle};
-use api::{ClipAndScrollInfo, EdgeAaSegmentMask, TileOffset, YuvColorSpace, YuvFormat};
+use api::{ClipAndScrollInfo, EdgeAaSegmentMask, PremultipliedColorF, TileOffset};
+use api::{YuvColorSpace, YuvFormat};
 use border::BorderCornerInstance;
 use clip::{ClipSourcesHandle, ClipStore, Geometry};
 use frame_builder::PrimitiveContext;
+use glyph_rasterizer::FontInstance;
 use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest,
                 ToGpuBlocks};
 use picture::PicturePrimitive;
 use render_task::{ClipWorkItem, ClipChainNode, RenderTask, RenderTaskId, RenderTaskTree};
 use renderer::MAX_VERTEX_TEXTURE_WIDTH;
 use resource_cache::{ImageProperties, ResourceCache};
 use std::{mem, usize};
 use std::rc::Rc;
@@ -175,25 +177,21 @@ pub enum RectangleContent {
 #[derive(Debug)]
 pub struct RectanglePrimitive {
     pub content: RectangleContent,
     pub edge_aa_segment_mask: EdgeAaSegmentMask,
 }
 
 impl ToGpuBlocks for RectanglePrimitive {
     fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
-        match &self.content {
-            &RectangleContent::Fill(ref color) => {
-                request.push(color.premultiplied());
-            }
-            &RectangleContent::Clear => {
-                // Opaque black with operator dest out
-                request.push(ColorF::new(0.0, 0.0, 0.0, 1.0));
-            }
-        }
+        request.push(match self.content {
+            RectangleContent::Fill(color) => color.premultiplied(),
+            // Opaque black with operator dest out
+            RectangleContent::Clear => PremultipliedColorF::BLACK,
+        });
         request.extend_from_slice(&[GpuBlockData {
             data: [self.edge_aa_segment_mask.bits() as f32, 0.0, 0.0, 0.0],
         }]);
     }
 }
 
 #[derive(Debug)]
 pub enum BrushMaskKind {
@@ -249,17 +247,17 @@ impl ToGpuBlocks for BrushPrimitive {
             }
         }
     }
 }
 
 #[derive(Debug, Clone)]
 #[repr(C)]
 pub struct LinePrimitive {
-    pub color: ColorF,
+    pub color: PremultipliedColorF,
     pub wavy_line_thickness: f32,
     pub style: LineStyle,
     pub orientation: LineOrientation,
 }
 
 impl ToGpuBlocks for LinePrimitive {
     fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
         request.push(self.color);
@@ -372,44 +370,44 @@ pub const GRADIENT_DATA_TABLE_SIZE: usiz
 
 // The number of entries in a gradient data: GRADIENT_DATA_TABLE_SIZE + first stop entry + last stop entry
 pub const GRADIENT_DATA_SIZE: usize = GRADIENT_DATA_TABLE_SIZE + 2;
 
 #[derive(Debug)]
 #[repr(C)]
 // An entry in a gradient data table representing a segment of the gradient color space.
 pub struct GradientDataEntry {
-    pub start_color: ColorF,
-    pub end_color: ColorF,
+    pub start_color: PremultipliedColorF,
+    pub end_color: PremultipliedColorF,
 }
 
 struct GradientGpuBlockBuilder<'a> {
     stops_range: ItemRange<GradientStop>,
     display_list: &'a BuiltDisplayList,
 }
 
 impl<'a> GradientGpuBlockBuilder<'a> {
     fn new(
         stops_range: ItemRange<GradientStop>,
         display_list: &'a BuiltDisplayList,
-    ) -> GradientGpuBlockBuilder<'a> {
+    ) -> Self {
         GradientGpuBlockBuilder {
             stops_range,
             display_list,
         }
     }
 
     /// Generate a color ramp filling the indices in [start_idx, end_idx) and interpolating
     /// from start_color to end_color.
     fn fill_colors(
         &self,
         start_idx: usize,
         end_idx: usize,
-        start_color: &ColorF,
-        end_color: &ColorF,
+        start_color: &PremultipliedColorF,
+        end_color: &PremultipliedColorF,
         entries: &mut [GradientDataEntry; GRADIENT_DATA_SIZE],
     ) {
         // Calculate the color difference for individual steps in the ramp.
         let inv_steps = 1.0 / (end_idx - start_idx) as f32;
         let step_r = (end_color.r - start_color.r) * inv_steps;
         let step_g = (end_color.g - start_color.g) * inv_steps;
         let step_b = (end_color.b - start_color.b) * inv_steps;
         let step_a = (end_color.a - start_color.a) * inv_steps;
@@ -623,18 +621,22 @@ impl TextRunPrimitiveCpu {
                 self.glyph_gpu_blocks.push(gpu_block);
             }
         }
 
         resource_cache.request_glyphs(font, &self.glyph_keys, gpu_cache);
     }
 
     fn write_gpu_blocks(&self, request: &mut GpuDataRequest) {
+        let bg_color = ColorF::from(self.font.bg_color);
         request.push(ColorF::from(self.font.color).premultiplied());
-        request.push(ColorF::from(self.font.bg_color));
+        // this is the only case where we need to provide plain color to GPU
+        request.extend_from_slice(&[
+            GpuBlockData { data: [bg_color.r, bg_color.g, bg_color.b, 1.0] }
+        ]);
         request.push([
             self.offset.x,
             self.offset.y,
             self.font.subpx_dir.limit_by(self.font.render_mode) as u32 as f32,
             0.0,
         ]);
         request.extend_from_slice(&self.glyph_gpu_blocks);
 
@@ -1234,34 +1236,35 @@ impl PrimitiveStore {
             resource_cache,
             prim_context.device_pixel_ratio,
         );
 
         // Try to create a mask if we may need to.
         let prim_clips = clip_store.get(&metadata.clip_sources);
         let is_axis_aligned = transform.transform_kind() == TransformedRectKind::AxisAligned;
 
-        let clip_task = if prim_context.clip_node.clip_chain_node.is_some() || prim_clips.is_masking() {
+        let has_clips = prim_context.clip_node.clip_chain_node.is_some() || prim_clips.has_clips();
+        let clip_task = if has_clips {
             // Take into account the actual clip info of the primitive, and
             // mutate the current bounds accordingly.
             let mask_rect = match prim_clips.bounds.outer {
                 Some(ref outer) => match prim_screen_rect.intersection(&outer.device_rect) {
                     Some(rect) => rect,
                     None => {
                         metadata.screen_rect = None;
                         return false;
                     }
                 },
                 _ => prim_screen_rect,
             };
 
-            let extra_clip = if prim_clips.is_masking() {
+            let extra_clip = if prim_clips.has_clips() {
                 Some(Rc::new(ClipChainNode {
                     work_item: ClipWorkItem {
-                        scroll_node_id: prim_context.scroll_node.id,
+                        scroll_node_data_index: prim_context.scroll_node.node_data_index,
                         clip_sources: metadata.clip_sources.weak(),
                         coordinate_system_id: prim_context.scroll_node.coordinate_system_id,
                     },
                     prev: None,
                 }))
             } else {
                 None
             };
--- a/gfx/webrender/src/profiler.rs
+++ b/gfx/webrender/src/profiler.rs
@@ -563,18 +563,18 @@ impl ProfileGraph {
         );
 
         rect.size.width += 140.0;
         debug_renderer.add_quad(
             rect.origin.x,
             rect.origin.y,
             rect.origin.x + rect.size.width + 10.0,
             rect.origin.y + rect.size.height,
-            ColorF::new(0.1, 0.1, 0.1, 0.8).into(),
-            ColorF::new(0.2, 0.2, 0.2, 0.8).into(),
+            ColorU::new(25, 25, 25, 200),
+            ColorU::new(51, 51, 51, 200),
         );
 
         let bx0 = x + 10.0;
         let by0 = y + 10.0;
         let bx1 = bx0 + size.width - 20.0;
         let by1 = by0 + size.height - 20.0;
 
         let w = (bx1 - bx0) / self.max_samples as f32;
@@ -650,18 +650,18 @@ impl GpuFrameCollection {
         );
         let graph_rect = bounding_rect.inflate(-GRAPH_PADDING, -GRAPH_PADDING);
 
         debug_renderer.add_quad(
             bounding_rect.origin.x,
             bounding_rect.origin.y,
             bounding_rect.origin.x + bounding_rect.size.width,
             bounding_rect.origin.y + bounding_rect.size.height,
-            ColorF::new(0.1, 0.1, 0.1, 0.8).into(),
-            ColorF::new(0.2, 0.2, 0.2, 0.8).into(),
+            ColorU::new(25, 25, 25, 200),
+            ColorU::new(51, 51, 51, 200),
         );
 
         let w = graph_rect.size.width;
         let mut y0 = graph_rect.origin.y;
 
         let max_time = self.frames
             .iter()
             .max_by_key(|f| f.total_time)
--- a/gfx/webrender/src/render_backend.rs
+++ b/gfx/webrender/src/render_backend.rs
@@ -466,21 +466,23 @@ impl RenderBackend {
                 }
             };
 
             match msg {
                 ApiMsg::UpdateResources(updates) => {
                     self.resource_cache
                         .update_resources(updates, &mut profile_counters.resources);
                 }
-                ApiMsg::GetGlyphDimensions(font, glyph_keys, tx) => {
+                ApiMsg::GetGlyphDimensions(instance_key, glyph_keys, tx) => {
                     let mut glyph_dimensions = Vec::with_capacity(glyph_keys.len());
-                    for glyph_key in &glyph_keys {
-                        let glyph_dim = self.resource_cache.get_glyph_dimensions(&font, glyph_key);
-                        glyph_dimensions.push(glyph_dim);
+                    if let Some(font) = self.resource_cache.get_font_instance(instance_key) {
+                        for glyph_key in &glyph_keys {
+                            let glyph_dim = self.resource_cache.get_glyph_dimensions(&font, glyph_key);
+                            glyph_dimensions.push(glyph_dim);
+                        }
                     }
                     tx.send(glyph_dimensions).unwrap();
                 }
                 ApiMsg::GetGlyphIndices(font_key, text, tx) => {
                     let mut glyph_indices = Vec::new();
                     for ch in text.chars() {
                         let index = self.resource_cache.get_glyph_index(font_key, ch);
                         glyph_indices.push(index);
--- a/gfx/webrender/src/render_task.rs
+++ b/gfx/webrender/src/render_task.rs
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{ClipId, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
-use api::{ColorF, FilterOp, LayerPoint, MixBlendMode};
-use api::{LayerRect, PipelineId};
+use api::{FilterOp, LayerPoint, LayerRect, MixBlendMode};
+use api::{PipelineId, PremultipliedColorF};
 use clip::{ClipSource, ClipSourcesWeakHandle, ClipStore};
 use clip_scroll_tree::CoordinateSystemId;
 use gpu_cache::GpuCacheHandle;
 use gpu_types::{ClipScrollNodeIndex};
 use internal_types::HardwareCompositeOp;
 use prim_store::PrimitiveIndex;
 use std::{cmp, usize, f32, i32};
 use std::rc::Rc;
@@ -199,17 +199,17 @@ pub enum MaskSegment {
 pub enum MaskGeometryKind {
     Default, // Draw the entire rect
     CornersOnly, // Draw the corners (simple axis aligned mask)
              // TODO(gw): Add more types here (e.g. 4 rectangles outside the inner rect)
 }
 
 #[derive(Debug, Clone)]
 pub struct ClipWorkItem {
-    pub scroll_node_id: ClipScrollNodeIndex,
+    pub scroll_node_data_index: ClipScrollNodeIndex,
     pub clip_sources: ClipSourcesWeakHandle,
     pub coordinate_system_id: CoordinateSystemId,
 }
 
 impl ClipWorkItem {
     fn get_geometry_kind(
         &self,
         clip_store: &ClipStore,
@@ -258,25 +258,25 @@ pub struct CacheMaskTask {
     pub coordinate_system_id: CoordinateSystemId,
 }
 
 #[derive(Debug)]
 pub struct PictureTask {
     pub prim_index: PrimitiveIndex,
     pub target_kind: RenderTargetKind,
     pub content_origin: LayerPoint,
-    pub color: ColorF,
+    pub color: PremultipliedColorF,
 }
 
 #[derive(Debug)]
 pub struct BlurTask {
     pub blur_std_deviation: f32,
     pub target_kind: RenderTargetKind,
     pub regions: Vec<LayerRect>,
-    pub color: ColorF,
+    pub color: PremultipliedColorF,
     pub scale_factor: f32,
 }
 
 #[derive(Debug)]
 pub struct RenderTaskData {
     pub data: [f32; FLOATS_PER_RENDER_TASK_INFO],
 }
 
@@ -311,61 +311,61 @@ pub struct RenderTask {
     pub clear_mode: ClearMode,
 }
 
 impl RenderTask {
     pub fn new_alpha_batch(
         screen_origin: DeviceIntPoint,
         location: RenderTaskLocation,
         frame_output_pipeline_id: Option<PipelineId>,
-    ) -> RenderTask {
+    ) -> Self {
         RenderTask {
             cache_key: None,
             children: Vec::new(),
             location,
             kind: RenderTaskKind::Alpha(AlphaRenderTask {
                 screen_origin,
                 items: Vec::new(),
                 frame_output_pipeline_id,
             }),
             clear_mode: ClearMode::Transparent,
         }
     }
 
     pub fn new_dynamic_alpha_batch(
         rect: &DeviceIntRect,
         frame_output_pipeline_id: Option<PipelineId>,
-    ) -> RenderTask {
+    ) -> Self {
         let location = RenderTaskLocation::Dynamic(None, rect.size);
         Self::new_alpha_batch(rect.origin, location, frame_output_pipeline_id)
     }
 
     pub fn new_picture(
         size: DeviceIntSize,
         prim_index: PrimitiveIndex,
         target_kind: RenderTargetKind,
         content_origin: LayerPoint,
-        color: ColorF,
+        color: PremultipliedColorF,
         clear_mode: ClearMode,
-    ) -> RenderTask {
+    ) -> Self {
         RenderTask {
             cache_key: None,
             children: Vec::new(),
             location: RenderTaskLocation::Dynamic(None, size),
             kind: RenderTaskKind::Picture(PictureTask {
                 prim_index,
                 target_kind,
                 content_origin,
                 color,
             }),
             clear_mode,
         }
     }
 
-    pub fn new_readback(screen_rect: DeviceIntRect) -> RenderTask {
+    pub fn new_readback(screen_rect: DeviceIntRect) -> Self {
         RenderTask {
             cache_key: None,
             children: Vec::new(),
             location: RenderTaskLocation::Dynamic(None, screen_rect.size),
             kind: RenderTaskKind::Readback(screen_rect),
             clear_mode: ClearMode::Transparent,
         }
     }
@@ -374,17 +374,17 @@ impl RenderTask {
         key: Option<ClipId>,
         task_rect: DeviceIntRect,
         raw_clips: ClipChain,
         extra_clip: ClipChain,
         prim_rect: DeviceIntRect,
         clip_store: &ClipStore,
         is_axis_aligned: bool,
         prim_coordinate_system_id: CoordinateSystemId,
-    ) -> Option<RenderTask> {
+    ) -> Option<Self> {
         // Filter out all the clip instances that don't contribute to the result
         let mut current_coordinate_system_id = prim_coordinate_system_id;
         let mut inner_rect = Some(task_rect);
         let clips: Vec<_> = ClipChainNodeIter { current: raw_clips }
             .chain(ClipChainNodeIter { current: extra_clip })
             .filter_map(|node| {
                 let work_item = node.work_item.clone();
 
@@ -394,17 +394,17 @@ impl RenderTask {
                     current_coordinate_system_id = node.work_item.coordinate_system_id;
                     inner_rect = None;
                     return Some(work_item)
                 }
 
                 let clip_info = clip_store
                     .get_opt(&node.work_item.clip_sources)
                     .expect("bug: clip item should exist");
-                debug_assert!(clip_info.is_masking());
+                debug_assert!(clip_info.has_clips());
 
                 match clip_info.bounds.inner {
                     Some(ref inner) if !inner.device_rect.is_empty() => {
                         inner_rect = inner_rect.and_then(|r| r.intersection(&inner.device_rect));
                         if inner.device_rect.contains_rect(&task_rect) {
                             return None;
                         }
                     }
@@ -448,17 +448,17 @@ impl RenderTask {
                 clips,
                 geometry_kind,
                 coordinate_system_id: prim_coordinate_system_id,
             }),
             clear_mode: ClearMode::One,
         })
     }
 
-    // Construct a render task to apply a blur to a primitive. 
+    // Construct a render task to apply a blur to a primitive.
     // The render task chain that is constructed looks like:
     //
     //    PrimitiveCacheTask: Draw the primitives.
     //           ^
     //           |
     //    DownscalingTask(s): Each downscaling task reduces the size of render target to
     //           ^            half. Also reduce the std deviation to half until the std
     //           |            deviation less than 4.0.
@@ -473,18 +473,18 @@ impl RenderTask {
     //
     pub fn new_blur(
         blur_std_deviation: f32,
         src_task_id: RenderTaskId,
         render_tasks: &mut RenderTaskTree,
         target_kind: RenderTargetKind,
         regions: &[LayerRect],
         clear_mode: ClearMode,
-        color: ColorF,
-    ) -> RenderTask {
+        color: PremultipliedColorF,
+    ) -> Self {
         // Adjust large std deviation value.
         let mut adjusted_blur_std_deviation = blur_std_deviation;
         let blur_target_size = render_tasks.get(src_task_id).get_dynamic_size();
         let mut adjusted_blur_target_size = blur_target_size;
         let mut downscaling_src_task_id = src_task_id;
         let mut scale_factor = 1.0;
         while adjusted_blur_std_deviation > MAX_BLUR_STD_DEVIATION {
             if adjusted_blur_target_size.width < MIN_DOWNSCALING_RT_SIZE ||
@@ -535,17 +535,17 @@ impl RenderTask {
 
         blur_task_h
     }
 
     pub fn new_scaling(
         target_kind: RenderTargetKind,
         src_task_id: RenderTaskId,
         target_size: DeviceIntSize,
-    ) -> RenderTask {
+    ) -> Self {
         RenderTask {
             cache_key: None,
             children: vec![src_task_id],
             location: RenderTaskLocation::Dynamic(None, target_size),
             kind: RenderTaskKind::Scaling(target_kind),
             clear_mode: match target_kind {
                 RenderTargetKind::Color => ClearMode::Transparent,
                 RenderTargetKind::Alpha => ClearMode::One,
@@ -644,33 +644,33 @@ impl RenderTask {
                         0.0,
                         task.inner_rect.origin.x as f32,
                         task.inner_rect.origin.y as f32,
                         (task.inner_rect.origin.x + task.inner_rect.size.width) as f32,
                         (task.inner_rect.origin.y + task.inner_rect.size.height) as f32,
                     ],
                 }
             }
-            RenderTaskKind::VerticalBlur(ref task_info) |
-            RenderTaskKind::HorizontalBlur(ref task_info) => {
+            RenderTaskKind::VerticalBlur(ref task) |
+            RenderTaskKind::HorizontalBlur(ref task) => {
                 let (target_rect, target_index) = self.get_target_rect();
                 RenderTaskData {
                     data: [
                         target_rect.origin.x as f32,
                         target_rect.origin.y as f32,
                         target_rect.size.width as f32,
                         target_rect.size.height as f32,
                         target_index.0 as f32,
-                        task_info.blur_std_deviation,
-                        task_info.scale_factor,
+                        task.blur_std_deviation,
+                        task.scale_factor,
                         0.0,
-                        task_info.color.r,
-                        task_info.color.g,
-                        task_info.color.b,
-                        task_info.color.a,
+                        task.color.r,
+                        task.color.g,
+                        task.color.b,
+                        task.color.a,
                     ],
                 }
             }
             RenderTaskKind::Readback(..) |
             RenderTaskKind::Scaling(..) => {
                 let (target_rect, target_index) = self.get_target_rect();
                 RenderTaskData {
                     data: [
--- a/gfx/webrender/src/renderer.rs
+++ b/gfx/webrender/src/renderer.rs
@@ -5,17 +5,17 @@
 //! The webrender API.
 //!
 //! The `webrender::renderer` module provides the interface to webrender, which
 //! is accessible through [`Renderer`][renderer]
 //!
 //! [renderer]: struct.Renderer.html
 
 use api::{channel, BlobImageRenderer, FontRenderMode};
-use api::{ColorF, ColorU, Epoch, PipelineId, RenderApiSender, RenderNotifier};
+use api::{ColorF, Epoch, PipelineId, RenderApiSender, RenderNotifier};
 use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceUintRect, DeviceUintSize};
 use api::{ExternalImageId, ExternalImageType, ImageFormat};
 use api::{YUV_COLOR_SPACES, YUV_FORMATS};
 use api::{YuvColorSpace, YuvFormat};
 #[cfg(not(feature = "debugger"))]
 use api::ApiMsg;
 use api::DebugCommand;
 #[cfg(not(feature = "debugger"))]
@@ -634,20 +634,19 @@ impl SourceTextureResolver {
         }
     }
 }
 
 #[derive(Debug, Copy, Clone, PartialEq)]
 #[allow(dead_code)] // SubpixelVariableTextColor is not used at the moment.
 pub enum BlendMode {
     None,
-    Alpha,
     PremultipliedAlpha,
     PremultipliedDestOut,
-    SubpixelConstantTextColor(ColorU),
+    SubpixelConstantTextColor(ColorF),
     SubpixelWithBgColor,
     SubpixelVariableTextColor,
 }
 
 // Tracks the state of each row in the GPU cache texture.
 struct CacheRow {
     is_dirty: bool,
 }
@@ -1008,17 +1007,16 @@ impl BrushShader {
         projection: &Transform3D<f32>,
         mode: M,
         renderer_errors: &mut Vec<RendererError>,
     ) where M: Into<ShaderMode> {
         match blend_mode {
             BlendMode::None => {
                 self.opaque.bind(device, projection, mode, renderer_errors)
             }
-            BlendMode::Alpha |
             BlendMode::PremultipliedAlpha |
             BlendMode::PremultipliedDestOut |
             BlendMode::SubpixelConstantTextColor(..) |
             BlendMode::SubpixelVariableTextColor |
             BlendMode::SubpixelWithBgColor => {
                 self.alpha.bind(device, projection, mode, renderer_errors)
             }
         }
@@ -2512,17 +2510,16 @@ impl Renderer {
                         GPU_TAG_BRUSH_IMAGE
                     }
                 }
             }
             BatchKind::Transformable(transform_kind, batch_kind) => match batch_kind {
                 TransformBatchKind::Rectangle(needs_clipping) => {
                     debug_assert!(
                         !needs_clipping || match key.blend_mode {
-                            BlendMode::Alpha |
                             BlendMode::PremultipliedAlpha |
                             BlendMode::PremultipliedDestOut |
                             BlendMode::SubpixelConstantTextColor(..) |
                             BlendMode::SubpixelVariableTextColor |
                             BlendMode::SubpixelWithBgColor => true,
                             BlendMode::None => false,
                         }
                     );
@@ -2754,17 +2751,16 @@ impl Renderer {
     ) {
         {
             let _gm = self.gpu_profile.add_marker(GPU_TAG_SETUP_TARGET);
             self.device
                 .bind_draw_target(render_target, Some(target_size));
             self.device.disable_depth();
             self.device.enable_depth_write();
             self.device.set_blend(false);
-            self.device.set_blend_mode_alpha();
             match render_target {
                 Some(..) if self.enable_clear_scissor => {
                     // TODO(gw): Applying a scissor rect and minimal clear here
                     // is a very large performance win on the Intel and nVidia
                     // GPUs that I have tested with. It's possible it may be a
                     // performance penalty on other GPU types - we should test this
                     // and consider different code paths.
                     self.device
@@ -2813,34 +2809,34 @@ impl Renderer {
         // Draw any textrun caches for this target. For now, this
         // is only used to cache text runs that are to be blurred
         // for shadow support. In the future it may be worth
         // considering using this for (some) other text runs, since
         // it removes the overhead of submitting many small glyphs
         // to multiple tiles in the normal text run case.
         if !target.text_run_cache_prims.is_empty() {
             self.device.set_blend(true);
-            self.device.set_blend_mode_alpha();
+            self.device.set_blend_mode_premultiplied_alpha();
 
             let _gm = self.gpu_profile.add_marker(GPU_TAG_CACHE_TEXT_RUN);
             self.cs_text_run
                 .bind(&mut self.device, projection, 0, &mut self.renderer_errors);
             for (texture_id, instances) in &target.text_run_cache_prims {
                 self.draw_instanced_batch(
                     instances,
                     VertexArrayKind::Primitive,
                     &BatchTextures::color(*texture_id),
                 );
             }
         }
         if !target.line_cache_prims.is_empty() {
             // TODO(gw): Technically, we don't need blend for solid
             //           lines. We could check that here?
             self.device.set_blend(true);
-            self.device.set_blend_mode_alpha();
+            self.device.set_blend_mode_premultiplied_alpha();
 
             let _gm = self.gpu_profile.add_marker(GPU_TAG_CACHE_LINE);
             self.cs_line
                 .bind(&mut self.device, projection, 0, &mut self.renderer_errors);
             self.draw_instanced_batch(
                 &target.line_cache_prims,
                 VertexArrayKind::Primitive,
                 &BatchTextures::no_texture(),
@@ -2883,17 +2879,16 @@ impl Renderer {
 
             self.device.disable_depth_write();
             self.gpu_profile.add_sampler(GPU_SAMPLER_TAG_TRANSPARENT);
 
             for batch in &target.alpha_batcher.batch_list.alpha_batch_list.batches {
                 if self.debug_flags.contains(DebugFlags::ALPHA_PRIM_DBG) {
                     let color = match batch.key.blend_mode {
                         BlendMode::None => debug_colors::BLACK,
-                        BlendMode::Alpha => debug_colors::YELLOW,
                         BlendMode::PremultipliedAlpha => debug_colors::GREY,
                         BlendMode::PremultipliedDestOut => debug_colors::SALMON,
                         BlendMode::SubpixelConstantTextColor(..) => debug_colors::GREEN,
                         BlendMode::SubpixelVariableTextColor => debug_colors::RED,
                         BlendMode::SubpixelWithBgColor => debug_colors::BLUE,
                     }.into();
                     for item_rect in &batch.item_rects {
                         self.debug.add_rect(item_rect, color);
@@ -2927,17 +2922,17 @@ impl Renderer {
 
                                 self.draw_instanced_batch(
                                     &batch.instances,
                                     VertexArrayKind::Primitive,
                                     &batch.key.textures
                                 );
                             }
                             BlendMode::SubpixelConstantTextColor(color) => {
-                                self.device.set_blend_mode_subpixel_constant_text_color(color.into());
+                                self.device.set_blend_mode_subpixel_constant_text_color(color);
 
                                 self.ps_text_run.bind(
                                     &mut self.device,
                                     transform_kind,
                                     projection,
                                     TextShaderMode::SubpixelConstantTextColor,
                                     &mut self.renderer_errors,
                                 );
@@ -3033,34 +3028,30 @@ impl Renderer {
                                     projection,
                                     TextShaderMode::SubpixelWithBgColorPass2,
                                     &mut self.renderer_errors,
                                 );
 
                                 self.device
                                     .draw_indexed_triangles_instanced_u16(6, batch.instances.len() as i32);
                             }
-                            BlendMode::Alpha | BlendMode::PremultipliedDestOut | BlendMode::None => {
+                            BlendMode::PremultipliedDestOut | BlendMode::None => {
                                 unreachable!("bug: bad blend mode for text");
                             }
                         }
 
                         prev_blend_mode = BlendMode::None;
                         self.device.set_blend(false);
                     }
                     _ => {
                         if batch.key.blend_mode != prev_blend_mode {
                             match batch.key.blend_mode {
                                 BlendMode::None => {
                                     self.device.set_blend(false);
                                 }
-                                BlendMode::Alpha => {
-                                    self.device.set_blend(true);
-                                    self.device.set_blend_mode_alpha();
-                                }
                                 BlendMode::PremultipliedAlpha => {
                                     self.device.set_blend(true);
                                     self.device.set_blend_mode_premultiplied_alpha();
                                 }
                                 BlendMode::PremultipliedDestOut => {
                                     self.device.set_blend(true);
                                     self.device.set_blend_mode_premultiplied_dest_out();
                                 }
--- a/gfx/webrender/src/resource_cache.rs
+++ b/gfx/webrender/src/resource_cache.rs
@@ -1,27 +1,27 @@
 /* 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 api::{AddFont, BlobImageData, BlobImageResources, ResourceUpdate, ResourceUpdates};
 use api::{BlobImageDescriptor, BlobImageError, BlobImageRenderer, BlobImageRequest};
 use api::{ColorF, FontRenderMode};
 use api::{DevicePoint, DeviceUintRect, DeviceUintSize};
-use api::{Epoch, FontInstance, FontInstanceKey, FontKey, FontTemplate};
+use api::{Epoch, FontInstanceKey, FontKey, FontTemplate};
 use api::{ExternalImageData, ExternalImageType};
 use api::{FontInstanceOptions, FontInstancePlatformOptions, FontVariation};
 use api::{GlyphDimensions, GlyphKey, IdNamespace};
 use api::{ImageData, ImageDescriptor, ImageKey, ImageRendering};
 use api::{TileOffset, TileSize};
 use app_units::Au;
 use device::TextureFilter;
 use frame::FrameId;
 use glyph_cache::GlyphCache;
-use glyph_rasterizer::{GlyphFormat, GlyphRasterizer, GlyphRequest};
+use glyph_rasterizer::{FontInstance, GlyphFormat, GlyphRasterizer, GlyphRequest};
 use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle};
 use internal_types::{FastHashMap, FastHashSet, SourceTexture, TextureUpdateList};
 use profiler::{ResourceProfileCounters, TextureCacheProfileCounters};
 use rayon::ThreadPool;
 use std::collections::hash_map::Entry::{self, Occupied, Vacant};
 use std::cmp;
 use std::fmt::Debug;
 use std::hash::Hash;
@@ -384,16 +384,21 @@ impl ResourceCache {
             r.delete_font_instance(instance_key);
         }
     }
 
     pub fn get_font_instances(&self) -> FontInstanceMap {
         self.resources.font_instances.clone()
     }
 
+    pub fn get_font_instance(&self, instance_key: FontInstanceKey) -> Option<FontInstance> {
+        let instance_map = self.resources.font_instances.read().unwrap();
+        instance_map.get(&instance_key).cloned()
+    }
+
     pub fn add_image_template(
         &mut self,
         image_key: ImageKey,
         descriptor: ImageDescriptor,
         mut data: ImageData,
         mut tiling: Option<TileSize>,
     ) {
         if tiling.is_none() && Self::should_tile(self.max_texture_size(), &descriptor, &data) {
--- a/gfx/webrender/src/tiling.rs
+++ b/gfx/webrender/src/tiling.rs
@@ -57,17 +57,17 @@ impl AlphaBatchHelpers for PrimitiveStor
         match metadata.prim_kind {
             PrimitiveKind::TextRun => {
                 let font = &self.cpu_text_runs[metadata.cpu_prim_index.0].font;
                 match font.render_mode {
                     FontRenderMode::Subpixel => {
                         if font.bg_color.a != 0 {
                             BlendMode::SubpixelWithBgColor
                         } else {
-                            BlendMode::SubpixelConstantTextColor(font.color)
+                            BlendMode::SubpixelConstantTextColor(font.color.into())
                         }
                     }
                     FontRenderMode::Alpha |
                     FontRenderMode::Mono |
                     FontRenderMode::Bitmap => BlendMode::PremultipliedAlpha,
                 }
             },
             PrimitiveKind::Rectangle => {
@@ -84,56 +84,46 @@ impl AlphaBatchHelpers for PrimitiveStor
                         // with alpha == 0.0 instead of alpha == 1.0, and the RectanglePrimitive
                         // would need to know about that.
                         BlendMode::PremultipliedDestOut
                     },
                 }
             },
             PrimitiveKind::Border |
             PrimitiveKind::Image |
+            PrimitiveKind::YuvImage |
             PrimitiveKind::AlignedGradient |
             PrimitiveKind::AngleGradient |
             PrimitiveKind::RadialGradient |
+            PrimitiveKind::Line |
+            PrimitiveKind::Brush |
             PrimitiveKind::Picture => if needs_blending {
                 BlendMode::PremultipliedAlpha
             } else {
                 BlendMode::None
             },
-            PrimitiveKind::YuvImage |
-            PrimitiveKind::Line |
-            PrimitiveKind::Brush => if needs_blending {
-                BlendMode::Alpha
-            } else {
-                BlendMode::None
-            },
         }
     }
 }
 
 #[derive(Debug)]
 pub struct ScrollbarPrimitive {
     pub clip_id: ClipId,
     pub prim_index: PrimitiveIndex,
-    pub border_radius: f32,
+    pub frame_rect: LayerRect,
 }
 
 #[derive(Debug)]
 pub enum PrimitiveRunCmd {
     PushStackingContext(StackingContextIndex),
     PopStackingContext,
     PrimitiveRun(PrimitiveRun),
 }
 
 #[derive(Debug, Copy, Clone)]
-pub enum PrimitiveFlags {
-    None,
-    Scrollbar(ClipId, f32),
-}
-
-#[derive(Debug, Copy, Clone)]
 pub struct RenderTargetIndex(pub usize);
 
 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
 pub struct RenderPassIndex(isize);
 
 #[derive(Debug)]
 struct DynamicTaskInfo {
     task_id: RenderTaskId,
@@ -277,17 +267,17 @@ impl BatchList {
 
     fn get_suitable_batch(
         &mut self,
         key: BatchKey,
         item_bounding_rect: &DeviceIntRect,
     ) -> &mut Vec<PrimitiveInstance> {
         match key.blend_mode {
             BlendMode::None => self.opaque_batch_list.get_suitable_batch(key),
-            BlendMode::Alpha | BlendMode::PremultipliedAlpha |
+            BlendMode::PremultipliedAlpha |
             BlendMode::PremultipliedDestOut |
             BlendMode::SubpixelConstantTextColor(..) |
             BlendMode::SubpixelVariableTextColor |
             BlendMode::SubpixelWithBgColor => {
                 self.alpha_batch_list
                     .get_suitable_batch(key, item_bounding_rect)
             }
         }
@@ -394,17 +384,17 @@ impl AlphaRenderItem {
             AlphaRenderItem::Composite(stacking_context_index, source_id, backdrop_id, mode, z) => {
                 let stacking_context = &ctx.stacking_context_store[stacking_context_index.0];
                 let key = BatchKey::new(
                     BatchKind::Composite {
                         task_id,
                         source_id,
                         backdrop_id,
                     },
-                    BlendMode::Alpha,
+                    BlendMode::PremultipliedAlpha,
                     BatchTextures::no_texture(),
                 );
                 let batch = batch_list.get_suitable_batch(key, &stacking_context.screen_bounds);
                 let backdrop_task_address = render_tasks.get_task_address(backdrop_id);
                 let source_task_address = render_tasks.get_task_address(source_id);
 
                 let instance = CompositePrimitiveInstance::new(
                     task_address,
@@ -876,17 +866,17 @@ impl ClipBatcher {
         gpu_cache: &GpuCache,
         geometry_kind: MaskGeometryKind,
         clip_store: &ClipStore,
     ) {
         let mut coordinate_system_id = coordinate_system_id;
         for work_item in clips.iter() {
             let instance = ClipMaskInstance {
                 render_task_address: task_address,
-                scroll_node_id: work_item.scroll_node_id,
+                scroll_node_data_index: work_item.scroll_node_data_index,
                 segment: 0,
                 clip_data_address: GpuCacheAddress::invalid(),
                 resource_address: GpuCacheAddress::invalid(),
             };
             let info = clip_store
                 .get_opt(&work_item.clip_sources)
                 .expect("bug: clip handle should be valid");
 
--- a/gfx/webrender_api/Cargo.toml
+++ b/gfx/webrender_api/Cargo.toml
@@ -1,11 +1,11 @@
 [package]
 name = "webrender_api"
-version = "0.53.1"
+version = "0.53.2"
 authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
 license = "MPL-2.0"
 repository = "https://github.com/servo/webrender"
 
 [features]
 nightly = ["euclid/unstable", "serde/unstable"]
 ipc = ["ipc-channel"]
 
--- a/gfx/webrender_api/src/api.rs
+++ b/gfx/webrender_api/src/api.rs
@@ -1,14 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use {BuiltDisplayList, BuiltDisplayListDescriptor, ClipId, ColorF, DeviceIntPoint, DeviceUintRect};
-use {DeviceUintSize, FontInstance, FontInstanceKey, FontInstanceOptions};
+use {DeviceUintSize, FontInstanceKey, FontInstanceOptions};
 use {FontInstancePlatformOptions, FontKey, FontVariation, GlyphDimensions, GlyphKey, ImageData};
 use {ImageDescriptor, ImageKey, ItemTag, LayoutPoint, LayoutSize, LayoutTransform, LayoutVector2D};
 use {NativeFontHandle, WorldPoint};
 use app_units::Au;
 use channel::{self, MsgSender, Payload, PayloadSender, PayloadSenderHelperMethods};
 use std::cell::Cell;
 use std::fmt;
 use std::marker::PhantomData;
@@ -260,17 +260,17 @@ pub enum DebugCommand {
 }
 
 #[derive(Clone, Deserialize, Serialize)]
 pub enum ApiMsg {
     /// Add/remove/update images and fonts.
     UpdateResources(ResourceUpdates),
     /// Gets the glyph dimensions
     GetGlyphDimensions(
-        FontInstance,
+        FontInstanceKey,
         Vec<GlyphKey>,
         MsgSender<Vec<Option<GlyphDimensions>>>,
     ),
     /// Gets the glyph indices from a string
     GetGlyphIndices(FontKey, String, MsgSender<Vec<Option<u32>>>),
     /// Adds a new document namespace.
     CloneApi(MsgSender<IdNamespace>),
     /// Adds a new document with given initial size.
@@ -382,23 +382,24 @@ impl RenderApiSender {
         RenderApiSender {
             api_sender,
             payload_sender,
         }
     }
 
     /// Creates a new resource API object with a dedicated namespace.
     pub fn create_api(&self) -> RenderApi {
-        let (sync_tx, sync_rx) = channel::msg_channel().unwrap();
+        let (sync_tx, sync_rx) =
+            channel::msg_channel().expect("Failed to create channel");
         let msg = ApiMsg::CloneApi(sync_tx);
-        self.api_sender.send(msg).unwrap();
+        self.api_sender.send(msg).expect("Failed to send CloneApi message");
         RenderApi {
             api_sender: self.api_sender.clone(),
             payload_sender: self.payload_sender.clone(),
-            namespace_id: sync_rx.recv().unwrap(),
+            namespace_id: sync_rx.recv().expect("Failed to receive API response"),
             next_id: Cell::new(ResourceId(0)),
         }
     }
 }
 
 pub struct RenderApi {
     api_sender: MsgSender<ApiMsg>,
     payload_sender: PayloadSender,
@@ -442,17 +443,17 @@ impl RenderApi {
 
     /// 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,
-        font: FontInstance,
+        font: FontInstanceKey,
         glyph_keys: Vec<GlyphKey>,
     ) -> Vec<Option<GlyphDimensions>> {
         let (tx, rx) = channel::msg_channel().unwrap();
         let msg = ApiMsg::GetGlyphDimensions(font, glyph_keys, tx);
         self.api_sender.send(msg).unwrap();
         rx.recv().unwrap()
     }
 
--- a/gfx/webrender_api/src/color.rs
+++ b/gfx/webrender_api/src/color.rs
@@ -1,14 +1,38 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+use std::cmp;
 use std::hash::{Hash, Hasher};
 
+/// Represents pre-multiplied RGBA colors with floating point numbers.
+///
+/// All components must be between 0.0 and 1.0.
+/// An alpha value of 1.0 is opaque while 0.0 is fully transparent.
+///
+/// In premultiplied colors transitions to transparent always look "nice"
+/// therefore they are used in CSS gradients.
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
+pub struct PremultipliedColorF {
+    pub r: f32,
+    pub g: f32,
+    pub b: f32,
+    pub a: f32,
+}
+
+impl PremultipliedColorF {
+    ///
+    pub const BLACK: Self = PremultipliedColorF { r: 0.0, g: 0.0, b: 0.0, a: 1.0 };
+    ///
+    pub const TRANSPARENT: Self = PremultipliedColorF { r: 0.0, g: 0.0, b: 0.0, a: 0.0 };
+}
+
 /// Represents RGBA screen colors with floating point numbers.
 ///
 /// All components must be between 0.0 and 1.0.
 /// An alpha value of 1.0 is opaque while 0.0 is fully transparent.
 #[repr(C)]
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct ColorF {
     pub r: f32,
@@ -33,43 +57,36 @@ impl ColorF {
         }
     }
 
     pub fn to_array(&self) -> [f32; 4] {
         [self.r, self.g, self.b, self.a]
     }
 
     /// Multiply the RGB components with the alpha channel.
-    ///
-    /// In premultiplied colors transistions to transparent always look "nice"
-    /// therefore they are used in CSS gradients.
-    pub fn premultiplied(&self) -> ColorF {
-        self.scale_rgb(self.a)
+    pub fn premultiplied(&self) -> PremultipliedColorF {
+        let c = self.scale_rgb(self.a);
+        PremultipliedColorF { r: c.r, g: c.g, b: c.b, a: c.a }
     }
 }
 
-// Floats don't impl Hash/Eq...
-impl Eq for ColorF {}
-impl Hash for ColorF {
+// Floats don't impl Hash/Eq/Ord...
+impl Eq for PremultipliedColorF {}
+impl Ord for PremultipliedColorF {
+    fn cmp(&self, other: &Self) -> cmp::Ordering {
+        self.partial_cmp(other).unwrap_or(cmp::Ordering::Equal)
+    }
+}
+impl Hash for PremultipliedColorF {
     fn hash<H: Hasher>(&self, state: &mut H) {
         // Note: this is inconsistent with the Eq impl for -0.0 (don't care).
-        self.r._to_bits().hash(state);
-        self.g._to_bits().hash(state);
-        self.b._to_bits().hash(state);
-        self.a._to_bits().hash(state);
-    }
-}
-
-// FIXME: remove this when Rust 1.21 is stable (float_bits_conv)
-pub trait ToBits {
-    fn _to_bits(self) -> u32;
-}
-impl ToBits for f32 {
-    fn _to_bits(self) -> u32 {
-        unsafe { ::std::mem::transmute(self) }
+        self.r.to_bits().hash(state);
+        self.g.to_bits().hash(state);
+        self.b.to_bits().hash(state);
+        self.a.to_bits().hash(state);
     }
 }
 
 /// Represents RGBA screen colors with one byte per channel.
 ///
 /// If the alpha value `a` is 255 the color is opaque.
 #[repr(C)]
 #[derive(Clone, Copy, Hash, Eq, Debug, Deserialize, PartialEq, PartialOrd, Ord, Serialize)]
@@ -90,29 +107,32 @@ impl ColorU {
 fn round_to_int(x: f32) -> u8 {
     debug_assert!((0.0 <= x) && (x <= 1.0));
     let f = (255.0 * x) + 0.5;
     let val = f.floor();
     debug_assert!(val <= 255.0);
     val as u8
 }
 
+// TODO: We shouldn't really convert back to `ColorU` ever,
+// since it's lossy. One of the blockers is that all of our debug colors
+// are specified in `ColorF`. Changing it to `ColorU` would be nice.
 impl From<ColorF> for ColorU {
-    fn from(color: ColorF) -> ColorU {
+    fn from(color: ColorF) -> Self {
         ColorU {
             r: round_to_int(color.r),
             g: round_to_int(color.g),
             b: round_to_int(color.b),
             a: round_to_int(color.a),
         }
     }
 }
 
 impl From<ColorU> for ColorF {
-    fn from(color: ColorU) -> ColorF {
+    fn from(color: ColorU) -> Self {
         ColorF {
             r: color.r as f32 / 255.0,
             g: color.g as f32 / 255.0,
             b: color.b as f32 / 255.0,
             a: color.a as f32 / 255.0,
         }
     }
 }
--- a/gfx/webrender_api/src/display_list.rs
+++ b/gfx/webrender_api/src/display_list.rs
@@ -528,32 +528,86 @@ impl<'a> Write for SizeCounter {
         self.0 += buf.len();
         Ok(())
     }
 
     #[inline(always)]
     fn flush(&mut self) -> io::Result<()> { Ok(()) }
 }
 
+/// Serializes a value assuming the Serialize impl has a stable size across two 
+/// invocations.
+///
+/// If this assumption is incorrect, the result will be Undefined Behaviour. This
+/// assumption should hold for all derived Serialize impls, which is all we currently
+/// use.
 fn serialize_fast<T: Serialize>(vec: &mut Vec<u8>, e: &T) {
     // manually counting the size is faster than vec.reserve(bincode::serialized_size(&e) as usize) for some reason
     let mut size = SizeCounter(0);
     bincode::serialize_into(&mut size,e , bincode::Infinite).unwrap();
     vec.reserve(size.0);
 
     let old_len = vec.len();
     let ptr = unsafe { vec.as_mut_ptr().offset(old_len as isize) };
     let mut w = UnsafeVecWriter(ptr);
     bincode::serialize_into(&mut w, e, bincode::Infinite).unwrap();
 
     // fix up the length
     unsafe { vec.set_len(old_len + size.0); }
 
     // make sure we wrote the right amount
-    debug_assert!(((w.0 as usize) - (vec.as_ptr() as usize)) == vec.len());
+    debug_assert_eq!(((w.0 as usize) - (vec.as_ptr() as usize)), vec.len());
+}
+
+/// Serializes an iterator, assuming: 
+///
+/// * The Clone impl is trivial (e.g. we're just memcopying a slice iterator)
+/// * The ExactSizeIterator impl is stable and correct across a Clone
+/// * The Serialize impl has a stable size across two invocations
+///
+/// If the first is incorrect, webrender will be very slow. If the other two are
+/// incorrect, the result will be Undefined Behaviour! The ExactSizeIterator
+/// bound would ideally be replaced with a TrustedLen bound to protect us a bit
+/// better, but that trait isn't stable (and won't be for a good while, if ever).
+///
+/// Debug asserts are included that should catch all Undefined Behaviour, but
+/// we can't afford to include these in release builds.
+fn serialize_iter_fast<I>(vec: &mut Vec<u8>, iter: I) -> usize
+where I: ExactSizeIterator + Clone,
+      I::Item: Serialize,
+{
+    // manually counting the size is faster than vec.reserve(bincode::serialized_size(&e) as usize) for some reason
+    let mut size = SizeCounter(0);
+    let mut count1 = 0;
+
+    for e in iter.clone() {
+        bincode::serialize_into(&mut size, &e, bincode::Infinite).unwrap();
+        count1 += 1;
+    }
+
+    vec.reserve(size.0);
+
+    let old_len = vec.len();
+    let ptr = unsafe { vec.as_mut_ptr().offset(old_len as isize) };
+    let mut w = UnsafeVecWriter(ptr);
+    let mut count2 = 0;
+
+    for e in iter {
+        bincode::serialize_into(&mut w, &e, bincode::Infinite).unwrap();
+        count2 += 1;
+    }
+
+    // fix up the length
+    unsafe { vec.set_len(old_len + size.0); }
+
+    // make sure we wrote the right amount
+    debug_assert_eq!(((w.0 as usize) - (vec.as_ptr() as usize)), vec.len());
+    debug_assert_eq!(count1, count2);
+
+    count1
 }
 
 // This uses a (start, end) representation instead of (start, len) so that
 // only need to update a single field as we read through it. This
 // makes it easier for llvm to understand what's going on. (https://github.com/rust-lang/rust/issues/45068)
 // We update the slice only once we're done reading
 struct UnsafeReader<'a: 'b, 'b> {
     start: *const u8,
@@ -749,36 +803,32 @@ impl DisplayListBuilder {
                 info,
             }
         )
     }
 
     fn push_iter<I>(&mut self, iter: I)
     where
         I: IntoIterator,
-        I::IntoIter: ExactSizeIterator,
+        I::IntoIter: ExactSizeIterator + Clone,
         I::Item: Serialize,
     {
         let iter = iter.into_iter();
         let len = iter.len();
-        let mut count = 0;
 
         // Format:
         // payload_byte_size: usize, item_count: usize, [I; item_count]
 
         // We write a dummy value so there's room for later
         let byte_size_offset = self.data.len();
         serialize_fast(&mut self.data, &0usize);
         serialize_fast(&mut self.data, &len);
         let payload_offset = self.data.len();
 
-        for elem in iter {
-            count += 1;
-            serialize_fast(&mut self.data, &elem);
-        }
+        let count = serialize_iter_fast(&mut self.data, iter.into_iter());
 
         // Now write the actual byte_size
         let final_offset = self.data.len();
         let byte_size = final_offset - payload_offset;
 
         // Note we don't use serialize_fast because we don't want to change the Vec's len
         bincode::serialize_into(&mut &mut self.data[byte_size_offset..],
                                 &byte_size,
@@ -1160,17 +1210,17 @@ impl DisplayListBuilder {
         content_rect: LayoutRect,
         clip_rect: LayoutRect,
         complex_clips: I,
         image_mask: Option<ImageMask>,
         scroll_sensitivity: ScrollSensitivity,
     ) -> ClipId
     where
         I: IntoIterator<Item = ComplexClipRegion>,
-        I::IntoIter: ExactSizeIterator,
+        I::IntoIter: ExactSizeIterator + Clone,
     {
         let parent = self.clip_stack.last().unwrap().scroll_node_id;
         self.define_scroll_frame_with_parent(
             id,
             parent,
             content_rect,
             clip_rect,
             complex_clips,
@@ -1185,17 +1235,17 @@ impl DisplayListBuilder {
         content_rect: LayoutRect,
         clip_rect: LayoutRect,
         complex_clips: I,
         image_mask: Option<ImageMask>,
         scroll_sensitivity: ScrollSensitivity,
     ) -> ClipId
     where
         I: IntoIterator<Item = ComplexClipRegion>,
-        I::IntoIter: ExactSizeIterator,
+        I::IntoIter: ExactSizeIterator + Clone,
     {
         let id = self.generate_clip_id(id);
         let item = SpecificDisplayItem::ScrollFrame(ScrollFrameDisplayItem {
             id: id,
             image_mask: image_mask,
             scroll_sensitivity,
         });
         let info = LayoutPrimitiveInfo::with_clip_rect(content_rect, clip_rect);
@@ -1210,17 +1260,17 @@ impl DisplayListBuilder {
         &mut self,
         id: Option<ClipId>,
         clip_rect: LayoutRect,
         complex_clips: I,
         image_mask: Option<ImageMask>,
     ) -> ClipId
     where
         I: IntoIterator<Item = ComplexClipRegion>,
-        I::IntoIter: ExactSizeIterator,
+        I::IntoIter: ExactSizeIterator + Clone,
     {
         let parent = self.clip_stack.last().unwrap().scroll_node_id;
         self.define_clip_with_parent(
             id,
             parent,
             clip_rect,
             complex_clips,
             image_mask)
@@ -1231,17 +1281,17 @@ impl DisplayListBuilder {
         id: Option<ClipId>,
         parent: ClipId,
         clip_rect: LayoutRect,
         complex_clips: I,
         image_mask: Option<ImageMask>,
     ) -> ClipId
     where
         I: IntoIterator<Item = ComplexClipRegion>,
-        I::IntoIter: ExactSizeIterator,
+        I::IntoIter: ExactSizeIterator + Clone,
     {
         let id = self.generate_clip_id(id);
         let item = SpecificDisplayItem::Clip(ClipDisplayItem {
             id,
             image_mask: image_mask,
         });
 
         let info = LayoutPrimitiveInfo::new(clip_rect);
--- a/gfx/webrender_api/src/font.rs
+++ b/gfx/webrender_api/src/font.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 {ColorF, ColorU, IdNamespace, LayoutPoint, ToBits};
-use app_units::Au;
+use {ColorU, IdNamespace, LayoutPoint};
 #[cfg(target_os = "macos")]
 use core_foundation::string::CFString;
 #[cfg(target_os = "macos")]
 use core_graphics::font::CGFont;
 #[cfg(target_os = "windows")]
 use dwrote::FontDescriptor;
 #[cfg(target_os = "macos")]
 use serde::de::{self, Deserialize, Deserializer};
@@ -173,33 +172,33 @@ impl Into<f64> for SubpixelOffset {
 pub struct FontVariation {
     pub tag: u32,
     pub value: f32,
 }
 
 impl Ord for FontVariation {
     fn cmp(&self, other: &FontVariation) -> Ordering {
         self.tag.cmp(&other.tag)
-            .then(self.value._to_bits().cmp(&other.value._to_bits()))
+            .then(self.value.to_bits().cmp(&other.value.to_bits()))
     }
 }
 
 impl PartialEq for FontVariation {
     fn eq(&self, other: &FontVariation) -> bool {
         self.tag == other.tag &&
-        self.value._to_bits() == other.value._to_bits()
+        self.value.to_bits() == other.value.to_bits()
     }
 }
 
 impl Eq for FontVariation {}
 
 impl Hash for FontVariation {
     fn hash<H: Hasher>(&self, state: &mut H) {
         self.tag.hash(state);
-        self.value._to_bits().hash(state);
+        self.value.to_bits().hash(state);
     }
 }
 
 #[repr(C)]
 #[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)]
 pub struct GlyphOptions {
     pub render_mode: FontRenderMode,
 }
@@ -304,68 +303,16 @@ impl Default for FontInstancePlatformOpt
         FontInstancePlatformOptions {
             flags: 0,
             lcd_filter: FontLCDFilter::Default,
             hinting: FontHinting::LCD,
         }
     }
 }
 
-#[derive(Clone, Hash, PartialEq, Eq, Debug, Deserialize, Serialize, Ord, PartialOrd)]
-pub struct FontInstance {
-    pub font_key: FontKey,
-    // The font size is in *device* pixels, not logical pixels.
-    // It is stored as an Au since we need sub-pixel sizes, but
-    // can't store as a f32 due to use of this type as a hash key.
-    // TODO(gw): Perhaps consider having LogicalAu and DeviceAu
-    //           or something similar to that.
-    pub size: Au,
-    pub color: ColorU,
-    pub bg_color: ColorU,
-    pub render_mode: FontRenderMode,
-    pub subpx_dir: SubpixelDirection,
-    pub platform_options: Option<FontInstancePlatformOptions>,
-    pub variations: Vec<FontVariation>,
-    pub synthetic_italics: bool,
-}
-
-impl FontInstance {
-    pub fn new(
-        font_key: FontKey,
-        size: Au,
-        color: ColorF,
-        bg_color: ColorU,
-        render_mode: FontRenderMode,
-        subpx_dir: SubpixelDirection,
-        platform_options: Option<FontInstancePlatformOptions>,
-        variations: Vec<FontVariation>,
-        synthetic_italics: bool,
-    ) -> FontInstance {
-        FontInstance {
-            font_key,
-            size,
-            color: color.into(),
-            bg_color,
-            render_mode,
-            subpx_dir,
-            platform_options,
-            variations,
-            synthetic_italics,
-        }
-    }
-
-    pub fn get_subpx_offset(&self, glyph: &GlyphKey) -> (f64, f64) {
-        match self.subpx_dir {
-            SubpixelDirection::None => (0.0, 0.0),
-            SubpixelDirection::Horizontal => (glyph.subpixel_offset.into(), 0.0),
-            SubpixelDirection::Vertical => (0.0, glyph.subpixel_offset.into()),
-        }
-    }
-}
-
 #[repr(C)]
 #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Ord, PartialOrd)]
 pub struct FontInstanceKey(pub IdNamespace, pub u32);
 
 impl FontInstanceKey {
     pub fn new(namespace: IdNamespace, key: u32) -> FontInstanceKey {
         FontInstanceKey(namespace, key)
     }
--- a/gfx/webrender_bindings/Cargo.toml
+++ b/gfx/webrender_bindings/Cargo.toml
@@ -1,26 +1,26 @@
 [package]
 name = "webrender_bindings"
 version = "0.1.0"
 authors = ["The Mozilla Project Developers"]
 license = "MPL-2.0"
 
 [dependencies]
-webrender_api = {path = "../webrender_api", version = "0.53.1"}
+webrender_api = {path = "../webrender_api", version = "0.53.2"}
 rayon = "0.8"
 thread_profiler = "0.1.1"
 euclid = "0.15"
 app_units = "0.5.6"
 gleam = "0.4"
 log = "0.3"
 
 [dependencies.webrender]
 path = "../webrender"
-version = "0.53.1"
+version = "0.53.2"
 default-features = false
 
 [target.'cfg(target_os = "windows")'.dependencies]
 dwrote = "0.4"
 
 [target.'cfg(target_os = "macos")'.dependencies]
 core-foundation = "0.4"
 core-graphics = "0.9"
--- a/toolkit/library/gtest/rust/Cargo.lock
+++ b/toolkit/library/gtest/rust/Cargo.lock
@@ -1467,17 +1467,17 @@ source = "registry+https://github.com/ru
 dependencies = [
  "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "webrender"
-version = "0.53.1"
+version = "0.53.2"
 dependencies = [
  "app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "bincode 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-foundation 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-text 7.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1488,22 +1488,22 @@ dependencies = [
  "gleam 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "num-traits 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
  "plane-split 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)",
- "webrender_api 0.53.1",
+ "webrender_api 0.53.2",
 ]
 
 [[package]]
 name = "webrender_api"
-version = "0.53.1"
+version = "0.53.2"
 dependencies = [
  "app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "bincode 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-foundation 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "dwrote 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1520,18 +1520,18 @@ dependencies = [
  "core-foundation 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "dwrote 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.15.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "gleam 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "webrender 0.53.1",
- "webrender_api 0.53.1",
+ "webrender 0.53.2",
+ "webrender_api 0.53.2",
 ]
 
 [[package]]
 name = "which"
 version = "1.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
--- a/toolkit/library/rust/Cargo.lock
+++ b/toolkit/library/rust/Cargo.lock
@@ -1479,17 +1479,17 @@ source = "registry+https://github.com/ru
 dependencies = [
  "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "webrender"
-version = "0.53.1"
+version = "0.53.2"
 dependencies = [
  "app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "bincode 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-foundation 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-text 7.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1500,22 +1500,22 @@ dependencies = [
  "gleam 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "num-traits 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
  "plane-split 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)",
- "webrender_api 0.53.1",
+ "webrender_api 0.53.2",
 ]
 
 [[package]]
 name = "webrender_api"
-version = "0.53.1"
+version = "0.53.2"
 dependencies = [
  "app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "bincode 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-foundation 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "dwrote 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1532,18 +1532,18 @@ dependencies = [
  "core-foundation 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "dwrote 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.15.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "gleam 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "webrender 0.53.1",
- "webrender_api 0.53.1",
+ "webrender 0.53.2",
+ "webrender_api 0.53.2",
 ]
 
 [[package]]
 name = "which"
 version = "1.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",