Bug 1409736 - Update webrender to commit d741f472dd3d6c3441646f7bf4e714c71bea39b7. r?jrmuizel draft
authorKartikaya Gupta <kgupta@mozilla.com>
Mon, 23 Oct 2017 09:46:47 -0400
changeset 684718 a799c80bf5a193c9a122ef0554c8c059fdcc08c8
parent 684703 d49501f258b105c5e2dcd0a59896ec1ceabf726b
child 684719 e89f906bdabf407594885d5f5197292d3e50a5b2
push id85704
push userkgupta@mozilla.com
push dateMon, 23 Oct 2017 13:50:55 +0000
reviewersjrmuizel
bugs1409736
milestone58.0a1
Bug 1409736 - Update webrender to commit d741f472dd3d6c3441646f7bf4e714c71bea39b7. r?jrmuizel MozReview-Commit-ID: FhFX2PAHwU
gfx/doc/README.webrender
gfx/webrender/Cargo.toml
gfx/webrender/examples/basic.rs
gfx/webrender/examples/blob.rs
gfx/webrender/examples/common/boilerplate.rs
gfx/webrender/res/brush_mask.glsl
gfx/webrender/res/cs_blur.glsl
gfx/webrender/src/border.rs
gfx/webrender/src/clip.rs
gfx/webrender/src/frame.rs
gfx/webrender/src/frame_builder.rs
gfx/webrender/src/gpu_types.rs
gfx/webrender/src/picture.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/tiling.rs
gfx/webrender_api/Cargo.toml
gfx/webrender_api/src/api.rs
gfx/webrender_api/src/color.rs
gfx/webrender_api/src/display_item.rs
gfx/webrender_api/src/display_list.rs
gfx/webrender_api/src/image.rs
gfx/webrender_api/src/lib.rs
gfx/webrender_bindings/Cargo.toml
--- a/gfx/doc/README.webrender
+++ b/gfx/doc/README.webrender
@@ -170,9 +170,9 @@ 2. Sometimes autoland tip has changed en
    has an env var you can set to do this). In theory you can get the same
    result by resolving the conflict manually but Cargo.lock files are usually not
    trivial to merge by hand. If it's just the third_party/rust dir that has conflicts
    you can delete it and run |mach vendor rust| again to repopulate it.
 
 -------------------------------------------------------------------------------
 
 The version of WebRender currently in the tree is:
-7892f5364bc4d35c7a9b42949f0ace4cc54f8b3c
+d741f472dd3d6c3441646f7bf4e714c71bea39b7
--- a/gfx/webrender/Cargo.toml
+++ b/gfx/webrender/Cargo.toml
@@ -1,11 +1,11 @@
 [package]
 name = "webrender"
-version = "0.52.1"
+version = "0.53.0"
 authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
 license = "MPL-2.0"
 repository = "https://github.com/servo/webrender"
 build = "build.rs"
 
 [features]
 default = ["freetype-lib"]
 freetype-lib = ["freetype/servo-freetype-sys"]
--- a/gfx/webrender/examples/basic.rs
+++ b/gfx/webrender/examples/basic.rs
@@ -213,17 +213,21 @@ impl Example for App {
             ImageData::new(vec![0, 80, 180, 255]),
             None,
         );
         let mask = ImageMask {
             image: image_mask_key,
             rect: (75, 75).by(100, 100),
             repeat: false,
         };
-        let complex = ComplexClipRegion::new((50, 50).to(150, 150), BorderRadius::uniform(20.0));
+        let complex = ComplexClipRegion::new(
+            (50, 50).to(150, 150),
+            BorderRadius::uniform(20.0),
+            ClipMode::Clip
+        );
         let id = builder.define_clip(None, bounds, vec![complex], Some(mask));
         builder.push_clip_id(id);
 
         let info = LayoutPrimitiveInfo::new((100, 100).to(200, 200));
         builder.push_rect(&info, ColorF::new(0.0, 1.0, 0.0, 1.0));
 
         let info = LayoutPrimitiveInfo::new((250, 100).to(350, 200));
         builder.push_rect(&info, ColorF::new(0.0, 1.0, 0.0, 1.0));
@@ -334,17 +338,17 @@ impl Example for App {
 
             builder.push_box_shadow(
                 &info,
                 simple_box_bounds,
                 offset,
                 color,
                 blur_radius,
                 spread_radius,
-                simple_border_radius,
+                BorderRadius::uniform(simple_border_radius),
                 box_shadow_type,
             );
         }
 
         builder.pop_stacking_context();
     }
 
     fn on_event(&mut self, event: glutin::Event, api: &RenderApi, document_id: DocumentId) -> bool {
--- a/gfx/webrender/examples/blob.rs
+++ b/gfx/webrender/examples/blob.rs
@@ -12,18 +12,18 @@ mod boilerplate;
 
 use boilerplate::{Example, HandyDandyRectBuilder};
 use rayon::Configuration as ThreadPoolConfig;
 use rayon::ThreadPool;
 use std::collections::HashMap;
 use std::collections::hash_map::Entry;
 use std::sync::Arc;
 use std::sync::mpsc::{channel, Receiver, Sender};
-use webrender::api::{self, DisplayListBuilder, DocumentId, LayoutSize, PipelineId, RenderApi,
-                     ResourceUpdates};
+use webrender::api::{self, DeviceUintRect, DisplayListBuilder, DocumentId, LayoutSize, PipelineId,
+                     RenderApi, ResourceUpdates};
 
 // This example shows how to implement a very basic BlobImageRenderer that can only render
 // a checkerboard pattern.
 
 // The deserialized command list internally used by this example is just a color.
 type ImageRenderingCommands = api::ColorU;
 
 // Serialize/deserialze the blob.
@@ -140,17 +140,17 @@ impl CheckerboardRenderer {
 }
 
 impl api::BlobImageRenderer for CheckerboardRenderer {
     fn add(&mut self, key: api::ImageKey, cmds: api::BlobImageData, _: Option<api::TileSize>) {
         self.image_cmds
             .insert(key, Arc::new(deserialize_blob(&cmds[..]).unwrap()));
     }
 
-    fn update(&mut self, key: api::ImageKey, cmds: api::BlobImageData) {
+    fn update(&mut self, key: api::ImageKey, cmds: api::BlobImageData, _dirty_rect: Option<DeviceUintRect>) {
         // Here, updating is just replacing the current version of the commands with
         // the new one (no incremental updates).
         self.image_cmds
             .insert(key, Arc::new(deserialize_blob(&cmds[..]).unwrap()));
     }
 
     fn delete(&mut self, key: api::ImageKey) {
         self.image_cmds.remove(&key);
--- a/gfx/webrender/examples/common/boilerplate.rs
+++ b/gfx/webrender/examples/common/boilerplate.rs
@@ -17,22 +17,28 @@ struct Notifier {
 
 impl Notifier {
     fn new(window_proxy: glutin::WindowProxy) -> Notifier {
         Notifier { window_proxy }
     }
 }
 
 impl RenderNotifier for Notifier {
-    fn new_frame_ready(&mut self) {
+    fn clone(&self) -> Box<RenderNotifier> {
+        Box::new(Notifier {
+            window_proxy: self.window_proxy.clone(),
+        })
+    }
+
+    fn new_frame_ready(&self) {
         #[cfg(not(target_os = "android"))]
         self.window_proxy.wakeup_event_loop();
     }
 
-    fn new_scroll_frame_ready(&mut self, _composite_needed: bool) {
+    fn new_scroll_frame_ready(&self, _composite_needed: bool) {
         #[cfg(not(target_os = "android"))]
         self.window_proxy.wakeup_event_loop();
     }
 }
 
 pub trait HandyDandyRectBuilder {
     fn to(&self, x2: i32, y2: i32) -> LayoutRect;
     fn by(&self, w: i32, h: i32) -> LayoutRect;
@@ -120,23 +126,21 @@ pub fn main_wrapper(example: &mut Exampl
         resource_override_path: res_path,
         debug: true,
         precache_shaders: true,
         device_pixel_ratio: window.hidpi_factor(),
         ..options.unwrap_or(webrender::RendererOptions::default())
     };
 
     let size = DeviceUintSize::new(width, height);
-    let (mut renderer, sender) = webrender::Renderer::new(gl.clone(), opts).unwrap();
+    let notifier = Box::new(Notifier::new(window.create_window_proxy()));
+    let (mut renderer, sender) = webrender::Renderer::new(gl.clone(), notifier, opts).unwrap();
     let api = sender.create_api();
     let document_id = api.add_document(size);
 
-    let notifier = Box::new(Notifier::new(window.create_window_proxy()));
-    renderer.set_render_notifier(notifier);
-
     if let Some(external_image_handler) = example.get_external_image_handler() {
         renderer.set_external_image_handler(external_image_handler);
     }
     if let Some(output_image_handler) = example.get_output_image_handler(&*gl) {
         renderer.set_output_image_handler(output_image_handler);
     }
 
     let epoch = Epoch(0);
--- a/gfx/webrender/res/brush_mask.glsl
+++ b/gfx/webrender/res/brush_mask.glsl
@@ -9,36 +9,38 @@ flat varying vec4 vClipCenter_Radius_TL;
 flat varying vec4 vClipCenter_Radius_TR;
 flat varying vec4 vClipCenter_Radius_BR;
 flat varying vec4 vClipCenter_Radius_BL;
 
 #ifdef WR_VERTEX_SHADER
 
 struct BrushPrimitive {
     float clip_mode;
-    float radius;
+    vec2 radius_tl;
+    vec2 radius_tr;
+    vec2 radius_br;
+    vec2 radius_bl;
 };
 
 BrushPrimitive fetch_brush_primitive(int address) {
-    vec4 data = fetch_from_resource_cache_1(address);
-    return BrushPrimitive(data.x, data.y);
+    vec4 data[3] = fetch_from_resource_cache_3(address);
+    return BrushPrimitive(data[0].x, data[1].xy, data[1].zw, data[2].xy, data[2].zw);
 }
 
 void brush_vs(int prim_address, vec4 prim_rect) {
     // Load the specific primitive.
     BrushPrimitive prim = fetch_brush_primitive(prim_address + 2);
 
     // Write clip parameters
     vClipMode = prim.clip_mode;
 
-    vec2 r = vec2(prim.radius);
-    vClipCenter_Radius_TL = vec4(prim_rect.xy + vec2(r.x, r.y), r);
-    vClipCenter_Radius_TR = vec4(prim_rect.zy + vec2(-r.x, r.y), r);
-    vClipCenter_Radius_BR = vec4(prim_rect.zw + vec2(-r.x, -r.y), r);
-    vClipCenter_Radius_BL = vec4(prim_rect.xw + vec2(r.x, -r.y), r);
+    vClipCenter_Radius_TL = vec4(prim_rect.xy + prim.radius_tl, prim.radius_tl);
+    vClipCenter_Radius_TR = vec4(prim_rect.zy + vec2(-prim.radius_tr.x, prim.radius_tr.y), prim.radius_tr);
+    vClipCenter_Radius_BR = vec4(prim_rect.zw - prim.radius_br, prim.radius_br);
+    vClipCenter_Radius_BL = vec4(prim_rect.xw + vec2(prim.radius_bl.x, -prim.radius_bl.y), prim.radius_bl);
 }
 #endif
 
 #ifdef WR_FRAGMENT_SHADER
 vec4 brush_fs(vec2 local_pos, vec4 local_rect) {
     // TODO(gw): The mask code below is super-inefficient. Once we
     // start using primitive segments in brush shaders, this can
     // be made much faster.
--- a/gfx/webrender/res/cs_blur.glsl
+++ b/gfx/webrender/res/cs_blur.glsl
@@ -15,26 +15,24 @@ flat varying int vBlurRadius;
 // by the dir field in the blur command.
 
 #define DIR_HORIZONTAL  0
 #define DIR_VERTICAL    1
 
 in int aBlurRenderTaskAddress;
 in int aBlurSourceTaskAddress;
 in int aBlurDirection;
+in vec4 aBlurRegion;
 
 void main(void) {
     RenderTaskData task = fetch_render_task(aBlurRenderTaskAddress);
     RenderTaskData src_task = fetch_render_task(aBlurSourceTaskAddress);
 
-    vec4 local_rect = task.data0;
-
-    vec2 pos = mix(local_rect.xy,
-                   local_rect.xy + local_rect.zw,
-                   aPosition.xy);
+    vec4 src_rect = src_task.data0;
+    vec4 target_rect = task.data0;
 
 #if defined WR_FEATURE_COLOR
     vec2 texture_size = vec2(textureSize(sCacheRGBA8, 0).xy);
 #else
     vec2 texture_size = vec2(textureSize(sCacheA8, 0).xy);
 #endif
     vUv.z = src_task.data1.x;
     vBlurRadius = 3 * int(task.data1.y);
@@ -44,22 +42,30 @@ void main(void) {
         case DIR_HORIZONTAL:
             vOffsetScale = vec2(1.0 / texture_size.x, 0.0);
             break;
         case DIR_VERTICAL:
             vOffsetScale = vec2(0.0, 1.0 / texture_size.y);
             break;
     }
 
-    vUvRect = vec4(src_task.data0.xy + vec2(0.5),
-                   src_task.data0.xy + src_task.data0.zw - vec2(0.5));
+    vUvRect = vec4(src_rect.xy + vec2(0.5),
+                   src_rect.xy + src_rect.zw - vec2(0.5));
     vUvRect /= texture_size.xyxy;
 
-    vec2 uv0 = src_task.data0.xy / texture_size;
-    vec2 uv1 = (src_task.data0.xy + src_task.data0.zw) / texture_size;
+    if (aBlurRegion.z > 0.0) {
+        vec4 blur_region = aBlurRegion * uDevicePixelRatio;
+        src_rect = vec4(src_rect.xy + blur_region.xy, blur_region.zw);
+        target_rect = vec4(target_rect.xy + blur_region.xy, blur_region.zw);
+    }
+
+    vec2 pos = target_rect.xy + target_rect.zw * aPosition.xy;
+
+    vec2 uv0 = src_rect.xy / texture_size;
+    vec2 uv1 = (src_rect.xy + src_rect.zw) / texture_size;
     vUv.xy = mix(uv0, uv1, aPosition.xy);
 
     gl_Position = uTransform * vec4(pos, 0.0, 1.0);
 }
 #endif
 
 #ifdef WR_FRAGMENT_SHADER
 
--- a/gfx/webrender/src/border.rs
+++ b/gfx/webrender/src/border.rs
@@ -10,16 +10,17 @@ use frame_builder::FrameBuilder;
 use gpu_cache::GpuDataRequest;
 use prim_store::{BorderPrimitiveCpu, PrimitiveContainer, TexelRect};
 use tiling::PrimitiveFlags;
 use util::{lerp, pack_as_float};
 
 #[repr(u8)]
 #[derive(Debug, Copy, Clone, PartialEq)]
 pub enum BorderCornerInstance {
+    None,
     Single, // Single instance needed - corner styles are same or similar.
     Double, // Different corner styles. Draw two instances, one per style.
 }
 
 #[repr(C)]
 pub enum BorderCornerSide {
     Both,
     First,
@@ -123,30 +124,34 @@ impl NormalBorderHelpers for NormalBorde
         edge0: &BorderSide,
         width0: f32,
         edge1: &BorderSide,
         width1: f32,
         radius: &LayerSize,
         corner: BorderCorner,
         border_rect: &LayerRect,
     ) -> BorderCornerKind {
-        // If either width is zero, a corner isn't formed.
-        if width0 == 0.0 || width1 == 0.0 {
+        // If both widths are zero, a corner isn't formed.
+        if width0 == 0.0 && width1 == 0.0 {
             return BorderCornerKind::None;
         }
 
         // If both edges are transparent, no corner is formed.
         if edge0.color.a == 0.0 && edge1.color.a == 0.0 {
             return BorderCornerKind::None;
         }
 
         match (edge0.style, edge1.style) {
-            // If either edge is none or hidden, no corner is needed.
-            (BorderStyle::None, _) | (_, BorderStyle::None) => BorderCornerKind::None,
-            (BorderStyle::Hidden, _) | (_, BorderStyle::Hidden) => BorderCornerKind::None,
+            // If both edges are none or hidden, no corner is needed.
+            (BorderStyle::None, BorderStyle::None) |
+            (BorderStyle::None, BorderStyle::Hidden) |
+            (BorderStyle::Hidden, BorderStyle::None) |
+            (BorderStyle::Hidden, BorderStyle::Hidden) => {
+                BorderCornerKind::None
+            }
 
             // If both borders are solid, we can draw them with a simple rectangle if
             // both the colors match and there is no radius.
             (BorderStyle::Solid, BorderStyle::Solid) => {
                 if edge0.color == edge1.color && radius.width == 0.0 && radius.height == 0.0 {
                     BorderCornerKind::Solid
                 } else {
                     BorderCornerKind::Clip(BorderCornerInstance::Single)
@@ -424,26 +429,29 @@ impl FrameBuilder {
                 );
             }
         } else {
             // Create clip masks for border corners, if required.
             let mut extra_clips = Vec::new();
             let mut corner_instances = [BorderCornerInstance::Single; 4];
 
             for (i, corner) in corners.iter().enumerate() {
-                match corner {
-                    &BorderCornerKind::Mask(corner_data, corner_radius, widths, kind) => {
+                match *corner {
+                    BorderCornerKind::Mask(corner_data, corner_radius, widths, kind) => {
                         let clip_source =
                             BorderCornerClipSource::new(corner_data, corner_radius, widths, kind);
                         extra_clips.push(ClipSource::BorderCorner(clip_source));
                     }
-                    &BorderCornerKind::Clip(instance_kind) => {
+                    BorderCornerKind::Clip(instance_kind) => {
                         corner_instances[i] = instance_kind;
                     }
-                    _ => {}
+                    BorderCornerKind::Solid => {}
+                    BorderCornerKind::None => {
+                        corner_instances[i] = BorderCornerInstance::None;
+                    }
                 }
             }
 
             self.add_normal_border_primitive(
                 info,
                 border,
                 widths,
                 clip_and_scroll,
--- a/gfx/webrender/src/clip.rs
+++ b/gfx/webrender/src/clip.rs
@@ -1,21 +1,21 @@
 /* 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, ComplexClipRegion, DeviceIntRect, ImageMask, ImageRendering, LayerPoint};
-use api::{LayerRect, LayerSize, LayerToWorldTransform, LayoutPoint, LayoutVector2D, LocalClip};
+use api::{ClipMode, LayerRect, LayerSize};
+use api::{LayerToWorldTransform, LayoutPoint, LayoutVector2D, LocalClip};
 use border::BorderCornerClipSource;
 use ellipse::Ellipse;
 use freelist::{FreeList, FreeListHandle, WeakFreeListHandle};
 use gpu_cache::{GpuCache, GpuCacheHandle, ToGpuBlocks};
 use prim_store::{ClipData, ImageMaskData};
 use resource_cache::ResourceCache;
-use std::ops::Not;
 use util::{extract_inner_rect_safe, TransformedRect};
 
 const MAX_CLIP: f32 = 1000000.0;
 
 pub type ClipStore = FreeList<ClipSources>;
 pub type ClipSourcesHandle = FreeListHandle<ClipSources>;
 pub type ClipSourcesWeakHandle = WeakFreeListHandle<ClipSources>;
 
@@ -56,34 +56,16 @@ impl ClipRegion {
         let complex_clips = match local_clip {
             &LocalClip::Rect(_) => Vec::new(),
             &LocalClip::RoundedRect(_, ref region) => vec![region.clone()],
         };
         ClipRegion::create_for_clip_node(*local_clip.clip_rect(), complex_clips, None)
     }
 }
 
-#[repr(C)]
-#[derive(Copy, Clone, Debug, PartialEq)]
-pub enum ClipMode {
-    Clip,    // Pixels inside the region are visible.
-    ClipOut, // Pixels outside the region are visible.
-}
-
-impl Not for ClipMode {
-    type Output = ClipMode;
-
-    fn not(self) -> ClipMode {
-        match self {
-            ClipMode::Clip => ClipMode::ClipOut,
-            ClipMode::ClipOut => ClipMode::Clip,
-        }
-    }
-}
-
 #[derive(Debug)]
 pub enum ClipSource {
     Rectangle(LayerRect),
     RoundedRectangle(LayerRect, BorderRadius, ClipMode),
     Image(ImageMask),
     /// TODO(gw): This currently only handles dashed style
     /// clips, where the border style is dashed for both
     /// adjacent border edges. Expand to handle dotted style
@@ -100,17 +82,17 @@ impl From<ClipRegion> for ClipSources {
         }
 
         clips.push(ClipSource::Rectangle(region.main));
 
         for complex in region.complex_clips {
             clips.push(ClipSource::RoundedRectangle(
                 complex.rect,
                 complex.radii,
-                ClipMode::Clip,
+                complex.mode,
             ));
         }
 
         ClipSources::new(clips)
     }
 }
 
 impl ClipSource {
--- a/gfx/webrender/src/frame.rs
+++ b/gfx/webrender/src/frame.rs
@@ -4,17 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{BuiltDisplayListIter, ClipAndScrollInfo, ClipId, ColorF, ComplexClipRegion};
 use api::{DeviceUintRect, DeviceUintSize, DisplayItemRef, Epoch, FilterOp, HitTestFlags};
 use api::{HitTestResult, ImageDisplayItem, ItemRange, LayerPoint, LayerPrimitiveInfo, LayerRect};
 use api::{LayerSize, LayerToScrollTransform, LayerVector2D, LayoutSize, LayoutTransform};
 use api::{LocalClip, PipelineId, ScrollClamping, ScrollEventPhase, ScrollLayerState};
 use api::{ScrollLocation, ScrollPolicy, ScrollSensitivity, SpecificDisplayItem, StackingContext};
-use api::{TileOffset, TransformStyle, WorldPoint};
+use api::{ClipMode, TileOffset, TransformStyle, WorldPoint};
 use clip::ClipRegion;
 use clip_scroll_tree::{ClipScrollTree, ScrollStates};
 use euclid::rect;
 use frame_builder::{FrameBuilder, FrameBuilderConfig};
 use gpu_cache::GpuCache;
 use internal_types::{FastHashMap, FastHashSet, RendererFrame};
 use profiler::{GpuCacheProfileCounters, TextureCacheProfileCounters};
 use resource_cache::{ResourceCache, TiledImageMap};
@@ -1146,17 +1146,22 @@ fn try_to_add_rectangle_splitting_on_cli
     // into an inner opaque region just ends up hurting batching and
     // doing more work than necessary.
     if color.a != 1.0 {
         return false;
     }
 
     let inner_unclipped_rect = match &info.local_clip {
         &LocalClip::Rect(_) => return false,
-        &LocalClip::RoundedRect(_, ref region) => region.get_inner_rect_full(),
+        &LocalClip::RoundedRect(_, ref region) => {
+            if region.mode == ClipMode::ClipOut {
+                return false;
+            }
+            region.get_inner_rect_full()
+        }
     };
     let inner_unclipped_rect = match inner_unclipped_rect {
         Some(rect) => rect,
         None => return false,
     };
 
     // The inner rectangle is not clipped by its assigned clipping node, so we can
     // let it be clipped by the parent of the clipping node, which may result in
--- a/gfx/webrender/src/frame_builder.rs
+++ b/gfx/webrender/src/frame_builder.rs
@@ -1,50 +1,53 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{BorderDetails, BorderDisplayItem, BorderRadius, BoxShadowClipMode, BuiltDisplayList};
-use api::{ComplexClipRegion, ClipAndScrollInfo, ClipId, ColorF};
+use api::{ClipMode, ComplexClipRegion, ClipAndScrollInfo, ClipId, ColorF, LayoutSize};
 use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceUintRect, DeviceUintSize};
 use api::{ExtendMode, FilterOp, FontInstance, FontRenderMode};
 use api::{GlyphInstance, GlyphOptions, GradientStop, HitTestFlags, HitTestItem, HitTestResult};
 use api::{ImageKey, ImageRendering, ItemRange, ItemTag, LayerPoint, LayerPrimitiveInfo, LayerRect};
 use api::{LayerPixel, LayerSize, LayerToScrollTransform, LayerVector2D, LayoutVector2D, LineOrientation};
 use api::{LineStyle, LocalClip, PipelineId, RepeatMode};
 use api::{ScrollSensitivity, Shadow, TileOffset, TransformStyle};
 use api::{WorldPixel, WorldPoint, YuvColorSpace, YuvData, device_length};
 use app_units::Au;
 use border::ImageBorderSegment;
-use clip::{ClipMode, ClipRegion, ClipSource, ClipSources, ClipStore, Contains};
+use clip::{ClipRegion, ClipSource, ClipSources, ClipStore, Contains};
 use clip_scroll_node::{ClipInfo, ClipScrollNode, NodeType};
 use clip_scroll_tree::{ClipScrollTree, CoordinateSystemId};
 use euclid::{SideOffsets2D, TypedTransform3D, 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::{BrushPrimitive, TexelRect, YuvImagePrimitiveCpu};
 use prim_store::{GradientPrimitiveCpu, ImagePrimitiveCpu, LinePrimitive, PrimitiveKind};
 use prim_store::{PrimitiveContainer, PrimitiveIndex};
 use prim_store::{PrimitiveStore, RadialGradientPrimitiveCpu};
 use prim_store::{RectanglePrimitive, TextRunPrimitiveCpu};
 use profiler::{FrameProfileCounters, GpuCacheProfileCounters, TextureCacheProfileCounters};
-use render_task::{AlphaRenderItem, ClipChain, RenderTask, RenderTaskId, RenderTaskLocation};
+use render_task::{AlphaRenderItem, ClearMode, ClipChain, RenderTask, RenderTaskId, RenderTaskLocation};
 use render_task::RenderTaskTree;
 use resource_cache::ResourceCache;
 use scene::ScenePipeline;
 use std::{mem, usize, f32, i32};
 use tiling::{ClipScrollGroup, ClipScrollGroupIndex, CompositeOps, Frame};
 use tiling::{ContextIsolation, RenderTargetKind, StackingContextIndex};
 use tiling::{PackedLayer, PackedLayerIndex, PrimitiveFlags, PrimitiveRunCmd, RenderPass};
 use tiling::{RenderTargetContext, ScrollbarPrimitive, StackingContext};
 use util::{self, pack_as_float, RectHelpers, recycle_vec};
 
+// The blur shader samples BLUR_SAMPLE_SCALE * blur_radius surrounding texels.
+const BLUR_SAMPLE_SCALE: f32 = 3.0;
+
 /// Construct a polygon from stacking context boundaries.
 /// `anchor` here is an index that's going to be preserved in all the
 /// splits of the polygon.
 fn make_polygon(
     stacking_context: &StackingContext,
     node: &ClipScrollNode,
     anchor: usize,
 ) -> Polygon<f64, WorldPixel> {
@@ -237,17 +240,17 @@ impl FrameBuilder {
             self.clip_scroll_group_indices.insert(clip_and_scroll, group_id);
         }
 
         if let &LocalClip::RoundedRect(main, region) = &info.local_clip {
             clip_sources.push(ClipSource::Rectangle(main));
             clip_sources.push(ClipSource::RoundedRectangle(
                 region.rect,
                 region.radii,
-                ClipMode::Clip,
+                region.mode,
             ));
         }
 
         let clip_sources = self.clip_store.insert(ClipSources::new(clip_sources));
         let prim_index = self.prim_store.add_primitive(
             &info.rect,
             &info.local_clip.clip_rect(),
             info.is_backface_visible,
@@ -552,17 +555,17 @@ impl FrameBuilder {
     }
 
     pub fn push_shadow(
         &mut self,
         shadow: Shadow,
         clip_and_scroll: ClipAndScrollInfo,
         info: &LayerPrimitiveInfo,
     ) {
-        let prim = PicturePrimitive::new_shadow(shadow, RenderTargetKind::Color);
+        let prim = PicturePrimitive::new_text_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.create_primitive(
             clip_and_scroll,
             info,
@@ -582,17 +585,17 @@ impl FrameBuilder {
         for (prim_index, pending_primitives) in shadows.drain(..) {
             {
                 // 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_pictures[metadata.cpu_prim_index.0];
-                let shadow = prim.as_shadow();
+                let shadow = prim.as_text_shadow();
 
                 metadata.local_rect = metadata.local_rect.translate(&shadow.offset);
             }
 
             // Push any fast-path shadows now
             for (prim_index, clip_and_scroll) in pending_primitives {
                 self.add_primitive_to_draw_list(prim_index, clip_and_scroll);
             }
@@ -671,17 +674,17 @@ impl FrameBuilder {
             style: style,
             orientation: orientation,
         };
 
         let mut fast_shadow_prims = Vec::new();
         for (idx, &(shadow_prim_index, _)) in self.shadow_prim_stack.iter().enumerate() {
             let shadow_metadata = &self.prim_store.cpu_metadata[shadow_prim_index.0];
             let picture = &self.prim_store.cpu_pictures[shadow_metadata.cpu_prim_index.0];
-            let shadow = picture.as_shadow();
+            let shadow = picture.as_text_shadow();
             if shadow.blur_radius == 0.0 {
                 fast_shadow_prims.push((idx, shadow.clone()));
             }
         }
 
         for (idx, shadow) in fast_shadow_prims {
             let mut line = line.clone();
             line.color = shadow.color;
@@ -714,17 +717,17 @@ impl FrameBuilder {
             }
         }
 
         for &(shadow_prim_index, _) in &self.shadow_prim_stack {
             let shadow_metadata = &mut self.prim_store.cpu_metadata[shadow_prim_index.0];
             debug_assert_eq!(shadow_metadata.prim_kind, PrimitiveKind::Picture);
             let picture =
                 &mut self.prim_store.cpu_pictures[shadow_metadata.cpu_prim_index.0];
-            let blur_radius = picture.as_shadow().blur_radius;
+            let blur_radius = picture.as_text_shadow().blur_radius;
 
             // Only run real blurs here (fast path zero blurs are handled above).
             if blur_radius > 0.0 {
                 let shadow_rect = new_rect.inflate(
                     blur_radius,
                     blur_radius,
                 );
                 shadow_metadata.local_rect = shadow_metadata.local_rect.union(&shadow_rect);
@@ -1178,17 +1181,17 @@ impl FrameBuilder {
         // primitive with the shadow's color and offset. These need to be added
         // *before* the visual text primitive in order to get the correct paint
         // order. Store them in a Vec first to work around borrowck issues.
         // TODO(gw): Refactor to avoid having to store them in a Vec first.
         let mut fast_shadow_prims = Vec::new();
         for (idx, &(shadow_prim_index, _)) in self.shadow_prim_stack.iter().enumerate() {
             let shadow_metadata = &self.prim_store.cpu_metadata[shadow_prim_index.0];
             let picture_prim = &self.prim_store.cpu_pictures[shadow_metadata.cpu_prim_index.0];
-            let shadow = picture_prim.as_shadow();
+            let shadow = picture_prim.as_text_shadow();
             if shadow.blur_radius == 0.0 {
                 let mut text_prim = prim.clone();
                 text_prim.font.color = shadow.color.into();
                 text_prim.offset += shadow.offset;
                 fast_shadow_prims.push((idx, text_prim));
             }
         }
 
@@ -1233,17 +1236,17 @@ impl FrameBuilder {
         // the indices as sub-primitives to the shadow primitives.
         for &(shadow_prim_index, _) in &self.shadow_prim_stack {
             let shadow_metadata = &mut self.prim_store.cpu_metadata[shadow_prim_index.0];
             debug_assert_eq!(shadow_metadata.prim_kind, PrimitiveKind::Picture);
             let picture_prim =
                 &mut self.prim_store.cpu_pictures[shadow_metadata.cpu_prim_index.0];
 
             // Only run real blurs here (fast path zero blurs are handled above).
-            let blur_radius = picture_prim.as_shadow().blur_radius;
+            let blur_radius = picture_prim.as_text_shadow().blur_radius;
             if blur_radius > 0.0 {
                 let shadow_rect = rect.inflate(
                     blur_radius,
                     blur_radius,
                 );
                 shadow_metadata.local_rect = shadow_metadata.local_rect.union(&shadow_rect);
                 picture_prim.add_primitive(prim_index, clip_and_scroll);
             }
@@ -1253,102 +1256,100 @@ impl FrameBuilder {
     pub fn add_box_shadow(
         &mut self,
         clip_and_scroll: ClipAndScrollInfo,
         prim_info: &LayerPrimitiveInfo,
         box_offset: &LayerVector2D,
         color: &ColorF,
         blur_radius: f32,
         spread_radius: f32,
-        border_radius: f32,
+        border_radius: BorderRadius,
         clip_mode: BoxShadowClipMode,
     ) {
         if color.a == 0.0 {
             return;
         }
 
         let spread_amount = match clip_mode {
             BoxShadowClipMode::Outset => {
                 spread_radius
             }
             BoxShadowClipMode::Inset => {
                 -spread_radius
             }
         };
 
-        // Adjust the shadow box radius as per:
-        // https://drafts.csswg.org/css-backgrounds-3/#shadow-shape
-        let sharpness_scale = if border_radius < spread_radius {
-            let r = border_radius / spread_amount;
-            1.0 + (r - 1.0) * (r - 1.0) * (r - 1.0)
-        } else {
-            1.0
-        };
-        let shadow_radius = (border_radius + spread_amount * sharpness_scale).max(0.0);
+        let shadow_radius = adjust_border_radius_for_box_shadow(
+            border_radius,
+            spread_amount,
+            spread_radius
+        );
         let shadow_rect = prim_info.rect
                                    .translate(box_offset)
                                    .inflate(spread_amount, spread_amount);
 
         if blur_radius == 0.0 {
             let mut clips = Vec::new();
 
             let fast_info = match clip_mode {
                 BoxShadowClipMode::Outset => {
                     // TODO(gw): Add a fast path for ClipOut + zero border radius!
                     clips.push(ClipSource::RoundedRectangle(
                         prim_info.rect,
-                        BorderRadius::uniform(border_radius),
+                        border_radius,
                         ClipMode::ClipOut
                     ));
 
                     LayerPrimitiveInfo::with_clip(
                         shadow_rect,
                         LocalClip::RoundedRect(
                             shadow_rect,
-                            ComplexClipRegion::new(shadow_rect, BorderRadius::uniform(shadow_radius)),
+                            ComplexClipRegion::new(
+                                shadow_rect,
+                                shadow_radius,
+                                ClipMode::Clip,
+                            ),
                         ),
                     )
                 }
                 BoxShadowClipMode::Inset => {
                     clips.push(ClipSource::RoundedRectangle(
                         shadow_rect,
-                        BorderRadius::uniform(shadow_radius),
+                        shadow_radius,
                         ClipMode::ClipOut
                     ));
 
                     LayerPrimitiveInfo::with_clip(
                         prim_info.rect,
                         LocalClip::RoundedRect(
                             prim_info.rect,
-                            ComplexClipRegion::new(prim_info.rect, BorderRadius::uniform(border_radius)),
+                            ComplexClipRegion::new(
+                                prim_info.rect,
+                                border_radius,
+                                ClipMode::Clip
+                            ),
                         ),
                     )
                 }
             };
 
             self.add_primitive(
                 clip_and_scroll,
                 &fast_info,
                 clips,
                 PrimitiveContainer::Rectangle(RectanglePrimitive {
                     color: *color,
                 }),
             );
         } else {
-            let shadow = Shadow {
-                blur_radius,
-                color: *color,
-                offset: LayerVector2D::zero(),
-            };
-
             let blur_offset = 2.0 * blur_radius;
             let mut extra_clips = vec![];
-            let mut pic_prim = PicturePrimitive::new_shadow(shadow, RenderTargetKind::Alpha);
+            let mut blur_regions = vec![];
 
-            let pic_info = match clip_mode {
+            match clip_mode {
                 BoxShadowClipMode::Outset => {
                     let brush_prim = BrushPrimitive {
                         clip_mode: ClipMode::Clip,
                         radius: shadow_radius,
                     };
 
                     let brush_rect = LayerRect::new(LayerPoint::new(blur_offset, blur_offset),
                                                     shadow_rect.size);
@@ -1357,26 +1358,82 @@ impl FrameBuilder {
 
                     let brush_prim_index = self.create_primitive(
                         clip_and_scroll,
                         &brush_info,
                         Vec::new(),
                         PrimitiveContainer::Brush(brush_prim),
                     );
 
+                    let pic_rect = shadow_rect.inflate(blur_offset, blur_offset);
+                    let blur_range = BLUR_SAMPLE_SCALE * blur_radius;
+
+                    let size = pic_rect.size;
+
+                    let tl = LayerSize::new(
+                        blur_radius.max(border_radius.top_left.width),
+                        blur_radius.max(border_radius.top_left.height)
+                    ) * BLUR_SAMPLE_SCALE;
+                    let tr = LayerSize::new(
+                        blur_radius.max(border_radius.top_right.width),
+                        blur_radius.max(border_radius.top_right.height)
+                    ) * BLUR_SAMPLE_SCALE;
+                    let br = LayerSize::new(
+                        blur_radius.max(border_radius.bottom_right.width),
+                        blur_radius.max(border_radius.bottom_right.height)
+                    ) * BLUR_SAMPLE_SCALE;
+                    let bl = LayerSize::new(
+                        blur_radius.max(border_radius.bottom_left.width),
+                        blur_radius.max(border_radius.bottom_left.height)
+                    ) * BLUR_SAMPLE_SCALE;
+
+                    let max_width = tl.width.max(tr.width.max(bl.width.max(br.width)));
+                    let max_height = tl.height.max(tr.height.max(bl.height.max(br.height)));
+
+                    // Apply a conservative test that if any of the blur regions below
+                    // will overlap, we won't bother applying the region optimization
+                    // and will just blur the entire thing. This should only happen
+                    // in rare cases, where either the blur radius or border radius
+                    // is very large, in which case there's no real point in trying
+                    // to only blur a small region anyway.
+                    if max_width < 0.5 * size.width && max_height < 0.5 * size.height {
+                        blur_regions.push(LayerRect::from_floats(0.0, 0.0, tl.width, tl.height));
+                        blur_regions.push(LayerRect::from_floats(size.width - tr.width, 0.0, size.width, tr.height));
+                        blur_regions.push(LayerRect::from_floats(size.width - br.width, size.height - br.height, size.width, size.height));
+                        blur_regions.push(LayerRect::from_floats(0.0, size.height - bl.height, bl.width, size.height));
+
+                        blur_regions.push(LayerRect::from_floats(0.0, tl.height, blur_range, size.height - bl.height));
+                        blur_regions.push(LayerRect::from_floats(size.width - blur_range, tr.height, size.width, size.height - br.height));
+                        blur_regions.push(LayerRect::from_floats(tl.width, 0.0, size.width - tr.width, blur_range));
+                        blur_regions.push(LayerRect::from_floats(bl.width, size.height - blur_range, size.width - br.width, size.height));
+                    }
+
+                    let mut pic_prim = PicturePrimitive::new_box_shadow(
+                        blur_radius,
+                        *color,
+                        blur_regions,
+                        BoxShadowClipMode::Outset,
+                    );
+
                     pic_prim.add_primitive(brush_prim_index, clip_and_scroll);
 
                     extra_clips.push(ClipSource::RoundedRectangle(
                         prim_info.rect,
-                        BorderRadius::uniform(border_radius),
+                        border_radius,
                         ClipMode::ClipOut,
                     ));
 
-                    let pic_rect = shadow_rect.inflate(blur_offset, blur_offset);
-                    LayerPrimitiveInfo::new(pic_rect)
+                    let pic_info = LayerPrimitiveInfo::new(pic_rect);
+
+                    self.add_primitive(
+                        clip_and_scroll,
+                        &pic_info,
+                        extra_clips,
+                        PrimitiveContainer::Picture(pic_prim),
+                    );
                 }
                 BoxShadowClipMode::Inset => {
                     let brush_prim = BrushPrimitive {
                         clip_mode: ClipMode::ClipOut,
                         radius: shadow_radius,
                     };
 
                     let mut brush_rect = shadow_rect;
@@ -1387,35 +1444,45 @@ impl FrameBuilder {
 
                     let brush_prim_index = self.create_primitive(
                         clip_and_scroll,
                         &brush_info,
                         Vec::new(),
                         PrimitiveContainer::Brush(brush_prim),
                     );
 
+                    let pic_rect = prim_info.rect.inflate(blur_offset, blur_offset);
+
+                    // TODO(gw): Apply minimal blur regions for inset box shadows.
+
+                    let mut pic_prim = PicturePrimitive::new_box_shadow(
+                        blur_radius,
+                        *color,
+                        blur_regions,
+                        BoxShadowClipMode::Inset,
+                    );
+
                     pic_prim.add_primitive(brush_prim_index, clip_and_scroll);
 
                     extra_clips.push(ClipSource::RoundedRectangle(
                         prim_info.rect,
-                        BorderRadius::uniform(border_radius),
+                        border_radius,
                         ClipMode::Clip,
                     ));
 
-                    let pic_rect = prim_info.rect.inflate(blur_offset, blur_offset);
-                    LayerPrimitiveInfo::with_clip_rect(pic_rect, prim_info.rect)
-                }
-            };
+                    let pic_info = LayerPrimitiveInfo::with_clip_rect(pic_rect, prim_info.rect);
 
-            self.add_primitive(
-                clip_and_scroll,
-                &pic_info,
-                extra_clips,
-                PrimitiveContainer::Picture(pic_prim),
-            );
+                    self.add_primitive(
+                        clip_and_scroll,
+                        &pic_info,
+                        extra_clips,
+                        PrimitiveContainer::Picture(pic_prim),
+                    );
+                }
+            }
         }
     }
 
     pub fn add_image(
         &mut self,
         clip_and_scroll: ClipAndScrollInfo,
         info: &LayerPrimitiveInfo,
         stretch_size: &LayerSize,
@@ -1765,18 +1832,19 @@ impl FrameBuilder {
             debug!(
                 "\tProcessing group scroll={:?}, clip={:?}",
                 group.scroll_node_id,
                 group.clip_node_id
             );
 
             let transform = scroll_node.world_content_transform;
             if !packed_layer.set_transform(transform) {
+                group.screen_bounding_rect = None;
                 debug!("\t\tUnable to set transform {:?}", transform);
-                return;
+                continue;
             }
 
             // Here we move the viewport rectangle into the coordinate system
             // of the stacking context content.
             let local_viewport_rect = clip_node
                 .combined_local_viewport_rect
                 .translate(&clip_node.reference_frame_relative_scroll_offset)
                 .translate(&-scroll_node.reference_frame_relative_scroll_offset)
@@ -2040,16 +2108,18 @@ impl FrameBuilder {
                                 let blur_radius = device_length(blur_radius, device_pixel_ratio);
                                 render_tasks.get_mut(current_task_id)
                                             .inflate(blur_radius.0);
                                 let blur_render_task = RenderTask::new_blur(
                                     blur_radius,
                                     current_task_id,
                                     render_tasks,
                                     RenderTargetKind::Color,
+                                    &[],
+                                    ClearMode::Transparent,
                                 );
                                 let blur_render_task_id = render_tasks.add(blur_render_task);
                                 let item = AlphaRenderItem::HardwareComposite(
                                     stacking_context_index,
                                     blur_render_task_id,
                                     HardwareCompositeOp::PremultipliedAlpha,
                                     DeviceIntPoint::new(
                                         screen_origin.x - blur_radius.0,
@@ -2321,8 +2391,72 @@ impl FrameBuilder {
             passes,
             layer_texture_data: self.packed_layers.clone(),
             render_tasks,
             deferred_resolves,
             gpu_cache_updates: Some(gpu_cache_updates),
         }
     }
 }
+
+fn adjust_border_radius_for_box_shadow(
+    radius: BorderRadius,
+    spread_amount: f32,
+    spread_radius: f32,
+) -> BorderRadius {
+    BorderRadius {
+        top_left: adjust_corner_for_box_shadow(
+            radius.top_left,
+            spread_radius,
+            spread_amount,
+        ),
+        top_right: adjust_corner_for_box_shadow(
+            radius.top_right,
+            spread_radius,
+            spread_amount,
+        ),
+        bottom_right: adjust_corner_for_box_shadow(
+            radius.bottom_right,
+            spread_radius,
+            spread_amount,
+        ),
+        bottom_left: adjust_corner_for_box_shadow(
+            radius.bottom_left,
+            spread_radius,
+            spread_amount,
+        ),
+    }
+}
+
+fn adjust_corner_for_box_shadow(
+    corner: LayoutSize,
+    spread_amount: f32,
+    spread_radius: f32,
+) -> LayoutSize {
+    LayoutSize::new(
+        adjust_radius_for_box_shadow(
+            corner.width,
+            spread_radius,
+            spread_amount
+        ),
+        adjust_radius_for_box_shadow(
+            corner.height,
+            spread_radius,
+            spread_amount
+        ),
+    )
+}
+
+fn adjust_radius_for_box_shadow(
+    border_radius: f32,
+    spread_amount: f32,
+    spread_radius: f32,
+) -> f32 {
+    // Adjust the shadow box radius as per:
+    // https://drafts.csswg.org/css-backgrounds-3/#shadow-shape
+    let sharpness_scale = if border_radius < spread_radius {
+        let r = border_radius / spread_amount;
+        1.0 + (r - 1.0) * (r - 1.0) * (r - 1.0)
+    } else {
+        1.0
+    };
+    (border_radius + spread_amount * sharpness_scale).max(0.0)
+}
--- a/gfx/webrender/src/gpu_types.rs
+++ b/gfx/webrender/src/gpu_types.rs
@@ -1,40 +1,42 @@
 /* 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::LayerRect;
 use gpu_cache::GpuCacheAddress;
 use render_task::RenderTaskAddress;
 use tiling::PackedLayerIndex;
 
 // Contains type that must exactly match the same structures declared in GLSL.
 
 #[derive(Debug, Copy, Clone)]
 pub struct PackedLayerAddress(i32);
 
 impl From<PackedLayerIndex> for PackedLayerAddress {
     fn from(index: PackedLayerIndex) -> PackedLayerAddress {
         PackedLayerAddress(index.0 as i32)
     }
 }
 
 #[repr(i32)]
-#[derive(Debug)]
+#[derive(Debug, Copy, Clone)]
 pub enum BlurDirection {
     Horizontal = 0,
     Vertical,
 }
 
 #[derive(Debug)]
 #[repr(C)]
 pub struct BlurInstance {
     pub task_address: RenderTaskAddress,
     pub src_task_address: RenderTaskAddress,
     pub blur_direction: BlurDirection,
+    pub region: LayerRect,
 }
 
 /// A clipping primitive drawn into the clipping mask.
 /// Could be an image or a rectangle, which defines the
 /// way `address` is treated.
 #[derive(Debug, Copy, Clone)]
 #[repr(C)]
 pub struct ClipMaskInstance {
--- a/gfx/webrender/src/picture.rs
+++ b/gfx/webrender/src/picture.rs
@@ -1,15 +1,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-use api::{ClipAndScrollInfo, Shadow};
-use prim_store::PrimitiveIndex;
-use render_task::RenderTaskId;
+use api::{ColorF, ClipAndScrollInfo, device_length, DeviceIntSize};
+use api::{BoxShadowClipMode, LayerRect, Shadow};
+use frame_builder::PrimitiveContext;
+use gpu_cache::GpuDataRequest;
+use prim_store::{PrimitiveIndex, PrimitiveMetadata};
+use render_task::{ClearMode, RenderTask, RenderTaskId, RenderTaskTree};
 use tiling::RenderTargetKind;
 
 /*
  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.
@@ -20,51 +23,72 @@ use tiling::RenderTargetKind;
 #[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.
+pub enum PictureKind {
+    TextShadow {
+        shadow: Shadow,
+    },
+    BoxShadow {
+        blur_radius: f32,
+        color: ColorF,
+        blur_regions: Vec<LayerRect>,
+        clip_mode: BoxShadowClipMode,
+    },
 }
 
 #[derive(Debug)]
 pub struct PicturePrimitive {
     pub prim_runs: Vec<PrimitiveRun>,
-    pub composite_op: CompositeOp,
     pub render_task_id: Option<RenderTaskId>,
-    pub kind: RenderTargetKind,
+    pub kind: PictureKind,
 
     // 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,
-        kind: RenderTargetKind,
+    pub fn new_text_shadow(shadow: Shadow) -> PicturePrimitive {
+        PicturePrimitive {
+            prim_runs: Vec::new(),
+            render_task_id: None,
+            kind: PictureKind::TextShadow {
+                shadow,
+            },
+        }
+    }
+
+    pub fn new_box_shadow(
+        blur_radius: f32,
+        color: ColorF,
+        blur_regions: Vec<LayerRect>,
+        clip_mode: BoxShadowClipMode,
     ) -> PicturePrimitive {
         PicturePrimitive {
             prim_runs: Vec::new(),
-            composite_op: CompositeOp::Shadow(shadow),
             render_task_id: None,
-            kind,
+            kind: PictureKind::BoxShadow {
+                blur_radius,
+                color,
+                blur_regions,
+                clip_mode,
+            },
         }
     }
 
-    pub fn as_shadow(&self) -> &Shadow {
-        match self.composite_op {
-            CompositeOp::Shadow(ref shadow) => shadow,
+    pub fn as_text_shadow(&self) -> &Shadow {
+        match self.kind {
+            PictureKind::TextShadow { ref shadow } => shadow,
+            PictureKind::BoxShadow { .. } => panic!("bug: not a text shadow")
         }
     }
 
     pub fn add_primitive(
         &mut self,
         prim_index: PrimitiveIndex,
         clip_and_scroll: ClipAndScrollInfo
     ) {
@@ -77,9 +101,88 @@ impl PicturePrimitive {
         }
 
         self.prim_runs.push(PrimitiveRun {
             prim_index,
             count: 1,
             clip_and_scroll,
         });
     }
+
+    pub fn prepare_for_render(
+        &mut self,
+        prim_index: PrimitiveIndex,
+        prim_metadata: &PrimitiveMetadata,
+        prim_context: &PrimitiveContext,
+        render_tasks: &mut RenderTaskTree,
+    ) {
+        // 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 =
+            (prim_metadata.local_rect.size.width * prim_context.device_pixel_ratio).ceil() as i32;
+        let cache_height =
+            (prim_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, target_kind, blur_regions, clear_mode) = match self.kind {
+            PictureKind::TextShadow { ref shadow } => {
+                let dummy: &[LayerRect] = &[];
+                (shadow.blur_radius, RenderTargetKind::Color, dummy, ClearMode::Transparent)
+            }
+            PictureKind::BoxShadow { blur_radius, clip_mode, ref blur_regions, .. } => {
+                let clear_mode = match clip_mode {
+                    BoxShadowClipMode::Outset => ClearMode::One,
+                    BoxShadowClipMode::Inset => ClearMode::Zero,
+                };
+                (blur_radius, RenderTargetKind::Alpha, blur_regions.as_slice(), clear_mode)
+            }
+        };
+        let blur_radius = device_length(blur_radius, prim_context.device_pixel_ratio);
+
+        let picture_task = RenderTask::new_picture(
+            cache_size,
+            prim_index,
+            target_kind,
+        );
+        let picture_task_id = render_tasks.add(picture_task);
+        let render_task = RenderTask::new_blur(
+            blur_radius,
+            picture_task_id,
+            render_tasks,
+            target_kind,
+            blur_regions,
+            clear_mode,
+        );
+        self.render_task_id = Some(render_tasks.add(render_task));
+    }
+
+    pub fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
+        match self.kind {
+            PictureKind::TextShadow { ref shadow } => {
+                request.push(shadow.color);
+                request.push([
+                    shadow.offset.x,
+                    shadow.offset.y,
+                    shadow.blur_radius,
+                    0.0,
+                ]);
+            }
+            PictureKind::BoxShadow { blur_radius, color, .. } => {
+                request.push(color);
+                request.push([
+                    0.0,
+                    0.0,
+                    blur_radius,
+                    0.0,
+                ]);
+            }
+        }
+    }
+
+    pub fn target_kind(&self) -> RenderTargetKind {
+        match self.kind {
+            PictureKind::TextShadow { .. } => RenderTargetKind::Color,
+            PictureKind::BoxShadow { .. } => RenderTargetKind::Alpha,
+        }
+    }
 }
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -1,19 +1,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-use api::{BorderRadius, BuiltDisplayList, ColorF, ComplexClipRegion, DeviceIntRect, DeviceIntSize};
+use api::{BorderRadius, BuiltDisplayList, ColorF, ComplexClipRegion, DeviceIntRect};
 use api::{DevicePoint, ExtendMode, FontInstance, FontRenderMode, GlyphInstance, GlyphKey};
 use api::{GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag, LayerPoint, LayerRect};
-use api::{LayerSize, LayerVector2D, LineOrientation, LineStyle};
-use api::{TileOffset, YuvColorSpace, YuvFormat, device_length};
+use api::{ClipMode, LayerSize, LayerVector2D, LineOrientation, LineStyle};
+use api::{TileOffset, YuvColorSpace, YuvFormat};
 use border::BorderCornerInstance;
-use clip::{ClipMode, ClipSourcesHandle, ClipStore, Geometry};
+use clip::{ClipSourcesHandle, ClipStore, Geometry};
 use frame_builder::PrimitiveContext;
 use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest,
                 ToGpuBlocks};
 use picture::PicturePrimitive;
 use render_task::{ClipWorkItem, ClipChainNode, RenderTask, RenderTaskId, RenderTaskTree};
 use renderer::MAX_VERTEX_TEXTURE_WIDTH;
 use resource_cache::{ImageProperties, ResourceCache};
 use std::{mem, usize};
@@ -162,27 +162,39 @@ impl ToGpuBlocks for RectanglePrimitive 
     fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
         request.push(self.color);
     }
 }
 
 #[derive(Debug)]
 pub struct BrushPrimitive {
     pub clip_mode: ClipMode,
-    pub radius: f32,
+    pub radius: BorderRadius,
 }
 
 impl ToGpuBlocks for BrushPrimitive {
     fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
         request.push([
             self.clip_mode as u32 as f32,
-            self.radius,
+            0.0,
             0.0,
             0.0
         ]);
+        request.push([
+            self.radius.top_left.width,
+            self.radius.top_left.height,
+            self.radius.top_right.width,
+            self.radius.top_right.height,
+        ]);
+        request.push([
+            self.radius.bottom_right.width,
+            self.radius.bottom_right.height,
+            self.radius.bottom_left.width,
+            self.radius.bottom_left.height,
+        ]);
     }
 }
 
 #[derive(Debug, Clone)]
 #[repr(C)]
 pub struct LinePrimitive {
     pub color: ColorF,
     pub style: LineStyle,
@@ -1032,56 +1044,30 @@ impl PrimitiveStore {
     }
 
     fn prepare_prim_for_render_inner(
         &mut self,
         prim_index: PrimitiveIndex,
         prim_context: &PrimitiveContext,
         resource_cache: &mut ResourceCache,
         gpu_cache: &mut GpuCache,
-        // For some primitives, we need to mark dependencies as needed for rendering
-        // without spawning new tasks, since there will be another call to
-        // `prepare_prim_for_render_inner` specifically for this primitive later on.
-        render_tasks: Option<&mut RenderTaskTree>,
+        render_tasks: &mut RenderTaskTree,
         text_run_mode: TextRunMode,
     ) {
         let metadata = &mut self.cpu_metadata[prim_index.0];
         match metadata.prim_kind {
             PrimitiveKind::Rectangle | PrimitiveKind::Border | PrimitiveKind::Line => {}
             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 = 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
-                picture.render_task_id = render_tasks.map(|rt| {
-                    let picture_task = RenderTask::new_picture(
-                        cache_size,
+                self.cpu_pictures[metadata.cpu_prim_index.0]
+                    .prepare_for_render(
                         prim_index,
-                        picture.kind,
+                        metadata,
+                        prim_context,
+                        render_tasks
                     );
-                    let picture_task_id = rt.add(picture_task);
-                    let render_task = RenderTask::new_blur(
-                        blur_radius,
-                        picture_task_id,
-                        rt,
-                        picture.kind
-                    );
-                    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,
                     prim_context.display_list,
                     text_run_mode,
@@ -1168,25 +1154,18 @@ impl PrimitiveStore {
                     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::Picture => {
-                    let picture = &self.cpu_pictures[metadata.cpu_prim_index.0];
-                    let shadow = picture.as_shadow();
-                    request.push(shadow.color);
-                    request.push([
-                        shadow.offset.x,
-                        shadow.offset.y,
-                        shadow.blur_radius,
-                        0.0,
-                    ]);
+                    self.cpu_pictures[metadata.cpu_prim_index.0]
+                        .write_gpu_blocks(request);
                 }
                 PrimitiveKind::Brush => {
                     let brush = &self.cpu_brushes[metadata.cpu_prim_index.0];
                     brush.write_gpu_blocks(request);
                 }
             }
         }
     }
@@ -1337,17 +1316,17 @@ impl PrimitiveStore {
             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,
+                    render_tasks,
                     TextRunMode::Shadow,
                 );
             }
         }
 
         if !self.update_clip_task(
             prim_index,
             prim_context,
@@ -1360,17 +1339,17 @@ impl PrimitiveStore {
             return None;
         }
 
         self.prepare_prim_for_render_inner(
             prim_index,
             prim_context,
             resource_cache,
             gpu_cache,
-            Some(render_tasks),
+            render_tasks,
             TextRunMode::Normal,
         );
 
         Some(geometry)
     }
 }
 
 
--- a/gfx/webrender/src/render_backend.rs
+++ b/gfx/webrender/src/render_backend.rs
@@ -17,17 +17,18 @@ use gpu_cache::GpuCache;
 use internal_types::{DebugOutput, FastHashMap, FastHashSet, RendererFrame, ResultMsg};
 use profiler::{BackendProfileCounters, ResourceProfileCounters};
 use rayon::ThreadPool;
 use record::ApiRecordingReceiver;
 use resource_cache::ResourceCache;
 use scene::Scene;
 #[cfg(feature = "debugger")]
 use serde_json;
-use std::sync::{Arc, Mutex};
+use std::sync::atomic::{ATOMIC_USIZE_INIT, AtomicUsize, Ordering};
+use std::sync::Arc;
 use std::sync::mpsc::Sender;
 use std::u32;
 use texture_cache::TextureCache;
 use thread_profiler::register_thread_with_profiler;
 use time::precise_time_ns;
 
 struct Document {
     scene: Scene,
@@ -119,70 +120,74 @@ impl Document {
 enum DocumentOp {
     Nop,
     Built,
     ScrolledNop,
     Scrolled(RendererFrame),
     Rendered(RendererFrame),
 }
 
+/// The unique id for WR resource identification.
+static NEXT_NAMESPACE_ID: AtomicUsize = ATOMIC_USIZE_INIT;
+
 /// The render backend is responsible for transforming high level display lists into
 /// GPU-friendly work which is then submitted to the renderer in the form of a frame::Frame.
 ///
 /// The render backend operates on its own thread.
 pub struct RenderBackend {
     api_rx: MsgReceiver<ApiMsg>,
     payload_rx: PayloadReceiver,
     payload_tx: PayloadSender,
     result_tx: Sender<ResultMsg>,
-    next_namespace_id: IdNamespace,
     default_device_pixel_ratio: f32,
 
     gpu_cache: GpuCache,
     resource_cache: ResourceCache,
 
     frame_config: FrameBuilderConfig,
     documents: FastHashMap<DocumentId, Document>,
 
-    notifier: Arc<Mutex<Option<Box<RenderNotifier>>>>,
+    notifier: Box<RenderNotifier>,
     recorder: Option<Box<ApiRecordingReceiver>>,
 
     enable_render_on_scroll: bool,
 }
 
 impl RenderBackend {
     pub fn new(
         api_rx: MsgReceiver<ApiMsg>,
         payload_rx: PayloadReceiver,
         payload_tx: PayloadSender,
         result_tx: Sender<ResultMsg>,
         default_device_pixel_ratio: f32,
         texture_cache: TextureCache,
         workers: Arc<ThreadPool>,
-        notifier: Arc<Mutex<Option<Box<RenderNotifier>>>>,
+        notifier: Box<RenderNotifier>,
         frame_config: FrameBuilderConfig,
         recorder: Option<Box<ApiRecordingReceiver>>,
         blob_image_renderer: Option<Box<BlobImageRenderer>>,
         enable_render_on_scroll: bool,
     ) -> RenderBackend {
+        // The namespace_id should start from 1.
+        NEXT_NAMESPACE_ID.fetch_add(1, Ordering::Relaxed);
+
         let resource_cache = ResourceCache::new(texture_cache, workers, blob_image_renderer);
 
         register_thread_with_profiler("Backend".to_string());
 
         RenderBackend {
             api_rx,
             payload_rx,
             payload_tx,
             result_tx,
             default_device_pixel_ratio,
             resource_cache,
             gpu_cache: GpuCache::new(),
             frame_config,
             documents: FastHashMap::default(),
-            next_namespace_id: IdNamespace(1),
             notifier,
             recorder,
 
             enable_render_on_scroll,
         }
     }
 
     fn process_document(
@@ -416,32 +421,35 @@ impl RenderBackend {
                     DocumentOp::Rendered(frame)
                 } else {
                     DocumentOp::ScrolledNop
                 }
             }
         }
     }
 
+    fn next_namespace_id(&self) -> IdNamespace {
+        IdNamespace(NEXT_NAMESPACE_ID.fetch_add(1, Ordering::Relaxed) as u32)
+    }
+
     pub fn run(&mut self, mut profile_counters: BackendProfileCounters) {
         let mut frame_counter: u32 = 0;
 
         loop {
             profile_scope!("handle_msg");
 
             let msg = match self.api_rx.recv() {
                 Ok(msg) => {
                     if let Some(ref mut r) = self.recorder {
                         r.write_msg(frame_counter, &msg);
                     }
                     msg
                 }
                 Err(..) => {
-                    let notifier = self.notifier.lock();
-                    notifier.unwrap().as_mut().unwrap().shut_down();
+                    self.notifier.shut_down();
                     break;
                 }
             };
 
             match msg {
                 ApiMsg::UpdateResources(updates) => {
                     self.resource_cache
                         .update_resources(updates, &mut profile_counters.resources);
@@ -458,19 +466,17 @@ impl RenderBackend {
                     let mut glyph_indices = Vec::new();
                     for ch in text.chars() {
                         let index = self.resource_cache.get_glyph_index(font_key, ch);
                         glyph_indices.push(index);
                     }
                     tx.send(glyph_indices).unwrap();
                 }
                 ApiMsg::CloneApi(sender) => {
-                    let namespace = self.next_namespace_id;
-                    self.next_namespace_id = IdNamespace(namespace.0 + 1);
-                    sender.send(namespace).unwrap();
+                    sender.send(self.next_namespace_id()).unwrap();
                 }
                 ApiMsg::AddDocument(document_id, initial_size) => {
                     let document = Document::new(
                         self.frame_config.clone(),
                         initial_size,
                         self.enable_render_on_scroll,
                         self.default_device_pixel_ratio,
                     );
@@ -499,18 +505,17 @@ impl RenderBackend {
                             &mut profile_counters,
                         );
                     }
                 },
                 ApiMsg::DeleteDocument(document_id) => {
                     self.documents.remove(&document_id);
                 }
                 ApiMsg::ExternalEvent(evt) => {
-                    let notifier = self.notifier.lock();
-                    notifier.unwrap().as_mut().unwrap().external_event(evt);
+                    self.notifier.external_event(evt);
                 }
                 ApiMsg::ClearNamespace(namespace_id) => {
                     self.resource_cache.clear_namespace(namespace_id);
                     let document_ids = self.documents
                         .keys()
                         .filter(|did| did.0 == namespace_id)
                         .cloned()
                         .collect::<Vec<_>>();
@@ -525,42 +530,35 @@ impl RenderBackend {
                     let msg = ResultMsg::UpdateResources {
                         updates: pending_update,
                         cancel_rendering: true,
                     };
                     self.result_tx.send(msg).unwrap();
                     // We use new_frame_ready to wake up the renderer and get the
                     // resource updates processed, but the UpdateResources message
                     // will cancel rendering the frame.
-                    self.notifier
-                        .lock()
-                        .unwrap()
-                        .as_mut()
-                        .unwrap()
-                        .new_frame_ready();
+                    self.notifier.new_frame_ready();
                 }
                 ApiMsg::DebugCommand(option) => {
                     let msg = match option {
                         DebugCommand::FetchDocuments => {
                             let json = self.get_docs_for_debugger();
                             ResultMsg::DebugOutput(DebugOutput::FetchDocuments(json))
                         }
                         DebugCommand::FetchClipScrollTree => {
                             let json = self.get_clip_scroll_tree_for_debugger();
                             ResultMsg::DebugOutput(DebugOutput::FetchClipScrollTree(json))
                         }
                         _ => ResultMsg::DebugCommand(option),
                     };
                     self.result_tx.send(msg).unwrap();
-                    let notifier = self.notifier.lock();
-                    notifier.unwrap().as_mut().unwrap().new_frame_ready();
+                    self.notifier.new_frame_ready();
                 }
                 ApiMsg::ShutDown => {
-                    let notifier = self.notifier.lock();
-                    notifier.unwrap().as_mut().unwrap().shut_down();
+                    self.notifier.shut_down();
                     break;
                 }
             }
         }
     }
 
     fn publish_frame(
         &mut self,
@@ -577,41 +575,21 @@ impl RenderBackend {
     fn publish_frame_and_notify_compositor(
         &mut self,
         document_id: DocumentId,
         frame: RendererFrame,
         profile_counters: &mut BackendProfileCounters,
     ) {
         self.publish_frame(document_id, frame, profile_counters);
 
-        // TODO(gw): This is kindof bogus to have to lock the notifier
-        //           each time it's used. This is due to some nastiness
-        //           in initialization order for Servo. Perhaps find a
-        //           cleaner way to do this, or use the OnceMutex on crates.io?
-        let mut notifier = self.notifier.lock();
-        notifier
-            .as_mut()
-            .unwrap()
-            .as_mut()
-            .unwrap()
-            .new_frame_ready();
+        self.notifier.new_frame_ready();
     }
 
-    fn notify_compositor_of_new_scroll_frame(&mut self, composite_needed: bool) {
-        // TODO(gw): This is kindof bogus to have to lock the notifier
-        //           each time it's used. This is due to some nastiness
-        //           in initialization order for Servo. Perhaps find a
-        //           cleaner way to do this, or use the OnceMutex on crates.io?
-        let mut notifier = self.notifier.lock();
-        notifier
-            .as_mut()
-            .unwrap()
-            .as_mut()
-            .unwrap()
-            .new_scroll_frame_ready(composite_needed);
+    fn notify_compositor_of_new_scroll_frame(&self, composite_needed: bool) {
+        self.notifier.new_scroll_frame_ready(composite_needed);
     }
 
 
     #[cfg(not(feature = "debugger"))]
     fn get_docs_for_debugger(&self) -> String {
         String::new()
     }
 
--- a/gfx/webrender/src/render_task.rs
+++ b/gfx/webrender/src/render_task.rs
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{ClipId, DeviceIntLength, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
 use api::{FilterOp, MixBlendMode};
-use api::PipelineId;
+use api::{LayerRect, PipelineId};
 use clip::{ClipSource, ClipSourcesWeakHandle, ClipStore};
 use clip_scroll_tree::CoordinateSystemId;
 use gpu_cache::GpuCacheHandle;
 use internal_types::HardwareCompositeOp;
 use prim_store::PrimitiveIndex;
 use std::{cmp, usize, f32, i32};
 use std::rc::Rc;
 use tiling::{ClipScrollGroupIndex, PackedLayerIndex, RenderPass, RenderTargetIndex};
@@ -259,16 +259,17 @@ pub struct PictureTask {
     pub prim_index: PrimitiveIndex,
     pub target_kind: RenderTargetKind,
 }
 
 #[derive(Debug)]
 pub struct BlurTask {
     pub blur_radius: DeviceIntLength,
     pub target_kind: RenderTargetKind,
+    pub regions: Vec<LayerRect>,
 }
 
 #[derive(Debug)]
 pub struct RenderTaskData {
     pub data: [f32; FLOATS_PER_RENDER_TASK_INFO],
 }
 
 #[derive(Debug)]
@@ -277,22 +278,33 @@ pub enum RenderTaskKind {
     Picture(PictureTask),
     CacheMask(CacheMaskTask),
     VerticalBlur(BlurTask),
     HorizontalBlur(BlurTask),
     Readback(DeviceIntRect),
     Alias(RenderTaskId),
 }
 
+#[derive(Debug, Copy, Clone)]
+pub enum ClearMode {
+    // Applicable to color and alpha targets.
+    Zero,
+    One,
+
+    // Applicable to color targets only.
+    Transparent,
+}
+
 #[derive(Debug)]
 pub struct RenderTask {
     pub cache_key: Option<RenderTaskKey>,
     pub location: RenderTaskLocation,
     pub children: Vec<RenderTaskId>,
     pub kind: RenderTaskKind,
+    pub clear_mode: ClearMode,
 }
 
 impl RenderTask {
     pub fn new_alpha_batch(
         screen_origin: DeviceIntPoint,
         location: RenderTaskLocation,
         frame_output_pipeline_id: Option<PipelineId>,
     ) -> RenderTask {
@@ -300,49 +312,57 @@ impl RenderTask {
             cache_key: None,
             children: Vec::new(),
             location,
             kind: RenderTaskKind::Alpha(AlphaRenderTask {
                 screen_origin,
                 items: Vec::new(),
                 frame_output_pipeline_id,
             }),
+            clear_mode: ClearMode::Transparent,
         }
     }
 
     pub fn new_dynamic_alpha_batch(
         rect: &DeviceIntRect,
         frame_output_pipeline_id: Option<PipelineId>,
     ) -> RenderTask {
         let location = RenderTaskLocation::Dynamic(None, rect.size);
         Self::new_alpha_batch(rect.origin, location, frame_output_pipeline_id)
     }
 
     pub fn new_picture(
         size: DeviceIntSize,
         prim_index: PrimitiveIndex,
         target_kind: RenderTargetKind,
     ) -> RenderTask {
+        let clear_mode = match target_kind {
+            RenderTargetKind::Color => ClearMode::Transparent,
+            RenderTargetKind::Alpha => ClearMode::One,
+        };
+
         RenderTask {
             cache_key: None,
             children: Vec::new(),
             location: RenderTaskLocation::Dynamic(None, size),
             kind: RenderTaskKind::Picture(PictureTask {
                 prim_index,
                 target_kind,
             }),
+            clear_mode,
         }
     }
 
     pub fn new_readback(screen_rect: DeviceIntRect) -> RenderTask {
         RenderTask {
             cache_key: None,
             children: Vec::new(),
             location: RenderTaskLocation::Dynamic(None, screen_rect.size),
             kind: RenderTaskKind::Readback(screen_rect),
+            clear_mode: ClearMode::Transparent,
         }
     }
 
     pub fn new_mask(
         key: Option<ClipId>,
         task_rect: DeviceIntRect,
         raw_clips: ClipChain,
         extra_clip: ClipChain,
@@ -415,16 +435,17 @@ impl RenderTask {
             location: RenderTaskLocation::Dynamic(None, task_rect.size),
             kind: RenderTaskKind::CacheMask(CacheMaskTask {
                 actual_rect: task_rect,
                 inner_rect: inner_rect.unwrap_or(DeviceIntRect::zero()),
                 clips,
                 geometry_kind,
                 coordinate_system_id: prim_coordinate_system_id,
             }),
+            clear_mode: ClearMode::One,
         })
     }
 
     // Construct a render task to apply a blur to a primitive. For now,
     // this is only used for text runs, but we can probably extend this
     // to handle general blurs to any render task in the future.
     // The render task chain that is constructed looks like:
     //
@@ -438,39 +459,45 @@ impl RenderTask {
     //           |
     //           +---- This is stored as the input task to the primitive shader.
     //
     pub fn new_blur(
         blur_radius: DeviceIntLength,
         src_task_id: RenderTaskId,
         render_tasks: &mut RenderTaskTree,
         target_kind: RenderTargetKind,
+        regions: &[LayerRect],
+        clear_mode: ClearMode,
     ) -> RenderTask {
         let blur_target_size = render_tasks.get(src_task_id).get_dynamic_size();
 
         let blur_task_v = RenderTask {
             cache_key: None,
             children: vec![src_task_id],
             location: RenderTaskLocation::Dynamic(None, blur_target_size),
             kind: RenderTaskKind::VerticalBlur(BlurTask {
                 blur_radius,
                 target_kind,
+                regions: regions.to_vec(),
             }),
+            clear_mode,
         };
 
         let blur_task_v_id = render_tasks.add(blur_task_v);
 
         let blur_task_h = RenderTask {
             cache_key: None,
             children: vec![blur_task_v_id],
             location: RenderTaskLocation::Dynamic(None, blur_target_size),
             kind: RenderTaskKind::HorizontalBlur(BlurTask {
                 blur_radius,
                 target_kind,
+                regions: regions.to_vec(),
             }),
+            clear_mode,
         };
 
         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,
--- a/gfx/webrender/src/renderer.rs
+++ b/gfx/webrender/src/renderer.rs
@@ -50,17 +50,17 @@ use serde_json;
 use std;
 use std::cmp;
 use std::collections::VecDeque;
 use std::collections::hash_map::Entry;
 use std::f32;
 use std::mem;
 use std::path::PathBuf;
 use std::rc::Rc;
-use std::sync::{Arc, Mutex};
+use std::sync::Arc;
 use std::sync::mpsc::{channel, Receiver, Sender};
 use std::thread;
 use texture_cache::TextureCache;
 use thread_profiler::{register_thread_with_profiler, write_profile};
 use tiling::{AlphaRenderTarget, ColorRenderTarget, RenderTargetKind};
 use tiling::{BatchKey, BatchKind, Frame, RenderTarget, TransformBatchKind};
 use time::precise_time_ns;
 use util::TransformedRectKind;
@@ -320,16 +320,21 @@ const DESC_BLUR: VertexDescriptor = Vert
             count: 1,
             kind: VertexAttributeKind::I32,
         },
         VertexAttribute {
             name: "aBlurDirection",
             count: 1,
             kind: VertexAttributeKind::I32,
         },
+        VertexAttribute {
+            name: "aBlurRegion",
+            count: 4,
+            kind: VertexAttributeKind::F32
+        },
     ],
 };
 
 const DESC_CLIP: VertexDescriptor = VertexDescriptor {
     vertex_attributes: &[
         VertexAttribute {
             name: "aPosition",
             count: 2,
@@ -920,30 +925,24 @@ impl LazilyCompiledShader {
 }
 
 struct PrimitiveShader {
     simple: LazilyCompiledShader,
     transform: LazilyCompiledShader,
 }
 
 struct FileWatcher {
-    notifier: Arc<Mutex<Option<Box<RenderNotifier>>>>,
+    notifier: Box<RenderNotifier>,
     result_tx: Sender<ResultMsg>,
 }
 
 impl FileWatcherHandler for FileWatcher {
     fn file_changed(&self, path: PathBuf) {
         self.result_tx.send(ResultMsg::RefreshShader(path)).ok();
-        let mut notifier = self.notifier.lock();
-        notifier
-            .as_mut()
-            .unwrap()
-            .as_mut()
-            .unwrap()
-            .new_frame_ready();
+        self.notifier.new_frame_ready();
     }
 }
 
 impl PrimitiveShader {
     fn new(
         name: &'static str,
         device: &mut Device,
         features: &[&'static str],
@@ -1127,18 +1126,16 @@ pub struct Renderer {
     ps_cache_image_a8: PrimitiveShader,
     ps_line: PrimitiveShader,
 
     ps_blend: LazilyCompiledShader,
     ps_hw_composite: LazilyCompiledShader,
     ps_split_composite: LazilyCompiledShader,
     ps_composite: LazilyCompiledShader,
 
-    notifier: Arc<Mutex<Option<Box<RenderNotifier>>>>,
-
     max_texture_size: u32,
 
     max_recorded_profiles: usize,
     clear_framebuffer: bool,
     clear_color: ColorF,
     enable_clear_scissor: bool,
     debug: DebugRenderer,
     debug_flags: DebugFlags,
@@ -1224,29 +1221,29 @@ impl Renderer {
     ///    resource_override_path: None,
     ///    enable_aa: false,
     /// };
     /// let (renderer, sender) = Renderer::new(opts);
     /// ```
     /// [rendereroptions]: struct.RendererOptions.html
     pub fn new(
         gl: Rc<gl::Gl>,
+        notifier: Box<RenderNotifier>,
         mut options: RendererOptions,
     ) -> Result<(Renderer, RenderApiSender), RendererError> {
         let (api_tx, api_rx) = try!{ channel::msg_channel() };
         let (payload_tx, payload_rx) = try!{ channel::payload_channel() };
         let (result_tx, result_rx) = channel();
         let gl_type = gl.get_type();
 
-        let notifier = Arc::new(Mutex::new(None));
         let debug_server = DebugServer::new(api_tx.clone());
 
         let file_watch_handler = FileWatcher {
             result_tx: result_tx.clone(),
-            notifier: Arc::clone(&notifier),
+            notifier: notifier.clone(),
         };
 
         let mut device = Device::new(
             gl,
             options.resource_override_path.clone(),
             Box::new(file_watch_handler),
         );
 
@@ -1647,17 +1644,17 @@ impl Renderer {
 
         let texture_resolver = SourceTextureResolver::new(&mut device);
 
         let layer_texture = VertexDataTexture::new(&mut device);
         let render_task_texture = VertexDataTexture::new(&mut device);
 
         device.end_frame();
 
-        let backend_notifier = Arc::clone(&notifier);
+        let backend_notifier = notifier.clone();
 
         let default_font_render_mode = match (options.enable_aa, options.enable_subpixel_aa) {
             (true, true) => FontRenderMode::Subpixel,
             (true, false) => FontRenderMode::Alpha,
             (false, _) => FontRenderMode::Mono,
         };
 
         let config = FrameBuilderConfig {
@@ -1730,17 +1727,16 @@ impl Renderer {
             ps_radial_gradient,
             ps_cache_image_rgba8,
             ps_cache_image_a8,
             ps_blend,
             ps_hw_composite,
             ps_split_composite,
             ps_composite,
             ps_line,
-            notifier,
             debug: debug_renderer,
             debug_flags,
             enable_batcher: options.enable_batcher,
             backend_profile_counters: BackendProfileCounters::new(),
             profile_counters: RendererProfileCounters::new(),
             profiler: Profiler::new(),
             max_texture_size: max_texture_size,
             max_recorded_profiles: options.max_recorded_profiles,
@@ -1789,25 +1785,16 @@ impl Renderer {
         buffer_kind: ImageBufferKind,
         format: YuvFormat,
         color_space: YuvColorSpace,
     ) -> usize {
         ((buffer_kind as usize) * YUV_FORMATS.len() + (format as usize)) * YUV_COLOR_SPACES.len() +
             (color_space as usize)
     }
 
-    /// Sets the new RenderNotifier.
-    ///
-    /// The RenderNotifier will be called when processing e.g. of a (scrolling) frame is done,
-    /// and therefore the screen should be updated.
-    pub fn set_render_notifier(&self, notifier: Box<RenderNotifier>) {
-        let mut notifier_arc = self.notifier.lock().unwrap();
-        *notifier_arc = Some(notifier);
-    }
-
     /// Returns the Epoch of the current frame in a pipeline.
     pub fn current_epoch(&self, pipeline_id: PipelineId) -> Option<Epoch> {
         self.pipeline_epoch_map.get(&pipeline_id).cloned()
     }
 
     /// Returns a HashMap containing the pipeline ids that have been received by the renderer and
     /// their respective epochs since the last time the method was called.
     pub fn flush_rendered_epochs(&mut self) -> FastHashMap<PipelineId, Epoch> {
@@ -2885,16 +2872,17 @@ impl Renderer {
     }
 
     fn draw_alpha_target(
         &mut self,
         render_target: (&Texture, i32),
         target: &AlphaRenderTarget,
         target_size: DeviceUintSize,
         projection: &Transform3D<f32>,
+        render_tasks: &RenderTaskTree,
     ) {
         self.gpu_profile.add_sampler(GPU_SAMPLER_TAG_ALPHA);
 
         {
             let _gm = self.gpu_profile.add_marker(GPU_TAG_SETUP_TARGET);
             self.device
                 .bind_draw_target(Some(render_target), Some(target_size));
             self.device.disable_depth();
@@ -2903,16 +2891,24 @@ impl Renderer {
             // TODO(gw): Applying a scissor rect and minimal clear here
             // is a very large performance win on the Intel and nVidia
             // GPUs that I have tested with. It's possible it may be a
             // performance penalty on other GPU types - we should test this
             // and consider different code paths.
             let clear_color = [1.0, 1.0, 1.0, 0.0];
             self.device
                 .clear_target_rect(Some(clear_color), None, target.used_rect());
+
+            let zero_color = [0.0, 0.0, 0.0, 0.0];
+            for task_id in &target.zero_clears {
+                let task = render_tasks.get(*task_id);
+                let (rect, _) = task.get_target_rect();
+                self.device
+                    .clear_target_rect(Some(zero_color), None, rect);
+            }
         }
 
         // Draw any blurs for this target.
         // Blurs are rendered as a standard 2-pass
         // separable implementation.
         // TODO(gw): In the future, consider having
         //           fast path blur shaders for common
         //           blur radii with fixed weights.
@@ -3233,16 +3229,17 @@ impl Renderer {
                         ORTHO_FAR_PLANE,
                     );
 
                     self.draw_alpha_target(
                         (pass.alpha_texture.as_ref().unwrap(), target_index as i32),
                         target,
                         pass.max_alpha_target_size,
                         &projection,
+                        &frame.render_tasks,
                     );
                 }
 
                 for (target_index, target) in pass.color_targets.targets.iter().enumerate() {
                     let size;
                     let clear_color;
                     let projection;
 
--- a/gfx/webrender/src/resource_cache.rs
+++ b/gfx/webrender/src/resource_cache.rs
@@ -425,17 +425,17 @@ impl ResourceCache {
             if tiling.is_none() && self.should_tile(&descriptor, &data) {
                 tiling = Some(DEFAULT_TILE_SIZE);
             }
 
             if let ImageData::Blob(ref mut blob) = data {
                 self.blob_image_renderer
                     .as_mut()
                     .unwrap()
-                    .update(image_key, mem::replace(blob, BlobImageData::new()));
+                    .update(image_key, mem::replace(blob, BlobImageData::new()), dirty_rect);
             }
 
             ImageResource {
                 descriptor,
                 data,
                 epoch: next_epoch,
                 tiling,
                 dirty_rect: match (dirty_rect, image.dirty_rect) {
--- a/gfx/webrender/src/tiling.rs
+++ b/gfx/webrender/src/tiling.rs
@@ -16,17 +16,17 @@ use gpu_types::{BlurDirection, BlurInsta
 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, TextRunMode};
 use profiler::FrameProfileCounters;
 use render_task::{AlphaRenderItem, ClipWorkItem, MaskGeometryKind, MaskSegment};
 use render_task::{RenderTaskAddress, RenderTaskId, RenderTaskKey, RenderTaskKind};
-use render_task::{RenderTaskLocation, RenderTaskTree};
+use render_task::{BlurTask, ClearMode, 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::{MatrixHelpers, TransformedRect, TransformedRectKind};
 use euclid::rect;
 
@@ -422,16 +422,17 @@ impl AlphaRenderItem {
                         // Work around borrow ck on borrowing batch_list twice.
                         {
                             let batch =
                                 batch_list.get_suitable_batch(corner_key, item_bounding_rect);
                             for (i, instance_kind) in border_cpu.corner_instances.iter().enumerate()
                             {
                                 let sub_index = i as i32;
                                 match *instance_kind {
+                                    BorderCornerInstance::None => {}
                                     BorderCornerInstance::Single => {
                                         batch.push(base_instance.build(
                                             sub_index,
                                             BorderCornerSide::Both as i32,
                                             0,
                                         ));
                                     }
                                     BorderCornerInstance::Double => {
@@ -575,17 +576,17 @@ impl AlphaRenderItem {
                     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(picture.kind),
+                            TransformBatchKind::CacheImage(picture.target_kind()),
                         );
                         let key = BatchKey::new(kind, blend_mode, textures);
                         let batch = batch_list.get_suitable_batch(key, item_bounding_rect);
                         batch.push(base_instance.build(0, cache_task_address.0 as i32, 0));
                     }
                     PrimitiveKind::AlignedGradient => {
                         let gradient_cpu =
                             &ctx.prim_store.cpu_gradients[prim_metadata.cpu_prim_index.0];
@@ -1132,33 +1133,33 @@ impl RenderTarget for ColorRenderTarget 
                 // store the information necessary to do the copy.
                 if let Some(pipeline_id) = info.frame_output_pipeline_id {
                     self.outputs.push(FrameOutput {
                         pipeline_id,
                         task_id,
                     });
                 }
             }
-            RenderTaskKind::VerticalBlur(..) => {
-                // Find the child render task that we are applying
-                // a vertical blur on.
-                self.vertical_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::Vertical,
-                });
+            RenderTaskKind::VerticalBlur(ref info) => {
+                info.add_instances(
+                    &mut self.vertical_blurs,
+                    task_id,
+                    task.children[0],
+                    BlurDirection::Vertical,
+                    render_tasks,
+                );
             }
-            RenderTaskKind::HorizontalBlur(..) => {
-                // 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::HorizontalBlur(ref info) => {
+                info.add_instances(
+                    &mut self.horizontal_blurs,
+                    task_id,
+                    task.children[0],
+                    BlurDirection::Horizontal,
+                    render_tasks,
+                );
             }
             RenderTaskKind::Picture(ref task_info) => {
                 let prim_metadata = ctx.prim_store.get_metadata(task_info.prim_index);
                 let prim_address = prim_metadata.gpu_location.as_int(gpu_cache);
 
                 match prim_metadata.prim_kind {
                     PrimitiveKind::Picture => {
                         let prim = &ctx.prim_store.cpu_pictures[prim_metadata.cpu_prim_index.0];
@@ -1239,30 +1240,32 @@ impl RenderTarget for ColorRenderTarget 
 }
 
 pub struct AlphaRenderTarget {
     pub clip_batcher: ClipBatcher,
     pub rect_cache_prims: Vec<PrimitiveInstance>,
     // List of blur operations to apply for this render target.
     pub vertical_blurs: Vec<BlurInstance>,
     pub horizontal_blurs: Vec<BlurInstance>,
+    pub zero_clears: Vec<RenderTaskId>,
     allocator: TextureAllocator,
 }
 
 impl RenderTarget for AlphaRenderTarget {
     fn allocate(&mut self, size: DeviceUintSize) -> Option<DeviceUintPoint> {
         self.allocator.allocate(&size)
     }
 
     fn new(size: Option<DeviceUintSize>) -> AlphaRenderTarget {
         AlphaRenderTarget {
             clip_batcher: ClipBatcher::new(),
             rect_cache_prims: Vec::new(),
             vertical_blurs: Vec::new(),
             horizontal_blurs: Vec::new(),
+            zero_clears: Vec::new(),
             allocator: TextureAllocator::new(size.expect("bug: alpha targets need size")),
         }
     }
 
     fn used_rect(&self) -> DeviceIntRect {
         self.allocator.used_rect
     }
 
@@ -1270,41 +1273,52 @@ impl RenderTarget for AlphaRenderTarget 
         &mut self,
         task_id: RenderTaskId,
         ctx: &RenderTargetContext,
         gpu_cache: &GpuCache,
         render_tasks: &RenderTaskTree,
         clip_store: &ClipStore,
     ) {
         let task = render_tasks.get(task_id);
+
+        match task.clear_mode {
+            ClearMode::Zero => {
+                self.zero_clears.push(task_id);
+            }
+            ClearMode::One => {}
+            ClearMode::Transparent => {
+                panic!("bug: invalid clear mode for alpha task");
+            }
+        }
+
         match task.kind {
             RenderTaskKind::Alias(..) => {
                 panic!("BUG: add_task() called on invalidated task");
             }
             RenderTaskKind::Alpha(..) |
             RenderTaskKind::Readback(..) => {
                 panic!("Should not be added to alpha target!");
             }
-            RenderTaskKind::VerticalBlur(..) => {
-                // Find the child render task that we are applying
-                // a vertical blur on.
-                self.vertical_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::Vertical,
-                });
+            RenderTaskKind::VerticalBlur(ref info) => {
+                info.add_instances(
+                    &mut self.vertical_blurs,
+                    task_id,
+                    task.children[0],
+                    BlurDirection::Vertical,
+                    render_tasks,
+                );
             }
-            RenderTaskKind::HorizontalBlur(..) => {
-                // 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::HorizontalBlur(ref info) => {
+                info.add_instances(
+                    &mut self.horizontal_blurs,
+                    task_id,
+                    task.children[0],
+                    BlurDirection::Horizontal,
+                    render_tasks,
+                );
             }
             RenderTaskKind::Picture(ref task_info) => {
                 let prim_metadata = ctx.prim_store.get_metadata(task_info.prim_index);
 
                 match prim_metadata.prim_kind {
                     PrimitiveKind::Picture => {
                         let prim = &ctx.prim_store.cpu_pictures[prim_metadata.cpu_prim_index.0];
 
@@ -1868,8 +1882,37 @@ fn resolve_image(
                         (SourceTexture::Invalid, GpuCacheHandle::new())
                     }
                 }
             }
         }
         None => (SourceTexture::Invalid, GpuCacheHandle::new()),
     }
 }
+
+impl BlurTask {
+    fn add_instances(
+        &self,
+        instances: &mut Vec<BlurInstance>,
+        task_id: RenderTaskId,
+        source_task_id: RenderTaskId,
+        blur_direction: BlurDirection,
+        render_tasks: &RenderTaskTree,
+    ) {
+        let instance = BlurInstance {
+            task_address: render_tasks.get_task_address(task_id),
+            src_task_address: render_tasks.get_task_address(source_task_id),
+            blur_direction,
+            region: LayerRect::zero(),
+        };
+
+        if self.regions.is_empty() {
+            instances.push(instance);
+        } else {
+            for region in &self.regions {
+                instances.push(BlurInstance {
+                    region: *region,
+                    ..instance
+                });
+            }
+        }
+    }
+}
--- a/gfx/webrender_api/Cargo.toml
+++ b/gfx/webrender_api/Cargo.toml
@@ -1,26 +1,25 @@
 [package]
 name = "webrender_api"
-version = "0.52.1"
+version = "0.53.0"
 authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
 license = "MPL-2.0"
 repository = "https://github.com/servo/webrender"
 
 [features]
 nightly = ["euclid/unstable", "serde/unstable"]
 ipc = ["ipc-channel"]
 
 [dependencies]
 app_units = "0.5.6"
 bitflags = "1.0"
 bincode = "0.9"
 byteorder = "1.0"
 euclid = "0.15"
-heapsize = ">= 0.3.6, < 0.5"
 ipc-channel = {version = "0.9", optional = true}
 serde = { version = "1.0", features = ["rc", "derive"] }
 time = "0.1"
 
 [target.'cfg(target_os = "macos")'.dependencies]
 core-foundation = "0.4"
 core-graphics = "0.9"
 
--- a/gfx/webrender_api/src/api.rs
+++ b/gfx/webrender_api/src/api.rs
@@ -868,15 +868,16 @@ pub struct PropertyValue<T> {
 /// animated properties.
 #[derive(Clone, Deserialize, Serialize, Debug)]
 pub struct DynamicProperties {
     pub transforms: Vec<PropertyValue<LayoutTransform>>,
     pub floats: Vec<PropertyValue<f32>>,
 }
 
 pub trait RenderNotifier: Send {
-    fn new_frame_ready(&mut self);
-    fn new_scroll_frame_ready(&mut self, composite_needed: bool);
-    fn external_event(&mut self, _evt: ExternalEvent) {
+    fn clone(&self) -> Box<RenderNotifier>;
+    fn new_frame_ready(&self);
+    fn new_scroll_frame_ready(&self, composite_needed: bool);
+    fn external_event(&self, _evt: ExternalEvent) {
         unimplemented!()
     }
-    fn shut_down(&mut self) {}
+    fn shut_down(&self) {}
 }
--- a/gfx/webrender_api/src/color.rs
+++ b/gfx/webrender_api/src/color.rs
@@ -11,17 +11,16 @@ use std::hash::{Hash, Hasher};
 #[repr(C)]
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct ColorF {
     pub r: f32,
     pub g: f32,
     pub b: f32,
     pub a: f32,
 }
-known_heap_size!(0, ColorF);
 
 impl ColorF {
     /// Constructs a new `ColorF` from its components.
     pub fn new(r: f32, g: f32, b: f32, a: f32) -> ColorF {
         ColorF { r, g, b, a }
     }
 
     /// Multiply the RGB channels (but not alpha) with a given factor.
--- a/gfx/webrender_api/src/display_item.rs
+++ b/gfx/webrender_api/src/display_item.rs
@@ -1,16 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use {ColorF, FontInstanceKey, ImageKey, LayerPixel, LayoutPixel, LayoutPoint, LayoutRect,
      LayoutSize, LayoutTransform};
 use {GlyphOptions, LayoutVector2D, PipelineId, PropertyBinding};
 use euclid::{SideOffsets2D, TypedRect, TypedSideOffsets2D};
+use std::ops::Not;
 
 // NOTE: some of these structs have an "IMPLICIT" comment.
 // This indicates that the BuiltDisplayList will have serialized
 // a list of values nearby that this item consumes. The traversal
 // iterator should handle finding these.
 
 #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
 pub struct ClipAndScrollInfo {
@@ -292,17 +293,17 @@ pub enum BoxShadowClipMode {
 
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct BoxShadowDisplayItem {
     pub box_bounds: LayoutRect,
     pub offset: LayoutVector2D,
     pub color: ColorF,
     pub blur_radius: f32,
     pub spread_radius: f32,
-    pub border_radius: f32,
+    pub border_radius: BorderRadius,
     pub clip_mode: BoxShadowClipMode,
 }
 
 #[repr(C)]
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct Shadow {
     pub offset: LayoutVector2D,
     pub color: ColorF,
@@ -331,17 +332,16 @@ pub struct GradientDisplayItem {
 }
 
 #[repr(C)]
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct GradientStop {
     pub offset: f32,
     pub color: ColorF,
 }
-known_heap_size!(0, GradientStop);
 
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct RadialGradient {
     pub start_center: LayoutPoint,
     pub start_radius: f32,
     pub end_center: LayoutPoint,
     pub end_radius: f32,
     pub ratio_xy: f32,
@@ -371,18 +371,16 @@ pub struct StackingContext {
 
 #[repr(u32)]
 #[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
 pub enum ScrollPolicy {
     Scrollable = 0,
     Fixed = 1,
 }
 
-known_heap_size!(0, ScrollPolicy);
-
 #[repr(u32)]
 #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
 pub enum TransformStyle {
     Flat = 0,
     Preserve3D = 1,
 }
 
 #[repr(u32)]
@@ -542,29 +540,51 @@ impl LocalClip {
     pub fn create_with_offset(&self, offset: &LayoutVector2D) -> LocalClip {
         match *self {
             LocalClip::Rect(rect) => LocalClip::from(rect.translate(offset)),
             LocalClip::RoundedRect(rect, complex) => LocalClip::RoundedRect(
                 rect.translate(offset),
                 ComplexClipRegion {
                     rect: complex.rect.translate(offset),
                     radii: complex.radii,
+                    mode: complex.mode,
                 },
             ),
         }
     }
 }
 
 #[repr(C)]
+#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub enum ClipMode {
+    Clip,    // Pixels inside the region are visible.
+    ClipOut, // Pixels outside the region are visible.
+}
+
+impl Not for ClipMode {
+    type Output = ClipMode;
+
+    fn not(self) -> ClipMode {
+        match self {
+            ClipMode::Clip => ClipMode::ClipOut,
+            ClipMode::ClipOut => ClipMode::Clip,
+        }
+    }
+}
+
+#[repr(C)]
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct ComplexClipRegion {
     /// The boundaries of the rectangle.
     pub rect: LayoutRect,
     /// Border radii of this rectangle.
     pub radii: BorderRadius,
+    /// Whether we are clipping inside or outside
+    /// the region.
+    pub mode: ClipMode,
 }
 
 impl BorderRadius {
     pub fn zero() -> BorderRadius {
         BorderRadius {
             top_left: LayoutSize::new(0.0, 0.0),
             top_right: LayoutSize::new(0.0, 0.0),
             bottom_left: LayoutSize::new(0.0, 0.0),
@@ -614,18 +634,22 @@ impl BorderRadius {
         } else {
             false
         }
     }
 }
 
 impl ComplexClipRegion {
     /// Create a new complex clip region.
-    pub fn new(rect: LayoutRect, radii: BorderRadius) -> ComplexClipRegion {
-        ComplexClipRegion { rect, radii }
+    pub fn new(
+        rect: LayoutRect,
+        radii: BorderRadius,
+        mode: ClipMode,
+    ) -> ComplexClipRegion {
+        ComplexClipRegion { rect, radii, mode }
     }
 }
 
 #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
 pub enum ClipId {
     Clip(u64, PipelineId),
     ClipExternalId(u64, PipelineId),
     DynamicallyAddedNode(u64, PipelineId),
@@ -667,26 +691,8 @@ impl ClipId {
 
     pub fn is_root_scroll_node(&self) -> bool {
         match *self {
             ClipId::Clip(0, _) => true,
             _ => false,
         }
     }
 }
-
-macro_rules! define_empty_heap_size_of {
-    ($name:ident) => {
-        impl ::heapsize::HeapSizeOf for $name {
-            fn heap_size_of_children(&self) -> usize { 0 }
-        }
-    }
-}
-
-define_empty_heap_size_of!(ClipAndScrollInfo);
-define_empty_heap_size_of!(ClipId);
-define_empty_heap_size_of!(ImageKey);
-define_empty_heap_size_of!(LocalClip);
-define_empty_heap_size_of!(MixBlendMode);
-define_empty_heap_size_of!(RepeatMode);
-define_empty_heap_size_of!(ScrollSensitivity);
-define_empty_heap_size_of!(StickySideConstraint);
-define_empty_heap_size_of!(TransformStyle);
--- a/gfx/webrender_api/src/display_list.rs
+++ b/gfx/webrender_api/src/display_list.rs
@@ -7,17 +7,17 @@ use {ClipAndScrollInfo, ClipDisplayItem,
 use {ExtendMode, FilterOp, FontInstanceKey, GlyphInstance};
 use {GlyphOptions, Gradient, GradientDisplayItem, GradientStop, IframeDisplayItem};
 use {ImageDisplayItem, ImageKey, ImageMask, ImageRendering, LayerPrimitiveInfo, LayoutPoint};
 use {LayoutPrimitiveInfo, LayoutRect, LayoutSize, LayoutTransform, LayoutVector2D};
 use {LineDisplayItem, LineOrientation, LineStyle, LocalClip, MixBlendMode, PipelineId};
 use {PropertyBinding, PushStackingContextDisplayItem, RadialGradient, RadialGradientDisplayItem};
 use {RectangleDisplayItem, ScrollFrameDisplayItem, ScrollPolicy, ScrollSensitivity};
 use {SpecificDisplayItem, StackingContext, StickyFrameDisplayItem, StickyFrameInfo};
-use {TextDisplayItem, Shadow, TransformStyle, YuvColorSpace, YuvData};
+use {BorderRadius, TextDisplayItem, Shadow, TransformStyle, YuvColorSpace, YuvData};
 use YuvImageDisplayItem;
 use bincode;
 use serde::{Deserialize, Serialize, Serializer};
 use serde::ser::{SerializeMap, SerializeSeq};
 use std::io::{Read, Write};
 use std::{io, ptr};
 use std::marker::PhantomData;
 use std::slice;
@@ -148,33 +148,35 @@ impl BuiltDisplayList {
 
 /// Returns the byte-range the slice occupied, and the number of elements
 /// in the slice.
 fn skip_slice<T: for<'de> Deserialize<'de>>(
     list: &BuiltDisplayList,
     data: &mut &[u8],
 ) -> (ItemRange<T>, usize) {
     let base = list.data.as_ptr() as usize;
-    let start = data.as_ptr() as usize;
 
-    // Read through the values (this is a bit of a hack to reuse logic)
-    let mut iter = AuxIter::<T>::new(*data);
-    let count = iter.len();
-    for _ in &mut iter {}
-    let end = iter.data.as_ptr() as usize;
+    let byte_size: usize = bincode::deserialize_from(data, bincode::Infinite)
+                                    .expect("MEH: malicious input?");
+    let start = data.as_ptr() as usize;
+    let item_count: usize = bincode::deserialize_from(data, bincode::Infinite)
+                                    .expect("MEH: malicious input?");
+
+    // Remember how many bytes item_count occupied
+    let item_count_size = data.as_ptr() as usize - start;
 
     let range = ItemRange {
-        start: start - base,
-        length: end - start,
+        start: start - base,                      // byte offset to item_count
+        length: byte_size + item_count_size,      // number of bytes for item_count + payload
         _boo: PhantomData,
     };
 
     // Adjust data pointer to skip read values
-    *data = &data[range.length ..];
-    (range, count)
+    *data = &data[byte_size ..];
+    (range, item_count)
 }
 
 
 impl<'a> BuiltDisplayListIter<'a> {
     pub fn new(list: &'a BuiltDisplayList) -> Self {
         Self::new_with_list_and_data(list, list.item_slice())
     }
 
@@ -732,22 +734,39 @@ impl DisplayListBuilder {
         I: IntoIterator,
         I::IntoIter: ExactSizeIterator,
         I::Item: Serialize,
     {
         let iter = iter.into_iter();
         let len = iter.len();
         let mut count = 0;
 
+        // Format:
+        // payload_byte_size: usize, item_count: usize, [I; item_count]
+
+        // We write a dummy value so there's room for later
+        let byte_size_offset = self.data.len();
+        serialize_fast(&mut self.data, &0usize);
         serialize_fast(&mut self.data, &len);
+        let payload_offset = self.data.len();
+
         for elem in iter {
             count += 1;
             serialize_fast(&mut self.data, &elem);
         }
 
+        // Now write the actual byte_size
+        let final_offset = self.data.len();
+        let byte_size = final_offset - payload_offset;
+
+        // Note we don't use serialize_fast because we don't want to change the Vec's len
+        bincode::serialize_into(&mut &mut self.data[byte_size_offset..],
+                                &byte_size,
+                                bincode::Infinite).unwrap();
+
         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);
     }
 
@@ -1019,17 +1038,17 @@ impl DisplayListBuilder {
     pub fn push_box_shadow(
         &mut self,
         info: &LayoutPrimitiveInfo,
         box_bounds: LayoutRect,
         offset: LayoutVector2D,
         color: ColorF,
         blur_radius: f32,
         spread_radius: f32,
-        border_radius: f32,
+        border_radius: BorderRadius,
         clip_mode: BoxShadowClipMode,
     ) {
         let item = SpecificDisplayItem::BoxShadow(BoxShadowDisplayItem {
             box_bounds,
             offset,
             color,
             blur_radius,
             spread_radius,
--- a/gfx/webrender_api/src/image.rs
+++ b/gfx/webrender_api/src/image.rs
@@ -146,17 +146,17 @@ impl ImageData {
 pub trait BlobImageResources {
     fn get_font_data(&self, key: FontKey) -> &FontTemplate;
     fn get_image(&self, key: ImageKey) -> Option<(&ImageData, &ImageDescriptor)>;
 }
 
 pub trait BlobImageRenderer: Send {
     fn add(&mut self, key: ImageKey, data: BlobImageData, tiling: Option<TileSize>);
 
-    fn update(&mut self, key: ImageKey, data: BlobImageData);
+    fn update(&mut self, key: ImageKey, data: BlobImageData, dirty_rect: Option<DeviceUintRect>);
 
     fn delete(&mut self, key: ImageKey);
 
     fn request(
         &mut self,
         services: &BlobImageResources,
         key: BlobImageRequest,
         descriptor: &BlobImageDescriptor,
--- a/gfx/webrender_api/src/lib.rs
+++ b/gfx/webrender_api/src/lib.rs
@@ -8,18 +8,16 @@
 extern crate app_units;
 extern crate bincode;
 #[macro_use]
 extern crate bitflags;
 extern crate byteorder;
 #[cfg(feature = "nightly")]
 extern crate core;
 extern crate euclid;
-#[macro_use]
-extern crate heapsize;
 #[cfg(feature = "ipc")]
 extern crate ipc_channel;
 #[macro_use]
 extern crate serde;
 extern crate time;
 
 #[cfg(target_os = "macos")]
 extern crate core_foundation;
--- a/gfx/webrender_bindings/Cargo.toml
+++ b/gfx/webrender_bindings/Cargo.toml
@@ -1,19 +1,19 @@
 [package]
 name = "webrender_bindings"
 version = "0.1.0"
 authors = ["The Mozilla Project Developers"]
 license = "MPL-2.0"
 
 [dependencies]
-webrender_api = {path = "../webrender_api", version = "0.52.1"}
+webrender_api = {path = "../webrender_api", version = "0.53.0"}
 bincode = "0.8"
 rayon = "0.8"
 thread_profiler = "0.1.1"
 euclid = "0.15"
 app_units = "0.5.6"
 gleam = "0.4"
 
 [dependencies.webrender]
 path = "../webrender"
-version = "0.52.1"
+version = "0.53.0"
 default-features = false