Bug 1167235 - Part 4 - Forward the shutdown notification to CanvasRenderingContext2D. r=Bas
--- 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;
}