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/BlobImpl.h"
10 #include "mozilla/dom/CanvasRenderingContext2D.h"
11 #include "mozilla/dom/CanvasUtils.h"
12 #include "mozilla/dom/Document.h"
13 #include "mozilla/dom/HTMLCanvasElement.h"
14 #include "mozilla/dom/HTMLImageElement.h"
15 #include "mozilla/dom/HTMLMediaElementBinding.h"
16 #include "mozilla/dom/HTMLVideoElement.h"
17 #include "mozilla/dom/ImageBitmapBinding.h"
18 #include "mozilla/dom/OffscreenCanvas.h"
19 #include "mozilla/dom/Promise.h"
20 #include "mozilla/dom/StructuredCloneTags.h"
21 #include "mozilla/dom/SVGImageElement.h"
22 #include "mozilla/dom/WorkerPrivate.h"
23 #include "mozilla/dom/WorkerRef.h"
24 #include "mozilla/dom/WorkerRunnable.h"
25 #include "mozilla/gfx/2D.h"
26 #include "mozilla/gfx/Logging.h"
27 #include "mozilla/gfx/Swizzle.h"
28 #include "mozilla/Mutex.h"
29 #include "mozilla/ScopeExit.h"
30 #include "nsGlobalWindowInner.h"
31 #include "nsIAsyncInputStream.h"
32 #include "nsNetUtil.h"
33 #include "nsLayoutUtils.h"
34 #include "nsStreamUtils.h"
35 #include "ImageUtils.h"
36 #include "imgLoader.h"
37 #include "imgTools.h"
38 
39 using namespace mozilla::gfx;
40 using namespace mozilla::layers;
41 using mozilla::dom::HTMLMediaElement_Binding::HAVE_METADATA;
42 using mozilla::dom::HTMLMediaElement_Binding::NETWORK_EMPTY;
43 
44 namespace mozilla::dom {
45 
46 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ImageBitmap, mParent)
47 NS_IMPL_CYCLE_COLLECTING_ADDREF(ImageBitmap)
48 NS_IMPL_CYCLE_COLLECTING_RELEASE(ImageBitmap)
49 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ImageBitmap)
50   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
51   NS_INTERFACE_MAP_ENTRY(nsISupports)
52 NS_INTERFACE_MAP_END
53 
54 /* This class observes shutdown notifications and sends that notification
55  * to the worker thread if the image bitmap is on a worker thread.
56  */
57 class ImageBitmapShutdownObserver final : public nsIObserver {
58  public:
ImageBitmapShutdownObserver(ImageBitmap * aImageBitmap)59   explicit ImageBitmapShutdownObserver(ImageBitmap* aImageBitmap)
60       : mImageBitmap(nullptr) {
61     if (NS_IsMainThread()) {
62       mImageBitmap = aImageBitmap;
63     } else {
64       WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
65       MOZ_ASSERT(workerPrivate);
66       mMainThreadEventTarget = workerPrivate->MainThreadEventTarget();
67       mSendToWorkerTask = new SendShutdownToWorkerThread(aImageBitmap);
68     }
69   }
70 
RegisterObserver()71   void RegisterObserver() {
72     if (NS_IsMainThread()) {
73       nsContentUtils::RegisterShutdownObserver(this);
74       return;
75     }
76 
77     MOZ_ASSERT(mMainThreadEventTarget);
78     RefPtr<ImageBitmapShutdownObserver> self = this;
79     nsCOMPtr<nsIRunnable> r =
80         NS_NewRunnableFunction("ImageBitmapShutdownObserver::RegisterObserver",
81                                [self]() { self->RegisterObserver(); });
82 
83     mMainThreadEventTarget->Dispatch(r.forget());
84   }
85 
UnregisterObserver()86   void UnregisterObserver() {
87     if (NS_IsMainThread()) {
88       nsContentUtils::UnregisterShutdownObserver(this);
89       return;
90     }
91 
92     MOZ_ASSERT(mMainThreadEventTarget);
93     RefPtr<ImageBitmapShutdownObserver> self = this;
94     nsCOMPtr<nsIRunnable> r =
95         NS_NewRunnableFunction("ImageBitmapShutdownObserver::RegisterObserver",
96                                [self]() { self->UnregisterObserver(); });
97 
98     mMainThreadEventTarget->Dispatch(r.forget());
99   }
100 
Clear()101   void Clear() {
102     mImageBitmap = nullptr;
103     if (mSendToWorkerTask) {
104       mSendToWorkerTask->mImageBitmap = nullptr;
105     }
106   }
107 
108   NS_DECL_THREADSAFE_ISUPPORTS
109   NS_DECL_NSIOBSERVER
110  private:
111   ~ImageBitmapShutdownObserver() = default;
112 
113   class SendShutdownToWorkerThread : public MainThreadWorkerControlRunnable {
114    public:
SendShutdownToWorkerThread(ImageBitmap * aImageBitmap)115     explicit SendShutdownToWorkerThread(ImageBitmap* aImageBitmap)
116         : MainThreadWorkerControlRunnable(GetCurrentThreadWorkerPrivate()),
117           mImageBitmap(aImageBitmap) {}
118 
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)119     bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
120       if (mImageBitmap) {
121         mImageBitmap->OnShutdown();
122         mImageBitmap = nullptr;
123       }
124       return true;
125     }
126 
127     ImageBitmap* mImageBitmap;
128   };
129 
130   ImageBitmap* mImageBitmap;
131   nsCOMPtr<nsIEventTarget> mMainThreadEventTarget;
132   RefPtr<SendShutdownToWorkerThread> mSendToWorkerTask;
133 };
134 
NS_IMPL_ISUPPORTS(ImageBitmapShutdownObserver,nsIObserver)135 NS_IMPL_ISUPPORTS(ImageBitmapShutdownObserver, nsIObserver)
136 
137 NS_IMETHODIMP
138 ImageBitmapShutdownObserver::Observe(nsISupports* aSubject, const char* aTopic,
139                                      const char16_t* aData) {
140   if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
141     if (mSendToWorkerTask) {
142       mSendToWorkerTask->Dispatch();
143     } else {
144       if (mImageBitmap) {
145         mImageBitmap->OnShutdown();
146         mImageBitmap = nullptr;
147       }
148     }
149     nsContentUtils::UnregisterShutdownObserver(this);
150   }
151 
152   return NS_OK;
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  */
FixUpNegativeDimension(const IntRect & aRect,ErrorResult & aRv)160 static IntRect FixUpNegativeDimension(const IntRect& aRect, ErrorResult& aRv) {
161   gfx::IntRect rect = aRect;
162 
163   // fix up negative dimensions
164   if (rect.width < 0) {
165     CheckedInt32 checkedX = CheckedInt32(rect.x) + rect.width;
166 
167     if (!checkedX.isValid()) {
168       aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
169       return rect;
170     }
171 
172     rect.x = checkedX.value();
173     rect.width = -(rect.width);
174   }
175 
176   if (rect.height < 0) {
177     CheckedInt32 checkedY = CheckedInt32(rect.y) + rect.height;
178 
179     if (!checkedY.isValid()) {
180       aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
181       return rect;
182     }
183 
184     rect.y = checkedY.value();
185     rect.height = -(rect.height);
186   }
187 
188   return rect;
189 }
190 
191 /*
192  * This helper function copies the data of the given DataSourceSurface,
193  *  _aSurface_, in the given area, _aCropRect_, into a new DataSourceSurface.
194  * This might return null if it can not create a new SourceSurface or it cannot
195  * read data from the given _aSurface_.
196  *
197  * Warning: Even though the area of _aCropRect_ is just the same as the size of
198  *          _aSurface_, this function still copy data into a new
199  *          DataSourceSurface.
200  */
CropAndCopyDataSourceSurface(DataSourceSurface * aSurface,const IntRect & aCropRect)201 static already_AddRefed<DataSourceSurface> CropAndCopyDataSourceSurface(
202     DataSourceSurface* aSurface, const IntRect& aCropRect) {
203   MOZ_ASSERT(aSurface);
204 
205   // Check the aCropRect
206   ErrorResult error;
207   const IntRect positiveCropRect = FixUpNegativeDimension(aCropRect, error);
208   if (NS_WARN_IF(error.Failed())) {
209     error.SuppressException();
210     return nullptr;
211   }
212 
213   // Calculate the size of the new SourceSurface.
214   // We cannot keep using aSurface->GetFormat() to create new DataSourceSurface,
215   // since it might be SurfaceFormat::B8G8R8X8 which does not handle opacity,
216   // however the specification explicitly define that "If any of the pixels on
217   // this rectangle are outside the area where the input bitmap was placed, then
218   // they will be transparent black in output."
219   // So, instead, we force the output format to be SurfaceFormat::B8G8R8A8.
220   const SurfaceFormat format = SurfaceFormat::B8G8R8A8;
221   const int bytesPerPixel = BytesPerPixel(format);
222   const IntSize dstSize =
223       IntSize(positiveCropRect.width, positiveCropRect.height);
224   const uint32_t dstStride = dstSize.width * bytesPerPixel;
225 
226   // Create a new SourceSurface.
227   RefPtr<DataSourceSurface> dstDataSurface =
228       Factory::CreateDataSourceSurfaceWithStride(dstSize, format, dstStride,
229                                                  true);
230 
231   if (NS_WARN_IF(!dstDataSurface)) {
232     return nullptr;
233   }
234 
235   // Only do copying and cropping when the positiveCropRect intersects with
236   // the size of aSurface.
237   const IntRect surfRect(IntPoint(0, 0), aSurface->GetSize());
238   if (surfRect.Intersects(positiveCropRect)) {
239     const IntRect surfPortion = surfRect.Intersect(positiveCropRect);
240     const IntPoint dest(std::max(0, surfPortion.X() - positiveCropRect.X()),
241                         std::max(0, surfPortion.Y() - positiveCropRect.Y()));
242 
243     // Copy the raw data into the newly created DataSourceSurface.
244     DataSourceSurface::ScopedMap srcMap(aSurface, DataSourceSurface::READ);
245     DataSourceSurface::ScopedMap dstMap(dstDataSurface,
246                                         DataSourceSurface::WRITE);
247     if (NS_WARN_IF(!srcMap.IsMapped()) || NS_WARN_IF(!dstMap.IsMapped())) {
248       return nullptr;
249     }
250 
251     uint8_t* srcBufferPtr = srcMap.GetData() +
252                             surfPortion.y * srcMap.GetStride() +
253                             surfPortion.x * bytesPerPixel;
254     uint8_t* dstBufferPtr =
255         dstMap.GetData() + dest.y * dstMap.GetStride() + dest.x * bytesPerPixel;
256     CheckedInt<uint32_t> copiedBytesPerRaw =
257         CheckedInt<uint32_t>(surfPortion.width) * bytesPerPixel;
258     if (!copiedBytesPerRaw.isValid()) {
259       return nullptr;
260     }
261 
262     for (int i = 0; i < surfPortion.height; ++i) {
263       memcpy(dstBufferPtr, srcBufferPtr, copiedBytesPerRaw.value());
264       srcBufferPtr += srcMap.GetStride();
265       dstBufferPtr += dstMap.GetStride();
266     }
267   }
268 
269   return dstDataSurface.forget();
270 }
271 
272 /*
273  * Encapsulate the given _aSurface_ into a layers::SourceSurfaceImage.
274  */
CreateImageFromSurface(SourceSurface * aSurface)275 static already_AddRefed<layers::Image> CreateImageFromSurface(
276     SourceSurface* aSurface) {
277   MOZ_ASSERT(aSurface);
278   RefPtr<layers::SourceSurfaceImage> image =
279       new layers::SourceSurfaceImage(aSurface->GetSize(), aSurface);
280   return image.forget();
281 }
282 
283 /*
284  * CreateImageFromRawData(), CreateSurfaceFromRawData() and
285  * CreateImageFromRawDataInMainThreadSyncTask are helpers for
286  * create-from-ImageData case
287  */
CreateSurfaceFromRawData(const gfx::IntSize & aSize,uint32_t aStride,gfx::SurfaceFormat aFormat,uint8_t * aBuffer,uint32_t aBufferLength,const Maybe<IntRect> & aCropRect)288 static already_AddRefed<SourceSurface> CreateSurfaceFromRawData(
289     const gfx::IntSize& aSize, uint32_t aStride, gfx::SurfaceFormat aFormat,
290     uint8_t* aBuffer, uint32_t aBufferLength, const Maybe<IntRect>& aCropRect) {
291   MOZ_ASSERT(!aSize.IsEmpty());
292   MOZ_ASSERT(aBuffer);
293 
294   // Wrap the source buffer into a SourceSurface.
295   RefPtr<DataSourceSurface> dataSurface =
296       Factory::CreateWrappingDataSourceSurface(aBuffer, aStride, aSize,
297                                                aFormat);
298 
299   if (NS_WARN_IF(!dataSurface)) {
300     return nullptr;
301   }
302 
303   // The temporary cropRect variable is equal to the size of source buffer if we
304   // do not need to crop, or it equals to the given cropping size.
305   const IntRect cropRect =
306       aCropRect.valueOr(IntRect(0, 0, aSize.width, aSize.height));
307 
308   // Copy the source buffer in the _cropRect_ area into a new SourceSurface.
309   RefPtr<DataSourceSurface> result =
310       CropAndCopyDataSourceSurface(dataSurface, cropRect);
311 
312   if (NS_WARN_IF(!result)) {
313     return nullptr;
314   }
315 
316   return result.forget();
317 }
318 
CreateImageFromRawData(const gfx::IntSize & aSize,uint32_t aStride,gfx::SurfaceFormat aFormat,uint8_t * aBuffer,uint32_t aBufferLength,const Maybe<IntRect> & aCropRect)319 static already_AddRefed<layers::Image> CreateImageFromRawData(
320     const gfx::IntSize& aSize, uint32_t aStride, gfx::SurfaceFormat aFormat,
321     uint8_t* aBuffer, uint32_t aBufferLength, const Maybe<IntRect>& aCropRect) {
322   MOZ_ASSERT(NS_IsMainThread());
323 
324   // Copy and crop the source buffer into a SourceSurface.
325   RefPtr<SourceSurface> rgbaSurface = CreateSurfaceFromRawData(
326       aSize, aStride, aFormat, aBuffer, aBufferLength, aCropRect);
327 
328   if (NS_WARN_IF(!rgbaSurface)) {
329     return nullptr;
330   }
331 
332   // Convert RGBA to BGRA
333   RefPtr<DataSourceSurface> rgbaDataSurface = rgbaSurface->GetDataSurface();
334   DataSourceSurface::ScopedMap rgbaMap(rgbaDataSurface,
335                                        DataSourceSurface::READ);
336   if (NS_WARN_IF(!rgbaMap.IsMapped())) {
337     return nullptr;
338   }
339 
340   RefPtr<DataSourceSurface> bgraDataSurface =
341       Factory::CreateDataSourceSurfaceWithStride(rgbaDataSurface->GetSize(),
342                                                  SurfaceFormat::B8G8R8A8,
343                                                  rgbaMap.GetStride());
344   if (NS_WARN_IF(!bgraDataSurface)) {
345     return nullptr;
346   }
347 
348   DataSourceSurface::ScopedMap bgraMap(bgraDataSurface,
349                                        DataSourceSurface::WRITE);
350   if (NS_WARN_IF(!bgraMap.IsMapped())) {
351     return nullptr;
352   }
353 
354   SwizzleData(rgbaMap.GetData(), rgbaMap.GetStride(), SurfaceFormat::R8G8B8A8,
355               bgraMap.GetData(), bgraMap.GetStride(), SurfaceFormat::B8G8R8A8,
356               bgraDataSurface->GetSize());
357 
358   // Create an Image from the BGRA SourceSurface.
359   return CreateImageFromSurface(bgraDataSurface);
360 }
361 
362 /*
363  * This is a synchronous task.
364  * This class is used to create a layers::SourceSurfaceImage from raw data in
365  * the main thread. While creating an ImageBitmap from an ImageData, we need to
366  * create a SouceSurface from the ImageData's raw data and then set the
367  * SourceSurface into a layers::SourceSurfaceImage. However, the
368  * layers::SourceSurfaceImage asserts the setting operation in the main thread,
369  * so if we are going to create an ImageBitmap from an ImageData off the main
370  * thread, we post an event to the main thread to create a
371  * layers::SourceSurfaceImage from an ImageData's raw data.
372  */
373 class CreateImageFromRawDataInMainThreadSyncTask final
374     : public WorkerMainThreadRunnable {
375  public:
CreateImageFromRawDataInMainThreadSyncTask(uint8_t * aBuffer,uint32_t aBufferLength,uint32_t aStride,gfx::SurfaceFormat aFormat,const gfx::IntSize & aSize,const Maybe<IntRect> & aCropRect,layers::Image ** aImage)376   CreateImageFromRawDataInMainThreadSyncTask(
377       uint8_t* aBuffer, uint32_t aBufferLength, uint32_t aStride,
378       gfx::SurfaceFormat aFormat, const gfx::IntSize& aSize,
379       const Maybe<IntRect>& aCropRect, layers::Image** aImage)
380       : WorkerMainThreadRunnable(
381             GetCurrentThreadWorkerPrivate(),
382             "ImageBitmap :: Create Image from Raw Data"_ns),
383         mImage(aImage),
384         mBuffer(aBuffer),
385         mBufferLength(aBufferLength),
386         mStride(aStride),
387         mFormat(aFormat),
388         mSize(aSize),
389         mCropRect(aCropRect) {
390     MOZ_ASSERT(!(*aImage),
391                "Don't pass an existing Image into "
392                "CreateImageFromRawDataInMainThreadSyncTask.");
393   }
394 
MainThreadRun()395   bool MainThreadRun() override {
396     RefPtr<layers::Image> image = CreateImageFromRawData(
397         mSize, mStride, mFormat, mBuffer, mBufferLength, mCropRect);
398 
399     if (NS_WARN_IF(!image)) {
400       return false;
401     }
402 
403     image.forget(mImage);
404 
405     return true;
406   }
407 
408  private:
409   layers::Image** mImage;
410   uint8_t* mBuffer;
411   uint32_t mBufferLength;
412   uint32_t mStride;
413   gfx::SurfaceFormat mFormat;
414   gfx::IntSize mSize;
415   const Maybe<IntRect>& mCropRect;
416 };
417 
418 /*
419  * A wrapper to the nsLayoutUtils::SurfaceFromElement() function followed by the
420  * security checking.
421  */
422 template <class ElementType>
GetSurfaceFromElement(nsIGlobalObject * aGlobal,ElementType & aElement,bool * aWriteOnly,ErrorResult & aRv)423 static already_AddRefed<SourceSurface> GetSurfaceFromElement(
424     nsIGlobalObject* aGlobal, ElementType& aElement, bool* aWriteOnly,
425     ErrorResult& aRv) {
426   SurfaceFromElementResult res = nsLayoutUtils::SurfaceFromElement(
427       &aElement, nsLayoutUtils::SFE_WANT_FIRST_FRAME_IF_IMAGE);
428 
429   RefPtr<SourceSurface> surface = res.GetSourceSurface();
430   if (NS_WARN_IF(!surface)) {
431     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
432     return nullptr;
433   }
434 
435   *aWriteOnly = res.mIsWriteOnly;
436 
437   return surface.forget();
438 }
439 
ImageBitmap(nsIGlobalObject * aGlobal,layers::Image * aData,bool aWriteOnly,gfxAlphaType aAlphaType)440 ImageBitmap::ImageBitmap(nsIGlobalObject* aGlobal, layers::Image* aData,
441                          bool aWriteOnly, gfxAlphaType aAlphaType)
442     : mParent(aGlobal),
443       mData(aData),
444       mSurface(nullptr),
445       mDataWrapper(new ImageUtils(mData)),
446       mPictureRect(aData->GetPictureRect()),
447       mAlphaType(aAlphaType),
448       mAllocatedImageData(false),
449       mWriteOnly(aWriteOnly) {
450   MOZ_ASSERT(aData, "aData is null in ImageBitmap constructor.");
451 
452   mShutdownObserver = new ImageBitmapShutdownObserver(this);
453   mShutdownObserver->RegisterObserver();
454 }
455 
~ImageBitmap()456 ImageBitmap::~ImageBitmap() {
457   if (mShutdownObserver) {
458     mShutdownObserver->Clear();
459     mShutdownObserver->UnregisterObserver();
460     mShutdownObserver = nullptr;
461   }
462 }
463 
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)464 JSObject* ImageBitmap::WrapObject(JSContext* aCx,
465                                   JS::Handle<JSObject*> aGivenProto) {
466   return ImageBitmap_Binding::Wrap(aCx, this, aGivenProto);
467 }
468 
Close()469 void ImageBitmap::Close() {
470   mData = nullptr;
471   mSurface = nullptr;
472   mDataWrapper = nullptr;
473   mPictureRect.SetEmpty();
474 }
475 
OnShutdown()476 void ImageBitmap::OnShutdown() {
477   mShutdownObserver = nullptr;
478 
479   Close();
480 }
481 
SetPictureRect(const IntRect & aRect,ErrorResult & aRv)482 void ImageBitmap::SetPictureRect(const IntRect& aRect, ErrorResult& aRv) {
483   mPictureRect = FixUpNegativeDimension(aRect, aRv);
484 }
485 
486 /*
487  * The functionality of PrepareForDrawTarget method:
488  * (1) Get a SourceSurface from the mData (which is a layers::Image).
489  * (2) Convert the SourceSurface to format B8G8R8A8 if the original format is
490  *     R8G8B8, B8G8R8, HSV or Lab.
491  *     Note: if the original format is A8 or Depth, then return null directly.
492  * (3) Do cropping if the size of SourceSurface does not equal to the
493  *     mPictureRect.
494  * (4) Pre-multiply alpha if needed.
495  */
PrepareForDrawTarget(gfx::DrawTarget * aTarget)496 already_AddRefed<SourceSurface> ImageBitmap::PrepareForDrawTarget(
497     gfx::DrawTarget* aTarget) {
498   MOZ_ASSERT(aTarget);
499 
500   if (!mData) {
501     return nullptr;
502   }
503 
504   if (!mSurface) {
505     mSurface = mData->GetAsSourceSurface();
506 
507     if (!mSurface) {
508       return nullptr;
509     }
510   }
511 
512   RefPtr<DrawTarget> target = aTarget;
513   IntRect surfRect(0, 0, mSurface->GetSize().width, mSurface->GetSize().height);
514 
515   // Check if we still need to crop our surface
516   if (!mPictureRect.IsEqualEdges(surfRect)) {
517     IntRect surfPortion = surfRect.Intersect(mPictureRect);
518 
519     // the crop lies entirely outside the surface area, nothing to draw
520     if (surfPortion.IsEmpty()) {
521       mSurface = nullptr;
522       RefPtr<gfx::SourceSurface> surface(mSurface);
523       return surface.forget();
524     }
525 
526     IntPoint dest(std::max(0, surfPortion.X() - mPictureRect.X()),
527                   std::max(0, surfPortion.Y() - mPictureRect.Y()));
528 
529     // We must initialize this target with mPictureRect.Size() because the
530     // specification states that if the cropping area is given, then return an
531     // ImageBitmap with the size equals to the cropping area.
532     target = target->CreateSimilarDrawTarget(mPictureRect.Size(),
533                                              target->GetFormat());
534 
535     if (!target) {
536       mSurface = nullptr;
537       RefPtr<gfx::SourceSurface> surface(mSurface);
538       return surface.forget();
539     }
540 
541     target->CopySurface(mSurface, surfPortion, dest);
542     mSurface = target->Snapshot();
543 
544     // Make mCropRect match new surface we've cropped to
545     mPictureRect.MoveTo(0, 0);
546   }
547 
548   // Pre-multiply alpha here.
549   // Ignore this step if the source surface does not have alpha channel; this
550   // kind of source surfaces might come form layers::PlanarYCbCrImage.
551   if (mAlphaType == gfxAlphaType::NonPremult &&
552       !IsOpaque(mSurface->GetFormat())) {
553     MOZ_ASSERT(mSurface->GetFormat() == SurfaceFormat::R8G8B8A8 ||
554                mSurface->GetFormat() == SurfaceFormat::B8G8R8A8 ||
555                mSurface->GetFormat() == SurfaceFormat::A8R8G8B8);
556 
557     RefPtr<DataSourceSurface> dstSurface = mSurface->GetDataSurface();
558     MOZ_ASSERT(dstSurface);
559 
560     RefPtr<DataSourceSurface> srcSurface;
561     DataSourceSurface::MappedSurface srcMap;
562     DataSourceSurface::MappedSurface dstMap;
563 
564     if (dstSurface->Map(DataSourceSurface::MapType::READ_WRITE, &dstMap)) {
565       srcMap = dstMap;
566     } else {
567       srcSurface = dstSurface;
568       if (!srcSurface->Map(DataSourceSurface::READ, &srcMap)) {
569         gfxCriticalError()
570             << "Failed to map source surface for premultiplying alpha.";
571         return nullptr;
572       }
573 
574       dstSurface = Factory::CreateDataSourceSurface(srcSurface->GetSize(),
575                                                     srcSurface->GetFormat());
576 
577       if (!dstSurface ||
578           !dstSurface->Map(DataSourceSurface::MapType::WRITE, &dstMap)) {
579         gfxCriticalError()
580             << "Failed to map destination surface for premultiplying alpha.";
581         srcSurface->Unmap();
582         return nullptr;
583       }
584     }
585 
586     PremultiplyData(srcMap.mData, srcMap.mStride, mSurface->GetFormat(),
587                     dstMap.mData, dstMap.mStride, mSurface->GetFormat(),
588                     dstSurface->GetSize());
589 
590     dstSurface->Unmap();
591     if (srcSurface) {
592       srcSurface->Unmap();
593     }
594 
595     mAlphaType = gfxAlphaType::Premult;
596     mSurface = dstSurface;
597   }
598 
599   // Replace our surface with one optimized for the target we're about to draw
600   // to, under the assumption it'll likely be drawn again to that target.
601   // This call should be a no-op for already-optimized surfaces
602   mSurface = target->OptimizeSourceSurface(mSurface);
603 
604   RefPtr<gfx::SourceSurface> surface(mSurface);
605   return surface.forget();
606 }
607 
TransferAsImage()608 already_AddRefed<layers::Image> ImageBitmap::TransferAsImage() {
609   RefPtr<layers::Image> image = mData;
610   Close();
611   return image.forget();
612 }
613 
ToCloneData() const614 UniquePtr<ImageBitmapCloneData> ImageBitmap::ToCloneData() const {
615   if (!mData) {
616     // A closed image cannot be cloned.
617     return nullptr;
618   }
619 
620   UniquePtr<ImageBitmapCloneData> result(new ImageBitmapCloneData());
621   result->mPictureRect = mPictureRect;
622   result->mAlphaType = mAlphaType;
623   RefPtr<SourceSurface> surface = mData->GetAsSourceSurface();
624   if (!surface) {
625     // It might just not be possible to get/map the surface. (e.g. from another
626     // process)
627     return nullptr;
628   }
629 
630   result->mSurface = surface->GetDataSurface();
631   MOZ_ASSERT(result->mSurface);
632   result->mWriteOnly = mWriteOnly;
633 
634   return result;
635 }
636 
637 /* static */
CreateFromSourceSurface(nsIGlobalObject * aGlobal,gfx::SourceSurface * aSource,ErrorResult & aRv)638 already_AddRefed<ImageBitmap> ImageBitmap::CreateFromSourceSurface(
639     nsIGlobalObject* aGlobal, gfx::SourceSurface* aSource, ErrorResult& aRv) {
640   RefPtr<layers::Image> data = CreateImageFromSurface(aSource);
641   RefPtr<ImageBitmap> ret =
642       new ImageBitmap(aGlobal, data, false /* writeOnly */);
643   ret->mAllocatedImageData = true;
644   return ret.forget();
645 }
646 
647 /* static */
CreateFromCloneData(nsIGlobalObject * aGlobal,ImageBitmapCloneData * aData)648 already_AddRefed<ImageBitmap> ImageBitmap::CreateFromCloneData(
649     nsIGlobalObject* aGlobal, ImageBitmapCloneData* aData) {
650   RefPtr<layers::Image> data = CreateImageFromSurface(aData->mSurface);
651 
652   RefPtr<ImageBitmap> ret =
653       new ImageBitmap(aGlobal, data, aData->mWriteOnly, aData->mAlphaType);
654 
655   ret->mAllocatedImageData = true;
656 
657   ErrorResult rv;
658   ret->SetPictureRect(aData->mPictureRect, rv);
659   return ret.forget();
660 }
661 
662 /* static */
CreateFromOffscreenCanvas(nsIGlobalObject * aGlobal,OffscreenCanvas & aOffscreenCanvas,ErrorResult & aRv)663 already_AddRefed<ImageBitmap> ImageBitmap::CreateFromOffscreenCanvas(
664     nsIGlobalObject* aGlobal, OffscreenCanvas& aOffscreenCanvas,
665     ErrorResult& aRv) {
666   // Check write-only mode.
667   bool writeOnly = aOffscreenCanvas.IsWriteOnly();
668 
669   SurfaceFromElementResult res = nsLayoutUtils::SurfaceFromOffscreenCanvas(
670       &aOffscreenCanvas, nsLayoutUtils::SFE_WANT_FIRST_FRAME_IF_IMAGE);
671 
672   RefPtr<SourceSurface> surface = res.GetSourceSurface();
673 
674   if (NS_WARN_IF(!surface)) {
675     aRv.Throw(NS_ERROR_NOT_AVAILABLE);
676     return nullptr;
677   }
678 
679   RefPtr<layers::Image> data = CreateImageFromSurface(surface);
680 
681   RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, writeOnly);
682 
683   ret->mAllocatedImageData = true;
684 
685   return ret.forget();
686 }
687 
688 /* static */
CreateInternal(nsIGlobalObject * aGlobal,HTMLImageElement & aImageEl,const Maybe<IntRect> & aCropRect,ErrorResult & aRv)689 already_AddRefed<ImageBitmap> ImageBitmap::CreateInternal(
690     nsIGlobalObject* aGlobal, HTMLImageElement& aImageEl,
691     const Maybe<IntRect>& aCropRect, ErrorResult& aRv) {
692   // Check if the image element is completely available or not.
693   if (!aImageEl.Complete()) {
694     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
695     return nullptr;
696   }
697 
698   bool writeOnly = true;
699 
700   // Get the SourceSurface out from the image element and then do security
701   // checking.
702   RefPtr<SourceSurface> surface =
703       GetSurfaceFromElement(aGlobal, aImageEl, &writeOnly, aRv);
704 
705   if (NS_WARN_IF(aRv.Failed())) {
706     return nullptr;
707   }
708 
709   // Create ImageBitmap.
710   RefPtr<layers::Image> data = CreateImageFromSurface(surface);
711   RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, writeOnly);
712 
713   // Set the picture rectangle.
714   if (ret && aCropRect.isSome()) {
715     ret->SetPictureRect(aCropRect.ref(), aRv);
716   }
717 
718   return ret.forget();
719 }
720 
721 /* static */
CreateInternal(nsIGlobalObject * aGlobal,SVGImageElement & aImageEl,const Maybe<IntRect> & aCropRect,ErrorResult & aRv)722 already_AddRefed<ImageBitmap> ImageBitmap::CreateInternal(
723     nsIGlobalObject* aGlobal, SVGImageElement& aImageEl,
724     const Maybe<IntRect>& aCropRect, ErrorResult& aRv) {
725   bool writeOnly = true;
726 
727   // Get the SourceSurface out from the image element and then do security
728   // checking.
729   RefPtr<SourceSurface> surface =
730       GetSurfaceFromElement(aGlobal, aImageEl, &writeOnly, aRv);
731 
732   if (NS_WARN_IF(aRv.Failed())) {
733     return nullptr;
734   }
735 
736   // Create ImageBitmap.
737   RefPtr<layers::Image> data = CreateImageFromSurface(surface);
738   RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, writeOnly);
739 
740   // Set the picture rectangle.
741   if (ret && aCropRect.isSome()) {
742     ret->SetPictureRect(aCropRect.ref(), aRv);
743   }
744 
745   return ret.forget();
746 }
747 
748 /* static */
CreateInternal(nsIGlobalObject * aGlobal,HTMLVideoElement & aVideoEl,const Maybe<IntRect> & aCropRect,ErrorResult & aRv)749 already_AddRefed<ImageBitmap> ImageBitmap::CreateInternal(
750     nsIGlobalObject* aGlobal, HTMLVideoElement& aVideoEl,
751     const Maybe<IntRect>& aCropRect, ErrorResult& aRv) {
752   aVideoEl.MarkAsContentSource(
753       mozilla::dom::HTMLVideoElement::CallerAPI::CREATE_IMAGEBITMAP);
754 
755   // Check network state.
756   if (aVideoEl.NetworkState() == NETWORK_EMPTY) {
757     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
758     return nullptr;
759   }
760 
761   // Check ready state.
762   // Cannot be HTMLMediaElement::HAVE_NOTHING or
763   // HTMLMediaElement::HAVE_METADATA.
764   if (aVideoEl.ReadyState() <= HAVE_METADATA) {
765     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
766     return nullptr;
767   }
768 
769   // Check security.
770   nsCOMPtr<nsIPrincipal> principal = aVideoEl.GetCurrentVideoPrincipal();
771   bool hadCrossOriginRedirects = aVideoEl.HadCrossOriginRedirects();
772   bool CORSUsed = aVideoEl.GetCORSMode() != CORS_NONE;
773   bool writeOnly = CanvasUtils::CheckWriteOnlySecurity(CORSUsed, principal,
774                                                        hadCrossOriginRedirects);
775 
776   // Create ImageBitmap.
777   RefPtr<layers::Image> data = aVideoEl.GetCurrentImage();
778   if (!data) {
779     aRv.Throw(NS_ERROR_NOT_AVAILABLE);
780     return nullptr;
781   }
782   RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, writeOnly);
783 
784   // Set the picture rectangle.
785   if (ret && aCropRect.isSome()) {
786     ret->SetPictureRect(aCropRect.ref(), aRv);
787   }
788 
789   return ret.forget();
790 }
791 
792 /* static */
CreateInternal(nsIGlobalObject * aGlobal,HTMLCanvasElement & aCanvasEl,const Maybe<IntRect> & aCropRect,ErrorResult & aRv)793 already_AddRefed<ImageBitmap> ImageBitmap::CreateInternal(
794     nsIGlobalObject* aGlobal, HTMLCanvasElement& aCanvasEl,
795     const Maybe<IntRect>& aCropRect, ErrorResult& aRv) {
796   if (aCanvasEl.Width() == 0 || aCanvasEl.Height() == 0) {
797     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
798     return nullptr;
799   }
800 
801   bool writeOnly = true;
802   RefPtr<SourceSurface> surface =
803       GetSurfaceFromElement(aGlobal, aCanvasEl, &writeOnly, aRv);
804 
805   if (NS_WARN_IF(aRv.Failed())) {
806     return nullptr;
807   }
808 
809   if (!writeOnly) {
810     writeOnly = aCanvasEl.IsWriteOnly();
811   }
812 
813   // Crop the source surface if needed.
814   RefPtr<SourceSurface> croppedSurface;
815   IntRect cropRect = aCropRect.valueOr(IntRect());
816 
817   // If the HTMLCanvasElement's rendering context is WebGL/WebGPU,
818   // then the snapshot we got from the HTMLCanvasElement is
819   // a DataSourceSurface which is a copy of the rendering context.
820   // We handle cropping in this case.
821   bool needToReportMemoryAllocation = false;
822   if ((aCanvasEl.GetCurrentContextType() == CanvasContextType::WebGL1 ||
823        aCanvasEl.GetCurrentContextType() == CanvasContextType::WebGL2 ||
824        aCanvasEl.GetCurrentContextType() == CanvasContextType::WebGPU) &&
825       aCropRect.isSome()) {
826     RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface();
827     croppedSurface = CropAndCopyDataSourceSurface(dataSurface, cropRect);
828     cropRect.MoveTo(0, 0);
829     needToReportMemoryAllocation = true;
830   } else {
831     croppedSurface = surface;
832   }
833 
834   if (NS_WARN_IF(!croppedSurface)) {
835     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
836     return nullptr;
837   }
838 
839   // Create an Image from the SourceSurface.
840   RefPtr<layers::Image> data = CreateImageFromSurface(croppedSurface);
841   RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, writeOnly);
842 
843   if (needToReportMemoryAllocation) {
844     ret->mAllocatedImageData = true;
845   }
846 
847   // Set the picture rectangle.
848   if (ret && aCropRect.isSome()) {
849     ret->SetPictureRect(cropRect, aRv);
850   }
851 
852   return ret.forget();
853 }
854 
855 /* static */
CreateInternal(nsIGlobalObject * aGlobal,ImageData & aImageData,const Maybe<IntRect> & aCropRect,ErrorResult & aRv)856 already_AddRefed<ImageBitmap> ImageBitmap::CreateInternal(
857     nsIGlobalObject* aGlobal, ImageData& aImageData,
858     const Maybe<IntRect>& aCropRect, ErrorResult& aRv) {
859   // Copy data into SourceSurface.
860   RootedSpiderMonkeyInterface<Uint8ClampedArray> array(RootingCx());
861   if (!array.Init(aImageData.GetDataObject())) {
862     aRv.ThrowInvalidStateError(
863         "Failed to extract Uint8ClampedArray from ImageData (security check "
864         "failed?)");
865     return nullptr;
866   }
867   array.ComputeState();
868   const SurfaceFormat FORMAT = SurfaceFormat::R8G8B8A8;
869   // ImageData's underlying data is not alpha-premultiplied.
870   const auto alphaType = gfxAlphaType::NonPremult;
871   const uint32_t BYTES_PER_PIXEL = BytesPerPixel(FORMAT);
872   const uint32_t imageWidth = aImageData.Width();
873   const uint32_t imageHeight = aImageData.Height();
874   const uint32_t imageStride = imageWidth * BYTES_PER_PIXEL;
875   const uint32_t dataLength = array.Length();
876   const gfx::IntSize imageSize(imageWidth, imageHeight);
877 
878   // Check the ImageData is neutered or not.
879   if (imageWidth == 0 || imageHeight == 0) {
880     aRv.ThrowInvalidStateError("Passed-in image is empty");
881     return nullptr;
882   }
883 
884   if ((imageWidth * imageHeight * BYTES_PER_PIXEL) != dataLength) {
885     aRv.ThrowInvalidStateError("Data size / image format mismatch");
886     return nullptr;
887   }
888 
889   // Create and Crop the raw data into a layers::Image
890   RefPtr<layers::Image> data;
891 
892   // If the data could move during a GC, copy it out into a local buffer that
893   // lives until a CreateImageFromRawData lower in the stack copies it.
894   // Reassure the static analysis that we know what we're doing.
895   size_t maxInline = JS_MaxMovableTypedArraySize();
896   uint8_t inlineDataBuffer[maxInline];
897   uint8_t* fixedData = array.FixedData(inlineDataBuffer, maxInline);
898 
899   // Lie to the hazard analysis and say that we're done with everything that
900   // `array` was using (safe because the data buffer is fixed, and the holding
901   // JSObject is being kept alive elsewhere.)
902   array.Reset();
903 
904   if (NS_IsMainThread()) {
905     data = CreateImageFromRawData(imageSize, imageStride, FORMAT, fixedData,
906                                   dataLength, aCropRect);
907   } else {
908     RefPtr<CreateImageFromRawDataInMainThreadSyncTask> task =
909         new CreateImageFromRawDataInMainThreadSyncTask(
910             fixedData, dataLength, imageStride, FORMAT, imageSize, aCropRect,
911             getter_AddRefs(data));
912     task->Dispatch(Canceling, aRv);
913   }
914 
915   if (NS_WARN_IF(!data)) {
916     aRv.ThrowInvalidStateError("Failed to create internal image");
917     return nullptr;
918   }
919 
920   // Create an ImageBitmap.
921   RefPtr<ImageBitmap> ret =
922       new ImageBitmap(aGlobal, data, false /* write-only */, alphaType);
923 
924   ret->mAllocatedImageData = true;
925 
926   // The cropping information has been handled in the CreateImageFromRawData()
927   // function.
928 
929   return ret.forget();
930 }
931 
932 /* static */
CreateInternal(nsIGlobalObject * aGlobal,CanvasRenderingContext2D & aCanvasCtx,const Maybe<IntRect> & aCropRect,ErrorResult & aRv)933 already_AddRefed<ImageBitmap> ImageBitmap::CreateInternal(
934     nsIGlobalObject* aGlobal, CanvasRenderingContext2D& aCanvasCtx,
935     const Maybe<IntRect>& aCropRect, ErrorResult& aRv) {
936   nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(aGlobal);
937   nsGlobalWindowInner* window = nsGlobalWindowInner::Cast(win);
938   if (NS_WARN_IF(!window) || !window->GetExtantDoc()) {
939     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
940     return nullptr;
941   }
942 
943   window->GetExtantDoc()->WarnOnceAbout(
944       DeprecatedOperations::eCreateImageBitmapCanvasRenderingContext2D);
945 
946   // Check write-only mode.
947   bool writeOnly =
948       aCanvasCtx.GetCanvas()->IsWriteOnly() || aCanvasCtx.IsWriteOnly();
949 
950   RefPtr<SourceSurface> surface = aCanvasCtx.GetSurfaceSnapshot();
951 
952   if (NS_WARN_IF(!surface)) {
953     aRv.Throw(NS_ERROR_NOT_AVAILABLE);
954     return nullptr;
955   }
956 
957   const IntSize surfaceSize = surface->GetSize();
958   if (surfaceSize.width == 0 || surfaceSize.height == 0) {
959     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
960     return nullptr;
961   }
962 
963   RefPtr<layers::Image> data = CreateImageFromSurface(surface);
964   RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, writeOnly);
965 
966   ret->mAllocatedImageData = true;
967 
968   // Set the picture rectangle.
969   if (ret && aCropRect.isSome()) {
970     ret->SetPictureRect(aCropRect.ref(), aRv);
971   }
972 
973   return ret.forget();
974 }
975 
976 /* static */
CreateInternal(nsIGlobalObject * aGlobal,ImageBitmap & aImageBitmap,const Maybe<IntRect> & aCropRect,ErrorResult & aRv)977 already_AddRefed<ImageBitmap> ImageBitmap::CreateInternal(
978     nsIGlobalObject* aGlobal, ImageBitmap& aImageBitmap,
979     const Maybe<IntRect>& aCropRect, ErrorResult& aRv) {
980   if (!aImageBitmap.mData) {
981     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
982     return nullptr;
983   }
984 
985   RefPtr<layers::Image> data = aImageBitmap.mData;
986   RefPtr<ImageBitmap> ret = new ImageBitmap(
987       aGlobal, data, aImageBitmap.mWriteOnly, aImageBitmap.mAlphaType);
988 
989   // Set the picture rectangle.
990   if (ret && aCropRect.isSome()) {
991     ret->SetPictureRect(aCropRect.ref(), aRv);
992   }
993 
994   return ret.forget();
995 }
996 
997 class FulfillImageBitmapPromise {
998  protected:
FulfillImageBitmapPromise(Promise * aPromise,ImageBitmap * aImageBitmap)999   FulfillImageBitmapPromise(Promise* aPromise, ImageBitmap* aImageBitmap)
1000       : mPromise(aPromise), mImageBitmap(aImageBitmap) {
1001     MOZ_ASSERT(aPromise);
1002   }
1003 
DoFulfillImageBitmapPromise()1004   void DoFulfillImageBitmapPromise() { mPromise->MaybeResolve(mImageBitmap); }
1005 
1006  private:
1007   RefPtr<Promise> mPromise;
1008   RefPtr<ImageBitmap> mImageBitmap;
1009 };
1010 
1011 class FulfillImageBitmapPromiseTask final : public Runnable,
1012                                             public FulfillImageBitmapPromise {
1013  public:
FulfillImageBitmapPromiseTask(Promise * aPromise,ImageBitmap * aImageBitmap)1014   FulfillImageBitmapPromiseTask(Promise* aPromise, ImageBitmap* aImageBitmap)
1015       : Runnable("dom::FulfillImageBitmapPromiseTask"),
1016         FulfillImageBitmapPromise(aPromise, aImageBitmap) {}
1017 
Run()1018   NS_IMETHOD Run() override {
1019     DoFulfillImageBitmapPromise();
1020     return NS_OK;
1021   }
1022 };
1023 
1024 class FulfillImageBitmapPromiseWorkerTask final
1025     : public WorkerSameThreadRunnable,
1026       public FulfillImageBitmapPromise {
1027  public:
FulfillImageBitmapPromiseWorkerTask(Promise * aPromise,ImageBitmap * aImageBitmap)1028   FulfillImageBitmapPromiseWorkerTask(Promise* aPromise,
1029                                       ImageBitmap* aImageBitmap)
1030       : WorkerSameThreadRunnable(GetCurrentThreadWorkerPrivate()),
1031         FulfillImageBitmapPromise(aPromise, aImageBitmap) {}
1032 
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)1033   bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
1034     DoFulfillImageBitmapPromise();
1035     return true;
1036   }
1037 };
1038 
AsyncFulfillImageBitmapPromise(Promise * aPromise,ImageBitmap * aImageBitmap)1039 static void AsyncFulfillImageBitmapPromise(Promise* aPromise,
1040                                            ImageBitmap* aImageBitmap) {
1041   if (NS_IsMainThread()) {
1042     nsCOMPtr<nsIRunnable> task =
1043         new FulfillImageBitmapPromiseTask(aPromise, aImageBitmap);
1044     NS_DispatchToCurrentThread(task);  // Actually, to the main-thread.
1045   } else {
1046     RefPtr<FulfillImageBitmapPromiseWorkerTask> task =
1047         new FulfillImageBitmapPromiseWorkerTask(aPromise, aImageBitmap);
1048     task->Dispatch();  // Actually, to the current worker-thread.
1049   }
1050 }
1051 
1052 class CreateImageBitmapFromBlobRunnable;
1053 
1054 class CreateImageBitmapFromBlob final : public DiscardableRunnable,
1055                                         public imgIContainerCallback,
1056                                         public nsIInputStreamCallback {
1057   friend class CreateImageBitmapFromBlobRunnable;
1058 
1059  public:
1060   NS_DECL_ISUPPORTS_INHERITED
1061   NS_DECL_IMGICONTAINERCALLBACK
1062   NS_DECL_NSIINPUTSTREAMCALLBACK
1063 
1064   static already_AddRefed<CreateImageBitmapFromBlob> Create(
1065       Promise* aPromise, nsIGlobalObject* aGlobal, Blob& aBlob,
1066       const Maybe<IntRect>& aCropRect, nsIEventTarget* aMainThreadEventTarget);
1067 
Run()1068   NS_IMETHOD Run() override {
1069     MOZ_ASSERT(IsCurrentThread());
1070 
1071     nsresult rv = StartMimeTypeAndDecodeAndCropBlob();
1072     if (NS_WARN_IF(NS_FAILED(rv))) {
1073       MimeTypeAndDecodeAndCropBlobCompletedMainThread(nullptr, rv);
1074     }
1075 
1076     return NS_OK;
1077   }
1078 
1079   // Called by the WorkerRef.
1080   void WorkerShuttingDown();
1081 
1082  private:
CreateImageBitmapFromBlob(Promise * aPromise,nsIGlobalObject * aGlobal,already_AddRefed<nsIInputStream> aInputStream,const Maybe<IntRect> & aCropRect,nsIEventTarget * aMainThreadEventTarget)1083   CreateImageBitmapFromBlob(Promise* aPromise, nsIGlobalObject* aGlobal,
1084                             already_AddRefed<nsIInputStream> aInputStream,
1085                             const Maybe<IntRect>& aCropRect,
1086                             nsIEventTarget* aMainThreadEventTarget)
1087       : DiscardableRunnable("dom::CreateImageBitmapFromBlob"),
1088         mMutex("dom::CreateImageBitmapFromBlob::mMutex"),
1089         mPromise(aPromise),
1090         mGlobalObject(aGlobal),
1091         mInputStream(std::move(aInputStream)),
1092         mCropRect(aCropRect),
1093         mOriginalCropRect(aCropRect),
1094         mMainThreadEventTarget(aMainThreadEventTarget),
1095         mThread(PR_GetCurrentThread()) {}
1096 
1097   virtual ~CreateImageBitmapFromBlob() = default;
1098 
IsCurrentThread() const1099   bool IsCurrentThread() const { return mThread == PR_GetCurrentThread(); }
1100 
1101   // Called on the owning thread.
1102   nsresult StartMimeTypeAndDecodeAndCropBlob();
1103 
1104   // Will be called when the decoding + cropping is completed on the
1105   // main-thread. This could the not the owning thread!
1106   void MimeTypeAndDecodeAndCropBlobCompletedMainThread(layers::Image* aImage,
1107                                                        nsresult aStatus);
1108 
1109   // Will be called when the decoding + cropping is completed on the owning
1110   // thread.
1111   void MimeTypeAndDecodeAndCropBlobCompletedOwningThread(layers::Image* aImage,
1112                                                          nsresult aStatus);
1113 
1114   // This is called on the main-thread only.
1115   nsresult MimeTypeAndDecodeAndCropBlob();
1116 
1117   // This is called on the main-thread only.
1118   nsresult DecodeAndCropBlob(const nsACString& aMimeType);
1119 
1120   // This is called on the main-thread only.
1121   nsresult GetMimeTypeSync(nsACString& aMimeType);
1122 
1123   // This is called on the main-thread only.
1124   nsresult GetMimeTypeAsync();
1125 
1126   Mutex mMutex;
1127 
1128   // The access to this object is protected by mutex but is always nullified on
1129   // the owning thread.
1130   RefPtr<ThreadSafeWorkerRef> mWorkerRef;
1131 
1132   // Touched only on the owning thread.
1133   RefPtr<Promise> mPromise;
1134 
1135   // Touched only on the owning thread.
1136   nsCOMPtr<nsIGlobalObject> mGlobalObject;
1137 
1138   nsCOMPtr<nsIInputStream> mInputStream;
1139   Maybe<IntRect> mCropRect;
1140   Maybe<IntRect> mOriginalCropRect;
1141   IntSize mSourceSize;
1142 
1143   nsCOMPtr<nsIEventTarget> mMainThreadEventTarget;
1144   void* mThread;
1145 };
1146 
1147 NS_IMPL_ISUPPORTS_INHERITED(CreateImageBitmapFromBlob, DiscardableRunnable,
1148                             imgIContainerCallback, nsIInputStreamCallback)
1149 
1150 class CreateImageBitmapFromBlobRunnable : public WorkerRunnable {
1151  public:
CreateImageBitmapFromBlobRunnable(WorkerPrivate * aWorkerPrivate,CreateImageBitmapFromBlob * aTask,layers::Image * aImage,nsresult aStatus)1152   explicit CreateImageBitmapFromBlobRunnable(WorkerPrivate* aWorkerPrivate,
1153                                              CreateImageBitmapFromBlob* aTask,
1154                                              layers::Image* aImage,
1155                                              nsresult aStatus)
1156       : WorkerRunnable(aWorkerPrivate),
1157         mTask(aTask),
1158         mImage(aImage),
1159         mStatus(aStatus) {}
1160 
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)1161   bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
1162     mTask->MimeTypeAndDecodeAndCropBlobCompletedOwningThread(mImage, mStatus);
1163     return true;
1164   }
1165 
1166  private:
1167   RefPtr<CreateImageBitmapFromBlob> mTask;
1168   RefPtr<layers::Image> mImage;
1169   nsresult mStatus;
1170 };
1171 
AsyncCreateImageBitmapFromBlob(Promise * aPromise,nsIGlobalObject * aGlobal,Blob & aBlob,const Maybe<IntRect> & aCropRect)1172 static void AsyncCreateImageBitmapFromBlob(Promise* aPromise,
1173                                            nsIGlobalObject* aGlobal,
1174                                            Blob& aBlob,
1175                                            const Maybe<IntRect>& aCropRect) {
1176   // Let's identify the main-thread event target.
1177   nsCOMPtr<nsIEventTarget> mainThreadEventTarget;
1178   if (NS_IsMainThread()) {
1179     mainThreadEventTarget = aGlobal->EventTargetFor(TaskCategory::Other);
1180   } else {
1181     WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
1182     MOZ_ASSERT(workerPrivate);
1183     mainThreadEventTarget = workerPrivate->MainThreadEventTarget();
1184   }
1185 
1186   RefPtr<CreateImageBitmapFromBlob> task = CreateImageBitmapFromBlob::Create(
1187       aPromise, aGlobal, aBlob, aCropRect, mainThreadEventTarget);
1188   if (NS_WARN_IF(!task)) {
1189     aPromise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
1190     return;
1191   }
1192 
1193   NS_DispatchToCurrentThread(task);
1194 }
1195 
1196 /* static */
Create(nsIGlobalObject * aGlobal,const ImageBitmapSource & aSrc,const Maybe<gfx::IntRect> & aCropRect,ErrorResult & aRv)1197 already_AddRefed<Promise> ImageBitmap::Create(
1198     nsIGlobalObject* aGlobal, const ImageBitmapSource& aSrc,
1199     const Maybe<gfx::IntRect>& aCropRect, ErrorResult& aRv) {
1200   MOZ_ASSERT(aGlobal);
1201 
1202   RefPtr<Promise> promise = Promise::Create(aGlobal, aRv);
1203 
1204   if (NS_WARN_IF(aRv.Failed())) {
1205     return nullptr;
1206   }
1207 
1208   if (aCropRect.isSome()) {
1209     if (aCropRect->Width() == 0) {
1210       aRv.ThrowRangeError(
1211           "The crop rect width passed to createImageBitmap must be nonzero");
1212       return promise.forget();
1213     }
1214 
1215     if (aCropRect->Height() == 0) {
1216       aRv.ThrowRangeError(
1217           "The crop rect height passed to createImageBitmap must be nonzero");
1218       return promise.forget();
1219     }
1220   }
1221 
1222   RefPtr<ImageBitmap> imageBitmap;
1223 
1224   if (aSrc.IsHTMLImageElement()) {
1225     MOZ_ASSERT(
1226         NS_IsMainThread(),
1227         "Creating ImageBitmap from HTMLImageElement off the main thread.");
1228     imageBitmap =
1229         CreateInternal(aGlobal, aSrc.GetAsHTMLImageElement(), aCropRect, aRv);
1230   } else if (aSrc.IsSVGImageElement()) {
1231     MOZ_ASSERT(
1232         NS_IsMainThread(),
1233         "Creating ImageBitmap from SVGImageElement off the main thread.");
1234     imageBitmap =
1235         CreateInternal(aGlobal, aSrc.GetAsSVGImageElement(), aCropRect, aRv);
1236   } else if (aSrc.IsHTMLVideoElement()) {
1237     MOZ_ASSERT(
1238         NS_IsMainThread(),
1239         "Creating ImageBitmap from HTMLVideoElement off the main thread.");
1240     imageBitmap =
1241         CreateInternal(aGlobal, aSrc.GetAsHTMLVideoElement(), aCropRect, aRv);
1242   } else if (aSrc.IsHTMLCanvasElement()) {
1243     MOZ_ASSERT(
1244         NS_IsMainThread(),
1245         "Creating ImageBitmap from HTMLCanvasElement off the main thread.");
1246     imageBitmap =
1247         CreateInternal(aGlobal, aSrc.GetAsHTMLCanvasElement(), aCropRect, aRv);
1248   } else if (aSrc.IsImageData()) {
1249     imageBitmap =
1250         CreateInternal(aGlobal, aSrc.GetAsImageData(), aCropRect, aRv);
1251   } else if (aSrc.IsCanvasRenderingContext2D()) {
1252     MOZ_ASSERT(NS_IsMainThread(),
1253                "Creating ImageBitmap from CanvasRenderingContext2D off the "
1254                "main thread.");
1255     imageBitmap = CreateInternal(aGlobal, aSrc.GetAsCanvasRenderingContext2D(),
1256                                  aCropRect, aRv);
1257   } else if (aSrc.IsImageBitmap()) {
1258     imageBitmap =
1259         CreateInternal(aGlobal, aSrc.GetAsImageBitmap(), aCropRect, aRv);
1260   } else if (aSrc.IsBlob()) {
1261     AsyncCreateImageBitmapFromBlob(promise, aGlobal, aSrc.GetAsBlob(),
1262                                    aCropRect);
1263     return promise.forget();
1264   } else {
1265     MOZ_CRASH("Unsupported type!");
1266     return nullptr;
1267   }
1268 
1269   if (!aRv.Failed()) {
1270     AsyncFulfillImageBitmapPromise(promise, imageBitmap);
1271   }
1272 
1273   return promise.forget();
1274 }
1275 
1276 /*static*/
ReadStructuredClone(JSContext * aCx,JSStructuredCloneReader * aReader,nsIGlobalObject * aParent,const nsTArray<RefPtr<DataSourceSurface>> & aClonedSurfaces,uint32_t aIndex)1277 JSObject* ImageBitmap::ReadStructuredClone(
1278     JSContext* aCx, JSStructuredCloneReader* aReader, nsIGlobalObject* aParent,
1279     const nsTArray<RefPtr<DataSourceSurface>>& aClonedSurfaces,
1280     uint32_t aIndex) {
1281   MOZ_ASSERT(aCx);
1282   MOZ_ASSERT(aReader);
1283   // aParent might be null.
1284 
1285   uint32_t picRectX_;
1286   uint32_t picRectY_;
1287   uint32_t picRectWidth_;
1288   uint32_t picRectHeight_;
1289   uint32_t alphaType_;
1290   uint32_t writeOnly;
1291 
1292   if (!JS_ReadUint32Pair(aReader, &picRectX_, &picRectY_) ||
1293       !JS_ReadUint32Pair(aReader, &picRectWidth_, &picRectHeight_) ||
1294       !JS_ReadUint32Pair(aReader, &alphaType_, &writeOnly)) {
1295     return nullptr;
1296   }
1297 
1298   int32_t picRectX = BitwiseCast<int32_t>(picRectX_);
1299   int32_t picRectY = BitwiseCast<int32_t>(picRectY_);
1300   int32_t picRectWidth = BitwiseCast<int32_t>(picRectWidth_);
1301   int32_t picRectHeight = BitwiseCast<int32_t>(picRectHeight_);
1302   const auto alphaType = BitwiseCast<gfxAlphaType>(alphaType_);
1303 
1304   // Create a new ImageBitmap.
1305   MOZ_ASSERT(!aClonedSurfaces.IsEmpty());
1306   MOZ_ASSERT(aIndex < aClonedSurfaces.Length());
1307 
1308   // RefPtr<ImageBitmap> needs to go out of scope before toObjectOrNull() is
1309   // called because the static analysis thinks dereferencing XPCOM objects
1310   // can GC (because in some cases it can!), and a return statement with a
1311   // JSObject* type means that JSObject* is on the stack as a raw pointer
1312   // while destructors are running.
1313   JS::Rooted<JS::Value> value(aCx);
1314   {
1315 #ifdef FUZZING
1316     if (aIndex >= aClonedSurfaces.Length()) {
1317       return nullptr;
1318     }
1319 #endif
1320     RefPtr<layers::Image> img = CreateImageFromSurface(aClonedSurfaces[aIndex]);
1321     RefPtr<ImageBitmap> imageBitmap =
1322         new ImageBitmap(aParent, img, !!writeOnly, alphaType);
1323 
1324     ErrorResult error;
1325     imageBitmap->SetPictureRect(
1326         IntRect(picRectX, picRectY, picRectWidth, picRectHeight), error);
1327     if (NS_WARN_IF(error.Failed())) {
1328       error.SuppressException();
1329       return nullptr;
1330     }
1331 
1332     if (!GetOrCreateDOMReflector(aCx, imageBitmap, &value)) {
1333       return nullptr;
1334     }
1335 
1336     imageBitmap->mAllocatedImageData = true;
1337   }
1338 
1339   return &(value.toObject());
1340 }
1341 
1342 /*static*/
WriteStructuredClone(JSStructuredCloneWriter * aWriter,nsTArray<RefPtr<DataSourceSurface>> & aClonedSurfaces,ImageBitmap * aImageBitmap)1343 bool ImageBitmap::WriteStructuredClone(
1344     JSStructuredCloneWriter* aWriter,
1345     nsTArray<RefPtr<DataSourceSurface>>& aClonedSurfaces,
1346     ImageBitmap* aImageBitmap) {
1347   MOZ_ASSERT(aWriter);
1348   MOZ_ASSERT(aImageBitmap);
1349 
1350   if (!aImageBitmap->mData) {
1351     // A closed image cannot be cloned.
1352     return false;
1353   }
1354 
1355   const uint32_t picRectX = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.x);
1356   const uint32_t picRectY = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.y);
1357   const uint32_t picRectWidth =
1358       BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.width);
1359   const uint32_t picRectHeight =
1360       BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.height);
1361   const uint32_t alphaType = BitwiseCast<uint32_t>(aImageBitmap->mAlphaType);
1362 
1363   // Indexing the cloned surfaces and send the index to the receiver.
1364   uint32_t index = aClonedSurfaces.Length();
1365 
1366   if (NS_WARN_IF(!JS_WriteUint32Pair(aWriter, SCTAG_DOM_IMAGEBITMAP, index)) ||
1367       NS_WARN_IF(!JS_WriteUint32Pair(aWriter, picRectX, picRectY)) ||
1368       NS_WARN_IF(!JS_WriteUint32Pair(aWriter, picRectWidth, picRectHeight)) ||
1369       NS_WARN_IF(
1370           !JS_WriteUint32Pair(aWriter, alphaType, aImageBitmap->mWriteOnly))) {
1371     return false;
1372   }
1373 
1374   RefPtr<SourceSurface> surface = aImageBitmap->mData->GetAsSourceSurface();
1375   RefPtr<DataSourceSurface> snapshot = surface->GetDataSurface();
1376   RefPtr<DataSourceSurface> dstDataSurface;
1377   {
1378     // DataSourceSurfaceD2D1::GetStride() will call EnsureMapped implicitly and
1379     // won't Unmap after exiting function. So instead calling GetStride()
1380     // directly, using ScopedMap to get stride.
1381     DataSourceSurface::ScopedMap map(snapshot, DataSourceSurface::READ);
1382     dstDataSurface = Factory::CreateDataSourceSurfaceWithStride(
1383         snapshot->GetSize(), snapshot->GetFormat(), map.GetStride(), true);
1384   }
1385   if (NS_WARN_IF(!dstDataSurface)) {
1386     return false;
1387   }
1388   Factory::CopyDataSourceSurface(snapshot, dstDataSurface);
1389   aClonedSurfaces.AppendElement(dstDataSurface);
1390   return true;
1391 }
1392 
GetAllocatedSize() const1393 size_t ImageBitmap::GetAllocatedSize() const {
1394   if (!mAllocatedImageData) {
1395     return 0;
1396   }
1397 
1398   // Calculate how many bytes are used.
1399   if (mData->GetFormat() == mozilla::ImageFormat::PLANAR_YCBCR) {
1400     return mData->AsPlanarYCbCrImage()->GetDataSize();
1401   }
1402 
1403   if (mData->GetFormat() == mozilla::ImageFormat::NV_IMAGE) {
1404     return mData->AsNVImage()->GetBufferSize();
1405   }
1406 
1407   RefPtr<SourceSurface> surface = mData->GetAsSourceSurface();
1408   const int bytesPerPixel = BytesPerPixel(surface->GetFormat());
1409   return surface->GetSize().height * surface->GetSize().width * bytesPerPixel;
1410 }
1411 
BindingJSObjectMallocBytes(ImageBitmap * aBitmap)1412 size_t BindingJSObjectMallocBytes(ImageBitmap* aBitmap) {
1413   return aBitmap->GetAllocatedSize();
1414 }
1415 
1416 /* static */
Create(Promise * aPromise,nsIGlobalObject * aGlobal,Blob & aBlob,const Maybe<IntRect> & aCropRect,nsIEventTarget * aMainThreadEventTarget)1417 already_AddRefed<CreateImageBitmapFromBlob> CreateImageBitmapFromBlob::Create(
1418     Promise* aPromise, nsIGlobalObject* aGlobal, Blob& aBlob,
1419     const Maybe<IntRect>& aCropRect, nsIEventTarget* aMainThreadEventTarget) {
1420   // Get the internal stream of the blob.
1421   nsCOMPtr<nsIInputStream> stream;
1422   ErrorResult error;
1423   aBlob.Impl()->CreateInputStream(getter_AddRefs(stream), error);
1424   if (NS_WARN_IF(error.Failed())) {
1425     return nullptr;
1426   }
1427 
1428   if (!NS_InputStreamIsBuffered(stream)) {
1429     nsCOMPtr<nsIInputStream> bufferedStream;
1430     nsresult rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream),
1431                                             stream.forget(), 4096);
1432     if (NS_WARN_IF(NS_FAILED(rv))) {
1433       return nullptr;
1434     }
1435 
1436     stream = bufferedStream;
1437   }
1438 
1439   RefPtr<CreateImageBitmapFromBlob> task = new CreateImageBitmapFromBlob(
1440       aPromise, aGlobal, stream.forget(), aCropRect, aMainThreadEventTarget);
1441 
1442   // Nothing to do for the main-thread.
1443   if (NS_IsMainThread()) {
1444     return task.forget();
1445   }
1446 
1447   // Let's use a WorkerRef to keep the worker alive if this is not the
1448   // main-thread.
1449   WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
1450   MOZ_ASSERT(workerPrivate);
1451 
1452   RefPtr<StrongWorkerRef> workerRef =
1453       StrongWorkerRef::Create(workerPrivate, "CreateImageBitmapFromBlob",
1454                               [task]() { task->WorkerShuttingDown(); });
1455   if (NS_WARN_IF(!workerRef)) {
1456     return nullptr;
1457   }
1458 
1459   task->mWorkerRef = new ThreadSafeWorkerRef(workerRef);
1460   return task.forget();
1461 }
1462 
StartMimeTypeAndDecodeAndCropBlob()1463 nsresult CreateImageBitmapFromBlob::StartMimeTypeAndDecodeAndCropBlob() {
1464   MOZ_ASSERT(IsCurrentThread());
1465 
1466   // Workers.
1467   if (!NS_IsMainThread()) {
1468     RefPtr<CreateImageBitmapFromBlob> self = this;
1469     nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
1470         "CreateImageBitmapFromBlob::MimeTypeAndDecodeAndCropBlob", [self]() {
1471           nsresult rv = self->MimeTypeAndDecodeAndCropBlob();
1472           if (NS_WARN_IF(NS_FAILED(rv))) {
1473             self->MimeTypeAndDecodeAndCropBlobCompletedMainThread(nullptr, rv);
1474           }
1475         });
1476 
1477     return mMainThreadEventTarget->Dispatch(r.forget());
1478   }
1479 
1480   // Main-thread.
1481   return MimeTypeAndDecodeAndCropBlob();
1482 }
1483 
MimeTypeAndDecodeAndCropBlob()1484 nsresult CreateImageBitmapFromBlob::MimeTypeAndDecodeAndCropBlob() {
1485   MOZ_ASSERT(NS_IsMainThread());
1486 
1487   nsAutoCString mimeType;
1488   nsresult rv = GetMimeTypeSync(mimeType);
1489   if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
1490     return GetMimeTypeAsync();
1491   }
1492 
1493   if (NS_WARN_IF(NS_FAILED(rv))) {
1494     return rv;
1495   }
1496 
1497   return DecodeAndCropBlob(mimeType);
1498 }
1499 
DecodeAndCropBlob(const nsACString & aMimeType)1500 nsresult CreateImageBitmapFromBlob::DecodeAndCropBlob(
1501     const nsACString& aMimeType) {
1502   // Get the Component object.
1503   nsCOMPtr<imgITools> imgtool = do_GetService(NS_IMGTOOLS_CID);
1504   if (NS_WARN_IF(!imgtool)) {
1505     return NS_ERROR_FAILURE;
1506   }
1507 
1508   // Decode image.
1509   nsresult rv = imgtool->DecodeImageAsync(mInputStream, aMimeType, this,
1510                                           mMainThreadEventTarget);
1511   if (NS_WARN_IF(NS_FAILED(rv))) {
1512     return rv;
1513   }
1514 
1515   return NS_OK;
1516 }
1517 
sniff_cb(nsIInputStream * aInputStream,void * aClosure,const char * aFromRawSegment,uint32_t aToOffset,uint32_t aCount,uint32_t * aWriteCount)1518 static nsresult sniff_cb(nsIInputStream* aInputStream, void* aClosure,
1519                          const char* aFromRawSegment, uint32_t aToOffset,
1520                          uint32_t aCount, uint32_t* aWriteCount) {
1521   nsACString* mimeType = static_cast<nsACString*>(aClosure);
1522   MOZ_ASSERT(mimeType);
1523 
1524   if (aCount > 0) {
1525     imgLoader::GetMimeTypeFromContent(aFromRawSegment, aCount, *mimeType);
1526   }
1527 
1528   *aWriteCount = 0;
1529 
1530   // We don't want to consume data from the stream.
1531   return NS_ERROR_FAILURE;
1532 }
1533 
GetMimeTypeSync(nsACString & aMimeType)1534 nsresult CreateImageBitmapFromBlob::GetMimeTypeSync(nsACString& aMimeType) {
1535   uint32_t dummy;
1536   return mInputStream->ReadSegments(sniff_cb, &aMimeType, 128, &dummy);
1537 }
1538 
GetMimeTypeAsync()1539 nsresult CreateImageBitmapFromBlob::GetMimeTypeAsync() {
1540   nsCOMPtr<nsIAsyncInputStream> asyncInputStream =
1541       do_QueryInterface(mInputStream);
1542   if (NS_WARN_IF(!asyncInputStream)) {
1543     // If the stream is not async, why are we here?
1544     return NS_ERROR_FAILURE;
1545   }
1546 
1547   return asyncInputStream->AsyncWait(this, 0, 128, mMainThreadEventTarget);
1548 }
1549 
1550 NS_IMETHODIMP
OnInputStreamReady(nsIAsyncInputStream * aStream)1551 CreateImageBitmapFromBlob::OnInputStreamReady(nsIAsyncInputStream* aStream) {
1552   // The stream should have data now. Let's start from scratch again.
1553   nsresult rv = MimeTypeAndDecodeAndCropBlob();
1554   if (NS_WARN_IF(NS_FAILED(rv))) {
1555     MimeTypeAndDecodeAndCropBlobCompletedMainThread(nullptr, rv);
1556   }
1557 
1558   return NS_OK;
1559 }
1560 
1561 NS_IMETHODIMP
OnImageReady(imgIContainer * aImgContainer,nsresult aStatus)1562 CreateImageBitmapFromBlob::OnImageReady(imgIContainer* aImgContainer,
1563                                         nsresult aStatus) {
1564   MOZ_ASSERT(NS_IsMainThread());
1565 
1566   if (NS_FAILED(aStatus)) {
1567     MimeTypeAndDecodeAndCropBlobCompletedMainThread(nullptr, aStatus);
1568     return NS_OK;
1569   }
1570 
1571   MOZ_ASSERT(aImgContainer);
1572 
1573   // Get the surface out.
1574   uint32_t frameFlags =
1575       imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY;
1576   uint32_t whichFrame = imgIContainer::FRAME_FIRST;
1577   RefPtr<SourceSurface> surface =
1578       aImgContainer->GetFrame(whichFrame, frameFlags);
1579 
1580   if (NS_WARN_IF(!surface)) {
1581     MimeTypeAndDecodeAndCropBlobCompletedMainThread(
1582         nullptr, NS_ERROR_DOM_INVALID_STATE_ERR);
1583     return NS_OK;
1584   }
1585 
1586   // Store the sourceSize value for the
1587   // MimeTypeAndDecodeAndCropBlobCompletedMainThread call.
1588   mSourceSize = surface->GetSize();
1589 
1590   // Crop the source surface if needed.
1591   RefPtr<SourceSurface> croppedSurface = surface;
1592 
1593   if (mCropRect.isSome()) {
1594     // The blob is just decoded into a RasterImage and not optimized yet, so the
1595     // _surface_ we get is a DataSourceSurface which wraps the RasterImage's
1596     // raw buffer.
1597     //
1598     // The _surface_ might already be optimized so that its type is not
1599     // SurfaceType::DATA. However, we could keep using the generic cropping and
1600     // copying since the decoded buffer is only used in this ImageBitmap so we
1601     // should crop it to save memory usage.
1602     //
1603     // TODO: Bug1189632 is going to refactor this create-from-blob part to
1604     //       decode the blob off the main thread. Re-check if we should do
1605     //       cropping at this moment again there.
1606     RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface();
1607     croppedSurface = CropAndCopyDataSourceSurface(dataSurface, mCropRect.ref());
1608     mCropRect->MoveTo(0, 0);
1609   }
1610 
1611   if (NS_WARN_IF(!croppedSurface)) {
1612     MimeTypeAndDecodeAndCropBlobCompletedMainThread(
1613         nullptr, NS_ERROR_DOM_INVALID_STATE_ERR);
1614     return NS_OK;
1615   }
1616 
1617   // Create an Image from the source surface.
1618   RefPtr<layers::Image> image = CreateImageFromSurface(croppedSurface);
1619 
1620   if (NS_WARN_IF(!image)) {
1621     MimeTypeAndDecodeAndCropBlobCompletedMainThread(
1622         nullptr, NS_ERROR_DOM_INVALID_STATE_ERR);
1623     return NS_OK;
1624   }
1625 
1626   MimeTypeAndDecodeAndCropBlobCompletedMainThread(image, NS_OK);
1627   return NS_OK;
1628 }
1629 
MimeTypeAndDecodeAndCropBlobCompletedMainThread(layers::Image * aImage,nsresult aStatus)1630 void CreateImageBitmapFromBlob::MimeTypeAndDecodeAndCropBlobCompletedMainThread(
1631     layers::Image* aImage, nsresult aStatus) {
1632   MOZ_ASSERT(NS_IsMainThread());
1633 
1634   if (!IsCurrentThread()) {
1635     MutexAutoLock lock(mMutex);
1636 
1637     if (!mWorkerRef) {
1638       // The worker is already gone.
1639       return;
1640     }
1641 
1642     RefPtr<CreateImageBitmapFromBlobRunnable> r =
1643         new CreateImageBitmapFromBlobRunnable(mWorkerRef->Private(), this,
1644                                               aImage, aStatus);
1645     r->Dispatch();
1646     return;
1647   }
1648 
1649   MimeTypeAndDecodeAndCropBlobCompletedOwningThread(aImage, aStatus);
1650 }
1651 
1652 void CreateImageBitmapFromBlob::
MimeTypeAndDecodeAndCropBlobCompletedOwningThread(layers::Image * aImage,nsresult aStatus)1653     MimeTypeAndDecodeAndCropBlobCompletedOwningThread(layers::Image* aImage,
1654                                                       nsresult aStatus) {
1655   MOZ_ASSERT(IsCurrentThread());
1656 
1657   if (!mPromise) {
1658     // The worker is going to be released soon. No needs to continue.
1659     return;
1660   }
1661 
1662   // Let's release what has to be released on the owning thread.
1663   auto raii = MakeScopeExit([&] {
1664     // Doing this we also release the worker.
1665     mWorkerRef = nullptr;
1666 
1667     mPromise = nullptr;
1668     mGlobalObject = nullptr;
1669   });
1670 
1671   if (NS_WARN_IF(NS_FAILED(aStatus))) {
1672     mPromise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
1673     return;
1674   }
1675 
1676   // Create ImageBitmap object.
1677   RefPtr<ImageBitmap> imageBitmap =
1678       new ImageBitmap(mGlobalObject, aImage, false /* write-only */);
1679 
1680   if (mCropRect.isSome()) {
1681     ErrorResult rv;
1682     imageBitmap->SetPictureRect(mCropRect.ref(), rv);
1683 
1684     if (rv.Failed()) {
1685       mPromise->MaybeReject(std::move(rv));
1686       return;
1687     }
1688   }
1689 
1690   imageBitmap->mAllocatedImageData = true;
1691 
1692   mPromise->MaybeResolve(imageBitmap);
1693 }
1694 
WorkerShuttingDown()1695 void CreateImageBitmapFromBlob::WorkerShuttingDown() {
1696   MOZ_ASSERT(IsCurrentThread());
1697 
1698   MutexAutoLock lock(mMutex);
1699 
1700   // Let's release all the non-thread-safe objects now.
1701   mWorkerRef = nullptr;
1702   mPromise = nullptr;
1703   mGlobalObject = nullptr;
1704 }
1705 
1706 }  // namespace mozilla::dom
1707