diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /dom/canvas/OffscreenCanvas.cpp | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/canvas/OffscreenCanvas.cpp')
-rw-r--r-- | dom/canvas/OffscreenCanvas.cpp | 325 |
1 files changed, 325 insertions, 0 deletions
diff --git a/dom/canvas/OffscreenCanvas.cpp b/dom/canvas/OffscreenCanvas.cpp new file mode 100644 index 0000000000..859f60b219 --- /dev/null +++ b/dom/canvas/OffscreenCanvas.cpp @@ -0,0 +1,325 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* 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 "OffscreenCanvas.h" + +#include "mozilla/dom/BlobImpl.h" +#include "mozilla/dom/OffscreenCanvasBinding.h" +#include "mozilla/dom/Promise.h" +#include "mozilla/dom/WorkerPrivate.h" +#include "mozilla/dom/WorkerScope.h" +#include "mozilla/layers/CanvasRenderer.h" +#include "mozilla/layers/CanvasClient.h" +#include "mozilla/layers/ImageBridgeChild.h" +#include "mozilla/Telemetry.h" +#include "CanvasRenderingContext2D.h" +#include "CanvasUtils.h" +#include "GLContext.h" +#include "GLScreenBuffer.h" +#include "ImageBitmap.h" + +namespace mozilla::dom { + +OffscreenCanvasCloneData::OffscreenCanvasCloneData( + layers::CanvasRenderer* aRenderer, uint32_t aWidth, uint32_t aHeight, + layers::LayersBackend aCompositorBackend, bool aNeutered, bool aIsWriteOnly) + : mRenderer(aRenderer), + mWidth(aWidth), + mHeight(aHeight), + mCompositorBackendType(aCompositorBackend), + mNeutered(aNeutered), + mIsWriteOnly(aIsWriteOnly) {} + +OffscreenCanvasCloneData::~OffscreenCanvasCloneData() = default; + +OffscreenCanvas::OffscreenCanvas(nsIGlobalObject* aGlobal, uint32_t aWidth, + uint32_t aHeight, + layers::LayersBackend aCompositorBackend, + layers::CanvasRenderer* aRenderer) + : DOMEventTargetHelper(aGlobal), + mAttrDirty(false), + mNeutered(false), + mIsWriteOnly(false), + mWidth(aWidth), + mHeight(aHeight), + mCompositorBackendType(aCompositorBackend), + mCanvasRenderer(aRenderer) {} + +OffscreenCanvas::~OffscreenCanvas() { ClearResources(); } + +JSObject* OffscreenCanvas::WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) { + return OffscreenCanvas_Binding::Wrap(aCx, this, aGivenProto); +} + +/* static */ +already_AddRefed<OffscreenCanvas> OffscreenCanvas::Constructor( + const GlobalObject& aGlobal, uint32_t aWidth, uint32_t aHeight) { + nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); + RefPtr<OffscreenCanvas> offscreenCanvas = new OffscreenCanvas( + global, aWidth, aHeight, layers::LayersBackend::LAYERS_NONE, nullptr); + return offscreenCanvas.forget(); +} + +void OffscreenCanvas::ClearResources() { + if (mCanvasClient) { + mCanvasClient->Clear(); + + MOZ_CRASH("todo"); + // if (mCanvasRenderer) { + // nsCOMPtr<nsISerialEventTarget> activeTarget = + // mCanvasRenderer->GetActiveEventTarget(); + // MOZ_RELEASE_ASSERT(activeTarget, + // "GFX: failed to get active event target."); + // bool current; + // activeTarget->IsOnCurrentThread(¤t); + // MOZ_RELEASE_ASSERT(current, "GFX: active thread is not current + // thread."); mCanvasRenderer->SetCanvasClient(nullptr); + // mCanvasRenderer->mContext = nullptr; + // mCanvasRenderer->mGLContext = nullptr; + // mCanvasRenderer->ResetActiveEventTarget(); + //} + + mCanvasClient = nullptr; + } +} + +already_AddRefed<nsISupports> OffscreenCanvas::GetContext( + JSContext* aCx, const nsAString& aContextId, + JS::Handle<JS::Value> aContextOptions, ErrorResult& aRv) { + if (mNeutered) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + // We only support WebGL in workers for now + CanvasContextType contextType; + if (!CanvasUtils::GetCanvasContextType(aContextId, &contextType)) { + aRv.Throw(NS_ERROR_NOT_IMPLEMENTED); + return nullptr; + } + + if (!(contextType == CanvasContextType::WebGL1 || + contextType == CanvasContextType::WebGL2 || + contextType == CanvasContextType::WebGPU || + contextType == CanvasContextType::ImageBitmap)) { + aRv.Throw(NS_ERROR_NOT_IMPLEMENTED); + return nullptr; + } + + RefPtr<nsISupports> result = CanvasRenderingContextHelper::GetContext( + aCx, aContextId, aContextOptions, aRv); + + if (!mCurrentContext) { + return nullptr; + } + + if (mCanvasRenderer) { + // mCanvasRenderer->SetContextType(contextType); + if (contextType == CanvasContextType::WebGL1 || + contextType == CanvasContextType::WebGL2) { + MOZ_ASSERT_UNREACHABLE("WebGL OffscreenCanvas not yet supported."); + return nullptr; + } + if (contextType == CanvasContextType::WebGPU) { + MOZ_ASSERT_UNREACHABLE("WebGPU OffscreenCanvas not yet supported."); + return nullptr; + } + } + + return result.forget(); +} + +layers::ImageContainer* OffscreenCanvas::GetImageContainer() { + if (!mCanvasRenderer) { + return nullptr; + } + // return mCanvasRenderer->GetImageContainer(); + MOZ_CRASH("todo"); +} + +already_AddRefed<nsICanvasRenderingContextInternal> +OffscreenCanvas::CreateContext(CanvasContextType aContextType) { + RefPtr<nsICanvasRenderingContextInternal> ret = + CanvasRenderingContextHelper::CreateContext(aContextType); + + ret->SetOffscreenCanvas(this); + return ret.forget(); +} + +void OffscreenCanvas::CommitFrameToCompositor() { + if (!mCanvasRenderer) { + // This offscreen canvas doesn't associate to any HTML canvas element. + // So, just bail out. + return; + } + MOZ_CRASH("todo"); + + // The attributes has changed, we have to notify main + // thread to change canvas size. + if (mAttrDirty) { + MOZ_CRASH("todo"); + // if (mCanvasRenderer) { + // mCanvasRenderer->SetWidth(mWidth); + // mCanvasRenderer->SetHeight(mHeight); + // mCanvasRenderer->NotifyElementAboutAttributesChanged(); + //} + mAttrDirty = false; + } + + // CanvasContextType contentType = mCanvasRenderer->GetContextType(); + // if (mCurrentContext && (contentType == CanvasContextType::WebGL1 || + // contentType == CanvasContextType::WebGL2)) { + // MOZ_ASSERT_UNREACHABLE("WebGL OffscreenCanvas not yet supported."); + // return; + //} + // if (mCurrentContext && (contentType == CanvasContextType::WebGPU)) { + // MOZ_ASSERT_UNREACHABLE("WebGPU OffscreenCanvas not yet supported."); + // return; + //} + + // if (mCanvasRenderer && mCanvasRenderer->mGLContext) { + // mCanvasRenderer->NotifyElementAboutInvalidation(); + // ImageBridgeChild::GetSingleton()->UpdateAsyncCanvasRenderer( + // mCanvasRenderer); + //} +} + +OffscreenCanvasCloneData* OffscreenCanvas::ToCloneData() { + return new OffscreenCanvasCloneData(mCanvasRenderer, mWidth, mHeight, + mCompositorBackendType, mNeutered, + mIsWriteOnly); +} + +already_AddRefed<ImageBitmap> OffscreenCanvas::TransferToImageBitmap( + ErrorResult& aRv) { + nsCOMPtr<nsIGlobalObject> globalObject = GetGlobalObject(); + RefPtr<ImageBitmap> result = + ImageBitmap::CreateFromOffscreenCanvas(globalObject, *this, aRv); + if (aRv.Failed()) { + return nullptr; + } + + // TODO: Clear the content? + return result.forget(); +} + +already_AddRefed<Promise> OffscreenCanvas::ToBlob(JSContext* aCx, + const nsAString& aType, + JS::Handle<JS::Value> aParams, + ErrorResult& aRv) { + // do a trust check if this is a write-only canvas + if (mIsWriteOnly) { + aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); + return nullptr; + } + + nsCOMPtr<nsIGlobalObject> global = GetGlobalObject(); + + RefPtr<Promise> promise = Promise::Create(global, aRv); + if (aRv.Failed()) { + return nullptr; + } + + // Encoder callback when encoding is complete. + class EncodeCallback : public EncodeCompleteCallback { + public: + EncodeCallback(nsIGlobalObject* aGlobal, Promise* aPromise) + : mGlobal(aGlobal), mPromise(aPromise) {} + + // This is called on main thread. + nsresult ReceiveBlobImpl(already_AddRefed<BlobImpl> aBlobImpl) override { + RefPtr<BlobImpl> blobImpl = aBlobImpl; + + if (mPromise) { + RefPtr<Blob> blob = Blob::Create(mGlobal, blobImpl); + if (NS_WARN_IF(!blob)) { + mPromise->MaybeReject(NS_ERROR_FAILURE); + } else { + mPromise->MaybeResolve(blob); + } + } + + mGlobal = nullptr; + mPromise = nullptr; + + return NS_OK; + } + + nsCOMPtr<nsIGlobalObject> mGlobal; + RefPtr<Promise> mPromise; + }; + + RefPtr<EncodeCompleteCallback> callback = new EncodeCallback(global, promise); + + bool usePlaceholder; + if (NS_IsMainThread()) { + nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(GetGlobalObject()); + Document* doc = window->GetExtantDoc(); + usePlaceholder = + doc ? nsContentUtils::ShouldResistFingerprinting(doc) : false; + } else { + dom::WorkerPrivate* workerPrivate = dom::GetCurrentThreadWorkerPrivate(); + usePlaceholder = nsContentUtils::ShouldResistFingerprinting(workerPrivate); + } + CanvasRenderingContextHelper::ToBlob(aCx, global, callback, aType, aParams, + usePlaceholder, aRv); + + return promise.forget(); +} + +already_AddRefed<gfx::SourceSurface> OffscreenCanvas::GetSurfaceSnapshot( + gfxAlphaType* const aOutAlphaType) { + if (!mCurrentContext) { + return nullptr; + } + + return mCurrentContext->GetSurfaceSnapshot(aOutAlphaType); +} + +nsCOMPtr<nsIGlobalObject> OffscreenCanvas::GetGlobalObject() { + if (NS_IsMainThread()) { + return GetParentObject(); + } + + dom::WorkerPrivate* workerPrivate = dom::GetCurrentThreadWorkerPrivate(); + return workerPrivate->GlobalScope(); +} + +/* static */ +already_AddRefed<OffscreenCanvas> OffscreenCanvas::CreateFromCloneData( + nsIGlobalObject* aGlobal, OffscreenCanvasCloneData* aData) { + MOZ_ASSERT(aData); + RefPtr<OffscreenCanvas> wc = + new OffscreenCanvas(aGlobal, aData->mWidth, aData->mHeight, + aData->mCompositorBackendType, aData->mRenderer); + if (aData->mNeutered) { + wc->SetNeutered(); + } + return wc.forget(); +} + +/* static */ +bool OffscreenCanvas::PrefEnabledOnWorkerThread(JSContext* aCx, + JSObject* aObj) { + if (NS_IsMainThread()) { + return true; + } + + return StaticPrefs::gfx_offscreencanvas_enabled(); +} + +NS_IMPL_CYCLE_COLLECTION_INHERITED(OffscreenCanvas, DOMEventTargetHelper, + mCurrentContext) + +NS_IMPL_ADDREF_INHERITED(OffscreenCanvas, DOMEventTargetHelper) +NS_IMPL_RELEASE_INHERITED(OffscreenCanvas, DOMEventTargetHelper) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(OffscreenCanvas) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) + +} // namespace mozilla::dom |