Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/canvas/ImageBitmap.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 "mozilla/dom/ImageBitmap.h"
8
#include "mozilla/CheckedInt.h"
9
#include "mozilla/dom/DOMPrefs.h"
10
#include "mozilla/dom/HTMLMediaElementBinding.h"
11
#include "mozilla/dom/ImageBitmapBinding.h"
12
#include "mozilla/dom/Promise.h"
13
#include "mozilla/dom/StructuredCloneTags.h"
14
#include "mozilla/dom/WorkerPrivate.h"
15
#include "mozilla/dom/WorkerRef.h"
16
#include "mozilla/dom/WorkerRunnable.h"
17
#include "mozilla/gfx/2D.h"
18
#include "mozilla/gfx/Swizzle.h"
19
#include "mozilla/Mutex.h"
20
#include "mozilla/ScopeExit.h"
21
#include "ImageBitmapColorUtils.h"
22
#include "ImageBitmapUtils.h"
23
#include "ImageUtils.h"
24
#include "imgTools.h"
25
26
using namespace mozilla::gfx;
27
using namespace mozilla::layers;
28
using mozilla::dom::HTMLMediaElement_Binding::NETWORK_EMPTY;
29
using mozilla::dom::HTMLMediaElement_Binding::HAVE_METADATA;
30
31
namespace mozilla {
32
namespace dom {
33
34
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ImageBitmap, mParent)
35
NS_IMPL_CYCLE_COLLECTING_ADDREF(ImageBitmap)
36
NS_IMPL_CYCLE_COLLECTING_RELEASE(ImageBitmap)
37
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ImageBitmap)
38
0
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
39
0
  NS_INTERFACE_MAP_ENTRY(nsISupports)
40
0
NS_INTERFACE_MAP_END
41
42
/* This class observes shutdown notifications and sends that notification
43
 * to the worker thread if the image bitmap is on a worker thread.
44
 */
45
class ImageBitmapShutdownObserver final : public nsIObserver
46
{
47
public:
48
  explicit ImageBitmapShutdownObserver(ImageBitmap* aImageBitmap)
49
  : mImageBitmap(nullptr)
50
0
  {
51
0
    if (NS_IsMainThread()) {
52
0
      mImageBitmap = aImageBitmap;
53
0
    } else {
54
0
      WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
55
0
      MOZ_ASSERT(workerPrivate);
56
0
      mMainThreadEventTarget = workerPrivate->MainThreadEventTarget();
57
0
      mSendToWorkerTask = new SendShutdownToWorkerThread(aImageBitmap);
58
0
    }
59
0
  }
60
61
0
  void RegisterObserver() {
62
0
    if (NS_IsMainThread()) {
63
0
      nsContentUtils::RegisterShutdownObserver(this);
64
0
      return;
65
0
    }
66
0
67
0
    MOZ_ASSERT(mMainThreadEventTarget);
68
0
    RefPtr<ImageBitmapShutdownObserver> self = this;
69
0
    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
70
0
      "ImageBitmapShutdownObserver::RegisterObserver",
71
0
      [self]() {
72
0
        self->RegisterObserver();
73
0
      });
74
0
75
0
    mMainThreadEventTarget->Dispatch(r.forget());
76
0
  }
77
78
0
  void UnregisterObserver() {
79
0
    if (NS_IsMainThread()) {
80
0
      nsContentUtils::UnregisterShutdownObserver(this);
81
0
      return;
82
0
    }
83
0
84
0
    MOZ_ASSERT(mMainThreadEventTarget);
85
0
    RefPtr<ImageBitmapShutdownObserver> self = this;
86
0
    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
87
0
      "ImageBitmapShutdownObserver::RegisterObserver",
88
0
      [self]() {
89
0
        self->UnregisterObserver();
90
0
      });
91
0
92
0
    mMainThreadEventTarget->Dispatch(r.forget());
93
0
  }
94
95
0
  void Clear() {
96
0
    mImageBitmap = nullptr;
97
0
    if (mSendToWorkerTask) {
98
0
      mSendToWorkerTask->mImageBitmap = nullptr;
99
0
    }
100
0
  }
101
102
  NS_DECL_THREADSAFE_ISUPPORTS
103
  NS_DECL_NSIOBSERVER
104
private:
105
0
  ~ImageBitmapShutdownObserver() {}
106
107
  class SendShutdownToWorkerThread : public MainThreadWorkerControlRunnable
108
  {
109
  public:
110
    explicit SendShutdownToWorkerThread(ImageBitmap* aImageBitmap)
111
    : MainThreadWorkerControlRunnable(GetCurrentThreadWorkerPrivate())
112
    , mImageBitmap(aImageBitmap)
113
0
    {}
114
115
    bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
116
0
    {
117
0
      if (mImageBitmap) {
118
0
        mImageBitmap->OnShutdown();
119
0
        mImageBitmap = nullptr;
120
0
      }
121
0
      return true;
122
0
    }
123
124
    ImageBitmap* mImageBitmap;
125
  };
126
127
  ImageBitmap* mImageBitmap;
128
  nsCOMPtr<nsIEventTarget> mMainThreadEventTarget;
129
  RefPtr<SendShutdownToWorkerThread> mSendToWorkerTask;
130
};
131
132
NS_IMPL_ISUPPORTS(ImageBitmapShutdownObserver, nsIObserver)
133
134
NS_IMETHODIMP
135
ImageBitmapShutdownObserver::Observe(nsISupports* aSubject,
136
                                     const char* aTopic,
137
                                     const char16_t* aData)
138
0
{
139
0
  if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
140
0
    if (mSendToWorkerTask) {
141
0
      mSendToWorkerTask->Dispatch();
142
0
    } else {
143
0
      if (mImageBitmap) {
144
0
        mImageBitmap->OnShutdown();
145
0
        mImageBitmap = nullptr;
146
0
      }
147
0
    }
148
0
    nsContentUtils::UnregisterShutdownObserver(this);
149
0
  }
150
0
151
0
  return NS_OK;
152
0
}
153
154
155
/*
156
 * If either aRect.width or aRect.height are negative, then return a new IntRect
157
 * which represents the same rectangle as the aRect does but with positive width
158
 * and height.
159
 */
160
static IntRect
161
FixUpNegativeDimension(const IntRect& aRect, ErrorResult& aRv)
162
0
{
163
0
  gfx::IntRect rect = aRect;
164
0
165
0
  // fix up negative dimensions
166
0
  if (rect.width < 0) {
167
0
    CheckedInt32 checkedX = CheckedInt32(rect.x) + rect.width;
168
0
169
0
    if (!checkedX.isValid()) {
170
0
      aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
171
0
      return rect;
172
0
    }
173
0
174
0
    rect.x = checkedX.value();
175
0
    rect.width = -(rect.width);
176
0
  }
177
0
178
0
  if (rect.height < 0) {
179
0
    CheckedInt32 checkedY = CheckedInt32(rect.y) + rect.height;
180
0
181
0
    if (!checkedY.isValid()) {
182
0
      aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
183
0
      return rect;
184
0
    }
185
0
186
0
    rect.y = checkedY.value();
187
0
    rect.height = -(rect.height);
188
0
  }
189
0
190
0
  return rect;
191
0
}
192
193
/*
194
 * This helper function copies the data of the given DataSourceSurface,
195
 *  _aSurface_, in the given area, _aCropRect_, into a new DataSourceSurface.
196
 * This might return null if it can not create a new SourceSurface or it cannot
197
 * read data from the given _aSurface_.
198
 *
199
 * Warning: Even though the area of _aCropRect_ is just the same as the size of
200
 *          _aSurface_, this function still copy data into a new
201
 *          DataSourceSurface.
202
 */
203
static already_AddRefed<DataSourceSurface>
204
CropAndCopyDataSourceSurface(DataSourceSurface* aSurface, const IntRect& aCropRect)
205
0
{
206
0
  MOZ_ASSERT(aSurface);
207
0
208
0
  // Check the aCropRect
209
0
  ErrorResult error;
210
0
  const IntRect positiveCropRect = FixUpNegativeDimension(aCropRect, error);
211
0
  if (NS_WARN_IF(error.Failed())) {
212
0
    error.SuppressException();
213
0
    return nullptr;
214
0
  }
215
0
216
0
  // Calculate the size of the new SourceSurface.
217
0
  // We cannot keep using aSurface->GetFormat() to create new DataSourceSurface,
218
0
  // since it might be SurfaceFormat::B8G8R8X8 which does not handle opacity,
219
0
  // however the specification explicitly define that "If any of the pixels on
220
0
  // this rectangle are outside the area where the input bitmap was placed, then
221
0
  // they will be transparent black in output."
222
0
  // So, instead, we force the output format to be SurfaceFormat::B8G8R8A8.
223
0
  const SurfaceFormat format = SurfaceFormat::B8G8R8A8;
224
0
  const int bytesPerPixel = BytesPerPixel(format);
225
0
  const IntSize dstSize = IntSize(positiveCropRect.width,
226
0
                                  positiveCropRect.height);
227
0
  const uint32_t dstStride = dstSize.width * bytesPerPixel;
228
0
229
0
  // Create a new SourceSurface.
230
0
  RefPtr<DataSourceSurface> dstDataSurface =
231
0
    Factory::CreateDataSourceSurfaceWithStride(dstSize, format, dstStride, true);
232
0
233
0
  if (NS_WARN_IF(!dstDataSurface)) {
234
0
    return nullptr;
235
0
  }
236
0
237
0
  // Only do copying and cropping when the positiveCropRect intersects with
238
0
  // the size of aSurface.
239
0
  const IntRect surfRect(IntPoint(0, 0), aSurface->GetSize());
240
0
  if (surfRect.Intersects(positiveCropRect)) {
241
0
    const IntRect surfPortion = surfRect.Intersect(positiveCropRect);
242
0
    const IntPoint dest(std::max(0, surfPortion.X() - positiveCropRect.X()),
243
0
                        std::max(0, surfPortion.Y() - positiveCropRect.Y()));
244
0
245
0
    // Copy the raw data into the newly created DataSourceSurface.
246
0
    DataSourceSurface::ScopedMap srcMap(aSurface, DataSourceSurface::READ);
247
0
    DataSourceSurface::ScopedMap dstMap(dstDataSurface, DataSourceSurface::WRITE);
248
0
    if (NS_WARN_IF(!srcMap.IsMapped()) ||
249
0
        NS_WARN_IF(!dstMap.IsMapped())) {
250
0
      return nullptr;
251
0
    }
252
0
253
0
    uint8_t* srcBufferPtr = srcMap.GetData() + surfPortion.y * srcMap.GetStride()
254
0
                                             + surfPortion.x * bytesPerPixel;
255
0
    uint8_t* dstBufferPtr = dstMap.GetData() + dest.y * dstMap.GetStride()
256
0
                                             + dest.x * bytesPerPixel;
257
0
    CheckedInt<uint32_t> copiedBytesPerRaw =
258
0
      CheckedInt<uint32_t>(surfPortion.width) * bytesPerPixel;
259
0
    if (!copiedBytesPerRaw.isValid()) {
260
0
      return nullptr;
261
0
    }
262
0
263
0
    for (int i = 0; i < surfPortion.height; ++i) {
264
0
      memcpy(dstBufferPtr, srcBufferPtr, copiedBytesPerRaw.value());
265
0
      srcBufferPtr += srcMap.GetStride();
266
0
      dstBufferPtr += dstMap.GetStride();
267
0
    }
268
0
  }
269
0
270
0
  return dstDataSurface.forget();
271
0
}
272
273
/*
274
 * Encapsulate the given _aSurface_ into a layers::SourceSurfaceImage.
275
 */
276
static already_AddRefed<layers::Image>
277
CreateImageFromSurface(SourceSurface* aSurface)
278
0
{
279
0
  MOZ_ASSERT(aSurface);
280
0
  RefPtr<layers::SourceSurfaceImage> image =
281
0
    new layers::SourceSurfaceImage(aSurface->GetSize(), aSurface);
282
0
  return image.forget();
283
0
}
284
285
/*
286
 * CreateImageFromRawData(), CreateSurfaceFromRawData() and
287
 * CreateImageFromRawDataInMainThreadSyncTask are helpers for
288
 * create-from-ImageData case
289
 */
290
static already_AddRefed<SourceSurface>
291
CreateSurfaceFromRawData(const gfx::IntSize& aSize,
292
                         uint32_t aStride,
293
                         gfx::SurfaceFormat aFormat,
294
                         uint8_t* aBuffer,
295
                         uint32_t aBufferLength,
296
                         const Maybe<IntRect>& aCropRect)
297
0
{
298
0
  MOZ_ASSERT(!aSize.IsEmpty());
299
0
  MOZ_ASSERT(aBuffer);
300
0
301
0
  // Wrap the source buffer into a SourceSurface.
302
0
  RefPtr<DataSourceSurface> dataSurface =
303
0
    Factory::CreateWrappingDataSourceSurface(aBuffer, aStride, aSize, aFormat);
304
0
305
0
  if (NS_WARN_IF(!dataSurface)) {
306
0
    return nullptr;
307
0
  }
308
0
309
0
  // The temporary cropRect variable is equal to the size of source buffer if we
310
0
  // do not need to crop, or it equals to the given cropping size.
311
0
  const IntRect cropRect = aCropRect.valueOr(IntRect(0, 0, aSize.width, aSize.height));
312
0
313
0
  // Copy the source buffer in the _cropRect_ area into a new SourceSurface.
314
0
  RefPtr<DataSourceSurface> result = CropAndCopyDataSourceSurface(dataSurface, cropRect);
315
0
316
0
  if (NS_WARN_IF(!result)) {
317
0
    return nullptr;
318
0
  }
319
0
320
0
  return result.forget();
321
0
}
322
323
static already_AddRefed<layers::Image>
324
CreateImageFromRawData(const gfx::IntSize& aSize,
325
                       uint32_t aStride,
326
                       gfx::SurfaceFormat aFormat,
327
                       uint8_t* aBuffer,
328
                       uint32_t aBufferLength,
329
                       const Maybe<IntRect>& aCropRect)
330
0
{
331
0
  MOZ_ASSERT(NS_IsMainThread());
332
0
333
0
  // Copy and crop the source buffer into a SourceSurface.
334
0
  RefPtr<SourceSurface> rgbaSurface =
335
0
    CreateSurfaceFromRawData(aSize, aStride, aFormat,
336
0
                             aBuffer, aBufferLength,
337
0
                             aCropRect);
338
0
339
0
  if (NS_WARN_IF(!rgbaSurface)) {
340
0
    return nullptr;
341
0
  }
342
0
343
0
  // Convert RGBA to BGRA
344
0
  RefPtr<DataSourceSurface> rgbaDataSurface = rgbaSurface->GetDataSurface();
345
0
  DataSourceSurface::ScopedMap rgbaMap(rgbaDataSurface, DataSourceSurface::READ);
346
0
  if (NS_WARN_IF(!rgbaMap.IsMapped())) {
347
0
    return nullptr;
348
0
  }
349
0
350
0
  RefPtr<DataSourceSurface> bgraDataSurface =
351
0
    Factory::CreateDataSourceSurfaceWithStride(rgbaDataSurface->GetSize(),
352
0
                                               SurfaceFormat::B8G8R8A8,
353
0
                                               rgbaMap.GetStride());
354
0
  if (NS_WARN_IF(!bgraDataSurface)) {
355
0
    return nullptr;
356
0
  }
357
0
358
0
  DataSourceSurface::ScopedMap bgraMap(bgraDataSurface, DataSourceSurface::WRITE);
359
0
  if (NS_WARN_IF(!bgraMap.IsMapped())) {
360
0
    return nullptr;
361
0
  }
362
0
363
0
  SwizzleData(rgbaMap.GetData(), rgbaMap.GetStride(), SurfaceFormat::R8G8B8A8,
364
0
              bgraMap.GetData(), bgraMap.GetStride(), SurfaceFormat::B8G8R8A8,
365
0
              bgraDataSurface->GetSize());
366
0
367
0
  // Create an Image from the BGRA SourceSurface.
368
0
  RefPtr<layers::Image> image = CreateImageFromSurface(bgraDataSurface);
369
0
370
0
  if (NS_WARN_IF(!image)) {
371
0
    return nullptr;
372
0
  }
373
0
374
0
  return image.forget();
375
0
}
376
377
/*
378
 * This is a synchronous task.
379
 * This class is used to create a layers::SourceSurfaceImage from raw data in the main
380
 * thread. While creating an ImageBitmap from an ImageData, we need to create
381
 * a SouceSurface from the ImageData's raw data and then set the SourceSurface
382
 * into a layers::SourceSurfaceImage. However, the layers::SourceSurfaceImage asserts the
383
 * setting operation in the main thread, so if we are going to create an
384
 * ImageBitmap from an ImageData off the main thread, we post an event to the
385
 * main thread to create a layers::SourceSurfaceImage from an ImageData's raw data.
386
 */
387
class CreateImageFromRawDataInMainThreadSyncTask final :
388
  public WorkerMainThreadRunnable
389
{
390
public:
391
  CreateImageFromRawDataInMainThreadSyncTask(uint8_t* aBuffer,
392
                                             uint32_t aBufferLength,
393
                                             uint32_t aStride,
394
                                             gfx::SurfaceFormat aFormat,
395
                                             const gfx::IntSize& aSize,
396
                                             const Maybe<IntRect>& aCropRect,
397
                                             layers::Image** aImage)
398
  : WorkerMainThreadRunnable(GetCurrentThreadWorkerPrivate(),
399
                               NS_LITERAL_CSTRING("ImageBitmap :: Create Image from Raw Data"))
400
  , mImage(aImage)
401
  , mBuffer(aBuffer)
402
  , mBufferLength(aBufferLength)
403
  , mStride(aStride)
404
  , mFormat(aFormat)
405
  , mSize(aSize)
406
  , mCropRect(aCropRect)
407
0
  {
408
0
    MOZ_ASSERT(!(*aImage), "Don't pass an existing Image into CreateImageFromRawDataInMainThreadSyncTask.");
409
0
  }
410
411
  bool MainThreadRun() override
412
0
  {
413
0
    RefPtr<layers::Image> image =
414
0
      CreateImageFromRawData(mSize, mStride, mFormat,
415
0
                             mBuffer, mBufferLength,
416
0
                             mCropRect);
417
0
418
0
    if (NS_WARN_IF(!image)) {
419
0
      return false;
420
0
    }
421
0
422
0
    image.forget(mImage);
423
0
424
0
    return true;
425
0
  }
426
427
private:
428
  layers::Image** mImage;
429
  uint8_t* mBuffer;
430
  uint32_t mBufferLength;
431
  uint32_t mStride;
432
  gfx::SurfaceFormat mFormat;
433
  gfx::IntSize mSize;
434
  const Maybe<IntRect>& mCropRect;
435
};
436
437
static bool
438
CheckSecurityForHTMLElements(bool aIsWriteOnly, bool aCORSUsed, nsIPrincipal* aPrincipal)
439
0
{
440
0
  if (aIsWriteOnly || !aPrincipal) {
441
0
    return false;
442
0
  }
443
0
444
0
  if (!aCORSUsed) {
445
0
    nsIGlobalObject* incumbentSettingsObject = GetIncumbentGlobal();
446
0
    if (NS_WARN_IF(!incumbentSettingsObject)) {
447
0
      return false;
448
0
    }
449
0
450
0
    nsIPrincipal* principal = incumbentSettingsObject->PrincipalOrNull();
451
0
    if (NS_WARN_IF(!principal) || !(principal->Subsumes(aPrincipal))) {
452
0
      return false;
453
0
    }
454
0
  }
455
0
456
0
  return true;
457
0
}
458
459
static bool
460
CheckSecurityForHTMLElements(const nsLayoutUtils::SurfaceFromElementResult& aRes)
461
0
{
462
0
  return CheckSecurityForHTMLElements(aRes.mIsWriteOnly, aRes.mCORSUsed, aRes.mPrincipal);
463
0
}
464
465
/*
466
 * A wrapper to the nsLayoutUtils::SurfaceFromElement() function followed by the
467
 * security checking.
468
 */
469
template<class HTMLElementType>
470
static already_AddRefed<SourceSurface>
471
GetSurfaceFromElement(nsIGlobalObject* aGlobal, HTMLElementType& aElement, ErrorResult& aRv)
472
0
{
473
0
  nsLayoutUtils::SurfaceFromElementResult res =
474
0
    nsLayoutUtils::SurfaceFromElement(&aElement, nsLayoutUtils::SFE_WANT_FIRST_FRAME_IF_IMAGE);
475
0
476
0
  // check origin-clean
477
0
  if (!CheckSecurityForHTMLElements(res)) {
478
0
    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
479
0
    return nullptr;
480
0
  }
481
0
482
0
  RefPtr<SourceSurface> surface = res.GetSourceSurface();
483
0
484
0
  if (NS_WARN_IF(!surface)) {
485
0
    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
486
0
    return nullptr;
487
0
  }
488
0
489
0
  return surface.forget();
490
0
}
Unexecuted instantiation: Unified_cpp_dom_canvas0.cpp:already_AddRefed<mozilla::gfx::SourceSurface> mozilla::dom::GetSurfaceFromElement<mozilla::dom::HTMLImageElement>(nsIGlobalObject*, mozilla::dom::HTMLImageElement&, mozilla::ErrorResult&)
Unexecuted instantiation: Unified_cpp_dom_canvas0.cpp:already_AddRefed<mozilla::gfx::SourceSurface> mozilla::dom::GetSurfaceFromElement<mozilla::dom::HTMLCanvasElement>(nsIGlobalObject*, mozilla::dom::HTMLCanvasElement&, mozilla::ErrorResult&)
491
492
/*
493
 * The specification doesn't allow to create an ImegeBitmap from a vector image.
494
 * This function is used to check if the given HTMLImageElement contains a
495
 * raster image.
496
 */
497
static bool
498
HasRasterImage(HTMLImageElement& aImageEl)
499
0
{
500
0
  nsresult rv;
501
0
502
0
  nsCOMPtr<imgIRequest> imgRequest;
503
0
  rv = aImageEl.GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
504
0
                           getter_AddRefs(imgRequest));
505
0
  if (NS_SUCCEEDED(rv) && imgRequest) {
506
0
    nsCOMPtr<imgIContainer> imgContainer;
507
0
    rv = imgRequest->GetImage(getter_AddRefs(imgContainer));
508
0
    if (NS_SUCCEEDED(rv) && imgContainer &&
509
0
        imgContainer->GetType() == imgIContainer::TYPE_RASTER) {
510
0
      return true;
511
0
    }
512
0
  }
513
0
514
0
  return false;
515
0
}
516
517
ImageBitmap::ImageBitmap(nsIGlobalObject* aGlobal, layers::Image* aData,
518
                         gfxAlphaType aAlphaType)
519
  : mParent(aGlobal)
520
  , mData(aData)
521
  , mSurface(nullptr)
522
  , mDataWrapper(new ImageUtils(mData))
523
  , mPictureRect(0, 0, aData->GetSize().width, aData->GetSize().height)
524
  , mAlphaType(aAlphaType)
525
  , mIsCroppingAreaOutSideOfSourceImage(false)
526
  , mAllocatedImageData(false)
527
0
{
528
0
  MOZ_ASSERT(aData, "aData is null in ImageBitmap constructor.");
529
0
530
0
  mShutdownObserver = new ImageBitmapShutdownObserver(this);
531
0
  mShutdownObserver->RegisterObserver();
532
0
}
533
534
ImageBitmap::~ImageBitmap()
535
0
{
536
0
  if (mShutdownObserver) {
537
0
    mShutdownObserver->Clear();
538
0
    mShutdownObserver->UnregisterObserver();
539
0
    mShutdownObserver = nullptr;
540
0
  }
541
0
}
542
543
JSObject*
544
ImageBitmap::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
545
0
{
546
0
  return ImageBitmap_Binding::Wrap(aCx, this, aGivenProto);
547
0
}
548
549
void
550
ImageBitmap::Close()
551
0
{
552
0
  mData = nullptr;
553
0
  mSurface = nullptr;
554
0
  mDataWrapper = nullptr;
555
0
  mPictureRect.SetEmpty();
556
0
}
557
558
void
559
ImageBitmap::OnShutdown()
560
0
{
561
0
  mShutdownObserver = nullptr;
562
0
563
0
  Close();
564
0
}
565
566
void
567
ImageBitmap::SetPictureRect(const IntRect& aRect, ErrorResult& aRv)
568
0
{
569
0
  mPictureRect = FixUpNegativeDimension(aRect, aRv);
570
0
}
571
572
void
573
ImageBitmap::SetIsCroppingAreaOutSideOfSourceImage(const IntSize& aSourceSize,
574
                                                   const Maybe<IntRect>& aCroppingRect)
575
0
{
576
0
  // No cropping at all.
577
0
  if (aCroppingRect.isNothing()) {
578
0
    mIsCroppingAreaOutSideOfSourceImage = false;
579
0
    return;
580
0
  }
581
0
582
0
  if (aCroppingRect->X() < 0 || aCroppingRect->Y() < 0 ||
583
0
      aCroppingRect->Width() > aSourceSize.width ||
584
0
      aCroppingRect->Height() > aSourceSize.height) {
585
0
    mIsCroppingAreaOutSideOfSourceImage = true;
586
0
  }
587
0
}
588
589
static already_AddRefed<SourceSurface>
590
ConvertColorFormatIfNeeded(RefPtr<SourceSurface> aSurface)
591
0
{
592
0
  const SurfaceFormat srcFormat = aSurface->GetFormat();
593
0
  if (srcFormat == SurfaceFormat::R8G8B8A8 ||
594
0
      srcFormat == SurfaceFormat::B8G8R8A8 ||
595
0
      srcFormat == SurfaceFormat::R8G8B8X8 ||
596
0
      srcFormat == SurfaceFormat::B8G8R8X8 ||
597
0
      srcFormat == SurfaceFormat::A8R8G8B8 ||
598
0
      srcFormat == SurfaceFormat::X8R8G8B8) {
599
0
    return aSurface.forget();
600
0
  }
601
0
602
0
  if (srcFormat == SurfaceFormat::A8 ||
603
0
      srcFormat == SurfaceFormat::Depth) {
604
0
    return nullptr;
605
0
  }
606
0
607
0
  const int bytesPerPixel = BytesPerPixel(SurfaceFormat::B8G8R8A8);
608
0
  const IntSize dstSize = aSurface->GetSize();
609
0
  const uint32_t dstStride = dstSize.width * bytesPerPixel;
610
0
611
0
  RefPtr<DataSourceSurface> dstDataSurface =
612
0
    Factory::CreateDataSourceSurfaceWithStride(dstSize,
613
0
                                               SurfaceFormat::B8G8R8A8,
614
0
                                               dstStride);
615
0
  if (NS_WARN_IF(!dstDataSurface)) {
616
0
    return nullptr;
617
0
  }
618
0
619
0
  RefPtr<DataSourceSurface> srcDataSurface = aSurface->GetDataSurface();
620
0
  if (NS_WARN_IF(!srcDataSurface)) {
621
0
    return nullptr;
622
0
  }
623
0
624
0
  DataSourceSurface::ScopedMap srcMap(srcDataSurface, DataSourceSurface::READ);
625
0
  DataSourceSurface::ScopedMap dstMap(dstDataSurface, DataSourceSurface::WRITE);
626
0
  if (NS_WARN_IF(!srcMap.IsMapped()) || NS_WARN_IF(!dstMap.IsMapped())) {
627
0
    return nullptr;
628
0
  }
629
0
630
0
  int rv = 0;
631
0
  if (srcFormat == SurfaceFormat::R8G8B8) {
632
0
    rv = RGB24ToBGRA32(srcMap.GetData(), srcMap.GetStride(),
633
0
                       dstMap.GetData(), dstMap.GetStride(),
634
0
                       dstSize.width, dstSize.height);
635
0
  } else if (srcFormat == SurfaceFormat::B8G8R8) {
636
0
    rv = BGR24ToBGRA32(srcMap.GetData(), srcMap.GetStride(),
637
0
                       dstMap.GetData(), dstMap.GetStride(),
638
0
                       dstSize.width, dstSize.height);
639
0
  } else if (srcFormat == SurfaceFormat::HSV) {
640
0
    rv = HSVToBGRA32((const float*)srcMap.GetData(), srcMap.GetStride(),
641
0
                     dstMap.GetData(), dstMap.GetStride(),
642
0
                     dstSize.width, dstSize.height);
643
0
  } else if (srcFormat == SurfaceFormat::Lab) {
644
0
    rv = LabToBGRA32((const float*)srcMap.GetData(), srcMap.GetStride(),
645
0
                     dstMap.GetData(), dstMap.GetStride(),
646
0
                     dstSize.width, dstSize.height);
647
0
  }
648
0
649
0
  if (NS_WARN_IF(rv != 0)) {
650
0
    return nullptr;
651
0
  }
652
0
653
0
  return dstDataSurface.forget();
654
0
}
655
656
/*
657
 * The functionality of PrepareForDrawTarget method:
658
 * (1) Get a SourceSurface from the mData (which is a layers::Image).
659
 * (2) Convert the SourceSurface to format B8G8R8A8 if the original format is
660
 *     R8G8B8, B8G8R8, HSV or Lab.
661
 *     Note: if the original format is A8 or Depth, then return null directly.
662
 * (3) Do cropping if the size of SourceSurface does not equal to the
663
 *     mPictureRect.
664
 * (4) Pre-multiply alpha if needed.
665
 */
666
already_AddRefed<SourceSurface>
667
ImageBitmap::PrepareForDrawTarget(gfx::DrawTarget* aTarget)
668
0
{
669
0
  MOZ_ASSERT(aTarget);
670
0
671
0
  if (!mData) {
672
0
    return nullptr;
673
0
  }
674
0
675
0
  if (!mSurface) {
676
0
    mSurface = mData->GetAsSourceSurface();
677
0
678
0
    if (!mSurface) {
679
0
      return nullptr;
680
0
    }
681
0
  }
682
0
683
0
  // Check if we need to convert the format.
684
0
  // Convert R8G8B8/B8G8R8/HSV/Lab to B8G8R8A8.
685
0
  // Return null if the original format is A8 or Depth.
686
0
  mSurface = ConvertColorFormatIfNeeded(mSurface);
687
0
  if (NS_WARN_IF(!mSurface)) {
688
0
    return nullptr;
689
0
  }
690
0
691
0
  RefPtr<DrawTarget> target = aTarget;
692
0
  IntRect surfRect(0, 0, mSurface->GetSize().width, mSurface->GetSize().height);
693
0
694
0
  // Check if we still need to crop our surface
695
0
  if (!mPictureRect.IsEqualEdges(surfRect)) {
696
0
697
0
    IntRect surfPortion = surfRect.Intersect(mPictureRect);
698
0
699
0
    // the crop lies entirely outside the surface area, nothing to draw
700
0
    if (surfPortion.IsEmpty()) {
701
0
      mSurface = nullptr;
702
0
      RefPtr<gfx::SourceSurface> surface(mSurface);
703
0
      return surface.forget();
704
0
    }
705
0
706
0
    IntPoint dest(std::max(0, surfPortion.X() - mPictureRect.X()),
707
0
                  std::max(0, surfPortion.Y() - mPictureRect.Y()));
708
0
709
0
    // We must initialize this target with mPictureRect.Size() because the
710
0
    // specification states that if the cropping area is given, then return an
711
0
    // ImageBitmap with the size equals to the cropping area.
712
0
    target = target->CreateSimilarDrawTarget(mPictureRect.Size(),
713
0
                                             target->GetFormat());
714
0
715
0
    if (!target) {
716
0
      mSurface = nullptr;
717
0
      RefPtr<gfx::SourceSurface> surface(mSurface);
718
0
      return surface.forget();
719
0
    }
720
0
721
0
    target->CopySurface(mSurface, surfPortion, dest);
722
0
    mSurface = target->Snapshot();
723
0
724
0
    // Make mCropRect match new surface we've cropped to
725
0
    mPictureRect.MoveTo(0, 0);
726
0
  }
727
0
728
0
  // Pre-multiply alpha here.
729
0
  // Ignore this step if the source surface does not have alpha channel; this
730
0
  // kind of source surfaces might come form layers::PlanarYCbCrImage.
731
0
  if (mAlphaType == gfxAlphaType::NonPremult &&
732
0
      !IsOpaque(mSurface->GetFormat()))
733
0
  {
734
0
    MOZ_ASSERT(mSurface->GetFormat() == SurfaceFormat::R8G8B8A8 ||
735
0
               mSurface->GetFormat() == SurfaceFormat::B8G8R8A8 ||
736
0
               mSurface->GetFormat() == SurfaceFormat::A8R8G8B8);
737
0
738
0
    RefPtr<DataSourceSurface> dstSurface = mSurface->GetDataSurface();
739
0
    MOZ_ASSERT(dstSurface);
740
0
741
0
    RefPtr<DataSourceSurface> srcSurface;
742
0
    DataSourceSurface::MappedSurface srcMap;
743
0
    DataSourceSurface::MappedSurface dstMap;
744
0
745
0
    if (dstSurface->Map(DataSourceSurface::MapType::READ_WRITE, &dstMap)) {
746
0
      srcMap = dstMap;
747
0
    } else {
748
0
      srcSurface = dstSurface;
749
0
      if (!srcSurface->Map(DataSourceSurface::READ, &srcMap)) {
750
0
        gfxCriticalError() << "Failed to map source surface for premultiplying alpha.";
751
0
        return nullptr;
752
0
      }
753
0
754
0
      dstSurface = Factory::CreateDataSourceSurface(srcSurface->GetSize(), srcSurface->GetFormat());
755
0
756
0
      if (!dstSurface || !dstSurface->Map(DataSourceSurface::MapType::WRITE, &dstMap)) {
757
0
        gfxCriticalError() << "Failed to map destination surface for premultiplying alpha.";
758
0
        srcSurface->Unmap();
759
0
        return nullptr;
760
0
      }
761
0
    }
762
0
763
0
    PremultiplyData(srcMap.mData, srcMap.mStride, mSurface->GetFormat(),
764
0
                    dstMap.mData, dstMap.mStride, mSurface->GetFormat(),
765
0
                    dstSurface->GetSize());
766
0
767
0
    dstSurface->Unmap();
768
0
    if (srcSurface) {
769
0
      srcSurface->Unmap();
770
0
    }
771
0
772
0
    mSurface = dstSurface;
773
0
  }
774
0
775
0
  // Replace our surface with one optimized for the target we're about to draw
776
0
  // to, under the assumption it'll likely be drawn again to that target.
777
0
  // This call should be a no-op for already-optimized surfaces
778
0
  mSurface = target->OptimizeSourceSurface(mSurface);
779
0
780
0
  RefPtr<gfx::SourceSurface> surface(mSurface);
781
0
  return surface.forget();
782
0
}
783
784
already_AddRefed<layers::Image>
785
ImageBitmap::TransferAsImage()
786
0
{
787
0
  RefPtr<layers::Image> image = mData;
788
0
  Close();
789
0
  return image.forget();
790
0
}
791
792
UniquePtr<ImageBitmapCloneData>
793
ImageBitmap::ToCloneData() const
794
0
{
795
0
  if (!mData) {
796
0
    // A closed image cannot be cloned.
797
0
    return nullptr;
798
0
  }
799
0
800
0
  UniquePtr<ImageBitmapCloneData> result(new ImageBitmapCloneData());
801
0
  result->mPictureRect = mPictureRect;
802
0
  result->mAlphaType = mAlphaType;
803
0
  result->mIsCroppingAreaOutSideOfSourceImage = mIsCroppingAreaOutSideOfSourceImage;
804
0
  RefPtr<SourceSurface> surface = mData->GetAsSourceSurface();
805
0
  result->mSurface = surface->GetDataSurface();
806
0
  MOZ_ASSERT(result->mSurface);
807
0
808
0
  return result;
809
0
}
810
811
/* static */ already_AddRefed<ImageBitmap>
812
ImageBitmap::CreateFromCloneData(nsIGlobalObject* aGlobal,
813
                                 ImageBitmapCloneData* aData)
814
0
{
815
0
  RefPtr<layers::Image> data = CreateImageFromSurface(aData->mSurface);
816
0
817
0
  RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, aData->mAlphaType);
818
0
819
0
  ret->mAllocatedImageData = true;
820
0
821
0
  ret->mIsCroppingAreaOutSideOfSourceImage =
822
0
    aData->mIsCroppingAreaOutSideOfSourceImage;
823
0
824
0
  ErrorResult rv;
825
0
  ret->SetPictureRect(aData->mPictureRect, rv);
826
0
  return ret.forget();
827
0
}
828
829
/* static */ already_AddRefed<ImageBitmap>
830
ImageBitmap::CreateFromOffscreenCanvas(nsIGlobalObject* aGlobal,
831
                                       OffscreenCanvas& aOffscreenCanvas,
832
                                       ErrorResult& aRv)
833
0
{
834
0
  // Check origin-clean.
835
0
  if (aOffscreenCanvas.IsWriteOnly()) {
836
0
    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
837
0
    return nullptr;
838
0
  }
839
0
840
0
  nsLayoutUtils::SurfaceFromElementResult res =
841
0
    nsLayoutUtils::SurfaceFromOffscreenCanvas(&aOffscreenCanvas,
842
0
                                              nsLayoutUtils::SFE_WANT_FIRST_FRAME_IF_IMAGE);
843
0
844
0
  RefPtr<SourceSurface> surface = res.GetSourceSurface();
845
0
846
0
  if (NS_WARN_IF(!surface)) {
847
0
    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
848
0
    return nullptr;
849
0
  }
850
0
851
0
  RefPtr<layers::Image> data =
852
0
    CreateImageFromSurface(surface);
853
0
854
0
  RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data);
855
0
856
0
  ret->mAllocatedImageData = true;
857
0
858
0
  return ret.forget();
859
0
}
860
861
/* static */ already_AddRefed<ImageBitmap>
862
ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, HTMLImageElement& aImageEl,
863
                            const Maybe<IntRect>& aCropRect, ErrorResult& aRv)
864
0
{
865
0
  // Check if the image element is completely available or not.
866
0
  if (!aImageEl.Complete()) {
867
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
868
0
    return nullptr;
869
0
  }
870
0
871
0
  // Check if the image element is a bitmap (e.g. it's a vector graphic) or not.
872
0
  if (!HasRasterImage(aImageEl)) {
873
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
874
0
    return nullptr;
875
0
  }
876
0
877
0
  // Get the SourceSurface out from the image element and then do security
878
0
  // checking.
879
0
  RefPtr<SourceSurface> surface = GetSurfaceFromElement(aGlobal, aImageEl, aRv);
880
0
881
0
  if (NS_WARN_IF(aRv.Failed())) {
882
0
    return nullptr;
883
0
  }
884
0
885
0
  // Create ImageBitmap.
886
0
  RefPtr<layers::Image> data = CreateImageFromSurface(surface);
887
0
888
0
  if (NS_WARN_IF(!data)) {
889
0
    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
890
0
    return nullptr;
891
0
  }
892
0
893
0
  RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data);
894
0
895
0
  // Set the picture rectangle.
896
0
  if (ret && aCropRect.isSome()) {
897
0
    ret->SetPictureRect(aCropRect.ref(), aRv);
898
0
  }
899
0
900
0
  // Set mIsCroppingAreaOutSideOfSourceImage.
901
0
  ret->SetIsCroppingAreaOutSideOfSourceImage(surface->GetSize(), aCropRect);
902
0
903
0
  return ret.forget();
904
0
}
905
906
/* static */ already_AddRefed<ImageBitmap>
907
ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, HTMLVideoElement& aVideoEl,
908
                            const Maybe<IntRect>& aCropRect, ErrorResult& aRv)
909
0
{
910
0
  aVideoEl.MarkAsContentSource(mozilla::dom::HTMLVideoElement::CallerAPI::CREATE_IMAGEBITMAP);
911
0
912
0
  // Check network state.
913
0
  if (aVideoEl.NetworkState() == NETWORK_EMPTY) {
914
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
915
0
    return nullptr;
916
0
  }
917
0
918
0
  // Check ready state.
919
0
  // Cannot be HTMLMediaElement::HAVE_NOTHING or HTMLMediaElement::HAVE_METADATA.
920
0
  if (aVideoEl.ReadyState() <= HAVE_METADATA) {
921
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
922
0
    return nullptr;
923
0
  }
924
0
925
0
  // Check security.
926
0
  nsCOMPtr<nsIPrincipal> principal = aVideoEl.GetCurrentVideoPrincipal();
927
0
  bool CORSUsed = aVideoEl.GetCORSMode() != CORS_NONE;
928
0
  if (!CheckSecurityForHTMLElements(false, CORSUsed, principal)) {
929
0
    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
930
0
    return nullptr;
931
0
  }
932
0
933
0
  // Create ImageBitmap.
934
0
  RefPtr<layers::Image> data = aVideoEl.GetCurrentImage();
935
0
  if (!data) {
936
0
    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
937
0
    return nullptr;
938
0
  }
939
0
  RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data);
940
0
941
0
  // Set the picture rectangle.
942
0
  if (ret && aCropRect.isSome()) {
943
0
    ret->SetPictureRect(aCropRect.ref(), aRv);
944
0
  }
945
0
946
0
  // Set mIsCroppingAreaOutSideOfSourceImage.
947
0
  ret->SetIsCroppingAreaOutSideOfSourceImage(data->GetSize(), aCropRect);
948
0
949
0
  return ret.forget();
950
0
}
951
952
/* static */ already_AddRefed<ImageBitmap>
953
ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, HTMLCanvasElement& aCanvasEl,
954
                            const Maybe<IntRect>& aCropRect, ErrorResult& aRv)
955
0
{
956
0
  if (aCanvasEl.Width() == 0 || aCanvasEl.Height() == 0) {
957
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
958
0
    return nullptr;
959
0
  }
960
0
961
0
  RefPtr<SourceSurface> surface = GetSurfaceFromElement(aGlobal, aCanvasEl, aRv);
962
0
963
0
  if (NS_WARN_IF(aRv.Failed())) {
964
0
    return nullptr;
965
0
  }
966
0
967
0
  // Crop the source surface if needed.
968
0
  RefPtr<SourceSurface> croppedSurface;
969
0
  IntRect cropRect = aCropRect.valueOr(IntRect());
970
0
971
0
  // If the HTMLCanvasElement's rendering context is WebGL, then the snapshot
972
0
  // we got from the HTMLCanvasElement is a DataSourceSurface which is a copy
973
0
  // of the rendering context. We handle cropping in this case.
974
0
  bool needToReportMemoryAllocation = false;
975
0
  if ((aCanvasEl.GetCurrentContextType() == CanvasContextType::WebGL1 ||
976
0
       aCanvasEl.GetCurrentContextType() == CanvasContextType::WebGL2) &&
977
0
      aCropRect.isSome()) {
978
0
    RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface();
979
0
    croppedSurface = CropAndCopyDataSourceSurface(dataSurface, cropRect);
980
0
    cropRect.MoveTo(0, 0);
981
0
    needToReportMemoryAllocation = true;
982
0
  }
983
0
  else {
984
0
    croppedSurface = surface;
985
0
  }
986
0
987
0
  if (NS_WARN_IF(!croppedSurface)) {
988
0
    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
989
0
    return nullptr;
990
0
  }
991
0
992
0
  // Create an Image from the SourceSurface.
993
0
  RefPtr<layers::Image> data = CreateImageFromSurface(croppedSurface);
994
0
995
0
  if (NS_WARN_IF(!data)) {
996
0
    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
997
0
    return nullptr;
998
0
  }
999
0
1000
0
  RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data);
1001
0
1002
0
  if (needToReportMemoryAllocation) {
1003
0
    ret->mAllocatedImageData = true;
1004
0
  }
1005
0
1006
0
  // Set the picture rectangle.
1007
0
  if (ret && aCropRect.isSome()) {
1008
0
    ret->SetPictureRect(cropRect, aRv);
1009
0
  }
1010
0
1011
0
  // Set mIsCroppingAreaOutSideOfSourceImage.
1012
0
  ret->SetIsCroppingAreaOutSideOfSourceImage(surface->GetSize(), aCropRect);
1013
0
1014
0
  return ret.forget();
1015
0
}
1016
1017
/* static */ already_AddRefed<ImageBitmap>
1018
ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, ImageData& aImageData,
1019
                            const Maybe<IntRect>& aCropRect, ErrorResult& aRv)
1020
0
{
1021
0
  // Copy data into SourceSurface.
1022
0
  dom::Uint8ClampedArray array;
1023
0
  DebugOnly<bool> inited = array.Init(aImageData.GetDataObject());
1024
0
  MOZ_ASSERT(inited);
1025
0
1026
0
  array.ComputeLengthAndData();
1027
0
  const SurfaceFormat FORMAT = SurfaceFormat::R8G8B8A8;
1028
0
  // ImageData's underlying data is not alpha-premultiplied.
1029
0
  const auto alphaType = gfxAlphaType::NonPremult;
1030
0
  const uint32_t BYTES_PER_PIXEL = BytesPerPixel(FORMAT);
1031
0
  const uint32_t imageWidth = aImageData.Width();
1032
0
  const uint32_t imageHeight = aImageData.Height();
1033
0
  const uint32_t imageStride = imageWidth * BYTES_PER_PIXEL;
1034
0
  const uint32_t dataLength = array.Length();
1035
0
  const gfx::IntSize imageSize(imageWidth, imageHeight);
1036
0
1037
0
  // Check the ImageData is neutered or not.
1038
0
  if (imageWidth == 0 || imageHeight == 0 ||
1039
0
      (imageWidth * imageHeight * BYTES_PER_PIXEL) != dataLength) {
1040
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1041
0
    return nullptr;
1042
0
  }
1043
0
1044
0
  // Create and Crop the raw data into a layers::Image
1045
0
  RefPtr<layers::Image> data;
1046
0
  if (NS_IsMainThread()) {
1047
0
    data = CreateImageFromRawData(imageSize, imageStride, FORMAT,
1048
0
                                  array.Data(), dataLength,
1049
0
                                  aCropRect);
1050
0
  } else {
1051
0
    RefPtr<CreateImageFromRawDataInMainThreadSyncTask> task
1052
0
      = new CreateImageFromRawDataInMainThreadSyncTask(array.Data(),
1053
0
                                                       dataLength,
1054
0
                                                       imageStride,
1055
0
                                                       FORMAT,
1056
0
                                                       imageSize,
1057
0
                                                       aCropRect,
1058
0
                                                       getter_AddRefs(data));
1059
0
    task->Dispatch(Canceling, aRv);
1060
0
  }
1061
0
1062
0
  if (NS_WARN_IF(!data)) {
1063
0
    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
1064
0
    return nullptr;
1065
0
  }
1066
0
1067
0
  // Create an ImageBimtap.
1068
0
  RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, alphaType);
1069
0
1070
0
  ret->mAllocatedImageData = true;
1071
0
1072
0
  // The cropping information has been handled in the CreateImageFromRawData()
1073
0
  // function.
1074
0
1075
0
  // Set mIsCroppingAreaOutSideOfSourceImage.
1076
0
  ret->SetIsCroppingAreaOutSideOfSourceImage(imageSize, aCropRect);
1077
0
1078
0
  return ret.forget();
1079
0
}
1080
1081
/* static */ already_AddRefed<ImageBitmap>
1082
ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, CanvasRenderingContext2D& aCanvasCtx,
1083
                            const Maybe<IntRect>& aCropRect, ErrorResult& aRv)
1084
0
{
1085
0
  // Check origin-clean.
1086
0
  if (aCanvasCtx.GetCanvas()->IsWriteOnly()) {
1087
0
    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
1088
0
    return nullptr;
1089
0
  }
1090
0
1091
0
  RefPtr<SourceSurface> surface = aCanvasCtx.GetSurfaceSnapshot();
1092
0
1093
0
  if (NS_WARN_IF(!surface)) {
1094
0
    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
1095
0
    return nullptr;
1096
0
  }
1097
0
1098
0
  const IntSize surfaceSize = surface->GetSize();
1099
0
  if (surfaceSize.width == 0 || surfaceSize.height == 0) {
1100
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1101
0
    return nullptr;
1102
0
  }
1103
0
1104
0
  RefPtr<layers::Image> data = CreateImageFromSurface(surface);
1105
0
1106
0
  if (NS_WARN_IF(!data)) {
1107
0
    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
1108
0
    return nullptr;
1109
0
  }
1110
0
1111
0
  RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data);
1112
0
1113
0
  ret->mAllocatedImageData = true;
1114
0
1115
0
  // Set the picture rectangle.
1116
0
  if (ret && aCropRect.isSome()) {
1117
0
    ret->SetPictureRect(aCropRect.ref(), aRv);
1118
0
  }
1119
0
1120
0
  // Set mIsCroppingAreaOutSideOfSourceImage.
1121
0
  ret->SetIsCroppingAreaOutSideOfSourceImage(surface->GetSize(), aCropRect);
1122
0
1123
0
  return ret.forget();
1124
0
}
1125
1126
/* static */ already_AddRefed<ImageBitmap>
1127
ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, ImageBitmap& aImageBitmap,
1128
                            const Maybe<IntRect>& aCropRect, ErrorResult& aRv)
1129
0
{
1130
0
  if (!aImageBitmap.mData) {
1131
0
    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
1132
0
    return nullptr;
1133
0
  }
1134
0
1135
0
  RefPtr<layers::Image> data = aImageBitmap.mData;
1136
0
  RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, aImageBitmap.mAlphaType);
1137
0
1138
0
  // Set the picture rectangle.
1139
0
  if (ret && aCropRect.isSome()) {
1140
0
    ret->SetPictureRect(aCropRect.ref(), aRv);
1141
0
  }
1142
0
1143
0
  // Set mIsCroppingAreaOutSideOfSourceImage.
1144
0
  if (aImageBitmap.mIsCroppingAreaOutSideOfSourceImage == true) {
1145
0
    ret->mIsCroppingAreaOutSideOfSourceImage = true;
1146
0
  } else {
1147
0
    ret->SetIsCroppingAreaOutSideOfSourceImage(aImageBitmap.mPictureRect.Size(),
1148
0
                                               aCropRect);
1149
0
  }
1150
0
1151
0
  return ret.forget();
1152
0
}
1153
1154
class FulfillImageBitmapPromise
1155
{
1156
protected:
1157
  FulfillImageBitmapPromise(Promise* aPromise, ImageBitmap* aImageBitmap)
1158
  : mPromise(aPromise)
1159
  , mImageBitmap(aImageBitmap)
1160
0
  {
1161
0
    MOZ_ASSERT(aPromise);
1162
0
  }
1163
1164
  void DoFulfillImageBitmapPromise()
1165
0
  {
1166
0
    mPromise->MaybeResolve(mImageBitmap);
1167
0
  }
1168
1169
private:
1170
  RefPtr<Promise> mPromise;
1171
  RefPtr<ImageBitmap> mImageBitmap;
1172
};
1173
1174
class FulfillImageBitmapPromiseTask final : public Runnable,
1175
                                            public FulfillImageBitmapPromise
1176
{
1177
public:
1178
  FulfillImageBitmapPromiseTask(Promise* aPromise, ImageBitmap* aImageBitmap)
1179
    : Runnable("dom::FulfillImageBitmapPromiseTask")
1180
    , FulfillImageBitmapPromise(aPromise, aImageBitmap)
1181
0
  {
1182
0
  }
1183
1184
  NS_IMETHOD Run() override
1185
0
  {
1186
0
    DoFulfillImageBitmapPromise();
1187
0
    return NS_OK;
1188
0
  }
1189
};
1190
1191
class FulfillImageBitmapPromiseWorkerTask final : public WorkerSameThreadRunnable,
1192
                                                  public FulfillImageBitmapPromise
1193
{
1194
public:
1195
  FulfillImageBitmapPromiseWorkerTask(Promise* aPromise, ImageBitmap* aImageBitmap)
1196
  : WorkerSameThreadRunnable(GetCurrentThreadWorkerPrivate()),
1197
    FulfillImageBitmapPromise(aPromise, aImageBitmap)
1198
0
  {
1199
0
  }
1200
1201
  bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
1202
0
  {
1203
0
    DoFulfillImageBitmapPromise();
1204
0
    return true;
1205
0
  }
1206
};
1207
1208
static void
1209
AsyncFulfillImageBitmapPromise(Promise* aPromise, ImageBitmap* aImageBitmap)
1210
0
{
1211
0
  if (NS_IsMainThread()) {
1212
0
    nsCOMPtr<nsIRunnable> task =
1213
0
      new FulfillImageBitmapPromiseTask(aPromise, aImageBitmap);
1214
0
    NS_DispatchToCurrentThread(task); // Actually, to the main-thread.
1215
0
  } else {
1216
0
    RefPtr<FulfillImageBitmapPromiseWorkerTask> task =
1217
0
      new FulfillImageBitmapPromiseWorkerTask(aPromise, aImageBitmap);
1218
0
    task->Dispatch(); // Actually, to the current worker-thread.
1219
0
  }
1220
0
}
1221
1222
class CreateImageBitmapFromBlobRunnable;
1223
1224
class CreateImageBitmapFromBlob final : public CancelableRunnable
1225
                                      , public imgIContainerCallback
1226
{
1227
  friend class CreateImageBitmapFromBlobRunnable;
1228
1229
public:
1230
  NS_DECL_ISUPPORTS_INHERITED
1231
  NS_DECL_IMGICONTAINERCALLBACK
1232
1233
  static already_AddRefed<CreateImageBitmapFromBlob>
1234
  Create(Promise* aPromise,
1235
         nsIGlobalObject* aGlobal,
1236
         Blob& aBlob,
1237
         const Maybe<IntRect>& aCropRect,
1238
         nsIEventTarget* aMainThreadEventTarget);
1239
1240
  NS_IMETHOD Run() override
1241
0
  {
1242
0
    MOZ_ASSERT(IsCurrentThread());
1243
0
1244
0
    nsresult rv = StartDecodeAndCropBlob();
1245
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1246
0
      DecodeAndCropBlobCompletedMainThread(nullptr, rv);
1247
0
    }
1248
0
1249
0
    return NS_OK;
1250
0
  }
1251
1252
  // Called by the WorkerRef.
1253
  void WorkerShuttingDown();
1254
1255
private:
1256
  CreateImageBitmapFromBlob(Promise* aPromise,
1257
                            nsIGlobalObject* aGlobal,
1258
                            already_AddRefed<nsIInputStream> aInputStream,
1259
                            const nsACString& aMimeType,
1260
                            const Maybe<IntRect>& aCropRect,
1261
                            nsIEventTarget* aMainThreadEventTarget)
1262
    : CancelableRunnable("dom::CreateImageBitmapFromBlob")
1263
    , mMutex("dom::CreateImageBitmapFromBlob::mMutex")
1264
    , mPromise(aPromise)
1265
    , mGlobalObject(aGlobal)
1266
    , mInputStream(std::move(aInputStream))
1267
    , mMimeType(aMimeType)
1268
    , mCropRect(aCropRect)
1269
    , mOriginalCropRect(aCropRect)
1270
    , mMainThreadEventTarget(aMainThreadEventTarget)
1271
    , mThread(GetCurrentVirtualThread())
1272
0
  {
1273
0
  }
1274
1275
  virtual ~CreateImageBitmapFromBlob()
1276
0
  {
1277
0
  }
1278
1279
  bool IsCurrentThread() const
1280
0
  {
1281
0
    return mThread == GetCurrentVirtualThread();
1282
0
  }
1283
1284
  // Called on the owning thread.
1285
  nsresult StartDecodeAndCropBlob();
1286
1287
  // Will be called when the decoding + cropping is completed on the
1288
  // main-thread. This could the not the owning thread!
1289
  void DecodeAndCropBlobCompletedMainThread(layers::Image* aImage,
1290
                                            nsresult aStatus);
1291
1292
  // Will be called when the decoding + cropping is completed on the owning
1293
  // thread.
1294
  void DecodeAndCropBlobCompletedOwningThread(layers::Image* aImage,
1295
                                              nsresult aStatus);
1296
1297
  // This is called on the main-thread only.
1298
  nsresult DecodeAndCropBlob();
1299
1300
  Mutex mMutex;
1301
1302
  // The access to this object is protected by mutex but is always nullified on
1303
  // the owning thread.
1304
  RefPtr<ThreadSafeWorkerRef> mWorkerRef;
1305
1306
  // Touched only on the owning thread.
1307
  RefPtr<Promise> mPromise;
1308
1309
  // Touched only on the owning thread.
1310
  nsCOMPtr<nsIGlobalObject> mGlobalObject;
1311
1312
  nsCOMPtr<nsIInputStream> mInputStream;
1313
  nsCString mMimeType;
1314
  Maybe<IntRect> mCropRect;
1315
  Maybe<IntRect> mOriginalCropRect;
1316
  IntSize mSourceSize;
1317
1318
  nsCOMPtr<nsIEventTarget> mMainThreadEventTarget;
1319
  void* mThread;
1320
};
1321
1322
NS_IMPL_ISUPPORTS_INHERITED(CreateImageBitmapFromBlob, CancelableRunnable,
1323
                            imgIContainerCallback)
1324
1325
class CreateImageBitmapFromBlobRunnable : public WorkerRunnable
1326
{
1327
public:
1328
  explicit CreateImageBitmapFromBlobRunnable(WorkerPrivate* aWorkerPrivate,
1329
                                             CreateImageBitmapFromBlob* aTask,
1330
                                             layers::Image* aImage,
1331
                                             nsresult aStatus)
1332
    : WorkerRunnable(aWorkerPrivate)
1333
    , mTask(aTask)
1334
    , mImage(aImage)
1335
    , mStatus(aStatus)
1336
0
  {}
1337
1338
  bool
1339
  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
1340
0
  {
1341
0
    mTask->DecodeAndCropBlobCompletedOwningThread(mImage, mStatus);
1342
0
    return true;
1343
0
  }
1344
1345
private:
1346
  RefPtr<CreateImageBitmapFromBlob> mTask;
1347
  RefPtr<layers::Image> mImage;
1348
  nsresult mStatus;
1349
};
1350
1351
static void
1352
AsyncCreateImageBitmapFromBlob(Promise* aPromise, nsIGlobalObject* aGlobal,
1353
                               Blob& aBlob, const Maybe<IntRect>& aCropRect)
1354
0
{
1355
0
  // Let's identify the main-thread event target.
1356
0
  nsCOMPtr<nsIEventTarget> mainThreadEventTarget;
1357
0
  if (NS_IsMainThread()) {
1358
0
     mainThreadEventTarget = aGlobal->EventTargetFor(TaskCategory::Other);
1359
0
  } else {
1360
0
    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
1361
0
    MOZ_ASSERT(workerPrivate);
1362
0
    mainThreadEventTarget = workerPrivate->MainThreadEventTarget();
1363
0
  }
1364
0
1365
0
  RefPtr<CreateImageBitmapFromBlob> task =
1366
0
    CreateImageBitmapFromBlob::Create(aPromise, aGlobal, aBlob, aCropRect,
1367
0
                                      mainThreadEventTarget);
1368
0
  if (NS_WARN_IF(!task)) {
1369
0
    aPromise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
1370
0
    return;
1371
0
  }
1372
0
1373
0
  NS_DispatchToCurrentThread(task);
1374
0
}
1375
1376
/* static */ already_AddRefed<Promise>
1377
ImageBitmap::Create(nsIGlobalObject* aGlobal, const ImageBitmapSource& aSrc,
1378
                    const Maybe<gfx::IntRect>& aCropRect, ErrorResult& aRv)
1379
0
{
1380
0
  MOZ_ASSERT(aGlobal);
1381
0
1382
0
  RefPtr<Promise> promise = Promise::Create(aGlobal, aRv);
1383
0
1384
0
  if (NS_WARN_IF(aRv.Failed())) {
1385
0
    return nullptr;
1386
0
  }
1387
0
1388
0
  if (aCropRect.isSome() && (aCropRect->Width() == 0 || aCropRect->Height() == 0)) {
1389
0
    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
1390
0
    return promise.forget();
1391
0
  }
1392
0
1393
0
  RefPtr<ImageBitmap> imageBitmap;
1394
0
1395
0
  if (aSrc.IsHTMLImageElement()) {
1396
0
    MOZ_ASSERT(NS_IsMainThread(),
1397
0
               "Creating ImageBitmap from HTMLImageElement off the main thread.");
1398
0
    imageBitmap = CreateInternal(aGlobal, aSrc.GetAsHTMLImageElement(), aCropRect, aRv);
1399
0
  } else if (aSrc.IsHTMLVideoElement()) {
1400
0
    MOZ_ASSERT(NS_IsMainThread(),
1401
0
               "Creating ImageBitmap from HTMLVideoElement off the main thread.");
1402
0
    imageBitmap = CreateInternal(aGlobal, aSrc.GetAsHTMLVideoElement(), aCropRect, aRv);
1403
0
  } else if (aSrc.IsHTMLCanvasElement()) {
1404
0
    MOZ_ASSERT(NS_IsMainThread(),
1405
0
               "Creating ImageBitmap from HTMLCanvasElement off the main thread.");
1406
0
    imageBitmap = CreateInternal(aGlobal, aSrc.GetAsHTMLCanvasElement(), aCropRect, aRv);
1407
0
  } else if (aSrc.IsImageData()) {
1408
0
    imageBitmap = CreateInternal(aGlobal, aSrc.GetAsImageData(), aCropRect, aRv);
1409
0
  } else if (aSrc.IsCanvasRenderingContext2D()) {
1410
0
    MOZ_ASSERT(NS_IsMainThread(),
1411
0
               "Creating ImageBitmap from CanvasRenderingContext2D off the main thread.");
1412
0
    imageBitmap = CreateInternal(aGlobal, aSrc.GetAsCanvasRenderingContext2D(), aCropRect, aRv);
1413
0
  } else if (aSrc.IsImageBitmap()) {
1414
0
    imageBitmap = CreateInternal(aGlobal, aSrc.GetAsImageBitmap(), aCropRect, aRv);
1415
0
  } else if (aSrc.IsBlob()) {
1416
0
    AsyncCreateImageBitmapFromBlob(promise, aGlobal, aSrc.GetAsBlob(), aCropRect);
1417
0
    return promise.forget();
1418
0
  } else {
1419
0
    aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
1420
0
    return nullptr;
1421
0
  }
1422
0
1423
0
  if (!aRv.Failed()) {
1424
0
    AsyncFulfillImageBitmapPromise(promise, imageBitmap);
1425
0
  }
1426
0
1427
0
  return promise.forget();
1428
0
}
1429
1430
/*static*/ JSObject*
1431
ImageBitmap::ReadStructuredClone(JSContext* aCx,
1432
                                 JSStructuredCloneReader* aReader,
1433
                                 nsIGlobalObject* aParent,
1434
                                 const nsTArray<RefPtr<DataSourceSurface>>& aClonedSurfaces,
1435
                                 uint32_t aIndex)
1436
0
{
1437
0
  MOZ_ASSERT(aCx);
1438
0
  MOZ_ASSERT(aReader);
1439
0
  // aParent might be null.
1440
0
1441
0
  uint32_t picRectX_;
1442
0
  uint32_t picRectY_;
1443
0
  uint32_t picRectWidth_;
1444
0
  uint32_t picRectHeight_;
1445
0
  uint32_t alphaType_;
1446
0
  uint32_t isCroppingAreaOutSideOfSourceImage_;
1447
0
1448
0
  if (!JS_ReadUint32Pair(aReader, &picRectX_, &picRectY_) ||
1449
0
      !JS_ReadUint32Pair(aReader, &picRectWidth_, &picRectHeight_) ||
1450
0
      !JS_ReadUint32Pair(aReader, &alphaType_,
1451
0
                                  &isCroppingAreaOutSideOfSourceImage_)) {
1452
0
    return nullptr;
1453
0
  }
1454
0
1455
0
  int32_t picRectX = BitwiseCast<int32_t>(picRectX_);
1456
0
  int32_t picRectY = BitwiseCast<int32_t>(picRectY_);
1457
0
  int32_t picRectWidth = BitwiseCast<int32_t>(picRectWidth_);
1458
0
  int32_t picRectHeight = BitwiseCast<int32_t>(picRectHeight_);
1459
0
  const auto alphaType = BitwiseCast<gfxAlphaType>(alphaType_);
1460
0
1461
0
  // Create a new ImageBitmap.
1462
0
  MOZ_ASSERT(!aClonedSurfaces.IsEmpty());
1463
0
  MOZ_ASSERT(aIndex < aClonedSurfaces.Length());
1464
0
1465
0
  // RefPtr<ImageBitmap> needs to go out of scope before toObjectOrNull() is
1466
0
  // called because the static analysis thinks dereferencing XPCOM objects
1467
0
  // can GC (because in some cases it can!), and a return statement with a
1468
0
  // JSObject* type means that JSObject* is on the stack as a raw pointer
1469
0
  // while destructors are running.
1470
0
  JS::Rooted<JS::Value> value(aCx);
1471
0
  {
1472
0
#ifdef FUZZING
1473
0
    if (aIndex >= aClonedSurfaces.Length()) {
1474
0
      return nullptr;
1475
0
    }
1476
0
#endif
1477
0
    RefPtr<layers::Image> img = CreateImageFromSurface(aClonedSurfaces[aIndex]);
1478
0
    RefPtr<ImageBitmap> imageBitmap = new ImageBitmap(aParent, img, alphaType);
1479
0
1480
0
    imageBitmap->mIsCroppingAreaOutSideOfSourceImage =
1481
0
      isCroppingAreaOutSideOfSourceImage_;
1482
0
1483
0
    ErrorResult error;
1484
0
    imageBitmap->SetPictureRect(IntRect(picRectX, picRectY,
1485
0
                                        picRectWidth, picRectHeight), error);
1486
0
    if (NS_WARN_IF(error.Failed())) {
1487
0
      error.SuppressException();
1488
0
      return nullptr;
1489
0
    }
1490
0
1491
0
    if (!GetOrCreateDOMReflector(aCx, imageBitmap, &value)) {
1492
0
      return nullptr;
1493
0
    }
1494
0
1495
0
    imageBitmap->mAllocatedImageData = true;
1496
0
  }
1497
0
1498
0
  return &(value.toObject());
1499
0
}
1500
1501
/*static*/ bool
1502
ImageBitmap::WriteStructuredClone(JSStructuredCloneWriter* aWriter,
1503
                                  nsTArray<RefPtr<DataSourceSurface>>& aClonedSurfaces,
1504
                                  ImageBitmap* aImageBitmap)
1505
0
{
1506
0
  MOZ_ASSERT(aWriter);
1507
0
  MOZ_ASSERT(aImageBitmap);
1508
0
1509
0
  const uint32_t picRectX = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.x);
1510
0
  const uint32_t picRectY = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.y);
1511
0
  const uint32_t picRectWidth = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.width);
1512
0
  const uint32_t picRectHeight = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.height);
1513
0
  const uint32_t alphaType = BitwiseCast<uint32_t>(aImageBitmap->mAlphaType);
1514
0
  const uint32_t isCroppingAreaOutSideOfSourceImage = aImageBitmap->mIsCroppingAreaOutSideOfSourceImage ? 1 : 0;
1515
0
1516
0
  // Indexing the cloned surfaces and send the index to the receiver.
1517
0
  uint32_t index = aClonedSurfaces.Length();
1518
0
1519
0
  if (NS_WARN_IF(!JS_WriteUint32Pair(aWriter, SCTAG_DOM_IMAGEBITMAP, index)) ||
1520
0
      NS_WARN_IF(!JS_WriteUint32Pair(aWriter, picRectX, picRectY)) ||
1521
0
      NS_WARN_IF(!JS_WriteUint32Pair(aWriter, picRectWidth, picRectHeight)) ||
1522
0
      NS_WARN_IF(!JS_WriteUint32Pair(aWriter, alphaType,
1523
0
                                              isCroppingAreaOutSideOfSourceImage))) {
1524
0
    return false;
1525
0
  }
1526
0
1527
0
  RefPtr<SourceSurface> surface =
1528
0
    aImageBitmap->mData->GetAsSourceSurface();
1529
0
  RefPtr<DataSourceSurface> snapshot = surface->GetDataSurface();
1530
0
  RefPtr<DataSourceSurface> dstDataSurface;
1531
0
  {
1532
0
    // DataSourceSurfaceD2D1::GetStride() will call EnsureMapped implicitly and
1533
0
    // won't Unmap after exiting function. So instead calling GetStride()
1534
0
    // directly, using ScopedMap to get stride.
1535
0
    DataSourceSurface::ScopedMap map(snapshot, DataSourceSurface::READ);
1536
0
    dstDataSurface =
1537
0
      Factory::CreateDataSourceSurfaceWithStride(snapshot->GetSize(),
1538
0
                                                 snapshot->GetFormat(),
1539
0
                                                 map.GetStride(),
1540
0
                                                 true);
1541
0
  }
1542
0
  if (NS_WARN_IF(!dstDataSurface)) {
1543
0
    return false;
1544
0
  }
1545
0
  Factory::CopyDataSourceSurface(snapshot, dstDataSurface);
1546
0
  aClonedSurfaces.AppendElement(dstDataSurface);
1547
0
  return true;
1548
0
}
1549
1550
// ImageBitmap extensions.
1551
ImageBitmapFormat
1552
ImageBitmap::FindOptimalFormat(const Optional<Sequence<ImageBitmapFormat>>& aPossibleFormats,
1553
                               ErrorResult& aRv)
1554
0
{
1555
0
  if (!mDataWrapper) {
1556
0
    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
1557
0
    return ImageBitmapFormat::EndGuard_;
1558
0
  }
1559
0
1560
0
  ImageBitmapFormat platformFormat = mDataWrapper->GetFormat();
1561
0
1562
0
  if (!aPossibleFormats.WasPassed() ||
1563
0
      aPossibleFormats.Value().Contains(platformFormat)) {
1564
0
    return platformFormat;
1565
0
  } else {
1566
0
    // If no matching is found, FindBestMatchingFromat() returns
1567
0
    // ImageBitmapFormat::EndGuard_ and we throw an exception.
1568
0
    ImageBitmapFormat optimalFormat =
1569
0
      FindBestMatchingFromat(platformFormat, aPossibleFormats.Value());
1570
0
1571
0
    if (optimalFormat == ImageBitmapFormat::EndGuard_) {
1572
0
      aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
1573
0
    }
1574
0
1575
0
    return optimalFormat;
1576
0
  }
1577
0
}
1578
1579
int32_t
1580
ImageBitmap::MappedDataLength(ImageBitmapFormat aFormat, ErrorResult& aRv)
1581
0
{
1582
0
  if (!mDataWrapper) {
1583
0
    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
1584
0
    return 0;
1585
0
  }
1586
0
1587
0
  if (aFormat == mDataWrapper->GetFormat()) {
1588
0
    return mDataWrapper->GetBufferLength();
1589
0
  } else {
1590
0
    return CalculateImageBufferSize(aFormat, Width(), Height());
1591
0
  }
1592
0
}
1593
1594
template<typename T>
1595
class MapDataIntoBufferSource
1596
{
1597
protected:
1598
  MapDataIntoBufferSource(JSContext* aCx,
1599
                          Promise *aPromise,
1600
                          ImageBitmap *aImageBitmap,
1601
                          const T& aBuffer,
1602
                          int32_t aOffset,
1603
                          ImageBitmapFormat aFormat)
1604
  : mPromise(aPromise)
1605
  , mImageBitmap(aImageBitmap)
1606
  , mBuffer(aCx, aBuffer.Obj())
1607
  , mOffset(aOffset)
1608
  , mFormat(aFormat)
1609
0
  {
1610
0
    MOZ_ASSERT(mPromise);
1611
0
    MOZ_ASSERT(JS_IsArrayBufferObject(mBuffer) ||
1612
0
               JS_IsArrayBufferViewObject(mBuffer));
1613
0
  }
Unexecuted instantiation: mozilla::dom::MapDataIntoBufferSource<mozilla::dom::TypedArray<unsigned char, &js::UnwrapArrayBuffer, &(JS_GetArrayBufferData(JSObject*, bool*, JS::AutoRequireNoGC const&)), &js::GetArrayBufferLengthAndData, &(JS_NewArrayBuffer(JSContext*, unsigned int))> >::MapDataIntoBufferSource(JSContext*, mozilla::dom::Promise*, mozilla::dom::ImageBitmap*, mozilla::dom::TypedArray<unsigned char, &js::UnwrapArrayBuffer, &(JS_GetArrayBufferData(JSObject*, bool*, JS::AutoRequireNoGC const&)), &js::GetArrayBufferLengthAndData, &(JS_NewArrayBuffer(JSContext*, unsigned int))> const&, int, mozilla::dom::ImageBitmapFormat)
Unexecuted instantiation: mozilla::dom::MapDataIntoBufferSource<mozilla::dom::ArrayBufferView_base<&js::UnwrapArrayBufferView, &js::GetArrayBufferViewLengthAndData, &(JS_GetArrayBufferViewType(JSObject*))> >::MapDataIntoBufferSource(JSContext*, mozilla::dom::Promise*, mozilla::dom::ImageBitmap*, mozilla::dom::ArrayBufferView_base<&js::UnwrapArrayBufferView, &js::GetArrayBufferViewLengthAndData, &(JS_GetArrayBufferViewType(JSObject*))> const&, int, mozilla::dom::ImageBitmapFormat)
1614
1615
0
  virtual ~MapDataIntoBufferSource() = default;
Unexecuted instantiation: mozilla::dom::MapDataIntoBufferSource<mozilla::dom::TypedArray<unsigned char, &js::UnwrapArrayBuffer, &(JS_GetArrayBufferData(JSObject*, bool*, JS::AutoRequireNoGC const&)), &js::GetArrayBufferLengthAndData, &(JS_NewArrayBuffer(JSContext*, unsigned int))> >::~MapDataIntoBufferSource()
Unexecuted instantiation: mozilla::dom::MapDataIntoBufferSource<mozilla::dom::ArrayBufferView_base<&js::UnwrapArrayBufferView, &js::GetArrayBufferViewLengthAndData, &(JS_GetArrayBufferViewType(JSObject*))> >::~MapDataIntoBufferSource()
1616
1617
  void DoMapDataIntoBufferSource()
1618
0
  {
1619
0
    ErrorResult error;
1620
0
1621
0
    auto rejectByDefault =
1622
0
      MakeScopeExit([this, &error]() {
1623
0
        this->mPromise->MaybeReject(error);
1624
0
      });
Unexecuted instantiation: mozilla::dom::MapDataIntoBufferSource<mozilla::dom::TypedArray<unsigned char, &js::UnwrapArrayBuffer, &(JS_GetArrayBufferData(JSObject*, bool*, JS::AutoRequireNoGC const&)), &js::GetArrayBufferLengthAndData, &(JS_NewArrayBuffer(JSContext*, unsigned int))> >::DoMapDataIntoBufferSource()::{lambda()#1}::operator()() const
Unexecuted instantiation: mozilla::dom::MapDataIntoBufferSource<mozilla::dom::ArrayBufferView_base<&js::UnwrapArrayBufferView, &js::GetArrayBufferViewLengthAndData, &(JS_GetArrayBufferViewType(JSObject*))> >::DoMapDataIntoBufferSource()::{lambda()#1}::operator()() const
1625
0
1626
0
    if (!mImageBitmap->mDataWrapper) {
1627
0
      error.Throw(NS_ERROR_NOT_AVAILABLE);
1628
0
      return;
1629
0
    }
1630
0
1631
0
    // Prepare destination buffer.
1632
0
    uint8_t* bufferData = nullptr;
1633
0
    uint32_t bufferLength = 0;
1634
0
    bool isSharedMemory = false;
1635
0
    if (JS_IsArrayBufferObject(mBuffer)) {
1636
0
      js::GetArrayBufferLengthAndData(mBuffer, &bufferLength, &isSharedMemory, &bufferData);
1637
0
    } else if (JS_IsArrayBufferViewObject(mBuffer)) {
1638
0
      js::GetArrayBufferViewLengthAndData(mBuffer, &bufferLength, &isSharedMemory, &bufferData);
1639
0
    } else {
1640
0
      error.Throw(NS_ERROR_NOT_IMPLEMENTED);
1641
0
      return;
1642
0
    }
1643
0
1644
0
    if (NS_WARN_IF(!bufferData) || NS_WARN_IF(!bufferLength)) {
1645
0
      error.Throw(NS_ERROR_NOT_AVAILABLE);
1646
0
      return;
1647
0
    }
1648
0
1649
0
    // Check length.
1650
0
    const int32_t neededBufferLength =
1651
0
      mImageBitmap->MappedDataLength(mFormat, error);
1652
0
1653
0
    if (((int32_t)bufferLength - mOffset) < neededBufferLength) {
1654
0
      error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
1655
0
      return;
1656
0
    }
1657
0
1658
0
    // Call ImageBitmapFormatUtils.
1659
0
    UniquePtr<ImagePixelLayout> layout =
1660
0
      mImageBitmap->mDataWrapper->MapDataInto(bufferData,
1661
0
                                              mOffset,
1662
0
                                              bufferLength,
1663
0
                                              mFormat,
1664
0
                                              error);
1665
0
1666
0
    if (NS_WARN_IF(!layout)) {
1667
0
      return;
1668
0
    }
1669
0
1670
0
    rejectByDefault.release();
1671
0
    mPromise->MaybeResolve(*layout);
1672
0
  }
Unexecuted instantiation: mozilla::dom::MapDataIntoBufferSource<mozilla::dom::TypedArray<unsigned char, &js::UnwrapArrayBuffer, &(JS_GetArrayBufferData(JSObject*, bool*, JS::AutoRequireNoGC const&)), &js::GetArrayBufferLengthAndData, &(JS_NewArrayBuffer(JSContext*, unsigned int))> >::DoMapDataIntoBufferSource()
Unexecuted instantiation: mozilla::dom::MapDataIntoBufferSource<mozilla::dom::ArrayBufferView_base<&js::UnwrapArrayBufferView, &js::GetArrayBufferViewLengthAndData, &(JS_GetArrayBufferViewType(JSObject*))> >::DoMapDataIntoBufferSource()
1673
1674
  RefPtr<Promise> mPromise;
1675
  RefPtr<ImageBitmap> mImageBitmap;
1676
  JS::PersistentRooted<JSObject*> mBuffer;
1677
  int32_t mOffset;
1678
  ImageBitmapFormat mFormat;
1679
};
1680
1681
template<typename T>
1682
class MapDataIntoBufferSourceTask final : public Runnable,
1683
                                          public MapDataIntoBufferSource<T>
1684
{
1685
public:
1686
  MapDataIntoBufferSourceTask(JSContext* aCx,
1687
                              Promise* aPromise,
1688
                              ImageBitmap* aImageBitmap,
1689
                              const T& aBuffer,
1690
                              int32_t aOffset,
1691
                              ImageBitmapFormat aFormat)
1692
    : Runnable("dom::MapDataIntoBufferSourceTask")
1693
    , MapDataIntoBufferSource<T>(aCx,
1694
                                 aPromise,
1695
                                 aImageBitmap,
1696
                                 aBuffer,
1697
                                 aOffset,
1698
                                 aFormat)
1699
0
  {
1700
0
  }
Unexecuted instantiation: mozilla::dom::MapDataIntoBufferSourceTask<mozilla::dom::TypedArray<unsigned char, &js::UnwrapArrayBuffer, &(JS_GetArrayBufferData(JSObject*, bool*, JS::AutoRequireNoGC const&)), &js::GetArrayBufferLengthAndData, &(JS_NewArrayBuffer(JSContext*, unsigned int))> >::MapDataIntoBufferSourceTask(JSContext*, mozilla::dom::Promise*, mozilla::dom::ImageBitmap*, mozilla::dom::TypedArray<unsigned char, &js::UnwrapArrayBuffer, &(JS_GetArrayBufferData(JSObject*, bool*, JS::AutoRequireNoGC const&)), &js::GetArrayBufferLengthAndData, &(JS_NewArrayBuffer(JSContext*, unsigned int))> const&, int, mozilla::dom::ImageBitmapFormat)
Unexecuted instantiation: mozilla::dom::MapDataIntoBufferSourceTask<mozilla::dom::ArrayBufferView_base<&js::UnwrapArrayBufferView, &js::GetArrayBufferViewLengthAndData, &(JS_GetArrayBufferViewType(JSObject*))> >::MapDataIntoBufferSourceTask(JSContext*, mozilla::dom::Promise*, mozilla::dom::ImageBitmap*, mozilla::dom::ArrayBufferView_base<&js::UnwrapArrayBufferView, &js::GetArrayBufferViewLengthAndData, &(JS_GetArrayBufferViewType(JSObject*))> const&, int, mozilla::dom::ImageBitmapFormat)
1701
1702
0
  virtual ~MapDataIntoBufferSourceTask() = default;
Unexecuted instantiation: mozilla::dom::MapDataIntoBufferSourceTask<mozilla::dom::TypedArray<unsigned char, &js::UnwrapArrayBuffer, &(JS_GetArrayBufferData(JSObject*, bool*, JS::AutoRequireNoGC const&)), &js::GetArrayBufferLengthAndData, &(JS_NewArrayBuffer(JSContext*, unsigned int))> >::~MapDataIntoBufferSourceTask()
Unexecuted instantiation: mozilla::dom::MapDataIntoBufferSourceTask<mozilla::dom::ArrayBufferView_base<&js::UnwrapArrayBufferView, &js::GetArrayBufferViewLengthAndData, &(JS_GetArrayBufferViewType(JSObject*))> >::~MapDataIntoBufferSourceTask()
1703
1704
  NS_IMETHOD Run() override
1705
0
  {
1706
0
    MapDataIntoBufferSource<T>::DoMapDataIntoBufferSource();
1707
0
    return NS_OK;
1708
0
  }
Unexecuted instantiation: mozilla::dom::MapDataIntoBufferSourceTask<mozilla::dom::TypedArray<unsigned char, &js::UnwrapArrayBuffer, &(JS_GetArrayBufferData(JSObject*, bool*, JS::AutoRequireNoGC const&)), &js::GetArrayBufferLengthAndData, &(JS_NewArrayBuffer(JSContext*, unsigned int))> >::Run()
Unexecuted instantiation: mozilla::dom::MapDataIntoBufferSourceTask<mozilla::dom::ArrayBufferView_base<&js::UnwrapArrayBufferView, &js::GetArrayBufferViewLengthAndData, &(JS_GetArrayBufferViewType(JSObject*))> >::Run()
1709
};
1710
1711
template<typename T>
1712
class MapDataIntoBufferSourceWorkerTask final : public WorkerSameThreadRunnable,
1713
                                                public MapDataIntoBufferSource<T>
1714
{
1715
public:
1716
  MapDataIntoBufferSourceWorkerTask(JSContext* aCx,
1717
                                    Promise *aPromise,
1718
                                    ImageBitmap *aImageBitmap,
1719
                                    const T& aBuffer,
1720
                                    int32_t aOffset,
1721
                                    ImageBitmapFormat aFormat)
1722
  : WorkerSameThreadRunnable(GetCurrentThreadWorkerPrivate()),
1723
    MapDataIntoBufferSource<T>(aCx, aPromise, aImageBitmap, aBuffer, aOffset, aFormat)
1724
0
  {
1725
0
  }
Unexecuted instantiation: mozilla::dom::MapDataIntoBufferSourceWorkerTask<mozilla::dom::TypedArray<unsigned char, &js::UnwrapArrayBuffer, &(JS_GetArrayBufferData(JSObject*, bool*, JS::AutoRequireNoGC const&)), &js::GetArrayBufferLengthAndData, &(JS_NewArrayBuffer(JSContext*, unsigned int))> >::MapDataIntoBufferSourceWorkerTask(JSContext*, mozilla::dom::Promise*, mozilla::dom::ImageBitmap*, mozilla::dom::TypedArray<unsigned char, &js::UnwrapArrayBuffer, &(JS_GetArrayBufferData(JSObject*, bool*, JS::AutoRequireNoGC const&)), &js::GetArrayBufferLengthAndData, &(JS_NewArrayBuffer(JSContext*, unsigned int))> const&, int, mozilla::dom::ImageBitmapFormat)
Unexecuted instantiation: mozilla::dom::MapDataIntoBufferSourceWorkerTask<mozilla::dom::ArrayBufferView_base<&js::UnwrapArrayBufferView, &js::GetArrayBufferViewLengthAndData, &(JS_GetArrayBufferViewType(JSObject*))> >::MapDataIntoBufferSourceWorkerTask(JSContext*, mozilla::dom::Promise*, mozilla::dom::ImageBitmap*, mozilla::dom::ArrayBufferView_base<&js::UnwrapArrayBufferView, &js::GetArrayBufferViewLengthAndData, &(JS_GetArrayBufferViewType(JSObject*))> const&, int, mozilla::dom::ImageBitmapFormat)
1726
1727
0
  virtual ~MapDataIntoBufferSourceWorkerTask() = default;
Unexecuted instantiation: mozilla::dom::MapDataIntoBufferSourceWorkerTask<mozilla::dom::TypedArray<unsigned char, &js::UnwrapArrayBuffer, &(JS_GetArrayBufferData(JSObject*, bool*, JS::AutoRequireNoGC const&)), &js::GetArrayBufferLengthAndData, &(JS_NewArrayBuffer(JSContext*, unsigned int))> >::~MapDataIntoBufferSourceWorkerTask()
Unexecuted instantiation: mozilla::dom::MapDataIntoBufferSourceWorkerTask<mozilla::dom::ArrayBufferView_base<&js::UnwrapArrayBufferView, &js::GetArrayBufferViewLengthAndData, &(JS_GetArrayBufferViewType(JSObject*))> >::~MapDataIntoBufferSourceWorkerTask()
1728
1729
  bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
1730
0
  {
1731
0
    MapDataIntoBufferSource<T>::DoMapDataIntoBufferSource();
1732
0
    return true;
1733
0
  }
Unexecuted instantiation: mozilla::dom::MapDataIntoBufferSourceWorkerTask<mozilla::dom::TypedArray<unsigned char, &js::UnwrapArrayBuffer, &(JS_GetArrayBufferData(JSObject*, bool*, JS::AutoRequireNoGC const&)), &js::GetArrayBufferLengthAndData, &(JS_NewArrayBuffer(JSContext*, unsigned int))> >::WorkerRun(JSContext*, mozilla::dom::WorkerPrivate*)
Unexecuted instantiation: mozilla::dom::MapDataIntoBufferSourceWorkerTask<mozilla::dom::ArrayBufferView_base<&js::UnwrapArrayBufferView, &js::GetArrayBufferViewLengthAndData, &(JS_GetArrayBufferViewType(JSObject*))> >::WorkerRun(JSContext*, mozilla::dom::WorkerPrivate*)
1734
};
1735
1736
void AsyncMapDataIntoBufferSource(JSContext* aCx,
1737
                                  Promise *aPromise,
1738
                                  ImageBitmap *aImageBitmap,
1739
                                  const ArrayBufferViewOrArrayBuffer& aBuffer,
1740
                                  int32_t aOffset,
1741
                                  ImageBitmapFormat aFormat)
1742
0
{
1743
0
  MOZ_ASSERT(aCx);
1744
0
  MOZ_ASSERT(aPromise);
1745
0
  MOZ_ASSERT(aImageBitmap);
1746
0
1747
0
  if (NS_IsMainThread()) {
1748
0
    nsCOMPtr<nsIRunnable> task;
1749
0
1750
0
    if (aBuffer.IsArrayBuffer()) {
1751
0
      const ArrayBuffer& buffer = aBuffer.GetAsArrayBuffer();
1752
0
      task = new MapDataIntoBufferSourceTask<ArrayBuffer>(aCx, aPromise, aImageBitmap, buffer, aOffset, aFormat);
1753
0
    } else if (aBuffer.IsArrayBufferView()) {
1754
0
      const ArrayBufferView& bufferView = aBuffer.GetAsArrayBufferView();
1755
0
      task = new MapDataIntoBufferSourceTask<ArrayBufferView>(aCx, aPromise, aImageBitmap, bufferView, aOffset, aFormat);
1756
0
    }
1757
0
1758
0
    NS_DispatchToCurrentThread(task); // Actually, to the main-thread.
1759
0
  } else {
1760
0
    RefPtr<WorkerSameThreadRunnable> task;
1761
0
1762
0
    if (aBuffer.IsArrayBuffer()) {
1763
0
      const ArrayBuffer& buffer = aBuffer.GetAsArrayBuffer();
1764
0
      task = new MapDataIntoBufferSourceWorkerTask<ArrayBuffer>(aCx, aPromise, aImageBitmap, buffer, aOffset, aFormat);
1765
0
    } else if (aBuffer.IsArrayBufferView()) {
1766
0
      const ArrayBufferView& bufferView = aBuffer.GetAsArrayBufferView();
1767
0
      task = new MapDataIntoBufferSourceWorkerTask<ArrayBufferView>(aCx, aPromise, aImageBitmap, bufferView, aOffset, aFormat);
1768
0
    }
1769
0
1770
0
    task->Dispatch(); // Actually, to the current worker-thread.
1771
0
  }
1772
0
}
1773
1774
already_AddRefed<Promise>
1775
ImageBitmap::MapDataInto(JSContext* aCx,
1776
                         ImageBitmapFormat aFormat,
1777
                         const ArrayBufferViewOrArrayBuffer& aBuffer,
1778
                         int32_t aOffset, ErrorResult& aRv)
1779
0
{
1780
0
  MOZ_ASSERT(aCx, "No JSContext while calling ImageBitmap::MapDataInto().");
1781
0
1782
0
  RefPtr<Promise> promise = Promise::Create(mParent, aRv);
1783
0
1784
0
  if (NS_WARN_IF(aRv.Failed())) {
1785
0
    return nullptr;
1786
0
  }
1787
0
1788
0
  if (!mDataWrapper) {
1789
0
    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
1790
0
    return promise.forget();
1791
0
1792
0
  }
1793
0
1794
0
  // Check for cases that should throws.
1795
0
  // Case 1:
1796
0
  // If image bitmap was cropped to the source rectangle so that it contains any
1797
0
  // transparent black pixels (cropping area is outside of the source image),
1798
0
  // then reject promise with IndexSizeError and abort these steps.
1799
0
  if (mIsCroppingAreaOutSideOfSourceImage) {
1800
0
    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
1801
0
    return promise.forget();
1802
0
  }
1803
0
1804
0
  // Case 2:
1805
0
  // If the image bitmap is going to be accessed in YUV422/YUV422 series with a
1806
0
  // cropping area starts at an odd x or y coordinate.
1807
0
  if (aFormat == ImageBitmapFormat::YUV422P ||
1808
0
      aFormat == ImageBitmapFormat::YUV420P ||
1809
0
      aFormat == ImageBitmapFormat::YUV420SP_NV12 ||
1810
0
      aFormat == ImageBitmapFormat::YUV420SP_NV21) {
1811
0
    if ((mPictureRect.x & 1) || (mPictureRect.y & 1)) {
1812
0
      aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
1813
0
      return promise.forget();
1814
0
    }
1815
0
  }
1816
0
1817
0
  AsyncMapDataIntoBufferSource(aCx, promise, this, aBuffer, aOffset, aFormat);
1818
0
  return promise.forget();
1819
0
}
1820
1821
// ImageBitmapFactories extensions.
1822
static SurfaceFormat
1823
ImageFormatToSurfaceFromat(mozilla::dom::ImageBitmapFormat aFormat)
1824
{
1825
  switch(aFormat) {
1826
  case ImageBitmapFormat::RGBA32:
1827
    return SurfaceFormat::R8G8B8A8;
1828
  case ImageBitmapFormat::BGRA32:
1829
    return SurfaceFormat::B8G8R8A8;
1830
  case ImageBitmapFormat::RGB24:
1831
    return SurfaceFormat::R8G8B8;
1832
  case ImageBitmapFormat::BGR24:
1833
    return SurfaceFormat::B8G8R8;
1834
  case ImageBitmapFormat::GRAY8:
1835
    return SurfaceFormat::A8;
1836
  case ImageBitmapFormat::HSV:
1837
    return SurfaceFormat::HSV;
1838
  case ImageBitmapFormat::Lab:
1839
    return SurfaceFormat::Lab;
1840
  case ImageBitmapFormat::DEPTH:
1841
    return SurfaceFormat::Depth;
1842
  default:
1843
    return SurfaceFormat::UNKNOWN;
1844
  }
1845
}
1846
1847
static already_AddRefed<layers::Image>
1848
CreateImageFromBufferSourceRawData(const uint8_t*aBufferData,
1849
                                   uint32_t aBufferLength,
1850
                                   mozilla::dom::ImageBitmapFormat aFormat,
1851
                                   const Sequence<ChannelPixelLayout>& aLayout)
1852
0
{
1853
0
  MOZ_ASSERT(aBufferData);
1854
0
  MOZ_ASSERT(aBufferLength > 0);
1855
0
1856
0
  switch(aFormat) {
1857
0
  case ImageBitmapFormat::RGBA32:
1858
0
  case ImageBitmapFormat::BGRA32:
1859
0
  case ImageBitmapFormat::RGB24:
1860
0
  case ImageBitmapFormat::BGR24:
1861
0
  case ImageBitmapFormat::GRAY8:
1862
0
  case ImageBitmapFormat::HSV:
1863
0
  case ImageBitmapFormat::Lab:
1864
0
  case ImageBitmapFormat::DEPTH:
1865
0
  {
1866
0
    const nsTArray<ChannelPixelLayout>& channels = aLayout;
1867
0
    MOZ_ASSERT(channels.Length() != 0, "Empty Channels.");
1868
0
1869
0
    const SurfaceFormat srcFormat = ImageFormatToSurfaceFromat(aFormat);
1870
0
    const uint32_t srcStride = channels[0].mStride;
1871
0
    const IntSize srcSize(channels[0].mWidth, channels[0].mHeight);
1872
0
1873
0
    RefPtr<DataSourceSurface> dstDataSurface =
1874
0
      Factory::CreateDataSourceSurfaceWithStride(srcSize, srcFormat, srcStride);
1875
0
1876
0
    if (NS_WARN_IF(!dstDataSurface)) {
1877
0
      return nullptr;
1878
0
    }
1879
0
1880
0
    // Copy the raw data into the newly created DataSourceSurface.
1881
0
    DataSourceSurface::ScopedMap dstMap(dstDataSurface, DataSourceSurface::WRITE);
1882
0
    if (NS_WARN_IF(!dstMap.IsMapped())) {
1883
0
      return nullptr;
1884
0
    }
1885
0
1886
0
    const uint8_t* srcBufferPtr = aBufferData;
1887
0
    uint8_t* dstBufferPtr = dstMap.GetData();
1888
0
1889
0
    for (int i = 0; i < srcSize.height; ++i) {
1890
0
      memcpy(dstBufferPtr, srcBufferPtr, srcStride);
1891
0
      srcBufferPtr += srcStride;
1892
0
      dstBufferPtr += dstMap.GetStride();
1893
0
    }
1894
0
1895
0
    // Create an Image from the BGRA SourceSurface.
1896
0
    RefPtr<SourceSurface> surface = dstDataSurface;
1897
0
    RefPtr<layers::Image> image = CreateImageFromSurface(surface);
1898
0
1899
0
    if (NS_WARN_IF(!image)) {
1900
0
      return nullptr;
1901
0
    }
1902
0
1903
0
    return image.forget();
1904
0
  }
1905
0
  case ImageBitmapFormat::YUV444P:
1906
0
  case ImageBitmapFormat::YUV422P:
1907
0
  case ImageBitmapFormat::YUV420P:
1908
0
  case ImageBitmapFormat::YUV420SP_NV12:
1909
0
  case ImageBitmapFormat::YUV420SP_NV21:
1910
0
  {
1911
0
    // Prepare the PlanarYCbCrData.
1912
0
    const ChannelPixelLayout& yLayout = aLayout[0];
1913
0
    const ChannelPixelLayout& uLayout = aFormat != ImageBitmapFormat::YUV420SP_NV21 ? aLayout[1] : aLayout[2];
1914
0
    const ChannelPixelLayout& vLayout = aFormat != ImageBitmapFormat::YUV420SP_NV21 ? aLayout[2] : aLayout[1];
1915
0
1916
0
    layers::PlanarYCbCrData data;
1917
0
1918
0
    // Luminance buffer
1919
0
    data.mYChannel = const_cast<uint8_t*>(aBufferData + yLayout.mOffset);
1920
0
    data.mYStride = yLayout.mStride;
1921
0
    data.mYSize = gfx::IntSize(yLayout.mWidth, yLayout.mHeight);
1922
0
    data.mYSkip = yLayout.mSkip;
1923
0
1924
0
    // Chroma buffers
1925
0
    data.mCbChannel = const_cast<uint8_t*>(aBufferData + uLayout.mOffset);
1926
0
    data.mCrChannel = const_cast<uint8_t*>(aBufferData + vLayout.mOffset);
1927
0
    data.mCbCrStride = uLayout.mStride;
1928
0
    data.mCbCrSize = gfx::IntSize(uLayout.mWidth, uLayout.mHeight);
1929
0
    data.mCbSkip = uLayout.mSkip;
1930
0
    data.mCrSkip = vLayout.mSkip;
1931
0
1932
0
    // Picture rectangle.
1933
0
    // We set the picture rectangle to exactly the size of the source image to
1934
0
    // keep the full original data.
1935
0
    data.mPicX = 0;
1936
0
    data.mPicY = 0;
1937
0
    data.mPicSize = data.mYSize;
1938
0
1939
0
    // Create a layers::Image and set data.
1940
0
    if (aFormat == ImageBitmapFormat::YUV444P ||
1941
0
        aFormat == ImageBitmapFormat::YUV422P ||
1942
0
        aFormat == ImageBitmapFormat::YUV420P) {
1943
0
      RefPtr<layers::PlanarYCbCrImage> image =
1944
0
        new layers::RecyclingPlanarYCbCrImage(new layers::BufferRecycleBin());
1945
0
1946
0
      if (NS_WARN_IF(!image)) {
1947
0
        return nullptr;
1948
0
      }
1949
0
1950
0
      // Set Data.
1951
0
      if (NS_WARN_IF(!image->CopyData(data))) {
1952
0
        return nullptr;
1953
0
      }
1954
0
1955
0
      return image.forget();
1956
0
    } else {
1957
0
      RefPtr<layers::NVImage>image = new layers::NVImage();
1958
0
1959
0
      if (NS_WARN_IF(!image)) {
1960
0
        return nullptr;
1961
0
      }
1962
0
1963
0
      // Set Data.
1964
0
      if (NS_WARN_IF(!image->SetData(data))) {
1965
0
        return nullptr;
1966
0
      }
1967
0
1968
0
      return image.forget();
1969
0
    }
1970
0
  }
1971
0
  default:
1972
0
    return nullptr;
1973
0
  }
1974
0
}
1975
1976
/*
1977
 * This is a synchronous task.
1978
 * This class is used to create a layers::CairoImage from raw data in the main
1979
 * thread. While creating an ImageBitmap from an BufferSource, we need to create
1980
 * a SouceSurface from the BufferSource raw data and then set the SourceSurface
1981
 * into a layers::CairoImage. However, the layers::CairoImage asserts the
1982
 * setting operation in the main thread, so if we are going to create an
1983
 * ImageBitmap from an BufferSource off the main thread, we post an event to the
1984
 * main thread to create a layers::CairoImage from an BufferSource raw data.
1985
 *
1986
 * TODO: Once the layers::CairoImage is constructible off the main thread, which
1987
 *       means the SouceSurface could be released anywhere, we do not need this
1988
 *       task anymore.
1989
 */
1990
class CreateImageFromBufferSourceRawDataInMainThreadSyncTask final :
1991
  public WorkerMainThreadRunnable
1992
{
1993
public:
1994
  CreateImageFromBufferSourceRawDataInMainThreadSyncTask(const uint8_t* aBuffer,
1995
                                                         uint32_t aBufferLength,
1996
                                                         mozilla::dom::ImageBitmapFormat aFormat,
1997
                                                         const Sequence<ChannelPixelLayout>& aLayout,
1998
                                                         /*output*/ layers::Image** aImage)
1999
  : WorkerMainThreadRunnable(GetCurrentThreadWorkerPrivate(),
2000
                             NS_LITERAL_CSTRING("ImageBitmap-extensions :: Create Image from BufferSource Raw Data"))
2001
  , mImage(aImage)
2002
  , mBuffer(aBuffer)
2003
  , mBufferLength(aBufferLength)
2004
  , mFormat(aFormat)
2005
  , mLayout(aLayout)
2006
0
  {
2007
0
    MOZ_ASSERT(!(*aImage), "Don't pass an existing Image into CreateImageFromBufferSourceRawDataInMainThreadSyncTask.");
2008
0
  }
2009
2010
  bool MainThreadRun() override
2011
0
  {
2012
0
    RefPtr<layers::Image> image =
2013
0
      CreateImageFromBufferSourceRawData(mBuffer, mBufferLength, mFormat, mLayout);
2014
0
2015
0
    if (NS_WARN_IF(!image)) {
2016
0
      return true;
2017
0
    }
2018
0
2019
0
    image.forget(mImage);
2020
0
2021
0
    return true;
2022
0
  }
2023
2024
private:
2025
  layers::Image** mImage;
2026
  const uint8_t* mBuffer;
2027
  uint32_t mBufferLength;
2028
  mozilla::dom::ImageBitmapFormat mFormat;
2029
  const Sequence<ChannelPixelLayout>& mLayout;
2030
};
2031
2032
/*static*/ already_AddRefed<Promise>
2033
ImageBitmap::Create(nsIGlobalObject* aGlobal,
2034
                    const ImageBitmapSource& aBuffer,
2035
                    int32_t aOffset, int32_t aLength,
2036
                    mozilla::dom::ImageBitmapFormat aFormat,
2037
                    const Sequence<ChannelPixelLayout>& aLayout,
2038
                    ErrorResult& aRv)
2039
0
{
2040
0
  MOZ_ASSERT(aGlobal);
2041
0
2042
0
  RefPtr<Promise> promise = Promise::Create(aGlobal, aRv);
2043
0
2044
0
  if (NS_WARN_IF(aRv.Failed())) {
2045
0
    return nullptr;
2046
0
  }
2047
0
2048
0
  uint8_t* bufferData = nullptr;
2049
0
  uint32_t bufferLength = 0;
2050
0
2051
0
  if (aBuffer.IsArrayBuffer()) {
2052
0
    const ArrayBuffer& buffer = aBuffer.GetAsArrayBuffer();
2053
0
    buffer.ComputeLengthAndData();
2054
0
    bufferData = buffer.Data();
2055
0
    bufferLength = buffer.Length();
2056
0
  } else if (aBuffer.IsArrayBufferView()) {
2057
0
    const ArrayBufferView& bufferView = aBuffer.GetAsArrayBufferView();
2058
0
    bufferView.ComputeLengthAndData();
2059
0
    bufferData = bufferView.Data();
2060
0
    bufferLength = bufferView.Length();
2061
0
  } else {
2062
0
    aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
2063
0
    return promise.forget();
2064
0
  }
2065
0
2066
0
  MOZ_ASSERT(bufferData && bufferLength > 0, "Cannot read data from BufferSource.");
2067
0
2068
0
  // Check the buffer.
2069
0
  if (((uint32_t)(aOffset + aLength) > bufferLength)) {
2070
0
    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
2071
0
    return promise.forget();
2072
0
  }
2073
0
2074
0
  // Create and Crop the raw data into a layers::Image
2075
0
  RefPtr<layers::Image> data;
2076
0
  if (NS_IsMainThread()) {
2077
0
    data = CreateImageFromBufferSourceRawData(bufferData + aOffset, bufferLength,
2078
0
                                              aFormat, aLayout);
2079
0
  } else {
2080
0
    RefPtr<CreateImageFromBufferSourceRawDataInMainThreadSyncTask> task =
2081
0
      new CreateImageFromBufferSourceRawDataInMainThreadSyncTask(bufferData + aOffset,
2082
0
                                                                 bufferLength,
2083
0
                                                                 aFormat,
2084
0
                                                                 aLayout,
2085
0
                                                                 getter_AddRefs(data));
2086
0
    task->Dispatch(Canceling, aRv);
2087
0
    if (aRv.Failed()) {
2088
0
      return promise.forget();
2089
0
    }
2090
0
  }
2091
0
2092
0
  if (NS_WARN_IF(!data)) {
2093
0
    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
2094
0
    return promise.forget();
2095
0
  }
2096
0
2097
0
  // Create an ImageBimtap.
2098
0
  // Assume the data from an external buffer is not alpha-premultiplied.
2099
0
  RefPtr<ImageBitmap> imageBitmap = new ImageBitmap(aGlobal, data,
2100
0
                                                    gfxAlphaType::NonPremult);
2101
0
2102
0
  imageBitmap->mAllocatedImageData = true;
2103
0
2104
0
  // We don't need to call SetPictureRect() here because there is no cropping
2105
0
  // supported and the ImageBitmap's mPictureRect is the size of the source
2106
0
  // image in default
2107
0
2108
0
  // We don't need to set mIsCroppingAreaOutSideOfSourceImage here because there
2109
0
  // is no cropping supported and the mIsCroppingAreaOutSideOfSourceImage is
2110
0
  // false in default.
2111
0
2112
0
  AsyncFulfillImageBitmapPromise(promise, imageBitmap);
2113
0
2114
0
  return promise.forget();
2115
0
}
2116
2117
size_t
2118
ImageBitmap::GetAllocatedSize() const
2119
0
{
2120
0
  if (!mAllocatedImageData) {
2121
0
    return 0;
2122
0
  }
2123
0
2124
0
  // Calculate how many bytes are used.
2125
0
  if (mData->GetFormat() == mozilla::ImageFormat::PLANAR_YCBCR) {
2126
0
    return mData->AsPlanarYCbCrImage()->GetDataSize();
2127
0
  }
2128
0
2129
0
  if (mData->GetFormat() == mozilla::ImageFormat::NV_IMAGE) {
2130
0
    return mData->AsNVImage()->GetBufferSize();
2131
0
  }
2132
0
2133
0
  RefPtr<SourceSurface> surface = mData->GetAsSourceSurface();
2134
0
  const int bytesPerPixel = BytesPerPixel(surface->GetFormat());
2135
0
  return surface->GetSize().height * surface->GetSize().width * bytesPerPixel;
2136
0
}
2137
2138
size_t
2139
BindingJSObjectMallocBytes(ImageBitmap* aBitmap)
2140
0
{
2141
0
  return aBitmap->GetAllocatedSize();
2142
0
}
2143
2144
/* static */ already_AddRefed<CreateImageBitmapFromBlob>
2145
CreateImageBitmapFromBlob::Create(Promise* aPromise,
2146
                                  nsIGlobalObject* aGlobal,
2147
                                  Blob& aBlob,
2148
                                  const Maybe<IntRect>& aCropRect,
2149
                                  nsIEventTarget* aMainThreadEventTarget)
2150
0
{
2151
0
  // Get the internal stream of the blob.
2152
0
  nsCOMPtr<nsIInputStream> stream;
2153
0
  ErrorResult error;
2154
0
  aBlob.Impl()->CreateInputStream(getter_AddRefs(stream), error);
2155
0
  if (NS_WARN_IF(error.Failed())) {
2156
0
    return nullptr;
2157
0
  }
2158
0
2159
0
  // Get the MIME type string of the blob.
2160
0
  // The type will be checked in the DecodeImageAsync() method.
2161
0
  nsAutoString mimeTypeUTF16;
2162
0
  aBlob.Impl()->GetType(mimeTypeUTF16);
2163
0
  NS_ConvertUTF16toUTF8 mimeType(mimeTypeUTF16);
2164
0
2165
0
  RefPtr<CreateImageBitmapFromBlob> task =
2166
0
    new CreateImageBitmapFromBlob(aPromise, aGlobal, stream.forget(), mimeType,
2167
0
                                  aCropRect, aMainThreadEventTarget);
2168
0
2169
0
  // Nothing to do for the main-thread.
2170
0
  if (NS_IsMainThread()) {
2171
0
    return task.forget();
2172
0
  }
2173
0
2174
0
  // Let's use a WorkerRef to keep the worker alive if this is not the
2175
0
  // main-thread.
2176
0
  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
2177
0
  MOZ_ASSERT(workerPrivate);
2178
0
2179
0
  RefPtr<StrongWorkerRef> workerRef =
2180
0
    StrongWorkerRef::Create(workerPrivate, "CreateImageBitmapFromBlob",
2181
0
                            [task]() {
2182
0
      task->WorkerShuttingDown();
2183
0
    });
2184
0
  if (NS_WARN_IF(!workerRef)) {
2185
0
    return nullptr;
2186
0
  }
2187
0
2188
0
  task->mWorkerRef = new ThreadSafeWorkerRef(workerRef);
2189
0
  return task.forget();
2190
0
}
2191
2192
nsresult
2193
CreateImageBitmapFromBlob::StartDecodeAndCropBlob()
2194
0
{
2195
0
  MOZ_ASSERT(IsCurrentThread());
2196
0
2197
0
  // Workers.
2198
0
  if (!NS_IsMainThread()) {
2199
0
    RefPtr<CreateImageBitmapFromBlob> self = this;
2200
0
    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
2201
0
      "CreateImageBitmapFromBlob::DecodeAndCropBlob",
2202
0
      [self]() {
2203
0
        nsresult rv = self->DecodeAndCropBlob();
2204
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
2205
0
          self->DecodeAndCropBlobCompletedMainThread(nullptr, rv);
2206
0
        }
2207
0
      });
2208
0
2209
0
    return mMainThreadEventTarget->Dispatch(r.forget());
2210
0
  }
2211
0
2212
0
  // Main-thread.
2213
0
  return DecodeAndCropBlob();
2214
0
}
2215
2216
nsresult
2217
CreateImageBitmapFromBlob::DecodeAndCropBlob()
2218
0
{
2219
0
  MOZ_ASSERT(NS_IsMainThread());
2220
0
2221
0
  // Get the Component object.
2222
0
  nsCOMPtr<imgITools> imgtool = do_GetService(NS_IMGTOOLS_CID);
2223
0
  if (NS_WARN_IF(!imgtool)) {
2224
0
    return NS_ERROR_FAILURE;
2225
0
  }
2226
0
2227
0
  // Decode image.
2228
0
  nsCOMPtr<imgIContainer> imgContainer;
2229
0
  nsresult rv = imgtool->DecodeImageAsync(mInputStream, mMimeType, this,
2230
0
                                          mMainThreadEventTarget);
2231
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
2232
0
    return rv;
2233
0
  }
2234
0
2235
0
  return NS_OK;
2236
0
}
2237
2238
NS_IMETHODIMP
2239
CreateImageBitmapFromBlob::OnImageReady(imgIContainer* aImgContainer,
2240
                                        nsresult aStatus)
2241
0
{
2242
0
  MOZ_ASSERT(NS_IsMainThread());
2243
0
2244
0
  if (NS_FAILED(aStatus)) {
2245
0
    DecodeAndCropBlobCompletedMainThread(nullptr, aStatus);
2246
0
    return NS_OK;
2247
0
  }
2248
0
2249
0
  MOZ_ASSERT(aImgContainer);
2250
0
2251
0
  // Get the surface out.
2252
0
  uint32_t frameFlags = imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_WANT_DATA_SURFACE;
2253
0
  uint32_t whichFrame = imgIContainer::FRAME_FIRST;
2254
0
  RefPtr<SourceSurface> surface = aImgContainer->GetFrame(whichFrame, frameFlags);
2255
0
2256
0
  if (NS_WARN_IF(!surface)) {
2257
0
    DecodeAndCropBlobCompletedMainThread(nullptr, NS_ERROR_FAILURE);
2258
0
    return NS_OK;
2259
0
  }
2260
0
2261
0
  // Store the sourceSize value for the DecodeAndCropBlobCompletedMainThread call.
2262
0
  mSourceSize = surface->GetSize();
2263
0
2264
0
  // Crop the source surface if needed.
2265
0
  RefPtr<SourceSurface> croppedSurface = surface;
2266
0
2267
0
  if (mCropRect.isSome()) {
2268
0
    // The blob is just decoded into a RasterImage and not optimized yet, so the
2269
0
    // _surface_ we get is a DataSourceSurface which wraps the RasterImage's
2270
0
    // raw buffer.
2271
0
    //
2272
0
    // The _surface_ might already be optimized so that its type is not
2273
0
    // SurfaceType::DATA. However, we could keep using the generic cropping and
2274
0
    // copying since the decoded buffer is only used in this ImageBitmap so we
2275
0
    // should crop it to save memory usage.
2276
0
    //
2277
0
    // TODO: Bug1189632 is going to refactor this create-from-blob part to
2278
0
    //       decode the blob off the main thread. Re-check if we should do
2279
0
    //       cropping at this moment again there.
2280
0
    RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface();
2281
0
    croppedSurface = CropAndCopyDataSourceSurface(dataSurface, mCropRect.ref());
2282
0
    mCropRect->MoveTo(0, 0);
2283
0
  }
2284
0
2285
0
  if (NS_WARN_IF(!croppedSurface)) {
2286
0
    DecodeAndCropBlobCompletedMainThread(nullptr, NS_ERROR_FAILURE);
2287
0
    return NS_OK;
2288
0
  }
2289
0
2290
0
  // Create an Image from the source surface.
2291
0
  RefPtr<layers::Image> image = CreateImageFromSurface(croppedSurface);
2292
0
2293
0
  if (NS_WARN_IF(!image)) {
2294
0
    DecodeAndCropBlobCompletedMainThread(nullptr, NS_ERROR_FAILURE);
2295
0
    return NS_OK;
2296
0
  }
2297
0
2298
0
  DecodeAndCropBlobCompletedMainThread(image, NS_OK);
2299
0
  return NS_OK;
2300
0
}
2301
2302
void
2303
CreateImageBitmapFromBlob::DecodeAndCropBlobCompletedMainThread(layers::Image* aImage,
2304
                                                                nsresult aStatus)
2305
0
{
2306
0
  MOZ_ASSERT(NS_IsMainThread());
2307
0
2308
0
  if (!IsCurrentThread()) {
2309
0
    MutexAutoLock lock(mMutex);
2310
0
2311
0
    if (!mWorkerRef) {
2312
0
      // The worker is already gone.
2313
0
      return;
2314
0
    }
2315
0
2316
0
    RefPtr<CreateImageBitmapFromBlobRunnable> r =
2317
0
      new CreateImageBitmapFromBlobRunnable(mWorkerRef->Private(),
2318
0
                                            this, aImage, aStatus);
2319
0
    r->Dispatch();
2320
0
    return;
2321
0
  }
2322
0
2323
0
  DecodeAndCropBlobCompletedOwningThread(aImage, aStatus);
2324
0
}
2325
2326
void
2327
CreateImageBitmapFromBlob::DecodeAndCropBlobCompletedOwningThread(layers::Image* aImage,
2328
                                                                  nsresult aStatus)
2329
0
{
2330
0
  MOZ_ASSERT(IsCurrentThread());
2331
0
2332
0
  if (!mPromise) {
2333
0
    // The worker is going to be released soon. No needs to continue.
2334
0
    return;
2335
0
  }
2336
0
2337
0
  // Let's release what has to be released on the owning thread.
2338
0
  auto raii = MakeScopeExit([&] {
2339
0
    // Doing this we also release the worker.
2340
0
    mWorkerRef = nullptr;
2341
0
2342
0
    mPromise = nullptr;
2343
0
    mGlobalObject = nullptr;
2344
0
  });
2345
0
2346
0
  if (NS_WARN_IF(NS_FAILED(aStatus))) {
2347
0
    mPromise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
2348
0
    return;
2349
0
  }
2350
0
2351
0
  // Create ImageBitmap object.
2352
0
  RefPtr<ImageBitmap> imageBitmap = new ImageBitmap(mGlobalObject, aImage);
2353
0
2354
0
  // Set mIsCroppingAreaOutSideOfSourceImage.
2355
0
  imageBitmap->SetIsCroppingAreaOutSideOfSourceImage(mSourceSize,
2356
0
                                                     mOriginalCropRect);
2357
0
2358
0
  if (mCropRect.isSome()) {
2359
0
    ErrorResult rv;
2360
0
    imageBitmap->SetPictureRect(mCropRect.ref(), rv);
2361
0
2362
0
    if (rv.Failed()) {
2363
0
      mPromise->MaybeReject(rv);
2364
0
      return;
2365
0
    }
2366
0
  }
2367
0
2368
0
  imageBitmap->mAllocatedImageData = true;
2369
0
2370
0
  mPromise->MaybeResolve(imageBitmap);
2371
0
}
2372
2373
void
2374
CreateImageBitmapFromBlob::WorkerShuttingDown()
2375
0
{
2376
0
  MOZ_ASSERT(IsCurrentThread());
2377
0
2378
0
  MutexAutoLock lock(mMutex);
2379
0
2380
0
  // Let's release all the non-thread-safe objects now.
2381
0
  mWorkerRef = nullptr;
2382
0
  mPromise = nullptr;
2383
0
  mGlobalObject = nullptr;
2384
0
}
2385
2386
} // namespace dom
2387
} // namespace mozilla