Reland "Generate canvas layer global state tests with new test variants feature."

This is a reland of commit 446ce8aea96e6d4696d9702c1ac86861c9c8cdf6,
with pixel fuzzy tolerance updated to account for the mac11-arm64-rel
builder.

Original change's description:
> Generate canvas layer global state tests with new test variants feature.
>
> All of these tests were very similar with the only important different
> being the global state applied in the setup. Adding a "variant" feature
> to the test generation script allows for cutting a lot of this
> repetitive code.
>
> Change-Id: Ic3ac63e80ff9c5290ae5f98a29c7d854951e9ba7
> Bug: 1409873
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4209308
> Reviewed-by: Aaron Krajeski <aaronhk@chromium.org>
> Commit-Queue: Jean-Philippe Gravel <jpgravel@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#1105685}

Bug: 1409873
Change-Id: I21eeeb2b1581097d37eaaa3fd41da159ffb02e0f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4255729
Reviewed-by: Aaron Krajeski <aaronhk@chromium.org>
Commit-Queue: Jean-Philippe Gravel <jpgravel@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1106242}
diff --git a/html/canvas/element/layers/2d.layer.alpha.html b/html/canvas/element/layers/2d.layer.alpha.html
deleted file mode 100644
index b4a87b4..0000000
--- a/html/canvas/element/layers/2d.layer.alpha.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!DOCTYPE html>
-<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
-<link rel="match" href="2d.layer.alpha-expected.html">
-<title>Canvas test: 2d.layer.alpha</title>
-<h1>2d.layer.alpha</h1>
-<p class="desc">A test to make sure shadow works with layers.</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(50, 50, 95, 70);
-
-  ctx.globalAlpha = 0.6;
-
-  ctx.beginLayer();
-
-  ctx.fillStyle = 'rgba(225, 0, 0, 1)';
-  ctx.fillRect(60, 40, 75, 50);
-  ctx.fillStyle = 'rgba(0, 255, 0, 1)';
-  ctx.fillRect(80, 60, 75, 50);
-
-  ctx.endLayer();
-</script>
diff --git a/html/canvas/element/layers/2d.layer.alpha-expected.html b/html/canvas/element/layers/2d.layer.global-states.alpha-expected.html
similarity index 64%
rename from html/canvas/element/layers/2d.layer.alpha-expected.html
rename to html/canvas/element/layers/2d.layer.global-states.alpha-expected.html
index 615aa1c..72d97c6 100644
--- a/html/canvas/element/layers/2d.layer.alpha-expected.html
+++ b/html/canvas/element/layers/2d.layer.global-states.alpha-expected.html
@@ -1,8 +1,8 @@
 <!DOCTYPE html>
 <!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
-<title>Canvas test: 2d.layer.alpha</title>
-<h1>2d.layer.alpha</h1>
-<p class="desc">A test to make sure shadow works with layers.</p>
+<title>Canvas test: 2d.layer.global-states.alpha</title>
+<h1>2d.layer.global-states.alpha</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
 <canvas id="canvas" width="200" height="200">
   <p class="fallback">FAIL (fallback content)</p>
 </canvas>
@@ -11,7 +11,10 @@
   const ctx = canvas.getContext('2d');
 
   ctx.fillStyle = 'rgba(0, 0, 255, 1)';
-  ctx.fillRect(50, 50, 95, 70);
+
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
 
   ctx.globalAlpha = 0.6;
 
@@ -19,9 +22,9 @@
   ctx2 = canvas2.getContext("2d");
 
   ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
-  ctx2.fillRect(60, 40, 75, 50);
+  ctx2.fillRect(50, 50, 75, 50);
   ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
-  ctx2.fillRect(80, 60, 75, 50);
+  ctx2.fillRect(70, 70, 75, 50);
 
   ctx.drawImage(canvas2, 0, 0);
 </script>
diff --git a/html/canvas/element/layers/2d.layer.global-states.alpha.filter-expected.html b/html/canvas/element/layers/2d.layer.global-states.alpha.filter-expected.html
new file mode 100644
index 0000000..b4512e2
--- /dev/null
+++ b/html/canvas/element/layers/2d.layer.global-states.alpha.filter-expected.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.alpha.filter</title>
+<h1>2d.layer.global-states.alpha.filter</h1>
+<p class="desc">Checks that layers correctly use global render states.</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)';
+
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
+
+  ctx.globalAlpha = 0.6;
+  ctx.filter = 'drop-shadow(-9px 9px 0px #e81)';
+
+  canvas2 = document.createElement("canvas");
+  ctx2 = canvas2.getContext("2d");
+
+  ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+  ctx2.fillRect(50, 50, 75, 50);
+  ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+  ctx2.fillRect(70, 70, 75, 50);
+
+  ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/html/canvas/element/layers/2d.layer.global-states.alpha.filter.globalcompositeoperation-expected.html b/html/canvas/element/layers/2d.layer.global-states.alpha.filter.globalcompositeoperation-expected.html
new file mode 100644
index 0000000..ab7e6cc
--- /dev/null
+++ b/html/canvas/element/layers/2d.layer.global-states.alpha.filter.globalcompositeoperation-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.global-states.alpha.filter.globalcompositeoperation</title>
+<h1>2d.layer.global-states.alpha.filter.globalcompositeoperation</h1>
+<p class="desc">Checks that layers correctly use global render states.</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)';
+
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
+
+  ctx.globalAlpha = 0.6;
+  ctx.globalCompositeOperation = 'source-in';
+  ctx.filter = 'drop-shadow(-9px 9px 0px #e81)';
+
+  canvas2 = document.createElement("canvas");
+  ctx2 = canvas2.getContext("2d");
+
+  ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+  ctx2.fillRect(50, 50, 75, 50);
+  ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+  ctx2.fillRect(70, 70, 75, 50);
+
+  ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/html/canvas/element/layers/2d.layer.global-states.alpha.filter.globalcompositeoperation.html b/html/canvas/element/layers/2d.layer.global-states.alpha.filter.globalcompositeoperation.html
new file mode 100644
index 0000000..54bafb9
--- /dev/null
+++ b/html/canvas/element/layers/2d.layer.global-states.alpha.filter.globalcompositeoperation.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.global-states.alpha.filter.globalcompositeoperation-expected.html">
+<title>Canvas test: 2d.layer.global-states.alpha.filter.globalcompositeoperation</title>
+<h1>2d.layer.global-states.alpha.filter.globalcompositeoperation</h1>
+<p class="desc">Checks that layers correctly use global render states.</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)';
+
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
+
+  ctx.globalAlpha = 0.6;
+  ctx.globalCompositeOperation = 'source-in';
+  ctx.filter = 'drop-shadow(-9px 9px 0px #e81)';
+
+  ctx.beginLayer();
+
+  ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+  ctx.fillRect(50, 50, 75, 50);
+  ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+  ctx.fillRect(70, 70, 75, 50);
+
+  ctx.endLayer();
+</script>
diff --git a/html/canvas/element/layers/2d.layer.global-states.alpha.filter.html b/html/canvas/element/layers/2d.layer.global-states.alpha.filter.html
new file mode 100644
index 0000000..bf522c2
--- /dev/null
+++ b/html/canvas/element/layers/2d.layer.global-states.alpha.filter.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.alpha.filter-expected.html">
+<title>Canvas test: 2d.layer.global-states.alpha.filter</title>
+<h1>2d.layer.global-states.alpha.filter</h1>
+<p class="desc">Checks that layers correctly use global render states.</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)';
+
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
+
+  ctx.globalAlpha = 0.6;
+  ctx.filter = 'drop-shadow(-9px 9px 0px #e81)';
+
+  ctx.beginLayer();
+
+  ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+  ctx.fillRect(50, 50, 75, 50);
+  ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+  ctx.fillRect(70, 70, 75, 50);
+
+  ctx.endLayer();
+</script>
diff --git a/html/canvas/element/layers/2d.layer.global-states.alpha.filter.shadow-expected.html b/html/canvas/element/layers/2d.layer.global-states.alpha.filter.shadow-expected.html
new file mode 100644
index 0000000..9a49e36
--- /dev/null
+++ b/html/canvas/element/layers/2d.layer.global-states.alpha.filter.shadow-expected.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.alpha.filter.shadow</title>
+<h1>2d.layer.global-states.alpha.filter.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</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)';
+
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
+
+  ctx.globalAlpha = 0.5;
+  ctx.filter = 'sepia(0.5)';
+  ctx.shadowOffsetX = -10;
+  ctx.shadowOffsetY = 10;
+  ctx.shadowColor = 'orange';
+
+  canvas2 = document.createElement("canvas");
+  ctx2 = canvas2.getContext("2d");
+
+  ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+  ctx2.fillRect(50, 50, 75, 50);
+  ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+  ctx2.fillRect(70, 70, 75, 50);
+
+  ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/html/canvas/element/layers/2d.layer.global-states.alpha.filter.shadow.html b/html/canvas/element/layers/2d.layer.global-states.alpha.filter.shadow.html
new file mode 100644
index 0000000..cccdf42
--- /dev/null
+++ b/html/canvas/element/layers/2d.layer.global-states.alpha.filter.shadow.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.global-states.alpha.filter.shadow-expected.html">
+<meta name=fuzzy content="maxDifference=0-2; totalPixels=0-5824">
+<title>Canvas test: 2d.layer.global-states.alpha.filter.shadow</title>
+<h1>2d.layer.global-states.alpha.filter.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</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)';
+
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
+
+  ctx.globalAlpha = 0.5;
+  ctx.filter = 'sepia(0.5)';
+  ctx.shadowOffsetX = -10;
+  ctx.shadowOffsetY = 10;
+  ctx.shadowColor = 'orange';
+
+  ctx.beginLayer();
+
+  ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+  ctx.fillRect(50, 50, 75, 50);
+  ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+  ctx.fillRect(70, 70, 75, 50);
+
+  ctx.endLayer();
+</script>
diff --git a/html/canvas/element/layers/2d.layer.global-states.alpha.html b/html/canvas/element/layers/2d.layer.global-states.alpha.html
new file mode 100644
index 0000000..10ef0e7
--- /dev/null
+++ b/html/canvas/element/layers/2d.layer.global-states.alpha.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.alpha-expected.html">
+<title>Canvas test: 2d.layer.global-states.alpha</title>
+<h1>2d.layer.global-states.alpha</h1>
+<p class="desc">Checks that layers correctly use global render states.</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)';
+
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
+
+  ctx.globalAlpha = 0.6;
+
+  ctx.beginLayer();
+
+  ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+  ctx.fillRect(50, 50, 75, 50);
+  ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+  ctx.fillRect(70, 70, 75, 50);
+
+  ctx.endLayer();
+</script>
diff --git a/html/canvas/element/layers/2d.layer.global-states.alpha.shadow-expected.html b/html/canvas/element/layers/2d.layer.global-states.alpha.shadow-expected.html
new file mode 100644
index 0000000..9daefc3
--- /dev/null
+++ b/html/canvas/element/layers/2d.layer.global-states.alpha.shadow-expected.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.alpha.shadow</title>
+<h1>2d.layer.global-states.alpha.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</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)';
+
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
+
+  ctx.globalAlpha = 0.5;
+  ctx.shadowOffsetX = -10;
+  ctx.shadowOffsetY = 10;
+  ctx.shadowColor = 'orange';
+
+  canvas2 = document.createElement("canvas");
+  ctx2 = canvas2.getContext("2d");
+
+  ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+  ctx2.fillRect(50, 50, 75, 50);
+  ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+  ctx2.fillRect(70, 70, 75, 50);
+
+  ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/html/canvas/element/layers/2d.layer.global-states.alpha.shadow.html b/html/canvas/element/layers/2d.layer.global-states.alpha.shadow.html
new file mode 100644
index 0000000..a0f4d79
--- /dev/null
+++ b/html/canvas/element/layers/2d.layer.global-states.alpha.shadow.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.alpha.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.alpha.shadow</title>
+<h1>2d.layer.global-states.alpha.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</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)';
+
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
+
+  ctx.globalAlpha = 0.5;
+  ctx.shadowOffsetX = -10;
+  ctx.shadowOffsetY = 10;
+  ctx.shadowColor = 'orange';
+
+  ctx.beginLayer();
+
+  ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+  ctx.fillRect(50, 50, 75, 50);
+  ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+  ctx.fillRect(70, 70, 75, 50);
+
+  ctx.endLayer();
+</script>
diff --git a/html/canvas/element/layers/2d.layer.alpha-expected.html b/html/canvas/element/layers/2d.layer.global-states.filter-expected.html
similarity index 60%
copy from html/canvas/element/layers/2d.layer.alpha-expected.html
copy to html/canvas/element/layers/2d.layer.global-states.filter-expected.html
index 615aa1c..cff8783 100644
--- a/html/canvas/element/layers/2d.layer.alpha-expected.html
+++ b/html/canvas/element/layers/2d.layer.global-states.filter-expected.html
@@ -1,8 +1,8 @@
 <!DOCTYPE html>
 <!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
-<title>Canvas test: 2d.layer.alpha</title>
-<h1>2d.layer.alpha</h1>
-<p class="desc">A test to make sure shadow works with layers.</p>
+<title>Canvas test: 2d.layer.global-states.filter</title>
+<h1>2d.layer.global-states.filter</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
 <canvas id="canvas" width="200" height="200">
   <p class="fallback">FAIL (fallback content)</p>
 </canvas>
@@ -11,17 +11,20 @@
   const ctx = canvas.getContext('2d');
 
   ctx.fillStyle = 'rgba(0, 0, 255, 1)';
-  ctx.fillRect(50, 50, 95, 70);
 
-  ctx.globalAlpha = 0.6;
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
+
+  ctx.filter = 'sepia(1) opacity(30%)';
 
   canvas2 = document.createElement("canvas");
   ctx2 = canvas2.getContext("2d");
 
   ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
-  ctx2.fillRect(60, 40, 75, 50);
+  ctx2.fillRect(50, 50, 75, 50);
   ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
-  ctx2.fillRect(80, 60, 75, 50);
+  ctx2.fillRect(70, 70, 75, 50);
 
   ctx.drawImage(canvas2, 0, 0);
 </script>
diff --git a/html/canvas/element/layers/2d.layer.global-states.filter.globalcompositeoperation-expected.html b/html/canvas/element/layers/2d.layer.global-states.filter.globalcompositeoperation-expected.html
new file mode 100644
index 0000000..12fc312
--- /dev/null
+++ b/html/canvas/element/layers/2d.layer.global-states.filter.globalcompositeoperation-expected.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.filter.globalcompositeoperation</title>
+<h1>2d.layer.global-states.filter.globalcompositeoperation</h1>
+<p class="desc">Checks that layers correctly use global render states.</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)';
+
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
+
+  ctx.globalCompositeOperation = 'source-in';
+  ctx.filter = 'drop-shadow(-9px 9px 0px #e81)';
+
+  canvas2 = document.createElement("canvas");
+  ctx2 = canvas2.getContext("2d");
+
+  ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+  ctx2.fillRect(50, 50, 75, 50);
+  ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+  ctx2.fillRect(70, 70, 75, 50);
+
+  ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/html/canvas/element/layers/2d.layer.global-states.filter.globalcompositeoperation.html b/html/canvas/element/layers/2d.layer.global-states.filter.globalcompositeoperation.html
new file mode 100644
index 0000000..1f23dba
--- /dev/null
+++ b/html/canvas/element/layers/2d.layer.global-states.filter.globalcompositeoperation.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.filter.globalcompositeoperation-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter.globalcompositeoperation</title>
+<h1>2d.layer.global-states.filter.globalcompositeoperation</h1>
+<p class="desc">Checks that layers correctly use global render states.</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)';
+
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
+
+  ctx.globalCompositeOperation = 'source-in';
+  ctx.filter = 'drop-shadow(-9px 9px 0px #e81)';
+
+  ctx.beginLayer();
+
+  ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+  ctx.fillRect(50, 50, 75, 50);
+  ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+  ctx.fillRect(70, 70, 75, 50);
+
+  ctx.endLayer();
+</script>
diff --git a/html/canvas/element/layers/2d.layer.global-states.filter.html b/html/canvas/element/layers/2d.layer.global-states.filter.html
new file mode 100644
index 0000000..7524ef5
--- /dev/null
+++ b/html/canvas/element/layers/2d.layer.global-states.filter.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.filter-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter</title>
+<h1>2d.layer.global-states.filter</h1>
+<p class="desc">Checks that layers correctly use global render states.</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)';
+
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
+
+  ctx.filter = 'sepia(1) opacity(30%)';
+
+  ctx.beginLayer();
+
+  ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+  ctx.fillRect(50, 50, 75, 50);
+  ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+  ctx.fillRect(70, 70, 75, 50);
+
+  ctx.endLayer();
+</script>
diff --git a/html/canvas/element/layers/2d.layer.global-states.filter.shadow-expected.html b/html/canvas/element/layers/2d.layer.global-states.filter.shadow-expected.html
new file mode 100644
index 0000000..6ec9b02
--- /dev/null
+++ b/html/canvas/element/layers/2d.layer.global-states.filter.shadow-expected.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.filter.shadow</title>
+<h1>2d.layer.global-states.filter.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</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)';
+
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
+
+  ctx.filter = 'sepia(1) opacity(70%)';
+  ctx.shadowOffsetX = -10;
+  ctx.shadowOffsetY = 10;
+  ctx.shadowColor = 'rgba(0,1,0,0.5)';
+
+  canvas2 = document.createElement("canvas");
+  ctx2 = canvas2.getContext("2d");
+
+  ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+  ctx2.fillRect(50, 50, 75, 50);
+  ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+  ctx2.fillRect(70, 70, 75, 50);
+
+  ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/html/canvas/element/layers/2d.layer.global-states.filter.shadow.html b/html/canvas/element/layers/2d.layer.global-states.filter.shadow.html
new file mode 100644
index 0000000..efa5dca
--- /dev/null
+++ b/html/canvas/element/layers/2d.layer.global-states.filter.shadow.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.global-states.filter.shadow-expected.html">
+<meta name=fuzzy content="maxDifference=0-1; totalPixels=0-49">
+<title>Canvas test: 2d.layer.global-states.filter.shadow</title>
+<h1>2d.layer.global-states.filter.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</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)';
+
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
+
+  ctx.filter = 'sepia(1) opacity(70%)';
+  ctx.shadowOffsetX = -10;
+  ctx.shadowOffsetY = 10;
+  ctx.shadowColor = 'rgba(0,1,0,0.5)';
+
+  ctx.beginLayer();
+
+  ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+  ctx.fillRect(50, 50, 75, 50);
+  ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+  ctx.fillRect(70, 70, 75, 50);
+
+  ctx.endLayer();
+</script>
diff --git a/html/canvas/element/layers/2d.layer.global-states.globalcompositeoperation-expected.html b/html/canvas/element/layers/2d.layer.global-states.globalcompositeoperation-expected.html
new file mode 100644
index 0000000..7e2e021
--- /dev/null
+++ b/html/canvas/element/layers/2d.layer.global-states.globalcompositeoperation-expected.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.globalcompositeoperation</title>
+<h1>2d.layer.global-states.globalcompositeoperation</h1>
+<p class="desc">Checks that layers correctly use global render states.</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)';
+
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
+
+  ctx.globalCompositeOperation = 'source-in';
+
+  canvas2 = document.createElement("canvas");
+  ctx2 = canvas2.getContext("2d");
+
+  ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+  ctx2.fillRect(50, 50, 75, 50);
+  ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+  ctx2.fillRect(70, 70, 75, 50);
+
+  ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/html/canvas/element/layers/2d.layer.global-states.globalcompositeoperation.html b/html/canvas/element/layers/2d.layer.global-states.globalcompositeoperation.html
new file mode 100644
index 0000000..f27336a
--- /dev/null
+++ b/html/canvas/element/layers/2d.layer.global-states.globalcompositeoperation.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.globalcompositeoperation-expected.html">
+<title>Canvas test: 2d.layer.global-states.globalcompositeoperation</title>
+<h1>2d.layer.global-states.globalcompositeoperation</h1>
+<p class="desc">Checks that layers correctly use global render states.</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)';
+
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
+
+  ctx.globalCompositeOperation = 'source-in';
+
+  ctx.beginLayer();
+
+  ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+  ctx.fillRect(50, 50, 75, 50);
+  ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+  ctx.fillRect(70, 70, 75, 50);
+
+  ctx.endLayer();
+</script>
diff --git a/html/canvas/element/layers/2d.layer.global-states.shadow-expected.html b/html/canvas/element/layers/2d.layer.global-states.shadow-expected.html
new file mode 100644
index 0000000..6787bdd
--- /dev/null
+++ b/html/canvas/element/layers/2d.layer.global-states.shadow-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.global-states.shadow</title>
+<h1>2d.layer.global-states.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</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)';
+
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
+
+  ctx.shadowOffsetX = -10;
+  ctx.shadowOffsetY = 10;
+  ctx.shadowColor = 'orange';
+
+  canvas2 = document.createElement("canvas");
+  ctx2 = canvas2.getContext("2d");
+
+  ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+  ctx2.fillRect(50, 50, 75, 50);
+  ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+  ctx2.fillRect(70, 70, 75, 50);
+
+  ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/html/canvas/element/layers/2d.layer.global-states.shadow.html b/html/canvas/element/layers/2d.layer.global-states.shadow.html
new file mode 100644
index 0000000..da78b34
--- /dev/null
+++ b/html/canvas/element/layers/2d.layer.global-states.shadow.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.global-states.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.shadow</title>
+<h1>2d.layer.global-states.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</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)';
+
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
+
+  ctx.shadowOffsetX = -10;
+  ctx.shadowOffsetY = 10;
+  ctx.shadowColor = 'orange';
+
+  ctx.beginLayer();
+
+  ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+  ctx.fillRect(50, 50, 75, 50);
+  ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+  ctx.fillRect(70, 70, 75, 50);
+
+  ctx.endLayer();
+</script>
diff --git a/html/canvas/element/manual/layers/layers-alpha-filter-expected.html b/html/canvas/element/manual/layers/layers-alpha-filter-expected.html
deleted file mode 100644
index 5b54384..0000000
--- a/html/canvas/element/manual/layers/layers-alpha-filter-expected.html
+++ /dev/null
@@ -1,24 +0,0 @@
-<p class="desc"> Test to ensure beginlayer works when both alpha and filter are applied. </p>
-<script>
-    var canvas, ctx;
-    canvas = document.createElement("canvas");
-    canvas.width = 200;
-    canvas.height = 200;
-    document.body.appendChild(canvas);
-
-    ctx = canvas.getContext("2d");
-    ctx.fillStyle = 'rgba(0,0,255,1)';
-    ctx.fillRect(60,60,75,50);
-    ctx.filter = 'drop-shadow(-9px 9px 0px #e81)';
-    ctx.globalAlpha = 0.6;
-
-    canvas2 = document.createElement("canvas");
-    ctx2 = canvas2.getContext("2d");
-
-    ctx2.fillStyle = 'rgba(225,0,0,1)';
-    ctx2.fillRect(50,50,75,50);
-    ctx2.fillStyle = 'rgba(0,255,0,1)';
-    ctx2.fillRect(70,70,75,50);
-
-    ctx.drawImage(canvas2,0,0);
-</script>
diff --git a/html/canvas/element/manual/layers/layers-alpha-filter-globalcompositeoperation-expected.html b/html/canvas/element/manual/layers/layers-alpha-filter-globalcompositeoperation-expected.html
deleted file mode 100644
index 8d84dcb..0000000
--- a/html/canvas/element/manual/layers/layers-alpha-filter-globalcompositeoperation-expected.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<p class="desc"> Test to ensure beginlayer works with global alpha, filter, and global composite operation. </p>
-<script>
-    var canvas, ctx;
-    canvas = document.createElement("canvas");
-    canvas.width = 200;
-    canvas.height = 200;
-    document.body.appendChild(canvas);
-
-    ctx = canvas.getContext("2d");
-    ctx.fillStyle = 'rgba(0,0,255,1)';
-    var circle = new Path2D();
-    circle.arc(90, 90, 40, 0, 2 * Math.PI);
-    ctx.fill(circle);
-    ctx.filter = 'drop-shadow(-9px 9px 0px #e81)';
-    ctx.globalAlpha = 0.6;
-    ctx.globalCompositeOperation = 'source-in';
-
-    canvas2 = document.createElement("canvas");
-    ctx2 = canvas2.getContext("2d");
-
-    ctx2.fillStyle = 'rgba(225,0,0,1)';
-    ctx2.fillRect(50,50,75,50);
-    ctx2.fillStyle = 'rgba(0,255,0,1)';
-    ctx2.fillRect(70,70,75,50);
-
-    ctx.drawImage(canvas2,0,0);
-
-</script>
diff --git a/html/canvas/element/manual/layers/layers-alpha-filter-globalcompositeoperation.html b/html/canvas/element/manual/layers/layers-alpha-filter-globalcompositeoperation.html
deleted file mode 100644
index 6912d6a..0000000
--- a/html/canvas/element/manual/layers/layers-alpha-filter-globalcompositeoperation.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<link rel="match" href="layers-alpha-filter-globalcompositeoperation-expected.html">
-<p class="desc"> Test to ensure beginlayer works with global alpha, filter, and global composite operation. </p>
-<script>
-    var canvas, ctx;
-    canvas = document.createElement("canvas");
-    canvas.width = 200;
-    canvas.height = 200;
-    document.body.appendChild(canvas);
-
-    ctx = canvas.getContext("2d");
-    ctx.fillStyle = 'rgba(0,0,255,1)';
-    var circle = new Path2D();
-    circle.arc(90, 90, 40, 0, 2 * Math.PI);
-    ctx.fill(circle);
-    ctx.filter = 'drop-shadow(-9px 9px 0px #e81)';
-    ctx.globalAlpha = 0.6;
-
-    ctx.globalCompositeOperation = 'source-in';
-
-    ctx.beginLayer();
-
-    ctx.fillStyle = 'rgba(225,0,0,1)';
-    ctx.fillRect(50,50,75,50);
-    ctx.fillStyle = 'rgba(0,255,0,1)';
-    ctx.fillRect(70,70,75,50);
-
-    ctx.endLayer();
-</script>
diff --git a/html/canvas/element/manual/layers/layers-alpha-filter-shadow-expected.html b/html/canvas/element/manual/layers/layers-alpha-filter-shadow-expected.html
deleted file mode 100644
index 89c3f64..0000000
--- a/html/canvas/element/manual/layers/layers-alpha-filter-shadow-expected.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<p class="desc"> Test to ensure beginlayer works for filter, alpha and shadow. </p>
-<script>
-var canvas, ctx;
-canvas = document.createElement("canvas");
-canvas.width = 200;
-canvas.height = 200;
-document.body.appendChild(canvas);
-
-ctx = canvas.getContext("2d");
-ctx.fillStyle = 'rgba(0,0,255,1)';
-ctx.fillRect(50,50,95,70);
-
-ctx.globalAlpha = 0.5;
-ctx.filter = 'sepia(0.5)';
-ctx.shadowOffsetX = -10;
-ctx.shadowOffsetY = 10;
-ctx.shadowColor = 'orange';
-
-canvas2 = document.createElement("canvas");
-ctx2 = canvas2.getContext("2d");
-ctx2.fillStyle = 'rgba(225,0,0,1)';
-ctx2.fillRect(60,40,75,50);
-ctx2.fillStyle = 'rgba(0,255,0,1)';
-ctx2.fillRect(80,60,75,50);
-
-ctx.drawImage(canvas2,0,0);
-</script>
diff --git a/html/canvas/element/manual/layers/layers-alpha-filter-shadow.html b/html/canvas/element/manual/layers/layers-alpha-filter-shadow.html
deleted file mode 100644
index 9c4a6fc..0000000
--- a/html/canvas/element/manual/layers/layers-alpha-filter-shadow.html
+++ /dev/null
@@ -1,30 +0,0 @@
-<link rel="match" href="layers-alpha-filter-shadow-expected.html">
-<meta name=fuzzy content="maxDifference=0-2; totalPixels=0-18000">
-<p class="desc"> Test to ensure beginlayer works for filter, alpha and shadow. </p>
-<script>
-var canvas, ctx;
-canvas = document.createElement("canvas");
-canvas.width = 200;
-canvas.height = 200;
-document.body.appendChild(canvas);
-
-ctx = canvas.getContext("2d");
-ctx.fillStyle = 'rgba(0,0,255,1)';
-ctx.fillRect(50,50,95,70);
-
-ctx.globalAlpha = 0.5;
-ctx.filter = 'sepia(0.5)';
-ctx.shadowOffsetX = -10;
-ctx.shadowOffsetY = 10;
-ctx.shadowColor = 'orange';
-
-ctx.beginLayer();
-
-ctx.fillStyle = 'rgba(225,0,0,1)';
-ctx.fillRect(60,40,75,50);
-ctx.fillStyle = 'rgba(0,255,0,1)';
-ctx.fillRect(80,60,75,50);
-
-ctx.endLayer();
-
-</script>
diff --git a/html/canvas/element/manual/layers/layers-alpha-filter.html b/html/canvas/element/manual/layers/layers-alpha-filter.html
deleted file mode 100644
index 7a895cf..0000000
--- a/html/canvas/element/manual/layers/layers-alpha-filter.html
+++ /dev/null
@@ -1,24 +0,0 @@
-<link rel="match" href="layers-alpha-filter-expected.html">
-<p class="desc"> Test to ensure beginlayer works when both alpha and filter are applied. </p>
-<script>
-    var canvas, ctx;
-    canvas = document.createElement("canvas");
-    canvas.width = 200;
-    canvas.height = 200;
-    document.body.appendChild(canvas);
-
-    ctx = canvas.getContext("2d");
-    ctx.fillStyle = 'rgba(0,0,255,1)';
-    ctx.fillRect(60,60,75,50);
-    ctx.filter = 'drop-shadow(-9px 9px 0px #e81)';
-    ctx.globalAlpha = 0.6;
-
-    ctx.beginLayer();
-
-    ctx.fillStyle = 'rgba(225,0,0,1)';
-    ctx.fillRect(50,50,75,50);
-    ctx.fillStyle = 'rgba(0,255,0,1)';
-    ctx.fillRect(70,70,75,50);
-
-    ctx.endLayer();
-</script>
diff --git a/html/canvas/element/manual/layers/layers-alpha-shadow-expected.html b/html/canvas/element/manual/layers/layers-alpha-shadow-expected.html
deleted file mode 100644
index f29fb73..0000000
--- a/html/canvas/element/manual/layers/layers-alpha-shadow-expected.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<p class="desc"> Test to ensure beginlayer works for alpha and shadow. </p>
-<script>
-var canvas, ctx;
-canvas = document.createElement("canvas");
-canvas.width = 200;
-canvas.height = 200;
-document.body.appendChild(canvas);
-
-ctx = canvas.getContext("2d");
-ctx.fillStyle = 'rgba(0,0,255,1)';
-ctx.fillRect(50,50,95,70);
-
-ctx.globalAlpha = 0.5;
-ctx.shadowOffsetX = -10;
-ctx.shadowOffsetY = 10;
-ctx.shadowColor = 'orange';
-
-canvas2 = document.createElement("canvas");
-ctx2 = canvas2.getContext("2d");
-ctx2.fillStyle = 'rgba(225,0,0,1)';
-ctx2.fillRect(60,40,75,50);
-ctx2.fillStyle = 'rgba(0,255,0,1)';
-ctx2.fillRect(80,60,75,50);
-
-ctx.drawImage(canvas2,0,0);
-</script>
diff --git a/html/canvas/element/manual/layers/layers-alpha-shadow.html b/html/canvas/element/manual/layers/layers-alpha-shadow.html
deleted file mode 100644
index e7d791d..0000000
--- a/html/canvas/element/manual/layers/layers-alpha-shadow.html
+++ /dev/null
@@ -1,29 +0,0 @@
-<link rel="match" href="layers-alpha-shadow-expected.html">
-<meta name=fuzzy content="maxDifference=0-2; totalPixels=0-18000">
-<p class="desc"> Test to ensure beginlayer works for alpha and shadow. </p>
-<script>
-var canvas, ctx;
-canvas = document.createElement("canvas");
-canvas.width = 200;
-canvas.height = 200;
-document.body.appendChild(canvas);
-
-ctx = canvas.getContext("2d");
-ctx.fillStyle = 'rgba(0,0,255,1)';
-ctx.fillRect(50,50,95,70);
-
-ctx.globalAlpha = 0.5;
-ctx.shadowOffsetX = -10;
-ctx.shadowOffsetY = 10;
-ctx.shadowColor = 'orange';
-
-ctx.beginLayer();
-
-ctx.fillStyle = 'rgba(225,0,0,1)';
-ctx.fillRect(60,40,75,50);
-ctx.fillStyle = 'rgba(0,255,0,1)';
-ctx.fillRect(80,60,75,50);
-
-ctx.endLayer();
-
-</script>
diff --git a/html/canvas/element/manual/layers/layers-filter-expected.html b/html/canvas/element/manual/layers/layers-filter-expected.html
deleted file mode 100644
index 7fe4d2c..0000000
--- a/html/canvas/element/manual/layers/layers-filter-expected.html
+++ /dev/null
@@ -1,23 +0,0 @@
-<p class="desc"> Test to ensure beginlayer works for filter. </p>
-<script>
-    var canvas, ctx;
-    canvas = document.createElement("canvas");
-    canvas.width = 200;
-    canvas.height = 200;
-    document.body.appendChild(canvas);
-
-    ctx = canvas.getContext("2d");
-    ctx.fillStyle = 'rgba(0,0,255,1)';
-    ctx.fillRect(60,60,75,50);
-    ctx.filter = 'sepia(1) opacity(30%)';
-
-    canvas2 = document.createElement("canvas");
-    ctx2 = canvas2.getContext("2d");
-
-    ctx2.fillStyle = 'rgba(225,0,0,1)';
-    ctx2.fillRect(50,50,75,50);
-    ctx2.fillStyle = 'rgba(0,255,0,1)';
-    ctx2.fillRect(70,70,75,50);
-
-    ctx.drawImage(canvas2,0,0);
-</script>
diff --git a/html/canvas/element/manual/layers/layers-filter-globalcompositeoperation-expected.html b/html/canvas/element/manual/layers/layers-filter-globalcompositeoperation-expected.html
deleted file mode 100644
index 0676d7e..0000000
--- a/html/canvas/element/manual/layers/layers-filter-globalcompositeoperation-expected.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<p class="desc"> Test to ensure beginlayer works for filter and global composition. </p>
-<script>
-    var canvas, ctx;
-    canvas = document.createElement("canvas");
-    canvas.width = 200;
-    canvas.height = 200;
-    document.body.appendChild(canvas);
-
-    ctx = canvas.getContext("2d");
-    ctx.fillStyle = 'rgba(0,0,255,1)';
-
-    var circle = new Path2D();
-    circle.arc(90, 90, 40, 0, 2 * Math.PI);
-    ctx.fill(circle);
-
-    ctx.globalCompositeOperation = 'source-in';
-    ctx.filter = 'drop-shadow(-9px 9px 0px #e81)';
-
-    canvas2 = document.createElement("canvas");
-    ctx2 = canvas2.getContext("2d");
-
-    ctx2.fillStyle = 'rgba(225,0,0,1)';
-    ctx2.fillRect(50,50,75,50);
-    ctx2.fillStyle = 'rgba(0,255,0,1)';
-    ctx2.fillRect(70,70,75,50);
-
-    ctx.drawImage(canvas2,0,0);
-</script>
diff --git a/html/canvas/element/manual/layers/layers-filter-globalcompositeoperation.html b/html/canvas/element/manual/layers/layers-filter-globalcompositeoperation.html
deleted file mode 100644
index 03d04d0..0000000
--- a/html/canvas/element/manual/layers/layers-filter-globalcompositeoperation.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<link rel="match" href="layers-filter-globalcompositeoperation-expected.html">
-<p class="desc"> Test to ensure beginlayer works for filter and global composition. </p>
-<script>
-    var canvas, ctx;
-    canvas = document.createElement("canvas");
-    canvas.width = 200;
-    canvas.height = 200;
-    document.body.appendChild(canvas);
-
-    ctx = canvas.getContext("2d");
-    ctx.fillStyle = 'rgba(0,0,255,1)';
-
-    var circle = new Path2D();
-    circle.arc(90, 90, 40, 0, 2 * Math.PI);
-    ctx.fill(circle);
-
-    ctx.globalCompositeOperation = 'source-in';
-    ctx.filter = 'drop-shadow(-9px 9px 0px #e81)';
-
-    ctx.beginLayer();
-
-    ctx.fillStyle = 'rgba(225,0,0,1)';
-    ctx.fillRect(50,50,75,50);
-    ctx.fillStyle = 'rgba(0,255,0,1)';
-    ctx.fillRect(70,70,75,50);
-
-    ctx.endLayer();
-</script>
diff --git a/html/canvas/element/manual/layers/layers-filter-shadow-expected.html b/html/canvas/element/manual/layers/layers-filter-shadow-expected.html
deleted file mode 100644
index 5d141c3..0000000
--- a/html/canvas/element/manual/layers/layers-filter-shadow-expected.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<p class="desc"> Test to ensure beginlayer works for filter and shadow. </p>
-<script>
-var canvas, ctx;
-canvas = document.createElement("canvas");
-canvas.width = 200;
-canvas.height = 200;
-document.body.appendChild(canvas);
-
-ctx = canvas.getContext("2d");
-ctx.fillStyle = 'rgba(0,0,255,1)';
-ctx.fillRect(50,50,95,70);
-
-ctx.filter = 'sepia(1) opacity(70%)';
-ctx.shadowOffsetX = -10;
-ctx.shadowOffsetY = 10;
-ctx.shadowColor = 'rgba(0,1,0,0.5)';
-
-canvas2 = document.createElement("canvas");
-ctx2 = canvas2.getContext("2d");
-ctx2.fillStyle = 'rgba(225,0,0,1)';
-ctx2.fillRect(60,40,75,50);
-ctx2.fillStyle = 'rgba(0,255,0,1)';
-ctx2.fillRect(80,60,75,50);
-
-ctx.drawImage(canvas2,0,0);
-</script>
diff --git a/html/canvas/element/manual/layers/layers-filter-shadow.html b/html/canvas/element/manual/layers/layers-filter-shadow.html
deleted file mode 100644
index 31ce671..0000000
--- a/html/canvas/element/manual/layers/layers-filter-shadow.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<link rel="match" href="layers-filter-shadow-expected.html">
-<p class="desc"> Test to ensure beginlayer works for filter and shadow. </p>
-<script>
-var canvas, ctx;
-canvas = document.createElement("canvas");
-canvas.width = 200;
-canvas.height = 200;
-document.body.appendChild(canvas);
-
-ctx = canvas.getContext("2d");
-ctx.fillStyle = 'rgba(0,0,255,1)';
-ctx.fillRect(50,50,95,70);
-
-ctx.filter = 'sepia(1) opacity(70%)';
-ctx.shadowOffsetX = -10;
-ctx.shadowOffsetY = 10;
-ctx.shadowColor = 'rgba(0,1,0,0.5)';
-
-ctx.beginLayer();
-
-ctx.fillStyle = 'rgba(225,0,0,1)';
-ctx.fillRect(60,40,75,50);
-ctx.fillStyle = 'rgba(0,255,0,1)';
-ctx.fillRect(80,60,75,50);
-
-ctx.endLayer();
-
-</script>
diff --git a/html/canvas/element/manual/layers/layers-filter.html b/html/canvas/element/manual/layers/layers-filter.html
deleted file mode 100644
index c4d63cd..0000000
--- a/html/canvas/element/manual/layers/layers-filter.html
+++ /dev/null
@@ -1,23 +0,0 @@
-<link rel="match" href="layers-filter-expected.html">
-<p class="desc"> Test to ensure beginlayer works for filter. </p>
-<script>
-    var canvas, ctx;
-    canvas = document.createElement("canvas");
-    canvas.width = 200;
-    canvas.height = 200;
-    document.body.appendChild(canvas);
-
-    ctx = canvas.getContext("2d");
-    ctx.fillStyle = 'rgba(0,0,255,1)';
-    ctx.fillRect(60,60,75,50);
-    ctx.filter = 'sepia(1) opacity(30%)';
-
-    ctx.beginLayer();
-
-    ctx.fillStyle = 'rgba(225,0,0,1)';
-    ctx.fillRect(50,50,75,50);
-    ctx.fillStyle = 'rgba(0,255,0,1)';
-    ctx.fillRect(70,70,75,50);
-
-    ctx.endLayer();
-</script>
diff --git a/html/canvas/element/manual/layers/layers-globalcompositeoperation-expected.html b/html/canvas/element/manual/layers/layers-globalcompositeoperation-expected.html
deleted file mode 100644
index d2d4317..0000000
--- a/html/canvas/element/manual/layers/layers-globalcompositeoperation-expected.html
+++ /dev/null
@@ -1,23 +0,0 @@
-<p class="desc"> Test to ensure beginlayer works for globalCompositeOperation. </p>
-<script>
-var canvas, ctx;
-canvas = document.createElement("canvas");
-canvas.width = 200;
-canvas.height = 200;
-document.body.appendChild(canvas);
-
-ctx = canvas.getContext("2d");
-ctx.fillStyle = 'rgba(0,0,255,1)';
-ctx.fillRect(50,50,95,70);
-
-ctx.globalCompositeOperation = 'source-in';
-
-canvas2 = document.createElement("canvas");
-ctx2 = canvas2.getContext("2d");
-ctx2.fillStyle = 'rgba(225,0,0,1)';
-ctx2.fillRect(60,40,75,50);
-ctx2.fillStyle = 'rgba(0,255,0,1)';
-ctx2.fillRect(80,60,75,50);
-
-ctx.drawImage(canvas2,0,0);
-</script>
diff --git a/html/canvas/element/manual/layers/layers-globalcompositeoperation.html b/html/canvas/element/manual/layers/layers-globalcompositeoperation.html
deleted file mode 100644
index 6d4fde3..0000000
--- a/html/canvas/element/manual/layers/layers-globalcompositeoperation.html
+++ /dev/null
@@ -1,25 +0,0 @@
-<link rel="match" href="layers-globalcompositeoperation-expected.html">
-<p class="desc"> Test to ensure beginlayer works for globalCompositeOperation. </p>
-<script>
-var canvas, ctx;
-canvas = document.createElement("canvas");
-canvas.width = 200;
-canvas.height = 200;
-document.body.appendChild(canvas);
-
-ctx = canvas.getContext("2d");
-ctx.fillStyle = 'rgba(0,0,255,1)';
-ctx.fillRect(50,50,95,70);
-
-ctx.globalCompositeOperation = 'source-in';
-
-ctx.beginLayer();
-
-ctx.fillStyle = 'rgba(225,0,0,1)';
-ctx.fillRect(60,40,75,50);
-ctx.fillStyle = 'rgba(0,255,0,1)';
-ctx.fillRect(80,60,75,50);
-
-ctx.endLayer();
-
-</script>
diff --git a/html/canvas/element/manual/layers/layers-shadow-expected.html b/html/canvas/element/manual/layers/layers-shadow-expected.html
deleted file mode 100644
index 7696045..0000000
--- a/html/canvas/element/manual/layers/layers-shadow-expected.html
+++ /dev/null
@@ -1,25 +0,0 @@
-<p class="desc"> A test to make sure shadow works with layers. </p>
-<script>
-var canvas, ctx;
-canvas = document.createElement("canvas");
-canvas.width = 200;
-canvas.height = 200;
-document.body.appendChild(canvas);
-
-ctx = canvas.getContext("2d");
-ctx.fillStyle = 'rgba(0,0,255,1)';
-ctx.fillRect(50,50,95,70);
-
-ctx.shadowOffsetX = -10;
-ctx.shadowOffsetY = 10;
-ctx.shadowColor = 'orange';
-
-canvas2 = document.createElement("canvas");
-ctx2 = canvas2.getContext("2d");
-ctx2.fillStyle = 'rgba(225,0,0,1)';
-ctx2.fillRect(60,40,75,50);
-ctx2.fillStyle = 'rgba(0,255,0,1)';
-ctx2.fillRect(80,60,75,50);
-
-ctx.drawImage(canvas2,0,0);
-</script>
diff --git a/html/canvas/element/manual/layers/layers-shadow.html b/html/canvas/element/manual/layers/layers-shadow.html
deleted file mode 100644
index 5976874..0000000
--- a/html/canvas/element/manual/layers/layers-shadow.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<link rel="match" href="layers-shadow-expected.html">
-<p class="desc"> A test to make sure shadow works with layers. </p>
-<script>
-
-var canvas, ctx;
-canvas = document.createElement("canvas");
-canvas.width = 200;
-canvas.height = 200;
-document.body.appendChild(canvas);
-
-ctx = canvas.getContext("2d");
-ctx.fillStyle = 'rgba(0,0,255,1)';
-ctx.fillRect(50,50,95,70);
-
-ctx.shadowOffsetX = -10;
-ctx.shadowOffsetY = 10;
-ctx.shadowColor = 'orange';
-
-ctx.beginLayer();
-
-ctx.fillStyle = 'rgba(225,0,0,1)';
-ctx.fillRect(60,40,75,50);
-ctx.fillStyle = 'rgba(0,255,0,1)';
-ctx.fillRect(80,60,75,50);
-
-ctx.endLayer();
-
-</script>
diff --git a/html/canvas/offscreen/layers/2d.layer.alpha-expected.html b/html/canvas/offscreen/layers/2d.layer.global-states.alpha-expected.html
similarity index 64%
rename from html/canvas/offscreen/layers/2d.layer.alpha-expected.html
rename to html/canvas/offscreen/layers/2d.layer.global-states.alpha-expected.html
index 615aa1c..72d97c6 100644
--- a/html/canvas/offscreen/layers/2d.layer.alpha-expected.html
+++ b/html/canvas/offscreen/layers/2d.layer.global-states.alpha-expected.html
@@ -1,8 +1,8 @@
 <!DOCTYPE html>
 <!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
-<title>Canvas test: 2d.layer.alpha</title>
-<h1>2d.layer.alpha</h1>
-<p class="desc">A test to make sure shadow works with layers.</p>
+<title>Canvas test: 2d.layer.global-states.alpha</title>
+<h1>2d.layer.global-states.alpha</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
 <canvas id="canvas" width="200" height="200">
   <p class="fallback">FAIL (fallback content)</p>
 </canvas>
@@ -11,7 +11,10 @@
   const ctx = canvas.getContext('2d');
 
   ctx.fillStyle = 'rgba(0, 0, 255, 1)';
-  ctx.fillRect(50, 50, 95, 70);
+
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
 
   ctx.globalAlpha = 0.6;
 
@@ -19,9 +22,9 @@
   ctx2 = canvas2.getContext("2d");
 
   ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
-  ctx2.fillRect(60, 40, 75, 50);
+  ctx2.fillRect(50, 50, 75, 50);
   ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
-  ctx2.fillRect(80, 60, 75, 50);
+  ctx2.fillRect(70, 70, 75, 50);
 
   ctx.drawImage(canvas2, 0, 0);
 </script>
diff --git a/html/canvas/offscreen/layers/2d.layer.global-states.alpha.filter-expected.html b/html/canvas/offscreen/layers/2d.layer.global-states.alpha.filter-expected.html
new file mode 100644
index 0000000..b4512e2
--- /dev/null
+++ b/html/canvas/offscreen/layers/2d.layer.global-states.alpha.filter-expected.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.alpha.filter</title>
+<h1>2d.layer.global-states.alpha.filter</h1>
+<p class="desc">Checks that layers correctly use global render states.</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)';
+
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
+
+  ctx.globalAlpha = 0.6;
+  ctx.filter = 'drop-shadow(-9px 9px 0px #e81)';
+
+  canvas2 = document.createElement("canvas");
+  ctx2 = canvas2.getContext("2d");
+
+  ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+  ctx2.fillRect(50, 50, 75, 50);
+  ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+  ctx2.fillRect(70, 70, 75, 50);
+
+  ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/html/canvas/offscreen/layers/2d.layer.global-states.alpha.filter.globalcompositeoperation-expected.html b/html/canvas/offscreen/layers/2d.layer.global-states.alpha.filter.globalcompositeoperation-expected.html
new file mode 100644
index 0000000..ab7e6cc
--- /dev/null
+++ b/html/canvas/offscreen/layers/2d.layer.global-states.alpha.filter.globalcompositeoperation-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.global-states.alpha.filter.globalcompositeoperation</title>
+<h1>2d.layer.global-states.alpha.filter.globalcompositeoperation</h1>
+<p class="desc">Checks that layers correctly use global render states.</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)';
+
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
+
+  ctx.globalAlpha = 0.6;
+  ctx.globalCompositeOperation = 'source-in';
+  ctx.filter = 'drop-shadow(-9px 9px 0px #e81)';
+
+  canvas2 = document.createElement("canvas");
+  ctx2 = canvas2.getContext("2d");
+
+  ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+  ctx2.fillRect(50, 50, 75, 50);
+  ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+  ctx2.fillRect(70, 70, 75, 50);
+
+  ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/html/canvas/offscreen/layers/2d.layer.global-states.alpha.filter.globalcompositeoperation.html b/html/canvas/offscreen/layers/2d.layer.global-states.alpha.filter.globalcompositeoperation.html
new file mode 100644
index 0000000..88d339f
--- /dev/null
+++ b/html/canvas/offscreen/layers/2d.layer.global-states.alpha.filter.globalcompositeoperation.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.global-states.alpha.filter.globalcompositeoperation-expected.html">
+<title>Canvas test: 2d.layer.global-states.alpha.filter.globalcompositeoperation</title>
+<h1>2d.layer.global-states.alpha.filter.globalcompositeoperation</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+  <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+  const offscreen_canvas = new OffscreenCanvas(200, 200);
+  const ctx = offscreen_canvas.getContext('2d');
+
+  ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
+
+  ctx.globalAlpha = 0.6;
+  ctx.globalCompositeOperation = 'source-in';
+  ctx.filter = 'drop-shadow(-9px 9px 0px #e81)';
+
+  ctx.beginLayer();
+
+  ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+  ctx.fillRect(50, 50, 75, 50);
+  ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+  ctx.fillRect(70, 70, 75, 50);
+
+  ctx.endLayer();
+
+  const canvas = document.getElementById("canvas");
+  canvas.getContext('2d').drawImage(offscreen_canvas, 0, 0);
+</script>
diff --git a/html/canvas/offscreen/layers/2d.layer.alpha.w.html b/html/canvas/offscreen/layers/2d.layer.global-states.alpha.filter.globalcompositeoperation.w.html
similarity index 64%
copy from html/canvas/offscreen/layers/2d.layer.alpha.w.html
copy to html/canvas/offscreen/layers/2d.layer.global-states.alpha.filter.globalcompositeoperation.w.html
index bc93442..a95085c 100644
--- a/html/canvas/offscreen/layers/2d.layer.alpha.w.html
+++ b/html/canvas/offscreen/layers/2d.layer.global-states.alpha.filter.globalcompositeoperation.w.html
@@ -1,10 +1,10 @@
 <!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.alpha-expected.html">
-<title>Canvas test: 2d.layer.alpha</title>
-<h1>2d.layer.alpha</h1>
-<p class="desc">A test to make sure shadow works with layers.</p>
+<link rel="match" href="2d.layer.global-states.alpha.filter.globalcompositeoperation-expected.html">
+<title>Canvas test: 2d.layer.global-states.alpha.filter.globalcompositeoperation</title>
+<h1>2d.layer.global-states.alpha.filter.globalcompositeoperation</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
 <canvas id="canvas" width="200" height="200">
   <p class="fallback">FAIL (fallback content)</p>
 </canvas>
@@ -14,16 +14,21 @@
     const ctx = oc.getContext('2d');
 
     ctx.fillStyle = 'rgba(0, 0, 255, 1)';
-    ctx.fillRect(50, 50, 95, 70);
+
+    var circle = new Path2D();
+    circle.arc(90, 90, 45, 0, 2 * Math.PI);
+    ctx.fill(circle);
 
     ctx.globalAlpha = 0.6;
+    ctx.globalCompositeOperation = 'source-in';
+    ctx.filter = 'drop-shadow(-9px 9px 0px #e81)';
 
     ctx.beginLayer();
 
     ctx.fillStyle = 'rgba(225, 0, 0, 1)';
-    ctx.fillRect(60, 40, 75, 50);
+    ctx.fillRect(50, 50, 75, 50);
     ctx.fillStyle = 'rgba(0, 255, 0, 1)';
-    ctx.fillRect(80, 60, 75, 50);
+    ctx.fillRect(70, 70, 75, 50);
 
     ctx.endLayer();
 
diff --git a/html/canvas/offscreen/layers/2d.layer.global-states.alpha.filter.html b/html/canvas/offscreen/layers/2d.layer.global-states.alpha.filter.html
new file mode 100644
index 0000000..e8d3d56
--- /dev/null
+++ b/html/canvas/offscreen/layers/2d.layer.global-states.alpha.filter.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.global-states.alpha.filter-expected.html">
+<title>Canvas test: 2d.layer.global-states.alpha.filter</title>
+<h1>2d.layer.global-states.alpha.filter</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+  <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+  const offscreen_canvas = new OffscreenCanvas(200, 200);
+  const ctx = offscreen_canvas.getContext('2d');
+
+  ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
+
+  ctx.globalAlpha = 0.6;
+  ctx.filter = 'drop-shadow(-9px 9px 0px #e81)';
+
+  ctx.beginLayer();
+
+  ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+  ctx.fillRect(50, 50, 75, 50);
+  ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+  ctx.fillRect(70, 70, 75, 50);
+
+  ctx.endLayer();
+
+  const canvas = document.getElementById("canvas");
+  canvas.getContext('2d').drawImage(offscreen_canvas, 0, 0);
+</script>
diff --git a/html/canvas/offscreen/layers/2d.layer.global-states.alpha.filter.shadow-expected.html b/html/canvas/offscreen/layers/2d.layer.global-states.alpha.filter.shadow-expected.html
new file mode 100644
index 0000000..9a49e36
--- /dev/null
+++ b/html/canvas/offscreen/layers/2d.layer.global-states.alpha.filter.shadow-expected.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.alpha.filter.shadow</title>
+<h1>2d.layer.global-states.alpha.filter.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</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)';
+
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
+
+  ctx.globalAlpha = 0.5;
+  ctx.filter = 'sepia(0.5)';
+  ctx.shadowOffsetX = -10;
+  ctx.shadowOffsetY = 10;
+  ctx.shadowColor = 'orange';
+
+  canvas2 = document.createElement("canvas");
+  ctx2 = canvas2.getContext("2d");
+
+  ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+  ctx2.fillRect(50, 50, 75, 50);
+  ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+  ctx2.fillRect(70, 70, 75, 50);
+
+  ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/html/canvas/offscreen/layers/2d.layer.global-states.alpha.filter.shadow.html b/html/canvas/offscreen/layers/2d.layer.global-states.alpha.filter.shadow.html
new file mode 100644
index 0000000..3e5e5bd
--- /dev/null
+++ b/html/canvas/offscreen/layers/2d.layer.global-states.alpha.filter.shadow.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.alpha.filter.shadow-expected.html">
+<meta name=fuzzy content="maxDifference=0-2; totalPixels=0-5824">
+<title>Canvas test: 2d.layer.global-states.alpha.filter.shadow</title>
+<h1>2d.layer.global-states.alpha.filter.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+  <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+  const offscreen_canvas = new OffscreenCanvas(200, 200);
+  const ctx = offscreen_canvas.getContext('2d');
+
+  ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
+
+  ctx.globalAlpha = 0.5;
+  ctx.filter = 'sepia(0.5)';
+  ctx.shadowOffsetX = -10;
+  ctx.shadowOffsetY = 10;
+  ctx.shadowColor = 'orange';
+
+  ctx.beginLayer();
+
+  ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+  ctx.fillRect(50, 50, 75, 50);
+  ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+  ctx.fillRect(70, 70, 75, 50);
+
+  ctx.endLayer();
+
+  const canvas = document.getElementById("canvas");
+  canvas.getContext('2d').drawImage(offscreen_canvas, 0, 0);
+</script>
diff --git a/html/canvas/offscreen/layers/2d.layer.alpha.w.html b/html/canvas/offscreen/layers/2d.layer.global-states.alpha.filter.shadow.w.html
similarity index 62%
copy from html/canvas/offscreen/layers/2d.layer.alpha.w.html
copy to html/canvas/offscreen/layers/2d.layer.global-states.alpha.filter.shadow.w.html
index bc93442..9fac030 100644
--- a/html/canvas/offscreen/layers/2d.layer.alpha.w.html
+++ b/html/canvas/offscreen/layers/2d.layer.global-states.alpha.filter.shadow.w.html
@@ -1,10 +1,11 @@
 <!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.alpha-expected.html">
-<title>Canvas test: 2d.layer.alpha</title>
-<h1>2d.layer.alpha</h1>
-<p class="desc">A test to make sure shadow works with layers.</p>
+<link rel="match" href="2d.layer.global-states.alpha.filter.shadow-expected.html">
+<meta name=fuzzy content="maxDifference=0-2; totalPixels=0-5824">
+<title>Canvas test: 2d.layer.global-states.alpha.filter.shadow</title>
+<h1>2d.layer.global-states.alpha.filter.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
 <canvas id="canvas" width="200" height="200">
   <p class="fallback">FAIL (fallback content)</p>
 </canvas>
@@ -14,16 +15,23 @@
     const ctx = oc.getContext('2d');
 
     ctx.fillStyle = 'rgba(0, 0, 255, 1)';
-    ctx.fillRect(50, 50, 95, 70);
 
-    ctx.globalAlpha = 0.6;
+    var circle = new Path2D();
+    circle.arc(90, 90, 45, 0, 2 * Math.PI);
+    ctx.fill(circle);
+
+    ctx.globalAlpha = 0.5;
+    ctx.filter = 'sepia(0.5)';
+    ctx.shadowOffsetX = -10;
+    ctx.shadowOffsetY = 10;
+    ctx.shadowColor = 'orange';
 
     ctx.beginLayer();
 
     ctx.fillStyle = 'rgba(225, 0, 0, 1)';
-    ctx.fillRect(60, 40, 75, 50);
+    ctx.fillRect(50, 50, 75, 50);
     ctx.fillStyle = 'rgba(0, 255, 0, 1)';
-    ctx.fillRect(80, 60, 75, 50);
+    ctx.fillRect(70, 70, 75, 50);
 
     ctx.endLayer();
 
diff --git a/html/canvas/offscreen/layers/2d.layer.alpha.w.html b/html/canvas/offscreen/layers/2d.layer.global-states.alpha.filter.w.html
similarity index 69%
copy from html/canvas/offscreen/layers/2d.layer.alpha.w.html
copy to html/canvas/offscreen/layers/2d.layer.global-states.alpha.filter.w.html
index bc93442..177bae4 100644
--- a/html/canvas/offscreen/layers/2d.layer.alpha.w.html
+++ b/html/canvas/offscreen/layers/2d.layer.global-states.alpha.filter.w.html
@@ -1,10 +1,10 @@
 <!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.alpha-expected.html">
-<title>Canvas test: 2d.layer.alpha</title>
-<h1>2d.layer.alpha</h1>
-<p class="desc">A test to make sure shadow works with layers.</p>
+<link rel="match" href="2d.layer.global-states.alpha.filter-expected.html">
+<title>Canvas test: 2d.layer.global-states.alpha.filter</title>
+<h1>2d.layer.global-states.alpha.filter</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
 <canvas id="canvas" width="200" height="200">
   <p class="fallback">FAIL (fallback content)</p>
 </canvas>
@@ -14,16 +14,20 @@
     const ctx = oc.getContext('2d');
 
     ctx.fillStyle = 'rgba(0, 0, 255, 1)';
-    ctx.fillRect(50, 50, 95, 70);
+
+    var circle = new Path2D();
+    circle.arc(90, 90, 45, 0, 2 * Math.PI);
+    ctx.fill(circle);
 
     ctx.globalAlpha = 0.6;
+    ctx.filter = 'drop-shadow(-9px 9px 0px #e81)';
 
     ctx.beginLayer();
 
     ctx.fillStyle = 'rgba(225, 0, 0, 1)';
-    ctx.fillRect(60, 40, 75, 50);
+    ctx.fillRect(50, 50, 75, 50);
     ctx.fillStyle = 'rgba(0, 255, 0, 1)';
-    ctx.fillRect(80, 60, 75, 50);
+    ctx.fillRect(70, 70, 75, 50);
 
     ctx.endLayer();
 
diff --git a/html/canvas/offscreen/layers/2d.layer.alpha.html b/html/canvas/offscreen/layers/2d.layer.global-states.alpha.html
similarity index 61%
rename from html/canvas/offscreen/layers/2d.layer.alpha.html
rename to html/canvas/offscreen/layers/2d.layer.global-states.alpha.html
index 90506bf..7064f47 100644
--- a/html/canvas/offscreen/layers/2d.layer.alpha.html
+++ b/html/canvas/offscreen/layers/2d.layer.global-states.alpha.html
@@ -1,9 +1,9 @@
 <!DOCTYPE html>
 <!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
-<link rel="match" href="2d.layer.alpha-expected.html">
-<title>Canvas test: 2d.layer.alpha</title>
-<h1>2d.layer.alpha</h1>
-<p class="desc">A test to make sure shadow works with layers.</p>
+<link rel="match" href="2d.layer.global-states.alpha-expected.html">
+<title>Canvas test: 2d.layer.global-states.alpha</title>
+<h1>2d.layer.global-states.alpha</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
 <canvas id="canvas" width="200" height="200">
   <p class="fallback">FAIL (fallback content)</p>
 </canvas>
@@ -12,16 +12,19 @@
   const ctx = offscreen_canvas.getContext('2d');
 
   ctx.fillStyle = 'rgba(0, 0, 255, 1)';
-  ctx.fillRect(50, 50, 95, 70);
+
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
 
   ctx.globalAlpha = 0.6;
 
   ctx.beginLayer();
 
   ctx.fillStyle = 'rgba(225, 0, 0, 1)';
-  ctx.fillRect(60, 40, 75, 50);
+  ctx.fillRect(50, 50, 75, 50);
   ctx.fillStyle = 'rgba(0, 255, 0, 1)';
-  ctx.fillRect(80, 60, 75, 50);
+  ctx.fillRect(70, 70, 75, 50);
 
   ctx.endLayer();
 
diff --git a/html/canvas/offscreen/layers/2d.layer.global-states.alpha.shadow-expected.html b/html/canvas/offscreen/layers/2d.layer.global-states.alpha.shadow-expected.html
new file mode 100644
index 0000000..9daefc3
--- /dev/null
+++ b/html/canvas/offscreen/layers/2d.layer.global-states.alpha.shadow-expected.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.alpha.shadow</title>
+<h1>2d.layer.global-states.alpha.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</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)';
+
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
+
+  ctx.globalAlpha = 0.5;
+  ctx.shadowOffsetX = -10;
+  ctx.shadowOffsetY = 10;
+  ctx.shadowColor = 'orange';
+
+  canvas2 = document.createElement("canvas");
+  ctx2 = canvas2.getContext("2d");
+
+  ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+  ctx2.fillRect(50, 50, 75, 50);
+  ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+  ctx2.fillRect(70, 70, 75, 50);
+
+  ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/html/canvas/offscreen/layers/2d.layer.global-states.alpha.shadow.html b/html/canvas/offscreen/layers/2d.layer.global-states.alpha.shadow.html
new file mode 100644
index 0000000..afbbac8
--- /dev/null
+++ b/html/canvas/offscreen/layers/2d.layer.global-states.alpha.shadow.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.global-states.alpha.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.alpha.shadow</title>
+<h1>2d.layer.global-states.alpha.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+  <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+  const offscreen_canvas = new OffscreenCanvas(200, 200);
+  const ctx = offscreen_canvas.getContext('2d');
+
+  ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
+
+  ctx.globalAlpha = 0.5;
+  ctx.shadowOffsetX = -10;
+  ctx.shadowOffsetY = 10;
+  ctx.shadowColor = 'orange';
+
+  ctx.beginLayer();
+
+  ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+  ctx.fillRect(50, 50, 75, 50);
+  ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+  ctx.fillRect(70, 70, 75, 50);
+
+  ctx.endLayer();
+
+  const canvas = document.getElementById("canvas");
+  canvas.getContext('2d').drawImage(offscreen_canvas, 0, 0);
+</script>
diff --git a/html/canvas/offscreen/layers/2d.layer.alpha.w.html b/html/canvas/offscreen/layers/2d.layer.global-states.alpha.shadow.w.html
similarity index 66%
copy from html/canvas/offscreen/layers/2d.layer.alpha.w.html
copy to html/canvas/offscreen/layers/2d.layer.global-states.alpha.shadow.w.html
index bc93442..9e0e709 100644
--- a/html/canvas/offscreen/layers/2d.layer.alpha.w.html
+++ b/html/canvas/offscreen/layers/2d.layer.global-states.alpha.shadow.w.html
@@ -1,10 +1,10 @@
 <!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.alpha-expected.html">
-<title>Canvas test: 2d.layer.alpha</title>
-<h1>2d.layer.alpha</h1>
-<p class="desc">A test to make sure shadow works with layers.</p>
+<link rel="match" href="2d.layer.global-states.alpha.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.alpha.shadow</title>
+<h1>2d.layer.global-states.alpha.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
 <canvas id="canvas" width="200" height="200">
   <p class="fallback">FAIL (fallback content)</p>
 </canvas>
@@ -14,16 +14,22 @@
     const ctx = oc.getContext('2d');
 
     ctx.fillStyle = 'rgba(0, 0, 255, 1)';
-    ctx.fillRect(50, 50, 95, 70);
 
-    ctx.globalAlpha = 0.6;
+    var circle = new Path2D();
+    circle.arc(90, 90, 45, 0, 2 * Math.PI);
+    ctx.fill(circle);
+
+    ctx.globalAlpha = 0.5;
+    ctx.shadowOffsetX = -10;
+    ctx.shadowOffsetY = 10;
+    ctx.shadowColor = 'orange';
 
     ctx.beginLayer();
 
     ctx.fillStyle = 'rgba(225, 0, 0, 1)';
-    ctx.fillRect(60, 40, 75, 50);
+    ctx.fillRect(50, 50, 75, 50);
     ctx.fillStyle = 'rgba(0, 255, 0, 1)';
-    ctx.fillRect(80, 60, 75, 50);
+    ctx.fillRect(70, 70, 75, 50);
 
     ctx.endLayer();
 
diff --git a/html/canvas/offscreen/layers/2d.layer.alpha.w.html b/html/canvas/offscreen/layers/2d.layer.global-states.alpha.w.html
similarity index 73%
rename from html/canvas/offscreen/layers/2d.layer.alpha.w.html
rename to html/canvas/offscreen/layers/2d.layer.global-states.alpha.w.html
index bc93442..59d528b 100644
--- a/html/canvas/offscreen/layers/2d.layer.alpha.w.html
+++ b/html/canvas/offscreen/layers/2d.layer.global-states.alpha.w.html
@@ -1,10 +1,10 @@
 <!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.alpha-expected.html">
-<title>Canvas test: 2d.layer.alpha</title>
-<h1>2d.layer.alpha</h1>
-<p class="desc">A test to make sure shadow works with layers.</p>
+<link rel="match" href="2d.layer.global-states.alpha-expected.html">
+<title>Canvas test: 2d.layer.global-states.alpha</title>
+<h1>2d.layer.global-states.alpha</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
 <canvas id="canvas" width="200" height="200">
   <p class="fallback">FAIL (fallback content)</p>
 </canvas>
@@ -14,16 +14,19 @@
     const ctx = oc.getContext('2d');
 
     ctx.fillStyle = 'rgba(0, 0, 255, 1)';
-    ctx.fillRect(50, 50, 95, 70);
+
+    var circle = new Path2D();
+    circle.arc(90, 90, 45, 0, 2 * Math.PI);
+    ctx.fill(circle);
 
     ctx.globalAlpha = 0.6;
 
     ctx.beginLayer();
 
     ctx.fillStyle = 'rgba(225, 0, 0, 1)';
-    ctx.fillRect(60, 40, 75, 50);
+    ctx.fillRect(50, 50, 75, 50);
     ctx.fillStyle = 'rgba(0, 255, 0, 1)';
-    ctx.fillRect(80, 60, 75, 50);
+    ctx.fillRect(70, 70, 75, 50);
 
     ctx.endLayer();
 
diff --git a/html/canvas/offscreen/layers/2d.layer.alpha-expected.html b/html/canvas/offscreen/layers/2d.layer.global-states.filter-expected.html
similarity index 60%
copy from html/canvas/offscreen/layers/2d.layer.alpha-expected.html
copy to html/canvas/offscreen/layers/2d.layer.global-states.filter-expected.html
index 615aa1c..cff8783 100644
--- a/html/canvas/offscreen/layers/2d.layer.alpha-expected.html
+++ b/html/canvas/offscreen/layers/2d.layer.global-states.filter-expected.html
@@ -1,8 +1,8 @@
 <!DOCTYPE html>
 <!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
-<title>Canvas test: 2d.layer.alpha</title>
-<h1>2d.layer.alpha</h1>
-<p class="desc">A test to make sure shadow works with layers.</p>
+<title>Canvas test: 2d.layer.global-states.filter</title>
+<h1>2d.layer.global-states.filter</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
 <canvas id="canvas" width="200" height="200">
   <p class="fallback">FAIL (fallback content)</p>
 </canvas>
@@ -11,17 +11,20 @@
   const ctx = canvas.getContext('2d');
 
   ctx.fillStyle = 'rgba(0, 0, 255, 1)';
-  ctx.fillRect(50, 50, 95, 70);
 
-  ctx.globalAlpha = 0.6;
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
+
+  ctx.filter = 'sepia(1) opacity(30%)';
 
   canvas2 = document.createElement("canvas");
   ctx2 = canvas2.getContext("2d");
 
   ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
-  ctx2.fillRect(60, 40, 75, 50);
+  ctx2.fillRect(50, 50, 75, 50);
   ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
-  ctx2.fillRect(80, 60, 75, 50);
+  ctx2.fillRect(70, 70, 75, 50);
 
   ctx.drawImage(canvas2, 0, 0);
 </script>
diff --git a/html/canvas/offscreen/layers/2d.layer.global-states.filter.globalcompositeoperation-expected.html b/html/canvas/offscreen/layers/2d.layer.global-states.filter.globalcompositeoperation-expected.html
new file mode 100644
index 0000000..12fc312
--- /dev/null
+++ b/html/canvas/offscreen/layers/2d.layer.global-states.filter.globalcompositeoperation-expected.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.filter.globalcompositeoperation</title>
+<h1>2d.layer.global-states.filter.globalcompositeoperation</h1>
+<p class="desc">Checks that layers correctly use global render states.</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)';
+
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
+
+  ctx.globalCompositeOperation = 'source-in';
+  ctx.filter = 'drop-shadow(-9px 9px 0px #e81)';
+
+  canvas2 = document.createElement("canvas");
+  ctx2 = canvas2.getContext("2d");
+
+  ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+  ctx2.fillRect(50, 50, 75, 50);
+  ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+  ctx2.fillRect(70, 70, 75, 50);
+
+  ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/html/canvas/offscreen/layers/2d.layer.global-states.filter.globalcompositeoperation.html b/html/canvas/offscreen/layers/2d.layer.global-states.filter.globalcompositeoperation.html
new file mode 100644
index 0000000..b3ce228
--- /dev/null
+++ b/html/canvas/offscreen/layers/2d.layer.global-states.filter.globalcompositeoperation.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.global-states.filter.globalcompositeoperation-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter.globalcompositeoperation</title>
+<h1>2d.layer.global-states.filter.globalcompositeoperation</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+  <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+  const offscreen_canvas = new OffscreenCanvas(200, 200);
+  const ctx = offscreen_canvas.getContext('2d');
+
+  ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
+
+  ctx.globalCompositeOperation = 'source-in';
+  ctx.filter = 'drop-shadow(-9px 9px 0px #e81)';
+
+  ctx.beginLayer();
+
+  ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+  ctx.fillRect(50, 50, 75, 50);
+  ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+  ctx.fillRect(70, 70, 75, 50);
+
+  ctx.endLayer();
+
+  const canvas = document.getElementById("canvas");
+  canvas.getContext('2d').drawImage(offscreen_canvas, 0, 0);
+</script>
diff --git a/html/canvas/offscreen/layers/2d.layer.alpha.w.html b/html/canvas/offscreen/layers/2d.layer.global-states.filter.globalcompositeoperation.w.html
similarity index 64%
copy from html/canvas/offscreen/layers/2d.layer.alpha.w.html
copy to html/canvas/offscreen/layers/2d.layer.global-states.filter.globalcompositeoperation.w.html
index bc93442..6d21ffb 100644
--- a/html/canvas/offscreen/layers/2d.layer.alpha.w.html
+++ b/html/canvas/offscreen/layers/2d.layer.global-states.filter.globalcompositeoperation.w.html
@@ -1,10 +1,10 @@
 <!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.alpha-expected.html">
-<title>Canvas test: 2d.layer.alpha</title>
-<h1>2d.layer.alpha</h1>
-<p class="desc">A test to make sure shadow works with layers.</p>
+<link rel="match" href="2d.layer.global-states.filter.globalcompositeoperation-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter.globalcompositeoperation</title>
+<h1>2d.layer.global-states.filter.globalcompositeoperation</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
 <canvas id="canvas" width="200" height="200">
   <p class="fallback">FAIL (fallback content)</p>
 </canvas>
@@ -14,16 +14,20 @@
     const ctx = oc.getContext('2d');
 
     ctx.fillStyle = 'rgba(0, 0, 255, 1)';
-    ctx.fillRect(50, 50, 95, 70);
 
-    ctx.globalAlpha = 0.6;
+    var circle = new Path2D();
+    circle.arc(90, 90, 45, 0, 2 * Math.PI);
+    ctx.fill(circle);
+
+    ctx.globalCompositeOperation = 'source-in';
+    ctx.filter = 'drop-shadow(-9px 9px 0px #e81)';
 
     ctx.beginLayer();
 
     ctx.fillStyle = 'rgba(225, 0, 0, 1)';
-    ctx.fillRect(60, 40, 75, 50);
+    ctx.fillRect(50, 50, 75, 50);
     ctx.fillStyle = 'rgba(0, 255, 0, 1)';
-    ctx.fillRect(80, 60, 75, 50);
+    ctx.fillRect(70, 70, 75, 50);
 
     ctx.endLayer();
 
diff --git a/html/canvas/offscreen/layers/2d.layer.global-states.filter.html b/html/canvas/offscreen/layers/2d.layer.global-states.filter.html
new file mode 100644
index 0000000..a36a34d
--- /dev/null
+++ b/html/canvas/offscreen/layers/2d.layer.global-states.filter.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.filter-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter</title>
+<h1>2d.layer.global-states.filter</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+  <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+  const offscreen_canvas = new OffscreenCanvas(200, 200);
+  const ctx = offscreen_canvas.getContext('2d');
+
+  ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
+
+  ctx.filter = 'sepia(1) opacity(30%)';
+
+  ctx.beginLayer();
+
+  ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+  ctx.fillRect(50, 50, 75, 50);
+  ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+  ctx.fillRect(70, 70, 75, 50);
+
+  ctx.endLayer();
+
+  const canvas = document.getElementById("canvas");
+  canvas.getContext('2d').drawImage(offscreen_canvas, 0, 0);
+</script>
diff --git a/html/canvas/offscreen/layers/2d.layer.global-states.filter.shadow-expected.html b/html/canvas/offscreen/layers/2d.layer.global-states.filter.shadow-expected.html
new file mode 100644
index 0000000..6ec9b02
--- /dev/null
+++ b/html/canvas/offscreen/layers/2d.layer.global-states.filter.shadow-expected.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.filter.shadow</title>
+<h1>2d.layer.global-states.filter.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</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)';
+
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
+
+  ctx.filter = 'sepia(1) opacity(70%)';
+  ctx.shadowOffsetX = -10;
+  ctx.shadowOffsetY = 10;
+  ctx.shadowColor = 'rgba(0,1,0,0.5)';
+
+  canvas2 = document.createElement("canvas");
+  ctx2 = canvas2.getContext("2d");
+
+  ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+  ctx2.fillRect(50, 50, 75, 50);
+  ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+  ctx2.fillRect(70, 70, 75, 50);
+
+  ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/html/canvas/offscreen/layers/2d.layer.global-states.filter.shadow.html b/html/canvas/offscreen/layers/2d.layer.global-states.filter.shadow.html
new file mode 100644
index 0000000..16439c6
--- /dev/null
+++ b/html/canvas/offscreen/layers/2d.layer.global-states.filter.shadow.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.global-states.filter.shadow-expected.html">
+<meta name=fuzzy content="maxDifference=0-1; totalPixels=0-49">
+<title>Canvas test: 2d.layer.global-states.filter.shadow</title>
+<h1>2d.layer.global-states.filter.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+  <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+  const offscreen_canvas = new OffscreenCanvas(200, 200);
+  const ctx = offscreen_canvas.getContext('2d');
+
+  ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
+
+  ctx.filter = 'sepia(1) opacity(70%)';
+  ctx.shadowOffsetX = -10;
+  ctx.shadowOffsetY = 10;
+  ctx.shadowColor = 'rgba(0,1,0,0.5)';
+
+  ctx.beginLayer();
+
+  ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+  ctx.fillRect(50, 50, 75, 50);
+  ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+  ctx.fillRect(70, 70, 75, 50);
+
+  ctx.endLayer();
+
+  const canvas = document.getElementById("canvas");
+  canvas.getContext('2d').drawImage(offscreen_canvas, 0, 0);
+</script>
diff --git a/html/canvas/offscreen/layers/2d.layer.alpha.w.html b/html/canvas/offscreen/layers/2d.layer.global-states.filter.shadow.w.html
similarity index 63%
copy from html/canvas/offscreen/layers/2d.layer.alpha.w.html
copy to html/canvas/offscreen/layers/2d.layer.global-states.filter.shadow.w.html
index bc93442..7abddb2 100644
--- a/html/canvas/offscreen/layers/2d.layer.alpha.w.html
+++ b/html/canvas/offscreen/layers/2d.layer.global-states.filter.shadow.w.html
@@ -1,10 +1,11 @@
 <!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.alpha-expected.html">
-<title>Canvas test: 2d.layer.alpha</title>
-<h1>2d.layer.alpha</h1>
-<p class="desc">A test to make sure shadow works with layers.</p>
+<link rel="match" href="2d.layer.global-states.filter.shadow-expected.html">
+<meta name=fuzzy content="maxDifference=0-1; totalPixels=0-49">
+<title>Canvas test: 2d.layer.global-states.filter.shadow</title>
+<h1>2d.layer.global-states.filter.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
 <canvas id="canvas" width="200" height="200">
   <p class="fallback">FAIL (fallback content)</p>
 </canvas>
@@ -14,16 +15,22 @@
     const ctx = oc.getContext('2d');
 
     ctx.fillStyle = 'rgba(0, 0, 255, 1)';
-    ctx.fillRect(50, 50, 95, 70);
 
-    ctx.globalAlpha = 0.6;
+    var circle = new Path2D();
+    circle.arc(90, 90, 45, 0, 2 * Math.PI);
+    ctx.fill(circle);
+
+    ctx.filter = 'sepia(1) opacity(70%)';
+    ctx.shadowOffsetX = -10;
+    ctx.shadowOffsetY = 10;
+    ctx.shadowColor = 'rgba(0,1,0,0.5)';
 
     ctx.beginLayer();
 
     ctx.fillStyle = 'rgba(225, 0, 0, 1)';
-    ctx.fillRect(60, 40, 75, 50);
+    ctx.fillRect(50, 50, 75, 50);
     ctx.fillStyle = 'rgba(0, 255, 0, 1)';
-    ctx.fillRect(80, 60, 75, 50);
+    ctx.fillRect(70, 70, 75, 50);
 
     ctx.endLayer();
 
diff --git a/html/canvas/offscreen/layers/2d.layer.alpha.w.html b/html/canvas/offscreen/layers/2d.layer.global-states.filter.w.html
similarity index 70%
copy from html/canvas/offscreen/layers/2d.layer.alpha.w.html
copy to html/canvas/offscreen/layers/2d.layer.global-states.filter.w.html
index bc93442..08e6c25 100644
--- a/html/canvas/offscreen/layers/2d.layer.alpha.w.html
+++ b/html/canvas/offscreen/layers/2d.layer.global-states.filter.w.html
@@ -1,10 +1,10 @@
 <!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.alpha-expected.html">
-<title>Canvas test: 2d.layer.alpha</title>
-<h1>2d.layer.alpha</h1>
-<p class="desc">A test to make sure shadow works with layers.</p>
+<link rel="match" href="2d.layer.global-states.filter-expected.html">
+<title>Canvas test: 2d.layer.global-states.filter</title>
+<h1>2d.layer.global-states.filter</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
 <canvas id="canvas" width="200" height="200">
   <p class="fallback">FAIL (fallback content)</p>
 </canvas>
@@ -14,16 +14,19 @@
     const ctx = oc.getContext('2d');
 
     ctx.fillStyle = 'rgba(0, 0, 255, 1)';
-    ctx.fillRect(50, 50, 95, 70);
 
-    ctx.globalAlpha = 0.6;
+    var circle = new Path2D();
+    circle.arc(90, 90, 45, 0, 2 * Math.PI);
+    ctx.fill(circle);
+
+    ctx.filter = 'sepia(1) opacity(30%)';
 
     ctx.beginLayer();
 
     ctx.fillStyle = 'rgba(225, 0, 0, 1)';
-    ctx.fillRect(60, 40, 75, 50);
+    ctx.fillRect(50, 50, 75, 50);
     ctx.fillStyle = 'rgba(0, 255, 0, 1)';
-    ctx.fillRect(80, 60, 75, 50);
+    ctx.fillRect(70, 70, 75, 50);
 
     ctx.endLayer();
 
diff --git a/html/canvas/offscreen/layers/2d.layer.global-states.globalcompositeoperation-expected.html b/html/canvas/offscreen/layers/2d.layer.global-states.globalcompositeoperation-expected.html
new file mode 100644
index 0000000..7e2e021
--- /dev/null
+++ b/html/canvas/offscreen/layers/2d.layer.global-states.globalcompositeoperation-expected.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.layer.global-states.globalcompositeoperation</title>
+<h1>2d.layer.global-states.globalcompositeoperation</h1>
+<p class="desc">Checks that layers correctly use global render states.</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)';
+
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
+
+  ctx.globalCompositeOperation = 'source-in';
+
+  canvas2 = document.createElement("canvas");
+  ctx2 = canvas2.getContext("2d");
+
+  ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+  ctx2.fillRect(50, 50, 75, 50);
+  ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+  ctx2.fillRect(70, 70, 75, 50);
+
+  ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/html/canvas/offscreen/layers/2d.layer.global-states.globalcompositeoperation.html b/html/canvas/offscreen/layers/2d.layer.global-states.globalcompositeoperation.html
new file mode 100644
index 0000000..1be8317
--- /dev/null
+++ b/html/canvas/offscreen/layers/2d.layer.global-states.globalcompositeoperation.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<link rel="match" href="2d.layer.global-states.globalcompositeoperation-expected.html">
+<title>Canvas test: 2d.layer.global-states.globalcompositeoperation</title>
+<h1>2d.layer.global-states.globalcompositeoperation</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+  <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+  const offscreen_canvas = new OffscreenCanvas(200, 200);
+  const ctx = offscreen_canvas.getContext('2d');
+
+  ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
+
+  ctx.globalCompositeOperation = 'source-in';
+
+  ctx.beginLayer();
+
+  ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+  ctx.fillRect(50, 50, 75, 50);
+  ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+  ctx.fillRect(70, 70, 75, 50);
+
+  ctx.endLayer();
+
+  const canvas = document.getElementById("canvas");
+  canvas.getContext('2d').drawImage(offscreen_canvas, 0, 0);
+</script>
diff --git a/html/canvas/offscreen/layers/2d.layer.alpha.w.html b/html/canvas/offscreen/layers/2d.layer.global-states.globalcompositeoperation.w.html
similarity index 67%
copy from html/canvas/offscreen/layers/2d.layer.alpha.w.html
copy to html/canvas/offscreen/layers/2d.layer.global-states.globalcompositeoperation.w.html
index bc93442..7757aa5 100644
--- a/html/canvas/offscreen/layers/2d.layer.alpha.w.html
+++ b/html/canvas/offscreen/layers/2d.layer.global-states.globalcompositeoperation.w.html
@@ -1,10 +1,10 @@
 <!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.alpha-expected.html">
-<title>Canvas test: 2d.layer.alpha</title>
-<h1>2d.layer.alpha</h1>
-<p class="desc">A test to make sure shadow works with layers.</p>
+<link rel="match" href="2d.layer.global-states.globalcompositeoperation-expected.html">
+<title>Canvas test: 2d.layer.global-states.globalcompositeoperation</title>
+<h1>2d.layer.global-states.globalcompositeoperation</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
 <canvas id="canvas" width="200" height="200">
   <p class="fallback">FAIL (fallback content)</p>
 </canvas>
@@ -14,16 +14,19 @@
     const ctx = oc.getContext('2d');
 
     ctx.fillStyle = 'rgba(0, 0, 255, 1)';
-    ctx.fillRect(50, 50, 95, 70);
 
-    ctx.globalAlpha = 0.6;
+    var circle = new Path2D();
+    circle.arc(90, 90, 45, 0, 2 * Math.PI);
+    ctx.fill(circle);
+
+    ctx.globalCompositeOperation = 'source-in';
 
     ctx.beginLayer();
 
     ctx.fillStyle = 'rgba(225, 0, 0, 1)';
-    ctx.fillRect(60, 40, 75, 50);
+    ctx.fillRect(50, 50, 75, 50);
     ctx.fillStyle = 'rgba(0, 255, 0, 1)';
-    ctx.fillRect(80, 60, 75, 50);
+    ctx.fillRect(70, 70, 75, 50);
 
     ctx.endLayer();
 
diff --git a/html/canvas/offscreen/layers/2d.layer.global-states.shadow-expected.html b/html/canvas/offscreen/layers/2d.layer.global-states.shadow-expected.html
new file mode 100644
index 0000000..6787bdd
--- /dev/null
+++ b/html/canvas/offscreen/layers/2d.layer.global-states.shadow-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.global-states.shadow</title>
+<h1>2d.layer.global-states.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</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)';
+
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
+
+  ctx.shadowOffsetX = -10;
+  ctx.shadowOffsetY = 10;
+  ctx.shadowColor = 'orange';
+
+  canvas2 = document.createElement("canvas");
+  ctx2 = canvas2.getContext("2d");
+
+  ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
+  ctx2.fillRect(50, 50, 75, 50);
+  ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
+  ctx2.fillRect(70, 70, 75, 50);
+
+  ctx.drawImage(canvas2, 0, 0);
+</script>
diff --git a/html/canvas/offscreen/layers/2d.layer.global-states.shadow.html b/html/canvas/offscreen/layers/2d.layer.global-states.shadow.html
new file mode 100644
index 0000000..87b8224
--- /dev/null
+++ b/html/canvas/offscreen/layers/2d.layer.global-states.shadow.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.global-states.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.shadow</title>
+<h1>2d.layer.global-states.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
+<canvas id="canvas" width="200" height="200">
+  <p class="fallback">FAIL (fallback content)</p>
+</canvas>
+<script>
+  const offscreen_canvas = new OffscreenCanvas(200, 200);
+  const ctx = offscreen_canvas.getContext('2d');
+
+  ctx.fillStyle = 'rgba(0, 0, 255, 1)';
+
+  var circle = new Path2D();
+  circle.arc(90, 90, 45, 0, 2 * Math.PI);
+  ctx.fill(circle);
+
+  ctx.shadowOffsetX = -10;
+  ctx.shadowOffsetY = 10;
+  ctx.shadowColor = 'orange';
+
+  ctx.beginLayer();
+
+  ctx.fillStyle = 'rgba(225, 0, 0, 1)';
+  ctx.fillRect(50, 50, 75, 50);
+  ctx.fillStyle = 'rgba(0, 255, 0, 1)';
+  ctx.fillRect(70, 70, 75, 50);
+
+  ctx.endLayer();
+
+  const canvas = document.getElementById("canvas");
+  canvas.getContext('2d').drawImage(offscreen_canvas, 0, 0);
+</script>
diff --git a/html/canvas/offscreen/layers/2d.layer.alpha.w.html b/html/canvas/offscreen/layers/2d.layer.global-states.shadow.w.html
similarity index 68%
copy from html/canvas/offscreen/layers/2d.layer.alpha.w.html
copy to html/canvas/offscreen/layers/2d.layer.global-states.shadow.w.html
index bc93442..b8d5988 100644
--- a/html/canvas/offscreen/layers/2d.layer.alpha.w.html
+++ b/html/canvas/offscreen/layers/2d.layer.global-states.shadow.w.html
@@ -1,10 +1,10 @@
 <!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.alpha-expected.html">
-<title>Canvas test: 2d.layer.alpha</title>
-<h1>2d.layer.alpha</h1>
-<p class="desc">A test to make sure shadow works with layers.</p>
+<link rel="match" href="2d.layer.global-states.shadow-expected.html">
+<title>Canvas test: 2d.layer.global-states.shadow</title>
+<h1>2d.layer.global-states.shadow</h1>
+<p class="desc">Checks that layers correctly use global render states.</p>
 <canvas id="canvas" width="200" height="200">
   <p class="fallback">FAIL (fallback content)</p>
 </canvas>
@@ -14,16 +14,21 @@
     const ctx = oc.getContext('2d');
 
     ctx.fillStyle = 'rgba(0, 0, 255, 1)';
-    ctx.fillRect(50, 50, 95, 70);
 
-    ctx.globalAlpha = 0.6;
+    var circle = new Path2D();
+    circle.arc(90, 90, 45, 0, 2 * Math.PI);
+    ctx.fill(circle);
+
+    ctx.shadowOffsetX = -10;
+    ctx.shadowOffsetY = 10;
+    ctx.shadowColor = 'orange';
 
     ctx.beginLayer();
 
     ctx.fillStyle = 'rgba(225, 0, 0, 1)';
-    ctx.fillRect(60, 40, 75, 50);
+    ctx.fillRect(50, 50, 75, 50);
     ctx.fillStyle = 'rgba(0, 255, 0, 1)';
-    ctx.fillRect(80, 60, 75, 50);
+    ctx.fillRect(70, 70, 75, 50);
 
     ctx.endLayer();
 
diff --git a/html/canvas/tools/gentestutilsunion.py b/html/canvas/tools/gentestutilsunion.py
index 097bf12..fdb6a39 100644
--- a/html/canvas/tools/gentestutilsunion.py
+++ b/html/canvas/tools/gentestutilsunion.py
@@ -28,7 +28,7 @@
 #
 # * Test the tests, add new ones to Git, remove deleted ones from Git, etc.
 
-from typing import List, Mapping, MutableMapping, Optional
+from typing import Any, List, Mapping, MutableMapping, Optional
 
 import re
 import collections
@@ -184,7 +184,7 @@
     OFFSCREEN_CANVAS = 'offscreencanvas'
 
 
-def _get_enabled_canvas_types(test: Mapping[str, str]) -> List[CanvasType]:
+def _get_enabled_canvas_types(test: Mapping[str, Any]) -> List[CanvasType]:
     return [CanvasType(t.lower()) for t in test.get('canvasType', CanvasType)]
 
 
@@ -199,7 +199,7 @@
                                 re.MULTILINE | re.DOTALL)
 
 
-def _get_canvas_size(test: Mapping[str, str]):
+def _get_canvas_size(test: Mapping[str, Any]):
     size = test.get('size', '100, 50')
     match = _CANVAS_SIZE_REGEX.match(size)
     if not match:
@@ -262,7 +262,7 @@
             worker_template % template_params, 'utf-8')
 
 
-def _generate_test(test: Mapping[str, str], templates: Mapping[str, str],
+def _generate_test(test: Mapping[str, Any], templates: Mapping[str, str],
                    sub_dir: str, html_canvas_cfg: TestConfig,
                    offscreen_canvas_cfg: TestConfig) -> None:
     name = test['name']
@@ -448,30 +448,42 @@
             pass  # Ignore if it already exists,
 
     used_tests = collections.defaultdict(set)
-    for test in tests:
-        name = test['name']
-        print('\r(%s)' % name, ' ' * 32, '\t')
+    for original_test in tests:
+        variants = original_test.get('variants', {'': dict()})
+        for variant_name, variant_params in variants.items():
+            test = original_test.copy()
+            if variant_name or variant_params:
+                test['name'] += '.' + variant_name
+                test['code'] = test['code'] % variant_params
+                if 'reference' in test:
+                  test['reference'] = test['reference'] % variant_params
+                test.update(variant_params)
 
-        enabled_canvas_types = _get_enabled_canvas_types(test)
+            name = test['name']
+            print('\r(%s)' % name, ' ' * 32, '\t')
 
-        already_tested = used_tests[name].intersection(enabled_canvas_types)
-        if already_tested:
-            raise InvalidTestDefinitionError(
-                f'Test {name} is defined twice for types {already_tested}')
-        used_tests[name].update(enabled_canvas_types)
+            enabled_canvas_types = _get_enabled_canvas_types(test)
 
-        sub_dir = _get_test_sub_dir(name, name_to_sub_dir)
-        _generate_test(
-            test,
-            templates,
-            sub_dir,
-            html_canvas_cfg=TestConfig(
-                out_dir=CANVASOUTPUTDIR,
-                image_out_dir=CANVASIMAGEOUTPUTDIR,
-                enabled=CanvasType.HTML_CANVAS in enabled_canvas_types),
-            offscreen_canvas_cfg=TestConfig(
-                out_dir=OFFSCREENCANVASOUTPUTDIR,
-                image_out_dir=OFFSCREENCANVASIMAGEOUTPUTDIR,
-                enabled=CanvasType.OFFSCREEN_CANVAS in enabled_canvas_types))
+            already_tested = used_tests[name].intersection(
+                enabled_canvas_types)
+            if already_tested:
+                raise InvalidTestDefinitionError(
+                    f'Test {name} is defined twice for types {already_tested}')
+            used_tests[name].update(enabled_canvas_types)
+
+            sub_dir = _get_test_sub_dir(name, name_to_sub_dir)
+            _generate_test(
+                test,
+                templates,
+                sub_dir,
+                html_canvas_cfg=TestConfig(
+                    out_dir=CANVASOUTPUTDIR,
+                    image_out_dir=CANVASIMAGEOUTPUTDIR,
+                    enabled=CanvasType.HTML_CANVAS in enabled_canvas_types),
+                offscreen_canvas_cfg=TestConfig(
+                    out_dir=OFFSCREENCANVASOUTPUTDIR,
+                    image_out_dir=OFFSCREENCANVASIMAGEOUTPUTDIR,
+                    enabled=CanvasType.OFFSCREEN_CANVAS in
+                    enabled_canvas_types))
 
     print()
diff --git a/html/canvas/tools/yaml-new/layers.yaml b/html/canvas/tools/yaml-new/layers.yaml
index 7f9719f..51dedfd 100644
--- a/html/canvas/tools/yaml-new/layers.yaml
+++ b/html/canvas/tools/yaml-new/layers.yaml
@@ -1,32 +1,84 @@
-- name: 2d.layer.alpha
-  desc: A test to make sure shadow works with layers.
+- name: 2d.layer.global-states
+  desc: Checks that layers correctly use global render states.
   size: 200, 200
   code: |
     ctx.fillStyle = 'rgba(0, 0, 255, 1)';
-    ctx.fillRect(50, 50, 95, 70);
 
-    ctx.globalAlpha = 0.6;
+    var circle = new Path2D();
+    circle.arc(90, 90, 45, 0, 2 * Math.PI);
+    ctx.fill(circle);
+
+    %(render_states)s
 
     ctx.beginLayer();
 
     ctx.fillStyle = 'rgba(225, 0, 0, 1)';
-    ctx.fillRect(60, 40, 75, 50);
+    ctx.fillRect(50, 50, 75, 50);
     ctx.fillStyle = 'rgba(0, 255, 0, 1)';
-    ctx.fillRect(80, 60, 75, 50);
+    ctx.fillRect(70, 70, 75, 50);
 
     ctx.endLayer();
   reference: |
     ctx.fillStyle = 'rgba(0, 0, 255, 1)';
-    ctx.fillRect(50, 50, 95, 70);
 
-    ctx.globalAlpha = 0.6;
+    var circle = new Path2D();
+    circle.arc(90, 90, 45, 0, 2 * Math.PI);
+    ctx.fill(circle);
+
+    %(render_states)s
 
     canvas2 = document.createElement("canvas");
     ctx2 = canvas2.getContext("2d");
 
     ctx2.fillStyle = 'rgba(225, 0, 0, 1)';
-    ctx2.fillRect(60, 40, 75, 50);
+    ctx2.fillRect(50, 50, 75, 50);
     ctx2.fillStyle = 'rgba(0, 255, 0, 1)';
-    ctx2.fillRect(80, 60, 75, 50);
+    ctx2.fillRect(70, 70, 75, 50);
 
     ctx.drawImage(canvas2, 0, 0);
+  variants:
+    alpha:
+      render_states: ctx.globalAlpha = 0.6;
+    globalcompositeoperation:
+      render_states: ctx.globalCompositeOperation = 'source-in';
+    shadow:
+      render_states: |-
+        ctx.shadowOffsetX = -10;
+        ctx.shadowOffsetY = 10;
+        ctx.shadowColor = 'orange';
+    filter:
+      render_states: ctx.filter = 'sepia(1) opacity(30%)';
+    alpha.shadow:
+      render_states: |-
+        ctx.globalAlpha = 0.5;
+        ctx.shadowOffsetX = -10;
+        ctx.shadowOffsetY = 10;
+        ctx.shadowColor = 'orange';
+    alpha.filter:
+      render_states: |-
+        ctx.globalAlpha = 0.6;
+        ctx.filter = 'drop-shadow(-9px 9px 0px #e81)';
+    alpha.filter.shadow:
+      fuzzy: maxDifference=0-2; totalPixels=0-5824
+      render_states: |-
+        ctx.globalAlpha = 0.5;
+        ctx.filter = 'sepia(0.5)';
+        ctx.shadowOffsetX = -10;
+        ctx.shadowOffsetY = 10;
+        ctx.shadowColor = 'orange';
+    alpha.filter.globalcompositeoperation:
+      render_states: |-
+        ctx.globalAlpha = 0.6;
+        ctx.globalCompositeOperation = 'source-in';
+        ctx.filter = 'drop-shadow(-9px 9px 0px #e81)';
+    filter.shadow:
+      fuzzy: maxDifference=0-1; totalPixels=0-49
+      render_states: |-
+        ctx.filter = 'sepia(1) opacity(70%)';
+        ctx.shadowOffsetX = -10;
+        ctx.shadowOffsetY = 10;
+        ctx.shadowColor = 'rgba(0,1,0,0.5)';
+    filter.globalcompositeoperation:
+      render_states: |-
+        ctx.globalCompositeOperation = 'source-in';
+        ctx.filter = 'drop-shadow(-9px 9px 0px #e81)';