new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/imagebitmap_extensions.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<head>
+ <title>Test ImageBitmap Extensions (Bug 1141979)</title>
+ <meta charset="utf-8">
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+<script src="imagebitmap_extensions_prepareSources.js"></script>
+<script src="imagebitmap_extensions.js"></script>
+<script type="text/javascript">
+
+runTests();
+
+function ok(expect, msg) {
+ window.parent.postMessage({"type": "status", status: !!expect, msg: msg}, "*");
+}
+
+function runTests() {
+
+ prepareSources().
+ then(testExceptions).
+ then( function() { return Promise.all([testAccessing_randomTest("Video", gVideo, 20), // video might use slightly different frames
+ testAccessing_randomTest("Image", gImage, 0),
+ testAccessing_randomTest("Canvas", gCanvas, 0),
+ testAccessing_randomTest("Ctx", gCtx, 0),
+ testAccessing_randomTest("ImageData", gImageData, 0),
+ testAccessing_randomTest("ImageBitmap", gImageBitmap, 0),
+ testAccessing_randomTest("PNG", gPNGBlob, 0),
+ testAccessing_randomTest("JPEG", gJPEGBlob, 10) // JPEG loses information
+ ]); }).
+ then( function() { return Promise.all([testCreateFromArrayBffer_randomTest("Video", gVideo, 20), // video might use slightly different frames
+ testCreateFromArrayBffer_randomTest("Image", gImage, 0),
+ testCreateFromArrayBffer_randomTest("Canvas", gCanvas, 0),
+ testCreateFromArrayBffer_randomTest("Ctx", gCtx, 0),
+ testCreateFromArrayBffer_randomTest("ImageData", gImageData, 0),
+ testCreateFromArrayBffer_randomTest("ImageBitmap", gImageBitmap, 0),
+ testCreateFromArrayBffer_randomTest("PNG", gPNGBlob, 0),
+ testCreateFromArrayBffer_randomTest("JPEG", gJPEGBlob, 10) // JPEG loses information
+ ]); }).
+ then(function() {window.parent.postMessage({"type": "finish"}, "*");}, function(ev) { failed(ev); window.parent.postMessage({"type": "finish"}, "*"); });
+}
+
+</script>
+</body>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/imagebitmap_extensions.js
@@ -0,0 +1,171 @@
+function failed(ex) {
+ ok(false, "Promise failure: " + ex);
+}
+
+function isPixel(sourceType, bitmapFormat, imageData, bitmapImageData, x, y, tolerance) {
+ if (imageData.width != bitmapImageData.width ||
+ imageData.height != bitmapImageData.height) {
+ ok(false, "Wrong dimension");
+ }
+
+ var index = 4 * (y * imageData.width + x);
+
+ var pr = imageData.data[index+0],
+ pg = imageData.data[index+1],
+ pb = imageData.data[index+2],
+ pa = imageData.data[index+3];
+
+ if (bitmapFormat == "RGBA32" || bitmapFormat == "RGBX32") {
+ var bpr = bitmapImageData.data[index+0],
+ bpg = bitmapImageData.data[index+1],
+ bpb = bitmapImageData.data[index+2],
+ bpa = bitmapImageData.data[index+3];
+ }
+ else if (bitmapFormat == "BGRA32" || bitmapFormat == "BGRX32") {
+ var bpb = bitmapImageData.data[index+0],
+ bpg = bitmapImageData.data[index+1],
+ bpr = bitmapImageData.data[index+2],
+ bpa = bitmapImageData.data[index+3];
+ }
+ else {
+ // format might be one of the followings: "R5G6B5", "A8", "YUV", ""
+ ok(false, "Not supported ImageFormat: " + bitmapFormat);
+ }
+
+ ok(pr - tolerance <= bpr && bpr <= pr + tolerance &&
+ pg - tolerance <= bpg && bpg <= pg + tolerance &&
+ pb - tolerance <= bpb && bpb <= pb + tolerance &&
+ pa - tolerance <= bpa && bpa <= pa + tolerance,
+ "pixel[" + x + "][" + y + "]: " + sourceType + " is "+pr+","+pg+","+pb+","+pa+"; ImageBitmap is "+ bpr + "," + bpg + "," + bpb + "," + bpa);
+}
+
+function promiseThrows(p, name) {
+ var didThrow;
+ return p.then(function() { didThrow = false; },
+ function() { didThrow = true; })
+ .then(function() { ok(didThrow, name); });
+}
+
+function testExceptions() {
+
+ function callMappedDataLengthWithUnsupportedFormat(format) {
+ return new Promise(function(resolve, reject) {
+ var p = createImageBitmap(gImageData);
+ p.then(
+ function(bitmap) {
+ try {
+ bitmap.mappedDataLength(format);
+ resolve();
+ }
+ catch (e) {
+ reject(e);
+ }
+ },
+ function() { resolve(); });
+ });
+ }
+
+ return Promise.all([
+ promiseThrows(callMappedDataLengthWithUnsupportedFormat("RGB24"), "[Exception] RGB24 is not supported yet."),
+ promiseThrows(callMappedDataLengthWithUnsupportedFormat("BGR24"), "[Exception] BGR24 is not supported yet."),
+ promiseThrows(callMappedDataLengthWithUnsupportedFormat("GRAY8"), "[Exception] GRAY8 is not supported yet."),
+ promiseThrows(callMappedDataLengthWithUnsupportedFormat("YUV444P"), "[Exception] GRYUV444PAY is not supported yet."),
+ promiseThrows(callMappedDataLengthWithUnsupportedFormat("YUV422P"), "[Exception] YUV422P is not supported yet."),
+ promiseThrows(callMappedDataLengthWithUnsupportedFormat("YUV420SP_NV12"), "[Exception] YUV420SP_NV12 is not supported yet."),
+ promiseThrows(callMappedDataLengthWithUnsupportedFormat("YUV420SP_NV21"), "[Exception] YUV420SP_NV21 is not supported yet."),
+ promiseThrows(callMappedDataLengthWithUnsupportedFormat("HSV"), "[Exception] HSV is not supported yet."),
+ promiseThrows(callMappedDataLengthWithUnsupportedFormat("Lab"), "[Exception] Lab is not supported yet."),
+ promiseThrows(callMappedDataLengthWithUnsupportedFormat("DEPTH"), "[Exception] DEPTH is not supported yet.")
+ ]);
+}
+
+// Create an ImageBitmap, _bitmap_, from the _source_.
+// Read the underlying data of _bitmap_ into _bitmapBuffer_.
+// Compare the _bitmapBuffer_ with gGroundTruthImageData.
+function testAccessing_randomTest(sourceType, source, duration) {
+ return new Promise(function(resolve, reject) {
+ var p = createImageBitmap(source);
+ p.then(
+ function(bitmap) {
+ bitmapFormat = "RGBA32";
+ var bitmapBufferLength = bitmap.mappedDataLength(bitmapFormat);
+
+ var bitmapBuffer = new ArrayBuffer(bitmapBufferLength);
+ var bitmapBufferView = new Uint8ClampedArray(bitmapBuffer, 0, bitmapBufferLength);
+ var promise = bitmap.mapDataInto(bitmapFormat, bitmapBuffer, 0);
+ promise.then(
+ function(bitmapPixelLayout) {
+ // Prepare.
+ bitmapImageData = new ImageData(bitmapBufferView, bitmap.width, bitmap.height);
+
+ // Test.
+ for (var t = 0; t < 50; ++t) {
+ var randomX = Math.floor(Math.random() * 240);
+ var randomY = Math.floor(Math.random() * 175);
+ isPixel(sourceType, "RGBA32", gGroundTruthImageData, bitmapImageData, randomX, randomY, duration);
+ }
+
+ resolve();
+ },
+ function(ev) { failed(ev); reject(); });
+ },
+ function(ev) { failed(ev); reject(); });
+ });
+}
+
+// Create an ImageBitmap, _bitmap_, from the _source_.
+// Read the underlying data of _bitmap_ into _bitmapBuffer_.
+// Create another ImageBitmap, _bitmap2_, from _bitmapBuffer_.
+// Read the underlying data of _bitmap2_ into _bitmapBuffer2_.
+// Compare the _bitmapBuffer2_ with gGroundTruthImageData.
+function testCreateFromArrayBffer_randomTest(sourceType, source, duration) {
+ return new Promise(function(resolve, reject) {
+ var p = createImageBitmap(source);
+ p.then(
+ function(bitmap) {
+ bitmapFormat = "RGBA32";
+ var bitmapBufferLength = bitmap.mappedDataLength(bitmapFormat);
+
+ var bitmapBuffer = new ArrayBuffer(bitmapBufferLength);
+ var bitmapBufferView = new Uint8ClampedArray(bitmapBuffer, 0, bitmapBufferLength);
+ var promiseMapDataInto = bitmap.mapDataInto(bitmapFormat, bitmapBuffer, 0);
+ promiseMapDataInto.then(
+ function(bitmapPixelLayout) {
+ // Create a new ImageBitmap from an ArrayBuffer.
+ var p2 = createImageBitmap(bitmapBufferView,
+ 0,
+ bitmapBufferLength,
+ bitmapFormat,
+ bitmapPixelLayout);
+
+ p2.then(
+ function(bitmap2) {
+ bitmapFormat2 = "RGBA32";
+ var bitmapBufferLength2 = bitmap2.mappedDataLength(bitmapFormat2);
+
+ var bitmapBuffer2 = new ArrayBuffer(bitmapBufferLength2);
+ var bitmapBufferView2 = new Uint8ClampedArray(bitmapBuffer2, 0, bitmapBufferLength2);
+ var promise2 = bitmap2.mapDataInto(bitmapFormat2, bitmapBuffer2, 0);
+ promise2.then(
+ function(bitmapPixelLayout2) {
+ // Prepare.
+ var bitmapImageData2 = new ImageData(bitmapBufferView2, bitmap2.width, bitmap2.height);
+
+ // Test.
+ for (var t = 0; t < 50; ++t) {
+ var randomX = Math.floor(Math.random() * 240);
+ var randomY = Math.floor(Math.random() * 175);
+ isPixel(sourceType, "RGBA32", gGroundTruthImageData, bitmapImageData2, randomX, randomY, duration);
+ }
+
+ resolve();
+ },
+ function(ev) { failed(ev); reject(); });
+ },
+ function(ev) { console.log("p2 rejected!"); failed(ev); reject(); });
+ },
+ function(ev) { console.log("promiseMapDataInto rejected!"); failed(ev); reject(); });
+ },
+ function(ev) { failed(ev); reject(); });
+ });
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/imagebitmap_extensions_on_worker.js
@@ -0,0 +1,44 @@
+importScripts("imagebitmap_extensions.js");
+
+var gGroundTruthImageData;
+var gImageData;
+var gImageBitmap;
+var gPNGBlob;
+var gJPEGBlob;
+
+onmessage = function(event) {
+ if (event.data.type == "setSources") {
+ gGroundTruthImageData = event.data.groundTruthImageData;
+ gImageData = event.data.imageData;
+ gImageBitmap = event.data.imageBitmap;
+ gPNGBlob = event.data.pngBlob;
+ gJPEGBlob = event.data.jpegBlob;
+
+ ok(!!gGroundTruthImageData, "Get gGroundTruthImageData!");
+ ok(!!gImageData, "Get gImageData!");
+ ok(!!gImageBitmap, "Get gImageBitmap!");
+ ok(!!gPNGBlob, "Get gPNGBlob!");
+ ok(!!gJPEGBlob, "Get gJPEGBlob!");
+
+ runTests();
+ }
+};
+
+function ok(expect, msg) {
+ postMessage({"type": "status", status: !!expect, msg: msg});
+}
+
+function runTests() {
+ testExceptions().
+ then( function() { return Promise.all([testAccessing_randomTest("ImageData", gImageData, 0),
+ testAccessing_randomTest("ImageBitmap", gImageBitmap, 0),
+ testAccessing_randomTest("PNG", gPNGBlob, 0),
+ testAccessing_randomTest("JPEG", gJPEGBlob, 10) // JPEG loses information
+ ]); }).
+ then( function() { return Promise.all([testCreateFromArrayBffer_randomTest("ImageData", gImageData, 0),
+ testCreateFromArrayBffer_randomTest("ImageBitmap", gImageBitmap, 0),
+ testCreateFromArrayBffer_randomTest("PNG", gPNGBlob, 0),
+ testCreateFromArrayBffer_randomTest("JPEG", gJPEGBlob, 10) // JPEG loses information
+ ]); }).
+ then(function() {postMessage({"type": "finish"});}, function(ev) { failed(ev); postMessage({"type": "finish"}); });
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/imagebitmap_extensions_prepareSources.js
@@ -0,0 +1,94 @@
+var gImage;
+var gVideo;
+var gCanvas;
+var gCtx;
+var gImageData;
+var gImageBitmap;
+var gPNGBlob;
+var gJPEGBlob;
+
+var gGroundTruthImageData;
+
+function prepareSources() {
+ gVideo = document.createElement("video");
+ gVideo.src = "http://example.com/tests/dom/canvas/test/crossorigin/video.sjs?name=tests/dom/media/test/320x240.ogv&type=video/ogg&cors=anonymous";
+ gVideo.crossOrigin = "anonymous";
+ gVideo.autoplay = "true"
+
+
+ gCanvas = document.createElement("canvas");
+ gCtx = gCanvas.getContext("2d");
+
+ var resolver;
+ var promise = new Promise(function(resolve, reject) {
+ resolver = resolve;
+ });
+
+ // Prepare video.
+ gVideo.onloadeddata = function() {
+ ok(gVideo, "[Prepare Sources] gVideo is ok.");
+
+ // Prepare canvas.
+ gCanvas.width = gVideo.videoWidth;
+ gCanvas.height = gVideo.videoHeight;
+ gCtx.drawImage(gVideo, 0, 0);
+ ok(gCanvas, "[Prepare Sources] gCanvas is ok.");
+ ok(gCtx, "[Prepare Sources] gCtx is ok.");
+
+ // Prepare gGroundTruthImageData.
+ gGroundTruthImageData = gCtx.getImageData(0, 0, gCanvas.width, gCanvas.height);
+ ok(gGroundTruthImageData, "[Prepare Sources] gGroundTruthImageData is ok.");
+
+ // Prepare image.
+ gImage = document.createElement("img");
+ gImage.src = gCanvas.toDataURL();
+ var resolverImage;
+ var promiseImage = new Promise(function(resolve, reject) {
+ resolverImage = resolve;
+ });
+ gImage.onload = function() {
+ resolverImage(true);
+ }
+
+ // Prepare ImageData.
+ gImageData = gCtx.getImageData(0, 0, gCanvas.width, gCanvas.height);
+ ok(gImageData, "[Prepare Sources] gImageData is ok.");
+
+ // Prepapre PNG Blob.
+ var promisePNGBlob = new Promise(function(resolve, reject) {
+ gCanvas.toBlob(function(blob) {
+ gPNGBlob = blob;
+ ok(gPNGBlob, "[Prepare Sources] gPNGBlob is ok.");
+ resolve(true);
+ });
+ });
+
+ // Prepare JPEG Blob.
+ var promiseJPEGBlob = new Promise(function(resolve, reject) {
+ gCanvas.toBlob(function(blob) {
+ gJPEGBlob = blob;
+ ok(gJPEGBlob, "[Prepare Sources] gJPEGBlob is ok.");
+ resolve(true);
+ }, "image/jpeg", 1.00);
+ });
+
+ // Prepare ImageBitmap.
+ var promiseImageBitmap = new Promise(function(resolve, reject) {
+ var p = createImageBitmap(gCanvas);
+ p.then(function(bitmap) {
+ gImageBitmap = bitmap;
+ ok(gImageBitmap, "[Prepare Sources] gImageBitmap is ok.");
+ resolve(true);
+ });
+ });
+
+ resolver(Promise.all([
+ promiseImage,
+ promisePNGBlob,
+ promiseJPEGBlob,
+ promiseImageBitmap
+ ]))
+ }
+
+ return promise;
+}
\ No newline at end of file
--- a/dom/canvas/test/mochitest.ini
+++ b/dom/canvas/test/mochitest.ini
@@ -21,16 +21,20 @@ support-files =
image_rgrg-256x256.png
image_rrgg-256x256.png
image_transparent.png
image_transparent50.png
image_yellow.png
image_yellow75.png
imagebitmap_bug1239300.js
imagebitmap_bug1239752.js
+ imagebitmap_extensions.html
+ imagebitmap_extensions.js
+ imagebitmap_extensions_on_worker.js
+ imagebitmap_extensions_prepareSources.js
imagebitmap_on_worker.js
imagebitmap_structuredclone.js
imagebitmap_structuredclone_iframe.html
imagebitmap_structuredclone_utils.js
offscreencanvas.js
offscreencanvas_mask.svg
offscreencanvas_neuter.js
offscreencanvas_serviceworker_inner.html
@@ -225,16 +229,20 @@ support-files = captureStream_common.js
support-files = file_drawWindow_source.html file_drawWindow_common.js
skip-if = (buildapp == 'b2g' && toolkit != 'gonk')
[test_imagebitmap.html]
tags = imagebitmap
[test_imagebitmap_close.html]
tags = imagebitmap
[test_imagebitmap_cropping.html]
tags = imagebitmap
+[test_imagebitmap_extensions.html]
+tags = imagebitmap
+[test_imagebitmap_extensions_on_worker.html]
+tags = imagebitmap
[test_imagebitmap_on_worker.html]
tags = imagebitmap
[test_imagebitmap_structuredclone.html]
tags = imagebitmap
[test_imagebitmap_structuredclone_iframe.html]
tags = imagebitmap
[test_imagebitmap_structuredclone_window.html]
tags = imagebitmap
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/test_imagebitmap_extensions.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<heand>
+ <title>Test ImageBitmap Extensions (Bug 1141979)</title>
+ <meta charset="utf-8">
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+ <div id="content"><div>
+ <script type="text/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ // The createImageBitmap() method is part of Window whose
+ // prototype was created before the preference is set. So I create another
+ // iframe with the right preference setting so that the
+ // createImageBitmap() will be visible.
+ SpecialPowers.pushPrefEnv({'set': [
+ ['canvas.imagebitmap_extensions.enabled', true]
+ ]}, function() {
+ var div = document.getElementById("content");
+ ok(div, "Parent exists");
+
+ var ifr = document.createElement("iframe");
+ ifr.setAttribute('src', "imagebitmap_extensions.html");
+ div.appendChild(ifr);
+ });
+
+ window.onmessage = function(event) {
+ if (event.data.type == "status") {
+ ok(event.data.status, event.data.msg);
+ } else if (event.data.type == "finish") {
+ SimpleTest.finish();
+ }
+ }
+ </script>
+</body>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/test_imagebitmap_extensions_on_worker.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<heand>
+ <title>Test ImageBitmap Extensions On Worker (Bug 1141979)</title>
+ <meta charset="utf-8">
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+ <div id="content"><div>
+ <script src="imagebitmap_extensions_prepareSources.js"></script>
+ <script type="text/javascript">
+
+ var worker;
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({'set': [
+ ['canvas.imagebitmap_extensions.enabled', true]
+ ]}, function() {
+ worker = new Worker("imagebitmap_extensions_on_worker.js");
+ worker.onmessage = function(event) {
+ if (event.data.type == "status") {
+ ok(event.data.status, event.data.msg);
+ } else if (event.data.type == "finish") {
+ SimpleTest.finish();
+ }
+ };
+
+ ok(!!worker, "Worker created successfully.");
+ prepareSources().then(function() {
+ worker.postMessage({"type": "setSources",
+ "groundTruthImageData": gGroundTruthImageData,
+ "imageData": gImageData,
+ "imageBitmap": gImageBitmap,
+ "pngBlob": gPNGBlob,
+ "jpegBlob": gJPEGBlob});
+ });
+ });
+ </script>
+</body>