Bug 1172796 - Part 6: Implements ImageBitmap::Close(). r=roc draft
authorMorris Tseng <mtseng@mozilla.com>
Wed, 18 Nov 2015 12:02:19 +0800
changeset 309552 18a0be1861ba4b2438d000f2cf6a2ef2e52581db
parent 309551 76b05bf5d585d534d51f6d5299acc26b657f24f0
child 309553 91fe173d170bedb859e9182fcfdacafd2e020402
push id7622
push usermtseng@mozilla.com
push dateWed, 18 Nov 2015 04:04:35 +0000
reviewersroc
bugs1172796
milestone45.0a1
Bug 1172796 - Part 6: Implements ImageBitmap::Close(). r=roc
dom/canvas/ImageBitmap.cpp
dom/canvas/ImageBitmap.h
dom/canvas/test/mochitest.ini
dom/canvas/test/test_imagebitmap_close.html
dom/webidl/ImageBitmap.webidl
--- a/dom/canvas/ImageBitmap.cpp
+++ b/dom/canvas/ImageBitmap.cpp
@@ -405,16 +405,24 @@ ImageBitmap::~ImageBitmap()
 
 JSObject*
 ImageBitmap::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return ImageBitmapBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
+ImageBitmap::Close()
+{
+  mData = nullptr;
+  mSurface = nullptr;
+  mPictureRect.SetEmpty();
+}
+
+void
 ImageBitmap::SetPictureRect(const IntRect& aRect, ErrorResult& aRv)
 {
   mPictureRect = FixUpNegativeDimension(aRect, aRv);
 }
 
 already_AddRefed<SourceSurface>
 ImageBitmap::PrepareForDrawTarget(gfx::DrawTarget* aTarget)
 {
--- a/dom/canvas/ImageBitmap.h
+++ b/dom/canvas/ImageBitmap.h
@@ -86,16 +86,18 @@ public:
     return mPictureRect.Width();
   }
 
   uint32_t Height() const
   {
     return mPictureRect.Height();
   }
 
+  void Close();
+
   /*
    * The PrepareForDrawTarget() might return null if the mPictureRect does not
    * intersect with the size of mData.
    */
   already_AddRefed<gfx::SourceSurface>
   PrepareForDrawTarget(gfx::DrawTarget* aTarget);
 
   ImageBitmapCloneData*
--- a/dom/canvas/test/mochitest.ini
+++ b/dom/canvas/test/mochitest.ini
@@ -240,16 +240,17 @@ skip-if = os == "android" || appname == 
 support-files = captureStream_common.js
 [test_drawImageIncomplete.html]
 [test_drawImage_document_domain.html]
 [test_drawImage_edge_cases.html]
 [test_drawWindow.html]
 support-files = file_drawWindow_source.html file_drawWindow_common.js
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk')
 [test_imagebitmap.html]
+[test_imagebitmap_close.html]
 [test_imagebitmap_cropping.html]
 [test_imagebitmap_on_worker.html]
 [test_imagebitmap_structuredclone.html]
 [test_imagebitmap_structuredclone_iframe.html]
 [test_imagebitmap_structuredclone_window.html]
 [test_imagebitmap_transfer.html]
 [test_ImageData_ctor.html]
 [test_isPointInStroke.html]
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/test_imagebitmap_close.html
@@ -0,0 +1,58 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<title>WebGL in OffscreenCanvas</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+<script>
+
+SimpleTest.waitForExplicitFinish();
+
+function createCanvas() {
+  var htmlCanvas = document.createElement('canvas');
+  htmlCanvas.width = 64;
+  htmlCanvas.height = 64;
+  document.body.appendChild(htmlCanvas);
+  return htmlCanvas;
+}
+
+function runTest() {
+  var canvas1 = createCanvas();
+  var ctx = canvas1.getContext("2d");
+  ctx.fillStyle = "#00FF00";
+  ctx.fillRect(0, 0, 64, 64);
+
+  var canvasRef = createCanvas();
+  var ctx = canvasRef.getContext("2d");
+  ctx.fillStyle = "#00FF00";
+  ctx.fillRect(0, 0, 64, 64);
+
+  createImageBitmap(canvas1).then(function(bmp) {
+    var canvas2 = createCanvas();
+    var ctx2 = canvas2.getContext("2d");
+    ctx2.drawImage(bmp, 0, 0);
+
+    ok(canvasRef.toDataURL() == canvas2.toDataURL(), "toDataURL should return same result.");
+    document.body.removeChild(canvas2);
+
+    bmp.close();
+    ok(bmp.width == 0 && bmp.height == 0, "After close(), width and height should return 0");
+    var canvas2 = createCanvas();
+    var ctx2 = canvas2.getContext("2d");
+    var beforeDrawImageDataURL = canvas2.toDataURL();
+    ctx2.drawImage(bmp, 0, 0);
+    var afterDrawImageDataURL = canvas2.toDataURL();
+    ok(beforeDrawImageDataURL == afterDrawImageDataURL,
+       "Drawing operations with a closed ImageBitmap should do nothing.");
+    SimpleTest.finish();
+  });
+}
+
+runTest();
+
+</script>
+</body>
+</html>
--- a/dom/webidl/ImageBitmap.webidl
+++ b/dom/webidl/ImageBitmap.webidl
@@ -18,15 +18,27 @@ typedef (HTMLImageElement or
 [Exposed=(Window,Worker)]
 interface ImageBitmap {
   [Constant]
   readonly attribute unsigned long width;
   [Constant]
   readonly attribute unsigned long height;
 };
 
+// It's crucial that there be a way to explicitly dispose of ImageBitmaps
+// since they refer to potentially large graphics resources. Some uses
+// of this API proposal will result in repeated allocations of ImageBitmaps,
+// and garbage collection will not reliably reclaim them quickly enough.
+// Here we reuse close(), which also exists on another Transferable type,
+// MessagePort. Potentially, all Transferable types should inherit from a
+// new interface type "Closeable".
+partial interface ImageBitmap {
+  // Dispose of all graphical resources associated with this ImageBitmap.
+  void close();
+};
+
 [NoInterfaceObject, Exposed=(Window,Worker)]
 interface ImageBitmapFactories {
   [Throws]
   Promise<ImageBitmap> createImageBitmap(ImageBitmapSource aImage);
   [Throws]
   Promise<ImageBitmap> createImageBitmap(ImageBitmapSource aImage, long aSx, long aSy, long aSw, long aSh);
 };