Bug 1318573 - (Part 12) Move EnsureDrawTarget and shutdown observer to BasicRenderingContext2D. r?mstange draft
authorKevin Chen <kechen@mozilla.com>
Thu, 02 Mar 2017 14:40:50 +0800
changeset 499865 8e7a965eb7d9dd8f8f41d500ba0b2864a390a3d9
parent 499864 6ff5325752ff8e1c92559d055385ce2b653e50a5
child 499866 82c292ffa42a4d6ca48d95c8e34265adca55682b
push id49564
push userbmo:kechen@mozilla.com
push dateThu, 16 Mar 2017 09:50:15 +0000
reviewersmstange
bugs1318573
milestone54.0a1
Bug 1318573 - (Part 12) Move EnsureDrawTarget and shutdown observer to BasicRenderingContext2D. r?mstange MozReview-Commit-ID: 6RuJ2WQw28h
dom/canvas/BasicRenderingContext2D.cpp
dom/canvas/BasicRenderingContext2D.h
dom/canvas/CanvasRenderingContext2D.cpp
dom/canvas/CanvasRenderingContext2D.h
--- a/dom/canvas/BasicRenderingContext2D.cpp
+++ b/dom/canvas/BasicRenderingContext2D.cpp
@@ -2,84 +2,511 @@
  * 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 "CanvasImageCache.h"
 #include "CanvasUtils.h"
 #include "DrawResult.h"
+#include "gfxPrefs.h"
 #include "gfxUtils.h"
 #include "GLContext.h"
 #include "ImageRegion.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 "mozilla/gfx/PathHelpers.h"
+#include "mozilla/layers/PersistentBufferProvider.h"
 #include "nsContentUtils.h"
 #include "nsICanvasRenderingContextInternal.h"
+#include "nsIMemoryReporter.h"
 #include "nsPrintfCString.h"
 #include "nsStyleUtil.h"
 
 #include "SkiaGLGlue.h"
 #ifdef USE_SKIA
 #include "GLBlitHelper.h"
 #include "SurfaceTypes.h"
 #endif
 
 using mozilla::CanvasUtils::FloatValidate;
 using mozilla::gfx::AntialiasMode;
 using mozilla::gfx::ArcToBezier;
 using mozilla::gfx::CapStyle;
 using mozilla::gfx::Color;
+using mozilla::gfx::CriticalLog;
 using mozilla::gfx::DrawOptions;
 using mozilla::gfx::DrawSurfaceOptions;
 using mozilla::gfx::ExtendMode;
+using mozilla::gfx::Factory;
 using mozilla::gfx::FillRule;
 using mozilla::gfx::IntPoint;
+using mozilla::gfx::IntRect;
 using mozilla::gfx::JoinStyle;
 using mozilla::gfx::LogReason;
 using mozilla::gfx::IntSize;
 using mozilla::gfx::NativeSurface;
 using mozilla::gfx::NativeSurfaceType;
 using mozilla::gfx::Path;
 using mozilla::gfx::SamplingBounds;
 using mozilla::gfx::SamplingFilter;
 using mozilla::gfx::Size;
 using mozilla::gfx::SourceSurface;
 using mozilla::gfx::StrokeOptions;
 using mozilla::gfx::ToDeviceColor;
 using mozilla::image::DrawResult;
 using mozilla::image::ImageRegion;
 using mozilla::layers::AutoLockImage;
+using mozilla::layers::PersistentBufferProvider;
+using mozilla::layers::PersistentBufferProviderBasic;
 
 namespace mozilla {
 namespace dom{
 
+// This is KIND_OTHER because it's not always clear where in memory the pixels
+// of a canvas are stored.  Furthermore, this memory will be tracked by the
+// underlying surface implementations.  See bug 655638 for details.
+class Canvas2dPixelsReporter final : public nsIMemoryReporter
+{
+  ~Canvas2dPixelsReporter() {}
+public:
+  NS_DECL_ISUPPORTS
+
+  NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+                            nsISupports* aData, bool aAnonymize) override
+  {
+    MOZ_COLLECT_REPORT(
+      "canvas-2d-pixels", KIND_OTHER, UNITS_BYTES,
+      BasicRenderingContext2D::sCanvasAzureMemoryUsed,
+      "Memory used by 2D canvases. Each canvas requires "
+      "(width * height * 4) bytes.");
+
+    return NS_OK;
+  }
+};
+
+NS_IMPL_ISUPPORTS(Canvas2dPixelsReporter, nsIMemoryReporter)
+
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CanvasGradient, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CanvasGradient, Release)
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CanvasGradient, mContext)
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CanvasPattern, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CanvasPattern, Release)
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CanvasPattern, mContext)
 
+NS_IMPL_ISUPPORTS(CanvasShutdownObserver, nsIObserver)
+
+NS_IMETHODIMP
+CanvasShutdownObserver::Observe(nsISupports *aSubject,
+                                const char *aTopic,
+                                const char16_t *aData)
+{
+  if (mCanvas && strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
+    mCanvas->OnShutdown();
+    nsContentUtils::UnregisterShutdownObserver(this);
+  }
+
+  return NS_OK;
+}
 
 // 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
  **/
+
+// Initialize our static variables.
+uint32_t BasicRenderingContext2D::sNumLivingContexts = 0;
+DrawTarget* BasicRenderingContext2D::sErrorTarget = nullptr;
+int64_t BasicRenderingContext2D::sCanvasAzureMemoryUsed = 0;
+
+BasicRenderingContext2D::BasicRenderingContext2D(layers::LayersBackend aCompositorBackend)
+  : mRenderingMode(RenderingMode::OpenGLBackendMode)
+  , mCompositorBackend(aCompositorBackend)
+    // these are the default values from the Canvas spec
+  , mWidth(0), mHeight(0)
+  , mPathTransformWillUpdate(false)
+  , mIsSkiaGL(false)
+  , mHasPendingStableStateCallback(false)
+{
+  sNumLivingContexts++;
+
+  mShutdownObserver = new CanvasShutdownObserver(this);
+  nsContentUtils::RegisterShutdownObserver(mShutdownObserver);
+}
+
+BasicRenderingContext2D::~BasicRenderingContext2D()
+{
+  RemoveShutdownObserver();
+  Reset();
+
+  sNumLivingContexts--;
+  if (!sNumLivingContexts) {
+    NS_IF_RELEASE(sErrorTarget);
+  }
+}
+
+nsresult
+BasicRenderingContext2D::Reset()
+{
+  // only do this for non-docshell created contexts,
+  // since those are the ones that we created a surface for
+  if (mTarget && IsTargetValid()) {
+    sCanvasAzureMemoryUsed -= mWidth * mHeight * 4;
+  }
+
+  bool forceReset = true;
+  ReturnTarget(forceReset);
+  mTarget = nullptr;
+  mBufferProvider = nullptr;
+
+  return NS_OK;
+}
+
+void
+BasicRenderingContext2D::RemoveShutdownObserver()
+{
+  if (mShutdownObserver) {
+    nsContentUtils::UnregisterShutdownObserver(mShutdownObserver);
+    mShutdownObserver = nullptr;
+  }
+}
+
+void
+BasicRenderingContext2D::OnShutdown()
+{
+  mShutdownObserver = nullptr;
+
+  RefPtr<PersistentBufferProvider> provider = mBufferProvider;
+
+  Reset();
+
+  if (provider) {
+    provider->OnShutdown();
+  }
+}
+
+BasicRenderingContext2D::RenderingMode
+BasicRenderingContext2D::EnsureTarget(const gfx::Rect* aCoveredRect,
+                                      RenderingMode aRenderingMode)
+{
+  if (AlreadyShutDown()) {
+    gfxCriticalError() << "Attempt to render into a Canvas2d after shutdown.";
+    SetErrorState();
+    return aRenderingMode;
+  }
+
+  // This would make no sense, so make sure we don't get ourselves in a mess
+  MOZ_ASSERT(mRenderingMode != RenderingMode::DefaultBackendMode);
+
+  RenderingMode mode = (aRenderingMode == RenderingMode::DefaultBackendMode) ? mRenderingMode : aRenderingMode;
+
+  if (mTarget && mode == mRenderingMode) {
+    return mRenderingMode;
+  }
+
+  // Check that the dimensions are sane
+  if (mWidth > gfxPrefs::MaxCanvasSize() ||
+      mHeight > gfxPrefs::MaxCanvasSize() ||
+      mWidth < 0 || mHeight < 0) {
+
+    SetErrorState();
+    return aRenderingMode;
+  }
+
+  // If the next drawing command covers the entire canvas, we can skip copying
+  // from the previous frame and/or clearing the canvas.
+  gfx::Rect canvasRect(0, 0, mWidth, mHeight);
+  bool canDiscardContent = aCoveredRect &&
+    CurrentState().transform.TransformBounds(*aCoveredRect).Contains(canvasRect);
+
+  // If a clip is active we don't know for sure that the next drawing command
+  // will really cover the entire canvas.
+  for (const auto& style : mStyleStack) {
+    if (!canDiscardContent) {
+      break;
+    }
+    for (const auto& clipOrTransform : style.clipsAndTransforms) {
+      if (clipOrTransform.IsClip()) {
+        canDiscardContent = false;
+        break;
+      }
+    }
+  }
+
+  ScheduleStableStateCallback();
+
+  IntRect persistedRect = canDiscardContent ? IntRect()
+                                            : IntRect(0, 0, mWidth, mHeight);
+
+  if (mBufferProvider && mode == mRenderingMode) {
+    mTarget = mBufferProvider->BorrowDrawTarget(persistedRect);
+
+    if (mTarget && !mBufferProvider->PreservesDrawingState()) {
+      RestoreClipsAndTransformToTarget();
+    }
+
+    if (mTarget) {
+      return mode;
+    }
+  }
+
+  RefPtr<DrawTarget> newTarget;
+  RefPtr<PersistentBufferProvider> newProvider;
+
+  if (mode == RenderingMode::OpenGLBackendMode &&
+      !TrySkiaGLTarget(newTarget, newProvider)) {
+    // Fall back to software.
+    mode = RenderingMode::SoftwareBackendMode;
+  }
+
+  if (mode == RenderingMode::SoftwareBackendMode &&
+      !TrySharedTarget(newTarget, newProvider) &&
+      !TryBasicTarget(newTarget, newProvider)) {
+
+    gfxCriticalError(
+      CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(GetSize()))
+    ) << "Failed borrow shared and basic targets.";
+
+    SetErrorState();
+    return mode;
+  }
+
+
+  MOZ_ASSERT(newTarget);
+  MOZ_ASSERT(newProvider);
+
+  bool needsClear = !canDiscardContent;
+  if (newTarget->GetBackendType() == gfx::BackendType::SKIA) {
+    // Skia expects the unused X channel to contains 0xFF even for opaque operations
+    // so we can't skip clearing in that case, even if we are going to cover the
+    // entire canvas in the next drawing operation.
+    newTarget->ClearRect(canvasRect);
+    needsClear = false;
+  }
+
+  // Try to copy data from the previous buffer provider if there is one.
+  if (!canDiscardContent && mBufferProvider && CopyBufferProvider(*mBufferProvider, *newTarget, persistedRect)) {
+    needsClear = false;
+  }
+
+  if (needsClear) {
+    newTarget->ClearRect(canvasRect);
+  }
+
+  mTarget = newTarget.forget();
+  mBufferProvider = newProvider.forget();
+
+  RegisterAllocation();
+
+  RestoreClipsAndTransformToTarget();
+
+  // Force a full layer transaction since we didn't have a layer before
+  // and now we might need one.
+  if (GetCanvasElement()) {
+    GetCanvasElement()->InvalidateCanvas();
+  }
+  // Calling Redraw() tells our invalidation machinery that the entire
+  // canvas is already invalid, which can speed up future drawing.
+  Redraw();
+
+  return mode;
+}
+
+void
+BasicRenderingContext2D::RegisterAllocation()
+{
+  // XXX - It would make more sense to track the allocation in
+  // PeristentBufferProvider, rather than here.
+  static bool registered = false;
+  // FIXME: Disable the reporter for now, see bug 1241865
+  if (!registered && false) {
+    registered = true;
+    RegisterStrongMemoryReporter(new Canvas2dPixelsReporter());
+  }
+
+  sCanvasAzureMemoryUsed += mWidth * mHeight * 4;
+  JSContext* context = nsContentUtils::GetCurrentJSContext();
+  if (context) {
+    JS_updateMallocCounter(context, mWidth * mHeight * 4);
+  }
+
+  JSObject* wrapper = GetWrapperPreserveColor();
+  if (wrapper) {
+    CycleCollectedJSContext::Get()->
+      AddZoneWaitingForGC(JS::GetObjectZone(wrapper));
+  }
+}
+
+bool
+BasicRenderingContext2D::TryBasicTarget(RefPtr<gfx::DrawTarget>& aOutDT,
+                                        RefPtr<layers::PersistentBufferProvider>& aOutProvider)
+{
+  aOutDT = gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget(GetSize(),
+                                                                       GetSurfaceFormat());
+  if (!aOutDT) {
+    return false;
+  }
+
+  aOutProvider = new PersistentBufferProviderBasic(aOutDT);
+  return true;
+}
+
+bool
+BasicRenderingContext2D::CopyBufferProvider(PersistentBufferProvider& aOld,
+                                            DrawTarget& aTarget,
+                                            IntRect aCopyRect)
+{
+  // Borrowing the snapshot must be done after ReturnTarget.
+  RefPtr<SourceSurface> snapshot = aOld.BorrowSnapshot();
+
+  if (!snapshot) {
+    return false;
+  }
+
+  aTarget.CopySurface(snapshot, aCopyRect, IntPoint());
+  aOld.ReturnSnapshot(snapshot.forget());
+  return true;
+}
+
+void
+BasicRenderingContext2D::RestoreClipsAndTransformToTarget()
+{
+  // Restore clips and transform.
+  mTarget->SetTransform(Matrix());
+
+  if (mTarget->GetBackendType() == gfx::BackendType::CAIRO) {
+    // Cairo doesn't play well with huge clips. When given a very big clip it
+    // will try to allocate big mask surface without taking the target
+    // size into account which can cause OOM. See bug 1034593.
+    // This limits the clip extents to the size of the canvas.
+    // A fix in Cairo would probably be preferable, but requires somewhat
+    // invasive changes.
+    mTarget->PushClipRect(gfx::Rect(0, 0, mWidth, mHeight));
+  }
+
+  for (const auto& style : mStyleStack) {
+    for (const auto& clipOrTransform : style.clipsAndTransforms) {
+      if (clipOrTransform.IsClip()) {
+        mTarget->PushClip(clipOrTransform.clip);
+      } else {
+        mTarget->SetTransform(clipOrTransform.transform);
+      }
+    }
+  }
+}
+
+void
+BasicRenderingContext2D::ReturnTarget(bool aForceReset)
+{
+  if (mTarget && mBufferProvider && mTarget != sErrorTarget) {
+    CurrentState().transform = mTarget->GetTransform();
+    if (aForceReset || !mBufferProvider->PreservesDrawingState()) {
+      for (const auto& style : mStyleStack) {
+        for (const auto& clipOrTransform : style.clipsAndTransforms) {
+          if (clipOrTransform.IsClip()) {
+            mTarget->PopClip();
+          }
+        }
+      }
+
+      if (mTarget->GetBackendType() == gfx::BackendType::CAIRO) {
+        // With the cairo backend we pushed an extra clip rect which we have to
+        // balance out here. See the comment in RestoreClipsAndTransformToTarget.
+        mTarget->PopClip();
+      }
+
+      mTarget->SetTransform(Matrix());
+    }
+
+    mBufferProvider->ReturnDrawTarget(mTarget.forget());
+  }
+}
+
+void
+BasicRenderingContext2D::ScheduleStableStateCallback()
+{
+  if (mHasPendingStableStateCallback) {
+    return;
+  }
+  mHasPendingStableStateCallback = true;
+
+  nsContentUtils::RunInStableState(
+    NewRunnableMethod(this, &BasicRenderingContext2D::OnStableState)
+  );
+}
+
+void
+BasicRenderingContext2D::OnStableState()
+{
+  if (!mHasPendingStableStateCallback) {
+    return;
+  }
+
+  ReturnTarget();
+
+  mHasPendingStableStateCallback = false;
+}
+
+void
+BasicRenderingContext2D::SetErrorState()
+{
+  EnsureErrorTarget();
+
+  if (mTarget && mTarget != sErrorTarget) {
+    sCanvasAzureMemoryUsed -= mWidth * mHeight * 4;
+  }
+
+  mTarget = sErrorTarget;
+  mBufferProvider = nullptr;
+
+  // clear transforms, clips, etc.
+  SetInitialState();
+}
+
+void
+BasicRenderingContext2D::SetInitialState()
+{
+  // Set up the initial canvas defaults
+  mPathBuilder = nullptr;
+  mPath = nullptr;
+  mDSPathBuilder = nullptr;
+
+  mStyleStack.Clear();
+  ContextState *state = mStyleStack.AppendElement();
+  state->globalAlpha = 1.0;
+
+  state->colorStyles[Style::FILL] = NS_RGB(0,0,0);
+  state->colorStyles[Style::STROKE] = NS_RGB(0,0,0);
+  state->shadowColor = NS_RGBA(0,0,0,0);
+}
+
+void
+BasicRenderingContext2D::EnsureErrorTarget()
+{
+  if (sErrorTarget) {
+    return;
+  }
+
+  RefPtr<DrawTarget> errorTarget = gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget(IntSize(1, 1), SurfaceFormat::B8G8R8A8);
+  MOZ_ASSERT(errorTarget, "Failed to allocate the error target!");
+
+  sErrorTarget = errorTarget;
+  NS_ADDREF(sErrorTarget);
+}
+
 bool
 BasicRenderingContext2D::PatternIsOpaque(BasicRenderingContext2D::Style aStyle) const
 {
   const ContextState& state = CurrentState();
   if (state.globalAlpha < 1.0) {
     return false;
   }
 
@@ -93,17 +520,16 @@ BasicRenderingContext2D::PatternIsOpaque
   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];
   } else if (state.gradientStyles[aWhichStyle]) {
--- a/dom/canvas/BasicRenderingContext2D.h
+++ b/dom/canvas/BasicRenderingContext2D.h
@@ -2,16 +2,17 @@
  * 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 BasicRenderingContext2D_h
 #define BasicRenderingContext2D_h
 
 #include "FilterSupport.h"
 #include "gfxTextRun.h"
+#include "Layers.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"
 
@@ -30,33 +31,54 @@ namespace dom {
   { 0x89, 0x95, 0x1c, 0x6a, 0x82, 0xeb, 0xef, 0x9f} }
 
 class HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElementOrImageBitmap;
 typedef HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElementOrImageBitmap
   CanvasImageSource;
 
 extern const mozilla::gfx::Float SIGMA_MAX;
 
+class CanvasShutdownObserver final : public nsIObserver
+{
+public:
+  explicit CanvasShutdownObserver(BasicRenderingContext2D* aCanvas)
+  : mCanvas(aCanvas)
+  {}
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+private:
+  ~CanvasShutdownObserver() {}
+
+  BasicRenderingContext2D* mCanvas;
+};
+
 /*
  * BasicRenderingContext2D
  */
-class BasicRenderingContext2D : public nsISupports
+class BasicRenderingContext2D :
+  public nsISupports,
+  public nsWrapperCache
 {
 public:
   enum RenderingMode {
     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<DrawTarget> mTarget;
 
+  void OnShutdown();
+
+  gfx::IntSize GetSize() const { return gfx::IntSize(mWidth, mHeight); }
+
   // this rect is in mTarget's current user space
   virtual void RedrawUser(const gfxRect& aR) = 0;
 
   virtual nsresult Redraw() = 0;
 
   void LineTo(const mozilla::gfx::Point& aPoint)
   {
     if (mPathBuilder) {
@@ -80,25 +102,19 @@ public:
     }
   }
 
   // Check the global setup, as well as the compositor type:
   bool AllowOpenGLCanvas() const;
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_BASICRENDERINGCONTEXT2D_IID)
 protected:
-  virtual ~BasicRenderingContext2D() {}
+  virtual ~BasicRenderingContext2D();
 public:
-  explicit BasicRenderingContext2D(layers::LayersBackend aCompositorBackend)
-    : mRenderingMode(RenderingMode::OpenGLBackendMode)
-    , mCompositorBackend(aCompositorBackend)
-      // these are the default values from the Canvas spec
-    , mWidth(0), mHeight(0)
-    , mPathTransformWillUpdate(false)
-    , mIsSkiaGL(false) {}
+  explicit BasicRenderingContext2D(layers::LayersBackend aCompositorBackend);
 
   //
   // CanvasState
   //
   void Save();
   void Restore();
 
   //
@@ -379,20 +395,21 @@ public:
   void Arc(double aX, double aY, double aRadius, double aStartAngle,
            double aEndAngle, bool aAnticlockwise,
            mozilla::ErrorResult& aError);
   void Ellipse(double aX, double aY, double aRadiusX, double aRadiusY,
                double aRotation, double aStartAngle, double aEndAngle,
                bool aAnticlockwise, ErrorResult& aError);
 
 protected:
+  friend class AdjustedTarget;
+  friend class AdjustedTargetForFilter;
+  friend class AdjustedTargetForShadow;
+  friend class Canvas2dPixelsReporter;
   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 {
@@ -578,16 +595,18 @@ protected:
   RenderingMode mRenderingMode;
 
   layers::LayersBackend mCompositorBackend;
 
   AutoTArray<ContextState, 3> mStyleStack;
 
   int32_t mWidth, mHeight;
 
+  RefPtr<mozilla::layers::PersistentBufferProvider> mBufferProvider;
+
   /**
     * 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.
     *
@@ -637,37 +656,107 @@ protected:
     * mPath is always in user-space.
     */
   RefPtr<mozilla::gfx::PathBuilder> mDSPathBuilder;
 
   // True if the current DrawTarget is using skia-gl, used so we can avoid
   // requesting the DT from mBufferProvider to check.
   bool mIsSkiaGL;
 
+  /**
+    * The number of living nsCanvasRenderingContexts.  When this goes down to
+    * 0, we free the premultiply and unpremultiply tables, if they exist.
+    */
+  static uint32_t sNumLivingContexts;
+  static int64_t sCanvasAzureMemoryUsed;
+
+  bool mHasPendingStableStateCallback;
+
+  static mozilla::gfx::DrawTarget* sErrorTarget;
+
 protected:
   virtual HTMLCanvasElement* GetCanvasElement() = 0;
 
-  virtual bool AlreadyShutDown() const = 0;
+  NS_IMETHOD Reset();
+
+  RefPtr<CanvasShutdownObserver> mShutdownObserver;
+  void RemoveShutdownObserver();
+  bool AlreadyShutDown() const { return !mShutdownObserver; }
 
   /**
    * 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.
    *
    * Returns the actual rendering mode being used by the created target.
    */
-  virtual RenderingMode
+  RenderingMode
   EnsureTarget(const gfx::Rect* aCoveredRect = nullptr,
-               RenderingMode aRenderMode = RenderingMode::DefaultBackendMode) = 0;
+               RenderingMode aRenderMode = RenderingMode::DefaultBackendMode);
+
+  virtual bool
+  TrySkiaGLTarget(RefPtr<gfx::DrawTarget>& aOutDT,
+                  RefPtr<layers::PersistentBufferProvider>& aOutProvider) = 0;
+
+  virtual bool
+  TrySharedTarget(RefPtr<gfx::DrawTarget>& aOutDT,
+                  RefPtr<layers::PersistentBufferProvider>& aOutProvider) = 0;
+
+  bool TryBasicTarget(RefPtr<gfx::DrawTarget>& aOutDT,
+                      RefPtr<layers::PersistentBufferProvider>& aOutProvider);
+
+  void RegisterAllocation();
+
+  bool CopyBufferProvider(layers::PersistentBufferProvider& aOld,
+                          gfx::DrawTarget& aTarget,
+                          gfx::IntRect aCopyRect);
+
+  void RestoreClipsAndTransformToTarget();
+
+  /*
+   * Returns the target to the buffer provider. i.e. this will queue a frame for
+   * rendering.
+   */
+  void ReturnTarget(bool aForceReset = false);
+
+  /**
+   * Cf. OnStableState.
+   */
+  void ScheduleStableStateCallback();
+
+  /**
+    * Returns the surface format this canvas should be allocated using. Takes
+    * into account mOpaque, platform requirements, etc.
+    */
+  virtual mozilla::gfx::SurfaceFormat GetSurfaceFormat() const = 0;
+
+  /**
+   * This method is run at the end of the event-loop spin where
+   * ScheduleStableStateCallback was called.
+   *
+   * We use it to unlock resources that need to be locked while drawing.
+   */
+  void OnStableState();
+
+  /**
+   * Creates the error target, if it doesn't exist
+   */
+  static void EnsureErrorTarget();
+
+  void SetErrorState();
+
+  void SetInitialState();
 
   /**
    * Check if the target is valid after calling EnsureTarget.
    */
-  virtual bool IsTargetValid() const = 0;
+  bool IsTargetValid() const {
+    return !!mTarget && mTarget != sErrorTarget;
+  }
 
   inline ContextState& CurrentState() {
     return mStyleStack[mStyleStack.Length() - 1];
   }
 
   inline const ContextState& CurrentState() const {
     return mStyleStack[mStyleStack.Length() - 1];
   }
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -52,17 +52,16 @@
 #include "gfxBlur.h"
 #include "gfxPrefs.h"
 
 #include "nsFrameLoader.h"
 #include "nsBidi.h"
 #include "nsBidiPresUtils.h"
 #include "Layers.h"
 #include "LayerUserData.h"
-#include "nsIMemoryReporter.h"
 #include "CanvasImageCache.h"
 
 #include <algorithm>
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "js/Conversions.h"
 #include "js/HeapAPI.h"
@@ -148,72 +147,16 @@ using namespace mozilla::css;
 using namespace mozilla::gfx;
 using namespace mozilla::image;
 using namespace mozilla::ipc;
 using namespace mozilla::layers;
 
 namespace mozilla {
 namespace dom {
 
-/* Memory reporter stuff */
-static int64_t gCanvasAzureMemoryUsed = 0;
-
-// This is KIND_OTHER because it's not always clear where in memory the pixels
-// of a canvas are stored.  Furthermore, this memory will be tracked by the
-// underlying surface implementations.  See bug 655638 for details.
-class Canvas2dPixelsReporter final : public nsIMemoryReporter
-{
-  ~Canvas2dPixelsReporter() {}
-public:
-  NS_DECL_ISUPPORTS
-
-  NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
-                            nsISupports* aData, bool aAnonymize) override
-  {
-    MOZ_COLLECT_REPORT(
-      "canvas-2d-pixels", KIND_OTHER, UNITS_BYTES, gCanvasAzureMemoryUsed,
-      "Memory used by 2D canvases. Each canvas requires "
-      "(width * height * 4) bytes.");
-
-    return NS_OK;
-  }
-};
-
-NS_IMPL_ISUPPORTS(Canvas2dPixelsReporter, nsIMemoryReporter)
-
-class CanvasShutdownObserver final : public nsIObserver
-{
-public:
-  explicit CanvasShutdownObserver(CanvasRenderingContext2D* aCanvas)
-  : mCanvas(aCanvas)
-  {}
-
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIOBSERVER
-private:
-  ~CanvasShutdownObserver() {}
-
-  CanvasRenderingContext2D* mCanvas;
-};
-
-NS_IMPL_ISUPPORTS(CanvasShutdownObserver, nsIObserver)
-
-NS_IMETHODIMP
-CanvasShutdownObserver::Observe(nsISupports *aSubject,
-                                const char *aTopic,
-                                const char16_t *aData)
-{
-  if (mCanvas && strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
-    mCanvas->OnShutdown();
-    nsContentUtils::UnregisterShutdownObserver(this);
-  }
-
-  return NS_OK;
-}
-
 class CanvasDrawObserver
 {
 public:
   explicit CanvasDrawObserver(CanvasRenderingContext2D* aCanvasContext);
 
   // Only enumerate draw calls that could affect the heuristic
   enum DrawCallType {
     PutImageData,
@@ -475,67 +418,56 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
 NS_INTERFACE_MAP_END
 
 /**
  ** CanvasRenderingContext2D impl
  **/
 
 
 // Initialize our static variables.
-uint32_t CanvasRenderingContext2D::sNumLivingContexts = 0;
-DrawTarget* CanvasRenderingContext2D::sErrorTarget = nullptr;
 static bool sMaxContextsInitialized = false;
 static int32_t sMaxContexts = 0;
 
 
 
 CanvasRenderingContext2D::CanvasRenderingContext2D(layers::LayersBackend aCompositorBackend)
   : BasicRenderingContext2D(aCompositorBackend)
   , mZero(false), mOpaque(false)
   , mResetLayer(true)
   , mIPC(false)
-  , mHasPendingStableStateCallback(false)
   , mDrawObserver(nullptr)
   , mIsEntireFrameInvalid(false)
   , mPredictManyRedrawCalls(false)
   , mIsCapturedFrameInvalid(false)
   , mInvalidateCount(0)
 {
   if (!sMaxContextsInitialized) {
     sMaxContexts = gfxPrefs::CanvasAzureAcceleratedLimit();
     sMaxContextsInitialized = true;
   }
 
-  sNumLivingContexts++;
-
-  mShutdownObserver = new CanvasShutdownObserver(this);
-  nsContentUtils::RegisterShutdownObserver(mShutdownObserver);
-
   // The default is to use OpenGL mode
   if (AllowOpenGLCanvas()) {
     mDrawObserver = new CanvasDrawObserver(this);
   } else {
     mRenderingMode = RenderingMode::SoftwareBackendMode;
   }
 }
 
 CanvasRenderingContext2D::~CanvasRenderingContext2D()
 {
   RemoveDrawObserver();
   RemovePostRefreshObserver();
-  RemoveShutdownObserver();
+
   Reset();
   // Drop references from all CanvasRenderingContext2DUserData to this context
   for (uint32_t i = 0; i < mUserDatas.Length(); ++i) {
     mUserDatas[i]->Forget();
   }
-  sNumLivingContexts--;
-  if (!sNumLivingContexts) {
-    NS_IF_RELEASE(sErrorTarget);
-  }
+
   RemoveDemotableContext(this);
 }
 
 JSObject*
 CanvasRenderingContext2D::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return CanvasRenderingContext2DBinding::Wrap(aCx, this, aGivenProto);
 }
@@ -581,17 +513,17 @@ CanvasRenderingContext2D::Reset()
 {
   if (mCanvasElement) {
     mCanvasElement->InvalidateCanvas();
   }
 
   // only do this for non-docshell created contexts,
   // since those are the ones that we created a surface for
   if (mTarget && IsTargetValid() && !mDocShell) {
-    gCanvasAzureMemoryUsed -= mWidth * mHeight * 4;
+    sCanvasAzureMemoryUsed -= mWidth * mHeight * 4;
   }
 
   bool forceReset = true;
   ReturnTarget(forceReset);
   mTarget = nullptr;
   mBufferProvider = nullptr;
 
   // reset hit regions
@@ -602,39 +534,16 @@ CanvasRenderingContext2D::Reset()
   mIsEntireFrameInvalid = false;
   mPredictManyRedrawCalls = false;
   mIsCapturedFrameInvalid = false;
 
   return NS_OK;
 }
 
 void
-CanvasRenderingContext2D::OnShutdown()
-{
-  mShutdownObserver = nullptr;
-
-  RefPtr<PersistentBufferProvider> provider = mBufferProvider;
-
-  Reset();
-
-  if (provider) {
-    provider->OnShutdown();
-  }
-}
-
-void
-CanvasRenderingContext2D::RemoveShutdownObserver()
-{
-  if (mShutdownObserver) {
-    nsContentUtils::UnregisterShutdownObserver(mShutdownObserver);
-    mShutdownObserver = nullptr;
-  }
-}
-
-void
 CanvasRenderingContext2D::DidImageDrawCall()
 {
   if (mDrawObserver) {
     mDrawObserver->DidDrawCall(CanvasDrawObserver::DrawCallType::DrawImage);
   }
 }
 
 // static
@@ -751,33 +660,16 @@ bool CanvasRenderingContext2D::SwitchRen
   }
 
   // We succeeded, so update mRenderingMode to reflect reality
   mRenderingMode = attemptedMode;
 
   return true;
 }
 
-bool
-CanvasRenderingContext2D::CopyBufferProvider(PersistentBufferProvider& aOld,
-                                             DrawTarget& aTarget,
-                                             IntRect aCopyRect)
-{
-  // Borrowing the snapshot must be done after ReturnTarget.
-  RefPtr<SourceSurface> snapshot = aOld.BorrowSnapshot();
-
-  if (!snapshot) {
-    return false;
-  }
-
-  aTarget.CopySurface(snapshot, aCopyRect, IntPoint());
-  aOld.ReturnSnapshot(snapshot.forget());
-  return true;
-}
-
 void CanvasRenderingContext2D::Demote()
 {
   if (SwitchRenderingMode(RenderingMode::SoftwareBackendMode)) {
     RemoveDemotableContext(this);
   }
 }
 
 std::vector<CanvasRenderingContext2D*>&
@@ -893,253 +785,16 @@ CanvasRenderingContext2D::CheckSizeForSk
 
   double scale = gDefaultScale > 0 ? gDefaultScale : 1.0;
   int32_t threshold = ceil(scale * scale * gScreenPixels);
 
   // screen size acts as max threshold
   return threshold < 0 || (aSize.width * aSize.height) <= threshold;
 }
 
-void
-CanvasRenderingContext2D::ScheduleStableStateCallback()
-{
-  if (mHasPendingStableStateCallback) {
-    return;
-  }
-  mHasPendingStableStateCallback = true;
-
-  nsContentUtils::RunInStableState(
-    NewRunnableMethod(this, &CanvasRenderingContext2D::OnStableState)
-  );
-}
-
-void
-CanvasRenderingContext2D::OnStableState()
-{
-  if (!mHasPendingStableStateCallback) {
-    return;
-  }
-
-  ReturnTarget();
-
-  mHasPendingStableStateCallback = false;
-}
-
-void
-CanvasRenderingContext2D::RestoreClipsAndTransformToTarget()
-{
-  // Restore clips and transform.
-  mTarget->SetTransform(Matrix());
-
-  if (mTarget->GetBackendType() == gfx::BackendType::CAIRO) {
-    // Cairo doesn't play well with huge clips. When given a very big clip it
-    // will try to allocate big mask surface without taking the target
-    // size into account which can cause OOM. See bug 1034593.
-    // This limits the clip extents to the size of the canvas.
-    // A fix in Cairo would probably be preferable, but requires somewhat
-    // invasive changes.
-    mTarget->PushClipRect(gfx::Rect(0, 0, mWidth, mHeight));
-  }
-
-  for (const auto& style : mStyleStack) {
-    for (const auto& clipOrTransform : style.clipsAndTransforms) {
-      if (clipOrTransform.IsClip()) {
-        mTarget->PushClip(clipOrTransform.clip);
-      } else {
-        mTarget->SetTransform(clipOrTransform.transform);
-      }
-    }
-  }
-}
-
-CanvasRenderingContext2D::RenderingMode
-CanvasRenderingContext2D::EnsureTarget(const gfx::Rect* aCoveredRect,
-                                       RenderingMode aRenderingMode)
-{
-  if (AlreadyShutDown()) {
-    gfxCriticalError() << "Attempt to render into a Canvas2d after shutdown.";
-    SetErrorState();
-    return aRenderingMode;
-  }
-
-  // This would make no sense, so make sure we don't get ourselves in a mess
-  MOZ_ASSERT(mRenderingMode != RenderingMode::DefaultBackendMode);
-
-  RenderingMode mode = (aRenderingMode == RenderingMode::DefaultBackendMode) ? mRenderingMode : aRenderingMode;
-
-  if (mTarget && mode == mRenderingMode) {
-    return mRenderingMode;
-  }
-
-  // Check that the dimensions are sane
-  if (mWidth > gfxPrefs::MaxCanvasSize() ||
-      mHeight > gfxPrefs::MaxCanvasSize() ||
-      mWidth < 0 || mHeight < 0) {
-
-    SetErrorState();
-    return aRenderingMode;
-  }
-
-  // If the next drawing command covers the entire canvas, we can skip copying
-  // from the previous frame and/or clearing the canvas.
-  gfx::Rect canvasRect(0, 0, mWidth, mHeight);
-  bool canDiscardContent = aCoveredRect &&
-    CurrentState().transform.TransformBounds(*aCoveredRect).Contains(canvasRect);
-
-  // If a clip is active we don't know for sure that the next drawing command
-  // will really cover the entire canvas.
-  for (const auto& style : mStyleStack) {
-    if (!canDiscardContent) {
-      break;
-    }
-    for (const auto& clipOrTransform : style.clipsAndTransforms) {
-      if (clipOrTransform.IsClip()) {
-        canDiscardContent = false;
-        break;
-      }
-    }
-  }
-
-  ScheduleStableStateCallback();
-
-  IntRect persistedRect = canDiscardContent ? IntRect()
-                                            : IntRect(0, 0, mWidth, mHeight);
-
-  if (mBufferProvider && mode == mRenderingMode) {
-    mTarget = mBufferProvider->BorrowDrawTarget(persistedRect);
-
-    if (mTarget && !mBufferProvider->PreservesDrawingState()) {
-      RestoreClipsAndTransformToTarget();
-    }
-
-    if (mTarget) {
-      return mode;
-    }
-  }
-
-  RefPtr<DrawTarget> newTarget;
-  RefPtr<PersistentBufferProvider> newProvider;
-
-  if (mode == RenderingMode::OpenGLBackendMode &&
-      !TrySkiaGLTarget(newTarget, newProvider)) {
-    // Fall back to software.
-    mode = RenderingMode::SoftwareBackendMode;
-  }
-
-  if (mode == RenderingMode::SoftwareBackendMode &&
-      !TrySharedTarget(newTarget, newProvider) &&
-      !TryBasicTarget(newTarget, newProvider)) {
-
-    gfxCriticalError(
-      CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(GetSize()))
-    ) << "Failed borrow shared and basic targets.";
-
-    SetErrorState();
-    return mode;
-  }
-
-
-  MOZ_ASSERT(newTarget);
-  MOZ_ASSERT(newProvider);
-
-  bool needsClear = !canDiscardContent;
-  if (newTarget->GetBackendType() == gfx::BackendType::SKIA) {
-    // Skia expects the unused X channel to contains 0xFF even for opaque operations
-    // so we can't skip clearing in that case, even if we are going to cover the
-    // entire canvas in the next drawing operation.
-    newTarget->ClearRect(canvasRect);
-    needsClear = false;
-  }
-
-  // Try to copy data from the previous buffer provider if there is one.
-  if (!canDiscardContent && mBufferProvider && CopyBufferProvider(*mBufferProvider, *newTarget, persistedRect)) {
-    needsClear = false;
-  }
-
-  if (needsClear) {
-    newTarget->ClearRect(canvasRect);
-  }
-
-  mTarget = newTarget.forget();
-  mBufferProvider = newProvider.forget();
-
-  RegisterAllocation();
-
-  RestoreClipsAndTransformToTarget();
-
-  // Force a full layer transaction since we didn't have a layer before
-  // and now we might need one.
-  if (mCanvasElement) {
-    mCanvasElement->InvalidateCanvas();
-  }
-  // Calling Redraw() tells our invalidation machinery that the entire
-  // canvas is already invalid, which can speed up future drawing.
-  Redraw();
-
-  return mode;
-}
-
-void
-CanvasRenderingContext2D::SetInitialState()
-{
-  // Set up the initial canvas defaults
-  mPathBuilder = nullptr;
-  mPath = nullptr;
-  mDSPathBuilder = nullptr;
-
-  mStyleStack.Clear();
-  ContextState *state = mStyleStack.AppendElement();
-  state->globalAlpha = 1.0;
-
-  state->colorStyles[Style::FILL] = NS_RGB(0,0,0);
-  state->colorStyles[Style::STROKE] = NS_RGB(0,0,0);
-  state->shadowColor = NS_RGBA(0,0,0,0);
-}
-
-void
-CanvasRenderingContext2D::SetErrorState()
-{
-  EnsureErrorTarget();
-
-  if (mTarget && mTarget != sErrorTarget) {
-    gCanvasAzureMemoryUsed -= mWidth * mHeight * 4;
-  }
-
-  mTarget = sErrorTarget;
-  mBufferProvider = nullptr;
-
-  // clear transforms, clips, etc.
-  SetInitialState();
-}
-
-void
-CanvasRenderingContext2D::RegisterAllocation()
-{
-  // XXX - It would make more sense to track the allocation in
-  // PeristentBufferProvider, rather than here.
-  static bool registered = false;
-  // FIXME: Disable the reporter for now, see bug 1241865
-  if (!registered && false) {
-    registered = true;
-    RegisterStrongMemoryReporter(new Canvas2dPixelsReporter());
-  }
-
-  gCanvasAzureMemoryUsed += mWidth * mHeight * 4;
-  JSContext* context = nsContentUtils::GetCurrentJSContext();
-  if (context) {
-    JS_updateMallocCounter(context, mWidth * mHeight * 4);
-  }
-
-  JSObject* wrapper = GetWrapperPreserveColor();
-  if (wrapper) {
-    CycleCollectedJSContext::Get()->
-      AddZoneWaitingForGC(JS::GetObjectZone(wrapper));
-  }
-}
-
 static already_AddRefed<LayerManager>
 LayerManagerFromCanvasElement(nsINode* aCanvasElement)
 {
   if (!aCanvasElement || !aCanvasElement->OwnerDoc()) {
     return nullptr;
   }
 
   return nsContentUtils::PersistentLayerManagerForDocument(aCanvasElement->OwnerDoc());
@@ -1228,30 +883,16 @@ CanvasRenderingContext2D::TrySharedTarge
   // We can pass an empty persisted rect since we just created the buffer
   // provider (nothing to restore).
   aOutDT = aOutProvider->BorrowDrawTarget(IntRect());
   MOZ_ASSERT(aOutDT);
 
   return !!aOutDT;
 }
 
-bool
-CanvasRenderingContext2D::TryBasicTarget(RefPtr<gfx::DrawTarget>& aOutDT,
-                                         RefPtr<layers::PersistentBufferProvider>& aOutProvider)
-{
-  aOutDT = gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget(GetSize(),
-                                                                       GetSurfaceFormat());
-  if (!aOutDT) {
-    return false;
-  }
-
-  aOutProvider = new PersistentBufferProviderBasic(aOutDT);
-  return true;
-}
-
 int32_t
 CanvasRenderingContext2D::GetWidth() const
 {
   return mWidth;
 }
 
 int32_t
 CanvasRenderingContext2D::GetHeight() const
@@ -1304,43 +945,16 @@ CanvasRenderingContext2D::ClearTarget()
         if (wm.IsVertical() && !wm.IsSideways()) {
           CurrentState().textBaseline = TextBaseline::MIDDLE;
         }
       }
     }
   }
 }
 
-void
-CanvasRenderingContext2D::ReturnTarget(bool aForceReset)
-{
-  if (mTarget && mBufferProvider && mTarget != sErrorTarget) {
-    CurrentState().transform = mTarget->GetTransform();
-    if (aForceReset || !mBufferProvider->PreservesDrawingState()) {
-      for (const auto& style : mStyleStack) {
-        for (const auto& clipOrTransform : style.clipsAndTransforms) {
-          if (clipOrTransform.IsClip()) {
-            mTarget->PopClip();
-          }
-        }
-      }
-
-      if (mTarget->GetBackendType() == gfx::BackendType::CAIRO) {
-        // With the cairo backend we pushed an extra clip rect which we have to
-        // balance out here. See the comment in RestoreClipsAndTransformToTarget.
-        mTarget->PopClip();
-      }
-
-      mTarget->SetTransform(Matrix());
-    }
-
-    mBufferProvider->ReturnDrawTarget(mTarget.forget());
-  }
-}
-
 NS_IMETHODIMP
 CanvasRenderingContext2D::InitializeWithDrawTarget(nsIDocShell* aShell,
                                                    NotNull<gfx::DrawTarget*> aTarget)
 {
   RemovePostRefreshObserver();
   mDocShell = aShell;
   AddPostRefreshObserverIfNecessary();
 
@@ -3263,30 +2877,16 @@ CanvasRenderingContext2D::GetImageDataAr
     readback->Unmap();
   }
 
   *aRetval = darray;
   return NS_OK;
 }
 
 void
-CanvasRenderingContext2D::EnsureErrorTarget()
-{
-  if (sErrorTarget) {
-    return;
-  }
-
-  RefPtr<DrawTarget> errorTarget = gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget(IntSize(1, 1), SurfaceFormat::B8G8R8A8);
-  MOZ_ASSERT(errorTarget, "Failed to allocate the error target!");
-
-  sErrorTarget = errorTarget;
-  NS_ADDREF(sErrorTarget);
-}
-
-void
 CanvasRenderingContext2D::FillRuleChanged()
 {
   if (mPath) {
     mPathBuilder = mPath->CopyToBuilder(CurrentState().fillRule);
     mPath = nullptr;
   }
 }
 
--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -42,25 +42,23 @@ class TextMetrics;
 class CanvasFilterChainObserver;
 class CanvasPath;
 
 template<typename T> class Optional;
 
 struct CanvasBidiProcessor;
 class CanvasRenderingContext2DUserData;
 class CanvasDrawObserver;
-class CanvasShutdownObserver;
 
 /**
  ** CanvasRenderingContext2D
  **/
 class CanvasRenderingContext2D final :
   public nsICanvasRenderingContextInternal,
-  public BasicRenderingContext2D,
-  public nsWrapperCache
+  public BasicRenderingContext2D
 {
   virtual ~CanvasRenderingContext2D();
 
 public:
   explicit CanvasRenderingContext2D(layers::LayersBackend aCompositorBackend);
 
   virtual JSObject* WrapObject(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override;
 
@@ -159,17 +157,16 @@ public:
 
   // Eventually this should be deprecated. Keeping for now to keep the binding functional.
   void Demote();
 
   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
     */
   virtual nsIPresShell *GetPresShell() override {
     if (mCanvasElement) {
       return mCanvasElement->OwnerDoc()->GetShell();
@@ -248,138 +245,69 @@ public:
 
   // Given a point, return hit region ID if it exists
   nsString GetHitRegion(const mozilla::gfx::Point& aPoint) override;
 
 
   // return true and fills in the bound rect if element has a hit region.
   bool GetHitRegionRect(Element* aElement, nsRect& aRect) override;
 
-  void OnShutdown();
-
 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);
 
-  bool CopyBufferProvider(layers::PersistentBufferProvider& aOld,
-                          gfx::DrawTarget& aTarget,
-                          gfx::IntRect aCopyRect);
-
   /**
    * Internal method to complete initialisation, expects mTarget to have been set
    */
   nsresult Initialize(int32_t aWidth, int32_t aHeight);
 
   nsresult InitializeWithTarget(mozilla::gfx::DrawTarget* aSurface,
                                 int32_t aWidth, int32_t aHeight);
 
-  /**
-    * The number of living nsCanvasRenderingContexts.  When this goes down to
-    * 0, we free the premultiply and unpremultiply tables, if they exist.
-    */
-  static uint32_t sNumLivingContexts;
-
-  static mozilla::gfx::DrawTarget* sErrorTarget;
-
   // Some helpers.  Doesn't modify a color on failure.
   void GetStyleAsUnion(OwningStringOrCanvasGradientOrCanvasPattern& aValue,
                        Style aWhichStyle);
 
   // Returns whether a color was successfully parsed.
   virtual bool ParseColor(const nsAString& aString, nscolor* aColor) override;
 
    // Returns whether a filter was successfully parsed.
   bool ParseFilter(const nsAString& aString,
                    nsTArray<nsStyleFilter>& aFilterChain,
                    ErrorResult& aError);
 
   // Returns whether the font was successfully updated.
   bool SetFontInternal(const nsAString& aFont, mozilla::ErrorResult& aError);
 
-
-  /**
-   * Creates the error target, if it doesn't exist
-   */
-  static void EnsureErrorTarget();
-
   // Report the fillRule has changed.
   void FillRuleChanged();
 
-  /**
-   * 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.
-   *
-   * Returns the actual rendering mode being used by the created target.
-   */
-  virtual RenderingMode
-  EnsureTarget(const gfx::Rect* aCoveredRect = nullptr,
-               RenderingMode aRenderMode = RenderingMode::DefaultBackendMode) override;
-
-  void RestoreClipsAndTransformToTarget();
-
   bool TrySkiaGLTarget(RefPtr<gfx::DrawTarget>& aOutDT,
-                       RefPtr<layers::PersistentBufferProvider>& aOutProvider);
+                       RefPtr<layers::PersistentBufferProvider>& aOutProvider) override;
 
   bool TrySharedTarget(RefPtr<gfx::DrawTarget>& aOutDT,
-                       RefPtr<layers::PersistentBufferProvider>& aOutProvider);
-
-  bool TryBasicTarget(RefPtr<gfx::DrawTarget>& aOutDT,
-                      RefPtr<layers::PersistentBufferProvider>& aOutProvider);
-
-  void RegisterAllocation();
-
-  void SetInitialState();
-
-  void SetErrorState();
-
-  /**
-   * This method is run at the end of the event-loop spin where
-   * ScheduleStableStateCallback was called.
-   *
-   * We use it to unlock resources that need to be locked while drawing.
-   */
-  void OnStableState();
-
-  /**
-   * Cf. OnStableState.
-   */
-  void ScheduleStableStateCallback();
+                       RefPtr<layers::PersistentBufferProvider>& aOutProvider) override;
 
   /**
    * Disposes an old target and prepares to lazily create a new target.
    */
   void ClearTarget();
 
-  /*
-   * Returns the target to the buffer provider. i.e. this will queue a frame for
-   * rendering.
-   */
-  void ReturnTarget(bool aForceReset = false);
-
-  /**
-   * Check if the target is valid after calling EnsureTarget.
-   */
-  bool IsTargetValid() const override{
-    return !!mTarget && mTarget != sErrorTarget;
-  }
-
   /**
     * Returns the surface format this canvas should be allocated using. Takes
     * into account mOpaque, platform requirements, etc.
     */
-  mozilla::gfx::SurfaceFormat GetSurfaceFormat() const;
+  mozilla::gfx::SurfaceFormat GetSurfaceFormat() const override;
 
   /**
    * 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();
 
@@ -409,38 +337,30 @@ protected:
   bool mOpaque;
 
   // This is true when the next time our layer is retrieved we need to
   // recreate it (i.e. our backing surface changed)
   bool mResetLayer;
   // This is needed for drawing in drawAsyncXULElement
   bool mIPC;
 
-  bool mHasPendingStableStateCallback;
-
   nsTArray<CanvasRenderingContext2DUserData*> mUserDatas;
 
   // If mCanvasElement is not provided, then a docshell is
   nsCOMPtr<nsIDocShell> mDocShell;
 
-  RefPtr<mozilla::layers::PersistentBufferProvider> mBufferProvider;
-
   uint32_t SkiaGLTex() const;
 
   // This observes our draw calls at the beginning of the canvas
   // lifetime and switches to software or GPU mode depending on
   // what it thinks is best
   CanvasDrawObserver* mDrawObserver;
   void RemoveDrawObserver();
   void DidImageDrawCall() override;
 
-  RefPtr<CanvasShutdownObserver> mShutdownObserver;
-  void RemoveShutdownObserver();
-  virtual bool AlreadyShutDown() const override{ return !mShutdownObserver; }
-
   /**
     * Flag to avoid duplicate calls to InvalidateFrame. Set to true whenever
     * Redraw is called, reset to false when Render is called.
     */
   bool mIsEntireFrameInvalid;
   /**
     * When this is set, the first call to Redraw(gfxRect) should set
     * mIsEntireFrameInvalid since we expect it will be followed by