[WebCodecs] Improve errors when operating on closed VideoFrame.

This adds exceptions when texturing a closed VideoFrame with WebGL,
just like we already have for the drawImage() path. These are clear
programming errors that should be called out and not operational
errors that might occur in the wild.

It's unclear if this is the problem of the linked issue, but it
will certainly help users track down issues if so.

Bug: 1236821
Change-Id: I590aa020b74483b58602e5237582889bc5e3cd3e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3075927
Commit-Queue: Dale Curtis <dalecurtis@chromium.org>
Reviewed-by: Dan Sanders <sandersd@chromium.org>
Reviewed-by: Kenneth Russell <kbr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#909422}
diff --git a/webcodecs/videoFrame-drawImage.any.js b/webcodecs/videoFrame-drawImage.any.js
index 5a969dc..5e07d3e 100644
--- a/webcodecs/videoFrame-drawImage.any.js
+++ b/webcodecs/videoFrame-drawImage.any.js
@@ -2,12 +2,11 @@
 // META: script=/webcodecs/utils.js
 
 function testDrawImageFromVideoFrame(
-    width, height, expectedPixel, canvasOptions, imageBitmapOptions,
-    imageSetting) {
-  let vfInit = {format: 'RGBA', timestamp: 0, codedWidth: width,
-                codedHeight: height};
+    width, height, expectedPixel, canvasOptions, imageSetting) {
+  let vfInit =
+      {format: 'RGBA', timestamp: 0, codedWidth: width, codedHeight: height};
   let data = new Uint32Array(vfInit.codedWidth * vfInit.codedHeight);
-  data.fill(0xFF966432); // 'rgb(50, 100, 150)';
+  data.fill(0xFF966432);  // 'rgb(50, 100, 150)';
   let frame = new VideoFrame(data, vfInit);
   let canvas = new OffscreenCanvas(width, height);
   let ctx = canvas.getContext('2d', canvasOptions);
@@ -16,34 +15,45 @@
   frame.close();
 }
 
-test(() => {
+test(_ => {
   return testDrawImageFromVideoFrame(48, 36, kSRGBPixel);
 }, 'drawImage(VideoFrame) with canvas(48x36 srgb uint8).');
 
-test(() => {
+test(_ => {
   return testDrawImageFromVideoFrame(480, 360, kSRGBPixel);
 }, 'drawImage(VideoFrame) with canvas(480x360 srgb uint8).');
 
-test(() => {
+test(_ => {
   return testDrawImageFromVideoFrame(
       48, 36, kP3Pixel, kCanvasOptionsP3Uint8, {colorSpaceConversion: 'none'},
       kImageSettingOptionsP3Uint8);
 }, 'drawImage(VideoFrame) with canvas(48x36 display-p3 uint8).');
 
-test(() => {
+test(_ => {
   return testDrawImageFromVideoFrame(
       480, 360, kP3Pixel, kCanvasOptionsP3Uint8, {colorSpaceConversion: 'none'},
       kImageSettingOptionsP3Uint8);
 }, 'drawImage(VideoFrame) with canvas(480x360 display-p3 uint8).');
 
-test(() => {
+test(_ => {
   return testDrawImageFromVideoFrame(
       48, 36, kRec2020Pixel, kCanvasOptionsRec2020Uint8,
       {colorSpaceConversion: 'none'}, kImageSettingOptionsRec2020Uint8);
 }, 'drawImage(VideoFrame) with canvas(48x36 rec2020 uint8).');
 
-test(() => {
-  return testDrawImageFromVideoFrame(
-      480, 360, kRec2020Pixel, kCanvasOptionsRec2020Uint8,
-      {colorSpaceConversion: 'none'}, kImageSettingOptionsRec2020Uint8);
-}, 'drawImage(VideoFrame) with canvas(480x360 rec2020 uint8).');
+test(_ => {
+  let width = 128;
+  let height = 128;
+  let vfInit =
+      {format: 'RGBA', timestamp: 0, codedWidth: width, codedHeight: height};
+  let data = new Uint32Array(vfInit.codedWidth * vfInit.codedHeight);
+  data.fill(0xFF966432);  // 'rgb(50, 100, 150)';
+  let frame = new VideoFrame(data, vfInit);
+  let canvas = new OffscreenCanvas(width, height);
+  let ctx = canvas.getContext('2d');
+
+  frame.close();
+  assert_throws_dom('InvalidStateError', _ => {
+    ctx.drawImage(frame, 0, 0);
+  }, 'drawImage with a closed VideoFrame should throw InvalidStateError.');
+}, 'drawImage on a closed VideoFrame throws InvalidStateError.');
diff --git a/webcodecs/videoFrame-texImage.any.js b/webcodecs/videoFrame-texImage.any.js
index f142f2b..51328b9 100644
--- a/webcodecs/videoFrame-texImage.any.js
+++ b/webcodecs/videoFrame-texImage.any.js
@@ -24,8 +24,8 @@
 
 function testTexImage2DFromVideoFrame(
     width, height, useTexSubImage2D, expectedPixel) {
-  let vfInit = {format: 'RGBA', timestamp: 0, codedWidth: width,
-                codedHeight: height};
+  let vfInit =
+      {format: 'RGBA', timestamp: 0, codedWidth: width, codedHeight: height};
   let argbData = new Uint32Array(vfInit.codedWidth * vfInit.codedHeight);
   argbData.fill(0xFF966432);  // 'rgb(50, 100, 150)';
   let frame = new VideoFrame(argbData, vfInit);
@@ -82,18 +82,48 @@
   testGLCanvas(gl, width, height, expectedPixel, assert_equals);
 }
 
-test(() => {
-  return testTexImage2DFromVideoFrame(48, 36, false, kSRGBPixel);
-}, 'drawImage(VideoFrame) with texImage2D (48x36) srgb.');
+function testTexImageWithClosedVideoFrame(useTexSubImage2D) {
+  let width = 128;
+  let height = 128;
+  let vfInit =
+      {format: 'RGBA', timestamp: 0, codedWidth: width, codedHeight: height};
+  let argbData = new Uint32Array(vfInit.codedWidth * vfInit.codedHeight);
+  argbData.fill(0xFF966432);  // 'rgb(50, 100, 150)';
+  let frame = new VideoFrame(argbData, vfInit);
 
-test(() => {
-  return testTexImage2DFromVideoFrame(48, 36, true, kSRGBPixel);
-}, 'drawImage(VideoFrame) with texSubImage2D (48x36) srgb.');
+  let gl_canvas = new OffscreenCanvas(width, height);
+  let gl = gl_canvas.getContext('webgl');
 
-test(() => {
-  return testTexImage2DFromVideoFrame(480, 360, false, kSRGBPixel);
-}, 'drawImage(VideoFrame) with texImage2D (480x360) srgb.');
+  frame.close();
+  if (useTexSubImage2D) {
+    gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, frame);
+  } else {
+    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, frame);
+  }
 
-test(() => {
-  return testTexImage2DFromVideoFrame(480, 360, true, kSRGBPixel);
-}, 'drawImage(VideoFrame) with texSubImage2D (480x360) srgb.');
+  assert_equals(gl.getError(), gl.INVALID_OPERATION);
+}
+
+test(_ => {
+  testTexImage2DFromVideoFrame(48, 36, false, kSRGBPixel);
+}, 'texImage2D with 48x36 srgb VideoFrame.');
+
+test(_ => {
+  testTexImage2DFromVideoFrame(48, 36, true, kSRGBPixel);
+}, 'texSubImage2D with 48x36 srgb VideoFrame.');
+
+test(_ => {
+  testTexImage2DFromVideoFrame(480, 360, false, kSRGBPixel);
+}, 'texImage2D with 480x360 srgb VideoFrame.');
+
+test(_ => {
+  testTexImage2DFromVideoFrame(480, 360, true, kSRGBPixel);
+}, 'texSubImage2D with 480x360 srgb VideoFrame.');
+
+test(_ => {
+  testTexImageWithClosedVideoFrame(false);
+}, 'texImage2D with a closed VideoFrame.');
+
+test(_ => {
+  testTexImageWithClosedVideoFrame(true);
+}, 'texSubImage2D with a closed VideoFrame.');