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