Adding Shadow handling to BeginEndLayers and minor code improvements

This CL adds the logic to handle Shadows and Layers.
In order to make it work, I've done some refactor on existing code, to
properly handle interactions of shadows with filters and alpha.

This CL also refactors out some code added to handle count and variable
number of intermediate layers added in beginLayer. The current code has
the assumption now that there is only one special case in beginLayer
where it will add an extra state. Therefore, the endLayer only has to
do an extra pop (and restore) in order to handle correctly the layers.

Bug: 1235854, 1258140
Change-Id: I1be26bbc300afec85ca91f36432d1553ce37f75b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3212902
Commit-Queue: Juanmi Huertas <juanmihd@chromium.org>
Commit-Queue: Fernando Serboncini <fserb@chromium.org>
Auto-Submit: Juanmi Huertas <juanmihd@chromium.org>
Reviewed-by: Fernando Serboncini <fserb@chromium.org>
Reviewed-by: Yi Xu <yiyix@chromium.org>
Cr-Commit-Position: refs/heads/main@{#932159}
diff --git a/html/canvas/element/manual/layers/layers-alpha-expected.html b/html/canvas/element/manual/layers/layers-alpha-expected.html
new file mode 100644
index 0000000..876eacc
--- /dev/null
+++ b/html/canvas/element/manual/layers/layers-alpha-expected.html
@@ -0,0 +1,25 @@
+<body>
+<p class="desc"> Test to ensure beginlayer works for alpha. </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.6;
+
+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>
+</body>
diff --git a/html/canvas/element/manual/layers/layers-alpha-filter-expected.html b/html/canvas/element/manual/layers/layers-alpha-filter-expected.html
new file mode 100644
index 0000000..bd0affb
--- /dev/null
+++ b/html/canvas/element/manual/layers/layers-alpha-filter-expected.html
@@ -0,0 +1,26 @@
+<body>
+<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>
+</body>
\ No newline at end of file
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
new file mode 100644
index 0000000..bd9b5ae
--- /dev/null
+++ b/html/canvas/element/manual/layers/layers-alpha-filter-globalcompositeoperation-expected.html
@@ -0,0 +1,30 @@
+<body>
+<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>
+</body>
\ No newline at end of file
diff --git a/html/canvas/element/manual/layers/layers-alpha-filter-globalcompositeoperation.html b/html/canvas/element/manual/layers/layers-alpha-filter-globalcompositeoperation.html
new file mode 100644
index 0000000..dd1af3d
--- /dev/null
+++ b/html/canvas/element/manual/layers/layers-alpha-filter-globalcompositeoperation.html
@@ -0,0 +1,29 @@
+<body>
+<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>
+</body>
\ No newline at end of file
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
new file mode 100644
index 0000000..bfdb26e
--- /dev/null
+++ b/html/canvas/element/manual/layers/layers-alpha-filter-shadow-expected.html
@@ -0,0 +1,29 @@
+<body>
+<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>
+</body>
diff --git a/html/canvas/element/manual/layers/layers-alpha-filter-shadow.html b/html/canvas/element/manual/layers/layers-alpha-filter-shadow.html
new file mode 100644
index 0000000..ae97240
--- /dev/null
+++ b/html/canvas/element/manual/layers/layers-alpha-filter-shadow.html
@@ -0,0 +1,30 @@
+<body>
+<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>
+</body>
diff --git a/html/canvas/element/manual/layers/layers-alpha-filter.html b/html/canvas/element/manual/layers/layers-alpha-filter.html
new file mode 100644
index 0000000..d45b1d5
--- /dev/null
+++ b/html/canvas/element/manual/layers/layers-alpha-filter.html
@@ -0,0 +1,25 @@
+<body>
+<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>
+</body>
\ No newline at end of file
diff --git a/html/canvas/element/manual/layers/layers-alpha-shadow-expected.html b/html/canvas/element/manual/layers/layers-alpha-shadow-expected.html
new file mode 100644
index 0000000..89343e7
--- /dev/null
+++ b/html/canvas/element/manual/layers/layers-alpha-shadow-expected.html
@@ -0,0 +1,28 @@
+<body>
+<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>
+</body>
diff --git a/html/canvas/element/manual/layers/layers-alpha-shadow.html b/html/canvas/element/manual/layers/layers-alpha-shadow.html
new file mode 100644
index 0000000..bfe7241
--- /dev/null
+++ b/html/canvas/element/manual/layers/layers-alpha-shadow.html
@@ -0,0 +1,29 @@
+<body>
+<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>
+</body>
diff --git a/html/canvas/element/manual/layers/layers-alpha.html b/html/canvas/element/manual/layers/layers-alpha.html
new file mode 100644
index 0000000..fe871e7
--- /dev/null
+++ b/html/canvas/element/manual/layers/layers-alpha.html
@@ -0,0 +1,26 @@
+<body>
+<p class="desc"> Test to ensure beginlayer works for alpha. </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.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>
+</body>
diff --git a/html/canvas/element/manual/layers/layers-endlayer-noop-expected.html b/html/canvas/element/manual/layers/layers-endlayer-noop-expected.html
new file mode 100644
index 0000000..e3c9571
--- /dev/null
+++ b/html/canvas/element/manual/layers/layers-endlayer-noop-expected.html
@@ -0,0 +1,26 @@
+<body>
+<p class="desc"> A test to make sure an unmatched endLayer is a no-op and has no effect on the code following it. </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.globalAlpha = 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>
+</body>
\ No newline at end of file
diff --git a/html/canvas/element/manual/layers/layers-endlayer-noop.html b/html/canvas/element/manual/layers/layers-endlayer-noop.html
new file mode 100644
index 0000000..d9d6200
--- /dev/null
+++ b/html/canvas/element/manual/layers/layers-endlayer-noop.html
@@ -0,0 +1,28 @@
+<body>
+<p class="desc"> A test to make sure an unmatched endLayer is a no-op and has no effect on the code following it. </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.globalAlpha = 0.5;
+
+// This endlayer call should no-op.
+ctx.endLayer();
+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>
+</body>
\ No newline at end of file
diff --git a/html/canvas/element/manual/layers/layers-filter-expected.html b/html/canvas/element/manual/layers/layers-filter-expected.html
new file mode 100644
index 0000000..a65f52b
--- /dev/null
+++ b/html/canvas/element/manual/layers/layers-filter-expected.html
@@ -0,0 +1,25 @@
+<body>
+<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>
+</body>
\ No newline at end of file
diff --git a/html/canvas/element/manual/layers/layers-filter-globalcompositeoperation-expected.html b/html/canvas/element/manual/layers/layers-filter-globalcompositeoperation-expected.html
new file mode 100644
index 0000000..3e9fbee
--- /dev/null
+++ b/html/canvas/element/manual/layers/layers-filter-globalcompositeoperation-expected.html
@@ -0,0 +1,30 @@
+<body>
+<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>
+</body>
\ No newline at end of file
diff --git a/html/canvas/element/manual/layers/layers-filter-globalcompositeoperation.html b/html/canvas/element/manual/layers/layers-filter-globalcompositeoperation.html
new file mode 100644
index 0000000..51c79c8
--- /dev/null
+++ b/html/canvas/element/manual/layers/layers-filter-globalcompositeoperation.html
@@ -0,0 +1,29 @@
+<body>
+<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>
+</body>
\ No newline at end of file
diff --git a/html/canvas/element/manual/layers/layers-filter-shadow-expected.html b/html/canvas/element/manual/layers/layers-filter-shadow-expected.html
new file mode 100644
index 0000000..85a5bea
--- /dev/null
+++ b/html/canvas/element/manual/layers/layers-filter-shadow-expected.html
@@ -0,0 +1,28 @@
+<body>
+<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>
+</body>
diff --git a/html/canvas/element/manual/layers/layers-filter-shadow.html b/html/canvas/element/manual/layers/layers-filter-shadow.html
new file mode 100644
index 0000000..e4da5b8
--- /dev/null
+++ b/html/canvas/element/manual/layers/layers-filter-shadow.html
@@ -0,0 +1,29 @@
+<body>
+<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>
+</body>
diff --git a/html/canvas/element/manual/layers/layers-filter.html b/html/canvas/element/manual/layers/layers-filter.html
new file mode 100644
index 0000000..9dbb09f
--- /dev/null
+++ b/html/canvas/element/manual/layers/layers-filter.html
@@ -0,0 +1,24 @@
+<body>
+<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>
+</body>
\ No newline at end of file
diff --git a/html/canvas/element/manual/layers/layers-loneendlayer-expected.html b/html/canvas/element/manual/layers/layers-loneendlayer-expected.html
new file mode 100644
index 0000000..d6e06cd
--- /dev/null
+++ b/html/canvas/element/manual/layers/layers-loneendlayer-expected.html
@@ -0,0 +1,20 @@
+<body>
+<p class="desc"> A test to make sure a single endLayer with no beginLayer is a no-op. </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.globalAlpha = 0.5;
+
+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);
+</script>
+</body>
\ No newline at end of file
diff --git a/html/canvas/element/manual/layers/layers-loneendlayer.html b/html/canvas/element/manual/layers/layers-loneendlayer.html
new file mode 100644
index 0000000..e762c5f
--- /dev/null
+++ b/html/canvas/element/manual/layers/layers-loneendlayer.html
@@ -0,0 +1,22 @@
+<body>
+<p class="desc"> A test to make sure a single endLayer with no beginLayer is a no-op. </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.globalAlpha = 0.5;
+
+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>
+</body>
\ No newline at end of file
diff --git a/html/canvas/element/manual/layers/layers-shadow-expected.html b/html/canvas/element/manual/layers/layers-shadow-expected.html
new file mode 100644
index 0000000..c58bc50
--- /dev/null
+++ b/html/canvas/element/manual/layers/layers-shadow-expected.html
@@ -0,0 +1,27 @@
+<body>
+<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>
+</body>
diff --git a/html/canvas/element/manual/layers/layers-shadow.html b/html/canvas/element/manual/layers/layers-shadow.html
new file mode 100644
index 0000000..a605ef4
--- /dev/null
+++ b/html/canvas/element/manual/layers/layers-shadow.html
@@ -0,0 +1,29 @@
+<body>
+<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>
+</body>