1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "OffscreenCanvas.h"
9 #include "mozilla/dom/BlobImpl.h"
10 #include "mozilla/dom/OffscreenCanvasBinding.h"
11 #include "mozilla/dom/Promise.h"
12 #include "mozilla/dom/WorkerPrivate.h"
13 #include "mozilla/dom/WorkerScope.h"
14 #include "mozilla/layers/CanvasRenderer.h"
15 #include "mozilla/layers/CanvasClient.h"
16 #include "mozilla/layers/ImageBridgeChild.h"
17 #include "mozilla/Telemetry.h"
18 #include "CanvasRenderingContext2D.h"
19 #include "CanvasUtils.h"
20 #include "GLContext.h"
21 #include "GLScreenBuffer.h"
22 #include "ImageBitmap.h"
24 namespace mozilla::dom
{
26 OffscreenCanvasCloneData::OffscreenCanvasCloneData(
27 layers::CanvasRenderer
* aRenderer
, uint32_t aWidth
, uint32_t aHeight
,
28 layers::LayersBackend aCompositorBackend
, bool aNeutered
, bool aIsWriteOnly
)
29 : mRenderer(aRenderer
),
32 mCompositorBackendType(aCompositorBackend
),
34 mIsWriteOnly(aIsWriteOnly
) {}
36 OffscreenCanvasCloneData::~OffscreenCanvasCloneData() = default;
38 OffscreenCanvas::OffscreenCanvas(nsIGlobalObject
* aGlobal
, uint32_t aWidth
,
40 layers::LayersBackend aCompositorBackend
,
41 layers::CanvasRenderer
* aRenderer
)
42 : DOMEventTargetHelper(aGlobal
),
48 mCompositorBackendType(aCompositorBackend
),
49 mCanvasRenderer(aRenderer
) {}
51 OffscreenCanvas::~OffscreenCanvas() { ClearResources(); }
53 JSObject
* OffscreenCanvas::WrapObject(JSContext
* aCx
,
54 JS::Handle
<JSObject
*> aGivenProto
) {
55 return OffscreenCanvas_Binding::Wrap(aCx
, this, aGivenProto
);
59 already_AddRefed
<OffscreenCanvas
> OffscreenCanvas::Constructor(
60 const GlobalObject
& aGlobal
, uint32_t aWidth
, uint32_t aHeight
) {
61 nsCOMPtr
<nsIGlobalObject
> global
= do_QueryInterface(aGlobal
.GetAsSupports());
62 RefPtr
<OffscreenCanvas
> offscreenCanvas
= new OffscreenCanvas(
63 global
, aWidth
, aHeight
, layers::LayersBackend::LAYERS_NONE
, nullptr);
64 return offscreenCanvas
.forget();
67 void OffscreenCanvas::ClearResources() {
69 mCanvasClient
->Clear();
72 // if (mCanvasRenderer) {
73 // nsCOMPtr<nsISerialEventTarget> activeTarget =
74 // mCanvasRenderer->GetActiveEventTarget();
75 // MOZ_RELEASE_ASSERT(activeTarget,
76 // "GFX: failed to get active event target.");
78 // activeTarget->IsOnCurrentThread(¤t);
79 // MOZ_RELEASE_ASSERT(current, "GFX: active thread is not current
80 // thread."); mCanvasRenderer->SetCanvasClient(nullptr);
81 // mCanvasRenderer->mContext = nullptr;
82 // mCanvasRenderer->mGLContext = nullptr;
83 // mCanvasRenderer->ResetActiveEventTarget();
86 mCanvasClient
= nullptr;
90 already_AddRefed
<nsISupports
> OffscreenCanvas::GetContext(
91 JSContext
* aCx
, const nsAString
& aContextId
,
92 JS::Handle
<JS::Value
> aContextOptions
, ErrorResult
& aRv
) {
94 aRv
.Throw(NS_ERROR_FAILURE
);
98 // We only support WebGL in workers for now
99 CanvasContextType contextType
;
100 if (!CanvasUtils::GetCanvasContextType(aContextId
, &contextType
)) {
101 aRv
.Throw(NS_ERROR_NOT_IMPLEMENTED
);
105 if (!(contextType
== CanvasContextType::WebGL1
||
106 contextType
== CanvasContextType::WebGL2
||
107 contextType
== CanvasContextType::WebGPU
||
108 contextType
== CanvasContextType::ImageBitmap
)) {
109 aRv
.Throw(NS_ERROR_NOT_IMPLEMENTED
);
113 RefPtr
<nsISupports
> result
= CanvasRenderingContextHelper::GetContext(
114 aCx
, aContextId
, aContextOptions
, aRv
);
116 if (!mCurrentContext
) {
120 if (mCanvasRenderer
) {
121 // mCanvasRenderer->SetContextType(contextType);
122 if (contextType
== CanvasContextType::WebGL1
||
123 contextType
== CanvasContextType::WebGL2
) {
124 MOZ_ASSERT_UNREACHABLE("WebGL OffscreenCanvas not yet supported.");
127 if (contextType
== CanvasContextType::WebGPU
) {
128 MOZ_ASSERT_UNREACHABLE("WebGPU OffscreenCanvas not yet supported.");
133 return result
.forget();
136 layers::ImageContainer
* OffscreenCanvas::GetImageContainer() {
137 if (!mCanvasRenderer
) {
140 // return mCanvasRenderer->GetImageContainer();
144 already_AddRefed
<nsICanvasRenderingContextInternal
>
145 OffscreenCanvas::CreateContext(CanvasContextType aContextType
) {
146 RefPtr
<nsICanvasRenderingContextInternal
> ret
=
147 CanvasRenderingContextHelper::CreateContext(aContextType
);
149 ret
->SetOffscreenCanvas(this);
153 void OffscreenCanvas::CommitFrameToCompositor() {
154 if (!mCanvasRenderer
) {
155 // This offscreen canvas doesn't associate to any HTML canvas element.
156 // So, just bail out.
161 // The attributes has changed, we have to notify main
162 // thread to change canvas size.
165 // if (mCanvasRenderer) {
166 // mCanvasRenderer->SetWidth(mWidth);
167 // mCanvasRenderer->SetHeight(mHeight);
168 // mCanvasRenderer->NotifyElementAboutAttributesChanged();
173 // CanvasContextType contentType = mCanvasRenderer->GetContextType();
174 // if (mCurrentContext && (contentType == CanvasContextType::WebGL1 ||
175 // contentType == CanvasContextType::WebGL2)) {
176 // MOZ_ASSERT_UNREACHABLE("WebGL OffscreenCanvas not yet supported.");
179 // if (mCurrentContext && (contentType == CanvasContextType::WebGPU)) {
180 // MOZ_ASSERT_UNREACHABLE("WebGPU OffscreenCanvas not yet supported.");
184 // if (mCanvasRenderer && mCanvasRenderer->mGLContext) {
185 // mCanvasRenderer->NotifyElementAboutInvalidation();
186 // ImageBridgeChild::GetSingleton()->UpdateAsyncCanvasRenderer(
191 OffscreenCanvasCloneData
* OffscreenCanvas::ToCloneData() {
192 return new OffscreenCanvasCloneData(mCanvasRenderer
, mWidth
, mHeight
,
193 mCompositorBackendType
, mNeutered
,
197 already_AddRefed
<ImageBitmap
> OffscreenCanvas::TransferToImageBitmap(
199 nsCOMPtr
<nsIGlobalObject
> globalObject
= GetGlobalObject();
200 RefPtr
<ImageBitmap
> result
=
201 ImageBitmap::CreateFromOffscreenCanvas(globalObject
, *this, aRv
);
206 // TODO: Clear the content?
207 return result
.forget();
210 already_AddRefed
<Promise
> OffscreenCanvas::ToBlob(JSContext
* aCx
,
211 const nsAString
& aType
,
212 JS::Handle
<JS::Value
> aParams
,
214 // do a trust check if this is a write-only canvas
216 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
220 nsCOMPtr
<nsIGlobalObject
> global
= GetGlobalObject();
222 RefPtr
<Promise
> promise
= Promise::Create(global
, aRv
);
227 // Encoder callback when encoding is complete.
228 class EncodeCallback
: public EncodeCompleteCallback
{
230 EncodeCallback(nsIGlobalObject
* aGlobal
, Promise
* aPromise
)
231 : mGlobal(aGlobal
), mPromise(aPromise
) {}
233 // This is called on main thread.
234 nsresult
ReceiveBlobImpl(already_AddRefed
<BlobImpl
> aBlobImpl
) override
{
235 RefPtr
<BlobImpl
> blobImpl
= aBlobImpl
;
238 RefPtr
<Blob
> blob
= Blob::Create(mGlobal
, blobImpl
);
239 if (NS_WARN_IF(!blob
)) {
240 mPromise
->MaybeReject(NS_ERROR_FAILURE
);
242 mPromise
->MaybeResolve(blob
);
252 nsCOMPtr
<nsIGlobalObject
> mGlobal
;
253 RefPtr
<Promise
> mPromise
;
256 RefPtr
<EncodeCompleteCallback
> callback
= new EncodeCallback(global
, promise
);
259 if (NS_IsMainThread()) {
260 nsCOMPtr
<nsPIDOMWindowInner
> window
= do_QueryInterface(GetGlobalObject());
261 Document
* doc
= window
->GetExtantDoc();
263 doc
? nsContentUtils::ShouldResistFingerprinting(doc
) : false;
265 dom::WorkerPrivate
* workerPrivate
= dom::GetCurrentThreadWorkerPrivate();
266 usePlaceholder
= nsContentUtils::ShouldResistFingerprinting(workerPrivate
);
268 CanvasRenderingContextHelper::ToBlob(aCx
, global
, callback
, aType
, aParams
,
269 usePlaceholder
, aRv
);
271 return promise
.forget();
274 already_AddRefed
<gfx::SourceSurface
> OffscreenCanvas::GetSurfaceSnapshot(
275 gfxAlphaType
* const aOutAlphaType
) {
276 if (!mCurrentContext
) {
280 return mCurrentContext
->GetSurfaceSnapshot(aOutAlphaType
);
283 nsCOMPtr
<nsIGlobalObject
> OffscreenCanvas::GetGlobalObject() {
284 if (NS_IsMainThread()) {
285 return GetParentObject();
288 dom::WorkerPrivate
* workerPrivate
= dom::GetCurrentThreadWorkerPrivate();
289 return workerPrivate
->GlobalScope();
293 already_AddRefed
<OffscreenCanvas
> OffscreenCanvas::CreateFromCloneData(
294 nsIGlobalObject
* aGlobal
, OffscreenCanvasCloneData
* aData
) {
296 RefPtr
<OffscreenCanvas
> wc
=
297 new OffscreenCanvas(aGlobal
, aData
->mWidth
, aData
->mHeight
,
298 aData
->mCompositorBackendType
, aData
->mRenderer
);
299 if (aData
->mNeutered
) {
306 bool OffscreenCanvas::PrefEnabledOnWorkerThread(JSContext
* aCx
,
308 if (NS_IsMainThread()) {
312 return StaticPrefs::gfx_offscreencanvas_enabled();
315 NS_IMPL_CYCLE_COLLECTION_INHERITED(OffscreenCanvas
, DOMEventTargetHelper
,
318 NS_IMPL_ADDREF_INHERITED(OffscreenCanvas
, DOMEventTargetHelper
)
319 NS_IMPL_RELEASE_INHERITED(OffscreenCanvas
, DOMEventTargetHelper
)
321 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(OffscreenCanvas
)
322 NS_INTERFACE_MAP_ENTRY(nsISupports
)
323 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper
)
325 } // namespace mozilla::dom