Bug 1395237 - Update webrender to commit 81cba6b139c4c1061cab6a1c38acf2ae7f50445d. r=jrmuizel
authorKartikaya Gupta <kgupta@mozilla.com>
Thu, 31 Aug 2017 14:09:26 -0400
changeset 378000 ad188215f64d913ad5f7f80489b2e3192493b815
parent 377999 7066bd72a0a79229b8326d777e78cfb90ce47c8a
child 378001 0363b34d637c714b452baeea0d3fbb860e9951eb
push id94385
push userkgupta@mozilla.com
push dateThu, 31 Aug 2017 18:11:11 +0000
treeherdermozilla-inbound@a47911a010ff [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs1395237
milestone57.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1395237 - Update webrender to commit 81cba6b139c4c1061cab6a1c38acf2ae7f50445d. r=jrmuizel Includes Cargo.lock updates. MozReview-Commit-ID: GsEbblWnzT3
gfx/doc/README.webrender
gfx/webrender/Cargo.toml
gfx/webrender/examples/basic.rs
gfx/webrender/examples/blob.rs
gfx/webrender/res/cs_box_shadow.glsl
gfx/webrender/res/cs_clip_rectangle.fs.glsl
gfx/webrender/res/cs_clip_rectangle.glsl
gfx/webrender/res/cs_clip_rectangle.vs.glsl
gfx/webrender/res/ellipse.glsl
gfx/webrender/res/prim_shared.glsl
gfx/webrender/res/ps_border_corner.fs.glsl
gfx/webrender/res/ps_border_corner.glsl
gfx/webrender/res/ps_border_corner.vs.glsl
gfx/webrender/res/ps_border_edge.fs.glsl
gfx/webrender/res/ps_border_edge.glsl
gfx/webrender/res/ps_border_edge.vs.glsl
gfx/webrender/res/ps_box_shadow.fs.glsl
gfx/webrender/res/ps_box_shadow.vs.glsl
gfx/webrender/src/debug_server.rs
gfx/webrender/src/device.rs
gfx/webrender/src/frame.rs
gfx/webrender/src/frame_builder.rs
gfx/webrender/src/glyph_rasterizer.rs
gfx/webrender/src/internal_types.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/scene.rs
gfx/webrender/src/texture_cache.rs
gfx/webrender/src/tiling.rs
gfx/webrender_api/Cargo.toml
gfx/webrender_api/src/api.rs
gfx/webrender_api/src/display_item.rs
gfx/webrender_api/src/display_list.rs
gfx/webrender_api/src/font.rs
gfx/webrender_api/src/image.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
@@ -74,9 +74,9 @@ there is another crate in m-c called moz
 the same folder to store its rust dependencies. If one of the libraries that is
 required by both mozjs_sys and webrender is updated without updating the other
 project's Cargo.lock file, that results in build bustage.
 This means that any time you do this sort of manual update of packages, you need
 to make sure that mozjs_sys also has its Cargo.lock file updated if needed, hence
 the need to run the cargo update command in js/src as well. Hopefully this will
 be resolved soon.
 
-Latest Commit: 5edd3da7ee11e1d0caaf0b53cb7f04cfab20e585
+Latest Commit: 81cba6b139c4c1061cab6a1c38acf2ae7f50445d
--- a/gfx/webrender/Cargo.toml
+++ b/gfx/webrender/Cargo.toml
@@ -1,11 +1,11 @@
 [package]
 name = "webrender"
-version = "0.49.0"
+version = "0.50.0"
 authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
 license = "MPL-2.0"
 repository = "https://github.com/servo/webrender"
 build = "build.rs"
 
 [features]
 default = ["freetype-lib"]
 freetype-lib = ["freetype/servo-freetype-sys"]
--- a/gfx/webrender/examples/basic.rs
+++ b/gfx/webrender/examples/basic.rs
@@ -236,16 +236,19 @@ impl Example for App {
         builder.push_border(bounds, None, border_widths, border_details);
 
 
         if false { // draw text?
             let font_key = api.generate_font_key();
             let font_bytes = load_file("res/FreeSans.ttf");
             resources.add_raw_font(font_key, font_bytes, 0);
 
+            let font_instance_key = api.generate_font_instance_key();
+            resources.add_font_instance(font_instance_key, font_key, Au::from_px(32), None, None);
+
             let text_bounds = (100, 200).by(700, 300);
             let glyphs = vec![
                 GlyphInstance {
                     index: 48,
                     point: LayoutPoint::new(100.0, 100.0),
                 },
                 GlyphInstance {
                     index: 68,
@@ -291,19 +294,18 @@ impl Example for App {
                     index: 17,
                     point: LayoutPoint::new(650.0, 100.0),
                 },
             ];
 
             builder.push_text(text_bounds,
                               None,
                               &glyphs,
-                              font_key,
+                              font_instance_key,
                               ColorF::new(1.0, 1.0, 0.0, 1.0),
-                              Au::from_px(32),
                               None);
         }
 
         if false { // draw box shadow?
             let rect = LayoutRect::zero();
             let simple_box_bounds = (20, 200).by(50, 50);
             let offset = vec2(10.0, 10.0);
             let color = ColorF::new(1.0, 1.0, 1.0, 1.0);
--- a/gfx/webrender/examples/blob.rs
+++ b/gfx/webrender/examples/blob.rs
@@ -205,16 +205,17 @@ impl api::BlobImageRenderer for Checkerb
             }
             self.rendered_images.insert(req, Some(result));
         }
 
         // If we break out of the loop above it means the channel closed unexpectedly.
         Err(api::BlobImageError::Other("Channel closed".into()))
     }
     fn delete_font(&mut self, _font: api::FontKey) { }
+    fn delete_font_instance(&mut self, _instance: api::FontInstanceKey) { }
 }
 
 struct App {
 
 }
 
 impl Example for App {
     fn render(&mut self,
--- a/gfx/webrender/res/cs_box_shadow.glsl
+++ b/gfx/webrender/res/cs_box_shadow.glsl
@@ -178,11 +178,11 @@ float color(vec2 pos, vec2 p0Rect, vec2 
 void main(void) {
     vec2 pos = vPos.xy;
     vec2 p0Rect = vBoxShadowRect.xy, p1Rect = vBoxShadowRect.zw;
     vec2 radii = vBorderRadii.xy;
     float sigma = vBlurRadius / 2.0;
     float value = color(pos, p0Rect, p1Rect, radii, sigma);
 
     value = max(value, 0.0);
-    oFragColor = dither(vec4(1.0, 1.0, 1.0, vInverted == 1.0 ? 1.0 - value : value));
+    oFragColor = dither(vec4(vInverted == 1.0 ? 1.0 - value : value));
 }
 #endif
deleted file mode 100644
--- a/gfx/webrender/res/cs_clip_rectangle.fs.glsl
+++ /dev/null
@@ -1,65 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-float clip_against_ellipse_if_needed(vec2 pos,
-                                     float current_distance,
-                                     vec4 ellipse_center_radius,
-                                     vec2 sign_modifier,
-                                     float afwidth) {
-    float ellipse_distance = distance_to_ellipse(pos - ellipse_center_radius.xy,
-                                                 ellipse_center_radius.zw);
-
-    return mix(current_distance,
-               ellipse_distance + afwidth,
-               all(lessThan(sign_modifier * pos, sign_modifier * ellipse_center_radius.xy)));
-}
-
-float rounded_rect(vec2 pos) {
-    float current_distance = 0.0;
-
-    // Apply AA
-    float afwidth = 0.5 * length(fwidth(pos));
-
-    // Clip against each ellipse.
-    current_distance = clip_against_ellipse_if_needed(pos,
-                                                      current_distance,
-                                                      vClipCenter_Radius_TL,
-                                                      vec2(1.0),
-                                                      afwidth);
-
-    current_distance = clip_against_ellipse_if_needed(pos,
-                                                      current_distance,
-                                                      vClipCenter_Radius_TR,
-                                                      vec2(-1.0, 1.0),
-                                                      afwidth);
-
-    current_distance = clip_against_ellipse_if_needed(pos,
-                                                      current_distance,
-                                                      vClipCenter_Radius_BR,
-                                                      vec2(-1.0),
-                                                      afwidth);
-
-    current_distance = clip_against_ellipse_if_needed(pos,
-                                                      current_distance,
-                                                      vClipCenter_Radius_BL,
-                                                      vec2(1.0, -1.0),
-                                                      afwidth);
-
-    return smoothstep(0.0, afwidth, 1.0 - current_distance);
-}
-
-
-void main(void) {
-    float alpha = 1.f;
-    vec2 local_pos = init_transform_fs(vPos, alpha);
-
-    float clip_alpha = rounded_rect(local_pos);
-
-    float combined_alpha = min(alpha, clip_alpha);
-
-    // Select alpha or inverse alpha depending on clip in/out.
-    float final_alpha = mix(combined_alpha, 1.0 - combined_alpha, vClipMode);
-
-    oFragColor = vec4(final_alpha, 0.0, 0.0, 1.0);
-}
--- a/gfx/webrender/res/cs_clip_rectangle.glsl
+++ b/gfx/webrender/res/cs_clip_rectangle.glsl
@@ -1,12 +1,151 @@
 /* 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/. */
 
-#include shared,prim_shared,clip_shared
+#include shared,prim_shared,clip_shared,ellipse
 
 varying vec3 vPos;
 flat varying float vClipMode;
 flat varying vec4 vClipCenter_Radius_TL;
 flat varying vec4 vClipCenter_Radius_TR;
 flat varying vec4 vClipCenter_Radius_BL;
 flat varying vec4 vClipCenter_Radius_BR;
+
+#ifdef WR_VERTEX_SHADER
+struct ClipRect {
+    RectWithSize rect;
+    vec4 mode;
+};
+
+ClipRect fetch_clip_rect(ivec2 address) {
+    vec4 data[2] = fetch_from_resource_cache_2_direct(address);
+    return ClipRect(RectWithSize(data[0].xy, data[0].zw), data[1]);
+}
+
+struct ClipCorner {
+    RectWithSize rect;
+    vec4 outer_inner_radius;
+};
+
+ClipCorner fetch_clip_corner(ivec2 address, int index) {
+    address += ivec2(2 + 2 * index, 0);
+    vec4 data[2] = fetch_from_resource_cache_2_direct(address);
+    return ClipCorner(RectWithSize(data[0].xy, data[0].zw), data[1]);
+}
+
+struct ClipData {
+    ClipRect rect;
+    ClipCorner top_left;
+    ClipCorner top_right;
+    ClipCorner bottom_left;
+    ClipCorner bottom_right;
+};
+
+ClipData fetch_clip(ivec2 address) {
+    ClipData clip;
+
+    clip.rect = fetch_clip_rect(address);
+    clip.top_left = fetch_clip_corner(address, 0);
+    clip.top_right = fetch_clip_corner(address, 1);
+    clip.bottom_left = fetch_clip_corner(address, 2);
+    clip.bottom_right = fetch_clip_corner(address, 3);
+
+    return clip;
+}
+
+void main(void) {
+    CacheClipInstance cci = fetch_clip_item(gl_InstanceID);
+    ClipArea area = fetch_clip_area(cci.render_task_index);
+    Layer layer = fetch_layer(cci.layer_index);
+    ClipData clip = fetch_clip(cci.clip_data_address);
+    RectWithSize local_rect = clip.rect.rect;
+
+    ClipVertexInfo vi = write_clip_tile_vertex(local_rect,
+                                               layer,
+                                               area,
+                                               cci.segment);
+    vPos = vi.local_pos;
+
+    vClipMode = clip.rect.mode.x;
+
+    RectWithEndpoint clip_rect = to_rect_with_endpoint(local_rect);
+
+    vClipCenter_Radius_TL = vec4(clip_rect.p0 + clip.top_left.outer_inner_radius.xy,
+                                 clip.top_left.outer_inner_radius.xy);
+
+    vClipCenter_Radius_TR = vec4(clip_rect.p1.x - clip.top_right.outer_inner_radius.x,
+                                 clip_rect.p0.y + clip.top_right.outer_inner_radius.y,
+                                 clip.top_right.outer_inner_radius.xy);
+
+    vClipCenter_Radius_BR = vec4(clip_rect.p1 - clip.bottom_right.outer_inner_radius.xy,
+                                 clip.bottom_right.outer_inner_radius.xy);
+
+    vClipCenter_Radius_BL = vec4(clip_rect.p0.x + clip.bottom_left.outer_inner_radius.x,
+                                 clip_rect.p1.y - clip.bottom_left.outer_inner_radius.y,
+                                 clip.bottom_left.outer_inner_radius.xy);
+}
+#endif
+
+#ifdef WR_FRAGMENT_SHADER
+float clip_against_ellipse_if_needed(vec2 pos,
+                                     float current_distance,
+                                     vec4 ellipse_center_radius,
+                                     vec2 sign_modifier,
+                                     float afwidth) {
+    float ellipse_distance = distance_to_ellipse(pos - ellipse_center_radius.xy,
+                                                 ellipse_center_radius.zw);
+
+    return mix(current_distance,
+               ellipse_distance + afwidth,
+               all(lessThan(sign_modifier * pos, sign_modifier * ellipse_center_radius.xy)));
+}
+
+float rounded_rect(vec2 pos) {
+    float current_distance = 0.0;
+
+    // Apply AA
+    float afwidth = 0.5 * length(fwidth(pos));
+
+    // Clip against each ellipse.
+    current_distance = clip_against_ellipse_if_needed(pos,
+                                                      current_distance,
+                                                      vClipCenter_Radius_TL,
+                                                      vec2(1.0),
+                                                      afwidth);
+
+    current_distance = clip_against_ellipse_if_needed(pos,
+                                                      current_distance,
+                                                      vClipCenter_Radius_TR,
+                                                      vec2(-1.0, 1.0),
+                                                      afwidth);
+
+    current_distance = clip_against_ellipse_if_needed(pos,
+                                                      current_distance,
+                                                      vClipCenter_Radius_BR,
+                                                      vec2(-1.0),
+                                                      afwidth);
+
+    current_distance = clip_against_ellipse_if_needed(pos,
+                                                      current_distance,
+                                                      vClipCenter_Radius_BL,
+                                                      vec2(1.0, -1.0),
+                                                      afwidth);
+
+    return smoothstep(0.0, afwidth, 1.0 - current_distance);
+}
+
+
+void main(void) {
+    float alpha = 1.f;
+    vec2 local_pos = init_transform_fs(vPos, alpha);
+
+    float clip_alpha = rounded_rect(local_pos);
+
+    float combined_alpha = min(alpha, clip_alpha);
+
+    // Select alpha or inverse alpha depending on clip in/out.
+    float final_alpha = mix(combined_alpha, 1.0 - combined_alpha, vClipMode);
+
+    oFragColor = vec4(final_alpha, 0.0, 0.0, 1.0);
+}
+#endif
deleted file mode 100644
--- a/gfx/webrender/res/cs_clip_rectangle.vs.glsl
+++ /dev/null
@@ -1,76 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-struct ClipRect {
-    RectWithSize rect;
-    vec4 mode;
-};
-
-ClipRect fetch_clip_rect(ivec2 address) {
-    vec4 data[2] = fetch_from_resource_cache_2_direct(address);
-    return ClipRect(RectWithSize(data[0].xy, data[0].zw), data[1]);
-}
-
-struct ClipCorner {
-    RectWithSize rect;
-    vec4 outer_inner_radius;
-};
-
-ClipCorner fetch_clip_corner(ivec2 address, int index) {
-    address += ivec2(2 + 2 * index, 0);
-    vec4 data[2] = fetch_from_resource_cache_2_direct(address);
-    return ClipCorner(RectWithSize(data[0].xy, data[0].zw), data[1]);
-}
-
-struct ClipData {
-    ClipRect rect;
-    ClipCorner top_left;
-    ClipCorner top_right;
-    ClipCorner bottom_left;
-    ClipCorner bottom_right;
-};
-
-ClipData fetch_clip(ivec2 address) {
-    ClipData clip;
-
-    clip.rect = fetch_clip_rect(address);
-    clip.top_left = fetch_clip_corner(address, 0);
-    clip.top_right = fetch_clip_corner(address, 1);
-    clip.bottom_left = fetch_clip_corner(address, 2);
-    clip.bottom_right = fetch_clip_corner(address, 3);
-
-    return clip;
-}
-
-void main(void) {
-    CacheClipInstance cci = fetch_clip_item(gl_InstanceID);
-    ClipArea area = fetch_clip_area(cci.render_task_index);
-    Layer layer = fetch_layer(cci.layer_index);
-    ClipData clip = fetch_clip(cci.clip_data_address);
-    RectWithSize local_rect = clip.rect.rect;
-
-    ClipVertexInfo vi = write_clip_tile_vertex(local_rect,
-                                               layer,
-                                               area,
-                                               cci.segment);
-    vPos = vi.local_pos;
-
-    vClipMode = clip.rect.mode.x;
-
-    RectWithEndpoint clip_rect = to_rect_with_endpoint(local_rect);
-
-    vClipCenter_Radius_TL = vec4(clip_rect.p0 + clip.top_left.outer_inner_radius.xy,
-                                 clip.top_left.outer_inner_radius.xy);
-
-    vClipCenter_Radius_TR = vec4(clip_rect.p1.x - clip.top_right.outer_inner_radius.x,
-                                 clip_rect.p0.y + clip.top_right.outer_inner_radius.y,
-                                 clip.top_right.outer_inner_radius.xy);
-
-    vClipCenter_Radius_BR = vec4(clip_rect.p1 - clip.bottom_right.outer_inner_radius.xy,
-                                 clip.bottom_right.outer_inner_radius.xy);
-
-    vClipCenter_Radius_BL = vec4(clip_rect.p0.x + clip.bottom_left.outer_inner_radius.x,
-                                 clip_rect.p1.y - clip.bottom_left.outer_inner_radius.y,
-                                 clip.bottom_left.outer_inner_radius.xy);
-}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ellipse.glsl
@@ -0,0 +1,69 @@
+/* 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/. */
+
+#ifdef WR_FRAGMENT_SHADER
+
+//
+// Signed distance to an ellipse.
+// Taken from http://www.iquilezles.org/www/articles/ellipsedist/ellipsedist.htm
+// Note that this fails for exact circles.
+//
+float sdEllipse( vec2 p, in vec2 ab ) {
+    p = abs( p ); if( p.x > p.y ){ p=p.yx; ab=ab.yx; }
+    float l = ab.y*ab.y - ab.x*ab.x;
+
+    float m = ab.x*p.x/l;
+    float n = ab.y*p.y/l;
+    float m2 = m*m;
+    float n2 = n*n;
+
+    float c = (m2 + n2 - 1.0)/3.0;
+    float c3 = c*c*c;
+
+    float q = c3 + m2*n2*2.0;
+    float d = c3 + m2*n2;
+    float g = m + m*n2;
+
+    float co;
+
+    if( d<0.0 )
+    {
+        float p = acos(q/c3)/3.0;
+        float s = cos(p);
+        float t = sin(p)*sqrt(3.0);
+        float rx = sqrt( -c*(s + t + 2.0) + m2 );
+        float ry = sqrt( -c*(s - t + 2.0) + m2 );
+        co = ( ry + sign(l)*rx + abs(g)/(rx*ry) - m)/2.0;
+    }
+    else
+    {
+        float h = 2.0*m*n*sqrt( d );
+        float s = sign(q+h)*pow( abs(q+h), 1.0/3.0 );
+        float u = sign(q-h)*pow( abs(q-h), 1.0/3.0 );
+        float rx = -s - u - c*4.0 + 2.0*m2;
+        float ry = (s - u)*sqrt(3.0);
+        float rm = sqrt( rx*rx + ry*ry );
+        float p = ry/sqrt(rm-rx);
+        co = (p + 2.0*g/rm - m)/2.0;
+    }
+
+    float si = sqrt( 1.0 - co*co );
+
+    vec2 r = vec2( ab.x*co, ab.y*si );
+
+    return length(r - p ) * sign(p.y-r.y);
+}
+
+float distance_to_ellipse(vec2 p, vec2 radii) {
+    // sdEllipse fails on exact circles, so handle equal
+    // radii here. The branch coherency should make this
+    // a performance win for the circle case too.
+    if (radii.x == radii.y) {
+        return length(p) - radii.x;
+    } else {
+        return sdEllipse(p, radii);
+    }
+}
+
+#endif
--- a/gfx/webrender/res/prim_shared.glsl
+++ b/gfx/webrender/res/prim_shared.glsl
@@ -850,71 +850,9 @@ vec4 sample_gradient(int address, float 
 
     // Fetch the start and end color.
     vec4 texels[2] = fetch_from_resource_cache_2(address + lut_offset);
 
     // Finally interpolate and apply dithering
     return dither(mix(texels[0], texels[1], fract(x)));
 }
 
-//
-// Signed distance to an ellipse.
-// Taken from http://www.iquilezles.org/www/articles/ellipsedist/ellipsedist.htm
-// Note that this fails for exact circles.
-//
-float sdEllipse( vec2 p, in vec2 ab ) {
-    p = abs( p ); if( p.x > p.y ){ p=p.yx; ab=ab.yx; }
-    float l = ab.y*ab.y - ab.x*ab.x;
-
-    float m = ab.x*p.x/l;
-    float n = ab.y*p.y/l;
-    float m2 = m*m;
-    float n2 = n*n;
-
-    float c = (m2 + n2 - 1.0)/3.0;
-    float c3 = c*c*c;
-
-    float q = c3 + m2*n2*2.0;
-    float d = c3 + m2*n2;
-    float g = m + m*n2;
-
-    float co;
-
-    if( d<0.0 )
-    {
-        float p = acos(q/c3)/3.0;
-        float s = cos(p);
-        float t = sin(p)*sqrt(3.0);
-        float rx = sqrt( -c*(s + t + 2.0) + m2 );
-        float ry = sqrt( -c*(s - t + 2.0) + m2 );
-        co = ( ry + sign(l)*rx + abs(g)/(rx*ry) - m)/2.0;
-    }
-    else
-    {
-        float h = 2.0*m*n*sqrt( d );
-        float s = sign(q+h)*pow( abs(q+h), 1.0/3.0 );
-        float u = sign(q-h)*pow( abs(q-h), 1.0/3.0 );
-        float rx = -s - u - c*4.0 + 2.0*m2;
-        float ry = (s - u)*sqrt(3.0);
-        float rm = sqrt( rx*rx + ry*ry );
-        float p = ry/sqrt(rm-rx);
-        co = (p + 2.0*g/rm - m)/2.0;
-    }
-
-    float si = sqrt( 1.0 - co*co );
-
-    vec2 r = vec2( ab.x*co, ab.y*si );
-
-    return length(r - p ) * sign(p.y-r.y);
-}
-
-float distance_to_ellipse(vec2 p, vec2 radii) {
-    // sdEllipse fails on exact circles, so handle equal
-    // radii here. The branch coherency should make this
-    // a performance win for the circle case too.
-    if (radii.x == radii.y) {
-        return length(p) - radii.x;
-    } else {
-        return sdEllipse(p, radii);
-    }
-}
-
 #endif //WR_FRAGMENT_SHADER
deleted file mode 100644
--- a/gfx/webrender/res/ps_border_corner.fs.glsl
+++ /dev/null
@@ -1,85 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-void main(void) {
-    float alpha = 1.0;
-#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());
-
-    // Find the appropriate distance to apply the AA smoothstep over.
-    vec2 fw = fwidth(local_pos);
-    float afwidth = length(fw);
-    float distance_for_color;
-    float color_mix_factor;
-
-    // Only apply the clip AA if inside the clip region. This is
-    // necessary for correctness when the border width is greater
-    // than the border radius.
-    if (all(lessThan(local_pos * vClipSign, vClipCenter * vClipSign))) {
-        vec2 p = local_pos - vClipCenter;
-
-        // Get signed distance from the inner/outer clips.
-        float d0 = distance_to_ellipse(p, vRadii0.xy);
-        float d1 = distance_to_ellipse(p, vRadii0.zw);
-        float d2 = distance_to_ellipse(p, vRadii1.xy);
-        float d3 = distance_to_ellipse(p, vRadii1.zw);
-
-        // SDF subtract main radii
-        float d_main = max(d0, 0.5 * afwidth - d1);
-
-        // SDF subtract inner radii (double style borders)
-        float d_inner = max(d2 - 0.5 * afwidth, -d3);
-
-        // Select how to combine the SDF based on border style.
-        float d = mix(max(d_main, -d_inner), d_main, vSDFSelect);
-
-        // Only apply AA to fragments outside the signed distance field.
-        alpha = min(alpha, 1.0 - smoothstep(0.0, 0.5 * afwidth, d));
-
-        // Get the groove/ridge mix factor.
-        color_mix_factor = smoothstep(-0.5 * afwidth,
-                                      0.5 * afwidth,
-                                      -d2);
-    } else {
-        // Handle the case where the fragment is outside the clip
-        // region in a corner. This occurs when border width is
-        // greater than border radius.
-
-        // Get linear distances along horizontal and vertical edges.
-        vec2 d0 = vClipSign.xx * (local_pos.xx - vEdgeDistance.xz);
-        vec2 d1 = vClipSign.yy * (local_pos.yy - vEdgeDistance.yw);
-        // Apply union to get the outer edge signed distance.
-        float da = min(d0.x, d1.x);
-        // Apply intersection to get the inner edge signed distance.
-        float db = max(-d0.y, -d1.y);
-        // Apply union to get both edges.
-        float d = min(da, db);
-        // Select fragment on/off based on signed distance.
-        // No AA here, since we know we're on a straight edge
-        // and the width is rounded to a whole CSS pixel.
-        alpha = min(alpha, mix(vAlphaSelect, 1.0, d < 0.0));
-
-        // Get the groove/ridge mix factor.
-        // TODO(gw): Support AA for groove/ridge border edge with transforms.
-        color_mix_factor = mix(0.0, 1.0, da > 0.0);
-    }
-
-    // Mix inner/outer color.
-    vec4 color0 = mix(vColor00, vColor01, color_mix_factor);
-    vec4 color1 = mix(vColor10, vColor11, color_mix_factor);
-
-    // Select color based on side of line. Get distance from the
-    // reference line, and then apply AA along the edge.
-    float ld = distance_to_line(vColorEdgeLine.xy, vColorEdgeLine.zw, local_pos);
-    float m = smoothstep(-0.5 * afwidth, 0.5 * afwidth, ld);
-    vec4 color = mix(color0, color1, m);
-
-    oFragColor = color * vec4(1.0, 1.0, 1.0, alpha);
-}
--- a/gfx/webrender/res/ps_border_corner.glsl
+++ b/gfx/webrender/res/ps_border_corner.glsl
@@ -1,13 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include shared,prim_shared,shared_border
+#include shared,prim_shared,shared_border,ellipse
 
 // Edge color transition
 flat varying vec4 vColor00;
 flat varying vec4 vColor01;
 flat varying vec4 vColor10;
 flat varying vec4 vColor11;
 flat varying vec4 vColorEdgeLine;
 
@@ -22,8 +22,376 @@ flat varying float vSDFSelect;
 // Border style
 flat varying float vAlphaSelect;
 
 #ifdef WR_FEATURE_TRANSFORM
 varying vec3 vLocalPos;
 #else
 varying vec2 vLocalPos;
 #endif
+
+#ifdef WR_VERTEX_SHADER
+// Matches BorderCornerSide enum in border.rs
+#define SIDE_BOTH       0
+#define SIDE_FIRST      1
+#define SIDE_SECOND     2
+
+vec2 get_radii(vec2 radius, vec2 invalid) {
+    if (all(greaterThan(radius, vec2(0.0)))) {
+        return radius;
+    }
+
+    return invalid;
+}
+
+void set_radii(int style,
+               vec2 radii,
+               vec2 widths,
+               vec2 adjusted_widths) {
+    vRadii0.xy = get_radii(radii, 2.0 * widths);
+    vRadii0.zw = get_radii(radii - widths, -widths);
+
+    switch (style) {
+        case BORDER_STYLE_RIDGE:
+        case BORDER_STYLE_GROOVE:
+            vRadii1.xy = radii - adjusted_widths;
+            // See comment in default branch
+            vRadii1.zw = vec2(-100.0);
+            break;
+        case BORDER_STYLE_DOUBLE:
+            vRadii1.xy = get_radii(radii - adjusted_widths, -widths);
+            vRadii1.zw = get_radii(radii - widths + adjusted_widths, -widths);
+            break;
+        default:
+            // These aren't needed, so we set them to some reasonably large
+            // negative value so later computations will discard them. This
+            // avoids branches and numerical issues in the fragment shader.
+            vRadii1.xy = vec2(-100.0);
+            vRadii1.zw = vec2(-100.0);
+            break;
+    }
+}
+
+void set_edge_line(vec2 border_width,
+                   vec2 outer_corner,
+                   vec2 gradient_sign) {
+    vec2 gradient = border_width * gradient_sign;
+    vColorEdgeLine = vec4(outer_corner, vec2(-gradient.y, gradient.x));
+}
+
+void write_color(vec4 color0, vec4 color1, int style, vec2 delta, int instance_kind) {
+    vec4 modulate;
+
+    switch (style) {
+        case BORDER_STYLE_GROOVE:
+            modulate = vec4(1.0 - 0.3 * delta.x,
+                            1.0 + 0.3 * delta.x,
+                            1.0 - 0.3 * delta.y,
+                            1.0 + 0.3 * delta.y);
+
+            break;
+        case BORDER_STYLE_RIDGE:
+            modulate = vec4(1.0 + 0.3 * delta.x,
+                            1.0 - 0.3 * delta.x,
+                            1.0 + 0.3 * delta.y,
+                            1.0 - 0.3 * delta.y);
+            break;
+        default:
+            modulate = vec4(1.0);
+            break;
+    }
+
+    // Optionally mask out one side of the border corner,
+    // depending on the instance kind.
+    switch (instance_kind) {
+        case SIDE_FIRST:
+            color0.a = 0.0;
+            break;
+        case SIDE_SECOND:
+            color1.a = 0.0;
+            break;
+    }
+
+    vColor00 = vec4(color0.rgb * modulate.x, color0.a);
+    vColor01 = vec4(color0.rgb * modulate.y, color0.a);
+    vColor10 = vec4(color1.rgb * modulate.z, color1.a);
+    vColor11 = vec4(color1.rgb * modulate.w, color1.a);
+}
+
+int select_style(int color_select, vec2 fstyle) {
+    ivec2 style = ivec2(fstyle);
+
+    switch (color_select) {
+        case SIDE_BOTH:
+        {
+            // TODO(gw): A temporary hack! While we don't support
+            //           border corners that have dots or dashes
+            //           with another style, pretend they are solid
+            //           border corners.
+            bool has_dots = style.x == BORDER_STYLE_DOTTED ||
+                            style.y == BORDER_STYLE_DOTTED;
+            bool has_dashes = style.x == BORDER_STYLE_DASHED ||
+                              style.y == BORDER_STYLE_DASHED;
+            if (style.x != style.y && (has_dots || has_dashes))
+                return BORDER_STYLE_SOLID;
+            return style.x;
+        }
+        case SIDE_FIRST:
+            return style.x;
+        case SIDE_SECOND:
+            return style.y;
+    }
+}
+
+void main(void) {
+    Primitive prim = load_primitive();
+    Border border = fetch_border(prim.specific_prim_address);
+    int sub_part = prim.user_data0;
+    BorderCorners corners = get_border_corners(border, prim.local_rect);
+
+    vec2 p0, p1;
+
+    // TODO(gw): We'll need to pass through multiple styles
+    //           once we support style transitions per corner.
+    int style;
+    vec4 edge_distances;
+    vec4 color0, color1;
+    vec2 color_delta;
+
+    // TODO(gw): Now that all border styles are supported, the switch
+    //           statement below can be tidied up quite a bit.
+
+    switch (sub_part) {
+        case 0: {
+            p0 = corners.tl_outer;
+            p1 = corners.tl_inner;
+            color0 = border.colors[0];
+            color1 = border.colors[1];
+            vClipCenter = corners.tl_outer + border.radii[0].xy;
+            vClipSign = vec2(1.0);
+            style = select_style(prim.user_data1, border.style.yx);
+            vec4 adjusted_widths = get_effective_border_widths(border, style);
+            vec4 inv_adjusted_widths = border.widths - adjusted_widths;
+            set_radii(style,
+                      border.radii[0].xy,
+                      border.widths.xy,
+                      adjusted_widths.xy);
+            set_edge_line(border.widths.xy,
+                          corners.tl_outer,
+                          vec2(1.0, 1.0));
+            edge_distances = vec4(p0 + adjusted_widths.xy,
+                                  p0 + inv_adjusted_widths.xy);
+            color_delta = vec2(1.0);
+            break;
+        }
+        case 1: {
+            p0 = vec2(corners.tr_inner.x, corners.tr_outer.y);
+            p1 = vec2(corners.tr_outer.x, corners.tr_inner.y);
+            color0 = border.colors[1];
+            color1 = border.colors[2];
+            vClipCenter = corners.tr_outer + vec2(-border.radii[0].z, border.radii[0].w);
+            vClipSign = vec2(-1.0, 1.0);
+            style = select_style(prim.user_data1, border.style.zy);
+            vec4 adjusted_widths = get_effective_border_widths(border, style);
+            vec4 inv_adjusted_widths = border.widths - adjusted_widths;
+            set_radii(style,
+                      border.radii[0].zw,
+                      border.widths.zy,
+                      adjusted_widths.zy);
+            set_edge_line(border.widths.zy,
+                          corners.tr_outer,
+                          vec2(-1.0, 1.0));
+            edge_distances = vec4(p1.x - adjusted_widths.z,
+                                  p0.y + adjusted_widths.y,
+                                  p1.x - border.widths.z + adjusted_widths.z,
+                                  p0.y + inv_adjusted_widths.y);
+            color_delta = vec2(1.0, -1.0);
+            break;
+        }
+        case 2: {
+            p0 = corners.br_inner;
+            p1 = corners.br_outer;
+            color0 = border.colors[2];
+            color1 = border.colors[3];
+            vClipCenter = corners.br_outer - border.radii[1].xy;
+            vClipSign = vec2(-1.0, -1.0);
+            style = select_style(prim.user_data1, border.style.wz);
+            vec4 adjusted_widths = get_effective_border_widths(border, style);
+            vec4 inv_adjusted_widths = border.widths - adjusted_widths;
+            set_radii(style,
+                      border.radii[1].xy,
+                      border.widths.zw,
+                      adjusted_widths.zw);
+            set_edge_line(border.widths.zw,
+                          corners.br_outer,
+                          vec2(-1.0, -1.0));
+            edge_distances = vec4(p1.x - adjusted_widths.z,
+                                  p1.y - adjusted_widths.w,
+                                  p1.x - border.widths.z + adjusted_widths.z,
+                                  p1.y - border.widths.w + adjusted_widths.w);
+            color_delta = vec2(-1.0);
+            break;
+        }
+        case 3: {
+            p0 = vec2(corners.bl_outer.x, corners.bl_inner.y);
+            p1 = vec2(corners.bl_inner.x, corners.bl_outer.y);
+            color0 = border.colors[3];
+            color1 = border.colors[0];
+            vClipCenter = corners.bl_outer + vec2(border.radii[1].z, -border.radii[1].w);
+            vClipSign = vec2(1.0, -1.0);
+            style = select_style(prim.user_data1, border.style.xw);
+            vec4 adjusted_widths = get_effective_border_widths(border, style);
+            vec4 inv_adjusted_widths = border.widths - adjusted_widths;
+            set_radii(style,
+                      border.radii[1].zw,
+                      border.widths.xw,
+                      adjusted_widths.xw);
+            set_edge_line(border.widths.xw,
+                          corners.bl_outer,
+                          vec2(1.0, -1.0));
+            edge_distances = vec4(p0.x + adjusted_widths.x,
+                                  p1.y - adjusted_widths.w,
+                                  p0.x + inv_adjusted_widths.x,
+                                  p1.y - border.widths.w + adjusted_widths.w);
+            color_delta = vec2(-1.0, 1.0);
+            break;
+        }
+    }
+
+    switch (style) {
+        case BORDER_STYLE_DOUBLE: {
+            vEdgeDistance = edge_distances;
+            vAlphaSelect = 0.0;
+            vSDFSelect = 0.0;
+            break;
+        }
+        case BORDER_STYLE_GROOVE:
+        case BORDER_STYLE_RIDGE:
+            vEdgeDistance = vec4(edge_distances.xy, 0.0, 0.0);
+            vAlphaSelect = 1.0;
+            vSDFSelect = 1.0;
+            break;
+        case BORDER_STYLE_DOTTED:
+            // Disable normal clip radii for dotted corners, since
+            // all the clipping is handled by the clip mask.
+            vClipSign = vec2(0.0);
+            vEdgeDistance = vec4(0.0);
+            vAlphaSelect = 1.0;
+            vSDFSelect = 0.0;
+            break;
+        default: {
+            vEdgeDistance = vec4(0.0);
+            vAlphaSelect = 1.0;
+            vSDFSelect = 0.0;
+            break;
+        }
+    }
+
+    write_color(color0, color1, style, color_delta, prim.user_data1);
+
+    RectWithSize segment_rect;
+    segment_rect.p0 = p0;
+    segment_rect.size = p1 - p0;
+
+#ifdef WR_FEATURE_TRANSFORM
+    TransformVertexInfo vi = write_transform_vertex(segment_rect,
+                                                    prim.local_clip_rect,
+                                                    prim.z,
+                                                    prim.layer,
+                                                    prim.task,
+                                                    prim.local_rect);
+#else
+    VertexInfo vi = write_vertex(segment_rect,
+                                 prim.local_clip_rect,
+                                 prim.z,
+                                 prim.layer,
+                                 prim.task,
+                                 prim.local_rect);
+#endif
+
+    vLocalPos = vi.local_pos;
+    write_clip(vi.screen_pos, prim.clip_area);
+}
+#endif
+
+#ifdef WR_FRAGMENT_SHADER
+void main(void) {
+    float alpha = 1.0;
+#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());
+
+    // Find the appropriate distance to apply the AA smoothstep over.
+    vec2 fw = fwidth(local_pos);
+    float afwidth = length(fw);
+    float distance_for_color;
+    float color_mix_factor;
+
+    // Only apply the clip AA if inside the clip region. This is
+    // necessary for correctness when the border width is greater
+    // than the border radius.
+    if (all(lessThan(local_pos * vClipSign, vClipCenter * vClipSign))) {
+        vec2 p = local_pos - vClipCenter;
+
+        // Get signed distance from the inner/outer clips.
+        float d0 = distance_to_ellipse(p, vRadii0.xy);
+        float d1 = distance_to_ellipse(p, vRadii0.zw);
+        float d2 = distance_to_ellipse(p, vRadii1.xy);
+        float d3 = distance_to_ellipse(p, vRadii1.zw);
+
+        // SDF subtract main radii
+        float d_main = max(d0, 0.5 * afwidth - d1);
+
+        // SDF subtract inner radii (double style borders)
+        float d_inner = max(d2 - 0.5 * afwidth, -d3);
+
+        // Select how to combine the SDF based on border style.
+        float d = mix(max(d_main, -d_inner), d_main, vSDFSelect);
+
+        // Only apply AA to fragments outside the signed distance field.
+        alpha = min(alpha, 1.0 - smoothstep(0.0, 0.5 * afwidth, d));
+
+        // Get the groove/ridge mix factor.
+        color_mix_factor = smoothstep(-0.5 * afwidth,
+                                      0.5 * afwidth,
+                                      -d2);
+    } else {
+        // Handle the case where the fragment is outside the clip
+        // region in a corner. This occurs when border width is
+        // greater than border radius.
+
+        // Get linear distances along horizontal and vertical edges.
+        vec2 d0 = vClipSign.xx * (local_pos.xx - vEdgeDistance.xz);
+        vec2 d1 = vClipSign.yy * (local_pos.yy - vEdgeDistance.yw);
+        // Apply union to get the outer edge signed distance.
+        float da = min(d0.x, d1.x);
+        // Apply intersection to get the inner edge signed distance.
+        float db = max(-d0.y, -d1.y);
+        // Apply union to get both edges.
+        float d = min(da, db);
+        // Select fragment on/off based on signed distance.
+        // No AA here, since we know we're on a straight edge
+        // and the width is rounded to a whole CSS pixel.
+        alpha = min(alpha, mix(vAlphaSelect, 1.0, d < 0.0));
+
+        // Get the groove/ridge mix factor.
+        // TODO(gw): Support AA for groove/ridge border edge with transforms.
+        color_mix_factor = mix(0.0, 1.0, da > 0.0);
+    }
+
+    // Mix inner/outer color.
+    vec4 color0 = mix(vColor00, vColor01, color_mix_factor);
+    vec4 color1 = mix(vColor10, vColor11, color_mix_factor);
+
+    // Select color based on side of line. Get distance from the
+    // reference line, and then apply AA along the edge.
+    float ld = distance_to_line(vColorEdgeLine.xy, vColorEdgeLine.zw, local_pos);
+    float m = smoothstep(-0.5 * afwidth, 0.5 * afwidth, ld);
+    vec4 color = mix(color0, color1, m);
+
+    oFragColor = color * vec4(1.0, 1.0, 1.0, alpha);
+}
+#endif
deleted file mode 100644
--- a/gfx/webrender/res/ps_border_corner.vs.glsl
+++ /dev/null
@@ -1,285 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-// Matches BorderCornerSide enum in border.rs
-#define SIDE_BOTH       0
-#define SIDE_FIRST      1
-#define SIDE_SECOND     2
-
-vec2 get_radii(vec2 radius, vec2 invalid) {
-    if (all(greaterThan(radius, vec2(0.0)))) {
-        return radius;
-    }
-
-    return invalid;
-}
-
-void set_radii(int style,
-               vec2 radii,
-               vec2 widths,
-               vec2 adjusted_widths) {
-    vRadii0.xy = get_radii(radii, 2.0 * widths);
-    vRadii0.zw = get_radii(radii - widths, -widths);
-
-    switch (style) {
-        case BORDER_STYLE_RIDGE:
-        case BORDER_STYLE_GROOVE:
-            vRadii1.xy = radii - adjusted_widths;
-            // See comment in default branch
-            vRadii1.zw = vec2(-100.0);
-            break;
-        case BORDER_STYLE_DOUBLE:
-            vRadii1.xy = get_radii(radii - adjusted_widths, -widths);
-            vRadii1.zw = get_radii(radii - widths + adjusted_widths, -widths);
-            break;
-        default:
-            // These aren't needed, so we set them to some reasonably large
-            // negative value so later computations will discard them. This
-            // avoids branches and numerical issues in the fragment shader.
-            vRadii1.xy = vec2(-100.0);
-            vRadii1.zw = vec2(-100.0);
-            break;
-    }
-}
-
-void set_edge_line(vec2 border_width,
-                   vec2 outer_corner,
-                   vec2 gradient_sign) {
-    vec2 gradient = border_width * gradient_sign;
-    vColorEdgeLine = vec4(outer_corner, vec2(-gradient.y, gradient.x));
-}
-
-void write_color(vec4 color0, vec4 color1, int style, vec2 delta, int instance_kind) {
-    vec4 modulate;
-
-    switch (style) {
-        case BORDER_STYLE_GROOVE:
-            modulate = vec4(1.0 - 0.3 * delta.x,
-                            1.0 + 0.3 * delta.x,
-                            1.0 - 0.3 * delta.y,
-                            1.0 + 0.3 * delta.y);
-
-            break;
-        case BORDER_STYLE_RIDGE:
-            modulate = vec4(1.0 + 0.3 * delta.x,
-                            1.0 - 0.3 * delta.x,
-                            1.0 + 0.3 * delta.y,
-                            1.0 - 0.3 * delta.y);
-            break;
-        default:
-            modulate = vec4(1.0);
-            break;
-    }
-
-    // Optionally mask out one side of the border corner,
-    // depending on the instance kind.
-    switch (instance_kind) {
-        case SIDE_FIRST:
-            color0.a = 0.0;
-            break;
-        case SIDE_SECOND:
-            color1.a = 0.0;
-            break;
-    }
-
-    vColor00 = vec4(color0.rgb * modulate.x, color0.a);
-    vColor01 = vec4(color0.rgb * modulate.y, color0.a);
-    vColor10 = vec4(color1.rgb * modulate.z, color1.a);
-    vColor11 = vec4(color1.rgb * modulate.w, color1.a);
-}
-
-int select_style(int color_select, vec2 fstyle) {
-    ivec2 style = ivec2(fstyle);
-
-    switch (color_select) {
-        case SIDE_BOTH:
-        {
-            // TODO(gw): A temporary hack! While we don't support
-            //           border corners that have dots or dashes
-            //           with another style, pretend they are solid
-            //           border corners.
-            bool has_dots = style.x == BORDER_STYLE_DOTTED ||
-                            style.y == BORDER_STYLE_DOTTED;
-            bool has_dashes = style.x == BORDER_STYLE_DASHED ||
-                              style.y == BORDER_STYLE_DASHED;
-            if (style.x != style.y && (has_dots || has_dashes))
-                return BORDER_STYLE_SOLID;
-            return style.x;
-        }
-        case SIDE_FIRST:
-            return style.x;
-        case SIDE_SECOND:
-            return style.y;
-    }
-}
-
-void main(void) {
-    Primitive prim = load_primitive();
-    Border border = fetch_border(prim.specific_prim_address);
-    int sub_part = prim.user_data0;
-    BorderCorners corners = get_border_corners(border, prim.local_rect);
-
-    vec2 p0, p1;
-
-    // TODO(gw): We'll need to pass through multiple styles
-    //           once we support style transitions per corner.
-    int style;
-    vec4 edge_distances;
-    vec4 color0, color1;
-    vec2 color_delta;
-
-    // TODO(gw): Now that all border styles are supported, the switch
-    //           statement below can be tidied up quite a bit.
-
-    switch (sub_part) {
-        case 0: {
-            p0 = corners.tl_outer;
-            p1 = corners.tl_inner;
-            color0 = border.colors[0];
-            color1 = border.colors[1];
-            vClipCenter = corners.tl_outer + border.radii[0].xy;
-            vClipSign = vec2(1.0);
-            style = select_style(prim.user_data1, border.style.yx);
-            vec4 adjusted_widths = get_effective_border_widths(border, style);
-            vec4 inv_adjusted_widths = border.widths - adjusted_widths;
-            set_radii(style,
-                      border.radii[0].xy,
-                      border.widths.xy,
-                      adjusted_widths.xy);
-            set_edge_line(border.widths.xy,
-                          corners.tl_outer,
-                          vec2(1.0, 1.0));
-            edge_distances = vec4(p0 + adjusted_widths.xy,
-                                  p0 + inv_adjusted_widths.xy);
-            color_delta = vec2(1.0);
-            break;
-        }
-        case 1: {
-            p0 = vec2(corners.tr_inner.x, corners.tr_outer.y);
-            p1 = vec2(corners.tr_outer.x, corners.tr_inner.y);
-            color0 = border.colors[1];
-            color1 = border.colors[2];
-            vClipCenter = corners.tr_outer + vec2(-border.radii[0].z, border.radii[0].w);
-            vClipSign = vec2(-1.0, 1.0);
-            style = select_style(prim.user_data1, border.style.zy);
-            vec4 adjusted_widths = get_effective_border_widths(border, style);
-            vec4 inv_adjusted_widths = border.widths - adjusted_widths;
-            set_radii(style,
-                      border.radii[0].zw,
-                      border.widths.zy,
-                      adjusted_widths.zy);
-            set_edge_line(border.widths.zy,
-                          corners.tr_outer,
-                          vec2(-1.0, 1.0));
-            edge_distances = vec4(p1.x - adjusted_widths.z,
-                                  p0.y + adjusted_widths.y,
-                                  p1.x - border.widths.z + adjusted_widths.z,
-                                  p0.y + inv_adjusted_widths.y);
-            color_delta = vec2(1.0, -1.0);
-            break;
-        }
-        case 2: {
-            p0 = corners.br_inner;
-            p1 = corners.br_outer;
-            color0 = border.colors[2];
-            color1 = border.colors[3];
-            vClipCenter = corners.br_outer - border.radii[1].xy;
-            vClipSign = vec2(-1.0, -1.0);
-            style = select_style(prim.user_data1, border.style.wz);
-            vec4 adjusted_widths = get_effective_border_widths(border, style);
-            vec4 inv_adjusted_widths = border.widths - adjusted_widths;
-            set_radii(style,
-                      border.radii[1].xy,
-                      border.widths.zw,
-                      adjusted_widths.zw);
-            set_edge_line(border.widths.zw,
-                          corners.br_outer,
-                          vec2(-1.0, -1.0));
-            edge_distances = vec4(p1.x - adjusted_widths.z,
-                                  p1.y - adjusted_widths.w,
-                                  p1.x - border.widths.z + adjusted_widths.z,
-                                  p1.y - border.widths.w + adjusted_widths.w);
-            color_delta = vec2(-1.0);
-            break;
-        }
-        case 3: {
-            p0 = vec2(corners.bl_outer.x, corners.bl_inner.y);
-            p1 = vec2(corners.bl_inner.x, corners.bl_outer.y);
-            color0 = border.colors[3];
-            color1 = border.colors[0];
-            vClipCenter = corners.bl_outer + vec2(border.radii[1].z, -border.radii[1].w);
-            vClipSign = vec2(1.0, -1.0);
-            style = select_style(prim.user_data1, border.style.xw);
-            vec4 adjusted_widths = get_effective_border_widths(border, style);
-            vec4 inv_adjusted_widths = border.widths - adjusted_widths;
-            set_radii(style,
-                      border.radii[1].zw,
-                      border.widths.xw,
-                      adjusted_widths.xw);
-            set_edge_line(border.widths.xw,
-                          corners.bl_outer,
-                          vec2(1.0, -1.0));
-            edge_distances = vec4(p0.x + adjusted_widths.x,
-                                  p1.y - adjusted_widths.w,
-                                  p0.x + inv_adjusted_widths.x,
-                                  p1.y - border.widths.w + adjusted_widths.w);
-            color_delta = vec2(-1.0, 1.0);
-            break;
-        }
-    }
-
-    switch (style) {
-        case BORDER_STYLE_DOUBLE: {
-            vEdgeDistance = edge_distances;
-            vAlphaSelect = 0.0;
-            vSDFSelect = 0.0;
-            break;
-        }
-        case BORDER_STYLE_GROOVE:
-        case BORDER_STYLE_RIDGE:
-            vEdgeDistance = vec4(edge_distances.xy, 0.0, 0.0);
-            vAlphaSelect = 1.0;
-            vSDFSelect = 1.0;
-            break;
-        case BORDER_STYLE_DOTTED:
-            // Disable normal clip radii for dotted corners, since
-            // all the clipping is handled by the clip mask.
-            vClipSign = vec2(0.0);
-            vEdgeDistance = vec4(0.0);
-            vAlphaSelect = 1.0;
-            vSDFSelect = 0.0;
-            break;
-        default: {
-            vEdgeDistance = vec4(0.0);
-            vAlphaSelect = 1.0;
-            vSDFSelect = 0.0;
-            break;
-        }
-    }
-
-    write_color(color0, color1, style, color_delta, prim.user_data1);
-
-    RectWithSize segment_rect;
-    segment_rect.p0 = p0;
-    segment_rect.size = p1 - p0;
-
-#ifdef WR_FEATURE_TRANSFORM
-    TransformVertexInfo vi = write_transform_vertex(segment_rect,
-                                                    prim.local_clip_rect,
-                                                    prim.z,
-                                                    prim.layer,
-                                                    prim.task,
-                                                    prim.local_rect);
-#else
-    VertexInfo vi = write_vertex(segment_rect,
-                                 prim.local_clip_rect,
-                                 prim.z,
-                                 prim.layer,
-                                 prim.task,
-                                 prim.local_rect);
-#endif
-
-    vLocalPos = vi.local_pos;
-    write_clip(vi.screen_pos, prim.clip_area);
-}
deleted file mode 100644
--- a/gfx/webrender/res/ps_border_edge.fs.glsl
+++ /dev/null
@@ -1,63 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-void main(void) {
-    float alpha = 1.0;
-#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());
-
-    // Find the appropriate distance to apply the step over.
-    vec2 fw = fwidth(local_pos);
-    float afwidth = length(fw);
-
-    // Applies the math necessary to draw a style: double
-    // border. In the case of a solid border, the vertex
-    // shader sets interpolator values that make this have
-    // no effect.
-
-    // Select the x/y coord, depending on which axis this edge is.
-    vec2 pos = mix(local_pos.xy, local_pos.yx, vAxisSelect);
-
-    // Get signed distance from each of the inner edges.
-    float d0 = pos.x - vEdgeDistance.x;
-    float d1 = vEdgeDistance.y - pos.x;
-
-    // SDF union to select both outer edges.
-    float d = min(d0, d1);
-
-    // Select fragment on/off based on signed distance.
-    // No AA here, since we know we're on a straight edge
-    // and the width is rounded to a whole CSS pixel.
-    alpha = min(alpha, mix(vAlphaSelect, 1.0, d < 0.0));
-
-    // Mix color based on first distance.
-    // TODO(gw): Support AA for groove/ridge border edge with transforms.
-    vec4 color = mix(vColor0, vColor1, bvec4(d0 * vEdgeDistance.y > 0.0));
-
-    // Apply dashing / dotting parameters.
-
-    // Get the main-axis position relative to closest dot or dash.
-    float x = mod(pos.y - vClipParams.x, vClipParams.y);
-
-    // Calculate dash alpha (on/off) based on dash length
-    float dash_alpha = step(x, vClipParams.z);
-
-    // Get the dot alpha
-    vec2 dot_relative_pos = vec2(x, pos.x) - vClipParams.zw;
-    float dot_distance = length(dot_relative_pos) - vClipParams.z;
-    float dot_alpha = 1.0 - smoothstep(-0.5 * afwidth,
-                                        0.5 * afwidth,
-                                        dot_distance);
-
-    // Select between dot/dash alpha based on clip mode.
-    alpha = min(alpha, mix(dash_alpha, dot_alpha, vClipSelect));
-
-    oFragColor = color * vec4(1.0, 1.0, 1.0, alpha);
-}
--- a/gfx/webrender/res/ps_border_edge.glsl
+++ b/gfx/webrender/res/ps_border_edge.glsl
@@ -12,8 +12,292 @@ flat varying float vAlphaSelect;
 flat varying vec4 vClipParams;
 flat varying float vClipSelect;
 
 #ifdef WR_FEATURE_TRANSFORM
 varying vec3 vLocalPos;
 #else
 varying vec2 vLocalPos;
 #endif
+
+#ifdef WR_VERTEX_SHADER
+void write_edge_distance(float p0,
+                         float original_width,
+                         float adjusted_width,
+                         float style,
+                         float axis_select,
+                         float sign_adjust) {
+    switch (int(style)) {
+        case BORDER_STYLE_DOUBLE:
+            vEdgeDistance = vec2(p0 + adjusted_width,
+                                 p0 + original_width - adjusted_width);
+            break;
+        case BORDER_STYLE_GROOVE:
+        case BORDER_STYLE_RIDGE:
+            vEdgeDistance = vec2(p0 + adjusted_width, sign_adjust);
+            break;
+        default:
+            vEdgeDistance = vec2(0.0);
+            break;
+    }
+
+    vAxisSelect = axis_select;
+}
+
+void write_alpha_select(float style) {
+    switch (int(style)) {
+        case BORDER_STYLE_DOUBLE:
+            vAlphaSelect = 0.0;
+            break;
+        default:
+            vAlphaSelect = 1.0;
+            break;
+    }
+}
+
+// write_color function is duplicated to work around a Mali-T880 GPU driver program link error.
+// See https://github.com/servo/webrender/issues/1403 for more info.
+// TODO: convert back to a single function once the driver issues are resolved, if ever.
+void write_color0(vec4 color, float style, bool flip) {
+    vec2 modulate;
+
+    switch (int(style)) {
+        case BORDER_STYLE_GROOVE:
+        {
+            modulate = flip ? vec2(1.3, 0.7) : vec2(0.7, 1.3);
+            break;
+        }
+        case BORDER_STYLE_RIDGE:
+        {
+            modulate = flip ? vec2(0.7, 1.3) : vec2(1.3, 0.7);
+            break;
+        }
+        default:
+            modulate = vec2(1.0);
+            break;
+    }
+
+    vColor0 = vec4(color.rgb * modulate.x, color.a);
+}
+
+void write_color1(vec4 color, float style, bool flip) {
+    vec2 modulate;
+
+    switch (int(style)) {
+        case BORDER_STYLE_GROOVE:
+        {
+            modulate = flip ? vec2(1.3, 0.7) : vec2(0.7, 1.3);
+            break;
+        }
+        case BORDER_STYLE_RIDGE:
+        {
+            modulate = flip ? vec2(0.7, 1.3) : vec2(1.3, 0.7);
+            break;
+        }
+        default:
+            modulate = vec2(1.0);
+            break;
+    }
+
+    vColor1 = vec4(color.rgb * modulate.y, color.a);
+}
+
+void write_clip_params(float style,
+                       float border_width,
+                       float edge_length,
+                       float edge_offset,
+                       float center_line) {
+    // x = offset
+    // y = dash on + off length
+    // z = dash length
+    // w = center line of edge cross-axis (for dots only)
+    switch (int(style)) {
+        case BORDER_STYLE_DASHED: {
+            float desired_dash_length = border_width * 3.0;
+            // Consider half total length since there is an equal on/off for each dash.
+            float dash_count = ceil(0.5 * edge_length / desired_dash_length);
+            float dash_length = 0.5 * edge_length / dash_count;
+            vClipParams = vec4(edge_offset - 0.5 * dash_length,
+                               2.0 * dash_length,
+                               dash_length,
+                               0.0);
+            vClipSelect = 0.0;
+            break;
+        }
+        case BORDER_STYLE_DOTTED: {
+            float diameter = border_width;
+            float radius = 0.5 * diameter;
+            float dot_count = ceil(0.5 * edge_length / diameter);
+            float empty_space = edge_length - dot_count * diameter;
+            float distance_between_centers = diameter + empty_space / dot_count;
+            vClipParams = vec4(edge_offset - radius,
+                               distance_between_centers,
+                               radius,
+                               center_line);
+            vClipSelect = 1.0;
+            break;
+        }
+        default:
+            vClipParams = vec4(1.0);
+            vClipSelect = 0.0;
+            break;
+    }
+}
+
+void main(void) {
+    Primitive prim = load_primitive();
+    Border border = fetch_border(prim.specific_prim_address);
+    int sub_part = prim.user_data0;
+    BorderCorners corners = get_border_corners(border, prim.local_rect);
+    vec4 color = border.colors[sub_part];
+
+    // TODO(gw): Now that all border styles are supported, the switch
+    //           statement below can be tidied up quite a bit.
+
+    float style;
+    bool color_flip;
+
+    RectWithSize segment_rect;
+    switch (sub_part) {
+        case 0: {
+            segment_rect.p0 = vec2(corners.tl_outer.x, corners.tl_inner.y);
+            segment_rect.size = vec2(border.widths.x, corners.bl_inner.y - corners.tl_inner.y);
+            vec4 adjusted_widths = get_effective_border_widths(border, int(border.style.x));
+            write_edge_distance(segment_rect.p0.x, border.widths.x, adjusted_widths.x, border.style.x, 0.0, 1.0);
+            style = border.style.x;
+            color_flip = false;
+            write_clip_params(border.style.x,
+                              border.widths.x,
+                              segment_rect.size.y,
+                              segment_rect.p0.y,
+                              segment_rect.p0.x + 0.5 * segment_rect.size.x);
+            break;
+        }
+        case 1: {
+            segment_rect.p0 = vec2(corners.tl_inner.x, corners.tl_outer.y);
+            segment_rect.size = vec2(corners.tr_inner.x - corners.tl_inner.x, border.widths.y);
+            vec4 adjusted_widths = get_effective_border_widths(border, int(border.style.y));
+            write_edge_distance(segment_rect.p0.y, border.widths.y, adjusted_widths.y, border.style.y, 1.0, 1.0);
+            style = border.style.y;
+            color_flip = false;
+            write_clip_params(border.style.y,
+                              border.widths.y,
+                              segment_rect.size.x,
+                              segment_rect.p0.x,
+                              segment_rect.p0.y + 0.5 * segment_rect.size.y);
+            break;
+        }
+        case 2: {
+            segment_rect.p0 = vec2(corners.tr_outer.x - border.widths.z, corners.tr_inner.y);
+            segment_rect.size = vec2(border.widths.z, corners.br_inner.y - corners.tr_inner.y);
+            vec4 adjusted_widths = get_effective_border_widths(border, int(border.style.z));
+            write_edge_distance(segment_rect.p0.x, border.widths.z, adjusted_widths.z, border.style.z, 0.0, -1.0);
+            style = border.style.z;
+            color_flip = true;
+            write_clip_params(border.style.z,
+                              border.widths.z,
+                              segment_rect.size.y,
+                              segment_rect.p0.y,
+                              segment_rect.p0.x + 0.5 * segment_rect.size.x);
+            break;
+        }
+        case 3: {
+            segment_rect.p0 = vec2(corners.bl_inner.x, corners.bl_outer.y - border.widths.w);
+            segment_rect.size = vec2(corners.br_inner.x - corners.bl_inner.x, border.widths.w);
+            vec4 adjusted_widths = get_effective_border_widths(border, int(border.style.w));
+            write_edge_distance(segment_rect.p0.y, border.widths.w, adjusted_widths.w, border.style.w, 1.0, -1.0);
+            style = border.style.w;
+            color_flip = true;
+            write_clip_params(border.style.w,
+                              border.widths.w,
+                              segment_rect.size.x,
+                              segment_rect.p0.x,
+                              segment_rect.p0.y + 0.5 * segment_rect.size.y);
+            break;
+        }
+    }
+
+    write_alpha_select(style);
+    write_color0(color, style, color_flip);
+    write_color1(color, style, color_flip);
+
+#ifdef WR_FEATURE_TRANSFORM
+    TransformVertexInfo vi = write_transform_vertex(segment_rect,
+                                                    prim.local_clip_rect,
+                                                    prim.z,
+                                                    prim.layer,
+                                                    prim.task,
+                                                    prim.local_rect);
+#else
+    VertexInfo vi = write_vertex(segment_rect,
+                                 prim.local_clip_rect,
+                                 prim.z,
+                                 prim.layer,
+                                 prim.task,
+                                 prim.local_rect);
+#endif
+
+    vLocalPos = vi.local_pos;
+    write_clip(vi.screen_pos, prim.clip_area);
+}
+#endif
+
+#ifdef WR_FRAGMENT_SHADER
+void main(void) {
+    float alpha = 1.0;
+#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());
+
+    // Find the appropriate distance to apply the step over.
+    vec2 fw = fwidth(local_pos);
+    float afwidth = length(fw);
+
+    // Applies the math necessary to draw a style: double
+    // border. In the case of a solid border, the vertex
+    // shader sets interpolator values that make this have
+    // no effect.
+
+    // Select the x/y coord, depending on which axis this edge is.
+    vec2 pos = mix(local_pos.xy, local_pos.yx, vAxisSelect);
+
+    // Get signed distance from each of the inner edges.
+    float d0 = pos.x - vEdgeDistance.x;
+    float d1 = vEdgeDistance.y - pos.x;
+
+    // SDF union to select both outer edges.
+    float d = min(d0, d1);
+
+    // Select fragment on/off based on signed distance.
+    // No AA here, since we know we're on a straight edge
+    // and the width is rounded to a whole CSS pixel.
+    alpha = min(alpha, mix(vAlphaSelect, 1.0, d < 0.0));
+
+    // Mix color based on first distance.
+    // TODO(gw): Support AA for groove/ridge border edge with transforms.
+    vec4 color = mix(vColor0, vColor1, bvec4(d0 * vEdgeDistance.y > 0.0));
+
+    // Apply dashing / dotting parameters.
+
+    // Get the main-axis position relative to closest dot or dash.
+    float x = mod(pos.y - vClipParams.x, vClipParams.y);
+
+    // Calculate dash alpha (on/off) based on dash length
+    float dash_alpha = step(x, vClipParams.z);
+
+    // Get the dot alpha
+    vec2 dot_relative_pos = vec2(x, pos.x) - vClipParams.zw;
+    float dot_distance = length(dot_relative_pos) - vClipParams.z;
+    float dot_alpha = 1.0 - smoothstep(-0.5 * afwidth,
+                                        0.5 * afwidth,
+                                        dot_distance);
+
+    // Select between dot/dash alpha based on clip mode.
+    alpha = min(alpha, mix(dash_alpha, dot_alpha, vClipSelect));
+
+    oFragColor = color * vec4(1.0, 1.0, 1.0, alpha);
+}
+#endif
deleted file mode 100644
--- a/gfx/webrender/res/ps_border_edge.vs.glsl
+++ /dev/null
@@ -1,223 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-void write_edge_distance(float p0,
-                         float original_width,
-                         float adjusted_width,
-                         float style,
-                         float axis_select,
-                         float sign_adjust) {
-    switch (int(style)) {
-        case BORDER_STYLE_DOUBLE:
-            vEdgeDistance = vec2(p0 + adjusted_width,
-                                 p0 + original_width - adjusted_width);
-            break;
-        case BORDER_STYLE_GROOVE:
-        case BORDER_STYLE_RIDGE:
-            vEdgeDistance = vec2(p0 + adjusted_width, sign_adjust);
-            break;
-        default:
-            vEdgeDistance = vec2(0.0);
-            break;
-    }
-
-    vAxisSelect = axis_select;
-}
-
-void write_alpha_select(float style) {
-    switch (int(style)) {
-        case BORDER_STYLE_DOUBLE:
-            vAlphaSelect = 0.0;
-            break;
-        default:
-            vAlphaSelect = 1.0;
-            break;
-    }
-}
-
-// write_color function is duplicated to work around a Mali-T880 GPU driver program link error.
-// See https://github.com/servo/webrender/issues/1403 for more info.
-// TODO: convert back to a single function once the driver issues are resolved, if ever.
-void write_color0(vec4 color, float style, bool flip) {
-    vec2 modulate;
-
-    switch (int(style)) {
-        case BORDER_STYLE_GROOVE:
-        {
-            modulate = flip ? vec2(1.3, 0.7) : vec2(0.7, 1.3);
-            break;
-        }
-        case BORDER_STYLE_RIDGE:
-        {
-            modulate = flip ? vec2(0.7, 1.3) : vec2(1.3, 0.7);
-            break;
-        }
-        default:
-            modulate = vec2(1.0);
-            break;
-    }
-
-    vColor0 = vec4(color.rgb * modulate.x, color.a);
-}
-
-void write_color1(vec4 color, float style, bool flip) {
-    vec2 modulate;
-
-    switch (int(style)) {
-        case BORDER_STYLE_GROOVE:
-        {
-            modulate = flip ? vec2(1.3, 0.7) : vec2(0.7, 1.3);
-            break;
-        }
-        case BORDER_STYLE_RIDGE:
-        {
-            modulate = flip ? vec2(0.7, 1.3) : vec2(1.3, 0.7);
-            break;
-        }
-        default:
-            modulate = vec2(1.0);
-            break;
-    }
-
-    vColor1 = vec4(color.rgb * modulate.y, color.a);
-}
-
-void write_clip_params(float style,
-                       float border_width,
-                       float edge_length,
-                       float edge_offset,
-                       float center_line) {
-    // x = offset
-    // y = dash on + off length
-    // z = dash length
-    // w = center line of edge cross-axis (for dots only)
-    switch (int(style)) {
-        case BORDER_STYLE_DASHED: {
-            float desired_dash_length = border_width * 3.0;
-            // Consider half total length since there is an equal on/off for each dash.
-            float dash_count = ceil(0.5 * edge_length / desired_dash_length);
-            float dash_length = 0.5 * edge_length / dash_count;
-            vClipParams = vec4(edge_offset - 0.5 * dash_length,
-                               2.0 * dash_length,
-                               dash_length,
-                               0.0);
-            vClipSelect = 0.0;
-            break;
-        }
-        case BORDER_STYLE_DOTTED: {
-            float diameter = border_width;
-            float radius = 0.5 * diameter;
-            float dot_count = ceil(0.5 * edge_length / diameter);
-            float empty_space = edge_length - dot_count * diameter;
-            float distance_between_centers = diameter + empty_space / dot_count;
-            vClipParams = vec4(edge_offset - radius,
-                               distance_between_centers,
-                               radius,
-                               center_line);
-            vClipSelect = 1.0;
-            break;
-        }
-        default:
-            vClipParams = vec4(1.0);
-            vClipSelect = 0.0;
-            break;
-    }
-}
-
-void main(void) {
-    Primitive prim = load_primitive();
-    Border border = fetch_border(prim.specific_prim_address);
-    int sub_part = prim.user_data0;
-    BorderCorners corners = get_border_corners(border, prim.local_rect);
-    vec4 color = border.colors[sub_part];
-
-    // TODO(gw): Now that all border styles are supported, the switch
-    //           statement below can be tidied up quite a bit.
-
-    float style;
-    bool color_flip;
-
-    RectWithSize segment_rect;
-    switch (sub_part) {
-        case 0: {
-            segment_rect.p0 = vec2(corners.tl_outer.x, corners.tl_inner.y);
-            segment_rect.size = vec2(border.widths.x, corners.bl_inner.y - corners.tl_inner.y);
-            vec4 adjusted_widths = get_effective_border_widths(border, int(border.style.x));
-            write_edge_distance(segment_rect.p0.x, border.widths.x, adjusted_widths.x, border.style.x, 0.0, 1.0);
-            style = border.style.x;
-            color_flip = false;
-            write_clip_params(border.style.x,
-                              border.widths.x,
-                              segment_rect.size.y,
-                              segment_rect.p0.y,
-                              segment_rect.p0.x + 0.5 * segment_rect.size.x);
-            break;
-        }
-        case 1: {
-            segment_rect.p0 = vec2(corners.tl_inner.x, corners.tl_outer.y);
-            segment_rect.size = vec2(corners.tr_inner.x - corners.tl_inner.x, border.widths.y);
-            vec4 adjusted_widths = get_effective_border_widths(border, int(border.style.y));
-            write_edge_distance(segment_rect.p0.y, border.widths.y, adjusted_widths.y, border.style.y, 1.0, 1.0);
-            style = border.style.y;
-            color_flip = false;
-            write_clip_params(border.style.y,
-                              border.widths.y,
-                              segment_rect.size.x,
-                              segment_rect.p0.x,
-                              segment_rect.p0.y + 0.5 * segment_rect.size.y);
-            break;
-        }
-        case 2: {
-            segment_rect.p0 = vec2(corners.tr_outer.x - border.widths.z, corners.tr_inner.y);
-            segment_rect.size = vec2(border.widths.z, corners.br_inner.y - corners.tr_inner.y);
-            vec4 adjusted_widths = get_effective_border_widths(border, int(border.style.z));
-            write_edge_distance(segment_rect.p0.x, border.widths.z, adjusted_widths.z, border.style.z, 0.0, -1.0);
-            style = border.style.z;
-            color_flip = true;
-            write_clip_params(border.style.z,
-                              border.widths.z,
-                              segment_rect.size.y,
-                              segment_rect.p0.y,
-                              segment_rect.p0.x + 0.5 * segment_rect.size.x);
-            break;
-        }
-        case 3: {
-            segment_rect.p0 = vec2(corners.bl_inner.x, corners.bl_outer.y - border.widths.w);
-            segment_rect.size = vec2(corners.br_inner.x - corners.bl_inner.x, border.widths.w);
-            vec4 adjusted_widths = get_effective_border_widths(border, int(border.style.w));
-            write_edge_distance(segment_rect.p0.y, border.widths.w, adjusted_widths.w, border.style.w, 1.0, -1.0);
-            style = border.style.w;
-            color_flip = true;
-            write_clip_params(border.style.w,
-                              border.widths.w,
-                              segment_rect.size.x,
-                              segment_rect.p0.x,
-                              segment_rect.p0.y + 0.5 * segment_rect.size.y);
-            break;
-        }
-    }
-
-    write_alpha_select(style);
-    write_color0(color, style, color_flip);
-    write_color1(color, style, color_flip);
-
-#ifdef WR_FEATURE_TRANSFORM
-    TransformVertexInfo vi = write_transform_vertex(segment_rect,
-                                                    prim.local_clip_rect,
-                                                    prim.z,
-                                                    prim.layer,
-                                                    prim.task,
-                                                    prim.local_rect);
-#else
-    VertexInfo vi = write_vertex(segment_rect,
-                                 prim.local_clip_rect,
-                                 prim.z,
-                                 prim.layer,
-                                 prim.task,
-                                 prim.local_rect);
-#endif
-
-    vLocalPos = vi.local_pos;
-    write_clip(vi.screen_pos, prim.clip_area);
-}
--- a/gfx/webrender/res/ps_box_shadow.fs.glsl
+++ b/gfx/webrender/res/ps_box_shadow.fs.glsl
@@ -13,10 +13,11 @@ void main(void) {
     // shadow corner. This can happen, for example, when
     // drawing the outer parts of an inset box shadow.
     uv = clamp(uv, vec2(0.0), vec2(1.0));
 
     // Map the unit UV to the actual UV rect in the cache.
     uv = mix(vCacheUvRectCoords.xy, vCacheUvRectCoords.zw, uv);
 
     // Modulate the box shadow by the color.
-    oFragColor = clip_scale * dither(vColor * texture(sCacheRGBA8, vec3(uv, vUv.z)));
+    float mask = texture(sColor1, vec3(uv, vUv.z)).r;
+    oFragColor = clip_scale * dither(vColor * vec4(1.0, 1.0, 1.0, mask));
 }
--- a/gfx/webrender/res/ps_box_shadow.vs.glsl
+++ b/gfx/webrender/res/ps_box_shadow.vs.glsl
@@ -22,15 +22,15 @@ void main(void) {
     // Constant offsets to inset from bilinear filtering border.
     vec2 patch_origin = child_task.data0.xy + vec2(1.0);
     vec2 patch_size_device_pixels = child_task.data0.zw - vec2(2.0);
     vec2 patch_size = patch_size_device_pixels / uDevicePixelRatio;
 
     vUv.xy = (vi.local_pos - prim.local_rect.p0) / patch_size;
     vMirrorPoint = 0.5 * prim.local_rect.size / patch_size;
 
-    vec2 texture_size = vec2(textureSize(sCacheRGBA8, 0));
+    vec2 texture_size = vec2(textureSize(sCacheA8, 0));
     vCacheUvRectCoords = vec4(patch_origin, patch_origin + patch_size_device_pixels) / texture_size.xyxy;
 
     vColor = bs.color;
 
     write_clip(vi.screen_pos, prim.clip_area);
 }
--- a/gfx/webrender/src/debug_server.rs
+++ b/gfx/webrender/src/debug_server.rs
@@ -9,28 +9,39 @@ use std::thread;
 use std::sync::mpsc::Sender;
 
 use ws;
 
 // Messages that are sent from the render backend to the renderer
 // debug command queue. These are sent in a separate queue so
 // that none of these types are exposed to the RenderApi interfaces.
 // We can't use select!() as it's not stable...
-pub enum DebugMsg {
-    FetchPasses(ws::Sender),
+enum DebugMsg {
+    AddSender(ws::Sender),
+    RemoveSender(ws::util::Token),
 }
 
 // Represents a connection to a client.
 struct Server {
     ws: ws::Sender,
     debug_tx: Sender<DebugMsg>,
     api_tx: MsgSender<ApiMsg>,
 }
 
 impl ws::Handler for Server {
+    fn on_open(&mut self, _: ws::Handshake) -> ws::Result<()> {
+        self.debug_tx.send(DebugMsg::AddSender(self.ws.clone())).ok();
+
+        Ok(())
+    }
+
+    fn on_close(&mut self, _: ws::CloseCode, _: &str) {
+        self.debug_tx.send(DebugMsg::RemoveSender(self.ws.token())).ok();
+    }
+
     fn on_message(&mut self, msg: ws::Message) -> ws::Result<()> {
         match msg {
             ws::Message::Text(string) => {
                 let cmd = match string.as_str() {
                     "enable_profiler" => {
                         DebugCommand::EnableProfiler(true)
                     }
                     "disable_profiler" => {
@@ -44,19 +55,20 @@ impl ws::Handler for Server {
                     }
                     "enable_render_target_debug" => {
                         DebugCommand::EnableRenderTargetDebug(true)
                     }
                     "disable_render_target_debug" => {
                         DebugCommand::EnableRenderTargetDebug(false)
                     }
                     "fetch_passes" => {
-                        let msg = DebugMsg::FetchPasses(self.ws.clone());
-                        self.debug_tx.send(msg).unwrap();
-                        DebugCommand::Flush
+                        DebugCommand::FetchPasses
+                    }
+                    "fetch_documents" => {
+                        DebugCommand::FetchDocuments
                     }
                     msg => {
                         println!("unknown msg {}", msg);
                         return Ok(());
                     }
                 };
 
                 let msg = ApiMsg::DebugCommand(cmd);
@@ -69,17 +81,18 @@ impl ws::Handler for Server {
     }
 }
 
 // Spawn a thread for a given renderer, and wait for
 // client connections.
 pub struct DebugServer {
     join_handle: Option<thread::JoinHandle<()>>,
     broadcaster: ws::Sender,
-    pub debug_rx: Receiver<DebugMsg>,
+    debug_rx: Receiver<DebugMsg>,
+    senders: Vec<ws::Sender>,
 }
 
 impl DebugServer {
     pub fn new(api_tx: MsgSender<ApiMsg>) -> DebugServer {
         let (debug_tx, debug_rx) = channel();
 
         let socket = ws::Builder::new().build(move |out| {
             Server {
@@ -96,16 +109,52 @@ impl DebugServer {
                 println!("ERROR: Unable to bind debugger websocket (port may be in use).");
             }
         }));
 
         DebugServer {
             join_handle,
             broadcaster,
             debug_rx,
+            senders: Vec::new(),
+        }
+    }
+
+    pub fn send(&mut self, message: String) {
+        // Add any new connections that have been queued.
+        while let Ok(msg) = self.debug_rx.try_recv() {
+            match msg {
+                DebugMsg::AddSender(sender) => {
+                    self.senders.push(sender);
+                }
+                DebugMsg::RemoveSender(token) => {
+                    self.senders.retain(|sender| {
+                        sender.token() != token
+                    });
+                }
+            }
+        }
+
+        // Broadcast the message to all senders. Keep
+        // track of the ones that failed, so they can
+        // be removed from the active sender list.
+        let mut disconnected_senders = Vec::new();
+
+        for (i, sender) in self.senders.iter().enumerate() {
+            if let Err(..) = sender.send(message.clone()) {
+                disconnected_senders.push(i);
+            }
+        }
+
+        // Remove the broken senders from the list
+        // for next broadcast. Remove in reverse
+        // order so the indices are valid for the
+        // entire loop.
+        for i in disconnected_senders.iter().rev() {
+            self.senders.remove(*i);
         }
     }
 }
 
 impl Drop for DebugServer {
     fn drop(&mut self) {
         self.broadcaster.shutdown().ok();
         self.join_handle.take().unwrap().join().ok();
@@ -185,8 +234,50 @@ impl Target {
 }
 
 #[derive(Serialize)]
 struct Batch {
     kind: BatchKind,
     description: String,
     count: usize,
 }
+
+#[derive(Serialize)]
+pub struct TreeNode {
+    description: String,
+    children: Vec<TreeNode>,
+}
+
+impl TreeNode {
+    pub fn new(description: &str) -> TreeNode {
+        TreeNode {
+            description: description.to_owned(),
+            children: Vec::new(),
+        }
+    }
+
+    pub fn add_child(&mut self, child: TreeNode) {
+        self.children.push(child);
+    }
+
+    pub fn add_item(&mut self, description: &str) {
+        self.children.push(TreeNode::new(description));
+    }
+}
+
+#[derive(Serialize)]
+pub struct DocumentList {
+    kind: &'static str,
+    root: TreeNode,
+}
+
+impl DocumentList {
+    pub fn new() -> DocumentList {
+        DocumentList {
+            kind: "documents",
+            root: TreeNode::new("root"),
+        }
+    }
+
+    pub fn add(&mut self, item: TreeNode) {
+        self.root.add_child(item);
+    }
+}
--- a/gfx/webrender/src/device.rs
+++ b/gfx/webrender/src/device.rs
@@ -1167,16 +1167,17 @@ impl Device {
                                   dest_rect.origin.x + dest_rect.size.width,
                                   dest_rect.origin.y + dest_rect.size.height,
                                   gl::COLOR_BUFFER_BIT,
                                   gl::LINEAR);
     }
 
     pub fn free_texture_storage(&mut self, texture: &mut Texture) {
         debug_assert!(self.inside_frame);
+        debug_assert_eq!(self.bound_pbo, PBOId(0));
 
         if texture.format == ImageFormat::Invalid {
             return;
         }
 
         self.bind_texture(DEFAULT_TEXTURE, texture);
 
         let (internal_format, gl_format) = gl_texture_formats_for_image_format(&*self.gl, texture.format);
@@ -1364,27 +1365,27 @@ impl Device {
             self.bound_pbo = pbo_id;
 
             self.gl.bind_buffer(gl::PIXEL_UNPACK_BUFFER, pbo_id.0);
         }
     }
 
     pub fn update_pbo_data<T>(&mut self, data: &[T]) {
         debug_assert!(self.inside_frame);
-        debug_assert!(self.bound_pbo.0 != 0);
+        debug_assert_ne!(self.bound_pbo, PBOId(0));
 
         gl::buffer_data(&*self.gl,
                         gl::PIXEL_UNPACK_BUFFER,
                         data,
                         gl::STREAM_DRAW);
     }
 
     pub fn orphan_pbo(&mut self, new_size: usize) {
         debug_assert!(self.inside_frame);
-        debug_assert!(self.bound_pbo.0 != 0);
+        debug_assert_ne!(self.bound_pbo, PBOId(0));
 
         self.gl.buffer_data_untyped(gl::PIXEL_UNPACK_BUFFER,
                                     new_size as isize,
                                     ptr::null(),
                                     gl::STREAM_DRAW);
     }
 
     pub fn update_texture_from_pbo(&mut self,
--- a/gfx/webrender/src/frame.rs
+++ b/gfx/webrender/src/frame.rs
@@ -90,30 +90,32 @@ impl NestedDisplayListInfo {
         }
         self.convert_id_to_nested(id)
     }
 }
 
 struct FlattenContext<'a> {
     scene: &'a Scene,
     builder: &'a mut FrameBuilder,
+    resource_cache: &'a ResourceCache,
     tiled_image_map: TiledImageMap,
     replacements: Vec<(ClipId, ClipId)>,
     nested_display_list_info: Vec<NestedDisplayListInfo>,
     current_nested_display_list_index: u64,
 }
 
 impl<'a> FlattenContext<'a> {
     fn new(scene: &'a Scene,
            builder: &'a mut FrameBuilder,
-           resource_cache: &ResourceCache)
+           resource_cache: &'a ResourceCache)
            -> FlattenContext<'a> {
         FlattenContext {
             scene,
             builder,
+            resource_cache,
             tiled_image_map: resource_cache.get_tiled_image_map(),
             replacements: Vec::new(),
             nested_display_list_info: Vec::new(),
             current_nested_display_list_index: 0,
         }
     }
 
     fn push_nested_display_list_ids(&mut self, info: ClipAndScrollInfo) {
@@ -560,22 +562,22 @@ impl Frame {
                 context.builder.add_yuv_image(clip_and_scroll,
                                               item_rect_with_offset,
                                               &clip_with_offset,
                                               info.yuv_data,
                                               info.color_space,
                                               info.image_rendering);
             }
             SpecificDisplayItem::Text(ref text_info) => {
+                let instance = context.resource_cache.get_font_instance(text_info.font_key).unwrap();
                 context.builder.add_text(clip_and_scroll,
                                          reference_frame_relative_offset,
                                          item_rect_with_offset,
                                          &clip_with_offset,
-                                         text_info.font_key,
-                                         text_info.size,
+                                         instance,
                                          &text_info.color,
                                          item.glyphs(),
                                          item.display_list().get(item.glyphs()).count(),
                                          text_info.glyph_options);
             }
             SpecificDisplayItem::Rectangle(ref info) => {
                 if !self.try_to_add_rectangle_splitting_on_clip(context,
                                                                 &item_rect_with_offset,
--- a/gfx/webrender/src/frame_builder.rs
+++ b/gfx/webrender/src/frame_builder.rs
@@ -1,15 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{BorderDetails, BorderDisplayItem, BoxShadowClipMode, ClipAndScrollInfo, ClipId, ColorF};
 use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceUintRect, DeviceUintSize};
-use api::{ExtendMode, FontKey, FontRenderMode, GlyphInstance, GlyphOptions, GradientStop};
+use api::{ExtendMode, FontInstance, FontRenderMode};
+use api::{GlyphInstance, GlyphOptions, GradientStop};
 use api::{ImageKey, ImageRendering, ItemRange, LayerPoint, LayerRect, LayerSize};
 use api::{LayerToScrollTransform, LayerVector2D, LayoutVector2D, LineOrientation, LineStyle};
 use api::{LocalClip, PipelineId, RepeatMode, ScrollSensitivity, SubpixelDirection, TextShadow};
 use api::{TileOffset, TransformStyle, WorldPixel, YuvColorSpace, YuvData};
 use app_units::Au;
 use frame::FrameId;
 use gpu_cache::GpuCache;
 use internal_types::{FastHashMap, HardwareCompositeOp};
@@ -870,34 +871,47 @@ impl FrameBuilder {
                            PrimitiveContainer::RadialGradient(radial_gradient_cpu));
     }
 
     pub fn add_text(&mut self,
                     clip_and_scroll: ClipAndScrollInfo,
                     run_offset: LayoutVector2D,
                     rect: LayerRect,
                     local_clip: &LocalClip,
-                    font_key: FontKey,
-                    size: Au,
+                    font: &FontInstance,
                     color: &ColorF,
                     glyph_range: ItemRange<GlyphInstance>,
                     glyph_count: usize,
                     glyph_options: Option<GlyphOptions>) {
         // Trivial early out checks
-        if size.0 <= 0 {
+        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
+        // the font_advance.html reftest, which creates a very large
+        // font as a crash test - the rendering is also ignored
+        // by the azure renderer.
+        if font.size >= Au::from_px(4096) {
             return
         }
 
         // TODO(gw): Use a proper algorithm to select
         // whether this item should be rendered with
         // subpixel AA!
-        let mut normal_render_mode = self.config.default_font_render_mode;
+        let mut default_render_mode = self.config.default_font_render_mode.limit_by(font.render_mode);
+        if let Some(options) = glyph_options {
+            default_render_mode = default_render_mode.limit_by(options.render_mode);
+        }
 
         // There are some conditions under which we can't use
         // subpixel text rendering, even if enabled.
+        let mut normal_render_mode = default_render_mode;
         if normal_render_mode == FontRenderMode::Subpixel {
             if color.a != 1.0 {
                 normal_render_mode = FontRenderMode::Alpha;
             }
 
             // text on a stacking context that has filters
             // (e.g. opacity) can't use sub-pixel.
             // TODO(gw): It's possible we can relax this in
@@ -908,56 +922,59 @@ impl FrameBuilder {
                 if stacking_context.composite_ops.count() > 0 {
                     normal_render_mode = FontRenderMode::Alpha;
                 }
             }
         }
 
         // Shadows never use subpixel AA, but need to respect the alpha/mono flag
         // for reftests.
-        let (shadow_render_mode, subpx_dir) = match self.config.default_font_render_mode {
+        let (shadow_render_mode, subpx_dir) = match default_render_mode {
             FontRenderMode::Subpixel | FontRenderMode::Alpha => {
                 // TODO(gw): Expose subpixel direction in API once WR supports
                 //           vertical text runs.
-                (FontRenderMode::Alpha, SubpixelDirection::Horizontal)
+                (FontRenderMode::Alpha, font.subpx_dir)
             }
             FontRenderMode::Mono => {
                 (FontRenderMode::Mono, SubpixelDirection::None)
             }
         };
 
+        let prim_font = FontInstance::new(font.font_key,
+                                          font.size,
+                                          *color,
+                                          normal_render_mode,
+                                          subpx_dir,
+                                          font.platform_options);
         let prim = TextRunPrimitiveCpu {
-            font_key,
-            logical_font_size: size,
+            font: prim_font,
             glyph_range,
             glyph_count,
             glyph_gpu_blocks: Vec::new(),
             glyph_keys: Vec::new(),
-            glyph_options,
-            normal_render_mode,
             shadow_render_mode,
             offset: run_offset,
             color: *color,
-            subpx_dir,
         };
 
         // Text shadows that have a blur radius of 0 need to be rendered as normal
         // text elements to get pixel perfect results for reftests. It's also a big
         // performance win to avoid blurs and render target allocations where
         // possible. For any text shadows that have zero blur, create a normal text
         // 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_text_shadow_prims = Vec::new();
         for shadow_prim_index in &self.shadow_prim_stack {
             let shadow_metadata = &self.prim_store.cpu_metadata[shadow_prim_index.0];
             let shadow_prim = &self.prim_store.cpu_text_shadows[shadow_metadata.cpu_prim_index.0];
             if shadow_prim.shadow.blur_radius == 0.0 {
                 let mut text_prim = prim.clone();
+                text_prim.font.color = shadow_prim.shadow.color.into();
                 text_prim.color = shadow_prim.shadow.color;
                 text_prim.offset += shadow_prim.shadow.offset;
                 fast_text_shadow_prims.push(text_prim);
             }
         }
         for text_prim in fast_text_shadow_prims {
             self.add_primitive(clip_and_scroll,
                                &rect.translate(&text_prim.offset),
--- a/gfx/webrender/src/glyph_rasterizer.rs
+++ b/gfx/webrender/src/glyph_rasterizer.rs
@@ -373,24 +373,22 @@ fn raterize_200_glyphs() {
 
     let mut font_file = File::open("../wrench/reftests/text/VeraBd.ttf").expect("Couldn't open font file");
     let mut font_data = vec![];
     font_file.read_to_end(&mut font_data).expect("failed to read font file");
 
     let font_key = FontKey::new(IdNamespace(0), 0);
     glyph_rasterizer.add_font(font_key, FontTemplate::Raw(Arc::new(font_data), 0));
 
-    let font = FontInstance {
-        font_key,
-        color: ColorF::new(0.0, 0.0, 0.0, 1.0).into(),
-        size: Au::from_px(32),
-        render_mode: FontRenderMode::Subpixel,
-        glyph_options: None,
-        subpx_dir: SubpixelDirection::Horizontal,
-    };
+    let font = FontInstance::new(font_key,
+                                 Au::from_px(32),
+                                 ColorF::new(0.0, 0.0, 0.0, 1.0),
+                                 FontRenderMode::Subpixel,
+                                 SubpixelDirection::Horizontal,
+                                 None);
 
     let mut glyph_keys = Vec::with_capacity(200);
     for i in 0..200 {
         glyph_keys.push(GlyphKey::new(i, LayoutPoint::zero(), font.render_mode, font.subpx_dir));
     }
 
     for i in 0..4 {
         glyph_rasterizer.request_glyphs(
--- a/gfx/webrender/src/internal_types.rs
+++ b/gfx/webrender/src/internal_types.rs
@@ -64,17 +64,17 @@ impl BatchTextures {
             colors: [SourceTexture::Invalid; 3],
         }
     }
 
     pub fn render_target_cache() -> Self {
         BatchTextures {
             colors: [
                 SourceTexture::CacheRGBA8,
-                SourceTexture::Invalid,
+                SourceTexture::CacheA8,
                 SourceTexture::Invalid,
             ]
         }
     }
 }
 
 #[derive(Copy, Clone, Debug, PartialEq)]
 pub enum RenderTargetMode {
@@ -151,18 +151,23 @@ impl RendererFrame {
         RendererFrame {
             pipeline_epoch_map,
             layers_bouncing_back,
             frame,
         }
     }
 }
 
+pub enum DebugOutput {
+    FetchDocuments(String),
+}
+
 pub enum ResultMsg {
     DebugCommand(DebugCommand),
+    DebugOutput(DebugOutput),
     RefreshShader(PathBuf),
     NewFrame(DocumentId, RendererFrame, TextureUpdateList, BackendProfileCounters),
     UpdateResources { updates: TextureUpdateList, cancel_rendering: bool },
 }
 
 #[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
 pub struct StackingContextIndex(pub usize);
 
--- a/gfx/webrender/src/platform/windows/font.rs
+++ b/gfx/webrender/src/platform/windows/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::{FontKey, FontRenderMode, GlyphDimensions};
-use api::{FontInstance, GlyphKey, GlyphOptions, SubpixelDirection};
+use api::{FontInstance, FontInstancePlatformOptions, FontKey, FontRenderMode};
+use api::{GlyphDimensions, GlyphKey, GlyphOptions, SubpixelDirection};
 use gamma_lut::{GammaLut, Color as ColorLut};
 use internal_types::FastHashMap;
 
 use dwrote;
 use std::sync::Arc;
 
 lazy_static! {
     static ref DEFAULT_FONT_DESCRIPTOR: dwrote::FontDescriptor = dwrote::FontDescriptor {
@@ -41,36 +41,36 @@ fn dwrite_texture_type(render_mode: Font
                        dwrote::DWRITE_TEXTURE_TYPE {
     match render_mode {
         FontRenderMode::Mono => dwrote::DWRITE_TEXTURE_ALIASED_1x1 ,
         FontRenderMode::Alpha |
         FontRenderMode::Subpixel => dwrote::DWRITE_TEXTURE_CLEARTYPE_3x1,
     }
 }
 
-fn dwrite_measure_mode(render_mode: FontRenderMode, options: Option<GlyphOptions>) ->
+fn dwrite_measure_mode(render_mode: FontRenderMode, options: Option<FontInstancePlatformOptions>) ->
                        dwrote::DWRITE_MEASURING_MODE {
-    if let Some(GlyphOptions{ force_gdi_rendering: true, .. }) = options {
+    if let Some(FontInstancePlatformOptions{ force_gdi_rendering: true, .. }) = options {
         return dwrote::DWRITE_MEASURING_MODE_GDI_CLASSIC;
     }
 
     match render_mode {
         FontRenderMode::Mono => dwrote::DWRITE_MEASURING_MODE_GDI_NATURAL,
         FontRenderMode::Alpha |
         FontRenderMode::Subpixel => dwrote::DWRITE_MEASURING_MODE_NATURAL,
     }
 }
 
 fn dwrite_render_mode(font_face: &dwrote::FontFace,
                       render_mode: FontRenderMode,
                       em_size: f32,
                       measure_mode: dwrote::DWRITE_MEASURING_MODE,
-                      options: Option<GlyphOptions>) ->
+                      options: Option<FontInstancePlatformOptions>) ->
                       dwrote::DWRITE_RENDERING_MODE {
-    if let Some(GlyphOptions{ force_gdi_rendering: true, .. }) = options {
+    if let Some(FontInstancePlatformOptions{ force_gdi_rendering: true, .. }) = options {
         return dwrote::DWRITE_RENDERING_MODE_GDI_CLASSIC;
     }
 
     let dwrite_render_mode = match render_mode {
         FontRenderMode::Mono => dwrote::DWRITE_RENDERING_MODE_ALIASED,
         FontRenderMode::Alpha |
         FontRenderMode::Subpixel => {
             font_face.get_recommended_rendering_mode_default_params(em_size,
@@ -170,22 +170,22 @@ impl FontContext {
             glyphIndices: &glyph,
             glyphAdvances: &advance,
             glyphOffsets: &offset,
             isSideways: 0,
             bidiLevel: 0,
         };
 
         let dwrite_measure_mode = dwrite_measure_mode(font.render_mode,
-                                                      font.glyph_options);
+                                                      font.platform_options);
         let dwrite_render_mode = dwrite_render_mode(face,
                                                     font.render_mode,
                                                     font.size.to_f32_px(),
                                                     dwrite_measure_mode,
-                                                    font.glyph_options);
+                                                    font.platform_options);
 
         let (x_offset, y_offset) = font.get_subpx_offset(key);
         let transform = Some(
                         dwrote::DWRITE_MATRIX { m11: 1.0, m12: 0.0, m21: 0.0, m22: 1.0,
                                                 dx: x_offset as f32, dy: y_offset as f32 }
                         );
 
         dwrote::GlyphRunAnalysis::create(&glyph_run, 1.0, transform,
@@ -297,17 +297,17 @@ impl FontContext {
         // Such as for spaces
         if width == 0 || height == 0 {
             return None;
         }
 
         let mut pixels = analysis.create_alpha_texture(texture_type, bounds);
 
         if font.render_mode != FontRenderMode::Mono {
-            let lut_correction = match font.glyph_options {
+            let lut_correction = match font.platform_options {
                 Some(option) => {
                     if option.force_gdi_rendering {
                         &self.gdi_gamma_lut
                     } else {
                         &self.gamma_lut
                     }
                 },
                 None => &self.gamma_lut
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.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::{BuiltDisplayList, ColorF, ComplexClipRegion, DeviceIntRect, DeviceIntSize, DevicePoint};
-use api::{ExtendMode, FontKey, FontRenderMode, GlyphInstance, GlyphOptions, GradientStop};
+use api::{ExtendMode, FontRenderMode, GlyphInstance, GradientStop};
 use api::{ImageKey, ImageRendering, ItemRange, LayerPoint, LayerRect, LayerSize, TextShadow};
 use api::{GlyphKey, LayerToWorldTransform, TileOffset, YuvColorSpace, YuvFormat};
-use api::{device_length, FontInstance, LayerVector2D, LineOrientation, LineStyle, SubpixelDirection};
+use api::{device_length, FontInstance, LayerVector2D, LineOrientation, LineStyle};
 use app_units::Au;
 use border::BorderCornerInstance;
 use euclid::{Size2D};
 use gpu_cache::{GpuCacheAddress, GpuBlockData, GpuCache, GpuCacheHandle, GpuDataRequest, ToGpuBlocks};
 use mask_cache::{ClipMode, ClipRegion, ClipSource, MaskCacheInfo};
 use renderer::MAX_VERTEX_TEXTURE_WIDTH;
 use render_task::{RenderTask, RenderTaskId, RenderTaskTree};
 use resource_cache::{ImageProperties, ResourceCache};
@@ -499,55 +499,51 @@ pub struct TextDecoration {
 #[derive(Debug, Clone)]
 pub struct TextShadowPrimitiveCpu {
     pub shadow: TextShadow,
     pub primitives: Vec<PrimitiveIndex>,
 }
 
 #[derive(Debug, Clone)]
 pub struct TextRunPrimitiveCpu {
-    pub font_key: FontKey,
+    pub font: FontInstance,
     pub offset: LayerVector2D,
-    pub logical_font_size: Au,
     pub glyph_range: ItemRange<GlyphInstance>,
     pub glyph_count: usize,
     pub glyph_keys: Vec<GlyphKey>,
     pub glyph_gpu_blocks: Vec<GpuBlockData>,
-    pub glyph_options: Option<GlyphOptions>,
-    pub normal_render_mode: FontRenderMode,
     pub shadow_render_mode: FontRenderMode,
     pub color: ColorF,
-    pub subpx_dir: SubpixelDirection,
 }
 
-#[derive(Debug, Copy, Clone)]
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
 pub enum TextRunMode {
     Normal,
     Shadow,
 }
 
 impl TextRunPrimitiveCpu {
     fn prepare_for_render(&mut self,
                           resource_cache: &mut ResourceCache,
                           device_pixel_ratio: f32,
                           display_list: &BuiltDisplayList,
                           run_mode: TextRunMode,
                           gpu_cache: &mut GpuCache) {
-        let font_size_dp = self.logical_font_size.scale_by(device_pixel_ratio);
-        let render_mode = match run_mode {
-            TextRunMode::Normal => self.normal_render_mode,
-            TextRunMode::Shadow => self.shadow_render_mode,
-        };
+        let mut font = self.font.clone();
+        font.size = font.size.scale_by(device_pixel_ratio);
+        match run_mode {
+            TextRunMode::Shadow => {
+                font.render_mode = self.shadow_render_mode;
+            }
+            TextRunMode::Normal => {}
+        }
 
-        let font = FontInstance::new(self.font_key,
-                                     font_size_dp,
-                                     self.color,
-                                     render_mode,
-                                     self.glyph_options,
-                                     self.subpx_dir);
+        if run_mode == TextRunMode::Shadow {
+            font.render_mode = self.shadow_render_mode;
+        }
 
         // Cache the glyph positions, if not in the cache already.
         // TODO(gw): In the future, remove `glyph_instances`
         //           completely, and just reference the glyphs
         //           directly from the display list.
         if self.glyph_keys.is_empty() {
             let src_glyphs = display_list.get(self.glyph_range);
 
@@ -585,17 +581,17 @@ impl TextRunPrimitiveCpu {
         resource_cache.request_glyphs(font, &self.glyph_keys, gpu_cache);
     }
 
     fn write_gpu_blocks(&self,
                         request: &mut GpuDataRequest) {
         request.push(self.color);
         request.push([self.offset.x,
                       self.offset.y,
-                      self.subpx_dir as u32 as f32,
+                      self.font.subpx_dir as u32 as f32,
                       0.0]);
         request.extend_from_slice(&self.glyph_gpu_blocks);
 
         assert!(request.current_used_block_num() <= MAX_VERTEX_TEXTURE_WIDTH);
     }
 }
 
 #[derive(Debug, Clone)]
--- a/gfx/webrender/src/profiler.rs
+++ b/gfx/webrender/src/profiler.rs
@@ -126,16 +126,21 @@ impl ResourceProfileCounter {
         self.size = 0;
     }
 
     #[inline(always)]
     pub fn inc(&mut self, size: usize) {
         self.value += 1;
         self.size += size;
     }
+
+    pub fn set(&mut self, count: usize, size: usize) {
+        self.value = count;
+        self.size = size;
+    }
 }
 
 impl ProfileCounter for ResourceProfileCounter {
     fn description(&self) -> &'static str {
         self.description
     }
 
     fn value(&self) -> String {
--- a/gfx/webrender/src/render_backend.rs
+++ b/gfx/webrender/src/render_backend.rs
@@ -1,32 +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/. */
 
+#[cfg(feature = "debugger")]
+use debug_server;
 use frame::Frame;
 use frame_builder::FrameBuilderConfig;
 use gpu_cache::GpuCache;
-use internal_types::{FastHashMap, ResultMsg, RendererFrame};
+use internal_types::{DebugOutput, FastHashMap, ResultMsg, RendererFrame};
 use profiler::{BackendProfileCounters, ResourceProfileCounters};
 use record::ApiRecordingReceiver;
 use resource_cache::ResourceCache;
 use scene::Scene;
+#[cfg(feature = "debugger")]
+use serde_json;
 use std::sync::{Arc, Mutex};
 use std::sync::mpsc::Sender;
 use std::u32;
 use texture_cache::TextureCache;
 use time::precise_time_ns;
 use thread_profiler::register_thread_with_profiler;
 use rayon::ThreadPool;
 use api::channel::{MsgReceiver, PayloadReceiver, PayloadReceiverHelperMethods};
 use api::channel::{PayloadSender, PayloadSenderHelperMethods};
-use api::{ApiMsg, BlobImageRenderer, BuiltDisplayList, DeviceIntPoint};
+use api::{ApiMsg, DebugCommand, BlobImageRenderer, BuiltDisplayList, DeviceIntPoint};
 use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize, DocumentId, DocumentMsg};
 use api::{IdNamespace, LayerPoint, RenderNotifier};
+#[cfg(feature = "debugger")]
+use api::{BuiltDisplayListIter, SpecificDisplayItem};
 
 struct Document {
     scene: Scene,
     frame: Frame,
     window_size: DeviceUintSize,
     inner_rect: DeviceUintRect,
     pan: DeviceIntPoint,
     page_zoom_factor: f32,
@@ -270,16 +276,22 @@ impl RenderBackend {
                 if doc.scene.display_lists.get(&pipeline_id).is_some() {
                     let _timer = profile_counters.total_time.timer();
                     doc.build_scene(&mut self.resource_cache, self.hidpi_factor);
                     DocumentOp::Built
                 } else {
                     DocumentOp::Nop
                 }
             }
+            DocumentMsg::RemovePipeline(pipeline_id) => {
+                profile_scope!("RemovePipeline");
+
+                doc.scene.remove_pipeline(pipeline_id);
+                DocumentOp::Nop
+            }
             DocumentMsg::Scroll(delta, cursor, move_phase) => {
                 profile_scope!("Scroll");
                 let _timer = profile_counters.total_time.timer();
 
                 if doc.frame.scroll(delta, cursor, move_phase) && doc.render_on_scroll == Some(true) {
                     let frame = doc.render(&mut self.resource_cache,
                                            &mut self.gpu_cache,
                                            &mut profile_counters.resources,
@@ -457,17 +469,25 @@ impl RenderBackend {
                     let msg = ResultMsg::UpdateResources { updates: pending_update, cancel_rendering: true };
                     self.result_tx.send(msg).unwrap();
                     // We use new_frame_ready to wake up the renderer and get the
                     // resource updates processed, but the UpdateResources message
                     // will cancel rendering the frame.
                     self.notifier.lock().unwrap().as_mut().unwrap().new_frame_ready();
                 }
                 ApiMsg::DebugCommand(option) => {
-                    let msg = ResultMsg::DebugCommand(option);
+                    let msg = match option {
+                        DebugCommand::FetchDocuments => {
+                            let json = self.get_docs_for_debugger();
+                            ResultMsg::DebugOutput(DebugOutput::FetchDocuments(json))
+                        }
+                        _ => {
+                            ResultMsg::DebugCommand(option)
+                        }
+                    };
                     self.result_tx.send(msg).unwrap();
                     let notifier = self.notifier.lock();
                     notifier.unwrap()
                             .as_mut()
                             .unwrap()
                             .new_frame_ready();
                 }
                 ApiMsg::ShutDown => {
@@ -509,9 +529,144 @@ impl RenderBackend {
     fn notify_compositor_of_new_scroll_frame(&mut self, composite_needed: bool) {
         // TODO(gw): This is kindof bogus to have to lock the notifier
         //           each time it's used. This is due to some nastiness
         //           in initialization order for Servo. Perhaps find a
         //           cleaner way to do this, or use the OnceMutex on crates.io?
         let mut notifier = self.notifier.lock();
         notifier.as_mut().unwrap().as_mut().unwrap().new_scroll_frame_ready(composite_needed);
     }
+
+
+    #[cfg(not(feature = "debugger"))]
+    fn get_docs_for_debugger(&self) -> String {
+        String::new()
+    }
+
+    #[cfg(feature = "debugger")]
+    fn traverse_items<'a>(&self,
+                          traversal: &mut BuiltDisplayListIter<'a>,
+                          node: &mut debug_server::TreeNode) {
+        loop {
+            let subtraversal = {
+                let item = match traversal.next() {
+                    Some(item) => item,
+                    None => break,
+                };
+
+                match *item.item() {
+                    display_item @ SpecificDisplayItem::PushStackingContext(..) => {
+                        let mut subtraversal = item.sub_iter();
+                        let mut child_node = debug_server::TreeNode::new(&display_item.debug_string());
+                        self.traverse_items(&mut subtraversal, &mut child_node);
+                        node.add_child(child_node);
+                        Some(subtraversal)
+                    }
+                    SpecificDisplayItem::PopStackingContext => {
+                        return;
+                    }
+                    display_item => {
+                        node.add_item(&display_item.debug_string());
+                        None
+                    }
+                }
+            };
+
+            // If flatten_item created a sub-traversal, we need `traversal` to have the
+            // same state as the completed subtraversal, so we reinitialize it here.
+            if let Some(subtraversal) = subtraversal {
+                *traversal = subtraversal;
+            }
+        }
+    }
+
+    #[cfg(feature = "debugger")]
+    fn get_docs_for_debugger(&self) -> String {
+        let mut docs = debug_server::DocumentList::new();
+
+        for (_, doc) in &self.documents {
+            let mut debug_doc = debug_server::TreeNode::new("document");
+
+            for (_, display_list) in &doc.scene.display_lists {
+                let mut debug_dl = debug_server::TreeNode::new("display_list");
+                self.traverse_items(&mut display_list.iter(), &mut debug_dl);
+                debug_doc.add_child(debug_dl);
+            }
+
+            docs.add(debug_doc);
+        }
+
+        serde_json::to_string(&docs).unwrap()
+    }
 }
+
+#[cfg(feature = "debugger")]
+trait ToDebugString {
+    fn debug_string(&self) -> String;
+}
+
+#[cfg(feature = "debugger")]
+impl ToDebugString for SpecificDisplayItem {
+    fn debug_string(&self) -> String {
+        match *self {
+            SpecificDisplayItem::Image(..) => {
+                String::from("image")
+            }
+            SpecificDisplayItem::YuvImage(..) => {
+                String::from("yuv_image")
+            }
+            SpecificDisplayItem::Text(..) => {
+                String::from("text")
+            }
+            SpecificDisplayItem::Rectangle(..) => {
+                String::from("rectangle")
+            }
+            SpecificDisplayItem::Line(..) => {
+                String::from("line")
+            }
+            SpecificDisplayItem::Gradient(..) => {
+                String::from("gradient")
+            }
+            SpecificDisplayItem::RadialGradient(..) => {
+                String::from("radial_gradient")
+            }
+            SpecificDisplayItem::BoxShadow(..) => {
+                String::from("box_shadow")
+            }
+            SpecificDisplayItem::Border(..) => {
+                String::from("border")
+            }
+            SpecificDisplayItem::PushStackingContext(..) => {
+                String::from("push_stacking_context")
+            }
+            SpecificDisplayItem::Iframe(..) => {
+                String::from("iframe")
+            }
+            SpecificDisplayItem::Clip(..) => {
+                String::from("clip")
+            }
+            SpecificDisplayItem::ScrollFrame(..) => {
+                String::from("scroll_frame")
+            }
+            SpecificDisplayItem::StickyFrame(..) => {
+                String::from("sticky_frame")
+            }
+            SpecificDisplayItem::PushNestedDisplayList => {
+                String::from("push_nested_display_list")
+            }
+            SpecificDisplayItem::PopNestedDisplayList => {
+                String::from("pop_nested_display_list")
+            }
+            SpecificDisplayItem::SetGradientStops => {
+                String::from("set_gradient_stops")
+            }
+            SpecificDisplayItem::PopStackingContext => {
+                String::from("pop_stacking_context")
+            }
+            SpecificDisplayItem::PushTextShadow(..) => {
+                String::from("push_text_shadow")
+            }
+            SpecificDisplayItem::PopTextShadow => {
+                String::from("pop_text_shadow")
+            }
+        }
+    }
+}
--- a/gfx/webrender/src/render_task.rs
+++ b/gfx/webrender/src/render_task.rs
@@ -161,16 +161,17 @@ pub struct CacheMaskTask {
 pub struct RenderTaskData {
     pub data: [f32; FLOATS_PER_RENDER_TASK_INFO],
 }
 
 #[derive(Debug, Clone)]
 pub enum RenderTaskKind {
     Alpha(AlphaRenderTask),
     CachePrimitive(PrimitiveIndex),
+    BoxShadow(PrimitiveIndex),
     CacheMask(CacheMaskTask),
     VerticalBlur(DeviceIntLength),
     HorizontalBlur(DeviceIntLength),
     Readback(DeviceIntRect),
     Alias(RenderTaskId),
 }
 
 #[derive(Debug, Clone)]
@@ -212,17 +213,17 @@ impl RenderTask {
 
     pub fn new_box_shadow(key: BoxShadowPrimitiveCacheKey,
                           size: DeviceIntSize,
                           prim_index: PrimitiveIndex) -> RenderTask {
         RenderTask {
             cache_key: Some(RenderTaskKey::BoxShadow(key)),
             children: Vec::new(),
             location: RenderTaskLocation::Dynamic(None, size),
-            kind: RenderTaskKind::CachePrimitive(prim_index),
+            kind: RenderTaskKind::BoxShadow(prim_index),
         }
     }
 
     pub fn new_readback(screen_rect: DeviceIntRect) -> RenderTask {
         RenderTask {
             cache_key: None,
             children: Vec::new(),
             location: RenderTaskLocation::Dynamic(None, screen_rect.size),
@@ -339,28 +340,30 @@ impl RenderTask {
 
         blur_task_h
     }
 
     pub fn as_alpha_batch_mut<'a>(&'a mut self) -> &'a mut AlphaRenderTask {
         match self.kind {
             RenderTaskKind::Alpha(ref mut task) => task,
             RenderTaskKind::CachePrimitive(..) |
+            RenderTaskKind::BoxShadow(..) |
             RenderTaskKind::CacheMask(..) |
             RenderTaskKind::VerticalBlur(..) |
             RenderTaskKind::Readback(..) |
             RenderTaskKind::HorizontalBlur(..) |
             RenderTaskKind::Alias(..) => unreachable!(),
         }
     }
 
     pub fn as_alpha_batch<'a>(&'a self) -> &'a AlphaRenderTask {
         match self.kind {
             RenderTaskKind::Alpha(ref task) => task,
             RenderTaskKind::CachePrimitive(..) |
+            RenderTaskKind::BoxShadow(..) |
             RenderTaskKind::CacheMask(..) |
             RenderTaskKind::VerticalBlur(..) |
             RenderTaskKind::Readback(..) |
             RenderTaskKind::HorizontalBlur(..) |
             RenderTaskKind::Alias(..) => unreachable!(),
         }
     }
 
@@ -391,17 +394,18 @@ impl RenderTask {
                         0.0,
                         0.0,
                         0.0,
                         0.0,
                         0.0,
                     ],
                 }
             }
-            RenderTaskKind::CachePrimitive(..) => {
+            RenderTaskKind::CachePrimitive(..) |
+            RenderTaskKind::BoxShadow(..) => {
                 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,
@@ -495,17 +499,20 @@ impl RenderTask {
 
     pub fn target_kind(&self) -> RenderTargetKind {
         match self.kind {
             RenderTaskKind::Alpha(..) |
             RenderTaskKind::CachePrimitive(..) |
             RenderTaskKind::VerticalBlur(..) |
             RenderTaskKind::Readback(..) |
             RenderTaskKind::HorizontalBlur(..) => RenderTargetKind::Color,
-            RenderTaskKind::CacheMask(..) => RenderTargetKind::Alpha,
+
+            RenderTaskKind::CacheMask(..) |
+            RenderTaskKind::BoxShadow(..) => RenderTargetKind::Alpha,
+
             RenderTaskKind::Alias(..) => {
                 panic!("BUG: target_kind() called on invalidated task");
             }
         }
     }
 
     pub fn set_alias(&mut self, id: RenderTaskId) {
         debug_assert!(self.cache_key.is_some());
--- a/gfx/webrender/src/renderer.rs
+++ b/gfx/webrender/src/renderer.rs
@@ -12,26 +12,26 @@
 #[cfg(not(feature = "debugger"))]
 use api::ApiMsg;
 #[cfg(not(feature = "debugger"))]
 use api::channel::MsgSender;
 use api::DebugCommand;
 use debug_colors;
 use debug_render::DebugRenderer;
 #[cfg(feature = "debugger")]
-use debug_server::{self, DebugMsg, DebugServer};
+use debug_server::{self, DebugServer};
 use device::{DepthFunction, Device, FrameId, Program, Texture, VertexDescriptor, GpuMarker, GpuProfiler, PBOId};
 use device::{GpuTimer, TextureFilter, VAO, VertexUsageHint, FileWatcherHandler, TextureTarget, ShaderError};
 use device::{ExternalTexture, get_gl_format_bgra, TextureSlot, VertexAttribute, VertexAttributeKind};
 use euclid::{Transform3D, rect};
 use frame_builder::FrameBuilderConfig;
 use gleam::gl;
 use gpu_cache::{GpuBlockData, GpuCacheUpdate, GpuCacheUpdateList};
 use internal_types::{FastHashMap, CacheTextureId, RendererFrame, ResultMsg, TextureUpdateOp};
-use internal_types::{TextureUpdateList, RenderTargetMode, TextureUpdateSource};
+use internal_types::{DebugOutput, TextureUpdateList, RenderTargetMode, TextureUpdateSource};
 use internal_types::{BatchTextures, ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE, SourceTexture};
 use profiler::{Profiler, BackendProfileCounters};
 use profiler::{GpuProfileTag, RendererProfileTimers, RendererProfileCounters};
 use record::ApiRecordingReceiver;
 use render_backend::RenderBackend;
 use render_task::RenderTaskTree;
 #[cfg(feature = "debugger")]
 use serde_json;
@@ -1582,90 +1582,91 @@ impl Renderer {
                     // pressure event.
                     if cancel_rendering {
                         self.current_frame = None;
                     }
                 }
                 ResultMsg::RefreshShader(path) => {
                     self.pending_shader_updates.push(path);
                 }
+                ResultMsg::DebugOutput(output) => {
+                    match output {
+                        DebugOutput::FetchDocuments(string) => {
+                            self.debug_server.send(string);
+                        }
+                    }
+                }
                 ResultMsg::DebugCommand(command) => {
                     self.handle_debug_command(command);
                 }
             }
         }
     }
 
     #[cfg(not(feature = "debugger"))]
-    fn update_debug_server(&self) {
+    fn get_passes_for_debugger(&self) -> String {
         // Avoid unused param warning.
         let _ = &self.debug_server;
+        String::new()
     }
 
     #[cfg(feature = "debugger")]
-    fn update_debug_server(&self) {
-        while let Ok(msg) = self.debug_server.debug_rx.try_recv() {
-            match msg {
-                DebugMsg::FetchPasses(sender) => {
-                    let mut debug_passes = debug_server::PassList::new();
+    fn get_passes_for_debugger(&self) -> String {
+        let mut debug_passes = debug_server::PassList::new();
 
-                    if let Some(frame) = self.current_frame.as_ref().and_then(|frame| frame.frame.as_ref()) {
-                        for pass in &frame.passes {
-                            let mut debug_pass = debug_server::Pass::new();
-
-                            for target in &pass.alpha_targets.targets {
-                                let mut debug_target = debug_server::Target::new("A8");
+        if let Some(frame) = self.current_frame.as_ref().and_then(|frame| frame.frame.as_ref()) {
+            for pass in &frame.passes {
+                let mut debug_pass = debug_server::Pass::new();
 
-                                debug_target.add(debug_server::BatchKind::Clip, "Clear", target.clip_batcher.border_clears.len());
-                                debug_target.add(debug_server::BatchKind::Clip, "Borders", target.clip_batcher.borders.len());
-                                debug_target.add(debug_server::BatchKind::Clip, "Rectangles", target.clip_batcher.rectangles.len());
-                                for (_, items) in target.clip_batcher.images.iter() {
-                                    debug_target.add(debug_server::BatchKind::Clip, "Image mask", items.len());
-                                }
+                for target in &pass.alpha_targets.targets {
+                    let mut debug_target = debug_server::Target::new("A8");
 
-                                debug_pass.add(debug_target);
-                            }
-
-                            for target in &pass.color_targets.targets {
-                                let mut debug_target = debug_server::Target::new("RGBA8");
+                    debug_target.add(debug_server::BatchKind::Clip, "Clear", target.clip_batcher.border_clears.len());
+                    debug_target.add(debug_server::BatchKind::Clip, "Borders", target.clip_batcher.borders.len());
+                    debug_target.add(debug_server::BatchKind::Clip, "Rectangles", target.clip_batcher.rectangles.len());
+                    for (_, items) in target.clip_batcher.images.iter() {
+                        debug_target.add(debug_server::BatchKind::Clip, "Image mask", items.len());
+                    }
+                    debug_target.add(debug_server::BatchKind::Cache, "Box Shadow", target.box_shadow_cache_prims.len());
 
-                                debug_target.add(debug_server::BatchKind::Cache, "Vertical Blur", target.vertical_blurs.len());
-                                debug_target.add(debug_server::BatchKind::Cache, "Horizontal Blur", target.horizontal_blurs.len());
-                                debug_target.add(debug_server::BatchKind::Cache, "Box Shadow", target.box_shadow_cache_prims.len());
-                                debug_target.add(debug_server::BatchKind::Cache, "Text Shadow", target.text_run_cache_prims.len());
-                                debug_target.add(debug_server::BatchKind::Cache, "Lines", target.line_cache_prims.len());
+                    debug_pass.add(debug_target);
+                }
+
+                for target in &pass.color_targets.targets {
+                    let mut debug_target = debug_server::Target::new("RGBA8");
 
-                                for batch in target.alpha_batcher
-                                                   .batch_list
-                                                   .opaque_batch_list
-                                                   .batches
-                                                   .iter()
-                                                   .rev() {
-                                    debug_target.add(debug_server::BatchKind::Opaque, batch.key.kind.debug_name(), batch.instances.len());
-                                }
+                    debug_target.add(debug_server::BatchKind::Cache, "Vertical Blur", target.vertical_blurs.len());
+                    debug_target.add(debug_server::BatchKind::Cache, "Horizontal Blur", target.horizontal_blurs.len());
+                    debug_target.add(debug_server::BatchKind::Cache, "Text Shadow", target.text_run_cache_prims.len());
+                    debug_target.add(debug_server::BatchKind::Cache, "Lines", target.line_cache_prims.len());
 
-                                for batch in &target.alpha_batcher
-                                                    .batch_list
-                                                    .alpha_batch_list
-                                                    .batches {
-                                    debug_target.add(debug_server::BatchKind::Alpha, batch.key.kind.debug_name(), batch.instances.len());
-                                }
-
-                                debug_pass.add(debug_target);
-                            }
-
-                            debug_passes.add(debug_pass);
-                        }
+                    for batch in target.alpha_batcher
+                                       .batch_list
+                                       .opaque_batch_list
+                                       .batches
+                                       .iter()
+                                       .rev() {
+                        debug_target.add(debug_server::BatchKind::Opaque, batch.key.kind.debug_name(), batch.instances.len());
                     }
 
-                    let json = serde_json::to_string(&debug_passes).unwrap();
-                    sender.send(json).ok();
+                    for batch in &target.alpha_batcher
+                                        .batch_list
+                                        .alpha_batch_list
+                                        .batches {
+                        debug_target.add(debug_server::BatchKind::Alpha, batch.key.kind.debug_name(), batch.instances.len());
+                    }
+
+                    debug_pass.add(debug_target);
                 }
+
+                debug_passes.add(debug_pass);
             }
         }
+
+        serde_json::to_string(&debug_passes).unwrap()
     }
 
     fn handle_debug_command(&mut self, command: DebugCommand) {
         match command {
             DebugCommand::EnableProfiler(enable) => {
                 if enable {
                     self.debug_flags.insert(PROFILER_DBG);
                 } else {
@@ -1681,18 +1682,20 @@ impl Renderer {
             }
             DebugCommand::EnableRenderTargetDebug(enable) => {
                 if enable {
                     self.debug_flags.insert(RENDER_TARGET_DBG);
                 } else {
                     self.debug_flags.remove(RENDER_TARGET_DBG);
                 }
             }
-            DebugCommand::Flush => {
-                self.update_debug_server();
+            DebugCommand::FetchDocuments => {}
+            DebugCommand::FetchPasses => {
+                let json = self.get_passes_for_debugger();
+                self.debug_server.send(json);
             }
         }
     }
 
     /// Set a callback for handling external images.
     pub fn set_external_image_handler(&mut self, handler: Box<ExternalImageHandler>) {
         self.external_image_handler = Some(handler);
     }
@@ -1834,17 +1837,16 @@ impl Renderer {
                             // Create a new native texture, as requested by the texture cache.
                             let texture = self.device.create_texture(TextureTarget::Array);
                             self.texture_resolver.cache_texture_map.push(texture);
                         }
                         let texture = &mut self.texture_resolver.cache_texture_map[cache_texture_index];
 
                         // Ensure no PBO is bound when creating the texture storage,
                         // or GL will attempt to read data from there.
-                        self.device.bind_pbo(None);
                         self.device.init_texture(texture,
                                                  width,
                                                  height,
                                                  format,
                                                  filter,
                                                  mode,
                                                  layer_count,
                                                  None);
@@ -1877,27 +1879,27 @@ impl Renderer {
                         self.device.update_texture_from_pbo(texture,
                                                             rect.origin.x,
                                                             rect.origin.y,
                                                             rect.size.width,
                                                             rect.size.height,
                                                             layer_index,
                                                             stride,
                                                             0);
+
+                        // Ensure that other texture updates won't read from this PBO.
+                        self.device.bind_pbo(None);
                     }
                     TextureUpdateOp::Free => {
                         let texture = &mut self.texture_resolver.cache_texture_map[update.id.0];
                         self.device.free_texture_storage(texture);
                     }
                 }
             }
         }
-
-        // Ensure that other texture updates won't read from this PBO.
-        self.device.bind_pbo(None);
     }
 
     fn draw_instanced_batch<T>(&mut self,
                                data: &[T],
                                vertex_array_kind: VertexArrayKind,
                                textures: &BatchTextures) {
         for i in 0..textures.colors.len() {
             self.texture_resolver.bind(&textures.colors[i], TextureSampler::color(i), &mut self.device);
@@ -2159,26 +2161,16 @@ impl Renderer {
 
             if !target.horizontal_blurs.is_empty() {
                 self.draw_instanced_batch(&target.horizontal_blurs,
                                           VertexArrayKind::Blur,
                                           &BatchTextures::no_texture());
             }
         }
 
-        // Draw any box-shadow caches for this target.
-        if !target.box_shadow_cache_prims.is_empty() {
-            self.device.set_blend(false);
-            let _gm = self.gpu_profile.add_marker(GPU_TAG_CACHE_BOX_SHADOW);
-            self.cs_box_shadow.bind(&mut self.device, projection);
-            self.draw_instanced_batch(&target.box_shadow_cache_prims,
-                                      VertexArrayKind::CacheBoxShadow,
-                                      &BatchTextures::no_texture());
-        }
-
         // Draw any textrun caches for this target. For now, this
         // is only used to cache text runs that are to be blurred
         // for text-shadow support. In the future it may be worth
         // considering using this for (some) other text runs, since
         // it removes the overhead of submitting many small glyphs
         // to multiple tiles in the normal text run case.
         if !target.text_run_cache_prims.is_empty() {
             self.device.set_blend(true);
@@ -2292,16 +2284,26 @@ impl Renderer {
             // performance penalty on other GPU types - we should test this
             // and consider different code paths.
             let clear_color = [1.0, 1.0, 1.0, 0.0];
             self.device.clear_target_rect(Some(clear_color),
                                           None,
                                           target.used_rect());
         }
 
+        // Draw any box-shadow caches for this target.
+        if !target.box_shadow_cache_prims.is_empty() {
+            self.device.set_blend(false);
+            let _gm = self.gpu_profile.add_marker(GPU_TAG_CACHE_BOX_SHADOW);
+            self.cs_box_shadow.bind(&mut self.device, projection);
+            self.draw_instanced_batch(&target.box_shadow_cache_prims,
+                                      VertexArrayKind::CacheBoxShadow,
+                                      &BatchTextures::no_texture());
+        }
+
         // Draw the clip items into the tiled alpha mask.
         {
             let _gm = self.gpu_profile.add_marker(GPU_TAG_CACHE_CLIP);
 
             // If we have border corner clips, the first step is to clear out the
             // area in the clip mask. This allows drawing multiple invididual clip
             // in regions below.
             if !target.clip_batcher.border_clears.is_empty() {
@@ -2858,9 +2860,11 @@ impl Default for RendererOptions {
 #[cfg(not(feature = "debugger"))]
 pub struct DebugServer;
 
 #[cfg(not(feature = "debugger"))]
 impl DebugServer {
     pub fn new(_: MsgSender<ApiMsg>) -> DebugServer {
         DebugServer
     }
+
+    pub fn send(&mut self, _: String) {}
 }
--- a/gfx/webrender/src/resource_cache.rs
+++ b/gfx/webrender/src/resource_cache.rs
@@ -1,28 +1,31 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+use app_units::Au;
 use device::TextureFilter;
 use frame::FrameId;
 use glyph_cache::GlyphCache;
 use gpu_cache::{GpuCache, GpuCacheHandle};
 use internal_types::{FastHashMap, FastHashSet, SourceTexture, TextureUpdateList};
 use profiler::{ResourceProfileCounters, TextureCacheProfileCounters};
 use std::collections::hash_map::Entry::{self, Occupied, Vacant};
 use std::fmt::Debug;
 use std::hash::Hash;
 use std::mem;
 use std::sync::Arc;
 use texture_cache::{TextureCache, TextureCacheHandle};
 use api::{BlobImageRenderer, BlobImageDescriptor, BlobImageError, BlobImageRequest};
 use api::{BlobImageResources, BlobImageData, ResourceUpdates, ResourceUpdate, AddFont};
 use api::{DevicePoint, DeviceUintRect, DeviceUintSize};
-use api::{Epoch, FontInstance, FontKey, FontTemplate};
+use api::{Epoch, FontInstance, FontInstanceKey, FontKey, FontTemplate};
+use api::{FontInstanceOptions, FontInstancePlatformOptions};
+use api::{ColorF, FontRenderMode, SubpixelDirection};
 use api::{GlyphDimensions, GlyphKey, IdNamespace};
 use api::{ImageData, ImageDescriptor, ImageKey, ImageRendering};
 use api::{TileOffset, TileSize};
 use api::{ExternalImageData, ExternalImageType};
 use rayon::ThreadPool;
 use glyph_rasterizer::{GlyphRasterizer, GlyphRequest};
 
 const DEFAULT_TILE_SIZE: TileSize = 512;
@@ -165,16 +168,17 @@ impl Into<BlobImageRequest> for ImageReq
             key: self.key,
             tile: self.tile,
         }
     }
 }
 
 struct Resources {
     font_templates: FastHashMap<FontKey, FontTemplate>,
+    font_instances: FastHashMap<FontInstanceKey, FontInstance>,
     image_templates: ImageTemplates,
 }
 
 impl BlobImageResources for Resources {
     fn get_font_data(&self, key: FontKey) -> &FontTemplate {
         self.font_templates.get(&key).unwrap()
     }
     fn get_image(&self, key: ImageKey) -> Option<(&ImageData, &ImageDescriptor)> {
@@ -208,16 +212,17 @@ impl ResourceCache {
     pub fn new(texture_cache: TextureCache,
                workers: Arc<ThreadPool>,
                blob_image_renderer: Option<Box<BlobImageRenderer>>) -> ResourceCache {
         ResourceCache {
             cached_glyphs: GlyphCache::new(),
             cached_images: ResourceClassCache::new(),
             resources: Resources {
                 font_templates: FastHashMap::default(),
+                font_instances: FastHashMap::default(),
                 image_templates: ImageTemplates::new(),
             },
             cached_glyph_dimensions: FastHashMap::default(),
             texture_cache,
             state: State::Idle,
             current_frame_id: FrameId(0),
             pending_image_requests: FastHashSet::default(),
             glyph_rasterizer: GlyphRasterizer::new(workers),
@@ -274,16 +279,23 @@ impl ResourceCache {
                         AddFont::Native(id, native_font_handle) => {
                             self.add_font_template(id, FontTemplate::Native(native_font_handle));
                         }
                     }
                 }
                 ResourceUpdate::DeleteFont(font) => {
                     self.delete_font_template(font);
                 }
+                ResourceUpdate::AddFontInstance(instance) => {
+                    self.add_font_instance(instance.key, instance.font_key, instance.glyph_size,
+                                           instance.options, instance.platform_options);
+                }
+                ResourceUpdate::DeleteFontInstance(instance) => {
+                    self.delete_font_instance(instance);
+                }
             }
         }
     }
 
     pub fn add_font_template(&mut self, font_key: FontKey, template: FontTemplate) {
         // Push the new font to the font renderer, and also store
         // it locally for glyph metric requests.
         self.glyph_rasterizer.add_font(font_key, template.clone());
@@ -293,16 +305,50 @@ impl ResourceCache {
     pub fn delete_font_template(&mut self, font_key: FontKey) {
         self.glyph_rasterizer.delete_font(font_key);
         self.resources.font_templates.remove(&font_key);
         if let Some(ref mut r) = self.blob_image_renderer {
             r.delete_font(font_key);
         }
     }
 
+    pub fn add_font_instance(&mut self,
+                             instance_key: FontInstanceKey,
+                             font_key: FontKey,
+                             glyph_size: Au,
+                             options: Option<FontInstanceOptions>,
+                             platform_options: Option<FontInstancePlatformOptions>) {
+        let mut render_mode = FontRenderMode::Subpixel;
+        let mut subpx_dir = SubpixelDirection::Horizontal;
+        if let Some(options) = options {
+            render_mode = options.render_mode;
+            if render_mode == FontRenderMode::Mono {
+                subpx_dir = SubpixelDirection::None;
+            }
+        }
+        let instance = FontInstance::new(font_key,
+                                         glyph_size,
+                                         ColorF::new(0.0, 0.0, 0.0, 1.0),
+                                         render_mode,
+                                         subpx_dir,
+                                         platform_options);
+        self.resources.font_instances.insert(instance_key, instance);
+    }
+
+    pub fn delete_font_instance(&mut self, instance_key: FontInstanceKey) {
+        self.resources.font_instances.remove(&instance_key);
+        if let Some(ref mut r) = self.blob_image_renderer {
+            r.delete_font_instance(instance_key);
+        }
+    }
+
+    pub fn get_font_instance(&self, instance_key: FontInstanceKey) -> Option<&FontInstance> {
+        self.resources.font_instances.get(&instance_key)
+    }
+
     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(&descriptor, &data) {
             // We aren't going to be able to upload a texture this big, so tile it, even
             // if tiling was not requested.
@@ -602,23 +648,21 @@ impl ResourceCache {
         self.glyph_rasterizer.resolve_glyphs(
             &mut self.cached_glyphs,
             &mut self.texture_cache,
             gpu_cache,
             texture_cache_profile,
         );
 
         // Apply any updates of new / updated images (incl. blobs) to the texture cache.
-        self.update_texture_cache(gpu_cache, texture_cache_profile);
-        self.texture_cache.end_frame();
+        self.update_texture_cache(gpu_cache);
+        self.texture_cache.end_frame(texture_cache_profile);
     }
 
-    fn update_texture_cache(&mut self,
-                            gpu_cache: &mut GpuCache,
-                            _texture_cache_profile: &mut TextureCacheProfileCounters) {
+    fn update_texture_cache(&mut self, gpu_cache: &mut GpuCache) {
         for request in self.pending_image_requests.drain() {
             let image_template = self.resources.image_templates.get_mut(request.key).unwrap();
             debug_assert!(image_template.data.uses_texture_cache());
 
             let image_data = match image_template.data {
                 ImageData::Raw(..) | ImageData::External(..) => {
                     // Safe to clone here since the Raw image data is an
                     // Arc, and the external image data is small.
@@ -662,17 +706,17 @@ impl ResourceCache {
                 // The tiled image could be stored on the CPU as one large image or be
                 // already broken up into tiles. This affects the way we compute the stride
                 // and offset.
                 let tiled_on_cpu = image_template.data.is_blob();
 
                 let (stride, offset) = if tiled_on_cpu {
                     (image_descriptor.stride, 0)
                 } else {
-                    let bpp = image_descriptor.format.bytes_per_pixel().unwrap();
+                    let bpp = image_descriptor.format.bytes_per_pixel();
                     let stride = image_descriptor.compute_stride();
                     let offset = image_descriptor.offset + tile.y as u32 * tile_size as u32 * stride
                                                          + tile.x as u32 * tile_size as u32 * bpp;
                     (Some(stride), offset)
                 };
 
                 ImageDescriptor {
                     width: actual_width,
--- a/gfx/webrender/src/scene.rs
+++ b/gfx/webrender/src/scene.rs
@@ -122,9 +122,18 @@ impl Scene {
             epoch,
             viewport_size,
             content_size,
             background_color,
         };
 
         self.pipeline_map.insert(pipeline_id, new_pipeline);
     }
+
+    pub fn remove_pipeline(&mut self,
+                           pipeline_id: PipelineId) {
+        if self.root_pipeline_id == Some(pipeline_id) {
+            self.root_pipeline_id = None;
+        }
+        self.display_lists.remove(&pipeline_id);
+        self.pipeline_map.remove(&pipeline_id);
+    }
 }
--- a/gfx/webrender/src/texture_cache.rs
+++ b/gfx/webrender/src/texture_cache.rs
@@ -3,16 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use device::TextureFilter;
 use frame::FrameId;
 use freelist::{FreeList, FreeListHandle, UpsertResult, WeakFreeListHandle};
 use gpu_cache::{GpuCache, GpuCacheHandle};
 use internal_types::{SourceTexture, TextureUpdate, TextureUpdateOp};
 use internal_types::{CacheTextureId, RenderTargetMode, TextureUpdateList, TextureUpdateSource};
+use profiler::{ResourceProfileCounter, TextureCacheProfileCounters};
 use resource_cache::CacheItem;
 use std::cmp;
 use std::mem;
 use api::{ExternalImageType, ImageData, ImageFormat};
 use api::{DeviceUintRect, DeviceUintSize, DeviceUintPoint};
 use api::{ImageDescriptor};
 
 // The fixed number of layers for the shared texture cache.
@@ -218,18 +219,23 @@ impl TextureCache {
             shared_entry_handles: Vec::new(),
         }
     }
 
     pub fn begin_frame(&mut self, frame_id: FrameId) {
         self.frame_id = frame_id;
     }
 
-    pub fn end_frame(&mut self) {
+    pub fn end_frame(&mut self, texture_cache_profile: &mut TextureCacheProfileCounters) {
         self.expire_old_standalone_entries();
+
+        self.array_a8.update_profile(&mut texture_cache_profile.pages_a8);
+        self.array_rg8.update_profile(&mut texture_cache_profile.pages_rg8);
+        self.array_rgb8.update_profile(&mut texture_cache_profile.pages_rgb8);
+        self.array_rgba8.update_profile(&mut texture_cache_profile.pages_rgba8);
     }
 
     // Request an item in the texture cache. All images that will
     // be used on a frame *must* have request() called on their
     // handle, to update the last used timestamp and ensure
     // that resources are not flushed from the cache too early.
     //
     // Returns true if the image needs to be uploaded to the
@@ -830,16 +836,28 @@ impl TextureArray {
         TextureArray {
             format,
             is_allocated: false,
             regions: Vec::new(),
             texture_id: None,
         }
     }
 
+    fn update_profile(&self, counter: &mut ResourceProfileCounter) {
+        if self.is_allocated {
+            let size = TEXTURE_ARRAY_LAYERS as u32 *
+                       TEXTURE_LAYER_DIMENSIONS *
+                       TEXTURE_LAYER_DIMENSIONS *
+                       self.format.bytes_per_pixel();
+            counter.set(TEXTURE_ARRAY_LAYERS as usize, size as usize);
+        } else {
+            counter.set(0, 0);
+        }
+    }
+
     // Allocate space in this texture array.
     fn alloc(&mut self,
              width: u32,
              height: u32,
              user_data: [f32; 2],
              frame_id: FrameId) -> Option<CacheEntry> {
         // Lazily allocate the regions if not already created.
         // This means that very rarely used image formats can be
@@ -952,17 +970,17 @@ impl TextureUpdate {
                             id: ext_image.id,
                             channel_index: ext_image.channel_index,
                         }
                     }
                 }
             }
             ImageData::Raw(bytes) => {
                 let finish = descriptor.offset +
-                             descriptor.width * descriptor.format.bytes_per_pixel().unwrap_or(0) +
+                             descriptor.width * descriptor.format.bytes_per_pixel() +
                              (descriptor.height-1) * descriptor.compute_stride();
                 assert!(bytes.len() >= finish as usize);
 
                 TextureUpdateSource::Bytes {
                     data: bytes
                 }
             }
         };
--- a/gfx/webrender/src/tiling.rs
+++ b/gfx/webrender/src/tiling.rs
@@ -17,17 +17,17 @@ use render_task::{RenderTaskAddress, Ren
 use render_task::{RenderTaskLocation, RenderTaskTree};
 use renderer::BlendMode;
 use renderer::ImageBufferKind;
 use resource_cache::ResourceCache;
 use std::{f32, i32, usize};
 use texture_allocator::GuillotineAllocator;
 use util::{TransformedRect, TransformedRectKind};
 use api::{BuiltDisplayList, ClipAndScrollInfo, ClipId, ColorF, DeviceIntPoint, ImageKey};
-use api::{DeviceIntRect, DeviceIntSize, DeviceUintPoint, DeviceUintSize, FontInstance};
+use api::{DeviceIntRect, DeviceIntSize, DeviceUintPoint, DeviceUintSize};
 use api::{ExternalImageType, FilterOp, FontRenderMode, ImageRendering, LayerRect};
 use api::{LayerToWorldTransform, MixBlendMode, PipelineId, PropertyBinding, TransformStyle};
 use api::{TileOffset, WorldToLayerTransform, YuvColorSpace, YuvFormat, LayerVector2D};
 
 // Special sentinel value recognized by the shader. It is considered to be
 // a dummy task that doesn't mask out anything.
 const OPAQUE_TASK_ADDRESS: RenderTaskAddress = RenderTaskAddress(i32::MAX as u32);
 
@@ -39,17 +39,17 @@ trait AlphaBatchHelpers {
                       metadata: &PrimitiveMetadata) -> BlendMode;
 }
 
 impl AlphaBatchHelpers for PrimitiveStore {
     fn get_blend_mode(&self, needs_blending: bool, metadata: &PrimitiveMetadata) -> BlendMode {
         match metadata.prim_kind {
             PrimitiveKind::TextRun => {
                 let text_run_cpu = &self.cpu_text_runs[metadata.cpu_prim_index.0];
-                match text_run_cpu.normal_render_mode {
+                match text_run_cpu.font.render_mode {
                     FontRenderMode::Subpixel => BlendMode::Subpixel(text_run_cpu.color),
                     FontRenderMode::Alpha | FontRenderMode::Mono => BlendMode::Alpha,
                 }
             }
             PrimitiveKind::Image |
             PrimitiveKind::AlignedGradient |
             PrimitiveKind::AngleGradient |
             PrimitiveKind::RadialGradient => {
@@ -448,27 +448,22 @@ impl AlphaRenderItem {
                         };
 
                         let key = AlphaBatchKey::new(batch_kind, flags, blend_mode, textures);
                         let batch = batch_list.get_suitable_batch(&key, item_bounding_rect);
                         batch.push(base_instance.build(uv_address.as_int(gpu_cache), 0, 0));
                     }
                     PrimitiveKind::TextRun => {
                         let text_cpu = &ctx.prim_store.cpu_text_runs[prim_metadata.cpu_prim_index.0];
-                        let font_size_dp = text_cpu.logical_font_size.scale_by(ctx.device_pixel_ratio);
 
                         // TODO(gw): avoid / recycle this allocation in the future.
                         let mut instances = Vec::new();
 
-                        let font = FontInstance::new(text_cpu.font_key,
-                                                     font_size_dp,
-                                                     text_cpu.color,
-                                                     text_cpu.normal_render_mode,
-                                                     text_cpu.glyph_options,
-                                                     text_cpu.subpx_dir);
+                        let mut font = text_cpu.font.clone();
+                        font.size = font.size.scale_by(ctx.device_pixel_ratio);
 
                         let texture_id = ctx.resource_cache.get_glyphs(font,
                                                                        &text_cpu.glyph_keys,
                                                                        |index, handle| {
                             let uv_address = handle.as_int(gpu_cache);
                             instances.push(base_instance.build(index as i32, uv_address, 0));
                         });
 
@@ -571,18 +566,19 @@ impl AlphaRenderItem {
                         batch.push(base_instance.build(uv_rect_addresses[0],
                                                        uv_rect_addresses[1],
                                                        uv_rect_addresses[2]));
                     }
                     PrimitiveKind::BoxShadow => {
                         let box_shadow = &ctx.prim_store.cpu_box_shadows[prim_metadata.cpu_prim_index.0];
                         let cache_task_id = prim_metadata.render_task_id.unwrap();
                         let cache_task_address = render_tasks.get_task_address(cache_task_id);
+                        let textures = BatchTextures::render_target_cache();
 
-                        let key = AlphaBatchKey::new(AlphaBatchKind::BoxShadow, flags, blend_mode, no_textures);
+                        let key = AlphaBatchKey::new(AlphaBatchKind::BoxShadow, flags, blend_mode, textures);
                         let batch = batch_list.get_suitable_batch(&key, item_bounding_rect);
 
                         for rect_index in 0..box_shadow.rects.len() {
                             batch.push(base_instance.build(rect_index as i32,
                                                            cache_task_address.0 as i32, 0));
                         }
                     }
                 }
@@ -901,17 +897,16 @@ impl<T: RenderTarget> RenderTargetList<T
 
         (origin, RenderTargetIndex(self.targets.len() - 1))
     }
 }
 
 /// A render target represents a number of rendering operations on a surface.
 pub struct ColorRenderTarget {
     pub alpha_batcher: AlphaBatcher,
-    pub box_shadow_cache_prims: Vec<BoxShadowCacheInstance>,
     // List of text runs to be cached to this render target.
     // TODO(gw): For now, assume that these all come from
     //           the same source texture id. This is almost
     //           always true except for pathological test
     //           cases with more than 4k x 4k of unique
     //           glyphs visible. Once the future glyph / texture
     //           cache changes land, this restriction will
     //           be removed anyway.
@@ -928,17 +923,16 @@ pub struct ColorRenderTarget {
 impl RenderTarget for ColorRenderTarget {
     fn allocate(&mut self, size: DeviceUintSize) -> Option<DeviceUintPoint> {
         self.allocator.allocate(&size)
     }
 
     fn new(size: DeviceUintSize) -> ColorRenderTarget {
         ColorRenderTarget {
             alpha_batcher: AlphaBatcher::new(),
-            box_shadow_cache_prims: Vec::new(),
             text_run_cache_prims: Vec::new(),
             line_cache_prims: Vec::new(),
             text_run_textures: BatchTextures::no_texture(),
             vertical_blurs: Vec::new(),
             horizontal_blurs: Vec::new(),
             readbacks: Vec::new(),
             allocator: TextureAllocator::new(size),
         }
@@ -988,26 +982,19 @@ impl RenderTarget for ColorRenderTarget 
                 self.horizontal_blurs.push(BlurCommand {
                     task_id: task_id.0 as i32,
                     src_task_id: task.children[0].0 as i32,
                     blur_direction: BlurDirection::Horizontal as i32,
                 });
             }
             RenderTaskKind::CachePrimitive(prim_index) => {
                 let prim_metadata = ctx.prim_store.get_metadata(prim_index);
-
                 let prim_address = prim_metadata.gpu_location.as_int(gpu_cache);
 
                 match prim_metadata.prim_kind {
-                    PrimitiveKind::BoxShadow => {
-                        self.box_shadow_cache_prims.push(BoxShadowCacheInstance {
-                            prim_address: gpu_cache.get_address(&prim_metadata.gpu_location),
-                            task_index: render_tasks.get_task_address(task_id),
-                        });
-                    }
                     PrimitiveKind::TextShadow => {
                         let prim = &ctx.prim_store.cpu_text_shadows[prim_metadata.cpu_prim_index.0];
 
                         // todo(gw): avoid / recycle this allocation...
                         let mut instances = Vec::new();
 
                         let task_index = render_tasks.get_task_address(task_id);
 
@@ -1021,24 +1008,19 @@ impl RenderTarget for ColorRenderTarget 
                                                                         0);     // z is disabled for rendering cache primitives
 
                             match sub_metadata.prim_kind {
                                 PrimitiveKind::TextRun => {
                                     // Add instances that reference the text run GPU location. Also supply
                                     // the parent text-shadow prim address as a user data field, allowing
                                     // the shader to fetch the text-shadow parameters.
                                     let text = &ctx.prim_store.cpu_text_runs[sub_metadata.cpu_prim_index.0];
-                                    let font_size_dp = text.logical_font_size.scale_by(ctx.device_pixel_ratio);
 
-                                    let font = FontInstance::new(text.font_key,
-                                                                 font_size_dp,
-                                                                 text.color,
-                                                                 text.shadow_render_mode,
-                                                                 text.glyph_options,
-                                                                 text.subpx_dir);
+                                    let mut font = text.font.clone();
+                                    font.size = font.size.scale_by(ctx.device_pixel_ratio);
 
                                     let texture_id = ctx.resource_cache.get_glyphs(font,
                                                                                    &text.glyph_keys,
                                                                                    |index, handle| {
                                         let uv_address = handle.as_int(gpu_cache);
                                         instances.push(instance.build(index as i32,
                                                                       uv_address,
                                                                       prim_address));
@@ -1068,39 +1050,42 @@ impl RenderTarget for ColorRenderTarget 
                         }
                     }
                     _ => {
                         // No other primitives make use of primitive caching yet!
                         unreachable!()
                     }
                 }
             }
-            RenderTaskKind::CacheMask(..) => {
+            RenderTaskKind::CacheMask(..) |
+            RenderTaskKind::BoxShadow(..) => {
                 panic!("Should not be added to color target!");
             }
             RenderTaskKind::Readback(device_rect) => {
                 self.readbacks.push(device_rect);
             }
         }
     }
 }
 
 pub struct AlphaRenderTarget {
     pub clip_batcher: ClipBatcher,
+    pub box_shadow_cache_prims: Vec<BoxShadowCacheInstance>,
     allocator: TextureAllocator,
 }
 
 impl RenderTarget for AlphaRenderTarget {
     fn allocate(&mut self, size: DeviceUintSize) -> Option<DeviceUintPoint> {
         self.allocator.allocate(&size)
     }
 
     fn new(size: DeviceUintSize) -> AlphaRenderTarget {
         AlphaRenderTarget {
             clip_batcher: ClipBatcher::new(),
+            box_shadow_cache_prims: Vec::new(),
             allocator: TextureAllocator::new(size),
         }
     }
 
     fn used_rect(&self) -> DeviceIntRect {
         self.allocator.used_rect
     }
 
@@ -1116,16 +1101,31 @@ impl RenderTarget for AlphaRenderTarget 
             }
             RenderTaskKind::Alpha(..) |
             RenderTaskKind::VerticalBlur(..) |
             RenderTaskKind::HorizontalBlur(..) |
             RenderTaskKind::CachePrimitive(..) |
             RenderTaskKind::Readback(..) => {
                 panic!("Should not be added to alpha target!");
             }
+            RenderTaskKind::BoxShadow(prim_index) => {
+                let prim_metadata = ctx.prim_store.get_metadata(prim_index);
+
+                match prim_metadata.prim_kind {
+                    PrimitiveKind::BoxShadow => {
+                        self.box_shadow_cache_prims.push(BoxShadowCacheInstance {
+                            prim_address: gpu_cache.get_address(&prim_metadata.gpu_location),
+                            task_index: render_tasks.get_task_address(task_id),
+                        });
+                    }
+                    _ => {
+                        panic!("BUG: invalid prim kind");
+                    }
+                }
+            }
             RenderTaskKind::CacheMask(ref task_info) => {
                 let task_address = render_tasks.get_task_address(task_id);
                 self.clip_batcher.add(task_address,
                                       &task_info.clips,
                                       &ctx.resource_cache,
                                       gpu_cache,
                                       task_info.geometry_kind);
             }
--- a/gfx/webrender_api/Cargo.toml
+++ b/gfx/webrender_api/Cargo.toml
@@ -1,11 +1,11 @@
 [package]
 name = "webrender_api"
-version = "0.49.0"
+version = "0.50.0"
 authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
 license = "MPL-2.0"
 repository = "https://github.com/servo/webrender"
 
 [features]
 nightly = ["euclid/unstable", "serde/unstable"]
 ipc = ["ipc-channel"]
 
--- a/gfx/webrender_api/src/api.rs
+++ b/gfx/webrender_api/src/api.rs
@@ -1,36 +1,39 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+use app_units::Au;
 use channel::{self, MsgSender, Payload, PayloadSenderHelperMethods, PayloadSender};
 use std::cell::Cell;
 use std::fmt;
 use std::marker::PhantomData;
 use {BuiltDisplayList, BuiltDisplayListDescriptor, ClipId, ColorF, DeviceIntPoint};
-use {DeviceUintRect, DeviceUintSize, FontKey, GlyphDimensions, GlyphKey};
+use {DeviceUintRect, DeviceUintSize, FontInstanceKey, FontKey, GlyphDimensions, GlyphKey};
 use {ImageData, ImageDescriptor, ImageKey, LayoutPoint, LayoutVector2D, LayoutSize, LayoutTransform};
-use {FontInstance, NativeFontHandle, WorldPoint};
+use {FontInstance, FontInstanceOptions, FontInstancePlatformOptions, NativeFontHandle, WorldPoint};
 
 pub type TileSize = u16;
 
 /// The resource updates for a given transaction (they must be applied in the same frame).
 #[derive(Clone, Deserialize, Serialize)]
 pub struct ResourceUpdates {
     pub updates: Vec<ResourceUpdate>,
 }
 
 #[derive(Clone, Deserialize, Serialize)]
 pub enum ResourceUpdate {
     AddImage(AddImage),
     UpdateImage(UpdateImage),
     DeleteImage(ImageKey),
     AddFont(AddFont),
     DeleteFont(FontKey),
+    AddFontInstance(AddFontInstance),
+    DeleteFontInstance(FontInstanceKey),
 }
 
 impl ResourceUpdates {
     pub fn new() -> Self {
         ResourceUpdates {
             updates: Vec::new(),
         }
     }
@@ -66,16 +69,29 @@ impl ResourceUpdates {
     pub fn add_native_font(&mut self, key: FontKey, native_handle: NativeFontHandle) {
         self.updates.push(ResourceUpdate::AddFont(AddFont::Native(key, native_handle)));
     }
 
     pub fn delete_font(&mut self, key: FontKey) {
         self.updates.push(ResourceUpdate::DeleteFont(key));
     }
 
+    pub fn add_font_instance(&mut self,
+                             key: FontInstanceKey,
+                             font_key: FontKey,
+                             glyph_size: Au,
+                             options: Option<FontInstanceOptions>,
+                             platform_options: Option<FontInstancePlatformOptions>) {
+        self.updates.push(ResourceUpdate::AddFontInstance(AddFontInstance { key, font_key, glyph_size, options, platform_options }));
+    }
+
+    pub fn delete_font_instance(&mut self, key: FontInstanceKey) {
+        self.updates.push(ResourceUpdate::DeleteFontInstance(key));
+    }
+
     pub fn merge(&mut self, mut other: ResourceUpdates) {
         self.updates.append(&mut other.updates);
     }
 
     pub fn clear(&mut self) {
         self.updates.clear()
     }
 }
@@ -98,31 +114,41 @@ pub struct UpdateImage {
 
 #[derive(Clone, Deserialize, Serialize)]
 pub enum AddFont {
     Raw(FontKey, Vec<u8>, u32),
     Native(FontKey, NativeFontHandle),
 }
 
 #[derive(Clone, Deserialize, Serialize)]
+pub struct AddFontInstance {
+    pub key: FontInstanceKey,
+    pub font_key: FontKey,
+    pub glyph_size: Au,
+    pub options: Option<FontInstanceOptions>,
+    pub platform_options: Option<FontInstancePlatformOptions>,
+}
+
+#[derive(Clone, Deserialize, Serialize)]
 pub enum DocumentMsg {
     SetDisplayList {
         list_descriptor: BuiltDisplayListDescriptor,
         epoch: Epoch,
         pipeline_id: PipelineId,
         background: Option<ColorF>,
         viewport_size: LayoutSize,
         content_size: LayoutSize,
         preserve_frame_state: bool,
         resources: ResourceUpdates,
     },
     SetPageZoom(ZoomFactor),
     SetPinchZoom(ZoomFactor),
     SetPan(DeviceIntPoint),
     SetRootPipeline(PipelineId),
+    RemovePipeline(PipelineId),
     SetWindowParameters {
         window_size: DeviceUintSize,
         inner_rect: DeviceUintRect,
     },
     Scroll(ScrollLocation, WorldPoint, ScrollEventPhase),
     ScrollNodeWithId(LayoutPoint, ClipId, ScrollClamping),
     TickScrollingBounce,
     GetScrollNodeState(MsgSender<Vec<ScrollLayerState>>),
@@ -132,16 +158,17 @@ pub enum DocumentMsg {
 impl fmt::Debug for DocumentMsg {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         f.write_str(match *self {
             DocumentMsg::SetDisplayList{..} => "DocumentMsg::SetDisplayList",
             DocumentMsg::SetPageZoom(..) => "DocumentMsg::SetPageZoom",
             DocumentMsg::SetPinchZoom(..) => "DocumentMsg::SetPinchZoom",
             DocumentMsg::SetPan(..) => "DocumentMsg::SetPan",
             DocumentMsg::SetRootPipeline(..) => "DocumentMsg::SetRootPipeline",
+            DocumentMsg::RemovePipeline(..) => "DocumentMsg::RemovePipeline",
             DocumentMsg::SetWindowParameters{..} => "DocumentMsg::SetWindowParameters",
             DocumentMsg::Scroll(..) => "DocumentMsg::Scroll",
             DocumentMsg::ScrollNodeWithId(..) => "DocumentMsg::ScrollNodeWithId",
             DocumentMsg::TickScrollingBounce => "DocumentMsg::TickScrollingBounce",
             DocumentMsg::GetScrollNodeState(..) => "DocumentMsg::GetScrollNodeState",
             DocumentMsg::GenerateFrame(..) => "DocumentMsg::GenerateFrame",
         })
     }
@@ -150,18 +177,20 @@ impl fmt::Debug for DocumentMsg {
 #[derive(Debug, Clone, Deserialize, Serialize)]
 pub enum DebugCommand {
     // Display the frame profiler on screen.
     EnableProfiler(bool),
     // Display all texture cache pages on screen.
     EnableTextureCacheDebug(bool),
     // Display intermediate render targets on screen.
     EnableRenderTargetDebug(bool),
-    // Flush any pending debug commands.
-    Flush,
+    // Fetch current documents and display lists.
+    FetchDocuments,
+    // Fetch current passes and batches.
+    FetchPasses,
 }
 
 #[derive(Clone, Deserialize, Serialize)]
 pub enum ApiMsg {
     /// Add/remove/update images and fonts.
     UpdateResources(ResourceUpdates),
     /// Gets the glyph dimensions
     GetGlyphDimensions(FontInstance, Vec<GlyphKey>, MsgSender<Vec<Option<GlyphDimensions>>>),
@@ -324,16 +353,21 @@ impl RenderApi {
         self.api_sender.send(msg).unwrap();
     }
 
     pub fn generate_font_key(&self) -> FontKey {
         let new_id = self.next_unique_id();
         FontKey::new(self.namespace_id, new_id)
     }
 
+    pub fn generate_font_instance_key(&self) -> FontInstanceKey {
+        let new_id = self.next_unique_id();
+        FontInstanceKey::new(self.namespace_id, new_id)
+    }
+
     /// 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,
                                 glyph_keys: Vec<GlyphKey>)
@@ -417,33 +451,40 @@ impl RenderApi {
     /// A helper method to send document messages.
     fn send(&self, document_id: DocumentId, msg: DocumentMsg) {
         // This assertion fails on Servo use-cases, because it creates different
         // `RenderApi` instances for layout and compositor.
         //assert_eq!(document_id.0, self.namespace_id);
         self.api_sender.send(ApiMsg::UpdateDocument(document_id, msg)).unwrap()
     }
 
-        /// Sets the root pipeline.
+    /// Sets the root pipeline.
     ///
     /// # Examples
     ///
     /// ```
     /// # use webrender_api::{DeviceUintSize, PipelineId, RenderApiSender};
     /// # fn example(sender: RenderApiSender) {
     /// let api = sender.create_api();
     /// let document_id = api.add_document(DeviceUintSize::zero());
     /// let pipeline_id = PipelineId(0, 0);
     /// api.set_root_pipeline(document_id, pipeline_id);
     /// # }
     /// ```
     pub fn set_root_pipeline(&self, document_id: DocumentId, pipeline_id: PipelineId) {
         self.send(document_id, DocumentMsg::SetRootPipeline(pipeline_id));
     }
 
+    /// Removes data associated with a pipeline from the internal data structures.
+    /// If the specified `pipeline_id` is for the root pipeline, the root pipeline
+    /// is reset back to `None`.
+    pub fn remove_pipeline(&self, document_id: DocumentId, pipeline_id: PipelineId) {
+        self.send(document_id, DocumentMsg::RemovePipeline(pipeline_id));
+    }
+
     /// Supplies a new frame to WebRender.
     ///
     /// Non-blocking, it notifies a worker process which processes the display list.
     /// When it's done and a RenderNotifier has been set in `webrender::Renderer`,
     /// [new_frame_ready()][notifier] gets called.
     ///
     /// Note: Scrolling doesn't require an own Frame.
     ///
--- a/gfx/webrender_api/src/display_item.rs
+++ b/gfx/webrender_api/src/display_item.rs
@@ -1,15 +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 app_units::Au;
 use euclid::{SideOffsets2D, TypedSideOffsets2D};
-use {ColorF, FontKey, ImageKey, LayoutPoint, LayoutRect, LayoutSize, LayoutTransform};
+use {ColorF, FontInstanceKey, ImageKey, LayoutPoint, LayoutRect, LayoutSize, LayoutTransform};
 use {GlyphOptions, LayoutVector2D, PipelineId, PropertyBinding};
 
 // NOTE: some of these structs have an "IMPLICIT" comment.
 // This indicates that the BuiltDisplayList will have serialized
 // a list of values nearby that this item consumes. The traversal
 // iterator should handle finding these.
 
 #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
@@ -134,18 +133,17 @@ pub enum LineStyle {
     Solid,
     Dotted,
     Dashed,
     Wavy,
 }
 
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct TextDisplayItem {
-    pub font_key: FontKey,
-    pub size: Au,
+    pub font_key: FontInstanceKey,
     pub color: ColorF,
     pub glyph_options: Option<GlyphOptions>,
 } // IMPLICIT: glyphs: Vec<GlyphInstance>
 
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct NormalBorder {
     pub left: BorderSide,
     pub right: BorderSide,
--- a/gfx/webrender_api/src/display_list.rs
+++ b/gfx/webrender_api/src/display_list.rs
@@ -1,20 +1,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-use app_units::Au;
 use bincode;
 use serde::{Deserialize, Serialize, Serializer};
 use serde::ser::{SerializeSeq, SerializeMap};
 use time::precise_time_ns;
 use {BorderDetails, BorderDisplayItem, BorderWidths, BoxShadowClipMode, BoxShadowDisplayItem};
 use {ClipAndScrollInfo, ClipDisplayItem, ClipId, ColorF, ComplexClipRegion, DisplayItem};
-use {ExtendMode, FastHashMap, FastHashSet, FilterOp, FontKey, GlyphIndex, GlyphInstance};
+use {ExtendMode, FastHashMap, FastHashSet, FilterOp, FontInstanceKey, GlyphIndex, GlyphInstance};
 use {GlyphOptions, Gradient, GradientDisplayItem, GradientStop, IframeDisplayItem};
 use {ImageDisplayItem, ImageKey, ImageMask, ImageRendering, LayoutPoint, LayoutRect, LayoutSize};
 use {LayoutTransform, LayoutVector2D, LineDisplayItem, LineOrientation, LineStyle, LocalClip};
 use {MixBlendMode, PipelineId, PropertyBinding, PushStackingContextDisplayItem, RadialGradient};
 use {RadialGradientDisplayItem, RectangleDisplayItem, ScrollFrameDisplayItem, ScrollPolicy};
 use {ScrollSensitivity, SpecificDisplayItem, StackingContext, StickyFrameDisplayItem};
 use {StickyFrameInfo, TextDisplayItem, TextShadow, TransformStyle};
 use {YuvColorSpace, YuvData, YuvImageDisplayItem};
@@ -296,17 +295,17 @@ impl<'a> BuiltDisplayListIter<'a> {
             self.next()
         } else {
             Some(self.as_ref())
         }
     }
 }
 
 impl<'a> Iterator for GlyphsIter<'a> {
-    type Item = (FontKey, ColorF, ItemRange<GlyphIndex>);
+    type Item = (FontInstanceKey, ColorF, ItemRange<GlyphIndex>);
 
     fn next(&mut self) -> Option<Self::Item> {
         if self.data.len() == 0 { return None; }
 
         let (font_key, color) = bincode::deserialize_from(&mut self.data, bincode::Infinite)
                                         .expect("MEH: malicious process?");
         let glyph_indices = skip_slice::<GlyphIndex>(self.list, &mut self.data).0;
         Some((font_key, color, glyph_indices))
@@ -452,17 +451,17 @@ impl<'a, 'b> Serialize for DisplayItemRe
 }
 
 #[derive(Clone)]
 pub struct DisplayListBuilder {
     pub data: Vec<u8>,
     pub pipeline_id: PipelineId,
     clip_stack: Vec<ClipAndScrollInfo>,
     // FIXME: audit whether fast hashers (FNV?) are safe here
-    glyphs: FastHashMap<(FontKey, ColorF), FastHashSet<GlyphIndex>>,
+    glyphs: FastHashMap<(FontInstanceKey, ColorF), FastHashSet<GlyphIndex>>,
     next_clip_id: u64,
     builder_start_time: u64,
 
     /// The size of the content of this display list. This is used to allow scrolling
     /// outside the bounds of the display list items themselves.
     content_size: LayoutSize,
 }
 
@@ -600,48 +599,38 @@ impl DisplayListBuilder {
         });
         self.push_item(item, rect, local_clip);
     }
 
     pub fn push_text(&mut self,
                      rect: LayoutRect,
                      local_clip: Option<LocalClip>,
                      glyphs: &[GlyphInstance],
-                     font_key: FontKey,
+                     font_key: FontInstanceKey,
                      color: ColorF,
-                     size: Au,
                      glyph_options: Option<GlyphOptions>) {
-        // Sanity check - anything with glyphs bigger than this
-        // is probably going to consume too much memory to render
-        // efficiently anyway. This is specifically to work around
-        // the font_advance.html reftest, which creates a very large
-        // font as a crash test - the rendering is also ignored
-        // by the azure renderer.
-        if size < Au::from_px(4096) {
-            let item = SpecificDisplayItem::Text(TextDisplayItem {
-                color,
-                font_key,
-                size,
-                glyph_options,
-            });
+        let item = SpecificDisplayItem::Text(TextDisplayItem {
+            color,
+            font_key,
+            glyph_options,
+        });
 
-            for split_glyphs in glyphs.chunks(MAX_TEXT_RUN_LENGTH) {
-                self.push_item(item, rect, local_clip);
-                self.push_iter(split_glyphs);
+        for split_glyphs in glyphs.chunks(MAX_TEXT_RUN_LENGTH) {
+            self.push_item(item, rect, local_clip);
+            self.push_iter(split_glyphs);
 
-                // Remember that we've seen these glyphs
-                self.cache_glyphs(font_key, color, split_glyphs.iter().map(|glyph| glyph.index));
-            }
+            // Remember that we've seen these glyphs
+            self.cache_glyphs(font_key, color, split_glyphs.iter().map(|glyph| glyph.index));
         }
     }
 
     fn cache_glyphs<I: Iterator<Item=GlyphIndex>>(&mut self,
-                                                     font_key: FontKey,
-                                                     color: ColorF,
-                                                     glyphs: I) {
+                                                  font_key: FontInstanceKey,
+                                                  color: ColorF,
+                                                  glyphs: I) {
         let mut font_glyphs = self.glyphs.entry((font_key, color))
                                          .or_insert(FastHashSet::default());
 
         font_glyphs.extend(glyphs);
     }
 
     // Gradients can be defined with stops outside the range of [0, 1]
     // when this happens the gradient needs to be normalized by adjusting
--- a/gfx/webrender_api/src/font.rs
+++ b/gfx/webrender_api/src/font.rs
@@ -67,25 +67,25 @@ impl FontKey {
 
 
 #[derive(Clone)]
 pub enum FontTemplate {
     Raw(Arc<Vec<u8>>, u32),
     Native(NativeFontHandle),
 }
 
-#[repr(C)]
+#[repr(u32)]
 #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Ord, PartialOrd)]
 pub enum FontRenderMode {
     Mono = 0,
     Alpha,
     Subpixel,
 }
 
-#[repr(C)]
+#[repr(u32)]
 #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug, Deserialize, Serialize, Ord, PartialOrd)]
 pub enum SubpixelDirection {
     None = 0,
     Horizontal,
     Vertical,
 }
 
 const FIXED16_SHIFT: i32 = 16;
@@ -110,16 +110,24 @@ impl FontRenderMode {
         match fraction {
             0 => SubpixelOffset::Zero,
             1 => SubpixelOffset::Quarter,
             2 => SubpixelOffset::Half,
             3 => SubpixelOffset::ThreeQuarters,
             _ => panic!("Should only be given the fractional part"),
         }
     }
+
+    // Combine two font render modes such that the lesser amount of AA limits the AA of the result.
+    pub fn limit_by(self, other: FontRenderMode) -> FontRenderMode {
+        match (self, other) {
+            (FontRenderMode::Subpixel, _) | (_, FontRenderMode::Mono) => other,
+            _ => self,
+        }
+    }
 }
 
 #[repr(u8)]
 #[derive(Hash, Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize, Deserialize)]
 pub enum SubpixelOffset {
     Zero            = 0,
     Quarter         = 1,
     Half            = 2,
@@ -132,71 +140,94 @@ impl Into<f64> for SubpixelOffset {
             SubpixelOffset::Zero => 0.0,
             SubpixelOffset::Quarter => 0.25,
             SubpixelOffset::Half => 0.5,
             SubpixelOffset::ThreeQuarters => 0.75,
         }
     }
 }
 
+#[repr(C)]
 #[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)]
 pub struct GlyphOptions {
+    pub render_mode: FontRenderMode,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)]
+pub struct FontInstanceOptions {
+    pub render_mode: FontRenderMode,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)]
+pub struct FontInstancePlatformOptions {
     // These are currently only used on windows for dwrite fonts.
     pub use_embedded_bitmap: bool,
     pub force_gdi_rendering: bool,
 }
 
 #[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 render_mode: FontRenderMode,
-    pub glyph_options: Option<GlyphOptions>,
     pub subpx_dir: SubpixelDirection,
+    pub platform_options: Option<FontInstancePlatformOptions>,
 }
 
 impl FontInstance {
     pub fn new(font_key: FontKey,
                size: Au,
                mut color: ColorF,
                render_mode: FontRenderMode,
-               glyph_options: Option<GlyphOptions>,
-               subpx_dir: SubpixelDirection) -> FontInstance {
+               subpx_dir: SubpixelDirection,
+               platform_options: Option<FontInstancePlatformOptions>) -> FontInstance {
         // In alpha/mono mode, the color of the font is irrelevant.
         // Forcing it to black in those cases saves rasterizing glyphs
         // of different colors when not needed.
         if render_mode != FontRenderMode::Subpixel {
             color = ColorF::new(0.0, 0.0, 0.0, 1.0);
         }
 
         FontInstance {
             font_key,
             size,
             color: color.into(),
             render_mode,
-            glyph_options,
             subpx_dir,
+            platform_options,
         }
     }
 
     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)
+    }
+}
+
 #[derive(Clone, Hash, PartialEq, Eq, Debug, Deserialize, Serialize, Ord, PartialOrd)]
 pub struct GlyphKey {
     pub index: u32,
     pub subpixel_offset: SubpixelOffset,
 }
 
 impl GlyphKey {
     pub fn new(index: u32,
--- a/gfx/webrender_api/src/image.rs
+++ b/gfx/webrender_api/src/image.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 std::sync::Arc;
 use {DeviceUintRect, DevicePoint};
 use {IdNamespace};
 use {TileOffset, TileSize};
-use font::{FontKey, FontTemplate};
+use font::{FontKey, FontInstanceKey, FontTemplate};
 
 #[repr(C)]
 #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
 pub struct ImageKey(pub IdNamespace, pub u32);
 
 impl ImageKey {
     pub fn new(namespace: IdNamespace, key: u32) -> ImageKey {
         ImageKey(namespace, key)
@@ -54,24 +54,24 @@ pub enum ImageFormat {
     A8       = 1,
     RGB8     = 2,
     BGRA8    = 3,
     RGBAF32  = 4,
     RG8      = 5,
 }
 
 impl ImageFormat {
-    pub fn bytes_per_pixel(self) -> Option<u32> {
+    pub fn bytes_per_pixel(self) -> u32 {
         match self {
-            ImageFormat::A8 => Some(1),
-            ImageFormat::RGB8 => Some(3),
-            ImageFormat::BGRA8 => Some(4),
-            ImageFormat::RGBAF32 => Some(16),
-            ImageFormat::RG8 => Some(2),
-            ImageFormat::Invalid => None,
+            ImageFormat::A8 => 1,
+            ImageFormat::RGB8 => 3,
+            ImageFormat::BGRA8 => 4,
+            ImageFormat::RGBAF32 => 16,
+            ImageFormat::RG8 => 2,
+            ImageFormat::Invalid => 0,
         }
     }
 }
 
 #[derive(Copy, Clone, Debug, Deserialize, PartialEq, Serialize)]
 pub struct ImageDescriptor {
     pub format: ImageFormat,
     pub width: u32,
@@ -89,17 +89,17 @@ impl ImageDescriptor {
             format,
             stride: None,
             offset: 0,
             is_opaque,
         }
     }
 
     pub fn compute_stride(&self) -> u32 {
-        self.stride.unwrap_or(self.width * self.format.bytes_per_pixel().unwrap())
+        self.stride.unwrap_or(self.width * self.format.bytes_per_pixel())
     }
 }
 
 #[derive(Clone, Debug, Serialize, Deserialize)]
 pub enum ImageData {
     Raw(Arc<Vec<u8>>),
     Blob(BlobImageData),
     External(ExternalImageData),
@@ -160,16 +160,18 @@ pub trait BlobImageRenderer: Send {
                services: &BlobImageResources,
                key: BlobImageRequest,
                descriptor: &BlobImageDescriptor,
                dirty_rect: Option<DeviceUintRect>);
 
     fn resolve(&mut self, key: BlobImageRequest) -> BlobImageResult;
 
     fn delete_font(&mut self, key: FontKey);
+
+    fn delete_font_instance(&mut self, key: FontInstanceKey);
 }
 
 pub type BlobImageData = Vec<u8>;
 
 pub type BlobImageResult = Result<RasterizedBlobImage, BlobImageError>;
 
 #[repr(C)]
 #[derive(Copy, Clone, Debug)]
--- a/gfx/webrender_bindings/Cargo.toml
+++ b/gfx/webrender_bindings/Cargo.toml
@@ -1,18 +1,18 @@
 [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.49.0"}
+webrender_api = {path = "../webrender_api", version = "0.50.0"}
 rayon = "0.8"
 thread_profiler = "0.1.1"
 euclid = "0.15"
 app_units = "0.5"
 gleam = "0.4"
 
 [dependencies.webrender]
 path = "../webrender"
-version = "0.49.0"
+version = "0.50.0"
 default-features = false
--- a/toolkit/library/gtest/rust/Cargo.lock
+++ b/toolkit/library/gtest/rust/Cargo.lock
@@ -1451,17 +1451,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.49.0"
+version = "0.50.0"
 dependencies = [
  "app_units 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-text 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1473,22 +1473,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.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "num-traits 0.1.37 (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.1 (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.49.0",
+ "webrender_api 0.50.0",
 ]
 
 [[package]]
 name = "webrender_api"
-version = "0.49.0"
+version = "0.50.0"
 dependencies = [
  "app_units 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "bincode 0.8.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.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.8.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.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1502,18 +1502,18 @@ dependencies = [
 name = "webrender_bindings"
 version = "0.1.0"
 dependencies = [
  "app_units 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "gleam 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "rayon 0.8.1 (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.49.0",
- "webrender_api 0.49.0",
+ "webrender 0.50.0",
+ "webrender_api 0.50.0",
 ]
 
 [[package]]
 name = "winapi"
 version = "0.2.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
--- a/toolkit/library/rust/Cargo.lock
+++ b/toolkit/library/rust/Cargo.lock
@@ -1438,17 +1438,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.49.0"
+version = "0.50.0"
 dependencies = [
  "app_units 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-text 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1460,22 +1460,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.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "num-traits 0.1.37 (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.1 (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.49.0",
+ "webrender_api 0.50.0",
 ]
 
 [[package]]
 name = "webrender_api"
-version = "0.49.0"
+version = "0.50.0"
 dependencies = [
  "app_units 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "bincode 0.8.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.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.8.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.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1489,18 +1489,18 @@ dependencies = [
 name = "webrender_bindings"
 version = "0.1.0"
 dependencies = [
  "app_units 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "gleam 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "rayon 0.8.1 (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.49.0",
- "webrender_api 0.49.0",
+ "webrender 0.50.0",
+ "webrender_api 0.50.0",
 ]
 
 [[package]]
 name = "winapi"
 version = "0.2.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]