Bug 1250037 - part 1 - only blur one quadrant of a box-shadow and mirror it to the other quadrants. r=mchang
authorLee Salzman <lsalzman@mozilla.com>
Mon, 21 Nov 2016 13:16:59 -0500
changeset 368682 9b47a1c822ae434f21d7177045c3e94dca21499b
parent 368681 b5541adf77a4eeb1f237828e1bd6b8076bafd1f3
child 368683 fddfac71bb12f64093846c50f64cb52535ad14fb
push id6996
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 20:48:21 +0000
treeherdermozilla-beta@d89512dab048 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmchang
bugs1250037
milestone53.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1250037 - part 1 - only blur one quadrant of a box-shadow and mirror it to the other quadrants. r=mchang MozReview-Commit-ID: B4FSDmAMfXK
gfx/2d/Blur.cpp
gfx/2d/Blur.h
gfx/2d/PathHelpers.h
gfx/thebes/gfxBlur.cpp
gfx/thebes/gfxBlur.h
layout/base/nsCSSRendering.cpp
layout/reftests/box-shadow/reftest.list
layout/reftests/outline/reftest.list
--- a/gfx/2d/Blur.cpp
+++ b/gfx/2d/Blur.cpp
@@ -331,52 +331,64 @@ AlphaBoxBlur::RoundUpToMultipleOf4(int32
   return val;
 }
 
 AlphaBoxBlur::AlphaBoxBlur(const Rect& aRect,
                            const IntSize& aSpreadRadius,
                            const IntSize& aBlurRadius,
                            const Rect* aDirtyRect,
                            const Rect* aSkipRect)
- : mSpreadRadius(aSpreadRadius),
-   mBlurRadius(aBlurRadius),
-   mSurfaceAllocationSize(0)
+  : mSurfaceAllocationSize(0)
+{
+  Init(aRect, aSpreadRadius, aBlurRadius, aDirtyRect, aSkipRect);
+}
+
+AlphaBoxBlur::AlphaBoxBlur()
+  : mSurfaceAllocationSize(0)
 {
+}
+
+void
+AlphaBoxBlur::Init(const Rect& aRect,
+                   const IntSize& aSpreadRadius,
+                   const IntSize& aBlurRadius,
+                   const Rect* aDirtyRect,
+                   const Rect* aSkipRect)
+{
+  mSpreadRadius = aSpreadRadius;
+  mBlurRadius = aBlurRadius;
+
   Rect rect(aRect);
   rect.Inflate(Size(aBlurRadius + aSpreadRadius));
   rect.RoundOut();
 
   if (aDirtyRect) {
     // If we get passed a dirty rect from layout, we can minimize the
     // shadow size and make painting faster.
     mHasDirtyRect = true;
     mDirtyRect = *aDirtyRect;
     Rect requiredBlurArea = mDirtyRect.Intersect(rect);
     requiredBlurArea.Inflate(Size(aBlurRadius + aSpreadRadius));
     rect = requiredBlurArea.Intersect(rect);
   } else {
     mHasDirtyRect = false;
   }
 
-  mRect = IntRect(int32_t(rect.x), int32_t(rect.y),
-                  int32_t(rect.width), int32_t(rect.height));
+  mRect = TruncatedToInt(rect);
   if (mRect.IsEmpty()) {
     return;
   }
 
   if (aSkipRect) {
     // If we get passed a skip rect, we can lower the amount of
     // blurring/spreading we need to do. We convert it to IntRect to avoid
     // expensive int<->float conversions if we were to use Rect instead.
     Rect skipRect = *aSkipRect;
-    skipRect.RoundIn();
     skipRect.Deflate(Size(aBlurRadius + aSpreadRadius));
-    mSkipRect = IntRect(int32_t(skipRect.x), int32_t(skipRect.y),
-                        int32_t(skipRect.width), int32_t(skipRect.height));
-
+    mSkipRect = RoundedIn(skipRect);
     mSkipRect = mSkipRect.Intersect(mRect);
     if (mSkipRect.IsEqualInterior(mRect))
       return;
 
     mSkipRect -= mRect.TopLeft();
   } else {
     mSkipRect = IntRect(0, 0, 0, 0);
   }
@@ -393,18 +405,17 @@ AlphaBoxBlur::AlphaBoxBlur(const Rect& a
     }
   }
 }
 
 AlphaBoxBlur::AlphaBoxBlur(const Rect& aRect,
                            int32_t aStride,
                            float aSigmaX,
                            float aSigmaY)
-  : mRect(int32_t(aRect.x), int32_t(aRect.y),
-          int32_t(aRect.width), int32_t(aRect.height)),
+  : mRect(TruncatedToInt(aRect)),
     mSpreadRadius(),
     mBlurRadius(CalculateBlurRadius(Point(aSigmaX, aSigmaY))),
     mStride(aStride),
     mSurfaceAllocationSize(0)
 {
   IntRect intRect;
   if (aRect.ToIntRect(&intRect)) {
     size_t minDataSize = BufferSizeFromStrideAndHeight(intRect.width, intRect.height);
--- a/gfx/2d/Blur.h
+++ b/gfx/2d/Blur.h
@@ -64,16 +64,24 @@ public:
                const Rect* aDirtyRect,
                const Rect* aSkipRect);
 
   AlphaBoxBlur(const Rect& aRect,
                int32_t aStride,
                float aSigmaX,
                float aSigmaY);
 
+  AlphaBoxBlur();
+
+  void Init(const Rect& aRect,
+            const IntSize& aSpreadRadius,
+            const IntSize& aBlurRadius,
+            const Rect* aDirtyRect,
+            const Rect* aSkipRect);
+
   ~AlphaBoxBlur();
 
   /**
    * Return the size, in pixels, of the 8-bit alpha surface we'd use.
    */
   IntSize GetSize();
 
   /**
--- a/gfx/2d/PathHelpers.h
+++ b/gfx/2d/PathHelpers.h
@@ -219,16 +219,25 @@ struct RectCornerRadii {
 
   bool operator==(const RectCornerRadii& aOther) const {
     for (size_t i = 0; i < RectCorner::Count; i++) {
       if (radii[i] != aOther.radii[i]) return false;
     }
     return true;
   }
 
+  bool AreRadiiSame() const {
+    for (size_t i = 1; i < RectCorner::Count; i++) {
+      if (radii[i] != radii[0]) {
+        return false;
+      }
+    }
+    return true;
+  }
+
   void Scale(Float aXScale, Float aYScale) {
     for (int i = 0; i < RectCorner::Count; i++) {
       radii[i].Scale(aXScale, aYScale);
     }
   }
 
   const Size TopLeft() const { return radii[RectCorner::TopLeft]; }
   Size& TopLeft() { return radii[RectCorner::TopLeft]; }
--- a/gfx/thebes/gfxBlur.cpp
+++ b/gfx/thebes/gfxBlur.cpp
@@ -6,91 +6,88 @@
 #include "gfxBlur.h"
 
 #include "gfx2DGlue.h"
 #include "gfxContext.h"
 #include "gfxPlatform.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/Blur.h"
 #include "mozilla/gfx/PathHelpers.h"
-#include "mozilla/UniquePtr.h"
-#include "mozilla/UniquePtrExtensions.h"
+#include "mozilla/Maybe.h"
 #include "nsExpirationTracker.h"
 #include "nsClassHashtable.h"
 #include "gfxUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 
 gfxAlphaBoxBlur::gfxAlphaBoxBlur()
+  : mData(nullptr)
 {
 }
 
 gfxAlphaBoxBlur::~gfxAlphaBoxBlur()
 {
-  mContext = nullptr;
+  if (mData) {
+    free(mData);
+  }
 }
 
-gfxContext*
+already_AddRefed<gfxContext>
 gfxAlphaBoxBlur::Init(const gfxRect& aRect,
                       const IntSize& aSpreadRadius,
                       const IntSize& aBlurRadius,
                       const gfxRect* aDirtyRect,
                       const gfxRect* aSkipRect)
 {
-    mozilla::gfx::Rect rect(Float(aRect.x), Float(aRect.y),
-                            Float(aRect.width), Float(aRect.height));
-    IntSize spreadRadius(aSpreadRadius.width, aSpreadRadius.height);
-    IntSize blurRadius(aBlurRadius.width, aBlurRadius.height);
-    UniquePtr<Rect> dirtyRect;
-    if (aDirtyRect) {
-      dirtyRect = MakeUnique<Rect>(Float(aDirtyRect->x),
-                                   Float(aDirtyRect->y),
-                                   Float(aDirtyRect->width),
-                                   Float(aDirtyRect->height));
-    }
-    UniquePtr<Rect> skipRect;
-    if (aSkipRect) {
-      skipRect = MakeUnique<Rect>(Float(aSkipRect->x),
-                                  Float(aSkipRect->y),
-                                  Float(aSkipRect->width),
-                                  Float(aSkipRect->height));
-    }
+  Maybe<Rect> dirtyRect = aDirtyRect ? Some(ToRect(*aDirtyRect)) : Nothing();
+  Maybe<Rect> skipRect = aSkipRect ? Some(ToRect(*aSkipRect)) : Nothing();
+  RefPtr<DrawTarget> dt =
+    InitDrawTarget(ToRect(aRect), aSpreadRadius, aBlurRadius,
+                   dirtyRect.ptrOr(nullptr), skipRect.ptrOr(nullptr));
+  if (!dt) {
+    return nullptr;
+  }
 
-    mBlur = MakeUnique<AlphaBoxBlur>(rect, spreadRadius, blurRadius, dirtyRect.get(), skipRect.get());
-    size_t blurDataSize = mBlur->GetSurfaceAllocationSize();
-    if (blurDataSize == 0)
-        return nullptr;
+  RefPtr<gfxContext> context = gfxContext::CreateOrNull(dt);
+  MOZ_ASSERT(context); // already checked for target above
+  context->SetMatrix(gfxMatrix::Translation(-mBlur.GetRect().TopLeft()));
+  return context.forget();
+}
 
-    IntSize size = mBlur->GetSize();
-
-    // Make an alpha-only surface to draw on. We will play with the data after
-    // everything is drawn to create a blur effect.
-    mData = MakeUniqueFallible<unsigned char[]>(blurDataSize);
-    if (!mData) {
-        return nullptr;
-    }
-    memset(mData.get(), 0, blurDataSize);
+already_AddRefed<DrawTarget>
+gfxAlphaBoxBlur::InitDrawTarget(const Rect& aRect,
+                                const IntSize& aSpreadRadius,
+                                const IntSize& aBlurRadius,
+                                const Rect* aDirtyRect,
+                                const Rect* aSkipRect)
+{
+  mBlur.Init(aRect, aSpreadRadius, aBlurRadius, aDirtyRect, aSkipRect);
+  size_t blurDataSize = mBlur.GetSurfaceAllocationSize();
+  if (blurDataSize == 0) {
+    return nullptr;
+  }
 
-    RefPtr<DrawTarget> dt =
-        gfxPlatform::CreateDrawTargetForData(mData.get(), size,
-                                             mBlur->GetStride(),
-                                             SurfaceFormat::A8);
-    if (!dt || !dt->IsValid()) {
-        return nullptr;
-    }
+  // Make an alpha-only surface to draw on. We will play with the data after
+  // everything is drawn to create a blur effect.
+  mData = static_cast<uint8_t*>(calloc(1, blurDataSize));
+  if (!mData) {
+    return nullptr;
+  }
 
-    IntRect irect = mBlur->GetRect();
-    gfxPoint topleft(irect.TopLeft().x, irect.TopLeft().y);
-
-    mContext = gfxContext::CreateOrNull(dt);
-    MOZ_ASSERT(mContext); // already checked for target above
-    mContext->SetMatrix(gfxMatrix::Translation(-topleft));
-
-    return mContext;
+  RefPtr<DrawTarget> dt =
+    gfxPlatform::CreateDrawTargetForData(mData,
+                                         mBlur.GetSize(),
+                                         mBlur.GetStride(),
+                                         SurfaceFormat::A8);
+  if (!dt || !dt->IsValid()) {
+    return nullptr;
+  }
+  dt->SetTransform(Matrix::Translation(-mBlur.GetRect().TopLeft()));
+  return dt.forget();
 }
 
 void
 DrawBlur(gfxContext* aDestinationCtx,
          SourceSurface* aBlur,
          const IntPoint& aTopLeft,
          const Rect* aDirtyRect)
 {
@@ -116,39 +113,39 @@ DrawBlur(gfxContext* aDestinationCtx,
     if (aDirtyRect) {
         dest->PopClip();
     }
 }
 
 already_AddRefed<SourceSurface>
 gfxAlphaBoxBlur::DoBlur(DrawTarget* aDT, IntPoint* aTopLeft)
 {
-    mBlur->Blur(mData.get());
+    mBlur.Blur(mData);
 
-    *aTopLeft = mBlur->GetRect().TopLeft();
+    *aTopLeft = mBlur.GetRect().TopLeft();
 
-    return aDT->CreateSourceSurfaceFromData(mData.get(),
-                                            mBlur->GetSize(),
-                                            mBlur->GetStride(),
+    return aDT->CreateSourceSurfaceFromData(mData,
+                                            mBlur.GetSize(),
+                                            mBlur.GetStride(),
                                             SurfaceFormat::A8);
 }
 
 void
 gfxAlphaBoxBlur::Paint(gfxContext* aDestinationCtx)
 {
-    if (!mContext)
+    if (!mData)
         return;
 
     DrawTarget *dest = aDestinationCtx->GetDrawTarget();
     if (!dest) {
       NS_WARNING("Blurring not supported for Thebes contexts!");
       return;
     }
 
-    Rect* dirtyRect = mBlur->GetDirtyRect();
+    Rect* dirtyRect = mBlur.GetDirtyRect();
 
     IntPoint topLeft;
     RefPtr<SourceSurface> mask = DoBlur(dest, &topLeft);
     if (!mask) {
       NS_ERROR("Failed to create mask!");
       return;
     }
 
@@ -170,51 +167,48 @@ struct BlurCacheKey : public PLDHashEntr
   IntSize mMinSize;
   IntSize mBlurRadius;
   Color mShadowColor;
   BackendType mBackend;
   RectCornerRadii mCornerRadii;
   bool mIsInset;
 
   // Only used for inset blurs
-  bool mHasBorderRadius;
   IntSize mInnerMinSize;
 
-  BlurCacheKey(IntSize aMinSize, IntSize aBlurRadius,
-               RectCornerRadii* aCornerRadii, const Color& aShadowColor,
+  BlurCacheKey(const IntSize& aMinSize, const IntSize& aBlurRadius,
+               const RectCornerRadii* aCornerRadii, const Color& aShadowColor,
                BackendType aBackendType)
     : BlurCacheKey(aMinSize, IntSize(0, 0),
                    aBlurRadius, aCornerRadii,
                    aShadowColor, false,
-                   false, aBackendType)
+                   aBackendType)
   {}
 
   explicit BlurCacheKey(const BlurCacheKey* aOther)
     : mMinSize(aOther->mMinSize)
     , mBlurRadius(aOther->mBlurRadius)
     , mShadowColor(aOther->mShadowColor)
     , mBackend(aOther->mBackend)
     , mCornerRadii(aOther->mCornerRadii)
     , mIsInset(aOther->mIsInset)
-    , mHasBorderRadius(aOther->mHasBorderRadius)
     , mInnerMinSize(aOther->mInnerMinSize)
   { }
 
-  explicit BlurCacheKey(IntSize aOuterMinSize, IntSize aInnerMinSize,
-                        IntSize aBlurRadius,
+  explicit BlurCacheKey(const IntSize& aOuterMinSize, const IntSize& aInnerMinSize,
+                        const IntSize& aBlurRadius,
                         const RectCornerRadii* aCornerRadii,
                         const Color& aShadowColor, bool aIsInset,
-                        bool aHasBorderRadius, BackendType aBackendType)
+                        BackendType aBackendType)
     : mMinSize(aOuterMinSize)
     , mBlurRadius(aBlurRadius)
     , mShadowColor(aShadowColor)
     , mBackend(aBackendType)
     , mCornerRadii(aCornerRadii ? *aCornerRadii : RectCornerRadii())
     , mIsInset(aIsInset)
-    , mHasBorderRadius(aHasBorderRadius)
     , mInnerMinSize(aInnerMinSize)
   { }
 
   static PLDHashNumber
   HashKey(const KeyTypePointer aKey)
   {
     PLDHashNumber hash = 0;
     hash = AddToHash(hash, aKey->mMinSize.width, aKey->mMinSize.height);
@@ -232,33 +226,31 @@ struct BlurCacheKey : public PLDHashEntr
     for (int i = 0; i < 4; i++) {
       hash = AddToHash(hash, aKey->mCornerRadii[i].width, aKey->mCornerRadii[i].height);
     }
 
     hash = AddToHash(hash, (uint32_t)aKey->mBackend);
 
     if (aKey->mIsInset) {
       hash = AddToHash(hash, aKey->mInnerMinSize.width, aKey->mInnerMinSize.height);
-      hash = AddToHash(hash, HashBytes(&aKey->mHasBorderRadius, sizeof(bool)));
     }
     return hash;
   }
 
   bool
   KeyEquals(KeyTypePointer aKey) const
   {
     if (aKey->mMinSize == mMinSize &&
         aKey->mBlurRadius == mBlurRadius &&
         aKey->mCornerRadii == mCornerRadii &&
         aKey->mShadowColor == mShadowColor &&
         aKey->mBackend == mBackend) {
 
       if (mIsInset) {
-        return (mHasBorderRadius == aKey->mHasBorderRadius) &&
-                (mInnerMinSize == aKey->mInnerMinSize);
+        return (mInnerMinSize == aKey->mInnerMinSize);
       }
 
       return true;
      }
 
      return false;
   }
 
@@ -269,35 +261,35 @@ struct BlurCacheKey : public PLDHashEntr
   }
 };
 
 /**
  * This class is what is cached. It need to be allocated in an object separated
  * to the cache entry to be able to be tracked by the nsExpirationTracker.
  * */
 struct BlurCacheData {
-  BlurCacheData(SourceSurface* aBlur, IntMargin aExtendDestBy, const BlurCacheKey& aKey)
+  BlurCacheData(SourceSurface* aBlur, const IntMargin& aBlurMargin, const BlurCacheKey& aKey)
     : mBlur(aBlur)
-    , mExtendDest(aExtendDestBy)
+    , mBlurMargin(aBlurMargin)
     , mKey(aKey)
   {}
 
   BlurCacheData(const BlurCacheData& aOther)
     : mBlur(aOther.mBlur)
-    , mExtendDest(aOther.mExtendDest)
+    , mBlurMargin(aOther.mBlurMargin)
     , mKey(aOther.mKey)
   { }
 
   nsExpirationState *GetExpirationState() {
     return &mExpirationState;
   }
 
   nsExpirationState mExpirationState;
   RefPtr<SourceSurface> mBlur;
-  IntMargin mExtendDest;
+  IntMargin mBlurMargin;
   BlurCacheKey mKey;
 };
 
 /**
  * This class implements a cache with no maximum size, that retains the
  * SourceSurfaces used to draw the blurs.
  *
  * An entry stays in the cache as long as it is used often.
@@ -311,46 +303,45 @@ class BlurCache final : public nsExpirat
     }
 
     virtual void NotifyExpired(BlurCacheData* aObject)
     {
       RemoveObject(aObject);
       mHashEntries.Remove(aObject->mKey);
     }
 
-    BlurCacheData* Lookup(const IntSize aMinSize,
+    BlurCacheData* Lookup(const IntSize& aMinSize,
                           const IntSize& aBlurRadius,
-                          RectCornerRadii* aCornerRadii,
+                          const RectCornerRadii* aCornerRadii,
                           const Color& aShadowColor,
                           BackendType aBackendType)
     {
       BlurCacheData* blur =
         mHashEntries.Get(BlurCacheKey(aMinSize, aBlurRadius,
                                       aCornerRadii, aShadowColor,
                                       aBackendType));
       if (blur) {
         MarkUsed(blur);
       }
 
       return blur;
     }
 
-    BlurCacheData* LookupInsetBoxShadow(const IntSize aOuterMinSize,
-                                        const IntSize aInnerMinSize,
+    BlurCacheData* LookupInsetBoxShadow(const IntSize& aOuterMinSize,
+                                        const IntSize& aInnerMinSize,
                                         const IntSize& aBlurRadius,
                                         const RectCornerRadii* aCornerRadii,
                                         const Color& aShadowColor,
-                                        const bool& aHasBorderRadius,
                                         BackendType aBackendType)
     {
       bool insetBoxShadow = true;
       BlurCacheKey key(aOuterMinSize, aInnerMinSize,
                        aBlurRadius, aCornerRadii,
                        aShadowColor, insetBoxShadow,
-                       aHasBorderRadius, aBackendType);
+                       aBackendType);
       BlurCacheData* blur = mHashEntries.Get(key);
       if (blur) {
         MarkUsed(blur);
       }
 
       return blur;
     }
 
@@ -378,196 +369,197 @@ class BlurCache final : public nsExpirat
      * https://bugzilla.mozilla.org/show_bug.cgi?id=761393#c47
      */
     nsClassHashtable<BlurCacheKey, BlurCacheData> mHashEntries;
 };
 
 static BlurCache* gBlurCache = nullptr;
 
 static IntSize
-ComputeMinSizeForShadowShape(RectCornerRadii* aCornerRadii,
-                             IntSize aBlurRadius,
-                             IntMargin& aSlice,
+ComputeMinSizeForShadowShape(const RectCornerRadii* aCornerRadii,
+                             const IntSize& aBlurRadius,
+                             IntMargin& aOutSlice,
                              const IntSize& aRectSize)
 {
-  float cornerWidth = 0;
-  float cornerHeight = 0;
+  Size cornerSize(0, 0);
   if (aCornerRadii) {
-    RectCornerRadii corners = *aCornerRadii;
-    for (size_t i = 0; i < 4; i++) {
-      cornerWidth = std::max(cornerWidth, corners[i].width);
-      cornerHeight = std::max(cornerHeight, corners[i].height);
+    const RectCornerRadii& corners = *aCornerRadii;
+    for (size_t i = 0; i < RectCorner::Count; i++) {
+      cornerSize.width = std::max(cornerSize.width, corners[i].width);
+      cornerSize.height = std::max(cornerSize.height, corners[i].height);
     }
   }
 
-  aSlice = IntMargin(ceil(cornerHeight) + aBlurRadius.height,
-                     ceil(cornerWidth) + aBlurRadius.width,
-                     ceil(cornerHeight) + aBlurRadius.height,
-                     ceil(cornerWidth) + aBlurRadius.width);
+  IntSize margin = IntSize::Ceil(cornerSize) + aBlurRadius;
+  aOutSlice = IntMargin(margin.height, margin.width,
+                        margin.height, margin.width);
 
-  IntSize minSize(aSlice.LeftRight() + 1,
-                      aSlice.TopBottom() + 1);
+  IntSize minSize(aOutSlice.LeftRight() + 1,
+                  aOutSlice.TopBottom() + 1);
 
   // If aRectSize is smaller than minSize, the border-image approach won't
   // work; there's no way to squeeze parts of the min box-shadow source
   // image such that the result looks correct. So we need to adjust minSize
   // in such a way that we can later draw it without stretching in the affected
   // dimension. We also need to adjust "slice" to ensure that we're not trying
   // to slice away more than we have.
   if (aRectSize.width < minSize.width) {
     minSize.width = aRectSize.width;
-    aSlice.left = 0;
-    aSlice.right = 0;
+    aOutSlice.left = 0;
+    aOutSlice.right = 0;
   }
   if (aRectSize.height < minSize.height) {
     minSize.height = aRectSize.height;
-    aSlice.top = 0;
-    aSlice.bottom = 0;
+    aOutSlice.top = 0;
+    aOutSlice.bottom = 0;
   }
 
-  MOZ_ASSERT(aSlice.LeftRight() <= minSize.width);
-  MOZ_ASSERT(aSlice.TopBottom() <= minSize.height);
+  MOZ_ASSERT(aOutSlice.LeftRight() <= minSize.width);
+  MOZ_ASSERT(aOutSlice.TopBottom() <= minSize.height);
   return minSize;
 }
 
 void
-CacheBlur(DrawTarget& aDT,
+CacheBlur(DrawTarget* aDT,
           const IntSize& aMinSize,
           const IntSize& aBlurRadius,
-          RectCornerRadii* aCornerRadii,
+          const RectCornerRadii* aCornerRadii,
           const Color& aShadowColor,
-          IntMargin aExtendDest,
+          const IntMargin& aBlurMargin,
           SourceSurface* aBoxShadow)
 {
-  BlurCacheKey key(aMinSize, aBlurRadius, aCornerRadii, aShadowColor, aDT.GetBackendType());
-  BlurCacheData* data = new BlurCacheData(aBoxShadow, aExtendDest, key);
+  BlurCacheKey key(aMinSize, aBlurRadius, aCornerRadii, aShadowColor, aDT->GetBackendType());
+  BlurCacheData* data = new BlurCacheData(aBoxShadow, aBlurMargin, key);
   if (!gBlurCache->RegisterEntry(data)) {
     delete data;
   }
 }
 
 // Blurs a small surface and creates the mask.
 static already_AddRefed<SourceSurface>
-CreateBlurMask(const IntSize& aMinSize,
-               RectCornerRadii* aCornerRadii,
-               IntSize aBlurRadius,
-               IntMargin& aExtendDestBy,
-               IntMargin& aSliceBorder,
-               DrawTarget& aDestDrawTarget)
+CreateBlurMask(DrawTarget* aDestDrawTarget,
+               const IntSize& aMinSize,
+               const RectCornerRadii* aCornerRadii,
+               const IntSize& aBlurRadius,
+               bool aMirrorCorners,
+               IntMargin& aOutBlurMargin)
 {
   gfxAlphaBoxBlur blur;
-  IntRect minRect(IntPoint(), aMinSize);
-
-  gfxContext* blurCtx = blur.Init(ThebesRect(Rect(minRect)), IntSize(),
-                                  aBlurRadius, nullptr, nullptr);
-
-  if (!blurCtx) {
+  Rect minRect(Point(0, 0), Size(aMinSize));
+  Rect blurRect(minRect);
+  // If mirroring corners, we only need to draw the top-left quadrant.
+  // Use ceil to preserve the remaining 1x1 middle area for minimized box
+  // shadows.
+  if (aMirrorCorners) {
+    blurRect.SizeTo(ceil(blurRect.width * 0.5f), ceil(blurRect.height * 0.5f));
+  }
+  IntSize zeroSpread(0, 0);
+  RefPtr<DrawTarget> blurDT =
+    blur.InitDrawTarget(blurRect, zeroSpread, aBlurRadius);
+  if (!blurDT) {
     return nullptr;
   }
 
-  DrawTarget* blurDT = blurCtx->GetDrawTarget();
   ColorPattern black(Color(0.f, 0.f, 0.f, 1.f));
 
   if (aCornerRadii) {
     RefPtr<Path> roundedRect =
-      MakePathForRoundedRect(*blurDT, Rect(minRect), *aCornerRadii);
+      MakePathForRoundedRect(*blurDT, minRect, *aCornerRadii);
     blurDT->Fill(roundedRect, black);
   } else {
-    blurDT->FillRect(Rect(minRect), black);
+    blurDT->FillRect(minRect, black);
   }
 
   IntPoint topLeft;
-  RefPtr<SourceSurface> result = blur.DoBlur(&aDestDrawTarget, &topLeft);
+  RefPtr<SourceSurface> result = blur.DoBlur(aDestDrawTarget, &topLeft);
   if (!result) {
     return nullptr;
   }
 
-  IntRect expandedMinRect(topLeft, result->GetSize());
-  aExtendDestBy = expandedMinRect - minRect;
-  aSliceBorder += aExtendDestBy;
-
-  MOZ_ASSERT(aSliceBorder.LeftRight() <= expandedMinRect.width);
-  MOZ_ASSERT(aSliceBorder.TopBottom() <= expandedMinRect.height);
+  // Since blurRect is at (0, 0), we can find the inflated margin by
+  // negating the new rect origin, which would have been negative if
+  // the rect was inflated.
+  aOutBlurMargin = IntMargin(-topLeft.y, -topLeft.x, -topLeft.y, -topLeft.x);
 
   return result.forget();
 }
 
 static already_AddRefed<SourceSurface>
-CreateBoxShadow(DrawTarget& aDestDT, SourceSurface* aBlurMask, const Color& aShadowColor)
+CreateBoxShadow(DrawTarget* aDestDT, SourceSurface* aBlurMask, const Color& aShadowColor)
 {
   IntSize blurredSize = aBlurMask->GetSize();
   RefPtr<DrawTarget> boxShadowDT =
-    Factory::CreateDrawTarget(aDestDT.GetBackendType(), blurredSize, SurfaceFormat::B8G8R8A8);
+    Factory::CreateDrawTarget(aDestDT->GetBackendType(), blurredSize, SurfaceFormat::B8G8R8A8);
 
   if (!boxShadowDT) {
     return nullptr;
   }
 
   ColorPattern shadowColor(ToDeviceColor(aShadowColor));
   boxShadowDT->MaskSurface(shadowColor, aBlurMask, Point(0, 0));
   return boxShadowDT->Snapshot();
 }
 
 static already_AddRefed<SourceSurface>
 GetBlur(gfxContext* aDestinationCtx,
         const IntSize& aRectSize,
         const IntSize& aBlurRadius,
-        RectCornerRadii* aCornerRadii,
+        const RectCornerRadii* aCornerRadii,
         const Color& aShadowColor,
-        IntMargin& aExtendDestBy,
-        IntMargin& aSlice)
+        bool aMirrorCorners,
+        IntMargin& aOutBlurMargin,
+        IntMargin& aOutSlice,
+        IntSize& aOutMinSize)
 {
   if (!gBlurCache) {
     gBlurCache = new BlurCache();
   }
 
   IntSize minSize =
-    ComputeMinSizeForShadowShape(aCornerRadii, aBlurRadius, aSlice, aRectSize);
+    ComputeMinSizeForShadowShape(aCornerRadii, aBlurRadius, aOutSlice, aRectSize);
 
   // We can get seams using the min size rect when drawing to the destination rect
   // if we have a non-pixel aligned destination transformation. In those cases,
   // fallback to just rendering the destination rect.
   Matrix destMatrix = ToMatrix(aDestinationCtx->CurrentMatrix());
   bool useDestRect = !destMatrix.IsRectilinear() || destMatrix.HasNonIntegerTranslation();
   if (useDestRect) {
     minSize = aRectSize;
   }
+  aOutMinSize = minSize;
 
-  DrawTarget& destDT = *aDestinationCtx->GetDrawTarget();
+  DrawTarget* destDT = aDestinationCtx->GetDrawTarget();
 
-  BlurCacheData* cached = gBlurCache->Lookup(minSize, aBlurRadius,
-                                             aCornerRadii, aShadowColor,
-                                             destDT.GetBackendType());
-  if (cached && !useDestRect) {
-    // See CreateBlurMask() for these values
-    aExtendDestBy = cached->mExtendDest;
-    aSlice = aSlice + aExtendDestBy;
-    RefPtr<SourceSurface> blur = cached->mBlur;
-    return blur.forget();
+  if (!useDestRect) {
+    BlurCacheData* cached = gBlurCache->Lookup(minSize, aBlurRadius,
+                                               aCornerRadii, aShadowColor,
+                                               destDT->GetBackendType());
+    if (cached) {
+      // See CreateBlurMask() for these values
+      aOutBlurMargin = cached->mBlurMargin;
+      RefPtr<SourceSurface> blur = cached->mBlur;
+      return blur.forget();
+    }
   }
 
   RefPtr<SourceSurface> blurMask =
-    CreateBlurMask(minSize, aCornerRadii, aBlurRadius, aExtendDestBy, aSlice,
-                   destDT);
-
+    CreateBlurMask(destDT, minSize, aCornerRadii, aBlurRadius,
+                   aMirrorCorners, aOutBlurMargin);
   if (!blurMask) {
     return nullptr;
   }
 
   RefPtr<SourceSurface> boxShadow = CreateBoxShadow(destDT, blurMask, aShadowColor);
   if (!boxShadow) {
     return nullptr;
   }
 
-  if (useDestRect) {
-    // Since we're just going to paint the actual rect to the destination
-    aSlice.SizeTo(0, 0, 0, 0);
-  } else {
+  if (!useDestRect) {
     CacheBlur(destDT, minSize, aBlurRadius, aCornerRadii, aShadowColor,
-              aExtendDestBy, boxShadow);
+              aOutBlurMargin, boxShadow);
   }
   return boxShadow.forget();
 }
 
 void
 gfxAlphaBoxBlur::ShutdownBlurCache()
 {
   delete gBlurCache;
@@ -575,59 +567,66 @@ gfxAlphaBoxBlur::ShutdownBlurCache()
 }
 
 static Rect
 RectWithEdgesTRBL(Float aTop, Float aRight, Float aBottom, Float aLeft)
 {
   return Rect(aLeft, aTop, aRight - aLeft, aBottom - aTop);
 }
 
+static bool
+ShouldStretchSurface(DrawTarget* aDT, SourceSurface* aSurface)
+{
+  // Use stretching if possible, since it leads to less seams when the
+  // destination is transformed. However, don't do this if we're using cairo,
+  // because if cairo is using pixman it won't render anything for large
+  // stretch factors because pixman's internal fixed point precision is not
+  // high enough to handle those scale factors.
+  // Calling FillRect on a D2D backend with a repeating pattern is much slower
+  // than DrawSurface, so special case the D2D backend here.
+  return (!aDT->GetTransform().IsRectilinear() &&
+          aDT->GetBackendType() != BackendType::CAIRO) ||
+         (aDT->GetBackendType() == BackendType::DIRECT2D1_1);
+}
+
 static void
-RepeatOrStretchSurface(DrawTarget& aDT, SourceSurface* aSurface,
-                       const Rect& aDest, const Rect& aSrc, Rect& aSkipRect)
+RepeatOrStretchSurface(DrawTarget* aDT, SourceSurface* aSurface,
+                       const Rect& aDest, const Rect& aSrc, const Rect& aSkipRect)
 {
   if (aSkipRect.Contains(aDest)) {
     return;
   }
 
-  if ((!aDT.GetTransform().IsRectilinear() &&
-       aDT.GetBackendType() != BackendType::CAIRO) ||
-      (aDT.GetBackendType() == BackendType::DIRECT2D1_1)) {
-    // Use stretching if possible, since it leads to less seams when the
-    // destination is transformed. However, don't do this if we're using cairo,
-    // because if cairo is using pixman it won't render anything for large
-    // stretch factors because pixman's internal fixed point precision is not
-    // high enough to handle those scale factors.
-    // Calling FillRect on a D2D backend with a repeating pattern is much slower
-    // than DrawSurface, so special case the D2D backend here.
-    aDT.DrawSurface(aSurface, aDest, aSrc);
+  if (ShouldStretchSurface(aDT, aSurface)) {
+    aDT->DrawSurface(aSurface, aDest, aSrc);
     return;
   }
 
   SurfacePattern pattern(aSurface, ExtendMode::REPEAT,
                          Matrix::Translation(aDest.TopLeft() - aSrc.TopLeft()),
                          SamplingFilter::GOOD, RoundedToInt(aSrc));
-  aDT.FillRect(aDest, pattern);
+  aDT->FillRect(aDest, pattern);
 }
 
 static void
-DrawCorner(DrawTarget& aDT, SourceSurface* aSurface,
-           const Rect& aDest, const Rect& aSrc, Rect& aSkipRect)
+DrawCorner(DrawTarget* aDT, SourceSurface* aSurface,
+           const Rect& aDest, const Rect& aSrc, const Rect& aSkipRect)
 {
   if (aSkipRect.Contains(aDest)) {
     return;
   }
 
-  aDT.DrawSurface(aSurface, aDest, aSrc);
+  aDT->DrawSurface(aSurface, aDest, aSrc);
 }
 
 static void
-DrawBoxShadows(DrawTarget& aDestDrawTarget, SourceSurface* aSourceBlur,
-               Rect aDstOuter, Rect aDstInner, Rect aSrcOuter, Rect aSrcInner,
-               Rect aSkipRect)
+DrawMinBoxShadow(DrawTarget* aDestDrawTarget, SourceSurface* aSourceBlur,
+                 const Rect& aDstOuter, const Rect& aDstInner,
+                 const Rect& aSrcOuter, const Rect& aSrcInner,
+                 const Rect& aSkipRect, bool aMiddle = false)
 {
   // Corners: top left, top right, bottom left, bottom right
   DrawCorner(aDestDrawTarget, aSourceBlur,
              RectWithEdgesTRBL(aDstOuter.Y(), aDstInner.X(),
                                aDstInner.Y(), aDstOuter.X()),
              RectWithEdgesTRBL(aSrcOuter.Y(), aSrcInner.X(),
                                aSrcInner.Y(), aSrcOuter.X()),
                                aSkipRect);
@@ -674,84 +673,272 @@ DrawBoxShadows(DrawTarget& aDestDrawTarg
                                            aSrcInner.YMost(), aSrcInner.XMost()),
                          aSkipRect);
   RepeatOrStretchSurface(aDestDrawTarget, aSourceBlur,
                          RectWithEdgesTRBL(aDstInner.YMost(), aDstInner.XMost(),
                                            aDstOuter.YMost(), aDstInner.X()),
                          RectWithEdgesTRBL(aSrcInner.YMost(), aSrcInner.XMost(),
                                            aSrcOuter.YMost(), aSrcInner.X()),
                          aSkipRect);
+
+  // Middle part
+  if (aMiddle) {
+    RepeatOrStretchSurface(aDestDrawTarget, aSourceBlur,
+                           RectWithEdgesTRBL(aDstInner.Y(), aDstInner.XMost(),
+                                             aDstInner.YMost(), aDstInner.X()),
+                           RectWithEdgesTRBL(aSrcInner.Y(), aSrcInner.XMost(),
+                                             aSrcInner.YMost(), aSrcInner.X()),
+                           aSkipRect);
+  }
+}
+
+static void
+DrawMirroredRect(DrawTarget* aDT,
+                 SourceSurface* aSurface,
+                 const Rect& aDest, const Point& aSrc,
+                 Float aScaleX, Float aScaleY)
+{
+  SurfacePattern pattern(aSurface, ExtendMode::CLAMP,
+                         Matrix::Scaling(aScaleX, aScaleY)
+                           .PreTranslate(-aSrc)
+                           .PostTranslate(
+                             aScaleX < 0 ? aDest.XMost() : aDest.x,
+                             aScaleY < 0 ? aDest.YMost() : aDest.y));
+  aDT->FillRect(aDest, pattern);
+}
+
+static void
+DrawMirroredBoxShadow(DrawTarget* aDT,
+                      SourceSurface* aSurface,
+                      const Rect& aDestRect)
+{
+  Point center(ceil(aDestRect.x + aDestRect.width / 2),
+               ceil(aDestRect.y + aDestRect.height / 2));
+  Rect topLeft(aDestRect.x, aDestRect.y,
+               center.x - aDestRect.x,
+               center.y - aDestRect.y);
+  Rect bottomRight(topLeft.BottomRight(), aDestRect.Size() - topLeft.Size());
+  Rect topRight(bottomRight.x, topLeft.y, bottomRight.width, topLeft.height);
+  Rect bottomLeft(topLeft.x, bottomRight.y, topLeft.width, bottomRight.height);
+  DrawMirroredRect(aDT, aSurface, topLeft, Point(), 1, 1);
+  DrawMirroredRect(aDT, aSurface, topRight, Point(), -1, 1);
+  DrawMirroredRect(aDT, aSurface, bottomLeft, Point(), 1, -1);
+  DrawMirroredRect(aDT, aSurface, bottomRight, Point(), -1, -1);
+}
+
+static void
+DrawMirroredCorner(DrawTarget* aDT, SourceSurface* aSurface,
+                   const Rect& aDest, const Point& aSrc,
+                   const Rect& aSkipRect, Float aScaleX, Float aScaleY)
+{
+  if (aSkipRect.Contains(aDest)) {
+    return;
+  }
+
+  DrawMirroredRect(aDT, aSurface, aDest, aSrc, aScaleX, aScaleY);
+}
+
+static void
+RepeatOrStretchMirroredSurface(DrawTarget* aDT, SourceSurface* aSurface,
+                               const Rect& aDest, const Rect& aSrc,
+                               const Rect& aSkipRect, Float aScaleX, Float aScaleY)
+{
+  if (aSkipRect.Contains(aDest)) {
+    return;
+  }
+
+  if (ShouldStretchSurface(aDT, aSurface)) {
+    aScaleX *= aDest.width / aSrc.width;
+    aScaleY *= aDest.height / aSrc.height;
+    DrawMirroredRect(aDT, aSurface, aDest, aSrc.TopLeft(), aScaleX, aScaleY);
+    return;
+  }
+
+  SurfacePattern pattern(aSurface, ExtendMode::REPEAT,
+                         Matrix::Scaling(aScaleX, aScaleY)
+                           .PreTranslate(-aSrc.TopLeft())
+                           .PostTranslate(
+                             aScaleX < 0 ? aDest.XMost() : aDest.x,
+                             aScaleY < 0 ? aDest.YMost() : aDest.y),
+                         SamplingFilter::GOOD, RoundedToInt(aSrc));
+  aDT->FillRect(aDest, pattern);
+}
+
+static void
+DrawMirroredMinBoxShadow(DrawTarget* aDestDrawTarget, SourceSurface* aSourceBlur,
+                         const Rect& aDstOuter, const Rect& aDstInner,
+                         const Rect& aSrcOuter, const Rect& aSrcInner,
+                         const Rect& aSkipRect, bool aMiddle = false)
+{
+  // Corners: top left, top right, bottom left, bottom right
+  // Compute quadrant bounds and then clip them to corners along
+  // dimensions where we need to stretch from min size.
+  Point center(ceil(aDstOuter.x + aDstOuter.width / 2),
+               ceil(aDstOuter.y + aDstOuter.height / 2));
+  Rect topLeft(aDstOuter.x, aDstOuter.y,
+               center.x - aDstOuter.x,
+               center.y - aDstOuter.y);
+  Rect bottomRight(topLeft.BottomRight(), aDstOuter.Size() - topLeft.Size());
+  Rect topRight(bottomRight.x, topLeft.y, bottomRight.width, topLeft.height);
+  Rect bottomLeft(topLeft.x, bottomRight.y, topLeft.width, bottomRight.height);
+
+  // Check if the middle part has been minimized along each dimension.
+  // If so, those will be strecthed/drawn separately and need to be clipped out.
+  if (aSrcInner.width == 1) {
+    topLeft.SetRightEdge(aDstInner.x);
+    topRight.SetLeftEdge(aDstInner.XMost());
+    bottomLeft.SetRightEdge(aDstInner.x);
+    bottomRight.SetLeftEdge(aDstInner.XMost());
+  }
+  if (aSrcInner.height == 1) {
+    topLeft.SetBottomEdge(aDstInner.y);
+    topRight.SetBottomEdge(aDstInner.y);
+    bottomLeft.SetTopEdge(aDstInner.YMost());
+    bottomRight.SetTopEdge(aDstInner.YMost());
+  }
+
+  DrawMirroredCorner(aDestDrawTarget, aSourceBlur, topLeft,
+                     aSrcOuter.TopLeft(), aSkipRect, 1, 1);
+  DrawMirroredCorner(aDestDrawTarget, aSourceBlur, topRight,
+                     aSrcOuter.TopLeft(), aSkipRect, -1, 1);
+  DrawMirroredCorner(aDestDrawTarget, aSourceBlur, bottomLeft,
+                     aSrcOuter.TopLeft(), aSkipRect, 1, -1);
+  DrawMirroredCorner(aDestDrawTarget, aSourceBlur, bottomRight,
+                     aSrcOuter.TopLeft(), aSkipRect, -1, -1);
+
+  // Edges: top, bottom, left, right
+  // Draw middle edges where they need to be stretched. The top and left
+  // sections that are part of the top-left quadrant will be mirrored to
+  // the bottom and right sections, respectively.
+  if (aSrcInner.width == 1) {
+    Rect dstTop = RectWithEdgesTRBL(aDstOuter.Y(), aDstInner.XMost(),
+                                    aDstInner.Y(), aDstInner.X());
+    Rect srcTop = RectWithEdgesTRBL(aSrcOuter.Y(), aSrcInner.XMost(),
+                                    aSrcInner.Y(), aSrcInner.X());
+    Rect dstBottom = RectWithEdgesTRBL(aDstInner.YMost(), aDstInner.XMost(),
+                                       aDstOuter.YMost(), aDstInner.X());
+    Rect srcBottom = RectWithEdgesTRBL(aSrcOuter.Y(), aSrcInner.XMost(),
+                                       aSrcInner.Y(), aSrcInner.X());
+    // If we only need to stretch along the X axis and we're drawing
+    // the middle section, just sample all the way to the center of the
+    // source on the Y axis to avoid extra draw calls.
+    if (aMiddle && aSrcInner.height != 1) {
+      dstTop.SetBottomEdge(center.y);
+      srcTop.height = dstTop.height;
+      dstBottom.SetTopEdge(dstTop.YMost());
+      srcBottom.height = dstBottom.height;
+    }
+    RepeatOrStretchMirroredSurface(aDestDrawTarget, aSourceBlur,
+                                   dstTop, srcTop, aSkipRect, 1, 1);
+    RepeatOrStretchMirroredSurface(aDestDrawTarget, aSourceBlur,
+                                   dstBottom, srcBottom, aSkipRect, 1, -1);
+  }
+
+  if (aSrcInner.height == 1) {
+    Rect dstLeft = RectWithEdgesTRBL(aDstInner.Y(), aDstInner.X(),
+                                     aDstInner.YMost(), aDstOuter.X());
+    Rect srcLeft = RectWithEdgesTRBL(aSrcInner.Y(), aSrcInner.X(),
+                                     aSrcInner.YMost(), aSrcOuter.X());
+    Rect dstRight = RectWithEdgesTRBL(aDstInner.Y(), aDstOuter.XMost(),
+                                      aDstInner.YMost(), aDstInner.XMost());
+    Rect srcRight = RectWithEdgesTRBL(aSrcInner.Y(), aSrcInner.X(),
+                                      aSrcInner.YMost(), aSrcOuter.X());
+    // Only stretching on Y axis, so sample source to the center of the X axis.
+    if (aMiddle && aSrcInner.width != 1) {
+      dstLeft.SetRightEdge(center.x);
+      srcLeft.width = dstLeft.width;
+      dstRight.SetLeftEdge(dstLeft.XMost());
+      srcRight.width = dstRight.width;
+    }
+    RepeatOrStretchMirroredSurface(aDestDrawTarget, aSourceBlur,
+                                   dstLeft, srcLeft, aSkipRect, 1, 1);
+    RepeatOrStretchMirroredSurface(aDestDrawTarget, aSourceBlur,
+                                   dstRight, srcRight, aSkipRect, -1, 1);
+  }
+
+  // If we need to stretch along both dimensions, then the middle part
+  // must be drawn separately.
+  if (aMiddle && aSrcInner.width == 1 && aSrcInner.height == 1) {
+    RepeatOrStretchSurface(aDestDrawTarget, aSourceBlur,
+                           RectWithEdgesTRBL(aDstInner.Y(), aDstInner.XMost(),
+                                             aDstInner.YMost(), aDstInner.X()),
+                           RectWithEdgesTRBL(aSrcInner.Y(), aSrcInner.XMost(),
+                                             aSrcInner.YMost(), aSrcInner.X()),
+                           aSkipRect);
+  }
 }
 
 /***
  * We draw a blurred a rectangle by only blurring a smaller rectangle and
  * splitting the rectangle into 9 parts.
  * First, a small minimum source rect is calculated and used to create a blur
  * mask since the actual blurring itself is expensive. Next, we use the mask
  * with the given shadow color to create a minimally-sized box shadow of the
  * right color. Finally, we cut out the 9 parts from the box-shadow source and
  * paint each part in the right place, stretching the non-corner parts to fill
  * the space between the corners.
  */
 
 /* static */ void
 gfxAlphaBoxBlur::BlurRectangle(gfxContext* aDestinationCtx,
                                const gfxRect& aRect,
-                               RectCornerRadii* aCornerRadii,
+                               const RectCornerRadii* aCornerRadii,
                                const gfxPoint& aBlurStdDev,
                                const Color& aShadowColor,
                                const gfxRect& aDirtyRect,
                                const gfxRect& aSkipRect)
 {
   IntSize blurRadius = CalculateBlurRadius(aBlurStdDev);
+  bool mirrorCorners = !aCornerRadii || aCornerRadii->AreRadiiSame();
 
   IntRect rect = RoundedToInt(ToRect(aRect));
-  IntMargin extendDestBy;
+  IntMargin blurMargin;
   IntMargin slice;
-
+  IntSize minSize;
   RefPtr<SourceSurface> boxShadow = GetBlur(aDestinationCtx,
                                             rect.Size(), blurRadius,
-                                            aCornerRadii, aShadowColor,
-                                            extendDestBy, slice);
+                                            aCornerRadii, aShadowColor, mirrorCorners,
+                                            blurMargin, slice, minSize);
   if (!boxShadow) {
     return;
   }
 
-  DrawTarget& destDrawTarget = *aDestinationCtx->GetDrawTarget();
-  destDrawTarget.PushClipRect(ToRect(aDirtyRect));
+  DrawTarget* destDrawTarget = aDestinationCtx->GetDrawTarget();
+  destDrawTarget->PushClipRect(ToRect(aDirtyRect));
 
   // Copy the right parts from boxShadow into destDrawTarget. The middle parts
   // will be stretched, border-image style.
 
-  Rect srcOuter(Point(), Size(boxShadow->GetSize()));
-  Rect srcInner = srcOuter;
+  Rect srcOuter(Point(blurMargin.left, blurMargin.top), Size(minSize));
+  Rect srcInner(srcOuter);
+  srcOuter.Inflate(Margin(blurMargin));
   srcInner.Deflate(Margin(slice));
 
-  rect.Inflate(extendDestBy);
   Rect dstOuter(rect);
   Rect dstInner(rect);
+  dstOuter.Inflate(Margin(blurMargin));
   dstInner.Deflate(Margin(slice));
 
   Rect skipRect = ToRect(aSkipRect);
 
-  if (srcInner.IsEqualInterior(srcOuter)) {
-    MOZ_ASSERT(dstInner.IsEqualInterior(dstOuter));
+  if (minSize == rect.Size()) {
     // The target rect is smaller than the minimal size so just draw the surface
-    destDrawTarget.DrawSurface(boxShadow, dstInner, srcInner);
+    if (mirrorCorners) {
+      DrawMirroredBoxShadow(destDrawTarget, boxShadow, dstOuter);
+    } else {
+      destDrawTarget->DrawSurface(boxShadow, dstOuter, srcOuter);
+    }
   } else {
-    DrawBoxShadows(destDrawTarget, boxShadow, dstOuter, dstInner,
-                   srcOuter, srcInner, skipRect);
-
-    // Middle part
-    RepeatOrStretchSurface(destDrawTarget, boxShadow,
-                           RectWithEdgesTRBL(dstInner.Y(), dstInner.XMost(),
-                                             dstInner.YMost(), dstInner.X()),
-                           RectWithEdgesTRBL(srcInner.Y(), srcInner.XMost(),
-                                             srcInner.YMost(), srcInner.X()),
-                           skipRect);
+    if (mirrorCorners) {
+      DrawMirroredMinBoxShadow(destDrawTarget, boxShadow, dstOuter, dstInner,
+                               srcOuter, srcInner, skipRect, true);
+    } else {
+      DrawMinBoxShadow(destDrawTarget, boxShadow, dstOuter, dstInner,
+                       srcOuter, srcInner, skipRect, true);
+    }
   }
 
   // A note about anti-aliasing and seems between adjacent parts:
   // We don't explicitly disable anti-aliasing in the DrawSurface calls above,
   // so if there's a transform on destDrawTarget that is not pixel-aligned,
   // there will be seams between adjacent parts of the box-shadow. It's hard to
   // avoid those without the use of an intermediate surface.
   // You might think that we could avoid those by just turning of AA, but there
@@ -761,23 +948,23 @@ gfxAlphaBoxBlur::BlurRectangle(gfxContex
   // we have a way to say "Please anti-alias the clip, but don't antialias the
   // destination rect of the DrawSurface call".
   // On OS X there is an additional problem with turning off AA: CoreGraphics
   // will not just fill the pixels that have their pixel center inside the
   // filled shape. Instead, it will fill all the pixels which are partially
   // covered by the shape. So for pixels on the edge between two adjacent parts,
   // all those pixels will be painted to by both parts, which looks very bad.
 
-  destDrawTarget.PopClip();
+  destDrawTarget->PopClip();
 }
 
 static already_AddRefed<Path>
 GetBoxShadowInsetPath(DrawTarget* aDrawTarget,
                       const Rect aOuterRect, const Rect aInnerRect,
-                      const bool aHasBorderRadius, const RectCornerRadii& aInnerClipRadii)
+                      const RectCornerRadii* aInnerClipRadii)
 {
   /***
    * We create an inset path by having two rects.
    *
    *  -----------------------
    *  |  ________________   |
    *  | |                |  |
    *  | |                |  |
@@ -786,147 +973,143 @@ GetBoxShadowInsetPath(DrawTarget* aDrawT
    *
    * The outer rect and the inside rect. The path
    * creates a frame around the content where we draw the inset shadow.
    */
   RefPtr<PathBuilder> builder =
     aDrawTarget->CreatePathBuilder(FillRule::FILL_EVEN_ODD);
   AppendRectToPath(builder, aOuterRect, true);
 
-  if (aHasBorderRadius) {
-    AppendRoundedRectToPath(builder, aInnerRect, aInnerClipRadii, false);
+  if (aInnerClipRadii) {
+    AppendRoundedRectToPath(builder, aInnerRect, *aInnerClipRadii, false);
   } else {
     AppendRectToPath(builder, aInnerRect, false);
   }
   return builder->Finish();
 }
 
 static void
 FillDestinationPath(gfxContext* aDestinationCtx,
-                    const Rect aDestinationRect,
-                    const Rect aShadowClipRect,
+                    const Rect& aDestinationRect,
+                    const Rect& aShadowClipRect,
                     const Color& aShadowColor,
-                    const bool aHasBorderRadius,
-                    const RectCornerRadii& aInnerClipRadii)
+                    const RectCornerRadii* aInnerClipRadii = nullptr)
 {
   // When there is no blur radius, fill the path onto the destination
   // surface.
   aDestinationCtx->SetColor(aShadowColor);
   DrawTarget* destDrawTarget = aDestinationCtx->GetDrawTarget();
   RefPtr<Path> shadowPath = GetBoxShadowInsetPath(destDrawTarget, aDestinationRect,
-                                                  aShadowClipRect, aHasBorderRadius,
-                                                  aInnerClipRadii);
+                                                  aShadowClipRect, aInnerClipRadii);
 
   aDestinationCtx->SetPath(shadowPath);
   aDestinationCtx->Fill();
 }
 
 static void
-CacheInsetBlur(const IntSize aMinOuterSize,
-               const IntSize aMinInnerSize,
+CacheInsetBlur(const IntSize& aMinOuterSize,
+               const IntSize& aMinInnerSize,
                const IntSize& aBlurRadius,
                const RectCornerRadii* aCornerRadii,
                const Color& aShadowColor,
-               const bool& aHasBorderRadius,
                BackendType aBackendType,
                SourceSurface* aBoxShadow)
 {
   bool isInsetBlur = true;
   BlurCacheKey key(aMinOuterSize, aMinInnerSize,
                    aBlurRadius, aCornerRadii,
                    aShadowColor, isInsetBlur,
-                   aHasBorderRadius, aBackendType);
-  IntMargin extendDestBy(0, 0, 0, 0);
-  BlurCacheData* data = new BlurCacheData(aBoxShadow, extendDestBy, key);
+                   aBackendType);
+  IntMargin blurMargin(0, 0, 0, 0);
+  BlurCacheData* data = new BlurCacheData(aBoxShadow, blurMargin, key);
   if (!gBlurCache->RegisterEntry(data)) {
     delete data;
   }
 }
 
-already_AddRefed<mozilla::gfx::SourceSurface>
-gfxAlphaBoxBlur::GetInsetBlur(const mozilla::gfx::Rect aOuterRect,
-                              const mozilla::gfx::Rect aWhitespaceRect,
-                              const bool aIsDestRect,
-                              const mozilla::gfx::Color& aShadowColor,
-                              const mozilla::gfx::IntSize& aBlurRadius,
-                              const bool aHasBorderRadius,
-                              const RectCornerRadii& aInnerClipRadii,
-                              DrawTarget* aDestDrawTarget)
+already_AddRefed<SourceSurface>
+gfxAlphaBoxBlur::GetInsetBlur(const Rect& aOuterRect,
+                              const Rect& aWhitespaceRect,
+                              bool aIsDestRect,
+                              const Color& aShadowColor,
+                              const IntSize& aBlurRadius,
+                              const RectCornerRadii* aInnerClipRadii,
+                              DrawTarget* aDestDrawTarget,
+                              bool aMirrorCorners)
 {
   if (!gBlurCache) {
     gBlurCache = new BlurCache();
   }
 
-  IntSize outerSize((int)aOuterRect.width, (int)aOuterRect.height);
-  IntSize whitespaceSize((int)aWhitespaceRect.width, (int)aWhitespaceRect.height);
-  BlurCacheData* cached =
+  IntSize outerSize = IntSize::Truncate(aOuterRect.Size());
+  IntSize whitespaceSize = IntSize::Truncate(aWhitespaceRect.Size());
+  if (!aIsDestRect) {
+    BlurCacheData* cached =
       gBlurCache->LookupInsetBoxShadow(outerSize, whitespaceSize,
-                                       aBlurRadius, &aInnerClipRadii,
-                                       aShadowColor, aHasBorderRadius,
-                                       aDestDrawTarget->GetBackendType());
-
-  if (cached && !aIsDestRect) {
-    // So we don't forget the actual cached blur
-    RefPtr<SourceSurface> cachedBlur = cached->mBlur;
-    return cachedBlur.forget();
+                                       aBlurRadius, aInnerClipRadii,
+                                       aShadowColor, aDestDrawTarget->GetBackendType());
+    if (cached) {
+      // So we don't forget the actual cached blur
+      RefPtr<SourceSurface> cachedBlur = cached->mBlur;
+      return cachedBlur.forget();
+    }
   }
 
   // If we can do a min rect, the whitespace rect will be expanded in Init to
   // aOuterRect.
   Rect blurRect = aIsDestRect ? aOuterRect : aWhitespaceRect;
+  // If mirroring corners, we only need to draw the top-left quadrant.
+  // Use ceil to preserve the remaining 1x1 middle area for minimized box
+  // shadows.
+  if (aMirrorCorners) {
+    blurRect.SizeTo(ceil(blurRect.width * 0.5f), ceil(blurRect.height * 0.5f));
+  }
   IntSize zeroSpread(0, 0);
-  gfxContext* minGfxContext = Init(ThebesRect(blurRect),
-                                   zeroSpread, aBlurRadius,
-                                   nullptr, nullptr);
-  if (!minGfxContext) {
+  RefPtr<DrawTarget> minDrawTarget =
+    InitDrawTarget(blurRect, zeroSpread, aBlurRadius);
+  if (!minDrawTarget) {
     return nullptr;
   }
 
-  // This is really annoying. When we create the AlphaBoxBlur, the gfxContext
+  // This is really annoying. When we create the AlphaBoxBlur, the DrawTarget
   // has a translation applied to it that is the topLeft point. This is actually
   // the rect we gave it plus the blur radius. The rects we give this for the outer
   // and whitespace rects are based at (0, 0). We could either translate those rects
   // when we don't have a destination rect or ignore the translation when using
   // the dest rect. The dest rects layout gives us expect this translation.
   if (!aIsDestRect) {
-    minGfxContext->SetMatrix(gfxMatrix());
+    minDrawTarget->SetTransform(Matrix());
   }
 
-  DrawTarget* minDrawTarget = minGfxContext->GetDrawTarget();
-
   // Fill in the path between the inside white space / outer rects
   // NOT the inner frame
   RefPtr<Path> maskPath =
     GetBoxShadowInsetPath(minDrawTarget, aOuterRect,
-                          aWhitespaceRect, aHasBorderRadius,
-                          aInnerClipRadii);
+                          aWhitespaceRect, aInnerClipRadii);
 
-  Color black(0.f, 0.f, 0.f, 1.f);
-  minGfxContext->SetColor(black);
-  minGfxContext->SetPath(maskPath);
-  minGfxContext->Fill();
+  ColorPattern black(Color(0.f, 0.f, 0.f, 1.f));
+  minDrawTarget->Fill(maskPath, black);
 
   // Create the A8 mask
   IntPoint topLeft;
   RefPtr<SourceSurface> minMask = DoBlur(minDrawTarget, &topLeft);
   if (!minMask) {
     return nullptr;
   }
 
   // Fill in with the color we actually wanted
-  RefPtr<SourceSurface> minInsetBlur = CreateBoxShadow(*aDestDrawTarget, minMask, aShadowColor);
+  RefPtr<SourceSurface> minInsetBlur = CreateBoxShadow(aDestDrawTarget, minMask, aShadowColor);
   if (!minInsetBlur) {
     return nullptr;
   }
 
   if (!aIsDestRect) {
     CacheInsetBlur(outerSize, whitespaceSize,
-                   aBlurRadius, &aInnerClipRadii,
-                   aShadowColor, aHasBorderRadius,
-                   aDestDrawTarget->GetBackendType(),
+                   aBlurRadius, aInnerClipRadii,
+                   aShadowColor, aDestDrawTarget->GetBackendType(),
                    minInsetBlur);
   }
 
   return minInsetBlur.forget();
 }
 
 /***
  * We create our minimal rect with 2 rects.
@@ -946,46 +1129,45 @@ gfxAlphaBoxBlur::GetInsetBlur(const mozi
  * |          |             |       |
  * |      (2) |    (1)      |  (2)  |
  * |       B  |     W       |   B   |
  * |          |             |       |
  * |          |             |       |
  * |          |                     |
  * |________________________________|
  */
-static void GetBlurMargins(const bool aHasBorderRadius,
-                           const RectCornerRadii& aInnerClipRadii,
-                           const IntSize aBlurRadius,
+static void GetBlurMargins(const RectCornerRadii* aInnerClipRadii,
+                           const IntSize& aBlurRadius,
                            Margin& aOutBlurMargin,
                            Margin& aOutInnerMargin)
 {
-  float cornerWidth = 0;
-  float cornerHeight = 0;
-  if (aHasBorderRadius) {
-    for (size_t i = 0; i < 4; i++) {
-      cornerWidth = std::max(cornerWidth, aInnerClipRadii[i].width);
-      cornerHeight = std::max(cornerHeight, aInnerClipRadii[i].height);
+  Size cornerSize(0, 0);
+  if (aInnerClipRadii) {
+    const RectCornerRadii& corners = *aInnerClipRadii;
+    for (size_t i = 0; i < RectCorner::Count; i++) {
+      cornerSize.width = std::max(cornerSize.width, corners[i].width);
+      cornerSize.height = std::max(cornerSize.height, corners[i].height);
     }
   }
 
   // Only the inside whitespace size cares about the border radius size.
   // Outer sizes only care about blur.
-  int width = cornerWidth + aBlurRadius.width;
-  int height = cornerHeight + aBlurRadius.height;
+  IntSize margin = IntSize::Ceil(cornerSize) + aBlurRadius;
 
-  aOutInnerMargin.SizeTo(height, width, height, width);
+  aOutInnerMargin.SizeTo(margin.height, margin.width,
+                         margin.height, margin.width);
   aOutBlurMargin.SizeTo(aBlurRadius.height, aBlurRadius.width,
                         aBlurRadius.height, aBlurRadius.width);
 }
 
 static bool
-GetInsetBoxShadowRects(const Margin aBlurMargin,
-                       const Margin aInnerMargin,
-                       const Rect aShadowClipRect,
-                       const Rect aDestinationRect,
+GetInsetBoxShadowRects(const Margin& aBlurMargin,
+                       const Margin& aInnerMargin,
+                       const Rect& aShadowClipRect,
+                       const Rect& aDestinationRect,
                        Rect& aOutWhitespaceRect,
                        Rect& aOutOuterRect)
 {
   // We always copy (2 * blur radius) + corner radius worth of data to the destination rect
   // This covers the blend of the path + the actual blur
   // Need +1 so that we copy the edges correctly as we'll copy
   // over the min box shadow corners then the +1 for the edges between
   // Note, the (x,y) coordinates are from the blur margin
@@ -1010,79 +1192,85 @@ GetInsetBoxShadowRects(const Margin aBlu
     aOutOuterRect.Inflate(aBlurMargin);
   }
 
   return useDestRect;
 }
 
 void
 gfxAlphaBoxBlur::BlurInsetBox(gfxContext* aDestinationCtx,
-                              const Rect aDestinationRect,
-                              const Rect aShadowClipRect,
-                              const IntSize aBlurRadius,
-                              const IntSize aSpreadRadius,
+                              const Rect& aDestinationRect,
+                              const Rect& aShadowClipRect,
+                              const IntSize& aBlurRadius,
                               const Color& aShadowColor,
-                              bool aHasBorderRadius,
-                              const RectCornerRadii& aInnerClipRadii,
-                              const Rect aSkipRect,
-                              const Point aShadowOffset)
+                              const RectCornerRadii* aInnerClipRadii,
+                              const Rect& aSkipRect,
+                              const Point& aShadowOffset)
 {
   if ((aBlurRadius.width == 0 && aBlurRadius.height == 0)
       || aShadowClipRect.IsEmpty()) {
     FillDestinationPath(aDestinationCtx, aDestinationRect, aShadowClipRect,
-        aShadowColor, aHasBorderRadius, aInnerClipRadii);
+        aShadowColor, aInnerClipRadii);
     return;
   }
 
   DrawTarget* destDrawTarget = aDestinationCtx->GetDrawTarget();
 
   Margin innerMargin;
   Margin blurMargin;
-  GetBlurMargins(aHasBorderRadius, aInnerClipRadii, aBlurRadius,
-                 blurMargin, innerMargin);
+  GetBlurMargins(aInnerClipRadii, aBlurRadius, blurMargin, innerMargin);
 
   Rect whitespaceRect;
   Rect outerRect;
-  bool useDestRect = GetInsetBoxShadowRects(blurMargin, innerMargin, aShadowClipRect,
-                                            aDestinationRect, whitespaceRect, outerRect);
+  bool useDestRect =
+    GetInsetBoxShadowRects(blurMargin, innerMargin, aShadowClipRect,
+                           aDestinationRect, whitespaceRect, outerRect);
 
-  RefPtr<SourceSurface> minBlur = GetInsetBlur(outerRect, whitespaceRect, useDestRect, aShadowColor,
-                                               aBlurRadius, aHasBorderRadius, aInnerClipRadii,
-                                               destDrawTarget);
+  bool mirrorCorners = !aInnerClipRadii || aInnerClipRadii->AreRadiiSame();
+  RefPtr<SourceSurface> minBlur =
+    GetInsetBlur(outerRect, whitespaceRect, useDestRect, aShadowColor,
+                 aBlurRadius, aInnerClipRadii, destDrawTarget, mirrorCorners);
   if (!minBlur) {
     return;
   }
 
   if (useDestRect) {
-    IntSize blurSize = minBlur->GetSize();
-    Rect srcBlur(0, 0, blurSize.width, blurSize.height);
     Rect destBlur = aDestinationRect;
-
-    // The blur itself expands the rect by the blur margin, so we
-    // have to mimic that here.
     destBlur.Inflate(blurMargin);
-    MOZ_ASSERT(srcBlur.Size() == destBlur.Size());
-    destDrawTarget->DrawSurface(minBlur, destBlur, srcBlur);
+    if (mirrorCorners) {
+      DrawMirroredBoxShadow(destDrawTarget, minBlur.get(), destBlur);
+    } else {
+      Rect srcBlur(Point(0, 0), Size(minBlur->GetSize()));
+      MOZ_ASSERT(srcBlur.Size() == destBlur.Size());
+      destDrawTarget->DrawSurface(minBlur, destBlur, srcBlur);
+    }
   } else {
     Rect srcOuter(outerRect);
     Rect srcInner(srcOuter);
     srcInner.Deflate(blurMargin);   // The outer color fill
     srcInner.Deflate(innerMargin);  // The inner whitespace
 
     // The shadow clip rect already takes into account the spread radius
     Rect outerFillRect(aShadowClipRect);
     outerFillRect.Inflate(blurMargin);
-    FillDestinationPath(aDestinationCtx, aDestinationRect, outerFillRect, aShadowColor, false, RectCornerRadii());
+    FillDestinationPath(aDestinationCtx, aDestinationRect, outerFillRect, aShadowColor);
 
     // Inflate once for the frame around the whitespace
     Rect destRect(aShadowClipRect);
     destRect.Inflate(blurMargin);
 
     // Deflate for the blurred in white space
     Rect destInnerRect(aShadowClipRect);
     destInnerRect.Deflate(innerMargin);
 
-    DrawBoxShadows(*destDrawTarget, minBlur,
-                   destRect, destInnerRect,
-                   srcOuter, srcInner,
-                   aSkipRect);
+    if (mirrorCorners) {
+      DrawMirroredMinBoxShadow(destDrawTarget, minBlur,
+                               destRect, destInnerRect,
+                               srcOuter, srcInner,
+                               aSkipRect);
+    } else {
+      DrawMinBoxShadow(destDrawTarget, minBlur,
+                       destRect, destInnerRect,
+                       srcOuter, srcInner,
+                       aSkipRect);
+    }
   }
 }
--- a/gfx/thebes/gfxBlur.h
+++ b/gfx/thebes/gfxBlur.h
@@ -5,24 +5,23 @@
 
 #ifndef GFX_BLUR_H
 #define GFX_BLUR_H
 
 #include "gfxTypes.h"
 #include "nsSize.h"
 #include "gfxPoint.h"
 #include "mozilla/RefPtr.h"
-#include "mozilla/UniquePtr.h"
+#include "mozilla/gfx/Blur.h"
 
 class gfxContext;
 struct gfxRect;
 
 namespace mozilla {
   namespace gfx {
-    class AlphaBoxBlur;
     struct Color;
     struct RectCornerRadii;
     class SourceSurface;
     class DrawTarget;
   } // namespace gfx
 } // namespace mozilla
 
 /**
@@ -68,31 +67,29 @@ public:
      * @param aDirtyRect A pointer to a dirty rect, measured in device units,
      *  if available. This will be used for optimizing the blur operation. It
      *  is safe to pass nullptr here.
      *
      * @param aSkipRect A pointer to a rect, measured in device units, that
      *  represents an area where blurring is unnecessary and shouldn't be done
      *  for speed reasons. It is safe to pass nullptr here.
      */
-    gfxContext* Init(const gfxRect& aRect,
-                     const mozilla::gfx::IntSize& aSpreadRadius,
-                     const mozilla::gfx::IntSize& aBlurRadius,
-                     const gfxRect* aDirtyRect,
-                     const gfxRect* aSkipRect);
+    already_AddRefed<gfxContext>
+    Init(const gfxRect& aRect,
+         const mozilla::gfx::IntSize& aSpreadRadius,
+         const mozilla::gfx::IntSize& aBlurRadius,
+         const gfxRect* aDirtyRect,
+         const gfxRect* aSkipRect);
 
-    /**
-     * Returns the context that should be drawn to supply the alpha mask to be
-     * blurred. If the returned surface is null, then there was an error in
-     * its creation.
-     */
-    gfxContext* GetContext()
-    {
-        return mContext;
-    }
+    already_AddRefed<DrawTarget>
+    InitDrawTarget(const mozilla::gfx::Rect& aRect,
+                   const mozilla::gfx::IntSize& aSpreadRadius,
+                   const mozilla::gfx::IntSize& aBlurRadius,
+                   const mozilla::gfx::Rect* aDirtyRect = nullptr,
+                   const mozilla::gfx::Rect* aSkipRect = nullptr);
 
     already_AddRefed<mozilla::gfx::SourceSurface>
     DoBlur(DrawTarget* aDT, mozilla::gfx::IntPoint* aTopLeft);
 
     /**
      * Does the actual blurring/spreading and mask applying. Users of this
      * object must have drawn whatever they want to be blurred onto the internal
      * gfxContext returned by GetContext before calling this.
@@ -124,17 +121,17 @@ public:
      * @param aShadowColor         The color to draw the blurred shadow.
      * @param aDirtyRect           An area in device pixels that is dirty and needs
      *                             to be redrawn.
      * @param aSkipRect            An area in device pixels to avoid blurring over,
      *                             to prevent unnecessary work.
      */
     static void BlurRectangle(gfxContext *aDestinationCtx,
                               const gfxRect& aRect,
-                              RectCornerRadii* aCornerRadii,
+                              const RectCornerRadii* aCornerRadii,
                               const gfxPoint& aBlurStdDev,
                               const Color& aShadowColor,
                               const gfxRect& aDirtyRect,
                               const gfxRect& aSkipRect);
 
     static void ShutdownBlurCache();
 
     /***
@@ -142,53 +139,44 @@ public:
      * This is equivalent to calling Init(), drawing the inset path,
      * and calling paint. Do not call Init() if using this method.
      *
      * @param aDestinationCtx     The destination to blur to.
      * @param aDestinationRect    The destination rect in device pixels
      * @param aShadowClipRect     The destiniation inner rect of the
      *                            inset path in device pixels.
      * @param aBlurRadius         The standard deviation of the blur.
-     * @param aSpreadRadius       The spread radius in device pixels.
      * @param aShadowColor        The color of the blur.
-     * @param aHasBorderRadius    If this element also has a border radius
      * @param aInnerClipRadii     Corner radii for the inside rect if it is a rounded rect.
-     * @param aSKipRect           An area in device pixels we don't have to paint in.
+     * @param aSkipRect           An area in device pixels we don't have to paint in.
      */
     void BlurInsetBox(gfxContext* aDestinationCtx,
-                      const mozilla::gfx::Rect aDestinationRect,
-                      const mozilla::gfx::Rect aShadowClipRect,
-                      const mozilla::gfx::IntSize aBlurRadius,
-                      const mozilla::gfx::IntSize aSpreadRadius,
+                      const mozilla::gfx::Rect& aDestinationRect,
+                      const mozilla::gfx::Rect& aShadowClipRect,
+                      const mozilla::gfx::IntSize& aBlurRadius,
                       const mozilla::gfx::Color& aShadowColor,
-                      const bool aHasBorderRadius,
-                      const RectCornerRadii& aInnerClipRadii,
-                      const mozilla::gfx::Rect aSkipRect,
-                      const mozilla::gfx::Point aShadowOffset);
+                      const RectCornerRadii* aInnerClipRadii,
+                      const mozilla::gfx::Rect& aSkipRect,
+                      const mozilla::gfx::Point& aShadowOffset);
 
 protected:
     already_AddRefed<mozilla::gfx::SourceSurface>
-    GetInsetBlur(const mozilla::gfx::Rect aOuterRect,
-                 const mozilla::gfx::Rect aWhitespaceRect,
-                 const bool aIsDestRect,
+    GetInsetBlur(const mozilla::gfx::Rect& aOuterRect,
+                 const mozilla::gfx::Rect& aWhitespaceRect,
+                 bool aIsDestRect,
                  const mozilla::gfx::Color& aShadowColor,
                  const mozilla::gfx::IntSize& aBlurRadius,
-                 const bool aHasBorderRadius,
-                 const RectCornerRadii& aInnerClipRadii,
-                 DrawTarget* aDestDrawTarget);
-
-    /**
-     * The context of the temporary alpha surface.
-     */
-    RefPtr<gfxContext> mContext;
+                 const RectCornerRadii* aInnerClipRadii,
+                 DrawTarget* aDestDrawTarget,
+                 bool aMirrorCorners);
 
     /**
      * The temporary alpha surface.
      */
-    mozilla::UniquePtr<unsigned char[]> mData;
+    uint8_t* mData;
 
      /**
       * The object that actually does the blurring for us.
       */
-    mozilla::UniquePtr<mozilla::gfx::AlphaBoxBlur> mBlur;
+    mozilla::gfx::AlphaBoxBlur mBlur;
 };
 
 #endif /* GFX_BLUR_H */
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -1627,17 +1627,16 @@ nsCSSRendering::PaintBoxShadowInner(nsPr
     }
 
     // When there's a blur radius, gfxAlphaBoxBlur leaves the skiprect area
     // unchanged. And by construction the gfxSkipRect is not touched by the
     // rendered shadow (even after blurring), so those pixels must be completely
     // transparent in the shadow, so drawing them changes nothing.
     gfxContext* renderContext = aRenderingContext.ThebesContext();
     DrawTarget* drawTarget = renderContext->GetDrawTarget();
-    nsContextBoxBlur blurringArea;
 
     // Clip the context to the area of the frame's padding rect, so no part of the
     // shadow is painted outside. Also cut out anything beyond where the inset shadow
     // will be.
     Rect shadowGfxRect = NSRectToRect(paddingRect, twipsPerPixel);
     shadowGfxRect.Round();
 
     // Set the shadow color; if not specified, use the foreground color
@@ -6118,14 +6117,13 @@ nsContextBoxBlur::InsetBoxBlur(gfxContex
 
   for (size_t i = 0; i < 4; i++) {
     aInnerClipRectRadii[i].width = std::floor(scale.width * aInnerClipRectRadii[i].width);
     aInnerClipRectRadii[i].height = std::floor(scale.height * aInnerClipRectRadii[i].height);
   }
 
   mAlphaBoxBlur.BlurInsetBox(aDestinationCtx, transformedDestRect,
                              transformedShadowClipRect,
-                             blurRadius, spreadRadius,
-                             aShadowColor, aHasBorderRadius,
-                             aInnerClipRectRadii, transformedSkipRect,
-                             aShadowOffset);
+                             blurRadius, aShadowColor,
+                             aHasBorderRadius ? &aInnerClipRectRadii : nullptr,
+                             transformedSkipRect, aShadowOffset);
   return true;
 }
--- a/layout/reftests/box-shadow/reftest.list
+++ b/layout/reftests/box-shadow/reftest.list
@@ -13,22 +13,22 @@ fails-if(Android) == boxshadow-button.ht
 fails-if(Android) == boxshadow-fileupload.html boxshadow-fileupload-ref.html
 == boxshadow-inner-basic.html boxshadow-inner-basic-ref.svg
 random-if(layersGPUAccelerated) == boxshadow-mixed.html boxshadow-mixed-ref.html
 random-if(d2d) == boxshadow-rounded-spread.html boxshadow-rounded-spread-ref.html
 fuzzy-if(skiaContent,1,50) HTTP(..) == boxshadow-dynamic.xul boxshadow-dynamic-ref.xul
 random-if(d2d) == boxshadow-onecorner.html boxshadow-onecorner-ref.html
 random-if(d2d) == boxshadow-twocorners.html boxshadow-twocorners-ref.html
 random-if(d2d) == boxshadow-threecorners.html boxshadow-threecorners-ref.html
-== boxshadow-skiprect.html boxshadow-skiprect-ref.html
+fuzzy(2,440) == boxshadow-skiprect.html boxshadow-skiprect-ref.html
 == boxshadow-opacity.html boxshadow-opacity-ref.html
 == boxshadow-color-rounding.html boxshadow-color-rounding-ref.html
 == boxshadow-color-rounding-middle.html boxshadow-color-rounding-middle-ref.html
 fuzzy-if(OSX==1010,1,24) fuzzy-if(d2d,16,568) == boxshadow-large-border-radius.html boxshadow-large-border-radius-ref.html # Bug 1209649
-fuzzy-if(d2d,2,1080) == boxshadow-border-radius-int.html boxshadow-border-radius-int-ref.html
+fuzzy(3,500) fuzzy-if(d2d,2,1080) == boxshadow-border-radius-int.html boxshadow-border-radius-int-ref.html
 == boxshadow-inset-neg-spread.html about:blank
 == boxshadow-inset-neg-spread2.html boxshadow-inset-neg-spread2-ref.html
 fuzzy(26,3610) == boxshadow-rotated.html boxshadow-rotated-ref.html # Bug 1211264
 == boxshadow-inset-large-border-radius.html boxshadow-inset-large-border-radius-ref.html
 
 # fuzzy due to blur going inside, but as long as it's essentially black instead of a light gray its ok.
 fuzzy(12,9445) == boxshadow-inset-large-offset.html boxshadow-inset-large-offset-ref.html
 
--- a/layout/reftests/outline/reftest.list
+++ b/layout/reftests/outline/reftest.list
@@ -1,9 +1,9 @@
-== outline-and-box-shadow.html outline-and-box-shadow-ref.html
+fuzzy(2,18) == outline-and-box-shadow.html outline-and-box-shadow-ref.html
 == outline-and-3d-transform-1a.html outline-and-3d-transform-1-ref.html
 == outline-and-3d-transform-1b.html outline-and-3d-transform-1-ref.html
 fuzzy-if(gtkWidget,136,120) fuzzy-if(Android,255,356) fuzzy-if(d2d,16,96) fuzzy-if(cocoaWidget,255,120) fuzzy-if(winWidget,255,216) == outline-and-3d-transform-2.html outline-and-3d-transform-2-ref.html
 == outline-overflow-block-abspos.html outline-overflow-block-ref.html
 == outline-overflow-block-float.html outline-overflow-block-ref.html
 == outline-overflow-inlineblock-abspos.html outline-overflow-inlineblock-ref.html
 == outline-overflow-inlineblock-float.html outline-overflow-inlineblock-ref.html
 pref(layout.css.outline-style-auto.enabled,true) skip-if(!gtkWidget&&!winWidget&&!cocoaWidget) == outline-auto-001.html outline-auto-001-ref.html # only works on platforms that supports NS_THEME_FOCUS_OUTLINE