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/OffscreenCanvasBinding.h"
10 #include "mozilla/dom/WorkerPrivate.h"
11 #include "mozilla/dom/WorkerScope.h"
12 #include "mozilla/layers/AsyncCanvasRenderer.h"
13 #include "mozilla/layers/CanvasClient.h"
14 #include "mozilla/layers/ImageBridgeChild.h"
15 #include "mozilla/Telemetry.h"
16 #include "CanvasRenderingContext2D.h"
17 #include "CanvasUtils.h"
18 #include "GLContext.h"
19 #include "GLScreenBuffer.h"
24 OffscreenCanvasCloneData::OffscreenCanvasCloneData(
25 layers::AsyncCanvasRenderer
* aRenderer
, uint32_t aWidth
, uint32_t aHeight
,
26 layers::LayersBackend aCompositorBackend
, bool aNeutered
, bool aIsWriteOnly
)
27 : mRenderer(aRenderer
),
30 mCompositorBackendType(aCompositorBackend
),
32 mIsWriteOnly(aIsWriteOnly
) {}
34 OffscreenCanvasCloneData::~OffscreenCanvasCloneData() {}
36 OffscreenCanvas::OffscreenCanvas(nsIGlobalObject
* aGlobal
, uint32_t aWidth
,
38 layers::LayersBackend aCompositorBackend
,
39 layers::AsyncCanvasRenderer
* aRenderer
)
40 : DOMEventTargetHelper(aGlobal
),
46 mCompositorBackendType(aCompositorBackend
),
47 mCanvasRenderer(aRenderer
) {}
49 OffscreenCanvas::~OffscreenCanvas() { ClearResources(); }
51 JSObject
* OffscreenCanvas::WrapObject(JSContext
* aCx
,
52 JS::Handle
<JSObject
*> aGivenProto
) {
53 return OffscreenCanvas_Binding::Wrap(aCx
, this, aGivenProto
);
57 already_AddRefed
<OffscreenCanvas
> OffscreenCanvas::Constructor(
58 const GlobalObject
& aGlobal
, uint32_t aWidth
, uint32_t aHeight
) {
59 nsCOMPtr
<nsIGlobalObject
> global
= do_QueryInterface(aGlobal
.GetAsSupports());
60 RefPtr
<OffscreenCanvas
> offscreenCanvas
= new OffscreenCanvas(
61 global
, aWidth
, aHeight
, layers::LayersBackend::LAYERS_NONE
, nullptr);
62 return offscreenCanvas
.forget();
65 void OffscreenCanvas::ClearResources() {
67 mCanvasClient
->Clear();
69 if (mCanvasRenderer
) {
70 nsCOMPtr
<nsISerialEventTarget
> activeTarget
=
71 mCanvasRenderer
->GetActiveEventTarget();
72 MOZ_RELEASE_ASSERT(activeTarget
,
73 "GFX: failed to get active event target.");
75 activeTarget
->IsOnCurrentThread(¤t
);
76 MOZ_RELEASE_ASSERT(current
, "GFX: active thread is not current thread.");
77 mCanvasRenderer
->SetCanvasClient(nullptr);
78 mCanvasRenderer
->mContext
= nullptr;
79 mCanvasRenderer
->mGLContext
= nullptr;
80 mCanvasRenderer
->ResetActiveEventTarget();
83 mCanvasClient
= nullptr;
87 already_AddRefed
<nsISupports
> OffscreenCanvas::GetContext(
88 JSContext
* aCx
, const nsAString
& aContextId
,
89 JS::Handle
<JS::Value
> aContextOptions
, ErrorResult
& aRv
) {
91 aRv
.Throw(NS_ERROR_FAILURE
);
95 // We only support WebGL in workers for now
96 CanvasContextType contextType
;
97 if (!CanvasUtils::GetCanvasContextType(aContextId
, &contextType
)) {
98 aRv
.Throw(NS_ERROR_NOT_IMPLEMENTED
);
102 if (!(contextType
== CanvasContextType::WebGL1
||
103 contextType
== CanvasContextType::WebGL2
||
104 contextType
== CanvasContextType::ImageBitmap
)) {
105 aRv
.Throw(NS_ERROR_NOT_IMPLEMENTED
);
109 RefPtr
<nsISupports
> result
= CanvasRenderingContextHelper::GetContext(
110 aCx
, aContextId
, aContextOptions
, aRv
);
112 if (!mCurrentContext
) {
116 if (mCanvasRenderer
) {
117 mCanvasRenderer
->SetContextType(contextType
);
118 if (contextType
== CanvasContextType::WebGL1
||
119 contextType
== CanvasContextType::WebGL2
) {
120 MOZ_ASSERT_UNREACHABLE("WebGL OffscreenCanvas not yet supported.");
125 return result
.forget();
128 ImageContainer
* OffscreenCanvas::GetImageContainer() {
129 if (!mCanvasRenderer
) {
132 return mCanvasRenderer
->GetImageContainer();
135 already_AddRefed
<nsICanvasRenderingContextInternal
>
136 OffscreenCanvas::CreateContext(CanvasContextType aContextType
) {
137 RefPtr
<nsICanvasRenderingContextInternal
> ret
=
138 CanvasRenderingContextHelper::CreateContext(aContextType
);
140 ret
->SetOffscreenCanvas(this);
144 void OffscreenCanvas::CommitFrameToCompositor() {
145 if (!mCanvasRenderer
) {
146 // This offscreen canvas doesn't associate to any HTML canvas element.
147 // So, just bail out.
151 // The attributes has changed, we have to notify main
152 // thread to change canvas size.
154 if (mCanvasRenderer
) {
155 mCanvasRenderer
->SetWidth(mWidth
);
156 mCanvasRenderer
->SetHeight(mHeight
);
157 mCanvasRenderer
->NotifyElementAboutAttributesChanged();
162 CanvasContextType contentType
= mCanvasRenderer
->GetContextType();
163 if (mCurrentContext
&& (contentType
== CanvasContextType::WebGL1
||
164 contentType
== CanvasContextType::WebGL2
)) {
165 MOZ_ASSERT_UNREACHABLE("WebGL OffscreenCanvas not yet supported.");
169 if (mCanvasRenderer
&& mCanvasRenderer
->mGLContext
) {
170 mCanvasRenderer
->NotifyElementAboutInvalidation();
171 ImageBridgeChild::GetSingleton()->UpdateAsyncCanvasRenderer(
176 OffscreenCanvasCloneData
* OffscreenCanvas::ToCloneData() {
177 return new OffscreenCanvasCloneData(mCanvasRenderer
, mWidth
, mHeight
,
178 mCompositorBackendType
, mNeutered
,
182 already_AddRefed
<ImageBitmap
> OffscreenCanvas::TransferToImageBitmap(
184 nsCOMPtr
<nsIGlobalObject
> globalObject
= GetGlobalObject();
185 RefPtr
<ImageBitmap
> result
=
186 ImageBitmap::CreateFromOffscreenCanvas(globalObject
, *this, aRv
);
191 // TODO: Clear the content?
192 return result
.forget();
195 already_AddRefed
<Promise
> OffscreenCanvas::ToBlob(JSContext
* aCx
,
196 const nsAString
& aType
,
197 JS::Handle
<JS::Value
> aParams
,
199 // do a trust check if this is a write-only canvas
201 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
205 nsCOMPtr
<nsIGlobalObject
> global
= GetGlobalObject();
207 RefPtr
<Promise
> promise
= Promise::Create(global
, aRv
);
212 // Encoder callback when encoding is complete.
213 class EncodeCallback
: public EncodeCompleteCallback
{
215 EncodeCallback(nsIGlobalObject
* aGlobal
, Promise
* aPromise
)
216 : mGlobal(aGlobal
), mPromise(aPromise
) {}
218 // This is called on main thread.
219 nsresult
ReceiveBlobImpl(already_AddRefed
<BlobImpl
> aBlobImpl
) override
{
220 RefPtr
<BlobImpl
> blobImpl
= aBlobImpl
;
223 RefPtr
<Blob
> blob
= Blob::Create(mGlobal
, blobImpl
);
224 if (NS_WARN_IF(!blob
)) {
225 mPromise
->MaybeReject(NS_ERROR_FAILURE
);
227 mPromise
->MaybeResolve(blob
);
237 nsCOMPtr
<nsIGlobalObject
> mGlobal
;
238 RefPtr
<Promise
> mPromise
;
241 RefPtr
<EncodeCompleteCallback
> callback
= new EncodeCallback(global
, promise
);
244 if (NS_IsMainThread()) {
245 nsCOMPtr
<nsPIDOMWindowInner
> window
= do_QueryInterface(GetGlobalObject());
246 Document
* doc
= window
->GetExtantDoc();
248 doc
? nsContentUtils::ShouldResistFingerprinting(doc
) : false;
250 dom::WorkerPrivate
* workerPrivate
= dom::GetCurrentThreadWorkerPrivate();
251 usePlaceholder
= nsContentUtils::ShouldResistFingerprinting(workerPrivate
);
253 CanvasRenderingContextHelper::ToBlob(aCx
, global
, callback
, aType
, aParams
,
254 usePlaceholder
, aRv
);
256 return promise
.forget();
259 already_AddRefed
<gfx::SourceSurface
> OffscreenCanvas::GetSurfaceSnapshot(
260 gfxAlphaType
* const aOutAlphaType
) {
261 if (!mCurrentContext
) {
265 return mCurrentContext
->GetSurfaceSnapshot(aOutAlphaType
);
268 nsCOMPtr
<nsIGlobalObject
> OffscreenCanvas::GetGlobalObject() {
269 if (NS_IsMainThread()) {
270 return GetParentObject();
273 dom::WorkerPrivate
* workerPrivate
= dom::GetCurrentThreadWorkerPrivate();
274 return workerPrivate
->GlobalScope();
278 already_AddRefed
<OffscreenCanvas
> OffscreenCanvas::CreateFromCloneData(
279 nsIGlobalObject
* aGlobal
, OffscreenCanvasCloneData
* aData
) {
281 RefPtr
<OffscreenCanvas
> wc
=
282 new OffscreenCanvas(aGlobal
, aData
->mWidth
, aData
->mHeight
,
283 aData
->mCompositorBackendType
, aData
->mRenderer
);
284 if (aData
->mNeutered
) {
291 bool OffscreenCanvas::PrefEnabledOnWorkerThread(JSContext
* aCx
,
293 if (NS_IsMainThread()) {
297 return StaticPrefs::gfx_offscreencanvas_enabled();
300 NS_IMPL_CYCLE_COLLECTION_INHERITED(OffscreenCanvas
, DOMEventTargetHelper
,
303 NS_IMPL_ADDREF_INHERITED(OffscreenCanvas
, DOMEventTargetHelper
)
304 NS_IMPL_RELEASE_INHERITED(OffscreenCanvas
, DOMEventTargetHelper
)
306 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(OffscreenCanvas
)
307 NS_INTERFACE_MAP_ENTRY(nsISupports
)
308 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper
)
311 } // namespace mozilla