Bug 1318573 - (Part 7) Move CanvasRect to BasicRenderingContext2D. r?mstange draft
authorKevin Chen <kechen@mozilla.com>
Wed, 15 Feb 2017 17:01:58 +0800
changeset 499860 a3527a8ca53169907930220284368bedf1e9b810
parent 499859 fbf638c8a07d67bf9431efc4d3ff330521d8c68d
child 499861 22802c0b8e7a1d6fa1573b4acc51b3d640118180
push id49564
push userbmo:kechen@mozilla.com
push dateThu, 16 Mar 2017 09:50:15 +0000
reviewersmstange
bugs1318573
milestone54.0a1
Bug 1318573 - (Part 7) Move CanvasRect to BasicRenderingContext2D. r?mstange MozReview-Commit-ID: H3AYCOzzpKG
dom/canvas/AdjustedTarget.cpp
dom/canvas/AdjustedTarget.h
dom/canvas/BasicRenderingContext2D.cpp
dom/canvas/BasicRenderingContext2D.h
dom/canvas/CanvasRenderingContext2D.cpp
dom/canvas/CanvasRenderingContext2D.h
dom/canvas/WebGLFramebuffer.cpp
dom/canvas/moz.build
new file mode 100644
--- /dev/null
+++ b/dom/canvas/AdjustedTarget.cpp
@@ -0,0 +1,331 @@
+/* This is an RAII based class that can be used as a drawtarget for
+ * operations that need to have a filter applied to their results.
+ * All coordinates passed to the constructor are in device space.
+ */
+#include "AdjustedTarget.h"
+#include "mozilla/dom/HTMLCanvasElement.h"
+
+using mozilla::gfx::DrawTarget;
+
+namespace mozilla {
+namespace dom {
+
+AdjustedTargetForFilter::AdjustedTargetForFilter(BasicRenderingContext2D* aCtx,
+                        DrawTarget* aFinalTarget,
+                        const gfx::IntPoint& aFilterSpaceToTargetOffset,
+                        const gfx::IntRect& aPreFilterBounds,
+                        const gfx::IntRect& aPostFilterBounds,
+                        gfx::CompositionOp aCompositionOp)
+  : mCtx(nullptr)
+  , mCompositionOp(aCompositionOp)
+{
+  mCtx = aCtx;
+  mFinalTarget = aFinalTarget;
+  mPostFilterBounds = aPostFilterBounds;
+  mOffset = aFilterSpaceToTargetOffset;
+
+  nsIntRegion sourceGraphicNeededRegion;
+  nsIntRegion fillPaintNeededRegion;
+  nsIntRegion strokePaintNeededRegion;
+
+  FilterSupport::ComputeSourceNeededRegions(
+    aCtx->CurrentState().filter, mPostFilterBounds,
+    sourceGraphicNeededRegion, fillPaintNeededRegion,
+    strokePaintNeededRegion);
+
+  mSourceGraphicRect = sourceGraphicNeededRegion.GetBounds();
+  mFillPaintRect = fillPaintNeededRegion.GetBounds();
+  mStrokePaintRect = strokePaintNeededRegion.GetBounds();
+
+  mSourceGraphicRect = mSourceGraphicRect.Intersect(aPreFilterBounds);
+
+  if (mSourceGraphicRect.IsEmpty()) {
+    // The filter might not make any use of the source graphic. We need to
+    // create a DrawTarget that we can return from DT() anyway, so we'll
+    // just use a 1x1-sized one.
+    mSourceGraphicRect.SizeTo(1, 1);
+  }
+
+  mTarget =
+    mFinalTarget->CreateSimilarDrawTarget(mSourceGraphicRect.Size(), SurfaceFormat::B8G8R8A8);
+
+  if (!mTarget) {
+    // XXX - Deal with the situation where our temp size is too big to
+    // fit in a texture (bug 1066622).
+    mTarget = mFinalTarget;
+    mCtx = nullptr;
+    mFinalTarget = nullptr;
+    return;
+  }
+
+  mTarget->SetTransform(
+    mFinalTarget->GetTransform().PostTranslate(-mSourceGraphicRect.TopLeft() + mOffset));
+}
+
+// Return a SourceSurface that contains the FillPaint or StrokePaint source.
+already_AddRefed<SourceSurface>
+AdjustedTargetForFilter::DoSourcePaint(gfx::IntRect& aRect, BasicRenderingContext2D::Style aStyle)
+{
+  if (aRect.IsEmpty()) {
+    return nullptr;
+  }
+
+  RefPtr<DrawTarget> dt =
+    mFinalTarget->CreateSimilarDrawTarget(aRect.Size(), SurfaceFormat::B8G8R8A8);
+  if (!dt) {
+    aRect.SetEmpty();
+    return nullptr;
+  }
+
+  Matrix transform =
+    mFinalTarget->GetTransform().PostTranslate(-aRect.TopLeft() + mOffset);
+
+  dt->SetTransform(transform);
+
+  if (transform.Invert()) {
+    gfx::Rect dtBounds(0, 0, aRect.width, aRect.height);
+    gfx::Rect fillRect = transform.TransformBounds(dtBounds);
+    dt->FillRect(fillRect, CanvasGeneralPattern().ForStyle(mCtx, aStyle, dt));
+  }
+  return dt->Snapshot();
+}
+
+AdjustedTargetForFilter::~AdjustedTargetForFilter()
+{
+  if (!mCtx) {
+    return;
+  }
+
+  RefPtr<SourceSurface> snapshot = mTarget->Snapshot();
+
+  RefPtr<SourceSurface> fillPaint =
+    DoSourcePaint(mFillPaintRect, BasicRenderingContext2D::Style::FILL);
+  RefPtr<SourceSurface> strokePaint =
+    DoSourcePaint(mStrokePaintRect, BasicRenderingContext2D::Style::STROKE);
+
+  AutoRestoreTransform autoRestoreTransform(mFinalTarget);
+  mFinalTarget->SetTransform(Matrix());
+
+  MOZ_RELEASE_ASSERT(!mCtx->CurrentState().filter.mPrimitives.IsEmpty());
+  gfx::FilterSupport::RenderFilterDescription(
+    mFinalTarget, mCtx->CurrentState().filter,
+    gfx::Rect(mPostFilterBounds),
+    snapshot, mSourceGraphicRect,
+    fillPaint, mFillPaintRect,
+    strokePaint, mStrokePaintRect,
+    mCtx->CurrentState().filterAdditionalImages,
+    mPostFilterBounds.TopLeft() - mOffset,
+    DrawOptions(1.0f, mCompositionOp));
+
+  const gfx::FilterDescription& filter = mCtx->CurrentState().filter;
+  MOZ_RELEASE_ASSERT(!filter.mPrimitives.IsEmpty());
+  if (filter.mPrimitives.LastElement().IsTainted() &&
+      mCtx->GetCanvasElement()) {
+    mCtx->GetCanvasElement()->SetWriteOnly();
+  }
+}
+
+DrawTarget* AdjustedTargetForFilter::DT()
+{
+  return mTarget;
+}
+
+AdjustedTargetForShadow::AdjustedTargetForShadow(BasicRenderingContext2D* aCtx,
+                        DrawTarget* aFinalTarget,
+                        const gfx::Rect& aBounds,
+                        gfx::CompositionOp aCompositionOp)
+  : mCtx(nullptr)
+  , mCompositionOp(aCompositionOp)
+{
+  mCtx = aCtx;
+  mFinalTarget = aFinalTarget;
+
+  const ContextState &state = mCtx->CurrentState();
+
+  mSigma = state.ShadowBlurSigma();
+
+  gfx::Rect bounds = aBounds;
+
+  int32_t blurRadius = state.ShadowBlurRadius();
+
+  // We actually include the bounds of the shadow blur, this makes it
+  // easier to execute the actual blur on hardware, and shouldn't affect
+  // the amount of pixels that need to be touched.
+  bounds.Inflate(blurRadius);
+
+  bounds.RoundOut();
+  bounds.ToIntRect(&mTempRect);
+
+  mTarget =
+    mFinalTarget->CreateShadowDrawTarget(mTempRect.Size(),
+                                         SurfaceFormat::B8G8R8A8, mSigma);
+
+  if (!mTarget) {
+    // XXX - Deal with the situation where our temp size is too big to
+    // fit in a texture (bug 1066622).
+    mTarget = mFinalTarget;
+    mCtx = nullptr;
+    mFinalTarget = nullptr;
+  } else {
+    mTarget->SetTransform(
+      mFinalTarget->GetTransform().PostTranslate(-mTempRect.TopLeft()));
+  }
+}
+
+AdjustedTargetForShadow::~AdjustedTargetForShadow()
+{
+  if (!mCtx) {
+    return;
+  }
+
+  RefPtr<SourceSurface> snapshot = mTarget->Snapshot();
+
+  mFinalTarget->DrawSurfaceWithShadow(snapshot, mTempRect.TopLeft(),
+                                      Color::FromABGR(mCtx->CurrentState().shadowColor),
+                                      mCtx->CurrentState().shadowOffset, mSigma,
+                                      mCompositionOp);
+}
+
+DrawTarget* AdjustedTargetForShadow::DT()
+{
+  return mTarget;
+}
+
+gfx::IntPoint AdjustedTargetForShadow::OffsetToFinalDT()
+{
+  return mTempRect.TopLeft();
+}
+
+AdjustedTarget::AdjustedTarget(BasicRenderingContext2D* aCtx, const gfx::Rect *aBounds)
+{
+  // There are operations that can invalidate aCtx->mTarget along the way,
+  // so don't cache the pointer to it too soon.
+  mTarget = nullptr;
+
+  // All rects in this function are in the device space of ctx->mTarget.
+
+  // In order to keep our temporary surfaces as small as possible, we first
+  // calculate what their maximum required bounds would need to be if we
+  // were to fill the whole canvas. Everything outside those bounds we don't
+  // need to render.
+  gfx::Rect r(0, 0, aCtx->mWidth, aCtx->mHeight);
+  gfx::Rect maxSourceNeededBoundsForShadow =
+    MaxSourceNeededBoundsForShadow(r, aCtx);
+  gfx::Rect maxSourceNeededBoundsForFilter =
+    MaxSourceNeededBoundsForFilter(maxSourceNeededBoundsForShadow, aCtx);
+
+  gfx::Rect bounds = maxSourceNeededBoundsForFilter;
+  if (aBounds) {
+    bounds = bounds.Intersect(*aBounds);
+  }
+  gfx::Rect boundsAfterFilter = BoundsAfterFilter(bounds, aCtx);
+
+  mozilla::gfx::CompositionOp op = aCtx->CurrentState().op;
+
+  gfx::IntPoint offsetToFinalDT;
+
+  // First set up the shadow draw target, because the shadow goes outside.
+  // It applies to the post-filter results, if both a filter and a shadow
+  // are used.
+  if (aCtx->NeedToDrawShadow()) {
+    mShadowTarget = MakeUnique<AdjustedTargetForShadow>(
+      aCtx, aCtx->mTarget, boundsAfterFilter, op);
+    mTarget = mShadowTarget->DT();
+    offsetToFinalDT = mShadowTarget->OffsetToFinalDT();
+
+    // If we also have a filter, the filter needs to be drawn with OP_OVER
+    // because shadow drawing already applies op on the result.
+    op = gfx::CompositionOp::OP_OVER;
+  }
+
+  // Now set up the filter draw target.
+  if (aCtx->NeedToApplyFilter()) {
+    bounds.RoundOut();
+
+    if (!mTarget) {
+      mTarget = aCtx->mTarget;
+    }
+    gfx::IntRect intBounds;
+    if (!bounds.ToIntRect(&intBounds)) {
+      return;
+    }
+    mFilterTarget = MakeUnique<AdjustedTargetForFilter>(
+      aCtx, mTarget, offsetToFinalDT, intBounds,
+      gfx::RoundedToInt(boundsAfterFilter), op);
+    mTarget = mFilterTarget->DT();
+  }
+  if (!mTarget) {
+    mTarget = aCtx->mTarget;
+  }
+}
+
+AdjustedTarget::~AdjustedTarget()
+{
+  // The order in which the targets are finalized is important.
+  // Filters are inside, any shadow applies to the post-filter results.
+  mFilterTarget.reset();
+  mShadowTarget.reset();
+}
+
+DrawTarget* AdjustedTarget::operator->() MOZ_NO_ADDREF_RELEASE_ON_RETURN
+{
+  return mTarget;
+}
+
+gfx::Rect
+AdjustedTarget::MaxSourceNeededBoundsForFilter(const gfx::Rect& aDestBounds, BasicRenderingContext2D* aCtx)
+{
+  if (!aCtx->NeedToApplyFilter()) {
+    return aDestBounds;
+  }
+
+  nsIntRegion sourceGraphicNeededRegion;
+  nsIntRegion fillPaintNeededRegion;
+  nsIntRegion strokePaintNeededRegion;
+
+  FilterSupport::ComputeSourceNeededRegions(
+    aCtx->CurrentState().filter, gfx::RoundedToInt(aDestBounds),
+    sourceGraphicNeededRegion, fillPaintNeededRegion, strokePaintNeededRegion);
+
+  return gfx::Rect(sourceGraphicNeededRegion.GetBounds());
+}
+
+gfx::Rect
+AdjustedTarget::MaxSourceNeededBoundsForShadow(const gfx::Rect& aDestBounds, BasicRenderingContext2D* aCtx)
+{
+  if (!aCtx->NeedToDrawShadow()) {
+    return aDestBounds;
+  }
+
+  const ContextState &state = aCtx->CurrentState();
+  gfx::Rect sourceBounds = aDestBounds - state.shadowOffset;
+  sourceBounds.Inflate(state.ShadowBlurRadius());
+
+  // Union the shadow source with the original rect because we're going to
+  // draw both.
+  return sourceBounds.Union(aDestBounds);
+}
+
+gfx::Rect
+AdjustedTarget::BoundsAfterFilter(const gfx::Rect& aBounds, BasicRenderingContext2D* aCtx)
+{
+  if (!aCtx->NeedToApplyFilter()) {
+    return aBounds;
+  }
+
+  gfx::Rect bounds(aBounds);
+  bounds.RoundOut();
+
+  gfx::IntRect intBounds;
+  if (!bounds.ToIntRect(&intBounds)) {
+    return gfx::Rect();
+  }
+
+  nsIntRegion extents =
+    gfx::FilterSupport::ComputePostFilterExtents(aCtx->CurrentState().filter,
+                                                 intBounds);
+  return gfx::Rect(extents.GetBounds());
+}
+
+} // dom
+} // mozilla
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/canvas/AdjustedTarget.h
@@ -0,0 +1,136 @@
+/* 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/. */
+
+#ifndef AdjustedTarget_h
+#define AdjustedTarget_h
+
+#include "FilterSupport.h"
+#include "mozilla/dom/BasicRenderingContext2D.h"
+#include "mozilla/gfx/Helpers.h"
+
+using mozilla::gfx::AutoRestoreTransform;
+using mozilla::gfx::Color;
+using mozilla::gfx::DrawOptions;
+using mozilla::gfx::FilterSupport;
+using mozilla::gfx::SurfaceFormat;
+using mozilla::gfx::SourceSurface;
+
+namespace mozilla {
+namespace dom {
+
+/* This is an RAII based class that can be used as a drawtarget for
+ * operations that need to have a filter applied to their results.
+ * All coordinates passed to the constructor are in device space.
+ */
+class AdjustedTargetForFilter
+{
+  //typedef BasicRenderingContext2D::RenderingClass RenderingClass;
+
+public:
+  typedef BasicRenderingContext2D::ContextState ContextState;
+
+  AdjustedTargetForFilter(BasicRenderingContext2D* aCtx,
+                          DrawTarget* aFinalTarget,
+                          const gfx::IntPoint& aFilterSpaceToTargetOffset,
+                          const gfx::IntRect& aPreFilterBounds,
+                          const gfx::IntRect& aPostFilterBounds,
+                          gfx::CompositionOp aCompositionOp);
+
+  // Return a SourceSurface that contains the FillPaint or StrokePaint source.
+  already_AddRefed<SourceSurface>
+  DoSourcePaint(gfx::IntRect& aRect, BasicRenderingContext2D::Style aStyle);
+
+  ~AdjustedTargetForFilter();
+
+  DrawTarget* DT();
+
+private:
+  RefPtr<DrawTarget> mTarget;
+  RefPtr<DrawTarget> mFinalTarget;
+  BasicRenderingContext2D *mCtx;
+  gfx::IntRect mSourceGraphicRect;
+  gfx::IntRect mFillPaintRect;
+  gfx::IntRect mStrokePaintRect;
+  gfx::IntRect mPostFilterBounds;
+  gfx::IntPoint mOffset;
+  gfx::CompositionOp mCompositionOp;
+};
+
+/* This is an RAII based class that can be used as a drawtarget for
+ * operations that need to have a shadow applied to their results.
+ * All coordinates passed to the constructor are in device space.
+ */
+class AdjustedTargetForShadow
+{
+public:
+  typedef BasicRenderingContext2D::ContextState ContextState;
+
+  AdjustedTargetForShadow(BasicRenderingContext2D* aCtx,
+                          DrawTarget* aFinalTarget,
+                          const gfx::Rect& aBounds,
+                          gfx::CompositionOp aCompositionOp);
+
+  ~AdjustedTargetForShadow();
+
+  DrawTarget* DT();
+
+  gfx::IntPoint OffsetToFinalDT();
+
+private:
+  RefPtr<DrawTarget> mTarget;
+  RefPtr<DrawTarget> mFinalTarget;
+  BasicRenderingContext2D *mCtx;
+  Float mSigma;
+  gfx::IntRect mTempRect;
+  gfx::CompositionOp mCompositionOp;
+};
+
+/* This is an RAII based class that can be used as a drawtarget for
+ * operations that need a shadow or a filter drawn. It will automatically
+ * provide a temporary target when needed, and if so blend it back with a
+ * shadow, filter, or both.
+ * If both a shadow and a filter are needed, the filter is applied first,
+ * and the shadow is applied to the filtered results.
+ *
+ * aBounds specifies the bounds of the drawing operation that will be
+ * drawn to the target, it is given in device space! If this is nullptr the
+ * drawing operation will be assumed to cover the whole canvas.
+ */
+class AdjustedTarget
+{
+public:
+  typedef BasicRenderingContext2D::ContextState ContextState;
+
+  explicit AdjustedTarget(BasicRenderingContext2D* aCtx,
+                          const gfx::Rect *aBounds = nullptr);
+
+  ~AdjustedTarget();
+
+  operator DrawTarget*()
+  {
+    return mTarget;
+  }
+
+  DrawTarget* operator->() MOZ_NO_ADDREF_RELEASE_ON_RETURN;
+
+private:
+
+  gfx::Rect
+  MaxSourceNeededBoundsForFilter(const gfx::Rect& aDestBounds, BasicRenderingContext2D* aCtx);
+
+  gfx::Rect
+  MaxSourceNeededBoundsForShadow(const gfx::Rect& aDestBounds, BasicRenderingContext2D* aCtx);
+
+  gfx::Rect
+  BoundsAfterFilter(const gfx::Rect& aBounds, BasicRenderingContext2D* aCtx);
+
+  RefPtr<DrawTarget> mTarget;
+  UniquePtr<AdjustedTargetForShadow> mShadowTarget;
+  UniquePtr<AdjustedTargetForFilter> mFilterTarget;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // AdjustedTarget_h
\ No newline at end of file
--- a/dom/canvas/BasicRenderingContext2D.cpp
+++ b/dom/canvas/BasicRenderingContext2D.cpp
@@ -1,24 +1,37 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "AdjustedTarget.h"
+#include "CanvasUtils.h"
+#include "gfxUtils.h"
 #include "mozilla/dom/BasicRenderingContext2D.h"
 #include "mozilla/dom/HTMLCanvasElement.h"
 #include "mozilla/dom/HTMLImageElement.h"
 #include "mozilla/dom/HTMLVideoElement.h"
 #include "mozilla/dom/ImageBitmap.h"
 #include "nsContentUtils.h"
 #include "nsICanvasRenderingContextInternal.h"
 #include "nsPrintfCString.h"
 #include "nsStyleUtil.h"
 
+using mozilla::gfx::AntialiasMode;
+using mozilla::gfx::CapStyle;
+using mozilla::gfx::Color;
+using mozilla::gfx::DrawOptions;
+using mozilla::gfx::ExtendMode;
+using mozilla::gfx::JoinStyle;
+using mozilla::gfx::IntSize;
+using mozilla::gfx::SamplingFilter;
 using mozilla::gfx::SourceSurface;
+using mozilla::gfx::StrokeOptions;
+using mozilla::gfx::ToDeviceColor;
 
 namespace mozilla {
 namespace dom{
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CanvasGradient, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CanvasGradient, Release)
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CanvasGradient, mContext)
@@ -32,16 +45,39 @@ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Ca
 // Cap sigma to avoid overly large temp surfaces.
 const mozilla::gfx::Float SIGMA_MAX = 100;
 
 const size_t MAX_STYLE_STACK_SIZE = 1024;
 
 /**
  ** BasicRenderingContext2D impl
  **/
+bool
+BasicRenderingContext2D::PatternIsOpaque(BasicRenderingContext2D::Style aStyle) const
+{
+  const ContextState& state = CurrentState();
+  if (state.globalAlpha < 1.0) {
+    return false;
+  }
+
+  if (state.patternStyles[aStyle] && state.patternStyles[aStyle]->mSurface) {
+    return IsOpaqueFormat(state.patternStyles[aStyle]->mSurface->GetFormat());
+  }
+
+  // TODO: for gradient patterns we could check that all stops are opaque
+  // colors.
+
+  if (!state.gradientStyles[aStyle]) {
+    // it's a color pattern.
+    return Color::FromABGR(state.colorStyles[aStyle]).a >= 1.0;
+  }
+
+  return false;
+}
+
 
 void
 BasicRenderingContext2D::GetStyleAsUnion(OwningStringOrCanvasGradientOrCanvasPattern& aValue,
                                          Style aWhichStyle)
 {
   const ContextState &state = CurrentState();
   if (state.patternStyles[aWhichStyle]) {
     aValue.SetAsCanvasPattern() = state.patternStyles[aWhichStyle];
@@ -498,16 +534,223 @@ BasicRenderingContext2D::SetShadowColor(
   if (!ParseColor(aShadowColor, &color)) {
     return;
   }
 
   CurrentState().shadowColor = color;
 }
 
 //
+// rects
+//
+
+static bool
+ValidateRect(double& aX, double& aY, double& aWidth, double& aHeight, bool aIsZeroSizeValid)
+{
+  if (!aIsZeroSizeValid && (aWidth == 0.0 || aHeight == 0.0)) {
+    return false;
+  }
+
+  // bug 1018527
+  // The values of canvas API input are in double precision, but Moz2D APIs are
+  // using float precision. Bypass canvas API calls when the input is out of
+  // float precision to avoid precision problem
+  if (!std::isfinite((float)aX) | !std::isfinite((float)aY) |
+      !std::isfinite((float)aWidth) | !std::isfinite((float)aHeight)) {
+    return false;
+  }
+
+  // bug 1074733
+  // The canvas spec does not forbid rects with negative w or h, so given
+  // corners (x, y), (x+w, y), (x+w, y+h), and (x, y+h) we must generate
+  // the appropriate rect by flipping negative dimensions. This prevents
+  // draw targets from receiving "empty" rects later on.
+  if (aWidth < 0) {
+    aWidth = -aWidth;
+    aX -= aWidth;
+  }
+  if (aHeight < 0) {
+    aHeight = -aHeight;
+    aY -= aHeight;
+  }
+  return true;
+}
+
+void
+BasicRenderingContext2D::ClearRect(double aX, double aY, double aW,
+                                   double aH)
+{
+  // Do not allow zeros - it's a no-op at that point per spec.
+  if (!ValidateRect(aX, aY, aW, aH, false)) {
+    return;
+  }
+
+  gfx::Rect clearRect(aX, aY, aW, aH);
+
+  EnsureTarget(&clearRect);
+
+  mTarget->ClearRect(clearRect);
+
+  RedrawUser(gfxRect(aX, aY, aW, aH));
+}
+
+void
+BasicRenderingContext2D::FillRect(double aX, double aY, double aW,
+                                  double aH)
+{
+  const ContextState &state = CurrentState();
+
+  if (!ValidateRect(aX, aY, aW, aH, true)) {
+    return;
+  }
+
+  if (state.patternStyles[Style::FILL]) {
+    CanvasPattern::RepeatMode repeat =
+      state.patternStyles[Style::FILL]->mRepeat;
+    // In the FillRect case repeat modes are easy to deal with.
+    bool limitx = repeat == CanvasPattern::RepeatMode::NOREPEAT || repeat == CanvasPattern::RepeatMode::REPEATY;
+    bool limity = repeat == CanvasPattern::RepeatMode::NOREPEAT || repeat == CanvasPattern::RepeatMode::REPEATX;
+
+    IntSize patternSize =
+      state.patternStyles[Style::FILL]->mSurface->GetSize();
+
+    // We always need to execute painting for non-over operators, even if
+    // we end up with w/h = 0.
+    if (limitx) {
+      if (aX < 0) {
+        aW += aX;
+        if (aW < 0) {
+          aW = 0;
+        }
+
+        aX = 0;
+      }
+      if (aX + aW > patternSize.width) {
+        aW = patternSize.width - aX;
+        if (aW < 0) {
+          aW = 0;
+        }
+      }
+    }
+    if (limity) {
+      if (aY < 0) {
+        aH += aY;
+        if (aH < 0) {
+          aH = 0;
+        }
+
+        aY = 0;
+      }
+      if (aY + aH > patternSize.height) {
+        aH = patternSize.height - aY;
+        if (aH < 0) {
+          aH = 0;
+        }
+      }
+    }
+  }
+
+  CompositionOp op = UsedOperation();
+  bool discardContent = PatternIsOpaque(Style::FILL)
+    && (op == CompositionOp::OP_OVER || op == CompositionOp::OP_SOURCE);
+
+  const gfx::Rect fillRect(aX, aY, aW, aH);
+  EnsureTarget(discardContent ? &fillRect : nullptr);
+
+  gfx::Rect bounds;
+  if (NeedToCalculateBounds()) {
+    bounds = mTarget->GetTransform().TransformBounds(fillRect);
+  }
+
+  AntialiasMode antialiasMode = CurrentState().imageSmoothingEnabled ?
+                                AntialiasMode::DEFAULT : AntialiasMode::NONE;
+
+  AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
+    FillRect(gfx::Rect(aX, aY, aW, aH),
+             CanvasGeneralPattern().ForStyle(this, Style::FILL, mTarget),
+             DrawOptions(state.globalAlpha, op, antialiasMode));
+
+  RedrawUser(gfxRect(aX, aY, aW, aH));
+}
+
+void
+BasicRenderingContext2D::StrokeRect(double aX, double aY, double aW,
+                                    double aH)
+{
+  const ContextState &state = CurrentState();
+
+  gfx::Rect bounds;
+
+  if (!aW && !aH) {
+    return;
+  }
+
+  if (!ValidateRect(aX, aY, aW, aH, true)) {
+    return;
+  }
+
+  EnsureTarget();
+  if (!IsTargetValid()) {
+    return;
+  }
+
+  if (NeedToCalculateBounds()) {
+    bounds = gfx::Rect(aX - state.lineWidth / 2.0f, aY - state.lineWidth / 2.0f,
+                       aW + state.lineWidth, aH + state.lineWidth);
+    bounds = mTarget->GetTransform().TransformBounds(bounds);
+  }
+
+  if (!aH) {
+    CapStyle cap = CapStyle::BUTT;
+    if (state.lineJoin == JoinStyle::ROUND) {
+      cap = CapStyle::ROUND;
+    }
+    AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
+      StrokeLine(Point(aX, aY), Point(aX + aW, aY),
+                  CanvasGeneralPattern().ForStyle(this, Style::STROKE, mTarget),
+                  StrokeOptions(state.lineWidth, state.lineJoin,
+                                cap, state.miterLimit,
+                                state.dash.Length(),
+                                state.dash.Elements(),
+                                state.dashOffset),
+                  DrawOptions(state.globalAlpha, UsedOperation()));
+    return;
+  }
+
+  if (!aW) {
+    CapStyle cap = CapStyle::BUTT;
+    if (state.lineJoin == JoinStyle::ROUND) {
+      cap = CapStyle::ROUND;
+    }
+    AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
+      StrokeLine(Point(aX, aY), Point(aX, aY + aH),
+                  CanvasGeneralPattern().ForStyle(this, Style::STROKE, mTarget),
+                  StrokeOptions(state.lineWidth, state.lineJoin,
+                                cap, state.miterLimit,
+                                state.dash.Length(),
+                                state.dash.Elements(),
+                                state.dashOffset),
+                  DrawOptions(state.globalAlpha, UsedOperation()));
+    return;
+  }
+
+  AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
+    StrokeRect(gfx::Rect(aX, aY, aW, aH),
+               CanvasGeneralPattern().ForStyle(this, Style::STROKE, mTarget),
+               StrokeOptions(state.lineWidth, state.lineJoin,
+                             state.lineCap, state.miterLimit,
+                             state.dash.Length(),
+                             state.dash.Elements(),
+                             state.dashOffset),
+               DrawOptions(state.globalAlpha, UsedOperation()));
+
+  Redraw();
+}
+
+//
 // path bits
 //
 
 void
 BasicRenderingContext2D::TransformWillUpdate()
 {
   EnsureTarget();
 
@@ -520,10 +763,68 @@ BasicRenderingContext2D::TransformWillUp
       // transform the current mPath into device space.
       // We should leave it alone.
       mPathToDS = mTarget->GetTransform();
     }
     mPathTransformWillUpdate = true;
   }
 }
 
+Pattern&
+CanvasGeneralPattern::ForStyle(BasicRenderingContext2D* aCtx,
+                               BasicRenderingContext2D::Style aStyle,
+                               DrawTarget* aRT)
+{
+  // This should only be called once or the mPattern destructor will
+  // not be executed.
+  NS_ASSERTION(!mPattern.GetPattern(), "ForStyle() should only be called once on CanvasGeneralPattern!");
+
+  const BasicRenderingContext2D::ContextState &state = aCtx->CurrentState();
+
+  if (state.StyleIsColor(aStyle)) {
+    mPattern.InitColorPattern(ToDeviceColor(state.colorStyles[aStyle]));
+  } else if (state.gradientStyles[aStyle] &&
+             state.gradientStyles[aStyle]->GetType() == CanvasGradient::Type::LINEAR) {
+    CanvasLinearGradient *gradient =
+      static_cast<CanvasLinearGradient*>(state.gradientStyles[aStyle].get());
+
+    mPattern.InitLinearGradientPattern(gradient->mBegin, gradient->mEnd,
+                                       gradient->GetGradientStopsForTarget(aRT));
+  } else if (state.gradientStyles[aStyle] &&
+             state.gradientStyles[aStyle]->GetType() == CanvasGradient::Type::RADIAL) {
+    CanvasRadialGradient *gradient =
+      static_cast<CanvasRadialGradient*>(state.gradientStyles[aStyle].get());
+
+    mPattern.InitRadialGradientPattern(gradient->mCenter1, gradient->mCenter2,
+                                       gradient->mRadius1, gradient->mRadius2,
+                                       gradient->GetGradientStopsForTarget(aRT));
+  } else if (state.patternStyles[aStyle]) {
+    if (aCtx->GetCanvasElement()) {
+      CanvasUtils::DoDrawImageSecurityCheck(aCtx->GetCanvasElement(),
+                                            state.patternStyles[aStyle]->mPrincipal,
+                                            state.patternStyles[aStyle]->mForceWriteOnly,
+                                            state.patternStyles[aStyle]->mCORSUsed);
+    }
+
+    ExtendMode mode;
+    if (state.patternStyles[aStyle]->mRepeat == CanvasPattern::RepeatMode::NOREPEAT) {
+      mode = ExtendMode::CLAMP;
+    } else {
+      mode = ExtendMode::REPEAT;
+    }
+
+    SamplingFilter samplingFilter;
+    if (state.imageSmoothingEnabled) {
+      samplingFilter = SamplingFilter::GOOD;
+    } else {
+      samplingFilter = SamplingFilter::POINT;
+    }
+
+    mPattern.InitSurfacePattern(state.patternStyles[aStyle]->mSurface, mode,
+                                state.patternStyles[aStyle]->mTransform,
+                                samplingFilter);
+  }
+
+  return *mPattern.GetPattern();
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/canvas/BasicRenderingContext2D.h
+++ b/dom/canvas/BasicRenderingContext2D.h
@@ -6,22 +6,26 @@
 #define BasicRenderingContext2D_h
 
 #include "FilterSupport.h"
 #include "gfxTextRun.h"
 #include "mozilla/dom/CanvasGradient.h"
 #include "mozilla/dom/CanvasPattern.h"
 #include "mozilla/dom/CanvasRenderingContext2DBinding.h"
 #include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/PatternHelpers.h"
 #include "nsStyleStruct.h"
 #include "nsSVGEffects.h"
 
 using mozilla::gfx::CompositionOp;
+using mozilla::gfx::DrawTarget;
 using mozilla::gfx::FilterDescription;
 using mozilla::gfx::Matrix;
+using mozilla::gfx::Pattern;
+using mozilla::gfx::GeneralPattern;
 
 namespace mozilla {
 namespace dom {
 
  #define NS_BASICRENDERINGCONTEXT2D_IID \
  { 0xbd7f3f74, 0x5ed8, 0x4451, \
   { 0x89, 0x95, 0x1c, 0x6a, 0x82, 0xeb, 0xef, 0x9f} }
 
@@ -41,24 +45,32 @@ public:
     SoftwareBackendMode,
     OpenGLBackendMode,
     DefaultBackendMode
   };
 
   // This is created lazily so it is necessary to call EnsureTarget before
   // accessing it. In the event of an error it will be equal to
   // sErrorTarget.
-  RefPtr<mozilla::gfx::DrawTarget> mTarget;
+  RefPtr<DrawTarget> mTarget;
+
+  // this rect is in mTarget's current user space
+  virtual void RedrawUser(const gfxRect& aR) = 0;
+
+  virtual nsresult Redraw() = 0;
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_BASICRENDERINGCONTEXT2D_IID)
 protected:
-  ~BasicRenderingContext2D(){}
+  virtual ~BasicRenderingContext2D() {}
 public:
   explicit BasicRenderingContext2D(layers::LayersBackend aCompositorBackend)
-    : mPathTransformWillUpdate(false){};
+    // these are the default values from the Canvas spec
+    : mWidth(0), mHeight(0)
+    , mPathTransformWillUpdate(false) {}
+
   //
   // CanvasState
   //
   void Save();
   void Restore();
 
   //
   // CanvasTransform
@@ -187,19 +199,19 @@ public:
     StyleColorToString(CurrentState().shadowColor, aShadowColor);
   }
 
   void SetShadowColor(const nsAString& aShadowColor);
 
   //
   // CanvasRect
   //
-  virtual void ClearRect(double aX, double aY, double aW, double aH) = 0;
-  virtual void FillRect(double aX, double aY, double aW, double aH) = 0;
-  virtual void StrokeRect(double aX, double aY, double aW, double aH) = 0;
+  void ClearRect(double aX, double aY, double aW, double aH);
+  void FillRect(double aX, double aY, double aW, double aH);
+  void StrokeRect(double aX, double aY, double aW, double aH);
 
   //
   // CanvasDrawImage
   //
   virtual void DrawImage(const CanvasImageSource& aImage,
                          double aDx,
                          double aDy,
                          mozilla::ErrorResult& aError) = 0;
@@ -273,16 +285,21 @@ public:
                        double aRadiusX,
                        double aRadiusY,
                        double aRotation,
                        double aStartAngle,
                        double aEndAngle,
                        bool aAnticlockwise,
                        ErrorResult& aError) = 0;
 protected:
+  friend class CanvasGeneralPattern;
+  friend class AdjustedTarget;
+  friend class AdjustedTargetForShadow;
+  friend class AdjustedTargetForFilter;
+
   enum class Style : uint8_t {
     STROKE = 0,
     FILL,
     MAX
   };
 
   enum class TextAlign : uint8_t {
     START,
@@ -457,18 +474,21 @@ protected:
   // We keep track of this to ensure that if this gets out of sync with the
   // tainted state of the canvas itself, we update our filters accordingly.
   bool filterSourceGraphicTainted;
 
   bool imageSmoothingEnabled;
   bool fontExplicitLanguage;
   };
 
+  // Member vars
   AutoTArray<ContextState, 3> mStyleStack;
 
+  int32_t mWidth, mHeight;
+
   /**
     * We also have a device space pathbuilder. The reason for this is as
     * follows, when a path is being built, but the transform changes, we
     * can no longer keep a single path in userspace, considering there's
     * several 'user spaces' now. We therefore transform the current path
     * into device space, and add all operations to this path in device
     * space.
     *
@@ -489,16 +509,18 @@ protected:
     * mPath is always in user-space.
     */
   RefPtr<mozilla::gfx::Path> mPath;
   RefPtr<mozilla::gfx::PathBuilder> mPathBuilder;
   bool mPathTransformWillUpdate;
   Matrix mPathToDS;
 
 protected:
+  virtual HTMLCanvasElement* GetCanvasElement() = 0;
+
   virtual bool AlreadyShutDown() const = 0;
 
   /**
    * Create the backing surfacing, if it doesn't exist. If there is an error
    * in creating the target then it will put sErrorTarget in place. If there
    * is in turn an error in creating the sErrorTarget then they would both
    * be null so IsTargetValid() would still return null.
    *
@@ -546,17 +568,71 @@ protected:
 
   // Returns whether a color was successfully parsed.
   virtual bool ParseColor(const nsAString& aString, nscolor* aColor) = 0;
 
   static void StyleColorToString(const nscolor& aColor, nsAString& aStr);
 
   void GetStyleAsUnion(OwningStringOrCanvasGradientOrCanvasPattern& aValue,
                        Style aWhichStyle);
+
+
+  /**
+   * Returns true if we know for sure that the pattern for a given style is opaque.
+   * Usefull to know if we can discard the content below in certain situations.
+   */
+  bool PatternIsOpaque(Style aStyle) const;
+
+  mozilla::gfx::CompositionOp UsedOperation()
+  {
+    if (NeedToDrawShadow() || NeedToApplyFilter()) {
+      // In this case the shadow or filter rendering will use the operator.
+      return mozilla::gfx::CompositionOp::OP_OVER;
+    }
+
+    return CurrentState().op;
+  }
+
+  /**
+    * Returns true if a shadow should be drawn along with a
+    * drawing operation.
+    */
+  bool NeedToDrawShadow()
+  {
+    const ContextState& state = CurrentState();
+
+    // The spec says we should not draw shadows if the operator is OVER.
+    // If it's over and the alpha value is zero, nothing needs to be drawn.
+    return NS_GET_A(state.shadowColor) != 0 &&
+      (state.shadowBlur != 0.f || state.shadowOffset.x != 0.f || state.shadowOffset.y != 0.f);
+  }
+
+  /**
+    * Returns true if the result of a drawing operation should be
+    * drawn with a filter.
+    */
+  virtual bool NeedToApplyFilter() = 0;
+
+  bool NeedToCalculateBounds()
+  {
+    return NeedToDrawShadow() || NeedToApplyFilter();
+  }
 };
 
  NS_DEFINE_STATIC_IID_ACCESSOR(BasicRenderingContext2D,
                                NS_BASICRENDERINGCONTEXT2D_IID)
 
+// This class is named 'GeneralCanvasPattern' instead of just
+// 'GeneralPattern' to keep Windows PGO builds from confusing the
+// GeneralPattern class in gfxContext.cpp with this one.
+class CanvasGeneralPattern
+{
+public:
+  Pattern& ForStyle(BasicRenderingContext2D* aCtx,
+                    BasicRenderingContext2D::Style aStyle, DrawTarget* aRT);
+
+  GeneralPattern mPattern;
+};
+
 } // namespace dom
 } // namespace mozilla
 
 #endif /* BasicRenderingContext2D_h */
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -47,17 +47,16 @@
 #include "ImageEncoder.h"
 #include "ImageRegion.h"
 
 #include "gfxContext.h"
 #include "gfxPlatform.h"
 #include "gfxFont.h"
 #include "gfxBlur.h"
 #include "gfxPrefs.h"
-#include "gfxUtils.h"
 
 #include "nsFrameLoader.h"
 #include "nsBidi.h"
 #include "nsBidiPresUtils.h"
 #include "Layers.h"
 #include "LayerUserData.h"
 #include "CanvasUtils.h"
 #include "nsIMemoryReporter.h"
@@ -81,17 +80,16 @@
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/dom/TypedArray.h"
 #include "mozilla/EndianUtils.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/Helpers.h"
 #include "mozilla/gfx/Tools.h"
 #include "mozilla/gfx/PathHelpers.h"
 #include "mozilla/gfx/DataSurfaceHelpers.h"
-#include "mozilla/gfx/PatternHelpers.h"
 #include "mozilla/gfx/Swizzle.h"
 #include "mozilla/ipc/DocumentRendererParent.h"
 #include "mozilla/ipc/PDocumentRendererParent.h"
 #include "mozilla/layers/PersistentBufferProvider.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/TimeStamp.h"
@@ -178,492 +176,16 @@ public:
       "(width * height * 4) bytes.");
 
     return NS_OK;
   }
 };
 
 NS_IMPL_ISUPPORTS(Canvas2dPixelsReporter, nsIMemoryReporter)
 
-bool
-CanvasRenderingContext2D::PatternIsOpaque(CanvasRenderingContext2D::Style aStyle) const
-{
-  const ContextState& state = CurrentState();
-  if (state.globalAlpha < 1.0) {
-    return false;
-  }
-
-  if (state.patternStyles[aStyle] && state.patternStyles[aStyle]->mSurface) {
-    return IsOpaqueFormat(state.patternStyles[aStyle]->mSurface->GetFormat());
-  }
-
-  // TODO: for gradient patterns we could check that all stops are opaque
-  // colors.
-
-  if (!state.gradientStyles[aStyle]) {
-    // it's a color pattern.
-    return Color::FromABGR(state.colorStyles[aStyle]).a >= 1.0;
-  }
-
-  return false;
-}
-
-// This class is named 'GeneralCanvasPattern' instead of just
-// 'GeneralPattern' to keep Windows PGO builds from confusing the
-// GeneralPattern class in gfxContext.cpp with this one.
-class CanvasGeneralPattern
-{
-public:
-  typedef CanvasRenderingContext2D::Style Style;
-  typedef CanvasRenderingContext2D::ContextState ContextState;
-
-  Pattern& ForStyle(CanvasRenderingContext2D* aCtx,
-                    Style aStyle,
-                    DrawTarget* aRT)
-  {
-    // This should only be called once or the mPattern destructor will
-    // not be executed.
-    NS_ASSERTION(!mPattern.GetPattern(), "ForStyle() should only be called once on CanvasGeneralPattern!");
-
-    const ContextState &state = aCtx->CurrentState();
-
-    if (state.StyleIsColor(aStyle)) {
-      mPattern.InitColorPattern(ToDeviceColor(state.colorStyles[aStyle]));
-    } else if (state.gradientStyles[aStyle] &&
-               state.gradientStyles[aStyle]->GetType() == CanvasGradient::Type::LINEAR) {
-      CanvasLinearGradient *gradient =
-        static_cast<CanvasLinearGradient*>(state.gradientStyles[aStyle].get());
-
-      mPattern.InitLinearGradientPattern(gradient->mBegin, gradient->mEnd,
-                                         gradient->GetGradientStopsForTarget(aRT));
-    } else if (state.gradientStyles[aStyle] &&
-               state.gradientStyles[aStyle]->GetType() == CanvasGradient::Type::RADIAL) {
-      CanvasRadialGradient *gradient =
-        static_cast<CanvasRadialGradient*>(state.gradientStyles[aStyle].get());
-
-      mPattern.InitRadialGradientPattern(gradient->mCenter1, gradient->mCenter2,
-                                         gradient->mRadius1, gradient->mRadius2,
-                                         gradient->GetGradientStopsForTarget(aRT));
-    } else if (state.patternStyles[aStyle]) {
-      if (aCtx->mCanvasElement) {
-        CanvasUtils::DoDrawImageSecurityCheck(aCtx->mCanvasElement,
-                                              state.patternStyles[aStyle]->mPrincipal,
-                                              state.patternStyles[aStyle]->mForceWriteOnly,
-                                              state.patternStyles[aStyle]->mCORSUsed);
-      }
-
-      ExtendMode mode;
-      if (state.patternStyles[aStyle]->mRepeat == CanvasPattern::RepeatMode::NOREPEAT) {
-        mode = ExtendMode::CLAMP;
-      } else {
-        mode = ExtendMode::REPEAT;
-      }
-
-      SamplingFilter samplingFilter;
-      if (state.imageSmoothingEnabled) {
-        samplingFilter = SamplingFilter::GOOD;
-      } else {
-        samplingFilter = SamplingFilter::POINT;
-      }
-
-      mPattern.InitSurfacePattern(state.patternStyles[aStyle]->mSurface, mode,
-                                  state.patternStyles[aStyle]->mTransform,
-                                  samplingFilter);
-    }
-
-    return *mPattern.GetPattern();
-  }
-
-  GeneralPattern mPattern;
-};
-
-/* This is an RAII based class that can be used as a drawtarget for
- * operations that need to have a filter applied to their results.
- * All coordinates passed to the constructor are in device space.
- */
-class AdjustedTargetForFilter
-{
-public:
-  typedef CanvasRenderingContext2D::ContextState ContextState;
-
-  AdjustedTargetForFilter(CanvasRenderingContext2D* aCtx,
-                          DrawTarget* aFinalTarget,
-                          const gfx::IntPoint& aFilterSpaceToTargetOffset,
-                          const gfx::IntRect& aPreFilterBounds,
-                          const gfx::IntRect& aPostFilterBounds,
-                          gfx::CompositionOp aCompositionOp)
-    : mCtx(nullptr)
-    , mCompositionOp(aCompositionOp)
-  {
-    mCtx = aCtx;
-    mFinalTarget = aFinalTarget;
-    mPostFilterBounds = aPostFilterBounds;
-    mOffset = aFilterSpaceToTargetOffset;
-
-    nsIntRegion sourceGraphicNeededRegion;
-    nsIntRegion fillPaintNeededRegion;
-    nsIntRegion strokePaintNeededRegion;
-
-    FilterSupport::ComputeSourceNeededRegions(
-      aCtx->CurrentState().filter, mPostFilterBounds,
-      sourceGraphicNeededRegion, fillPaintNeededRegion,
-      strokePaintNeededRegion);
-
-    mSourceGraphicRect = sourceGraphicNeededRegion.GetBounds();
-    mFillPaintRect = fillPaintNeededRegion.GetBounds();
-    mStrokePaintRect = strokePaintNeededRegion.GetBounds();
-
-    mSourceGraphicRect = mSourceGraphicRect.Intersect(aPreFilterBounds);
-
-    if (mSourceGraphicRect.IsEmpty()) {
-      // The filter might not make any use of the source graphic. We need to
-      // create a DrawTarget that we can return from DT() anyway, so we'll
-      // just use a 1x1-sized one.
-      mSourceGraphicRect.SizeTo(1, 1);
-    }
-
-    mTarget =
-      mFinalTarget->CreateSimilarDrawTarget(mSourceGraphicRect.Size(), SurfaceFormat::B8G8R8A8);
-
-    if (!mTarget) {
-      // XXX - Deal with the situation where our temp size is too big to
-      // fit in a texture (bug 1066622).
-      mTarget = mFinalTarget;
-      mCtx = nullptr;
-      mFinalTarget = nullptr;
-      return;
-    }
-
-    mTarget->SetTransform(
-      mFinalTarget->GetTransform().PostTranslate(-mSourceGraphicRect.TopLeft() + mOffset));
-  }
-
-  // Return a SourceSurface that contains the FillPaint or StrokePaint source.
-  already_AddRefed<SourceSurface>
-  DoSourcePaint(gfx::IntRect& aRect, CanvasRenderingContext2D::Style aStyle)
-  {
-    if (aRect.IsEmpty()) {
-      return nullptr;
-    }
-
-    RefPtr<DrawTarget> dt =
-      mFinalTarget->CreateSimilarDrawTarget(aRect.Size(), SurfaceFormat::B8G8R8A8);
-    if (!dt) {
-      aRect.SetEmpty();
-      return nullptr;
-    }
-
-    Matrix transform =
-      mFinalTarget->GetTransform().PostTranslate(-aRect.TopLeft() + mOffset);
-
-    dt->SetTransform(transform);
-
-    if (transform.Invert()) {
-      gfx::Rect dtBounds(0, 0, aRect.width, aRect.height);
-      gfx::Rect fillRect = transform.TransformBounds(dtBounds);
-      dt->FillRect(fillRect, CanvasGeneralPattern().ForStyle(mCtx, aStyle, dt));
-    }
-    return dt->Snapshot();
-  }
-
-  ~AdjustedTargetForFilter()
-  {
-    if (!mCtx) {
-      return;
-    }
-
-    RefPtr<SourceSurface> snapshot = mTarget->Snapshot();
-
-    RefPtr<SourceSurface> fillPaint =
-      DoSourcePaint(mFillPaintRect, CanvasRenderingContext2D::Style::FILL);
-    RefPtr<SourceSurface> strokePaint =
-      DoSourcePaint(mStrokePaintRect, CanvasRenderingContext2D::Style::STROKE);
-
-    AutoRestoreTransform autoRestoreTransform(mFinalTarget);
-    mFinalTarget->SetTransform(Matrix());
-
-    MOZ_RELEASE_ASSERT(!mCtx->CurrentState().filter.mPrimitives.IsEmpty());
-    gfx::FilterSupport::RenderFilterDescription(
-      mFinalTarget, mCtx->CurrentState().filter,
-      gfx::Rect(mPostFilterBounds),
-      snapshot, mSourceGraphicRect,
-      fillPaint, mFillPaintRect,
-      strokePaint, mStrokePaintRect,
-      mCtx->CurrentState().filterAdditionalImages,
-      mPostFilterBounds.TopLeft() - mOffset,
-      DrawOptions(1.0f, mCompositionOp));
-
-    const gfx::FilterDescription& filter = mCtx->CurrentState().filter;
-    MOZ_RELEASE_ASSERT(!filter.mPrimitives.IsEmpty());
-    if (filter.mPrimitives.LastElement().IsTainted() && mCtx->mCanvasElement) {
-      mCtx->mCanvasElement->SetWriteOnly();
-    }
-  }
-
-  DrawTarget* DT()
-  {
-    return mTarget;
-  }
-
-private:
-  RefPtr<DrawTarget> mTarget;
-  RefPtr<DrawTarget> mFinalTarget;
-  CanvasRenderingContext2D *mCtx;
-  gfx::IntRect mSourceGraphicRect;
-  gfx::IntRect mFillPaintRect;
-  gfx::IntRect mStrokePaintRect;
-  gfx::IntRect mPostFilterBounds;
-  gfx::IntPoint mOffset;
-  gfx::CompositionOp mCompositionOp;
-};
-
-/* This is an RAII based class that can be used as a drawtarget for
- * operations that need to have a shadow applied to their results.
- * All coordinates passed to the constructor are in device space.
- */
-class AdjustedTargetForShadow
-{
-public:
-  typedef CanvasRenderingContext2D::ContextState ContextState;
-
-  AdjustedTargetForShadow(CanvasRenderingContext2D* aCtx,
-                          DrawTarget* aFinalTarget,
-                          const gfx::Rect& aBounds,
-                          gfx::CompositionOp aCompositionOp)
-    : mCtx(nullptr)
-    , mCompositionOp(aCompositionOp)
-  {
-    mCtx = aCtx;
-    mFinalTarget = aFinalTarget;
-
-    const ContextState &state = mCtx->CurrentState();
-
-    mSigma = state.ShadowBlurSigma();
-
-    gfx::Rect bounds = aBounds;
-
-    int32_t blurRadius = state.ShadowBlurRadius();
-
-    // We actually include the bounds of the shadow blur, this makes it
-    // easier to execute the actual blur on hardware, and shouldn't affect
-    // the amount of pixels that need to be touched.
-    bounds.Inflate(blurRadius);
-
-    bounds.RoundOut();
-    bounds.ToIntRect(&mTempRect);
-
-    mTarget =
-      mFinalTarget->CreateShadowDrawTarget(mTempRect.Size(),
-                                           SurfaceFormat::B8G8R8A8, mSigma);
-
-    if (!mTarget) {
-      // XXX - Deal with the situation where our temp size is too big to
-      // fit in a texture (bug 1066622).
-      mTarget = mFinalTarget;
-      mCtx = nullptr;
-      mFinalTarget = nullptr;
-    } else {
-      mTarget->SetTransform(
-        mFinalTarget->GetTransform().PostTranslate(-mTempRect.TopLeft()));
-    }
-  }
-
-  ~AdjustedTargetForShadow()
-  {
-    if (!mCtx) {
-      return;
-    }
-
-    RefPtr<SourceSurface> snapshot = mTarget->Snapshot();
-
-    mFinalTarget->DrawSurfaceWithShadow(snapshot, mTempRect.TopLeft(),
-                                        Color::FromABGR(mCtx->CurrentState().shadowColor),
-                                        mCtx->CurrentState().shadowOffset, mSigma,
-                                        mCompositionOp);
-  }
-
-  DrawTarget* DT()
-  {
-    return mTarget;
-  }
-
-  gfx::IntPoint OffsetToFinalDT()
-  {
-    return mTempRect.TopLeft();
-  }
-
-private:
-  RefPtr<DrawTarget> mTarget;
-  RefPtr<DrawTarget> mFinalTarget;
-  CanvasRenderingContext2D *mCtx;
-  Float mSigma;
-  gfx::IntRect mTempRect;
-  gfx::CompositionOp mCompositionOp;
-};
-
-/* This is an RAII based class that can be used as a drawtarget for
- * operations that need a shadow or a filter drawn. It will automatically
- * provide a temporary target when needed, and if so blend it back with a
- * shadow, filter, or both.
- * If both a shadow and a filter are needed, the filter is applied first,
- * and the shadow is applied to the filtered results.
- *
- * aBounds specifies the bounds of the drawing operation that will be
- * drawn to the target, it is given in device space! If this is nullptr the
- * drawing operation will be assumed to cover the whole canvas.
- */
-class AdjustedTarget
-{
-public:
-  typedef CanvasRenderingContext2D::ContextState ContextState;
-
-  explicit AdjustedTarget(CanvasRenderingContext2D* aCtx,
-                          const gfx::Rect *aBounds = nullptr)
-  {
-    // There are operations that can invalidate aCtx->mTarget along the way,
-    // so don't cache the pointer to it too soon.
-    mTarget = nullptr;
-
-    // All rects in this function are in the device space of ctx->mTarget.
-
-    // In order to keep our temporary surfaces as small as possible, we first
-    // calculate what their maximum required bounds would need to be if we
-    // were to fill the whole canvas. Everything outside those bounds we don't
-    // need to render.
-    gfx::Rect r(0, 0, aCtx->mWidth, aCtx->mHeight);
-    gfx::Rect maxSourceNeededBoundsForShadow =
-      MaxSourceNeededBoundsForShadow(r, aCtx);
-    gfx::Rect maxSourceNeededBoundsForFilter =
-      MaxSourceNeededBoundsForFilter(maxSourceNeededBoundsForShadow, aCtx);
-
-    gfx::Rect bounds = maxSourceNeededBoundsForFilter;
-    if (aBounds) {
-      bounds = bounds.Intersect(*aBounds);
-    }
-    gfx::Rect boundsAfterFilter = BoundsAfterFilter(bounds, aCtx);
-
-    mozilla::gfx::CompositionOp op = aCtx->CurrentState().op;
-
-    gfx::IntPoint offsetToFinalDT;
-
-    // First set up the shadow draw target, because the shadow goes outside.
-    // It applies to the post-filter results, if both a filter and a shadow
-    // are used.
-    if (aCtx->NeedToDrawShadow()) {
-      mShadowTarget = MakeUnique<AdjustedTargetForShadow>(
-        aCtx, aCtx->mTarget, boundsAfterFilter, op);
-      mTarget = mShadowTarget->DT();
-      offsetToFinalDT = mShadowTarget->OffsetToFinalDT();
-
-      // If we also have a filter, the filter needs to be drawn with OP_OVER
-      // because shadow drawing already applies op on the result.
-      op = gfx::CompositionOp::OP_OVER;
-    }
-
-    // Now set up the filter draw target.
-    if (aCtx->NeedToApplyFilter()) {
-      bounds.RoundOut();
-
-      if (!mTarget) {
-        mTarget = aCtx->mTarget;
-      }
-      gfx::IntRect intBounds;
-      if (!bounds.ToIntRect(&intBounds)) {
-        return;
-      }
-      mFilterTarget = MakeUnique<AdjustedTargetForFilter>(
-        aCtx, mTarget, offsetToFinalDT, intBounds,
-        gfx::RoundedToInt(boundsAfterFilter), op);
-      mTarget = mFilterTarget->DT();
-    }
-    if (!mTarget) {
-      mTarget = aCtx->mTarget;
-    }
-  }
-
-  ~AdjustedTarget()
-  {
-    // The order in which the targets are finalized is important.
-    // Filters are inside, any shadow applies to the post-filter results.
-    mFilterTarget.reset();
-    mShadowTarget.reset();
-  }
-
-  operator DrawTarget*()
-  {
-    return mTarget;
-  }
-
-  DrawTarget* operator->() MOZ_NO_ADDREF_RELEASE_ON_RETURN
-  {
-    return mTarget;
-  }
-
-private:
-
-  gfx::Rect
-  MaxSourceNeededBoundsForFilter(const gfx::Rect& aDestBounds, CanvasRenderingContext2D* aCtx)
-  {
-    if (!aCtx->NeedToApplyFilter()) {
-      return aDestBounds;
-    }
-
-    nsIntRegion sourceGraphicNeededRegion;
-    nsIntRegion fillPaintNeededRegion;
-    nsIntRegion strokePaintNeededRegion;
-
-    FilterSupport::ComputeSourceNeededRegions(
-      aCtx->CurrentState().filter, gfx::RoundedToInt(aDestBounds),
-      sourceGraphicNeededRegion, fillPaintNeededRegion, strokePaintNeededRegion);
-
-    return gfx::Rect(sourceGraphicNeededRegion.GetBounds());
-  }
-
-  gfx::Rect
-  MaxSourceNeededBoundsForShadow(const gfx::Rect& aDestBounds, CanvasRenderingContext2D* aCtx)
-  {
-    if (!aCtx->NeedToDrawShadow()) {
-      return aDestBounds;
-    }
-
-    const ContextState &state = aCtx->CurrentState();
-    gfx::Rect sourceBounds = aDestBounds - state.shadowOffset;
-    sourceBounds.Inflate(state.ShadowBlurRadius());
-
-    // Union the shadow source with the original rect because we're going to
-    // draw both.
-    return sourceBounds.Union(aDestBounds);
-  }
-
-  gfx::Rect
-  BoundsAfterFilter(const gfx::Rect& aBounds, CanvasRenderingContext2D* aCtx)
-  {
-    if (!aCtx->NeedToApplyFilter()) {
-      return aBounds;
-    }
-
-    gfx::Rect bounds(aBounds);
-    bounds.RoundOut();
-
-    gfx::IntRect intBounds;
-    if (!bounds.ToIntRect(&intBounds)) {
-      return gfx::Rect();
-    }
-
-    nsIntRegion extents =
-      gfx::FilterSupport::ComputePostFilterExtents(aCtx->CurrentState().filter,
-                                                   intBounds);
-    return gfx::Rect(extents.GetBounds());
-  }
-
-  RefPtr<DrawTarget> mTarget;
-  UniquePtr<AdjustedTargetForShadow> mShadowTarget;
-  UniquePtr<AdjustedTargetForFilter> mFilterTarget;
-};
-
 class CanvasShutdownObserver final : public nsIObserver
 {
 public:
   explicit CanvasShutdownObserver(CanvasRenderingContext2D* aCanvas)
   : mCanvas(aCanvas)
   {}
 
   NS_DECL_ISUPPORTS
@@ -966,18 +488,16 @@ static bool sMaxContextsInitialized = fa
 static int32_t sMaxContexts = 0;
 
 
 
 CanvasRenderingContext2D::CanvasRenderingContext2D(layers::LayersBackend aCompositorBackend)
   : BasicRenderingContext2D(aCompositorBackend)
   , mRenderingMode(RenderingMode::OpenGLBackendMode)
   , mCompositorBackend(aCompositorBackend)
-  // these are the default values from the Canvas spec
-  , mWidth(0), mHeight(0)
   , mZero(false), mOpaque(false)
   , mResetLayer(true)
   , mIPC(false)
   , mIsSkiaGL(false)
   , mHasPendingStableStateCallback(false)
   , mDrawObserver(nullptr)
   , mIsEntireFrameInvalid(false)
   , mPredictManyRedrawCalls(false)
@@ -2481,223 +2001,16 @@ CanvasRenderingContext2D::UpdateFilter()
                              CurrentState().fontExplicitLanguage,
                              presShell->GetPresContext()),
       gfxRect(0, 0, mWidth, mHeight),
       CurrentState().filterAdditionalImages);
   CurrentState().filterSourceGraphicTainted = sourceGraphicIsTainted;
 }
 
 //
-// rects
-//
-
-static bool
-ValidateRect(double& aX, double& aY, double& aWidth, double& aHeight, bool aIsZeroSizeValid)
-{
-  if (!aIsZeroSizeValid && (aWidth == 0.0 || aHeight == 0.0)) {
-    return false;
-  }
-
-  // bug 1018527
-  // The values of canvas API input are in double precision, but Moz2D APIs are
-  // using float precision. Bypass canvas API calls when the input is out of
-  // float precision to avoid precision problem
-  if (!std::isfinite((float)aX) | !std::isfinite((float)aY) |
-      !std::isfinite((float)aWidth) | !std::isfinite((float)aHeight)) {
-    return false;
-  }
-
-  // bug 1074733
-  // The canvas spec does not forbid rects with negative w or h, so given
-  // corners (x, y), (x+w, y), (x+w, y+h), and (x, y+h) we must generate
-  // the appropriate rect by flipping negative dimensions. This prevents
-  // draw targets from receiving "empty" rects later on.
-  if (aWidth < 0) {
-    aWidth = -aWidth;
-    aX -= aWidth;
-  }
-  if (aHeight < 0) {
-    aHeight = -aHeight;
-    aY -= aHeight;
-  }
-  return true;
-}
-
-void
-CanvasRenderingContext2D::ClearRect(double aX, double aY, double aW,
-                                    double aH)
-{
-  // Do not allow zeros - it's a no-op at that point per spec.
-  if (!ValidateRect(aX, aY, aW, aH, false)) {
-    return;
-  }
-
-  gfx::Rect clearRect(aX, aY, aW, aH);
-
-  EnsureTarget(&clearRect);
-
-  mTarget->ClearRect(clearRect);
-
-  RedrawUser(gfxRect(aX, aY, aW, aH));
-}
-
-void
-CanvasRenderingContext2D::FillRect(double aX, double aY, double aW,
-                                   double aH)
-{
-  const ContextState &state = CurrentState();
-
-  if (!ValidateRect(aX, aY, aW, aH, true)) {
-    return;
-  }
-
-  if (state.patternStyles[Style::FILL]) {
-    CanvasPattern::RepeatMode repeat =
-      state.patternStyles[Style::FILL]->mRepeat;
-    // In the FillRect case repeat modes are easy to deal with.
-    bool limitx = repeat == CanvasPattern::RepeatMode::NOREPEAT || repeat == CanvasPattern::RepeatMode::REPEATY;
-    bool limity = repeat == CanvasPattern::RepeatMode::NOREPEAT || repeat == CanvasPattern::RepeatMode::REPEATX;
-
-    IntSize patternSize =
-      state.patternStyles[Style::FILL]->mSurface->GetSize();
-
-    // We always need to execute painting for non-over operators, even if
-    // we end up with w/h = 0.
-    if (limitx) {
-      if (aX < 0) {
-        aW += aX;
-        if (aW < 0) {
-          aW = 0;
-        }
-
-        aX = 0;
-      }
-      if (aX + aW > patternSize.width) {
-        aW = patternSize.width - aX;
-        if (aW < 0) {
-          aW = 0;
-        }
-      }
-    }
-    if (limity) {
-      if (aY < 0) {
-        aH += aY;
-        if (aH < 0) {
-          aH = 0;
-        }
-
-        aY = 0;
-      }
-      if (aY + aH > patternSize.height) {
-        aH = patternSize.height - aY;
-        if (aH < 0) {
-          aH = 0;
-        }
-      }
-    }
-  }
-
-  CompositionOp op = UsedOperation();
-  bool discardContent = PatternIsOpaque(Style::FILL)
-    && (op == CompositionOp::OP_OVER || op == CompositionOp::OP_SOURCE);
-
-  const gfx::Rect fillRect(aX, aY, aW, aH);
-  EnsureTarget(discardContent ? &fillRect : nullptr);
-
-  gfx::Rect bounds;
-  if (NeedToCalculateBounds()) {
-    bounds = mTarget->GetTransform().TransformBounds(fillRect);
-  }
-
-  AntialiasMode antialiasMode = CurrentState().imageSmoothingEnabled ?
-                                AntialiasMode::DEFAULT : AntialiasMode::NONE;
-
-  AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
-    FillRect(gfx::Rect(aX, aY, aW, aH),
-             CanvasGeneralPattern().ForStyle(this, Style::FILL, mTarget),
-             DrawOptions(state.globalAlpha, op, antialiasMode));
-
-  RedrawUser(gfxRect(aX, aY, aW, aH));
-}
-
-void
-CanvasRenderingContext2D::StrokeRect(double aX, double aY, double aW,
-                                     double aH)
-{
-  const ContextState &state = CurrentState();
-
-  gfx::Rect bounds;
-
-  if (!aW && !aH) {
-    return;
-  }
-
-  if (!ValidateRect(aX, aY, aW, aH, true)) {
-    return;
-  }
-
-  EnsureTarget();
-  if (!IsTargetValid()) {
-    return;
-  }
-
-  if (NeedToCalculateBounds()) {
-    bounds = gfx::Rect(aX - state.lineWidth / 2.0f, aY - state.lineWidth / 2.0f,
-                       aW + state.lineWidth, aH + state.lineWidth);
-    bounds = mTarget->GetTransform().TransformBounds(bounds);
-  }
-
-  if (!aH) {
-    CapStyle cap = CapStyle::BUTT;
-    if (state.lineJoin == JoinStyle::ROUND) {
-      cap = CapStyle::ROUND;
-    }
-    AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
-      StrokeLine(Point(aX, aY), Point(aX + aW, aY),
-                  CanvasGeneralPattern().ForStyle(this, Style::STROKE, mTarget),
-                  StrokeOptions(state.lineWidth, state.lineJoin,
-                                cap, state.miterLimit,
-                                state.dash.Length(),
-                                state.dash.Elements(),
-                                state.dashOffset),
-                  DrawOptions(state.globalAlpha, UsedOperation()));
-    return;
-  }
-
-  if (!aW) {
-    CapStyle cap = CapStyle::BUTT;
-    if (state.lineJoin == JoinStyle::ROUND) {
-      cap = CapStyle::ROUND;
-    }
-    AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
-      StrokeLine(Point(aX, aY), Point(aX, aY + aH),
-                  CanvasGeneralPattern().ForStyle(this, Style::STROKE, mTarget),
-                  StrokeOptions(state.lineWidth, state.lineJoin,
-                                cap, state.miterLimit,
-                                state.dash.Length(),
-                                state.dash.Elements(),
-                                state.dashOffset),
-                  DrawOptions(state.globalAlpha, UsedOperation()));
-    return;
-  }
-
-  AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
-    StrokeRect(gfx::Rect(aX, aY, aW, aH),
-               CanvasGeneralPattern().ForStyle(this, Style::STROKE, mTarget),
-               StrokeOptions(state.lineWidth, state.lineJoin,
-                             state.lineCap, state.miterLimit,
-                             state.dash.Length(),
-                             state.dash.Elements(),
-                             state.dashOffset),
-               DrawOptions(state.globalAlpha, UsedOperation()));
-
-  Redraw();
-}
-
-//
 // path bits
 //
 
 void
 CanvasRenderingContext2D::BeginPath()
 {
   mPath = nullptr;
   mPathBuilder = nullptr;
--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -75,19 +75,16 @@ public:
   }
 
   void GetFilter(nsAString& aFilter)
   {
     aFilter = CurrentState().filterString;
   }
 
   void SetFilter(const nsAString& aFilter, mozilla::ErrorResult& aError);
-  void ClearRect(double aX, double aY, double aW, double aH) override;
-  void FillRect(double aX, double aY, double aW, double aH) override;
-  void StrokeRect(double aX, double aY, double aW, double aH) override;
   void BeginPath();
   void Fill(const CanvasWindingRule& aWinding);
   void Fill(const CanvasPath& aPath, const CanvasWindingRule& aWinding);
   void Stroke();
   void Stroke(const CanvasPath& aPath);
   void DrawFocusIfNeeded(mozilla::dom::Element& aElement, ErrorResult& aRv);
   bool DrawCustomFocusRing(mozilla::dom::Element& aElement);
   void Clip(const CanvasWindingRule& aWinding);
@@ -289,17 +286,17 @@ public:
                   const nsAString& aBgColor, uint32_t aFlags,
                   mozilla::ErrorResult& aError);
 
   bool SwitchRenderingMode(RenderingMode aRenderingMode);
 
   // Eventually this should be deprecated. Keeping for now to keep the binding functional.
   void Demote();
 
-  nsresult Redraw();
+  virtual nsresult Redraw() override;
 
   virtual int32_t GetWidth() const override;
   virtual int32_t GetHeight() const override;
   gfx::IntSize GetSize() const { return gfx::IntSize(mWidth, mHeight); }
 
   // nsICanvasRenderingContextInternal
   /**
     * Gets the pres shell from either the canvas element or the doc shell
@@ -352,17 +349,17 @@ public:
   /**
    * An abstract base class to be implemented by callers wanting to be notified
    * that a refresh has occurred. Callers must ensure an observer is removed
    * before it is destroyed.
    */
   virtual void DidRefresh() override;
 
   // this rect is in mTarget's current user space
-  void RedrawUser(const gfxRect& aR);
+  virtual void RedrawUser(const gfxRect& aR) override;
 
   // nsISupports interface + CC
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_AMBIGUOUS(CanvasRenderingContext2D,
                                                                    nsICanvasRenderingContextInternal)
 
   enum class CanvasMultiGetterType : uint8_t {
     STRING = 0,
@@ -411,16 +408,17 @@ public:
   bool GetHitRegionRect(Element* aElement, nsRect& aRect) override;
 
   void OnShutdown();
 
   // Check the global setup, as well as the compositor type:
   bool AllowOpenGLCanvas() const;
 
 protected:
+  HTMLCanvasElement* GetCanvasElement() override { return mCanvasElement; }
   nsresult GetImageDataArray(JSContext* aCx, int32_t aX, int32_t aY,
                              uint32_t aWidth, uint32_t aHeight,
                              JSObject** aRetval);
 
   nsresult PutImageData_explicit(int32_t aX, int32_t aY, uint32_t aW, uint32_t aH,
                                  dom::Uint8ClampedArray* aArray,
                                  bool aHasDirtyRect, int32_t aDirtyX, int32_t aDirtyY,
                                  int32_t aDirtyWidth, int32_t aDirtyHeight);
@@ -541,22 +539,16 @@ protected:
 
   /**
     * Returns the surface format this canvas should be allocated using. Takes
     * into account mOpaque, platform requirements, etc.
     */
   mozilla::gfx::SurfaceFormat GetSurfaceFormat() const;
 
   /**
-   * Returns true if we know for sure that the pattern for a given style is opaque.
-   * Usefull to know if we can discard the content below in certain situations.
-   */
-  bool PatternIsOpaque(Style aStyle) const;
-
-  /**
    * Update CurrentState().filter with the filter description for
    * CurrentState().filterChain.
    * Flushes the PresShell, so the world can change if you call this function.
    */
   void UpdateFilter();
 
   nsLayoutUtils::SurfaceFromElementResult
     CachedSurfaceFromElement(Element* aElement);
@@ -589,17 +581,16 @@ protected:
   static void AddDemotableContext(CanvasRenderingContext2D* aContext);
   static void RemoveDemotableContext(CanvasRenderingContext2D* aContext);
 
   RenderingMode mRenderingMode;
 
   layers::LayersBackend mCompositorBackend;
 
   // Member vars
-  int32_t mWidth, mHeight;
 
   // This is true when the canvas is valid, but of zero size, this requires
   // specific behavior on some operations.
   bool mZero;
 
   bool mOpaque;
 
   // This is true when the next time our layer is retrieved we need to
@@ -693,34 +684,20 @@ protected:
     RefPtr<Element> mElement;
     // Path of the hit region in the 2d context coordinate space (not user space)
     RefPtr<gfx::Path> mPath;
   };
 
   nsTArray<RegionInfo> mHitRegionsOptions;
 
   /**
-    * Returns true if a shadow should be drawn along with a
-    * drawing operation.
-    */
-  bool NeedToDrawShadow()
-  {
-    const ContextState& state = CurrentState();
-
-    // The spec says we should not draw shadows if the operator is OVER.
-    // If it's over and the alpha value is zero, nothing needs to be drawn.
-    return NS_GET_A(state.shadowColor) != 0 &&
-      (state.shadowBlur != 0.f || state.shadowOffset.x != 0.f || state.shadowOffset.y != 0.f);
-  }
-
-  /**
     * Returns true if the result of a drawing operation should be
     * drawn with a filter.
     */
-  bool NeedToApplyFilter()
+  virtual bool NeedToApplyFilter() override
   {
     return EnsureUpdatedFilter().mPrimitives.Length() > 0;
   }
 
   /**
    * Calls UpdateFilter if the canvas's WriteOnly state has changed between the
    * last call to UpdateFilter and now.
    */
@@ -729,33 +706,16 @@ protected:
     if (CurrentState().filterSourceGraphicTainted != isWriteOnly) {
       UpdateFilter();
       EnsureTarget();
     }
     MOZ_ASSERT(CurrentState().filterSourceGraphicTainted == isWriteOnly);
     return CurrentState().filter;
   }
 
-  bool NeedToCalculateBounds()
-  {
-    return NeedToDrawShadow() || NeedToApplyFilter();
-  }
-
-  mozilla::gfx::CompositionOp UsedOperation()
-  {
-    if (NeedToDrawShadow() || NeedToApplyFilter()) {
-      // In this case the shadow or filter rendering will use the operator.
-      return mozilla::gfx::CompositionOp::OP_OVER;
-    }
-
-    return CurrentState().op;
-  }
-
-  // text
-
 protected:
   enum class TextDrawOperation : uint8_t {
     FILL,
     STROKE,
     MEASURE
   };
 
 protected:
@@ -769,17 +729,16 @@ protected:
                              float aX,
                              float aY,
                              const Optional<double>& aMaxWidth,
                              TextDrawOperation aOp,
                              float* aWidth);
 
   bool CheckSizeForSkiaGL(mozilla::gfx::IntSize aSize);
 
-  friend class CanvasGeneralPattern;
   friend class CanvasFilterChainObserver;
   friend class AdjustedTarget;
   friend class AdjustedTargetForShadow;
   friend class AdjustedTargetForFilter;
 
   // other helpers
   void GetAppUnitsValues(int32_t* aPerDevPixel, int32_t* aPerCSSPixel)
   {
--- a/dom/canvas/WebGLFramebuffer.cpp
+++ b/dom/canvas/WebGLFramebuffer.cpp
@@ -1942,16 +1942,36 @@ template<typename C>
 inline void
 ImplCycleCollectionUnlink(C& field)
 {
     for (auto& cur : field) {
         cur.Unlink();
     }
 }
 
+void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback,
+                            std::vector<IndexedBufferBinding>& field,
+                            const char* name, uint32_t flags)
+{
+  for (const auto& cur : field) {
+    ImplCycleCollectionTraverse(callback, cur.mBufferBinding, name, flags);
+  }
+}
+
+template <typename T>
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback,
+                            WebGLRefPtr<T>& field,
+                            const char* name,
+                            uint32_t flags = 0)
+{
+  CycleCollectionNoteChild(callback, field.get(), name, flags);
+}
+
 template<typename C>
 inline void
 ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback,
                             const C& field,
                             const char* name,
                             uint32_t flags = 0)
 {
     for (auto& cur : field) {
--- a/dom/canvas/moz.build
+++ b/dom/canvas/moz.build
@@ -68,16 +68,17 @@ EXPORTS.mozilla.dom += [
     'ImageData.h',
     'OffscreenCanvas.h',
     'TextMetrics.h',
     'WebGLVertexArrayObject.h',
 ]
 
 # Canvas 2D and common sources
 UNIFIED_SOURCES += [
+    'AdjustedTarget.cpp',
     'BasicRenderingContext2D.cpp',
     'CanvasGradient.cpp',
     'CanvasImageCache.cpp',
     'CanvasPattern.cpp',
     'CanvasRenderingContext2D.cpp',
     'CanvasRenderingContextHelper.cpp',
     'CanvasUtils.cpp',
     'DocumentRendererChild.cpp',