summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/html/canvas/element/manual/imagebitmap
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/html/canvas/element/manual/imagebitmap')
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/canvas-ImageBitmap-close.html89
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/canvas-createImageBitmap-resize.html170
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/canvas-createImageBitmap-video-resize.html62
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/common.sub.js168
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-blob-invalidtype.html27
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-bounds.html67
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-colorSpaceConversion.html50
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-drawImage-closed.html27
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-drawImage.html87
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-exif-orientation.html121
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-exif-orientation_none.html61
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-flipY.html73
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-in-worker-transfer.html20
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-invalid-args.html239
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-origin.sub.html47
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-serializable.html52
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-sizeOverflow.html52
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-transfer.html53
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-worker.js17
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmap-from-imageData-no-image-rotation-expected.html18
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmap-from-imageData-no-image-rotation.html26
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-expected.html27
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-flipped-expected.html27
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-flipped.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-webgl-expected.html35
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-webgl.html38
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap.html34
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imagebitmap-replication-exif-orientation.html146
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_1.jpgbin0 -> 1227 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_2.jpgbin0 -> 1227 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_3.jpgbin0 -> 1227 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_4.jpgbin0 -> 1227 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_5.jpgbin0 -> 1227 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_6.jpgbin0 -> 1227 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_7.jpgbin0 -> 1227 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_8.jpgbin0 -> 1227 bytes
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/serialize-worker.js3
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/transfer-worker.js3
-rw-r--r--testing/web-platform/tests/html/canvas/element/manual/imagebitmap/worker-onmessage-noop.js3
39 files changed, 1876 insertions, 0 deletions
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/canvas-ImageBitmap-close.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/canvas-ImageBitmap-close.html
new file mode 100644
index 0000000000..fca8274528
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/canvas-ImageBitmap-close.html
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<body>
+ <p>Tests that the close method of ImageBitmap does dispose the image data.</p>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+
+promise_test(function(t) {
+ var worker = new Worker('worker-onmessage-noop.js');
+
+ var imgHeight = 10;
+ var imgWidth = 10;
+ var imageData = new ImageData(10, 10);
+ var bitmap;
+ var ctx;
+ return createImageBitmap(imageData).then(imageBitmap => {
+ bitmap = imageBitmap;
+ assert_equals(bitmap.width, imgWidth, "bitmap.width = 10");
+ assert_equals(bitmap.height, imgWidth, "bitmap.height = 10");
+
+ // Apply structured clone to the bitmap, nothing should be changed
+ worker.postMessage({data: bitmap});
+ assert_equals(bitmap.width, imgWidth, "bitmap.width = 10");
+ assert_equals(bitmap.height, imgWidth, "bitmap.height = 10");
+
+ // After calling close, the image data associated with the bitmap should no longer exist
+ bitmap.close();
+ assert_equals(bitmap.width, 0, "bitmap.width = 0");
+ assert_equals(bitmap.height, 0, "bitmap.height = 0");
+
+ var canvas = document.createElement("canvas");
+ canvas.width = imgWidth;
+ canvas.height = imgHeight;
+ ctx = canvas.getContext("2d");
+ assert_throws_dom("InvalidStateError", function() { ctx.drawImage(bitmap, 0, 0); });
+
+ // Try to apply structured clone to an already closed bitmap
+ try {
+ worker.postMessage({data: bitmap});
+ throw new Error("Apply clone to an closed bitmap should be rejected");
+ }
+ catch(ex) {
+ // Apply structured clone to an already closed bitmap is rejected as expected.
+ }
+
+ // Try to apply transferring to an already closed bitmap
+ try {
+ worker.postMessage({data: bitmap}, [bitmap]);
+ throw new Error("Apply transferring to an closed bitmap should be rejected");
+ } catch(ex) {
+ // Apply structured clone to an already closed bitmap is rejected as expected.
+ }
+
+ // Calling createImageBitmap from a closed bitmap should be rejected
+ return createImageBitmap(bitmap).then(function() {
+ throw new Error("createImageBitmap from a closed bitmap should be rejected");
+ }, ex => {
+ // Calling createImageBitmap from a closed ImageBitmap is rejected as expected.
+ });
+ }).then(() => {
+ // Call close to a already closed bitmap should be noop.
+ bitmap.close();
+ assert_equals(bitmap.width, 0, "bitmap.height = 0");
+ assert_equals(bitmap.height, 0, "bitmap.height = 0");
+
+ return createImageBitmap(imageData).then(imageBitmap => {
+ bitmap = imageBitmap;
+ assert_equals(bitmap.width, imgWidth, "bitmap.width = 10");
+ assert_equals(bitmap.height, imgWidth, "bitmap.height = 10");
+
+ // Transfer the bitmap to a worker
+ worker.postMessage({data: bitmap}, [bitmap]);
+
+ // After transferring, the bitmap is neutered.
+ assert_equals(bitmap.width, 0, "bitmap.height = 0");
+ assert_equals(bitmap.height, 0, "bitmap.height = 0");
+
+ // Calling close to a neutered bitmap should be noop.
+ bitmap.close();
+ assert_equals(bitmap.width, 0, "bitmap.height = 0");
+ assert_equals(bitmap.height, 0, "bitmap.height = 0");
+
+ });
+ }).catch(function(ex) {
+ throw new Error("No exception should be thrown.");
+ })
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/canvas-createImageBitmap-resize.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/canvas-createImageBitmap-resize.html
new file mode 100644
index 0000000000..782c7e130c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/canvas-createImageBitmap-resize.html
@@ -0,0 +1,170 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+function checkNoCrop(imageBitmap)
+{
+ var canvas = document.createElement("canvas");
+ canvas.width = 50;
+ canvas.height = 50;
+ var ctx = canvas.getContext("2d");
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ ctx.drawImage(imageBitmap, 0, 0);
+ var d = ctx.getImageData(0, 0, 1, 1).data;
+ assert_array_equals(d, [255, 0, 0, 255], "This pixel should be red.");
+ d = ctx.getImageData(39, 0, 1, 1).data;
+ assert_array_equals(d, [0, 255, 0, 255], "This pixel should be green.");
+ d = ctx.getImageData(0, 39, 1, 1).data;
+ assert_array_equals(d, [0, 0, 255, 255], "This pixel should be blue.");
+ d = ctx.getImageData(39, 39, 1, 1).data;
+ assert_array_equals(d, [0, 0, 0, 255], "This pixel should be black.");
+ d = ctx.getImageData(41, 41, 1, 1).data;
+ assert_array_equals(d, [0, 0, 0, 0], "This pixel should be transparent black.");
+}
+
+function checkCrop(imageBitmap)
+{
+ var canvas = document.createElement("canvas");
+ canvas.width = 50;
+ canvas.height = 50;
+ var ctx = canvas.getContext("2d");
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ ctx.drawImage(imageBitmap, 0, 0);
+ var d = ctx.getImageData(0, 0, 1, 1).data;
+ assert_array_equals(d, [255, 0, 0, 255], "This pixel should be red.");
+ d = ctx.getImageData(19, 0, 1, 1).data;
+ assert_array_equals(d, [0, 255, 0, 255], "This pixel should be green.");
+ d = ctx.getImageData(0, 19, 1, 1).data;
+ assert_array_equals(d, [0, 0, 255, 255], "This pixel should be blue.");
+ d = ctx.getImageData(19, 19, 1, 1).data;
+ assert_array_equals(d, [0, 0, 0, 255], "This pixel should be black.");
+ d = ctx.getImageData(21, 21, 1, 1).data;
+ assert_array_equals(d, [0, 0, 0, 0], "This pixel should be transparent black.");
+}
+
+function compareBitmaps(bitmap1, bitmap2)
+{
+ var canvas1 = document.createElement("canvas");
+ var canvas2 = document.createElement("canvas");
+ canvas1.width = 50;
+ canvas1.height = 50;
+ canvas2.width = 50;
+ canvas2.height = 50;
+ var ctx1 = canvas1.getContext("2d");
+ var ctx2 = canvas2.getContext("2d");
+ ctx1.clearRect(0, 0, canvas1.width, canvas1.height);
+ ctx2.clearRect(0, 0, canvas2.width, canvas2.height);
+ ctx1.drawImage(bitmap1, 0, 0);
+ ctx2.drawImage(bitmap2, 0, 0);
+ var data1 = ctx1.getImageData(0, 0, 50, 50).data;
+ var data2 = ctx2.getImageData(0, 0, 50, 50).data;
+ var dataMatched = true;
+ for (var i = 0; i < data1.length; i++) {
+ if (data1[i] != data2[i]) {
+ dataMatched = false;
+ break;
+ }
+ }
+ assert_false(dataMatched);
+}
+
+function testImageBitmap(source)
+{
+ return Promise.all([
+ createImageBitmap(source, {resizeWidth: 40, resizeHeight: 40, resizeQuality: "high"}),
+ createImageBitmap(source, {resizeWidth: 40, resizeHeight: 40, resizeQuality: "medium"}),
+ createImageBitmap(source, {resizeWidth: 40, resizeHeight: 40, resizeQuality: "low"}),
+ createImageBitmap(source, {resizeWidth: 40, resizeHeight: 40, resizeQuality: "pixelated"}),
+ createImageBitmap(source, 5, 5, 10, 10, {resizeWidth: 20, resizeHeight: 20, resizeQuality: "high"}),
+ createImageBitmap(source, 5, 5, 10, 10, {resizeWidth: 20, resizeHeight: 20, resizeQuality: "medium"}),
+ createImageBitmap(source, 5, 5, 10, 10, {resizeWidth: 20, resizeHeight: 20, resizeQuality: "low"}),
+ createImageBitmap(source, 5, 5, 10, 10, {resizeWidth: 20, resizeHeight: 20, resizeQuality: "pixelated"}),
+ ]).then(([noCropHigh, noCropMedium, noCropLow, noCropPixelated, cropHigh, cropMedium, cropLow, cropPixelated]) => {
+ checkNoCrop(noCropHigh);
+ checkNoCrop(noCropMedium);
+ checkNoCrop(noCropLow);
+ checkNoCrop(noCropPixelated);
+ checkCrop(cropHigh);
+ checkCrop(cropMedium);
+ checkCrop(cropLow);
+ checkCrop(cropPixelated);
+ // Brute-force comparison among all bitmaps is too expensive
+ compareBitmaps(noCropHigh, noCropMedium);
+ compareBitmaps(noCropLow, noCropPixelated);
+ compareBitmaps(cropHigh, cropMedium);
+ compareBitmaps(cropLow, cropPixelated);
+ });
+}
+
+function initializeTestCanvas()
+{
+ var testCanvas = document.createElement("canvas");
+ testCanvas.width = 20;
+ testCanvas.height = 20;
+ var testCtx = testCanvas.getContext("2d");
+ testCtx.fillStyle = "rgb(255, 0, 0)";
+ testCtx.fillRect(0, 0, 10, 10);
+ testCtx.fillStyle = "rgb(0, 255, 0)";
+ testCtx.fillRect(10, 0, 10, 10);
+ testCtx.fillStyle = "rgb(0, 0, 255)";
+ testCtx.fillRect(0, 10, 10, 10);
+ testCtx.fillStyle = "rgb(0, 0, 0)";
+ testCtx.fillRect(10, 10, 10, 10);
+ return testCanvas;
+}
+
+// Blob
+promise_test(function() {
+ return new Promise((resolve, reject) => {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/pattern.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ }).then(testImageBitmap);
+}, 'createImageBitmap from a Blob with resize option.');
+
+// HTMLCanvasElement
+promise_test(function() {
+ var testCanvas = initializeTestCanvas();
+ return testImageBitmap(testCanvas);
+}, 'createImageBitmap from a HTMLCanvasElement with resize option.');
+
+// HTMLImageElement
+promise_test(function() {
+ return new Promise((resolve, reject) => {
+ var image = new Image();
+ image.onload = function() {
+ resolve(image);
+ }
+ image.src = '/images/pattern.png'
+ }).then(testImageBitmap);
+}, 'createImageBitmap from a HTMLImageElement with resize option.');
+
+// HTMLImageElement of svg with no specified size
+promise_test(function() {
+ return new Promise((resolve, reject) => {
+ var image = new Image();
+ image.onload = function() {
+ resolve(image);
+ }
+ image.src = '/images/pattern-nosize.svg'
+ }).then(testImageBitmap);
+}, 'createImageBitmap from a HTMLImageElement of svg with no specified size with resize option.');
+
+// ImageBitmap
+promise_test(function() {
+ var testCanvas = initializeTestCanvas();
+ return createImageBitmap(testCanvas).then(testImageBitmap);
+}, 'createImageBitmap from an ImageBitmap with resize option.');
+
+// ImageData
+promise_test(function() {
+ var canvas = initializeTestCanvas();
+ var ctx = canvas.getContext("2d");
+ var data = ctx.getImageData(0, 0, 20, 20);
+ return testImageBitmap(data);
+}, 'createImageBitmap from an ImageData with resize option.');
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/canvas-createImageBitmap-video-resize.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/canvas-createImageBitmap-video-resize.html
new file mode 100644
index 0000000000..d48d9e80ca
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/canvas-createImageBitmap-video-resize.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/media.js"></script>
+<script>
+function createNewCanvas(width, height)
+{
+ var canvas = document.createElement("canvas");
+ canvas.width = width;
+ canvas.height = height;
+ var ctx = canvas.getContext("2d");
+ ctx.clearRect(0, 0, width, height);
+ return ctx;
+}
+
+function checkLowResult(imageBitmap, bw, bh, video, sx, sy, sw, sh)
+{
+ var ctx1 = createNewCanvas(bw, bh);
+ var ctx2 = createNewCanvas(bw, bh);
+ ctx1.drawImage(imageBitmap, 0, 0);
+ ctx2.drawImage(video, sx, sy, sw, sh, 0, 0, bw, bh);
+ var data1 = ctx1.getImageData(0, 0, bw, bh).data;
+ var data2 = ctx2.getImageData(0, 0, bw, bh).data;
+ var dataMatched = true;
+ for (var i = 0; i < data1.length; i++) {
+ // data1[i] is strictly the same as data2[i] on software rendering.
+ // But on GPU, the difference could be quite significant.
+ if (Math.abs(data1[i] - data2[i]) > 33) {
+ dataMatched = false;
+ break;
+ }
+ }
+ assert_true(dataMatched);
+}
+
+function generateTest()
+{
+ bitmapWidth = video.videoWidth/2;
+ bitmapHeight = video.videoHeight/2;
+ return Promise.all([
+ createImageBitmap(video, {resizeWidth: bitmapWidth, resizeHeight: bitmapHeight, resizeQuality: "low"}),
+ createImageBitmap(video, 10, 10, bitmapWidth, bitmapHeight, {resizeWidth: bitmapWidth, resizeHeight: bitmapHeight, resizeQuality: "low"}),
+ ]).then(t.step_func_done(([noCropLow, cropLow]) => {
+ checkLowResult(noCropLow, bitmapWidth, bitmapHeight, video, 0, 0, video.videoWidth, video.videoHeight);
+ checkLowResult(cropLow, bitmapWidth, bitmapHeight, video, 10, 10, bitmapWidth, bitmapHeight);
+ }), t.step_func_done(function() {
+ assert_true(false, 'Promise rejected');
+ }));
+}
+
+var t = async_test('createImageBitmap(HTMLVideoElement) with resize option');
+
+// HTMLVideoElement
+var video = document.createElement("video");
+video.preload = "auto";
+if (video.requestVideoFrameCallback) {
+ video.requestVideoFrameCallback(t.step_func(() => generateTest()));
+} else {
+ video.oncanplaythrough = t.step_func(() => generateTest());
+}
+video.src = getVideoURI("/media/counting");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/common.sub.js b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/common.sub.js
new file mode 100644
index 0000000000..1889035202
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/common.sub.js
@@ -0,0 +1,168 @@
+function makeCanvas() {
+ return new Promise(resolve => {
+ var testCanvas = document.createElement("canvas");
+ testCanvas.width = 20;
+ testCanvas.height = 20;
+ var testCtx = testCanvas.getContext("2d");
+ testCtx.fillStyle = "rgb(255, 0, 0)";
+ testCtx.fillRect(0, 0, 10, 10);
+ testCtx.fillStyle = "rgb(0, 255, 0)";
+ testCtx.fillRect(10, 0, 10, 10);
+ testCtx.fillStyle = "rgb(0, 0, 255)";
+ testCtx.fillRect(0, 10, 10, 10);
+ testCtx.fillStyle = "rgb(0, 0, 0)";
+ testCtx.fillRect(10, 10, 10, 10);
+ resolve(testCanvas);
+ });
+}
+
+function makeOffscreenCanvas() {
+ return new Promise(resolve => {
+ let canvas = new OffscreenCanvas(20, 20);
+ var testCtx = canvas.getContext("2d");
+ testCtx.fillStyle = "rgb(255, 0, 0)";
+ testCtx.fillRect(0, 0, 10, 10);
+ testCtx.fillStyle = "rgb(0, 255, 0)";
+ testCtx.fillRect(10, 0, 10, 10);
+ testCtx.fillStyle = "rgb(0, 0, 255)";
+ testCtx.fillRect(0, 10, 10, 10);
+ testCtx.fillStyle = "rgb(0, 0, 0)";
+ testCtx.fillRect(10, 10, 10, 10);
+ resolve(canvas);
+ });
+}
+
+var imageBitmapVideoPromise = new Promise(function(resolve, reject) {
+ var video = document.createElement("video");
+ video.oncanplaythrough = function() {
+ resolve(video);
+ };
+ video.onerror = reject;
+
+ // preload=auto is required to ensure a frame is available once
+ // canplaythrough is fired. The default of preload=metadata does not
+ // gaurantee this.
+ video.preload = "auto";
+ video.src = getVideoURI("/images/pattern");
+
+ // Prevent WebKit from garbage collecting event handlers.
+ window._video = video;
+});
+
+function makeVideo() {
+ return imageBitmapVideoPromise;
+}
+
+var imageBitmapDataUrlVideoPromise = fetch(getVideoURI("/images/pattern"))
+ .then(response => Promise.all([response.headers.get("Content-Type"), response.arrayBuffer()]))
+ .then(([type, data]) => {
+ return new Promise(function(resolve, reject) {
+ var video = document.createElement("video");
+ video.oncanplaythrough = function() {
+ resolve(video);
+ };
+ video.onerror = reject;
+
+ var encoded = btoa(String.fromCodePoint(...new Uint8Array(data)));
+ var dataUrl = `data:${type};base64,${encoded}`;
+
+ // preload=auto is required to ensure a frame is available once
+ // canplaythrough is fired. The default of preload=metadata does not
+ // gaurantee this.
+ video.preload = "auto";
+ video.src = dataUrl;
+
+ // Prevent WebKit from garbage collecting event handlers.
+ window._dataVideo = video;
+ });
+ });
+
+function makeDataUrlVideo() {
+ return imageBitmapDataUrlVideoPromise;
+}
+
+function makeMakeHTMLImage(src) {
+ return function() {
+ return new Promise((resolve, reject) => {
+ var img = new Image();
+ img.onload = function() {
+ resolve(img);
+ };
+ img.onerror = reject;
+ img.src = src;
+ });
+ }
+}
+
+function makeMakeSVGImage(src) {
+ return function() {
+ return new Promise((resolve, reject) => {
+ var image = document.createElementNS("http://www.w3.org/2000/svg", "image");
+ image.onload = () => resolve(image);
+ image.onerror = reject;
+ image.setAttribute("externalResourcesRequired", "true");
+ image.setAttributeNS("http://www.w3.org/1999/xlink", 'xlink:href', src);
+ document.body.appendChild(image);
+ });
+ }
+}
+
+function makeImageData() {
+ return new Promise(function(resolve, reject) {
+ var width = 20, height = 20;
+ var imgData = new ImageData(width, height);
+ for (var i = 0; i < width * height * 4; i += 4) {
+ imgData.data[i] = 0;
+ imgData.data[i + 1] = 0;
+ imgData.data[i + 2] = 0;
+ imgData.data[i + 3] = 255; //alpha channel: 255
+ }
+ var halfWidth = width / 2;
+ var halfHeight = height / 2;
+ // initialize to R, G, B, Black, with each one 10*10 pixels
+ for (var i = 0; i < halfHeight; i++)
+ for (var j = 0; j < halfWidth; j++)
+ imgData.data[i * width * 4 + j * 4] = 255;
+ for (var i = 0; i < halfHeight; i++)
+ for (var j = halfWidth; j < width; j++)
+ imgData.data[i * width * 4 + j * 4 + 1] = 255;
+ for (var i = halfHeight; i < height; i++)
+ for (var j = 0; j < halfWidth; j++)
+ imgData.data[i * width * 4 + j * 4 + 2] = 255;
+ resolve(imgData);
+ });
+}
+
+function makeImageBitmap() {
+ return makeCanvas().then(canvas => {
+ return createImageBitmap(canvas);
+ });
+}
+
+function makeBlob(src) {
+ return function () {
+ return new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", src);
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+ }
+}
+
+var imageSourceTypes = [
+ { name: 'an HTMLCanvasElement', factory: makeCanvas },
+ { name: 'an HTMLVideoElement', factory: makeVideo },
+ { name: 'an HTMLVideoElement from a data URL', factory: makeDataUrlVideo },
+ { name: 'a bitmap HTMLImageElement', factory: makeMakeHTMLImage("/images/pattern.png") },
+ { name: 'a vector HTMLImageElement', factory: makeMakeHTMLImage("/images/pattern.svg") },
+ { name: 'a bitmap SVGImageElement', factory: makeMakeSVGImage("/images/pattern.png") },
+ { name: 'a vector SVGImageElement', factory: makeMakeSVGImage("/images/pattern.svg") },
+ { name: 'an OffscreenCanvas', factory: makeOffscreenCanvas },
+ { name: 'an ImageData', factory: makeImageData },
+ { name: 'an ImageBitmap', factory: makeImageBitmap },
+ { name: 'a Blob', factory: makeBlob("/images/pattern.png") },
+];
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-blob-invalidtype.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-blob-invalidtype.html
new file mode 100644
index 0000000000..23af96408a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-blob-invalidtype.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<title>createImageBitmap: blob with wrong mime type</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<script>
+promise_test(t => {
+ // Source: https://commons.wikimedia.org/wiki/File:1x1.png (Public Domain)
+ const IMAGE = atob("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAA" +
+ "ACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII=");
+
+ let bytes = new Array(IMAGE.length);
+ for (let i = 0; i < IMAGE.length; i++) {
+ bytes[i] = IMAGE.charCodeAt(i);
+ }
+
+ let blob = new Blob([new Uint8Array(bytes)], { type: "text/html"});
+
+ return window.createImageBitmap(blob)
+ .then(imageBitmap => {
+ assert_true(true, "Image created!");
+ assert_equals(imageBitmap.width, 1, "Image is 1x1");
+ assert_equals(imageBitmap.height, 1, "Image is 1x1");
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-bounds.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-bounds.html
new file mode 100644
index 0000000000..a2dcf0cc0b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-bounds.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<html>
+<title>createImageBitmap: clipping to the bitmap</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<div id="results"></div>
+<script>
+const color = 204;
+function testClip( name, sx, sy, sw, sh, expectedColors, expectedWidth, expectedHeight ) {
+ promise_test(function(t) {
+ return new Promise(function(resolve, reject) {
+ const image = new Image();
+ image.onload = function() { resolve(image); };
+ image.onerror = function() { reject(); };
+ image.src = "/images/green-16x16.png";
+ }).then(function(image) {
+ return createImageBitmap(image, sx, sy, sw, sh);
+ }).then(function(imageBitmap) {
+
+ assert_equals(imageBitmap.width, expectedWidth);
+ assert_equals(imageBitmap.height, expectedHeight);
+
+ const canvas = document.createElement("canvas");
+ canvas.width = 16;
+ canvas.height = 16;
+
+ // debug
+ document.getElementById("results").append(canvas);
+ canvas.setAttribute("style", "width: 100px; height: 100px;");
+
+ const ctx = canvas.getContext("2d");
+ ctx.fillStyle = `rgb(${color}, ${color}, ${color})`;
+ ctx.fillRect(0, 0, 20, 20);
+ ctx.drawImage(imageBitmap, 0, 0);
+
+ for (let [x, y, r, g, b, a] of expectedColors) {
+ _assertPixel(canvas, x,y, r,g,b,a);
+ }
+ });
+ }, name);
+}
+testClip( "simple clip inside",
+ 8, 8, 8, 8, [
+ [ 4, 4, 0,255,0,255], [12, 4, color,color,color,255],
+ [ 4, 12, color,color,color,255], [12, 12, color,color,color,255]
+ ], 8, 8
+);
+testClip( "clip outside negative",
+ -8, -8, 16, 16, [
+ [ 4, 4, color,color,color,255], [12, 4, color,color,color,255],
+ [ 4, 12, color,color,color,255], [12, 12, 0,255,0,255]
+ ], 16, 16
+);
+testClip( "clip outside positive",
+ 8, 8, 16, 16, [
+ [ 4, 4, 0,255,0,255], [12, 4, color,color,color,255],
+ [ 4, 12, color,color,color,255], [12, 12, color,color,color,255]
+ ], 16, 16
+);
+testClip( "clip inside using negative width and height",
+ 24, 24, -16, -16, [
+ [ 4, 4, 0,255,0,255], [12, 4, color,color,color,255],
+ [ 4, 12, color,color,color,255], [12, 12, color,color,color,255]
+ ], 16, 16
+);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-colorSpaceConversion.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-colorSpaceConversion.html
new file mode 100644
index 0000000000..2ddf3648f5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-colorSpaceConversion.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html>
+<title>Test colorSpaceConversion option for createImageBitmap</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<script src="/common/media.js"></script>
+<script src="common.sub.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body>
+<script>
+function testCanvasDisplayingPattern(canvas, width, height)
+{
+ var tolerance = 10; // high tolerance for differing color management results
+ const check = (x, y, r, g, b, a) =>
+ _assertPixelApprox(canvas, x,y, r,g,b,a, tolerance);
+
+ check(1 * width / 4, 1 * height / 4, 124,0,27,255);
+ check(3 * width / 4, 1 * height / 4, 0,124,46,255);
+ check(1 * width / 4, 3 * height / 4, 60,0,123,255);
+ check(3 * width / 4, 3 * height / 4, 0,0,0,255);
+}
+
+function testDrawImageBitmap(source, options)
+{
+ var canvas = document.createElement("canvas");
+ canvas.width = 20;
+ canvas.height = 20;
+ var ctx = canvas.getContext("2d");
+ return createImageBitmap(source, options).then(imageBitmap => {
+ ctx.drawImage(imageBitmap, 0, 0);
+ testCanvasDisplayingPattern(canvas, 20, 20);
+ });
+}
+
+var wideGamutImageSourceTypes = [
+ {name: 'a bitmap HTMLImageElement', factory: makeMakeHTMLImage("/images/wide-gamut-pattern.png")},
+ {name: 'a Blob', factory: makeBlob("/images/wide-gamut-pattern.png")},
+];
+
+for (let { name, factory } of wideGamutImageSourceTypes) {
+ promise_test(function() {
+ return factory().then(function(img) {
+ return testDrawImageBitmap(img, {colorSpaceConversion: "none"});
+ });
+ }, `createImageBitmap from ${name}, and drawImage on the created ImageBitmap with colorSpaceConversion: none`);
+}
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-drawImage-closed.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-drawImage-closed.html
new file mode 100644
index 0000000000..3b8644cff5
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-drawImage-closed.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<title>attempt to draw a closed ImageBitmap to a 2d canvas throws INVALID_STATE_ERR</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<script>
+promise_test(function(t) {
+ return new Promise(function(resolve, reject) {
+ const image = new Image();
+ image.onload = function() { resolve(image); };
+ image.onerror = function() { reject(); };
+ image.src = "/images/green-16x16.png";
+ }).then(function(image) {
+ return createImageBitmap(image, 0, 0, 16, 16);
+ }).then(function(imageBitmap) {
+ imageBitmap.close();
+
+ const canvas = document.createElement("canvas");
+ canvas.width = 16;
+ canvas.height = 16;
+
+ const ctx = canvas.getContext("2d");
+ assert_throws_dom("InvalidStateError", function() { ctx.drawImage(imageBitmap, 0, 0); });
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-drawImage.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-drawImage.html
new file mode 100644
index 0000000000..5b5698813a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-drawImage.html
@@ -0,0 +1,87 @@
+<!DOCTYPE html>
+<html>
+<title>createImageBitmap + drawImage test</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<script src="/common/media.js"></script>
+<script src="common.sub.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body>
+<script>
+function testCanvasDisplayingPattern(canvas, width, height, sourceIsVideo)
+{
+ var tolerance = 3;
+ let topLeft = [255, 0, 0, 255];
+ let topRight = [0, 255, 0, 255];
+ let bottomLeft = [0, 0, 255, 255];
+ let bottomRight = [0, 0, 0, 255];
+ if (sourceIsVideo) {
+ // The source video uses colors in the Rec.601 color space whose
+ // values are close to full red, full green, full blue, and black,
+ // but when converted to sRGB, are somewhat different.
+ topLeft = [247, 37, 0, 255];
+ topRight = [63, 251, 0, 255];
+ bottomLeft = [28, 35, 255, 255];
+ bottomRight = [5, 0, 2, 255];
+ }
+ const check = (x, y, [r, g, b, a]) =>
+ _assertPixelApprox(canvas, x,y, r,g,b,a, tolerance);
+ check(1 * width / 4, 1 * height / 4, topLeft);
+ check(3 * width / 4, 1 * height / 4, topRight);
+ check(1 * width / 4, 3 * height / 4, bottomLeft);
+ check(3 * width / 4, 3 * height / 4, bottomRight);
+}
+
+function testDrawImageBitmap(source, args = [], { resizeWidth = 20, resizeHeight = 20 } = {})
+{
+ let sourceIsVideo = source instanceof HTMLVideoElement;
+ var canvas = document.createElement("canvas");
+ canvas.width = resizeWidth;
+ canvas.height = resizeHeight;
+ var ctx = canvas.getContext("2d");
+ return createImageBitmap(source, ...args).then(imageBitmap => {
+ assert_equals(imageBitmap.width, resizeWidth);
+ assert_equals(imageBitmap.height, resizeHeight);
+ ctx.drawImage(imageBitmap, 0, 0);
+ testCanvasDisplayingPattern(canvas, resizeWidth, resizeHeight, sourceIsVideo);
+ });
+}
+
+for (let { name, factory } of imageSourceTypes) {
+ promise_test(function() {
+ return factory().then(function(img) {
+ return testDrawImageBitmap(img);
+ });
+ }, `createImageBitmap from ${name}, and drawImage on the created ImageBitmap`);
+
+ promise_test(function() {
+ return factory().then(function(img) {
+ const options = { resizeWidth: 10, resizeHeight: 10 };
+ return testDrawImageBitmap(img, [options], options);
+ });
+ }, `createImageBitmap from ${name} scaled down, and drawImage on the created ImageBitmap`);
+
+ promise_test(function() {
+ return factory().then(function(img) {
+ const options = { resizeWidth: 40, resizeHeight: 40 };
+ return testDrawImageBitmap(img, [options], options);
+ });
+ }, `createImageBitmap from ${name} scaled up, and drawImage on the created ImageBitmap`);
+
+ promise_test(function() {
+ return factory().then(function(img) {
+ const options = { resizeWidth: 10, resizeHeight: 40 };
+ return testDrawImageBitmap(img, [options], options);
+ });
+ }, `createImageBitmap from ${name} resized, and drawImage on the created ImageBitmap`);
+
+ promise_test(function() {
+ return factory().then(function(img) {
+ return testDrawImageBitmap(img, [20, 20, -20, -20]);
+ });
+ }, `createImageBitmap from ${name} with negative sw/sh, and drawImage on the created ImageBitmap`);
+}
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-exif-orientation.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-exif-orientation.html
new file mode 100644
index 0000000000..8b2a33e85b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-exif-orientation.html
@@ -0,0 +1,121 @@
+<!DOCTYPE html>
+<title>Test that createImageBitmap honors EXIF orientation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>canvas { outline: 1px solid black; margin-right: 1em; }</style>
+<body>
+<script>
+function loadImage(src) {
+ return new Promise(function(resolve) {
+ const image = new Image();
+ image.addEventListener("load", () => resolve(image), { once: true });
+ image.src = src;
+ });
+}
+
+function checkColors(ctx, w, h, expectedColors) {
+ let data = ctx.getImageData(0, 0, w, h).data;
+ for (let [row, col, r, g, b, a] of expectedColors) {
+ let x = col * 80 + 40;
+ let y = row * 80 + 40;
+ let i = (x + y * w) * 4;
+
+ let expected = [r, g, b, a];
+ let actual = [data[i], data[i + 1], data[i + 2], data[i + 3]];
+
+ assert_array_approx_equals(actual, expected, 1, `Pixel value at (${x},${y}) ${expected} =~ ${actual}.`);
+ }
+}
+
+async_test(function(t) {
+ const canvas = document.createElement("canvas");
+ canvas.width = 320;
+ canvas.height = 160;
+ document.body.append(canvas);
+
+ const ctx = canvas.getContext("2d");
+ loadImage("resources/squares_6.jpg")
+ .then((image) => createImageBitmap(image))
+ .then(t.step_func_done(function(imageBitmap) {
+ ctx.drawImage(imageBitmap, 0, 0);
+ checkColors(ctx, canvas.width, canvas.height, [
+ // row, col, r, g, b, a
+ [0, 0, 255, 0, 0, 255],
+ [0, 1, 0, 255, 0, 255],
+ [0, 2, 0, 0, 255, 255],
+ [0, 3, 0, 0, 0, 255],
+ [1, 0, 255, 128, 128, 255],
+ [1, 1, 128, 255, 128, 255],
+ [1, 2, 128, 128, 255, 255],
+ [1, 3, 128, 128, 128, 255],
+ ]);
+ }));
+}, "createImageBitmap with EXIF rotation, imageOrientation from-image, and no cropping");
+
+async_test(function(t) {
+ const canvas = document.createElement("canvas");
+ canvas.width = 320;
+ canvas.height = 160;
+ document.body.append(canvas);
+
+ const ctx = canvas.getContext("2d");
+ loadImage("resources/squares_6.jpg")
+ .then((image) => createImageBitmap(image, { imageOrientation: "flipY" }))
+ .then(t.step_func_done(function(imageBitmap) {
+ ctx.drawImage(imageBitmap, 0, 0);
+ checkColors(ctx, canvas.width, canvas.height, [
+ // row, col, r, g, b, a
+ [0, 0, 255, 128, 128, 255],
+ [0, 1, 128, 255, 128, 255],
+ [0, 2, 128, 128, 255, 255],
+ [0, 3, 128, 128, 128, 255],
+ [1, 0, 255, 0, 0, 255],
+ [1, 1, 0, 255, 0, 255],
+ [1, 2, 0, 0, 255, 255],
+ [1, 3, 0, 0, 0, 255],
+ ]);
+ }));
+}, "createImageBitmap with EXIF rotation, imageOrientation flipY, and no cropping");
+
+async_test(function(t) {
+ const canvas = document.createElement("canvas");
+ canvas.width = 160;
+ canvas.height = 160;
+ document.body.append(canvas);
+
+ const ctx = canvas.getContext("2d");
+ loadImage("resources/squares_6.jpg")
+ .then(image => createImageBitmap(image, 80, 0, 160, 160))
+ .then(t.step_func_done(function(imageBitmap) {
+ ctx.drawImage(imageBitmap, 0, 0);
+ checkColors(ctx, canvas.width, canvas.height, [
+ // row, col, r, g, b, a
+ [0, 0, 0, 255, 0, 255],
+ [0, 1, 0, 0, 255, 255],
+ [1, 0, 128, 255, 128, 255],
+ [1, 1, 128, 128, 255, 255],
+ ]);
+ }));
+}, "createImageBitmap with EXIF rotation, imageOrientation from-image, and cropping");
+
+async_test(function(t) {
+ const canvas = document.createElement("canvas");
+ canvas.width = 160;
+ canvas.height = 160;
+ document.body.append(canvas);
+
+ const ctx = canvas.getContext("2d");
+ loadImage("resources/squares_6.jpg")
+ .then(image => createImageBitmap(image, 80, 0, 160, 160, { imageOrientation: "flipY" }))
+ .then(t.step_func_done(function(imageBitmap) {
+ ctx.drawImage(imageBitmap, 0, 0);
+ checkColors(ctx, canvas.width, canvas.height, [
+ // row, col, r, g, b, a
+ [0, 0, 128, 255, 128, 255],
+ [0, 1, 128, 128, 255, 255],
+ [1, 0, 0, 255, 0, 255],
+ [1, 1, 0, 0, 255, 255],
+ ]);
+ }));
+}, "createImageBitmap with EXIF rotation, imageOrientation flipY, and cropping");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-exif-orientation_none.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-exif-orientation_none.html
new file mode 100644
index 0000000000..807925b88a
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-exif-orientation_none.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<title>Test that createImageBitmap honors EXIF orientation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>canvas { outline: 1px solid black; margin-right: 1em; }</style>
+<body>
+<script>
+function loadImage(src) {
+ return new Promise(function(resolve) {
+ const image = new Image();
+ image.addEventListener("load", () => resolve(image), { once: true });
+ image.src = src;
+ });
+}
+
+function checkColors(ctx, w, h, is_verticle, expectedColors) {
+ let data = ctx.getImageData(0, 0, w, h).data;
+ row_width = 80;
+ col_width = 80;
+
+ for (let [row, col, r, g, b, a] of expectedColors) {
+ let x = col * row_width + 10;
+ let y = row * col_width + 10;
+ let i = (x + y * w) * 4;
+
+ let expected = [r, g, b, a];
+ let actual = [data[i], data[i + 1], data[i + 2], data[i + 3]];
+
+ assert_array_approx_equals(actual, expected, 1, `Pixel value at (${x},${y}) ${expected} =~ ${actual}.`);
+ }
+}
+
+for (let orientation of [1, 2, 3, 4, 5, 6, 7, 8]) {
+ async_test(function(t) {
+ const canvas = document.createElement("canvas");
+ canvas.width = 160;
+ canvas.height = 320;
+ document.body.append(canvas);
+
+ const ctx = canvas.getContext("2d");
+ loadImage(`resources/squares_${orientation}.jpg`)
+ .then((image) => createImageBitmap(image, { imageOrientation: "none" }))
+ .then(t.step_func_done(function(imageBitmap) {
+ ctx.drawImage(imageBitmap, 0, 0, 160, 320);
+
+ checkColors(ctx, canvas.width, canvas.height, false, [
+ // row, col, r, g, b, a
+ [0, 0, 0, 0, 0, 255],
+ [0, 1, 128, 128, 128, 255],
+ [1, 0, 0, 0, 255, 255],
+ [1, 1, 128, 128, 255, 255],
+ [2, 0, 0, 255, 0, 255],
+ [2, 1, 128, 255, 128, 255],
+ [3, 0, 255, 0, 0, 255],
+ [3, 1, 255, 128, 128, 255],
+ ]);
+ }));
+ }, `createImageBitmap with Orientation ${orientation}`);
+}
+
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-flipY.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-flipY.html
new file mode 100644
index 0000000000..9b0d2dfb79
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-flipY.html
@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+<html>
+<title>createImageBitmap + drawImage test</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<script src="/common/media.js"></script>
+<script src="common.sub.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body>
+<script>
+function testCanvasDisplayingPattern(canvas, width, height, sourceIsVideo, flipped)
+{
+ var tolerance = 3;
+ let topLeft = [255, 0, 0, 255];
+ let topRight = [0, 255, 0, 255];
+ let bottomLeft = [0, 0, 255, 255];
+ let bottomRight = [0, 0, 0, 255];
+ if (sourceIsVideo) {
+ // The source video uses colors in the Rec.601 color space whose
+ // values are close to full red, full green, full blue, and black,
+ // but when converted to sRGB, are somewhat different.
+ topLeft = [247, 37, 0, 255];
+ topRight = [63, 251, 0, 255];
+ bottomLeft = [28, 35, 255, 255];
+ bottomRight = [5, 0, 2, 255];
+ }
+ if (flipped) {
+ [topLeft, bottomLeft] = [bottomLeft, topLeft];
+ [topRight, bottomRight] = [bottomRight, topRight];
+ }
+ const check = (x, y, [r, g, b, a]) =>
+ _assertPixelApprox(canvas, x,y, r,g,b,a, tolerance);
+ check(1 * width / 4, 1 * height / 4, topLeft);
+ check(3 * width / 4, 1 * height / 4, topRight);
+ check(1 * width / 4, 3 * height / 4, bottomLeft);
+ check(3 * width / 4, 3 * height / 4, bottomRight);
+}
+
+function testDrawImageBitmap(source, args = [], flipped, { resizeWidth = 20, resizeHeight = 20 } = {})
+{
+ let sourceIsVideo = source instanceof HTMLVideoElement;
+ var canvas = document.createElement("canvas");
+ canvas.width = resizeWidth;
+ canvas.height = resizeHeight;
+ var ctx = canvas.getContext("2d");
+ return createImageBitmap(source, ...args).then(imageBitmap => {
+ assert_equals(imageBitmap.width, resizeWidth);
+ assert_equals(imageBitmap.height, resizeHeight);
+ ctx.drawImage(imageBitmap, 0, 0);
+ testCanvasDisplayingPattern(canvas, resizeWidth, resizeHeight, sourceIsVideo, flipped);
+ });
+}
+
+for (let { name, factory } of imageSourceTypes) {
+ promise_test(function() {
+ return factory().then(function(img) {
+ const options = { imageOrientation: 'from-image' };
+ return testDrawImageBitmap(img, [options], false);
+ });
+ }, `createImageBitmap from ${name} imageOrientation: "from-image", and drawImage on the created ImageBitmap`);
+
+ promise_test(function() {
+ return factory().then(function(img) {
+ const options = { imageOrientation: 'flipY' };
+ return testDrawImageBitmap(img, [options], true);
+ });
+ }, `createImageBitmap from ${name} imageOrientation: "flipY", and drawImage on the created ImageBitmap`);
+
+}
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-in-worker-transfer.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-in-worker-transfer.html
new file mode 100644
index 0000000000..727a8a4978
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-in-worker-transfer.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>createImageBitmap in worker and transfer</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+promise_test(function(t) {
+ return new Promise(function(resolve, reject) {
+ var worker = new Worker("createImageBitmap-worker.js");
+ worker.addEventListener("message", function(evt) {
+ var bitmap = evt.data;
+ assert_equals(bitmap.width, 20);
+ assert_equals(bitmap.height, 20);
+ resolve();
+ });
+ worker.postMessage('test');
+ });
+}, 'Transfer ImageBitmap created in worker');
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-invalid-args.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-invalid-args.html
new file mode 100644
index 0000000000..3cae3523ef
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-invalid-args.html
@@ -0,0 +1,239 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/media.js"></script>
+<script src="common.sub.js"></script>
+<script>
+
+function makeOversizedCanvas() {
+
+ return new Promise(resolve => {
+ let canvas = document.createElement('canvas');
+ canvas.setAttribute('width', '100000000');
+ canvas.setAttribute('height', '100000000');
+ resolve(canvas);
+ });
+}
+
+function makeOversizedOffscreenCanvas() {
+ return new Promise(resolve =>{
+ let canvas = new OffscreenCanvas(100000000, 100000000);
+ resolve(canvas);
+ });
+}
+
+function makeInvalidBlob() {
+ return new Promise(resolve => {
+ resolve(new Blob()); // Blob with no data cannot be decoded.
+ });
+}
+
+function makeBrokenImage() {
+ return new Promise((resolve, reject) => {
+ const image = new Image();
+ image.src = "data:,x";
+ image.onload = reject;
+ image.onerror = () => resolve(image);
+ });
+}
+
+function makeAvailableButBrokenImage(path) {
+ return new Promise((resolve, reject) => {
+ const image = new Image();
+ image.src = path;
+ image.onload = () => resolve(image);
+ image.onerror = reject;
+ });
+}
+
+testCases = [
+ {
+ description: 'createImageBitmap with <sourceType> source and sw set to 0',
+ promiseTestFunction:
+ (source, t) => {
+ return promise_rejects_js(t, RangeError,
+ createImageBitmap(source, 0, 0, 0, 10));
+ }
+ },
+ {
+ description: 'createImageBitmap with <sourceType> source and sh set to 0',
+ promiseTestFunction:
+ (source, t) => {
+ return promise_rejects_js(t, RangeError,
+ createImageBitmap(source, 0, 0, 10, 0));
+ }
+ },
+ {
+ description: 'createImageBitmap with <sourceType> source and oversized ' +
+ '(unallocatable) crop region',
+ promiseTestFunction:
+ (source, t) => {
+ return createImageBitmap(source, 0, 0, 100000000, 100000000)
+ .then(i => {
+ assert_equals(i.width, 100000000);
+ assert_equals(i.height, 100000000);
+ })
+ .catch(e => {
+ // This case is not explicitly documented in the specification for
+ // createImageBitmap, but it is expected that internal failures
+ // cause InvalidStateError.
+ //
+ // Note: https://bugs.chromium.org/p/chromium/issues/detail?id=799025
+ assert_throws_dom("InvalidStateError", () => { throw e });
+ });
+ }
+ },
+ {
+ description: 'createImageBitmap with <sourceType> source and ' +
+ 'a value of 0 int resizeWidth',
+ promiseTestFunction:
+ (source, t) => {
+ return createImageBitmap(source, {resizeWidth:0, resizeHeight:10})
+ .catch(e => {
+ assert_throws_dom("InvalidStateError", () => { throw e });
+ });
+ }
+ },
+ {
+ description: 'createImageBitmap with <sourceType> source and ' +
+ 'a value of 0 in resizeHeight',
+ promiseTestFunction:
+ (source, t) => {
+ return createImageBitmap(source, {resizeWidth:10, resizeHeight:0})
+ .catch(e => {
+ assert_throws_dom("InvalidStateError", () => { throw e });
+ });
+ }
+ },
+ {
+ description: 'createImageBitmap with <sourceType> source and ' +
+ 'a value between 0 and 1 in resizeWidth',
+ promiseTestFunction:
+ (source, t) => {
+ return createImageBitmap(source, {resizeWidth:0.5, resizeHeight:10})
+ .catch(e => {
+ assert_throws_dom("InvalidStateError", () => { throw e });
+ });
+ }
+ },
+ {
+ description: 'createImageBitmap with <sourceType> source and ' +
+ 'a value between 0 and 1 in resizeHeight',
+ promiseTestFunction:
+ (source, t) => {
+ return createImageBitmap(source, {resizeWidth:10, resizeHeight:0.5})
+ .catch(e => {
+ assert_throws_dom("InvalidStateError", () => { throw e });
+ });
+ }
+ },
+];
+
+// Generate the test matrix for each sourceType + testCase combo.
+imageSourceTypes.forEach(imageSourceType => {
+ testCases.forEach(testCase => {
+ let description = testCase.description.replace('<sourceType>',
+ imageSourceType.name);
+ promise_test( t => {
+ return imageSourceType.factory().then(source => {
+ return testCase.promiseTestFunction(source, t);
+ });
+ }, description);
+ });
+});
+
+promise_test( t => {
+ return promise_rejects_js(t, TypeError, createImageBitmap(undefined));
+}, "createImageBitmap with undefined image source.");
+
+promise_test( t => {
+ return promise_rejects_js(t, TypeError, createImageBitmap(null));
+}, "createImageBitmap with null image source.");
+
+promise_test( t => {
+ var context = document.createElement("canvas").getContext("2d");
+ return promise_rejects_js(t, TypeError, createImageBitmap(context));
+}, "createImageBitmap with CanvasRenderingContext2D image source.");
+
+promise_test( t => {
+ var context = document.createElement("canvas").getContext("webgl");
+ return promise_rejects_js(t, TypeError, createImageBitmap(context));
+}, "createImageBitmap with WebGLRenderingContext image source.");
+
+promise_test( t => {
+ var buffer = new Uint8Array();
+ return promise_rejects_js(t, TypeError, createImageBitmap(buffer));
+}, "createImageBitmap with Uint8Array image source.");
+
+promise_test( t => {
+ var buffer = new ArrayBuffer(8);
+ return promise_rejects_js(t, TypeError, createImageBitmap(buffer));
+}, "createImageBitmap with ArrayBuffer image source.");
+
+promise_test( t => {
+ return promise_rejects_dom(t, "InvalidStateError",
+ createImageBitmap(new Image()));
+}, "createImageBitmap with empty image source.");
+
+promise_test( t => {
+ return promise_rejects_dom(t, "InvalidStateError",
+ createImageBitmap(document.createElement('video')));
+}, "createImageBitmap with empty video source.");
+
+promise_test( t => {
+ return makeOversizedCanvas().then(canvas => {
+ return promise_rejects_dom(t, "InvalidStateError",
+ createImageBitmap(canvas));
+ });
+}, "createImageBitmap with an oversized canvas source.");
+
+promise_test( t => {
+ return makeOversizedOffscreenCanvas().then(offscreenCanvas => {
+ return promise_rejects_dom(t, "InvalidStateError",
+ createImageBitmap(offscreenCanvas));
+ });
+}, "createImageBitmap with an invalid OffscreenCanvas source.");
+
+promise_test( t => {
+ return makeInvalidBlob().then(blob => {
+ return promise_rejects_dom(t, "InvalidStateError",
+ createImageBitmap(blob));
+ });
+}, "createImageBitmap with an undecodable blob source.");
+
+promise_test( t => {
+ return makeBrokenImage().then(image => {
+ return promise_rejects_dom(t, "InvalidStateError",
+ createImageBitmap(image));
+ });
+}, "createImageBitmap with a broken image source.");
+
+promise_test( t => {
+ return makeAvailableButBrokenImage("/images/undecodable.png").then(image => {
+ return promise_rejects_dom(t, "InvalidStateError",
+ createImageBitmap(image));
+ });
+}, "createImageBitmap with an available but undecodable image source.");
+
+promise_test( t => {
+ return makeAvailableButBrokenImage("/images/red-zeroheight.svg").then(image => {
+ return promise_rejects_dom(t, "InvalidStateError",
+ createImageBitmap(image));
+ });
+}, "createImageBitmap with an available but zero height image source.");
+
+promise_test( t => {
+ return makeAvailableButBrokenImage("/images/red-zerowidth.svg").then(image => {
+ return promise_rejects_dom(t, "InvalidStateError",
+ createImageBitmap(image));
+ });
+}, "createImageBitmap with an available but zero width image source.");
+
+promise_test( t => {
+ return makeImageBitmap().then(bitmap => {
+ bitmap.close()
+ return promise_rejects_dom(t, "InvalidStateError",
+ createImageBitmap(bitmap));
+ });
+}, "createImageBitmap with a closed ImageBitmap.");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-origin.sub.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-origin.sub.html
new file mode 100644
index 0000000000..ae3d23cfbc
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-origin.sub.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>createImageBitmap: origin-clean flag</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/media.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<div id=log></div>
+<script>
+
+function assert_origin_unclean_getImageData(bitmap) {
+ const context = document.createElement("canvas").getContext("2d");
+ context.drawImage(bitmap, 0, 0);
+ assert_throws_dom("SecurityError", () => {
+ context.getImageData(0, 0, 1, 1);
+ });
+}
+
+function assert_origin_unclean_drawImage(bitmap) {
+ const canvas = document.createElement('canvas');
+ const ctx = canvas.getContext('2d');
+ ctx.drawImage(bitmap, 0, 0);
+ assert_throws_dom('SecurityError', () => canvas.toDataURL());
+}
+
+function assert_origin_unclean_transferFromImageBitmap(bitmap) {
+ var canvas = document.createElement('canvas');
+ var ctx = canvas.getContext('bitmaprenderer');
+ ctx.transferFromImageBitmap(bitmap);
+ assert_throws_dom('SecurityError', () => canvas.toDataURL());
+}
+
+forEachCanvasSource(get_host_info().HTTP_REMOTE_ORIGIN,
+ get_host_info().HTTP_ORIGIN,
+ (name, factory) => {
+ promise_test(function() {
+ return factory().then(createImageBitmap).then(assert_origin_unclean_getImageData);
+ }, `${name}: origin unclear getImageData`);
+ promise_test(function() {
+ return factory().then(createImageBitmap).then(assert_origin_unclean_drawImage);
+ }, `${name}: origin unclear 2dContext.drawImage`);
+ promise_test(function() {
+ return factory().then(createImageBitmap).then(assert_origin_unclean_transferFromImageBitmap);
+ }, `${name}: origin unclear bitmaprenderer.transferFromImageBitmap`);
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-serializable.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-serializable.html
new file mode 100644
index 0000000000..c185cd9cbd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-serializable.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>createImageBitmap serialize test</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/namespaces.js"></script>
+<script src="/common/media.js"></script>
+<script src="common.sub.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<div id=log></div>
+<script>
+let worker, continuations = {};
+setup(function() {
+ worker = new Worker("transfer-worker.js");
+ worker.addEventListener("message", function(event) {
+ let { name, bitmap } = event.data;
+ if (continuations.hasOwnProperty(name)) {
+ continuations[name](bitmap);
+ }
+ });
+});
+
+for (let { name, factory } of imageSourceTypes) {
+ promise_test(function(t) {
+ return factory().then(createImageBitmap).then(function(bitmap) {
+ assert_equals(bitmap.width, 20);
+ assert_equals(bitmap.height, 20);
+
+ worker.postMessage({ name: t.name, bitmap: bitmap });
+
+ assert_equals(bitmap.width, 20);
+ assert_equals(bitmap.height, 20);
+
+ return new Promise(function(resolve) {
+ continuations[t.name] = resolve;
+ });
+ }).then(function(bitmap) {
+ assert_class_string(bitmap, "ImageBitmap");
+ assert_equals(bitmap.width, 20);
+ assert_equals(bitmap.height, 20);
+ });
+ }, `Serialize ImageBitmap created from ${name}`);
+}
+
+promise_test(async (t) => {
+ const url = get_host_info().REMOTE_ORIGIN + '/images/pattern.png';
+ const image = await makeMakeHTMLImage(url)();
+ const bitmap = await createImageBitmap(image);
+
+ assert_throws_dom('DataCloneError', () => worker.postMessage(bitmap));
+}, 'Serializing a non-origin-clean ImageBitmap throws.');
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-sizeOverflow.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-sizeOverflow.html
new file mode 100644
index 0000000000..25c1fb6885
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-sizeOverflow.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<html>
+<title>createImageBitmap with size overflow</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+promise_test(function() {
+ var imgData = new ImageData(20, 20);
+ return createImageBitmap(imgData, 4294967400, 10, 10, 10);
+}, "createImageBitmap does not crash or reject the promise when passing very large sx");
+
+promise_test(function() {
+ var imgData = new ImageData(20, 20);
+ return createImageBitmap(imgData, 10, 4294967400, 10, 10);
+}, "createImageBitmap does not crash or reject the promise when passing very large sy");
+
+promise_test(function() {
+ var imgData = new ImageData(20, 20);
+ return createImageBitmap(imgData, 10, 10, 4294967400, 10);
+}, "createImageBitmap does not crash or reject the promise when passing very large sw");
+
+promise_test(function() {
+ var imgData = new ImageData(20, 20);
+ return createImageBitmap(imgData, 10, 10, 10, 4294967400);
+}, "createImageBitmap does not crash or reject the promise when passing very large sh");
+
+promise_test(function() {
+ var imgData = new ImageData(20, 20);
+ return createImageBitmap(imgData, 4294967400, 4294967400, 4294967400, 4294967400);
+}, "createImageBitmap does not crash or reject the promise when passing very large sx, sy, sw and sh");
+
+promise_test(function(t) {
+ var imgData = new ImageData(20, 20);
+ var imageBitmapOptions = {imageOrientation:'from-image', premultiplyAlpha:'default',
+ colorSpaceConversion:'none', resizeHeight:2122252543, resizeQuality:'high'};
+ return createImageBitmap(imgData, 0, 0, 4294967295, 64)
+ .then(imageBitmap => promise_rejects_dom(t, "InvalidStateError",
+ createImageBitmap(imageBitmap, imageBitmapOptions)));
+}, "createImageBitmap throws an InvalidStateError error with big imageBitmap scaled up in big height");
+
+promise_test(function(t) {
+ var imgData = new ImageData(20, 20);
+ var imageBitmapOptions = {imageOrientation:'from-image', premultiplyAlpha:'default',
+ colorSpaceConversion:'none', resizeWidth:2122252543, resizeQuality:'high'};
+ return createImageBitmap(imgData, 0, 0, 4294967295, 64)
+ .then(imageBitmap => promise_rejects_dom(t, "InvalidStateError",
+ createImageBitmap(imageBitmap, imageBitmapOptions)));
+}, "createImageBitmap throws an InvalidStateError error with big imageBitmap scaled up in big width");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-transfer.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-transfer.html
new file mode 100644
index 0000000000..3ec02fcbf4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-transfer.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>createImageBitmap transferring test</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/media.js"></script>
+<script src="common.sub.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<div id=log></div>
+<script>
+let worker, continuations = {};
+setup(function() {
+ worker = new Worker("transfer-worker.js");
+ worker.addEventListener("message", function(event) {
+ let { name, bitmap } = event.data;
+ if (continuations.hasOwnProperty(name)) {
+ continuations[name](bitmap);
+ }
+ });
+});
+
+for (let { name, factory } of imageSourceTypes) {
+ promise_test(function(t) {
+ return factory().then(createImageBitmap).then(function(bitmap) {
+ assert_equals(bitmap.width, 20);
+ assert_equals(bitmap.height, 20);
+
+ worker.postMessage({ name: t.name, bitmap: bitmap }, [bitmap]);
+
+ assert_equals(bitmap.width, 0);
+ assert_equals(bitmap.height, 0);
+
+ return new Promise(function(resolve) {
+ continuations[t.name] = resolve;
+ });
+ }).then(function(bitmap) {
+ assert_class_string(bitmap, "ImageBitmap");
+ assert_equals(bitmap.width, 20);
+ assert_equals(bitmap.height, 20);
+ });
+ }, `Transfer ImageBitmap created from ${name}`);
+}
+
+promise_test(async (t) => {
+ const url = get_host_info().REMOTE_ORIGIN + '/images/pattern.png';
+ const image = await makeMakeHTMLImage(url)();
+ const bitmap = await createImageBitmap(image);
+
+ assert_throws_dom('DataCloneError',
+ () => worker.postMessage(bitmap, [bitmap]));
+}, 'Transferring a non-origin-clean ImageBitmap throws.');
+
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-worker.js b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-worker.js
new file mode 100644
index 0000000000..67a0904e47
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-worker.js
@@ -0,0 +1,17 @@
+function makeBlob() {
+ return new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", '/images/pattern.png');
+ xhr.responseType = 'blob';
+ xhr.send();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ });
+}
+
+addEventListener("message", () => {
+ makeBlob().then(createImageBitmap).then(bitmap => {
+ postMessage(bitmap, [bitmap]);
+ });
+});
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmap-from-imageData-no-image-rotation-expected.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmap-from-imageData-no-image-rotation-expected.html
new file mode 100644
index 0000000000..64b1791afe
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmap-from-imageData-no-image-rotation-expected.html
@@ -0,0 +1,18 @@
+<html>
+<body>
+ <canvas id="myCanvas" width="400" height="400"></canvas>
+</body>>
+<script>
+var canvas = document.getElementById('myCanvas');
+ctx = canvas.getContext('2d');
+image = document.createElement("img");
+image.src = "../../../resources/black_white.png"
+image.onload = function() {
+ Promise.all([
+ createImageBitmap(image, { imageOrientation: 'flipY' }),
+ ]).then(function(sprites) {
+ // Draw image onto the canvas
+ ctx.drawImage(sprites[0], 0, 0);
+});
+}
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmap-from-imageData-no-image-rotation.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmap-from-imageData-no-image-rotation.html
new file mode 100644
index 0000000000..e93c17c8e8
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmap-from-imageData-no-image-rotation.html
@@ -0,0 +1,26 @@
+<html>
+ <link rel="match" href="imageBitmap-from-imageData-no-image-rotation-expected.html" />
+ <style type="text/css">
+ canvas {
+ image-orientation: none;
+ }
+ </style>
+<body>
+ <canvas id="myCanvas" width="400" height="400"></canvas>
+</body>>
+<script>
+var canvas = document.getElementById('myCanvas');
+ctx = canvas.getContext('2d');
+image = document.createElement("img");
+image.src = "../../../resources/black_white.png"
+image.onload = function() {
+ Promise.all([
+ // The image should be flipped and ignoring "image-orientation" setting
+ // in css style.
+ createImageBitmap(image, { imageOrientation: 'flipY' }),
+ ]).then(function(sprites) {
+ // Draw image onto the canvas
+ ctx.drawImage(sprites[0], 0, 0);
+});
+}
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-expected.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-expected.html
new file mode 100644
index 0000000000..bababda44c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-expected.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<body>
+ <p>Test whether the imageOrientation "from-image" works when creating an ImageBitmap from the ImageData of a canvas, and then transfered to an ImageBitmapRenderingContext.</p>
+ <canvas id="canvas" width="300" height="300"></canvas>
+</body>
+<script>
+
+function drawSquares(ctx) {
+ ctx.fillStyle = 'red';
+ ctx.fillRect(0,0,150,150);
+ ctx.fillStyle = 'green';
+ ctx.fillRect(150,0,300,150);
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(0,150,150,300);
+}
+
+async function runTest() {
+ const canvas = document.getElementById('canvas');
+ canvas.width = 300;
+ canvas.height = 300;
+ const ctx = canvas.getContext('2d');
+ drawSquares(ctx);
+}
+
+runTest();
+
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-flipped-expected.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-flipped-expected.html
new file mode 100644
index 0000000000..5e21671973
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-flipped-expected.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<body>
+ <p>Test whether the imageOrientation "flipY" works when creating an ImageBitmap from the ImageData of a canvas, and then transfered to an ImageBitmapRenderingContext.</p>
+ <canvas id="canvas" width="300" height="300"></canvas>
+</body>
+<script>
+
+function drawSquares(ctx) {
+ ctx.fillStyle = 'red';
+ ctx.fillRect(0,150,150,150);
+ ctx.fillStyle = 'green';
+ ctx.fillRect(150,150,300,150);
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(0,0,150,150);
+}
+
+async function runTest() {
+ const canvas = document.getElementById('canvas');
+ canvas.width = 300;
+ canvas.height = 300;
+ const ctx = canvas.getContext('2d');
+ drawSquares(ctx);
+}
+
+runTest();
+
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-flipped.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-flipped.html
new file mode 100644
index 0000000000..02e0690876
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-flipped.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<link rel="match" href="imageBitmapRendering-transferFromImageBitmap-flipped-expected.html" />
+<body>
+ <p>Test whether the imageOrientation "flipY" works when creating an ImageBitmap from the ImageData of a canvas, and then transfered to an ImageBitmapRenderingContext.</p>
+ <canvas id="canvas" width="300" height="300"></canvas>
+</body>
+<script>
+
+function drawSquares(ctx) {
+ ctx.fillStyle = 'red';
+ ctx.fillRect(0,0,150,150);
+ ctx.fillStyle = 'green';
+ ctx.fillRect(150,0,300,150);
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(0,150,150,300);
+}
+
+async function runTest() {
+ const canvas_temp = document.createElement('canvas');
+ canvas_temp.width = 300;
+ canvas_temp.height = 300;
+ const ctx_temp = canvas_temp.getContext('2d');
+ drawSquares(ctx_temp);
+ const imageSource = ctx_temp.getImageData(0, 0, 300, 300);
+ const imageOrientation = 'flipY';
+ imageIDFlipped = await createImageBitmap(imageSource, 0, 0, 300, 300, { imageOrientation });
+ const canvas = document.getElementById('canvas');
+ const ctx = canvas.getContext('bitmaprenderer');
+ ctx.transferFromImageBitmap(imageIDFlipped);
+}
+
+runTest();
+
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-webgl-expected.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-webgl-expected.html
new file mode 100644
index 0000000000..4f155316f0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-webgl-expected.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+
+<body>
+ <p>
+ Test creating an ImageBitmap from the transferToImageBitmap of a webgl OffscreenCanvas, and then
+ transferred to an ImageBitmapRenderingContext.
+ </p>
+ <canvas id="canvas" width="300" height="300"></canvas>
+</body>
+<script>
+
+ function drawSquares(ctx) {
+ ctx.enable(ctx.SCISSOR_TEST);
+ ctx.scissor(0, 150, 150, 150);
+ ctx.clearColor(1, 0, 0, 1);
+ ctx.clear(ctx.COLOR_BUFFER_BIT);
+ ctx.scissor(150, 150, 300, 150);
+ ctx.clearColor(0, 1, 0, 1);
+ ctx.clear(ctx.COLOR_BUFFER_BIT);
+ ctx.scissor(0, 0, 150, 150);
+ ctx.clearColor(0, 0, 1, 1);
+ ctx.clear(ctx.COLOR_BUFFER_BIT);
+ }
+
+ async function runTest() {
+ const canvas = document.getElementById('canvas');
+ canvas.width = 300;
+ canvas.height = 300;
+ const ctx = canvas.getContext('webgl');
+ drawSquares(ctx);
+ }
+
+ runTest();
+
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-webgl.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-webgl.html
new file mode 100644
index 0000000000..049a3822cd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-webgl.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<link rel="match" href="imageBitmapRendering-transferFromImageBitmap-webgl-expected.html" />
+
+<body>
+ <p>
+ Test creating an ImageBitmap from the transferToImageBitmap of a webgl OffscreenCanvas, and then
+ transferred to an ImageBitmapRenderingContext.
+ </p>
+ <canvas id="canvas" width="300" height="300"></canvas>
+</body>
+<script>
+
+ function drawSquares(ctx) {
+ ctx.enable(ctx.SCISSOR_TEST);
+ ctx.scissor(0, 150, 150, 150);
+ ctx.clearColor(1, 0, 0, 1);
+ ctx.clear(ctx.COLOR_BUFFER_BIT);
+ ctx.scissor(150, 150, 300, 150);
+ ctx.clearColor(0, 1, 0, 1);
+ ctx.clear(ctx.COLOR_BUFFER_BIT);
+ ctx.scissor(0, 0, 150, 150);
+ ctx.clearColor(0, 0, 1, 1);
+ ctx.clear(ctx.COLOR_BUFFER_BIT);
+ }
+
+ async function runTest() {
+ const offscreen = new OffscreenCanvas(300, 300);
+ const ctxOffscreen = offscreen.getContext('webgl');
+ drawSquares(ctxOffscreen);
+ const image = offscreen.transferToImageBitmap();
+ const canvas = document.getElementById('canvas');
+ const ctx = canvas.getContext('bitmaprenderer');
+ ctx.transferFromImageBitmap(image);
+ }
+
+ runTest();
+
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap.html
new file mode 100644
index 0000000000..6d3d886759
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<link rel="match" href="imageBitmapRendering-transferFromImageBitmap-expected.html" />
+<body>
+ <p>Test whether the imageOrientation "from-image" works when creating an ImageBitmap from the ImageData of a canvas, and then transfered to an ImageBitmapRenderingContext.</p>
+ <canvas id="canvas" width="300" height="300"></canvas>
+</body>
+<script>
+
+function drawSquares(ctx) {
+ ctx.fillStyle = 'red';
+ ctx.fillRect(0,0,150,150);
+ ctx.fillStyle = 'green';
+ ctx.fillRect(150,0,300,150);
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(0,150,150,300);
+}
+
+async function runTest() {
+ const canvas_temp = document.createElement('canvas');
+ canvas_temp.width = 300;
+ canvas_temp.height = 300;
+ const ctx_temp = canvas_temp.getContext('2d');
+ drawSquares(ctx_temp);
+ const imageSource = ctx_temp.getImageData(0, 0, 300, 300);
+ const imageOrientation = 'from-image';
+ imageIDFlipped = await createImageBitmap(imageSource, 0, 0, 300, 300, { imageOrientation });
+ const canvas = document.getElementById('canvas');
+ const ctx = canvas.getContext('bitmaprenderer');
+ ctx.transferFromImageBitmap(imageIDFlipped);
+}
+
+runTest();
+
+</script>
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imagebitmap-replication-exif-orientation.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imagebitmap-replication-exif-orientation.html
new file mode 100644
index 0000000000..ab4331adef
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imagebitmap-replication-exif-orientation.html
@@ -0,0 +1,146 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Verify that image orientation is propagated when ImageBitmap objects are replicated.</title>
+<link rel="author" title="Justin Novosad" href="mailto:junov@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+
+<body>
+<script>
+// This test is most relevant for browser implementations that apply EXIF image
+// orientation lazily. That is to say that the transform is applied at rasterization
+// time rather than at image decode time. This implies that image orientation metadata
+// is stored internally in the decoded image's data structure. This test ensures
+// that the orientation metadata is correctly carried over when ImageBitmap objects
+// are replicated (serialized/deserialized, copied or transferred).
+
+function checkImageBitmapRotated(bitmap) {
+ assert_equals(bitmap.width, 320, 'Bitmap width');
+ assert_equals(bitmap.height, 160, 'Bitmap height');
+
+ const canvas = document.createElement('canvas');
+ const ctx = canvas.getContext('2d');
+
+ const expected_colors = [
+ // row, col, r, g, b, a
+ [0, 0, 255, 0, 0, 255],
+ [0, 1, 0, 255, 0, 255],
+ [0, 2, 0, 0, 255, 255],
+ [0, 3, 0, 0, 0, 255],
+ [1, 0, 255, 128, 128, 255],
+ [1, 1, 128, 255, 128, 255],
+ [1, 2, 128, 128, 255, 255],
+ [1, 3, 128, 128, 128, 255],
+ ];
+
+ canvas.width = bitmap.width;
+ canvas.height = bitmap.height;
+ ctx.drawImage(bitmap, 0, 0);
+
+ let data = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
+ for (let [row, col, r, g, b, a] of expected_colors) {
+ let x = col * 80 + 40;
+ let y = row * 80 + 40;
+ let i = (x + y * canvas.width) * 4;
+ let expected = [r, g, b, a];
+ let actual = [data[i], data[i + 1], data[i + 2], data[i + 3]];
+ assert_array_approx_equals(actual, expected, 1, `Pixel value at (${x},${y}) ${expected} =~ ${actual}.`);
+ }
+}
+
+promise_test(async t => {
+ const response = await fetch("resources/squares_6.jpg");
+ const blob = await response.blob();
+ const image = await createImageBitmap(blob)
+ const image_copy = structuredClone(image);
+ checkImageBitmapRotated(image_copy);
+}, "ImageBitmap from file with EXIF rotation, duplicated via structuredClone");
+
+promise_test(async t => {
+ const image = new Image();
+ image.src = "resources/squares_6.jpg"
+ await new Promise(resolve => image.onload = resolve);
+ const image_copy = await createImageBitmap(image);
+ checkImageBitmapRotated(image_copy);
+}, "ImageBitmap from file with EXIF rotation, loaded via <img>");
+
+promise_test(async t => {
+ const image = new Image();
+ image.src = "resources/squares_6.jpg"
+ // The following has no effect because the image's style is not
+ // processed unless the element is connected to the DOM.
+ image.style.imageOrientation = "none";
+ await new Promise(resolve => image.onload = resolve);
+ const image_copy = await createImageBitmap(image);
+ checkImageBitmapRotated(image_copy);
+}, "ImageBitmap from file with EXIF rotation, loaded via <img> not in DOM, imageOrientation = none");
+
+promise_test(async t => {
+ const image = new Image();
+ document.body.appendChild(image);
+ image.src = "resources/squares_6.jpg"
+ // The style is being processed in this case, but the imageOrientation
+ // CSS property must still have no effect because createImageBitmap
+ // accesses the element's underlying media directly, without being
+ // affected by the image's style (unlike drawImage).
+ image.style.imageOrientation = "none";
+ await new Promise(resolve => image.onload = resolve);
+ const image_copy = await createImageBitmap(image);
+ checkImageBitmapRotated(image_copy);
+}, "ImageBitmap from file with EXIF rotation, loaded via <img> in DOM, imageOrientation = none");
+
+
+promise_test(async t => {
+ const response = await fetch("resources/squares_6.jpg");
+ const blob = await response.blob();
+ const image = await createImageBitmap(blob);
+ const image_copy = await createImageBitmap(image);
+ checkImageBitmapRotated(image_copy);
+}, "ImageBitmap from file with EXIF rotation, duplicated via createImageBitmap");
+
+promise_test(async t => {
+ const worker = new Worker("serialize-worker.js");
+ const response = await fetch("resources/squares_6.jpg");
+ const blob = await response.blob()
+ const image = await createImageBitmap(blob);
+ worker.postMessage({bitmap: image});
+ const bitmap = (await new Promise(resolve => {worker.addEventListener("message", resolve)})).data.bitmap;
+ checkImageBitmapRotated(bitmap);
+}, "ImageBitmap from file with EXIF rotation, duplicated via worker serialization round-trip");
+
+promise_test(async t => {
+ const worker = new Worker("transfer-worker.js");
+ let response = await fetch("resources/squares_6.jpg");
+ let blob = await response.blob();
+ let image = await createImageBitmap(blob);
+ worker.postMessage({bitmap: image}, [image]);
+ const bitmap = (await new Promise(resolve => {worker.addEventListener("message", resolve)})).data.bitmap;
+ checkImageBitmapRotated(bitmap);
+}, "ImageBitmap from file with EXIF rotation, duplicated via worker transfer round-trip");
+
+promise_test(async t => {
+ // This test variant ensures additional code coverage.
+ // By creating a canvas pattern, a reference to the ImageBitmap's
+ // underlying pixel data is held in the source realm. This forces
+ // implementations that do lazy copying to duplicate the pixel
+ // data at transfer time. This test verifies that the lazy
+ // duplication code path (if applicable) carries over the image
+ // orientation metadata.
+ const worker = new Worker("transfer-worker.js");
+ let response = await fetch("resources/squares_6.jpg");
+ let blob = await response.blob();
+ let image = await createImageBitmap(blob);
+ const canvas = document.createElement('canvas');
+ const ctx = canvas.getContext('2d');
+ const pattern = ctx.createPattern(image, 'repeat');
+ worker.postMessage({bitmap: image}, [image]);
+ const bitmap = (await new Promise(resolve => {worker.addEventListener("message", resolve)})).data.bitmap;
+ checkImageBitmapRotated(bitmap);
+}, "ImageBitmap from file with EXIF rotation, duplicated via worker transfer round-trip, while referenced by a CanvasPattern");
+
+
+</script>
+</body> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_1.jpg b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_1.jpg
new file mode 100644
index 0000000000..0f0e8866b4
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_1.jpg
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_2.jpg b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_2.jpg
new file mode 100644
index 0000000000..526f7a6c82
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_2.jpg
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_3.jpg b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_3.jpg
new file mode 100644
index 0000000000..a21e521c2d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_3.jpg
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_4.jpg b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_4.jpg
new file mode 100644
index 0000000000..c4380b1e67
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_4.jpg
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_5.jpg b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_5.jpg
new file mode 100644
index 0000000000..0bdd89aa1b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_5.jpg
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_6.jpg b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_6.jpg
new file mode 100644
index 0000000000..f197760a11
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_6.jpg
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_7.jpg b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_7.jpg
new file mode 100644
index 0000000000..9b1a346888
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_7.jpg
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_8.jpg b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_8.jpg
new file mode 100644
index 0000000000..41d2fbe7f0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_8.jpg
Binary files differ
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/serialize-worker.js b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/serialize-worker.js
new file mode 100644
index 0000000000..a76537cc9e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/serialize-worker.js
@@ -0,0 +1,3 @@
+addEventListener('message', evt => {
+ postMessage(evt.data);
+});
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/transfer-worker.js b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/transfer-worker.js
new file mode 100644
index 0000000000..55465a899c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/transfer-worker.js
@@ -0,0 +1,3 @@
+addEventListener('message', evt => {
+ postMessage(evt.data, [evt.data.bitmap]);
+});
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/worker-onmessage-noop.js b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/worker-onmessage-noop.js
new file mode 100644
index 0000000000..c0a352b4d9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/worker-onmessage-noop.js
@@ -0,0 +1,3 @@
+self.onmessage = function(e) {
+};
+