From 086c044dc34dfc0f74fbe41f4ecb402b2cd34884 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 03:13:33 +0200 Subject: Merging upstream version 125.0.1. Signed-off-by: Daniel Baumann --- dom/canvas/CanvasRenderingContext2D.cpp | 271 +++++++++++++++++++------------- 1 file changed, 166 insertions(+), 105 deletions(-) (limited to 'dom/canvas/CanvasRenderingContext2D.cpp') diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp index 28579d4b75..529466cc81 100644 --- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -24,7 +24,7 @@ #include "mozilla/dom/HTMLCanvasElement.h" #include "mozilla/dom/GeneratePlaceholderCanvasData.h" #include "mozilla/dom/VideoFrame.h" -#include "mozilla/gfx/CanvasManagerChild.h" +#include "mozilla/gfx/CanvasShutdownManager.h" #include "nsPresContext.h" #include "nsIInterfaceRequestorUtils.h" @@ -118,6 +118,7 @@ #include "mozilla/dom/SVGImageElement.h" #include "mozilla/dom/TextMetrics.h" #include "mozilla/FloatingPoint.h" +#include "mozilla/Logging.h" #include "nsGlobalWindowInner.h" #include "nsDeviceContext.h" #include "nsFontMetrics.h" @@ -871,41 +872,6 @@ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CanvasGradient, mContext) NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CanvasPattern, mContext) -class CanvasShutdownObserver final : public nsIObserver { - public: - explicit CanvasShutdownObserver(CanvasRenderingContext2D* aCanvas) - : mCanvas(aCanvas) {} - - void OnShutdown() { - if (!mCanvas) { - return; - } - - mCanvas = nullptr; - nsContentUtils::UnregisterShutdownObserver(this); - } - - NS_DECL_ISUPPORTS - NS_DECL_NSIOBSERVER - private: - ~CanvasShutdownObserver() = default; - - 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(); - OnShutdown(); - } - - return NS_OK; -} - NS_IMPL_CYCLE_COLLECTING_ADDREF(CanvasRenderingContext2D) NS_IMPL_CYCLE_COLLECTING_RELEASE(CanvasRenderingContext2D) @@ -1242,14 +1208,8 @@ void CanvasRenderingContext2D::OnShutdown() { } bool CanvasRenderingContext2D::AddShutdownObserver() { - auto* const canvasManager = CanvasManagerChild::Get(); + auto* const canvasManager = CanvasShutdownManager::Get(); if (NS_WARN_IF(!canvasManager)) { - if (NS_IsMainThread()) { - mShutdownObserver = new CanvasShutdownObserver(this); - nsContentUtils::RegisterShutdownObserver(mShutdownObserver); - return true; - } - mHasShutdown = true; return false; } @@ -1259,18 +1219,70 @@ bool CanvasRenderingContext2D::AddShutdownObserver() { } void CanvasRenderingContext2D::RemoveShutdownObserver() { - if (mShutdownObserver) { - mShutdownObserver->OnShutdown(); - mShutdownObserver = nullptr; + auto* const canvasManager = CanvasShutdownManager::MaybeGet(); + if (!canvasManager) { return; } - auto* const canvasManager = CanvasManagerChild::MaybeGet(); - if (!canvasManager) { + canvasManager->RemoveShutdownObserver(this); +} + +void CanvasRenderingContext2D::OnRemoteCanvasLost() { + // We only lose context / data if we are using remote canvas, which is only + // for accelerated targets. + if (!mBufferProvider || !mBufferProvider->IsAccelerated() || mIsContextLost) { return; } - canvasManager->RemoveShutdownObserver(this); + // 2. Set context's context lost to true. + mIsContextLost = mAllowContextRestore = true; + + // 3. Reset the rendering context to its default state given context. + ClearTarget(); + + // We dispatch because it isn't safe to call into the script event handlers, + // and we don't want to mutate our state in CanvasShutdownManager. + NS_DispatchToCurrentThread(NS_NewCancelableRunnableFunction( + "CanvasRenderingContext2D::OnRemoteCanvasLost", [self = RefPtr{this}] { + // 4. Let shouldRestore be the result of firing an event named + // contextlost at canvas, with the cancelable attribute initialized to + // true. + self->mAllowContextRestore = self->DispatchEvent( + u"contextlost"_ns, CanBubble::eNo, Cancelable::eYes); + })); +} + +void CanvasRenderingContext2D::OnRemoteCanvasRestored() { + // We never lost our context if it was not a remote canvas, nor can we restore + // if we have already shutdown. + if (mHasShutdown || !mIsContextLost || !mAllowContextRestore) { + return; + } + + // We dispatch because it isn't safe to call into the script event handlers, + // and we don't want to mutate our state in CanvasShutdownManager. + NS_DispatchToCurrentThread(NS_NewCancelableRunnableFunction( + "CanvasRenderingContext2D::OnRemoteCanvasRestored", + [self = RefPtr{this}] { + // 5. If shouldRestore is false, then abort these steps. + if (!self->mHasShutdown && self->mIsContextLost && + self->mAllowContextRestore) { + // 7. Set context's context lost to false. + self->mIsContextLost = false; + + // 6. Attempt to restore context by creating a backing storage using + // context's attributes and associating them with context. If this + // fails, then abort these steps. + if (!self->EnsureTarget()) { + self->mIsContextLost = true; + return; + } + + // 8. Fire an event named contextrestored at canvas. + self->DispatchEvent(u"contextrestored"_ns, CanBubble::eNo, + Cancelable::eNo); + } + })); } void CanvasRenderingContext2D::SetStyleFromString(const nsACString& aStr, @@ -1489,23 +1501,47 @@ bool CanvasRenderingContext2D::BorrowTarget(const IntRect& aPersistedRect, return true; } -bool CanvasRenderingContext2D::EnsureTarget(const gfx::Rect* aCoveredRect, +bool CanvasRenderingContext2D::EnsureTarget(ErrorResult& aError, + const gfx::Rect* aCoveredRect, bool aWillClear) { if (AlreadyShutDown()) { gfxCriticalNoteOnce << "Attempt to render into a Canvas2d after shutdown."; SetErrorState(); + aError.ThrowInvalidStateError( + "Cannot use canvas after shutdown initiated."); + return false; + } + + // The spec doesn't say what to do in this case, but Chrome silently fails + // without throwing an error. We should at least throw if the canvas is + // permanently disabled. + if (NS_WARN_IF(mIsContextLost)) { + if (!mAllowContextRestore) { + aError.ThrowInvalidStateError( + "Cannot use canvas as context is lost forever."); + } return false; } if (mTarget) { - return mTarget != sErrorTarget.get(); + if (mTarget == sErrorTarget.get()) { + aError.ThrowInvalidStateError("Canvas is already in error state."); + return false; + } + return true; } // Check that the dimensions are sane if (mWidth > StaticPrefs::gfx_canvas_max_size() || - mHeight > StaticPrefs::gfx_canvas_max_size() || mWidth < 0 || - mHeight < 0) { + mHeight > StaticPrefs::gfx_canvas_max_size()) { + SetErrorState(); + aError.ThrowInvalidStateError("Canvas exceeds max size."); + return false; + } + + if (mWidth < 0 || mHeight < 0) { SetErrorState(); + aError.ThrowInvalidStateError("Canvas has invalid size."); return false; } @@ -1547,7 +1583,7 @@ bool CanvasRenderingContext2D::EnsureTarget(const gfx::Rect* aCoveredRect, if (!TryAcceleratedTarget(newTarget, newProvider) && !TrySharedTarget(newTarget, newProvider) && - !TryBasicTarget(newTarget, newProvider)) { + !TryBasicTarget(newTarget, newProvider, aError)) { gfxCriticalError( CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(GetSize()))) << "Failed borrow shared and basic targets."; @@ -1684,15 +1720,27 @@ bool CanvasRenderingContext2D::TryAcceleratedTarget( return false; } - if (!mCanvasElement) { - return false; - } - WindowRenderer* renderer = WindowRendererFromCanvasElement(mCanvasElement); - if (!renderer) { - return false; + if (mCanvasElement) { + MOZ_ASSERT(NS_IsMainThread()); + + WindowRenderer* renderer = WindowRendererFromCanvasElement(mCanvasElement); + if (NS_WARN_IF(!renderer)) { + return false; + } + + aOutProvider = PersistentBufferProviderAccelerated::Create( + GetSize(), GetSurfaceFormat(), renderer->AsKnowsCompositor()); + } else if (mOffscreenCanvas && + StaticPrefs::gfx_canvas_remote_allow_offscreen()) { + RefPtr imageBridge = ImageBridgeChild::GetSingleton(); + if (NS_WARN_IF(!imageBridge)) { + return false; + } + + aOutProvider = PersistentBufferProviderAccelerated::Create( + GetSize(), GetSurfaceFormat(), imageBridge); } - aOutProvider = PersistentBufferProviderAccelerated::Create( - GetSize(), GetSurfaceFormat(), renderer->AsKnowsCompositor()); + if (!aOutProvider) { return false; } @@ -1716,8 +1764,10 @@ bool CanvasRenderingContext2D::TrySharedTarget( } if (mCanvasElement) { + MOZ_ASSERT(NS_IsMainThread()); + WindowRenderer* renderer = WindowRendererFromCanvasElement(mCanvasElement); - if (!renderer) { + if (NS_WARN_IF(!renderer)) { return false; } @@ -1735,7 +1785,7 @@ bool CanvasRenderingContext2D::TrySharedTarget( return false; } - aOutProvider = layers::PersistentBufferProviderShared::Create( + aOutProvider = PersistentBufferProviderShared::Create( GetSize(), GetSurfaceFormat(), imageBridge, !mAllowAcceleration || GetEffectiveWillReadFrequently(), mOffscreenCanvas->GetWindowID()); @@ -1755,10 +1805,12 @@ bool CanvasRenderingContext2D::TrySharedTarget( bool CanvasRenderingContext2D::TryBasicTarget( RefPtr& aOutDT, - RefPtr& aOutProvider) { + RefPtr& aOutProvider, + ErrorResult& aError) { aOutDT = gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget( GetSize(), GetSurfaceFormat()); if (!aOutDT) { + aError.ThrowInvalidStateError("Canvas could not create basic draw target."); return false; } @@ -1767,6 +1819,7 @@ bool CanvasRenderingContext2D::TryBasicTarget( if (!aOutDT->IsValid()) { aOutDT = nullptr; + aError.ThrowInvalidStateError("Canvas could not init basic draw target."); return false; } @@ -2042,7 +2095,7 @@ CanvasRenderingContext2D::GetOptimizedSnapshot(DrawTarget* aTarget, // already exists, otherwise we get performance issues. See bug 1567054. if (!EnsureTarget()) { MOZ_ASSERT( - mTarget == sErrorTarget.get(), + mTarget == sErrorTarget.get() || mIsContextLost, "On EnsureTarget failure mTarget should be set to sErrorTarget."); // In rare circumstances we may have failed to create an error target. return mTarget ? mTarget->Snapshot() : nullptr; @@ -2110,36 +2163,36 @@ void CanvasRenderingContext2D::Restore() { void CanvasRenderingContext2D::Scale(double aX, double aY, ErrorResult& aError) { - EnsureTarget(); - if (!IsTargetValid()) { - aError.Throw(NS_ERROR_FAILURE); + if (!EnsureTarget(aError)) { return; } + MOZ_ASSERT(IsTargetValid()); + Matrix newMatrix = mTarget->GetTransform(); newMatrix.PreScale(aX, aY); SetTransformInternal(newMatrix); } void CanvasRenderingContext2D::Rotate(double aAngle, ErrorResult& aError) { - EnsureTarget(); - if (!IsTargetValid()) { - aError.Throw(NS_ERROR_FAILURE); + if (!EnsureTarget(aError)) { return; } + MOZ_ASSERT(IsTargetValid()); + Matrix newMatrix = Matrix::Rotation(aAngle) * mTarget->GetTransform(); SetTransformInternal(newMatrix); } void CanvasRenderingContext2D::Translate(double aX, double aY, ErrorResult& aError) { - EnsureTarget(); - if (!IsTargetValid()) { - aError.Throw(NS_ERROR_FAILURE); + if (!EnsureTarget(aError)) { return; } + MOZ_ASSERT(IsTargetValid()); + Matrix newMatrix = mTarget->GetTransform(); newMatrix.PreTranslate(aX, aY); SetTransformInternal(newMatrix); @@ -2148,12 +2201,12 @@ void CanvasRenderingContext2D::Translate(double aX, double aY, void CanvasRenderingContext2D::Transform(double aM11, double aM12, double aM21, double aM22, double aDx, double aDy, ErrorResult& aError) { - EnsureTarget(); - if (!IsTargetValid()) { - aError.Throw(NS_ERROR_FAILURE); + if (!EnsureTarget(aError)) { return; } + MOZ_ASSERT(IsTargetValid()); + Matrix newMatrix(aM11, aM12, aM21, aM22, aDx, aDy); newMatrix *= mTarget->GetTransform(); SetTransformInternal(newMatrix); @@ -2161,11 +2214,12 @@ void CanvasRenderingContext2D::Transform(double aM11, double aM12, double aM21, already_AddRefed CanvasRenderingContext2D::GetTransform( ErrorResult& aError) { - EnsureTarget(); - if (!IsTargetValid()) { - aError.Throw(NS_ERROR_FAILURE); + if (!EnsureTarget(aError)) { return nullptr; } + + MOZ_ASSERT(IsTargetValid()); + RefPtr matrix = new DOMMatrix(GetParentObject(), mTarget->GetTransform()); return matrix.forget(); @@ -2175,24 +2229,24 @@ void CanvasRenderingContext2D::SetTransform(double aM11, double aM12, double aM21, double aM22, double aDx, double aDy, ErrorResult& aError) { - EnsureTarget(); - if (!IsTargetValid()) { - aError.Throw(NS_ERROR_FAILURE); + if (!EnsureTarget(aError)) { return; } + MOZ_ASSERT(IsTargetValid()); + Matrix newMatrix(aM11, aM12, aM21, aM22, aDx, aDy); SetTransformInternal(newMatrix); } void CanvasRenderingContext2D::SetTransform(const DOMMatrix2DInit& aInit, ErrorResult& aError) { - EnsureTarget(); - if (!IsTargetValid()) { - aError.Throw(NS_ERROR_FAILURE); + if (!EnsureTarget(aError)) { return; } + MOZ_ASSERT(IsTargetValid()); + RefPtr matrix = DOMMatrixReadOnly::FromMatrix(GetParentObject(), aInit, aError); if (!aError.Failed()) { @@ -2420,11 +2474,12 @@ already_AddRefed CanvasRenderingContext2D::CreatePattern( } else { // Special case for ImageBitmap ImageBitmap& imgBitmap = aSource.GetAsImageBitmap(); - EnsureTarget(); - if (!IsTargetValid()) { - aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + if (!EnsureTarget(aError)) { return nullptr; } + + MOZ_ASSERT(IsTargetValid()); + RefPtr srcSurf = imgBitmap.PrepareForDrawTarget(mTarget); if (!srcSurf) { aError.ThrowInvalidStateError( @@ -2441,12 +2496,12 @@ already_AddRefed CanvasRenderingContext2D::CreatePattern( return pat.forget(); } - EnsureTarget(); - if (!IsTargetValid()) { - aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + if (!EnsureTarget(aError)) { return nullptr; } + MOZ_ASSERT(IsTargetValid()); + // The canvas spec says that createPattern should use the first frame // of animated images auto flags = nsLayoutUtils::SFE_WANT_FIRST_FRAME_IF_IMAGE | @@ -4883,12 +4938,12 @@ UniquePtr CanvasRenderingContext2D::DrawOrMeasureText( processor.mPt.x *= processor.mAppUnitsPerDevPixel; processor.mPt.y *= processor.mAppUnitsPerDevPixel; - EnsureTarget(); - if (!IsTargetValid()) { - aError = NS_ERROR_FAILURE; + if (!EnsureTarget(aError)) { return nullptr; } + MOZ_ASSERT(IsTargetValid()); + Matrix oldTransform = mTarget->GetTransform(); bool restoreTransform = false; // if text is over aMaxWidth, then scale the text horizontally such that its @@ -5329,11 +5384,12 @@ void CanvasRenderingContext2D::DrawImage(const CanvasImageSource& aImage, OffscreenCanvas* offscreenCanvas = nullptr; VideoFrame* videoFrame = nullptr; - EnsureTarget(); - if (!IsTargetValid()) { + if (!EnsureTarget(aError)) { return; } + MOZ_ASSERT(IsTargetValid()); + if (aImage.IsHTMLCanvasElement()) { HTMLCanvasElement* canvas = &aImage.GetAsHTMLCanvasElement(); element = canvas; @@ -5776,11 +5832,12 @@ void CanvasRenderingContext2D::DrawWindow(nsGlobalWindowInner& aWindow, GlobalAlpha() == 1.0f && (op == CompositionOp::OP_OVER || op == CompositionOp::OP_SOURCE); const gfx::Rect drawRect(aX, aY, aW, aH); - EnsureTarget(discardContent ? &drawRect : nullptr); - if (!IsTargetValid()) { + if (!EnsureTarget(aError, discardContent ? &drawRect : nullptr)) { return; } + MOZ_ASSERT(IsTargetValid()); + RefPtr presContext; nsIDocShell* docshell = aWindow.GetDocShell(); if (docshell) { @@ -5882,7 +5939,11 @@ void CanvasRenderingContext2D::DrawWindow(nsGlobalWindowInner& aWindow, &thebes.ref()); // If this canvas was contained in the drawn window, the pre-transaction // callback may have returned its DT. If so, we must reacquire it here. - EnsureTarget(discardContent ? &drawRect : nullptr); + if (!EnsureTarget(aError, discardContent ? &drawRect : nullptr)) { + return; + } + + MOZ_ASSERT(IsTargetValid()); if (drawDT) { RefPtr snapshot = drawDT->Snapshot(); @@ -6222,12 +6283,12 @@ void CanvasRenderingContext2D::PutImageData_explicit( // composition operator must not affect the getImageData() and // putImageData() methods. const gfx::Rect putRect(dirtyRect); - EnsureTarget(&putRect); - - if (!IsTargetValid()) { - return aRv.Throw(NS_ERROR_FAILURE); + if (!EnsureTarget(aRv, &putRect)) { + return; } + MOZ_ASSERT(IsTargetValid()); + DataSourceSurface::MappedSurface map; uint8_t* dstData; IntSize dstSize; -- cgit v1.2.3