Test font instance changes. try: -b do -p linux64 -u all[linux64-qr] -t none
authorGlenn Watson <gwatson@mozilla.com>
Fri, 06 Oct 2017 14:55:48 +1000
changeset 1313611 84cb9319744c83e467024fab5e313bb2e867e98f
parent 1312887 19b32a138d08f73961df878a29de6f0aad441683
child 1581592 c1b24020827c173bb8b6f89568682ba1772427e1
push id226356
push usergwatson@mozilla.com
push dateFri, 06 Oct 2017 04:56:18 +0000
treeherdertry@84cb9319744c [default view] [failures only]
milestone58.0a1
Test font instance changes. try: -b do -p linux64 -u all[linux64-qr] -t none
gfx/webrender/res/cs_text_run.glsl
gfx/webrender/res/prim_shared.glsl
gfx/webrender/res/ps_text_run.glsl
gfx/webrender/src/frame.rs
gfx/webrender/src/frame_builder.rs
gfx/webrender/src/glyph_cache.rs
gfx/webrender/src/glyph_rasterizer.rs
gfx/webrender/src/lib.rs
gfx/webrender/src/picture.rs
gfx/webrender/src/platform/macos/font.rs
gfx/webrender/src/platform/unix/font.rs
gfx/webrender/src/platform/windows/font.rs
gfx/webrender/src/prim_store.rs
gfx/webrender/src/render_backend.rs
gfx/webrender/src/render_task.rs
gfx/webrender/src/renderer.rs
gfx/webrender/src/resource_cache.rs
gfx/webrender/src/texture_cache.rs
gfx/webrender/src/tiling.rs
gfx/webrender/src/util.rs
gfx/webrender_api/src/display_item.rs
gfx/webrender_api/src/display_list.rs
gfx/webrender_api/src/font.rs
gfx/webrender_bindings/src/bindings.rs
--- a/gfx/webrender/res/cs_text_run.glsl
+++ b/gfx/webrender/res/cs_text_run.glsl
@@ -33,17 +33,17 @@ void main(void) {
                               glyph_index,
                               text.subpx_dir);
 
     GlyphResource res = fetch_glyph_resource(resource_address);
 
     // Glyphs size is already in device-pixels.
     // The render task origin is in device-pixels. Offset that by
     // the glyph offset, relative to its primitive bounding rect.
-    vec2 size = res.uv_rect.zw - res.uv_rect.xy;
+    vec2 size = (res.uv_rect.zw - res.uv_rect.xy) * res.scale;
     vec2 local_pos = glyph.offset + vec2(res.offset.x, -res.offset.y) / uDevicePixelRatio;
     vec2 origin = prim.task.render_target_origin +
                   uDevicePixelRatio * (local_pos + shadow.offset - shadow_geom.local_rect.p0);
     vec4 local_rect = vec4(origin, size);
 
     vec2 texture_size = vec2(textureSize(sColor0, 0));
     vec2 st0 = res.uv_rect.xy / texture_size;
     vec2 st1 = res.uv_rect.zw / texture_size;
--- a/gfx/webrender/res/prim_shared.glsl
+++ b/gfx/webrender/res/prim_shared.glsl
@@ -622,21 +622,22 @@ TransformVertexInfo write_transform_vert
 }
 
 #endif //WR_FEATURE_TRANSFORM
 
 struct GlyphResource {
     vec4 uv_rect;
     float layer;
     vec2 offset;
+    float scale;
 };
 
 GlyphResource fetch_glyph_resource(int address) {
     vec4 data[2] = fetch_from_resource_cache_2(address);
-    return GlyphResource(data[0], data[1].x, data[1].yz);
+    return GlyphResource(data[0], data[1].x, data[1].yz, data[1].w);
 }
 
 struct ImageResource {
     vec4 uv_rect;
     float layer;
 };
 
 ImageResource fetch_image_resource(int address) {
--- a/gfx/webrender/res/ps_text_run.glsl
+++ b/gfx/webrender/res/ps_text_run.glsl
@@ -25,17 +25,17 @@ void main(void) {
                               text.subpx_dir);
     GlyphResource res = fetch_glyph_resource(resource_address);
 
     vec2 local_pos = glyph.offset +
                      text.offset +
                      vec2(res.offset.x, -res.offset.y) / uDevicePixelRatio;
 
     RectWithSize local_rect = RectWithSize(local_pos,
-                                           (res.uv_rect.zw - res.uv_rect.xy) / uDevicePixelRatio);
+                                           (res.uv_rect.zw - res.uv_rect.xy) * res.scale / uDevicePixelRatio);
 
 #ifdef WR_FEATURE_TRANSFORM
     TransformVertexInfo vi = write_transform_vertex(local_rect,
                                                     prim.local_clip_rect,
                                                     prim.z,
                                                     prim.layer,
                                                     prim.task,
                                                     local_rect);
@@ -52,32 +52,33 @@ void main(void) {
 #endif
 
     write_clip(vi.screen_pos, prim.clip_area);
 
     vec2 texture_size = vec2(textureSize(sColor0, 0));
     vec2 st0 = res.uv_rect.xy / texture_size;
     vec2 st1 = res.uv_rect.zw / texture_size;
 
-    vColor = text.color;
+    vColor = vec4(text.color.rgb * text.color.a, text.color.a);
     vUv = vec3(mix(st0, st1, f), res.layer);
     vUvBorder = (res.uv_rect + vec4(0.5, 0.5, -0.5, -0.5)) / texture_size.xyxy;
 }
 #endif
 
 #ifdef WR_FRAGMENT_SHADER
 void main(void) {
     vec3 tc = vec3(clamp(vUv.xy, vUvBorder.xy, vUvBorder.zw), vUv.z);
 #ifdef WR_FEATURE_SUBPIXEL_AA
     //note: the blend mode is not compatible with clipping
     oFragColor = texture(sColor0, tc);
 #else
     vec4 color = texture(sColor0, tc) * vColor;
+    float alpha = 1.0;
 #ifdef WR_FEATURE_TRANSFORM
     float a = 0.0;
     init_transform_fs(vLocalPos, a);
-    color.a *= a;
+    alpha *= a;
 #endif
-    color.a = min(color.a, do_clip());
-    oFragColor = color;
+    alpha = min(alpha, do_clip());
+    oFragColor = color * alpha;
 #endif
 }
 #endif
--- a/gfx/webrender/src/frame.rs
+++ b/gfx/webrender/src/frame.rs
@@ -26,147 +26,39 @@ pub struct FrameId(pub u32);
 
 static DEFAULT_SCROLLBAR_COLOR: ColorF = ColorF {
     r: 0.3,
     g: 0.3,
     b: 0.3,
     a: 0.6,
 };
 
-/// Nested display lists cause two types of replacements to ClipIds inside the nesting:
-///     1. References to the root scroll frame are replaced by the ClipIds that
-///        contained the nested display list.
-///     2. Other ClipIds (that aren't custom or reference frames) are assumed to be
-///        local to the nested display list and are converted to an id that is unique
-///        outside of the nested display list as well.
-///
-/// This structure keeps track of what ids are the "root" for one particular level of
-/// nesting as well as keeping and index, which can make ClipIds used internally unique
-/// in the full ClipScrollTree.
-#[derive(Debug)]
-struct NestedDisplayListInfo {
-    /// The index of this nested display list, which is used to generate
-    /// new ClipIds for clips that are defined inside it.
-    nest_index: u64,
-
-    /// The ClipId of the scroll frame node which contains this nested
-    /// display list. This is used to replace references to the root with
-    /// the proper ClipId.
-    scroll_node_id: ClipId,
-
-    /// The ClipId of the clip node which contains this nested display list.
-    /// This is used to replace references to the root with the proper ClipId.
-    clip_node_id: ClipId,
-}
-
-impl NestedDisplayListInfo {
-    fn convert_id_to_nested(&self, id: &ClipId) -> ClipId {
-        match *id {
-            ClipId::Clip(id, _, pipeline_id) => ClipId::Clip(id, self.nest_index, pipeline_id),
-            _ => *id,
-        }
-    }
-
-    fn convert_scroll_id_to_nested(&self, id: &ClipId) -> ClipId {
-        if id.pipeline_id() != self.scroll_node_id.pipeline_id() {
-            return *id;
-        }
-
-        if id.is_root_scroll_node() {
-            self.scroll_node_id
-        } else {
-            self.convert_id_to_nested(id)
-        }
-    }
-
-    fn convert_clip_id_to_nested(&self, id: &ClipId) -> ClipId {
-        if id.pipeline_id() != self.clip_node_id.pipeline_id() {
-            return *id;
-        }
-
-        if id.is_root_scroll_node() {
-            self.clip_node_id
-        } else {
-            self.convert_id_to_nested(id)
-        }
-    }
-
-    fn convert_new_id_to_nested(&self, id: &ClipId) -> ClipId {
-        if id.pipeline_id() != self.clip_node_id.pipeline_id() {
-            return *id;
-        }
-        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: &'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) {
-        self.current_nested_display_list_index += 1;
-        self.nested_display_list_info.push(NestedDisplayListInfo {
-            nest_index: self.current_nested_display_list_index,
-            scroll_node_id: info.scroll_node_id,
-            clip_node_id: info.clip_node_id(),
-        });
-    }
-
-    fn pop_nested_display_list_ids(&mut self) {
-        self.nested_display_list_info.pop();
-    }
-
-    fn convert_new_id_to_nested(&self, id: &ClipId) -> ClipId {
-        if let Some(nested_info) = self.nested_display_list_info.last() {
-            nested_info.convert_new_id_to_nested(id)
-        } else {
-            *id
-        }
-    }
-
-    fn convert_clip_scroll_info_to_nested(&self, info: &mut ClipAndScrollInfo) {
-        if let Some(nested_info) = self.nested_display_list_info.last() {
-            info.scroll_node_id = nested_info.convert_scroll_id_to_nested(&info.scroll_node_id);
-            info.clip_node_id = info.clip_node_id
-                .map(|ref id| nested_info.convert_clip_id_to_nested(id));
-        }
-
-        // We only want to produce nested ClipIds if we are in a nested display
-        // list situation.
-        debug_assert!(
-            !info.scroll_node_id.is_nested() || !self.nested_display_list_info.is_empty()
-        );
-        debug_assert!(
-            !info.clip_node_id().is_nested() || !self.nested_display_list_info.is_empty()
-        );
-    }
-
     /// Since WebRender still handles fixed position and reference frame content internally
     /// we need to apply this table of id replacements only to the id that affects the
     /// position of a node. We can eventually remove this when clients start handling
     /// reference frames themselves. This method applies these replacements.
     fn apply_scroll_frame_id_replacement(&self, id: ClipId) -> ClipId {
         match self.replacements.last() {
             Some(&(to_replace, replacement)) if to_replace == id => replacement,
             _ => id,
@@ -332,19 +224,18 @@ impl Frame {
     fn flatten_clip<'a>(
         &mut self,
         context: &mut FlattenContext,
         pipeline_id: PipelineId,
         parent_id: &ClipId,
         new_clip_id: &ClipId,
         clip_region: ClipRegion,
     ) {
-        let new_clip_id = context.convert_new_id_to_nested(new_clip_id);
         context.builder.add_clip_node(
-            new_clip_id,
+            *new_clip_id,
             *parent_id,
             pipeline_id,
             clip_region,
             &mut self.clip_scroll_tree,
         );
     }
 
     fn flatten_scroll_frame<'a>(
@@ -362,19 +253,18 @@ impl Frame {
         context.builder.add_clip_node(
             clip_id,
             *parent_id,
             pipeline_id,
             clip_region,
             &mut self.clip_scroll_tree,
         );
 
-        let new_scroll_frame_id = context.convert_new_id_to_nested(new_scroll_frame_id);
         context.builder.add_scroll_frame(
-            new_scroll_frame_id,
+            *new_scroll_frame_id,
             clip_id,
             pipeline_id,
             &frame_rect,
             &content_rect.size,
             scroll_sensitivity,
             &mut self.clip_scroll_tree,
         );
     }
@@ -550,17 +440,16 @@ impl Frame {
     fn flatten_item<'a, 'b>(
         &mut self,
         item: DisplayItemRef<'a, 'b>,
         pipeline_id: PipelineId,
         context: &mut FlattenContext,
         reference_frame_relative_offset: LayerVector2D,
     ) -> Option<BuiltDisplayListIter<'a>> {
         let mut clip_and_scroll = item.clip_and_scroll();
-        context.convert_clip_scroll_info_to_nested(&mut clip_and_scroll);
 
         let unreplaced_scroll_id = clip_and_scroll.scroll_node_id;
         clip_and_scroll.scroll_node_id =
             context.apply_scroll_frame_id_replacement(clip_and_scroll.scroll_node_id);
 
         let prim_info = item.get_layer_primitive_info(&reference_frame_relative_offset);
         match *item.item() {
             SpecificDisplayItem::Image(ref info) => {
@@ -772,32 +661,23 @@ impl Frame {
                     &frame_rect,
                     &content_rect,
                     clip_region,
                     info.scroll_sensitivity,
                 );
             }
             SpecificDisplayItem::StickyFrame(ref info) => {
                 let frame_rect = item.rect().translate(&reference_frame_relative_offset);
-                let new_clip_id = context.convert_new_id_to_nested(&info.id);
                 self.clip_scroll_tree.add_sticky_frame(
-                    new_clip_id,
+                    info.id,
                     clip_and_scroll.scroll_node_id, /* parent id */
                     frame_rect,
                     info.sticky_frame_info,
                 );
             }
-            SpecificDisplayItem::PushNestedDisplayList => {
-                // Using the clip and scroll already processed for nesting here
-                // means that in the case of multiple nested display lists, we
-                // will enter the outermost ids into the table and avoid having
-                // to do a replacement for every level of nesting.
-                context.push_nested_display_list_ids(clip_and_scroll);
-            }
-            SpecificDisplayItem::PopNestedDisplayList => context.pop_nested_display_list_ids(),
 
             // Do nothing; these are dummy items for the display list parser
             SpecificDisplayItem::SetGradientStops => {}
 
             SpecificDisplayItem::PopStackingContext => {
                 unreachable!("Should have returned in parent method.")
             }
             SpecificDisplayItem::PushShadow(shadow) => {
--- a/gfx/webrender/src/frame_builder.rs
+++ b/gfx/webrender/src/frame_builder.rs
@@ -5,33 +5,34 @@
 use api::{BorderDetails, BorderDisplayItem, BorderRadius, BoxShadowClipMode, BuiltDisplayList};
 use api::{ClipAndScrollInfo, ClipId, ColorF};
 use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceUintRect, DeviceUintSize};
 use api::{ExtendMode, FIND_ALL, FilterOp, FontInstance, FontRenderMode};
 use api::{GlyphInstance, GlyphOptions, GradientStop, HitTestFlags, HitTestItem, HitTestResult};
 use api::{ImageKey, ImageRendering, ItemRange, ItemTag, LayerPoint, LayerPrimitiveInfo, LayerRect};
 use api::{LayerSize, LayerToScrollTransform, LayerVector2D, LayoutVector2D, LineOrientation};
 use api::{LineStyle, LocalClip, POINT_RELATIVE_TO_PIPELINE_VIEWPORT, PipelineId, RepeatMode};
-use api::{ScrollSensitivity, SubpixelDirection, Shadow, TileOffset, TransformStyle};
+use api::{ScrollSensitivity, Shadow, TileOffset, TransformStyle};
 use api::{WorldPixel, WorldPoint, YuvColorSpace, YuvData, device_length};
 use app_units::Au;
 use border::ImageBorderSegment;
 use clip::{ClipMode, ClipRegion, ClipSource, ClipSources, ClipStore, Contains};
 use clip_scroll_node::{ClipInfo, ClipScrollNode, NodeType};
 use clip_scroll_tree::ClipScrollTree;
 use euclid::{SideOffsets2D, vec2, vec3};
 use frame::FrameId;
 use gpu_cache::GpuCache;
 use internal_types::{FastHashMap, FastHashSet, HardwareCompositeOp};
+use picture::PicturePrimitive;
 use plane_split::{BspSplitter, Polygon, Splitter};
 use prim_store::{BoxShadowPrimitiveCpu, TexelRect, YuvImagePrimitiveCpu};
 use prim_store::{GradientPrimitiveCpu, ImagePrimitiveCpu, LinePrimitive, PrimitiveKind};
 use prim_store::{PrimitiveContainer, PrimitiveIndex};
 use prim_store::{PrimitiveStore, RadialGradientPrimitiveCpu};
-use prim_store::{RectanglePrimitive, TextRunPrimitiveCpu, ShadowPrimitiveCpu};
+use prim_store::{RectanglePrimitive, TextRunPrimitiveCpu};
 use profiler::{FrameProfileCounters, GpuCacheProfileCounters, TextureCacheProfileCounters};
 use render_task::{AlphaRenderItem, ClipWorkItem, RenderTask};
 use render_task::{RenderTaskId, RenderTaskLocation, RenderTaskTree};
 use resource_cache::ResourceCache;
 use scene::ScenePipeline;
 use std::{mem, usize, f32, i32};
 use tiling::{ClipScrollGroup, ClipScrollGroupIndex, CompositeOps, Frame};
 use tiling::{ContextIsolation, StackingContextIndex};
@@ -579,49 +580,46 @@ impl FrameBuilder {
     }
 
     pub fn push_shadow(
         &mut self,
         shadow: Shadow,
         clip_and_scroll: ClipAndScrollInfo,
         info: &LayerPrimitiveInfo,
     ) {
-        let prim = ShadowPrimitiveCpu {
-            shadow,
-            primitives: Vec::new(),
-            render_task_id: None,
-        };
+        let prim = PicturePrimitive::new_shadow(shadow);
 
         // Create an empty shadow primitive. Insert it into
         // the draw lists immediately so that it will be drawn
         // before any visual text elements that are added as
         // part of this shadow context.
         let prim_index = self.add_primitive(
             clip_and_scroll,
             info,
             Vec::new(),
-            PrimitiveContainer::Shadow(prim),
+            PrimitiveContainer::Picture(prim),
         );
 
         self.shadow_prim_stack.push(prim_index);
     }
 
     pub fn pop_shadow(&mut self) {
         let prim_index = self.shadow_prim_stack
             .pop()
             .expect("invalid shadow push/pop count");
 
         // By now, the local rect of the text shadow has been calculated. It
         // is calculated as the items in the shadow are added. It's now
         // safe to offset the local rect by the offset of the shadow, which
         // is then used when blitting the shadow to the final location.
         let metadata = &mut self.prim_store.cpu_metadata[prim_index.0];
-        let prim = &self.prim_store.cpu_shadows[metadata.cpu_prim_index.0];
+        let prim = &self.prim_store.cpu_pictures[metadata.cpu_prim_index.0];
+        let shadow = prim.as_shadow();
 
-        metadata.local_rect = metadata.local_rect.translate(&prim.shadow.offset);
+        metadata.local_rect = metadata.local_rect.translate(&shadow.offset);
     }
 
     pub fn add_solid_rectangle(
         &mut self,
         clip_and_scroll: ClipAndScrollInfo,
         info: &LayerPrimitiveInfo,
         color: &ColorF,
         flags: PrimitiveFlags,
@@ -681,19 +679,20 @@ impl FrameBuilder {
             color: *color,
             style: style,
             orientation: orientation,
         };
 
         let mut fast_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_shadows[shadow_metadata.cpu_prim_index.0];
-            if shadow_prim.shadow.blur_radius == 0.0 {
-                fast_shadow_prims.push(shadow_prim.shadow);
+            let picture = &self.prim_store.cpu_pictures[shadow_metadata.cpu_prim_index.0];
+            let shadow = picture.as_shadow();
+            if shadow.blur_radius == 0.0 {
+                fast_shadow_prims.push(shadow.clone());
             }
         }
         for shadow in fast_shadow_prims {
             let mut line = line.clone();
             line.color = shadow.color;
             let mut info = info.clone();
             info.rect = new_rect.translate(&shadow.offset);
             self.add_primitive(
@@ -715,28 +714,29 @@ impl FrameBuilder {
 
         if color.a > 0.0 {
             self.add_primitive_to_hit_testing_list(&info, clip_and_scroll);
             self.add_primitive_to_draw_list(prim_index, clip_and_scroll);
         }
 
         for shadow_prim_index in &self.shadow_prim_stack {
             let shadow_metadata = &mut self.prim_store.cpu_metadata[shadow_prim_index.0];
-            debug_assert_eq!(shadow_metadata.prim_kind, PrimitiveKind::Shadow);
-            let shadow_prim =
-                &mut self.prim_store.cpu_shadows[shadow_metadata.cpu_prim_index.0];
+            debug_assert_eq!(shadow_metadata.prim_kind, PrimitiveKind::Picture);
+            let picture =
+                &mut self.prim_store.cpu_pictures[shadow_metadata.cpu_prim_index.0];
+            let blur_radius = picture.as_shadow().blur_radius;
 
             // Only run real blurs here (fast path zero blurs are handled above).
-            if shadow_prim.shadow.blur_radius > 0.0 {
+            if blur_radius > 0.0 {
                 let shadow_rect = new_rect.inflate(
-                    shadow_prim.shadow.blur_radius,
-                    shadow_prim.shadow.blur_radius,
+                    blur_radius,
+                    blur_radius,
                 );
                 shadow_metadata.local_rect = shadow_metadata.local_rect.union(&shadow_rect);
-                shadow_prim.primitives.push(prim_index);
+                picture.add_primitive(prim_index, clip_and_scroll);
             }
         }
     }
 
     pub fn add_border(
         &mut self,
         clip_and_scroll: ClipAndScrollInfo,
         info: &LayerPrimitiveInfo,
@@ -1127,109 +1127,85 @@ impl FrameBuilder {
         // 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 default_render_mode = self.config
+        let mut 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);
+            render_mode = 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 render_mode == FontRenderMode::Subpixel {
             if color.a != 1.0 {
-                normal_render_mode = FontRenderMode::Alpha;
+                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
             //           the future, if we modify the way
             //           we handle subpixel blending.
             if let Some(sc_index) = self.stacking_context_stack.last() {
                 let stacking_context = &self.stacking_context_store[sc_index.0];
                 if stacking_context.composite_ops.count() > 0 {
-                    normal_render_mode = FontRenderMode::Alpha;
+                    render_mode = FontRenderMode::Alpha;
                 }
             }
         }
 
-        let color = match font.render_mode {
-            FontRenderMode::Bitmap => ColorF::new(1.0, 1.0, 1.0, 1.0),
-            FontRenderMode::Subpixel |
-            FontRenderMode::Alpha |
-            FontRenderMode::Mono => *color,
-        };
-
-        // Shadows never use subpixel AA, but need to respect the alpha/mono flag
-        // for reftests.
-        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, font.subpx_dir)
-            }
-            FontRenderMode::Mono => (FontRenderMode::Mono, SubpixelDirection::None),
-            FontRenderMode::Bitmap => (FontRenderMode::Bitmap, font.subpx_dir),
-        };
-
         let prim_font = FontInstance::new(
             font.font_key,
             font.size,
-            color,
-            normal_render_mode,
-            subpx_dir,
+            *color,
+            render_mode,
+            font.subpx_dir,
             font.platform_options,
             font.variations.clone(),
             font.synthetic_italics,
         );
         let prim = TextRunPrimitiveCpu {
             font: prim_font,
             glyph_range,
             glyph_count,
             glyph_gpu_blocks: Vec::new(),
             glyph_keys: Vec::new(),
-            shadow_render_mode,
             offset: run_offset,
-            color: color,
         };
 
         // 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_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_shadows[shadow_metadata.cpu_prim_index.0];
-            if shadow_prim.shadow.blur_radius == 0.0 {
+            let picture_prim = &self.prim_store.cpu_pictures[shadow_metadata.cpu_prim_index.0];
+            let shadow = picture_prim.as_shadow();
+            if shadow.blur_radius == 0.0 {
                 let mut text_prim = prim.clone();
-                if font.render_mode != FontRenderMode::Bitmap {
-                    text_prim.font.color = shadow_prim.shadow.color.into();
-                }
+                text_prim.font.color = shadow.color.into();
                 // If we have translucent text, we need to ensure it won't go
                 // through the subpixel blend mode, which doesn't work with
                 // traditional alpha blending.
-                if shadow_prim.shadow.color.a != 1.0 {
+                if shadow.color.a != 1.0 {
                     text_prim.font.render_mode = text_prim.font.render_mode.limit_by(FontRenderMode::Alpha);
                 }
-                text_prim.color = shadow_prim.shadow.color;
-                text_prim.offset += shadow_prim.shadow.offset;
+                text_prim.offset += shadow.offset;
                 fast_shadow_prims.push(text_prim);
             }
         }
         for text_prim in fast_shadow_prims {
             let rect = info.rect;
             let mut info = info.clone();
             info.rect = rect.translate(&text_prim.offset);
             self.add_primitive(
@@ -1259,28 +1235,29 @@ impl FrameBuilder {
         // primitives. Although we're adding the indices *after* the visual
         // primitive here, they will still draw before the visual text, since
         // the shadow primitive itself has been added to the draw cmd
         // list *before* the visual element, during push_shadow. We need
         // the primitive index of the visual element here before we can add
         // the indices as sub-primitives to the shadow primitives.
         for shadow_prim_index in &self.shadow_prim_stack {
             let shadow_metadata = &mut self.prim_store.cpu_metadata[shadow_prim_index.0];
-            debug_assert_eq!(shadow_metadata.prim_kind, PrimitiveKind::Shadow);
-            let shadow_prim =
-                &mut self.prim_store.cpu_shadows[shadow_metadata.cpu_prim_index.0];
+            debug_assert_eq!(shadow_metadata.prim_kind, PrimitiveKind::Picture);
+            let picture_prim =
+                &mut self.prim_store.cpu_pictures[shadow_metadata.cpu_prim_index.0];
 
             // Only run real blurs here (fast path zero blurs are handled above).
-            if shadow_prim.shadow.blur_radius > 0.0 {
+            let blur_radius = picture_prim.as_shadow().blur_radius;
+            if blur_radius > 0.0 {
                 let shadow_rect = rect.inflate(
-                    shadow_prim.shadow.blur_radius,
-                    shadow_prim.shadow.blur_radius,
+                    blur_radius,
+                    blur_radius,
                 );
                 shadow_metadata.local_rect = shadow_metadata.local_rect.union(&shadow_rect);
-                shadow_prim.primitives.push(prim_index);
+                picture_prim.add_primitive(prim_index, clip_and_scroll);
             }
         }
     }
 
     pub fn fill_box_shadow_rect(
         &mut self,
         clip_and_scroll: ClipAndScrollInfo,
         info: &LayerPrimitiveInfo,
--- a/gfx/webrender/src/glyph_cache.rs
+++ b/gfx/webrender/src/glyph_cache.rs
@@ -8,16 +8,17 @@ use resource_cache::ResourceClassCache;
 use std::sync::Arc;
 use texture_cache::TextureCacheHandle;
 
 pub struct CachedGlyphInfo {
     pub texture_cache_handle: TextureCacheHandle,
     pub glyph_bytes: Arc<Vec<u8>>,
     pub size: DeviceUintSize,
     pub offset: DevicePoint,
+    pub scale: f32,
 }
 
 pub type GlyphKeyCache = ResourceClassCache<GlyphKey, Option<CachedGlyphInfo>>;
 
 pub struct GlyphCache {
     pub glyph_key_caches: FastHashMap<FontInstance, GlyphKeyCache>,
 }
 
--- a/gfx/webrender/src/glyph_rasterizer.rs
+++ b/gfx/webrender/src/glyph_rasterizer.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/. */
 
 #[cfg(test)]
-use api::{ColorF, FontRenderMode, IdNamespace, LayoutPoint, SubpixelDirection};
+use api::{ColorF, IdNamespace, LayoutPoint};
 use api::{DevicePoint, DeviceUintSize, FontInstance};
-use api::{FontKey, FontTemplate};
-use api::{GlyphDimensions, GlyphKey};
+use api::{FontKey, FontTemplate, FontRenderMode, ColorU};
+use api::{GlyphDimensions, GlyphKey, SubpixelDirection};
 use api::{ImageData, ImageDescriptor, ImageFormat};
 #[cfg(test)]
 use app_units::Au;
 use device::TextureFilter;
 use glyph_cache::{CachedGlyphInfo, GlyphCache};
 use gpu_cache::GpuCache;
 use internal_types::FastHashSet;
 use platform::font::{FontContext, RasterizedGlyph};
@@ -139,16 +139,39 @@ impl GlyphRasterizer {
                 .add_font(&font_key, &template);
         }
     }
 
     pub fn delete_font(&mut self, font_key: FontKey) {
         self.fonts_to_remove.push(font_key);
     }
 
+    pub fn prepare_font(&self, font: &mut 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.
+        match font.render_mode {
+            FontRenderMode::Mono | FontRenderMode::Bitmap => {
+                font.color = ColorU::new(255, 255, 255, 255);
+                // Subpixel positioning is disabled in mono and bitmap modes.
+                font.subpx_dir = SubpixelDirection::None;
+            }
+            FontRenderMode::Alpha => {
+                font.color = ColorU::new(255, 255, 255, 255);
+            }
+            FontRenderMode::Subpixel => {
+                // In subpixel mode, we only actually need the color if preblending
+                // is used in the font backend.
+                if !FontContext::has_gamma_correct_subpixel_aa() {
+                    font.color = ColorU::new(255, 255, 255, 255);
+                }
+            }
+        }
+    }
+
     pub fn request_glyphs(
         &mut self,
         glyph_cache: &mut GlyphCache,
         font: FontInstance,
         glyph_keys: &[GlyphKey],
         texture_cache: &mut TextureCache,
         gpu_cache: &mut GpuCache,
     ) {
@@ -178,17 +201,17 @@ impl GlyphRasterizer {
                                     height: glyph_info.size.height,
                                     stride: None,
                                     format: ImageFormat::BGRA8,
                                     is_opaque: false,
                                     offset: 0,
                                 },
                                 TextureFilter::Linear,
                                 ImageData::Raw(glyph_info.glyph_bytes.clone()),
-                                [glyph_info.offset.x, glyph_info.offset.y],
+                                [glyph_info.offset.x, glyph_info.offset.y, glyph_info.scale],
                                 None,
                                 gpu_cache,
                             );
                         }
                     }
                 }
                 Entry::Vacant(..) => {
                     let request = GlyphRequest::new(&font, key);
@@ -241,20 +264,20 @@ impl GlyphRasterizer {
         font: &FontInstance,
         glyph_key: &GlyphKey,
     ) -> Option<GlyphDimensions> {
         self.font_contexts
             .lock_shared_context()
             .get_glyph_dimensions(font, glyph_key)
     }
 
-    pub fn is_bitmap_font(&self, font_key: FontKey) -> bool {
+    pub fn is_bitmap_font(&self, font: &FontInstance) -> bool {
         self.font_contexts
             .lock_shared_context()
-            .is_bitmap_font(font_key)
+            .is_bitmap_font(font)
     }
 
     pub fn get_glyph_index(&mut self, font_key: FontKey, ch: char) -> Option<u32> {
         self.font_contexts
             .lock_shared_context()
             .get_glyph_index(font_key, ch)
     }
 
@@ -307,25 +330,26 @@ impl GlyphRasterizer {
                             height: glyph.height,
                             stride: None,
                             format: ImageFormat::BGRA8,
                             is_opaque: false,
                             offset: 0,
                         },
                         TextureFilter::Linear,
                         ImageData::Raw(glyph_bytes.clone()),
-                        [glyph.left, glyph.top],
+                        [glyph.left, glyph.top, glyph.scale],
                         None,
                         gpu_cache,
                     );
                     Some(CachedGlyphInfo {
                         texture_cache_handle,
                         glyph_bytes,
                         size: DeviceUintSize::new(glyph.width, glyph.height),
                         offset: DevicePoint::new(glyph.left, glyph.top),
+                        scale: glyph.scale,
                     })
                 } else {
                     None
                 });
 
             let glyph_key_cache = glyph_cache.get_glyph_key_cache_for_font_mut(job.request.font);
 
             glyph_key_cache.insert(job.request.key, glyph_info);
--- a/gfx/webrender/src/lib.rs
+++ b/gfx/webrender/src/lib.rs
@@ -64,16 +64,17 @@ mod frame;
 mod frame_builder;
 mod freelist;
 mod geometry;
 mod glyph_cache;
 mod glyph_rasterizer;
 mod gpu_cache;
 mod gpu_types;
 mod internal_types;
+mod picture;
 mod prim_store;
 mod print_tree;
 mod profiler;
 mod record;
 mod render_backend;
 mod render_task;
 mod renderer;
 mod resource_cache;
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/src/picture.rs
@@ -0,0 +1,79 @@
+/* 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::{ClipAndScrollInfo, Shadow};
+use prim_store::PrimitiveIndex;
+use render_task::RenderTaskId;
+
+/*
+ A picture represents a dynamically rendered image. It consists of:
+
+ * A number of primitives that are drawn onto the picture.
+ * A composite operation describing how to composite this
+   picture into its parent.
+ * A configuration describing how to draw the primitives on
+   this picture (e.g. in screen space or local space).
+ */
+
+#[derive(Clone, Debug)]
+pub struct PrimitiveRun {
+    pub prim_index: PrimitiveIndex,
+    pub count: usize,
+    pub clip_and_scroll: ClipAndScrollInfo,
+}
+
+#[derive(Debug)]
+pub enum CompositeOp {
+    Shadow(Shadow),
+
+    // TODO(gw): Support other composite ops, such
+    //           as blur, blend etc.
+}
+
+#[derive(Debug)]
+pub struct PicturePrimitive {
+    pub prim_runs: Vec<PrimitiveRun>,
+    pub composite_op: CompositeOp,
+    pub render_task_id: Option<RenderTaskId>,
+
+    // TODO(gw): Add a mode that specifies if this
+    //           picture should be rasterized in
+    //           screen-space or local-space.
+}
+
+impl PicturePrimitive {
+    pub fn new_shadow(shadow: Shadow) -> PicturePrimitive {
+        PicturePrimitive {
+            prim_runs: Vec::new(),
+            composite_op: CompositeOp::Shadow(shadow),
+            render_task_id: None,
+        }
+    }
+
+    pub fn as_shadow(&self) -> &Shadow {
+        match self.composite_op {
+            CompositeOp::Shadow(ref shadow) => shadow,
+        }
+    }
+
+    pub fn add_primitive(
+        &mut self,
+        prim_index: PrimitiveIndex,
+        clip_and_scroll: ClipAndScrollInfo
+    ) {
+        if let Some(ref mut run) = self.prim_runs.last_mut() {
+            if run.clip_and_scroll == clip_and_scroll &&
+               run.prim_index.0 + run.count == prim_index.0 {
+                run.count += 1;
+                return;
+            }
+        }
+
+        self.prim_runs.push(PrimitiveRun {
+            prim_index,
+            count: 1,
+            clip_and_scroll,
+        });
+    }
+}
--- a/gfx/webrender/src/platform/macos/font.rs
+++ b/gfx/webrender/src/platform/macos/font.rs
@@ -37,26 +37,28 @@ pub struct FontContext {
 // all hidden inside their font context.
 unsafe impl Send for FontContext {}
 
 pub struct RasterizedGlyph {
     pub top: f32,
     pub left: f32,
     pub width: u32,
     pub height: u32,
+    pub scale: f32,
     pub bytes: Vec<u8>,
 }
 
 impl RasterizedGlyph {
     pub fn blank() -> RasterizedGlyph {
         RasterizedGlyph {
             top: 0.0,
             left: 0.0,
             width: 0,
             height: 0,
+            scale: 1.0,
             bytes: vec![],
         }
     }
 }
 
 struct GlyphMetrics {
     rasterized_left: i32,
     rasterized_descent: i32,
@@ -417,28 +419,30 @@ impl FontContext {
                 let r = pixel[2];
                 let a = pixel[3];
                 print!("({}, {}, {}, {}) ", r, g, b, a);
             }
             println!("");
         }
     }
 
-    pub fn is_bitmap_font(&mut self, font_key: FontKey) -> bool {
-        match self.get_ct_font(font_key, Au(16 * 60), &[]) {
+    pub fn is_bitmap_font(&mut self, font: &FontInstance) -> bool {
+        match self.get_ct_font(font.font_key, font.size, &font.variations) {
             Some(ref ct_font) => {
                 let traits = ct_font.symbolic_traits();
                 (traits & kCTFontColorGlyphsTrait) != 0
             }
-            None => {
-                false
-            }
+            None => false,
         }
     }
 
+    pub fn has_gamma_correct_subpixel_aa() -> bool {
+        true
+    }
+
     pub fn rasterize_glyph(
         &mut self,
         font: &FontInstance,
         key: &GlyphKey,
     ) -> Option<RasterizedGlyph> {
         let ct_font = match self.get_ct_font(font.font_key, font.size, &font.variations) {
             Some(font) => font,
             None => return Some(RasterizedGlyph::blank()),
@@ -580,12 +584,13 @@ impl FontContext {
             );
         }
 
         Some(RasterizedGlyph {
             left: metrics.rasterized_left as f32,
             top: metrics.rasterized_ascent as f32,
             width: metrics.rasterized_width,
             height: metrics.rasterized_height,
+            scale: 1.0,
             bytes: rasterized_pixels,
         })
     }
 }
--- a/gfx/webrender/src/platform/unix/font.rs
+++ b/gfx/webrender/src/platform/unix/font.rs
@@ -1,36 +1,40 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{FontInstance, FontKey, FontRenderMode, GlyphDimensions};
-use api::{NativeFontHandle, SubpixelDirection};
-use api::GlyphKey;
+use api::{FontInstancePlatformOptions, FontLCDFilter, FontHinting};
+use api::{NativeFontHandle, SubpixelDirection, GlyphKey};
+use api::{FONT_FORCE_AUTOHINT, FONT_NO_AUTOHINT, FONT_EMBEDDED_BITMAP};
+use api::{FONT_EMBOLDEN, FONT_VERTICAL_LAYOUT, FONT_SUBPIXEL_BGR};
 use freetype::freetype::{FT_BBox, FT_Outline_Translate, FT_Pixel_Mode, FT_Render_Mode};
 use freetype::freetype::{FT_Done_Face, FT_Error, FT_Get_Char_Index, FT_Int32};
 use freetype::freetype::{FT_Done_FreeType, FT_Library_SetLcdFilter, FT_Pos};
 use freetype::freetype::{FT_F26Dot6, FT_Face, FT_Glyph_Format, FT_Long, FT_UInt};
-use freetype::freetype::{FT_GlyphSlot, FT_LcdFilter, FT_New_Memory_Face, FT_Outline_Transform};
+use freetype::freetype::{FT_GlyphSlot, FT_LcdFilter, FT_New_Memory_Face};
 use freetype::freetype::{FT_Init_FreeType, FT_Load_Glyph, FT_Render_Glyph};
-use freetype::freetype::{FT_Library, FT_Matrix, FT_Outline_Get_CBox, FT_Set_Char_Size};
+use freetype::freetype::{FT_Library, FT_Outline_Get_CBox, FT_Set_Char_Size, FT_Select_Size};
+use freetype::freetype::{FT_LOAD_COLOR, FT_LOAD_DEFAULT, FT_LOAD_FORCE_AUTOHINT};
+use freetype::freetype::{FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH, FT_LOAD_NO_AUTOHINT};
+use freetype::freetype::{FT_LOAD_NO_BITMAP, FT_LOAD_NO_HINTING, FT_LOAD_VERTICAL_LAYOUT};
+use freetype::freetype::{FT_FACE_FLAG_SCALABLE, FT_FACE_FLAG_FIXED_SIZES, FT_Err_Cannot_Render_Glyph};
 use internal_types::FastHashMap;
-use std::{mem, ptr, slice};
+use std::{cmp, mem, ptr, slice};
 use std::sync::Arc;
 
-// This constant is not present in the freetype
+// These constants are not present in the freetype
 // bindings due to bindgen not handling the way
-// the macro is defined.
-const FT_LOAD_TARGET_LIGHT: FT_Int32 = 1 << 16;
-
-// Default to slight hinting, which is what most
-// Linux distros use by default, and is a better
-// default than no hinting.
-// TODO(gw): Make this configurable.
-const GLYPH_LOAD_FLAGS: FT_Int32 = FT_LOAD_TARGET_LIGHT;
+// the macros are defined.
+//const FT_LOAD_TARGET_NORMAL: FT_UInt = 0 << 16;
+const FT_LOAD_TARGET_LIGHT: FT_UInt  = 1 << 16;
+const FT_LOAD_TARGET_MONO: FT_UInt   = 2 << 16;
+const FT_LOAD_TARGET_LCD: FT_UInt    = 3 << 16;
+const FT_LOAD_TARGET_LCD_V: FT_UInt  = 4 << 16;
 
 struct Face {
     face: FT_Face,
     // Raw byte data has to live until the font is deleted, according to
     // https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_New_Memory_Face
     _bytes: Arc<Vec<u8>>,
 }
 
@@ -45,51 +49,49 @@ pub struct FontContext {
 // a given FontContext so it is safe to move the latter between threads.
 unsafe impl Send for FontContext {}
 
 pub struct RasterizedGlyph {
     pub top: f32,
     pub left: f32,
     pub width: u32,
     pub height: u32,
+    pub scale: f32,
     pub bytes: Vec<u8>,
 }
 
-const SUCCESS: FT_Error = FT_Error(0);
+extern "C" {
+    fn FT_GlyphSlot_Embolden(slot: FT_GlyphSlot);
+    fn FT_GlyphSlot_Oblique(slot: FT_GlyphSlot);
+}
 
 impl FontContext {
     pub fn new() -> FontContext {
         let mut lib: FT_Library = ptr::null_mut();
 
-        // Per Skia, using a filter adds one full pixel to each side.
-        let mut lcd_extra_pixels = 1;
+        // Using an LCD filter may add one full pixel to each side if support is built in.
+        // As of FreeType 2.8.1, an LCD filter is always used regardless of settings
+        // if support for the patent-encumbered LCD filter algorithms is not built in.
+        // Thus, the only reasonable way to guess padding is to unconditonally add it if
+        // subpixel AA is used.
+        let lcd_extra_pixels = 1;
 
         unsafe {
             let result = FT_Init_FreeType(&mut lib);
             assert!(
                 result.succeeded(),
                 "Unable to initialize FreeType library {:?}",
                 result
             );
-
-            // TODO(gw): Check result of this to determine if freetype build supports subpixel.
-            let result = FT_Library_SetLcdFilter(lib, FT_LcdFilter::FT_LCD_FILTER_DEFAULT);
-
-            if !result.succeeded() {
-                println!(
-                    "WARN: Initializing a FreeType library build without subpixel AA enabled!"
-                );
-                lcd_extra_pixels = 0;
-            }
         }
 
         FontContext {
             lib,
             faces: FastHashMap::default(),
-            lcd_extra_pixels: lcd_extra_pixels,
+            lcd_extra_pixels,
         }
     }
 
     pub fn has_font(&self, font_key: &FontKey) -> bool {
         self.faces.contains_key(font_key)
     }
 
     pub fn add_raw_font(&mut self, font_key: &FontKey, bytes: Arc<Vec<u8>>, index: u32) {
@@ -127,35 +129,81 @@ impl FontContext {
             let result = unsafe { FT_Done_Face(face.face) };
             assert!(result.succeeded());
         }
     }
 
     fn load_glyph(&self, font: &FontInstance, glyph: &GlyphKey) -> Option<FT_GlyphSlot> {
         debug_assert!(self.faces.contains_key(&font.font_key));
         let face = self.faces.get(&font.font_key).unwrap();
-        let char_size = font.size.to_f64_px() * 64.0 + 0.5;
+
+        let mut load_flags = FT_LOAD_DEFAULT;
+        let FontInstancePlatformOptions { flags, hinting, .. } = font.platform_options.unwrap_or_default();
+        match (hinting, font.render_mode) {
+            (FontHinting::None, _) => load_flags |= FT_LOAD_NO_HINTING,
+            (FontHinting::Mono, _) => load_flags = FT_LOAD_TARGET_MONO,
+            (FontHinting::Light, _) => load_flags = FT_LOAD_TARGET_LIGHT,
+            (FontHinting::LCD, FontRenderMode::Subpixel) => {
+                load_flags = match font.subpx_dir {
+                    SubpixelDirection::Vertical => FT_LOAD_TARGET_LCD_V,
+                    _ => FT_LOAD_TARGET_LCD,
+                };
+                if (flags & FONT_FORCE_AUTOHINT) != 0 {
+                    load_flags |= FT_LOAD_FORCE_AUTOHINT;
+                }
+            }
+            _ => {
+                if (flags & FONT_FORCE_AUTOHINT) != 0 {
+                    load_flags |= FT_LOAD_FORCE_AUTOHINT;
+                }
+            }
+        }
 
-        assert_eq!(SUCCESS, unsafe {
-            FT_Set_Char_Size(face.face, char_size as FT_F26Dot6, 0, 0, 0)
-        });
+        if (flags & FONT_NO_AUTOHINT) != 0 {
+            load_flags |= FT_LOAD_NO_AUTOHINT;
+        }
+        if (flags & FONT_EMBEDDED_BITMAP) == 0 {
+            load_flags |= FT_LOAD_NO_BITMAP;
+        }
+        if (flags & FONT_VERTICAL_LAYOUT) != 0 {
+            load_flags |= FT_LOAD_VERTICAL_LAYOUT;
+        }
+
+        load_flags |= FT_LOAD_COLOR;
+        load_flags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
 
-        let result = unsafe { FT_Load_Glyph(face.face, glyph.index as FT_UInt, GLYPH_LOAD_FLAGS) };
+        let mut result = if font.render_mode == FontRenderMode::Bitmap {
+            if (load_flags & FT_LOAD_NO_BITMAP) != 0 {
+                FT_Error(FT_Err_Cannot_Render_Glyph as i32)
+            } else {
+                self.choose_bitmap_size(face.face, font.size.to_f64_px())
+            }
+        } else {
+            let char_size = font.size.to_f64_px() * 64.0 + 0.5;
+            unsafe { FT_Set_Char_Size(face.face, char_size as FT_F26Dot6, 0, 0, 0) }
+        };
 
-        if result == SUCCESS {
+        if result.succeeded() {
+            result = unsafe { FT_Load_Glyph(face.face, glyph.index as FT_UInt, load_flags as FT_Int32) };
+        };
+
+        if result.succeeded() {
             let slot = unsafe { (*face.face).glyph };
             assert!(slot != ptr::null_mut());
 
-            // TODO(gw): We use the FT_Outline_* APIs to manage sub-pixel offsets.
-            //           We will need a custom code path for bitmap fonts (which
-            //           are very rare).
-            match unsafe { (*slot).format } {
-                FT_Glyph_Format::FT_GLYPH_FORMAT_OUTLINE => Some(slot),
+            if (flags & FONT_EMBOLDEN) != 0 {
+                unsafe { FT_GlyphSlot_Embolden(slot) };
+            }
+
+            let format = unsafe { (*slot).format };
+            match format {
+                FT_Glyph_Format::FT_GLYPH_FORMAT_OUTLINE |
+                FT_Glyph_Format::FT_GLYPH_FORMAT_BITMAP => Some(slot),
                 _ => {
-                    error!("TODO: Support bitmap fonts!");
+                    error!("Unsupported {:?}", format);
                     None
                 }
             }
         } else {
             error!(
                 "Unable to load glyph for {} of size {:?} from font {:?}, {:?}",
                 glyph.index,
                 font.size,
@@ -173,44 +221,47 @@ impl FontContext {
         font: &FontInstance,
         glyph: &GlyphKey,
     ) -> FT_BBox {
         let mut cbox: FT_BBox = unsafe { mem::uninitialized() };
 
         // Get the estimated bounding box from FT (control points).
         unsafe {
             FT_Outline_Get_CBox(&(*slot).outline, &mut cbox);
+
+            // For spaces and other non-printable characters, early out.
+            if (*slot).outline.n_contours == 0 {
+                return cbox;
+            }
         }
 
         // Convert the subpixel offset to floats.
         let (dx, dy) = font.get_subpx_offset(glyph);
 
         // Apply extra pixel of padding for subpixel AA, due to the filter.
         let padding = match font.render_mode {
-            FontRenderMode::Subpixel => self.lcd_extra_pixels * 64,
+            FontRenderMode::Subpixel => (self.lcd_extra_pixels * 64) as FT_Pos,
             FontRenderMode::Alpha |
             FontRenderMode::Mono |
-            FontRenderMode::Bitmap => 0,
+            FontRenderMode::Bitmap => 0 as FT_Pos,
         };
-        cbox.xMin -= padding as FT_Pos;
-        cbox.xMax += padding as FT_Pos;
 
         // Offset the bounding box by subpixel positioning.
         // Convert to 26.6 fixed point format for FT.
         match font.subpx_dir {
             SubpixelDirection::None => {}
             SubpixelDirection::Horizontal => {
                 let dx = (dx * 64.0 + 0.5) as FT_Long;
-                cbox.xMin += dx;
-                cbox.xMax += dx;
+                cbox.xMin += dx - padding;
+                cbox.xMax += dx + padding;
             }
             SubpixelDirection::Vertical => {
                 let dy = (dy * 64.0 + 0.5) as FT_Long;
-                cbox.yMin += dy;
-                cbox.yMax += dy;
+                cbox.yMin += dy - padding;
+                cbox.yMax += dy + padding;
             }
         }
 
         // Outset the box to device pixel boundaries
         cbox.xMin &= !63;
         cbox.yMin &= !63;
         cbox.xMax = (cbox.xMax + 63) & !63;
         cbox.yMax = (cbox.yMax + 63) & !63;
@@ -218,33 +269,68 @@ impl FontContext {
         cbox
     }
 
     fn get_glyph_dimensions_impl(
         &self,
         slot: FT_GlyphSlot,
         font: &FontInstance,
         glyph: &GlyphKey,
+        scale_bitmaps: bool,
     ) -> Option<GlyphDimensions> {
         let metrics = unsafe { &(*slot).metrics };
 
         // If there's no advance, no need to consider this glyph
         // for layout.
         if metrics.horiAdvance == 0 {
-            None
-        } else {
-            let cbox = self.get_bounding_box(slot, font, glyph);
+            return None
+        }
 
-            Some(GlyphDimensions {
-                left: (cbox.xMin >> 6) as i32,
-                top: (cbox.yMax >> 6) as i32,
-                width: ((cbox.xMax - cbox.xMin) >> 6) as u32,
-                height: ((cbox.yMax - cbox.yMin) >> 6) as u32,
-                advance: metrics.horiAdvance as f32 / 64.0,
-            })
+        let advance = metrics.horiAdvance as f32 / 64.0;
+        match unsafe { (*slot).format } {
+            FT_Glyph_Format::FT_GLYPH_FORMAT_BITMAP => {
+                let left = unsafe { (*slot).bitmap_left };
+                let top = unsafe { (*slot).bitmap_top };
+                let width = unsafe { (*slot).bitmap.width };
+                let height = unsafe { (*slot).bitmap.rows };
+                if scale_bitmaps {
+                    let y_size = unsafe { (*(*(*slot).face).size).metrics.y_ppem };
+                    let scale = font.size.to_f32_px() / y_size as f32;
+                    let x0 = left as f32 * scale;
+                    let x1 = width as f32 * scale + x0;
+                    let y1 = top as f32 * scale;
+                    let y0 = y1 - height as f32 * scale;
+                    Some(GlyphDimensions {
+                        left: x0.round() as i32,
+                        top: y1.round() as i32,
+                        width: (x1.ceil() - x0.floor()) as u32,
+                        height: (y1.ceil() - y0.floor()) as u32,
+                        advance: advance * scale,
+                    })
+                } else {
+                    Some(GlyphDimensions {
+                        left,
+                        top,
+                        width,
+                        height,
+                        advance,
+                    })
+                }
+            }
+            FT_Glyph_Format::FT_GLYPH_FORMAT_OUTLINE => {
+                let cbox = self.get_bounding_box(slot, font, glyph);
+                Some(GlyphDimensions {
+                    left: (cbox.xMin >> 6) as i32,
+                    top: (cbox.yMax >> 6) as i32,
+                    width: ((cbox.xMax - cbox.xMin) >> 6) as u32,
+                    height: ((cbox.yMax - cbox.yMin) >> 6) as u32,
+                    advance,
+                })
+            }
+            _ => None,
         }
     }
 
     pub fn get_glyph_index(&mut self, font_key: FontKey, ch: char) -> Option<u32> {
         let face = self.faces.get(&font_key).expect("Unknown font key!");
         unsafe {
             let idx = FT_Get_Char_Index(face.face, ch as _);
             if idx != 0 {
@@ -256,52 +342,64 @@ impl FontContext {
     }
 
     pub fn get_glyph_dimensions(
         &mut self,
         font: &FontInstance,
         key: &GlyphKey,
     ) -> Option<GlyphDimensions> {
         let slot = self.load_glyph(font, key);
-        slot.and_then(|slot| self.get_glyph_dimensions_impl(slot, font, key))
+        slot.and_then(|slot| self.get_glyph_dimensions_impl(slot, font, key, true))
+    }
+
+    pub fn is_bitmap_font(&mut self, font: &FontInstance) -> bool {
+        debug_assert!(self.faces.contains_key(&font.font_key));
+        let face = self.faces.get(&font.font_key).unwrap();
+        let face_flags = unsafe { (*face.face).face_flags };
+        // If the face has embedded bitmaps, they should only be used if either
+        // embedded bitmaps are explicitly requested or if the face has no outline.
+        if (face_flags & (FT_FACE_FLAG_FIXED_SIZES as FT_Long)) != 0 {
+            let FontInstancePlatformOptions { flags, .. } = font.platform_options.unwrap_or_default();
+            if (flags & FONT_EMBEDDED_BITMAP) != 0 {
+                return true;
+            }
+            (face_flags & (FT_FACE_FLAG_SCALABLE as FT_Long)) == 0
+        } else {
+            false
+        }
     }
 
-    pub fn is_bitmap_font(&mut self, _font_key: FontKey) -> bool {
-        // TODO(gw): Support bitmap fonts in Freetype.
+    fn choose_bitmap_size(&self, face: FT_Face, requested_size: f64) -> FT_Error {
+        let mut best_dist = unsafe { *(*face).available_sizes.offset(0) }.y_ppem as f64 / 64.0 - requested_size;
+        let mut best_size = 0;
+        let num_fixed_sizes = unsafe { (*face).num_fixed_sizes };
+        for i in 1 .. num_fixed_sizes {
+            // Distance is positive if strike is larger than desired size,
+            // or negative if smaller. If previously a found smaller strike,
+            // then prefer a larger strike. Otherwise, minimize distance.
+            let dist = unsafe { *(*face).available_sizes.offset(i as isize) }.y_ppem as f64 / 64.0 - requested_size;
+            if (best_dist < 0.0 && dist >= best_dist) || dist.abs() <= best_dist {
+                best_dist = dist;
+                best_size = i;
+            }
+        }
+        unsafe { FT_Select_Size(face, best_size) }
+    }
+
+    pub fn has_gamma_correct_subpixel_aa() -> bool {
+        // We don't do any preblending with FreeType currently, so the color is not used.
         false
     }
 
-    pub fn rasterize_glyph(
+    fn rasterize_glyph_outline(
         &mut self,
+        slot: FT_GlyphSlot,
         font: &FontInstance,
         key: &GlyphKey,
-    ) -> Option<RasterizedGlyph> {
-        let slot = match self.load_glyph(font, key) {
-            Some(slot) => slot,
-            None => return None,
-        };
-
-        let render_mode = match font.render_mode {
-            FontRenderMode::Mono => FT_Render_Mode::FT_RENDER_MODE_MONO,
-            FontRenderMode::Alpha => FT_Render_Mode::FT_RENDER_MODE_NORMAL,
-            FontRenderMode::Subpixel => FT_Render_Mode::FT_RENDER_MODE_LCD,
-            FontRenderMode::Bitmap => FT_Render_Mode::FT_RENDER_MODE_NORMAL,
-        };
-
-        // Get dimensions of the glyph, to see if we need to rasterize it.
-        let dimensions = match self.get_glyph_dimensions_impl(slot, font, key) {
-            Some(val) => val,
-            None => return None,
-        };
-
-        // For spaces and other non-printable characters, early out.
-        if dimensions.width == 0 || dimensions.height == 0 {
-            return None;
-        }
-
+    ) -> bool {
         // Get the subpixel offsets in FT 26.6 format.
         let (dx, dy) = font.get_subpx_offset(key);
         let dx = (dx * 64.0 + 0.5) as FT_Long;
         let dy = (dy * 64.0 + 0.5) as FT_Long;
 
         // Move the outline curves to be at the origin, taking
         // into account the subpixel positioning.
         unsafe {
@@ -310,103 +408,214 @@ impl FontContext {
             FT_Outline_Get_CBox(outline, &mut cbox);
             FT_Outline_Translate(
                 outline,
                 dx - ((cbox.xMin + dx) & !63),
                 dy - ((cbox.yMin + dy) & !63),
             );
 
             if font.synthetic_italics {
-                // These magic numbers are pre-encoded fixed point
-                // values that apply ~12 degree shear. Borrowed
-                // from the Freetype implementation of the
-                // FT_GlyphSlot_Oblique function.
-                let transform = FT_Matrix {
-                    xx: 0x10000,
-                    yx: 0x00000,
-                    xy: 0x0366A,
-                    yy: 0x10000,
-                };
-                FT_Outline_Transform(outline, &transform);
+                FT_GlyphSlot_Oblique(slot);
             }
         }
 
+        if font.render_mode == FontRenderMode::Subpixel {
+            let FontInstancePlatformOptions { lcd_filter, .. } = font.platform_options.unwrap_or_default();
+            let filter = match lcd_filter {
+                FontLCDFilter::None => FT_LcdFilter::FT_LCD_FILTER_NONE,
+                FontLCDFilter::Default => FT_LcdFilter::FT_LCD_FILTER_DEFAULT,
+                FontLCDFilter::Light => FT_LcdFilter::FT_LCD_FILTER_LIGHT,
+                FontLCDFilter::Legacy => FT_LcdFilter::FT_LCD_FILTER_LEGACY,
+            };
+            unsafe { FT_Library_SetLcdFilter(self.lib, filter) };
+        }
+        let render_mode = match (font.render_mode, font.subpx_dir) {
+            (FontRenderMode::Mono, _) => FT_Render_Mode::FT_RENDER_MODE_MONO,
+            (FontRenderMode::Alpha, _) | (FontRenderMode::Bitmap, _) => FT_Render_Mode::FT_RENDER_MODE_NORMAL,
+            (FontRenderMode::Subpixel, SubpixelDirection::Vertical) => FT_Render_Mode::FT_RENDER_MODE_LCD_V,
+            (FontRenderMode::Subpixel, _) => FT_Render_Mode::FT_RENDER_MODE_LCD,
+        };
         let result = unsafe { FT_Render_Glyph(slot, render_mode) };
-        if result != SUCCESS {
+        if !result.succeeded() {
             error!(
                 "Unable to rasterize {:?} with {:?}, {:?}",
                 key,
                 render_mode,
                 result
             );
+            false
+        } else {
+            true
+        }
+    }
+
+    pub fn rasterize_glyph(
+        &mut self,
+        font: &FontInstance,
+        key: &GlyphKey,
+    ) -> Option<RasterizedGlyph> {
+        let slot = match self.load_glyph(font, key) {
+            Some(slot) => slot,
+            None => return None,
+        };
+
+        // Get dimensions of the glyph, to see if we need to rasterize it.
+        let dimensions = match self.get_glyph_dimensions_impl(slot, font, key, false) {
+            Some(val) => val,
+            None => return None,
+        };
+
+        // For spaces and other non-printable characters, early out.
+        if dimensions.width == 0 || dimensions.height == 0 {
             return None;
         }
 
+        let format = unsafe { (*slot).format };
+        let mut scale = 1.0;
+        match format {
+            FT_Glyph_Format::FT_GLYPH_FORMAT_BITMAP => {
+                let y_size = unsafe { (*(*(*slot).face).size).metrics.y_ppem };
+                scale = font.size.to_f32_px() / y_size as f32;
+            }
+            FT_Glyph_Format::FT_GLYPH_FORMAT_OUTLINE => {
+                if !self.rasterize_glyph_outline(slot, font, key) {
+                    return None;
+                }
+            }
+            _ => {
+                error!("Unsupported {:?}", format);
+                return None;
+            }
+        }
+
         let bitmap = unsafe { &(*slot).bitmap };
         let pixel_mode = unsafe { mem::transmute(bitmap.pixel_mode as u32) };
         info!(
             "Rasterizing {:?} as {:?} with dimensions {:?}",
             key,
-            render_mode,
+            font.render_mode,
             dimensions
         );
 
-        let actual_width = match pixel_mode {
+        let (actual_width, actual_height) = match pixel_mode {
             FT_Pixel_Mode::FT_PIXEL_MODE_LCD => {
                 assert!(bitmap.width % 3 == 0);
-                bitmap.width / 3
+                ((bitmap.width / 3) as i32, bitmap.rows as i32)
             }
-            FT_Pixel_Mode::FT_PIXEL_MODE_MONO | FT_Pixel_Mode::FT_PIXEL_MODE_GRAY => bitmap.width,
-            _ => {
-                panic!("Unexpected pixel mode!");
+            FT_Pixel_Mode::FT_PIXEL_MODE_LCD_V => {
+                assert!(bitmap.rows % 3 == 0);
+                (bitmap.width as i32, (bitmap.rows / 3) as i32)
             }
-        } as i32;
-
-        let actual_height = bitmap.rows as i32;
-        let top = unsafe { (*slot).bitmap_top };
-        let left = unsafe { (*slot).bitmap_left };
+            FT_Pixel_Mode::FT_PIXEL_MODE_MONO | FT_Pixel_Mode::FT_PIXEL_MODE_GRAY | FT_Pixel_Mode::FT_PIXEL_MODE_BGRA => {
+                (bitmap.width as i32, bitmap.rows as i32)
+            }
+            _ => panic!("Unsupported {:?}", pixel_mode),
+        };
+        let (left, top) = unsafe { ((*slot).bitmap_left, (*slot).bitmap_top) };
         let mut final_buffer = vec![0; (actual_width * actual_height * 4) as usize];
 
         // Extract the final glyph from FT format into RGBA8 format, which is
         // what WR expects.
-        for y in 0 .. actual_height {
-            // Get pointer to the bytes for this row.
-            let mut src = unsafe { bitmap.buffer.offset((y * bitmap.pitch) as isize) };
-
-            for x in 0 .. actual_width {
-                let value = match pixel_mode {
-                    FT_Pixel_Mode::FT_PIXEL_MODE_MONO => {
-                        let mask = 0x80 >> (x & 0x7);
-                        let byte = unsafe { *src.offset((x >> 3) as isize) };
-                        let alpha = if byte & mask != 0 { 0xff } else { 0 };
-                        [0xff, 0xff, 0xff, alpha]
+        let FontInstancePlatformOptions { flags, .. } = font.platform_options.unwrap_or_default();
+        let subpixel_bgr = (flags & FONT_SUBPIXEL_BGR) != 0;
+        let mut src_row = bitmap.buffer;
+        let mut dest: usize = 0;
+        while dest < final_buffer.len() {
+            let mut src = src_row;
+            let row_end = dest + actual_width as usize * 4;
+            match pixel_mode {
+                FT_Pixel_Mode::FT_PIXEL_MODE_MONO => {
+                    while dest < row_end {
+                        // Cast the byte to signed so that we can left shift each bit into
+                        // the top bit, then right shift to fill out the bits with 0s or 1s.
+                        let mut byte: i8 = unsafe { *src as i8 };
+                        src = unsafe { src.offset(1) };
+                        let byte_end = cmp::min(row_end, dest + 8 * 4);
+                        while dest < byte_end {
+                            let alpha = (byte >> 7) as u8;
+                            final_buffer[dest + 0] = alpha;
+                            final_buffer[dest + 1] = alpha;
+                            final_buffer[dest + 2] = alpha;
+                            final_buffer[dest + 3] = alpha;
+                            dest += 4;
+                            byte <<= 1;
+                        }
                     }
-                    FT_Pixel_Mode::FT_PIXEL_MODE_GRAY => {
+                }
+                FT_Pixel_Mode::FT_PIXEL_MODE_GRAY => {
+                    while dest < row_end {
                         let alpha = unsafe { *src };
+                        final_buffer[dest + 0] = alpha;
+                        final_buffer[dest + 1] = alpha;
+                        final_buffer[dest + 2] = alpha;
+                        final_buffer[dest + 3] = alpha;
                         src = unsafe { src.offset(1) };
-                        [0xff, 0xff, 0xff, alpha]
+                        dest += 4;
+                    }
+                }
+                FT_Pixel_Mode::FT_PIXEL_MODE_LCD => {
+                    if subpixel_bgr {
+                        while dest < row_end {
+                            final_buffer[dest + 0] = unsafe { *src };
+                            final_buffer[dest + 1] = unsafe { *src.offset(1) };
+                            final_buffer[dest + 2] = unsafe { *src.offset(2) };
+                            final_buffer[dest + 3] = 0xff;
+                            src = unsafe { src.offset(3) };
+                            dest += 4;
+                        }
+                    } else {
+                        while dest < row_end {
+                            final_buffer[dest + 2] = unsafe { *src };
+                            final_buffer[dest + 1] = unsafe { *src.offset(1) };
+                            final_buffer[dest + 0] = unsafe { *src.offset(2) };
+                            final_buffer[dest + 3] = 0xff;
+                            src = unsafe { src.offset(3) };
+                            dest += 4;
+                        }
                     }
-                    FT_Pixel_Mode::FT_PIXEL_MODE_LCD => {
-                        let t = unsafe { slice::from_raw_parts(src, 3) };
-                        src = unsafe { src.offset(3) };
-                        [t[2], t[1], t[0], 0xff]
+                }
+                FT_Pixel_Mode::FT_PIXEL_MODE_LCD_V => {
+                    if subpixel_bgr {
+                        while dest < row_end {
+                            final_buffer[dest + 0] = unsafe { *src };
+                            final_buffer[dest + 1] = unsafe { *src.offset(bitmap.pitch as isize) };
+                            final_buffer[dest + 2] = unsafe { *src.offset((2 * bitmap.pitch) as isize) };
+                            final_buffer[dest + 3] = 0xff;
+                            src = unsafe { src.offset(1) };
+                            dest += 4;
+                        }
+                    } else {
+                        while dest < row_end {
+                            final_buffer[dest + 2] = unsafe { *src };
+                            final_buffer[dest + 1] = unsafe { *src.offset(bitmap.pitch as isize) };
+                            final_buffer[dest + 0] = unsafe { *src.offset((2 * bitmap.pitch) as isize) };
+                            final_buffer[dest + 3] = 0xff;
+                            src = unsafe { src.offset(1) };
+                            dest += 4;
+                        }
                     }
-                    _ => panic!("Unsupported {:?}", pixel_mode),
-                };
-                let i = 4 * (y * actual_width + x) as usize;
-                let dest = &mut final_buffer[i .. i + 4];
-                dest.clone_from_slice(&value);
+                    src_row = unsafe { src_row.offset((2 * bitmap.pitch) as isize) };
+                }
+                FT_Pixel_Mode::FT_PIXEL_MODE_BGRA => {
+                    // The source is premultiplied BGRA data.
+                    let dest_slice = &mut final_buffer[dest .. row_end];
+                    let src_slice = unsafe { slice::from_raw_parts(src, dest_slice.len()) };
+                    dest_slice.copy_from_slice(src_slice);
+                }
+                _ => panic!("Unsupported {:?}", pixel_mode),
             }
+            src_row = unsafe { src_row.offset(bitmap.pitch as isize) };
         }
 
         Some(RasterizedGlyph {
-            left: (dimensions.left + left) as f32,
-            top: (dimensions.top + top - actual_height) as f32,
+            left: ((dimensions.left + left) as f32 * scale).round(),
+            top: ((dimensions.top + top - actual_height) as f32 * scale).round(),
             width: actual_width as u32,
             height: actual_height as u32,
+            scale,
             bytes: final_buffer,
         })
     }
 }
 
 impl Drop for FontContext {
     fn drop(&mut self) {
         unsafe {
--- a/gfx/webrender/src/platform/windows/font.rs
+++ b/gfx/webrender/src/platform/windows/font.rs
@@ -28,16 +28,17 @@ pub struct FontContext {
 // all hidden inside their font context.
 unsafe impl Send for FontContext {}
 
 pub struct RasterizedGlyph {
     pub top: f32,
     pub left: f32,
     pub width: u32,
     pub height: u32,
+    pub scale: f32,
     pub bytes: Vec<u8>,
 }
 
 fn dwrite_texture_type(render_mode: FontRenderMode) -> dwrote::DWRITE_TEXTURE_TYPE {
     match render_mode {
         FontRenderMode::Mono | FontRenderMode::Bitmap => dwrote::DWRITE_TEXTURE_ALIASED_1x1,
         FontRenderMode::Alpha | FontRenderMode::Subpixel => dwrote::DWRITE_TEXTURE_CLEARTYPE_3x1,
     }
@@ -299,21 +300,25 @@ impl FontContext {
                     rgba_pixels[i * 4 + 2] = pixels[i * 3 + 2];
                     rgba_pixels[i * 4 + 3] = 0xff;
                 }
                 rgba_pixels
             }
         }
     }
 
-    pub fn is_bitmap_font(&mut self, _font_key: FontKey) -> bool {
+    pub fn is_bitmap_font(&mut self, _font: &FontInstance) -> bool {
         // TODO(gw): Support bitmap fonts in DWrite.
         false
     }
 
+    pub fn has_gamma_correct_subpixel_aa() -> bool {
+        true
+    }
+
     pub fn rasterize_glyph(
         &mut self,
         font: &FontInstance,
         key: &GlyphKey,
     ) -> Option<RasterizedGlyph> {
         let analysis = self.create_glyph_analysis(font, key);
         let texture_type = dwrite_texture_type(font.render_mode);
 
@@ -324,37 +329,41 @@ impl FontContext {
         // Alpha texture bounds can sometimes return an empty rect
         // 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.platform_options {
-                Some(option) => if option.force_gdi_rendering {
-                    &self.gdi_gamma_lut
-                } else {
-                    &self.gamma_lut
-                },
-                None => &self.gamma_lut,
-            };
+        match font.render_mode {
+            FontRenderMode::Mono | FontRenderMode::Bitmap => {}
+            FontRenderMode::Alpha | FontRenderMode::Subpixel => {
+                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,
+                };
 
-            lut_correction.preblend_rgb(
-                &mut pixels,
-                width,
-                height,
-                ColorLut::new(font.color.r, font.color.g, font.color.b, font.color.a),
-            );
+                lut_correction.preblend_rgb(
+                    &mut pixels,
+                    width,
+                    height,
+                    ColorLut::new(font.color.r, font.color.g, font.color.b, font.color.a),
+                );
+            }
         }
 
         let rgba_pixels = self.convert_to_rgba(&mut pixels, font.render_mode);
 
         Some(RasterizedGlyph {
             left: bounds.left as f32,
             top: -bounds.top as f32,
             width: width as u32,
             height: height as u32,
+            scale: 1.0,
             bytes: rgba_pixels,
         })
     }
 }
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -1,24 +1,25 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{BorderRadius, BuiltDisplayList, ColorF, ComplexClipRegion, DeviceIntRect, DeviceIntSize};
 use api::{DevicePoint, ExtendMode, FontInstance, FontRenderMode, GlyphInstance, GlyphKey};
 use api::{GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag, LayerPoint, LayerRect};
-use api::{LayerSize, LayerVector2D, LineOrientation, LineStyle, Shadow};
+use api::{LayerSize, LayerVector2D, LineOrientation, LineStyle};
 use api::{TileOffset, YuvColorSpace, YuvFormat, device_length};
 use app_units::Au;
 use border::BorderCornerInstance;
 use clip::{ClipMode, ClipSourcesHandle, ClipStore, Geometry};
 use euclid::Size2D;
 use frame_builder::PrimitiveContext;
 use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest,
                 ToGpuBlocks};
+use picture::PicturePrimitive;
 use render_task::{ClipWorkItem, RenderTask, RenderTaskId, RenderTaskTree};
 use renderer::MAX_VERTEX_TEXTURE_WIDTH;
 use resource_cache::{ImageProperties, ResourceCache};
 use std::{mem, usize};
 use util::{MatrixHelpers, pack_as_float, recycle_vec, TransformedRect};
 
 #[derive(Debug, Copy, Clone)]
 pub struct PrimitiveOpacity {
@@ -105,18 +106,18 @@ pub enum PrimitiveKind {
     TextRun,
     Image,
     YuvImage,
     Border,
     AlignedGradient,
     AngleGradient,
     RadialGradient,
     BoxShadow,
-    Shadow,
     Line,
+    Picture,
 }
 
 impl GpuCacheHandle {
     pub fn as_int(&self, gpu_cache: &GpuCache) -> i32 {
         gpu_cache.get_address(self).as_int()
     }
 }
 
@@ -509,77 +510,74 @@ impl RadialGradientPrimitiveCpu {
     ) {
         request.extend_from_slice(&self.gpu_blocks);
 
         let gradient_builder = GradientGpuBlockBuilder::new(self.stops_range, display_list);
         gradient_builder.build(false, &mut request);
     }
 }
 
-#[derive(Debug)]
-pub struct ShadowPrimitiveCpu {
-    pub shadow: Shadow,
-    pub primitives: Vec<PrimitiveIndex>,
-    pub render_task_id: Option<RenderTaskId>,
-}
-
 #[derive(Debug, Clone)]
 pub struct TextRunPrimitiveCpu {
     pub font: FontInstance,
     pub offset: LayerVector2D,
     pub glyph_range: ItemRange<GlyphInstance>,
     pub glyph_count: usize,
     pub glyph_keys: Vec<GlyphKey>,
     pub glyph_gpu_blocks: Vec<GpuBlockData>,
-    pub shadow_render_mode: FontRenderMode,
-    pub color: ColorF,
 }
 
 #[derive(Debug, Copy, Clone, Eq, PartialEq)]
 pub enum TextRunMode {
     Normal,
     Shadow,
 }
 
 impl TextRunPrimitiveCpu {
+    pub fn get_font(&self,
+                    run_mode: TextRunMode,
+                    device_pixel_ratio: f32,
+    ) -> FontInstance {
+        let mut font = self.font.clone();
+        match run_mode {
+            TextRunMode::Normal => {}
+            TextRunMode::Shadow => {
+                // Shadows never use subpixel AA, but need to respect the alpha/mono flag
+                // for reftests.
+                font.render_mode = font.render_mode.limit_by(FontRenderMode::Alpha);
+            }
+        };
+        font.size = font.size.scale_by(device_pixel_ratio);
+        font
+    }
+
     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 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 => {}
-        }
-
-        if run_mode == TextRunMode::Shadow {
-            font.render_mode = self.shadow_render_mode;
-        }
+        let font = self.get_font(run_mode, device_pixel_ratio);
 
         // 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 subpx_dir = font.subpx_dir.limit_by(font.render_mode);
             let src_glyphs = display_list.get(self.glyph_range);
 
             // TODO(gw): If we support chunks() on AuxIter
             //           in the future, this code below could
             //           be much simpler...
             let mut gpu_block = GpuBlockData::empty();
-
             for (i, src) in src_glyphs.enumerate() {
-                let key = GlyphKey::new(src.index, src.point, font.render_mode, font.subpx_dir);
+                let key = GlyphKey::new(src.index, src.point, font.render_mode, subpx_dir);
                 self.glyph_keys.push(key);
 
                 // Two glyphs are packed per GPU block.
 
                 if (i & 1) == 0 {
                     gpu_block.data[0] = src.point.x;
                     gpu_block.data[1] = src.point.y;
                 } else {
@@ -595,21 +593,21 @@ impl TextRunPrimitiveCpu {
                 self.glyph_gpu_blocks.push(gpu_block);
             }
         }
 
         resource_cache.request_glyphs(font, &self.glyph_keys, gpu_cache);
     }
 
     fn write_gpu_blocks(&self, request: &mut GpuDataRequest) {
-        request.push(self.color);
+        request.push(ColorF::from(self.font.color));
         request.push([
             self.offset.x,
             self.offset.y,
-            self.font.subpx_dir as u32 as f32,
+            self.font.subpx_dir.limit_by(self.font.render_mode) as u32 as f32,
             0.0,
         ]);
         request.extend_from_slice(&self.glyph_gpu_blocks);
 
         assert!(request.current_used_block_num() <= MAX_VERTEX_TEXTURE_WIDTH);
     }
 }
 
@@ -802,58 +800,58 @@ pub enum PrimitiveContainer {
     TextRun(TextRunPrimitiveCpu),
     Image(ImagePrimitiveCpu),
     YuvImage(YuvImagePrimitiveCpu),
     Border(BorderPrimitiveCpu),
     AlignedGradient(GradientPrimitiveCpu),
     AngleGradient(GradientPrimitiveCpu),
     RadialGradient(RadialGradientPrimitiveCpu),
     BoxShadow(BoxShadowPrimitiveCpu),
-    Shadow(ShadowPrimitiveCpu),
+    Picture(PicturePrimitive),
     Line(LinePrimitive),
 }
 
 pub struct PrimitiveStore {
     /// CPU side information only.
     pub cpu_rectangles: Vec<RectanglePrimitive>,
     pub cpu_text_runs: Vec<TextRunPrimitiveCpu>,
-    pub cpu_shadows: Vec<ShadowPrimitiveCpu>,
+    pub cpu_pictures: Vec<PicturePrimitive>,
     pub cpu_images: Vec<ImagePrimitiveCpu>,
     pub cpu_yuv_images: Vec<YuvImagePrimitiveCpu>,
     pub cpu_gradients: Vec<GradientPrimitiveCpu>,
     pub cpu_radial_gradients: Vec<RadialGradientPrimitiveCpu>,
     pub cpu_metadata: Vec<PrimitiveMetadata>,
     pub cpu_borders: Vec<BorderPrimitiveCpu>,
     pub cpu_box_shadows: Vec<BoxShadowPrimitiveCpu>,
     pub cpu_lines: Vec<LinePrimitive>,
 }
 
 impl PrimitiveStore {
     pub fn new() -> PrimitiveStore {
         PrimitiveStore {
             cpu_metadata: Vec::new(),
             cpu_rectangles: Vec::new(),
             cpu_text_runs: Vec::new(),
-            cpu_shadows: Vec::new(),
+            cpu_pictures: Vec::new(),
             cpu_images: Vec::new(),
             cpu_yuv_images: Vec::new(),
             cpu_gradients: Vec::new(),
             cpu_radial_gradients: Vec::new(),
             cpu_borders: Vec::new(),
             cpu_box_shadows: Vec::new(),
             cpu_lines: Vec::new(),
         }
     }
 
     pub fn recycle(self) -> Self {
         PrimitiveStore {
             cpu_metadata: recycle_vec(self.cpu_metadata),
             cpu_rectangles: recycle_vec(self.cpu_rectangles),
             cpu_text_runs: recycle_vec(self.cpu_text_runs),
-            cpu_shadows: recycle_vec(self.cpu_shadows),
+            cpu_pictures: recycle_vec(self.cpu_pictures),
             cpu_images: recycle_vec(self.cpu_images),
             cpu_yuv_images: recycle_vec(self.cpu_yuv_images),
             cpu_gradients: recycle_vec(self.cpu_gradients),
             cpu_radial_gradients: recycle_vec(self.cpu_radial_gradients),
             cpu_borders: recycle_vec(self.cpu_borders),
             cpu_box_shadows: recycle_vec(self.cpu_box_shadows),
             cpu_lines: recycle_vec(self.cpu_lines),
         }
@@ -915,25 +913,25 @@ impl PrimitiveStore {
                     prim_kind: PrimitiveKind::TextRun,
                     cpu_prim_index: SpecificPrimitiveIndex(self.cpu_text_runs.len()),
                     ..base_metadata
                 };
 
                 self.cpu_text_runs.push(text_cpu);
                 metadata
             }
-            PrimitiveContainer::Shadow(shadow) => {
+            PrimitiveContainer::Picture(picture) => {
                 let metadata = PrimitiveMetadata {
                     opacity: PrimitiveOpacity::translucent(),
-                    prim_kind: PrimitiveKind::Shadow,
-                    cpu_prim_index: SpecificPrimitiveIndex(self.cpu_shadows.len()),
+                    prim_kind: PrimitiveKind::Picture,
+                    cpu_prim_index: SpecificPrimitiveIndex(self.cpu_pictures.len()),
                     ..base_metadata
                 };
 
-                self.cpu_shadows.push(shadow);
+                self.cpu_pictures.push(picture);
                 metadata
             }
             PrimitiveContainer::Image(image_cpu) => {
                 let metadata = PrimitiveMetadata {
                     opacity: PrimitiveOpacity::translucent(),
                     prim_kind: PrimitiveKind::Image,
                     cpu_prim_index: SpecificPrimitiveIndex(self.cpu_images.len()),
                     ..base_metadata
@@ -1030,19 +1028,19 @@ impl PrimitiveStore {
         // Add any dynamic render tasks needed to render this primitive
         let metadata = &self.cpu_metadata[prim_index.0];
 
         let render_task_id = match metadata.prim_kind {
             PrimitiveKind::BoxShadow => {
                 let box_shadow = &self.cpu_box_shadows[metadata.cpu_prim_index.0];
                 box_shadow.render_task_id
             }
-            PrimitiveKind::Shadow => {
-                let shadow = &self.cpu_shadows[metadata.cpu_prim_index.0];
-                shadow.render_task_id
+            PrimitiveKind::Picture => {
+                let picture = &self.cpu_pictures[metadata.cpu_prim_index.0];
+                picture.render_task_id
             }
             PrimitiveKind::Rectangle |
             PrimitiveKind::TextRun |
             PrimitiveKind::Image |
             PrimitiveKind::AlignedGradient |
             PrimitiveKind::YuvImage |
             PrimitiveKind::Border |
             PrimitiveKind::AngleGradient |
@@ -1109,36 +1107,37 @@ impl PrimitiveStore {
                     cache_key,
                     cache_size,
                     prim_index
                 );
 
                 // ignore the new task if we are in a dependency context
                 box_shadow.render_task_id = render_tasks.map(|rt| rt.add(render_task));
             }
-            PrimitiveKind::Shadow => {
-                let shadow = &mut self.cpu_shadows[metadata.cpu_prim_index.0];
+            PrimitiveKind::Picture => {
+                let picture = &mut self.cpu_pictures[metadata.cpu_prim_index.0];
 
                 // This is a shadow element. Create a render task that will
                 // render the text run to a target, and then apply a gaussian
                 // blur to that text run in order to build the actual primitive
                 // which will be blitted to the framebuffer.
                 let cache_width =
                     (metadata.local_rect.size.width * prim_context.device_pixel_ratio).ceil() as i32;
                 let cache_height =
                     (metadata.local_rect.size.height * prim_context.device_pixel_ratio).ceil() as i32;
                 let cache_size = DeviceIntSize::new(cache_width, cache_height);
-                let blur_radius = device_length(shadow.shadow.blur_radius, prim_context.device_pixel_ratio);
+                let blur_radius = picture.as_shadow().blur_radius;
+                let blur_radius = device_length(blur_radius, prim_context.device_pixel_ratio);
 
                 // ignore new tasks if we are in a dependency context
-                shadow.render_task_id = render_tasks.map(|rt| {
-                    let prim_cache_task = RenderTask::new_prim_cache(cache_size, prim_index);
-                    let prim_cache_task_id = rt.add(prim_cache_task);
+                picture.render_task_id = render_tasks.map(|rt| {
+                    let picture_task = RenderTask::new_picture(cache_size, prim_index);
+                    let picture_task_id = rt.add(picture_task);
                     let render_task =
-                        RenderTask::new_blur(blur_radius, prim_cache_task_id, rt);
+                        RenderTask::new_blur(blur_radius, picture_task_id, rt);
                     rt.add(render_task)
                 });
             }
             PrimitiveKind::TextRun => {
                 let text = &mut self.cpu_text_runs[metadata.cpu_prim_index.0];
                 text.prepare_for_render(
                     resource_cache,
                     prim_context.device_pixel_ratio,
@@ -1229,23 +1228,24 @@ impl PrimitiveStore {
                 PrimitiveKind::RadialGradient => {
                     let gradient = &self.cpu_radial_gradients[metadata.cpu_prim_index.0];
                     gradient.build_gpu_blocks_for_angle_radial(prim_context.display_list, request);
                 }
                 PrimitiveKind::TextRun => {
                     let text = &self.cpu_text_runs[metadata.cpu_prim_index.0];
                     text.write_gpu_blocks(&mut request);
                 }
-                PrimitiveKind::Shadow => {
-                    let prim = &self.cpu_shadows[metadata.cpu_prim_index.0];
-                    request.push(prim.shadow.color);
+                PrimitiveKind::Picture => {
+                    let picture = &self.cpu_pictures[metadata.cpu_prim_index.0];
+                    let shadow = picture.as_shadow();
+                    request.push(shadow.color);
                     request.push([
-                        prim.shadow.offset.x,
-                        prim.shadow.offset.y,
-                        prim.shadow.blur_radius,
+                        shadow.offset.x,
+                        shadow.offset.y,
+                        shadow.blur_radius,
                         0.0,
                     ]);
                 }
             }
         }
     }
 
     fn update_clip_task(
@@ -1369,37 +1369,41 @@ impl PrimitiveStore {
                 Some(device_rect) => Geometry {
                     local_rect,
                     device_rect,
                 },
                 None => return None,
             };
 
             let dependencies = match metadata.prim_kind {
-                PrimitiveKind::Shadow =>
-                    self.cpu_shadows[metadata.cpu_prim_index.0].primitives.clone(),
+                PrimitiveKind::Picture =>
+                    self.cpu_pictures[metadata.cpu_prim_index.0].prim_runs.clone(),
                 _ => Vec::new(),
             };
             (geometry, dependencies)
         };
 
         // Recurse into any sub primitives and prepare them for rendering first.
         // TODO(gw): This code is a bit hacky to work around the borrow checker.
         //           Specifically, the clone() below on the primitive list for
         //           text shadow primitives. Consider restructuring this code to
         //           avoid borrow checker issues.
-        for sub_prim_index in dependent_primitives {
-            self.prepare_prim_for_render_inner(
-                sub_prim_index,
-                prim_context,
-                resource_cache,
-                gpu_cache,
-                None,
-                TextRunMode::Shadow,
-            );
+        for run in dependent_primitives {
+            for i in 0 .. run.count {
+                let sub_prim_index = PrimitiveIndex(run.prim_index.0 + i);
+
+                self.prepare_prim_for_render_inner(
+                    sub_prim_index,
+                    prim_context,
+                    resource_cache,
+                    gpu_cache,
+                    None,
+                    TextRunMode::Shadow,
+                );
+            }
         }
 
         if !self.update_clip_task(
             prim_index,
             prim_context,
             geometry.device_rect,
             resource_cache,
             gpu_cache,
--- a/gfx/webrender/src/render_backend.rs
+++ b/gfx/webrender/src/render_backend.rs
@@ -707,17 +707,15 @@ impl ToDebugString for SpecificDisplayIt
             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::PushShadow(..) => String::from("push_shadow"),
             SpecificDisplayItem::PopShadow => String::from("pop_shadow"),
         }
     }
 }
--- a/gfx/webrender/src/render_task.rs
+++ b/gfx/webrender/src/render_task.rs
@@ -221,17 +221,17 @@ pub struct CacheMaskTask {
 #[derive(Debug)]
 pub struct RenderTaskData {
     pub data: [f32; FLOATS_PER_RENDER_TASK_INFO],
 }
 
 #[derive(Debug)]
 pub enum RenderTaskKind {
     Alpha(AlphaRenderTask),
-    CachePrimitive(PrimitiveIndex),
+    Picture(PrimitiveIndex),
     BoxShadow(PrimitiveIndex),
     CacheMask(CacheMaskTask),
     VerticalBlur(DeviceIntLength),
     HorizontalBlur(DeviceIntLength),
     Readback(DeviceIntRect),
     Alias(RenderTaskId),
 }
 
@@ -264,22 +264,22 @@ impl RenderTask {
     pub fn new_dynamic_alpha_batch(
         rect: &DeviceIntRect,
         frame_output_pipeline_id: Option<PipelineId>,
     ) -> RenderTask {
         let location = RenderTaskLocation::Dynamic(None, rect.size);
         Self::new_alpha_batch(rect.origin, location, frame_output_pipeline_id)
     }
 
-    pub fn new_prim_cache(size: DeviceIntSize, prim_index: PrimitiveIndex) -> RenderTask {
+    pub fn new_picture(size: DeviceIntSize, prim_index: PrimitiveIndex) -> RenderTask {
         RenderTask {
             cache_key: None,
             children: Vec::new(),
             location: RenderTaskLocation::Dynamic(None, size),
-            kind: RenderTaskKind::CachePrimitive(prim_index),
+            kind: RenderTaskKind::Picture(prim_index),
         }
     }
 
     pub fn new_box_shadow(
         key: BoxShadowPrimitiveCacheKey,
         size: DeviceIntSize,
         prim_index: PrimitiveIndex,
     ) -> RenderTask {
@@ -418,30 +418,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::Picture(..) |
             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::Picture(..) |
             RenderTaskKind::BoxShadow(..) |
             RenderTaskKind::CacheMask(..) |
             RenderTaskKind::VerticalBlur(..) |
             RenderTaskKind::Readback(..) |
             RenderTaskKind::HorizontalBlur(..) |
             RenderTaskKind::Alias(..) => unreachable!(),
         }
     }
@@ -473,17 +473,17 @@ impl RenderTask {
                         0.0,
                         0.0,
                         0.0,
                         0.0,
                         0.0,
                     ],
                 }
             }
-            RenderTaskKind::CachePrimitive(..) | RenderTaskKind::BoxShadow(..) => {
+            RenderTaskKind::Picture(..) | 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,
@@ -575,17 +575,17 @@ impl RenderTask {
                 (DeviceIntRect::new(origin, size), target_index)
             }
         }
     }
 
     pub fn target_kind(&self) -> RenderTargetKind {
         match self.kind {
             RenderTaskKind::Alpha(..) |
-            RenderTaskKind::CachePrimitive(..) |
+            RenderTaskKind::Picture(..) |
             RenderTaskKind::VerticalBlur(..) |
             RenderTaskKind::Readback(..) |
             RenderTaskKind::HorizontalBlur(..) => RenderTargetKind::Color,
 
             RenderTaskKind::CacheMask(..) | RenderTaskKind::BoxShadow(..) => {
                 RenderTargetKind::Alpha
             }
 
@@ -599,17 +599,17 @@ impl RenderTask {
     // to all passes (except the first) in the render task tree.
     // To qualify for this, the task needs to have no children / dependencies.
     // Currently, this is only supported for A8 targets, but it can be
     // trivially extended to also support RGBA8 targets in the future
     // if we decide that is useful.
     pub fn is_shared(&self) -> bool {
         match self.kind {
             RenderTaskKind::Alpha(..) |
-            RenderTaskKind::CachePrimitive(..) |
+            RenderTaskKind::Picture(..) |
             RenderTaskKind::VerticalBlur(..) |
             RenderTaskKind::Readback(..) |
             RenderTaskKind::HorizontalBlur(..) => false,
 
             RenderTaskKind::CacheMask(..) | RenderTaskKind::BoxShadow(..) => true,
 
             RenderTaskKind::Alias(..) => {
                 panic!("BUG: is_shared() called on aliased task");
--- a/gfx/webrender/src/renderer.rs
+++ b/gfx/webrender/src/renderer.rs
@@ -5,17 +5,17 @@
 //! The webrender API.
 //!
 //! The `webrender::renderer` module provides the interface to webrender, which
 //! is accessible through [`Renderer`][renderer]
 //!
 //! [renderer]: struct.Renderer.html
 
 use api::{channel, BlobImageRenderer, FontRenderMode};
-use api::{ColorF, Epoch, PipelineId, RenderApiSender, RenderNotifier};
+use api::{ColorF, ColorU, Epoch, PipelineId, RenderApiSender, RenderNotifier};
 use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceUintRect, DeviceUintSize};
 use api::{ExternalImageId, ExternalImageType, ImageFormat};
 use api::{YUV_COLOR_SPACES, YUV_FORMATS};
 use api::{YuvColorSpace, YuvFormat};
 #[cfg(not(feature = "debugger"))]
 use api::ApiMsg;
 use api::DebugCommand;
 #[cfg(not(feature = "debugger"))]
@@ -622,17 +622,17 @@ impl SourceTextureResolver {
 
 #[derive(Debug, Copy, Clone, PartialEq)]
 pub enum BlendMode {
     None,
     Alpha,
     PremultipliedAlpha,
 
     // Use the color of the text itself as a constant color blend factor.
-    Subpixel(ColorF),
+    Subpixel(ColorU),
 }
 
 // Tracks the state of each row in the GPU cache texture.
 struct CacheRow {
     is_dirty: bool,
 }
 
 impl CacheRow {
@@ -2742,17 +2742,17 @@ impl Renderer {
                             self.device.set_blend_mode_alpha();
                         }
                         BlendMode::PremultipliedAlpha => {
                             self.device.set_blend(true);
                             self.device.set_blend_mode_premultiplied_alpha();
                         }
                         BlendMode::Subpixel(color) => {
                             self.device.set_blend(true);
-                            self.device.set_blend_mode_subpixel(color);
+                            self.device.set_blend_mode_subpixel(color.into());
                         }
                     }
                     prev_blend_mode = batch.key.blend_mode;
                 }
 
                 if self.debug_flags.contains(ALPHA_PRIM_DBG) {
                     let color = match batch.key.blend_mode {
                         BlendMode::None => ColorF::new(0.3, 0.3, 0.3, 1.0),
--- a/gfx/webrender/src/resource_cache.rs
+++ b/gfx/webrender/src/resource_cache.rs
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{AddFont, BlobImageData, BlobImageResources, ResourceUpdate, ResourceUpdates};
 use api::{BlobImageDescriptor, BlobImageError, BlobImageRenderer, BlobImageRequest};
-use api::{ColorF, FontRenderMode, SubpixelDirection};
+use api::{ColorF, FontRenderMode};
 use api::{DevicePoint, DeviceUintRect, DeviceUintSize};
 use api::{Epoch, FontInstance, FontInstanceKey, FontKey, FontTemplate};
 use api::{ExternalImageData, ExternalImageType};
 use api::{FontInstanceOptions, FontInstancePlatformOptions, FontVariation};
 use api::{GlyphDimensions, GlyphKey, IdNamespace};
 use api::{ImageData, ImageDescriptor, ImageKey, ImageRendering};
 use api::{TileOffset, TileSize};
 use app_units::Au;
@@ -332,39 +332,36 @@ impl ResourceCache {
         &mut self,
         instance_key: FontInstanceKey,
         font_key: FontKey,
         glyph_size: Au,
         options: Option<FontInstanceOptions>,
         platform_options: Option<FontInstancePlatformOptions>,
         variations: Vec<FontVariation>,
     ) {
-        let mut requested_render_mode = FontRenderMode::Subpixel;
-        let mut subpx_dir = SubpixelDirection::Horizontal;
-        if let Some(options) = options {
-            if let Some(render_mode) = options.render_mode {
-                requested_render_mode = render_mode;
-            }
-        }
-        if self.glyph_rasterizer.is_bitmap_font(font_key) {
-            requested_render_mode = requested_render_mode.limit_by(FontRenderMode::Bitmap);
-        }
-        if requested_render_mode == FontRenderMode::Mono {
-            subpx_dir = SubpixelDirection::None;
-        }
-        let instance = FontInstance::new(
+        let FontInstanceOptions {
+            render_mode,
+            subpx_dir,
+            synthetic_italics,
+            ..
+        } = options.unwrap_or_default();
+        assert!(render_mode != FontRenderMode::Bitmap);
+        let mut instance = FontInstance::new(
             font_key,
             glyph_size,
             ColorF::new(0.0, 0.0, 0.0, 1.0),
-            requested_render_mode,
+            render_mode,
             subpx_dir,
             platform_options,
             variations,
-            options.map_or(false, |opts| opts.synthetic_italics),
+            synthetic_italics,
         );
+        if self.glyph_rasterizer.is_bitmap_font(&instance) {
+            instance.render_mode = instance.render_mode.limit_by(FontRenderMode::Bitmap);
+        }
         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);
         }
@@ -559,57 +556,57 @@ impl ResourceCache {
                     key
                 );
             }
         }
     }
 
     pub fn request_glyphs(
         &mut self,
-        font: FontInstance,
+        mut font: FontInstance,
         glyph_keys: &[GlyphKey],
         gpu_cache: &mut GpuCache,
     ) {
         debug_assert_eq!(self.state, State::AddResources);
 
+        self.glyph_rasterizer.prepare_font(&mut font);
         self.glyph_rasterizer.request_glyphs(
             &mut self.cached_glyphs,
             font,
             glyph_keys,
             &mut self.texture_cache,
             gpu_cache,
         );
     }
 
     pub fn pending_updates(&mut self) -> TextureUpdateList {
         self.texture_cache.pending_updates()
     }
 
     pub fn fetch_glyphs<F>(
         &self,
-        font: FontInstance,
+        mut font: FontInstance,
         glyph_keys: &[GlyphKey],
         fetch_buffer: &mut Vec<GlyphFetchResult>,
         gpu_cache: &GpuCache,
         mut f: F,
     ) where
         F: FnMut(SourceTexture, &[GlyphFetchResult]),
     {
         debug_assert_eq!(self.state, State::QueryResources);
+
+        self.glyph_rasterizer.prepare_font(&mut font);
         let glyph_key_cache = self.cached_glyphs.get_glyph_key_cache_for_font(&font);
 
         let mut current_texture_id = SourceTexture::Invalid;
         debug_assert!(fetch_buffer.is_empty());
 
         for (loop_index, key) in glyph_keys.iter().enumerate() {
-            let glyph = glyph_key_cache.get(key);
-            let cache_item = glyph
-                .as_ref()
-                .map(|info| self.texture_cache.get(&info.texture_cache_handle));
-            if let Some(cache_item) = cache_item {
+            if let Some(ref glyph) = *glyph_key_cache.get(key) {
+                let cache_item = self.texture_cache.get(&glyph.texture_cache_handle);
                 if current_texture_id != cache_item.texture_id {
                     if !fetch_buffer.is_empty() {
                         f(current_texture_id, fetch_buffer);
                         fetch_buffer.clear();
                     }
                     current_texture_id = cache_item.texture_id;
                 }
                 fetch_buffer.push(GlyphFetchResult {
@@ -820,17 +817,17 @@ impl ResourceCache {
             };
 
             let entry = self.cached_images.get_mut(&request).unwrap();
             self.texture_cache.update(
                 &mut entry.texture_cache_handle,
                 descriptor,
                 filter,
                 image_data,
-                [0.0; 2],
+                [0.0; 3],
                 image_template.dirty_rect,
                 gpu_cache,
             );
             image_template.dirty_rect = None;
         }
     }
 
     pub fn end_frame(&mut self) {
--- a/gfx/webrender/src/texture_cache.rs
+++ b/gfx/webrender/src/texture_cache.rs
@@ -79,34 +79,34 @@ enum EntryKind {
 // cache or a standalone texture.
 #[derive(Debug)]
 struct CacheEntry {
     // Size the requested item, in device pixels.
     size: DeviceUintSize,
     // Details specific to standalone or shared items.
     kind: EntryKind,
     // Arbitrary user data associated with this item.
-    user_data: [f32; 2],
+    user_data: [f32; 3],
     // The last frame this item was requested for rendering.
     last_access: FrameId,
     // Handle to the resource rect in the GPU cache.
     uv_rect_handle: GpuCacheHandle,
     // Image format of the item.
     format: ImageFormat,
     // The actual device texture ID this is part of.
     texture_id: CacheTextureId,
 }
 
 impl CacheEntry {
     // Create a new entry for a standalone texture.
     fn new_standalone(
         texture_id: CacheTextureId,
         size: DeviceUintSize,
         format: ImageFormat,
-        user_data: [f32; 2],
+        user_data: [f32; 3],
         last_access: FrameId,
     ) -> CacheEntry {
         CacheEntry {
             size,
             user_data,
             last_access,
             kind: EntryKind::Standalone,
             texture_id,
@@ -130,17 +130,17 @@ impl CacheEntry {
                 } => (origin, layer_index as f32),
             };
             request.push([
                 origin.x as f32,
                 origin.y as f32,
                 (origin.x + self.size.width) as f32,
                 (origin.y + self.size.height) as f32,
             ]);
-            request.push([layer_index, self.user_data[0], self.user_data[1], 0.0]);
+            request.push([layer_index, self.user_data[0], self.user_data[1], self.user_data[2]]);
         }
     }
 }
 
 type WeakCacheEntryHandle = WeakFreeListHandle<CacheEntry>;
 
 // A texture cache handle is a weak reference to a cache entry.
 // If the handle has not been inserted into the cache yet, the
@@ -270,17 +270,17 @@ impl TextureCache {
 
     // Update the data stored by a given texture cache handle.
     pub fn update(
         &mut self,
         handle: &mut TextureCacheHandle,
         descriptor: ImageDescriptor,
         filter: TextureFilter,
         data: ImageData,
-        user_data: [f32; 2],
+        user_data: [f32; 3],
         mut dirty_rect: Option<DeviceUintRect>,
         gpu_cache: &mut GpuCache,
     ) {
         // Determine if we need to allocate texture cache memory
         // for this item. We need to reallocate if any of the following
         // is true:
         // - Never been in the cache
         // - Has been in the cache but was evicted.
@@ -507,17 +507,17 @@ impl TextureCache {
             }
         }
     }
 
     // Attempt to allocate a block from the shared cache.
     fn allocate_from_shared_cache(
         &mut self,
         descriptor: &ImageDescriptor,
-        user_data: [f32; 2],
+        user_data: [f32; 3],
     ) -> Option<CacheEntry> {
         // Work out which cache it goes in, based on format.
         let texture_array = match descriptor.format {
             ImageFormat::A8 => &mut self.array_a8,
             ImageFormat::BGRA8 => &mut self.array_rgba8,
             ImageFormat::RGB8 => &mut self.array_rgb8,
             ImageFormat::RG8 => &mut self.array_rg8,
             ImageFormat::Invalid | ImageFormat::RGBAF32 => unreachable!(),
@@ -556,17 +556,17 @@ impl TextureCache {
     // Allocate storage for a given image. This attempts to allocate
     // from the shared cache, but falls back to standalone texture
     // if the image is too large, or the cache is full.
     fn allocate(
         &mut self,
         handle: &mut TextureCacheHandle,
         descriptor: ImageDescriptor,
         filter: TextureFilter,
-        user_data: [f32; 2],
+        user_data: [f32; 3],
     ) {
         assert!(descriptor.width > 0 && descriptor.height > 0);
 
         // Work out if this image qualifies to go in the shared (batching) cache.
         let mut allowed_in_shared_cache = true;
         let mut allocated_in_shared_cache = true;
         let mut new_cache_entry = None;
         let size = DeviceUintSize::new(descriptor.width, descriptor.height);
@@ -850,17 +850,17 @@ impl TextureArray {
         }
     }
 
     // Allocate space in this texture array.
     fn alloc(
         &mut self,
         width: u32,
         height: u32,
-        user_data: [f32; 2],
+        user_data: [f32; 3],
         frame_id: FrameId,
     ) -> Option<CacheEntry> {
         // Lazily allocate the regions if not already created.
         // This means that very rarely used image formats can be
         // added but won't allocate a cache if never used.
         if !self.is_allocated {
             debug_assert!(TEXTURE_LAYER_DIMENSIONS % TEXTURE_REGION_DIMENSIONS == 0);
             let regions_per_axis = TEXTURE_LAYER_DIMENSIONS / TEXTURE_REGION_DIMENSIONS;
--- a/gfx/webrender/src/tiling.rs
+++ b/gfx/webrender/src/tiling.rs
@@ -11,27 +11,28 @@ use border::{BorderCornerInstance, Borde
 use clip::{ClipSource, ClipStore};
 use device::Texture;
 use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle, GpuCacheUpdateList};
 use gpu_types::{BlurDirection, BlurInstance, BoxShadowCacheInstance, ClipMaskInstance};
 use gpu_types::{CompositePrimitiveInstance, PrimitiveInstance, SimplePrimitiveInstance};
 use internal_types::{FastHashMap, SourceTexture};
 use internal_types::BatchTextures;
 use prim_store::{PrimitiveIndex, PrimitiveKind, PrimitiveMetadata, PrimitiveStore};
-use prim_store::DeferredResolve;
+use prim_store::{DeferredResolve, TextRunMode};
 use profiler::FrameProfileCounters;
 use render_task::{AlphaRenderItem, ClipWorkItem, MaskGeometryKind, MaskSegment};
 use render_task::{RenderTaskAddress, RenderTaskId, RenderTaskKey, RenderTaskKind};
 use render_task::{RenderTaskLocation, RenderTaskTree};
 use renderer::BlendMode;
 use renderer::ImageBufferKind;
 use resource_cache::{GlyphFetchResult, ResourceCache};
 use std::{cmp, usize, f32, i32};
 use texture_allocator::GuillotineAllocator;
-use util::{TransformedRect, TransformedRectKind};
+use util::{MatrixHelpers, TransformedRect, TransformedRectKind};
+use euclid::rect;
 
 // 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);
 const MIN_TARGET_SIZE: u32 = 2048;
 
 trait AlphaBatchHelpers {
     fn get_blend_mode(
@@ -49,27 +50,27 @@ impl AlphaBatchHelpers for PrimitiveStor
     ) -> BlendMode {
         let needs_blending = !metadata.opacity.is_opaque || metadata.clip_task_id.is_some() ||
             transform_kind == TransformedRectKind::Complex;
 
         match metadata.prim_kind {
             PrimitiveKind::TextRun => {
                 let text_run_cpu = &self.cpu_text_runs[metadata.cpu_prim_index.0];
                 match text_run_cpu.font.render_mode {
-                    FontRenderMode::Subpixel => BlendMode::Subpixel(text_run_cpu.color),
+                    FontRenderMode::Subpixel => BlendMode::Subpixel(text_run_cpu.font.color),
                     FontRenderMode::Alpha |
                     FontRenderMode::Mono |
-                    FontRenderMode::Bitmap => BlendMode::Alpha,
+                    FontRenderMode::Bitmap => BlendMode::PremultipliedAlpha,
                 }
             }
             PrimitiveKind::Image |
             PrimitiveKind::AlignedGradient |
             PrimitiveKind::AngleGradient |
             PrimitiveKind::RadialGradient |
-            PrimitiveKind::Shadow => if needs_blending {
+            PrimitiveKind::Picture => if needs_blending {
                 BlendMode::PremultipliedAlpha
             } else {
                 BlendMode::None
             },
             _ => if needs_blending {
                 BlendMode::Alpha
             } else {
                 BlendMode::None
@@ -502,18 +503,17 @@ impl AlphaRenderItem {
                         );
                         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 mut font = text_cpu.font.clone();
-                        font.size = font.size.scale_by(ctx.device_pixel_ratio);
+                        let font = text_cpu.get_font(TextRunMode::Normal, ctx.device_pixel_ratio);
 
                         ctx.resource_cache.fetch_glyphs(
                             font,
                             &text_cpu.glyph_keys,
                             glyph_fetch_buffer,
                             gpu_cache,
                             |texture_id, glyphs| {
                                 let textures = BatchTextures {
@@ -523,33 +523,34 @@ impl AlphaRenderItem {
                                         SourceTexture::Invalid,
                                     ],
                                 };
 
                                 let kind = BatchKind::Transformable(
                                     transform_kind,
                                     TransformBatchKind::TextRun,
                                 );
+
                                 let key = BatchKey::new(kind, blend_mode, textures);
                                 let batch = batch_list.get_suitable_batch(key, item_bounding_rect);
 
                                 for glyph in glyphs {
                                     batch.push(base_instance.build(
                                         glyph.index_in_text_run,
                                         glyph.uv_rect_address.as_int(),
                                         0,
                                     ));
                                 }
                             },
                         );
                     }
-                    PrimitiveKind::Shadow => {
-                        let shadow =
-                            &ctx.prim_store.cpu_shadows[prim_metadata.cpu_prim_index.0];
-                        let cache_task_id = shadow.render_task_id.expect("no render task!");
+                    PrimitiveKind::Picture => {
+                        let picture =
+                            &ctx.prim_store.cpu_pictures[prim_metadata.cpu_prim_index.0];
+                        let cache_task_id = picture.render_task_id.expect("no render task!");
                         let cache_task_address = render_tasks.get_task_address(cache_task_id);
                         let textures = BatchTextures::render_target_cache();
                         let kind = BatchKind::Transformable(
                             transform_kind,
                             TransformBatchKind::CacheImage,
                         );
                         let key = BatchKey::new(kind, blend_mode, textures);
                         let batch = batch_list.get_suitable_batch(key, item_bounding_rect);
@@ -1130,77 +1131,79 @@ impl RenderTarget for ColorRenderTarget 
                 // Find the child render task that we are applying
                 // a horizontal blur on.
                 self.horizontal_blurs.push(BlurInstance {
                     task_address: render_tasks.get_task_address(task_id),
                     src_task_address: render_tasks.get_task_address(task.children[0]),
                     blur_direction: BlurDirection::Horizontal,
                 });
             }
-            RenderTaskKind::CachePrimitive(prim_index) => {
+            RenderTaskKind::Picture(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::Shadow => {
-                        let prim = &ctx.prim_store.cpu_shadows[prim_metadata.cpu_prim_index.0];
+                    PrimitiveKind::Picture => {
+                        let prim = &ctx.prim_store.cpu_pictures[prim_metadata.cpu_prim_index.0];
 
                         let task_index = render_tasks.get_task_address(task_id);
 
-                        for sub_prim_index in &prim.primitives {
-                            let sub_metadata = ctx.prim_store.get_metadata(*sub_prim_index);
-                            let sub_prim_address =
-                                gpu_cache.get_address(&sub_metadata.gpu_location);
-                            let instance = SimplePrimitiveInstance::new(
-                                sub_prim_address,
-                                task_index,
-                                RenderTaskAddress(0),
-                                PackedLayerIndex(0).into(),
-                                0,
-                            ); // z is disabled for rendering cache primitives
+                        for run in &prim.prim_runs {
+                            for i in 0 .. run.count {
+                                let sub_prim_index = PrimitiveIndex(run.prim_index.0 + i);
 
-                            match sub_metadata.prim_kind {
-                                PrimitiveKind::TextRun => {
-                                    // Add instances that reference the text run GPU location. Also supply
-                                    // the parent shadow prim address as a user data field, allowing
-                                    // the shader to fetch the shadow parameters.
-                                    let text = &ctx.prim_store.cpu_text_runs
-                                        [sub_metadata.cpu_prim_index.0];
-                                    let text_run_cache_prims = &mut self.text_run_cache_prims;
+                                let sub_metadata = ctx.prim_store.get_metadata(sub_prim_index);
+                                let sub_prim_address =
+                                    gpu_cache.get_address(&sub_metadata.gpu_location);
+                                let instance = SimplePrimitiveInstance::new(
+                                    sub_prim_address,
+                                    task_index,
+                                    RenderTaskAddress(0),
+                                    PackedLayerIndex(0).into(),
+                                    0,
+                                ); // z is disabled for rendering cache primitives
 
-                                    let mut font = text.font.clone();
-                                    font.size = font.size.scale_by(ctx.device_pixel_ratio);
-                                    font.render_mode = text.shadow_render_mode;
+                                match sub_metadata.prim_kind {
+                                    PrimitiveKind::TextRun => {
+                                        // Add instances that reference the text run GPU location. Also supply
+                                        // the parent shadow prim address as a user data field, allowing
+                                        // the shader to fetch the shadow parameters.
+                                        let text = &ctx.prim_store.cpu_text_runs
+                                            [sub_metadata.cpu_prim_index.0];
+                                        let text_run_cache_prims = &mut self.text_run_cache_prims;
+
+                                        let font = text.get_font(TextRunMode::Shadow, ctx.device_pixel_ratio);
 
-                                    ctx.resource_cache.fetch_glyphs(
-                                        font,
-                                        &text.glyph_keys,
-                                        &mut self.glyph_fetch_buffer,
-                                        gpu_cache,
-                                        |texture_id, glyphs| {
-                                            let batch = text_run_cache_prims
-                                                .entry(texture_id)
-                                                .or_insert(Vec::new());
+                                        ctx.resource_cache.fetch_glyphs(
+                                            font,
+                                            &text.glyph_keys,
+                                            &mut self.glyph_fetch_buffer,
+                                            gpu_cache,
+                                            |texture_id, glyphs| {
+                                                let batch = text_run_cache_prims
+                                                    .entry(texture_id)
+                                                    .or_insert(Vec::new());
 
-                                            for glyph in glyphs {
-                                                batch.push(instance.build(
-                                                    glyph.index_in_text_run,
-                                                    glyph.uv_rect_address.as_int(),
-                                                    prim_address,
-                                                ));
-                                            }
-                                        },
-                                    );
-                                }
-                                PrimitiveKind::Line => {
-                                    self.line_cache_prims
-                                        .push(instance.build(prim_address, 0, 0));
-                                }
-                                _ => {
-                                    unreachable!("Unexpected sub primitive type");
+                                                for glyph in glyphs {
+                                                    batch.push(instance.build(
+                                                        glyph.index_in_text_run,
+                                                        glyph.uv_rect_address.as_int(),
+                                                        prim_address,
+                                                    ));
+                                                }
+                                            },
+                                        );
+                                    }
+                                    PrimitiveKind::Line => {
+                                        self.line_cache_prims
+                                            .push(instance.build(prim_address, 0, 0));
+                                    }
+                                    _ => {
+                                        unreachable!("Unexpected sub primitive type");
+                                    }
                                 }
                             }
                         }
                     }
                     _ => {
                         // No other primitives make use of primitive caching yet!
                         unreachable!()
                     }
@@ -1250,17 +1253,17 @@ impl RenderTarget for AlphaRenderTarget 
         let task = render_tasks.get(task_id);
         match task.kind {
             RenderTaskKind::Alias(..) => {
                 panic!("BUG: add_task() called on invalidated task");
             }
             RenderTaskKind::Alpha(..) |
             RenderTaskKind::VerticalBlur(..) |
             RenderTaskKind::HorizontalBlur(..) |
-            RenderTaskKind::CachePrimitive(..) |
+            RenderTaskKind::Picture(..) |
             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 => {
@@ -1681,17 +1684,23 @@ impl PackedLayer {
     }
 
     pub fn set_rect(
         &mut self,
         local_rect: &LayerRect,
         screen_rect: &DeviceIntRect,
         device_pixel_ratio: f32,
     ) -> Option<(TransformedRectKind, DeviceIntRect)> {
-        self.local_clip_rect = *local_rect;
+        self.local_clip_rect = if self.transform.has_perspective_component() {
+            // Given a very large rect which means any rect would be inside this rect.
+            // That is, nothing would be clipped.
+            rect(f32::MIN / 2.0, f32::MIN / 2.0, f32::MAX, f32::MAX)
+        } else {
+            *local_rect
+        };
         let xf_rect = TransformedRect::new(local_rect, &self.transform, device_pixel_ratio);
         xf_rect
             .bounding_rect
             .intersection(screen_rect)
             .map(|rect| (xf_rect.kind, rect))
     }
 }
 
--- a/gfx/webrender/src/util.rs
+++ b/gfx/webrender/src/util.rs
@@ -12,16 +12,17 @@ use std::f32::consts::FRAC_1_SQRT_2;
 // Matches the definition of SK_ScalarNearlyZero in Skia.
 const NEARLY_ZERO: f32 = 1.0 / 4096.0;
 
 // TODO: Implement these in euclid!
 pub trait MatrixHelpers<Src, Dst> {
     fn transform_rect(&self, rect: &TypedRect<f32, Src>) -> TypedRect<f32, Dst>;
     fn is_identity(&self) -> bool;
     fn preserves_2d_axis_alignment(&self) -> bool;
+    fn has_perspective_component(&self) -> bool;
     fn inverse_project(&self, target: &TypedPoint2D<f32, Dst>) -> Option<TypedPoint2D<f32, Src>>;
     fn inverse_rect_footprint(&self, rect: &TypedRect<f32, Dst>) -> TypedRect<f32, Src>;
 }
 
 impl<Src, Dst> MatrixHelpers<Src, Dst> for TypedTransform3D<f32, Src, Dst> {
     fn transform_rect(&self, rect: &TypedRect<f32, Src>) -> TypedRect<f32, Dst> {
         let top_left = self.transform_point2d(&rect.origin);
         let top_right = self.transform_point2d(&rect.top_right());
@@ -61,16 +62,20 @@ impl<Src, Dst> MatrixHelpers<Src, Dst> f
         if self.m22.abs() > NEARLY_ZERO {
             col1 += 1;
             row1 += 1;
         }
 
         col0 < 2 && col1 < 2 && row0 < 2 && row1 < 2
     }
 
+    fn has_perspective_component(&self) -> bool {
+         self.m14 != 0.0 || self.m24 != 0.0 || self.m34 != 0.0 || self.m44 != 1.0
+    }
+
     fn inverse_project(&self, target: &TypedPoint2D<f32, Dst>) -> Option<TypedPoint2D<f32, Src>> {
         let m: TypedTransform2D<f32, Src, Dst>;
         m = TypedTransform2D::column_major(
             self.m11 - target.x * self.m14,
             self.m21 - target.x * self.m24,
             self.m41 - target.x * self.m44,
             self.m12 - target.y * self.m14,
             self.m22 - target.y * self.m24,
--- a/gfx/webrender_api/src/display_item.rs
+++ b/gfx/webrender_api/src/display_item.rs
@@ -97,18 +97,16 @@ pub enum SpecificDisplayItem {
     Border(BorderDisplayItem),
     BoxShadow(BoxShadowDisplayItem),
     Gradient(GradientDisplayItem),
     RadialGradient(RadialGradientDisplayItem),
     Iframe(IframeDisplayItem),
     PushStackingContext(PushStackingContextDisplayItem),
     PopStackingContext,
     SetGradientStops,
-    PushNestedDisplayList,
-    PopNestedDisplayList,
     PushShadow(Shadow),
     PopShadow,
 }
 
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct ClipDisplayItem {
     pub id: ClipId,
     pub parent_id: ClipId,
@@ -624,28 +622,26 @@ impl BorderRadius {
 
 impl ComplexClipRegion {
     /// Create a new complex clip region.
     pub fn new(rect: LayoutRect, radii: BorderRadius) -> ComplexClipRegion {
         ComplexClipRegion { rect, radii }
     }
 }
 
-pub type NestingIndex = u64;
-
 #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
 pub enum ClipId {
-    Clip(u64, NestingIndex, PipelineId),
+    Clip(u64, PipelineId),
     ClipExternalId(u64, PipelineId),
     DynamicallyAddedNode(u64, PipelineId),
 }
 
 impl ClipId {
     pub fn root_scroll_node(pipeline_id: PipelineId) -> ClipId {
-        ClipId::Clip(0, 0, pipeline_id)
+        ClipId::Clip(0, pipeline_id)
     }
 
     pub fn root_reference_frame(pipeline_id: PipelineId) -> ClipId {
         ClipId::DynamicallyAddedNode(0, pipeline_id)
     }
 
     pub fn new(id: u64, pipeline_id: PipelineId) -> ClipId {
         // We do this because it is very easy to create accidentally create something that
@@ -654,39 +650,32 @@ impl ClipId {
             return ClipId::root_scroll_node(pipeline_id);
         }
 
         ClipId::ClipExternalId(id, pipeline_id)
     }
 
     pub fn pipeline_id(&self) -> PipelineId {
         match *self {
-            ClipId::Clip(_, _, pipeline_id) |
+            ClipId::Clip(_, pipeline_id) |
             ClipId::ClipExternalId(_, pipeline_id) |
             ClipId::DynamicallyAddedNode(_, pipeline_id) => pipeline_id,
         }
     }
 
     pub fn external_id(&self) -> Option<u64> {
         match *self {
             ClipId::ClipExternalId(id, _) => Some(id),
             _ => None,
         }
     }
 
     pub fn is_root_scroll_node(&self) -> bool {
         match *self {
-            ClipId::Clip(0, 0, _) => true,
-            _ => false,
-        }
-    }
-
-    pub fn is_nested(&self) -> bool {
-        match *self {
-            ClipId::Clip(_, nesting_level, _) => nesting_level != 0,
+            ClipId::Clip(0, _) => true,
             _ => false,
         }
     }
 }
 
 macro_rules! define_empty_heap_size_of {
     ($name:ident) => {
         impl ::heapsize::HeapSizeOf for $name {
--- a/gfx/webrender_api/src/display_list.rs
+++ b/gfx/webrender_api/src/display_list.rs
@@ -12,16 +12,18 @@ use {LineDisplayItem, LineOrientation, L
 use {PropertyBinding, PushStackingContextDisplayItem, RadialGradient, RadialGradientDisplayItem};
 use {RectangleDisplayItem, ScrollFrameDisplayItem, ScrollPolicy, ScrollSensitivity};
 use {SpecificDisplayItem, StackingContext, StickyFrameDisplayItem, StickyFrameInfo};
 use {TextDisplayItem, Shadow, TransformStyle, YuvColorSpace, YuvData};
 use YuvImageDisplayItem;
 use bincode;
 use serde::{Deserialize, Serialize, Serializer};
 use serde::ser::{SerializeMap, SerializeSeq};
+use std::io::Write;
+use std::{io, ptr};
 use std::marker::PhantomData;
 use time::precise_time_ns;
 
 // We don't want to push a long text-run. If a text-run is too long, split it into several parts.
 // Please check the renderer::MAX_VERTEX_TEXTURE_WIDTH for the detail.
 pub const MAX_TEXT_RUN_LENGTH: usize = 2040;
 
 #[repr(C)]
@@ -478,16 +480,82 @@ impl<'a, 'b> Serialize for DisplayItemRe
                 &self.iter.list.get(gradient_stops).collect::<Vec<_>>(),
             )?;
         }
 
         map.end()
     }
 }
 
+// This is a replacement for bincode::serialize_into(&vec)
+// The default implementation Write for Vec will basically
+// call extend_from_slice(). Serde ends up calling that for every
+// field of a struct that we're serializing. extend_from_slice()
+// does not get inlined and thus we end up calling a generic memcpy()
+// implementation. If we instead reserve enough room for the serialized
+// struct in the Vec ahead of time we can rely on that and use
+// the following UnsafeVecWriter to write into the vec without
+// any checks. This writer assumes that size returned by the
+// serialize function will not change between calls to serialize_into:
+//
+// For example, the following struct will cause memory unsafety when
+// used with UnsafeVecWriter.
+//
+// struct S {
+//    first: Cell<bool>,
+// }
+//
+// impl Serialize for S {
+//    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+//        where S: Serializer
+//    {
+//        if self.first.get() {
+//            self.first.set(false);
+//            ().serialize(serializer)
+//        } else {
+//            0.serialize(serializer)
+//        }
+//    }
+// }
+//
+
+struct UnsafeVecWriter<'a>(&'a mut Vec<u8>);
+
+impl<'a> Write for UnsafeVecWriter<'a> {
+    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+        unsafe {
+            let old_len = self.0.len();
+            self.0.set_len(old_len + buf.len());
+            debug_assert!(self.0.len() <= self.0.capacity());
+            ptr::copy_nonoverlapping(buf.as_ptr(), self.0.as_mut_ptr().offset(old_len as isize), buf.len());
+        }
+        Ok(buf.len())
+    }
+    fn flush(&mut self) -> io::Result<()> { Ok(()) }
+}
+
+struct SizeCounter(usize);
+
+impl<'a> Write for SizeCounter {
+    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+        self.0 += buf.len();
+        Ok(buf.len())
+    }
+    fn flush(&mut self) -> io::Result<()> { Ok(()) }
+}
+
+fn serialize_fast<T: Serialize>(vec: &mut Vec<u8>, e: &T) {
+    // manually counting the size is faster than vec.reserve(bincode::serialized_size(&e) as usize) for some reason
+    let mut size = SizeCounter(0);
+    bincode::serialize_into(&mut size,e , bincode::Infinite).unwrap();
+    vec.reserve(size.0);
+
+    bincode::serialize_into(&mut UnsafeVecWriter(vec), e, bincode::Infinite).unwrap();
+}
+
 #[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<(FontInstanceKey, ColorF), FastHashSet<GlyphIndex>>,
     next_clip_id: u64,
@@ -536,54 +604,52 @@ impl DisplayListBuilder {
                 println!("{:?}", item.display_item());
             }
         }
 
         self.data = temp.data;
     }
 
     fn push_item(&mut self, item: SpecificDisplayItem, info: &LayoutPrimitiveInfo) {
-        bincode::serialize_into(
+        serialize_fast(
             &mut self.data,
             &DisplayItem {
                 item,
                 clip_and_scroll: *self.clip_stack.last().unwrap(),
                 info: *info,
             },
-            bincode::Infinite,
-        ).unwrap();
+        )
     }
 
     fn push_new_empty_item(&mut self, item: SpecificDisplayItem) {
         let info = LayoutPrimitiveInfo::new(LayoutRect::zero());
-        bincode::serialize_into(
+        serialize_fast(
             &mut self.data,
             &DisplayItem {
                 item,
                 clip_and_scroll: *self.clip_stack.last().unwrap(),
                 info,
-            },
-            bincode::Infinite,
-        ).unwrap();
+            }
+        )
     }
 
     fn push_iter<I>(&mut self, iter: I)
     where
         I: IntoIterator,
         I::IntoIter: ExactSizeIterator,
         I::Item: Serialize,
     {
         let iter = iter.into_iter();
         let len = iter.len();
         let mut count = 0;
 
-        bincode::serialize_into(&mut self.data, &len, bincode::Infinite).unwrap();
+        serialize_fast(&mut self.data, &len);
         for elem in iter {
             count += 1;
-            bincode::serialize_into(&mut self.data, &elem, bincode::Infinite).unwrap();
+            serialize_fast(&mut self.data, &elem);
         }
 
         debug_assert_eq!(len, count);
     }
 
     pub fn push_rect(&mut self, info: &LayoutPrimitiveInfo, color: ColorF) {
         let item = SpecificDisplayItem::Rectangle(RectangleDisplayItem { color });
         self.push_item(item, info);
@@ -964,17 +1030,17 @@ impl DisplayListBuilder {
         }
         self.push_new_empty_item(SpecificDisplayItem::SetGradientStops);
         self.push_iter(stops);
     }
 
     fn generate_clip_id(&mut self, id: Option<ClipId>) -> ClipId {
         id.unwrap_or_else(|| {
             self.next_clip_id += 1;
-            ClipId::Clip(self.next_clip_id - 1, 0, self.pipeline_id)
+            ClipId::Clip(self.next_clip_id - 1, self.pipeline_id)
         })
     }
 
     pub fn define_scroll_frame<I>(
         &mut self,
         id: Option<ClipId>,
         content_rect: LayoutRect,
         clip_rect: LayoutRect,
@@ -1109,53 +1175,34 @@ impl DisplayListBuilder {
 
     pub fn push_iframe(&mut self, info: &LayoutPrimitiveInfo, pipeline_id: PipelineId) {
         let item = SpecificDisplayItem::Iframe(IframeDisplayItem {
             pipeline_id: pipeline_id,
         });
         self.push_item(item, info);
     }
 
-    // Don't use this function. It will go away.
-    //
-    // We're using this method as a hack in Gecko to retain parts sub-parts of display
-    // lists so that we can regenerate them without building Gecko display items. WebRender
-    // will replace references to the root scroll frame id with the current scroll frame
-    // id.
-    pub fn push_nested_display_list(&mut self, built_display_list: &BuiltDisplayList) {
-        self.push_new_empty_item(SpecificDisplayItem::PushNestedDisplayList);
-
-        // Need to read out all the glyph data to update the cache
-        for (font_key, color, glyphs) in built_display_list.glyphs() {
-            self.cache_glyphs(font_key, color, built_display_list.get(glyphs));
-        }
-
-        // Only append the actual items, not any caches
-        self.data.extend_from_slice(built_display_list.item_slice());
-        self.push_new_empty_item(SpecificDisplayItem::PopNestedDisplayList);
-    }
-
     pub fn push_shadow(&mut self, info: &LayoutPrimitiveInfo, shadow: Shadow) {
         self.push_item(SpecificDisplayItem::PushShadow(shadow), info);
     }
 
     pub fn pop_shadow(&mut self) {
         self.push_new_empty_item(SpecificDisplayItem::PopShadow);
     }
 
     pub fn finalize(mut self) -> (PipelineId, LayoutSize, BuiltDisplayList) {
         let glyph_offset = self.data.len();
 
         // Want to use self.push_iter, so can't borrow self
         let glyphs = ::std::mem::replace(&mut self.glyphs, FastHashMap::default());
 
         // Append glyph data to the end
         for ((font_key, color), sub_glyphs) in glyphs {
-            bincode::serialize_into(&mut self.data, &font_key, bincode::Infinite).unwrap();
-            bincode::serialize_into(&mut self.data, &color, bincode::Infinite).unwrap();
+            serialize_fast(&mut self.data, &font_key);
+            serialize_fast(&mut self.data, &color);
             self.push_iter(sub_glyphs);
         }
 
         let end_time = precise_time_ns();
 
 
         (
             self.pipeline_id,
--- a/gfx/webrender_api/src/font.rs
+++ b/gfx/webrender_api/src/font.rs
@@ -132,16 +132,26 @@ impl FontRenderMode {
         match (self, other) {
             (FontRenderMode::Bitmap, _) | (_, FontRenderMode::Bitmap) => FontRenderMode::Bitmap,
             (FontRenderMode::Subpixel, _) | (_, FontRenderMode::Mono) => other,
             _ => self,
         }
     }
 }
 
+impl SubpixelDirection {
+    // Limit the subpixel direction to what is supported by the render mode.
+    pub fn limit_by(self, render_mode: FontRenderMode) -> SubpixelDirection {
+        match render_mode {
+            FontRenderMode::Mono | FontRenderMode::Bitmap => SubpixelDirection::None,
+            FontRenderMode::Alpha | FontRenderMode::Subpixel => self,
+        }
+    }
+}
+
 #[repr(u8)]
 #[derive(Hash, Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize, Deserialize)]
 pub enum SubpixelOffset {
     Zero = 0,
     Quarter = 1,
     Half = 2,
     ThreeQuarters = 3,
 }
@@ -191,26 +201,111 @@ impl Hash for FontVariation {
 #[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: Option<FontRenderMode>,
+    pub render_mode: FontRenderMode,
+    pub subpx_dir: SubpixelDirection,
     pub synthetic_italics: bool,
 }
 
+impl Default for FontInstanceOptions {
+    fn default() -> FontInstanceOptions {
+        FontInstanceOptions {
+            render_mode: FontRenderMode::Subpixel,
+            subpx_dir: SubpixelDirection::Horizontal,
+            synthetic_italics: false,
+        }
+    }
+}
+
+#[cfg(target_os = "windows")]
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)]
+pub struct FontInstancePlatformOptions {
+    pub use_embedded_bitmap: bool,
+    pub force_gdi_rendering: bool,
+}
+
+#[cfg(target_os = "windows")]
+impl Default for FontInstancePlatformOptions {
+    fn default() -> FontInstancePlatformOptions {
+        FontInstancePlatformOptions {
+            use_embedded_bitmap: false,
+            force_gdi_rendering: false,
+        }
+    }
+}
+
+#[cfg(target_os = "macos")]
 #[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,
+    pub unused: u32,
+}
+
+#[cfg(target_os = "macos")]
+impl Default for FontInstancePlatformOptions {
+    fn default() -> FontInstancePlatformOptions {
+        FontInstancePlatformOptions {
+            unused: 0,
+        }
+    }
+}
+
+pub const FONT_FORCE_AUTOHINT: u16  = 0b1;
+pub const FONT_NO_AUTOHINT: u16     = 0b10;
+pub const FONT_EMBEDDED_BITMAP: u16 = 0b100;
+pub const FONT_EMBOLDEN: u16        = 0b1000;
+pub const FONT_VERTICAL_LAYOUT: u16 = 0b10000;
+pub const FONT_SUBPIXEL_BGR: u16    = 0b100000;
+
+#[cfg(not(any(target_os = "macos", target_os = "windows")))]
+#[repr(u8)]
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize)]
+pub enum FontLCDFilter {
+    None,
+    Default,
+    Light,
+    Legacy,
+}
+
+#[cfg(not(any(target_os = "macos", target_os = "windows")))]
+#[repr(u8)]
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize)]
+pub enum FontHinting {
+    None,
+    Mono,
+    Light,
+    Normal,
+    LCD,
+}
+
+#[cfg(not(any(target_os = "macos", target_os = "windows")))]
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)]
+pub struct FontInstancePlatformOptions {
+    pub flags: u16,
+    pub lcd_filter: FontLCDFilter,
+    pub hinting: FontHinting,
+}
+
+#[cfg(not(any(target_os = "macos", target_os = "windows")))]
+impl Default for FontInstancePlatformOptions {
+    fn default() -> FontInstancePlatformOptions {
+        FontInstancePlatformOptions {
+            flags: 0,
+            lcd_filter: FontLCDFilter::Default,
+            hinting: FontHinting::LCD,
+        }
+    }
 }
 
 #[derive(Clone, Hash, PartialEq, Eq, Debug, Deserialize, Serialize, Ord, PartialOrd)]
 pub struct FontInstance {
     pub font_key: FontKey,
     // The font size is in *device* pixels, not logical pixels.
     // It is stored as an Au since we need sub-pixel sizes, but
     // can't store as a f32 due to use of this type as a hash key.
@@ -224,36 +319,23 @@ pub struct FontInstance {
     pub variations: Vec<FontVariation>,
     pub synthetic_italics: bool,
 }
 
 impl FontInstance {
     pub fn new(
         font_key: FontKey,
         size: Au,
-        mut color: ColorF,
+        color: ColorF,
         render_mode: FontRenderMode,
         subpx_dir: SubpixelDirection,
         platform_options: Option<FontInstancePlatformOptions>,
         variations: Vec<FontVariation>,
         synthetic_italics: bool,
     ) -> 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.
-        match render_mode {
-            FontRenderMode::Alpha | FontRenderMode::Mono => {
-                color = ColorF::new(0.0, 0.0, 0.0, 1.0);
-            }
-            FontRenderMode::Bitmap => {
-                color = ColorF::new(1.0, 1.0, 1.0, 1.0);
-            }
-            FontRenderMode::Subpixel => {}
-        }
-
         FontInstance {
             font_key,
             size,
             color: color.into(),
             render_mode,
             subpx_dir,
             platform_options,
             variations,
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -179,18 +179,19 @@ impl MutByteSlice {
 pub struct WrFontInstanceOptions {
     pub render_mode: FontRenderMode,
     pub synthetic_italics: bool,
 }
 
 impl Into<FontInstanceOptions> for WrFontInstanceOptions {
     fn into(self) -> FontInstanceOptions {
         FontInstanceOptions {
-            render_mode: Some(self.render_mode),
+            render_mode: self.render_mode,
             synthetic_italics: self.synthetic_italics,
+            ..FontInstanceOptions::default()
         }
     }
 }
 
 #[repr(C)]
 #[derive(Debug, Clone, Copy)]
 pub struct WrImageMask {
     image: WrImageKey,
@@ -1189,30 +1190,29 @@ pub extern "C" fn wr_dp_define_clip(stat
     debug_assert!(unsafe { is_in_main_thread() });
     let complex_slice = make_slice(complex, complex_count);
     let complex_iter = complex_slice.iter().cloned();
     let mask : Option<ImageMask> = unsafe { mask.as_ref() }.map(|x| x.into());
 
     let clip_id = state.frame_builder.dl_builder.define_clip(None, clip_rect, complex_iter, mask);
     // return the u64 id value from inside the ClipId::Clip(..)
     match clip_id {
-        ClipId::Clip(id, nesting_index, pipeline_id) => {
+        ClipId::Clip(id, pipeline_id) => {
             assert!(pipeline_id == state.pipeline_id);
-            assert!(nesting_index == 0);
             id
         },
         _ => panic!("Got unexpected clip id type"),
     }
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_clip(state: &mut WrState,
                                   clip_id: u64) {
     debug_assert!(unsafe { is_in_main_thread() });
-    state.frame_builder.dl_builder.push_clip_id(ClipId::Clip(clip_id, 0, state.pipeline_id));
+    state.frame_builder.dl_builder.push_clip_id(ClipId::Clip(clip_id, state.pipeline_id));
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_pop_clip(state: &mut WrState) {
     debug_assert!(unsafe { !is_in_render_thread() });
     state.frame_builder.dl_builder.pop_clip_id();
 }
 
@@ -1227,19 +1227,18 @@ pub extern "C" fn wr_dp_define_sticky_fr
     assert!(unsafe { is_in_main_thread() });
     let clip_id = state.frame_builder.dl_builder.define_sticky_frame(
         None, content_rect, StickyFrameInfo::new(
             unsafe { top_range.as_ref() }.cloned(),
             unsafe { right_range.as_ref() }.cloned(),
             unsafe { bottom_range.as_ref() }.cloned(),
             unsafe { left_range.as_ref() }.cloned()));
     match clip_id {
-        ClipId::Clip(id, nesting_index, pipeline_id) => {
+        ClipId::Clip(id, pipeline_id) => {
             assert!(pipeline_id == state.pipeline_id);
-            assert!(nesting_index == 0);
             id
         },
         _ => panic!("Got unexpected clip id type"),
     }
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_define_scroll_layer(state: &mut WrState,
@@ -1281,17 +1280,17 @@ pub extern "C" fn wr_scroll_layer_with_i
 pub extern "C" fn wr_dp_push_clip_and_scroll_info(state: &mut WrState,
                                                   scroll_id: u64,
                                                   clip_id: *const u64) {
     debug_assert!(unsafe { is_in_main_thread() });
     let scroll_id = ClipId::new(scroll_id, state.pipeline_id);
     let info = if let Some(&id) = unsafe { clip_id.as_ref() } {
         ClipAndScrollInfo::new(
             scroll_id,
-            ClipId::Clip(id, 0, state.pipeline_id))
+            ClipId::Clip(id, state.pipeline_id))
     } else {
         ClipAndScrollInfo::simple(scroll_id)
     };
     state.frame_builder.dl_builder.push_clip_and_scroll_info(info);
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_pop_clip_and_scroll_info(state: &mut WrState) {
@@ -1723,26 +1722,20 @@ pub unsafe extern "C" fn wr_api_finalize
     let (_, size, dl) = frame_builder.dl_builder.finalize();
     *content_size = LayoutSize::new(size.width, size.height);
     let (data, descriptor) = dl.into_data();
     *dl_data = WrVecU8::from_vec(data);
     *dl_descriptor = descriptor;
 }
 
 #[no_mangle]
-pub extern "C" fn wr_dp_push_built_display_list(state: &mut WrState,
-                                                dl_descriptor: BuiltDisplayListDescriptor,
-                                                dl_data: &mut WrVecU8) {
-    let dl_vec = mem::replace(dl_data, WrVecU8::from_vec(Vec::new())).to_vec();
-
-    let dl = BuiltDisplayList::from_data(dl_vec, dl_descriptor);
-
-    state.frame_builder.dl_builder.push_nested_display_list(&dl);
-    let (data, _) = dl.into_data();
-    mem::replace(dl_data, WrVecU8::from_vec(data));
+pub extern "C" fn wr_dp_push_built_display_list(_state: &mut WrState,
+                                                _dl_descriptor: BuiltDisplayListDescriptor,
+                                                _dl_data: &mut WrVecU8) {
+    unreachable!();
 }
 
 pub type VecU8 = Vec<u8>;
 pub type ArcVecU8 = Arc<VecU8>;
 
 #[no_mangle]
 pub extern "C" fn wr_add_ref_arc(arc: &ArcVecU8) -> *const VecU8 {
     Arc::into_raw(arc.clone())