Bug 1729395 - Handle message sender going away during message processing r=robwu
[gecko.git] / dom / canvas / OffscreenCanvas.cpp
blob859f60b219afac1cf8640768f7887a01b52ab68a
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),
30 mWidth(aWidth),
31 mHeight(aHeight),
32 mCompositorBackendType(aCompositorBackend),
33 mNeutered(aNeutered),
34 mIsWriteOnly(aIsWriteOnly) {}
36 OffscreenCanvasCloneData::~OffscreenCanvasCloneData() = default;
38 OffscreenCanvas::OffscreenCanvas(nsIGlobalObject* aGlobal, uint32_t aWidth,
39 uint32_t aHeight,
40 layers::LayersBackend aCompositorBackend,
41 layers::CanvasRenderer* aRenderer)
42 : DOMEventTargetHelper(aGlobal),
43 mAttrDirty(false),
44 mNeutered(false),
45 mIsWriteOnly(false),
46 mWidth(aWidth),
47 mHeight(aHeight),
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);
58 /* static */
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() {
68 if (mCanvasClient) {
69 mCanvasClient->Clear();
71 MOZ_CRASH("todo");
72 // if (mCanvasRenderer) {
73 // nsCOMPtr<nsISerialEventTarget> activeTarget =
74 // mCanvasRenderer->GetActiveEventTarget();
75 // MOZ_RELEASE_ASSERT(activeTarget,
76 // "GFX: failed to get active event target.");
77 // bool current;
78 // activeTarget->IsOnCurrentThread(&current);
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();
84 //}
86 mCanvasClient = nullptr;
90 already_AddRefed<nsISupports> OffscreenCanvas::GetContext(
91 JSContext* aCx, const nsAString& aContextId,
92 JS::Handle<JS::Value> aContextOptions, ErrorResult& aRv) {
93 if (mNeutered) {
94 aRv.Throw(NS_ERROR_FAILURE);
95 return nullptr;
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);
102 return nullptr;
105 if (!(contextType == CanvasContextType::WebGL1 ||
106 contextType == CanvasContextType::WebGL2 ||
107 contextType == CanvasContextType::WebGPU ||
108 contextType == CanvasContextType::ImageBitmap)) {
109 aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
110 return nullptr;
113 RefPtr<nsISupports> result = CanvasRenderingContextHelper::GetContext(
114 aCx, aContextId, aContextOptions, aRv);
116 if (!mCurrentContext) {
117 return nullptr;
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.");
125 return nullptr;
127 if (contextType == CanvasContextType::WebGPU) {
128 MOZ_ASSERT_UNREACHABLE("WebGPU OffscreenCanvas not yet supported.");
129 return nullptr;
133 return result.forget();
136 layers::ImageContainer* OffscreenCanvas::GetImageContainer() {
137 if (!mCanvasRenderer) {
138 return nullptr;
140 // return mCanvasRenderer->GetImageContainer();
141 MOZ_CRASH("todo");
144 already_AddRefed<nsICanvasRenderingContextInternal>
145 OffscreenCanvas::CreateContext(CanvasContextType aContextType) {
146 RefPtr<nsICanvasRenderingContextInternal> ret =
147 CanvasRenderingContextHelper::CreateContext(aContextType);
149 ret->SetOffscreenCanvas(this);
150 return ret.forget();
153 void OffscreenCanvas::CommitFrameToCompositor() {
154 if (!mCanvasRenderer) {
155 // This offscreen canvas doesn't associate to any HTML canvas element.
156 // So, just bail out.
157 return;
159 MOZ_CRASH("todo");
161 // The attributes has changed, we have to notify main
162 // thread to change canvas size.
163 if (mAttrDirty) {
164 MOZ_CRASH("todo");
165 // if (mCanvasRenderer) {
166 // mCanvasRenderer->SetWidth(mWidth);
167 // mCanvasRenderer->SetHeight(mHeight);
168 // mCanvasRenderer->NotifyElementAboutAttributesChanged();
170 mAttrDirty = false;
173 // CanvasContextType contentType = mCanvasRenderer->GetContextType();
174 // if (mCurrentContext && (contentType == CanvasContextType::WebGL1 ||
175 // contentType == CanvasContextType::WebGL2)) {
176 // MOZ_ASSERT_UNREACHABLE("WebGL OffscreenCanvas not yet supported.");
177 // return;
179 // if (mCurrentContext && (contentType == CanvasContextType::WebGPU)) {
180 // MOZ_ASSERT_UNREACHABLE("WebGPU OffscreenCanvas not yet supported.");
181 // return;
184 // if (mCanvasRenderer && mCanvasRenderer->mGLContext) {
185 // mCanvasRenderer->NotifyElementAboutInvalidation();
186 // ImageBridgeChild::GetSingleton()->UpdateAsyncCanvasRenderer(
187 // mCanvasRenderer);
191 OffscreenCanvasCloneData* OffscreenCanvas::ToCloneData() {
192 return new OffscreenCanvasCloneData(mCanvasRenderer, mWidth, mHeight,
193 mCompositorBackendType, mNeutered,
194 mIsWriteOnly);
197 already_AddRefed<ImageBitmap> OffscreenCanvas::TransferToImageBitmap(
198 ErrorResult& aRv) {
199 nsCOMPtr<nsIGlobalObject> globalObject = GetGlobalObject();
200 RefPtr<ImageBitmap> result =
201 ImageBitmap::CreateFromOffscreenCanvas(globalObject, *this, aRv);
202 if (aRv.Failed()) {
203 return nullptr;
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,
213 ErrorResult& aRv) {
214 // do a trust check if this is a write-only canvas
215 if (mIsWriteOnly) {
216 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
217 return nullptr;
220 nsCOMPtr<nsIGlobalObject> global = GetGlobalObject();
222 RefPtr<Promise> promise = Promise::Create(global, aRv);
223 if (aRv.Failed()) {
224 return nullptr;
227 // Encoder callback when encoding is complete.
228 class EncodeCallback : public EncodeCompleteCallback {
229 public:
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;
237 if (mPromise) {
238 RefPtr<Blob> blob = Blob::Create(mGlobal, blobImpl);
239 if (NS_WARN_IF(!blob)) {
240 mPromise->MaybeReject(NS_ERROR_FAILURE);
241 } else {
242 mPromise->MaybeResolve(blob);
246 mGlobal = nullptr;
247 mPromise = nullptr;
249 return NS_OK;
252 nsCOMPtr<nsIGlobalObject> mGlobal;
253 RefPtr<Promise> mPromise;
256 RefPtr<EncodeCompleteCallback> callback = new EncodeCallback(global, promise);
258 bool usePlaceholder;
259 if (NS_IsMainThread()) {
260 nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(GetGlobalObject());
261 Document* doc = window->GetExtantDoc();
262 usePlaceholder =
263 doc ? nsContentUtils::ShouldResistFingerprinting(doc) : false;
264 } else {
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) {
277 return nullptr;
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();
292 /* static */
293 already_AddRefed<OffscreenCanvas> OffscreenCanvas::CreateFromCloneData(
294 nsIGlobalObject* aGlobal, OffscreenCanvasCloneData* aData) {
295 MOZ_ASSERT(aData);
296 RefPtr<OffscreenCanvas> wc =
297 new OffscreenCanvas(aGlobal, aData->mWidth, aData->mHeight,
298 aData->mCompositorBackendType, aData->mRenderer);
299 if (aData->mNeutered) {
300 wc->SetNeutered();
302 return wc.forget();
305 /* static */
306 bool OffscreenCanvas::PrefEnabledOnWorkerThread(JSContext* aCx,
307 JSObject* aObj) {
308 if (NS_IsMainThread()) {
309 return true;
312 return StaticPrefs::gfx_offscreencanvas_enabled();
315 NS_IMPL_CYCLE_COLLECTION_INHERITED(OffscreenCanvas, DOMEventTargetHelper,
316 mCurrentContext)
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