Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/canvas/OffscreenCanvas.cpp
Line
Count
Source (jump to first uncovered line)
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/. */
6
7
#include "OffscreenCanvas.h"
8
9
#include "mozilla/dom/DOMPrefs.h"
10
#include "mozilla/dom/OffscreenCanvasBinding.h"
11
#include "mozilla/dom/WorkerPrivate.h"
12
#include "mozilla/dom/WorkerScope.h"
13
#include "mozilla/layers/AsyncCanvasRenderer.h"
14
#include "mozilla/layers/CanvasClient.h"
15
#include "mozilla/layers/ImageBridgeChild.h"
16
#include "mozilla/Telemetry.h"
17
#include "CanvasRenderingContext2D.h"
18
#include "CanvasUtils.h"
19
#include "GLScreenBuffer.h"
20
#include "WebGL1Context.h"
21
#include "WebGL2Context.h"
22
23
namespace mozilla {
24
namespace dom {
25
26
OffscreenCanvasCloneData::OffscreenCanvasCloneData(layers::AsyncCanvasRenderer* aRenderer,
27
                                                   uint32_t aWidth, uint32_t aHeight,
28
                                                   layers::LayersBackend aCompositorBackend,
29
                                                   bool aNeutered, bool aIsWriteOnly)
30
  : mRenderer(aRenderer)
31
  , mWidth(aWidth)
32
  , mHeight(aHeight)
33
  , mCompositorBackendType(aCompositorBackend)
34
  , mNeutered(aNeutered)
35
  , mIsWriteOnly(aIsWriteOnly)
36
0
{
37
0
}
38
39
OffscreenCanvasCloneData::~OffscreenCanvasCloneData()
40
0
{
41
0
}
42
43
OffscreenCanvas::OffscreenCanvas(nsIGlobalObject* aGlobal,
44
                                 uint32_t aWidth,
45
                                 uint32_t aHeight,
46
                                 layers::LayersBackend aCompositorBackend,
47
                                 layers::AsyncCanvasRenderer* aRenderer)
48
  : DOMEventTargetHelper(aGlobal)
49
  , mAttrDirty(false)
50
  , mNeutered(false)
51
  , mIsWriteOnly(false)
52
  , mWidth(aWidth)
53
  , mHeight(aHeight)
54
  , mCompositorBackendType(aCompositorBackend)
55
  , mCanvasRenderer(aRenderer)
56
0
{}
57
58
OffscreenCanvas::~OffscreenCanvas()
59
0
{
60
0
  ClearResources();
61
0
}
62
63
JSObject*
64
OffscreenCanvas::WrapObject(JSContext* aCx,
65
                            JS::Handle<JSObject*> aGivenProto)
66
0
{
67
0
  return OffscreenCanvas_Binding::Wrap(aCx, this, aGivenProto);
68
0
}
69
70
/* static */ already_AddRefed<OffscreenCanvas>
71
OffscreenCanvas::Constructor(const GlobalObject& aGlobal,
72
                             uint32_t aWidth,
73
                             uint32_t aHeight,
74
                             ErrorResult& aRv)
75
0
{
76
0
  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
77
0
  RefPtr<OffscreenCanvas> offscreenCanvas =
78
0
    new OffscreenCanvas(global, aWidth, aHeight,
79
0
                        layers::LayersBackend::LAYERS_NONE, nullptr);
80
0
  return offscreenCanvas.forget();
81
0
}
82
83
void
84
OffscreenCanvas::ClearResources()
85
0
{
86
0
  if (mCanvasClient) {
87
0
    mCanvasClient->Clear();
88
0
89
0
    if (mCanvasRenderer) {
90
0
      nsCOMPtr<nsISerialEventTarget> activeTarget = mCanvasRenderer->GetActiveEventTarget();
91
0
      MOZ_RELEASE_ASSERT(activeTarget, "GFX: failed to get active event target.");
92
0
      bool current;
93
0
      activeTarget->IsOnCurrentThread(&current);
94
0
      MOZ_RELEASE_ASSERT(current, "GFX: active thread is not current thread.");
95
0
      mCanvasRenderer->SetCanvasClient(nullptr);
96
0
      mCanvasRenderer->mContext = nullptr;
97
0
      mCanvasRenderer->mGLContext = nullptr;
98
0
      mCanvasRenderer->ResetActiveEventTarget();
99
0
    }
100
0
101
0
    mCanvasClient = nullptr;
102
0
  }
103
0
}
104
105
already_AddRefed<nsISupports>
106
OffscreenCanvas::GetContext(JSContext* aCx,
107
                            const nsAString& aContextId,
108
                            JS::Handle<JS::Value> aContextOptions,
109
                            ErrorResult& aRv)
110
0
{
111
0
  if (mNeutered) {
112
0
    aRv.Throw(NS_ERROR_FAILURE);
113
0
    return nullptr;
114
0
  }
115
0
116
0
  // We only support WebGL in workers for now
117
0
  CanvasContextType contextType;
118
0
  if (!CanvasUtils::GetCanvasContextType(aContextId, &contextType)) {
119
0
    aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
120
0
    return nullptr;
121
0
  }
122
0
123
0
  if (!(contextType == CanvasContextType::WebGL1 ||
124
0
        contextType == CanvasContextType::WebGL2 ||
125
0
        contextType == CanvasContextType::ImageBitmap))
126
0
  {
127
0
    aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
128
0
    return nullptr;
129
0
  }
130
0
131
0
  RefPtr<nsISupports> result =
132
0
    CanvasRenderingContextHelper::GetContext(aCx,
133
0
                                             aContextId,
134
0
                                             aContextOptions,
135
0
                                             aRv);
136
0
137
0
  if (!mCurrentContext) {
138
0
    return nullptr;
139
0
  }
140
0
141
0
  if (mCanvasRenderer) {
142
0
    if (contextType == CanvasContextType::WebGL1 ||
143
0
        contextType == CanvasContextType::WebGL2) {
144
0
      WebGLContext* webGL = static_cast<WebGLContext*>(mCurrentContext.get());
145
0
      gl::GLContext* gl = webGL->GL();
146
0
      mCanvasRenderer->mContext = mCurrentContext;
147
0
      mCanvasRenderer->SetActiveEventTarget();
148
0
      mCanvasRenderer->mGLContext = gl;
149
0
      mCanvasRenderer->SetIsAlphaPremultiplied(webGL->IsPremultAlpha() || !gl->Caps().alpha);
150
0
151
0
      if (RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton()) {
152
0
        TextureFlags flags = TextureFlags::ORIGIN_BOTTOM_LEFT;
153
0
        mCanvasClient = imageBridge->CreateCanvasClient(CanvasClient::CanvasClientTypeShSurf, flags);
154
0
        mCanvasRenderer->SetCanvasClient(mCanvasClient);
155
0
156
0
        gl::GLScreenBuffer* screen = gl->Screen();
157
0
        gl::SurfaceCaps caps = screen->mCaps;
158
0
        auto forwarder = mCanvasClient->GetForwarder();
159
0
160
0
        UniquePtr<gl::SurfaceFactory> factory =
161
0
          gl::GLScreenBuffer::CreateFactory(gl, caps, forwarder, flags);
162
0
163
0
        if (factory)
164
0
          screen->Morph(std::move(factory));
165
0
      }
166
0
    }
167
0
  }
168
0
169
0
  return result.forget();
170
0
}
171
172
already_AddRefed<nsICanvasRenderingContextInternal>
173
OffscreenCanvas::CreateContext(CanvasContextType aContextType)
174
0
{
175
0
  RefPtr<nsICanvasRenderingContextInternal> ret =
176
0
    CanvasRenderingContextHelper::CreateContext(aContextType);
177
0
178
0
  ret->SetOffscreenCanvas(this);
179
0
  return ret.forget();
180
0
}
181
182
void
183
OffscreenCanvas::CommitFrameToCompositor()
184
0
{
185
0
  if (!mCanvasRenderer) {
186
0
    // This offscreen canvas doesn't associate to any HTML canvas element.
187
0
    // So, just bail out.
188
0
    return;
189
0
  }
190
0
191
0
  // The attributes has changed, we have to notify main
192
0
  // thread to change canvas size.
193
0
  if (mAttrDirty) {
194
0
    if (mCanvasRenderer) {
195
0
      mCanvasRenderer->SetWidth(mWidth);
196
0
      mCanvasRenderer->SetHeight(mHeight);
197
0
      mCanvasRenderer->NotifyElementAboutAttributesChanged();
198
0
    }
199
0
    mAttrDirty = false;
200
0
  }
201
0
202
0
  if (mCurrentContext) {
203
0
    static_cast<WebGLContext*>(mCurrentContext.get())->PresentScreenBuffer();
204
0
  }
205
0
206
0
  if (mCanvasRenderer && mCanvasRenderer->mGLContext) {
207
0
    mCanvasRenderer->NotifyElementAboutInvalidation();
208
0
    ImageBridgeChild::GetSingleton()->
209
0
      UpdateAsyncCanvasRenderer(mCanvasRenderer);
210
0
  }
211
0
}
212
213
OffscreenCanvasCloneData*
214
OffscreenCanvas::ToCloneData()
215
0
{
216
0
  return new OffscreenCanvasCloneData(mCanvasRenderer, mWidth, mHeight,
217
0
                                      mCompositorBackendType, mNeutered, mIsWriteOnly);
218
0
}
219
220
already_AddRefed<ImageBitmap>
221
OffscreenCanvas::TransferToImageBitmap(ErrorResult& aRv)
222
0
{
223
0
  nsCOMPtr<nsIGlobalObject> globalObject = GetGlobalObject();
224
0
  RefPtr<ImageBitmap> result = ImageBitmap::CreateFromOffscreenCanvas(globalObject, *this, aRv);
225
0
  if (aRv.Failed()) {
226
0
    return nullptr;
227
0
  }
228
0
229
0
  // TODO: Clear the content?
230
0
  return result.forget();
231
0
}
232
233
already_AddRefed<Promise>
234
OffscreenCanvas::ToBlob(JSContext* aCx,
235
                        const nsAString& aType,
236
                        JS::Handle<JS::Value> aParams,
237
                        ErrorResult& aRv)
238
0
{
239
0
  // do a trust check if this is a write-only canvas
240
0
  if (mIsWriteOnly) {
241
0
    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
242
0
    return nullptr;
243
0
  }
244
0
245
0
  nsCOMPtr<nsIGlobalObject> global = GetGlobalObject();
246
0
247
0
  RefPtr<Promise> promise = Promise::Create(global, aRv);
248
0
  if (aRv.Failed()) {
249
0
    return nullptr;
250
0
  }
251
0
252
0
  // Encoder callback when encoding is complete.
253
0
  class EncodeCallback : public EncodeCompleteCallback
254
0
  {
255
0
  public:
256
0
    EncodeCallback(nsIGlobalObject* aGlobal, Promise* aPromise)
257
0
      : mGlobal(aGlobal)
258
0
      , mPromise(aPromise) {}
259
0
260
0
    // This is called on main thread.
261
0
    nsresult ReceiveBlob(already_AddRefed<Blob> aBlob) override
262
0
    {
263
0
      RefPtr<Blob> blob = aBlob;
264
0
265
0
      if (mPromise) {
266
0
        RefPtr<Blob> newBlob = Blob::Create(mGlobal, blob->Impl());
267
0
        mPromise->MaybeResolve(newBlob);
268
0
      }
269
0
270
0
      mGlobal = nullptr;
271
0
      mPromise = nullptr;
272
0
273
0
      return NS_OK;
274
0
    }
275
0
276
0
    nsCOMPtr<nsIGlobalObject> mGlobal;
277
0
    RefPtr<Promise> mPromise;
278
0
  };
279
0
280
0
  RefPtr<EncodeCompleteCallback> callback =
281
0
    new EncodeCallback(global, promise);
282
0
283
0
  // TODO: Can we obtain the context and document here somehow
284
0
  // so that we can decide when usePlaceholder should be true/false?
285
0
  // See https://trac.torproject.org/18599
286
0
  // For now, we always return a placeholder if fingerprinting resistance is on.
287
0
  bool usePlaceholder = nsContentUtils::ShouldResistFingerprinting();
288
0
  CanvasRenderingContextHelper::ToBlob(aCx, global, callback, aType, aParams,
289
0
                                       usePlaceholder, aRv);
290
0
291
0
  return promise.forget();
292
0
}
293
294
already_AddRefed<gfx::SourceSurface>
295
OffscreenCanvas::GetSurfaceSnapshot(gfxAlphaType* const aOutAlphaType)
296
0
{
297
0
  if (!mCurrentContext) {
298
0
    return nullptr;
299
0
  }
300
0
301
0
  return mCurrentContext->GetSurfaceSnapshot(aOutAlphaType);
302
0
}
303
304
nsCOMPtr<nsIGlobalObject>
305
OffscreenCanvas::GetGlobalObject()
306
0
{
307
0
  if (NS_IsMainThread()) {
308
0
    return GetParentObject();
309
0
  }
310
0
311
0
  dom::WorkerPrivate* workerPrivate = dom::GetCurrentThreadWorkerPrivate();
312
0
  return workerPrivate->GlobalScope();
313
0
}
314
315
/* static */ already_AddRefed<OffscreenCanvas>
316
OffscreenCanvas::CreateFromCloneData(nsIGlobalObject* aGlobal, OffscreenCanvasCloneData* aData)
317
0
{
318
0
  MOZ_ASSERT(aData);
319
0
  RefPtr<OffscreenCanvas> wc =
320
0
    new OffscreenCanvas(aGlobal, aData->mWidth, aData->mHeight,
321
0
                        aData->mCompositorBackendType, aData->mRenderer);
322
0
  if (aData->mNeutered) {
323
0
    wc->SetNeutered();
324
0
  }
325
0
  return wc.forget();
326
0
}
327
328
/* static */ bool
329
OffscreenCanvas::PrefEnabledOnWorkerThread(JSContext* aCx, JSObject* aObj)
330
0
{
331
0
  if (NS_IsMainThread()) {
332
0
    return true;
333
0
  }
334
0
335
0
  return DOMPrefs::gfx_offscreencanvas_enabled(aCx, aObj);
336
0
}
337
338
NS_IMPL_CYCLE_COLLECTION_INHERITED(OffscreenCanvas, DOMEventTargetHelper, mCurrentContext)
339
340
NS_IMPL_ADDREF_INHERITED(OffscreenCanvas, DOMEventTargetHelper)
341
NS_IMPL_RELEASE_INHERITED(OffscreenCanvas, DOMEventTargetHelper)
342
343
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(OffscreenCanvas)
344
0
  NS_INTERFACE_MAP_ENTRY(nsISupports)
345
0
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
346
347
} // namespace dom
348
} // namespace mozilla