Bug 1379295 - Support vibrancy and rounded window corners with WebRender. draft
authorMarkus Stange <mstange@themasta.com>
Mon, 23 Oct 2017 14:15:53 -0400
changeset 684908 6089b7fd5cb490447ec1e62be4aedd89c97e714d
parent 684907 8ffc8a5754700c5486bd3199999ada161efbbd48
child 736994 6d04e2ff64dbdf17eb8748dc0fac620e79ed8bdb
push id85757
push userbmo:mstange@themasta.com
push dateMon, 23 Oct 2017 18:16:46 +0000
bugs1379295
milestone58.0a1
Bug 1379295 - Support vibrancy and rounded window corners with WebRender. MozReview-Commit-ID: EgcfJ9ZwpsI
gfx/layers/composite/TextureHost.cpp
gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp
gfx/layers/wr/AsyncImagePipelineManager.cpp
gfx/layers/wr/WebRenderCommandBuilder.cpp
gfx/webrender/res/ps_rectangle.glsl
gfx/webrender/src/device.rs
gfx/webrender/src/frame.rs
gfx/webrender/src/frame_builder.rs
gfx/webrender/src/internal_types.rs
gfx/webrender/src/prim_store.rs
gfx/webrender/src/renderer.rs
gfx/webrender/src/tiling.rs
gfx/webrender_api/src/api.rs
gfx/webrender_api/src/display_item.rs
gfx/webrender_api/src/display_list.rs
gfx/webrender_bindings/WebRenderAPI.cpp
gfx/webrender_bindings/WebRenderAPI.h
gfx/webrender_bindings/src/bindings.rs
gfx/webrender_bindings/webrender_ffi_generated.h
layout/generic/TextDrawTarget.h
layout/generic/nsBulletFrame.cpp
layout/generic/nsCanvasFrame.cpp
layout/painting/nsDisplayList.cpp
layout/painting/nsDisplayList.h
layout/painting/nsImageRenderer.cpp
layout/xul/nsImageBoxFrame.cpp
widget/cocoa/nsChildView.h
widget/cocoa/nsChildView.mm
--- a/gfx/layers/composite/TextureHost.cpp
+++ b/gfx/layers/composite/TextureHost.cpp
@@ -608,17 +608,17 @@ void
 BufferTextureHost::PushDisplayItems(wr::DisplayListBuilder& aBuilder,
                                     const wr::LayoutRect& aBounds,
                                     const wr::LayoutRect& aClip,
                                     wr::ImageRendering aFilter,
                                     const Range<wr::ImageKey>& aImageKeys)
 {
   if (GetFormat() != gfx::SurfaceFormat::YUV) {
     MOZ_ASSERT(aImageKeys.length() == 1);
-    aBuilder.PushImage(aBounds, aClip, true, aFilter, aImageKeys[0]);
+    aBuilder.PushImage(aBounds, aClip, true, wr::CompositionOp::Over, aFilter, aImageKeys[0]);
   } else {
     MOZ_ASSERT(aImageKeys.length() == 3);
     aBuilder.PushYCbCrPlanarImage(aBounds,
                                   aClip,
                                   true,
                                   aImageKeys[0],
                                   aImageKeys[1],
                                   aImageKeys[2],
--- a/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp
+++ b/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp
@@ -206,17 +206,17 @@ MacIOSurfaceTextureHostOGL::PushDisplayI
 {
   switch (GetFormat()) {
     case gfx::SurfaceFormat::R8G8B8X8:
     case gfx::SurfaceFormat::R8G8B8A8:
     case gfx::SurfaceFormat::B8G8R8A8:
     case gfx::SurfaceFormat::B8G8R8X8: {
       MOZ_ASSERT(aImageKeys.length() == 1);
       MOZ_ASSERT(mSurface->GetPlaneCount() == 0);
-      aBuilder.PushImage(aBounds, aClip, true, aFilter, aImageKeys[0]);
+      aBuilder.PushImage(aBounds, aClip, true, wr::CompositionOp::Over, aFilter, aImageKeys[0]);
       break;
     }
     case gfx::SurfaceFormat::YUV422: {
       MOZ_ASSERT(aImageKeys.length() == 1);
       MOZ_ASSERT(mSurface->GetPlaneCount() == 0);
       aBuilder.PushYCbCrInterleavedImage(aBounds,
                                          aClip,
                                          true,
--- a/gfx/layers/wr/AsyncImagePipelineManager.cpp
+++ b/gfx/layers/wr/AsyncImagePipelineManager.cpp
@@ -307,16 +307,17 @@ AsyncImagePipelineManager::ApplyAsyncIma
                                                   pipeline->mFilter,
                                                   range_keys);
       HoldExternalImage(pipelineId, epoch, pipeline->mCurrentTexture->AsWebRenderTextureHost());
     } else {
       MOZ_ASSERT(keys.Length() == 1);
       builder.PushImage(wr::ToLayoutRect(rect),
                         wr::ToLayoutRect(rect),
                         true,
+                        wr::CompositionOp::Over,
                         pipeline->mFilter,
                         keys[0]);
     }
     builder.PopStackingContext();
 
     wr::BuiltDisplayList dl;
     wr::LayoutSize builderContentSize;
     builder.Finalize(builderContentSize, dl);
--- a/gfx/layers/wr/WebRenderCommandBuilder.cpp
+++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp
@@ -327,17 +327,18 @@ WebRenderCommandBuilder::PushImage(nsDis
     return true;
   }
   if (!key) {
     return false;
   }
 
   auto r = aSc.ToRelativeLayoutRect(aRect);
   gfx::SamplingFilter sampleFilter = nsLayoutUtils::GetSamplingFilterForFrame(aItem->Frame());
-  aBuilder.PushImage(r, r, !aItem->BackfaceIsHidden(), wr::ToImageRendering(sampleFilter), key.value());
+  aBuilder.PushImage(r, r, !aItem->BackfaceIsHidden(), wr::CompositionOp::Over,
+                     wr::ToImageRendering(sampleFilter), key.value());
 
   return true;
 }
 
 static void
 PaintItemByDrawTarget(nsDisplayItem* aItem,
                       gfx::DrawTarget* aDT,
                       const LayerRect& aImageRect,
@@ -593,17 +594,17 @@ WebRenderCommandBuilder::PushItemAsImage
   if (!fallbackData) {
     return false;
   }
 
   wr::LayoutRect dest = aSc.ToRelativeLayoutRect(imageRect);
   gfx::SamplingFilter sampleFilter = nsLayoutUtils::GetSamplingFilterForFrame(aItem->Frame());
   aBuilder.PushImage(dest,
                      dest,
-                     !aItem->BackfaceIsHidden(),
+                     !aItem->BackfaceIsHidden(), wr::CompositionOp::Over,
                      wr::ToImageRendering(sampleFilter),
                      fallbackData->GetKey().value());
   return true;
 }
 
 void
 WebRenderCommandBuilder::RemoveUnusedAndResetWebRenderUserData()
 {
--- a/gfx/webrender/res/ps_rectangle.glsl
+++ b/gfx/webrender/res/ps_rectangle.glsl
@@ -44,11 +44,11 @@ void main(void) {
 #ifdef WR_FEATURE_TRANSFORM
     alpha = 0.0;
     init_transform_fs(vLocalPos, alpha);
 #endif
 
 #ifdef WR_FEATURE_CLIP
     alpha = min(alpha, do_clip());
 #endif
-    oFragColor = vColor * vec4(1.0, 1.0, 1.0, alpha);
+    oFragColor = vColor * vec4(alpha * vColor.a, alpha * vColor.a, alpha * vColor.a, alpha);
 }
 #endif
--- a/gfx/webrender/src/device.rs
+++ b/gfx/webrender/src/device.rs
@@ -1,14 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use super::shader_source;
-use api::ImageFormat;
+use api::{CompositionOp, ImageFormat};
 use api::{DeviceIntRect, DeviceUintSize};
 use euclid::Transform3D;
 use gleam::gl;
 use internal_types::RenderTargetMode;
 use std::fs::File;
 use std::io::Read;
 use std::iter::repeat;
 use std::mem;
@@ -1867,18 +1867,53 @@ impl Device {
     pub fn set_blend(&self, enable: bool) {
         if enable {
             self.gl.enable(gl::BLEND);
         } else {
             self.gl.disable(gl::BLEND);
         }
     }
 
-    pub fn set_blend_mode_premultiplied_alpha(&self) {
-        self.gl.blend_func(gl::ONE, gl::ONE_MINUS_SRC_ALPHA);
+    pub fn set_blend_mode_premultiplied_alpha(&self, op: CompositionOp) {
+        match op {
+            CompositionOp::Over => {
+                self.gl.blend_func(gl::ONE, gl::ONE_MINUS_SRC_ALPHA);
+            },
+            CompositionOp::Add => {
+                self.gl.blend_func(gl::ONE, gl::ONE);
+            },
+            CompositionOp::Atop => {
+                self.gl.blend_func(gl::DST_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
+            },
+            CompositionOp::Out => {
+                self.gl.blend_func(gl::ONE_MINUS_DST_ALPHA, gl::ZERO);
+            },
+            CompositionOp::In => {
+                self.gl.blend_func(gl::DST_ALPHA, gl::ZERO);
+            },
+            CompositionOp::Source => {
+                self.gl.blend_func(gl::ONE, gl::ZERO);
+            },
+            CompositionOp::DestIn => {
+                self.gl.blend_func(gl::ZERO, gl::SRC_ALPHA);
+            },
+            CompositionOp::DestOut => {
+                self.gl.blend_func(gl::ZERO, gl::ONE_MINUS_SRC_ALPHA);
+            },
+            CompositionOp::DestOver => {
+                self.gl.blend_func(gl::ONE_MINUS_DST_ALPHA, gl::ONE);
+            },
+            CompositionOp::DestAtop => {
+                self.gl.blend_func(gl::ONE_MINUS_DST_ALPHA, gl::SRC_ALPHA);
+            },
+            CompositionOp::Xor => {
+                self.gl.blend_func(gl::ONE_MINUS_DST_ALPHA,
+                                   gl::ONE_MINUS_SRC_ALPHA);
+            },
+        }
         self.gl.blend_equation(gl::FUNC_ADD);
     }
 
     pub fn set_blend_mode_alpha(&self) {
         self.gl.blend_func_separate(
             gl::SRC_ALPHA,
             gl::ONE_MINUS_SRC_ALPHA,
             gl::ONE,
--- a/gfx/webrender/src/frame.rs
+++ b/gfx/webrender/src/frame.rs
@@ -525,16 +525,17 @@ impl Frame {
                     );
                 }
             }
             SpecificDisplayItem::Line(ref info) => {
                 let prim_info = LayerPrimitiveInfo {
                     rect: LayerRect::zero(),
                     local_clip: *item.local_clip(),
                     is_backface_visible: prim_info.is_backface_visible,
+                    composition_op: prim_info.composition_op,
                     tag: prim_info.tag,
                 };
 
                 context.builder.add_line(
                     clip_and_scroll,
                     &prim_info,
                     info.baseline,
                     info.start,
@@ -1168,16 +1169,17 @@ fn try_to_add_rectangle_splitting_on_cli
     // less masking some cases.
     let mut clipped_rects = Vec::new();
     subtract_rect(&info.rect, &inner_unclipped_rect, &mut clipped_rects);
 
     let prim_info = LayerPrimitiveInfo {
         rect: inner_unclipped_rect,
         local_clip: LocalClip::from(*info.local_clip.clip_rect()),
         is_backface_visible: info.is_backface_visible,
+        composition_op: info.composition_op,
         tag: None,
     };
 
     context.builder.add_solid_rectangle(
         *clip_and_scroll,
         &prim_info,
         color,
         PrimitiveFlags::None,
--- a/gfx/webrender/src/frame_builder.rs
+++ b/gfx/webrender/src/frame_builder.rs
@@ -1,14 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{BorderDetails, BorderDisplayItem, BorderRadius, BoxShadowClipMode, BuiltDisplayList};
-use api::{ClipMode, ComplexClipRegion, ClipAndScrollInfo, ClipId, ColorF, LayoutSize};
+use api::{ClipMode, ComplexClipRegion, ClipAndScrollInfo, ClipId, CompositionOp, 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};
@@ -249,16 +249,17 @@ impl FrameBuilder {
             ));
         }
 
         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,
+            info.composition_op,
             clip_sources,
             info.tag,
             container,
         );
 
         prim_index
     }
 
@@ -615,19 +616,20 @@ impl FrameBuilder {
         &mut self,
         clip_and_scroll: ClipAndScrollInfo,
         info: &LayerPrimitiveInfo,
         color: &ColorF,
         flags: PrimitiveFlags,
     ) {
         let prim = RectanglePrimitive { color: *color };
 
-        // Don't add transparent rectangles to the draw list, but do consider them for hit
+        // When using the default operator (operator over), don't add
+        // transparent rectangles to the draw list, but do consider them for hit
         // testing. This allows specifying invisible hit testing areas.
-        if color.a == 0.0 {
+        if color.a == 0.0 && info.composition_op == CompositionOp::Over {
             self.add_primitive_to_hit_testing_list(info, clip_and_scroll);
             return;
         }
 
         let prim_index = self.add_primitive(
             clip_and_scroll,
             info,
             Vec::new(),
@@ -2084,17 +2086,17 @@ impl FrameBuilder {
                     if stacking_context.isolation == ContextIsolation::Full && composite_count == 0
                     {
                         let mut prev_task = alpha_task_stack.pop().unwrap();
                         let screen_origin = current_task.as_alpha_batch().screen_origin;
                         let current_task_id = render_tasks.add(current_task);
                         let item = AlphaRenderItem::HardwareComposite(
                             stacking_context_index,
                             current_task_id,
-                            HardwareCompositeOp::PremultipliedAlpha,
+                            HardwareCompositeOp::PremultipliedAlpha(CompositionOp::Over),
                             screen_origin,
                             next_z,
                         );
                         next_z += 1;
                         prev_task.as_alpha_batch_mut().items.push(item);
                         prev_task.children.push(current_task_id);
                         current_task = prev_task;
                     }
@@ -2115,17 +2117,17 @@ impl FrameBuilder {
                                     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,
+                                    HardwareCompositeOp::PremultipliedAlpha(CompositionOp::Over),
                                     DeviceIntPoint::new(
                                         screen_origin.x - blur_radius.0,
                                         screen_origin.y - blur_radius.0,
                                     ),
                                     next_z,
                                 );
                                 prev_task.as_alpha_batch_mut().items.push(item);
                                 prev_task.children.push(blur_render_task_id);
@@ -2215,17 +2217,17 @@ impl FrameBuilder {
                         output_pipelines.contains(&stacking_context.pipeline_id)
                     {
                         let mut prev_task = alpha_task_stack.pop().unwrap();
                         let screen_origin = current_task.as_alpha_batch().screen_origin;
                         let current_task_id = render_tasks.add(current_task);
                         let item = AlphaRenderItem::HardwareComposite(
                             stacking_context_index,
                             current_task_id,
-                            HardwareCompositeOp::PremultipliedAlpha,
+                            HardwareCompositeOp::PremultipliedAlpha(CompositionOp::Over),
                             screen_origin,
                             next_z,
                         );
                         next_z += 1;
                         prev_task.as_alpha_batch_mut().items.push(item);
                         prev_task.children.push(current_task_id);
                         current_task = prev_task;
                     }
--- a/gfx/webrender/src/internal_types.rs
+++ b/gfx/webrender/src/internal_types.rs
@@ -1,13 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-use api::{ClipId, DevicePoint, DeviceUintRect, DocumentId, Epoch};
+use api::{CompositionOp, ClipId, DevicePoint, DeviceUintRect, DocumentId, Epoch};
 use api::{ExternalImageData, ExternalImageId};
 use api::{ImageFormat, PipelineId};
 use api::DebugCommand;
 use device::TextureFilter;
 use fxhash::FxHasher;
 use profiler::BackendProfileCounters;
 use renderer::BlendMode;
 use std::{usize, i32};
@@ -193,18 +193,18 @@ pub struct StackingContextIndex(pub usiz
 #[derive(Clone, Copy, Debug)]
 pub struct UvRect {
     pub uv0: DevicePoint,
     pub uv1: DevicePoint,
 }
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 pub enum HardwareCompositeOp {
-    PremultipliedAlpha,
+    PremultipliedAlpha(CompositionOp),
 }
 
 impl HardwareCompositeOp {
     pub fn to_blend_mode(&self) -> BlendMode {
         match *self {
-            HardwareCompositeOp::PremultipliedAlpha => BlendMode::PremultipliedAlpha,
+            HardwareCompositeOp::PremultipliedAlpha(op) => BlendMode::PremultipliedAlpha(op),
         }
     }
 }
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -1,13 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-use api::{BorderRadius, BuiltDisplayList, ColorF, ComplexClipRegion, DeviceIntRect};
+use api::{BorderRadius, BuiltDisplayList, CompositionOp, ColorF, ComplexClipRegion, DeviceIntRect};
 use api::{DevicePoint, ExtendMode, FontInstance, FontRenderMode, GlyphInstance, GlyphKey};
 use api::{GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag, LayerPoint, LayerRect};
 use api::{ClipMode, LayerSize, LayerVector2D, LineOrientation, LineStyle};
 use api::{TileOffset, YuvColorSpace, YuvFormat};
 use border::BorderCornerInstance;
 use clip::{ClipSourcesHandle, ClipStore, Geometry};
 use frame_builder::PrimitiveContext;
 use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest,
@@ -128,16 +128,17 @@ impl GpuCacheAddress {
         self.v as i32 * MAX_VERTEX_TEXTURE_WIDTH as i32 + self.u as i32
     }
 }
 
 // TODO(gw): Pack the fields here better!
 #[derive(Debug)]
 pub struct PrimitiveMetadata {
     pub opacity: PrimitiveOpacity,
+    pub composition_op: CompositionOp,
     pub clip_sources: ClipSourcesHandle,
     pub prim_kind: PrimitiveKind,
     pub cpu_prim_index: SpecificPrimitiveIndex,
     pub gpu_location: GpuCacheHandle,
     pub clip_task_id: Option<RenderTaskId>,
 
     // TODO(gw): In the future, we should just pull these
     //           directly from the DL item, instead of
@@ -845,31 +846,33 @@ impl PrimitiveStore {
         }
     }
 
     pub fn add_primitive(
         &mut self,
         local_rect: &LayerRect,
         local_clip_rect: &LayerRect,
         is_backface_visible: bool,
+        composition_op: CompositionOp,
         clip_sources: ClipSourcesHandle,
         tag: Option<ItemTag>,
         container: PrimitiveContainer,
     ) -> PrimitiveIndex {
         let prim_index = self.cpu_metadata.len();
 
         let base_metadata = PrimitiveMetadata {
             clip_sources,
             gpu_location: GpuCacheHandle::new(),
             clip_task_id: None,
             local_rect: *local_rect,
             local_clip_rect: *local_clip_rect,
             is_backface_visible: is_backface_visible,
             screen_rect: None,
             tag,
+            composition_op,
 
             opacity: PrimitiveOpacity::translucent(),
             prim_kind: PrimitiveKind::Rectangle,
             cpu_prim_index: SpecificPrimitiveIndex(0),
         };
 
         let metadata = match container {
             PrimitiveContainer::Rectangle(rect) => {
--- a/gfx/webrender/src/renderer.rs
+++ b/gfx/webrender/src/renderer.rs
@@ -5,32 +5,32 @@
 //! The webrender API.
 //!
 //! The `webrender::renderer` module provides the interface to webrender, which
 //! is accessible through [`Renderer`][renderer]
 //!
 //! [renderer]: struct.Renderer.html
 
 use api::{channel, BlobImageRenderer, FontRenderMode};
-use api::{ColorF, Epoch, PipelineId, RenderApiSender, RenderNotifier};
+use api::{ColorF, CompositionOp, Epoch, PipelineId, RenderApiSender, RenderNotifier};
 use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceUintRect, DeviceUintSize};
 use api::{ExternalImageId, ExternalImageType, ImageFormat};
 use api::{YUV_COLOR_SPACES, YUV_FORMATS};
 use api::{YuvColorSpace, YuvFormat};
 #[cfg(not(feature = "debugger"))]
 use api::ApiMsg;
 use api::DebugCommand;
 #[cfg(not(feature = "debugger"))]
 use api::channel::MsgSender;
 use debug_colors;
 use debug_render::DebugRenderer;
 #[cfg(feature = "debugger")]
 use debug_server::{self, DebugServer};
-use device::{DepthFunction, Device, FrameId, GpuMarker, GpuProfiler, Program, Texture,
-             VertexDescriptor, PBO};
+use device::{DepthFunction, Device, FrameId, GpuMarker,
+              GpuProfiler, PBO, Program, Texture, VertexDescriptor};
 use device::{get_gl_format_bgra, ExternalTexture, FBOId, TextureSlot, VertexAttribute,
              VertexAttributeKind};
 use device::{FileWatcherHandler, GpuTimer, ShaderError, TextureFilter, TextureTarget,
              VertexUsageHint, VAO};
 use euclid::{rect, Transform3D};
 use frame_builder::FrameBuilderConfig;
 use gleam::gl;
 use gpu_cache::{GpuBlockData, GpuCacheUpdate, GpuCacheUpdateList};
@@ -612,17 +612,17 @@ impl SourceTextureResolver {
         }
     }
 }
 
 #[derive(Debug, Copy, Clone, PartialEq)]
 pub enum BlendMode {
     None,
     Alpha,
-    PremultipliedAlpha,
+    PremultipliedAlpha(CompositionOp),
     Subpixel,
 }
 
 // Tracks the state of each row in the GPU cache texture.
 struct CacheRow {
     is_dirty: bool,
 }
 
@@ -2362,17 +2362,17 @@ impl Renderer {
                     .bind(&mut self.device, projection, 0, &mut self.renderer_errors);
                 GPU_TAG_PRIM_BLEND
             }
             BatchKind::Transformable(transform_kind, batch_kind) => match batch_kind {
                 TransformBatchKind::Rectangle(needs_clipping) => {
                     debug_assert!(
                         !needs_clipping || match key.blend_mode {
                             BlendMode::Alpha |
-                            BlendMode::PremultipliedAlpha |
+                            BlendMode::PremultipliedAlpha(_) |
                             BlendMode::Subpixel => true,
                             BlendMode::None => false,
                         }
                     );
 
                     if needs_clipping {
                         self.ps_rectangle_clip.bind(
                             &mut self.device,
@@ -2727,17 +2727,17 @@ impl Renderer {
             self.device.disable_depth_write();
             self.gpu_profile.add_sampler(GPU_SAMPLER_TAG_TRANSPARENT);
 
             for batch in &target.alpha_batcher.batch_list.alpha_batch_list.batches {
                 if self.debug_flags.contains(DebugFlags::ALPHA_PRIM_DBG) {
                     let color = match batch.key.blend_mode {
                         BlendMode::None => ColorF::new(0.3, 0.3, 0.3, 1.0),
                         BlendMode::Alpha => ColorF::new(0.0, 0.9, 0.1, 1.0),
-                        BlendMode::PremultipliedAlpha => ColorF::new(0.0, 0.3, 0.7, 1.0),
+                        BlendMode::PremultipliedAlpha(_) => ColorF::new(0.0, 0.3, 0.7, 1.0),
                         BlendMode::Subpixel => ColorF::new(0.5, 0.0, 0.4, 1.0),
                     }.into();
                     for item_rect in &batch.item_rects {
                         self.debug.add_rect(item_rect, color);
                     }
                 }
 
                 match batch.key.kind {
@@ -2749,18 +2749,18 @@ impl Renderer {
                         // 1) Use dual source blending where available (almost all recent hardware).
                         // 2) Use frame buffer fetch where available (most modern hardware).
                         // 3) Consider the old constant color blend method where no clip is applied.
                         let _gm = self.gpu_profile.add_marker(GPU_TAG_PRIM_TEXT_RUN);
 
                         self.device.set_blend(true);
 
                         match batch.key.blend_mode {
-                            BlendMode::PremultipliedAlpha => {
-                                self.device.set_blend_mode_premultiplied_alpha();
+                            BlendMode::PremultipliedAlpha(op) => {
+                                self.device.set_blend_mode_premultiplied_alpha(op);
 
                                 self.ps_text_run.bind(
                                     &mut self.device,
                                     transform_kind,
                                     projection,
                                     TextShaderMode::Alpha,
                                     &mut self.renderer_errors,
                                 );
@@ -2822,19 +2822,19 @@ impl Renderer {
                             match batch.key.blend_mode {
                                 BlendMode::None => {
                                     self.device.set_blend(false);
                                 }
                                 BlendMode::Alpha => {
                                     self.device.set_blend(true);
                                     self.device.set_blend_mode_alpha();
                                 }
-                                BlendMode::PremultipliedAlpha => {
+                                BlendMode::PremultipliedAlpha(op) => {
                                     self.device.set_blend(true);
-                                    self.device.set_blend_mode_premultiplied_alpha();
+                                    self.device.set_blend_mode_premultiplied_alpha(op);
                                 }
                                 BlendMode::Subpixel => {
                                     unreachable!("bug: subpx text handled earlier");
                                 }
                             }
                             prev_blend_mode = batch.key.blend_mode;
                         }
 
--- a/gfx/webrender/src/tiling.rs
+++ b/gfx/webrender/src/tiling.rs
@@ -1,13 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-use api::{ClipAndScrollInfo, ClipId, ColorF, DeviceIntPoint, ImageKey};
+use api::{ClipAndScrollInfo, ClipId, CompositionOp, ColorF, DeviceIntPoint, ImageKey};
 use api::{DeviceIntRect, DeviceIntSize, DeviceUintPoint, DeviceUintSize};
 use api::{ExternalImageType, FilterOp, FontRenderMode, ImageRendering, LayerRect};
 use api::{LayerToWorldTransform, MixBlendMode, PipelineId, PropertyBinding, TransformStyle};
 use api::{LayerVector2D, TileOffset, WorldToLayerTransform, YuvColorSpace, YuvFormat};
 use border::{BorderCornerInstance, BorderCornerSide};
 use clip::{ClipSource, ClipStore};
 use clip_scroll_tree::CoordinateSystemId;
 use device::Texture;
@@ -44,35 +44,41 @@ trait AlphaBatchHelpers {
 }
 
 impl AlphaBatchHelpers for PrimitiveStore {
     fn get_blend_mode(
         &self,
         metadata: &PrimitiveMetadata,
         transform_kind: TransformedRectKind,
     ) -> BlendMode {
-        let needs_blending = !metadata.opacity.is_opaque || metadata.clip_task_id.is_some() ||
+        let needs_blending = match metadata.composition_op {
+            CompositionOp::Source => false,
+            CompositionOp::Over =>  !metadata.opacity.is_opaque,
+            _ => true,
+        };
+        let needs_blending = needs_blending || metadata.clip_task_id.is_some() ||
             transform_kind == TransformedRectKind::Complex;
 
         match metadata.prim_kind {
             PrimitiveKind::TextRun => {
                 let text_run_cpu = &self.cpu_text_runs[metadata.cpu_prim_index.0];
                 match text_run_cpu.font.render_mode {
                     FontRenderMode::Subpixel => BlendMode::Subpixel,
                     FontRenderMode::Alpha |
                     FontRenderMode::Mono |
-                    FontRenderMode::Bitmap => BlendMode::PremultipliedAlpha,
+                    FontRenderMode::Bitmap => BlendMode::PremultipliedAlpha(CompositionOp::Over),
                 }
-            }
+            },
+            PrimitiveKind::Rectangle |
             PrimitiveKind::Image |
             PrimitiveKind::AlignedGradient |
             PrimitiveKind::AngleGradient |
             PrimitiveKind::RadialGradient |
             PrimitiveKind::Picture => if needs_blending {
-                BlendMode::PremultipliedAlpha
+                BlendMode::PremultipliedAlpha(metadata.composition_op)
             } else {
                 BlendMode::None
             },
             _ => if needs_blending {
                 BlendMode::Alpha
             } else {
                 BlendMode::None
             },
@@ -248,17 +254,17 @@ impl BatchList {
 
     fn get_suitable_batch(
         &mut self,
         key: BatchKey,
         item_bounding_rect: &DeviceIntRect,
     ) -> &mut Vec<PrimitiveInstance> {
         match key.blend_mode {
             BlendMode::None => self.opaque_batch_list.get_suitable_batch(key),
-            BlendMode::Alpha | BlendMode::PremultipliedAlpha | BlendMode::Subpixel => {
+            BlendMode::Alpha | BlendMode::PremultipliedAlpha(_) | BlendMode::Subpixel => {
                 self.alpha_batch_list
                     .get_suitable_batch(key, item_bounding_rect)
             }
         }
     }
 
     fn finalize(&mut self) {
         self.opaque_batch_list.finalize()
@@ -284,17 +290,17 @@ impl AlphaRenderItem {
         deferred_resolves: &mut Vec<DeferredResolve>,
         glyph_fetch_buffer: &mut Vec<GlyphFetchResult>,
     ) {
         match *self {
             AlphaRenderItem::Blend(stacking_context_index, src_id, filter, z) => {
                 let stacking_context = &ctx.stacking_context_store[stacking_context_index.0];
                 let key = BatchKey::new(
                     BatchKind::Blend,
-                    BlendMode::PremultipliedAlpha,
+                    BlendMode::PremultipliedAlpha(CompositionOp::Over),
                     BatchTextures::no_texture(),
                 );
                 let src_task_address = render_tasks.get_task_address(src_id);
 
                 let (filter_mode, amount) = match filter {
                     FilterOp::Blur(..) => (0, 0.0),
                     FilterOp::Contrast(amount) => (1, amount),
                     FilterOp::Grayscale(amount) => (2, amount),
@@ -700,17 +706,17 @@ impl AlphaRenderItem {
                             uv_rect_addresses[2],
                         ));
                     }
                 }
             }
             AlphaRenderItem::SplitComposite(sc_index, task_id, gpu_handle, z) => {
                 let key = BatchKey::new(
                     BatchKind::SplitComposite,
-                    BlendMode::PremultipliedAlpha,
+                    BlendMode::PremultipliedAlpha(CompositionOp::Over),
                     BatchTextures::no_texture(),
                 );
                 let stacking_context = &ctx.stacking_context_store[sc_index.0];
                 let batch = batch_list.get_suitable_batch(key, &stacking_context.screen_bounds);
                 let source_task_address = render_tasks.get_task_address(task_id);
                 let gpu_address = gpu_handle.as_int(gpu_cache);
 
                 let instance = CompositePrimitiveInstance::new(
--- a/gfx/webrender_api/src/api.rs
+++ b/gfx/webrender_api/src/api.rs
@@ -10,16 +10,32 @@ use {NativeFontHandle, WorldPoint};
 use app_units::Au;
 use channel::{self, MsgSender, Payload, PayloadSender, PayloadSenderHelperMethods};
 use std::cell::Cell;
 use std::fmt;
 use std::marker::PhantomData;
 
 pub type TileSize = u16;
 
+#[repr(u32)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
+pub enum CompositionOp {
+    Over = 0,
+    Add = 1,
+    Atop = 2,
+    Out = 3,
+    In = 4,
+    Source = 5,
+    DestIn = 6,
+    DestOut = 7,
+    DestOver = 8,
+    DestAtop = 9,
+    Xor = 10,
+}
+
 /// The resource updates for a given transaction (they must be applied in the same frame).
 #[derive(Clone, Deserialize, Serialize)]
 pub struct ResourceUpdates {
     pub updates: Vec<ResourceUpdate>,
 }
 
 #[derive(Clone, Deserialize, Serialize)]
 pub enum ResourceUpdate {
--- a/gfx/webrender_api/src/display_item.rs
+++ b/gfx/webrender_api/src/display_item.rs
@@ -1,14 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-use {ColorF, FontInstanceKey, ImageKey, LayerPixel, LayoutPixel, LayoutPoint, LayoutRect,
-     LayoutSize, LayoutTransform};
+use {CompositionOp, 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.
@@ -53,16 +53,17 @@ pub struct DisplayItem {
     pub info: LayoutPrimitiveInfo,
 }
 
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct PrimitiveInfo<T> {
     pub rect: TypedRect<f32, T>,
     pub local_clip: LocalClip,
     pub is_backface_visible: bool,
+    pub composition_op: CompositionOp,
     pub tag: Option<ItemTag>,
 }
 
 impl LayerPrimitiveInfo {
     pub fn new(rect: TypedRect<f32, LayerPixel>) -> Self {
         Self::with_clip_rect(rect, rect)
     }
 
@@ -72,16 +73,17 @@ impl LayerPrimitiveInfo {
         Self::with_clip(rect, LocalClip::from(clip_rect))
     }
 
     pub fn with_clip(rect: TypedRect<f32, LayerPixel>, clip: LocalClip) -> Self {
         PrimitiveInfo {
             rect: rect,
             local_clip: clip,
             is_backface_visible: true,
+            composition_op: CompositionOp::Over,
             tag: None,
         }
     }
 }
 
 pub type LayoutPrimitiveInfo = PrimitiveInfo<LayoutPixel>;
 pub type LayerPrimitiveInfo = PrimitiveInfo<LayerPixel>;
 
--- a/gfx/webrender_api/src/display_list.rs
+++ b/gfx/webrender_api/src/display_list.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 {BorderDetails, BorderDisplayItem, BorderWidths, BoxShadowClipMode, BoxShadowDisplayItem};
-use {ClipAndScrollInfo, ClipDisplayItem, ClipId, ColorF, ComplexClipRegion, DisplayItem};
-use {ExtendMode, FilterOp, FontInstanceKey, GlyphInstance};
+use {ClipAndScrollInfo, ClipDisplayItem, ClipId, CompositionOp, ColorF, ComplexClipRegion};
+use {DisplayItem, 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 {BorderRadius, TextDisplayItem, Shadow, TransformStyle, YuvColorSpace, YuvData};
@@ -309,16 +309,17 @@ impl<'a, 'b> DisplayItemRef<'a, 'b> {
     }
 
     pub fn get_layer_primitive_info(&self, offset: &LayoutVector2D) -> LayerPrimitiveInfo {
         let info = self.iter.cur_item.info;
         LayerPrimitiveInfo {
             rect: info.rect.translate(&offset),
             local_clip: info.local_clip.create_with_offset(offset),
             is_backface_visible: info.is_backface_visible,
+            composition_op: info.composition_op,
             tag: info.tag,
         }
     }
 
     pub fn local_clip(&self) -> &LocalClip {
         &self.iter.cur_item.info.local_clip
     }
 
@@ -1159,16 +1160,17 @@ impl DisplayListBuilder {
             image_mask: image_mask,
             scroll_sensitivity,
         });
 
         let info = LayoutPrimitiveInfo {
             rect: content_rect,
             local_clip: LocalClip::from(clip_rect),
             is_backface_visible: true,
+            composition_op: CompositionOp::Over,
             tag: None,
         };
 
         let scrollinfo = ClipAndScrollInfo::simple(parent);
         self.push_item_with_clip_scroll_info(item, &info, scrollinfo);
         self.push_iter(complex_clips);
         id
     }
--- a/gfx/webrender_bindings/WebRenderAPI.cpp
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
@@ -820,23 +820,24 @@ DisplayListBuilder::PopClipAndScrollInfo
   mScrollIdStack.pop_back();
   wr_dp_pop_clip_and_scroll_info(mWrState);
 }
 
 void
 DisplayListBuilder::PushRect(const wr::LayoutRect& aBounds,
                              const wr::LayoutRect& aClip,
                              bool aIsBackfaceVisible,
+                             wr::CompositionOp aCompositionOp,
                              const wr::ColorF& aColor)
 {
   WRDL_LOG("PushRect b=%s cl=%s c=%s\n", mWrState,
       Stringify(aBounds).c_str(),
       Stringify(aClip).c_str(),
       Stringify(aColor).c_str());
-  wr_dp_push_rect(mWrState, aBounds, aClip, aIsBackfaceVisible, aColor);
+  wr_dp_push_rect(mWrState, aBounds, aClip, aIsBackfaceVisible, aCompositionOp, aColor);
 }
 
 void
 DisplayListBuilder::PushLinearGradient(const wr::LayoutRect& aBounds,
                                        const wr::LayoutRect& aClip,
                                        bool aIsBackfaceVisible,
                                        const wr::LayoutPoint& aStartPoint,
                                        const wr::LayoutPoint& aEndPoint,
@@ -871,39 +872,42 @@ DisplayListBuilder::PushRadialGradient(c
                              aExtendMode,
                              aTileSize, aTileSpacing);
 }
 
 void
 DisplayListBuilder::PushImage(const wr::LayoutRect& aBounds,
                               const wr::LayoutRect& aClip,
                               bool aIsBackfaceVisible,
+                              wr::CompositionOp aCompositionOp,
                               wr::ImageRendering aFilter,
                               wr::ImageKey aImage)
 {
   wr::LayoutSize size;
   size.width = aBounds.size.width;
   size.height = aBounds.size.height;
-  PushImage(aBounds, aClip, aIsBackfaceVisible, size, size, aFilter, aImage);
+  PushImage(aBounds, aClip, aIsBackfaceVisible, aCompositionOp, size, size, aFilter, aImage);
 }
 
 void
 DisplayListBuilder::PushImage(const wr::LayoutRect& aBounds,
                               const wr::LayoutRect& aClip,
                               bool aIsBackfaceVisible,
+                              wr::CompositionOp aCompositionOp,
                               const wr::LayoutSize& aStretchSize,
                               const wr::LayoutSize& aTileSpacing,
                               wr::ImageRendering aFilter,
                               wr::ImageKey aImage)
 {
   WRDL_LOG("PushImage b=%s cl=%s s=%s t=%s\n", mWrState,
       Stringify(aBounds).c_str(),
       Stringify(aClip).c_str(), Stringify(aStretchSize).c_str(),
       Stringify(aTileSpacing).c_str());
-  wr_dp_push_image(mWrState, aBounds, aClip, aIsBackfaceVisible, aStretchSize, aTileSpacing, aFilter, aImage);
+  wr_dp_push_image(mWrState, aBounds, aClip, aIsBackfaceVisible, aCompositionOp,
+                   aStretchSize, aTileSpacing, aFilter, aImage);
 }
 
 void
 DisplayListBuilder::PushYCbCrPlanarImage(const wr::LayoutRect& aBounds,
                                          const wr::LayoutRect& aClip,
                                          bool aIsBackfaceVisible,
                                          wr::ImageKey aImageChannel0,
                                          wr::ImageKey aImageChannel1,
--- a/gfx/webrender_bindings/WebRenderAPI.h
+++ b/gfx/webrender_bindings/WebRenderAPI.h
@@ -249,16 +249,17 @@ public:
 
   void PushClipAndScrollInfo(const layers::FrameMetrics::ViewID& aScrollId,
                              const wr::WrClipId* aClipId);
   void PopClipAndScrollInfo();
 
   void PushRect(const wr::LayoutRect& aBounds,
                 const wr::LayoutRect& aClip,
                 bool aIsBackfaceVisible,
+                wr::CompositionOp aCompositionOp,
                 const wr::ColorF& aColor);
 
   void PushLinearGradient(const wr::LayoutRect& aBounds,
                           const wr::LayoutRect& aClip,
                           bool aIsBackfaceVisible,
                           const wr::LayoutPoint& aStartPoint,
                           const wr::LayoutPoint& aEndPoint,
                           const nsTArray<wr::GradientStop>& aStops,
@@ -274,22 +275,24 @@ public:
                           const nsTArray<wr::GradientStop>& aStops,
                           wr::ExtendMode aExtendMode,
                           const wr::LayoutSize aTileSize,
                           const wr::LayoutSize aTileSpacing);
 
   void PushImage(const wr::LayoutRect& aBounds,
                  const wr::LayoutRect& aClip,
                  bool aIsBackfaceVisible,
+                 wr::CompositionOp aCompositionOp,
                  wr::ImageRendering aFilter,
                  wr::ImageKey aImage);
 
   void PushImage(const wr::LayoutRect& aBounds,
                  const wr::LayoutRect& aClip,
                  bool aIsBackfaceVisible,
+                 wr::CompositionOp aCompositionOp,
                  const wr::LayoutSize& aStretchSize,
                  const wr::LayoutSize& aTileSpacing,
                  wr::ImageRendering aFilter,
                  wr::ImageKey aImage);
 
   void PushYCbCrPlanarImage(const wr::LayoutRect& aBounds,
                             const wr::LayoutRect& aClip,
                             bool aIsBackfaceVisible,
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -1317,38 +1317,42 @@ pub extern "C" fn wr_dp_push_iframe(stat
     state.frame_builder.dl_builder.push_iframe(&prim_info, pipeline_id);
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_rect(state: &mut WrState,
                                   rect: LayoutRect,
                                   clip: LayoutRect,
                                   is_backface_visible: bool,
+                                  composition_op: CompositionOp,
                                   color: ColorF) {
     debug_assert!(unsafe { !is_in_render_thread() });
 
     let mut prim_info = LayoutPrimitiveInfo::with_clip_rect(rect, clip.into());
     prim_info.is_backface_visible = is_backface_visible;
+    prim_info.composition_op = composition_op;
     state.frame_builder.dl_builder.push_rect(&prim_info,
                                              color);
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_image(state: &mut WrState,
                                    bounds: LayoutRect,
                                    clip: LayoutRect,
                                    is_backface_visible: bool,
+                                   composition_op: CompositionOp,
                                    stretch_size: LayoutSize,
                                    tile_spacing: LayoutSize,
                                    image_rendering: ImageRendering,
                                    key: WrImageKey) {
     debug_assert!(unsafe { is_in_main_thread() || is_in_compositor_thread() });
 
     let mut prim_info = LayoutPrimitiveInfo::with_clip_rect(bounds, clip.into());
     prim_info.is_backface_visible = is_backface_visible;
+    prim_info.composition_op = composition_op;
     state.frame_builder
          .dl_builder
          .push_image(&prim_info,
                      stretch_size,
                      tile_spacing,
                      image_rendering,
                      key);
 }
--- a/gfx/webrender_bindings/webrender_ffi_generated.h
+++ b/gfx/webrender_bindings/webrender_ffi_generated.h
@@ -1,13 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-/* Generated with cbindgen:0.1.26 */
+/* Generated with cbindgen:0.1.25 */
 
 /* DO NOT MODIFY THIS MANUALLY! This file was generated using cbindgen.
  * To generate this file:
  *   1. Get the latest cbindgen using `cargo install --force cbindgen`
  *      a. Alternatively, you can clone `https://github.com/rlhunt/cbindgen` and use a tagged release
  *   2. Run `rustup run nightly cbindgen toolkit/library/rust/ --crate webrender_bindings -o gfx/webrender_bindings/webrender_ffi_generated.h`
  */
 
@@ -43,16 +43,32 @@ enum class BoxShadowClipMode : uint32_t 
 
 enum class ClipMode {
   Clip = 0,
   ClipOut = 1,
 
   Sentinel /* this must be last for serialization purposes. */
 };
 
+enum class CompositionOp : uint32_t {
+  Over = 0,
+  Add = 1,
+  Atop = 2,
+  Out = 3,
+  In = 4,
+  Source = 5,
+  DestIn = 6,
+  DestOut = 7,
+  DestOver = 8,
+  DestAtop = 9,
+  Xor = 10,
+
+  Sentinel /* this must be last for serialization purposes. */
+};
+
 enum class ExtendMode : uint32_t {
   Clamp = 0,
   Repeat = 1,
 
   Sentinel /* this must be last for serialization purposes. */
 };
 
 enum class ExternalImageType : uint32_t {
@@ -274,17 +290,17 @@ struct TypedSize2D_f32__LayerPixel {
   }
 };
 
 typedef TypedSize2D_f32__LayerPixel LayerSize;
 
 typedef LayerSize LayoutSize;
 
 // Describes the memory layout of a display list.
-//
+// 
 // A display list consists of some number of display list items, followed by a number of display
 // items.
 struct BuiltDisplayListDescriptor {
   // The first IPC time stamp: before any work has been done
   uint64_t builder_start_time;
   // The second IPC time stamp: after serialization
   uint64_t builder_finish_time;
   // The third IPC time stamp: just before sending
@@ -314,27 +330,16 @@ struct WrOpacityProperty {
   float opacity;
 
   bool operator==(const WrOpacityProperty& aOther) const {
     return id == aOther.id &&
            opacity == aOther.opacity;
   }
 };
 
-// A 3d transform stored as a 4 by 4 matrix in row-major order in memory.
-//
-// Transforms can be parametrized over the source and destination units, to describe a
-// transformation from a space to another.
-// For example, `TypedTransform3D<f32, WordSpace, ScreenSpace>::transform_point3d`
-// takes a `TypedPoint3D<f32, WordSpace>` and returns a `TypedPoint3D<f32, ScreenSpace>`.
-//
-// Transforms expose a set of convenience methods for pre- and post-transformations.
-// A pre-transformation corresponds to adding an operation that is applied before
-// the rest of the transformation, while a post-transformation adds an operation
-// that is applied after.
 struct TypedTransform3D_f32__LayoutPixel__LayoutPixel {
   float m11;
   float m12;
   float m13;
   float m14;
   float m21;
   float m22;
   float m23;
@@ -390,34 +395,33 @@ struct IdNamespace {
   bool operator<=(const IdNamespace& aOther) const {
     return mHandle <= aOther.mHandle;
   }
 };
 
 typedef IdNamespace WrIdNamespace;
 
 // Represents RGBA screen colors with floating point numbers.
-//
+// 
 // All components must be between 0.0 and 1.0.
 // An alpha value of 1.0 is opaque while 0.0 is fully transparent.
 struct ColorF {
   float r;
   float g;
   float b;
   float a;
 
   bool operator==(const ColorF& aOther) const {
     return r == aOther.r &&
            g == aOther.g &&
            b == aOther.b &&
            a == aOther.a;
   }
 };
 
-// A 2d Point tagged with a unit.
 struct TypedPoint2D_f32__LayerPixel {
   float x;
   float y;
 
   bool operator==(const TypedPoint2D_f32__LayerPixel& aOther) const {
     return x == aOther.x &&
            y == aOther.y;
   }
@@ -577,17 +581,16 @@ struct NinePatchDescriptor {
 
   bool operator==(const NinePatchDescriptor& aOther) const {
     return width == aOther.width &&
            height == aOther.height &&
            slice == aOther.slice;
   }
 };
 
-// A 2d Vector tagged with a unit.
 struct TypedVector2D_f32__LayerPixel {
   float x;
   float y;
 
   bool operator==(const TypedVector2D_f32__LayerPixel& aOther) const {
     return x == aOther.x &&
            y == aOther.y;
   }
@@ -658,17 +661,16 @@ struct ByteSlice {
   size_t len;
 
   bool operator==(const ByteSlice& aOther) const {
     return buffer == aOther.buffer &&
            len == aOther.len;
   }
 };
 
-// A 2d Point tagged with a unit.
 struct TypedPoint2D_u16__Tiles {
   uint16_t x;
   uint16_t y;
 
   bool operator==(const TypedPoint2D_u16__Tiles& aOther) const {
     return x == aOther.x &&
            y == aOther.y;
   }
@@ -1068,16 +1070,17 @@ void wr_dp_push_iframe(WrState *aState,
                        WrPipelineId aPipelineId)
 WR_FUNC;
 
 WR_INLINE
 void wr_dp_push_image(WrState *aState,
                       LayoutRect aBounds,
                       LayoutRect aClip,
                       bool aIsBackfaceVisible,
+                      CompositionOp aCompositionOp,
                       LayoutSize aStretchSize,
                       LayoutSize aTileSpacing,
                       ImageRendering aImageRendering,
                       WrImageKey aKey)
 WR_FUNC;
 
 WR_INLINE
 void wr_dp_push_line(WrState *aState,
@@ -1120,16 +1123,17 @@ void wr_dp_push_radial_gradient(WrState 
                                 LayoutSize aTileSpacing)
 WR_FUNC;
 
 WR_INLINE
 void wr_dp_push_rect(WrState *aState,
                      LayoutRect aRect,
                      LayoutRect aClip,
                      bool aIsBackfaceVisible,
+                     CompositionOp aCompositionOp,
                      ColorF aColor)
 WR_FUNC;
 
 WR_INLINE
 void wr_dp_push_scroll_layer(WrState *aState,
                              uint64_t aScrollId)
 WR_FUNC;
 
--- a/layout/generic/TextDrawTarget.h
+++ b/layout/generic/TextDrawTarget.h
@@ -153,17 +153,18 @@ public:
     }
   }
 
   void
   AppendSelectionRect(const LayoutDeviceRect& aRect, const Color& aColor)
   {
     auto rect = wr::ToLayoutRect(aRect);
     auto color = wr::ToColorF(aColor);
-    mBuilder.PushRect(rect, mClipRect, mBackfaceVisible, color);
+    mBuilder.PushRect(rect, mClipRect, mBackfaceVisible,
+                      wr::CompositionOp::Over, color);
   }
 
   void
   AppendDecoration(const Point& aStart,
                    const Point& aEnd,
                    const float aThickness,
                    const bool aVertical,
                    const Color& aColor,
--- a/layout/generic/nsBulletFrame.cpp
+++ b/layout/generic/nsBulletFrame.cpp
@@ -483,16 +483,17 @@ BulletRenderer::CreateWebRenderCommandsF
 
   const int32_t appUnitsPerDevPixel = aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
   LayoutDeviceRect destRect = LayoutDeviceRect::FromAppUnits(mDest, appUnitsPerDevPixel);
   wr::LayoutRect dest = aSc.ToRelativeLayoutRect(destRect);
 
   aBuilder.PushImage(dest,
                      dest,
                      !aItem->BackfaceIsHidden(),
+                     wr::CompositionOp::Over,
                      wr::ImageRendering::Auto,
                      key.value());
 
   return true;
 }
 
 bool
 BulletRenderer::CreateWebRenderCommandsForPath(nsDisplayItem* aItem,
--- a/layout/generic/nsCanvasFrame.cpp
+++ b/layout/generic/nsCanvasFrame.cpp
@@ -324,17 +324,17 @@ nsDisplayCanvasBackgroundColor::CreateWe
   int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
 
   LayoutDeviceRect rect = LayoutDeviceRect::FromAppUnits(
           bgClipRect, appUnitsPerDevPixel);
 
   wr::LayoutRect transformedRect = aSc.ToRelativeLayoutRect(rect);
   aBuilder.PushRect(transformedRect,
                     transformedRect,
-                    !BackfaceIsHidden(),
+                    !BackfaceIsHidden(), wr::CompositionOp::Over,
                     wr::ToColorF(ToDeviceColor(mColor)));
   return true;
 }
 
 #ifdef MOZ_DUMP_PAINTING
 void
 nsDisplayCanvasBackgroundColor::WriteDebugInfo(std::stringstream& aStream)
 {
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -2947,17 +2947,17 @@ nsDisplaySolidColor::CreateWebRenderComm
                                              nsDisplayListBuilder* aDisplayListBuilder)
 {
   LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
         mVisibleRect, mFrame->PresContext()->AppUnitsPerDevPixel());
   wr::LayoutRect transformedRect = aSc.ToRelativeLayoutRect(bounds);
 
   aBuilder.PushRect(transformedRect,
                     transformedRect,
-                    !BackfaceIsHidden(),
+                    !BackfaceIsHidden(), wr::CompositionOp::Over,
                     wr::ToColorF(ToDeviceColor(mColor)));
 
   return true;
 }
 
 nsRect
 nsDisplaySolidColorRegion::GetBounds(nsDisplayListBuilder* aBuilder,
                                      bool* aSnap) const
@@ -2999,17 +2999,17 @@ nsDisplaySolidColorRegion::CreateWebRend
 {
   for (auto iter = mRegion.RectIter(); !iter.Done(); iter.Next()) {
     nsRect rect = iter.Get();
     LayoutDeviceRect layerRects = LayoutDeviceRect::FromAppUnits(
       rect, mFrame->PresContext()->AppUnitsPerDevPixel());
     wr::LayoutRect transformedRect = aSc.ToRelativeLayoutRect(layerRects);
     aBuilder.PushRect(transformedRect,
                       transformedRect,
-                      !BackfaceIsHidden(),
+                      !BackfaceIsHidden(), wr::CompositionOp::Over,
                       wr::ToColorF(ToDeviceColor(mColor)));
   }
 
   return true;
 }
 
 static void
 RegisterThemeGeometry(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
@@ -4278,17 +4278,17 @@ nsDisplayBackgroundColor::CreateWebRende
   }
 
   LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
         mBackgroundRect, mFrame->PresContext()->AppUnitsPerDevPixel());
   wr::LayoutRect transformedRect = aSc.ToRelativeLayoutRect(bounds);
 
   aBuilder.PushRect(transformedRect,
                     transformedRect,
-                    !BackfaceIsHidden(),
+                    !BackfaceIsHidden(), wr::CompositionOp::Over,
                     wr::ToColorF(ToDeviceColor(mColor)));
 
   return true;
 }
 
 void
 nsDisplayBackgroundColor::Paint(nsDisplayListBuilder* aBuilder,
                                 gfxContext* aCtx)
@@ -4410,16 +4410,36 @@ nsDisplayClearBackground::BuildLayer(nsD
   bool snap;
   nsRect bounds = GetBounds(aBuilder, &snap);
   int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
   layer->SetBounds(bounds.ToNearestPixels(appUnitsPerDevPixel)); // XXX Do we need to respect the parent layer's scale here?
 
   return layer.forget();
 }
 
+bool
+nsDisplayClearBackground::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
+                                                  mozilla::wr::IpcResourceUpdateQueue& aResources,
+                                                  const StackingContextHelper& aSc,
+                                                  mozilla::layers::WebRenderLayerManager* aManager,
+                                                  nsDisplayListBuilder* aDisplayListBuilder)
+{
+  LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
+    nsRect(ToReferenceFrame(), mFrame->GetSize()),
+    mFrame->PresContext()->AppUnitsPerDevPixel());
+  wr::LayoutRect transformedRect = aSc.ToRelativeLayoutRect(bounds);
+
+  aBuilder.PushRect(transformedRect,
+                    transformedRect,
+                    true, wr::CompositionOp::Source,
+                    wr::ColorF{ 0.0f, 0.0f, 0.0f, 0.0f });
+
+  return true;
+}
+
 nsRect
 nsDisplayOutline::GetBounds(nsDisplayListBuilder* aBuilder,
                             bool* aSnap) const
 {
   *aSnap = false;
   return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
 }
 
@@ -4789,23 +4809,23 @@ nsDisplayCaret::CreateWebRenderCommands(
     hookRect + ToReferenceFrame(), appUnitsPerDevPixel);
 
   wr::LayoutRect caret = aSc.ToRelativeLayoutRect(devCaretRect);
   wr::LayoutRect hook = aSc.ToRelativeLayoutRect(devHookRect);
 
   // Note, WR will pixel snap anything that is layout aligned.
   aBuilder.PushRect(caret,
                     caret,
-                    !BackfaceIsHidden(),
+                    !BackfaceIsHidden(), wr::CompositionOp::Over,
                     wr::ToColorF(color));
 
   if (!devHookRect.IsEmpty()) {
     aBuilder.PushRect(hook,
                       hook,
-                      !BackfaceIsHidden(),
+                      !BackfaceIsHidden(), wr::CompositionOp::Over,
                       wr::ToColorF(color));
   }
   return true;
 }
 
 LayerState
 nsDisplayCaret::GetLayerState(nsDisplayListBuilder* aBuilder,
                               LayerManager* aManager,
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -3680,16 +3680,22 @@ public:
   {
     return mozilla::LAYER_ACTIVE_FORCE;
   }
 
   virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                              LayerManager* aManager,
                                              const ContainerLayerParameters& aContainerParameters) override;
 
+  bool CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
+                               mozilla::wr::IpcResourceUpdateQueue& aResources,
+                               const StackingContextHelper& aSc,
+                               mozilla::layers::WebRenderLayerManager* aManager,
+                               nsDisplayListBuilder* aDisplayListBuilder) override;
+
   NS_DISPLAY_DECL_NAME("ClearBackground", TYPE_CLEAR_BACKGROUND)
 };
 
 /**
  * The standard display item to paint the outer CSS box-shadows of a frame.
  */
 class nsDisplayBoxShadowOuter final : public nsDisplayItem {
 public:
--- a/layout/painting/nsImageRenderer.cpp
+++ b/layout/painting/nsImageRenderer.cpp
@@ -659,17 +659,17 @@ nsImageRenderer::BuildWebRenderDisplayIt
       wr::LayoutRect fill = aSc.ToRelativeLayoutRect(fillRect);
       wr::LayoutRect clip = aSc.ToRelativeLayoutRect(
           LayoutDeviceRect::FromAppUnits(aFill, appUnitsPerDevPixel));
 
       LayoutDeviceSize gapSize = LayoutDeviceSize::FromAppUnits(
           aRepeatSize - aDest.Size(), appUnitsPerDevPixel);
 
       SamplingFilter samplingFilter = nsLayoutUtils::GetSamplingFilterForFrame(mForFrame);
-      aBuilder.PushImage(fill, clip, !aItem->BackfaceIsHidden(),
+      aBuilder.PushImage(fill, clip, !aItem->BackfaceIsHidden(), wr::CompositionOp::Over,
                          wr::ToLayoutSize(destRect.Size()), wr::ToLayoutSize(gapSize),
                          wr::ToImageRendering(samplingFilter), key.value());
       break;
     }
     default:
       break;
   }
 
--- a/layout/xul/nsImageBoxFrame.cpp
+++ b/layout/xul/nsImageBoxFrame.cpp
@@ -456,17 +456,17 @@ nsImageBoxFrame::CreateWebRenderCommands
   }
   const int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel();
   LayoutDeviceRect fillRect = LayoutDeviceRect::FromAppUnits(dest,
                                                              appUnitsPerDevPixel);
   wr::LayoutRect fill = aSc.ToRelativeLayoutRect(fillRect);
 
   LayoutDeviceSize gapSize(0, 0);
   SamplingFilter sampleFilter = nsLayoutUtils::GetSamplingFilterForFrame(aItem->Frame());
-  aBuilder.PushImage(fill, fill, !BackfaceIsHidden(),
+  aBuilder.PushImage(fill, fill, !BackfaceIsHidden(), wr::CompositionOp::Over,
                      wr::ToLayoutSize(fillRect.Size()), wr::ToLayoutSize(gapSize),
                      wr::ToImageRendering(sampleFilter), key.value());
 
   return DrawResult::SUCCESS;
 }
 
 nsRect
 nsImageBoxFrame::GetDestRect(const nsPoint& aOffset, Maybe<nsPoint>& aAnchorPoint)
--- a/widget/cocoa/nsChildView.h
+++ b/widget/cocoa/nsChildView.h
@@ -632,18 +632,19 @@ protected:
 
   // Compositor thread only
   mozilla::UniquePtr<mozilla::widget::RectTextureImage> mResizerImage;
   mozilla::UniquePtr<mozilla::widget::RectTextureImage> mCornerMaskImage;
   mozilla::UniquePtr<mozilla::widget::RectTextureImage> mTitlebarImage;
   mozilla::UniquePtr<mozilla::widget::RectTextureImage> mBasicCompositorImage;
 
   // Main thread + webrender only
+  mozilla::Maybe<mozilla::wr::ImageKey> mCornerMaskImageKey;
+  LayoutDeviceIntSize mCornerMaskImageSize;
   mozilla::Maybe<mozilla::wr::ImageKey> mTitlebarImageKey;
-  mozilla::gfx::IntSize mTitlebarImageSize;
 
   // The area of mTitlebarCGContext that has changed and needs to be
   // uploaded to to mTitlebarImage. Main thread only.
   nsIntRegion           mDirtyTitlebarRegion;
 
   mozilla::ViewRegion   mNonDraggableRegion;
 
   // Cached value of [mView backingScaleFactor], to avoid sending two obj-c
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -2073,16 +2073,44 @@ nsChildView::PrepareWindowEffects()
 void
 nsChildView::CleanupWindowEffects()
 {
   mResizerImage = nullptr;
   mCornerMaskImage = nullptr;
   mTitlebarImage = nullptr;
 }
 
+static CGContextRef
+CreateCGContext(const LayoutDeviceIntSize& aSize)
+{
+  CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
+  CGContextRef ctx =
+    CGBitmapContextCreate(NULL,
+                          aSize.width,
+                          aSize.height,
+                          8 /* bitsPerComponent */,
+                          aSize.width * 4,
+                          cs,
+                          kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst);
+  CGColorSpaceRelease(cs);
+
+  CGContextTranslateCTM(ctx, 0, aSize.height);
+  CGContextScaleCTM(ctx, 1, -1);
+  CGContextSetInterpolationQuality(ctx, kCGInterpolationLow);
+
+  return ctx;
+}
+
+static void
+DrawTopLeftCornerMask(CGContextRef aCtx, int aRadius)
+{
+  CGContextSetRGBFillColor(aCtx, 1.0, 1.0, 1.0, 1.0);
+  CGContextFillEllipseInRect(aCtx, CGRectMake(0, 0, aRadius * 2, aRadius * 2));
+}
+
 void
 nsChildView::AddWindowOverlayWebRenderCommands(layers::WebRenderBridgeChild* aWrBridge,
                                                wr::DisplayListBuilder& aBuilder,
                                                wr::IpcResourceUpdateQueue& aResources)
 {
   PrepareWindowEffects();
 
   bool needUpdate = mUpdatedTitlebarRegion.Intersects(mTitlebarRect);
@@ -2094,51 +2122,96 @@ nsChildView::AddWindowOverlayWebRenderCo
     size_t stride = CGBitmapContextGetBytesPerRow(mTitlebarCGContext);
     size_t titlebarCGContextDataLength = stride * size.height;
     gfx::SurfaceFormat format = gfx::SurfaceFormat::B8G8R8A8;
     Range<uint8_t> buffer(
       static_cast<uint8_t *>(CGBitmapContextGetData(mTitlebarCGContext)),
       titlebarCGContextDataLength
     );
 
-    if (mTitlebarImageKey &&
-        mTitlebarImageSize != size) {
-      // Delete wr::ImageKey. wr::ImageKey does not support size change.
-      // TODO: that's not true anymore! (size change is now supported).
-      CleanupWebRenderWindowOverlay(aWrBridge, aResources);
-      MOZ_ASSERT(mTitlebarImageKey.isNothing());
-    }
-
     if (!mTitlebarImageKey) {
       mTitlebarImageKey = Some(aWrBridge->GetNextImageKey());
       wr::ImageDescriptor descriptor(size, stride, format);
       aResources.AddImage(*mTitlebarImageKey, descriptor, buffer);
-      mTitlebarImageSize = size;
       needUpdate = false;
     }
 
     if (needUpdate) {
       wr::ImageDescriptor descriptor(size, stride, format);
       aResources.UpdateImageBuffer(*mTitlebarImageKey, descriptor, buffer);
     }
 
     wr::LayoutRect rect = wr::ToLayoutRect(mTitlebarRect);
     aBuilder.PushImage(wr::LayoutRect{ rect.origin, { float(size.width), float(size.height) } },
-                       rect, true, wr::ImageRendering::Auto, *mTitlebarImageKey);
+                       rect, true, wr::CompositionOp::Over, wr::ImageRendering::Auto, *mTitlebarImageKey);
+  }
+
+  LayoutDeviceIntSize cornerMaskSize(mDevPixelCornerRadius * 2, mDevPixelCornerRadius * 2);
+  size_t stride = 0;
+  CGContextRef cornerMaskContext = nullptr;
+  Maybe<Range<uint8_t>> buffer;
+  needUpdate = !mCornerMaskImageKey || (cornerMaskSize != mCornerMaskImageSize);
+  if (needUpdate) {
+    cornerMaskContext = CreateCGContext(cornerMaskSize);
+    DrawTopLeftCornerMask(cornerMaskContext, mDevPixelCornerRadius);
+    stride = CGBitmapContextGetBytesPerRow(cornerMaskContext);
+    size_t dataLength = stride * cornerMaskSize.height;
+    buffer.emplace(
+      static_cast<uint8_t *>(CGBitmapContextGetData(cornerMaskContext)),
+      dataLength
+    );
+  }
+  wr::ImageDescriptor descriptor(cornerMaskSize.ToUnknownSize(), stride, gfx::SurfaceFormat::B8G8R8A8);
+  if (!mCornerMaskImageKey) {
+    mCornerMaskImageKey = Some(aWrBridge->GetNextImageKey());
+    aResources.AddImage(*mCornerMaskImageKey, descriptor, *buffer);
+    needUpdate = false;
+    mCornerMaskImageSize = cornerMaskSize;
+  }
+  if (needUpdate) {
+    aResources.UpdateImageBuffer(*mCornerMaskImageKey, descriptor, *buffer);
+  }
+
+  if (mIsCoveringTitlebar && !mIsFullscreen) {
+    // Mask the top corners.
+    aBuilder.PushImage(wr::ToLayoutRect(LayoutDeviceIntRect(0, 0, cornerMaskSize.width, cornerMaskSize.height)),
+                       wr::ToLayoutRect(LayoutDeviceIntRect(0, 0, mDevPixelCornerRadius, mDevPixelCornerRadius)),
+                       true, wr::CompositionOp::DestIn,
+                       wr::ImageRendering::Auto, *mCornerMaskImageKey);
+    aBuilder.PushImage(wr::ToLayoutRect(LayoutDeviceIntRect(mBounds.width - cornerMaskSize.width, 0, cornerMaskSize.width, cornerMaskSize.height)),
+                       wr::ToLayoutRect(LayoutDeviceIntRect(mBounds.width - mDevPixelCornerRadius, 0, mDevPixelCornerRadius, mDevPixelCornerRadius)),
+                       true, wr::CompositionOp::DestIn,
+                       wr::ImageRendering::Auto, *mCornerMaskImageKey);
+  }
+
+  if (mHasRoundedBottomCorners && !mIsFullscreen) {
+    // Mask the bottom corners.
+    aBuilder.PushImage(wr::ToLayoutRect(LayoutDeviceIntRect(0, mBounds.height - cornerMaskSize.height, cornerMaskSize.width, cornerMaskSize.height)),
+                       wr::ToLayoutRect(LayoutDeviceIntRect(0, mBounds.height - mDevPixelCornerRadius, mDevPixelCornerRadius, mDevPixelCornerRadius)),
+                       true, wr::CompositionOp::DestIn,
+                       wr::ImageRendering::Auto, *mCornerMaskImageKey);
+    aBuilder.PushImage(wr::ToLayoutRect(LayoutDeviceIntRect(mBounds.width - cornerMaskSize.width, mBounds.height - cornerMaskSize.height, cornerMaskSize.width, cornerMaskSize.height)),
+                       wr::ToLayoutRect(LayoutDeviceIntRect(mBounds.width - mDevPixelCornerRadius, mBounds.height - mDevPixelCornerRadius, mDevPixelCornerRadius, mDevPixelCornerRadius)),
+                       true, wr::CompositionOp::DestIn,
+                       wr::ImageRendering::Auto, *mCornerMaskImageKey);
   }
 }
 
 void
 nsChildView::CleanupWebRenderWindowOverlay(layers::WebRenderBridgeChild* aWrBridge,
                                            wr::IpcResourceUpdateQueue& aResources)
 {
   if (mTitlebarImageKey) {
     aResources.DeleteImage(*mTitlebarImageKey);
     mTitlebarImageKey = Nothing();
   }
+  if (mCornerMaskImageKey) {
+    aResources.DeleteImage(*mCornerMaskImageKey);
+    mCornerMaskImageKey = Nothing();
+  }
 }
 
 bool
 nsChildView::PreRender(WidgetRenderingContext* aContext)
 {
   UniquePtr<GLManager> manager(GLManager::CreateGLManager(aContext->mLayerManager));
   gl::GLContext* gl = manager ? manager->gl() : aContext->mGL;
   if (!gl) {
@@ -2252,37 +2325,16 @@ nsChildView::MaybeDrawResizeIndicator(GL
     gfx::BorrowedCGContext borrow(drawTarget);
     DrawResizer(borrow.cg);
     borrow.Finish();
   });
 
   mResizerImage->Draw(aManager, mResizeIndicatorRect.TopLeft());
 }
 
-static CGContextRef
-CreateCGContext(const LayoutDeviceIntSize& aSize)
-{
-  CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
-  CGContextRef ctx =
-    CGBitmapContextCreate(NULL,
-                          aSize.width,
-                          aSize.height,
-                          8 /* bitsPerComponent */,
-                          aSize.width * 4,
-                          cs,
-                          kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst);
-  CGColorSpaceRelease(cs);
-
-  CGContextTranslateCTM(ctx, 0, aSize.height);
-  CGContextScaleCTM(ctx, 1, -1);
-  CGContextSetInterpolationQuality(ctx, kCGInterpolationLow);
-
-  return ctx;
-}
-
 LayoutDeviceIntSize
 TextureSizeForSize(const LayoutDeviceIntSize& aSize)
 {
   return LayoutDeviceIntSize(RoundUpPow2(aSize.width),
                              RoundUpPow2(aSize.height));
 }
 
 // When this method is entered, mEffectsLock is already being held.
@@ -2436,23 +2488,16 @@ nsChildView::MaybeDrawTitlebar(GLManager
 
   mTitlebarImage->UpdateFromCGContext(mTitlebarRect.Size(),
                                       updatedTitlebarRegion,
                                       mTitlebarCGContext);
 
   mTitlebarImage->Draw(aManager, mTitlebarRect.TopLeft());
 }
 
-static void
-DrawTopLeftCornerMask(CGContextRef aCtx, int aRadius)
-{
-  CGContextSetRGBFillColor(aCtx, 1.0, 1.0, 1.0, 1.0);
-  CGContextFillEllipseInRect(aCtx, CGRectMake(0, 0, aRadius * 2, aRadius * 2));
-}
-
 void
 nsChildView::MaybeDrawRoundedCorners(GLManager* aManager,
                                      const LayoutDeviceIntRect& aRect)
 {
   MutexAutoLock lock(mEffectsLock);
 
   if (!mCornerMaskImage) {
     mCornerMaskImage = MakeUnique<RectTextureImage>();