summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/html/canvas/offscreen/manual
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/html/canvas/offscreen/manual')
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/convert-to-blob/offscreencanvas.convert.to.blob.html165
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/convert-to-blob/offscreencanvas.convert.to.blob.w.html337
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/draw-generic-family/2d.text.draw.generic.family.html49
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/draw-generic-family/2d.text.draw.generic.family.w.html52
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/filter/offscreencanvas.filter.html27
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/filter/offscreencanvas.filter.js48
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/filter/offscreencanvas.filter.w.html54
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/image-smoothing/image.smoothing.html128
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/image-smoothing/image.smoothing.worker.js126
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-layers-expected.html14
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-layers.html33
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-layers.w.html48
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-nested-layers-expected.html14
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-nested-layers.html39
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-nested-layers.w.html54
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas-worker-font-load-crash.html19
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.constructor.html51
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.constructor.worker.js45
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.getcontext.html80
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.getcontext.worker.js77
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.resize.html240
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.lowlatency.nocrash.html12
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap.html112
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap.nocrash.html16
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap.w.html201
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfercontrol.to.offscreen.html34
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfercontrol.to.offscreen.w.html76
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transferrable.html83
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transferrable.w.html142
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/transformations/2d.transformation.getTransform.html40
-rw-r--r--testing/web-platform/tests/html/canvas/offscreen/manual/wide-gamut-canvas/2d.color.space.p3.convertToBlobp3.canvas.html50
31 files changed, 2466 insertions, 0 deletions
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/convert-to-blob/offscreencanvas.convert.to.blob.html b/testing/web-platform/tests/html/canvas/offscreen/manual/convert-to-blob/offscreencanvas.convert.to.blob.html
new file mode 100644
index 0000000000..1cc0e5bffd
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/convert-to-blob/offscreencanvas.convert.to.blob.html
@@ -0,0 +1,165 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#dom-offscreencanvas-converttoblob">
+<script id="myWorker" type="text/worker">
+self.onmessage = function(e) {
+};
+</script>
+<script>
+function makeWorker(script)
+{
+ var blob = new Blob([script]);
+ return new Worker(URL.createObjectURL(blob));
+}
+
+function drawCanvas(ctx)
+{
+ ctx.fillStyle = "red";
+ ctx.fillRect(0, 0, 5, 5);
+ ctx.fillStyle = "green";
+ ctx.fillRect(5, 0, 5, 5);
+ ctx.fillStyle = "blue";
+ ctx.fillRect(0, 5, 5, 5);
+ ctx.fillStyle = "black";
+ ctx.fillRect(5, 5, 5, 5);
+}
+
+function compareImages(image1, image2)
+{
+ var canvas1 = document.createElement('canvas');
+ var canvas2 = document.createElement('canvas');
+ canvas1.width = canvas1.height = 10;
+ canvas2.width = canvas2.height = 10;
+ var ctx1 = canvas1.getContext('2d');
+ var ctx2 = canvas1.getContext('2d');
+ ctx1.drawImage(image1, 0, 0);
+ ctx2.drawImage(image2, 0, 0);
+ var data1 = ctx1.getImageData(0, 0, 10, 10).data;
+ var data2 = ctx2.getImageData(0, 0, 10, 10).data;
+ assert_equals(data1.length, data2.length);
+ var imageMatched = true;
+ for (var i = 0; i < data1.length; i++) {
+ if (data1[i] != data2[i]) {
+ imageMatched = false;
+ break;
+ }
+ }
+ assert_true(imageMatched);
+}
+
+function testConvertToBlob(t, typeVal, qualityVal) {
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ var oCtx = offscreenCanvas.getContext('2d');
+ drawCanvas(oCtx);
+ var canvas = document.createElement('canvas');
+ var ctx = canvas.getContext('2d');
+ drawCanvas(ctx);
+ var imageLoadedCounter = 0;
+
+ var image1 = new Image();
+ var image2 = new Image();
+ var promise;
+ if (typeVal == "empty" && qualityVal == "empty")
+ promise = offscreenCanvas.convertToBlob();
+ else if (typeVal == "empty" && qualityVal != "empty")
+ promise = offscreenCanvas.convertToBlob({quality: qualityVal});
+ else if (typeVal != "empty" && qualityVal == "empty")
+ promise = offscreenCanvas.convertToBlob({type: typeVal});
+ else
+ promise = offscreenCanvas.convertToBlob({type: typeVal, quality: qualityVal});
+ promise.then(function(blob2) {
+ image2.src = URL.createObjectURL(blob2);
+ if (typeVal == "empty" && qualityVal == "empty") {
+ canvas.toBlob(function(blob1) {
+ image1.src = URL.createObjectURL(blob1);
+ });
+ } else if (typeVal == "empty" && qualityVal != "empty") {
+ canvas.toBlob(function(blob1) {
+ image1.src = URL.createObjectURL(blob1);
+ }, "image/png", qualityVal);
+ } else if (typeVal != "empty" && qualityVal == "empty") {
+ canvas.toBlob(function(blob1) {
+ image1.src = URL.createObjectURL(blob1);
+ }, typeVal, 1.0);
+ } else {
+ canvas.toBlob(function(blob1) {
+ image1.src = URL.createObjectURL(blob1);
+ }, typeVal, qualityVal);
+ }
+ image1.onload = image2.onload = t.step_func(function() {
+ imageLoadedCounter++;
+ if (imageLoadedCounter == 2) {
+ compareImages(image1, image2);
+ t.done();
+ }
+ });
+ });
+}
+
+async_test(function(t) {
+ testConvertToBlob(t, "empty", "empty");
+ testConvertToBlob(t, "empty", 1.0);
+ testConvertToBlob(t, "empty", 0.2);
+}, "Test that convertToBlob with default type produces correct result");
+
+async_test(function(t) {
+ testConvertToBlob(t, "image/png", "empty");
+ testConvertToBlob(t, "image/png", 1.0);
+ testConvertToBlob(t, "image/png", 0.2);
+}, "Test that convertToBlob with png produces correct result");
+
+async_test(function(t) {
+ testConvertToBlob(t, "image/jpeg", "empty");
+ testConvertToBlob(t, "image/jpeg", 1.0);
+ testConvertToBlob(t, "image/jpeg", 0.2);
+}, "Test that convertToBlob with jpge produces correct result");
+
+async_test(function(t) {
+ testConvertToBlob(t, "image/webp", "empty");
+ testConvertToBlob(t, "image/webp", 1.0);
+ testConvertToBlob(t, "image/webp", 0.2);
+}, "Test that convertToBlob with webp produces correct result");
+
+async_test(function(t) {
+ var worker = makeWorker(document.getElementById("myWorker").textContent);
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ worker.postMessage({offscreenCanvas}, [offscreenCanvas]);
+ offscreenCanvas.convertToBlob().then(t.step_func_done(function() {
+ assert_false("convertToBlob didn't throw, but should be");
+ }), t.step_func_done(function(e) {
+ assert_true(e instanceof DOMException);
+ assert_equals(e.name, "InvalidStateError");
+ }));
+}, "Test that call convertToBlob on a detached OffscreenCanvas throws exception");
+
+async_test(function(t) {
+ var offscreenCanvas = new OffscreenCanvas(0, 0);
+ offscreenCanvas.convertToBlob().then(t.step_func_done(function() {
+ assert_false("convertToBlob didn't throw, but should be");
+ }), t.step_func_done(function(e) {
+ assert_true(e instanceof DOMException);
+ assert_equals(e.name, "IndexSizeError");
+ }));
+}, "Test that call convertToBlob on a OffscreenCanvas with size 0 throws exception");
+
+async_test(function(t) {
+ var img = new Image();
+ img.src = "/images/green.png";
+ img.crossOrigin = "anonymous";
+ img.onload = t.step_func_done(() => {
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ var ctx = offscreenCanvas.getContext("2d");
+ ctx.drawImage(img, 0, 0);
+ offscreenCanvas.convertToBlob().then(t.step_func_done(function() {
+ assert_false("convertToBlob didn't throw, but should");
+ }), t.step_func_done(function(e) {
+ assert_true(e instanceof DOMException);
+ assert_equals(e.name, "SecurityError");
+ }));
+ });
+}, "Test that call convertToBlob on a OffscreenCanvas with tainted origin throws exception");
+
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/convert-to-blob/offscreencanvas.convert.to.blob.w.html b/testing/web-platform/tests/html/canvas/offscreen/manual/convert-to-blob/offscreencanvas.convert.to.blob.w.html
new file mode 100644
index 0000000000..5c1fa4cf40
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/convert-to-blob/offscreencanvas.convert.to.blob.w.html
@@ -0,0 +1,337 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#dom-offscreencanvas-converttoblob">
+<script id="myWorker" type="text/worker">
+function drawCanvas(ctx)
+{
+ ctx.fillStyle = "red";
+ ctx.fillRect(0, 0, 5, 5);
+ ctx.fillStyle = "green";
+ ctx.fillRect(5, 0, 5, 5);
+ ctx.fillStyle = "blue";
+ ctx.fillRect(0, 5, 5, 5);
+ ctx.fillStyle = "black";
+ ctx.fillRect(5, 5, 5, 5);
+}
+
+function testConvertToBlob(typeVal, qualityVal) {
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ var oCtx = offscreenCanvas.getContext('2d');
+ drawCanvas(oCtx);
+
+ var promise;
+ if (typeVal == "empty" && qualityVal == "empty")
+ promise = offscreenCanvas.convertToBlob();
+ else if (typeVal == "empty" && qualityVal != "empty")
+ promise = offscreenCanvas.convertToBlob({quality: qualityVal});
+ else if (typeVal != "empty" && qualityVal == "empty")
+ promise = offscreenCanvas.convertToBlob({type: typeVal});
+ else
+ promise = offscreenCanvas.convertToBlob({type: typeVal, quality: qualityVal});
+ promise.then(function(blob) {
+ self.postMessage(blob);
+ });
+}
+
+function testConvertToBlobException1()
+{
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ self.postMessage({offscreenCanvas}, [offscreenCanvas]);
+ offscreenCanvas.convertToBlob().then(function() {
+ self.postMessage(false);
+ }, function(e) {
+ self.postMessage(e instanceof DOMException && e.name == "InvalidStateError");
+ });
+}
+
+function testConvertToBlobException2()
+{
+ var offscreenCanvas = new OffscreenCanvas(0, 0);
+ offscreenCanvas.convertToBlob().then(function() {
+ self.postMessage(false);
+ }, function(e) {
+ self.postMessage(e instanceof DOMException && e.name == "IndexSizeError");
+ });
+}
+
+function testConvertToBlobException3(bitmap)
+{
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ var ctx = offscreenCanvas.getContext("2d");
+ ctx.drawImage(bitmap, 0, 0);
+ offscreenCanvas.convertToBlob().then(function() {
+ self.postMessage(false);
+ }, function(e) {
+ self.postMessage(e instanceof DOMException && e.name == "SecurityError");
+ });
+}
+
+self.onmessage = function(e) {
+ if (e.data instanceof ImageBitmap) {
+ testConvertToBlobException3(e.data);
+ return;
+ }
+
+ switch(e.data) {
+ case 'test1':
+ testConvertToBlob("empty", "empty");
+ break;
+ case 'test2':
+ testConvertToBlob("empty", 1.0);
+ break;
+ case 'test3':
+ testConvertToBlob("empty", 0.2);
+ break;
+ case 'test4':
+ testConvertToBlob("image/png", "empty");
+ break;
+ case 'test5':
+ testConvertToBlob("image/png", 1.0);
+ break;
+ case 'test6':
+ testConvertToBlob("image/png", 0.2);
+ break;
+ case 'test7':
+ testConvertToBlob("image/jpeg", "empty");
+ break;
+ case 'test8':
+ testConvertToBlob("image/jpeg", 1.0);
+ break;
+ case 'test9':
+ testConvertToBlob("image/jpeg", 0.2);
+ break;
+ case 'test10':
+ testConvertToBlob("image/webp", "empty");
+ break;
+ case 'test11':
+ testConvertToBlob("image/webp", 1.0);
+ break;
+ case 'test12':
+ testConvertToBlob("image/webp", 0.2);
+ break;
+ case 'test13':
+ testConvertToBlobException1();
+ break;
+ case 'test14':
+ testConvertToBlobException2();
+ break;
+ }
+};
+
+</script>
+
+<script>
+function makeWorker(test) {
+ var blob = new Blob([document.getElementById("myWorker").textContent]);
+ var worker = new Worker(URL.createObjectURL(blob));
+ worker.onerror = test.unreached_func("error");
+ return worker;
+}
+
+function drawCanvas(ctx)
+{
+ ctx.fillStyle = "red";
+ ctx.fillRect(0, 0, 5, 5);
+ ctx.fillStyle = "green";
+ ctx.fillRect(5, 0, 5, 5);
+ ctx.fillStyle = "blue";
+ ctx.fillRect(0, 5, 5, 5);
+ ctx.fillStyle = "black";
+ ctx.fillRect(5, 5, 5, 5);
+}
+
+function compareImages(image1, image2)
+{
+ var canvas1 = document.createElement('canvas');
+ var canvas2 = document.createElement('canvas');
+ canvas1.width = canvas1.height = 10;
+ canvas2.width = canvas2.height = 10;
+ var ctx1 = canvas1.getContext('2d');
+ var ctx2 = canvas1.getContext('2d');
+ ctx1.drawImage(image1, 0, 0);
+ ctx2.drawImage(image2, 0, 0);
+ var data1 = ctx1.getImageData(0, 0, 10, 10).data;
+ var data2 = ctx2.getImageData(0, 0, 10, 10).data;
+ assert_equals(data1.length, data2.length);
+ var imageMatched = true;
+ for (var i = 0; i < data1.length; i++) {
+ if (data1[i] != data2[i]) {
+ imageMatched = false;
+ break;
+ }
+ }
+ assert_true(imageMatched);
+}
+
+function compareWithToBlob(t, typeVal, qualityVal, blob2)
+{
+ var image1 = new Image();
+ var image2 = new Image();
+ var canvas = document.createElement('canvas');
+ var ctx = canvas.getContext('2d');
+ drawCanvas(ctx);
+ var imageLoadedCounter = 0;
+
+ if (typeVal == "empty" && qualityVal == "empty") {
+ canvas.toBlob(function(blob1) {
+ image1.src = URL.createObjectURL(blob1);
+ });
+ } else if (typeVal == "empty" && qualityVal != "empty") {
+ canvas.toBlob(function(blob1) {
+ image1.src = URL.createObjectURL(blob1);
+ }, "image/png", qualityVal);
+ } else if (typeVal != "empty" && qualityVal == "empty") {
+ canvas.toBlob(function(blob1) {
+ image1.src = URL.createObjectURL(blob1);
+ }, typeVal, 1.0);
+ } else {
+ canvas.toBlob(function(blob1) {
+ image1.src = URL.createObjectURL(blob1);
+ }, typeVal, qualityVal);
+ }
+ image2.src = URL.createObjectURL(blob2);
+ image1.onload = image2.onload = t.step_func(function() {
+ imageLoadedCounter++;
+ if (imageLoadedCounter == 2) {
+ compareImages(image1, image2);
+ t.done();
+ }
+ });
+}
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func(function(msg) {
+ compareWithToBlob(t, "empty", "empty", msg.data);
+ }));
+ worker.postMessage('test1');
+}, "Test that convertToBlob with default arguments produces correct result in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func(function(msg) {
+ compareWithToBlob(t, "empty", 1.0, msg.data);
+ }));
+ worker.postMessage('test2');
+}, "Test that convertToBlob with default type/1.0 quality produces correct result in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func(function(msg) {
+ compareWithToBlob(t, "empty", 0.2, msg.data);
+ }));
+ worker.postMessage('test3');
+}, "Test that convertToBlob with default type/0.2 quality produces correct result in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func(function(msg) {
+ compareWithToBlob(t, "image/png", "empty", msg.data);
+ }));
+ worker.postMessage('test4');
+}, "Test that convertToBlob with png/default quality produces correct result in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func(function(msg) {
+ compareWithToBlob(t, "image/png", 1.0, msg.data);
+ }));
+ worker.postMessage('test5');
+}, "Test that convertToBlob with png/1.0 quality produces correct result in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func(function(msg) {
+ compareWithToBlob(t, "image/png", 0.2, msg.data);
+ }));
+ worker.postMessage('test6');
+}, "Test that convertToBlob with png/0.2 quality produces correct result in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func(function(msg) {
+ compareWithToBlob(t, "image/jpeg", "empty", msg.data);
+ }));
+ worker.postMessage('test7');
+}, "Test that convertToBlob with jpeg/default quality produces correct result in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func(function(msg) {
+ compareWithToBlob(t, "image/jpeg", 1.0, msg.data);
+ }));
+ worker.postMessage('test8');
+}, "Test that convertToBlob with jpeg/1.0 quality produces correct result in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func(function(msg) {
+ compareWithToBlob(t, "image/jpeg", 0.2, msg.data);
+ }));
+ worker.postMessage('test9');
+}, "Test that convertToBlob with jpeg/0.2 quality produces correct result in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func(function(msg) {
+ compareWithToBlob(t, "image/webp", "empty", msg.data);
+ }));
+ worker.postMessage('test10');
+}, "Test that convertToBlob with webp/default quality produces correct result in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func(function(msg) {
+ compareWithToBlob(t, "image/webp", 1.0, msg.data);
+ }));
+ worker.postMessage('test11');
+}, "Test that convertToBlob with webp/1.0 quality produces correct result in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func(function(msg) {
+ compareWithToBlob(t, "image/webp", 0.2, msg.data);
+ }));
+ worker.postMessage('test12');
+}, "Test that convertToBlob with webp/0.2 quality produces correct result in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ if (msg.data instanceof Object)
+ return;
+ assert_true(msg.data);
+ t.done();
+ }));
+ worker.postMessage('test13');
+}, "Test that call convertToBlob on a detached OffscreenCanvas throws exception in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ assert_true(msg.data);
+ t.done();
+ }));
+ worker.postMessage('test14');
+}, "Test that call convertToBlob on a OffscreenCanvas with size 0 throws exception in a worker");
+
+async_test(function(t) {
+ var img = new Image();
+ img.src = "/images/green.png";
+ img.crossOrigin = "anonymous";
+ img.onload = t.step_func_done(() => {
+ createImageBitmap(img).then(t.step_func_done(bitmap => {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ assert_true(msg.data);
+ t.done();
+ }));
+ worker.postMessage(bitmap);
+ }));
+ });
+}, "Test that call convertToBlob on a OffscreenCanvas with tainted origin throws exception in a worker");
+
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/draw-generic-family/2d.text.draw.generic.family.html b/testing/web-platform/tests/html/canvas/offscreen/manual/draw-generic-family/2d.text.draw.generic.family.html
new file mode 100644
index 0000000000..a5fd7ab066
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/draw-generic-family/2d.text.draw.generic.family.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<title>OffscreenCanvas test: 2d.text.draw.generic.family</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<script>
+function drawCanvas(ctx, family)
+{
+ ctx.font = '16px ' + family;
+ ctx.fillText(family, 0, 16);
+}
+
+function testDrawGenericFamily(family)
+{
+ let offscreenCanvas = new OffscreenCanvas(88, 24);
+ let oCtx = offscreenCanvas.getContext('2d');
+ drawCanvas(oCtx, family);
+ let canvas = document.createElement('canvas');
+ let ctx = canvas.getContext('2d');
+ drawCanvas(ctx, family);
+
+ let data1 = oCtx.getImageData(0, 0, 88, 24).data;
+ let data2 = ctx.getImageData(0, 0, 88, 24).data;
+ assert_array_equals(data1, data2,
+ "The image data generated by drawing generic font family '" + family +
+ "' should be the same for both OffscreenCanvas and regular canvas");
+}
+
+test(function() {
+ testDrawGenericFamily('sans-serif');
+}, "Test that drawing sans-serif produces the same result between canvas and OffscreenCanvas");
+
+test(function() {
+ testDrawGenericFamily('serif');
+}, "Test that drawing serif produces the same result between canvas and OffscreenCanvas");
+
+test(function() {
+ testDrawGenericFamily('cursive');
+}, "Test that drawing cursive produces the same result between canvas and OffscreenCanvas");
+
+test(function() {
+ testDrawGenericFamily('fantasy');
+}, "Test that drawing fantasy produces the same result between canvas and OffscreenCanvas");
+
+test(function() {
+ testDrawGenericFamily('monospace');
+}, "Test that drawing monospace produces the same result between canvas and OffscreenCanvas");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/draw-generic-family/2d.text.draw.generic.family.w.html b/testing/web-platform/tests/html/canvas/offscreen/manual/draw-generic-family/2d.text.draw.generic.family.w.html
new file mode 100644
index 0000000000..7a88c032ad
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/draw-generic-family/2d.text.draw.generic.family.w.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<title>OffscreenCanvas test: 2d.text.draw.generic.family.w</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<script id='myWorker' type='text/worker'>
+self.onmessage = function(e) {
+ let oc = new OffscreenCanvas(88, 24);
+ let ctx = oc.getContext('2d');
+ ctx.font = '32px ' + e.data.family;
+ ctx.fillText(e.data.family, 0, 16);
+ self.postMessage(ctx.getImageData(0, 0, 88, 24).data);
+};
+</script>
+<script>
+function testDrawGenericFamily(t, family)
+{
+ let blob = new Blob([document.getElementById('myWorker').textContent]);
+ let worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ let ctx = document.createElement('canvas').getContext('2d');
+ ctx.font = '32px ' + family;
+ ctx.fillText(family, 0, 16);
+ assert_array_equals(ctx.getImageData(0, 0, 88, 24).data, msg.data,
+ "The image data generated by drawing generic font family '" + family +
+ "' should be the same for both OffscreenCanvas and regular canvas");
+ t.done();
+ });
+ worker.postMessage({family: family});
+}
+
+async_test(function(t) {
+ testDrawGenericFamily(t, 'sans-serif');
+}, "Test that drawing sans-serif produces the same result between canvas and OffscreenCanvas in a Worker");
+
+async_test(function(t) {
+ testDrawGenericFamily(t, 'serif');
+}, "Test that drawing serif produces the same result between canvas and OffscreenCanvas in a Worker");
+
+async_test(function(t) {
+ testDrawGenericFamily(t, 'cursive');
+}, "Test that drawing cursive produces the same result between canvas and OffscreenCanvas in a Worker");
+
+async_test(function(t) {
+ testDrawGenericFamily(t, 'fantasy');
+}, "Test that drawing fantasy produces the same result between canvas and OffscreenCanvas in a Worker");
+
+async_test(function(t) {
+ testDrawGenericFamily(t, 'monospace');
+}, "Test that drawing monospace produces the same result between canvas and OffscreenCanvas in a Worker");
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/filter/offscreencanvas.filter.html b/testing/web-platform/tests/html/canvas/offscreen/manual/filter/offscreencanvas.filter.html
new file mode 100644
index 0000000000..25691983f1
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/filter/offscreencanvas.filter.html
@@ -0,0 +1,27 @@
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="offscreencanvas.filter.js"></script>
+<script>
+var patternCanvas = createPatternCanvas();
+
+var getOffscreenContextForFilter = function(filter, pattern) {
+ var oc = new OffscreenCanvas(80, 80);
+ var offCtx = oc.getContext('2d');
+ offCtx.filter = filter;
+ offCtx.drawImage(pattern, 5, 5);
+ offCtx.drawImage(pattern, 25, 25);
+ offCtx.drawImage(pattern, 45, 45);
+ return offCtx;
+};
+
+var testFilter = function(filter) {
+ var offCtx = getOffscreenContextForFilter(filter, patternCanvas);
+ var ctx = getRegularContextForFilter(filter, patternCanvas);
+ var offImageData = offCtx.getImageData(0, 0, 80, 80).data;
+ var imageData = ctx.getImageData(0, 0, 80, 80).data;
+ matchImageDataResults(offImageData, imageData, filter);
+};
+
+generate_tests(testFilter, [filters]);
+
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/filter/offscreencanvas.filter.js b/testing/web-platform/tests/html/canvas/offscreen/manual/filter/offscreencanvas.filter.js
new file mode 100644
index 0000000000..cb2e245803
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/filter/offscreencanvas.filter.js
@@ -0,0 +1,48 @@
+var getRegularContextForFilter = function(filter, pattern) {
+ var c = document.createElement("canvas");
+ c.width = c.height = 80;
+ var ctx = c.getContext('2d');
+ ctx.filter = filter;
+ ctx.drawImage(pattern, 5, 5);
+ ctx.drawImage(pattern, 25, 25);
+ ctx.drawImage(pattern, 45, 45);
+ return ctx;
+};
+
+var matchImageDataResults = function(offscreenImage, regularImage, filter) {
+ assert_array_equals(offscreenImage, regularImage,
+ "The image data generated by filter " +
+ filter +
+ " should be the same for both OffscreenCanvas and regular canvas");
+};
+
+var createPatternCanvas = function() {
+ var patternCanvas = document.createElement('canvas');
+ patternCanvas.width = 20;
+ patternCanvas.height = 20;
+ var patternCtx = patternCanvas.getContext('2d');
+ patternCtx.fillStyle = '#A00';
+ patternCtx.fillRect(0, 0, 10, 10);
+ patternCtx.fillStyle = '#0A0';
+ patternCtx.fillRect(10, 0, 10, 10);
+ patternCtx.fillStyle = '#00A';
+ patternCtx.fillRect(0, 10, 10, 10);
+ patternCtx.fillStyle = "#AA0";
+ patternCtx.fillRect(10, 10, 10, 10);
+ return patternCanvas;
+};
+
+var filters = [ "none" ,
+ "blur(10px)" ,
+ "brightness(40%)" ,
+ "contrast(20%)" ,
+ "drop-shadow(0 0 5px green)" ,
+ "grayscale(100%)" ,
+ "invert(100%)" ,
+ "opacity(50%)" ,
+ "saturate(20%)" ,
+ "sepia(100%)" ,
+ "sepia(1) hue-rotate(200deg)",
+ "url(#url)" ];
+
+
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/filter/offscreencanvas.filter.w.html b/testing/web-platform/tests/html/canvas/offscreen/manual/filter/offscreencanvas.filter.w.html
new file mode 100644
index 0000000000..d6ed915b53
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/filter/offscreencanvas.filter.w.html
@@ -0,0 +1,54 @@
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="offscreencanvas.filter.js"></script>
+<script id='myWorker' type='text/worker'>
+self.onmessage = function(e) {
+ var getOffscreenCanvasForFilter = function(filter, pattern) {
+ var oc = new OffscreenCanvas(80, 80);
+ var offCtx = oc.getContext('2d');
+ offCtx.filter = filter;
+ offCtx.drawImage(pattern, 5, 5);
+ offCtx.drawImage(pattern, 25, 25);
+ offCtx.drawImage(pattern, 45, 45);
+ return oc;
+ };
+
+ var filters = e.data.filters;
+ var pattern = e.data.pattern;
+ var ret = [];
+ for (var i = 0; i < filters.length; i++) {
+ var oc = getOffscreenCanvasForFilter(filters[i], pattern);
+ var imageBitmap = oc.transferToImageBitmap();
+ ret.push(imageBitmap);
+ }
+ self.postMessage(ret, ret);
+};
+</script>
+<script>
+var patternCanvas = createPatternCanvas();
+
+// Build a list of image data on regular canvas with different filters
+var listCanvasImageData = [];
+for (var j = 0; j < filters.length; j++) {
+ var ctx = getRegularContextForFilter(filters[j], patternCanvas);
+ listCanvasImageData.push(ctx.getImageData(0, 0, 80, 80).data);
+}
+
+function consumeImageBitmap(patternImage) {
+ async_test(t => {
+ var blob = new Blob([document.getElementById('myWorker').textContent]);
+ var worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ for (var i = 0; i < msg.data.length; ++i) {
+ var outputCtx = document.createElement("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data[i], 0, 0, 80, 80);
+ matchImageDataResults(outputCtx.getImageData(0, 0, 80, 80).data, listCanvasImageData[i], filters[i]);
+ }
+ t.done();
+ });
+ worker.postMessage({filters: filters, pattern: patternImage}, [patternImage]);
+ });
+}
+
+createImageBitmap(patternCanvas).then(consumeImageBitmap);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/image-smoothing/image.smoothing.html b/testing/web-platform/tests/html/canvas/offscreen/manual/image-smoothing/image.smoothing.html
new file mode 100644
index 0000000000..a78524c51d
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/image-smoothing/image.smoothing.html
@@ -0,0 +1,128 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>OffscreenCanvasRenderingContext2D imageSmoothingEnabled test</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#the-offscreen-2d-rendering-context">
+<script>
+function createTestImage() {
+ var image = new OffscreenCanvas(100, 50);
+ var imgctx = image.getContext('2d');
+ imgctx.fillStyle = "#F00";
+ imgctx.fillRect(0, 0, 2, 2);
+ imgctx.fillStyle = "#0F0";
+ imgctx.fillRect(0, 0, 1, 1);
+ return image;
+}
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ assert_true(ctx.imageSmoothingEnabled);
+}, "When the context is created, imageSmoothingEnabled must be set to true.");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ ctx.imageSmoothingEnabled = false;
+ assert_false(ctx.imageSmoothingEnabled);
+}, "On getting imageSmoothingEnabled, the user agent must return the last value it was set to.");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ var image = createTestImage();
+ ctx.scale(10, 10);
+ ctx.drawImage(image, 0, 0);
+ var pixels = ctx.getImageData(9, 9, 1, 1).data;
+ assert_not_equals(pixels[0], 0);
+ assert_not_equals(pixels[1], 255);
+}, "Test that image smoothing is actually on by default.");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ ctx.imageSmoothingEnabled = true;
+ var image = createTestImage();
+ ctx.scale(10, 10);
+ ctx.drawImage(image, 0, 0);
+ var pixels = ctx.getImageData(9, 9, 1, 1).data;
+ assert_not_equals(pixels[0], 0);
+ assert_not_equals(pixels[1], 255);
+}, "Test that image smoothing works when imageSmoothingEnabled is set to true");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ var image = createTestImage();
+ ctx.imageSmoothingEnabled = false;
+ ctx.scale(10, 10);
+ ctx.drawImage(image, 0, 0);
+ var pixels = ctx.getImageData(9, 9, 1, 1).data;
+ assert_array_equals(pixels, [0, 255, 0, 255]);
+}, "Test that imageSmoothingEnabled = false (nearest-neighbor interpolation) works with drawImage().");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ var image = createTestImage();
+ ctx.imageSmoothingEnabled = false;
+ ctx.scale(10, 10);
+ ctx.fillStyle = ctx.createPattern(image, 'repeat');
+ ctx.fillRect(0, 0, 10, 10);
+ var pixels = ctx.getImageData(9, 9, 1, 1).data;
+ assert_array_equals(pixels, [0, 255, 0, 255]);
+}, "Test that imageSmoothingEnabled = false (nearest-neighbor interpolation) works with fillRect and createPattern().");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ var image = createTestImage();
+ ctx.imageSmoothingEnabled = false;
+ ctx.fillStyle = ctx.createPattern(image, 'repeat');
+ ctx.scale(10, 10);
+ ctx.rect(0, 0, 10, 10);
+ ctx.fill();
+ var pixels = ctx.getImageData(9, 9, 1, 1).data;
+ assert_array_equals(pixels, [0, 255, 0, 255]);
+}, "Test that imageSmoothingEnabled = false (nearest-neighbor interpolation) works with fill() and createPattern().");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ var image = createTestImage();
+ ctx.strokeStyle = ctx.createPattern(image, 'repeat');
+ ctx.lineWidth = 5;
+ ctx.imageSmoothingEnabled = false;
+ ctx.scale(10, 10);
+ ctx.beginPath();
+ ctx.moveTo(0, 0);
+ ctx.lineTo(10, 10);
+ ctx.stroke();
+ var pixels = ctx.getImageData(9, 9, 1, 1).data;
+ assert_array_equals(pixels, [0, 255, 0, 255]);
+}, "Test that imageSmoothingEnabled = false (nearest-neighbor interpolation) works with stroke() and createPattern().");
+
+test(function() {
+ var repaints = 5;
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+
+ function draw() {
+ ctx.clearRect(0, 0, 10, 10);
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
+ var image = createTestImage();
+ ctx.imageSmoothingEnabled = false;
+ ctx.scale(10, 10);
+ ctx.drawImage(image, 0, 0);
+ var pixels = ctx.getImageData(9, 9, 1, 1).data;
+ assert_array_equals(pixels, [0, 255, 0, 255]);
+ }
+
+ while (repaints > 0) {
+ draw();
+ repaints = repaints - 1;
+ }
+
+}, "Test that imageSmoothingEnabled = false (nearest-neighbor interpolation) still works after repaints.");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/image-smoothing/image.smoothing.worker.js b/testing/web-platform/tests/html/canvas/offscreen/manual/image-smoothing/image.smoothing.worker.js
new file mode 100644
index 0000000000..4c37e84b1f
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/image-smoothing/image.smoothing.worker.js
@@ -0,0 +1,126 @@
+// spec link: https://html.spec.whatwg.org/#the-offscreen-2d-rendering-context
+
+importScripts("/resources/testharness.js");
+
+function createTestImage() {
+ var image = new OffscreenCanvas(100, 50);
+ var imgctx = image.getContext('2d');
+ imgctx.fillStyle = "#F00";
+ imgctx.fillRect(0, 0, 2, 2);
+ imgctx.fillStyle = "#0F0";
+ imgctx.fillRect(0, 0, 1, 1);
+ return image;
+}
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ assert_true(ctx.imageSmoothingEnabled);
+}, "When the context is created, imageSmoothingEnabled must be set to true.");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ ctx.imageSmoothingEnabled = false;
+ assert_false(ctx.imageSmoothingEnabled);
+}, "On getting imageSmoothingEnabled, the user agent must return the last value it was set to.");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ var image = createTestImage();
+ ctx.scale(10, 10);
+ ctx.drawImage(image, 0, 0);
+ var pixels = ctx.getImageData(9, 9, 1, 1).data;
+ assert_not_equals(pixels[0], 0);
+ assert_not_equals(pixels[1], 255);
+}, "Test that image smoothing is actually on by default.");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ ctx.imageSmoothingEnabled = true;
+ var image = createTestImage();
+ ctx.scale(10, 10);
+ ctx.drawImage(image, 0, 0);
+ var pixels = ctx.getImageData(9, 9, 1, 1).data;
+ assert_not_equals(pixels[0], 0);
+ assert_not_equals(pixels[1], 255);
+}, "Test that image smoothing works when imageSmoothingEnabled is set to true");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ var image = createTestImage();
+ ctx.imageSmoothingEnabled = false;
+ ctx.scale(10, 10);
+ ctx.drawImage(image, 0, 0);
+ var pixels = ctx.getImageData(9, 9, 1, 1).data;
+ assert_array_equals(pixels, [0, 255, 0, 255]);
+}, "Test that imageSmoothingEnabled = false (nearest-neighbor interpolation) works with drawImage().");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ var image = createTestImage();
+ ctx.imageSmoothingEnabled = false;
+ ctx.scale(10, 10);
+ ctx.fillStyle = ctx.createPattern(image, 'repeat');
+ ctx.fillRect(0, 0, 10, 10);
+ var pixels = ctx.getImageData(9, 9, 1, 1).data;
+ assert_array_equals(pixels, [0, 255, 0, 255]);
+}, "Test that imageSmoothingEnabled = false (nearest-neighbor interpolation) works with fillRect and createPattern().");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ var image = createTestImage();
+ ctx.imageSmoothingEnabled = false;
+ ctx.fillStyle = ctx.createPattern(image, 'repeat');
+ ctx.scale(10, 10);
+ ctx.rect(0, 0, 10, 10);
+ ctx.fill();
+ var pixels = ctx.getImageData(9, 9, 1, 1).data;
+ assert_array_equals(pixels, [0, 255, 0, 255]);
+}, "Test that imageSmoothingEnabled = false (nearest-neighbor interpolation) works with fill() and createPattern().");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ var image = createTestImage();
+ ctx.strokeStyle = ctx.createPattern(image, 'repeat');
+ ctx.lineWidth = 5;
+ ctx.imageSmoothingEnabled = false;
+ ctx.scale(10, 10);
+ ctx.beginPath();
+ ctx.moveTo(0, 0);
+ ctx.lineTo(10, 10);
+ ctx.stroke();
+ var pixels = ctx.getImageData(9, 9, 1, 1).data;
+ assert_array_equals(pixels, [0, 255, 0, 255]);
+}, "Test that imageSmoothingEnabled = false (nearest-neighbor interpolation) works with stroke() and createPattern().");
+
+test(function() {
+ var repaints = 5;
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+
+ function draw() {
+ ctx.clearRect(0, 0, 10, 10);
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
+ var image = createTestImage();
+ ctx.imageSmoothingEnabled = false;
+ ctx.scale(10, 10);
+ ctx.drawImage(image, 0, 0);
+ var pixels = ctx.getImageData(9, 9, 1, 1).data;
+ assert_array_equals(pixels, [0, 255, 0, 255]);
+ }
+
+ while (repaints > 0) {
+ draw();
+ repaints = repaints - 1;
+ }
+
+}, "Test that imageSmoothingEnabled = false (nearest-neighbor interpolation) still works after repaints.");
+
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-layers-expected.html b/testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-layers-expected.html
new file mode 100644
index 0000000000..873869ea72
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-layers-expected.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<title>Canvas test: unclosed-layers</title>
+<h1>unclosed-layers</h1>
+<p class="desc">Check that unclosed layers aren't rendered.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(60, 60, 75, 50);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-layers.html b/testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-layers.html
new file mode 100644
index 0000000000..4fee600d6c
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-layers.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<link rel="match" href="unclosed-layers-expected.html">
+<title>Canvas test: unclosed-layers</title>
+<h1>unclosed-layers</h1>
+<p class="desc">Check that unclosed layers aren't rendered.</p>
+<canvas id="canvas" width="1" height="1">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ var placeholder = document.getElementById('canvas');
+ var offscreen = placeholder.transferControlToOffscreen();
+ const ctx = offscreen.getContext('2d');
+ offscreen.width = offscreen.height = 200;
+
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(60, 60, 75, 50);
+
+ ctx.beginLayer({filter: {name: 'dropShadow', dx: -2, dy: 2}});
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+
+ function draw () {
+ // Wait until frame propagates.
+ if(placeholder.width != 200) {
+ requestAnimationFrame(draw);
+ } else {
+ document.documentElement.classList.remove("reftest-wait");
+ }
+ }
+ requestAnimationFrame(draw);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-layers.w.html b/testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-layers.w.html
new file mode 100644
index 0000000000..a0014bb597
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-layers.w.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<link rel="match" href="unclosed-layers-expected.html">
+<title>Canvas test: unclosed-layers</title>
+<h1>unclosed-layers</h1>
+<p class="desc">Check that unclosed layers aren't rendered.</p>
+<canvas id="canvas" width="1" height="1">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = msg => {
+ const offscreen = msg.data.canvas;
+ const ctx = offscreen.getContext('2d');
+ offscreen.width = offscreen.height = 200;
+
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(60, 60, 75, 50);
+
+ ctx.beginLayer({filter: {name: 'dropShadow', dx: -2, dy: 2}});
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+
+ self.postMessage('setup ready');
+ }
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ var placeholder = document.getElementById('canvas');
+ var offscreen = placeholder.transferControlToOffscreen();
+ worker.addEventListener('message', msg => {
+ if(msg.data == 'setup ready') {
+ function draw () {
+ // Wait until frame propagates.
+ if(placeholder.width != 1) {
+ document.documentElement.classList.remove("reftest-wait");
+ } else {
+ requestAnimationFrame(draw);
+ }
+ }
+ requestAnimationFrame(draw);
+ }
+ });
+ worker.postMessage({
+ canvas: offscreen
+ }, [offscreen]);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-nested-layers-expected.html b/testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-nested-layers-expected.html
new file mode 100644
index 0000000000..8557441f7e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-nested-layers-expected.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<title>Canvas test: unclosed-nested-layers</title>
+<h1>unclosed-nested-layers</h1>
+<p class="desc">Check that unclosed nested layers aren't rendered.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'rgba(0,0,255,1)';
+ ctx.fillRect(60,60,75,50);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-nested-layers.html b/testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-nested-layers.html
new file mode 100644
index 0000000000..c6925beb74
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-nested-layers.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<link rel="match" href="unclosed-nested-layers-expected.html">
+<title>Canvas test: unclosed-nested-layers</title>
+<h1>unclosed-nested-layers</h1>
+<p class="desc">Check that unclosed nested layers aren't rendered.</p>
+<canvas id="canvas" width="1" height="1">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ var placeholder = document.getElementById('canvas');
+ var offscreen = placeholder.transferControlToOffscreen();
+ const ctx = offscreen.getContext('2d');
+ offscreen.width = offscreen.height = 200;
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+ ctx.fillRect(60, 60, 75, 50);
+
+ ctx.beginLayer();
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+
+ ctx.beginLayer();
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+ // Missing ctx.endLayer() here.
+
+ function draw () {
+ // Wait until frame propagates.
+ if(placeholder.width != 200) {
+ requestAnimationFrame(draw);
+ } else {
+ document.documentElement.classList.remove("reftest-wait");
+ }
+ }
+ requestAnimationFrame(draw);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-nested-layers.w.html b/testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-nested-layers.w.html
new file mode 100644
index 0000000000..1a1dc54052
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/layers/unclosed-nested-layers.w.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<link rel="match" href="unclosed-nested-layers-expected.html">
+<title>Canvas test: unclosed-nested-layers</title>
+<h1>unclosed-nested-layers</h1>
+<p class="desc">Check that unclosed nested layers aren't rendered.</p>
+<canvas id="canvas" width="1" height="1">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = msg => {
+ const offscreen = msg.data.canvas;
+ const ctx = offscreen.getContext('2d');
+ offscreen.width = offscreen.height = 200;
+
+ ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+ ctx.fillRect(60, 60, 75, 50);
+
+ ctx.beginLayer();
+ ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+ ctx.fillRect(50, 50, 75, 50);
+
+ ctx.beginLayer();
+ ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+ ctx.fillRect(70, 70, 75, 50);
+
+ ctx.endLayer();
+ // Missing ctx.endLayer() here.
+
+ self.postMessage('setup ready');
+ }
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ var placeholder = document.getElementById('canvas');
+ var offscreen = placeholder.transferControlToOffscreen();
+ worker.addEventListener('message', msg => {
+ if(msg.data == 'setup ready') {
+ function draw () {
+ // Wait until frame propagates.
+ if(placeholder.width != 1) {
+ document.documentElement.classList.remove("reftest-wait");
+ } else {
+ requestAnimationFrame(draw);
+ }
+ }
+ requestAnimationFrame(draw);
+ }
+ });
+ worker.postMessage({
+ canvas: offscreen
+ }, [offscreen]);
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas-worker-font-load-crash.html b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas-worker-font-load-crash.html
new file mode 100644
index 0000000000..1baf7847de
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas-worker-font-load-crash.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<html class="test-wait reftest-wait">
+<script>
+ let url = URL.createObjectURL(new Blob([`
+ let font = new FontFace('Ahem', 'url(/fonts/Ahem.ttf)');
+ self.fonts.add(font);
+ let canvas = new OffscreenCanvas(100, 100);
+ let ctx = canvas.getContext('2d');
+ ctx.font = "10px Ahem";
+ ctx.fillText('Hello', 0, 10);
+ postMessage('done');
+ `], { type: "application/javascript" }));
+ var worker = new Worker(url);
+ worker.onmessage = function() {
+ worker.terminate();
+ URL.revokeObjectURL(url);
+ document.documentElement.className = "";
+ };
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.constructor.html b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.constructor.html
new file mode 100644
index 0000000000..a2ad1dcc7e
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.constructor.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#dom-offscreencanvas">
+<script>
+
+test(function() {
+ assert_throws_js(
+ TypeError,
+ () => OffscreenCanvas(100, 50),
+ "Calling OffscreenCanvas constructor without 'new' must throw"
+ );
+}, "OffscreenCanvas constructor called as normal function");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ assert_equals(offscreenCanvas.width, 100);
+ assert_equals(offscreenCanvas.height, 50);
+
+ offscreenCanvas.width = 50;
+ offscreenCanvas.height = 100;
+ assert_equals(offscreenCanvas.width, 50);
+ assert_equals(offscreenCanvas.height, 100);
+}, "Test that calling OffscreenCanvas's constructor generates correct width and height.");
+
+test(function() {
+ var offscreenCanvas1 = new OffscreenCanvas(1, 1);
+
+ offscreenCanvas1.width = null;
+ offscreenCanvas1.height = null;
+ assert_equals(offscreenCanvas1.width, 0);
+ assert_equals(offscreenCanvas1.height, 0);
+
+ assert_throws_js(TypeError, function() { new OffscreenCanvas(-1, -1); });
+
+ var offscreenCanvas2 = new OffscreenCanvas(null, null);
+ assert_equals(offscreenCanvas2.width, 0);
+ assert_equals(offscreenCanvas2.height, 0);
+
+ assert_throws_js(TypeError, function() { offscreenCanvas2.width = -1; });
+ assert_throws_js(TypeError, function() { offscreenCanvas2.height = -1; });
+
+ var obj = {Name: "John Doe", Age: 30};
+ assert_throws_js(TypeError, function() { offscreenCanvas2.width = obj; });
+ assert_throws_js(TypeError, function() { offscreenCanvas2.height = obj; });
+ assert_throws_js(TypeError, function() { new OffscreenCanvas(obj, obj); });
+}, "Test that OffscreenCanvas constructor handles invalid arguments correctly");
+
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.constructor.worker.js b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.constructor.worker.js
new file mode 100644
index 0000000000..72cfb728f0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.constructor.worker.js
@@ -0,0 +1,45 @@
+// spec link: https://html.spec.whatwg.org/#dom-offscreencanvas
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+var t1 = async_test("Test that calling OffscreenCanvas's constructor generates correct width and height.");
+t1.step(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ assert_equals(offscreenCanvas.width, 100);
+ assert_equals(offscreenCanvas.height, 50);
+
+ offscreenCanvas.width = 50;
+ offscreenCanvas.height = 100;
+ assert_equals(offscreenCanvas.width, 50);
+ assert_equals(offscreenCanvas.height, 100);
+ t1.done();
+});
+
+var t2 = async_test("Test that OffscreenCanvas constructor handles invalid arguments correctly in a worker");
+t2.step(function() {
+ var offscreenCanvas1 = new OffscreenCanvas(1, 1);
+
+ offscreenCanvas1.width = null;
+ offscreenCanvas1.height = null;
+ assert_equals(offscreenCanvas1.width, 0);
+ assert_equals(offscreenCanvas1.height, 0);
+
+ assert_throws_js(TypeError, function() { new OffscreenCanvas(-1, -1); });
+
+ var offscreenCanvas2 = new OffscreenCanvas(null, null);
+ assert_equals(offscreenCanvas2.width, 0);
+ assert_equals(offscreenCanvas2.height, 0);
+
+ assert_throws_js(TypeError, function() { offscreenCanvas2.width = -1; });
+ assert_throws_js(TypeError, function() { offscreenCanvas2.height = -1; });
+
+ var obj = {Name: "John Doe", Age: 30};
+ assert_throws_js(TypeError, function() { offscreenCanvas2.width = obj; });
+ assert_throws_js(TypeError, function() { offscreenCanvas2.height = obj; });
+ assert_throws_js(TypeError, function() { new OffscreenCanvas(obj, obj); });
+ t2.done();
+});
+
+done();
+
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.getcontext.html b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.getcontext.html
new file mode 100644
index 0000000000..51c167ddca
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.getcontext.html
@@ -0,0 +1,80 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#dom-offscreencanvas-getcontext">
+<script>
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(1, 1);
+ assert_throws_js(TypeError, function() { offscreenCanvas.getContext('3d'); });
+}, "Test that getContext with un-supported string throws a TypeError.");
+
+test(function() {
+ var offscreenCanvas1 = new OffscreenCanvas(1, 1);
+ var ctx1 = offscreenCanvas1.getContext('2d');
+ assert_true(ctx1 instanceof OffscreenCanvasRenderingContext2D);
+
+ var offscreenCanvas2 = new OffscreenCanvas(1, 1);
+ var ctx2 = offscreenCanvas2.getContext('webgl');
+ assert_true(ctx2 instanceof WebGLRenderingContext);
+
+ var offscreenCanvas3 = new OffscreenCanvas(1, 1);
+ var ctx3 = offscreenCanvas3.getContext('webgl2');
+ assert_true(ctx3 instanceof WebGL2RenderingContext);
+}, "Test that getContext with supported string returns correct results");
+
+test(function() {
+ var offscreenCanvas1 = new OffscreenCanvas(1, 1);
+ var ctx1 = offscreenCanvas1.getContext('2d');
+ var ctx2 = offscreenCanvas1.getContext('webgl');
+ assert_equals(ctx2, null);
+
+ var offscreenCanvas2 = new OffscreenCanvas(1, 1);
+ var ctx3 = offscreenCanvas2.getContext('webgl');
+ var ctx4 = offscreenCanvas2.getContext('2d');
+ assert_equals(ctx4, null);
+}, "Test that getContext twice with different context type returns null the second time");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(1, 2);
+ var ctx = offscreenCanvas.getContext('2d');
+ var dstCanvas = ctx.canvas;
+ assert_equals(dstCanvas.width, 1);
+ assert_equals(dstCanvas.height, 2);
+}, "Test that 2dcontext.canvas should return the original OffscreenCanvas");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(1, 2);
+ var ctx = offscreenCanvas.getContext('webgl');
+ var dstCanvas = ctx.canvas;
+ assert_equals(dstCanvas.width, 1);
+ assert_equals(dstCanvas.height, 2);
+}, "Test that webglcontext.canvas should return the original OffscreenCanvas");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ var ctx = offscreenCanvas.getContext('2d', {alpha: false});
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 10, 10);
+ _assertPixelApprox(offscreenCanvas, 5,5, 0,127,0,255, 2);
+}, "Test that OffscreenCanvasRenderingContext2D with alpha disabled makes the OffscreenCanvas opaque");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ var ctx = offscreenCanvas.getContext('2d', {alpha: true});
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 10, 10);
+ _assertPixelApprox(offscreenCanvas, 5,5, 0,255,0,127, 2);
+}, "Test that OffscreenCanvasRenderingContext2D with alpha enabled preserves the alpha");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ var ctx = offscreenCanvas.getContext('2d');
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 10, 10);
+ _assertPixelApprox(offscreenCanvas, 5,5, 0,255,0,127, 2);
+}, "Test that 'alpha' context creation attribute is true by default");
+
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.getcontext.worker.js b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.getcontext.worker.js
new file mode 100644
index 0000000000..c413791f61
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.getcontext.worker.js
@@ -0,0 +1,77 @@
+// spec link: https://html.spec.whatwg.org/#dom-offscreencanvas-getcontext
+
+importScripts("/resources/testharness.js");
+importScripts("/html/canvas/resources/canvas-tests.js");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(1, 1);
+ assert_throws_js(TypeError, function() { offscreenCanvas.getContext('3d'); });
+}, "Test that getContext with un-supported string throws a TypeError.");
+
+test(function() {
+ var offscreenCanvas1 = new OffscreenCanvas(1, 1);
+ var ctx1 = offscreenCanvas1.getContext('2d');
+ assert_true(ctx1 instanceof OffscreenCanvasRenderingContext2D);
+
+ var offscreenCanvas2 = new OffscreenCanvas(1, 1);
+ var ctx2 = offscreenCanvas2.getContext('webgl');
+ assert_true(ctx2 instanceof WebGLRenderingContext);
+
+ var offscreenCanvas3 = new OffscreenCanvas(1, 1);
+ var ctx3 = offscreenCanvas3.getContext('webgl2');
+ assert_true(ctx3 instanceof WebGL2RenderingContext);
+}, "Test that getContext with supported string returns correct results");
+
+test(function() {
+ var offscreenCanvas1 = new OffscreenCanvas(1, 1);
+ var ctx1 = offscreenCanvas1.getContext('2d');
+ var ctx2 = offscreenCanvas1.getContext('webgl');
+ assert_equals(ctx2, null);
+
+ var offscreenCanvas2 = new OffscreenCanvas(1, 1);
+ var ctx3 = offscreenCanvas2.getContext('webgl');
+ var ctx4 = offscreenCanvas2.getContext('2d');
+ assert_equals(ctx4, null);
+}, "Test that getContext twice with different context type returns null the second time");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(1, 2);
+ var ctx = offscreenCanvas.getContext('2d');
+ var dstCanvas = ctx.canvas;
+ assert_equals(dstCanvas.width, 1);
+ assert_equals(dstCanvas.height, 2);
+}, "Test that 2dcontext.canvas should return the original OffscreenCanvas");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(1, 2);
+ var ctx = offscreenCanvas.getContext('webgl');
+ var dstCanvas = ctx.canvas;
+ assert_equals(dstCanvas.width, 1);
+ assert_equals(dstCanvas.height, 2);
+}, "Test that webglcontext.canvas should return the original OffscreenCanvas");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ var ctx = offscreenCanvas.getContext('2d', {alpha: false});
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 10, 10);
+ _assertPixelApprox(offscreenCanvas, 5,5, 0,127,0,255, 2);
+}, "Test that OffscreenCanvasRenderingContext2D with alpha disabled makes the OffscreenCanvas opaque");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ var ctx = offscreenCanvas.getContext('2d', {alpha: true});
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 10, 10);
+ _assertPixelApprox(offscreenCanvas, 5,5, 0,255,0,127, 2);
+}, "Test that OffscreenCanvasRenderingContext2D with alpha enabled preserves the alpha");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ var ctx = offscreenCanvas.getContext('2d');
+ ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ ctx.fillRect(0, 0, 10, 10);
+ _assertPixelApprox(offscreenCanvas, 5,5, 0,255,0,127, 2);
+}, "Test that 'alpha' context creation attribute is true by default");
+
+done();
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.resize.html b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.resize.html
new file mode 100644
index 0000000000..3ff0eea153
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.resize.html
@@ -0,0 +1,240 @@
+<!DOCTYPE html>
+<title>Test resizing an OffscreenCanvas with a 2d context</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<body></body>
+<script>
+test(function() {
+ var canvas = new OffscreenCanvas(10, 20);
+ canvas.width = 30;
+ canvas.height = 40;
+ assert_equals(canvas.width, 30);
+ assert_equals(canvas.height, 40);
+}, "Verify that writing to the width and height attributes of an OffscreenCanvas works when there is no context attached.");
+
+test(function() {
+ var canvas = new OffscreenCanvas(10, 20);
+ canvas.getContext('2d');
+ canvas.width = 30;
+ canvas.height = 40;
+ assert_equals(canvas.width, 30);
+ assert_equals(canvas.height, 40);
+ var image = canvas.transferToImageBitmap();
+ assert_equals(image.width, 30);
+ assert_equals(image.height, 40);
+}, "Verify that writing to the width and height attributes of an OffscreenCanvas works when there is a 2d context attached.");
+
+test(function() {
+ var canvas = new OffscreenCanvas(10, 20);
+ canvas.getContext('webgl');
+ canvas.width = 30;
+ canvas.height = 40;
+ assert_equals(canvas.width, 30);
+ assert_equals(canvas.height, 40);
+ var image = canvas.transferToImageBitmap();
+ assert_equals(image.width, 30);
+ assert_equals(image.height, 40);
+}, "Verify that writing to the width and height attributes of an OffscreenCanvas works when there is a webgl context attached.");
+
+test(function() {
+ var placeholder = document.createElement('canvas');
+ placeholder.width = 2;
+ placeholder.height = 2;
+ var offscreen = placeholder.transferControlToOffscreen();
+ assert_throws_dom("InvalidStateError", () => { placeholder.width = 1; });
+ assert_throws_dom("InvalidStateError", () => { placeholder.height = 1; });
+}, "Verify that writing to the width or height attribute of a placeholder canvas throws an exception");
+
+test(function() {
+ var placeholder = document.createElement('canvas');
+ placeholder.width = 1;
+ placeholder.height = 1;
+ var offscreen = placeholder.transferControlToOffscreen();
+ assert_throws_dom("InvalidStateError", () => { placeholder.width = 1; });
+ assert_throws_dom("InvalidStateError", () => { placeholder.height = 1; });
+}, "Verify that writing to the width or height attribute of a placeholder canvas throws an exception even when not changing the value of the attribute.");
+
+test(function() {
+ var canvas = new OffscreenCanvas(10, 20);
+ var ctx = canvas.getContext('2d');
+ ctx.lineWidth = 5;
+ canvas.width = 30;
+ assert_equals(ctx.lineWidth, 1);
+ ctx.lineWidth = 5;
+ canvas.height = 40;
+ assert_equals(ctx.lineWidth, 1);
+}, "Verify that resizing a 2d context resets its state.");
+
+test(function() {
+ var canvas = new OffscreenCanvas(10, 20);
+ var ctx = canvas.getContext('2d');
+ ctx.lineWidth = 5;
+ canvas.width = canvas.width;
+ assert_equals(ctx.lineWidth, 1);
+ ctx.lineWidth = 5;
+ canvas.height = canvas.height;
+ assert_equals(ctx.lineWidth, 1);
+}, "Verify that setting the size of a 2d context to the same size it already had resets its state.");
+
+async_test(function(t) {
+ var placeholder = document.createElement('canvas');
+ document.body.appendChild(placeholder); // So that we can check computed style/
+ placeholder.width = 10;
+ placeholder.height = 20;
+ var offscreen = placeholder.transferControlToOffscreen();
+ var ctx = offscreen.getContext('2d');
+ t.step(function() {
+ // Verify that setting the size of an OffscreenCanvas that has a placeholder works.
+ offscreen.width = 30;
+ offscreen.height = 40;
+ assert_equals(offscreen.width, 30);
+ assert_equals(offscreen.height, 40);
+ var image = offscreen.transferToImageBitmap();
+ assert_equals(image.width, 30);
+ assert_equals(image.height, 40);
+ });
+ t.step(function() {
+ // Verify that setting the size of an OffscreenCanvas does not directly update the size of its placeholder canvas.
+ assert_equals(placeholder.width, 10);
+ assert_equals(placeholder.height, 20);
+ });
+ var asyncStepsCompleted = 0;
+ createImageBitmap(placeholder).then(image => {
+ t.step(function() {
+ // Verify that the placeholder was not updated synchronously.
+ assert_equals(image.width, 10);
+ assert_equals(image.height, 20);
+ });
+ asyncStepsCompleted = asyncStepsCompleted + 1;
+ if (asyncStepsCompleted == 2) {
+ t.done();
+ }
+ });
+
+ function testImage() {
+ if (placeholder.width != 30) {
+ requestAnimationFrame(testImage);
+ } else {
+ t.step(function() {
+ assert_equals(placeholder.width, 30);
+ assert_equals(placeholder.height, 40);
+ var computedStyle = window.getComputedStyle(placeholder);
+ assert_equals(computedStyle.getPropertyValue('width'), "30px");
+ assert_equals(computedStyle.getPropertyValue('height'), "40px");
+ });
+ createImageBitmap(placeholder).then(image => {
+ t.step(function() {
+ // Verify that an image grabbed from the placeholder has the correct dimensions
+ assert_equals(image.width, 30);
+ assert_equals(image.height, 40);
+ });
+ asyncStepsCompleted = asyncStepsCompleted + 1;
+ if (asyncStepsCompleted == 2) {
+ t.done();
+ }
+ });
+ }
+ }
+
+ requestAnimationFrame(testImage);
+}, "Verify that resizing an OffscreenCanvas with a 2d context propagates the new size to its placeholder canvas asynchronously.");
+
+async_test(function(t) {
+ var placeholder = document.createElement('canvas');
+ document.body.appendChild(placeholder); // So that we can check computed style/
+ placeholder.width = 10;
+ placeholder.height = 20;
+ var offscreen = placeholder.transferControlToOffscreen();
+ var ctx = offscreen.getContext('webgl');
+ t.step(function() {
+ // Verify that setting the size of an OffscreenCanvas that has a placeholder works.
+ offscreen.width = 30;
+ offscreen.height = 40;
+ assert_equals(offscreen.width, 30);
+ assert_equals(offscreen.height, 40);
+ var image = offscreen.transferToImageBitmap();
+ assert_equals(image.width, 30);
+ assert_equals(image.height, 40);
+ });
+ t.step(function() {
+ // Verify that setting the size of an OffscreenCanvas does not directly update the size of its placeholder canvas.
+ assert_equals(placeholder.width, 10);
+ assert_equals(placeholder.height, 20);
+ });
+ var asyncStepsCompleted = 0;
+ createImageBitmap(placeholder).then(image => {
+ t.step(function() {
+ // Verify that the placeholder was not updated synchronously.
+ assert_equals(image.width, 10);
+ assert_equals(image.height, 20);
+ });
+ asyncStepsCompleted = asyncStepsCompleted + 1;
+ if (asyncStepsCompleted == 2) {
+ t.done();
+ }
+ });
+
+ function testImage() {
+ if (placeholder.width != 30) {
+ requestAnimationFrame(testImage);
+ } else {
+ t.step(function() {
+ assert_equals(placeholder.width, 30);
+ assert_equals(placeholder.height, 40);
+ var computedStyle = window.getComputedStyle(placeholder);
+ assert_equals(computedStyle.getPropertyValue('width'), "30px");
+ assert_equals(computedStyle.getPropertyValue('height'), "40px");
+ });
+ createImageBitmap(placeholder).then(image => {
+ t.step(function() {
+ // Verify that an image grabbed from the placeholder has the correct dimensions
+ assert_equals(image.width, 30);
+ assert_equals(image.height, 40);
+ });
+ asyncStepsCompleted = asyncStepsCompleted + 1;
+ if (asyncStepsCompleted == 2) {
+ t.done();
+ }
+ });
+ }
+ }
+ requestAnimationFrame(testImage);
+}, "Verify that resizing an OffscreenCanvas with a webgl context propagates the new size to its placeholder canvas asynchronously.");
+
+async_test(function(t){
+ var placeholder = document.createElement('canvas');
+ placeholder.width = 1;
+ placeholder.height = 1;
+ var offscreen = placeholder.transferControlToOffscreen();
+ var ctx = offscreen.getContext('2d');
+ offscreen.width = offscreen.height = 10;
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 10, 10);
+
+ var testCanvas = document.createElement('canvas');
+ testCanvas.width = testCanvas.height = 20;
+ var testCtx = testCanvas.getContext('2d');
+
+ function testImage() {
+ if (placeholder.width != 10) {
+ requestAnimationFrame(testImage);
+ } else {
+ testCtx.drawImage(placeholder, 0, 0);
+ var pixel1 = testCtx.getImageData(9, 9, 1, 1).data;
+ var pixel2 = testCtx.getImageData(9, 10, 1, 1).data;
+ var pixel3 = testCtx.getImageData(10, 9, 1, 1).data;
+ t.step(function() {
+ assert_equals(placeholder.width, 10);
+ assert_equals(placeholder.height, 10);
+ assert_array_equals(pixel1, [0, 255, 0, 255]);
+ assert_array_equals(pixel2, [0, 0, 0, 0]);
+ assert_array_equals(pixel3, [0, 0, 0, 0]);
+ });
+ t.done();
+ }
+ }
+
+ requestAnimationFrame(testImage);
+}, "Verify that drawImage uses the size of the frame as the intinsic size of a placeholder canvas.");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.lowlatency.nocrash.html b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.lowlatency.nocrash.html
new file mode 100644
index 0000000000..1960841564
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.lowlatency.nocrash.html
@@ -0,0 +1,12 @@
+<title>Transfer Low-Latency Canvas</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<body></body>
+
+<script>
+test(function(t) {
+ var ctx = document.createElement("canvas").getContext('2d', {desynchronized:true});
+ assert_throws_dom("InvalidStateError", () => { ctx.canvas.transferControlToOffscreen(); });
+}, "Tests that transferring a low latency canvas does not cause a crash. See crbug.com/1255153");
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap.html b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap.html
new file mode 100644
index 0000000000..6e1adf9591
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap.html
@@ -0,0 +1,112 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#dom-offscreencanvas-transfertoimagebitmap">
+
+<script id="myWorker" type="text/worker">
+
+self.onmessage = function(e) {
+};
+
+</script>
+
+<script>
+function makeWorker(script)
+{
+ var blob = new Blob([script]);
+ return new Worker(URL.createObjectURL(blob));
+}
+
+test(function() {
+ function testSize(contextType) {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext(contextType);
+ var image = offscreenCanvas.transferToImageBitmap();
+ assert_equals(image.width, 100);
+ assert_equals(image.height, 50);
+ }
+
+ testSize('2d');
+ testSize('webgl');
+}, "Test that transferToImageBitmap returns an ImageBitmap with correct width and height");
+
+test(function() {
+ function testImageBitmapClr(shouldCallTwice, alphaVal) {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d', {alpha: alphaVal});
+ ctx.fillStyle = "#0f0";
+ ctx.fillRect(0, 0, 100, 50);
+ var image = offscreenCanvas.transferToImageBitmap();
+
+ if (shouldCallTwice)
+ image = offscreenCanvas.transferToImageBitmap();
+
+ var drawCanvas = document.createElement('canvas');
+ drawCanvas.width = 100;
+ drawCanvas.height = 50;
+ var dCtx = drawCanvas.getContext('2d');
+ dCtx.drawImage(image, 0, 0);
+
+ if (shouldCallTwice) {
+ if (alphaVal)
+ _assertPixel(drawCanvas, 50,25, 0,0,0,0);
+ else
+ _assertPixel(drawCanvas, 50,25, 0,0,0,255);
+ } else {
+ _assertPixel(drawCanvas, 50,25, 0,255,0,255);
+ }
+ }
+
+ testImageBitmapClr(false, true);
+ testImageBitmapClr(true, true);
+ testImageBitmapClr(true, false);
+}, "Test that transferToImageBitmap returns an ImageBitmap with correct color");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ ctx.lineWidth = 10;
+ var image = offscreenCanvas.transferToImageBitmap();
+ assert_equals(ctx.lineWidth, 10);
+}, "Test that transferToImageBitmap won't change context's property");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ ctx.rect(0, 0, 25, 50);
+ ctx.clip();
+ ctx.translate(20, 20);
+
+ ctx.fillStyle = '#0f0';
+ var image1 = offscreenCanvas.transferToImageBitmap();
+ // trasnform should be preserved
+ ctx.fillRect(0, 0, 10, 10);
+ var image2 = offscreenCanvas.transferToImageBitmap();
+
+ var drawCanvas = document.createElement('canvas');
+ drawCanvas.width = 100;
+ drawCanvas.height = 50;
+ var dCtx = drawCanvas.getContext('2d');
+ dCtx.drawImage(image2, 0, 0);
+ // Verify that transform was carried over.
+ _assertPixel(drawCanvas, 23,25, 0,255,0,255);
+ // Verify that clip was carried over.
+ _assertPixel(drawCanvas, 27,25, 0,0,0,0);
+}, "Test that transferToImageBitmap preserves transform");
+
+async_test(function(t) {
+ var worker = makeWorker(document.getElementById("myWorker").textContent);
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ worker.postMessage(offscreenCanvas, [offscreenCanvas]);
+ assert_throws_dom("InvalidStateError", function() { offscreenCanvas.transferToImageBitmap(); });
+ t.done();
+}, "Test that call transferToImageBitmap on a detached OffscreenCanvas throws an exception");
+
+test(function() {
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ assert_throws_dom("InvalidStateError", function() { offscreenCanvas.transferToImageBitmap(); });
+}, "Test that transferToImageBitmap without a context throws an exception");
+
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap.nocrash.html b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap.nocrash.html
new file mode 100644
index 0000000000..09a388ed35
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap.nocrash.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<body>
+ <p>Tests that an ImageBitmap in Offscreen without content does not crash.</p>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+
+async_test(function(t) {
+ var v1 = document.createElement("canvas");
+ var v2 = v1.transferControlToOffscreen();
+ var v3 = v2.getContext("bitmaprenderer");
+ v2.width = 67;
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap.w.html b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap.w.html
new file mode 100644
index 0000000000..b51ce0efa2
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap.w.html
@@ -0,0 +1,201 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#dom-offscreencanvas-transfertoimagebitmap">
+
+<script id="myWorker" type="text/worker">
+
+function testSize(contextType)
+{
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext(contextType);
+ var image = offscreenCanvas.transferToImageBitmap();
+ if (image.width == 100 && image.height == 50)
+ return true;
+ return false;
+}
+
+function testImageBitmapClr(shouldCallTwice, alphaVal) {
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d', {alpha: alphaVal});
+ ctx.fillStyle = "#0f0";
+ ctx.fillRect(0, 0, 100, 50);
+ var image = offscreenCanvas.transferToImageBitmap();
+
+ if (shouldCallTwice)
+ image = offscreenCanvas.transferToImageBitmap();
+ return image;
+}
+
+function isInvalidStateError(funcStr, offscreenCanvas)
+{
+ try {
+ eval(funcStr);
+ } catch (e) {
+ if (e instanceof DOMException && e.name == "InvalidStateError")
+ return true;
+ return false;
+ }
+}
+
+function testImageBitmapSize()
+{
+ return testSize('2d') && testSize('webgl');
+}
+
+function testLineWidthNotAltered()
+{
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ ctx.lineWidth = 10;
+ var image = offscreenCanvas.transferToImageBitmap();
+ return ctx.lineWidth == 10;
+}
+
+function testTransformPreserved()
+{
+ var offscreenCanvas = new OffscreenCanvas(100, 50);
+ var ctx = offscreenCanvas.getContext('2d');
+ ctx.rect(0, 0, 25, 50);
+ ctx.clip();
+ ctx.translate(20, 20);
+
+ ctx.fillStyle = '#0f0';
+ var image1 = offscreenCanvas.transferToImageBitmap();
+ // trasnform should be preserved
+ ctx.fillRect(0, 0, 10, 10);
+ var image2 = offscreenCanvas.transferToImageBitmap();
+ return image2;
+}
+
+function testException()
+{
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ return isInvalidStateError("offscreenCanvas.transferToImageBitmap()", offscreenCanvas);
+}
+
+self.onmessage = function(e) {
+ switch (e.data) {
+ case 'test1':
+ self.postMessage(testImageBitmapSize());
+ break;
+ case 'test2':
+ self.postMessage(testImageBitmapClr(false, true));
+ break;
+ case 'test3':
+ self.postMessage(testImageBitmapClr(true, true));
+ break;
+ case 'test4':
+ self.postMessage(testImageBitmapClr(true, false));
+ break;
+ case 'test5':
+ self.postMessage(testLineWidthNotAltered());
+ break;
+ case 'test6':
+ self.postMessage(testTransformPreserved());
+ break;
+ case 'test7':
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ self.postMessage({offscreenCanvas: offscreenCanvas}, [offscreenCanvas]);
+ self.postMessage(isInvalidStateError("offscreenCanvas.transferToImageBitmap()", offscreenCanvas));
+ break;
+ case 'test8':
+ self.postMessage(testException());
+ break;
+ }
+};
+
+</script>
+
+<script>
+function makeWorker(test) {
+ var blob = new Blob([document.getElementById("myWorker").textContent]);
+ var worker = new Worker(URL.createObjectURL(blob));
+ worker.onerror = test.unreached_func("error");
+ return worker;
+}
+
+function drawImageBitmap(image, x, y)
+{
+ var drawCanvas = document.createElement('canvas');
+ drawCanvas.width = 100;
+ drawCanvas.height = 50;
+ var dCtx = drawCanvas.getContext('2d');
+ dCtx.drawImage(image, 0, 0);
+ return dCtx.getImageData(x, y, 1, 1).data;
+}
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ assert_true(msg.data);
+ }));
+ worker.postMessage('test1');
+}, "Test that transferToImageBitmap returns an ImageBitmap with correct width and height in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ var clr = drawImageBitmap(msg.data, 50, 25);
+ assert_array_equals(clr, [0, 255, 0, 255]);
+ }));
+ worker.postMessage('test2');
+}, "Test that transferToImageBitmap returns an ImageBitmap with correct color in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ var clr = drawImageBitmap(msg.data, 50, 25);
+ assert_array_equals(clr, [0, 0, 0, 0]);
+ }));
+ worker.postMessage('test3');
+}, "Test that call transferToImageBitmap twice returns an ImageBitmap with correct color in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ var clr = drawImageBitmap(msg.data, 50, 25);
+ assert_array_equals(clr, [0, 0, 0, 255]);
+ }));
+ worker.postMessage('test4');
+}, "Test that call transferToImageBitmap twice on a alpha-disabled context returns an ImageBitmap with correct color in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ assert_true(msg.data);
+ }));
+ worker.postMessage('test5');
+}, "Test that transferToImageBitmap won't change context's property in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ var clr1 = drawImageBitmap(msg.data, 23, 25);
+ assert_array_equals(clr1, [0, 255, 0, 255]);
+ var clr2 = drawImageBitmap(msg.data, 27, 25);
+ assert_array_equals(clr2, [0, 0, 0, 0]);
+ }));
+ worker.postMessage('test6');
+}, "Test that call transferToImageBitmap preserves transform in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ if (msg.data == true || msg.data == false)
+ assert_true(msg.data);
+ }));
+ worker.postMessage('test7');
+}, "Test that call transferToImageBitmap on a detached OffscreenCanvas throws an exception in a worker");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ assert_true(msg.data);
+ }));
+ worker.postMessage('test8');
+}, "Test that call transferToImageBitmap without a context throws an exception in a worker");
+
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfercontrol.to.offscreen.html b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfercontrol.to.offscreen.html
new file mode 100644
index 0000000000..fea375b2ad
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfercontrol.to.offscreen.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#dom-canvas-transfercontroltooffscreen">
+<script>
+
+test(function() {
+ var placeholder = document.createElement('canvas');
+ placeholder.width = 100;
+ placeholder.height = 50;
+ var offscreenCanvas = placeholder.transferControlToOffscreen();
+ assert_equals(offscreenCanvas.width, 100);
+ assert_equals(offscreenCanvas.height, 50);
+}, "Test that an OffscreenCanvas generated by transferControlToOffscreen gets correct width and height");
+
+test(function() {
+ var placeholder = document.createElement('canvas');
+ placeholder.width = 100;
+ placeholder.height = 50;
+ var offscreenCanvas = placeholder.transferControlToOffscreen();
+ assert_throws_dom("InvalidStateError", function() { placeholder.getContext('2d'); });
+}, "Test that calling getContext on a placeholder canvas that has already transferred its control throws an exception");
+
+test(function() {
+ var placeholder = document.createElement('canvas');
+ placeholder.width = 100;
+ placeholder.height = 50;
+ var offscreenCanvas = placeholder.transferControlToOffscreen();
+ assert_throws_dom("InvalidStateError", function() { placeholder.transferControlToOffscreen(); });
+}, "Test that calling transferControlToOffscreen twice throws an exception");
+
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfercontrol.to.offscreen.w.html b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfercontrol.to.offscreen.w.html
new file mode 100644
index 0000000000..1347e7b50b
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfercontrol.to.offscreen.w.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#dom-canvas-transfercontroltooffscreen">
+
+<script id="myWorker" type="text/worker">
+
+function testSize(offscreenCanvas)
+{
+ if (offscreenCanvas.width == 100 && offscreenCanvas.height == 50)
+ return true;
+ return false;
+}
+
+self.onmessage = function(e) {
+ switch (e.data.msg) {
+ case 'test1':
+ self.postMessage(testSize(e.data.data));
+ break;
+ case 'test2':
+ self.postMessage("");
+ break;
+ case 'test3':
+ self.postMessage("");
+ break;
+ }
+};
+
+</script>
+
+<script>
+function makeWorker(script)
+{
+ var blob = new Blob([script]);
+ return new Worker(URL.createObjectURL(blob));
+}
+
+async_test(function(t) {
+ var placeholder = document.createElement('canvas');
+ placeholder.width = 100;
+ placeholder.height = 50;
+ var offscreenCanvas = placeholder.transferControlToOffscreen();
+ var worker = makeWorker(document.getElementById("myWorker").textContent);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ assert_true(msg.data);
+ }));
+ worker.postMessage({msg: 'test1', data: offscreenCanvas}, [offscreenCanvas]);
+}, "Test that an OffscreenCanvas generated by transferControlToOffscreen gets correct width and height when it is transferred to a worker");
+
+async_test(function(t) {
+ var placeholder = document.createElement('canvas');
+ placeholder.width = 100;
+ placeholder.height = 50;
+ var offscreenCanvas = placeholder.transferControlToOffscreen();
+ var worker = makeWorker(document.getElementById("myWorker").textContent);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ assert_throws_dom("InvalidStateError", function() { placeholder.getContext('2d'); });
+ }));
+ worker.postMessage({msg: 'test2', data: offscreenCanvas}, [offscreenCanvas]);
+}, "Test that calling getContext on a placeholder canvas that is transferred its control to an OffscreenCanvas throws an exception, when the OffscreenCanvas is transferred to a worker");
+
+async_test(function(t) {
+ var placeholder = document.createElement('canvas');
+ placeholder.width = 100;
+ placeholder.height = 50;
+ var offscreenCanvas = placeholder.transferControlToOffscreen();
+ var worker = makeWorker(document.getElementById("myWorker").textContent);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ assert_throws_dom("InvalidStateError", function() { placeholder.transferControlToOffscreen(); });
+ }));
+ worker.postMessage({msg: 'test3', data: offscreenCanvas}, [offscreenCanvas]);
+}, "Test that calling transferControlToOffscreen twice throws an exception, when its associated OffscreenCanvas is transferred to a worker");
+
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transferrable.html b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transferrable.html
new file mode 100644
index 0000000000..76796034e9
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transferrable.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#offscreencanvas">
+
+<script id="myWorker" type="text/worker">
+
+function test1(offscreenCanvas)
+{
+ return offscreenCanvas.width == 10 && offscreenCanvas.height == 10;
+}
+
+self.onmessage = function(e) {
+ switch(e.data.msg) {
+ case 'test1':
+ self.postMessage(test1(e.data.data));
+ break;
+ }
+};
+
+</script>
+
+<script>
+function makeWorker(script)
+{
+ var blob = new Blob([script]);
+ return new Worker(URL.createObjectURL(blob));
+}
+
+async_test(function(t) {
+ var worker = makeWorker(document.getElementById("myWorker").textContent);
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ worker.postMessage({msg: 'test1', data: offscreenCanvas}, [offscreenCanvas]);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ assert_true(msg.data);
+ }));
+ assert_equals(offscreenCanvas.width, 0);
+ assert_equals(offscreenCanvas.height, 0);
+}, "Test that offscreenCanvas's size is correct after being transferred to a worker.");
+
+
+test(function() {
+ function testException(contextType) {
+ var worker = makeWorker(document.getElementById("myWorker").textContent);
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ var ctx = offscreenCanvas.getContext(contextType);
+ assert_throws_dom("InvalidStateError", function() {
+ worker.postMessage({offscreenCanvas}, [offscreenCanvas]);
+ });
+ }
+ testException('2d');
+}, "Test that transfer an OffscreenCanvas that already have a 2d context throws exception.");
+
+test(function() {
+ var worker = makeWorker(document.getElementById("myWorker").textContent);
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ worker.postMessage({offscreenCanvas}, [offscreenCanvas]);
+ assert_throws_dom("DataCloneError", function() {
+ worker.postMessage({offscreenCanvas}, [offscreenCanvas]);
+ });
+}, "Test that transfer an OffscreenCanvas twice throws exception.");
+
+test(function() {
+ var worker = makeWorker(document.getElementById("myWorker").textContent);
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ worker.postMessage({offscreenCanvas}, [offscreenCanvas]);
+ assert_throws_dom("InvalidStateError", function() {
+ offscreenCanvas.getContext('2d');
+ });
+}, "Test that calling getContext('2d') on a detached OffscreenCanvas throws exception.");
+
+test(function() {
+ var worker = makeWorker(document.getElementById("myWorker").textContent);
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ worker.postMessage({offscreenCanvas}, [offscreenCanvas]);
+ assert_throws_dom("InvalidStateError", function() {
+ offscreenCanvas.getContext('webgl');
+ });
+}, "Test that calling getContext('webgl') on a detached OffscreenCanvas throws exception.");
+
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transferrable.w.html b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transferrable.w.html
new file mode 100644
index 0000000000..38f981e8f0
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transferrable.w.html
@@ -0,0 +1,142 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#offscreencanvas">
+
+<script id="myWorker" type="text/worker">
+
+function isInvalidStateError(funcStr, offscreenCanvas)
+{
+ try {
+ eval(funcStr);
+ } catch (e) {
+ if (e instanceof DOMException && e.name == "InvalidStateError")
+ return true;
+ return false;
+ }
+}
+
+function testExceptionWith2DContext()
+{
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ var ctx = offscreenCanvas.getContext('2d');
+ return isInvalidStateError("self.postMessage(offscreenCanvas, [offscreenCanvas])", offscreenCanvas);
+}
+
+function testExceptionWithWebGLContext()
+{
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ var ctx = offscreenCanvas.getContext('webgl');
+ return isInvalidStateError("self.postMessage(offscreenCanvas, [offscreenCanvas])", offscreenCanvas);
+}
+
+function testExceptionWithDetachedOffscreenCanvas1()
+{
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ self.postMessage(offscreenCanvas, [offscreenCanvas]);
+ return isInvalidStateError("self.postMessage(offscreenCanvas, [offscreenCanvas])", offscreenCanvas);
+}
+
+function testExceptionWithDetachedOffscreenCanvas2()
+{
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ self.postMessage(offscreenCanvas, [offscreenCanvas]);
+ return isInvalidStateError("offscreenCanvas.getContext('2d')", offscreenCanvas);
+}
+
+function testExceptionWithDetachedOffscreenCanvas3()
+{
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ self.postMessage(offscreenCanvas, [offscreenCanvas]);
+ return isInvalidStateError("offscreenCanvas.getContext('webgl')", offscreenCanvas);
+}
+
+self.onmessage = function(e) {
+ switch(e.data) {
+ case 'test1':
+ var offscreenCanvas = new OffscreenCanvas(10, 10);
+ self.postMessage(offscreenCanvas, [offscreenCanvas]);
+ break;
+ case 'test2':
+ self.postMessage(testExceptionWith2DContext());
+ break;
+ case 'test3':
+ self.postMessage(testExceptionWithWebGLContext());
+ break;
+ case 'test4':
+ self.postMessage(testExceptionWithDetachedOffscreenCanvas1());
+ break;
+ case 'test5':
+ self.postMessage(testExceptionWithDetachedOffscreenCanvas2());
+ break;
+ case 'test6':
+ self.postMessage(testExceptionWithDetachedOffscreenCanvas3());
+ break;
+ }
+};
+
+</script>
+
+<script>
+function makeWorker(test) {
+ var blob = new Blob([document.getElementById("myWorker").textContent]);
+ var worker = new Worker(URL.createObjectURL(blob));
+ worker.onerror = test.unreached_func("error");
+ return worker;
+}
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ assert_equals(msg.data.width, 10);
+ assert_equals(msg.data.height, 10);
+ }));
+ worker.postMessage('test1');
+}, "Test that OffscreenCanvas's size is correct after being transferred from a worker.");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ assert_true(msg.data);
+ }));
+ worker.postMessage('test2');
+}, "Test that transfer an OffscreenCanvas that has a 2d context throws exception in a worker.");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ assert_true(msg.data);
+ }));
+ worker.postMessage('test3');
+}, "Test that transfer an OffscreenCanvas that has a webgl context throws exception in a worker.");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ if (msg.data == true || msg.data == false)
+ assert_true(msg.data);
+ }));
+ worker.postMessage('test4');
+}, "Test that transfer an OffscreenCanvas twice throws exception in a worker.");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ if (msg.data == true || msg.data == false)
+ assert_true(msg.data);
+ }));
+ worker.postMessage('test5');
+}, "Test that calling getContext('2d') on a detached OffscreenCanvas throws exception in a worker.");
+
+async_test(function(t) {
+ var worker = makeWorker(t);
+ worker.addEventListener('message', t.step_func_done(function(msg) {
+ if (msg.data == true || msg.data == false)
+ assert_true(msg.data);
+ }));
+ worker.postMessage('test6');
+}, "Test that calling getContext('webgl') on a detached OffscreenCanvas throws exception in a worker.");
+
+</script>
+
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/transformations/2d.transformation.getTransform.html b/testing/web-platform/tests/html/canvas/offscreen/manual/transformations/2d.transformation.getTransform.html
new file mode 100644
index 0000000000..b3b70ac208
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/transformations/2d.transformation.getTransform.html
@@ -0,0 +1,40 @@
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+// Ensure that context2d.getTransform works
+const epsilon = 1e-5;
+const canvas = new OffscreenCanvas(300, 150);
+const ctx = canvas.getContext('2d');
+
+test(function(t) {
+
+ assert_array_equals(ctx.getTransform().toFloat32Array(),
+ [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
+ "Assert that an untransformed matrix is identity");
+
+ ctx.scale(2, 3);
+ transform = ctx.getTransform();
+ assert_array_equals(ctx.getTransform().toFloat32Array(),
+ [2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
+ "Assert that context2d scaling works");
+
+ ctx.rotate(Math.PI/2);
+ transform = ctx.getTransform();
+ assert_array_approx_equals(ctx.getTransform().toFloat32Array(),
+ [0, 3, 0, 0, -2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], epsilon,
+ "Assert that context2d rotate works");
+
+ ctx.translate(1, -1);
+ transform = ctx.getTransform();
+ assert_array_approx_equals(ctx.getTransform().toFloat32Array(),
+ [0, 3, 0, 0, -2, 0, 0, 0, 0, 0, 1, 0, 2, 3, 0, 1], epsilon,
+ "Assert context2d translate works.");
+
+ ctx.resetTransform();
+ assert_array_equals(ctx.getTransform().toFloat32Array(),
+ [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
+ "Assert that a reset matrix is identity");
+}, 'This test ensures that getTransform works correctly.');
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/canvas/offscreen/manual/wide-gamut-canvas/2d.color.space.p3.convertToBlobp3.canvas.html b/testing/web-platform/tests/html/canvas/offscreen/manual/wide-gamut-canvas/2d.color.space.p3.convertToBlobp3.canvas.html
new file mode 100644
index 0000000000..9fc63a9f49
--- /dev/null
+++ b/testing/web-platform/tests/html/canvas/offscreen/manual/wide-gamut-canvas/2d.color.space.p3.convertToBlobp3.canvas.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<title>OffscreenCanvas test: 2d.color.space.p3.convertToBlob.p3.canvas</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.color.space.p3.convertToBlob.p3.canvas</h1>
+<p class="desc">test if toblob returns p3 data from p3 color space canvas</p>
+
+
+<script>
+var t = async_test("test if toblob returns p3 data from p3 color space canvas");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+ throw reason;
+});
+t.step(function() {
+
+var offscreenCanvas = new OffscreenCanvas(100, 50);
+var ctx = offscreenCanvas.getContext('2d', {colorSpace: "display-p3"});
+
+ctx.fillStyle = "rgba(155, 27, 27, 1)";
+ctx.fillRect(0, 0, 1, 1);
+ctx.fillStyle = "rgba(27, 155, 27, 0)";
+ctx.fillRect(1, 0, 1, 1);
+ctx.fillStyle = "rgba(27, 27, 155, 0.5)";
+ctx.fillRect(0, 1, 1, 1);
+ctx.fillStyle = "rgba(27, 27, 27, 0.5)";
+ctx.fillRect(1, 1, 1, 1);
+expectedPixels = ctx.getImageData(0, 0, 2, 2, {colorSpace: "display-p3"}).data;
+
+var image = new Image();
+image.onload = t.step_func_done(function() {
+ var dstCanvas = document.createElement("canvas");
+ dstCanvas.width = 2;
+ dstCanvas.height = 2;
+ var ctx = dstCanvas.getContext('2d', {colorSpace: "display-p3"});
+ ctx.drawImage(image, 0, 0);
+ var actualPixels = ctx.getImageData(0, 0, 2, 2, {colorSpace: "display-p3"}).data;
+ assert_array_approx_equals(actualPixels, expectedPixels, 2);
+});
+
+offscreenCanvas.convertToBlob(function(blob) {
+ var urlCreator = window.URL || window.webkitURL;
+ image.src = urlCreator.createObjectURL(blob);
+}, 'image/png', 1);
+t.done()
+
+});
+</script>