Bug 1141979 - part11 - handle cases that mapDataInto() should throw; r=jrmuizel draft
authorKaku Kuo <tkuo@mozilla.com>
Wed, 16 Mar 2016 12:01:32 +0800
changeset 373850 555760b434b27bb275065527609a48bfbdfd25b7
parent 373849 dbb63b5c5215c6edf92cc954b2cb3fc79a5d6331
child 373851 a74ef02529d7b7c3fa26ebab599b9446ecaffe3d
push id19853
push usertkuo@mozilla.com
push dateWed, 01 Jun 2016 09:17:41 +0000
reviewersjrmuizel
bugs1141979
milestone49.0a1
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