Correctly rebuild cc::PaintCanvas matrix stack when layers are used.
Whenever a frame is rendered while layers are opened (e.g. by calling
getImageData between calls to beginLayer and endLayer), the
cc::PaintCanvas stack have to be flushed and all layer closed. This stack
then has to be rebuilt, re-opening any layers that were previously
opened.
Bug: 1237275, 1396372, 1403977
Fixed: 1278112
Change-Id: I8a6d0aded67d35ff625f72c8138b1f2fe682561a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4538303
Reviewed-by: Justin Novosad <junov@chromium.org>
Commit-Queue: Jean-Philippe Gravel <jpgravel@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1146245}
diff --git a/html/canvas/element/layers/2d.layer.render-opportunities.createImageBitmap-expected.html b/html/canvas/element/layers/2d.layer.render-opportunities.createImageBitmap-expected.html
new file mode 100644
index 0000000..8ce0c51
--- /dev/null
+++ b/html/canvas/element/layers/2d.layer.render-opportunities.createImageBitmap-expected.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.render-opportunities.createImageBitmap</title>
+<h1>2d.layer.render-opportunities.createImageBitmap</h1>
+<p class="desc">Check that layers state stack is flushed and rebuilt on frame renders.</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);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.endLayer();
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+</script>
diff --git a/html/canvas/element/layers/2d.layer.render-opportunities.createImageBitmap.html b/html/canvas/element/layers/2d.layer.render-opportunities.createImageBitmap.html
new file mode 100644
index 0000000..4ffcaa3
--- /dev/null
+++ b/html/canvas/element/layers/2d.layer.render-opportunities.createImageBitmap.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.render-opportunities.createImageBitmap-expected.html">
+<title>Canvas test: 2d.layer.render-opportunities.createImageBitmap</title>
+<h1>2d.layer.render-opportunities.createImageBitmap</h1>
+<p class="desc">Check that layers state stack is flushed and rebuilt on frame renders.</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);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+
+ // Force a flush and restoration of the state stack:
+ createImageBitmap(canvas);
+
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+</script>
diff --git a/html/canvas/element/layers/2d.layer.render-opportunities.drawImage-expected.html b/html/canvas/element/layers/2d.layer.render-opportunities.drawImage-expected.html
new file mode 100644
index 0000000..1ddc6d1
--- /dev/null
+++ b/html/canvas/element/layers/2d.layer.render-opportunities.drawImage-expected.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.render-opportunities.drawImage</title>
+<h1>2d.layer.render-opportunities.drawImage</h1>
+<p class="desc">Check that layers state stack is flushed and rebuilt on frame renders.</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);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.endLayer();
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+</script>
diff --git a/html/canvas/element/layers/2d.layer.render-opportunities.drawImage.html b/html/canvas/element/layers/2d.layer.render-opportunities.drawImage.html
new file mode 100644
index 0000000..4eb0b6f
--- /dev/null
+++ b/html/canvas/element/layers/2d.layer.render-opportunities.drawImage.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.render-opportunities.drawImage-expected.html">
+<title>Canvas test: 2d.layer.render-opportunities.drawImage</title>
+<h1>2d.layer.render-opportunities.drawImage</h1>
+<p class="desc">Check that layers state stack is flushed and rebuilt on frame renders.</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);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+
+ // Force a flush and restoration of the state stack:
+ const canvas2 = new OffscreenCanvas(200, 200);
+ const ctx2 = canvas2.getContext('2d');
+ ctx2.drawImage(canvas, 0, 0);
+
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+</script>
diff --git a/html/canvas/element/layers/2d.layer.render-opportunities.getImageData-expected.html b/html/canvas/element/layers/2d.layer.render-opportunities.getImageData-expected.html
new file mode 100644
index 0000000..ea0e78e
--- /dev/null
+++ b/html/canvas/element/layers/2d.layer.render-opportunities.getImageData-expected.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.render-opportunities.getImageData</title>
+<h1>2d.layer.render-opportunities.getImageData</h1>
+<p class="desc">Check that layers state stack is flushed and rebuilt on frame renders.</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);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.endLayer();
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+</script>
diff --git a/html/canvas/element/layers/2d.layer.render-opportunities.getImageData.html b/html/canvas/element/layers/2d.layer.render-opportunities.getImageData.html
new file mode 100644
index 0000000..3cf514a
--- /dev/null
+++ b/html/canvas/element/layers/2d.layer.render-opportunities.getImageData.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.render-opportunities.getImageData-expected.html">
+<title>Canvas test: 2d.layer.render-opportunities.getImageData</title>
+<h1>2d.layer.render-opportunities.getImageData</h1>
+<p class="desc">Check that layers state stack is flushed and rebuilt on frame renders.</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);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+
+ // Force a flush and restoration of the state stack:
+ ctx.getImageData(0, 0, 200, 200);
+
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+</script>
diff --git a/html/canvas/element/layers/2d.layer.render-opportunities.putImageData-expected.html b/html/canvas/element/layers/2d.layer.render-opportunities.putImageData-expected.html
new file mode 100644
index 0000000..3d63bbe
--- /dev/null
+++ b/html/canvas/element/layers/2d.layer.render-opportunities.putImageData-expected.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.render-opportunities.putImageData</title>
+<h1>2d.layer.render-opportunities.putImageData</h1>
+<p class="desc">Check that layers state stack is flushed and rebuilt on frame renders.</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);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.endLayer();
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+</script>
diff --git a/html/canvas/element/layers/2d.layer.render-opportunities.putImageData.html b/html/canvas/element/layers/2d.layer.render-opportunities.putImageData.html
new file mode 100644
index 0000000..8da3daf
--- /dev/null
+++ b/html/canvas/element/layers/2d.layer.render-opportunities.putImageData.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.render-opportunities.putImageData-expected.html">
+<title>Canvas test: 2d.layer.render-opportunities.putImageData</title>
+<h1>2d.layer.render-opportunities.putImageData</h1>
+<p class="desc">Check that layers state stack is flushed and rebuilt on frame renders.</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);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+
+ // Force a flush and restoration of the state stack:
+ const canvas2 = new OffscreenCanvas(200, 200);
+ const ctx2 = canvas2.getContext('2d');
+ ctx.putImageData(ctx2.getImageData(0, 0, 1, 1), 0, 0);
+
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+</script>
diff --git a/html/canvas/element/layers/2d.layer.render-opportunities.requestAnimationFrame-expected.html b/html/canvas/element/layers/2d.layer.render-opportunities.requestAnimationFrame-expected.html
new file mode 100644
index 0000000..138f3a7
--- /dev/null
+++ b/html/canvas/element/layers/2d.layer.render-opportunities.requestAnimationFrame-expected.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<title>Canvas test: 2d.layer.render-opportunities.requestAnimationFrame</title>
+<h1>2d.layer.render-opportunities.requestAnimationFrame</h1>
+<p class="desc">Check that layers state stack is flushed and rebuilt on frame renders.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script type="module">
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(60, 60, 75, 50);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.endLayer();
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+ document.documentElement.classList.remove("reftest-wait");
+</script>
+</html>
\ No newline at end of file
diff --git a/html/canvas/element/layers/2d.layer.render-opportunities.requestAnimationFrame.html b/html/canvas/element/layers/2d.layer.render-opportunities.requestAnimationFrame.html
new file mode 100644
index 0000000..889ff5b
--- /dev/null
+++ b/html/canvas/element/layers/2d.layer.render-opportunities.requestAnimationFrame.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.render-opportunities.requestAnimationFrame-expected.html">
+<title>Canvas test: 2d.layer.render-opportunities.requestAnimationFrame</title>
+<h1>2d.layer.render-opportunities.requestAnimationFrame</h1>
+<p class="desc">Check that layers state stack is flushed and rebuilt on frame renders.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script type="module">
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(60, 60, 75, 50);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+
+ // Force a flush and restoration of the state stack:
+ await new Promise(resolve => requestAnimationFrame(resolve));
+
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+ document.documentElement.classList.remove("reftest-wait");
+</script>
+</html>
\ No newline at end of file
diff --git a/html/canvas/element/layers/2d.layer.render-opportunities.toBlob-expected.html b/html/canvas/element/layers/2d.layer.render-opportunities.toBlob-expected.html
new file mode 100644
index 0000000..fda8e8d
--- /dev/null
+++ b/html/canvas/element/layers/2d.layer.render-opportunities.toBlob-expected.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<title>Canvas test: 2d.layer.render-opportunities.toBlob</title>
+<h1>2d.layer.render-opportunities.toBlob</h1>
+<p class="desc">Check that layers state stack is flushed and rebuilt on frame renders.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script type="module">
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(60, 60, 75, 50);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.endLayer();
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+ document.documentElement.classList.remove("reftest-wait");
+</script>
+</html>
\ No newline at end of file
diff --git a/html/canvas/element/layers/2d.layer.render-opportunities.toBlob.html b/html/canvas/element/layers/2d.layer.render-opportunities.toBlob.html
new file mode 100644
index 0000000..9087304
--- /dev/null
+++ b/html/canvas/element/layers/2d.layer.render-opportunities.toBlob.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.render-opportunities.toBlob-expected.html">
+<title>Canvas test: 2d.layer.render-opportunities.toBlob</title>
+<h1>2d.layer.render-opportunities.toBlob</h1>
+<p class="desc">Check that layers state stack is flushed and rebuilt on frame renders.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script type="module">
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(60, 60, 75, 50);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+
+ // Force a flush and restoration of the state stack:
+ await new Promise(resolve => canvas.toBlob(resolve));
+
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+ document.documentElement.classList.remove("reftest-wait");
+</script>
+</html>
\ No newline at end of file
diff --git a/html/canvas/element/layers/2d.layer.render-opportunities.toDataURL-expected.html b/html/canvas/element/layers/2d.layer.render-opportunities.toDataURL-expected.html
new file mode 100644
index 0000000..22a9770
--- /dev/null
+++ b/html/canvas/element/layers/2d.layer.render-opportunities.toDataURL-expected.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.render-opportunities.toDataURL</title>
+<h1>2d.layer.render-opportunities.toDataURL</h1>
+<p class="desc">Check that layers state stack is flushed and rebuilt on frame renders.</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);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.endLayer();
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+</script>
diff --git a/html/canvas/element/layers/2d.layer.render-opportunities.toDataURL.html b/html/canvas/element/layers/2d.layer.render-opportunities.toDataURL.html
new file mode 100644
index 0000000..152f66a
--- /dev/null
+++ b/html/canvas/element/layers/2d.layer.render-opportunities.toDataURL.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.render-opportunities.toDataURL-expected.html">
+<title>Canvas test: 2d.layer.render-opportunities.toDataURL</title>
+<h1>2d.layer.render-opportunities.toDataURL</h1>
+<p class="desc">Check that layers state stack is flushed and rebuilt on frame renders.</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);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+
+ // Force a flush and restoration of the state stack:
+ canvas.toDataURL();
+
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+</script>
diff --git a/html/canvas/element/layers/2d.layer.unclosed-expected.html b/html/canvas/element/layers/2d.layer.unclosed-expected.html
new file mode 100644
index 0000000..c41b253
--- /dev/null
+++ b/html/canvas/element/layers/2d.layer.unclosed-expected.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.unclosed</title>
+<h1>2d.layer.unclosed</h1>
+<p class="desc">Check that layers are rendered even if not closed.</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);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.endLayer();
+</script>
diff --git a/html/canvas/element/layers/2d.layer.unclosed.html b/html/canvas/element/layers/2d.layer.unclosed.html
new file mode 100644
index 0000000..788889e
--- /dev/null
+++ b/html/canvas/element/layers/2d.layer.unclosed.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.unclosed-expected.html">
+<title>Canvas test: 2d.layer.unclosed</title>
+<h1>2d.layer.unclosed</h1>
+<p class="desc">Check that layers are rendered even if not closed.</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);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+</script>
diff --git a/html/canvas/offscreen/layers/2d.layer.render-opportunities.convertToBlob-expected.html b/html/canvas/offscreen/layers/2d.layer.render-opportunities.convertToBlob-expected.html
new file mode 100644
index 0000000..1e61775
--- /dev/null
+++ b/html/canvas/offscreen/layers/2d.layer.render-opportunities.convertToBlob-expected.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<title>Canvas test: 2d.layer.render-opportunities.convertToBlob</title>
+<h1>2d.layer.render-opportunities.convertToBlob</h1>
+<p class="desc">Check that layers state stack is flushed and rebuilt on frame renders.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script type="module">
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(60, 60, 75, 50);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.endLayer();
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+ document.documentElement.classList.remove("reftest-wait");
+</script>
+</html>
\ No newline at end of file
diff --git a/html/canvas/offscreen/layers/2d.layer.render-opportunities.convertToBlob.html b/html/canvas/offscreen/layers/2d.layer.render-opportunities.convertToBlob.html
new file mode 100644
index 0000000..c1a139a
--- /dev/null
+++ b/html/canvas/offscreen/layers/2d.layer.render-opportunities.convertToBlob.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.render-opportunities.convertToBlob-expected.html">
+<title>Canvas test: 2d.layer.render-opportunities.convertToBlob</title>
+<h1>2d.layer.render-opportunities.convertToBlob</h1>
+<p class="desc">Check that layers state stack is flushed and rebuilt on frame renders.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script type="module">
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(60, 60, 75, 50);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+
+ // Force a flush and restoration of the state stack:
+ await canvas.convertToBlob();
+
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+</script>
+</html>
\ No newline at end of file
diff --git a/html/canvas/offscreen/layers/2d.layer.render-opportunities.convertToBlob.w.html b/html/canvas/offscreen/layers/2d.layer.render-opportunities.convertToBlob.w.html
new file mode 100644
index 0000000..404fef3
--- /dev/null
+++ b/html/canvas/offscreen/layers/2d.layer.render-opportunities.convertToBlob.w.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.render-opportunities.convertToBlob-expected.html">
+<title>Canvas test: 2d.layer.render-opportunities.convertToBlob</title>
+<h1>2d.layer.render-opportunities.convertToBlob</h1>
+<p class="desc">Check that layers state stack is flushed and rebuilt on frame renders.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = async function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(60, 60, 75, 50);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+
+ // Force a flush and restoration of the state stack:
+ await canvas.convertToBlob();
+
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/html/canvas/offscreen/layers/2d.layer.render-opportunities.createImageBitmap-expected.html b/html/canvas/offscreen/layers/2d.layer.render-opportunities.createImageBitmap-expected.html
new file mode 100644
index 0000000..8ce0c51
--- /dev/null
+++ b/html/canvas/offscreen/layers/2d.layer.render-opportunities.createImageBitmap-expected.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.render-opportunities.createImageBitmap</title>
+<h1>2d.layer.render-opportunities.createImageBitmap</h1>
+<p class="desc">Check that layers state stack is flushed and rebuilt on frame renders.</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);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.endLayer();
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+</script>
diff --git a/html/canvas/offscreen/layers/2d.layer.render-opportunities.createImageBitmap.html b/html/canvas/offscreen/layers/2d.layer.render-opportunities.createImageBitmap.html
new file mode 100644
index 0000000..876b027
--- /dev/null
+++ b/html/canvas/offscreen/layers/2d.layer.render-opportunities.createImageBitmap.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.render-opportunities.createImageBitmap-expected.html">
+<title>Canvas test: 2d.layer.render-opportunities.createImageBitmap</title>
+<h1>2d.layer.render-opportunities.createImageBitmap</h1>
+<p class="desc">Check that layers state stack is flushed and rebuilt on frame renders.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(60, 60, 75, 50);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+
+ // Force a flush and restoration of the state stack:
+ createImageBitmap(canvas);
+
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/html/canvas/offscreen/layers/2d.layer.render-opportunities.createImageBitmap.w.html b/html/canvas/offscreen/layers/2d.layer.render-opportunities.createImageBitmap.w.html
new file mode 100644
index 0000000..9a89492
--- /dev/null
+++ b/html/canvas/offscreen/layers/2d.layer.render-opportunities.createImageBitmap.w.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.render-opportunities.createImageBitmap-expected.html">
+<title>Canvas test: 2d.layer.render-opportunities.createImageBitmap</title>
+<h1>2d.layer.render-opportunities.createImageBitmap</h1>
+<p class="desc">Check that layers state stack is flushed and rebuilt on frame renders.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(60, 60, 75, 50);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+
+ // Force a flush and restoration of the state stack:
+ createImageBitmap(canvas);
+
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/html/canvas/offscreen/layers/2d.layer.render-opportunities.drawImage-expected.html b/html/canvas/offscreen/layers/2d.layer.render-opportunities.drawImage-expected.html
new file mode 100644
index 0000000..1ddc6d1
--- /dev/null
+++ b/html/canvas/offscreen/layers/2d.layer.render-opportunities.drawImage-expected.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.render-opportunities.drawImage</title>
+<h1>2d.layer.render-opportunities.drawImage</h1>
+<p class="desc">Check that layers state stack is flushed and rebuilt on frame renders.</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);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.endLayer();
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+</script>
diff --git a/html/canvas/offscreen/layers/2d.layer.render-opportunities.drawImage.html b/html/canvas/offscreen/layers/2d.layer.render-opportunities.drawImage.html
new file mode 100644
index 0000000..dd17820
--- /dev/null
+++ b/html/canvas/offscreen/layers/2d.layer.render-opportunities.drawImage.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.render-opportunities.drawImage-expected.html">
+<title>Canvas test: 2d.layer.render-opportunities.drawImage</title>
+<h1>2d.layer.render-opportunities.drawImage</h1>
+<p class="desc">Check that layers state stack is flushed and rebuilt on frame renders.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(60, 60, 75, 50);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+
+ // Force a flush and restoration of the state stack:
+ const canvas2 = new OffscreenCanvas(200, 200);
+ const ctx2 = canvas2.getContext('2d');
+ ctx2.drawImage(canvas, 0, 0);
+
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/html/canvas/offscreen/layers/2d.layer.render-opportunities.drawImage.w.html b/html/canvas/offscreen/layers/2d.layer.render-opportunities.drawImage.w.html
new file mode 100644
index 0000000..80fbfbd
--- /dev/null
+++ b/html/canvas/offscreen/layers/2d.layer.render-opportunities.drawImage.w.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.render-opportunities.drawImage-expected.html">
+<title>Canvas test: 2d.layer.render-opportunities.drawImage</title>
+<h1>2d.layer.render-opportunities.drawImage</h1>
+<p class="desc">Check that layers state stack is flushed and rebuilt on frame renders.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(60, 60, 75, 50);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+
+ // Force a flush and restoration of the state stack:
+ const canvas2 = new OffscreenCanvas(200, 200);
+ const ctx2 = canvas2.getContext('2d');
+ ctx2.drawImage(canvas, 0, 0);
+
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/html/canvas/offscreen/layers/2d.layer.render-opportunities.getImageData-expected.html b/html/canvas/offscreen/layers/2d.layer.render-opportunities.getImageData-expected.html
new file mode 100644
index 0000000..ea0e78e
--- /dev/null
+++ b/html/canvas/offscreen/layers/2d.layer.render-opportunities.getImageData-expected.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.render-opportunities.getImageData</title>
+<h1>2d.layer.render-opportunities.getImageData</h1>
+<p class="desc">Check that layers state stack is flushed and rebuilt on frame renders.</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);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.endLayer();
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+</script>
diff --git a/html/canvas/offscreen/layers/2d.layer.render-opportunities.getImageData.html b/html/canvas/offscreen/layers/2d.layer.render-opportunities.getImageData.html
new file mode 100644
index 0000000..b6f3c1b
--- /dev/null
+++ b/html/canvas/offscreen/layers/2d.layer.render-opportunities.getImageData.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.render-opportunities.getImageData-expected.html">
+<title>Canvas test: 2d.layer.render-opportunities.getImageData</title>
+<h1>2d.layer.render-opportunities.getImageData</h1>
+<p class="desc">Check that layers state stack is flushed and rebuilt on frame renders.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(60, 60, 75, 50);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+
+ // Force a flush and restoration of the state stack:
+ ctx.getImageData(0, 0, 200, 200);
+
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/html/canvas/offscreen/layers/2d.layer.render-opportunities.getImageData.w.html b/html/canvas/offscreen/layers/2d.layer.render-opportunities.getImageData.w.html
new file mode 100644
index 0000000..187eb0f
--- /dev/null
+++ b/html/canvas/offscreen/layers/2d.layer.render-opportunities.getImageData.w.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.render-opportunities.getImageData-expected.html">
+<title>Canvas test: 2d.layer.render-opportunities.getImageData</title>
+<h1>2d.layer.render-opportunities.getImageData</h1>
+<p class="desc">Check that layers state stack is flushed and rebuilt on frame renders.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(60, 60, 75, 50);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+
+ // Force a flush and restoration of the state stack:
+ ctx.getImageData(0, 0, 200, 200);
+
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/html/canvas/offscreen/layers/2d.layer.render-opportunities.putImageData-expected.html b/html/canvas/offscreen/layers/2d.layer.render-opportunities.putImageData-expected.html
new file mode 100644
index 0000000..3d63bbe
--- /dev/null
+++ b/html/canvas/offscreen/layers/2d.layer.render-opportunities.putImageData-expected.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.render-opportunities.putImageData</title>
+<h1>2d.layer.render-opportunities.putImageData</h1>
+<p class="desc">Check that layers state stack is flushed and rebuilt on frame renders.</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);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.endLayer();
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+</script>
diff --git a/html/canvas/offscreen/layers/2d.layer.render-opportunities.putImageData.html b/html/canvas/offscreen/layers/2d.layer.render-opportunities.putImageData.html
new file mode 100644
index 0000000..b460015
--- /dev/null
+++ b/html/canvas/offscreen/layers/2d.layer.render-opportunities.putImageData.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.render-opportunities.putImageData-expected.html">
+<title>Canvas test: 2d.layer.render-opportunities.putImageData</title>
+<h1>2d.layer.render-opportunities.putImageData</h1>
+<p class="desc">Check that layers state stack is flushed and rebuilt on frame renders.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(60, 60, 75, 50);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+
+ // Force a flush and restoration of the state stack:
+ const canvas2 = new OffscreenCanvas(200, 200);
+ const ctx2 = canvas2.getContext('2d');
+ ctx.putImageData(ctx2.getImageData(0, 0, 1, 1), 0, 0);
+
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/html/canvas/offscreen/layers/2d.layer.render-opportunities.putImageData.w.html b/html/canvas/offscreen/layers/2d.layer.render-opportunities.putImageData.w.html
new file mode 100644
index 0000000..9ffac07
--- /dev/null
+++ b/html/canvas/offscreen/layers/2d.layer.render-opportunities.putImageData.w.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.render-opportunities.putImageData-expected.html">
+<title>Canvas test: 2d.layer.render-opportunities.putImageData</title>
+<h1>2d.layer.render-opportunities.putImageData</h1>
+<p class="desc">Check that layers state stack is flushed and rebuilt on frame renders.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(60, 60, 75, 50);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+
+ // Force a flush and restoration of the state stack:
+ const canvas2 = new OffscreenCanvas(200, 200);
+ const ctx2 = canvas2.getContext('2d');
+ ctx.putImageData(ctx2.getImageData(0, 0, 1, 1), 0, 0);
+
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/html/canvas/offscreen/layers/2d.layer.render-opportunities.transferToImageBitmap-expected.html b/html/canvas/offscreen/layers/2d.layer.render-opportunities.transferToImageBitmap-expected.html
new file mode 100644
index 0000000..2833849
--- /dev/null
+++ b/html/canvas/offscreen/layers/2d.layer.render-opportunities.transferToImageBitmap-expected.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.render-opportunities.transferToImageBitmap</title>
+<h1>2d.layer.render-opportunities.transferToImageBitmap</h1>
+<p class="desc">Checks that transferToImageBitmap flushes and rebuilds the state stack.</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.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+</script>
diff --git a/html/canvas/offscreen/layers/2d.layer.render-opportunities.transferToImageBitmap.html b/html/canvas/offscreen/layers/2d.layer.render-opportunities.transferToImageBitmap.html
new file mode 100644
index 0000000..2783064
--- /dev/null
+++ b/html/canvas/offscreen/layers/2d.layer.render-opportunities.transferToImageBitmap.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.render-opportunities.transferToImageBitmap-expected.html">
+<title>Canvas test: 2d.layer.render-opportunities.transferToImageBitmap</title>
+<h1>2d.layer.render-opportunities.transferToImageBitmap</h1>
+<p class="desc">Checks that transferToImageBitmap flushes and rebuilds the state stack.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(60, 60, 75, 50);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+
+ // Force a flush and restoration of the state stack.
+ // `transferToImageBitmap` clears the frame but preserves render states.
+ canvas.transferToImageBitmap();
+
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/html/canvas/offscreen/layers/2d.layer.render-opportunities.transferToImageBitmap.w.html b/html/canvas/offscreen/layers/2d.layer.render-opportunities.transferToImageBitmap.w.html
new file mode 100644
index 0000000..06e0160
--- /dev/null
+++ b/html/canvas/offscreen/layers/2d.layer.render-opportunities.transferToImageBitmap.w.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.render-opportunities.transferToImageBitmap-expected.html">
+<title>Canvas test: 2d.layer.render-opportunities.transferToImageBitmap</title>
+<h1>2d.layer.render-opportunities.transferToImageBitmap</h1>
+<p class="desc">Checks that transferToImageBitmap flushes and rebuilds the state stack.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(60, 60, 75, 50);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+
+ // Force a flush and restoration of the state stack.
+ // `transferToImageBitmap` clears the frame but preserves render states.
+ canvas.transferToImageBitmap();
+
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/html/canvas/offscreen/layers/2d.layer.unclosed-expected.html b/html/canvas/offscreen/layers/2d.layer.unclosed-expected.html
new file mode 100644
index 0000000..c41b253
--- /dev/null
+++ b/html/canvas/offscreen/layers/2d.layer.unclosed-expected.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.unclosed</title>
+<h1>2d.layer.unclosed</h1>
+<p class="desc">Check that layers are rendered even if not closed.</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);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.endLayer();
+</script>
diff --git a/html/canvas/offscreen/layers/2d.layer.unclosed.html b/html/canvas/offscreen/layers/2d.layer.unclosed.html
new file mode 100644
index 0000000..689ee80
--- /dev/null
+++ b/html/canvas/offscreen/layers/2d.layer.unclosed.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.unclosed-expected.html">
+<title>Canvas test: 2d.layer.unclosed</title>
+<h1>2d.layer.unclosed</h1>
+<p class="desc">Check that layers are rendered even if not closed.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(60, 60, 75, 50);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+
+ const outputCanvas = document.getElementById("canvas");
+ outputCanvas.getContext('2d').drawImage(canvas, 0, 0);
+</script>
diff --git a/html/canvas/offscreen/layers/2d.layer.unclosed.w.html b/html/canvas/offscreen/layers/2d.layer.unclosed.w.html
new file mode 100644
index 0000000..0c7812e
--- /dev/null
+++ b/html/canvas/offscreen/layers/2d.layer.unclosed.w.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<html class="reftest-wait">
+<link rel="match" href="2d.layer.unclosed-expected.html">
+<title>Canvas test: 2d.layer.unclosed</title>
+<h1>2d.layer.unclosed</h1>
+<p class="desc">Check that layers are rendered even if not closed.</p>
+<canvas id="canvas" width="200" height="200">
+ <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script id='myWorker' type='text/worker'>
+ self.onmessage = function(e) {
+ const canvas = new OffscreenCanvas(200, 200);
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(60, 60, 75, 50);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+
+ const bitmap = canvas.transferToImageBitmap();
+ self.postMessage(bitmap, bitmap);
+ };
+</script>
+<script>
+ const blob = new Blob([document.getElementById('myWorker').textContent]);
+ const worker = new Worker(URL.createObjectURL(blob));
+ worker.addEventListener('message', msg => {
+ const outputCtx = document.getElementById("canvas").getContext('2d');
+ outputCtx.drawImage(msg.data, 0, 0);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ worker.postMessage(null);
+</script>
+</html>
diff --git a/html/canvas/tools/templates-new.yaml b/html/canvas/tools/templates-new.yaml
index 581c1d7..7d8ebfc 100644
--- a/html/canvas/tools/templates-new.yaml
+++ b/html/canvas/tools/templates-new.yaml
@@ -131,6 +131,9 @@
offscreen_ref_test: |-
<!DOCTYPE html>
<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+ {% if promise_test %}\
+ <html class="reftest-wait">
+ {% endif %}\
%(links)s\
%(fuzzy)s\
%(timeout)s\
@@ -140,7 +143,7 @@
%(fonts)s%(fonthack)s%(notes)s<canvas id="canvas" width="%(width)s" height="%(height)s"%(canvas)s>
%(fallback)s
</canvas>
- <script>
+ <script{% if promise_test %} type="module"{% endif %}>
const canvas = new OffscreenCanvas(%(width)s, %(height)s);
const ctx = canvas.getContext(%(context_args)s);
@@ -148,9 +151,12 @@
const outputCanvas = document.getElementById("canvas");
outputCanvas.getContext(%(context_args)s).drawImage(canvas, 0, 0);
+ {% if promise_test %}\
+ document.documentElement.classList.remove("reftest-wait");
+ {% endif %}\
</script>
- %(images)s
-
+ %(images)s\
+ {% if promise_test %}</html>{% endif %}
worker_ref_test: |
<!DOCTYPE html>
@@ -166,7 +172,7 @@
%(fallback)s
</canvas>
<script id='myWorker' type='text/worker'>
- self.onmessage = function(e) {
+ self.onmessage = {% if promise_test %}async {% endif %}function(e) {
const canvas = new OffscreenCanvas(%(width)s, %(height)s);
const ctx = canvas.getContext('2d');
@@ -192,6 +198,9 @@
element_ref_test: |-
<!DOCTYPE html>
<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+ {% if promise_test %}\
+ <html class="reftest-wait">
+ {% endif %}\
%(links)s\
%(fuzzy)s\
%(timeout)s\
@@ -201,13 +210,17 @@
%(fonts)s%(fonthack)s%(notes)s<canvas id="canvas" width="%(width)s" height="%(height)s"%(canvas)s>
%(fallback)s
</canvas>
- <script>
+ <script{% if promise_test %} type="module"{% endif %}>
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext(%(context_args)s);
%(code)s
+ {% if promise_test %}\
+ document.documentElement.classList.remove("reftest-wait");
+ {% endif %}\
</script>
- %(images)s
+ %(images)s\
+ {% if promise_test %}</html>{% endif %}
html_ref_test: |-
diff --git a/html/canvas/tools/yaml-new/layers.yaml b/html/canvas/tools/yaml-new/layers.yaml
index d398ef2..fe1902c 100644
--- a/html/canvas/tools/yaml-new/layers.yaml
+++ b/html/canvas/tools/yaml-new/layers.yaml
@@ -247,6 +247,146 @@
ctx.fillRect(70, 70, 75, 50);
+- name: 2d.layer.unclosed
+ desc: Check that layers are rendered even if not closed.
+ size: 200, 200
+ code: |
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(60, 60, 75, 50);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+ reference: |
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(60, 60, 75, 50);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.endLayer();
+
+
+- name: 2d.layer.render-opportunities
+ desc: Check that layers state stack is flushed and rebuilt on frame renders.
+ size: 200, 200
+ code: |
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(60, 60, 75, 50);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+
+ // Force a flush and restoration of the state stack:
+ %(flush_canvas)s
+
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+ reference: |
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(60, 60, 75, 50);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+ ctx.endLayer();
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+ variants:
+ convertToBlob:
+ test_type: "promise"
+ canvasType: ['OffscreenCanvas']
+ flush_canvas: |-
+ await canvas.convertToBlob();
+ createImageBitmap:
+ flush_canvas: createImageBitmap(canvas);
+ drawImage:
+ flush_canvas: |-
+ const canvas2 = new OffscreenCanvas(200, 200);
+ const ctx2 = canvas2.getContext('2d');
+ ctx2.drawImage(canvas, 0, 0);
+ getImageData:
+ flush_canvas: ctx.getImageData(0, 0, 200, 200);
+ requestAnimationFrame:
+ canvasType: ['HTMLCanvas']
+ test_type: "promise"
+ flush_canvas: |-
+ await new Promise(resolve => requestAnimationFrame(resolve));
+ putImageData:
+ flush_canvas: |-
+ const canvas2 = new OffscreenCanvas(200, 200);
+ const ctx2 = canvas2.getContext('2d');
+ ctx.putImageData(ctx2.getImageData(0, 0, 1, 1), 0, 0);
+ toBlob:
+ test_type: "promise"
+ canvasType: ['HTMLCanvas']
+ flush_canvas: |-
+ await new Promise(resolve => canvas.toBlob(resolve));
+ toDataURL:
+ canvasType: ['HTMLCanvas']
+ flush_canvas: canvas.toDataURL();
+
+
+- name: 2d.layer.render-opportunities.transferToImageBitmap
+ desc: Checks that transferToImageBitmap flushes and rebuilds the state stack.
+ size: 200, 200
+ canvasType: ['OffscreenCanvas']
+ code: |
+ ctx.fillStyle = 'purple';
+ ctx.fillRect(60, 60, 75, 50);
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillRect(40, 40, 75, 50);
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(50, 50, 75, 50);
+
+ // Force a flush and restoration of the state stack.
+ // `transferToImageBitmap` clears the frame but preserves render states.
+ canvas.transferToImageBitmap();
+
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+ reference: |
+ ctx.fillStyle = 'purple';
+ ctx.globalAlpha = 0.5;
+
+ ctx.beginLayer({filter: 'dropShadow', dx: -2, dy: 2});
+ ctx.fillStyle = 'grey';
+ ctx.fillRect(70, 70, 75, 50);
+ ctx.fillStyle = 'orange';
+ ctx.fillRect(80, 80, 75, 50);
+ ctx.endLayer();
+
+ ctx.fillRect(80, 40, 75, 50);
+
+
- name: 2d.layer.several-complex
desc: >-
Test to ensure beginlayer works for filter, alpha and shadow, even with