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