Bug 1167235 - Part 4 - Forward the shutdown notification to CanvasRenderingContext2D. r=Bas
☠☠ backed out by 1dcc4d0ee3d7 ☠ ☠
authorNicolas Silva <nsilva@mozilla.com>
Tue, 28 Jun 2016 14:07:00 +0200
changeset 302949 f534fcb785c9044c1714343113d4685c53d0eefa
parent 302948 6ee8762044bd45934bf9b786a6ac3be8ca5007ce
child 302950 61465f67b5911d7544cf6e3e13f60b7ffec05365
push id30354
push usercbook@mozilla.com
push dateWed, 29 Jun 2016 14:23:15 +0000
treeherderautoland@0c56fe904602 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersBas
bugs1167235
milestone50.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1167235 - Part 4 - Forward the shutdown notification to CanvasRenderingContext2D. r=Bas
dom/canvas/CanvasRenderingContext2D.cpp
dom/canvas/CanvasRenderingContext2D.h
dom/html/HTMLCanvasElement.cpp
gfx/layers/PersistentBufferProvider.cpp
gfx/layers/PersistentBufferProvider.h
gfx/layers/client/TextureClient.cpp
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -740,16 +740,46 @@ NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(C
 
 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)
 
+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,
@@ -1039,28 +1069,32 @@ CanvasRenderingContext2D::CanvasRenderin
   , mIsEntireFrameInvalid(false)
   , mPredictManyRedrawCalls(false)
   , mIsCapturedFrameInvalid(false)
   , mPathTransformWillUpdate(false)
   , mInvalidateCount(0)
 {
   sNumLivingContexts++;
 
+  mShutdownObserver = new CanvasShutdownObserver(this);
+  nsContentUtils::RegisterShutdownObserver(mShutdownObserver);
+
   // The default is to use OpenGL mode
   if (gfxPlatform::GetPlatform()->UseAcceleratedCanvas()) {
     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);
@@ -1144,16 +1178,39 @@ 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::SetStyleFromString(const nsAString& aStr,
                                              Style aWhichStyle)
 {
   MOZ_ASSERT(!aStr.IsVoid());
 
   nscolor color;
   if (!ParseColor(aStr, &color)) {
     return;
@@ -1451,16 +1508,23 @@ CanvasRenderingContext2D::CheckSizeForSk
   // screen size acts as max threshold
   return threshold < 0 || (aSize.width * aSize.height) <= threshold;
 }
 
 CanvasRenderingContext2D::RenderingMode
 CanvasRenderingContext2D::EnsureTarget(const gfx::Rect* aCoveredRect,
                                        RenderingMode aRenderingMode)
 {
+  if (AlreadyShutDown()) {
+    gfxCriticalError() << "Attempt to render into a Canvas2d after shutdown.";
+    EnsureErrorTarget();
+    mTarget = sErrorTarget;
+    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;
   }
@@ -5686,16 +5750,20 @@ void CanvasRenderingContext2D::RemoveDra
     delete mDrawObserver;
     mDrawObserver = nullptr;
   }
 }
 
 PersistentBufferProvider*
 CanvasRenderingContext2D::GetBufferProvider(LayerManager* aManager)
 {
+  if (AlreadyShutDown()) {
+    return nullptr;
+  }
+
   if (mBufferProvider) {
     return mBufferProvider;
   }
 
   if (!mTarget) {
     return nullptr;
   }
 
--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -49,16 +49,17 @@ class CanvasPath;
 
 extern const mozilla::gfx::Float SIGMA_MAX;
 
 template<typename T> class Optional;
 
 struct CanvasBidiProcessor;
 class CanvasRenderingContext2DUserData;
 class CanvasDrawObserver;
+class CanvasShutdownObserver;
 
 /**
  ** CanvasRenderingContext2D
  **/
 class CanvasRenderingContext2D final :
   public nsICanvasRenderingContextInternal,
   public nsWrapperCache
 {
@@ -540,16 +541,17 @@ 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:
   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,
@@ -744,16 +746,20 @@ protected:
   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();
 
+  RefPtr<CanvasShutdownObserver> mShutdownObserver;
+  void RemoveShutdownObserver();
+  bool AlreadyShutDown() const { 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
--- a/dom/html/HTMLCanvasElement.cpp
+++ b/dom/html/HTMLCanvasElement.cpp
@@ -348,18 +348,17 @@ HTMLCanvasElementObserver::HandleEvent(n
 NS_IMPL_ISUPPORTS(HTMLCanvasElementObserver, nsIObserver)
 
 // ---------------------------------------------------------------------------
 
 HTMLCanvasElement::HTMLCanvasElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
   : nsGenericHTMLElement(aNodeInfo),
     mResetLayer(true) ,
     mWriteOnly(false)
-{
-}
+{}
 
 HTMLCanvasElement::~HTMLCanvasElement()
 {
   if (mContextObserver) {
     mContextObserver->Destroy();
     mContextObserver = nullptr;
   }
 
--- a/gfx/layers/PersistentBufferProvider.cpp
+++ b/gfx/layers/PersistentBufferProvider.cpp
@@ -121,26 +121,17 @@ PersistentBufferProviderShared::Persiste
 PersistentBufferProviderShared::~PersistentBufferProviderShared()
 {
   MOZ_COUNT_DTOR(PersistentBufferProviderShared);
 
   if (IsActivityTracked()) {
     mFwd->GetActiveResourceTracker().RemoveObject(this);
   }
 
-  mDrawTarget = nullptr;
-  if (mBack && mBack->IsLocked()) {
-    mBack->Unlock();
-  }
-  if (mFront && mFront->IsLocked()) {
-    mFront->Unlock();
-  }
-  if (mBuffer && mBuffer->IsLocked()) {
-    mBuffer->Unlock();
-  }
+  Destroy();
 }
 
 already_AddRefed<gfx::DrawTarget>
 PersistentBufferProviderShared::BorrowDrawTarget(const gfx::IntRect& aPersistedRect)
 {
   if (!mFwd->IPCOpen()) {
     return nullptr;
   }
@@ -271,10 +262,33 @@ PersistentBufferProviderShared::NotifyIn
   if (mBuffer && mBuffer->IsLocked()) {
     // mBuffer should never be locked
     MOZ_ASSERT(false);
     mBuffer->Unlock();
   }
   mBuffer = nullptr;
 }
 
+void
+PersistentBufferProviderShared::Destroy()
+{
+  mSnapshot = nullptr;
+  mDrawTarget = nullptr;
+
+  if (mFront && mFront->IsLocked()) {
+    mFront->Unlock();
+  }
+
+  if (mBack && mBack->IsLocked()) {
+    mBack->Unlock();
+  }
+
+  if (mBuffer && mBuffer->IsLocked()) {
+    mBuffer->Unlock();
+  }
+
+  mFront = nullptr;
+  mBack = nullptr;
+  mBuffer = nullptr;
+}
+
 } // namespace layers
 } // namespace mozilla
\ No newline at end of file
--- a/gfx/layers/PersistentBufferProvider.h
+++ b/gfx/layers/PersistentBufferProvider.h
@@ -55,16 +55,18 @@ public:
    */
   virtual bool ReturnDrawTarget(already_AddRefed<gfx::DrawTarget> aDT) = 0;
 
   virtual already_AddRefed<gfx::SourceSurface> BorrowSnapshot() = 0;
 
   virtual void ReturnSnapshot(already_AddRefed<gfx::SourceSurface> aSnapshot) = 0;
 
   virtual TextureClient* GetTextureClient() { return nullptr; }
+
+  virtual void OnShutdown() {}
 };
 
 
 class PersistentBufferProviderBasic : public PersistentBufferProvider
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PersistentBufferProviderBasic, override)
 
@@ -116,23 +118,27 @@ public:
   virtual void ReturnSnapshot(already_AddRefed<gfx::SourceSurface> aSnapshot) override;
 
   TextureClient* GetTextureClient() override {
     return mFront;
   }
 
   virtual void NotifyInactive() override;
 
+  virtual void OnShutdown() override { Destroy(); }
+
 protected:
   PersistentBufferProviderShared(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
                                  CompositableForwarder* aFwd,
                                  RefPtr<TextureClient>& aTexture);
 
   ~PersistentBufferProviderShared();
 
+  void Destroy();
+
   gfx::IntSize mSize;
   gfx::SurfaceFormat mFormat;
   RefPtr<CompositableForwarder> mFwd;
   // The texture presented to the compositor.
   RefPtr<TextureClient> mFront;
   // The texture that the canvas uses.
   RefPtr<TextureClient> mBack;
   // An extra texture we keep around temporarily to avoid allocating.
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -359,16 +359,18 @@ DeallocateTextureClient(TextureDeallocPa
 }
 
 void TextureClient::Destroy(bool aForceSync)
 {
   if (mActor) {
     mActor->Lock();
   }
 
+  mReadLock = nullptr;
+
   CancelWaitFenceHandleOnImageBridge();
   RefPtr<TextureChild> actor = mActor;
   mActor = nullptr;
 
   if (actor && !actor->mDestroyed.compareExchange(false, true)) {
     actor->Unlock();
     actor = nullptr;
   }