--- a/dom/canvas/ImageBitmap.cpp
+++ b/dom/canvas/ImageBitmap.cpp
@@ -30,16 +30,55 @@ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Im
NS_IMPL_CYCLE_COLLECTING_ADDREF(ImageBitmap)
NS_IMPL_CYCLE_COLLECTING_RELEASE(ImageBitmap)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ImageBitmap)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
/*
+ * This helper function is used to notify DOM that aBytes memory is allocated
+ * here so that we could trigger GC appropriately.
+ */
+static void
+RegisterAllocation(nsIGlobalObject* aGlobal, size_t aBytes)
+{
+ AutoJSAPI jsapi;
+ if (jsapi.Init(aGlobal)) {
+ JS_updateMallocCounter(jsapi.cx(), aBytes);
+ }
+}
+
+static void
+RegisterAllocation(nsIGlobalObject* aGlobal, SourceSurface* aSurface)
+{
+ // Calculate how many bytes are used.
+ const int bytesPerPixel = BytesPerPixel(aSurface->GetFormat());
+ const size_t bytes =
+ aSurface->GetSize().height * aSurface->GetSize().width * bytesPerPixel;
+
+ // Register.
+ RegisterAllocation(aGlobal, bytes);
+}
+
+static void
+RegisterAllocation(nsIGlobalObject* aGlobal, layers::Image* aImage)
+{
+ // Calculate how many bytes are used.
+ if (aImage->GetFormat() == mozilla::ImageFormat::PLANAR_YCBCR) {
+ RegisterAllocation(aGlobal, aImage->AsPlanarYCbCrImage()->GetDataSize());
+ } else if (aImage->GetFormat() == mozilla::ImageFormat::NV_IMAGE) {
+ RegisterAllocation(aGlobal, aImage->AsNVImage()->GetBufferSize());
+ } else {
+ RefPtr<SourceSurface> surface = aImage->GetAsSourceSurface();
+ RegisterAllocation(aGlobal, surface);
+ }
+}
+
+/*
* If either aRect.width or aRect.height are negative, then return a new IntRect
* which represents the same rectangle as the aRect does but with positive width
* and height.
*/
static IntRect
FixUpNegativeDimension(const IntRect& aRect, ErrorResult& aRv)
{
gfx::IntRect rect = aRect;
@@ -702,16 +741,19 @@ ImageBitmap::ToCloneData()
ImageBitmap::CreateFromCloneData(nsIGlobalObject* aGlobal,
ImageBitmapCloneData* aData)
{
RefPtr<layers::Image> data = CreateImageFromSurface(aData->mSurface);
RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data,
aData->mIsPremultipliedAlpha);
+ // Report memory allocation.
+ RegisterAllocation(aGlobal, aData->mSurface);
+
ret->mIsCroppingAreaOutSideOfSourceImage =
aData->mIsCroppingAreaOutSideOfSourceImage;
ErrorResult rv;
ret->SetPictureRect(aData->mPictureRect, rv);
return ret.forget();
}
@@ -736,16 +778,20 @@ ImageBitmap::CreateFromOffscreenCanvas(n
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return nullptr;
}
RefPtr<layers::Image> data =
CreateImageFromSurface(surface);
RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data);
+
+ // Report memory allocation.
+ RegisterAllocation(aGlobal, surface);
+
return ret.forget();
}
/* static */ already_AddRefed<ImageBitmap>
ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, HTMLImageElement& aImageEl,
const Maybe<IntRect>& aCropRect, ErrorResult& aRv)
{
// Check if the image element is completely available or not.
@@ -860,26 +906,28 @@ ImageBitmap::CreateInternal(nsIGlobalObj
// Crop the source surface if needed.
RefPtr<SourceSurface> croppedSurface;
IntRect cropRect = aCropRect.valueOr(IntRect());
// If the HTMLCanvasElement's rendering context is WebGL, then the snapshot
// we got from the HTMLCanvasElement is a DataSourceSurface which is a copy
// of the rendering context. We handle cropping in this case.
+ bool needToReportMemoryAllocation = false;
if ((aCanvasEl.GetCurrentContextType() == CanvasContextType::WebGL1 ||
aCanvasEl.GetCurrentContextType() == CanvasContextType::WebGL2) &&
aCropRect.isSome()) {
// The _surface_ must be a DataSourceSurface.
MOZ_ASSERT(surface->GetType() == SurfaceType::DATA,
"The snapshot SourceSurface from WebGL rendering contest is not \
DataSourceSurface.");
RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface();
croppedSurface = CropAndCopyDataSourceSurface(dataSurface, cropRect);
cropRect.MoveTo(0, 0);
+ needToReportMemoryAllocation = true;
}
else {
croppedSurface = surface;
}
if (NS_WARN_IF(!croppedSurface)) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return nullptr;
@@ -890,16 +938,21 @@ ImageBitmap::CreateInternal(nsIGlobalObj
if (NS_WARN_IF(!data)) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return nullptr;
}
RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data);
+ // Report memory allocation if needed.
+ if (needToReportMemoryAllocation) {
+ RegisterAllocation(aGlobal, croppedSurface);
+ }
+
// Set the picture rectangle.
if (ret && aCropRect.isSome()) {
ret->SetPictureRect(cropRect, aRv);
}
// Set mIsCroppingAreaOutSideOfSourceImage.
ret->SetIsCroppingAreaOutSideOfSourceImage(surface->GetSize(), aCropRect);
@@ -953,16 +1006,19 @@ ImageBitmap::CreateInternal(nsIGlobalObj
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return nullptr;
}
// Create an ImageBimtap.
// ImageData's underlying data is not alpha-premultiplied.
RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, false);
+ // Report memory allocation.
+ RegisterAllocation(aGlobal, data);
+
// The cropping information has been handled in the CreateImageFromRawData()
// function.
// Set mIsCroppingAreaOutSideOfSourceImage.
ret->SetIsCroppingAreaOutSideOfSourceImage(imageSize, aCropRect);
return ret.forget();
}
@@ -994,16 +1050,19 @@ ImageBitmap::CreateInternal(nsIGlobalObj
if (NS_WARN_IF(!data)) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return nullptr;
}
RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data);
+ // Report memory allocation.
+ RegisterAllocation(aGlobal, surface);
+
// Set the picture rectangle.
if (ret && aCropRect.isSome()) {
ret->SetPictureRect(aCropRect.ref(), aRv);
}
// Set mIsCroppingAreaOutSideOfSourceImage.
ret->SetIsCroppingAreaOutSideOfSourceImage(surface->GetSize(), aCropRect);
@@ -1235,16 +1294,19 @@ protected:
imageBitmap->SetPictureRect(mCropRect.ref(), rv);
if (rv.Failed()) {
mPromise->MaybeReject(rv);
return false;
}
}
+ // Report memory allocation.
+ RegisterAllocation(mGlobalObject, imageBitmap->mData);
+
mPromise->MaybeResolve(imageBitmap);
return true;
}
// Will return null on failure. In that case, mPromise will already
// be rejected with the right thing.
virtual already_AddRefed<ImageBitmap> CreateImageBitmap() = 0;
@@ -1519,16 +1581,19 @@ ImageBitmap::ReadStructuredClone(JSConte
if (NS_WARN_IF(error.Failed())) {
error.SuppressException();
return nullptr;
}
if (!GetOrCreateDOMReflector(aCx, imageBitmap, &value)) {
return nullptr;
}
+
+ // Report memory allocation.
+ RegisterAllocation(aParent, aClonedSurfaces[aIndex]);
}
return &(value.toObject());
}
/*static*/ bool
ImageBitmap::WriteStructuredClone(JSStructuredCloneWriter* aWriter,
nsTArray<RefPtr<DataSourceSurface>>& aClonedSurfaces,
@@ -2107,16 +2172,19 @@ ImageBitmap::Create(nsIGlobalObject* aGl
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return promise.forget();
}
// Create an ImageBimtap.
// Assume the data from an external buffer is not alpha-premultiplied.
RefPtr<ImageBitmap> imageBitmap = new ImageBitmap(aGlobal, data, false);
+ // Report memory allocation.
+ RegisterAllocation(aGlobal, data);
+
// 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.