summaryrefslogtreecommitdiffstats
path: root/dom/canvas/CanvasRenderingContext2D.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:13:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:13:27 +0000
commit40a355a42d4a9444dc753c04c6608dade2f06a23 (patch)
tree871fc667d2de662f171103ce5ec067014ef85e61 /dom/canvas/CanvasRenderingContext2D.cpp
parentAdding upstream version 124.0.1. (diff)
downloadfirefox-40a355a42d4a9444dc753c04c6608dade2f06a23.tar.xz
firefox-40a355a42d4a9444dc753c04c6608dade2f06a23.zip
Adding upstream version 125.0.1.upstream/125.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/canvas/CanvasRenderingContext2D.cpp')
-rw-r--r--dom/canvas/CanvasRenderingContext2D.cpp271
1 files changed, 166 insertions, 105 deletions
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<ImageBridgeChild> 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<gfx::DrawTarget>& aOutDT,
- RefPtr<layers::PersistentBufferProvider>& aOutProvider) {
+ RefPtr<layers::PersistentBufferProvider>& 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<DOMMatrix> CanvasRenderingContext2D::GetTransform(
ErrorResult& aError) {
- EnsureTarget();
- if (!IsTargetValid()) {
- aError.Throw(NS_ERROR_FAILURE);
+ if (!EnsureTarget(aError)) {
return nullptr;
}
+
+ MOZ_ASSERT(IsTargetValid());
+
RefPtr<DOMMatrix> 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<DOMMatrixReadOnly> matrix =
DOMMatrixReadOnly::FromMatrix(GetParentObject(), aInit, aError);
if (!aError.Failed()) {
@@ -2420,11 +2474,12 @@ already_AddRefed<CanvasPattern> 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<SourceSurface> srcSurf = imgBitmap.PrepareForDrawTarget(mTarget);
if (!srcSurf) {
aError.ThrowInvalidStateError(
@@ -2441,12 +2496,12 @@ already_AddRefed<CanvasPattern> 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<TextMetrics> 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<nsPresContext> 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<SourceSurface> 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;