Bug 1141979 - part11 - handle cases that mapDataInto() should throw; r=jrmuizel
authorKaku Kuo <tkuo@mozilla.com>
Wed, 16 Mar 2016 12:01:32 +0800
changeset 339149 3d688790126494b8cfc4ba34d91cdea862f061c4
parent 339148 88544efdd4bdce59e1f122af3da5925e9a82fe26
child 339150 603da76d89e7d2b1b89f5205c68b28edfecc970c
push id6249
push userjlund@mozilla.com
push dateMon, 01 Aug 2016 13:59:36 +0000
treeherdermozilla-beta@bad9d4f5bf7e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs1141979
milestone49.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1141979 - part11 - handle cases that mapDataInto() should throw; r=jrmuizel MozReview-Commit-ID: JuPj54fNB3s
dom/canvas/ImageBitmap.cpp
dom/canvas/ImageBitmap.h
--- a/dom/canvas/ImageBitmap.cpp
+++ b/dom/canvas/ImageBitmap.cpp
@@ -395,16 +395,17 @@ HasRasterImage(HTMLImageElement& aImageE
 ImageBitmap::ImageBitmap(nsIGlobalObject* aGlobal, layers::Image* aData,
                          bool aIsPremultipliedAlpha /* = true */)
   : mParent(aGlobal)
   , mData(aData)
   , mSurface(nullptr)
   , mDataWrapper(new ImageUtils(mData))
   , mPictureRect(0, 0, aData->GetSize().width, aData->GetSize().height)
   , mIsPremultipliedAlpha(aIsPremultipliedAlpha)
+  , mIsCroppingAreaOutSideOfSourceImage(false)
 {
   MOZ_ASSERT(aData, "aData is null in ImageBitmap constructor.");
 }
 
 ImageBitmap::~ImageBitmap()
 {
 }
 
@@ -423,16 +424,33 @@ ImageBitmap::Close()
 }
 
 void
 ImageBitmap::SetPictureRect(const IntRect& aRect, ErrorResult& aRv)
 {
   mPictureRect = FixUpNegativeDimension(aRect, aRv);
 }
 
+void
+ImageBitmap::SetIsCroppingAreaOutSideOfSourceImage(const IntSize& aSourceSize,
+                                                   const Maybe<IntRect>& aCroppingRect)
+{
+  // No cropping at all.
+  if (aCroppingRect.isNothing()) {
+    mIsCroppingAreaOutSideOfSourceImage = false;
+    return;
+  }
+
+  if (aCroppingRect->X() < 0 || aCroppingRect->Y() < 0 ||
+      aCroppingRect->Width() > aSourceSize.width ||
+      aCroppingRect->Height() > aSourceSize.height) {
+    mIsCroppingAreaOutSideOfSourceImage = true;
+  }
+}
+
 static already_AddRefed<SourceSurface>
 ConvertColorFormatIfNeeded(RefPtr<SourceSurface> aSurface)
 {
   const SurfaceFormat srcFormat = aSurface->GetFormat();
   if (srcFormat == SurfaceFormat::R8G8B8A8 ||
       srcFormat == SurfaceFormat::B8G8R8A8 ||
       srcFormat == SurfaceFormat::R8G8B8X8 ||
       srcFormat == SurfaceFormat::B8G8R8X8 ||
@@ -666,32 +684,36 @@ ImageBitmap::TransferAsImage()
 }
 
 ImageBitmapCloneData*
 ImageBitmap::ToCloneData()
 {
   ImageBitmapCloneData* result = new ImageBitmapCloneData();
   result->mPictureRect = mPictureRect;
   result->mIsPremultipliedAlpha = mIsPremultipliedAlpha;
+  result->mIsCroppingAreaOutSideOfSourceImage = mIsCroppingAreaOutSideOfSourceImage;
   RefPtr<SourceSurface> surface = mData->GetAsSourceSurface();
   result->mSurface = surface->GetDataSurface();
   MOZ_ASSERT(result->mSurface);
 
   return result;
 }
 
 /* static */ already_AddRefed<ImageBitmap>
 ImageBitmap::CreateFromCloneData(nsIGlobalObject* aGlobal,
                                  ImageBitmapCloneData* aData)
 {
-  RefPtr<layers::Image> data =
-    CreateImageFromSurface(aData->mSurface);
+  RefPtr<layers::Image> data = CreateImageFromSurface(aData->mSurface);
 
   RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data,
                                             aData->mIsPremultipliedAlpha);
+
+  ret->mIsCroppingAreaOutSideOfSourceImage =
+    aData->mIsCroppingAreaOutSideOfSourceImage;
+
   ErrorResult rv;
   ret->SetPictureRect(aData->mPictureRect, rv);
   return ret.forget();
 }
 
 /* static */ already_AddRefed<ImageBitmap>
 ImageBitmap::CreateFromOffscreenCanvas(nsIGlobalObject* aGlobal,
                                        OffscreenCanvas& aOffscreenCanvas,
@@ -755,16 +777,19 @@ ImageBitmap::CreateInternal(nsIGlobalObj
 
   RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data);
 
   // Set the picture rectangle.
   if (ret && aCropRect.isSome()) {
     ret->SetPictureRect(aCropRect.ref(), aRv);
   }
 
+  // Set mIsCroppingAreaOutSideOfSourceImage.
+  ret->SetIsCroppingAreaOutSideOfSourceImage(surface->GetSize(), aCropRect);
+
   return ret.forget();
 }
 
 /* static */ already_AddRefed<ImageBitmap>
 ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, HTMLVideoElement& aVideoEl,
                             const Maybe<IntRect>& aCropRect, ErrorResult& aRv)
 {
   // Check network state.
@@ -800,16 +825,19 @@ ImageBitmap::CreateInternal(nsIGlobalObj
   layers::Image* data = lockImage.GetImage();
   RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data);
 
   // Set the picture rectangle.
   if (ret && aCropRect.isSome()) {
     ret->SetPictureRect(aCropRect.ref(), aRv);
   }
 
+  // Set mIsCroppingAreaOutSideOfSourceImage.
+  ret->SetIsCroppingAreaOutSideOfSourceImage(data->GetSize(), aCropRect);
+
   return ret.forget();
 }
 
 /* static */ already_AddRefed<ImageBitmap>
 ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, HTMLCanvasElement& aCanvasEl,
                             const Maybe<IntRect>& aCropRect, ErrorResult& aRv)
 {
   if (aCanvasEl.Width() == 0 || aCanvasEl.Height() == 0) {
@@ -860,16 +888,19 @@ ImageBitmap::CreateInternal(nsIGlobalObj
 
   RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data);
 
   // Set the picture rectangle.
   if (ret && aCropRect.isSome()) {
     ret->SetPictureRect(cropRect, aRv);
   }
 
+  // Set mIsCroppingAreaOutSideOfSourceImage.
+  ret->SetIsCroppingAreaOutSideOfSourceImage(surface->GetSize(), aCropRect);
+
   return ret.forget();
 }
 
 /* static */ already_AddRefed<ImageBitmap>
 ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, ImageData& aImageData,
                             const Maybe<IntRect>& aCropRect, ErrorResult& aRv)
 {
   // Copy data into SourceSurface.
@@ -918,16 +949,19 @@ ImageBitmap::CreateInternal(nsIGlobalObj
 
   // Create an ImageBimtap.
   // ImageData's underlying data is not alpha-premultiplied.
   RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, false);
 
   // The cropping information has been handled in the CreateImageFromRawData()
   // function.
 
+  // Set mIsCroppingAreaOutSideOfSourceImage.
+  ret->SetIsCroppingAreaOutSideOfSourceImage(imageSize, aCropRect);
+
   return ret.forget();
 }
 
 /* static */ already_AddRefed<ImageBitmap>
 ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, CanvasRenderingContext2D& aCanvasCtx,
                             const Maybe<IntRect>& aCropRect, ErrorResult& aRv)
 {
   // Check origin-clean.
@@ -958,16 +992,19 @@ ImageBitmap::CreateInternal(nsIGlobalObj
 
   RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data);
 
   // Set the picture rectangle.
   if (ret && aCropRect.isSome()) {
     ret->SetPictureRect(aCropRect.ref(), aRv);
   }
 
+  // Set mIsCroppingAreaOutSideOfSourceImage.
+  ret->SetIsCroppingAreaOutSideOfSourceImage(surface->GetSize(), aCropRect);
+
   return ret.forget();
 }
 
 /* static */ already_AddRefed<ImageBitmap>
 ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, ImageBitmap& aImageBitmap,
                             const Maybe<IntRect>& aCropRect, ErrorResult& aRv)
 {
   if (!aImageBitmap.mData) {
@@ -978,16 +1015,24 @@ ImageBitmap::CreateInternal(nsIGlobalObj
   RefPtr<layers::Image> data = aImageBitmap.mData;
   RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, aImageBitmap.mIsPremultipliedAlpha);
 
   // Set the picture rectangle.
   if (ret && aCropRect.isSome()) {
     ret->SetPictureRect(aCropRect.ref(), aRv);
   }
 
+  // Set mIsCroppingAreaOutSideOfSourceImage.
+  if (aImageBitmap.mIsCroppingAreaOutSideOfSourceImage == true) {
+    ret->mIsCroppingAreaOutSideOfSourceImage = true;
+  } else {
+    ret->SetIsCroppingAreaOutSideOfSourceImage(aImageBitmap.mPictureRect.Size(),
+                                               aCropRect);
+  }
+
   return ret.forget();
 }
 
 class FulfillImageBitmapPromise
 {
 protected:
   FulfillImageBitmapPromise(Promise* aPromise, ImageBitmap* aImageBitmap)
   : mPromise(aPromise)
@@ -1091,25 +1136,29 @@ DecodeBlob(Blob& aBlob)
   if (NS_WARN_IF(!surface)) {
     return nullptr;
   }
 
   return surface.forget();
 }
 
 static already_AddRefed<layers::Image>
-DecodeAndCropBlob(Blob& aBlob, Maybe<IntRect>& aCropRect)
+DecodeAndCropBlob(Blob& aBlob, Maybe<IntRect>& aCropRect,
+                  /*Output*/ IntSize& sourceSize)
 {
   // Decode the blob into a SourceSurface.
   RefPtr<SourceSurface> surface = DecodeBlob(aBlob);
 
   if (NS_WARN_IF(!surface)) {
     return nullptr;
   }
 
+  // Set the _sourceSize_ output parameter.
+  sourceSize = surface->GetSize();
+
   // Crop the source surface if needed.
   RefPtr<SourceSurface> croppedSurface = surface;
 
   if (aCropRect.isSome()) {
     // The blob is just decoded into a RasterImage and not optimized yet, so the
     // _surface_ we get is a DataSourceSurface which wraps the RasterImage's
     // raw buffer.
     //
@@ -1212,65 +1261,80 @@ public:
   {
     DoCreateImageBitmapFromBlob();
     return NS_OK;
   }
 
 private:
   already_AddRefed<ImageBitmap> CreateImageBitmap() override
   {
-    RefPtr<layers::Image> data = DecodeAndCropBlob(*mBlob, mCropRect);
+    // _sourceSize_ is used to get the original size of the source image,
+    // before being cropped.
+    IntSize sourceSize;
+
+    // Keep the orignal cropping rectangle because the mCropRect might be
+    // modified in DecodeAndCropBlob().
+    Maybe<IntRect> originalCropRect = mCropRect;
+
+    RefPtr<layers::Image> data = DecodeAndCropBlob(*mBlob, mCropRect, sourceSize);
 
     if (NS_WARN_IF(!data)) {
       mPromise->MaybeRejectWithNull();
       return nullptr;
     }
 
     // Create ImageBitmap object.
     RefPtr<ImageBitmap> imageBitmap = new ImageBitmap(mGlobalObject, data);
+
+    // Set mIsCroppingAreaOutSideOfSourceImage.
+    imageBitmap->SetIsCroppingAreaOutSideOfSourceImage(sourceSize, originalCropRect);
+
     return imageBitmap.forget();
   }
 };
 
 class CreateImageBitmapFromBlobWorkerTask final : public WorkerSameThreadRunnable,
                                                   public CreateImageBitmapFromBlob
 {
   // This is a synchronous task.
   class DecodeBlobInMainThreadSyncTask final : public WorkerMainThreadRunnable
   {
   public:
     DecodeBlobInMainThreadSyncTask(WorkerPrivate* aWorkerPrivate,
                                    Blob& aBlob,
                                    Maybe<IntRect>& aCropRect,
-                                   layers::Image** aImage)
+                                   layers::Image** aImage,
+                                   IntSize& aSourceSize)
     : WorkerMainThreadRunnable(aWorkerPrivate,
                                NS_LITERAL_CSTRING("ImageBitmap :: Create Image from Blob"))
     , mBlob(aBlob)
     , mCropRect(aCropRect)
     , mImage(aImage)
+    , mSourceSize(aSourceSize)
     {
     }
 
     bool MainThreadRun() override
     {
-      RefPtr<layers::Image> image = DecodeAndCropBlob(mBlob, mCropRect);
+      RefPtr<layers::Image> image = DecodeAndCropBlob(mBlob, mCropRect, mSourceSize);
 
       if (NS_WARN_IF(!image)) {
         return true;
       }
 
       image.forget(mImage);
 
       return true;
     }
 
   private:
     Blob& mBlob;
     Maybe<IntRect>& mCropRect;
     layers::Image** mImage;
+    IntSize mSourceSize;
   };
 
 public:
   CreateImageBitmapFromBlobWorkerTask(Promise* aPromise,
                                   nsIGlobalObject* aGlobal,
                                   mozilla::dom::Blob& aBlob,
                                   const Maybe<IntRect>& aCropRect)
   : WorkerSameThreadRunnable(GetCurrentThreadWorkerPrivate()),
@@ -1281,37 +1345,49 @@ public:
   bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
     return DoCreateImageBitmapFromBlob();
   }
 
 private:
   already_AddRefed<ImageBitmap> CreateImageBitmap() override
   {
+    // _sourceSize_ is used to get the original size of the source image,
+    // before being cropped.
+    IntSize sourceSize;
+
+    // Keep the orignal cropping rectangle because the mCropRect might be
+    // modified in DecodeAndCropBlob().
+    Maybe<IntRect> originalCropRect = mCropRect;
+
     RefPtr<layers::Image> data;
 
     ErrorResult rv;
     RefPtr<DecodeBlobInMainThreadSyncTask> task =
       new DecodeBlobInMainThreadSyncTask(mWorkerPrivate, *mBlob, mCropRect,
-                                         getter_AddRefs(data));
+                                         getter_AddRefs(data), sourceSize);
     task->Dispatch(rv); // This is a synchronous call.
 
     if (NS_WARN_IF(rv.Failed())) {
       // XXXbz does this really make sense if we're shutting down?  Ah, well.
       mPromise->MaybeReject(rv);
       return nullptr;
     }
 
     if (NS_WARN_IF(!data)) {
       mPromise->MaybeRejectWithNull();
       return nullptr;
     }
 
     // Create ImageBitmap object.
     RefPtr<ImageBitmap> imageBitmap = new ImageBitmap(mGlobalObject, data);
+
+    // Set mIsCroppingAreaOutSideOfSourceImage.
+    imageBitmap->SetIsCroppingAreaOutSideOfSourceImage(sourceSize, originalCropRect);
+
     return imageBitmap.forget();
   }
 
 };
 
 static void
 AsyncCreateImageBitmapFromBlob(Promise* aPromise, nsIGlobalObject* aGlobal,
                                Blob& aBlob, const Maybe<IntRect>& aCropRect)
@@ -1391,21 +1467,22 @@ ImageBitmap::ReadStructuredClone(JSConte
   MOZ_ASSERT(aReader);
   // aParent might be null.
 
   uint32_t picRectX_;
   uint32_t picRectY_;
   uint32_t picRectWidth_;
   uint32_t picRectHeight_;
   uint32_t isPremultipliedAlpha_;
-  uint32_t dummy_;
+  uint32_t isCroppingAreaOutSideOfSourceImage_;
 
   if (!JS_ReadUint32Pair(aReader, &picRectX_, &picRectY_) ||
       !JS_ReadUint32Pair(aReader, &picRectWidth_, &picRectHeight_) ||
-      !JS_ReadUint32Pair(aReader, &isPremultipliedAlpha_, &dummy_)) {
+      !JS_ReadUint32Pair(aReader, &isPremultipliedAlpha_,
+                                  &isCroppingAreaOutSideOfSourceImage_)) {
     return nullptr;
   }
 
   int32_t picRectX = BitwiseCast<int32_t>(picRectX_);
   int32_t picRectY = BitwiseCast<int32_t>(picRectY_);
   int32_t picRectWidth = BitwiseCast<int32_t>(picRectWidth_);
   int32_t picRectHeight = BitwiseCast<int32_t>(picRectHeight_);
 
@@ -1419,16 +1496,19 @@ ImageBitmap::ReadStructuredClone(JSConte
   // JSObject* type means that JSObject* is on the stack as a raw pointer
   // while destructors are running.
   JS::Rooted<JS::Value> value(aCx);
   {
     RefPtr<layers::Image> img = CreateImageFromSurface(aClonedSurfaces[aIndex]);
     RefPtr<ImageBitmap> imageBitmap =
       new ImageBitmap(aParent, img, isPremultipliedAlpha_);
 
+    imageBitmap->mIsCroppingAreaOutSideOfSourceImage =
+      isCroppingAreaOutSideOfSourceImage_;
+
     ErrorResult error;
     imageBitmap->SetPictureRect(IntRect(picRectX, picRectY,
                                         picRectWidth, picRectHeight), error);
     if (NS_WARN_IF(error.Failed())) {
       error.SuppressException();
       return nullptr;
     }
 
@@ -1448,24 +1528,26 @@ ImageBitmap::WriteStructuredClone(JSStru
   MOZ_ASSERT(aWriter);
   MOZ_ASSERT(aImageBitmap);
 
   const uint32_t picRectX = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.x);
   const uint32_t picRectY = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.y);
   const uint32_t picRectWidth = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.width);
   const uint32_t picRectHeight = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.height);
   const uint32_t isPremultipliedAlpha = aImageBitmap->mIsPremultipliedAlpha ? 1 : 0;
+  const uint32_t isCroppingAreaOutSideOfSourceImage = aImageBitmap->mIsCroppingAreaOutSideOfSourceImage ? 1 : 0;
 
   // Indexing the cloned surfaces and send the index to the receiver.
   uint32_t index = aClonedSurfaces.Length();
 
   if (NS_WARN_IF(!JS_WriteUint32Pair(aWriter, SCTAG_DOM_IMAGEBITMAP, index)) ||
       NS_WARN_IF(!JS_WriteUint32Pair(aWriter, picRectX, picRectY)) ||
       NS_WARN_IF(!JS_WriteUint32Pair(aWriter, picRectWidth, picRectHeight)) ||
-      NS_WARN_IF(!JS_WriteUint32Pair(aWriter, isPremultipliedAlpha, 0))) {
+      NS_WARN_IF(!JS_WriteUint32Pair(aWriter, isPremultipliedAlpha,
+                                              isCroppingAreaOutSideOfSourceImage))) {
     return false;
   }
 
   RefPtr<SourceSurface> surface =
     aImageBitmap->mData->GetAsSourceSurface();
   RefPtr<DataSourceSurface> snapshot = surface->GetDataSurface();
   RefPtr<DataSourceSurface> dstDataSurface;
   {
@@ -1712,16 +1794,39 @@ ImageBitmap::MapDataInto(JSContext* aCx,
   MOZ_ASSERT(aCx, "No JSContext while calling ImageBitmap::MapDataInto().");
 
   RefPtr<Promise> promise = Promise::Create(mParent, aRv);
 
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
+  // Check for cases that should throws.
+  // Case 1:
+  // If image bitmap was cropped to the source rectangle so that it contains any
+  // transparent black pixels (cropping area is outside of the source image),
+  // then reject promise with IndexSizeError and abort these steps.
+  if (mIsCroppingAreaOutSideOfSourceImage) {
+    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
+    return promise.forget();
+  }
+
+  // Case 2:
+  // If the image bitmap is going to be accessed in YUV422/YUV422 series with a
+  // cropping area starts at an odd x or y coordinate.
+  if (aFormat == ImageBitmapFormat::YUV422P ||
+      aFormat == ImageBitmapFormat::YUV420P ||
+      aFormat == ImageBitmapFormat::YUV420SP_NV12 ||
+      aFormat == ImageBitmapFormat::YUV420SP_NV21) {
+    if ((mPictureRect.x & 1) || (mPictureRect.y & 1)) {
+      aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
+      return promise.forget();
+    }
+  }
+
   AsyncMapDataIntoBufferSource(aCx, promise, this, aBuffer, aOffset, aFormat);
   return promise.forget();
 }
 
 // ImageBitmapFactories extensions.
 static SurfaceFormat
 ImageFormatToSurfaceFromat(mozilla::dom::ImageBitmapFormat aFormat)
 {
@@ -1997,15 +2102,19 @@ ImageBitmap::Create(nsIGlobalObject* aGl
   // Create an ImageBimtap.
   // Assume the data from an external buffer is not alpha-premultiplied.
   RefPtr<ImageBitmap> imageBitmap = new ImageBitmap(aGlobal, data, false);
 
   // We don't need to call SetPictureRect() here because there is no cropping
   // supported and the ImageBitmap's mPictureRect is the size of the source
   // image in default
 
+  // We don't need to set mIsCroppingAreaOutSideOfSourceImage here because there
+  // is no cropping supported and the mIsCroppingAreaOutSideOfSourceImage is
+  // false in default.
+
   AsyncFulfillImageBitmapPromise(promise, imageBitmap);
 
   return promise.forget();
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/canvas/ImageBitmap.h
+++ b/dom/canvas/ImageBitmap.h
@@ -58,16 +58,17 @@ template<typename T> class MapDataIntoBu
 class Promise;
 class PostMessageEvent; // For StructuredClone between windows.
 
 struct ImageBitmapCloneData final
 {
   RefPtr<gfx::DataSourceSurface> mSurface;
   gfx::IntRect mPictureRect;
   bool mIsPremultipliedAlpha;
+  bool mIsCroppingAreaOutSideOfSourceImage;
 };
 
 /*
  * ImageBitmap is an opaque handler to several kinds of image-like objects from
  * HTMLImageElement, HTMLVideoElement, HTMLCanvasElement, ImageData to
  * CanvasRenderingContext2D and Image Blob.
  *
  * An ImageBitmap could be painted to a canvas element.
@@ -196,16 +197,19 @@ protected:
    */
   ImageBitmap(nsIGlobalObject* aGlobal, layers::Image* aData,
               bool aIsPremultipliedAlpha = true);
 
   virtual ~ImageBitmap();
 
   void SetPictureRect(const gfx::IntRect& aRect, ErrorResult& aRv);
 
+  void SetIsCroppingAreaOutSideOfSourceImage(const gfx::IntSize& aSourceSize,
+                                             const Maybe<gfx::IntRect>& aCroppingRect);
+
   static already_AddRefed<ImageBitmap>
   CreateInternal(nsIGlobalObject* aGlobal, HTMLImageElement& aImageEl,
                  const Maybe<gfx::IntRect>& aCropRect, ErrorResult& aRv);
 
   static already_AddRefed<ImageBitmap>
   CreateInternal(nsIGlobalObject* aGlobal, HTMLVideoElement& aVideoEl,
                  const Maybe<gfx::IntRect>& aCropRect, ErrorResult& aRv);
 
@@ -260,16 +264,26 @@ protected:
    * image, then this mPictureRect is just the size of the final mData.
    *
    * The mPictureRect will be used at PrepareForDrawTarget() while user is going
    * to draw this ImageBitmap into a HTMLCanvasElement.
    */
   gfx::IntRect mPictureRect;
 
   const bool mIsPremultipliedAlpha;
+
+  /*
+   * Set mIsCroppingAreaOutSideOfSourceImage if image bitmap was cropped to the
+   * source rectangle so that it contains any transparent black pixels (cropping
+   * area is outside of the source image).
+   * This is used in mapDataInto() to check if we should reject promise with
+   * IndexSizeError.
+   */
+  bool mIsCroppingAreaOutSideOfSourceImage;
+
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_ImageBitmap_h