Bug 1497440 - Revendor webgl-conf from upstream/master.
authorJeff Gilbert <jgilbert@mozilla.com>
Mon, 08 Oct 2018 23:08:46 -0700
changeset 488471 47dd9400c467249c13f8957f62aed9a0d512f386
parent 488470 c50bab3424c7e33565df2538b4b00ebeb5a60ae1
child 488472 d103737552074c130ec2599608f705bb837e5d60
push id246
push userfmarier@mozilla.com
push dateSat, 13 Oct 2018 00:15:40 +0000
bugs1497440
milestone64.0a1
Bug 1497440 - Revendor webgl-conf from upstream/master.
dom/canvas/test/webgl-conf/checkout/conformance/buffers/buffer-data-array-buffer-delete.html
dom/canvas/test/webgl-conf/checkout/conformance/canvas/canvas-test.html
dom/canvas/test/webgl-conf/checkout/conformance/canvas/draw-static-webgl-to-multiple-canvas-test.html
dom/canvas/test/webgl-conf/checkout/conformance/canvas/draw-webgl-to-canvas-test.html
dom/canvas/test/webgl-conf/checkout/conformance/canvas/framebuffer-bindings-affected-by-to-data-url.html
dom/canvas/test/webgl-conf/checkout/conformance/canvas/rapid-resizing.html
dom/canvas/test/webgl-conf/checkout/conformance/canvas/to-data-url-test.html
dom/canvas/test/webgl-conf/checkout/conformance/context/00_test_list.txt
dom/canvas/test/webgl-conf/checkout/conformance/context/context-attribute-preserve-drawing-buffer.html
dom/canvas/test/webgl-conf/checkout/conformance/context/incorrect-context-object-behaviour.html
dom/canvas/test/webgl-conf/checkout/conformance/context/premultiplyalpha-test.html
dom/canvas/test/webgl-conf/checkout/conformance/context/resource-sharing-test.html
dom/canvas/test/webgl-conf/checkout/conformance/extensions/00_test_list.txt
dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-texture-compression-bptc.html
dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-texture-compression-rgtc.html
dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-astc.html
dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-atc.html
dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-etc.html
dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-etc1.html
dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-s3tc-srgb.html
dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-s3tc.html
dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-shared-resources.html
dom/canvas/test/webgl-conf/checkout/conformance/glsl/bugs/00_test_list.txt
dom/canvas/test/webgl-conf/checkout/conformance/glsl/bugs/struct-with-single-member-constructor.html
dom/canvas/test/webgl-conf/checkout/conformance/glsl/misc/00_test_list.txt
dom/canvas/test/webgl-conf/checkout/conformance/glsl/misc/fragcolor-fragdata-invariant.html
dom/canvas/test/webgl-conf/checkout/conformance/glsl/misc/struct-as-inout-parameter.html
dom/canvas/test/webgl-conf/checkout/conformance/glsl/misc/struct-as-out-parameter.html
dom/canvas/test/webgl-conf/checkout/conformance/misc/webgl-specific-stencil-settings.html
dom/canvas/test/webgl-conf/checkout/conformance/ogles/GL/struct/nestedstructcomb_various_frag.frag
dom/canvas/test/webgl-conf/checkout/conformance/ogles/GL/struct/nestedstructcomb_various_vert.vert
dom/canvas/test/webgl-conf/checkout/conformance/renderbuffers/framebuffer-state-restoration.html
dom/canvas/test/webgl-conf/checkout/conformance/rendering/00_test_list.txt
dom/canvas/test/webgl-conf/checkout/conformance/rendering/canvas-alpha-bug.html
dom/canvas/test/webgl-conf/checkout/conformance/rendering/framebuffer-switch.html
dom/canvas/test/webgl-conf/checkout/conformance/rendering/framebuffer-texture-switch.html
dom/canvas/test/webgl-conf/checkout/conformance/rendering/line-rendering-quality.html
dom/canvas/test/webgl-conf/checkout/conformance/rendering/multisample-corruption.html
dom/canvas/test/webgl-conf/checkout/conformance/rendering/preservedrawingbuffer-leak.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/00_test_list.txt
dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/canvas-teximage-after-multiple-drawimages.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/compressed-tex-image.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/tex-video-using-tex-unit-non-zero.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/texture-size-limit.html
dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/texture-upload-size.html
dom/canvas/test/webgl-conf/checkout/conformance2/attribs/00_test_list.txt
dom/canvas/test/webgl-conf/checkout/conformance2/attribs/render-no-enabled-attrib-arrays.html
dom/canvas/test/webgl-conf/checkout/conformance2/buffers/00_test_list.txt
dom/canvas/test/webgl-conf/checkout/conformance2/buffers/get-buffer-sub-data-validity.html
dom/canvas/test/webgl-conf/checkout/conformance2/context/00_test_list.txt
dom/canvas/test/webgl-conf/checkout/conformance2/context/context-mode.html
dom/canvas/test/webgl-conf/checkout/conformance2/context/incorrect-context-object-behaviour.html
dom/canvas/test/webgl-conf/checkout/conformance2/extensions/00_test_list.txt
dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl-get-buffer-sub-data-async-lose-context.html
dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl-get-buffer-sub-data-async-stress.html
dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl-get-buffer-sub-data-async.html
dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl_multiview.html
dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl_multiview_depth.html
dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl_multiview_draw_buffers.html
dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl_multiview_flat_varying.html
dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl_multiview_instanced_draw.html
dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl_multiview_non_multiview_shaders.html
dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl_multiview_single_view_operations.html
dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl_multiview_timer_query.html
dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl_multiview_transform_feedback.html
dom/canvas/test/webgl-conf/checkout/conformance2/glsl3/00_test_list.txt
dom/canvas/test/webgl-conf/checkout/conformance2/glsl3/const-struct-from-array-as-function-parameter.html
dom/canvas/test/webgl-conf/checkout/conformance2/rendering/00_test_list.txt
dom/canvas/test/webgl-conf/checkout/conformance2/rendering/blitframebuffer-size-overflow.html
dom/canvas/test/webgl-conf/checkout/conformance2/rendering/canvas-resizing-with-pbo-bound.html
dom/canvas/test/webgl-conf/checkout/conformance2/rendering/framebuffer-completeness-draw-framebuffer.html
dom/canvas/test/webgl-conf/checkout/conformance2/rendering/instanced-arrays.html
dom/canvas/test/webgl-conf/checkout/conformance2/rendering/instanced-rendering-large-divisor.html
dom/canvas/test/webgl-conf/checkout/conformance2/rendering/line-rendering-quality.html
dom/canvas/test/webgl-conf/checkout/conformance2/textures/misc/00_test_list.txt
dom/canvas/test/webgl-conf/checkout/conformance2/textures/misc/copy-texture-cube-map-bug.html
dom/canvas/test/webgl-conf/checkout/conformance2/textures/misc/tex-storage-2d.html
dom/canvas/test/webgl-conf/checkout/conformance2/textures/misc/tex-storage-and-subimage-3d.html
dom/canvas/test/webgl-conf/checkout/conformance2/textures/misc/tex-subimage3d-canvas-bug.html
dom/canvas/test/webgl-conf/checkout/conformance2/textures/misc/tex-unpack-params-imagedata.html
dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/00_test_list.txt
dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/same-buffer-two-binding-points.html
dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/simultaneous_binding.html
dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/transform_feedback.html
dom/canvas/test/webgl-conf/checkout/deqp/temp_externs/gecko_dom.js
dom/canvas/test/webgl-conf/checkout/extra/getbuffersubdata-nonblocking-benchmark.html
dom/canvas/test/webgl-conf/checkout/extra/logo-256x256.png
dom/canvas/test/webgl-conf/checkout/extra/texture-from-camera-stress.html
dom/canvas/test/webgl-conf/checkout/extra/workload-simulator.html
dom/canvas/test/webgl-conf/checkout/js/js-test-pre.js
dom/canvas/test/webgl-conf/checkout/js/tests/compressed-texture-utils.js
dom/canvas/test/webgl-conf/checkout/js/tests/iterable-test.js
dom/canvas/test/webgl-conf/checkout/js/tests/line-rendering-quality.js
dom/canvas/test/webgl-conf/checkout/js/tests/out-of-bounds-test.js
dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-canvas.js
dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-webgl-canvas.js
dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-canvas.js
dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-webgl-canvas.js
dom/canvas/test/webgl-conf/checkout/js/tests/webgl-compressed-texture-size-limit.js
dom/canvas/test/webgl-conf/checkout/js/tests/webgl_multiview_util.js
dom/canvas/test/webgl-conf/checkout/js/webgl-test-utils.js
dom/canvas/test/webgl-conf/checkout/test-guidelines.md
dom/canvas/test/webgl-conf/cherry_picks.txt
--- a/dom/canvas/test/webgl-conf/checkout/conformance/buffers/buffer-data-array-buffer-delete.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/buffers/buffer-data-array-buffer-delete.html
@@ -49,32 +49,36 @@ canvas.addEventListener(
     function(event) {
         testFailed("Context lost");
         event.preventDefault();
     },
     false);
 
 var wtu = WebGLTestUtils;
 var gl = wtu.create3DContext(canvas, {preserveDrawingBuffer: true});
-shouldBeNonNull("gl");
 
-var array = new Float32Array([0]);
-var buf = gl.createBuffer();
-gl.bindBuffer(gl.ARRAY_BUFFER, buf);
-gl.bufferData(gl.ARRAY_BUFFER, array, gl.STATIC_DRAW);
-wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+if (!gl) {
+    testFailed("context does not exist");
+    finishTest();
+} else {
+    var array = new Float32Array([0]);
+    var buf = gl.createBuffer();
+    gl.bindBuffer(gl.ARRAY_BUFFER, buf);
+    gl.bufferData(gl.ARRAY_BUFFER, array, gl.STATIC_DRAW);
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR);
 
-var attribLocation = 1;
-gl.enableVertexAttribArray(attribLocation);
-gl.vertexAttribPointer(attribLocation, 1, gl.FLOAT, false, 0, 0);
+    var attribLocation = 1;
+    gl.enableVertexAttribArray(attribLocation);
+    gl.vertexAttribPointer(attribLocation, 1, gl.FLOAT, false, 0, 0);
+
+    gl.deleteBuffer(buf);
 
-gl.deleteBuffer(buf);
-
-setTimeout(function() {
-    // Wait for possible context loss
-    finishTest();
-}, 2000);
+    setTimeout(function() {
+        // Wait for possible context loss
+        finishTest();
+    }, 2000);
+}
 
 var successfullyParsed = true;
 </script>
 
 </body>
 </html>
--- a/dom/canvas/test/webgl-conf/checkout/conformance/canvas/canvas-test.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/canvas/canvas-test.html
@@ -50,16 +50,17 @@ debug("Canvas.getContext");
 var err;
 var wtu = WebGLTestUtils;
 var canvas = document.getElementById("canvas");
 var canvas2d = document.getElementById("canvas2d");
 var ctx2d = canvas2d.getContext("2d");
 var gl = wtu.create3DContext(canvas);
 if (!gl) {
   testFailed("context does not exist");
+  finishTest();
 } else {
   testPassed("context exists");
 
   debug("");
   debug("Checking canvas and WebGL interaction");
 
   // Check that a canvas with no width or height is 300x150 pixels
   shouldBe('canvas.width', '300');
--- a/dom/canvas/test/webgl-conf/checkout/conformance/canvas/draw-static-webgl-to-multiple-canvas-test.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/canvas/draw-static-webgl-to-multiple-canvas-test.html
@@ -83,14 +83,14 @@ if (!gl) {
       wtu.checkCanvasRect(ctx2d[2], 0, 0, canvas2d[2].width, canvas2d[2].height, colorValue[i],
                           "drawImage: Should be (" + colorValue[i][0] + "," + colorValue[i][1] +
                           "," + colorValue[i][2] + "," + colorValue[i][3] + ").", 2);
     }
   }
 
   err = gl.getError();
   debug("");
-  finishTest();
 }
+finishTest();
 </script>
 
 </body>
 </html>
--- a/dom/canvas/test/webgl-conf/checkout/conformance/canvas/draw-webgl-to-canvas-test.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/canvas/draw-webgl-to-canvas-test.html
@@ -86,14 +86,14 @@ if (!gl) {
     wtu.checkCanvasRect(ctx2d[1], 0, 0, canvas2d[1].width, canvas2d[1].height, [255, 0, 0, 255],
                         "drawImage: Should be [255, 0, 0, 255]", 2);
     wtu.checkCanvasRect(ctx2d[2], 0, 0, canvas2d[2].width, canvas2d[2].height, [255, 0, 255, 255],
                         "drawImage: Should be [255, 0, 255, 255]", 2);
   }
 
   err = gl.getError();
   debug("");
-  finishTest();
 }
+finishTest();
 </script>
 
 </body>
 </html>
--- a/dom/canvas/test/webgl-conf/checkout/conformance/canvas/framebuffer-bindings-affected-by-to-data-url.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/canvas/framebuffer-bindings-affected-by-to-data-url.html
@@ -40,16 +40,21 @@
 "use strict";
 description("Verifies than GL framebuffer bindings do not change by toDataURL()");
 
 var wtu = WebGLTestUtils;
 function test() {
   var glCanvas = document.getElementById("example");
   var gl = wtu.create3DContext(glCanvas, {preserveDrawingBuffer: true, premultipliedAlpha: true});
 
+  if (!gl) {
+    testFailed("context does not exist");
+    return;
+  }
+
   var program = wtu.setupColorQuad(gl);
 
   // Clear backbuffer in red.
   gl.clearColor(1.0, 0.0, 0.0, 1.0);
   gl.clear(gl.COLOR_BUFFER_BIT);
   wtu.checkCanvas(gl, [255, 0, 0, 255], "should be red");
 
   var fbo = gl.createFramebuffer();
--- a/dom/canvas/test/webgl-conf/checkout/conformance/canvas/rapid-resizing.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/canvas/rapid-resizing.html
@@ -86,16 +86,18 @@ function nextTest() {
   canvas.width = currentSize;
   canvas.height = currentSize;
   var usePreserveDrawingBuffer = (testNumber == 1) ? true : false;
   debug("Testing preserveDrawingBuffer = " + usePreserveDrawingBuffer);
   gl = wtu.create3DContext(canvas, { preserveDrawingBuffer: usePreserveDrawingBuffer });
 
   if (!gl) {
     testFailed("context does not exist");
+
+    wtu.requestAnimFrame(nextTest);
   } else {
     testPassed("context exists");
 
     gl.clearColor(0, 0, 0, 1);
 
     program = wtu.setupProgram(gl, ["vshader", "fshader"], ["position"]);
     shouldBeNonNull("program");
 
--- a/dom/canvas/test/webgl-conf/checkout/conformance/canvas/to-data-url-test.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/canvas/to-data-url-test.html
@@ -46,16 +46,17 @@ var ctx;
 
 var main = function() {
   description();
   ctx = document.getElementById("c2d").getContext("2d");
   gl = wtu.create3DContext("c3d");
 
   if (!gl) {
     testFailed("can't create 3d context");
+    finishTest();
     return;
   }
 
   var clearRect = function(gl, x, y, width, height, color) {
     gl.clearColor(color[0] / 255, color[1] / 255, color[2] / 255, color[3] / 255);
     gl.scissor(x, y, width, height);
     gl.clear(gl.COLOR_BUFFER_BIT);
   };
--- a/dom/canvas/test/webgl-conf/checkout/conformance/context/00_test_list.txt
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/context/00_test_list.txt
@@ -10,10 +10,9 @@ context-attributes-alpha-depth-stencil-a
 --min-version 1.0.2 context-release-upon-reload.html
 --min-version 1.0.2 context-release-with-workers.html
 context-lost-restored.html
 context-lost.html
 --max-version 1.9.9 context-type-test.html
 incorrect-context-object-behaviour.html
 --max-version 1.9.9 methods.html
 premultiplyalpha-test.html
-resource-sharing-test.html
 --min-version 1.0.4 user-defined-properties-on-context.html
--- a/dom/canvas/test/webgl-conf/checkout/conformance/context/context-attribute-preserve-drawing-buffer.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/context/context-attribute-preserve-drawing-buffer.html
@@ -77,16 +77,25 @@ function checkResult(ctx1, ctx2, preserv
 
 function runTest(preserve) {
   var c1 = document.getElementById('c' + (preserve * 3 + 1));
   var c2 = document.getElementById('c' + (preserve * 3 + 2));
   var c3 = document.getElementById('c' + (preserve * 3 + 3));
   var ctx1 = c1.getContext('2d');
   var ctx2 = c2.getContext('2d');
   var gl = wtu.create3DContext(c3, { alpha:false, preserveDrawingBuffer:preserve });
+  if (!gl) {
+    testFailed("context does not exist");
+    if (preserve) {
+      finishTest()
+    } else {
+      runTest(true);
+    }
+    return;
+  }
   gl.clearColor(1, 0, 0, 1);
   gl.clear(gl.COLOR_BUFFER_BIT);
   ctx1.drawImage(c3, 0, 0);
   wtu.waitForComposite(function() {
     ctx2.drawImage(c3, 0, 0);
     checkResult(ctx1, ctx2, preserve);
   });
 }
--- a/dom/canvas/test/webgl-conf/checkout/conformance/context/incorrect-context-object-behaviour.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/context/incorrect-context-object-behaviour.html
@@ -39,50 +39,148 @@
 
 <script>
 "use strict";
 description("Tests calling WebGL APIs with objects from other contexts");
 
 var wtu = WebGLTestUtils;
 var contextA = wtu.create3DContext();
 var contextB = wtu.create3DContext();
+var bufferA = contextA.createBuffer();
+var bufferB = contextB.createBuffer();
+var frameBufferA = contextA.createFramebuffer();
+var frameBufferB = contextB.createFramebuffer();
 var programA = wtu.loadStandardProgram(contextA);
 var programB = wtu.loadStandardProgram(contextB);
+var renderBufferA = contextA.createRenderbuffer();
+var renderBufferB = contextB.createRenderbuffer();
 var shaderA = wtu.loadStandardVertexShader(contextA);
 var shaderB = wtu.loadStandardVertexShader(contextB);
 var textureA = contextA.createTexture();
 var textureB = contextB.createTexture();
-var frameBufferA = contextA.createFramebuffer();
-var frameBufferB = contextB.createFramebuffer();
-var renderBufferA = contextA.createRenderbuffer();
-var renderBufferB = contextB.createRenderbuffer();
 var locationA = contextA.getUniformLocation(programA, 'u_modelViewProjMatrix');
 var locationB = contextB.getUniformLocation(programB, 'u_modelViewProjMatrix');
+var uniformData = [];
 
-wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.compileShader(shaderB)");
-wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.linkProgram(programB)");
+function generateFloat32Array(length) {
+    uniformData = new Float32Array(length);
+}
+
+function generateFloatArray(length) {
+    uniformData = new Array(length);
+    for (var i = 0; i < length; i++) {
+        uniformData[i] = 0.0;
+    }
+}
+
+function generateInt32Array(length) {
+    uniformData = new Int32Array(length);
+}
+
+function generateIntArray(length) {
+    uniformData = new Array(length);
+    for (var i = 0; i < length; i++) {
+        uniformData[i] = 0;
+    }
+}
+
+// Make the bindable objects valid in both contexts first.
+wtu.shouldGenerateGLError(contextA, contextA.NO_ERROR, "contextA.bindBuffer(contextA.ARRAY_BUFFER, bufferA)");
+wtu.shouldGenerateGLError(contextA, contextA.NO_ERROR, "contextA.bindBuffer(contextA.ARRAY_BUFFER, null)");
+wtu.shouldGenerateGLError(contextB, contextB.NO_ERROR, "contextB.bindBuffer(contextB.ARRAY_BUFFER, bufferB)");
+wtu.shouldGenerateGLError(contextB, contextB.NO_ERROR, "contextB.bindBuffer(contextB.ARRAY_BUFFER, null)");
+wtu.shouldGenerateGLError(contextA, contextA.NO_ERROR, "contextA.bindFramebuffer(contextA.FRAMEBUFFER, frameBufferA)");
+wtu.shouldGenerateGLError(contextA, contextA.NO_ERROR, "contextA.bindFramebuffer(contextA.FRAMEBUFFER, null)");
+wtu.shouldGenerateGLError(contextB, contextB.NO_ERROR, "contextB.bindFramebuffer(contextB.FRAMEBUFFER, frameBufferB)");
+wtu.shouldGenerateGLError(contextB, contextB.NO_ERROR, "contextB.bindFramebuffer(contextB.FRAMEBUFFER, null)");
+wtu.shouldGenerateGLError(contextA, contextA.NO_ERROR, "contextA.bindRenderbuffer(contextA.RENDERBUFFER, renderBufferA)");
+wtu.shouldGenerateGLError(contextA, contextA.NO_ERROR, "contextA.bindRenderbuffer(contextA.RENDERBUFFER, null)");
+wtu.shouldGenerateGLError(contextB, contextB.NO_ERROR, "contextB.bindRenderbuffer(contextB.RENDERBUFFER, renderBufferB)");
+wtu.shouldGenerateGLError(contextB, contextB.NO_ERROR, "contextB.bindRenderbuffer(contextB.RENDERBUFFER, null)");
+wtu.shouldGenerateGLError(contextA, contextA.NO_ERROR, "contextA.bindTexture(contextA.TEXTURE_2D, textureA)");
+wtu.shouldGenerateGLError(contextA, contextA.NO_ERROR, "contextA.bindTexture(contextA.TEXTURE_2D, null)");
+wtu.shouldGenerateGLError(contextB, contextB.NO_ERROR, "contextB.bindTexture(contextB.TEXTURE_2D, textureB)");
+wtu.shouldGenerateGLError(contextB, contextB.NO_ERROR, "contextB.bindTexture(contextB.TEXTURE_2D, null)");
+
 wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.attachShader(programA, shaderB)");
 wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.attachShader(programB, shaderA)");
 wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.attachShader(programB, shaderB)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.bindAttribLocation(programB, 0, 'foo')");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.bindBuffer(contextA.ARRAY_BUFFER, bufferB)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.bindFramebuffer(contextA.FRAMEBUFFER, frameBufferB)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.bindRenderbuffer(contextA.RENDERBUFFER, renderBufferB)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.bindTexture(contextA.TEXTURE_2D, textureB)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.compileShader(shaderB)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.deleteBuffer(bufferB)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.deleteFramebuffer(frameBufferB)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.deleteProgram(programB)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.deleteRenderbuffer(renderBufferB)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.deleteShader(shaderB)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.deleteShader(shaderB)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.deleteTexture(textureB)");
 wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.detachShader(programA, shaderB)");
 wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.detachShader(programB, shaderA)");
 wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.detachShader(programB, shaderB)");
-wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.shaderSource(shaderB, 'foo')");
-wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.bindAttribLocation(programB, 0, 'foo')");
-wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.bindFramebuffer(contextA.FRAMEBUFFER, frameBufferB)");
-wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.bindRenderbuffer(contextA.RENDERBUFFER, renderBufferB)");
-wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.bindTexture(contextA.TEXTURE_2D, textureB)");
 wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.framebufferRenderbuffer(contextA.FRAMEBUFFER, contextA.DEPTH_ATTACHMENT, contextA.RENDERBUFFER, renderBufferB)");
 wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.framebufferTexture2D(contextA.FRAMEBUFFER, contextA.COLOR_ATTACHMENT0, contextA.TEXTURE_2D, textureB, 0)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.getActiveAttrib(programB, 0)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.getActiveUniform(programB, 0)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.getAttachedShaders(programB, 0)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.getAttribLocation(programB, 'a_vertex')");
 wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.getProgramParameter(programB, 0)");
 wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.getProgramInfoLog(programB, 0)");
 wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.getShaderParameter(shaderB, 0)");
 wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.getShaderInfoLog(shaderB, 0)");
 wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.getShaderSource(shaderB)");
 wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.getUniform(programB, locationA)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.getUniform(programA, locationB)");
 wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.getUniformLocation(programB, 'u_modelViewProjMatrix')");
+wtu.shouldGenerateGLError(contextA, contextA.NO_ERROR, "contextA.isBuffer(bufferB)");
+shouldBeFalse("contextA.isBuffer(bufferB)");
+wtu.shouldGenerateGLError(contextA, contextA.NO_ERROR, "contextA.isFramebuffer(frameBufferB)");
+shouldBeFalse("contextA.isFramebuffer(frameBufferB)");
+wtu.shouldGenerateGLError(contextA, contextA.NO_ERROR, "contextA.isProgram(programB)");
+shouldBeFalse("contextA.isProgram(programB)");
+wtu.shouldGenerateGLError(contextA, contextA.NO_ERROR, "contextA.isRenderbuffer(renderBufferB)");
+shouldBeFalse("contextA.isRenderbuffer(renderBufferB)");
+wtu.shouldGenerateGLError(contextA, contextA.NO_ERROR, "contextA.isShader(shaderB)");
+shouldBeFalse("contextA.isShader(shaderB)");
+wtu.shouldGenerateGLError(contextA, contextA.NO_ERROR, "contextA.isTexture(textureB)");
+shouldBeFalse("contextA.isTexture(textureB)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.linkProgram(programB)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.shaderSource(shaderB, 'foo')");
+                          wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform1f(locationB, 0.0)");
+generateFloat32Array(1);  wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform1fv(locationB, uniformData)");
+generateFloatArray(1);    wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform1fv(locationB, uniformData)");
+                          wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform1i(locationB, 0)");
+generateInt32Array(1);    wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform1iv(locationB, uniformData)");
+generateIntArray(1);      wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform1iv(locationB, uniformData)");
+                          wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform2f(locationB, 0.0, 0.0)");
+generateFloat32Array(2);  wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform2fv(locationB, uniformData)");
+generateFloatArray(2);    wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform2fv(locationB, uniformData)");
+                          wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform2i(locationB, 0, 0)");
+generateInt32Array(2);    wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform2iv(locationB, uniformData)");
+generateIntArray(2);      wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform2iv(locationB, uniformData)");
+                          wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform3f(locationB, 0.0, 0.0, 0.0)");
+generateFloat32Array(3);  wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform3fv(locationB, uniformData)");
+generateFloatArray(3);    wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform3fv(locationB, uniformData)");
+                          wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform3i(locationB, 0, 0, 0)");
+generateInt32Array(3);    wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform3iv(locationB, uniformData)");
+generateIntArray(3);      wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform3iv(locationB, uniformData)");
+                          wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform4f(locationB, 0.0, 0.0, 0.0, 0.0)");
+generateFloat32Array(4);  wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform4fv(locationB, uniformData)");
+generateFloatArray(4);    wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform4fv(locationB, uniformData)");
+                          wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform4i(locationB, 0, 0, 0, 0)");
+generateInt32Array(4);    wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform4iv(locationB, uniformData)");
+generateIntArray(4);      wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform4iv(locationB, uniformData)");
+generateFloat32Array(4);  wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniformMatrix2fv(locationB, false, uniformData)");
+generateFloatArray(4);    wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniformMatrix2fv(locationB, false, uniformData)");
+generateFloat32Array(9);  wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniformMatrix3fv(locationB, false, uniformData)");
+generateFloatArray(9);    wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniformMatrix3fv(locationB, false, uniformData)");
+generateFloat32Array(16); wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniformMatrix4fv(locationB, false, uniformData)");
+generateFloatArray(16);   wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniformMatrix4fv(locationB, false, uniformData)");
 
 var successfullyParsed = true;
 </script>
 
 <script src="../../js/js-test-post.js"></script>
 </body>
 </html>
--- a/dom/canvas/test/webgl-conf/checkout/conformance/context/premultiplyalpha-test.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/context/premultiplyalpha-test.html
@@ -168,16 +168,22 @@ function doNextTest() {
     gl = wtu.create3DContext(canvas, test.creationAttributes);
     var premultipliedAlpha = test.creationAttributes.premultipliedAlpha != false;
     var antialias = test.creationAttributes.antialias == true;
     debug("")
     debug("testing: premultipliedAlpha: " + premultipliedAlpha
         + ", antialias: " + antialias
         + ", imageFormat: " + test.imageFormat);
 
+    if (!gl) {
+      testFailed("context does not exist");
+      doNextTest();
+      return;
+    }
+
     shouldBe('gl.getContextAttributes().premultipliedAlpha', premultipliedAlpha.toString());
     shouldBeTrue('gl.getContextAttributes().preserveDrawingBuffer');
 
     wtu.log(gl.getContextAttributes());
     var program = wtu.setupTexturedQuad(gl);
 
     wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup.");
     var tex = gl.createTexture();
deleted file mode 100644
--- a/dom/canvas/test/webgl-conf/checkout/conformance/context/resource-sharing-test.html
+++ /dev/null
@@ -1,64 +0,0 @@
-<!--
-
-/*
-** Copyright (c) 2012 The Khronos Group Inc.
-**
-** Permission is hereby granted, free of charge, to any person obtaining a
-** copy of this software and/or associated documentation files (the
-** "Materials"), to deal in the Materials without restriction, including
-** without limitation the rights to use, copy, modify, merge, publish,
-** distribute, sublicense, and/or sell copies of the Materials, and to
-** permit persons to whom the Materials are furnished to do so, subject to
-** the following conditions:
-**
-** The above copyright notice and this permission notice shall be included
-** in all copies or substantial portions of the Materials.
-**
-** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
-*/
-
--->
-
-<!DOCTYPE html>
-<html>
-<head>
-<meta charset="utf-8">
-<title>WebGL Resource Sharing.</title>
-<link rel="stylesheet" href="../../resources/js-test-style.css"/>
-<script src="../../js/js-test-pre.js"></script>
-<script src="../../js/webgl-test-utils.js"></script>
-</head>
-<body>
-<canvas id="example1" width="2" height="2" style="width: 40px; height: 40px;"></canvas>
-<canvas id="example2" width="2" height="2" style="width: 40px; height: 40px;"></canvas>
-<div id="description"></div>
-<div id="console"></div>
-<script>
-"use strict";
-description("Tests that resources can not be shared.");
-debug("");
-
-var wtu = WebGLTestUtils;
-var gl1 = wtu.create3DContext("example1");
-var gl2 = wtu.create3DContext("example2");
-assertMsg(gl1 && gl2,
-          "Got 3d context.");
-
-var vertexObject = gl1.createBuffer();
-gl2.bindBuffer(gl2.ARRAY_BUFFER, vertexObject);
-assertMsg(
-  gl2.getError() == gl2.INVALID_OPERATION,
-  "attempt to use a resource from the wrong context should fail with INVALID_OPERATION");
-
-var successfullyParsed = true;
-</script>
-<script src="../../js/js-test-post.js"></script>
-</body>
-</html>
-
--- a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/00_test_list.txt
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/00_test_list.txt
@@ -1,11 +1,13 @@
 --min-version 1.0.3 --max-version 1.9.9 angle-instanced-arrays.html
 --min-version 1.0.3 --max-version 1.9.9 angle-instanced-arrays-out-of-bounds.html
 --min-version 1.0.3 --max-version 1.9.9 ext-blend-minmax.html
+--min-version 1.0.4 ext-texture-compression-bptc.html
+--min-version 1.0.4 ext-texture-compression-rgtc.html
 --min-version 1.0.4 ext-disjoint-timer-query.html
 --min-version 1.0.3 --max-version 1.9.9 ext-frag-depth.html
 --min-version 1.0.3 --max-version 1.9.9 ext-shader-texture-lod.html
 --min-version 1.0.3 --max-version 1.9.9 ext-sRGB.html
 --min-version 1.0.2 ext-texture-filter-anisotropic.html
 --min-version 1.0.2 get-extension.html
 --max-version 1.9.9 oes-standard-derivatives.html
 --max-version 1.9.9 oes-texture-float-with-canvas.html
@@ -21,22 +23,20 @@
 --min-version 1.0.3 --max-version 1.9.9 oes-texture-half-float-with-canvas.html
 --min-version 1.0.3 --max-version 1.9.9 oes-texture-half-float-with-image-data.html
 --min-version 1.0.3 --max-version 1.9.9 oes-texture-half-float-with-image.html
 --min-version 1.0.3 --max-version 1.9.9 oes-texture-half-float-with-video.html
 --min-version 1.0.2 --max-version 1.9.9 oes-element-index-uint.html
 webgl-debug-renderer-info.html
 webgl-debug-shaders.html
 --min-version 1.0.4 webgl-compressed-texture-astc.html
---min-version 1.0.3 webgl-compressed-texture-atc.html
 --min-version 1.0.4 webgl-compressed-texture-etc.html
+--min-version 1.0.4 webgl-compressed-texture-etc1.html
 --min-version 1.0.3 webgl-compressed-texture-pvrtc.html
 --min-version 1.0.2 webgl-compressed-texture-s3tc.html
 --min-version 1.0.4 webgl-compressed-texture-s3tc-srgb.html
 --min-version 1.0.3 webgl-compressed-texture-size-limit.html
 --min-version 1.0.2 --max-version 1.9.9 webgl-depth-texture.html
 --min-version 1.0.3 --max-version 1.9.9 webgl-draw-buffers.html
 --min-version 1.0.4 --max-version 1.9.9 webgl-draw-buffers-broadcast-return.html
 --min-version 1.0.3 --max-version 1.9.9 webgl-draw-buffers-feedback-loop.html
 --min-version 1.0.4 --max-version 1.9.9 webgl-draw-buffers-framebuffer-unsupported.html
 --min-version 1.0.4 --max-version 1.9.9 webgl-draw-buffers-max-draw-buffers.html
---min-version 1.0.3 webgl-shared-resources.html
-
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-texture-compression-bptc.html
@@ -0,0 +1,102 @@
+<!--
+
+/*
+** Copyright (c) 2018 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a
+** copy of this software and/or associated documentation files (the
+** "Materials"), to deal in the Materials without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Materials, and to
+** permit persons to whom the Materials are furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be included
+** in all copies or substantial portions of the Materials.
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+*/
+
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL EXT_texture_compression_bptc Conformance Tests</title>
+<LINK rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+<script src="../../js/tests/compressed-texture-utils.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+description("This test verifies the functionality of the EXT_texture_compression_bptc extension, if it is available.");
+
+debug("");
+
+var validFormats = {
+  COMPRESSED_RGBA_BPTC_UNORM_EXT: 0x8E8C,
+  COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT: 0x8E8D,
+  COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT: 0x8E8E,
+  COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT: 0x8E8F
+};
+
+function expectedByteLength(width, height, format) {
+  return Math.ceil(width / 4) * Math.ceil(height / 4) * 16;
+}
+
+function getBlockDimensions(format) {
+  return {width: 4, height: 4};
+}
+
+var wtu = WebGLTestUtils;
+var ctu = CompressedTextureUtils;
+var contextVersion = wtu.getDefault3DContextVersion();
+var gl = wtu.create3DContext();
+var ext;
+
+var formats = null;
+
+function runTestExtension() {
+  // Test that enum values are listed correctly in supported formats and in the extension object.
+  ctu.testCompressedFormatsListed(gl, validFormats);
+  ctu.testCorrectEnumValuesInExt(ext, validFormats);
+  // Test that texture upload buffer size is validated correctly.
+  ctu.testFormatRestrictionsOnBufferSize(gl, validFormats, expectedByteLength, getBlockDimensions);
+};
+
+function runTest() {
+  if (!gl) {
+    testFailed("context does not exist");
+  } else {
+    testPassed("context exists");
+
+    ctu.testCompressedFormatsUnavailableWhenExtensionDisabled(gl, validFormats, expectedByteLength, 4);
+
+    ext = gl.getExtension("EXT_texture_compression_bptc");
+
+    wtu.runExtensionSupportedTest(gl, "EXT_texture_compression_bptc", ext !== null);
+
+    if (ext !== null) {
+      runTestExtension();
+    }
+  }
+}
+
+runTest();
+
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-texture-compression-rgtc.html
@@ -0,0 +1,107 @@
+<!--
+
+/*
+** Copyright (c) 2018 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a
+** copy of this software and/or associated documentation files (the
+** "Materials"), to deal in the Materials without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Materials, and to
+** permit persons to whom the Materials are furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be included
+** in all copies or substantial portions of the Materials.
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+*/
+
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL EXT_texture_compression_rgtc Conformance Tests</title>
+<LINK rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+<script src="../../js/tests/compressed-texture-utils.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+description("This test verifies the functionality of the EXT_texture_compression_rgtc extension, if it is available.");
+
+debug("");
+
+var validFormats = {
+  COMPRESSED_RED_RGTC1_EXT: 0x8DBB,
+  COMPRESSED_SIGNED_RED_RGTC1_EXT: 0x8DBC,
+  COMPRESSED_RED_GREEN_RGTC2_EXT: 0x8DBD,
+  COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT: 0x8DBE
+};
+
+function expectedByteLength(width, height, format) {
+  if (format == validFormats.COMPRESSED_RED_RGTC1_EXT || format == validFormats.COMPRESSED_SIGNED_RED_RGTC1_EXT) {
+    return Math.ceil(width / 4) * Math.ceil(height / 4) * 8;
+  }
+  else {
+    return Math.ceil(width / 4) * Math.ceil(height / 4) * 16;
+  }
+}
+
+function getBlockDimensions(format) {
+  return {width: 4, height: 4};
+}
+
+var wtu = WebGLTestUtils;
+var ctu = CompressedTextureUtils;
+var contextVersion = wtu.getDefault3DContextVersion();
+var gl = wtu.create3DContext();
+var ext;
+
+var formats = null;
+
+function runTestExtension() {
+  // Test that enum values are listed correctly in supported formats and in the extension object.
+  ctu.testCompressedFormatsListed(gl, validFormats);
+  ctu.testCorrectEnumValuesInExt(ext, validFormats);
+  // Test that texture upload buffer size is validated correctly.
+  ctu.testFormatRestrictionsOnBufferSize(gl, validFormats, expectedByteLength, getBlockDimensions);
+};
+
+function runTest() {
+  if (!gl) {
+    testFailed("context does not exist");
+  } else {
+    testPassed("context exists");
+
+    ctu.testCompressedFormatsUnavailableWhenExtensionDisabled(gl, validFormats, expectedByteLength, 4);
+
+    ext = gl.getExtension("EXT_texture_compression_rgtc");
+
+    wtu.runExtensionSupportedTest(gl, "EXT_texture_compression_rgtc", ext !== null);
+
+    if (ext !== null) {
+      runTestExtension();
+    }
+  }
+}
+
+runTest();
+
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
--- a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-astc.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-astc.html
@@ -25,16 +25,17 @@
 
 <!DOCTYPE html>
 <html>
 <head>
 <meta charset="utf-8">
 <link rel="stylesheet" href="../../resources/js-test-style.css"/>
 <script src="../../js/js-test-pre.js"></script>
 <script src="../../js/webgl-test-utils.js"></script>
+<script src="../../js/tests/compressed-texture-utils.js"></script>
 <title>WebGL WEBGL_compressed_texture_astc Conformance Tests</title>
 <style>
 img {
  border: 1px solid black;
  margin-right: 1em;
 }
 .testimages {
 }
@@ -1596,16 +1597,17 @@ var decoded_12x12_argb_hdr = new Uint8Ar
     0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff,
     0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff,
     0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff,
     0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff,
     0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff,
 ]);
 
 var wtu = WebGLTestUtils;
+var ctu = CompressedTextureUtils;
 var contextVersion = wtu.getDefault3DContextVersion();
 var canvas = document.getElementById("canvas");
 var gl = wtu.create3DContext(canvas, {antialias: false});
 var program = wtu.setupTexturedQuad(gl);
 var extFlag = "WEBGL_compressed_texture_astc";
 var ext = null;
 var vao = null;
 var validFormats = {
@@ -1636,137 +1638,54 @@ var validFormats = {
     COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR : 0x93D9,
     COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR : 0x93DA,
     COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR : 0x93DB,
     COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR : 0x93DC,
     COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR : 0x93DD
 
 };
 var name;
-var supportedFormats;
 
 if (!gl) {
     testFailed("WebGL context does not exist");
 } else {
     testPassed("WebGL context exists");
 
-    // Run tests with extension disabled
-    runTestDisabled();
-    debug("");
+    // The texture size 3 * 5 * 8 below is divisible by all the different block sizes supported by the extension.
+    ctu.testCompressedFormatsUnavailableWhenExtensionDisabled(gl, validFormats, expectedByteLength, 3 * 5 * 8);
 
     // Query the extension and store globally so shouldBe can access it
     ext = wtu.getExtensionWithKnownPrefixes(gl, extFlag);
     if (!ext) {
         testPassed("No WEBGL_compressed_texture_astc support -- this is legal");
-        runSupportedTest(false);
+        wtu.runExtensionSupportedTest(gl, "WEBGL_compressed_texture_astc", false);
     } else {
         testPassed("Successfully enabled WEBGL_compressed_texture_astc extension");
 
         debug("");
-        runSupportedTest(true);
+        wtu.runExtensionSupportedTest(gl, "WEBGL_compressed_texture_astc", true);
         runTestExtension();
     }
 }
 
-function runSupportedTest(extensionEnabled) {
-    var name = wtu.getSupportedExtensionWithKnownPrefixes(gl, extFlag);
-    if (name !== undefined) {
-        if (extensionEnabled) {
-            testPassed("WEBGL_compressed_texture_astc listed as supported and getExtension succeeded");
-        } else {
-            testFailed("WEBGL_compressed_texture_astc listed as supported but getExtension failed");
-        }
-    } else {
-        if (extensionEnabled) {
-            testFailed("WEBGL_compressed_texture_astc not listed as supported but getExtension succeeded");
-        } else {
-            testPassed("WEBGL_compressed_texture_astc not listed as supported and getExtension failed -- this is legal");
-        }
-    }
-}
-
-
-function runTestDisabled() {
-    debug("Testing binding enum with extension disabled");
-
-    supportedFormats = gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS);
-    shouldBe("supportedFormats", "[]");
-}
-
 function runTestExtension() {
     debug("");
     debug("Testing " + extFlag);
 
-    supportedFormats = gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS);
-    // There should be exactly 28 formats for both WebGL 1.0 and WebGL 2.0.
-    shouldBe("supportedFormats.length", "28");
-
-    console.log(wtu);
+    // Test that enum values are listed correctly in supported formats and in the extension object.
+    ctu.testCompressedFormatsListed(gl, validFormats);
+    ctu.testCorrectEnumValuesInExt(ext, validFormats);
+    // Test that texture upload buffer size is validated correctly.
+    ctu.testFormatRestrictionsOnBufferSize(gl, validFormats, expectedByteLength, getBlockDimensions);
 
-    // check that all 28 formats exist
-    testFormatsExist(supportedFormats);
-    // check that all format enums exist.
-    testCompressionFormatsValidity();
-    // check that the specified restrictions fails with
-    // an INVALID_VALUE error when not respected
-    testASTCFormatsRestrictions();
     // Tests ASTC texture with every format
     testLDRTextures();
     testHDRTextures();
 }
 
-function testFormatsExist(supportedFormats) {
-    debug("");
-    debug("Testing every supported formats exist");
-
-    var failed;
-    for (var name in validFormats) {
-        var format = validFormats[name];
-        failed = true;
-        for (var ii = 0; ii < supportedFormats.length; ++ii) {
-            if (format == supportedFormats[ii]) {
-                testPassed("supported format " + formatToString(format) + " exists");
-                failed = false;
-                break;
-            }
-        }
-        if (failed) {
-            testFailed("supported format " + formatToString(format) + " does not exist");
-        }
-    }
-}
-
-function testCompressionFormatsValidity() {
-    debug("");
-    debug("Testing every supported formats is valid");
-
-    for (name in validFormats) {
-        var expected = "0x" + validFormats[name].toString(16);
-        var actual = "ext['" + name + "']";
-        shouldBe(actual, expected);
-    }
-}
-
-function testASTCFormatsRestrictions() {
-    debug("");
-    debug("Testing format restrictions on buffer size");
-
-    var data = new Uint8Array(17);
-
-    var tex = gl.createTexture();
-    gl.bindTexture(gl.TEXTURE_2D, tex);
-    for (var formatId in validFormats) {
-        var format = validFormats[formatId];
-        var expectedSize = expectedByteLength(16, 16, format, data.length);
-        gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, 16, 16, 0, data);
-        wtu.glErrorShouldBe(gl, gl.INVALID_VALUE,
-            formatToString(format) + " expected size: " + expectedSize);
-    }
-}
-
 function testLDRTextures() {
     debug("");
     debug("Testing every LDR texture format compression");
 
     var data = [];
     var formats = [];
     var raws = [];
 
@@ -1845,17 +1764,17 @@ function testASTCTexture(test, useTexSto
     var height = test.height;
     var format = test.format;
     var raw = test.raw;
 
     canvas.width = width;
     canvas.height = height;
     gl.viewport(0, 0, width, height);
     debug("");
-    debug("testing " + formatToString(format) + " " + width + "x" + height + " (" + test.mode + ")" +
+    debug("testing " + ctu.formatToString(ext, format) + " " + width + "x" + height + " (" + test.mode + ")" +
           (useTexStorage ? " via texStorage2D" : " via compressedTexImage2D"));
     debug("");
 
     // Texture upload
     var tex = gl.createTexture();
     gl.bindTexture(gl.TEXTURE_2D, tex);
     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
@@ -1908,20 +1827,20 @@ function compareRect(
 
     if(typeof(tolerance) == 'undefined') { tolerance = 5; }
     var actual = new Uint8Array(actualWidth * actualHeight * 4);
     gl.readPixels(0, 0, actualWidth, actualHeight, gl.RGBA, gl.UNSIGNED_BYTE, actual);
     flipImage(actual, actualWidth, actualHeight);
 
     var div = document.createElement("div");
     div.className = "testimages";
-    insertImg(div, "expected", makeImage(
+    ctu.insertCaptionedImg(div, "expected", ctu.makeScaledImage(
             actualWidth, actualHeight, dataWidth, expectedData,
             actualChannels == 4));
-    insertImg(div, "actual", makeImage(
+    ctu.insertCaptionedImg(div, "actual", ctu.makeScaledImage(
             actualWidth, actualHeight, actualWidth, actual,
             actualChannels == 4));
     div.appendChild(document.createElement('br'));
     document.getElementById("console").appendChild(div);
 
     var failed = false;
     for (var yy = 0; yy < actualHeight; ++yy) {
         for (var xx = 0; xx < actualWidth; ++xx) {
@@ -1970,84 +1889,61 @@ function buildTests(data, formats, raws,
             mode: mode
         };
         tests.push(test);
     }
 
     return tests;
 }
 
-function formatToString(format) {
-    for (var p in ext) {
-        if (ext[p] == format) {
-            return p;
-        }
-    }
-    return "0x" + format.toString(16);
-}
-
 function expectedByteLength(w, h, format) {
 
-    if (format == ext.COMPRESSED_RGBA_ASTC_4x4_KHR)
+    if (format == validFormats.COMPRESSED_RGBA_ASTC_4x4_KHR || format == validFormats.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR)
         return Math.floor((w + 3) / 4) * Math.floor((h + 3) / 4) * 16;
-    else if (format == ext.COMPRESSED_RGBA_ASTC_5x4_KHR)
+    else if (format == validFormats.COMPRESSED_RGBA_ASTC_5x4_KHR || format == validFormats.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR)
         return Math.floor((w + 4) / 5) * Math.floor((h + 3) / 4) * 16;
-    else if (format == ext.COMPRESSED_RGBA_ASTC_5x5_KHR)
+    else if (format == validFormats.COMPRESSED_RGBA_ASTC_5x5_KHR || format == validFormats.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR)
         return Math.floor((w + 4) / 5) * Math.floor((h + 4) / 5) * 16;
-    else if (format == ext.COMPRESSED_RGBA_ASTC_6x5_KHR)
+    else if (format == validFormats.COMPRESSED_RGBA_ASTC_6x5_KHR || format == validFormats.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR)
         return Math.floor((w + 5) / 6) * Math.floor((h + 4) / 5) * 16;
-    else if (format == ext.COMPRESSED_RGBA_ASTC_6x6_KHR)
+    else if (format == validFormats.COMPRESSED_RGBA_ASTC_6x6_KHR || format == validFormats.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR)
         return Math.floor((w + 5) / 6) * Math.floor((h + 5) / 6) * 16;
-    else if (format == ext.COMPRESSED_RGBA_ASTC_8x5_KHR)
+    else if (format == validFormats.COMPRESSED_RGBA_ASTC_8x5_KHR || format == validFormats.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR)
         return Math.floor((w + 7) / 8) * Math.floor((h + 4) / 5) * 16;
-    else if (format == ext.COMPRESSED_RGBA_ASTC_8x6_KHR)
+    else if (format == validFormats.COMPRESSED_RGBA_ASTC_8x6_KHR || format == validFormats.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR)
         return Math.floor((w + 7) / 8) * Math.floor((h + 5) / 6) * 16;
-    else if (format == ext.COMPRESSED_RGBA_ASTC_8x8_KHR)
+    else if (format == validFormats.COMPRESSED_RGBA_ASTC_8x8_KHR || format == validFormats.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR)
         return Math.floor((w + 7) / 8) * Math.floor((h + 7) / 8) * 16;
-    else if (format == ext.COMPRESSED_RGBA_ASTC_10x5_KHR)
+    else if (format == validFormats.COMPRESSED_RGBA_ASTC_10x5_KHR || format == validFormats.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR)
         return Math.floor((w + 9) / 10) * Math.floor((h + 4) / 5) * 16;
-    else if (format == ext.COMPRESSED_RGBA_ASTC_10x6_KHR)
+    else if (format == validFormats.COMPRESSED_RGBA_ASTC_10x6_KHR || format == validFormats.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR)
         return Math.floor((w + 9) / 10) * Math.floor((h + 5) / 6) * 16;
-    else if (format == ext.COMPRESSED_RGBA_ASTC_10x8_KHR)
+    else if (format == validFormats.COMPRESSED_RGBA_ASTC_10x8_KHR || format == validFormats.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR)
         return Math.floor((w + 9) / 10) * Math.floor((h + 7) / 8) * 16;
-    else if (format == ext.COMPRESSED_RGBA_ASTC_10x10_KHR)
+    else if (format == validFormats.COMPRESSED_RGBA_ASTC_10x10_KHR || format == validFormats.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR)
         return Math.floor((w + 9) / 10) * Math.floor((h + 9) / 10) * 16;
-    else if (format == ext.COMPRESSED_RGBA_ASTC_12x10_KHR)
+    else if (format == validFormats.COMPRESSED_RGBA_ASTC_12x10_KHR || format == validFormats.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR)
         return Math.floor((w + 11) / 12) * Math.floor((h + 9) / 10) * 16;
 
     return Math.floor((w + 11) / 12) * Math.floor((h + 11) / 12) * 16;
 }
 
-function insertImg(element, caption, img) {
-    var div = document.createElement("div");
-    div.appendChild(img);
-    var label = document.createElement("div");
-    label.appendChild(document.createTextNode(caption));
-    div.appendChild(label);
-    element.appendChild(div);
-}
-
-function makeImage(imageWidth, imageHeight, dataWidth, data, alpha) {
-    var scale = 8;
-    var c = document.createElement("canvas");
-    c.width = imageWidth * scale;
-    c.height = imageHeight * scale;
-    var ctx = c.getContext("2d");
-    for (var yy = 0; yy < imageHeight; ++yy) {
-        for (var xx = 0; xx < imageWidth; ++xx) {
-            var offset = (yy * dataWidth + xx) * 4;
-            ctx.fillStyle = "rgba(" +
-                    data[offset + 0] + "," +
-                    data[offset + 1] + "," +
-                    data[offset + 2] + "," +
-                    (alpha ? data[offset + 3] / 255 : 1) + ")";
-            ctx.fillRect(xx * scale, yy * scale, scale, scale);
+function getBlockDimensions(format) {
+    var re = /.*_(\d+)x(\d+)_KHR/;
+    for (name in validFormats) {
+        if (validFormats[name] === format) {
+            var match = name.match(re);
+            return {
+              width: parseInt(match[1], 10),
+              height: parseInt(match[2], 10)
+              };
         }
     }
-    return wtu.makeImageFromCanvas(c);
+    testFailed('Could not find block dimensions for format ' + ctu.formatToString(ext, format));
+    return {width: 4, height: 4};
 }
 
 // Swaps two cells in an arraybuffer.
 // this function is used in the image flipping function
 function swapCell(array, i, j) {
     var a = array[i];
     array[i] = array[j];
     array[j] = a;
deleted file mode 100644
--- a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-atc.html
+++ /dev/null
@@ -1,427 +0,0 @@
-<!--
-
-/*
-** Copyright (c) 2013 The Khronos Group Inc.
-**
-** Permission is hereby granted, free of charge, to any person obtaining a
-** copy of this software and/or associated documentation files (the
-** "Materials"), to deal in the Materials without restriction, including
-** without limitation the rights to use, copy, modify, merge, publish,
-** distribute, sublicense, and/or sell copies of the Materials, and to
-** permit persons to whom the Materials are furnished to do so, subject to
-** the following conditions:
-**
-** The above copyright notice and this permission notice shall be included
-** in all copies or substantial portions of the Materials.
-**
-** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
-*/
-
--->
-
-<!DOCTYPE html>
-<html>
-<head>
-<meta charset="utf-8">
-<link rel="stylesheet" href="../../resources/js-test-style.css"/>
-<script src="../../js/js-test-pre.js"></script>
-<script src="../../js/webgl-test-utils.js"></script>
-<title>WebGL WEBGL_compressed_texture_atc Conformance Tests</title>
-<style>
-img {
- border: 1px solid black;
- margin-right: 1em;
-}
-.testimages {
-}
-
-.testimages br {
-  clear: both;
-}
-
-.testimages > div {
-  float: left;
-  margin: 1em;
-}
-</style>
-</head>
-<body>
-<div id="description"></div>
-<canvas id="canvas" width="8" height="8" style="width: 8px; height: 8px;"></canvas>
-<div id="console"></div>
-<script>
-"use strict";
-description("This test verifies the functionality of the WEBGL_compressed_texture_atc extension, if it is available.");
-
-debug("");
-
-// Compressed textures generated with AMD's Compressonator tool
-// http://developer.amd.com/resources/archive/archived-tools/gpu-tools-archive/the-compressonator/
-var img_4x4_rgba_raw = new Uint8Array([
-    0xff,0x00,0x00,0x69,0x00,0xff,0x00,0xff,0xff,0x00,0x00,0xff,0x00,0xff,0x00,0xff,
-    0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0x00,0x00,0xff,0x00,0xff,0x00,0xff,
-    0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0x00,0xff,0x00,0xff,
-    0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,
-]);
-var img_4x4_rgb_atc = new Uint8Array([
-    0x00,0x7c,0xe0,0x07,0xcc,0xcf,0xc0,0xff,
-]);
-var img_4x4_rgba_atc_explicit = new Uint8Array([
-    0xf6,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x7c,0xe0,0x07,0xcc,0xcf,0xc0,0xff,
-]);
-var img_4x4_rgba_atc_interpolated = new Uint8Array([
-    0xff,0x6a,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x7c,0xe0,0x07,0xcc,0xcf,0xc0,0xff,
-]);
-var img_8x8_rgba_raw = new Uint8Array([
-    0xff,0x00,0x00,0x69,0x00,0xff,0x00,0xff,0xff,0x00,0x00,0xff,0x00,0xff,0x00,0xff,
-    0xff,0xff,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0xff,0x00,0x00,0xff,0xff,
-    0x00,0xff,0x00,0x69,0x00,0xff,0x00,0xff,0xff,0x00,0x00,0xff,0x00,0xff,0x00,0xff,
-    0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0xff,0x00,0x00,0xff,0xff,
-    0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0x00,0xff,0x00,0xff,
-    0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0x00,0xff,0xff,
-    0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,
-    0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,
-    0x00,0xff,0x00,0xff,0xff,0x00,0xff,0xff,0x00,0xff,0x00,0xff,0xff,0x00,0xff,0xff,
-    0x00,0x00,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0xff,0xff,0xff,
-    0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0x00,0xff,0x00,0xff,0xff,0x00,0xff,0xff,
-    0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0xff,0xff,0xff,
-    0x00,0xff,0x00,0x69,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0x00,0xff,0xff,
-    0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0xff,0xff,0xff,
-    0xff,0x00,0xff,0x69,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,
-    0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,
-]);
-var img_8x8_rgb_atc = new Uint8Array([
-    0x00,0x7c,0xe0,0x07,0xcc,0xcf,0xc0,0xff,0x1f,0x00,0xe0,0xff,0x33,0x30,0x3f,0x00,
-    0x1f,0x7c,0xe0,0x07,0x33,0x30,0x3f,0x00,0x1f,0x00,0xff,0x07,0xcc,0xcf,0xc0,0xff,
-]);
-var img_8x8_rgba_atc_explicit = new Uint8Array([
-    0xf6,0xff,0xf6,0xff,0xff,0xff,0xff,0xff,0x00,0x7c,0xe0,0x07,0xcc,0xcf,0xc0,0xff,
-    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x1f,0x00,0xe0,0xff,0x33,0x30,0x3f,0x00,
-    0xff,0xff,0xff,0xff,0xf6,0xff,0xf6,0xff,0x1f,0x7c,0xe0,0x07,0x33,0x30,0x3f,0x00,
-    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x1f,0x00,0xff,0x07,0xcc,0xcf,0xc0,0xff,
-]);
-var img_8x8_rgba_atc_interpolated = new Uint8Array([
-    0xff,0x6a,0x01,0x10,0x00,0x00,0x00,0x00,0x00,0x7c,0xe0,0x07,0xcc,0xcf,0xc0,0xff,
-    0x00,0xff,0x49,0x92,0x24,0x49,0x92,0x24,0x1f,0x00,0xe0,0xff,0x33,0x30,0x3f,0x00,
-    0xff,0x69,0x00,0x00,0x00,0x01,0x10,0x00,0x1f,0x7c,0xe0,0x07,0x33,0x30,0x3f,0x00,
-    0x00,0xff,0x49,0x92,0x24,0x49,0x92,0x24,0x1f,0x00,0xff,0x07,0xcc,0xcf,0xc0,0xff,
-]);
-
-var wtu = WebGLTestUtils;
-var contextVersion = wtu.getDefault3DContextVersion();
-var canvas = document.getElementById("canvas");
-var gl = wtu.create3DContext(canvas, {antialias: false});
-var program = wtu.setupTexturedQuad(gl);
-var ext = null;
-var vao = null;
-var validFormats = {
-    COMPRESSED_RGB_ATC_WEBGL                        : 0x8C92,
-    COMPRESSED_RGBA_ATC_EXPLICIT_ALPHA_WEBGL        : 0x8C93,
-    COMPRESSED_RGBA_ATC_INTERPOLATED_ALPHA_WEBGL    : 0x87EE,
-};
-var name;
-var supportedFormats;
-
-if (!gl) {
-    testFailed("WebGL context does not exist");
-} else {
-    testPassed("WebGL context exists");
-
-    // Run tests with extension disabled
-    runTestDisabled();
-
-    // Query the extension and store globally so shouldBe can access it
-    ext = wtu.getExtensionWithKnownPrefixes(gl, "WEBGL_compressed_texture_atc");
-    if (!ext) {
-        testPassed("No WEBGL_compressed_texture_atc support -- this is legal");
-        runSupportedTest(false);
-    } else {
-        testPassed("Successfully enabled WEBGL_compressed_texture_atc extension");
-
-        runSupportedTest(true);
-        runTestExtension();
-    }
-}
-
-function runSupportedTest(extensionEnabled) {
-    var name = wtu.getSupportedExtensionWithKnownPrefixes(gl, "WEBGL_compressed_texture_atc");
-    if (name !== undefined) {
-        if (extensionEnabled) {
-            testPassed("WEBGL_compressed_texture_atc listed as supported and getExtension succeeded");
-        } else {
-            testFailed("WEBGL_compressed_texture_atc listed as supported but getExtension failed");
-        }
-    } else {
-        if (extensionEnabled) {
-            testFailed("WEBGL_compressed_texture_atc not listed as supported but getExtension succeeded");
-        } else {
-            testPassed("WEBGL_compressed_texture_atc not listed as supported and getExtension failed -- this is legal");
-        }
-    }
-}
-
-
-function runTestDisabled() {
-    debug("Testing binding enum with extension disabled");
-
-    supportedFormats = gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS);
-    shouldBe("supportedFormats", "[]");
-}
-
-function formatExists(format, supportedFormats) {
-    for (var ii = 0; ii < supportedFormats.length; ++ii) {
-        if (format == supportedFormats[ii]) {
-            testPassed("supported format " + formatToString(format) + " is exists");
-            return;
-        }
-    }
-    testFailed("supported format " + formatToString(format) + " does not exist");
-}
-
-function formatToString(format) {
-    for (var p in ext) {
-        if (ext[p] == format) {
-            return p;
-        }
-    }
-    return "0x" + format.toString(16);
-}
-
-function runTestExtension() {
-    debug("Testing WEBGL_compressed_texture_atc");
-
-    // check that all format enums exist.
-    for (name in validFormats) {
-        var expected = "0x" + validFormats[name].toString(16);
-        var actual = "ext['" + name + "']";
-        shouldBe(actual, expected);
-    }
-
-    supportedFormats = gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS);
-    // There should be exactly 3 formats for both WebGL 1.0 and WebGL 2.0.
-    shouldBe("supportedFormats.length", "3");
-
-    // check that all 3 formats exist
-    for (var name in validFormats.length) {
-        formatExists(validFormats[name], supportedFormats);
-    }
-
-    // Test each format
-    testATC_RGB();
-    testATC_RGBA_Explicit();
-    testATC_RGBA_Interpolated();
-}
-
-function testATC_RGB() {
-    var tests = [
-        {   width: 4,
-            height: 4,
-            channels: 3,
-            data: img_4x4_rgb_atc,
-            raw: img_4x4_rgba_raw,
-            format: ext.COMPRESSED_RGB_ATC_WEBGL
-        },
-        {   width: 8,
-            height: 8,
-            channels: 3,
-            data: img_8x8_rgb_atc,
-            raw: img_8x8_rgba_raw,
-            format: ext.COMPRESSED_RGB_ATC_WEBGL
-        }
-    ];
-    testACTTextures(tests);
-}
-
-function testATC_RGBA_Explicit() {
-    var tests = [
-        {   width: 4,
-            height: 4,
-            channels: 4,
-            data: img_4x4_rgba_atc_explicit,
-            raw: img_4x4_rgba_raw,
-            format: ext.COMPRESSED_RGBA_ATC_EXPLICIT_ALPHA_WEBGL
-        },
-        {   width: 8,
-            height: 8,
-            channels: 4,
-            data: img_8x8_rgba_atc_explicit,
-            raw: img_8x8_rgba_raw,
-            format: ext.COMPRESSED_RGBA_ATC_EXPLICIT_ALPHA_WEBGL
-        }
-    ];
-    testACTTextures(tests);
-}
-
-function testATC_RGBA_Interpolated() {
-    var tests = [
-        {   width: 4,
-            height: 4,
-            channels: 4,
-            data: img_4x4_rgba_atc_interpolated,
-            raw: img_4x4_rgba_raw,
-            format: ext.COMPRESSED_RGBA_ATC_INTERPOLATED_ALPHA_WEBGL
-        },
-        {   width: 8,
-            height: 8,
-            channels: 4,
-            data: img_8x8_rgba_atc_interpolated,
-            raw: img_8x8_rgba_raw,
-            format: ext.COMPRESSED_RGBA_ATC_INTERPOLATED_ALPHA_WEBGL
-        }
-    ];
-    testACTTextures(tests);
-}
-
-function testACTTextures(tests) {
-    debug("<hr/>");
-    for (var ii = 0; ii < tests.length; ++ii) {
-        testACTTexture(tests[ii]);
-    }
-}
-
-function testACTTexture(test) {
-    var data = new Uint8Array(test.data);
-    var width = test.width;
-    var height = test.height;
-    var format = test.format;
-    var uncompressedData = test.raw;
-
-    canvas.width = width;
-    canvas.height = height;
-    gl.viewport(0, 0, width, height);
-    debug("testing " + formatToString(format) + " " + width + "x" + height);
-
-    var tex = gl.createTexture();
-    gl.bindTexture(gl.TEXTURE_2D, tex);
-    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
-    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
-    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
-    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
-    gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height, 0, data);
-    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "uploading compressed texture");
-    gl.generateMipmap(gl.TEXTURE_2D);
-    wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "trying to generate mipmaps from compressed texture");
-    wtu.clearAndDrawUnitQuad(gl);
-    compareRect(width, height, test.channels, width, height, uncompressedData, data, format, undefined, "NEAREST");
-    // Test again with linear filtering.
-    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
-    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
-    wtu.clearAndDrawUnitQuad(gl);
-    compareRect(width, height, test.channels, width, height, uncompressedData, data, format, undefined, "LINEAR");
-
-    gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width + 4, height, 0, data);
-    wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions");
-    gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height + 4, 0, data);
-    wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions");
-    gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width - 4, height, 0, data);
-    wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions");
-    gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height - 4, 0, data);
-    wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions");
-
-    gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width - 1, height, 0, data);
-    wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions");
-    gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width - 2, height, 0, data);
-    wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions");
-    gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height - 1, 0, data);
-    wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions");
-    gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height - 2, 0, data);
-    wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions");
-
-    gl.compressedTexImage2D(gl.TEXTURE_2D, -1, format, 1, height, 0, data);
-    wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "cannot specify negative mip level");
-
-    // ATC Does not allow use of CompressedTexSubImage
-    gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height, format, data);
-    wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "compressedTexSubImage2D not allowed");
-}
-
-function insertImg(element, caption, img) {
-    var div = document.createElement("div");
-    div.appendChild(img);
-    var label = document.createElement("div");
-    label.appendChild(document.createTextNode(caption));
-    div.appendChild(label);
-    element.appendChild(div);
-}
-
-function makeImage(imageWidth, imageHeight, dataWidth, data, alpha) {
-    var scale = 8;
-    var c = document.createElement("canvas");
-    c.width = imageWidth * scale;
-    c.height = imageHeight * scale;
-    var ctx = c.getContext("2d");
-    for (var yy = 0; yy < imageHeight; ++yy) {
-        for (var xx = 0; xx < imageWidth; ++xx) {
-            var offset = (yy * dataWidth + xx) * 4;
-            ctx.fillStyle = "rgba(" +
-                    data[offset + 0] + "," +
-                    data[offset + 1] + "," +
-                    data[offset + 2] + "," +
-                    (alpha ? data[offset + 3] / 255 : 1) + ")";
-            ctx.fillRect(xx * scale, yy * scale, scale, scale);
-        }
-    }
-    return wtu.makeImageFromCanvas(c);
-}
-function compareRect(
-        actualWidth, actualHeight, actualChannels,
-        dataWidth, dataHeight, expectedData,
-        testData, testFormat, tolerance, filteringMode) {
-    if(typeof(tolerance) == 'undefined') { tolerance = 5; }
-    var actual = new Uint8Array(actualWidth * actualHeight * 4);
-    gl.readPixels(
-            0, 0, actualWidth, actualHeight, gl.RGBA, gl.UNSIGNED_BYTE, actual);
-
-    var div = document.createElement("div");
-    div.className = "testimages";
-    insertImg(div, "expected", makeImage(
-            actualWidth, actualHeight, dataWidth, expectedData,
-            actualChannels == 4));
-    insertImg(div, "actual", makeImage(
-            actualWidth, actualHeight, actualWidth, actual,
-            actualChannels == 4));
-    div.appendChild(document.createElement('br'));
-    document.getElementById("console").appendChild(div);
-
-    var failed = false;
-    for (var yy = 0; yy < actualHeight; ++yy) {
-        for (var xx = 0; xx < actualWidth; ++xx) {
-            var actualOffset = (yy * actualWidth + xx) * 4;
-            var expectedOffset = (yy * dataWidth + xx) * 4;
-            var expected = [
-                    expectedData[expectedOffset + 0],
-                    expectedData[expectedOffset + 1],
-                    expectedData[expectedOffset + 2],
-                    (actualChannels == 3 ? 255 : expectedData[expectedOffset + 3])
-            ];
-            for (var jj = 0; jj < 4; ++jj) {
-                if (Math.abs(actual[actualOffset + jj] - expected[jj]) > tolerance) {
-                    failed = true;
-                    var was = actual[actualOffset + 0].toString();
-                    for (j = 1; j < 4; ++j) {
-                        was += "," + actual[actualOffset + j];
-                    }
-                    testFailed('at (' + xx + ', ' + yy +
-                                         ') expected: ' + expected + ' was ' + was);
-                }
-            }
-        }
-    }
-    if (!failed) {
-        testPassed("texture rendered correctly with " + filteringMode + " filtering");
-    }
-}
-
-debug("");
-var successfullyParsed = true;
-</script>
-<script src="../../js/js-test-post.js"></script>
-
-</body>
-</html>
--- a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-etc.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-etc.html
@@ -28,134 +28,117 @@
 <!DOCTYPE html>
 <html>
 <head>
 <meta charset="utf-8">
 <title>WebGL WEBGL_compressed_texture_etc Conformance Tests</title>
 <LINK rel="stylesheet" href="../../resources/js-test-style.css"/>
 <script src="../../js/js-test-pre.js"></script>
 <script src="../../js/webgl-test-utils.js"></script>
+<script src="../../js/tests/compressed-texture-utils.js"></script>
 </head>
 <body>
 <div id="description"></div>
 <div id="console"></div>
 <script>
 "use strict";
 description("This test verifies the functionality of the WEBGL_compressed_texture_etc extension, if it is available.");
 
 debug("");
-var COMPRESSED_RGB_S3TC_DXT1_EXT              = 0x83F0;
-var COMPRESSED_RGBA_S3TC_DXT1_EXT             = 0x83F1;
-var COMPRESSED_RGBA_S3TC_DXT3_EXT             = 0x83F2;
-var COMPRESSED_RGBA_S3TC_DXT5_EXT             = 0x83F3;
-var COMPRESSED_RGB_PVRTC_4BPPV1_IMG           = 0x8C00;
-var COMPRESSED_RGBA_PVRTC_4BPPV1_IMG          = 0x8C02;
-var ETC1_RGB8_OES                             = 0x8D64;
-var COMPRESSED_R11_EAC                        = 0x9270;
-var COMPRESSED_SIGNED_R11_EAC                 = 0x9271;
-var COMPRESSED_RG11_EAC                       = 0x9272;
-var COMPRESSED_SIGNED_RG11_EAC                = 0x9273;
-var COMPRESSED_RGB8_ETC2                      = 0x9274;
-var COMPRESSED_SRGB8_ETC2                     = 0x9275;
-var COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2  = 0x9276;
-var COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9277;
-var COMPRESSED_RGBA8_ETC2_EAC                 = 0x9278;
-var COMPRESSED_SRGB8_ALPHA8_ETC2_EAC          = 0x9279;
+
+var validFormats = {
+  COMPRESSED_R11_EAC                        : 0x9270,
+  COMPRESSED_SIGNED_R11_EAC                 : 0x9271,
+  COMPRESSED_RG11_EAC                       : 0x9272,
+  COMPRESSED_SIGNED_RG11_EAC                : 0x9273,
+  COMPRESSED_RGB8_ETC2                      : 0x9274,
+  COMPRESSED_SRGB8_ETC2                     : 0x9275,
+  COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2  : 0x9276,
+  COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 : 0x9277,
+  COMPRESSED_RGBA8_ETC2_EAC                 : 0x9278,
+  COMPRESSED_SRGB8_ALPHA8_ETC2_EAC          : 0x9279
+};
+
+function expectedByteLength(width, height, format) {
+  var blockSizeInBytes = 8;
+
+  var largerBlockFormats = [
+    validFormats.COMPRESSED_RG11_EAC,
+    validFormats.COMPRESSED_SIGNED_RG11_EAC,
+    validFormats.COMPRESSED_RGBA8_ETC2_EAC,
+    validFormats.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC];
+
+  if (largerBlockFormats.indexOf(format) >= 0) {
+    blockSizeInBytes = 16;
+  }
+
+  return Math.floor((width + 3) / 4) * Math.floor((height + 3) / 4) * blockSizeInBytes;
+}
+
+function getBlockDimensions(format) {
+  return {width: 4, height: 4};
+}
 
 var wtu = WebGLTestUtils;
+var ctu = CompressedTextureUtils;
 var contextVersion = wtu.getDefault3DContextVersion();
 var gl = wtu.create3DContext();
 var WEBGL_compressed_texture_etc;
 
 var formats = null;
 
 function runTest() {
   if (!gl) {
     testFailed("context does not exist");
   } else {
     testPassed("context exists");
 
-    var tex = gl.createTexture();
-    gl.bindTexture(gl.TEXTURE_2D, tex);
+    ctu.testCompressedFormatsUnavailableWhenExtensionDisabled(gl, validFormats, expectedByteLength, 4);
 
-    var haveExt = gl.getSupportedExtensions().indexOf("WEBGL_compressed_texture_etc") >= 0;
     WEBGL_compressed_texture_etc = gl.getExtension("WEBGL_compressed_texture_etc");
 
-    var isPositive = true;
+    wtu.runExtensionSupportedTest(gl, "WEBGL_compressed_texture_etc", WEBGL_compressed_texture_etc !== null);
+
+    var isPositive = WEBGL_compressed_texture_etc !== null;
 
-    if (haveExt) {
-      if (WEBGL_compressed_texture_etc !== null) {
-        testPassed("WEBGL_compressed_texture_etc listed as supported and getExtension succeeded");
-      } else {
-        testFailed("WEBGL_compressed_texture_etc listed as supported but getExtension failed");
-        return;
-      }
-    } else {
-      if (WEBGL_compressed_texture_etc !== null) {
-        testFailed("WEBGL_compressed_texture_etc listed as unsupported but getExtension succeeded");
-        return;
-      } else {
-        testPassed("No WEBGL_compressed_texture_etc support -- this is legal");
-        isPositive = false;
+    if (isPositive) {
+      // Test that enum values are listed correctly in supported formats and in the extension object.
+      ctu.testCompressedFormatsListed(gl, validFormats);
+      ctu.testCorrectEnumValuesInExt(WEBGL_compressed_texture_etc, validFormats);
+      // Test that texture upload buffer size is validated correctly.
+      ctu.testFormatRestrictionsOnBufferSize(gl, validFormats, expectedByteLength, getBlockDimensions);
+
+      var tex = gl.createTexture();
+      gl.bindTexture(gl.TEXTURE_2D, tex);
+
+      for (var name in validFormats) {
+        if (validFormats.hasOwnProperty(name)) {
+          var format = validFormats[name];
+          wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, " + format + ", 4, 4, 0, new Uint8Array(" + expectedByteLength(4, 4, format) + "))");
+          wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 4, 4, " + format + ", new Uint8Array(" + expectedByteLength(4, 4, format) + "))");
+        }
       }
     }
 
-    if (isPositive) {
-      shouldBe("WEBGL_compressed_texture_etc.COMPRESSED_R11_EAC", "0x9270");
-      shouldBe("WEBGL_compressed_texture_etc.COMPRESSED_SIGNED_R11_EAC", "0x9271");
-      shouldBe("WEBGL_compressed_texture_etc.COMPRESSED_RG11_EAC", "0x9272");
-      shouldBe("WEBGL_compressed_texture_etc.COMPRESSED_SIGNED_RG11_EAC", "0x9273");
-      shouldBe("WEBGL_compressed_texture_etc.COMPRESSED_RGB8_ETC2", "0x9274");
-      shouldBe("WEBGL_compressed_texture_etc.COMPRESSED_SRGB8_ETC2", "0x9275");
-      shouldBe("WEBGL_compressed_texture_etc.COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2", "0x9276");
-      shouldBe("WEBGL_compressed_texture_etc.COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2", "0x9277");
-      shouldBe("WEBGL_compressed_texture_etc.COMPRESSED_RGBA8_ETC2_EAC", "0x9278");
-      shouldBe("WEBGL_compressed_texture_etc.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC", "0x9279");
-    }
-
-    wtu.shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGB_S3TC_DXT1_EXT, 4, 4, 0, new Uint8Array(8))");
-    wtu.shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGBA_S3TC_DXT1_EXT, 4, 4, 0, new Uint8Array(8))");
-    wtu.shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGBA_S3TC_DXT5_EXT, 4, 4, 0, new Uint8Array(16))");
-    wtu.shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, ETC1_RGB8_OES, 4, 4, 0, new Uint8Array(8))");
-    wtu.shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGB_PVRTC_4BPPV1_IMG, 8, 8, 0, new Uint8Array(8))");
-    wtu.shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, 8, 8, 0, new Uint8Array(8))");
-    wtu.shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.compressedTexImage2D(gl.TEXTURE_3D, 0, COMPRESSED_R11_EAC, 4, 4, 0, new Uint8Array(8))");
-    wtu.shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.compressedTexSubImage2D(gl.TEXTURE_3D, 0, 0, 0, 4, 4, COMPRESSED_R11_EAC, new Uint8Array(8))");
-
-    var expected = isPositive ? gl.NO_ERROR : gl.INVALID_ENUM;
-    var expectedSub = isPositive ? gl.NO_ERROR : [gl.INVALID_ENUM, gl.INVALID_OPERATION];
-    wtu.shouldGenerateGLError(gl, expected, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_R11_EAC, 4, 4, 0, new Uint8Array(8))");
-    wtu.shouldGenerateGLError(gl, expectedSub, "gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 4, 4, COMPRESSED_R11_EAC, new Uint8Array(8))");
-    wtu.shouldGenerateGLError(gl, expected, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_SIGNED_R11_EAC, 4, 4, 0, new Uint8Array(8))");
-    wtu.shouldGenerateGLError(gl, expected, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RG11_EAC, 4, 4, 0, new Uint8Array(16))");
-    wtu.shouldGenerateGLError(gl, expected, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_SIGNED_RG11_EAC, 4, 4, 0, new Uint8Array(16))");
-    wtu.shouldGenerateGLError(gl, expected, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGB8_ETC2, 4, 4, 0, new Uint8Array(8))");
-    wtu.shouldGenerateGLError(gl, expected, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_SRGB8_ETC2, 4, 4, 0, new Uint8Array(8))");
-    wtu.shouldGenerateGLError(gl, expected, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2, 4, 4, 0, new Uint8Array(8))");
-    wtu.shouldGenerateGLError(gl, expected, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2, 4, 4, 0, new Uint8Array(8))");
-    wtu.shouldGenerateGLError(gl, expected, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGBA8_ETC2_EAC, 4, 4, 0, new Uint8Array(16))");
-    wtu.shouldGenerateGLError(gl, expected, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_SRGB8_ALPHA8_ETC2_EAC, 4, 4, 0, new Uint8Array(16))");
-
-    wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "formats = gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS)");
-    shouldBeNonNull("formats");
-    shouldBe("formats.length", isPositive ? "10" : "0");
+    var tex2 = gl.createTexture();
+    gl.bindTexture(gl.TEXTURE_2D, tex2);
 
     debug("");
     if (contextVersion >= 2) {
       var expectedError = isPositive ? gl.INVALID_OPERATION: [gl.INVALID_ENUM, gl.INVALID_OPERATION];
       // `null` coerces into `0` for the PBO entrypoint, yielding INVALID_OP due to no PBO bound.
-      wtu.shouldGenerateGLError(gl, expectedError, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_R11_EAC, 4, 4, 0, 0, null)");
-      wtu.shouldGenerateGLError(gl, expectedError, "gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 0, 0, COMPRESSED_R11_EAC, 0, null)");
-      wtu.shouldGenerateGLError(gl, expectedError, "gl.compressedTexImage3D(gl.TEXTURE_2D_ARRAY, 0, COMPRESSED_R11_EAC, 4, 4, 4, 0, 0, null)");
-      wtu.shouldGenerateGLError(gl, expectedError, "gl.compressedTexSubImage3D(gl.TEXTURE_2D_ARRAY, 0, 0, 0, 0, 0, 0, 0, COMPRESSED_R11_EAC, 0, null)");
+      wtu.shouldGenerateGLError(gl, expectedError, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, validFormats.COMPRESSED_R11_EAC, 4, 4, 0, 0, null)");
+      wtu.shouldGenerateGLError(gl, expectedError, "gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 0, 0, validFormats.COMPRESSED_R11_EAC, 0, null)");
+      wtu.shouldGenerateGLError(gl, expectedError, "gl.compressedTexImage3D(gl.TEXTURE_2D_ARRAY, 0, validFormats.COMPRESSED_R11_EAC, 4, 4, 4, 0, 0, null)");
+      wtu.shouldGenerateGLError(gl, expectedError, "gl.compressedTexSubImage3D(gl.TEXTURE_2D_ARRAY, 0, 0, 0, 0, 0, 0, 0, validFormats.COMPRESSED_R11_EAC, 0, null)");
     } else {
-      shouldThrow("gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_R11_EAC, 4, 4, 0, null)");
-      shouldThrow("gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 0, 0, COMPRESSED_R11_EAC, null)");
-      shouldThrow("gl.compressedTexImage3D(gl.TEXTURE_2D_ARRAY, 0, COMPRESSED_R11_EAC, 4, 4, 4, 0, null)");
-      shouldThrow("gl.compressedTexSubImage3D(gl.TEXTURE_2D_ARRAY, 0, 0, 0, 0, 0, 0, 0, COMPRESSED_R11_EAC, null)");
+      shouldThrow("gl.compressedTexImage2D(gl.TEXTURE_2D, 0, validFormats.COMPRESSED_R11_EAC, 4, 4, 0, null)");
+      shouldThrow("gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 0, 0, validFormats.COMPRESSED_R11_EAC, null)");
+      shouldThrow("gl.compressedTexImage3D(gl.TEXTURE_2D_ARRAY, 0, validFormats.COMPRESSED_R11_EAC, 4, 4, 4, 0, null)");
+      shouldThrow("gl.compressedTexSubImage3D(gl.TEXTURE_2D_ARRAY, 0, 0, 0, 0, 0, 0, 0, validFormats.COMPRESSED_R11_EAC, null)");
     }
   }
 }
 
 runTest();
 
 var successfullyParsed = true;
 </script>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-etc1.html
@@ -0,0 +1,95 @@
+<!--
+
+/*
+** Copyright (c) 2018 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a
+** copy of this software and/or associated documentation files (the
+** "Materials"), to deal in the Materials without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Materials, and to
+** permit persons to whom the Materials are furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be included
+** in all copies or substantial portions of the Materials.
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+*/
+
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL WEBGL_compressed_texture_etc1 Conformance Tests</title>
+<LINK rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+<script src="../../js/tests/compressed-texture-utils.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+description("This test verifies the functionality of the WEBGL_compressed_texture_etc1 extension, if it is available.");
+
+debug("");
+
+var validFormats = {
+  COMPRESSED_RGB_ETC1_WEBGL: 0x8D64,
+};
+
+function expectedByteLength(width, height, format) {
+  return Math.floor((width + 3) / 4) * Math.floor((height + 3) / 4) * 8;
+}
+
+function getBlockDimensions(format) {
+  return {width: 4, height: 4};
+}
+
+var wtu = WebGLTestUtils;
+var ctu = CompressedTextureUtils;
+var contextVersion = wtu.getDefault3DContextVersion();
+var gl = wtu.create3DContext();
+var WEBGL_compressed_texture_etc1;
+
+var formats = null;
+
+function runTest() {
+  if (!gl) {
+    testFailed("context does not exist");
+  } else {
+    testPassed("context exists");
+
+    ctu.testCompressedFormatsUnavailableWhenExtensionDisabled(gl, validFormats, expectedByteLength, 4);
+
+    WEBGL_compressed_texture_etc1 = gl.getExtension("WEBGL_compressed_texture_etc1");
+
+    wtu.runExtensionSupportedTest(gl, "WEBGL_compressed_texture_etc1", WEBGL_compressed_texture_etc1 !== null);
+
+    if (WEBGL_compressed_texture_etc1 !== null) {
+      // Test that enum values are listed correctly in supported formats and in the extension object.
+      ctu.testCompressedFormatsListed(gl, validFormats);
+      ctu.testCorrectEnumValuesInExt(WEBGL_compressed_texture_etc1, validFormats);
+      // Test that texture upload buffer size is validated correctly.
+      ctu.testFormatRestrictionsOnBufferSize(gl, validFormats, expectedByteLength, getBlockDimensions);
+    }
+  }
+}
+
+runTest();
+
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
--- a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-s3tc-srgb.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-s3tc-srgb.html
@@ -27,16 +27,17 @@
 
 <!DOCTYPE html>
 <html>
 <head>
 <meta charset="utf-8">
 <link rel="stylesheet" href="../../resources/js-test-style.css"/>
 <script src="../../js/js-test-pre.js"></script>
 <script src="../../js/webgl-test-utils.js"></script>
+<script src="../../js/tests/compressed-texture-utils.js"></script>
 <title>WebGL WEBGL_compressed_texture_s3tc_srgb Conformance Tests</title>
 <style>
 img {
  border: 1px solid black;
  margin-right: 1em;
 }
 .testimages {
 }
@@ -100,16 +101,17 @@ var img_8x8_rgba_dxt1 = new Uint8Array([
 var img_8x8_rgba_dxt3 = new Uint8Array([
     0xf7,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x08,0xba,0xe8,0x45,0x44,0x45,0x40,0x55,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xe8,0xbd,0x17,0x42,0x44,0x45,0x40,0x55,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x17,0xba,0xe8,0x45,0x11,0x10,0x15,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf7,0x45,0x17,0x42,0x11,0x10,0x15,0x00,
 ]);
 var img_8x8_rgba_dxt5 = new Uint8Array([
     0xff,0x7f,0x01,0x00,0x00,0x00,0x00,0x00,0x08,0xba,0xe8,0x45,0x44,0x45,0x40,0x55,0xff,0xff,0x49,0x92,0x24,0x49,0x92,0x24,0xe8,0xbd,0x17,0x42,0x44,0x45,0x40,0x55,0xff,0xff,0x49,0x92,0x24,0x49,0x92,0x24,0x17,0xba,0xe8,0x45,0x11,0x10,0x15,0x00,0xff,0xff,0x49,0x92,0x24,0x49,0x92,0x24,0xf7,0x45,0x17,0x42,0x11,0x10,0x15,0x00,
 ]);
 
 var wtu = WebGLTestUtils;
+var ctu = CompressedTextureUtils;
 var contextVersion = wtu.getDefault3DContextVersion();
 var canvas = document.getElementById("canvas");
 var gl = wtu.create3DContext(canvas, {antialias: false});
 var program = wtu.setupTexturedQuad(gl);
 var ext = null;
 var vao = null;
 var validFormats = {
     COMPRESSED_SRGB_S3TC_DXT1_EXT       : 0x8C4C,
@@ -120,94 +122,50 @@ var validFormats = {
 var name;
 var supportedFormats;
 
 if (!gl) {
     testFailed("WebGL context does not exist");
 } else {
     testPassed("WebGL context exists");
 
-    // Run tests with extension disabled
-    runTestDisabled();
+    ctu.testCompressedFormatsUnavailableWhenExtensionDisabled(gl, validFormats, expectedByteLength, 4);
 
     // Query the extension and store globally so shouldBe can access it
     ext = wtu.getExtensionWithKnownPrefixes(gl, "WEBGL_compressed_texture_s3tc_srgb");
     if (!ext) {
         testPassed("No WEBGL_compressed_texture_s3tc_srgb support -- this is legal");
-        runSupportedTest(false);
+        wtu.runExtensionSupportedTest(gl, "WEBGL_compressed_texture_s3tc_srgb", false);
     } else {
         testPassed("Successfully enabled WEBGL_compressed_texture_s3tc_srgb extension");
 
-        runSupportedTest(true);
+        wtu.runExtensionSupportedTest(gl, "WEBGL_compressed_texture_s3tc_srgb", true);
         runTestExtension();
     }
 }
 
-function runSupportedTest(extensionEnabled) {
-    var name = wtu.getSupportedExtensionWithKnownPrefixes(gl, "WEBGL_compressed_texture_s3tc_srgb");
-    if (name !== undefined) {
-        if (extensionEnabled) {
-            testPassed("WEBGL_compressed_texture_s3tc_srgb listed as supported and getExtension succeeded");
-        } else {
-            testFailed("WEBGL_compressed_texture_s3tc_srgb listed as supported but getExtension failed");
-        }
-    } else {
-        if (extensionEnabled) {
-            testFailed("WEBGL_compressed_texture_s3tc_srgb not listed as supported but getExtension succeeded");
-        } else {
-            testPassed("WEBGL_compressed_texture_s3tc_srgb not listed as supported and getExtension failed -- this is legal");
-        }
+function expectedByteLength(width, height, format) {
+    if (format == validFormats.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT || format == validFormats.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT) {
+        return Math.floor((width + 3) / 4) * Math.floor((height + 3) / 4) * 16;
     }
+    return Math.floor((width + 3) / 4) * Math.floor((height + 3) / 4) * 8;
 }
 
-
-function runTestDisabled() {
-    debug("Testing binding enum with extension disabled");
-
-    supportedFormats = gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS);
-    shouldBe("supportedFormats", "[]");
-}
-
-function formatExists(format, supportedFormats) {
-    for (var ii = 0; ii < supportedFormats.length; ++ii) {
-        if (format == supportedFormats[ii]) {
-            testPassed("supported format " + formatToString(format) + " is exists");
-            return;
-        }
-    }
-    testFailed("supported format " + formatToString(format) + " does not exist");
-}
-
-function formatToString(format) {
-    for (var p in ext) {
-        if (ext[p] == format) {
-            return p;
-        }
-    }
-    return "0x" + format.toString(16);
+function getBlockDimensions(format) {
+    return {width: 4, height: 4};
 }
 
 function runTestExtension() {
     debug("Testing WEBGL_compressed_texture_s3tc_srgb");
 
-    // check that all format enums exist.
-    for (name in validFormats) {
-        var expected = "0x" + validFormats[name].toString(16);
-        var actual = "ext['" + name + "']";
-        shouldBe(actual, expected);
-    }
-
-    supportedFormats = gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS);
-    // There should be exactly 4 formats for both WebGL 1.0 and WebGL 2.0.
-    shouldBe("supportedFormats.length", "4");
-
-    // check that all 4 formats exist
-    for (var name in validFormats.length) {
-        formatExists(validFormats[name], supportedFormats);
-    }
+    // Test that enum values are listed correctly in supported formats and in the extension object.
+    ctu.testCompressedFormatsListed(gl, validFormats);
+    ctu.testCorrectEnumValuesInExt(ext, validFormats);
+    // Test that texture upload buffer size is validated correctly.
+    ctu.testFormatRestrictionsOnBufferSize(gl, validFormats, expectedByteLength, getBlockDimensions);
 
     // Test each format
     testDXT1_SRGB();
     testDXT1_SRGB_ALPHA();
     testDXT3_SRGB_ALPHA();
     testDXT5_SRGB_ALPHA();
 }
 
@@ -461,17 +419,17 @@ function testDXTTexture(test, useTexStor
     var height = test.height;
     var format = test.format;
 
     var uncompressedData = uncompressDXTSRGB(width, height, data, format);
 
     canvas.width = width;
     canvas.height = height;
     gl.viewport(0, 0, width, height);
-    debug("testing " + formatToString(format) + " " + width + "x" + height +
+    debug("testing " + ctu.formatToString(ext, format) + " " + width + "x" + height +
           (useTexStorage ? " via texStorage2D" : " via compressedTexImage2D"));
 
     var tex = gl.createTexture();
     gl.bindTexture(gl.TEXTURE_2D, tex);
     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
@@ -607,45 +565,16 @@ function testDXTTexture(test, useTexStor
             gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
             wtu.clearAndDrawUnitQuad(gl);
             wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawing unit quad");
             compareRect(width, height, test.channels, width, height, uncompressedData, data, format, "LINEAR");
         }
     }
 }
 
-function insertImg(element, caption, img) {
-    var div = document.createElement("div");
-    div.appendChild(img);
-    var label = document.createElement("div");
-    label.appendChild(document.createTextNode(caption));
-    div.appendChild(label);
-    element.appendChild(div);
-}
-
-function makeImage(imageWidth, imageHeight, dataWidth, data, alpha) {
-    var scale = 8;
-    var c = document.createElement("canvas");
-    c.width = imageWidth * scale;
-    c.height = imageHeight * scale;
-    var ctx = c.getContext("2d");
-    for (var yy = 0; yy < imageHeight; ++yy) {
-        for (var xx = 0; xx < imageWidth; ++xx) {
-            var offset = (yy * dataWidth + xx) * 4;
-            ctx.fillStyle = "rgba(" +
-                    data[offset + 0] + "," +
-                    data[offset + 1] + "," +
-                    data[offset + 2] + "," +
-                    (alpha ? data[offset + 3] / 255 : 1) + ")";
-            ctx.fillRect(xx * scale, yy * scale, scale, scale);
-        }
-    }
-    return wtu.makeImageFromCanvas(c);
-}
-
 // See EXT_texture_sRGB, Section 3.8.x, sRGB Texture Color Conversion.
 function sRGBChannelToLinear(value) {
     value = value / 255;
     if (value <= 0.04045) {
         value = value / 12.92;
     } else {
         value = Math.pow((value + 0.055) / 1.055, 2.4);
     }
@@ -658,20 +587,20 @@ function compareRect(
         testData, testFormat, filteringMode) {
     var actual = new Uint8Array(actualWidth * actualHeight * 4);
     gl.readPixels(
             0, 0, actualWidth, actualHeight, gl.RGBA, gl.UNSIGNED_BYTE, actual);
     wtu.glErrorShouldBe(gl, gl.NO_ERROR, "reading back pixels");
 
     var div = document.createElement("div");
     div.className = "testimages";
-    insertImg(div, "expected", makeImage(
+    ctu.insertCaptionedImg(div, "expected", ctu.makeScaledImage(
             actualWidth, actualHeight, dataWidth, expectedData,
             actualChannels == 4));
-    insertImg(div, "actual", makeImage(
+    ctu.insertCaptionedImg(div, "actual", ctu.makeScaledImage(
             actualWidth, actualHeight, actualWidth, actual,
             actualChannels == 4));
     div.appendChild(document.createElement('br'));
     document.getElementById("console").appendChild(div);
 
     // This threshold is required because the values we get back from the
     // implementation don't exactly match our javascript implementation.
     // This is probably due to allowances in the way sRGB interacts with S3TC.
--- a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-s3tc.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-s3tc.html
@@ -27,16 +27,17 @@
 
 <!DOCTYPE html>
 <html>
 <head>
 <meta charset="utf-8">
 <link rel="stylesheet" href="../../resources/js-test-style.css"/>
 <script src="../../js/js-test-pre.js"></script>
 <script src="../../js/webgl-test-utils.js"></script>
+<script src="../../js/tests/compressed-texture-utils.js"></script>
 <title>WebGL WEBGL_compressed_texture_s3tc Conformance Tests</title>
 <style>
 img {
  border: 1px solid black;
  margin-right: 1em;
 }
 .testimages {
 }
@@ -88,16 +89,17 @@ var img_8x8_rgba_dxt1 = new Uint8Array([
 var img_8x8_rgba_dxt3 = new Uint8Array([
     0xf6,0xff,0xf6,0xff,0xff,0xff,0xff,0xff,0x00,0xf8,0xe0,0x07,0x44,0x45,0x40,0x55,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xe0,0xff,0x1f,0x00,0x44,0x45,0x40,0x55,0xff,0xff,0xff,0xff,0xf6,0xff,0xf6,0xff,0x1f,0xf8,0xe0,0x07,0x11,0x10,0x15,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x07,0x1f,0x00,0x11,0x10,0x15,0x00,
 ]);
 var img_8x8_rgba_dxt5 = new Uint8Array([
     0xff,0x69,0x01,0x10,0x00,0x00,0x00,0x00,0x00,0xf8,0xe0,0x07,0x44,0x45,0x40,0x55,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xe0,0xff,0x1f,0x00,0x44,0x45,0x40,0x55,0xff,0x69,0x00,0x00,0x00,0x01,0x10,0x00,0x1f,0xf8,0xe0,0x07,0x11,0x10,0x15,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x07,0x1f,0x00,0x11,0x10,0x15,0x00,
 ]);
 
 var wtu = WebGLTestUtils;
+var ctu = CompressedTextureUtils;
 var contextVersion = wtu.getDefault3DContextVersion();
 var canvas = document.getElementById("canvas");
 var gl = wtu.create3DContext(canvas, {antialias: false});
 var program = wtu.setupTexturedQuad(gl);
 var ext = null;
 var vao = null;
 var validFormats = {
     COMPRESSED_RGB_S3TC_DXT1_EXT        : 0x83F0,
@@ -109,93 +111,51 @@ var name;
 var supportedFormats;
 
 if (!gl) {
     testFailed("WebGL context does not exist");
 } else {
     testPassed("WebGL context exists");
 
     // Run tests with extension disabled
-    runTestDisabled();
+    ctu.testCompressedFormatsUnavailableWhenExtensionDisabled(gl, validFormats, expectedByteLength, 4);
 
     // Query the extension and store globally so shouldBe can access it
     ext = wtu.getExtensionWithKnownPrefixes(gl, "WEBGL_compressed_texture_s3tc");
     if (!ext) {
         testPassed("No WEBGL_compressed_texture_s3tc support -- this is legal");
-        runSupportedTest(false);
+        wtu.runExtensionSupportedTest(gl, "WEBGL_compressed_texture_s3tc", false);
     } else {
         testPassed("Successfully enabled WEBGL_compressed_texture_s3tc extension");
 
-        runSupportedTest(true);
+        wtu.runExtensionSupportedTest(gl, "WEBGL_compressed_texture_s3tc", true);
         runTestExtension();
     }
 }
 
-function runSupportedTest(extensionEnabled) {
-    var name = wtu.getSupportedExtensionWithKnownPrefixes(gl, "WEBGL_compressed_texture_s3tc");
-    if (name !== undefined) {
-        if (extensionEnabled) {
-            testPassed("WEBGL_compressed_texture_s3tc listed as supported and getExtension succeeded");
-        } else {
-            testFailed("WEBGL_compressed_texture_s3tc listed as supported but getExtension failed");
-        }
-    } else {
-        if (extensionEnabled) {
-            testFailed("WEBGL_compressed_texture_s3tc not listed as supported but getExtension succeeded");
-        } else {
-            testPassed("WEBGL_compressed_texture_s3tc not listed as supported and getExtension failed -- this is legal");
-        }
+function expectedByteLength(width, height, format) {
+    if (format == validFormats.COMPRESSED_RGBA_S3TC_DXT3_EXT || format == validFormats.COMPRESSED_RGBA_S3TC_DXT5_EXT) {
+        return Math.floor((width + 3) / 4) * Math.floor((height + 3) / 4) * 16;
     }
+    return Math.floor((width + 3) / 4) * Math.floor((height + 3) / 4) * 8;
 }
 
-
-function runTestDisabled() {
-    debug("Testing binding enum with extension disabled");
-
-    supportedFormats = gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS);
-    shouldBe("supportedFormats", "[]");
-}
-
-function formatExists(format, supportedFormats) {
-    for (var ii = 0; ii < supportedFormats.length; ++ii) {
-        if (format == supportedFormats[ii]) {
-            testPassed("supported format " + formatToString(format) + " is exists");
-            return;
-        }
-    }
-    testFailed("supported format " + formatToString(format) + " does not exist");
-}
-
-function formatToString(format) {
-    for (var p in ext) {
-        if (ext[p] == format) {
-            return p;
-        }
-    }
-    return "0x" + format.toString(16);
+function getBlockDimensions(format) {
+    return {width: 4, height: 4};
 }
 
 function runTestExtension() {
+    debug("");
     debug("Testing WEBGL_compressed_texture_s3tc");
 
-    // check that all format enums exist.
-    for (name in validFormats) {
-        var expected = "0x" + validFormats[name].toString(16);
-        var actual = "ext['" + name + "']";
-        shouldBe(actual, expected);
-    }
-
-    supportedFormats = gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS);
-    // There should be exactly 4 formats for both WebGL 1.0 and WebGL 2.0.
-    shouldBe("supportedFormats.length", "4");
-
-    // check that all 4 formats exist
-    for (var name in validFormats.length) {
-        formatExists(validFormats[name], supportedFormats);
-    }
+    // Test that enum values are listed correctly in supported formats and in the extension object.
+    ctu.testCompressedFormatsListed(gl, validFormats);
+    ctu.testCorrectEnumValuesInExt(ext, validFormats);
+    // Test that texture upload buffer size is validated correctly.
+    ctu.testFormatRestrictionsOnBufferSize(gl, validFormats, expectedByteLength, getBlockDimensions);
 
     // Test each format
     testDXT1_RGB();
     testDXT1_RGBA();
     testDXT3_RGBA();
     testDXT5_RGBA();
 }
 
@@ -482,17 +442,17 @@ function testDXTTexture(test, useTexStor
     var height = test.height;
     var format = test.format;
 
     var uncompressedData = uncompressDXT(width, height, data, format);
 
     canvas.width = width;
     canvas.height = height;
     gl.viewport(0, 0, width, height);
-    debug("testing " + formatToString(format) + " " + width + "x" + height +
+    debug("testing " + ctu.formatToString(ext, format) + " " + width + "x" + height +
           (useTexStorage ? " via texStorage2D" : " via compressedTexImage2D"));
 
     var tex = gl.createTexture();
     gl.bindTexture(gl.TEXTURE_2D, tex);
     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
@@ -547,25 +507,16 @@ function testDXTTexture(test, useTexStor
     wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawing unit quad 2");
     compareRect(width, height, test.channels, uncompressedData, "LINEAR");
 
     if (!useTexStorage) {
         // It's not allowed to redefine textures defined via texStorage2D.
         gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height, 1, data);
         wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "non 0 border");
 
-        gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width + 4, height, 0, data);
-        wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions");
-        gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height + 4, 0, data);
-        wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions");
-        gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width - 4, height, 0, data);
-        wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions");
-        gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height - 4, 0, data);
-        wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions");
-
         gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width - 1, height, 0, data);
         wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions");
         gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width - 2, height, 0, data);
         wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions");
         gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height - 1, 0, data);
         wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions");
         gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height - 2, 0, data);
         wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions");
@@ -653,54 +604,25 @@ function testDXTTexture(test, useTexStor
             gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
             wtu.clearAndDrawUnitQuad(gl);
             wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawing unit quad");
             compareRect(width, height, test.channels, uncompressedData, "LINEAR");
         }
     }
 }
 
-function insertImg(element, caption, img) {
-    var div = document.createElement("div");
-    div.appendChild(img);
-    var label = document.createElement("div");
-    label.appendChild(document.createTextNode(caption));
-    div.appendChild(label);
-    element.appendChild(div);
-}
-
-function makeImage(imageWidth, imageHeight, data, alpha) {
-    var scale = 8;
-    var c = document.createElement("canvas");
-    c.width = imageWidth * scale;
-    c.height = imageHeight * scale;
-    var ctx = c.getContext("2d");
-    for (var yy = 0; yy < imageHeight; ++yy) {
-        for (var xx = 0; xx < imageWidth; ++xx) {
-            var offset = (yy * imageWidth + xx) * 4;
-            ctx.fillStyle = "rgba(" +
-                    data[offset + 0] + "," +
-                    data[offset + 1] + "," +
-                    data[offset + 2] + "," +
-                    (alpha ? data[offset + 3] / 255 : 1) + ")";
-            ctx.fillRect(xx * scale, yy * scale, scale, scale);
-        }
-    }
-    return wtu.makeImageFromCanvas(c);
-}
-
 function compareRect(width, height, channels, expectedData, filteringMode) {
     var actual = new Uint8Array(width * height * 4);
     gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, actual);
     wtu.glErrorShouldBe(gl, gl.NO_ERROR, "reading back pixels");
 
     var div = document.createElement("div");
     div.className = "testimages";
-    insertImg(div, "expected", makeImage(width, height, expectedData, channels == 4));
-    insertImg(div, "actual", makeImage(width, height, actual, channels == 4));
+    ctu.insertCaptionedImg(div, "expected", ctu.makeScaledImage(width, height, width, expectedData, channels == 4));
+    ctu.insertCaptionedImg(div, "actual", ctu.makeScaledImage(width, height, width, actual, channels == 4));
     div.appendChild(document.createElement('br'));
     document.getElementById("console").appendChild(div);
 
     var failed = false;
     for (var yy = 0; yy < height; ++yy) {
         for (var xx = 0; xx < width; ++xx) {
             var offset = (yy * width + xx) * 4;
             var expected = [
deleted file mode 100644
--- a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-shared-resources.html
+++ /dev/null
@@ -1,861 +0,0 @@
-<!--
-
-/*
-** Copyright (c) 2013 The Khronos Group Inc.
-**
-** Permission is hereby granted, free of charge, to any person obtaining a
-** copy of this software and/or associated documentation files (the
-** "Materials"), to deal in the Materials without restriction, including
-** without limitation the rights to use, copy, modify, merge, publish,
-** distribute, sublicense, and/or sell copies of the Materials, and to
-** permit persons to whom the Materials are furnished to do so, subject to
-** the following conditions:
-**
-** The above copyright notice and this permission notice shall be included
-** in all copies or substantial portions of the Materials.
-**
-** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
-*/
-
--->
-
-<!DOCTYPE html>
-<html>
-<head>
-<meta charset="utf-8">
-<title>WebGL WEBGL_Shared_Resources Conformance Test</title>
-<link rel="stylesheet" href="../../resources/js-test-style.css"/>
-<script src="../../js/js-test-pre.js"></script>
-<script src="../../js/webgl-test-utils.js"></script>
-</head>
-<body>
-<script id="vshader" type="x-shader/x-vertex">
-attribute vec4 a_position;
-void main() {
-  gl_Position = a_position;
-}
-</script>
-<script id="fshader" type="x-shader/x-fragment">
-precision mediump float;
-uniform vec4 u_color;
-void main() {
-  gl_FragColor = u_color;
-}
-</script>
-<style>
-canvas {
-    border: 1px solid black;
-}
-</style>
-<canvas id="canvas1" width="64" height="64"> </canvas>
-<canvas id="canvas2" width="64" height="64"> </canvas>
-<div id="description"></div>
-<div id="console"></div>
-<script>
-"use strict";
-description();
-
-var wtu = WebGLTestUtils;
-var shouldGenerateGLError = wtu.shouldGenerateGLError;
-var canvas1 = document.getElementById("canvas1");
-var gl = wtu.create3DContext(canvas1);
-var gl2;
-var ext = null;
-var ext2;
-var ext3;
-var buf;
-var elementBuf;
-var tex;
-var tex3;
-var rb;
-var fb;
-var id;
-var resource;
-var shader;
-var program;
-var uniformLocation;
-var acquiredFlag;
-var shaderProgram;  // acquired progam (never released) used for shader testing
-var programShader;  // acquired shader (never released) used for program testing
-
-if (!gl) {
-  testFailed("context does not exist");
-} else {
-  testPassed("context exists");
-
-  // Run tests with extension disabled
-  runTestDisabled();
-
-  // Query the extension and store globally so shouldBe can access it
-  ext = wtu.getExtensionWithKnownPrefixes(gl, "WEBGL_shared_resources");
-  if (!ext) {
-      testPassed("No WEBGL_shared_resources support -- this is legal");
-      runSupportedTest(false);
-      finishTest();
-  } else {
-      testPassed("Successfully enabled WEBGL_shared_resources extension");
-
-      runSupportedTest(true);
-      runTestExtension();
-  }
-}
-
-function runSupportedTest(extensionEnabled) {
-    var name = wtu.getSupportedExtensionWithKnownPrefixes(gl, "WEBGL_shared_resources");
-    if (name !== undefined) {
-        if (extensionEnabled) {
-            testPassed("WEBGL_shared_resources listed as supported and getExtension succeeded");
-        } else {
-            testFailed("WEBGL_shared_resources listed as supported but getExtension failed");
-        }
-    } else {
-        if (extensionEnabled) {
-            testFailed("WEBGL_shared_resources not listed as supported but getExtension succeeded");
-        } else {
-            testPassed("WEBGL_shared_resources not listed as supported and getExtension failed -- this is legal");
-        }
-    }
-}
-
-function runTestDisabled() {
-    // There is no functionality accessable with this extension disabled.
-}
-
-function makeFailCallback(msg) {
-  return function() {
-    testFailed(msg);
-  }
-};
-
-
-function runTestExtension() {
-  var canvas2 = document.getElementById("canvas2");
-  gl2 = wtu.create3DContext(canvas2, { group: ext.group });
-  ext2 = wtu.getExtensionWithKnownPrefixes(gl2, "WEBGL_shared_resources");
-
-  // Runs an array of functions. Expects each function takes a callback
-  // it will call when finished.
-  var runSequence = function(steps) {
-    var stepNdx = 0;
-    var runNextStep = function() {
-      if (stepNdx < steps.length) {
-        steps[stepNdx++](runNextStep);
-      }
-    };
-    runNextStep();
-  };
-
-  var bufferTests = {
-    resourceType: "buffer",
-
-    setupFunction: function() {
-      buf = gl.createBuffer();
-      gl.bindBuffer(gl.ARRAY_BUFFER, buf);
-      gl.bufferData(gl.ARRAY_BUFFER, 16, gl.STATIC_DRAW);
-      return buf;
-    },
-
-    bindFunction: function(buf) {
-      gl.bindBuffer(gl.ARRAY_BUFFER, buf);
-    },
-
-    implicitBindFunctions: function(expectedError) {
-      shouldGenerateGLError(gl, expectedError, "gl.vertexAttribPointer(0, 4, gl.FLOAT, false, 0, 0)");
-    },
-
-    modificationFunctions: function(expectedError) {
-      shouldGenerateGLError(gl, expectedError, "gl.bufferData(gl.ARRAY_BUFFER, 16, gl.STATIC_DRAW)");
-      shouldGenerateGLError(gl, expectedError, "gl.bufferSubData(gl.ARRAY_BUFFER, 0, new Uint8Array(4))");
-    },
-
-    queryFunctions: function(expectedError) {
-      shouldGenerateGLError(gl, expectedError, "gl.getBufferParameter(gl.ARRAY_BUFFER, gl.BUFFER_SIZE)");
-    },
-  };
-
-  var programTests = {
-    resourceType: "program",
-
-    setupFunction: function() {
-      // We need a valid a program with valid shaders to share because the only way to 'bind'
-      // a program is to call gl.useProgram and you can't call gl.useProgram on an invalid program.
-      program = wtu.setupProgram(gl, ["vshader", "fshader"]);
-      programShader = gl.getAttachedShaders(program)[0];
-      uniformLocation = gl.getUniformLocation(program, "u_color");
-      return program;
-    },
-
-    bindFunction: function(program) {
-      gl.useProgram(program);
-    },
-
-    implicitBindFunctions: function(expectedError) {
-    },
-
-    modificationFunctions: function(expectedError) {
-      if (expectedError == gl.NO_ERROR) {
-        // Need to get a new location because we may have re-linked.
-        uniformLocation = gl.getUniformLocation(program, 'u_color');
-      }
-      shouldGenerateGLError(gl, expectedError, "gl.uniform4f(uniformLocation, 0, 1, 2, 3)");
-      shouldGenerateGLError(gl, expectedError, "gl.detachShader(program, programShader)");
-      shouldGenerateGLError(gl, expectedError, "gl.attachShader(program, programShader)");
-      shouldGenerateGLError(gl, expectedError, "gl.bindAttribLocation(program, 0, 'foo')");
-      shouldGenerateGLError(gl, expectedError, "gl.linkProgram(program)");
-    },
-
-    queryFunctions: function(expectedError) {
-      shouldGenerateGLError(gl, expectedError, "gl.getProgramParameter(program, gl.LINK_STATUS)");
-      shouldGenerateGLError(gl, expectedError, "gl.getProgramInfoLog(program)");
-      shouldGenerateGLError(gl, expectedError, "gl.getAttachedShaders(program)");
-      shouldGenerateGLError(gl, expectedError, "gl.getAttribLocation(program, 'foo')");
-      shouldGenerateGLError(gl, expectedError, "gl.getUniformLocation(program, 'foo')");
-      shouldGenerateGLError(gl, expectedError, "gl.getActiveAttrib(program, 0)");
-      shouldGenerateGLError(gl, expectedError, "gl.getActiveUniform(program, 0)");
-      shouldGenerateGLError(gl, expectedError, "gl.getUniform(program, uniformLocation)");
-    },
-  };
-
-  var renderbufferTests = {
-    resourceType: "renderbuffer",
-
-    setupFunction: function() {
-      rb = gl.createRenderbuffer();
-      gl.bindRenderbuffer(gl.RENDERBUFFER, rb);
-      gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 16, 16);
-      fb = gl.createFramebuffer();
-      return rb;
-    },
-
-    bindFunction: function(rb) {
-      gl.bindRenderbuffer(gl.RENDERBUFFER, rb);
-    },
-
-    implicitBindFunctions: function(expectedError) {
-       gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
-       shouldGenerateGLError(gl, expectedError, "gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb)");
-       gl.bindFramebuffer(gl.FRAMEBUFFER, null);
-    },
-
-    modificationFunctions: function(expectedError) {
-      shouldGenerateGLError(gl, expectedError, "gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 16, 16)");
-    },
-
-    queryFunctions: function(expectedError) {
-      shouldGenerateGLError(gl, expectedError, "gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_WIDTH)");
-    },
-  };
-
-
-  var shaderTests = {
-    resourceType: "shader",
-
-    setupFunction: function() {
-      var shaderSource = "Hello World";
-      shader = gl.createShader(gl.VERTEX_SHADER);
-      gl.shaderSource(shader, shaderSource);
-      shaderProgram = gl.createProgram();
-      gl.attachShader(shaderProgram, shader);
-      return shader;
-    },
-
-    bindFunction: function(shader) {
-      gl.detachShader(shaderProgram, shader);  // you can't attach if a shader is already attached.
-      gl.attachShader(shaderProgram, shader);
-    },
-
-    implicitBindFunctions: function(expectedError) {
-    },
-
-    modificationFunctions: function(expectedError) {
-      shouldGenerateGLError(gl, expectedError, "gl.shaderSource(shader, 'foo')");
-      shouldGenerateGLError(gl, expectedError, "gl.compileShader(shader)");
-    },
-
-    queryFunctions: function(expectedError) {
-      shouldGenerateGLError(gl, expectedError, "gl.getShaderParameter(shader, gl.COMPILE_STATUS)");
-      shouldGenerateGLError(gl, expectedError, "gl.getShaderInfoLog(shader)");
-    },
-  };
-
-  var textureTests = {
-    resourceType: "texture",
-
-    setupFunction: function() {
-      tex = gl.createTexture();
-      gl.bindTexture(gl.TEXTURE_2D, tex);
-      gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 16, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
-      return tex;
-    },
-
-    bindFunction: function(tex) {
-      gl.bindTexture(gl.TEXTURE_2D, tex);
-    },
-
-    implicitBindFunctions: function(expectedError) {
-    },
-
-    modificationFunctions: function(expectedError) {
-      shouldGenerateGLError(gl, expectedError, "gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT)");
-      shouldGenerateGLError(gl, expectedError, "gl.generateMipmap(gl.TEXTURE_2D)");
-      shouldGenerateGLError(gl, expectedError, "gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 16, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, null)");
-      shouldGenerateGLError(gl, expectedError, "gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4))");
-      shouldGenerateGLError(gl, expectedError, "gl.copyTexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 0, 0, 16, 16, 0)");
-      shouldGenerateGLError(gl, expectedError, "gl.copyTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 0, 0, 16, 16)");
-      // TODO: Add compressed texture test if extension exists?
-    },
-
-    queryFunctions: function(expectedError) {
-      shouldGenerateGLError(gl, expectedError, "gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S)");
-    },
-  };
-
-  var testResourceWithSingleContext = function(info, callback) {
-    var resourceType = info.resourceType;
-
-    debug("")
-    debug("test " + resourceType);
-    var resource = info.setupFunction();
-    ext.releaseSharedResource(resource);
-
-    debug("");
-    debug("test " + resourceType + " functions can not be called on released " + resourceType);
-    info.modificationFunctions(gl.INVALID_OPERATION);
-    info.implicitBindFunctions(gl.INVALID_OPERATION);
-    info.queryFunctions(gl.INVALID_OPERATION);
-
-    debug("");
-    debug("test acquring " + resourceType);
-    ext.acquireSharedResource(resource, ext.READ_ONLY, function() {
-      debug("");
-      debug("test " + resourceType + " functions can not be called on READ_ONLY acquired " + resourceType + " that has not been bound");
-      info.queryFunctions(gl.INVALID_OPERATION);
-      info.modificationFunctions(gl.INVALID_OPERATION);
-
-      debug("");
-      debug("test query " + resourceType + " functions can be called on READ_ONLY acquired " + resourceType + " that has been bound but not " + resourceType + " modification functions");
-      info.bindFunction(resource);
-      info.queryFunctions(gl.NO_ERROR);
-      info.modificationFunctions(gl.INVALID_OPERATION);
-
-      ext.releaseSharedResource(resource);
-      ext.acquireSharedResource(resource, ext.EXCLUSIVE, function() {
-        debug("");
-        debug("test " + resourceType + " functions can not be called on EXCLUSIVE acquired " + resourceType + " that has not been bound");
-        info.queryFunctions(gl.INVALID_OPERATION);
-        info.modificationFunctions(gl.INVALID_OPERATION);
-
-        debug("");
-        debug("test all " + resourceType + " functions can be called on EXCLUSIVE acquired " + resourceType + " that has been bound.");
-        info.bindFunction(resource)
-        info.queryFunctions(gl.NO_ERROR);
-        info.modificationFunctions(gl.NO_ERROR);
-        callback();
-      });
-    });
-  };
-
-  var makeSingleContextResourceTest = function(info) {
-    return function(callback) {
-      testResourceWithSingleContext(info, callback);
-    };
-  };
-
-  var testCommonResourceFeatures = function(info, callback) {
-    var type = info.resourceType.charAt(0).toUpperCase() + info.resourceType.slice(1);
-    acquiredFlag = false;
-    debug("");
-    debug("test common features of " + type);
-
-    resource = info.setupFunction();
-    info.bindFunction(resource);
-
-    debug("Test a deleted resource can still be acquired.");
-    var checkAcquireAfterDelete = function() {
-      debug("check Acquire After Delete");
-      shouldGenerateGLError(gl, gl.INVALID_OPERATION, "gl.delete" + type + "(resource)");
-// TODO: fix spec then comment this in      shouldGenerateGLError(gl, gl.INVALID_OPERATION, "gl.bind" + type + "(gl." + target + ", resource)");  // You can't bind a deleted resource
-      shouldGenerateGLError(gl, gl.NO_ERROR, "ext.releaseSharedResource(resource)");
-      callback();
-    };
-
-    var checkDeleteExclusive = function() {
-      debug("check delete EXLUSIVE");
-      shouldGenerateGLError(gl, gl.INVALID_OPERATION, "gl.delete" + type + "(resource)");
-      info.bindFunction(resource);
-      wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no error deleting exclusively acquired resource");
-      shouldGenerateGLError(gl, gl.NO_ERROR, "gl.delete" + type + "(resource)");
-      ext.releaseSharedResource(resource);
-      ext.acquireSharedResource(resource, ext.EXCLUSIVE, checkAcquireAfterDelete);
-    };
-
-    var checkDeleteReadOnly = function() {
-      acquiredFlag = true;
-      debug("check delete READ_ONLY");
-      shouldGenerateGLError(gl, gl.INVALID_OPERATION, "gl.delete" + type + "(resource)");
-      info.bindFunction(resource);
-      wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no error bind read only acquired resource");
-      shouldGenerateGLError(gl, gl.INVALID_OPERATION, "gl.delete" + type + "(resource)");  // We're READ_ONLY so this should fail
-      ext.releaseSharedResource(resource);
-      ext.acquireSharedResource(resource, ext.EXCLUSIVE, checkDeleteExclusive);
-    };
-
-    debug("Test you can't have 2 outstanding requests for the same resource.");
-    ext.releaseSharedResource(resource);
-    ext.acquireSharedResource(resource, ext.READ_ONLY, checkDeleteReadOnly);
-    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no error from 1st acquire request");
-    ext.acquireSharedResource(resource, ext.READ_ONLY, checkDeleteReadOnly);
-    wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "should be INVALID_OPERATION from 2nd acquire request");
-
-    debug("Test acquire does not happen immediately on release (must exit current event)");
-    shouldBeTrue("acquiredFlag === false");
-  };
-
-  var makeCommonResourceFeatureTest = function(info) {
-    return function(callback) {
-      testCommonResourceFeatures(info, callback);
-    };
-  };
-
-  // acquire multiple resources in multiple contexts.
-  var acquireMultipleResources = function(extensions, resources, mode, callback) {
-    var numNeeded = resources.length * extensions.length;
-
-    var checkAcquire = function() {
-      --numNeeded;
-      if (numNeeded == 0) {
-        callback();
-      }
-    };
-
-    resources.forEach(function(resource) {
-      extensions.forEach(function(ext) {
-        ext.acquireSharedResource(resource, mode, checkAcquire);
-      });
-    });
-  };
-
-  // release multiple resources in multiple contexts.
-  var releaseMultipleResources = function(extensions, resources) {
-    resources.forEach(function(resource) {
-      extensions.forEach(function(ext) {
-        ext.releaseSharedResource(resource);
-      });
-    });
-  };
-
-  var testRendering = function(callback) {
-    debug("");
-    debug("test rendering");
-    var positionLocation = 0;
-    var texcoordLocation = 1;
-    var program = wtu.setupSimpleTextureProgram(gl, positionLocation, texcoordLocation);
-    var buffers = wtu.setupUnitQuad(gl, positionLocation, texcoordLocation);
-    var rb = gl.createRenderbuffer();
-    var fb = gl.createFramebuffer();
-
-    var elementBuf = gl.createBuffer();
-    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementBuf);
-    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 3, 4, 5]), gl.STATIC_DRAW);
-
-    var width = 16;
-    var height = 16;
-
-    gl.bindRenderbuffer(gl.RENDERBUFFER, rb);
-    gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, width, height);
-
-    var destTex = gl.createTexture();
-    gl.bindTexture(gl.TEXTURE_2D, destTex);
-    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
-    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
-    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
-    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
-    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
-
-    gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
-    gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb);
-    // It's not clear if gl.RGBA4 must be framebuffer complete.
-    var canCheckRenderbuffer = (gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE);
-
-    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, destTex, 0);
-    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "setup");
-    shouldBeTrue("gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE");
-
-    var tex = gl.createTexture();
-    wtu.fillTexture(gl, tex, 1, 1, [0, 255, 0, 255]);
-
-    if (!program) {
-      testFailed("could not link program");
-      callback();
-      return;
-    }
-
-    var releaseAndAcquireResources = function(callback) {
-      var resources = [buffers[0], buffers[1], tex, program, elementBuf];
-      releaseMultipleResources([ext], resources);
-      acquireMultipleResources([ext, ext2], resources, ext.READ_ONLY, callback);
-    };
-
-    var doRenderTest = function(callback) {
-      debug("Test 2 contexts can render with read only resources.");
-
-      shouldGenerateGLError(gl, gl.INVALID_OPERATION, "gl.drawArrays(gl.TRIANGLES, 0, 6)");
-      gl.bindBuffer(gl.ARRAY_BUFFER, buffers[0]);
-      shouldGenerateGLError(gl, gl.INVALID_OPERATION, "gl.drawArrays(gl.TRIANGLES, 0, 6)");
-      gl.bindBuffer(gl.ARRAY_BUFFER, buffers[1]);
-      shouldGenerateGLError(gl, gl.INVALID_OPERATION, "gl.drawArrays(gl.TRIANGLES, 0, 6)");
-      gl.bindTexture(gl.TEXTURE_2D, tex);
-      shouldGenerateGLError(gl, gl.INVALID_OPERATION, "gl.drawArrays(gl.TRIANGLES, 0, 6)");
-      gl.useProgram(program);
-
-      // Render to canvas1;
-      debug("render with context 1");
-      wtu.checkCanvasRect(gl, 0, 0, width, height, [0, 0, 0, 0]);
-      wtu.drawUnitQuad(gl);
-      wtu.checkCanvasRect(gl, 0, 0, width, height, [0, 255, 0, 255]);
-
-      shouldGenerateGLError(gl, gl.INVALID_OPERATION, "gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0)");
-      gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementBuf);
-      gl.clear(gl.COLOR_BUFFER_BIT);
-      shouldGenerateGLError(gl, gl.NO_ERROR, "gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0)");
-      wtu.checkCanvasRect(gl, 0, 0, width, height, [0, 255, 0, 255]);
-
-      // Render to canvas2;
-      debug("render with context 2");
-      gl2.useProgram(program);
-
-      gl2.bindBuffer(gl.ARRAY_BUFFER, buffers[0]);
-      gl2.enableVertexAttribArray(positionLocation);
-      gl2.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
-      gl2.bindBuffer(gl.ARRAY_BUFFER, buffers[1]);
-      gl2.enableVertexAttribArray(texcoordLocation);
-      gl2.vertexAttribPointer(texcoordLocation, 2, gl.FLOAT, false, 0, 0);
-
-      gl2.bindTexture(gl.TEXTURE_2D, tex);
-
-      wtu.checkCanvas(gl2, [0, 0, 0, 0]);
-      wtu.drawUnitQuad(gl2);
-      wtu.checkCanvas(gl2, [0, 255, 0, 255]);
-
-      shouldGenerateGLError(gl2, gl2.INVALID_OPERATION, "gl2.drawElements(gl2.TRIANGLES, 6, gl2.UNSIGNED_SHORT, 0)");
-      gl2.bindBuffer(gl2.ELEMENT_ARRAY_BUFFER, elementBuf);
-      gl2.clear(gl2.COLOR_BUFFER_BIT);
-      shouldGenerateGLError(gl2, gl2.NO_ERROR, "gl2.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0)");
-      wtu.checkCanvas(gl2, [0, 255, 0, 255]);
-
-      debug("Test you can't render to a framebuffer with a released texture");
-      ext.releaseSharedResource(destTex);
-      shouldGenerateGLError(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "gl.drawArrays(gl.TRIANGLES, 0, 6)");
-      shouldGenerateGLError(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "gl.clear(gl.COLOR_BUFFER_BIT)");
-
-      debug("Test you can't read from a framebuffer with a released texture");
-      shouldGenerateGLError(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4))");
-
-      ext.acquireSharedResource(destTex, ext.READ_ONLY, callback);
-    };
-
-    var checkReadOnlyTextureOnFramebuffer = function(callback) {
-      debug("");
-      debug("test READ_ONLY texture attachment");
-      debug("Test we fail of we haven't bound again");
-      shouldGenerateGLError(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4))");
-      shouldBeTrue("gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE");
-      shouldBeTrue("gl.checkFramebufferStatus(gl.READ_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE");
-      shouldBeTrue("gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE");
-
-      gl.activeTexture(gl.TEXTURE1);
-      gl.bindTexture(gl.TEXTURE_2D, destTex);
-      gl.activeTexture(gl.TEXTURE0);
-      debug("Test we fail to draw because we're read only.");
-      shouldBeTrue("gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE");
-      shouldBeTrue("gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE");
-      shouldGenerateGLError(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "gl.drawArrays(gl.TRIANGLES, 0, 6)");
-      shouldGenerateGLError(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "gl.clear(gl.COLOR_BUFFER_BIT)");
-
-      debug("Test we can read a READ_ONLY texture.");
-      shouldBeTrue("gl.checkFramebufferStatus(gl.READ_FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE");
-      shouldGenerateGLError(gl, gl.NO_ERROR, "gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4))");
-
-      checkRenderbuffer(callback);
-    };
-
-    var checkRenderbuffer = function(callback) {
-      if (canCheckRenderbuffer) {
-        gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb);
-        wtu.drawUnitQuad(gl);
-        wtu.checkCanvasRect(gl, 0, 0, width, height, [0, 255, 0, 255]);
-
-        debug("Test you can't render to a framebuffer with a released renderbuffer");
-        ext.releaseSharedResource(rb);
-        shouldGenerateGLError(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "gl.drawArrays(gl.TRIANGLES, 0, 6)");
-        shouldGenerateGLError(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "gl.clear(gl.COLOR_BUFFER_BIT)");
-
-        debug("Test you can't read from a framebuffer with a released renderbuffer");
-        shouldGenerateGLError(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4))");
-      }
-
-      ext.acquireSharedResource(rb, ext.READ_ONLY, callback);
-    };
-
-    var checkReadOnlyRenderbufferOnFramebuffer = function(callback) {
-      if (canCheckRenderbuffer) {
-        debug("");
-        debug("test READ_ONLY renderbuffer attachment");
-        debug("Test we fail of we haven't bound again");
-        shouldGenerateGLError(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4))");
-        shouldBeTrue("gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE");
-        shouldBeTrue("gl.checkFramebufferStatus(gl.READ_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE");
-        shouldBeTrue("gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE");
-
-        gl.bindRenderbuffer(gl.RENDERBUFFER, rb);
-        debug("Test we fail to draw because we're read only.");
-        shouldGenerateGLError(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "gl.drawArrays(gl.TRIANGLES, 0, 6)");
-        shouldGenerateGLError(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "gl.clear(gl.COLOR_BUFFER_BIT)");
-        shouldBeTrue("gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE");
-        shouldBeTrue("gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE");
-
-        debug("Test we can read a READ_ONLY renderbuffer.");
-        shouldBeTrue("gl.checkFramebufferStatus(gl.READ_FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE");
-        shouldGenerateGLError(gl, gl.NO_ERROR, "gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4))");
-      }
-
-      ext.releaseSharedResource(rb);
-      ext.acquireSharedResource(rb, ext.READ_ONLY, callback);
-    };
-
-    var checkRenderbufferBindsOnAttach = function(callback) {
-      if (canCheckRenderbuffer) {
-        debug("");
-        debug("Test we fail of we haven't bound again");
-        shouldGenerateGLError(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4))");
-
-        debug("Test attaching a renderbuffer marks it as bound");
-        gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb);
-
-        debug("Test we can read a READ_ONLY renderbuffer.");
-        shouldGenerateGLError(gl, gl.NO_ERROR, "gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4))");
-      }
-
-      gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, destTex, 0);
-      shouldGenerateGLError(gl, gl.NO_ERROR, "gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4))");
-      ext.releaseSharedResource(destTex);
-      ext.acquireSharedResource(destTex, ext.READ_ONLY, callback);
-    };
-
-    var checkTextureBindsOnAttach = function(callback) {
-      debug("");
-      debug("Test we fail of we haven't bound again");
-      shouldGenerateGLError(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4))");
-
-      debug("Test attaching a texture marks it as bound");
-      gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, destTex, 0);
-
-      debug("Test we can read a READ_ONLY texture.");
-      shouldGenerateGLError(gl, gl.NO_ERROR, "gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4))");
-
-      callback();
-    };
-
-    var checkCanNotRenderWithReleasedProgram = function(callback) {
-      debug("");
-      gl.bindFramebuffer(gl.FRAMEBUFFER, null);
-
-      shouldGenerateGLError(gl, gl.NO_ERROR, "gl.drawArrays(gl.TRIANGLES, 0, 6)");
-
-      ext.releaseSharedResource(program);
-
-      debug("Test we can't draw with a released program.");
-      shouldGenerateGLError(gl, gl.INVALID_OPERATION, "gl.drawArrays(gl.TRIANGLES, 0, 6)");
-
-      ext.acquireSharedResource(program, ext.EXCLUSIVE, callback);
-      ext2.releaseSharedResource(program);
-    };
-
-    var checkCanNotRenderWithReleasedBuffer = function(callback) {
-      debug("");
-      gl.useProgram(program);
-      shouldGenerateGLError(gl, gl.NO_ERROR, "gl.drawArrays(gl.TRIANGLES, 0, 6)");
-
-      ext.releaseSharedResource(buffers[0]);
-
-      debug("Test we can't draw with a released buffer.");
-      shouldGenerateGLError(gl, gl.INVALID_OPERATION, "gl.drawArrays(gl.TRIANGLES, 0, 6)");
-
-      ext.acquireSharedResource(buffers[0], ext.READ_ONLY, callback);
-    };
-
-    var checkCanNotRenderWithReleasedTexture = function(callback) {
-      debug("");
-      gl.bindBuffer(gl.ARRAY_BUFFER, buffers[0]);
-      shouldGenerateGLError(gl, gl.NO_ERROR, "gl.drawArrays(gl.TRIANGLES, 0, 6)");
-
-      ext.releaseSharedResource(tex);
-
-      debug("Test we can't draw with a released texture.");
-      shouldGenerateGLError(gl, gl.INVALID_OPERATION, "gl.drawArrays(gl.TRIANGLES, 0, 6)");
-
-      ext.acquireSharedResource(tex, ext.READ_ONLY, callback);
-    };
-
-    var checkCanRenderWithReleasedShader = function(callback) {
-      gl.bindTexture(gl.TEXTURE_2D, tex);
-      var shaders = gl.getAttachedShaders(program);
-      ext.releaseSharedResource(shaders[0]);
-
-      debug("");
-      debug("Test we can draw with a released shader.");
-      shouldGenerateGLError(gl, gl.NO_ERROR, "gl.drawArrays(gl.TRIANGLES, 0, 6)");
-      callback();
-    };
-
-    runSequence(
-      [
-        releaseAndAcquireResources,
-        doRenderTest,
-        checkReadOnlyTextureOnFramebuffer,
-        checkReadOnlyRenderbufferOnFramebuffer,
-        checkRenderbufferBindsOnAttach,
-        checkTextureBindsOnAttach,
-        checkCanNotRenderWithReleasedProgram,
-        checkCanNotRenderWithReleasedBuffer,
-        checkCanNotRenderWithReleasedTexture,
-        checkCanRenderWithReleasedShader,
-        callback,
-      ]);
-  };
-
-  var testMisc = function(callback) {
-    debug("");
-    debug("Test you can't release a framebuffer");
-    // TODO: It's not clear what should happen here to me.
-    //shouldThrow("ext.releaseSharedResource(fb)", "TypeError");
-
-    debug("")
-    debug("Test you can compare sharegroups");
-    var gl3 = wtu.create3DContext();
-    ext3 = wtu.getExtensionWithKnownPrefixes(gl3, "WEBGL_shared_resources");
-    // TODO: comment in once this comparison works.
-    //shouldBeTrue("ext.group == ext2.group");
-    shouldBeTrue("ext.group != ext3.group");
-
-    debug("Test you can't use resources from another different group.");
-    tex3 = gl3.createTexture();
-    shouldGenerateGLError(gl, gl.INVALID_OPERATION, "ext.releaseSharedResource(tex3)");
-    shouldGenerateGLError(gl, gl.INVALID_OPERATION, "ext.acquireSharedResource(tex3, ext.READ_ONLY, makeFailCallback('should not be able to acquire resource from other context'))");
-
-    var failTest = function() {
-      testFailed("cancelled callback was called");
-    };
-
-    var tex = gl.createTexture();
-    debug("test releasing from the wrong context. Should be a no-op");
-    shouldGenerateGLError(gl, gl.NO_ERROR, "ext2.releaseSharedResource(tex)");
-
-    id = ext2.acquireSharedResource(tex, ext.READ_ONLY, failTest);
-    debug("test cancelling a request for which an event has not been posted");
-    ext2.cancelAcquireSharedResource(id);
-
-    debug("test cancelling a request for which an event has already been posted");
-    ext.releaseSharedResource(tex);
-    id = ext.acquireSharedResource(tex, ext.READ_ONLY, failTest);
-    ext.cancelAcquireSharedResource(id);
-
-    debug("test cancelling on the wrong context's extension is ignored");
-    id = ext2.acquireSharedResource(tex, ext.READ_ONLY, callback);
-    shouldGenerateGLError(gl, gl.NO_ERROR, 'ext.cancelAcquireSharedResource(id)');
-  };
-
-  var testLostContext = function(callback) {
-    var WEBGL_lose_context = wtu.getExtensionWithKnownPrefixes(gl, "WEBGL_lose_context");
-    if (!WEBGL_lose_context) {
-      callback();
-      return;
-    }
-
-    var tex = gl.createTexture();
-    var tex2 = gl.createTexture();
-
-    var setupAcquire = function(callback) {
-      var callbacksNeeded = 3;
-      var waitForContextLostAndAcquire = function(e) {
-        if (e && e.preventDefault) {
-          e.preventDefault();  // allow context restore.
-        }
-        --callbacksNeeded;
-        if (callbacksNeeded == 0) {
-          callback();
-        }
-        return false;
-      };
-
-      debug("");
-      debug("Test extension still functions during context lost.");
-      acquireMultipleResources([ext2], [tex, tex2], ext2.READ_ONLY, waitForContextLostAndAcquire);
-      canvas1.addEventListener("webglcontextlost", waitForContextLostAndAcquire, false);
-      canvas2.addEventListener("webglcontextlost", waitForContextLostAndAcquire, false);
-      // Release one before context lost
-      ext.releaseSharedResource(tex);
-      WEBGL_lose_context.loseContext();
-      // Release one after context lost
-      ext.releaseSharedResource(tex2);
-
-      shouldBeTrue('gl.isContextLost()');
-      shouldBeTrue('gl2.isContextLost()');
-    };
-
-    var checkAcquireExt2 = function(callback) {
-      testPassed("was able to acquire resources during context lost");
-      acquireMultipleResources([ext], [tex, tex2], ext.READ_ONLY, callback);
-    };
-
-    var checkAcquireExt = function(callback) {
-      testPassed("was able to request acquire resources during context lost");
-      canvas1.addEventListener("webglcontextrestored", callback, false);
-      WEBGL_lose_context.restoreContext();
-    };
-
-    var passTest = function(callback) {
-      testPassed("extension works during lost context");
-      callback();
-    };
-
-    runSequence(
-      [
-        setupAcquire,
-        checkAcquireExt2,
-        checkAcquireExt,
-        passTest,
-        callback,
-      ]);
-  };
-
-  runSequence(
-    [
-      makeCommonResourceFeatureTest(bufferTests),
-      makeCommonResourceFeatureTest(programTests),
-      makeCommonResourceFeatureTest(shaderTests),
-      makeCommonResourceFeatureTest(renderbufferTests),
-      makeCommonResourceFeatureTest(textureTests),
-      makeSingleContextResourceTest(bufferTests),
-      makeSingleContextResourceTest(programTests),
-      makeSingleContextResourceTest(renderbufferTests),
-      makeSingleContextResourceTest(shaderTests),
-      makeSingleContextResourceTest(textureTests),
-      testRendering,
-      testMisc,
-      testLostContext,
-      finishTest,
-    ]);
-
-}
-var successfullyParsed = true;
-</script>
-</body>
-</html>
-
--- a/dom/canvas/test/webgl-conf/checkout/conformance/glsl/bugs/00_test_list.txt
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/glsl/bugs/00_test_list.txt
@@ -35,15 +35,16 @@
 --min-version 1.0.4 qualcomm-crash.html
 --min-version 1.0.4 qualcomm-loop-with-continue-crash.html
 --min-version 1.0.4 sampler-array-struct-function-arg.html
 --min-version 1.0.3 sampler-array-using-loop-index.html
 --min-version 1.0.4 sampler-struct-function-arg.html
 --min-version 1.0.4 sequence-operator-evaluation-order.html
 --min-version 1.0.4 sketchfab-lighting-shader-crash.html
 --min-version 1.0.4 struct-constructor-highp-bug.html
+--min-version 1.0.4 struct-with-single-member-constructor.html
 --min-version 1.0.3 temp-expressions-should-not-crash.html
 --min-version 1.0.4 unary-minus-operator-float-bug.html
 --min-version 1.0.4 undefined-index-should-not-crash.html
 --min-version 1.0.3 uniforms-should-not-lose-values.html
 --min-version 1.0.4 varying-arrays-should-not-be-reversed.html
 --min-version 1.0.4 vector-scalar-arithmetic-inside-loop.html
 --min-version 1.0.4 vector-scalar-arithmetic-inside-loop-complex.html
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/glsl/bugs/struct-with-single-member-constructor.html
@@ -0,0 +1,73 @@
+<!--
+
+/*
+** Copyright (c) 2018 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a
+** copy of this software and/or associated documentation files (the
+** "Materials"), to deal in the Materials without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Materials, and to
+** permit persons to whom the Materials are furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be included
+** in all copies or substantial portions of the Materials.
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+*/
+
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>GLSL struct with a single member constructor test</title>
+<link rel="stylesheet" href="../../../resources/js-test-style.css"/>
+<script src="../../../js/js-test-pre.js"></script>
+<script src="../../../js/webgl-test-utils.js"></script>
+<script src="../../../js/glsl-conformance-test.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script id="fshader" type="x-shader/x-fragment">
+#version 100
+
+precision mediump float;
+
+struct S {
+    mat2 rotation;
+};
+void main(void)
+{
+    float angle = 1.0;
+    S(mat2(1.0, angle, 1.0, 1.0));
+}
+</script>
+<script>
+"use strict";
+description();
+debug("This is a regression test for <a href='https://bugs.chromium.org/p/swiftshader/issues/detail?id=56'>Swiftshader bug 56</a>.");
+debug("");
+
+GLSLConformanceTester.runTests([
+{
+  fShaderId: 'fshader',
+  fShaderSuccess: true,
+  linkSuccess: true,
+  passMsg: "Construct a struct with a single matrix member"
+}
+]);
+
+</script>
+
+</body>
+</html>
--- a/dom/canvas/test/webgl-conf/checkout/conformance/glsl/misc/00_test_list.txt
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/glsl/misc/00_test_list.txt
@@ -1,15 +1,16 @@
 --max-version 1.9.9 attrib-location-length-limits.html
 --min-version 1.0.3 boolean_precision.html
 --min-version 1.0.4 const-variable-initialization.html
 embedded-struct-definitions-forbidden.html
 --min-version 1.0.4 empty-declaration.html
 empty_main.vert.html
 --min-version 1.0.3 expression-list-in-declarator-initializer.html
+--min-version 1.0.4 fragcolor-fragdata-invariant.html
 gl_position_unset.vert.html
 --min-version 1.0.4 global-variable-init.html
 # this test is intentionally disabled as it is too strict and to hard to simulate
 # glsl-2types-of-textures-on-same-unit.html
 glsl-function-nodes.html
 --min-version 1.0.2 glsl-vertex-branch.html
 glsl-long-variable-names.html
 --min-version 1.0.4 local-variable-shadowing-outer-function.html
@@ -93,16 +94,18 @@ shader-without-precision.frag.html
 --min-version 1.0.3 shaders-with-invariance.html
 --min-version 1.0.3 shaders-with-name-conflicts.html
 --min-version 1.0.2 shaders-with-mis-matching-uniforms.html
 --min-version 1.0.2 shaders-with-mis-matching-varyings.html
 --min-version 1.0.2 shaders-with-missing-varyings.html
 --min-version 1.0.3 shaders-with-uniform-structs.html
 --min-version 1.0.2 shaders-with-varyings.html
 shared.html
+--min-version 1.0.4 struct-as-inout-parameter.html
+--min-version 1.0.4 struct-as-out-parameter.html
 struct-nesting-exceeds-maximum.html
 struct-nesting-under-maximum.html
 --max-version 1.9.9 uniform-location-length-limits.html
 --min-version 1.0.2 shader-with-short-circuiting-operators.html
 --min-version 1.0.2 shader-with-global-variable-precision-mismatch.html
 --min-version 1.0.2 large-loop-compile.html
 --min-version 1.0.3 struct-equals.html
 --min-version 1.0.4 struct-assign.html
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/glsl/misc/fragcolor-fragdata-invariant.html
@@ -0,0 +1,61 @@
+<!--
+
+/*
+** Copyright (c) 2018 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a
+** copy of this software and/or associated documentation files (the
+** "Materials"), to deal in the Materials without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Materials, and to
+** permit persons to whom the Materials are furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be included
+** in all copies or substantial portions of the Materials.
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+*/
+
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL GLSL Conformance Tests - gl_FragColor and gl_FragData both declared as invariant</title>
+<link rel="stylesheet" href="../../../resources/js-test-style.css"/>
+<link rel="stylesheet" href="../../../resources/glsl-feature-tests.css"/>
+<script src="../../../js/js-test-pre.js"></script>
+<script src="../../../js/webgl-test-utils.js"></script>
+<script src="../../../js/glsl-conformance-test.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script id="fragmentShader" type="text/something-not-javascript">
+// Declaring both gl_FragColor and gl_FragData invariant should succeed.
+precision mediump float;
+
+// Static write of both gl_FragColor and gl_FragData is disallowed. However, simply declaring a variable invariant is not really accessing the variable, so it should be ok.
+invariant gl_FragColor;
+invariant gl_FragData;
+
+void main()
+{
+    gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
+}
+</script>
+<script>
+"use strict";
+GLSLConformanceTester.runTest();
+var successfullyParsed = true;
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/glsl/misc/struct-as-inout-parameter.html
@@ -0,0 +1,138 @@
+<!--
+
+/*
+** Copyright (c) 2018 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a
+** copy of this software and/or associated documentation files (the
+** "Materials"), to deal in the Materials without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Materials, and to
+** permit persons to whom the Materials are furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be included
+** in all copies or substantial portions of the Materials.
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+*/
+
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>GLSL Structure as Inout Parameter Test</title>
+<link rel="stylesheet" href="../../../resources/js-test-style.css"/>
+<link rel="stylesheet" href="../../../resources/glsl-feature-tests.css"/>
+<script src="../../../js/js-test-pre.js"></script>
+<script src="../../../js/webgl-test-utils.js"> </script>
+<script src="../../../js/glsl-conformance-test.js"></script>
+
+<script id="simple-vs" type="x-shader/x-vertex">
+attribute vec4 a_position;
+void main(void) {
+    gl_Position = a_position;
+}
+</script>
+<script id="struct-inout-parameter-fs" type="x-shader/x-fragment">
+struct ColorData {
+  vec3 red;
+  vec3 blue;
+};
+
+void modify(inout ColorData colorData) {
+  colorData.red += vec3(0.5, 0.0, 0.0);
+  colorData.blue += vec3(0.0, 0.0, 0.5);
+}
+
+void main() {
+  ColorData colorData;
+  colorData.red = vec3(0.5, 0.0, 0.0);
+  colorData.blue = vec3(0.0, 0.0, 0.5);
+
+  vec3 red = vec3(1.0, 0.0, 0.0);
+  vec3 green = vec3(0.0, 1.0, 0.0);
+  vec3 blue = vec3(0.0, 0.0, 1.0);
+  vec3 finalColor;
+
+  modify(colorData);
+
+  if (colorData.red == red && colorData.blue == blue)
+    finalColor = green;
+  else
+    finalColor = red;
+
+  gl_FragColor = vec4(finalColor, 1.0);
+}
+</script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+description("Testing structs as inout parameter");
+
+debug('Regression test for <a href="http://crbug.com/851870">http://crbug.com/851870</a> / <a href="https://github.com/mrdoob/three.js/issues/14137">https://github.com/mrdoob/three.js/issues/14137</a>');
+
+function prepend(floatPrecision) {
+  let source = document.getElementById('struct-inout-parameter-fs').text;
+  return 'precision ' + floatPrecision + ' float;\n' + source;
+}
+
+let tests = [
+  {
+    vShaderId: "simple-vs",
+    vShaderSuccess: true,
+    fShaderSource: prepend('lowp'),
+    fShaderSuccess: true,
+    linkSuccess: true,
+    render: true,
+    passMsg: "lowp struct used as inout parameter",
+  },
+  {
+    vShaderId: "simple-vs",
+    vShaderSuccess: true,
+    fShaderSource: prepend('mediump'),
+    fShaderSuccess: true,
+    linkSuccess: true,
+    render: true,
+    passMsg: "mediump struct used as inout parameter",
+  },
+];
+
+let wtu = WebGLTestUtils;
+let gl = wtu.create3DContext();
+let precision = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT);
+let highpSupported = (precision.rangeMin >= 62 && precision.rangeMax >= 62 && precision.precision >= 16);
+debug("highp is" + (highpSupported ? "" : " not") + " supported in fragment shaders");
+
+if (highpSupported) {
+  tests.push(
+    {
+      vShaderId: "simple-vs",
+      vShaderSuccess: true,
+      fShaderSource: prepend('highp'),
+      fShaderSuccess: true,
+      linkSuccess: true,
+      render: true,
+      passMsg: "highp struct used as inout parameter",
+    }
+  );
+}
+
+GLSLConformanceTester.runTests(tests);
+debug("");
+
+var successfullyParsed = true;
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/glsl/misc/struct-as-out-parameter.html
@@ -0,0 +1,136 @@
+<!--
+
+/*
+** Copyright (c) 2018 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a
+** copy of this software and/or associated documentation files (the
+** "Materials"), to deal in the Materials without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Materials, and to
+** permit persons to whom the Materials are furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be included
+** in all copies or substantial portions of the Materials.
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+*/
+
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>GLSL Structure as Out Parameter Test</title>
+<link rel="stylesheet" href="../../../resources/js-test-style.css"/>
+<link rel="stylesheet" href="../../../resources/glsl-feature-tests.css"/>
+<script src="../../../js/js-test-pre.js"></script>
+<script src="../../../js/webgl-test-utils.js"> </script>
+<script src="../../../js/glsl-conformance-test.js"></script>
+
+<script id="simple-vs" type="x-shader/x-vertex">
+attribute vec4 a_position;
+void main(void) {
+    gl_Position = a_position;
+}
+</script>
+<script id="struct-out-parameter-fs" type="x-shader/x-fragment">
+struct ColorData {
+  vec3 red;
+  vec3 blue;
+};
+
+void modify(out ColorData colorData) {
+  colorData.red = vec3(1.0, 0.0, 0.0);
+  colorData.blue = vec3(0.0, 0.0, 1.0);
+}
+
+void main() {
+  ColorData colorData;
+
+  vec3 red = vec3(1.0, 0.0, 0.0);
+  vec3 green = vec3(0.0, 1.0, 0.0);
+  vec3 blue = vec3(0.0, 0.0, 1.0);
+  vec3 finalColor;
+
+  modify(colorData);
+
+  if (colorData.red == red && colorData.blue == blue)
+    finalColor = green;
+  else
+    finalColor = red;
+
+  gl_FragColor = vec4(finalColor, 1.0);
+}
+</script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+description("Testing structs as out parameter");
+
+debug('Regression test for <a href="http://crbug.com/851873">http://crbug.com/851873</a> / <a href="https://github.com/mrdoob/three.js/issues/14137">https://github.com/mrdoob/three.js/issues/14137</a>');
+
+function prepend(floatPrecision) {
+  let source = document.getElementById('struct-out-parameter-fs').text;
+  return 'precision ' + floatPrecision + ' float;\n' + source;
+}
+
+let tests = [
+  {
+    vShaderId: "simple-vs",
+    vShaderSuccess: true,
+    fShaderSource: prepend('lowp'),
+    fShaderSuccess: true,
+    linkSuccess: true,
+    render: true,
+    passMsg: "lowp struct used as out parameter",
+  },
+  {
+    vShaderId: "simple-vs",
+    vShaderSuccess: true,
+    fShaderSource: prepend('mediump'),
+    fShaderSuccess: true,
+    linkSuccess: true,
+    render: true,
+    passMsg: "mediump struct used as out parameter",
+  },
+];
+
+let wtu = WebGLTestUtils;
+let gl = wtu.create3DContext();
+let precision = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT);
+let highpSupported = (precision.rangeMin >= 62 && precision.rangeMax >= 62 && precision.precision >= 16);
+debug("highp is" + (highpSupported ? "" : " not") + " supported in fragment shaders");
+
+if (highpSupported) {
+  tests.push(
+    {
+      vShaderId: "simple-vs",
+      vShaderSuccess: true,
+      fShaderSource: prepend('highp'),
+      fShaderSuccess: true,
+      linkSuccess: true,
+      render: true,
+      passMsg: "highp struct used as out parameter",
+    }
+  );
+}
+
+GLSLConformanceTester.runTests(tests);
+debug("");
+
+var successfullyParsed = true;
+</script>
+</body>
+</html>
--- a/dom/canvas/test/webgl-conf/checkout/conformance/misc/webgl-specific-stencil-settings.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/misc/webgl-specific-stencil-settings.html
@@ -38,38 +38,50 @@
 <div id="description"></div>
 <div id="console"></div>
 
 <script>
 "use strict";
 var wtu = WebGLTestUtils;
 description("Tests that stencil mask/func are validated correctly when the front state and back state differ.");
 
-var gl = wtu.create3DContext();
+var gl;
+
+function checkDrawError(errIfMismatch) {
+    wtu.shouldGenerateGLError(gl, errIfMismatch, "wtu.dummySetProgramAndDrawNothing(gl)");
+}
+
+function setStencilMask(mask) {
+    wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.stencilMaskSeparate(gl.FRONT, " + mask[0] + ")");
+    wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.stencilMaskSeparate(gl.BACK,  " + mask[1] + ")");
+}
 
 function testStencilMaskCase(mask, error) {
-    wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.stencilMaskSeparate(gl.FRONT, " + mask[0] + ")");
-    wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.stencilMaskSeparate(gl.BACK,  " + mask[1] + ")");
+    setStencilMask(mask);
     // If an error is generated, it should be at draw time.
-    wtu.shouldGenerateGLError(gl, error, "wtu.dummySetProgramAndDrawNothing(gl)");
+    checkDrawError(error);
 }
 
 function testStencilMask(errIfMismatch) {
     testStencilMaskCase([0, 256], gl.NO_ERROR);
     testStencilMaskCase([1, 256], errIfMismatch);
     testStencilMaskCase([1, 257], gl.NO_ERROR);
     testStencilMaskCase([1, 258], errIfMismatch);
     wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.stencilMask(1023)", "resetting stencilMask");
 }
 
-function testStencilFuncCase(ref, mask, error) {
+function setStencilFunc(ref, mask) {
     wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.stencilFuncSeparate(gl.FRONT, gl.ALWAYS, " + ref[0] + ", " + mask[0] + ")");
     wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.stencilFuncSeparate(gl.BACK,  gl.ALWAYS, " + ref[1] + ", " + mask[1] + ")");
+}
+
+function testStencilFuncCase(ref, mask, error) {
+    setStencilFunc(ref, mask);
     // If an error is generated, it should be at draw time.
-    wtu.shouldGenerateGLError(gl, error, "wtu.dummySetProgramAndDrawNothing(gl)");
+    checkDrawError(error);
 }
 
 function testStencilFunc(errIfMismatch) {
     testStencilFuncCase([ 256,  257], [1023, 1023], gl.NO_ERROR);
     testStencilFuncCase([ 256,  254], [1023, 1023], errIfMismatch);
 
     testStencilFuncCase([  -1,    0], [1023, 1023], gl.NO_ERROR);
     testStencilFuncCase([  -1,  254], [1023, 1023], errIfMismatch);
@@ -92,33 +104,54 @@ function testStencilFunc(errIfMismatch) 
     testStencilFuncCase([ 255,   -1], [1023, 1023], errIfMismatch);
     testStencilFuncCase([ 256,    0], [1023, 1023], errIfMismatch);
     testStencilFuncCase([1024,    0], [1023, 1023], errIfMismatch);
     testStencilFuncCase([ 257,    1], [1023, 1023], errIfMismatch);
 
     wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.stencilFunc(gl.ALWAYS, 0, 1023)", "resetting stencilFunc");
 }
 
+//
+// Tests of the default framebuffer
+//
+
+debug("");
+
+debug("Testing default framebuffer with { stencil: true }");
+gl = wtu.create3DContext(undefined, { stencil: true });
+{
+    gl.enable(gl.STENCIL_TEST);
+    testStencilMaskCase([1, 256], gl.INVALID_OPERATION);
+    testStencilFuncCase([256, 0], [1023, 1023], gl.INVALID_OPERATION);
+}
+
+debug("Testing default framebuffer with { stencil: false }");
+gl = wtu.create3DContext(undefined, { stencil: false });
+{
+    // with { stencil: false }
+    gl.enable(gl.STENCIL_TEST);
+    testStencilMaskCase([1, 256], gl.NO_ERROR);
+    testStencilFuncCase([256, 0], [1023, 1023], gl.NO_ERROR);
+}
+// (continue using this GL context for the other tests)
+
+//
+// Tests with a framebuffer object
+//
+
 const fb = gl.createFramebuffer();
 gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
 const colorRB = gl.createRenderbuffer();
 gl.bindRenderbuffer(gl.RENDERBUFFER, colorRB);
 gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 1, 1);
 gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorRB);
 
 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "initial framebuffer setup")
 
-function testStencilSettings(haveDepthBuffer, haveStencilBuffer, enableStencilTest, errIfMismatch) {
-    debug("");
-    debug("With depthbuffer=" + haveDepthBuffer +
-            ", stencilbuffer=" + haveStencilBuffer +
-            ", stencilTest=" + enableStencilTest +
-            ", expecting error=" + wtu.glEnumToString(gl, errIfMismatch) +
-            " for mismatching mask or func settings.");
-
+function runWithStencilSettings(haveDepthBuffer, haveStencilBuffer, enableStencilTest, fn) {
     let rbo = null;
     let attachment;
     if (haveDepthBuffer || haveStencilBuffer) {
         rbo = gl.createRenderbuffer();
         gl.bindRenderbuffer(gl.RENDERBUFFER, rbo);
 
         let internalformat;
         if (haveDepthBuffer && haveStencilBuffer) {
@@ -138,30 +171,42 @@ function testStencilSettings(haveDepthBu
     wtu.glErrorShouldBe(gl, gl.NO_ERROR, "depth/stencil renderbuffer setup")
 
     if (enableStencilTest) {
         gl.enable(gl.STENCIL_TEST);
     } else {
         gl.disable(gl.STENCIL_TEST);
     }
 
-    // Errors should be the same for both mask and func, because stencil test
-    // and stencil write are always enabled/disabled in tandem.
-    testStencilMask(errIfMismatch);
-    testStencilFunc(errIfMismatch);
+    fn();
 
     if (rbo) {
         gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachment, gl.RENDERBUFFER, null);
         gl.bindRenderbuffer(gl.RENDERBUFFER, null);
         gl.deleteRenderbuffer(rbo);
     }
 
     wtu.glErrorShouldBe(gl, gl.NO_ERROR, "depth/stencil renderbuffer cleanup")
 }
 
+function testStencilSettings(haveDepthBuffer, haveStencilBuffer, enableStencilTest, errIfMismatch) {
+    debug("");
+    debug("With depthbuffer=" + haveDepthBuffer +
+            ", stencilbuffer=" + haveStencilBuffer +
+            ", stencilTest=" + enableStencilTest +
+            ", expecting error=" + wtu.glEnumToString(gl, errIfMismatch) +
+            " for mismatching mask or func settings.");
+
+    runWithStencilSettings(haveDepthBuffer, haveStencilBuffer, enableStencilTest, () => {
+        // Errors should be the same for both mask and func, because stencil test
+        // and stencil write are always enabled/disabled in tandem.
+        testStencilMask(errIfMismatch);
+        testStencilFunc(errIfMismatch);
+    });
+}
 
 debug("");
 debug("Base case checks:");
 testStencilMaskCase([0, 0], gl.NO_ERROR);
 testStencilFuncCase([0, 0], [1023, 1023], gl.NO_ERROR);
 
 //                  haveDepthBuffer
 //                  |      haveStencilBuffer
@@ -172,16 +217,103 @@ testStencilSettings( true, false, false,
 testStencilSettings(false,  true, false, gl.NO_ERROR);
 testStencilSettings( true,  true, false, gl.NO_ERROR);
 
 testStencilSettings(false, false,  true, gl.NO_ERROR);
 testStencilSettings( true, false,  true, gl.NO_ERROR);
 testStencilSettings(false,  true,  true, gl.INVALID_OPERATION);
 testStencilSettings( true,  true,  true, gl.INVALID_OPERATION);
 
+//
+// Tests to make sure the stencil validation check, if cached, is invalidated correctly.
+//
+
+debug("");
+
+debug("Setup for stencil validation cache invalidation tests");
+setStencilMask([1, 258]);
+setStencilFunc([0, 256], [1023, 1023]);
+
+debug("Test with enabling/disabling stencil test");
+runWithStencilSettings(false, true, false, () => {
+    checkDrawError(gl.NO_ERROR);
+    gl.enable(gl.STENCIL_TEST);
+    checkDrawError(gl.INVALID_OPERATION);
+    gl.disable(gl.STENCIL_TEST);
+    checkDrawError(gl.NO_ERROR);
+});
+
+debug("Test with swapping in a new FBO");
+runWithStencilSettings(false, false, true, () => {
+    // no error with no stencil buffer
+    checkDrawError(gl.NO_ERROR);
+
+    // swap in a new FBO with a stencil buffer
+    const fb2 = gl.createFramebuffer();
+    gl.bindFramebuffer(gl.FRAMEBUFFER, fb2);
+    gl.bindRenderbuffer(gl.RENDERBUFFER, colorRB);
+    gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorRB);
+    const rbo = gl.createRenderbuffer();
+    gl.bindRenderbuffer(gl.RENDERBUFFER, rbo);
+    gl.renderbufferStorage(gl.RENDERBUFFER, gl.STENCIL_INDEX8, 1, 1);
+    gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, rbo);
+    shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_COMPLETE");
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+
+    // this draw sholud detect the new fbo state
+    checkDrawError(gl.INVALID_OPERATION);
+
+    gl.deleteFramebuffer(fb2);
+    gl.deleteRenderbuffer(rbo)
+    gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+});
+
+debug("Test with adding a stencil attachment");
+runWithStencilSettings(false, false, true, () => {
+    // no error with no stencil buffer
+    checkDrawError(gl.NO_ERROR);
+
+    // add a stencil attachment
+    const rbo = gl.createRenderbuffer();
+    gl.bindRenderbuffer(gl.RENDERBUFFER, rbo);
+    gl.renderbufferStorage(gl.RENDERBUFFER, gl.STENCIL_INDEX8, 1, 1);
+    gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, rbo);
+    shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_COMPLETE");
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+
+    // this draw sholud detect the new fbo state
+    checkDrawError(gl.INVALID_OPERATION);
+
+    gl.deleteRenderbuffer(rbo)
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+});
+
+debug("Test with reallocating the DEPTH_STENCIL attachment from depth to depth+stencil");
+runWithStencilSettings(false, false, true, () => {
+    // attach a depth buffer to the DEPTH_STENCIL attachment
+    const rbo = gl.createRenderbuffer();
+    gl.bindRenderbuffer(gl.RENDERBUFFER, rbo);
+    gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, 1, 1);
+    gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, rbo);
+    shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT");
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+
+    // this draw is invalid, but it still might trigger caching of the stencil validation
+    checkDrawError(gl.INVALID_FRAMEBUFFER_OPERATION);
+
+    gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, 1, 1);
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+    // this draw sholud detect the new fbo state
+    checkDrawError(gl.INVALID_OPERATION);
+
+    gl.deleteRenderbuffer(rbo)
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+});
+
 gl.deleteFramebuffer(fb);
 gl.deleteRenderbuffer(colorRB);
 
 var successfullyParsed = true;
 </script>
 
 <script src="../../js/js-test-post.js"></script>
 </body>
--- a/dom/canvas/test/webgl-conf/checkout/conformance/ogles/GL/struct/nestedstructcomb_various_frag.frag
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/ogles/GL/struct/nestedstructcomb_various_frag.frag
@@ -23,17 +23,17 @@
 */
 
 
 #ifdef GL_ES
 precision mediump float;
 #endif
 varying vec4 color;
 
-#define ERROR_EPSILON 0.1
+#define ERROR_EPSILON 0.125
 
 void main (void){
   	struct second_nest
 	{
 	   float sc_nt;
 	   mat2 sc_mt2;
 	   vec4 sc_vc4;
 	};
--- a/dom/canvas/test/webgl-conf/checkout/conformance/ogles/GL/struct/nestedstructcomb_various_vert.vert
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/ogles/GL/struct/nestedstructcomb_various_vert.vert
@@ -22,17 +22,17 @@
 ** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
 */
 
 
 attribute vec4 gtf_Vertex;
 uniform mat4 gtf_ModelViewProjectionMatrix;
 varying vec4 color;
 
-#define ERROR_EPSILON 0.1
+#define ERROR_EPSILON 0.125
 
 void main (void)
 {
 	struct second_nest
 	{
 	   float sc_nt;
 	   mat2 sc_mt2;
 	   vec4 sc_vc4;
--- a/dom/canvas/test/webgl-conf/checkout/conformance/renderbuffers/framebuffer-state-restoration.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/renderbuffers/framebuffer-state-restoration.html
@@ -41,16 +41,21 @@
 <div id="console"></div>
 <script>
 "use strict";
 var wtu = WebGLTestUtils;
 description();
 
 function test() {
   var gl = wtu.create3DContext("example", {preserveDrawingBuffer: true});
+  if (!gl) {
+    testFailed("context does not exist");
+    finishTest();
+    return;
+  }
   var program = wtu.setupColorQuad(gl);
   var colorLoc = gl.getUniformLocation(program, "u_color");
   gl.enable(gl.DEPTH_TEST);
   gl.depthFunc(gl.LESS);
 
   var testDrawToBackBuffer = function(label) {
     debug("");
     debug("drawing to backbuffer " + label);
--- a/dom/canvas/test/webgl-conf/checkout/conformance/rendering/00_test_list.txt
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/rendering/00_test_list.txt
@@ -1,23 +1,25 @@
+--min-version 1.0.4 canvas-alpha-bug.html
 --min-version 1.0.4 --max-version 1.9.9 clipping-wide-points.html
 --min-version 1.0.2 culling.html
 --min-version 1.0.4 default-texture-draw-bug.html
 draw-arrays-out-of-bounds.html
 draw-elements-out-of-bounds.html
 --min-version 1.0.4 draw-with-changing-start-vertex-bug.html
 --min-version 1.0.3 framebuffer-switch.html
 --min-version 1.0.3 framebuffer-texture-switch.html
 gl-clear.html
 --min-version 1.0.3 gl-drawarrays.html
 gl-drawelements.html
 gl-scissor-test.html
 --min-version 1.0.2 gl-scissor-fbo-test.html
 --min-version 1.0.3 gl-scissor-canvas-dimensions.html
 --min-version 1.0.3 gl-viewport-test.html
+--min-version 1.0.4 line-rendering-quality.html
 --min-version 1.0.3 many-draw-calls.html
 more-than-65536-indices.html
 multisample-corruption.html
 --min-version 1.0.3 negative-one-index.html
 out-of-bounds-index-buffers.html
 --min-version 1.0.3 point-no-attributes.html
 point-size.html
 --min-version 1.0.4 point-specific-shader-variables.html
@@ -26,9 +28,9 @@ point-size.html
 --min-version 1.0.4 preservedrawingbuffer-leak.html
 --min-version 1.0.4 scissor-rect-repeated-rendering.html
 --min-version 1.0.2 simple.html
 triangle.html
 line-loop-tri-fan.html
 --min-version 1.0.4 framebuffer-texture-clear.html
 --min-version 1.0.4 clear-after-copyTexImage2D.html
 --min-version 1.0.4 texture-switch-performance.html
---min-version 1.0.4 rendering-stencil-large-viewport.html
\ No newline at end of file
+--min-version 1.0.4 rendering-stencil-large-viewport.html
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/rendering/canvas-alpha-bug.html
@@ -0,0 +1,137 @@
+<!--
+
+/*
+** Copyright (c) 2018 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a
+** copy of this software and/or associated documentation files (the
+** "Materials"), to deal in the Materials without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Materials, and to
+** permit persons to whom the Materials are furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be included
+** in all copies or substantial portions of the Materials.
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+*/
+
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Alpha blending bug on WebGL canvas</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<canvas id="canvas" width="128" height="128"> </canvas>
+
+<script id="vshader" type="x-shader/x-vertex">
+attribute vec2 a_position;
+varying vec2 v_uv;
+void main()
+{
+    v_uv = a_position.xy * vec2(0.5, 0.5) + vec2(0.5, 0.5);
+    gl_Position = vec4(a_position.xy, 0.0, 1.0);
+}
+</script>
+
+<script id="fshader" type="x-shader/x-fragment">
+precision mediump float;
+varying vec2 v_uv;
+uniform sampler2D u_texture;
+uniform vec4 u_color;
+void main()
+{
+    vec4 tex = texture2D(u_texture, v_uv);
+    gl_FragColor = tex * u_color;
+}
+</script>
+
+<script>
+"use strict";
+// This test exposes an Intel driver issue on macOS.
+var wtu = WebGLTestUtils;
+var gl = wtu.create3DContext("canvas");
+
+var program;
+var offscreen_tex;
+var fbo;
+
+function init()
+{
+    // Init program
+    program = wtu.setupProgram(gl, ["vshader", "fshader"], ["a_position"]);
+
+    // Init offscreen render targets and specify the format of offscreen texture to be RGB.
+    offscreen_tex = gl.createTexture();
+    gl.activeTexture(gl.TEXTURE0);
+    gl.bindTexture(gl.TEXTURE_2D, offscreen_tex);
+    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, canvas.width, canvas.height, 0, gl.RGB, gl.UNSIGNED_BYTE, null);
+
+    fbo = gl.createFramebuffer();
+    gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, offscreen_tex, 0);
+
+    // Init blend state
+    gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
+}
+
+function draw_rect()
+{
+    gl.useProgram(program);
+
+    gl.bindTexture(gl.TEXTURE_2D, offscreen_tex);
+    var u_tex_loc = gl.getUniformLocation(program, 'u_texture');
+    gl.uniform1i(u_tex_loc, 0);
+
+    wtu.setupUnitQuad(gl);
+    wtu.drawFloatColorQuad(gl, [1.0, 1.0, 1.0, 1.0]);
+}
+
+function test_canvas_alpha() {
+    init();
+
+    // Clear offscreen texture to Green
+    gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+    gl.viewport(0, 0, canvas.width, canvas.height);
+    gl.clearColor(0.0, 1.0, 0.0, 1.0);
+    gl.clear(gl.COLOR_BUFFER_BIT);
+
+    // Clear default framebuffer
+    gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+    gl.viewport(0, 0, canvas.width, canvas.height);
+    gl.clearColor(1.0, 0.0, 0.0, 1.0);
+    gl.clear(gl.COLOR_BUFFER_BIT);
+
+    // Enable alpha blending and render to default framebuffer
+    gl.enable(gl.BLEND);
+    draw_rect();
+    wtu.checkCanvasRect(gl, 0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight, [0, 255, 0, 255]);
+}
+
+test_canvas_alpha();
+
+description("Exposes alpha blending bug in Intel drivers on macOS - see https://crbug.com/886970");
+var successfullyParsed = true;
+
+</script>
+<script src="../../js/js-test-post.js"></script>
+
+</body>
+</html>
--- a/dom/canvas/test/webgl-conf/checkout/conformance/rendering/framebuffer-switch.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/rendering/framebuffer-switch.html
@@ -37,16 +37,17 @@
 <body>
 <canvas id="canvas" width="64" height="64"></canvas>
 <div id="description"></div>
 <div id="console"></div>
 <script>
 "use strict";
 description("Test framebuffer switching. The test switches between two framebuffers, copying rendering results from one to the other.");
 var wtu = WebGLTestUtils;
+var canvas = document.getElementById("canvas");
 
 var gl = wtu.create3DContext("canvas");
 var program = wtu.setupTexturedQuad(gl);
 
 var tex1 = gl.createTexture();
 gl.bindTexture(gl.TEXTURE_2D, tex1);
 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, canvas.width, canvas.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
--- a/dom/canvas/test/webgl-conf/checkout/conformance/rendering/framebuffer-texture-switch.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/rendering/framebuffer-texture-switch.html
@@ -37,16 +37,17 @@
 <body>
 <canvas id="canvas" width="64" height="64"></canvas>
 <div id="description"></div>
 <div id="console"></div>
 <script>
 "use strict";
 description("Test framebuffer texture attachment switching. The test uses one framebuffer object and switches its color attachment.");
 var wtu = WebGLTestUtils;
+var canvas = document.getElementById("canvas");
 
 var gl = wtu.create3DContext("canvas");
 var program = wtu.setupTexturedQuad(gl);
 
 var fb = gl.createFramebuffer();
 gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
 
 var tex2 = gl.createTexture();
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/rendering/line-rendering-quality.html
@@ -0,0 +1,48 @@
+<!--
+
+/*
+** Copyright (c) 2018 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a
+** copy of this software and/or associated documentation files (the
+** "Materials"), to deal in the Materials without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Materials, and to
+** permit persons to whom the Materials are furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be included
+** in all copies or substantial portions of the Materials.
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+*/
+
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<title>Line rendering quality test</title>
+<meta charset="utf-8">
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+</head>
+<body>
+<canvas id="testbed" width="256" height="256"></canvas>
+<canvas id="testbed2" width="256" height="256"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+var contextVersion = 1;
+</script>
+<script src="../../js/tests/line-rendering-quality.js"></script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
--- a/dom/canvas/test/webgl-conf/checkout/conformance/rendering/multisample-corruption.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/rendering/multisample-corruption.html
@@ -44,17 +44,22 @@
 
 enableJSTestPreVerboseLogging();
 description(document.title);
 debug('Regression test for <a href="https://code.google.com/p/chromium/issues/detail?id=137303">Chromium bug 137303</a>');
 
 var wtu = WebGLTestUtils;
 
 var gl = wtu.create3DContext("example", {antialias: true, preserveDrawingBuffer: true});
-var test = IterableTest.createMultisampleCorruptionTest(gl);
-var iterations = parseInt(wtu.getUrlOptions().iterations, 10) || 25;
-IterableTest.run(test, iterations);
+if (!gl) {
+    testFailed("context does not exist");
+    finishTest();
+} else {
+    var test = IterableTest.createMultisampleCorruptionTest(gl);
+    var iterations = parseInt(wtu.getUrlOptions().iterations, 10) || 25;
+    IterableTest.run(test, iterations);
+}
 
 var successfullyParsed = true;
 </script>
 
 </body>
 </html>
--- a/dom/canvas/test/webgl-conf/checkout/conformance/rendering/preservedrawingbuffer-leak.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/rendering/preservedrawingbuffer-leak.html
@@ -44,17 +44,22 @@
 
 enableJSTestPreVerboseLogging();
 description(document.title);
 debug('Regression test for <a href="https://code.google.com/p/chromium/issues/detail?id=682482">Chromium bug 682482</a>');
 
 var wtu = WebGLTestUtils;
 
 var gl = wtu.create3DContext("example", {preserveDrawingBuffer: true});
-var test = IterableTest.createPreserveDrawingBufferLeakTest(gl);
-var iterations = parseInt(wtu.getUrlOptions().iterations, 10) || 50;
-IterableTest.run(test, iterations);
+if (!gl) {
+    testFailed("context does not exist");
+    finishTest();
+} else {
+    var test = IterableTest.createPreserveDrawingBufferLeakTest(gl);
+    var iterations = parseInt(wtu.getUrlOptions().iterations, 10) || 50;
+    IterableTest.run(test, iterations);
+}
 
 var successfullyParsed = true;
 </script>
 
 </body>
 </html>
--- a/dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/00_test_list.txt
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/00_test_list.txt
@@ -1,9 +1,10 @@
---max-version 1.9.9 compressed-tex-image.html
+--min-version 1.0.4 canvas-teximage-after-multiple-drawimages.html
+compressed-tex-image.html
 copy-tex-image-and-sub-image-2d.html
 --min-version 1.0.2 copy-tex-image-2d-formats.html
 --min-version 1.0.4 copy-tex-image-crash.html
 --min-version 1.0.4 copytexsubimage2d-large-partial-copy-corruption.html
 --min-version 1.0.4 copytexsubimage2d-subrects.html 
 --min-version 1.0.4 cube-incomplete-fbo.html
 --min-version 1.0.4 cube-map-uploads-out-of-order.html
 --min-version 1.0.3 default-texture.html
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/canvas-teximage-after-multiple-drawimages.html
@@ -0,0 +1,123 @@
+<!--
+
+/*
+** Copyright (c) 2018 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a
+** copy of this software and/or associated documentation files (the
+** "Materials"), to deal in the Materials without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Materials, and to
+** permit persons to whom the Materials are furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be included
+** in all copies or substantial portions of the Materials.
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+*/
+
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>TexImage2D of 2D Canvas after multiple drawImages</title>
+<link rel="stylesheet" href="../../../resources/js-test-style.css"/>
+<script src="../../../js/js-test-pre.js"></script>
+<script src="../../../js/webgl-test-utils.js"></script>
+<script>
+"use strict";
+const wtu = WebGLTestUtils;
+
+const sz = 512;
+let greenImage = null;
+
+function loadImageAndStart(startFunc) {
+  let c = document.createElement('canvas');
+  c.width = sz;
+  c.height = sz;
+  let ctx = c.getContext('2d');
+  ctx.fillStyle = 'rgb(0,255,0)';
+  ctx.fillRect(0, 0, sz, sz);
+  greenImage = wtu.makeImageFromCanvas(c, startFunc);
+}
+
+function runTest() {
+  description();
+  debug('Regression test for <a href="http://crbug.com/878545">http://crbug.com/878545</a>');
+  // Mimics the image-to-texture upload path from the old JavaScript
+  // port of O3D, salvaged at https://github.com/petersont/o3d .
+
+  // Create a canvas and draw the entire image into it.
+  let bigCanvas = document.createElement('canvas');
+  bigCanvas.width = greenImage.naturalWidth;
+  bigCanvas.height = greenImage.naturalHeight;
+  let bc = bigCanvas.getContext('2d');
+  bc.drawImage(greenImage, 0, 0, bigCanvas.width, bigCanvas.height);
+  // Create a temp canvas to flip vertically.
+  let tempCanvas = document.createElement('canvas');
+  tempCanvas.width = bigCanvas.width;
+  tempCanvas.height = bigCanvas.height;
+  let tc = tempCanvas.getContext('2d');
+  tc.translate(0, tempCanvas.height);
+  tc.scale(1, -1);
+  tc.drawImage(bigCanvas, 0, 0, tempCanvas.width, tempCanvas.height);
+  // Set up to draw via WebGL.
+  let c3d = document.getElementById('canvas');
+  let gl = wtu.create3DContext(c3d);
+  let prog = wtu.setupTexturedQuad(gl);
+  gl.uniform1i(gl.getUniformLocation(prog, 'tex'), 0);
+  let tex = gl.createTexture();
+  // Iterate through the large canvas, drawing pieces of it to a
+  // scratch canvas.
+  let scratchCanvas = document.createElement('canvas');
+  const width = tempCanvas.width / 2;
+  const height = tempCanvas.height / 2;
+  let bb = new Uint8Array(4 * c3d.width * c3d.height);
+  for (let jj = 0; jj < 2; ++jj) {
+    for (let ii = 0; ii < 2; ++ii) {
+      // Prepare texture.
+      scratchCanvas.width = width;
+      scratchCanvas.height = height;
+      let ctx = scratchCanvas.getContext('2d');
+      ctx.save();
+      ctx.translate(-(ii * width), -(jj * height));
+      ctx.scale(1.0, 1.0);
+      ctx.drawImage(tempCanvas, 0, 0, tempCanvas.width, tempCanvas.height);
+      gl.bindTexture(gl.TEXTURE_2D, tex);
+      gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE,
+                    scratchCanvas);
+      ctx.restore();
+      gl.generateMipmap(gl.TEXTURE_2D);
+      // Draw and check that rendering occurred properly.
+      gl.uniform1i(gl.getUniformLocation(prog, 'tex'), 0);
+      wtu.drawUnitQuad(gl);
+      let tolerance = 2;
+      wtu.checkCanvasRect(gl, 1, 1, c3d.width - 2, c3d.height - 2,
+                          [ 0, 255, 0, 255 ],
+                          "should be green",
+                          tolerance);
+
+    }
+  }
+
+  finishTest();
+}
+
+loadImageAndStart(runTest);
+</script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<canvas id="canvas" width="40" height="40"></canvas>
+</body>
+</html>
--- a/dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/compressed-tex-image.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/compressed-tex-image.html
@@ -24,54 +24,46 @@
 */
 
 -->
 
 <!DOCTYPE html>
 <html>
 <head>
 <meta charset="utf-8">
-<title>WebGL CompressedTexImage and CompressedTexSubImage Tests</title>
+<title>WebGL compressed texture test</title>
 <LINK rel="stylesheet" href="../../../resources/js-test-style.css"/>
 <script src="../../../js/js-test-pre.js"></script>
 <script src="../../../js/webgl-test-utils.js"></script>
 </head>
 <body>
 <div id="description"></div>
 <div id="console"></div>
 <script>
 "use strict";
-description("This test ensures WebGL implementations correctly implement compressedTexImage2D and compressedTexSubImage2D.");
+description("This test ensures WebGL implementations correctly implement querying for compressed textures when extensions are disabled.");
 
 debug("");
 
 var wtu = WebGLTestUtils;
 var gl = wtu.create3DContext();
 
-var COMPRESSED_RGB_S3TC_DXT1_EXT        = 0x83F0;
-var COMPRESSED_RGBA_S3TC_DXT1_EXT       = 0x83F1;
-var COMPRESSED_RGBA_S3TC_DXT5_EXT       = 0x83F3;
-var ETC1_RGB8_OES                       = 0x8D64;
 var COMPRESSED_RGB_PVRTC_4BPPV1_IMG     = 0x8C00;
 var COMPRESSED_RGBA_PVRTC_4BPPV1_IMG    = 0x8C02;
 
 var formats = null;
 
 if (!gl) {
   testFailed("context does not exist");
 } else {
   testPassed("context exists");
 
   var tex = gl.createTexture();
   gl.bindTexture(gl.TEXTURE_2D, tex);
 
-  wtu.shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGB_S3TC_DXT1_EXT, 4, 4, 0, new Uint8Array(8))");
-  wtu.shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGBA_S3TC_DXT1_EXT, 4, 4, 0, new Uint8Array(8))");
-  wtu.shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGBA_S3TC_DXT5_EXT, 4, 4, 0, new Uint8Array(16))");
-  wtu.shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, ETC1_RGB8_OES, 4, 4, 0, new Uint8Array(8))");
   wtu.shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGB_PVRTC_4BPPV1_IMG, 8, 8, 0, new Uint8Array(8))");
   wtu.shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, 8, 8, 0, new Uint8Array(8))");
 
   wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "formats = gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS)");
   shouldBeNonNull("formats");
   shouldBe("formats.length", "0");
 }
 
--- a/dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/tex-video-using-tex-unit-non-zero.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/tex-video-using-tex-unit-non-zero.html
@@ -33,17 +33,17 @@
 <link rel="stylesheet" href="../../../resources/js-test-style.css"/>
 <script src="../../../js/js-test-pre.js"></script>
 <script src="../../../js/webgl-test-utils.js"></script>
 </head>
 <body>
 <canvas id="example" width="640" height="480"></canvas>
 <div id="description"></div>
 <div id="console"></div>
-<img id="src-image" src="data:type/png;base64,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">
+<img id="src-image">
 <script id="shader-fs" type="x-shader/x-fragment">
     precision mediump float;
 
     uniform sampler2D uTexture0;
     uniform sampler2D uTexture1;
 
     varying vec2 vUV;
 
@@ -64,157 +64,170 @@
         vUV = aTextureCoord;
         gl_Position = vec4(aVertexPosition, 1.0);
     }
 </script>
 
 <script>
 "use strict";
 
-var wtu = WebGLTestUtils;
-description("Regression test for crbug.com/676719");
+function test() {
+  var wtu = WebGLTestUtils;
+  description("Regression test for crbug.com/676719");
 
-var gl = wtu.create3DContext("example", {preserveDrawingBuffer: true});
+  var gl = wtu.create3DContext("example", {preserveDrawingBuffer: true});
 
-var program = wtu.setupProgram(gl, ["shader-vs", "shader-fs"],
-                               ["aVertexPosition", "aTextureCoord"], [0, 1]);
+  if (!gl) {
+    testFailed("context does not exist");
+    finishTest();
+    return;
+  }
 
-if (!gl || !program) {
-  testFailed("Test setup failed");
-}
+  var program = wtu.setupProgram(gl, ["shader-vs", "shader-fs"],
+                                ["aVertexPosition", "aTextureCoord"], [0, 1]);
+
+  if (!program) {
+    testFailed("program does not exist");
+    finishTest();
+    return;
+  }
 
-var loc0 = gl.getUniformLocation(program, "uTexture0");
-var loc1 = gl.getUniformLocation(program, "uTexture1");
-if (!loc0 || !loc1) {
-  testFailed("Failed to query uniform locations");
-}
-gl.uniform1i(loc0, 0);
-gl.uniform1i(loc1, 1);
+  var loc0 = gl.getUniformLocation(program, "uTexture0");
+  var loc1 = gl.getUniformLocation(program, "uTexture1");
+  if (!loc0 || !loc1) {
+    testFailed("Failed to query uniform locations");
+    finishTest();
+    return;
+  }
+  gl.uniform1i(loc0, 0);
+  gl.uniform1i(loc1, 1);
+
+  wtu.setupUnitQuad(gl, 0, 1);
 
-wtu.setupUnitQuad(gl, 0, 1);
+  // Load textures
+  var imageSrc = "data:type/png;base64,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";
+  var imageElement = document.getElementById("src-image");
+  var resourcePath = "../../../resources/";
 
-// Load textures
-var imageElement = document.getElementById("src-image");
-var resourcePath = "../../../resources/";
+  var videos = [
+    { src: resourcePath + "red-green.mp4",
+      type: 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"' },
+    { src: resourcePath + "red-green.webmvp8.webm",
+      type: 'video/webm; codecs="vp8, vorbis"' },
+    { src: resourcePath + "red-green.bt601.vp9.webm",
+      type: 'video/webm; codecs="vp9"' },
+    { src: resourcePath + "red-green.theora.ogv",
+      type: 'video/ogg; codecs="theora, vorbis"' },
+  ];
+  var currentVideo = null;
 
-var videos = [
-  { src: resourcePath + "red-green.mp4",
-    type: 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"' },
-  { src: resourcePath + "red-green.webmvp8.webm",
-    type: 'video/webm; codecs="vp8, vorbis"' },
-  { src: resourcePath + "red-green.bt601.vp9.webm",
-    type: 'video/webm; codecs="vp9"' },
-  { src: resourcePath + "red-green.theora.ogv",
-    type: 'video/ogg; codecs="theora, vorbis"' },
-];
-
-function runAllTests() {
-  return new Promise(function(resolve, reject) {
-    var videoNdx = 0;
-    var video;
-    function runNextVideo() {
-      if (video) {
+  function runAllTests() {
+    return new Promise(function(resolve, reject) {
+      var videoNdx = 0;
+      var video;
+      function runNextVideo() {
+        if (video) {
+          video.pause();
+        }
+        if (videoNdx == videos.length) {
+          resolve("SUCCESS");
+          return;
+        }
+        var info = videos[videoNdx++];
+        currentVideo = info;
+        debug("");
+        debug("testing: " + info.type);
+        video = document.createElement("video");
+        var canPlay = true;
+        if (!video.canPlayType) {
+          testFailed("video.canPlayType required method missing");
+          runNextVideo();
+          return;
+        }
+        if(!video.canPlayType(info.type).replace(/no/, '')) {
+          debug(info.type + " unsupported");
+          runNextVideo();
+          return;
+        };
+        document.body.appendChild(video);
+        video.type = info.type;
+        video.src = info.src;
+        wtu.startPlayingAndWaitForVideo(video, runTest);
+      }
+      function runTest() {
         video.pause();
-      }
-      if (videoNdx == videos.length) {
-        resolve("SUCCESS");
-        return;
-      }
-      var info = videos[videoNdx++];
-      debug("");
-      debug("testing: " + info.type);
-      video = document.createElement("video");
-      var canPlay = true;
-      if (!video.canPlayType) {
-        testFailed("video.canPlayType required method missing");
-        runNextVideo();
-        return;
-      }
-      if(!video.canPlayType(info.type).replace(/no/, '')) {
-        debug(info.type + " unsupported");
+
+        var texture0 = gl.createTexture();
+        gl.bindTexture(gl.TEXTURE_2D, texture0);
+        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, video);
+        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+        gl.bindTexture(gl.TEXTURE_2D, null);
+
+        var texture1 = gl.createTexture();
+        gl.bindTexture(gl.TEXTURE_2D, texture1);
+        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, imageElement);
+        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+        gl.bindTexture(gl.TEXTURE_2D, null);
+
+        // <-- Begin Observed -->
+        gl.activeTexture(gl.TEXTURE1);
+        gl.bindTexture(gl.TEXTURE_2D, texture1);
+
+        gl.activeTexture(gl.TEXTURE0);
+        gl.bindTexture(gl.TEXTURE_2D, texture0);
+        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, video);
+
+        wtu.clearAndDrawUnitQuad(gl, [255, 0, 255, 255]);
+
+        var observedPixels = new Uint8Array(640 * 480 * 4);
+        gl.readPixels(0, 0, 640, 480, gl.RGBA, gl.UNSIGNED_BYTE, observedPixels);
+        // <-- End Observed -->
+
+        // <-- Begin Expected -->
+        gl.activeTexture(gl.TEXTURE0);
+        gl.bindTexture(gl.TEXTURE_2D, texture0);
+        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, video);
+
+        gl.activeTexture(gl.TEXTURE1);
+        gl.bindTexture(gl.TEXTURE_2D, texture1);
+
+        wtu.clearAndDrawUnitQuad(gl, [255, 0, 255, 255]);
+
+        var expectedPixels = new Uint8Array(640 * 480 * 4);
+        gl.readPixels(0, 0, 640, 480, gl.RGBA, gl.UNSIGNED_BYTE, expectedPixels);
+        // <-- End Expected -->
+
+        // Compare results
+        var numDifferentPixels = wtu.comparePixels(observedPixels, expectedPixels, 0, undefined);
+        if (numDifferentPixels) {
+          testFailed("Texture states were incorrect for " + currentVideo.type +
+                    ", rendering was wrong: found " + numDifferentPixels + " differing pixels");
+        } else {
+          testPassed("Texture states were correct, rendering as expected");
+        }
+        wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+
         runNextVideo();
-        return;
-      };
-      document.body.appendChild(video);
-      video.type = info.type;
-      video.src = info.src;
-      wtu.startPlayingAndWaitForVideo(video, runTest);
-    }
-    function runTest() {
-      video.pause();
-
-      var texture0 = gl.createTexture();
-      gl.bindTexture(gl.TEXTURE_2D, texture0);
-      gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, video);
-      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
-      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
-      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
-      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
-      gl.bindTexture(gl.TEXTURE_2D, null);
-
-      var texture1 = gl.createTexture();
-      gl.bindTexture(gl.TEXTURE_2D, texture1);
-      gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, imageElement);
-      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
-      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
-      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
-      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
-      gl.bindTexture(gl.TEXTURE_2D, null);
-
-      // <-- Begin Observed -->
-      gl.activeTexture(gl.TEXTURE1);
-      gl.bindTexture(gl.TEXTURE_2D, texture1);
-
-      gl.activeTexture(gl.TEXTURE0);
-      gl.bindTexture(gl.TEXTURE_2D, texture0);
-      gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, video);
-
-      wtu.clearAndDrawUnitQuad(gl, [255, 0, 255, 255]);
-
-      var observedPixels = new Uint8Array(640 * 480 * 4);
-      gl.readPixels(0, 0, 640, 480, gl.RGBA, gl.UNSIGNED_BYTE, observedPixels);
-      // <-- End Observed -->
-
-      // <-- Begin Expected -->
-      gl.activeTexture(gl.TEXTURE0);
-      gl.bindTexture(gl.TEXTURE_2D, texture0);
-      gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, video);
-
-      gl.activeTexture(gl.TEXTURE1);
-      gl.bindTexture(gl.TEXTURE_2D, texture1);
-
-      wtu.clearAndDrawUnitQuad(gl, [255, 0, 255, 255]);
-
-      var expectedPixels = new Uint8Array(640 * 480 * 4);
-      gl.readPixels(0, 0, 640, 480, gl.RGBA, gl.UNSIGNED_BYTE, expectedPixels);
-      // <-- End Expected -->
-
-      // Compare results
-      var compareOk = true;
-      for (var index = 0; index < expectedPixels.length; ++index) {
-        if (observedPixels[index] != expectedPixels[index]) {
-          compareOk = false;
-          break;
-        }
       }
-      if (compareOk) {
-        testPassed("Texture states were correct, rendering as expected");
-      } else {
-        testFailed("Texture states were incorrect, rendering was wrong");
-      }
-      wtu.glErrorShouldBe(gl, gl.NO_ERROR);
 
       runNextVideo();
-    }
+    });
+  }
 
-    runNextVideo();
-  });
+  imageElement.onload = function() {
+    runAllTests().then(function(val) {
+      debug("");
+      finishTest();
+    });
+  };
+  imageElement.src = imageSrc;
 }
-
-runAllTests().then(function(val) {
-  debug("");
-  finishTest();
-});
+test();
 </script>
 
 </body>
 </html>
-
--- a/dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/texture-size-limit.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/texture-size-limit.html
@@ -114,17 +114,17 @@ function runNextTest() {
   if (tt == tests.length) {
     tt = 0;
     ++trg;
     if (trg == targets.length) {
       finishTest();
       return;
     }
   }
-  wtu.waitForComposite(runNextTest)
+  wtu.dispatchTask(runNextTest);
 }
 
 function testFormatType(t, test) {
   debug("");
   debug("testing: " + wtu.glEnumToString(gl, test.format) + ", " + wtu.glEnumToString(gl, test.type));
 
   for (var j = 0; j < t.targets.length; ++j) {
     var target = t.targets[j];
--- a/dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/texture-upload-size.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/texture-upload-size.html
@@ -130,16 +130,19 @@ var runNextTest = function() {
     debug("");
     var test = tests[testIndex];
     ++testIndex;
     if (test.type == "img") {
       debug("HTMLImageElement" + (test.isSVG ? " (SVG)" : ""));
       var img = wtu.makeImage(test.src, function() {
         testImage(test, img);
         setTimeout(runNextTest, 0);
+      }, function () {
+        testFailed("could not create image" + (test.isSVG ? " (SVG)" : ""));
+        setTimeout(runNextTest, 0);
       });
     } else if (test.type == "video") {
       debug("HTMLVideoElement (" + test.videoType + ")");
       var video = wtu.makeVideo(test.src);
       if(!video.canPlayType(test.videoType).replace(/no/, '')) {
         debug(test.videoType + " unsupported");
         setTimeout(runNextTest, 0);
         return;
--- a/dom/canvas/test/webgl-conf/checkout/conformance2/attribs/00_test_list.txt
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/attribs/00_test_list.txt
@@ -1,6 +1,7 @@
 --min-version 2.0.1 gl-bindAttribLocation-aliasing-inactive.html
 gl-vertex-attrib.html
 gl-vertex-attrib-i-render.html
 --min-version 2.0.1 gl-vertex-attrib-normalized-int.html
 gl-vertexattribipointer.html
 gl-vertexattribipointer-offsets.html
+--min-version 2.0.1 render-no-enabled-attrib-arrays.html
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/attribs/render-no-enabled-attrib-arrays.html
@@ -0,0 +1,87 @@
+<!--
+
+/*
+** Copyright (c) 2018 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a
+** copy of this software and/or associated documentation files (the
+** "Materials"), to deal in the Materials without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Materials, and to
+** permit persons to whom the Materials are furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be included
+** in all copies or substantial portions of the Materials.
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+*/
+
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<title>Verify drawing without any enabled vertex attribute arrays</title>
+<meta charset="utf-8">
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+<script id='vshader' type='x-shader/x-vertex'>#version 300 es
+layout(location=0) in vec4 inColor;
+out vec4 varyingColor;
+void main()
+{
+    varyingColor = inColor;
+    gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
+    gl_PointSize = 1.0;
+}
+</script>
+<script id='fshader' type='x-shader/x-fragment'>#version 300 es
+precision mediump float;
+in vec4 varyingColor;
+layout(location=0) out vec4 oColor;
+void main()
+{
+    oColor = varyingColor;
+}
+</script>
+<script>
+"use strict";
+
+function runTest() {
+    var wtu = WebGLTestUtils;
+    var gl = wtu.create3DContext("testCanvas", undefined, 2);
+    if (!gl) {
+        testFailed('could not create context');
+        return;
+    }
+    var program = wtu.setupProgram(gl, ['vshader', 'fshader']);
+    gl.disableVertexAttribArray(0);
+    gl.vertexAttrib4f(0, 0.0, 1.0, 0.0, 1.0);
+    gl.clearColor(1, 0, 0, 1);
+    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+    gl.drawArrays(gl.POINTS, 0, 1);
+    wtu.checkCanvas(gl, [ 0, 255, 0, 255 ], "Canvas should be covered by a single green point");
+}
+</script>
+</head>
+<body>
+<canvas id="testCanvas" width="1" height="1" style="width: 32px; height: 32px;"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+description();
+runTest();
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
--- a/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/00_test_list.txt
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/00_test_list.txt
@@ -1,12 +1,13 @@
 bound-buffer-size-change-test.html
 buffer-copying-contents.html
 buffer-copying-restrictions.html
 buffer-data-and-buffer-sub-data-sub-source.html
 buffer-type-restrictions.html
 buffer-overflow-test.html
 --min-version 2.0.1 delete-buffer.html
 get-buffer-sub-data.html
+--min-version 2.0.1 get-buffer-sub-data-validity.html
 one-large-uniform-buffer.html
 uniform-buffers.html
 --min-version 2.0.1 uniform-buffers-second-compile.html
 --min-version 2.0.1 uniform-buffers-state-restoration.html
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/get-buffer-sub-data-validity.html
@@ -0,0 +1,264 @@
+<!--
+
+/*
+** Copyright (c) 2018 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a
+** copy of this software and/or associated documentation files (the
+** "Materials"), to deal in the Materials without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Materials, and to
+** permit persons to whom the Materials are furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be included
+** in all copies or substantial portions of the Materials.
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+*/
+
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL2 getBufferSubData validity tests</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"> </script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+
+<script id="vshader" type="x-shader/x-vertex">#version 300 es
+in uint in_data;
+flat out uint out_data;
+void main() {
+    out_data = in_data;
+}
+</script>
+<script id="fshader" type="x-shader/x-fragment">#version 300 es
+void main() {}
+</script>
+
+<script>
+"use strict";
+description("Test that getBufferSubData returns valid data in edge cases");
+
+var wtu = WebGLTestUtils;
+
+var gl = wtu.create3DContext(undefined, undefined, 2);
+
+const srcData = new Uint8Array([ 1, 2, 3, 4, 5, 6, 7, 8 ]);
+const noData = new Uint8Array(8);
+
+const srcBuffer = gl.createBuffer();
+gl.bindBuffer(gl.COPY_READ_BUFFER, srcBuffer);
+gl.bufferData(gl.COPY_READ_BUFFER, srcData, gl.STATIC_DRAW);
+
+const badBuffer = gl.createBuffer();
+gl.bindBuffer(gl.ARRAY_BUFFER, badBuffer);
+gl.bufferData(gl.ARRAY_BUFFER, 8, gl.STATIC_DRAW);
+
+let readbackBuffer;
+function deleteReadbackBuffer() {
+    gl.deleteBuffer(readbackBuffer);
+}
+function recreateReadbackBuffer() {
+    readbackBuffer = gl.createBuffer();
+    gl.bindBuffer(gl.COPY_WRITE_BUFFER, readbackBuffer);
+    gl.bufferData(gl.COPY_WRITE_BUFFER, 8, gl.STREAM_READ);
+}
+recreateReadbackBuffer();
+
+const dest = new Uint8Array(8);
+
+// Makes a new "resolvable" Promise
+function resolvable() {
+    let resolve;
+    const promise = new Promise(res => { resolve = res; });
+    promise.resolve = resolve;
+    return promise;
+}
+
+function fence() {
+    const promise = resolvable();
+
+    const sync = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0);
+    gl.flush();
+    function check() {
+        const status = gl.clientWaitSync(sync, 0, 0);
+        if (status == gl.ALREADY_SIGNALED || status == gl.CONDITION_SATISFIED) {
+            gl.deleteSync(sync);
+            promise.resolve();
+        } else {
+            setTimeout(check, 0);
+        }
+    }
+    setTimeout(check, 0);
+
+    return promise;
+}
+
+function checkGetBufferSubData(err, data) {
+    dest.fill(0);
+    wtu.shouldGenerateGLError(gl, err, "gl.getBufferSubData(gl.COPY_WRITE_BUFFER, 0, dest)");
+    if (!err) {
+        shouldBeTrue(`areArraysEqual(dest, ${data})`);
+    }
+}
+
+const tfProgram = wtu.setupTransformFeedbackProgram(gl, ["vshader", "fshader"],
+        ["out_data"], gl.SEPARATE_ATTRIBS,
+        ["in_data"]);
+wtu.glErrorShouldBe(gl, gl.NO_ERROR, "linking transform feedback shader should not set an error");
+shouldBeNonNull("tfProgram");
+const tf = gl.createTransformFeedback();
+
+function copyBufferUsingTransformFeedback(src, dst) {
+    gl.enableVertexAttribArray(0);
+    gl.bindBuffer(gl.ARRAY_BUFFER, src);
+    gl.vertexAttribIPointer(0, 1, gl.UNSIGNED_INT, 0, 0);
+
+    gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
+    gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, dst);
+
+    gl.enable(gl.RASTERIZER_DISCARD);
+    gl.beginTransformFeedback(gl.POINTS);
+    // treats the input and output data as two uint32s
+    gl.drawArrays(gl.POINTS, 0, 2);
+    gl.endTransformFeedback();
+    gl.disable(gl.RASTERIZER_DISCARD);
+
+    gl.bindBuffer(gl.ARRAY_BUFFER, badBuffer);
+    gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, null);
+    gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);
+}
+
+Promise.resolve()
+    .then(() => {
+        debug("");
+        debug("write-read");
+        gl.copyBufferSubData(gl.COPY_READ_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, 8);
+        checkGetBufferSubData(gl.NO_ERROR, "srcData");
+    })
+    .then(() => {
+        debug("");
+        debug("fence-wait-write-read");
+        return fence().then(() => {
+            gl.copyBufferSubData(gl.COPY_READ_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, 8);
+            checkGetBufferSubData(gl.NO_ERROR, "srcData");
+        });
+    })
+    .then(() => {
+        debug("");
+        debug("write-read-fence-wait");
+        gl.copyBufferSubData(gl.COPY_READ_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, 8);
+        checkGetBufferSubData(gl.NO_ERROR, "srcData");
+        return fence();
+    })
+    .then(() => {
+        debug("");
+        debug("write-fence-fence-wait-read");
+        gl.copyBufferSubData(gl.COPY_READ_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, 8);
+        fence();
+        return fence().then(() => {
+            checkGetBufferSubData(gl.NO_ERROR, "srcData");
+        });
+    })
+    .then(() => {
+        debug("");
+        debug("write-fence-wait-read");
+        gl.copyBufferSubData(gl.COPY_READ_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, 8);
+        return fence().then(() => {
+            checkGetBufferSubData(gl.NO_ERROR, "srcData");
+        });
+    })
+    .then(() => {
+        debug("");
+        debug("write-fence-wait-write-read");
+        gl.copyBufferSubData(gl.ARRAY_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, 8);
+        return fence().then(() => {
+            gl.copyBufferSubData(gl.COPY_READ_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, 8);
+            checkGetBufferSubData(gl.NO_ERROR, "srcData");
+        });
+    })
+    .then(() => {
+        debug("");
+        debug("write-fence-write-wait-read");
+        gl.copyBufferSubData(gl.ARRAY_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, 8);
+        const p = fence();
+        gl.copyBufferSubData(gl.COPY_READ_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, 8);
+        return p.then(() => {
+            checkGetBufferSubData(gl.NO_ERROR, "srcData");
+        });
+    })
+    .then(() => {
+        debug("");
+        debug("write-fence-transformfeedback-wait-read");
+        gl.copyBufferSubData(gl.ARRAY_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, 8);
+        const p = fence();
+        gl.bindBuffer(gl.COPY_WRITE_BUFFER, null);
+        copyBufferUsingTransformFeedback(srcBuffer, readbackBuffer);
+        gl.bindBuffer(gl.COPY_WRITE_BUFFER, readbackBuffer);
+        return p.then(() => {
+            checkGetBufferSubData(gl.NO_ERROR, "srcData");
+        });
+    })
+    .then(() => {
+        debug("");
+        debug("write-unbind-fence-wait-bind-read");
+        gl.bindBuffer(gl.COPY_WRITE_BUFFER, null);
+        gl.bindBuffer(gl.ARRAY_BUFFER, readbackBuffer);
+        gl.copyBufferSubData(gl.COPY_READ_BUFFER, gl.ARRAY_BUFFER, 0, 0, 8);
+        gl.bindBuffer(gl.ARRAY_BUFFER, badBuffer);
+        return fence().then(() => {
+            gl.bindBuffer(gl.COPY_WRITE_BUFFER, readbackBuffer);
+            checkGetBufferSubData(gl.NO_ERROR, "srcData");
+        });
+    })
+    .then(() => {
+        debug("");
+        debug("write-fence-wait-delete-read");
+        gl.copyBufferSubData(gl.ARRAY_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, 8);
+        return fence().then(() => {
+            deleteReadbackBuffer();
+            checkGetBufferSubData(gl.INVALID_OPERATION, "noData");
+            recreateReadbackBuffer();
+        });
+    })
+    .then(() => {
+        debug("");
+        debug("write-fence-delete-wait-read");
+        gl.copyBufferSubData(gl.ARRAY_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, 8);
+        const p = fence();
+        deleteReadbackBuffer();
+        return p.then(() => {
+            checkGetBufferSubData(gl.INVALID_OPERATION, "noData");
+            recreateReadbackBuffer();
+        });
+    })
+    .then(() => {
+        debug("");
+        debug("write-fence-delete-wait-read");
+        gl.copyBufferSubData(gl.ARRAY_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, 8);
+        deleteReadbackBuffer();
+        return fence().then(() => {
+            checkGetBufferSubData(gl.INVALID_OPERATION, "noData");
+            recreateReadbackBuffer();
+        });
+    })
+    .then(finishTest);
+
+var successfullyParsed = true;
+</script>
+</body>
+</html>
--- a/dom/canvas/test/webgl-conf/checkout/conformance2/context/00_test_list.txt
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/context/00_test_list.txt
@@ -1,8 +1,10 @@
 constants-and-properties-2.html
 context-attributes-depth-stencil-antialias-obeyed.html
+--min-version 2.0.1 context-mode.html
 --min-version 2.0.1 context-sharing-texture2darray-texture3d-data-bug.html
 context-type-test-2.html
 --min-version 2.0.1 context-resize-changes-buffer-binding-bug.html
+--min-version 2.0.1 incorrect-context-object-behaviour.html
 methods-2.html
 --min-version 2.0.1 no-experimental-webgl2.html
 
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/context/context-mode.html
@@ -0,0 +1,77 @@
+<!--
+
+/*
+** Copyright (c) 2018 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a
+** copy of this software and/or associated documentation files (the
+** "Materials"), to deal in the Materials without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Materials, and to
+** permit persons to whom the Materials are furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be included
+** in all copies or substantial portions of the Materials.
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+*/
+
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL2 Canvas Context Mode Conformance Tests</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+description("This test ensures WebGL 2.0 implementations respect the canvas's context mode.");
+
+debug("");
+
+assertMsg(window.WebGLRenderingContext,
+          "WebGL2RenderingContext should be a member of window");
+assertMsg('WebGL2RenderingContext' in window,
+          "WebGL2RenderingContext should be 'in' window");
+
+function testContextMode(mode, altMode) {
+  debug("Testing " + mode + " context type");
+
+  let c = document.createElement('canvas');
+  c.width = 2;
+  c.height = 2;
+  let gl = c.getContext(mode);
+  assertMsg(c.getContext(mode) == gl,
+            "Canvas.getContext('" + mode + "') should return the same value every time");
+  try {
+    assertMsg(c.getContext(altMode) == null,
+              "Canvas.getContext('" + altMode + "') after getContext('" + mode + "') should return null");
+  } catch (e) {
+    testFailed("Canvas.getContext('" + altMode + "') after getContext('" + mode + "') should not throw an exception");
+  }
+}
+
+testContextMode('webgl2', 'webgl');
+testContextMode('webgl', 'webgl2');
+
+debug("");
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/context/incorrect-context-object-behaviour.html
@@ -0,0 +1,242 @@
+<!--
+
+/*
+** Copyright (c) 2018 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a
+** copy of this software and/or associated documentation files (the
+** "Materials"), to deal in the Materials without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Materials, and to
+** permit persons to whom the Materials are furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be included
+** in all copies or substantial portions of the Materials.
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+*/
+
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+
+<script id="tf-vshader" type="x-shader/x-vertex">#version 300 es
+in uint in_data;
+flat out uint out_data;
+void main() {
+    out_data = in_data;
+}
+</script>
+<script id="tf-fshader" type="x-shader/x-fragment">#version 300 es
+void main() {}
+</script>
+
+<script>
+"use strict";
+description("Tests calling WebGL 2.0 APIs with objects from other contexts");
+
+function loadTFProgram(context) {
+    const tfProgram = wtu.setupTransformFeedbackProgram(context, ["tf-vshader", "tf-fshader"],
+        ["out_data"], context.SEPARATE_ATTRIBS,
+        ["in_data"]);
+    wtu.glErrorShouldBe(context, context.NO_ERROR, "linking transform feedback shader should not set an error");
+    return tfProgram;
+}
+
+function create2DTextureArray4x4x4(context) {
+    const texture = context.createTexture();
+    context.bindTexture(context.TEXTURE_2D_ARRAY, texture);
+    context.texImage3D(context.TEXTURE_2D_ARRAY, 0, context.RGBA8, 4, 4, 4, 0, context.RGBA, context.UNSIGNED_BYTE, null);
+    wtu.glErrorShouldBe(context, context.NO_ERROR, "allocating 2D texture array should not set an error");
+    return texture;
+}
+
+var wtu = WebGLTestUtils;
+var contextA = wtu.create3DContext(undefined, undefined, 2);
+var contextB = wtu.create3DContext(undefined, undefined, 2);
+var bufferA = contextA.createBuffer();
+var bufferB = contextB.createBuffer();
+var frameBufferA = contextA.createFramebuffer();
+var frameBufferB = contextB.createFramebuffer();
+var programB = wtu.loadStandardProgram(contextB);
+var queryB = contextB.createQuery();
+var samplerB = contextB.createSampler();
+var syncB = contextB.fenceSync(contextB.SYNC_GPU_COMMANDS_COMPLETE, 0);
+var transformFeedbackA = contextA.createTransformFeedback();
+var transformFeedbackB = contextB.createTransformFeedback();
+var vertexArrayA = contextA.createVertexArray();
+var vertexArrayB = contextB.createVertexArray();
+var tfProgramB = loadTFProgram(contextB);
+var texture2DArrayA = create2DTextureArray4x4x4(contextA);
+var texture2DArrayB = create2DTextureArray4x4x4(contextB);
+var locationB = contextB.getUniformLocation(programB, 'u_modelViewProjMatrix');
+var uniformData = [];
+
+function generateFloat32Array(length) {
+    uniformData = new Float32Array(length);
+}
+
+function generateFloatArray(length) {
+    uniformData = new Array(length);
+    for (var i = 0; i < length; i++) {
+        uniformData[i] = 0.0;
+    }
+}
+
+function generateInt32Array(length) {
+    uniformData = new Int32Array(length);
+}
+
+function generateIntArray(length) {
+    uniformData = new Array(length);
+    for (var i = 0; i < length; i++) {
+        uniformData[i] = 0;
+    }
+}
+
+function generateUint32Array(length) {
+    uniformData = new Uint32Array(length);
+}
+
+// Make the bindable objects valid in both contexts first.
+wtu.shouldGenerateGLError(contextA, contextA.NO_ERROR, "contextA.bindBuffer(contextA.ARRAY_BUFFER, bufferA)");
+wtu.shouldGenerateGLError(contextA, contextA.NO_ERROR, "contextA.bindBuffer(contextA.ARRAY_BUFFER, null)");
+wtu.shouldGenerateGLError(contextB, contextB.NO_ERROR, "contextB.bindBuffer(contextB.ARRAY_BUFFER, bufferB)");
+wtu.shouldGenerateGLError(contextB, contextB.NO_ERROR, "contextB.bindBuffer(contextB.ARRAY_BUFFER, null)");
+wtu.shouldGenerateGLError(contextA, contextA.NO_ERROR, "contextA.bindTransformFeedback(contextA.TRANSFORM_FEEDBACK, transformFeedbackA)");
+wtu.shouldGenerateGLError(contextA, contextA.NO_ERROR, "contextA.bindTransformFeedback(contextA.TRANSFORM_FEEDBACK, null)");
+wtu.shouldGenerateGLError(contextB, contextB.NO_ERROR, "contextB.bindTransformFeedback(contextB.TRANSFORM_FEEDBACK, transformFeedbackB)");
+wtu.shouldGenerateGLError(contextB, contextB.NO_ERROR, "contextB.bindTransformFeedback(contextB.TRANSFORM_FEEDBACK, null)");
+wtu.shouldGenerateGLError(contextA, contextA.NO_ERROR, "contextA.bindVertexArray(vertexArrayA)");
+wtu.shouldGenerateGLError(contextA, contextA.NO_ERROR, "contextA.bindVertexArray(null)");
+wtu.shouldGenerateGLError(contextB, contextB.NO_ERROR, "contextB.bindVertexArray(vertexArrayB)");
+wtu.shouldGenerateGLError(contextB, contextB.NO_ERROR, "contextB.bindVertexArray(null)");
+
+wtu.shouldGenerateGLError(contextA, contextA.NO_ERROR, "contextA.bindFramebuffer(contextA.FRAMEBUFFER, frameBufferA)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.framebufferTextureLayer(contextA.FRAMEBUFFER, contextA.COLOR_ATTACHMENT0, texture2DArrayB, 0, 0)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.getFragDataLocation(tfProgramB, 'nonexistent_variable')");
+// Unsigned int uniforms
+                        wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform1ui(locationB, 0)");
+generateUint32Array(1); wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform1uiv(locationB, uniformData)");
+generateIntArray(1);    wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform1uiv(locationB, uniformData)");
+                        wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform2ui(locationB, 0, 0)");
+generateUint32Array(2); wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform2uiv(locationB, uniformData)");
+generateIntArray(2);    wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform2uiv(locationB, uniformData)");
+                        wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform3ui(locationB, 0, 0, 0)");
+generateUint32Array(3); wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform3uiv(locationB, uniformData)");
+generateIntArray(3);    wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform3uiv(locationB, uniformData)");
+                        wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform4ui(locationB, 0, 0, 0, 0)");
+generateUint32Array(4); wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform4uiv(locationB, uniformData)");
+generateIntArray(4);    wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform4uiv(locationB, uniformData)");
+// New uniform entry points with offsets
+generateFloat32Array(2);  wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform1fv(locationB, uniformData, 1)");
+generateFloatArray(2);    wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform1fv(locationB, uniformData, 1)");
+generateInt32Array(2);    wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform1iv(locationB, uniformData, 1)");
+generateIntArray(2);      wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform1iv(locationB, uniformData, 1)");
+generateFloat32Array(3);  wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform2fv(locationB, uniformData, 1)");
+generateFloatArray(3);    wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform2fv(locationB, uniformData, 1)");
+generateInt32Array(3);    wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform2iv(locationB, uniformData, 1)");
+generateIntArray(3);      wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform2iv(locationB, uniformData, 1)");
+generateFloat32Array(4);  wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform3fv(locationB, uniformData, 1)");
+generateFloatArray(4);    wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform3fv(locationB, uniformData, 1)");
+generateInt32Array(4);    wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform3iv(locationB, uniformData, 1)");
+generateIntArray(4);      wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform3iv(locationB, uniformData, 1)");
+generateFloat32Array(5);  wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform4fv(locationB, uniformData, 1)");
+generateFloatArray(5);    wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform4fv(locationB, uniformData, 1)");
+generateInt32Array(5);    wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform4iv(locationB, uniformData, 1)");
+generateIntArray(5);      wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform4iv(locationB, uniformData, 1)");
+generateFloat32Array(5);  wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniformMatrix2fv(locationB, false, uniformData, 1)");
+generateFloatArray(5);    wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniformMatrix2fv(locationB, false, uniformData, 1)");
+generateFloat32Array(10); wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniformMatrix3fv(locationB, false, uniformData, 1)");
+generateFloatArray(10);   wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniformMatrix3fv(locationB, false, uniformData, 1)");
+generateFloat32Array(17); wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniformMatrix4fv(locationB, false, uniformData, 1)");
+generateFloatArray(17);   wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniformMatrix4fv(locationB, false, uniformData, 1)");
+// Non-rectangular matrix uniform entry points
+generateFloat32Array(6);
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniformMatrix3x2fv(locationB, false, uniformData)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniformMatrix2x3fv(locationB, false, uniformData)");
+generateFloatArray(6);
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniformMatrix3x2fv(locationB, false, uniformData)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniformMatrix2x3fv(locationB, false, uniformData)");
+generateFloat32Array(8);
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniformMatrix4x2fv(locationB, false, uniformData)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniformMatrix2x4fv(locationB, false, uniformData)");
+generateFloatArray(8);
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniformMatrix4x2fv(locationB, false, uniformData)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniformMatrix2x4fv(locationB, false, uniformData)");
+generateFloat32Array(12);
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniformMatrix4x3fv(locationB, false, uniformData)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniformMatrix3x4fv(locationB, false, uniformData)");
+generateFloatArray(12);
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniformMatrix4x3fv(locationB, false, uniformData)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniformMatrix3x4fv(locationB, false, uniformData)");
+
+// Query objects
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.deleteQuery(queryB)");
+wtu.shouldGenerateGLError(contextA, contextA.NO_ERROR, "contextA.isQuery(queryB)");
+shouldBeFalse("contextA.isQuery(queryB)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.beginQuery(contextA.ANY_SAMPLES_PASSED, queryB)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.getQueryParameter(queryB, contextA.QUERY_RESULT_AVAILABLE)");
+
+// Sampler objects
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.deleteSampler(samplerB)");
+wtu.shouldGenerateGLError(contextA, contextA.NO_ERROR, "contextA.isSampler(samplerB)");
+shouldBeFalse("contextA.isSampler(samplerB)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.samplerParameteri(samplerB, contextA.TEXTURE_WRAP_S, contextA.CLAMP_TO_EDGE)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.samplerParameterf(samplerB, contextA.TEXTURE_MIN_LOD, -500.5)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.getSamplerParameter(samplerB, contextA.TEXTURE_WRAP_S)");
+
+// Sync objects
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.deleteSync(syncB)");
+wtu.shouldGenerateGLError(contextA, contextA.NO_ERROR, "contextA.isSync(syncB)");
+shouldBeFalse("contextA.isSync(syncB)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.clientWaitSync(syncB, 0, 0)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.waitSync(syncB, 0, contextA.TIMEOUT_IGNORED)");
+
+// Transform feedback
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.deleteTransformFeedback(transformFeedbackB)");
+wtu.shouldGenerateGLError(contextA, contextA.NO_ERROR, "contextA.isTransformFeedback(transformFeedbackB)");
+shouldBeFalse("contextA.isTransformFeedback(transformFeedbackB)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.bindTransformFeedback(contextA.TRANSFORM_FEEDBACK, transformFeedbackB)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.transformFeedbackVaryings(tfProgramB, ['out_data'], contextA.SEPARATE_ATTRIBS)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.getTransformFeedbackVarying(tfProgramB, 0)");
+
+// Uniform buffer objects and transform feedback buffers
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.bindBufferBase(contextA.UNIFORM_BUFFER, 0, bufferB)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.bindBufferRange(contextA.UNIFORM_BUFFER, 0, bufferB, 0, 0)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.getUniformIndices(programB, ['u_modelViewProjMatrix'])");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.getActiveUniforms(programB, [0], contextA.UNIFORM_TYPE)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.getUniformBlockIndex(programB, 'nonexistent_uniform_block_name')");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.getActiveUniformBlockParameter(programB, 0, contextA.UNIFORM_BLOCK_BINDING)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.getActiveUniformBlockName(programB, 0)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniformBlockBinding(programB, 0, 0)");
+
+// Vertex Array objects
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.deleteVertexArray(vertexArrayB)");
+wtu.shouldGenerateGLError(contextA, contextA.NO_ERROR, "contextA.isVertexArray(vertexArrayB)");
+shouldBeFalse("contextA.isVertexArray(vertexArrayB)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.bindVertexArray(vertexArrayB)");
+
+var successfullyParsed = true;
+</script>
+
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
--- a/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/00_test_list.txt
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/00_test_list.txt
@@ -1,7 +1,13 @@
 ext-color-buffer-float.html
 ext-disjoint-timer-query-webgl2.html
 promoted-extensions.html
 promoted-extensions-in-shaders.html
-webgl-get-buffer-sub-data-async.html
-webgl-get-buffer-sub-data-async-lose-context.html
-webgl-get-buffer-sub-data-async-stress.html
+--min-version 2.0.1 webgl_multiview.html
+--min-version 2.0.1 webgl_multiview_depth.html
+--min-version 2.0.1 webgl_multiview_draw_buffers.html
+--min-version 2.0.1 webgl_multiview_flat_varying.html
+--min-version 2.0.1 webgl_multiview_instanced_draw.html
+--min-version 2.0.1 webgl_multiview_non_multiview_shaders.html
+--min-version 2.0.1 webgl_multiview_single_view_operations.html
+--min-version 2.0.1 webgl_multiview_timer_query.html
+--min-version 2.0.1 webgl_multiview_transform_feedback.html
deleted file mode 100644
--- a/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl-get-buffer-sub-data-async-lose-context.html
+++ /dev/null
@@ -1,117 +0,0 @@
-<!--
-
-/*
-** Copyright (c) 2016 The Khronos Group Inc.
-**
-** Permission is hereby granted, free of charge, to any person obtaining a
-** copy of this software and/or associated documentation files (the
-** "Materials"), to deal in the Materials without restriction, including
-** without limitation the rights to use, copy, modify, merge, publish,
-** distribute, sublicense, and/or sell copies of the Materials, and to
-** permit persons to whom the Materials are furnished to do so, subject to
-** the following conditions:
-**
-** The above copyright notice and this permission notice shall be included
-** in all copies or substantial portions of the Materials.
-**
-** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
-*/
-
--->
-
-<!DOCTYPE html>
-<html>
-<head>
-<meta charset="utf-8">
-<title>WEBGL_get_buffer_sub_data_async context loss regression test.</title>
-<link rel="stylesheet" href="../../resources/js-test-style.css"/>
-<script src="../../js/js-test-pre.js"></script>
-<script src="../../js/webgl-test-utils.js"></script>
-</head>
-<body>
-<div id="description"></div>
-<canvas id="canvas" width="1" height="1"></canvas>
-<div id="console"></div>
-<script>
-"use strict";
-description("This test makes sure that getBufferSubDataAsync does not cause crashes upon context loss.");
-
-var wtu = WebGLTestUtils;
-var canvas = document.getElementById("canvas");
-
-var gl = wtu.create3DContext(canvas, undefined, 2);
-var WEBGL_get_buffer_sub_data_async = gl.getExtension("WEBGL_get_buffer_sub_data_async");
-wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup.");
-if (!WEBGL_get_buffer_sub_data_async) {
-  testPassed("No WEBGL_get_buffer_sub_data_async support -- this is legal");
-  finishTest();
-} else {
-  testPassed("Successfully enabled WEBGL_get_buffer_sub_data_async extension");
-  runTests();
-}
-
-var extension;
-
-function runTests() {
-  var extensionName = wtu.getSupportedExtensionWithKnownPrefixes(gl, "WEBGL_lose_context");
-  if (!extensionName) {
-    debug("Could not find WEBGL_lose_context extension");
-    return;
-  }
-  extension = gl.getExtension(extensionName);
-
-  var iter = 0;
-  var ITERS = 20;
-  canvas.addEventListener("webglcontextrestored", test);
-  canvas.addEventListener("webglcontextlost", function(e) {
-    e.preventDefault();
-    webglHarnessCollectGarbage();
-    setTimeout(function() {
-      wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "extension.restoreContext()");
-    }, 100);
-  });
-
-  test();
-
-  function test() {
-    shouldBeFalse("gl.isContextLost()");
-    if (iter >= ITERS) {
-      finishTest();
-      return;
-    }
-    iter++;
-
-    var pbo = gl.createBuffer();
-    gl.bindBuffer(gl.PIXEL_PACK_BUFFER, pbo);
-    gl.bufferData(gl.PIXEL_PACK_BUFFER, 4, gl.DYNAMIC_READ);
-    gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null);
-    var readbackBuffer = new Uint8Array(4);
-
-    gl.bindBuffer(gl.PIXEL_PACK_BUFFER, pbo);
-    gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, 0);
-    var promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl.PIXEL_PACK_BUFFER, 0, readbackBuffer);
-    gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null);
-    var completed = false;
-    promise.then(function(buf) {
-      testFailed("should not resolve");
-    }, function(e) {
-      testPassed("correctly rejected with " + e);
-      wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no new errors from late rejection");
-    }).then(function() {
-      completed = true;
-    });
-
-    wtu.shouldGenerateGLError(gl, gl.CONTEXT_LOST_WEBGL, "extension.loseContext()");
-  }
-}
-
-var successfullyParsed = true;
-</script>
-</body>
-</html>
deleted file mode 100644
--- a/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl-get-buffer-sub-data-async-stress.html
+++ /dev/null
@@ -1,108 +0,0 @@
-<!--
-
-/*
-** Copyright (c) 2016 The Khronos Group Inc.
-**
-** Permission is hereby granted, free of charge, to any person obtaining a
-** copy of this software and/or associated documentation files (the
-** "Materials"), to deal in the Materials without restriction, including
-** without limitation the rights to use, copy, modify, merge, publish,
-** distribute, sublicense, and/or sell copies of the Materials, and to
-** permit persons to whom the Materials are furnished to do so, subject to
-** the following conditions:
-**
-** The above copyright notice and this permission notice shall be included
-** in all copies or substantial portions of the Materials.
-**
-** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
-*/
-
--->
-
-<!DOCTYPE html>
-<html>
-<head>
-<meta charset="utf-8">
-<title>WEBGL_get_buffer_sub_data_async stress test.</title>
-<link rel="stylesheet" href="../../resources/js-test-style.css"/>
-<script src="../../js/js-test-pre.js"></script>
-<script src="../../js/webgl-test-utils.js"></script>
-</head>
-<body>
-<div id="description"></div>
-<canvas id="canvas" width="1" height="1"></canvas>
-<div id="console"></div>
-<script>
-"use strict";
-description("This test makes sure that getBufferSubDataAsync acts under stress as expected governed by WebGL 2.");
-
-var wtu = WebGLTestUtils;
-var canvas = document.getElementById("canvas");
-
-var gl = wtu.create3DContext(canvas, undefined, 2);
-var WEBGL_get_buffer_sub_data_async = gl.getExtension("WEBGL_get_buffer_sub_data_async");
-wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup.");
-if (!WEBGL_get_buffer_sub_data_async) {
-  testPassed("No WEBGL_get_buffer_sub_data_async support -- this is legal");
-  finishTest();
-} else {
-  testPassed("Successfully enabled WEBGL_get_buffer_sub_data_async extension");
-  runTests();
-}
-
-function runTests() {
-  var pbo = gl.createBuffer();
-  gl.bindBuffer(gl.PIXEL_PACK_BUFFER, pbo);
-  gl.bufferData(gl.PIXEL_PACK_BUFFER, 4, gl.DYNAMIC_READ);
-  gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null);
-  var readbackBuffer = new Uint8Array(4);
-
-  var TEST_COUNT = 300;
-  var lastCounter = -1;
-  var promises = [];
-  debug("kicking off " + TEST_COUNT + " getBufferSubDataAsync calls");
-  for (let counter = 0; counter < TEST_COUNT; counter++) {
-    let color = [counter & 0xff, (counter >> 8) & 0xff, (counter >> 16) & 0xff, 255];
-    gl.clearColor(color[0] / 255, color[1] / 255, color[2] / 255, color[3] / 255);
-    gl.clear(gl.COLOR_BUFFER_BIT);
-    gl.bindBuffer(gl.PIXEL_PACK_BUFFER, pbo);
-    gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, 0);
-    let promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl.PIXEL_PACK_BUFFER, 0, readbackBuffer);
-    gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null);
-    promise = promise.then(function(buf) {
-      if (buf[0] == color[0] &&
-          buf[1] == color[1] &&
-          buf[2] == color[2] &&
-          buf[3] == color[3]) {
-        testPassed("color readback #" + counter + " was correct");
-      } else {
-        testFailed("color readback #" + counter + " was incorrect: was " +
-            buf + " but expected " + color);
-      }
-      if (counter == lastCounter + 1) {
-        testPassed("    and its Promise resolved in order after " + lastCounter);
-      } else {
-        testFailed("    and its Promise resolved out of order order after " + lastCounter);
-      }
-      lastCounter = counter;
-    }, function(e) {
-      testFailed(e.toString());
-    });
-    promises.push(promise);
-  }
-
-  Promise.all(promises).catch(function(e) {
-    testFailed(e.toString());
-  }).then(finishTest);
-}
-
-var successfullyParsed = true;
-</script>
-</body>
-</html>
deleted file mode 100644
--- a/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl-get-buffer-sub-data-async.html
+++ /dev/null
@@ -1,327 +0,0 @@
-<!--
-
-/*
-** Copyright (c) 2016 The Khronos Group Inc.
-**
-** Permission is hereby granted, free of charge, to any person obtaining a
-** copy of this software and/or associated documentation files (the
-** "Materials"), to deal in the Materials without restriction, including
-** without limitation the rights to use, copy, modify, merge, publish,
-** distribute, sublicense, and/or sell copies of the Materials, and to
-** permit persons to whom the Materials are furnished to do so, subject to
-** the following conditions:
-**
-** The above copyright notice and this permission notice shall be included
-** in all copies or substantial portions of the Materials.
-**
-** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
-*/
-
--->
-
-<!DOCTYPE html>
-<html>
-<head>
-<meta charset="utf-8">
-<title>WEBGL_get_buffer_sub_data_async test.</title>
-<link rel="stylesheet" href="../../resources/js-test-style.css"/>
-<script src="../../js/js-test-pre.js"></script>
-<script src="../../js/webgl-test-utils.js"></script>
-</head>
-<body>
-<div id="description"></div>
-<div id="console"></div>
-<script>
-"use strict";
-description("This test makes sure that getBufferSubDataAsync acts as expected governed by WebGL 2.");
-
-var wtu = WebGLTestUtils;
-
-var gl = wtu.create3DContext(undefined, undefined, 2);
-var WEBGL_get_buffer_sub_data_async = gl.getExtension("WEBGL_get_buffer_sub_data_async");
-wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup.");
-if (!WEBGL_get_buffer_sub_data_async) {
-  testPassed("No WEBGL_get_buffer_sub_data_async support -- this is legal");
-  finishTest();
-} else {
-  testPassed("Successfully enabled WEBGL_get_buffer_sub_data_async extension");
-  runTests();
-}
-
-var floatArray, dstData, extraLargeBuffer, resolvedArray, promise;
-
-function runTests() {
-  var vertices = [
-    1.1,  1.0,  1.3,
-    -1.0, -1.0,  -5.0,
-    5.3, -1.0,  1.0
-  ];
-  floatArray = new Float32Array(vertices);
-  dstData = new Float32Array(vertices.length);
-  extraLargeBuffer = new Float32Array(vertices.length + 1);
-
-  function clearDstData() {
-    dstData = new Float32Array(vertices.length);
-  }
-
-  wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup.");
-
-  function expectRejected(promise, message) {
-    return promise.then(function(v) {
-      testFailed("should be rejected but was " + v + ": " + message);
-    }, function(e) {
-      testPassed("correctly rejected with " + e + ": " + message);
-    });
-  }
-
-  var types = [
-    'ARRAY_BUFFER',
-    'ELEMENT_ARRAY_BUFFER',
-    'COPY_READ_BUFFER',
-    'COPY_WRITE_BUFFER',
-    'PIXEL_PACK_BUFFER',
-    'PIXEL_UNPACK_BUFFER',
-    'TRANSFORM_FEEDBACK_BUFFER',
-    'UNIFORM_BUFFER'
-  ];
-  var buffers = [];
-
-  var buffer;
-  for (var i = 0; i < types.length; i++) {
-    buffer = gl.createBuffer();
-    gl.bindBuffer(gl[types[i]], buffer);
-    gl.bufferData(gl[types[i]], floatArray, gl.STATIC_DRAW);
-    buffers[i] = buffer;
-  }
-
-  var uninitializedBuffer = gl.createBuffer();
-  gl.bindBuffer(gl.ARRAY_BUFFER, uninitializedBuffer);
-  gl.bufferData(gl.ARRAY_BUFFER, 36, gl.STATIC_DRAW);
-  gl.bindBuffer(gl.ARRAY_BUFFER, null);
-
-  wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from buffer setup.");
-
-  // This long promise chain is to serialize the tests - so that the errors
-  // associated with a given test always appear underneath that test.
-  // For each test in this chain, if it performs test checks in a promise,
-  // it MUST return that promise so that it completes before the next test.
-  var allPromises = Promise.resolve();
-
-  allPromises = allPromises.then(function() {
-    debug("Testing promise rejection");
-  });
-
-  allPromises = allPromises.then(function() {
-    debug("Argument must be ArrayBufferView, not ArrayBuffer")
-      gl.bindBuffer(gl.ARRAY_BUFFER, buffers[0]);
-    var code = "promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, new ArrayBuffer(4))";
-    wtu.shouldGenerateGLError(gl, gl.NO_ERROR, code);
-    shouldBeType("promise", "Promise");
-    gl.bindBuffer(gl.ARRAY_BUFFER, null);
-    return expectRejected(promise, code);
-  });
-
-  allPromises = allPromises.then(function() {
-    debug("Argument must be ArrayBufferView, not null")
-      gl.bindBuffer(gl.ARRAY_BUFFER, buffers[0]);
-    var code = "promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, null)";
-    wtu.shouldGenerateGLError(gl, gl.NO_ERROR, code);
-    shouldBeType("promise", "Promise");
-    gl.bindBuffer(gl.ARRAY_BUFFER, null);
-    return expectRejected(promise, code);
-  });
-
-  allPromises = allPromises.then(function() {
-    debug("");
-  });
-
-  for (let i = 0; i < types.length; i++) {
-    allPromises = allPromises.then(function() {
-      debug("Test that getBufferSubDataAsync successfully works reading buffer data from gl." + type);
-      var type = types[i];
-      var buf = buffers[i];
-      gl.bindBuffer(gl[type], buf);
-      wtu.shouldGenerateGLError(gl, gl.NO_ERROR,
-          "promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl." + type + ", 0, dstData)");
-      gl.bindBuffer(gl[type], null);
-      return promise.then(function(arr) {
-        resolvedArray = arr;
-        shouldBe("resolvedArray", "dstData");
-        shouldBeTrue("areArraysEqual(resolvedArray, floatArray)");
-      });
-    });
-  }
-
-  allPromises = allPromises.then(function() {
-    debug("");
-    debug("Test that getBufferSubDataAsync successfully works with dstOffset");
-
-    gl.bindBuffer(gl.ARRAY_BUFFER, buffers[0]);
-
-    clearDstData();
-    wtu.shouldGenerateGLError(gl, gl.NO_ERROR,
-        "promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData, 2)");
-    return promise.then(function(arr) {
-      resolvedArray = arr;
-      shouldBeTrue("areArraysEqual(resolvedArray.slice(0, 2), [0, 0])");
-      shouldBeTrue("areArraysEqual(resolvedArray.slice(2), floatArray.slice(0, floatArray.length - 2))");
-    });
-  });
-
-  allPromises = allPromises.then(function() {
-    clearDstData();
-    wtu.shouldGenerateGLError(gl, gl.NO_ERROR,
-        "promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData, dstData.length)");
-    return promise.then(function(arr) {
-      resolvedArray = arr;
-      shouldBeTrue("areArraysEqual(resolvedArray, [0, 0, 0, 0, 0, 0, 0, 0, 0])");
-    });
-  });
-
-  allPromises = allPromises.then(function() {
-    debug("Test that getBufferSubDataAsync fails when given a dstOffset beyond the end of dstData");
-
-    var code = "promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData, dstData.length + 1)";
-    wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, code);
-    return expectRejected(promise, code);
-  });
-
-  allPromises = allPromises.then(function() {
-    debug("");
-    debug("Test that getBufferSubDataAsync successfully works with dstOffset and length");
-
-    clearDstData();
-    wtu.shouldGenerateGLError(gl, gl.NO_ERROR,
-        "promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData, 2, 2)");
-    return promise.then(function(arr) {
-      resolvedArray = arr;
-      shouldBeTrue("areArraysEqual(resolvedArray.slice(0, 2), [0, 0])");
-      shouldBeTrue("areArraysEqual(resolvedArray.slice(2, 4), floatArray.slice(0, 2))");
-      shouldBeTrue("areArraysEqual(resolvedArray.slice(4), [0, 0, 0, 0, 0])");
-    });
-  });
-
-  allPromises = allPromises.then(function() {
-    clearDstData();
-    wtu.shouldGenerateGLError(gl, gl.NO_ERROR,
-        "promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData, dstData.length, 0)");
-    return promise.then(function(arr) {
-      resolvedArray = arr;
-      shouldBeTrue("areArraysEqual(resolvedArray, [0, 0, 0, 0, 0, 0, 0, 0, 0])");
-    });
-  });
-
-  allPromises = allPromises.then(function() {
-    debug("");
-    debug("Test that getBufferSubDataAsync successfully works with uninitialized buffers");
-    dstData = new Float32Array([1, 2, 3, 4, 5, 6, 7, 8, 9]);
-    gl.bindBuffer(gl.ARRAY_BUFFER, uninitializedBuffer);
-    wtu.shouldGenerateGLError(gl, gl.NO_ERROR,
-        "promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData)");
-    return promise.then(function(arr) {
-      resolvedArray = arr;
-      shouldBeTrue("areArraysEqual(resolvedArray, [0, 0, 0, 0, 0, 0, 0, 0, 0])");
-    });
-  });
-
-  allPromises = allPromises.then(function() {
-    debug("Test that getBufferSubDataAsync fails when given a dstOffset+length beyond the end of dstData");
-
-    gl.bindBuffer(gl.ARRAY_BUFFER, buffers[0]);
-    var code = "promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData, dstData.length - 1, 2)";
-    wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, code);
-    return expectRejected(promise, code);
-  });
-
-  allPromises = allPromises.then(function() {
-    debug("");
-    debug("Test that getBufferSubDataAsync fails when given a buffer with its size larger than the original data");
-    gl.bindBuffer(gl.ARRAY_BUFFER, buffers[0]);
-    var code = "promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, extraLargeBuffer)";
-    wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, code);
-    return expectRejected(promise, code);
-  });
-
-  allPromises = allPromises.then(function() {
-    debug("Test that getBufferSubDataAsync fails when offset summed with buffer length is larger than the size of the original data size");
-    gl.bindBuffer(gl.ARRAY_BUFFER, buffers[0]);
-    var code = "promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl.ARRAY_BUFFER, dstData.byteLength + 1, dstData)";
-    wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, code);
-    return expectRejected(promise, code);
-  });
-
-  allPromises = allPromises.then(function() {
-    gl.bindBuffer(gl.ARRAY_BUFFER, buffers[0]);
-    var code = "promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl.ARRAY_BUFFER, 1, dstData)";
-    wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, code);
-    return expectRejected(promise, code);
-  });
-
-  allPromises = allPromises.then(function() {
-    debug("Test that getBufferSubDataAsync fails when no buffer is bound to the target");
-    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
-    var code = "promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl.ELEMENT_ARRAY_BUFFER, 0, dstData)";
-    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
-    wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, code);
-    return expectRejected(promise, code);
-  });
-
-  allPromises = allPromises.then(function() {
-    debug("Test that getBufferSubDataAsync fails when offset is less than 0");
-    gl.bindBuffer(gl.ARRAY_BUFFER, buffers[0]);
-    var code = "promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl.ARRAY_BUFFER, -1, dstData)";
-    wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, code);
-    return expectRejected(promise, code);
-  });
-
-  allPromises = allPromises.then(function() {
-    debug("");
-    debug("Test that getBufferSubDataAsync works when a buffer is immediately resized to be too small");
-
-    buffer = gl.createBuffer();
-    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
-    gl.bufferData(gl.ARRAY_BUFFER, floatArray, gl.STATIC_DRAW);
-    buffers[i] = buffer;
-
-    dstData = new Float32Array([1, 2, 3, 4, 5, 6, 7, 8, 9]);
-    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
-    wtu.shouldGenerateGLError(gl, gl.NO_ERROR,
-        "promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData)");
-    gl.bufferData(gl.ARRAY_BUFFER, 4, gl.STATIC_DRAW);
-    return promise.then(function(arr) {
-      resolvedArray = arr;
-      shouldBeTrue("areArraysEqual(resolvedArray, floatArray)");
-    });
-  });
-
-  allPromises = allPromises.then(function() {
-    debug("");
-    debug("Test that getBufferSubDataAsync works when a buffer is immediately deleted");
-
-    dstData = new Float32Array([1, 2, 3, 4, 5, 6, 7, 8, 9]);
-    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
-    gl.bufferData(gl.ARRAY_BUFFER, floatArray, gl.STATIC_DRAW);
-    wtu.shouldGenerateGLError(gl, gl.NO_ERROR,
-        "promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData)");
-    gl.deleteBuffer(buffer);
-    return promise.then(function(arr) {
-      resolvedArray = arr;
-      shouldBeTrue("areArraysEqual(resolvedArray, floatArray)");
-    });
-  });
-
-  allPromises.catch(function(e) {
-    testFailed(e.toString());
-  }).then(finishTest);
-}
-
-var successfullyParsed = true;
-</script>
-</body>
-</html>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl_multiview.html
@@ -0,0 +1,541 @@
+<!--
+
+/*
+** Copyright (c) 2018 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a
+** copy of this software and/or associated documentation files (the
+** "Materials"), to deal in the Materials without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Materials, and to
+** permit persons to whom the Materials are furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be included
+** in all copies or substantial portions of the Materials.
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+*/
+
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL WEBGL_multiview Conformance Tests</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+<script src="../../js/tests/webgl_multiview_util.js"></script>
+<script id="macroFragmentShader" type="x-shader/x-fragment">#version 300 es
+precision highp float;
+out vec4 my_FragColor;
+void main() {
+#ifdef GL_OVR_multiview
+    my_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
+#else
+    // Error expected
+    #error no GL_OVR_multiview;
+#endif
+}
+</script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+
+let wtu = WebGLTestUtils;
+let gl = wtu.create3DContext(null, null, 2);
+let ext = null;
+
+function runExtensionDisabledTest()
+{
+    debug("");
+    debug("Testing queries with extension disabled");
+
+    let maxViews = gl.getParameter(0x9631);
+    wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "Can't query MAX_VIEWS_OVR without enabling WEBGL_multiview");
+
+    let baseViewIndex = gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.BACK, 0x9630);
+    wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "Can't query FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_OVR without enabling WEBGL_multiview");
+    let numViews = gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.BACK, 0x9632);
+    wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "Can't query FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR without enabling WEBGL_multiview");
+}
+
+function runQueryTest()
+{
+    debug("");
+    debug("Testing querying MAX_VIEWS_OVR");
+
+    let maxViews = gl.getParameter(ext.MAX_VIEWS_OVR);
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from querying MAX_VIEWS_OVR");
+    if (typeof maxViews != 'number') {
+        testFailed("Type of the value of MAX_VIEWS_OVR should be number, was " + (typeof maxViews));
+    }
+    if (maxViews < 2) {
+        testFailed("Value of MAX_VIEWS_OVR should be at least two, was: " + maxViews);
+    }
+}
+
+function runDefaultFramebufferQueryTest()
+{
+    debug("");
+    debug("Testing querying base view index and num views on the default framebuffer");
+    gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+    let baseViewIndex = gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.BACK, ext.FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_OVR);
+    let numViews = gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.BACK, ext.FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR);
+    if (baseViewIndex != 0) {
+        testFailed('Unexpected baseViewIndex ' + baseViewIndex + ' on the default framebuffer');
+    } else {
+        testPassed('baseViewIndex on the default framebuffer was 0');
+    }
+    if (numViews != 0) {
+        testFailed('Unexpected numViews ' + numViews + ' on the default framebuffer');
+    } else {
+        testPassed('numViews on the default framebuffer was 0');
+    }
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from framebuffer queries on the default framebuffer");
+}
+
+function runInvalidTextureTypeTest()
+{
+    debug("");
+    debug("Testing invalid texture types");
+    let tex2D = createTextureWithNearestFiltering(gl.TEXTURE_2D);
+    gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA8, 128, 128);
+    ext.framebufferTextureMultiviewWEBGL(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, tex2D, 0, 0, 1);
+    wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "should not be possible to create a multiview framebuffer against a 2D texture");
+
+    let texCube = createTextureWithNearestFiltering(gl.TEXTURE_CUBE_MAP);
+    gl.texStorage2D(gl.TEXTURE_CUBE_MAP, 1, gl.RGBA8, 128, 128);
+    ext.framebufferTextureMultiviewWEBGL(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, texCube, 0, 0, 1);
+    wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "should not be possible to create a multiview framebuffer against a cube map texture");
+
+    let tex3D = createTextureWithNearestFiltering(gl.TEXTURE_3D);
+    gl.texStorage3D(gl.TEXTURE_3D, 1, gl.RGBA8, 128, 128, 2);
+    ext.framebufferTextureMultiviewWEBGL(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, tex3D, 0, 0, 2);
+    wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "should not be possible to create a multiview framebuffer against a 3D texture");
+}
+
+/**
+ * If allocateStorage is true, the test will allocate texture storage. If it is false, attachments are done without allocations.
+ */
+function runFramebufferQueryTest(allocateStorage)
+{
+    debug("");
+    debug("Testing querying attachment object type, baseViewIndex, numViews and framebuffer status. Texture storage is " + (allocateStorage ? "allocated" : "not allocated") + ".");
+
+    let checkQueryResult = function(actual, expected, name) {
+        if (actual != expected) {
+            testFailed('Unexpected ' + name + ': ' + actual + ' when it was set to ' + expected);
+        }  else {
+            testPassed(name + ' was ' + actual + ' when queried from the framebuffer');
+        }
+    }
+
+    let setupAndQuery = function(colorTex, levelSet, baseViewIndexSet, numViewsSet) {
+        ext.framebufferTextureMultiviewWEBGL(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, levelSet, baseViewIndexSet, numViewsSet);
+        let objectType = gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE);
+        if (objectType != gl.TEXTURE) {
+            testFailed('Unexpected FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE ' + wtu.glEnumToString(gl, objectType) + ', should be TEXTURE');
+        } else {
+            testPassed('FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE was TEXTURE');
+        }
+
+        let level = gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL);
+        checkQueryResult(level, levelSet, "level");
+
+        let textureName = gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME);
+        checkQueryResult(textureName, colorTex, "texture object");
+
+        let baseViewIndex = gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, ext.FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_OVR);
+        checkQueryResult(baseViewIndex, baseViewIndexSet, "baseViewIndex");
+
+        let numViews = gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, ext.FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR);
+        checkQueryResult(numViews, numViewsSet, "numViews");
+
+        let layer = gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER);
+        checkQueryResult(layer, baseViewIndexSet, "texture layer (should match baseViewIndex)");
+    }
+
+    let setupSecondAttachmentAndQueryStatus = function(colorTex2, baseViewIndex, numViews, expectedStatus, msg) {
+        ext.framebufferTextureMultiviewWEBGL(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT1, colorTex2, 0, baseViewIndex, numViews);
+        let status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
+        if (status != expectedStatus) {
+            testFailed('Framebuffer status: ' + wtu.glEnumToString(gl, status) + ' did not match with the expected value: ' + wtu.glEnumToString(gl, expectedStatus) + ' - ' + msg);
+        }  else {
+            testPassed('Framebuffer status: ' + wtu.glEnumToString(gl, status) + ' matched with the expected value - ' + msg);
+        }
+    }
+
+    let maxViews = gl.getParameter(ext.MAX_VIEWS_OVR);
+
+    let fb = gl.createFramebuffer();
+    gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
+    let baseViewIndex = gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, ext.FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_OVR);
+    wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Querying baseViewIndex from a nonexistent attachment");
+    if (baseViewIndex != null) {
+        testFailed('Unexpected baseViewIndex ' + baseViewIndex + ' on a framebuffer without attachments');
+    }  else {
+        testPassed('Querying baseViewIndex returned null on a framebuffer without attachments');
+    }
+    let numViews = gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, ext.FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR);
+    wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Querying numViews from a nonexistent attachment");
+    if (numViews != null) {
+        testFailed('Unexpected numViews ' + numViews + ' on a framebuffer without attachments');
+    } else {
+        testPassed('Querying numViews returned null on a framebuffer without attachments');
+    }
+
+    let colorTex = createTextureWithNearestFiltering(gl.TEXTURE_2D_ARRAY);
+    if (allocateStorage) {
+        gl.texStorage3D(gl.TEXTURE_2D_ARRAY, 2, gl.RGBA8, 128, 128, maxViews);
+    }
+    setupAndQuery(colorTex, 0, 0, maxViews);
+    setupAndQuery(colorTex, 1, 0, 2);
+    setupAndQuery(colorTex, 0, 1, maxViews - 1);
+
+    // Test matching and mismatching attachments for framebuffer status.
+    let colorTex2 = createTextureWithNearestFiltering(gl.TEXTURE_2D_ARRAY);
+    if (allocateStorage) {
+        gl.texStorage3D(gl.TEXTURE_2D_ARRAY, 1, gl.RGBA8, 128, 128, maxViews);
+    }
+    setupSecondAttachmentAndQueryStatus(colorTex2, 1, maxViews - 1, allocateStorage ? gl.FRAMEBUFFER_COMPLETE : gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT, 'matching baseViewIndex and numViews on different attachments');
+    if (allocateStorage) {
+        setupSecondAttachmentAndQueryStatus(colorTex2, 0, maxViews - 1, ext.FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR, 'baseViewIndex mismatch');
+        ext.framebufferTextureMultiviewWEBGL(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, 0, maxViews);
+        setupSecondAttachmentAndQueryStatus(colorTex2, 0, maxViews - 1, ext.FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR, 'numViews mismatch');
+    }
+
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from framebuffer queries");
+}
+
+function runInvalidViewsTest()
+{
+    debug("");
+    debug("Testing invalid out-of-range values for baseViewIndex and numViews");
+
+    let maxViews = gl.getParameter(ext.MAX_VIEWS_OVR);
+    let maxLayers = gl.getParameter(gl.MAX_ARRAY_TEXTURE_LAYERS);
+
+    let fb = gl.createFramebuffer();
+    gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fb);
+    let colorTex = createTextureWithNearestFiltering(gl.TEXTURE_2D_ARRAY);
+    // Don't allocate storage since it's not needed for the validation.
+    ext.framebufferTextureMultiviewWEBGL(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, 0, maxViews + 1);
+    wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "Specified too many views");
+    ext.framebufferTextureMultiviewWEBGL(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, 0, 0);
+    wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "Specified zero views");
+    ext.framebufferTextureMultiviewWEBGL(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, -1, 2);
+    wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "Specified negative baseViewIndex");
+
+    let colorTex2 = createTextureWithNearestFiltering(gl.TEXTURE_2D_ARRAY);
+    ext.framebufferTextureMultiviewWEBGL(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex2, 0, maxLayers - maxViews + 1, maxViews);
+    // baseViewIndex + numViews  =  (maxLayers - maxViews + 1) + maxViews  =  maxLayers + 1
+    wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "Specified so many views that baseViewIndex + numViews is greater than MAX_ARRAY_TEXTURE_LAYERS");
+    ext.framebufferTextureMultiviewWEBGL(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex2, 0, maxLayers - maxViews, maxViews);
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "baseViewIndex + numViews is exactly MAX_ARRAY_TEXTURE_LAYERS");
+}
+
+function runDetachTest()
+{
+    debug("");
+    debug("Testing detaching multiview attachments");
+
+    let maxViews = gl.getParameter(ext.MAX_VIEWS_OVR);
+    let maxLayers = gl.getParameter(gl.MAX_ARRAY_TEXTURE_LAYERS);
+
+    let fb = gl.createFramebuffer();
+    gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fb);
+    let colorTex = createTextureWithNearestFiltering(gl.TEXTURE_2D_ARRAY);
+    ext.framebufferTextureMultiviewWEBGL(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, 0, maxViews);
+    ext.framebufferTextureMultiviewWEBGL(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, null, 0, maxLayers + 1, 0);
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "baseViewIndex and numViews are not validated when detaching");
+    let objectType = gl.getFramebufferAttachmentParameter(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE);
+    if (objectType != gl.NONE) {
+        testFailed('Unexpected FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE ' + wtu.glEnumToString(gl, objectType) + ' after detach, should be NONE');
+    } else {
+        testPassed('FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE was NONE after detach');
+    }
+
+    ext.framebufferTextureMultiviewWEBGL(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, 0, maxViews);
+    gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, null, 0);
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Can detach with framebufferTexture2D as well.");
+    objectType = gl.getFramebufferAttachmentParameter(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE);
+    if (objectType != gl.NONE) {
+        testFailed('Unexpected FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE ' + wtu.glEnumToString(gl, objectType) + ' after detach, should be NONE');
+    } else {
+        testPassed('FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE was NONE after detach');
+    }
+}
+
+function runShaderCompileTest(extensionEnabled)
+{
+    debug("");
+    debug("Testing shader compiles with extension " + (extensionEnabled ? "enabled" : "disabled"));
+
+    var macroFragmentProgram = wtu.setupProgram(gl, [wtu.simpleVertexShaderESSL300, "macroFragmentShader"], undefined, undefined, true);
+    if (extensionEnabled) {
+        if (macroFragmentProgram) {
+            testPassed("GL_OVR_multiview defined in shaders when extension is enabled");
+        } else {
+            testFailed("GL_OVR_multiview not defined in shaders when extension is enabled");
+        }
+    } else {
+        if (macroFragmentProgram) {
+            testFailed("GL_OVR_multiview defined in shaders when extension is disabled");
+        } else {
+            testPassed("GL_OVR_multiview not defined in shaders when extension disabled");
+        }
+    }
+
+    if (!extensionEnabled) {
+        let multiviewShaders = [
+          getMultiviewPassthroughVertexShader(2),
+          getMultiviewColorFragmentShader()
+        ];
+        let testProgram = wtu.setupProgram(gl, multiviewShaders, ['a_position'], [0], true);
+        if (testProgram) {
+            testFailed("Compilation of shaders using extension functionality succeeded when the extension is disabled, should fail.");
+        } else {
+            testPassed("Compilation of shaders using extension functionality should fail when the extension is disabled.");
+        }
+    }
+}
+
+function runClearTest()
+{
+    debug("");
+    debug("Testing that calling clear() clears all views");
+
+    let width = 256;
+    let height = 256;
+
+    let views = gl.getParameter(ext.MAX_VIEWS_OVR);
+
+    let fb = gl.createFramebuffer();
+    gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
+    let colorTex = createTextureWithNearestFiltering(gl.TEXTURE_2D_ARRAY);
+    gl.texStorage3D(gl.TEXTURE_2D_ARRAY, 1, gl.RGBA8, width, height, views);
+    ext.framebufferTextureMultiviewWEBGL(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, 0, views);
+
+    gl.viewport(0, 0, width, height);
+
+    gl.clearColor(0, 1, 1, 1);
+    gl.clear(gl.COLOR_BUFFER_BIT);
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from clear");
+
+    let readFb = gl.createFramebuffer();
+    gl.bindFramebuffer(gl.READ_FRAMEBUFFER, readFb);
+    for (let viewIndex = 0; viewIndex < views; ++viewIndex) {
+        gl.framebufferTextureLayer(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, viewIndex);
+        let expectedColor = [0, 255, 255, 255];
+        wtu.checkCanvasRect(gl, 0, 0, width, height, expectedColor, 'view ' + viewIndex + ' should be cyan');
+    }
+}
+
+function runFragmentShaderRenderTest()
+{
+    debug("");
+    debug("Testing rendering with different colors in fragment shader");
+
+    let width = 256;
+    let height = 256;
+
+    let views = gl.getParameter(ext.MAX_VIEWS_OVR);
+
+    let multiviewShaders = [
+      getMultiviewPassthroughVertexShader(views),
+      getMultiviewColorFragmentShader()
+    ];
+    let testProgram = wtu.setupProgram(gl, multiviewShaders, ['a_position'], [0], true);
+    if (!testProgram) {
+        testFailed("Compilation with extension enabled failed.");
+        return;
+    }
+
+    let fb = gl.createFramebuffer();
+    gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
+    let colorTex = createTextureWithNearestFiltering(gl.TEXTURE_2D_ARRAY);
+    gl.texStorage3D(gl.TEXTURE_2D_ARRAY, 1, gl.RGBA8, width, height, views);
+    ext.framebufferTextureMultiviewWEBGL(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, 0, views);
+
+    gl.viewport(0, 0, width, height);
+    wtu.drawUnitQuad(gl);
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from draw");
+
+    let readFb = gl.createFramebuffer();
+    gl.bindFramebuffer(gl.READ_FRAMEBUFFER, readFb);
+    for (let viewIndex = 0; viewIndex < views; ++viewIndex) {
+        gl.framebufferTextureLayer(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, viewIndex);
+        let expectedColor = getExpectedColor(viewIndex);
+        wtu.checkCanvasRect(gl, 0, 0, width, height, expectedColor, 'view ' + viewIndex + ' should be colored ' + expectedColor);
+    }
+}
+
+function runVertexShaderRenderTest()
+{
+    debug("");
+    debug("Testing rendering with different colors in fragment shader, different offsets in vertex shader");
+
+    let width = 256;
+    let height = 256;
+
+    let views = gl.getParameter(ext.MAX_VIEWS_OVR);
+
+    let multiviewShaders = [
+      getMultiviewOffsetVertexShader(views),
+      getMultiviewColorFragmentShader()
+    ];
+
+    let testProgram = wtu.setupProgram(gl, multiviewShaders, ['a_position'], [0], true);
+    if (!testProgram) {
+        testFailed("Compilation with extension enabled failed.");
+        return;
+    }
+
+    let fb = gl.createFramebuffer();
+    gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
+    let colorTex = createTextureWithNearestFiltering(gl.TEXTURE_2D_ARRAY);
+    gl.texStorage3D(gl.TEXTURE_2D_ARRAY, 1, gl.RGBA8, width, height, views);
+    ext.framebufferTextureMultiviewWEBGL(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, 0, views);
+
+    gl.viewport(0, 0, width, height);
+    wtu.drawUnitQuad(gl);
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from draw");
+
+    let readFb = gl.createFramebuffer();
+    gl.bindFramebuffer(gl.READ_FRAMEBUFFER, readFb);
+    for (let viewIndex = 0; viewIndex < views; ++viewIndex) {
+        gl.framebufferTextureLayer(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, viewIndex);
+        let expectedColor = getExpectedColor(viewIndex);
+
+        checkVerticalStrip(width, height, views, viewIndex, expectedColor, 'view ' + viewIndex);
+    }
+}
+
+function runRealisticUseCaseRenderTest()
+{
+    debug("");
+    debug("Testing rendering with a different transformation matrix chosen from a uniform array according to ViewID");
+
+    let width = 256;
+    let height = 256;
+
+    let views = gl.getParameter(ext.MAX_VIEWS_OVR);
+
+    let multiviewShaders = [
+      getMultiviewRealisticUseCaseVertexShader(views),
+      getMultiviewColorFragmentShader()
+    ];
+
+    let testProgram = wtu.setupProgram(gl, multiviewShaders, ['a_position'], [0], true);
+    if (!testProgram) {
+        testFailed("Compilation with extension enabled failed.");
+        return;
+    }
+
+    let fb = gl.createFramebuffer();
+    gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
+    let colorTex = createTextureWithNearestFiltering(gl.TEXTURE_2D_ARRAY);
+    gl.texStorage3D(gl.TEXTURE_2D_ARRAY, 1, gl.RGBA8, width, height, views);
+    ext.framebufferTextureMultiviewWEBGL(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, 0, views);
+
+    gl.viewport(0, 0, width, height);
+
+    let transformLocation = gl.getUniformLocation(testProgram, 'transform');
+    let transformData = new Float32Array (views * 16);
+    for (let viewIndex = 0; viewIndex < views; ++viewIndex) {
+        let scaleX = 1.0 / views;
+        // offsetX is the position of the left edge of the quad we want to get in normalized device coordinates
+        let offsetX = viewIndex / views * 2.0 - 1.0;
+
+        setupTranslateAndScaleXMatrix(transformData, viewIndex * 16, scaleX, offsetX);
+    }
+    gl.uniformMatrix4fv(transformLocation, false, transformData);
+
+    wtu.drawUnitQuad(gl);
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from draw");
+
+    let readFb = gl.createFramebuffer();
+    gl.bindFramebuffer(gl.READ_FRAMEBUFFER, readFb);
+    for (let viewIndex = 0; viewIndex < views; ++viewIndex) {
+        gl.framebufferTextureLayer(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, viewIndex);
+        let expectedColor = getExpectedColor(viewIndex);
+
+        checkVerticalStrip(width, height, views, viewIndex, expectedColor, 'view ' + viewIndex);
+    }
+}
+
+function runUniqueObjectTest()
+{
+    debug("");
+    debug("Testing that getExtension() returns the same object each time");
+    gl.getExtension("WEBGL_multiview").myProperty = 2;
+    webglHarnessCollectGarbage();
+    shouldBe('gl.getExtension("WEBGL_multiview").myProperty', '2');
+}
+
+description("This test verifies the functionality of the WEBGL_multiview extension, if it is available.");
+
+debug("");
+
+if (!gl) {
+  testFailed("WebGL context does not exist");
+} else {
+  testPassed("WebGL context exists");
+
+  runExtensionDisabledTest();
+
+  runShaderCompileTest(false);
+
+  debug("");
+
+  if (!gl.getExtension("WEBGL_multiview")) {
+      testPassed("No WEBGL_multiview support -- this is legal");
+  } else {
+      testPassed("Successfully enabled WEBGL_multiview extension");
+      ext = gl.getExtension('WEBGL_multiview');
+
+      runShaderCompileTest(true);
+
+      runQueryTest();
+
+      runDefaultFramebufferQueryTest();
+
+      runInvalidTextureTypeTest();
+
+      runFramebufferQueryTest(true);
+      runFramebufferQueryTest(false);
+
+      runInvalidViewsTest();
+
+      runDetachTest();
+
+      runClearTest();
+
+      wtu.setupUnitQuad(gl, 0, 1);
+
+      runFragmentShaderRenderTest();
+      runVertexShaderRenderTest();
+      runRealisticUseCaseRenderTest();
+      runUniqueObjectTest();
+  }
+}
+
+debug("");
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl_multiview_depth.html
@@ -0,0 +1,159 @@
+<!--
+
+/*
+** Copyright (c) 2018 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a
+** copy of this software and/or associated documentation files (the
+** "Materials"), to deal in the Materials without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Materials, and to
+** permit persons to whom the Materials are furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be included
+** in all copies or substantial portions of the Materials.
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+*/
+
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL WEBGL_multiview Conformance Tests</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+<script src="../../js/tests/webgl_multiview_util.js"></script>
+<script id="macroFragmentShader" type="x-shader/x-fragment">#version 300 es
+precision highp float;
+out vec4 my_FragColor;
+void main() {
+#ifdef GL_OVR_multiview
+    my_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
+#else
+    // Error expected
+    #error no GL_OVR_multiview;
+#endif
+}
+</script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+
+let wtu = WebGLTestUtils;
+let gl = wtu.create3DContext(null, null, 2);
+let ext = null;
+
+function runDepthRenderTest()
+{
+    debug("");
+    debug("Testing rendering with a depth texture array and depth test on");
+
+    let width = 64;
+    let height = 64;
+
+    let views = gl.getParameter(ext.MAX_VIEWS_OVR);
+
+    let multiviewShaders = [
+      getMultiviewPassthroughVertexShader(views),
+      getMultiviewColorFragmentShader()
+    ];
+    let testProgram = wtu.setupProgram(gl, multiviewShaders, ['a_position'], [0], true);
+    if (!testProgram) {
+        testFailed("Compilation with extension enabled failed.");
+        return;
+    }
+
+    let fb = gl.createFramebuffer();
+    gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
+    let colorTex = createTextureWithNearestFiltering(gl.TEXTURE_2D_ARRAY);
+    gl.texStorage3D(gl.TEXTURE_2D_ARRAY, 1, gl.RGBA8, width, height, views);
+    ext.framebufferTextureMultiviewWEBGL(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, 0, views);
+
+    let depthTex = createTextureWithNearestFiltering(gl.TEXTURE_2D_ARRAY);
+    gl.texStorage3D(gl.TEXTURE_2D_ARRAY, 1, gl.DEPTH32F_STENCIL8, width, height, views);
+    ext.framebufferTextureMultiviewWEBGL(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, depthTex, 0, 0, views);
+
+    let expectedStatus = gl.FRAMEBUFFER_COMPLETE;
+    let status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
+    if (status != expectedStatus) {
+        testFailed('Framebuffer status: ' + wtu.glEnumToString(gl, status) + ' did not match with the expected value: ' + wtu.glEnumToString(gl, expectedStatus));
+    }  else {
+        testPassed('Framebuffer status: ' + wtu.glEnumToString(gl, status) + ' matched with the expected value');
+    }
+
+    // Draw so that the depth test succeeds for all pixels.
+    gl.viewport(0, 0, width, height);
+    gl.enable(gl.DEPTH_TEST);
+    gl.clearDepth(1.0);
+    gl.clear(gl.DEPTH_BUFFER_BIT);
+
+    wtu.drawUnitQuad(gl);
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from draw when depth test succeeds");
+
+    let readFb = gl.createFramebuffer();
+    gl.bindFramebuffer(gl.READ_FRAMEBUFFER, readFb);
+    for (let viewIndex = 0; viewIndex < views; ++viewIndex) {
+        gl.framebufferTextureLayer(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, viewIndex);
+        let expectedColor = getExpectedColor(viewIndex);
+        wtu.checkCanvasRect(gl, 0, 0, width, height, expectedColor, 'view ' + viewIndex + ' should be colored ' + expectedColor);
+    }
+
+    // Draw so that the depth test fails for all pixels.
+    gl.clearDepth(0.0);
+    gl.clearColor(0.0, 0.0, 0.0, 0.0);
+    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+    wtu.drawUnitQuad(gl);
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from draw when depth test fails");
+
+    gl.bindFramebuffer(gl.READ_FRAMEBUFFER, readFb);
+    for (let viewIndex = 0; viewIndex < views; ++viewIndex) {
+        gl.framebufferTextureLayer(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, viewIndex);
+        let expectedColor = [0, 0, 0, 0];
+        wtu.checkCanvasRect(gl, 0, 0, width, height, expectedColor, 'view ' + viewIndex + ' should be colored ' + expectedColor);
+    }
+}
+
+description("This test verifies drawing to depth buffers with the WEBGL_multiview extension, if it is available.");
+
+debug("");
+
+if (!gl) {
+  testFailed("WebGL context does not exist");
+} else {
+  testPassed("WebGL context exists");
+
+  debug("");
+
+  if (!gl.getExtension("WEBGL_multiview")) {
+      testPassed("No WEBGL_multiview support -- this is legal");
+  } else {
+      testPassed("Successfully enabled WEBGL_multiview extension");
+      ext = gl.getExtension('WEBGL_multiview');
+
+      wtu.setupUnitQuad(gl, 0, 1);
+
+      runDepthRenderTest();
+  }
+}
+
+debug("");
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl_multiview_draw_buffers.html
@@ -0,0 +1,179 @@
+<!--
+
+/*
+** Copyright (c) 2018 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a
+** copy of this software and/or associated documentation files (the
+** "Materials"), to deal in the Materials without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Materials, and to
+** permit persons to whom the Materials are furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be included
+** in all copies or substantial portions of the Materials.
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+*/
+
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL WEBGL_multiview Conformance Tests</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+<script src="../../js/tests/webgl_multiview_util.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+
+let wtu = WebGLTestUtils;
+let gl = wtu.create3DContext(null, null, 2);
+let ext = null;
+
+function runDrawBuffersClearTest()
+{
+    debug("");
+    debug("Testing that calling clear() clears all views in all draw buffers");
+
+    let width = 256;
+    let height = 256;
+
+    let views = gl.getParameter(ext.MAX_VIEWS_OVR);
+
+    let fb = gl.createFramebuffer();
+    gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
+    let colorTex = [null, null, null];
+    let drawBuffers = [0, 0, 0];
+    for (let texIndex = 0; texIndex < colorTex.length; ++texIndex) {
+        colorTex[texIndex] = createTextureWithNearestFiltering(gl.TEXTURE_2D_ARRAY);
+        gl.texStorage3D(gl.TEXTURE_2D_ARRAY, 1, gl.RGBA8, width, height, views);
+        ext.framebufferTextureMultiviewWEBGL(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + texIndex, colorTex[texIndex], 0, 0, views);
+        drawBuffers[texIndex] = gl.COLOR_ATTACHMENT0 + texIndex;
+    }
+
+    gl.viewport(0, 0, width, height);
+    gl.drawBuffers(drawBuffers);
+
+    gl.clearColor(0, 1, 1, 1);
+    gl.clear(gl.COLOR_BUFFER_BIT);
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from clear");
+
+    let readFb = gl.createFramebuffer();
+    gl.bindFramebuffer(gl.READ_FRAMEBUFFER, readFb);
+    for (let texIndex = 0; texIndex < colorTex.length; ++texIndex) {
+        for (let viewIndex = 0; viewIndex < views; ++viewIndex) {
+            gl.framebufferTextureLayer(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex[texIndex], 0, viewIndex);
+            let expectedColor = [0, 255, 255, 255];
+            wtu.checkCanvasRect(gl, 0, 0, width, height, expectedColor, 'view ' + viewIndex + ' of color attachment ' + texIndex + ' should be cyan');
+        }
+        debug("");
+    }
+}
+
+function runDrawBuffersRenderTest()
+{
+    debug("");
+    debug("Testing rendering into multiple draw buffers with a different transformation matrix chosen from a uniform array according to ViewID");
+
+    let width = 256;
+    let height = 256;
+
+    let views = gl.getParameter(ext.MAX_VIEWS_OVR);
+
+    let fb = gl.createFramebuffer();
+    gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
+    let colorTex = [null, null, null];
+    let drawBuffers = [0, 0, 0];
+    for (let texIndex = 0; texIndex < colorTex.length; ++texIndex) {
+        colorTex[texIndex] = createTextureWithNearestFiltering(gl.TEXTURE_2D_ARRAY);
+        gl.texStorage3D(gl.TEXTURE_2D_ARRAY, 1, gl.RGBA8, width, height, views);
+        ext.framebufferTextureMultiviewWEBGL(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + texIndex, colorTex[texIndex], 0, 0, views);
+        drawBuffers[texIndex] = gl.COLOR_ATTACHMENT0 + texIndex;
+    }
+
+    let multiviewShaders = [
+      getMultiviewRealisticUseCaseVertexShader(views),
+      getMultiviewColorFragmentShaderForDrawBuffers(colorTex.length)
+    ];
+
+    let testProgram = wtu.setupProgram(gl, multiviewShaders, ['a_position'], [0], true);
+    if (!testProgram) {
+        testFailed("Compilation with extension enabled failed.");
+        return;
+    }
+
+    gl.viewport(0, 0, width, height);
+    gl.drawBuffers(drawBuffers);
+
+    let transformLocation = gl.getUniformLocation(testProgram, 'transform');
+    let transformData = new Float32Array (views * 16);
+    for (let viewIndex = 0; viewIndex < views; ++viewIndex) {
+        let scaleX = 1.0 / views;
+        // offsetX is the position of the left edge of the quad we want to get in normalized device coordinates
+        let offsetX = viewIndex / views * 2.0 - 1.0;
+
+        setupTranslateAndScaleXMatrix(transformData, viewIndex * 16, scaleX, offsetX);
+    }
+    gl.uniformMatrix4fv(transformLocation, false, transformData);
+
+    wtu.drawUnitQuad(gl);
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from draw");
+
+    let readFb = gl.createFramebuffer();
+    gl.bindFramebuffer(gl.READ_FRAMEBUFFER, readFb);
+    for (let texIndex = 0; texIndex < colorTex.length; ++texIndex) {
+        for (let viewIndex = 0; viewIndex < views; ++viewIndex) {
+            gl.framebufferTextureLayer(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex[texIndex], 0, viewIndex);
+            let expectedColor = getExpectedColor(viewIndex + texIndex);
+
+            checkVerticalStrip(width, height, views, viewIndex, expectedColor, 'view ' + viewIndex + ' of color attachment ' + texIndex);
+        }
+        debug("");
+    }
+}
+
+description("This test verifies the functionality of the WEBGL_multiview extension when used with multiple draw buffers, if it is available.");
+
+debug("");
+
+if (!gl) {
+  testFailed("WebGL context does not exist");
+} else {
+  testPassed("WebGL context exists");
+
+  if (!gl.getExtension("WEBGL_multiview")) {
+      testPassed("No WEBGL_multiview support -- this is legal");
+  } else {
+      testPassed("Successfully enabled WEBGL_multiview extension");
+      ext = gl.getExtension('WEBGL_multiview');
+
+      runDrawBuffersClearTest();
+
+      wtu.setupUnitQuad(gl, 0, 1);
+
+      runDrawBuffersRenderTest();
+  }
+}
+
+debug("");
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl_multiview_flat_varying.html
@@ -0,0 +1,114 @@
+<!--
+
+/*
+** Copyright (c) 2018 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a
+** copy of this software and/or associated documentation files (the
+** "Materials"), to deal in the Materials without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Materials, and to
+** permit persons to whom the Materials are furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be included
+** in all copies or substantial portions of the Materials.
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+*/
+
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL WEBGL_multiview Conformance Tests</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+<script src="../../js/tests/webgl_multiview_util.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+
+let wtu = WebGLTestUtils;
+let gl = wtu.create3DContext(null, null, 2);
+let ext = null;
+
+function runFlatVaryingTest()
+{
+    debug("");
+    debug("Testing rendering with different colors in fragment shader");
+
+    let width = 256;
+    let height = 256;
+
+    let views = gl.getParameter(ext.MAX_VIEWS_OVR);
+
+    let multiviewShaders = [
+      getMultiviewFlatVaryingVertexShader(views),
+      getMultiviewFlatVaryingFragmentShader()
+    ];
+    let testProgram = wtu.setupProgram(gl, multiviewShaders, ['a_position'], [0], true);
+    if (!testProgram) {
+        testFailed("Compilation with extension enabled failed.");
+        return;
+    }
+
+    let fb = gl.createFramebuffer();
+    gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
+    let colorTex = createTextureWithNearestFiltering(gl.TEXTURE_2D_ARRAY);
+    gl.texStorage3D(gl.TEXTURE_2D_ARRAY, 1, gl.RGBA8, width, height, views);
+    ext.framebufferTextureMultiviewWEBGL(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, 0, views);
+
+    gl.viewport(0, 0, width, height);
+    wtu.drawUnitQuad(gl);
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from draw");
+
+    let readFb = gl.createFramebuffer();
+    gl.bindFramebuffer(gl.READ_FRAMEBUFFER, readFb);
+    for (let viewIndex = 0; viewIndex < views; ++viewIndex) {
+        gl.framebufferTextureLayer(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, viewIndex);
+        let expectedColor = getExpectedColor(viewIndex);
+        wtu.checkCanvasRect(gl, 0, 0, width, height, expectedColor, 'view ' + viewIndex + ' should be colored ' + expectedColor);
+    }
+}
+
+description("This test verifies that flat varyings work in multiview shaders using WEBGL_multiview extension, if it is available.");
+
+debug("");
+
+if (!gl) {
+  testFailed("WebGL context does not exist");
+} else {
+  testPassed("WebGL context exists");
+
+  if (!gl.getExtension("WEBGL_multiview")) {
+      testPassed("No WEBGL_multiview support -- this is legal");
+  } else {
+      testPassed("Successfully enabled WEBGL_multiview extension");
+      ext = gl.getExtension('WEBGL_multiview');
+
+      wtu.setupUnitQuad(gl, 0, 1);
+
+      runFlatVaryingTest();
+  }
+}
+
+debug("");
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl_multiview_instanced_draw.html
@@ -0,0 +1,126 @@
+<!--
+
+/*
+** Copyright (c) 2018 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a
+** copy of this software and/or associated documentation files (the
+** "Materials"), to deal in the Materials without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Materials, and to
+** permit persons to whom the Materials are furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be included
+** in all copies or substantial portions of the Materials.
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+*/
+
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL WEBGL_multiview Conformance Tests</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+<script src="../../js/tests/webgl_multiview_util.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+
+let wtu = WebGLTestUtils;
+let gl = wtu.create3DContext(null, null, 2);
+let ext = null;
+
+function runInstancedDrawTest()
+{
+    debug("");
+    debug("Testing instanced rendering");
+
+    let width = 256;
+    let height = 256;
+
+    let views = gl.getParameter(ext.MAX_VIEWS_OVR);
+
+    let multiviewShaders = [
+      getMultiviewInstancedVertexShader(views),
+      getInstanceColorFragmentShader()
+    ];
+
+    let testProgram = wtu.setupProgram(gl, multiviewShaders, ['a_position'], [0], true);
+    if (!testProgram) {
+        testFailed("Compilation with extension enabled failed.");
+        return;
+    }
+
+    let fb = gl.createFramebuffer();
+    gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
+    let colorTex = createTextureWithNearestFiltering(gl.TEXTURE_2D_ARRAY);
+    gl.texStorage3D(gl.TEXTURE_2D_ARRAY, 1, gl.RGBA8, width, height, views);
+    ext.framebufferTextureMultiviewWEBGL(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, 0, views);
+
+    gl.viewport(0, 0, width, height);
+    gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, 2);
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from draw");
+
+    let readFb = gl.createFramebuffer();
+    gl.bindFramebuffer(gl.READ_FRAMEBUFFER, readFb);
+    for (let viewIndex = 0; viewIndex < views; ++viewIndex) {
+        gl.framebufferTextureLayer(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, viewIndex);
+        let colorRegionLeftEdge = (width / (views * 2)) * viewIndex;
+        let colorRegionRightEdge = (width / (views * 2)) * (viewIndex + 2);
+        let stripWidth = (colorRegionRightEdge - colorRegionLeftEdge) / 2;
+        if (colorRegionLeftEdge > 0) {
+            wtu.checkCanvasRect(gl, 0, 0, Math.floor(colorRegionLeftEdge) - 1, height, [0, 0, 0, 0], 'the left edge of view ' + viewIndex + ' should be untouched');
+        }
+        if (colorRegionRightEdge < width) {
+            wtu.checkCanvasRect(gl, colorRegionRightEdge + 1, 0, width - colorRegionRightEdge - 1, height, [0, 0, 0, 0], 'the right edge of view ' + viewIndex + ' should be untouched');
+        }
+        let expectedColor = getExpectedColor(0);
+        wtu.checkCanvasRect(gl, colorRegionLeftEdge + 1, 0, stripWidth - 2, height, expectedColor, 'a thin strip in view ' + viewIndex + ' drawn by instance 0 should be colored ' + expectedColor);
+        expectedColor = getExpectedColor(1);
+        wtu.checkCanvasRect(gl, colorRegionLeftEdge + stripWidth + 1, 0, stripWidth - 2, height, expectedColor, 'a thin strip in view ' + viewIndex + ' drawn by instance 1 should be colored ' + expectedColor);
+    }
+}
+
+description("This test verifies instanced draws together with the WEBGL_multiview extension, if it is available.");
+
+debug("");
+
+if (!gl) {
+  testFailed("WebGL context does not exist");
+} else {
+  testPassed("WebGL context exists");
+
+  if (!gl.getExtension("WEBGL_multiview")) {
+      testPassed("No WEBGL_multiview support -- this is legal");
+  } else {
+      testPassed("Successfully enabled WEBGL_multiview extension");
+      ext = gl.getExtension('WEBGL_multiview');
+
+      wtu.setupUnitQuad(gl, 0, 1);
+
+      runInstancedDrawTest();
+  }
+}
+
+debug("");
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl_multiview_non_multiview_shaders.html
@@ -0,0 +1,114 @@
+<!--
+
+/*
+** Copyright (c) 2018 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a
+** copy of this software and/or associated documentation files (the
+** "Materials"), to deal in the Materials without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Materials, and to
+** permit persons to whom the Materials are furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be included
+** in all copies or substantial portions of the Materials.
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+*/
+
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL WEBGL_multiview Conformance Tests</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+<script src="../../js/tests/webgl_multiview_util.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+
+let wtu = WebGLTestUtils;
+let gl = wtu.create3DContext(null, null, 2);
+let ext = null;
+
+function runNonMultiviewShaderTest()
+{
+    debug("");
+    debug("Testing rendering with shaders that don't declare num_views");
+
+    let width = 256;
+    let height = 256;
+    let depth = 2;  // always supported so no need to query MAX_VIEWS_OVR.
+
+    let testProgram = wtu.setupSimpleColorProgram(gl);
+    if (!testProgram) {
+        testFailed("Compilation with extension enabled failed.");
+        return;
+    }
+
+    let colorUniformLocation = gl.getUniformLocation(testProgram, 'u_color');
+
+    let fb = gl.createFramebuffer();
+    gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
+    let colorTex = createTextureWithNearestFiltering(gl.TEXTURE_2D_ARRAY);
+    gl.texStorage3D(gl.TEXTURE_2D_ARRAY, 1, gl.RGBA8, width, height, depth);
+    ext.framebufferTextureMultiviewWEBGL(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, 0, 1);
+
+    gl.viewport(0, 0, width, height);
+
+    gl.uniform4f(colorUniformLocation, 0.0, 1.0, 0.0, 1.0);
+    wtu.drawUnitQuad(gl);
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from draw when using a non-multiview shader as long as the number of views is 1");
+
+    let readFb = gl.createFramebuffer();
+    gl.bindFramebuffer(gl.READ_FRAMEBUFFER, readFb);
+    gl.framebufferTextureLayer(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, 0);
+    wtu.checkCanvasRect(gl, 0, 0, width, height, [0, 255, 0, 255], 'view 0 should be green');
+
+    ext.framebufferTextureMultiviewWEBGL(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, 0, 2);
+    wtu.drawUnitQuad(gl);
+    wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "draw should generate an INVALID_OPERATION error when using a non-multiview shader and the number of views is > 1");
+}
+
+description("This test verifies that non-multiview shaders work correctly with WEBGL_multiview extension, if it is available.");
+
+debug("");
+
+if (!gl) {
+  testFailed("WebGL context does not exist");
+} else {
+  testPassed("WebGL context exists");
+
+  if (!gl.getExtension("WEBGL_multiview")) {
+      testPassed("No WEBGL_multiview support -- this is legal");
+  } else {
+      testPassed("Successfully enabled WEBGL_multiview extension");
+      ext = gl.getExtension('WEBGL_multiview');
+
+      wtu.setupUnitQuad(gl, 0, 1);
+
+      runNonMultiviewShaderTest();
+  }
+}
+
+debug("");
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl_multiview_single_view_operations.html
@@ -0,0 +1,274 @@
+<!--
+
+/*
+** Copyright (c) 2018 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a
+** copy of this software and/or associated documentation files (the
+** "Materials"), to deal in the Materials without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Materials, and to
+** permit persons to whom the Materials are furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be included
+** in all copies or substantial portions of the Materials.
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+*/
+
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL WEBGL_multiview Conformance Tests</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+<script src="../../js/tests/webgl_multiview_util.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+
+let wtu = WebGLTestUtils;
+let gl = wtu.create3DContext(null, null, 2);
+let ext = null;
+
+function runSingleViewReadTest()
+{
+    debug("");
+    debug("Testing reading from a multiview framebuffer with num_views = 1");
+
+    let width = 256;
+    let height = 256;
+    let depth = 2;  // always supported so no need to query MAX_VIEWS_OVR.
+
+    let fb = gl.createFramebuffer();
+    gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
+    let colorTex = createTextureWithNearestFiltering(gl.TEXTURE_2D_ARRAY);
+    gl.texStorage3D(gl.TEXTURE_2D_ARRAY, 1, gl.RGBA8, width, height, depth);
+    ext.framebufferTextureMultiviewWEBGL(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, 0, 1);
+
+    gl.viewport(0, 0, width, height);
+    gl.clearColor(0.0, 1.0, 1.0, 1.0);
+    gl.clear(gl.COLOR_BUFFER_BIT);
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from clear");
+
+    let buf = new Uint8Array(width * height * 4);
+    gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from readPixels");
+
+    wtu.checkCanvasRect(gl, 0, 0, width, height, [0, 255, 255, 255], 'view 0 should be cyan');
+    gl.getError();
+
+    // Also test for the error case with two views
+    ext.framebufferTextureMultiviewWEBGL(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, 0, 2);
+    gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
+    wtu.glErrorShouldBe(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "should be an error to read from a framebuffer with two views");
+}
+
+function runSingleViewBlitTest()
+{
+    debug("");
+    debug("Testing blitting from a multiview framebuffer with num_views = 1");
+
+    let width = 256;
+    let height = 256;
+    let depth = 2;  // always supported so no need to query MAX_VIEWS_OVR.
+
+    let fb = gl.createFramebuffer();
+    gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
+    let colorTex = createTextureWithNearestFiltering(gl.TEXTURE_2D_ARRAY);
+    gl.texStorage3D(gl.TEXTURE_2D_ARRAY, 1, gl.RGBA8, width, height, depth);
+    ext.framebufferTextureMultiviewWEBGL(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, 0, 1);
+
+    gl.viewport(0, 0, width, height);
+    gl.clearColor(0.0, 1.0, 1.0, 1.0);
+    gl.clear(gl.COLOR_BUFFER_BIT);
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from clear");
+
+    gl.canvas.width = width;
+    gl.canvas.height = height;
+    gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null);
+    gl.clearColor(0.0, 0.0, 0.0, 0.0)
+    gl.clear(gl.COLOR_BUFFER_BIT);
+    gl.blitFramebuffer(0, 0, width, height, 0, 0, width, height, gl.COLOR_BUFFER_BIT, gl.NEAREST);
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from blitFramebuffer");
+
+    gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+    wtu.checkCanvasRect(gl, 0, 0, width, height, [0, 255, 255, 255], 'view 0 blitted to the default framebuffer should be cyan');
+
+    // Also test for the error case with multiview blit target
+    gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fb);
+    gl.blitFramebuffer(0, 0, width, height, 0, 0, width, height, gl.COLOR_BUFFER_BIT, gl.NEAREST);
+    wtu.glErrorShouldBe(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "should be an error to blit to a multiview framebuffer even if it has just one view");
+
+    // Also test for the error case with two views
+    gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
+    ext.framebufferTextureMultiviewWEBGL(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, 0, 2);
+    gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null);
+    gl.blitFramebuffer(0, 0, width, height, 0, 0, width, height, gl.COLOR_BUFFER_BIT, gl.NEAREST);
+    wtu.glErrorShouldBe(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "should be an error to blit from a framebuffer with two views");
+}
+
+function runSingleViewCopyTexImage2DTest()
+{
+    debug("");
+    debug("Testing copyTexImage2D from a multiview framebuffer with num_views = 1");
+
+    let width = 256;
+    let height = 256;
+    let depth = 2;  // always supported so no need to query MAX_VIEWS_OVR.
+
+    let fb = gl.createFramebuffer();
+    gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
+    let colorTex = createTextureWithNearestFiltering(gl.TEXTURE_2D_ARRAY);
+    gl.texStorage3D(gl.TEXTURE_2D_ARRAY, 1, gl.RGBA8, width, height, depth);
+    ext.framebufferTextureMultiviewWEBGL(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, 0, 1);
+
+    gl.viewport(0, 0, width, height);
+    gl.clearColor(0.0, 1.0, 1.0, 1.0);
+    gl.clear(gl.COLOR_BUFFER_BIT);
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from clear");
+
+    let copyTargetTex = createTextureWithNearestFiltering(gl.TEXTURE_2D);
+    gl.bindTexture(gl.TEXTURE_2D, copyTargetTex);
+    gl.copyTexImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, 0, 0, width, height, 0);
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from copyTexImage2D");
+
+    let copyFb = gl.createFramebuffer();
+    gl.bindFramebuffer(gl.FRAMEBUFFER, copyFb);
+    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, copyTargetTex, 0);
+    wtu.checkCanvasRect(gl, 0, 0, width, height, [0, 255, 255, 255], 'copy of view 0 in the 2D texture should be cyan');
+
+    // Also test for the error case with two views
+    gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
+    ext.framebufferTextureMultiviewWEBGL(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, 0, 2);
+    gl.bindTexture(gl.TEXTURE_2D, copyTargetTex);
+    gl.copyTexImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, 0, 0, width, height, 0);
+    wtu.glErrorShouldBe(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "should be an error to copy from a framebuffer with two views");
+}
+
+function runSingleViewCopyTexSubImage2DTest()
+{
+    debug("");
+    debug("Testing copyTexSubImage2D from a multiview framebuffer with num_views = 1");
+
+    let width = 256;
+    let height = 256;
+    let depth = 2;  // always supported so no need to query MAX_VIEWS_OVR.
+
+    let fb = gl.createFramebuffer();
+    gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
+    let colorTex = createTextureWithNearestFiltering(gl.TEXTURE_2D_ARRAY);
+    gl.texStorage3D(gl.TEXTURE_2D_ARRAY, 1, gl.RGBA8, width, height, depth);
+    ext.framebufferTextureMultiviewWEBGL(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, 0, 1);
+
+    gl.viewport(0, 0, width, height);
+    gl.clearColor(0.0, 1.0, 1.0, 1.0);
+    gl.clear(gl.COLOR_BUFFER_BIT);
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from clear");
+
+    let copyTargetTex = createTextureWithNearestFiltering(gl.TEXTURE_2D);
+    gl.bindTexture(gl.TEXTURE_2D, copyTargetTex);
+    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
+    gl.copyTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 0, 0, width, height);
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from copyTexImage2D");
+
+    let copyFb = gl.createFramebuffer();
+    gl.bindFramebuffer(gl.FRAMEBUFFER, copyFb);
+    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, copyTargetTex, 0);
+    wtu.checkCanvasRect(gl, 0, 0, width, height, [0, 255, 255, 255], 'copy of view 0 in the 2D texture should be cyan');
+
+    // Also test for the error case with two views
+    gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
+    ext.framebufferTextureMultiviewWEBGL(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, 0, 2);
+    gl.bindTexture(gl.TEXTURE_2D, copyTargetTex);
+    gl.copyTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 0, 0, width, height);
+    wtu.glErrorShouldBe(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "should be an error to copy from a framebuffer with two views");
+}
+
+function runSingleViewCopyTexSubImage3DTest()
+{
+    debug("");
+    debug("Testing copyTexSubImage3D from a multiview framebuffer with num_views = 1");
+
+    let width = 256;
+    let height = 256;
+    let depth = 2;  // always supported so no need to query MAX_VIEWS_OVR.
+
+    let fb = gl.createFramebuffer();
+    gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
+    let colorTex = createTextureWithNearestFiltering(gl.TEXTURE_2D_ARRAY);
+    gl.texStorage3D(gl.TEXTURE_2D_ARRAY, 1, gl.RGBA8, width, height, depth);
+    ext.framebufferTextureMultiviewWEBGL(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, 0, 1);
+
+    gl.viewport(0, 0, width, height);
+    gl.clearColor(0.0, 1.0, 1.0, 1.0);
+    gl.clear(gl.COLOR_BUFFER_BIT);
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from clear");
+
+    let copyTargetTex = createTextureWithNearestFiltering(gl.TEXTURE_3D);
+    gl.bindTexture(gl.TEXTURE_3D, copyTargetTex);
+    gl.texImage3D(gl.TEXTURE_3D, 0, gl.RGBA8, width, height, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
+    gl.copyTexSubImage3D(gl.TEXTURE_3D, 0, 0, 0, 0, 0, 0, width, height);
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from copyTexSubImage3D");
+
+    let copyFb = gl.createFramebuffer();
+    gl.bindFramebuffer(gl.FRAMEBUFFER, copyFb);
+    gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, copyTargetTex, 0, 0);
+    wtu.checkCanvasRect(gl, 0, 0, width, height, [0, 255, 255, 255], 'copy of view 0 in the 3D texture should be cyan');
+
+    // Also test for the error case with two views
+    gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
+    ext.framebufferTextureMultiviewWEBGL(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, 0, 2);
+    gl.bindTexture(gl.TEXTURE_3D, copyTargetTex);
+    gl.copyTexSubImage3D(gl.TEXTURE_3D, 0, 0, 0, 0, 0, 0, width, height);
+    wtu.glErrorShouldBe(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "should be an error to copy from a framebuffer with two views");
+}
+
+description("This test verifies that framebuffers with only one view can be read from with WEBGL_multiview extension, if it is available.");
+
+debug("");
+
+if (!gl) {
+  testFailed("WebGL context does not exist");
+} else {
+  testPassed("WebGL context exists");
+
+  if (!gl.getExtension("WEBGL_multiview")) {
+      testPassed("No WEBGL_multiview support -- this is legal");
+  } else {
+      testPassed("Successfully enabled WEBGL_multiview extension");
+      ext = gl.getExtension('WEBGL_multiview');
+
+      runSingleViewReadTest();
+
+      runSingleViewBlitTest();
+
+      runSingleViewCopyTexImage2DTest();
+
+      runSingleViewCopyTexSubImage2DTest();
+
+      runSingleViewCopyTexSubImage3DTest();
+  }
+}
+
+debug("");
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl_multiview_timer_query.html
@@ -0,0 +1,163 @@
+<!--
+
+/*
+** Copyright (c) 2018 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a
+** copy of this software and/or associated documentation files (the
+** "Materials"), to deal in the Materials without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Materials, and to
+** permit persons to whom the Materials are furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be included
+** in all copies or substantial portions of the Materials.
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+*/
+
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL WEBGL_multiview Conformance Tests</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+<script src="../../js/tests/webgl_multiview_util.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+
+let wtu = WebGLTestUtils;
+let gl = wtu.create3DContext(null, null, 2);
+let ext = null;
+let queryExt = null;
+
+function runClearTest()
+{
+    debug("");
+    debug("Testing clear");
+
+    let width = 256;
+    let height = 256;
+
+    let views = gl.getParameter(ext.MAX_VIEWS_OVR);
+
+    let fb = gl.createFramebuffer();
+    gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
+    let colorTex = createTextureWithNearestFiltering(gl.TEXTURE_2D_ARRAY);
+    gl.texStorage3D(gl.TEXTURE_2D_ARRAY, 1, gl.RGBA8, width, height, views);
+    ext.framebufferTextureMultiviewWEBGL(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, 0, views);
+
+    gl.viewport(0, 0, width, height);
+
+    gl.clearColor(1, 0, 0, 1);
+
+    let query = gl.createQuery();
+    gl.beginQuery(queryExt.TIME_ELAPSED_EXT, query);
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from setup");
+
+    gl.clear(gl.COLOR_BUFFER_BIT);
+    wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "multiview clear should generate invalid operation when a timer query is active");
+
+    gl.endQuery(queryExt.TIME_ELAPSED_EXT);
+
+    let readFb = gl.createFramebuffer();
+    gl.bindFramebuffer(gl.READ_FRAMEBUFFER, readFb);
+    for (let viewIndex = 0; viewIndex < views; ++viewIndex) {
+        gl.framebufferTextureLayer(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, viewIndex);
+        let expectedColor = [0, 0, 0, 0];
+        wtu.checkCanvasRect(gl, 0, 0, width, height, expectedColor, 'view ' + viewIndex + ' should be untouched');
+    }
+}
+
+function runFragmentShaderRenderTest()
+{
+    debug("");
+    debug("Testing rendering with different colors in fragment shader");
+
+    let width = 256;
+    let height = 256;
+
+    let views = gl.getParameter(ext.MAX_VIEWS_OVR);
+
+    let multiviewShaders = [
+      getMultiviewPassthroughVertexShader(views),
+      getMultiviewColorFragmentShader()
+    ];
+    let testProgram = wtu.setupProgram(gl, multiviewShaders, ['a_position'], [0], true);
+    if (!testProgram) {
+        testFailed("Compilation with extension enabled failed.");
+        return;
+    }
+
+    let fb = gl.createFramebuffer();
+    gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
+    let colorTex = createTextureWithNearestFiltering(gl.TEXTURE_2D_ARRAY);
+    gl.texStorage3D(gl.TEXTURE_2D_ARRAY, 1, gl.RGBA8, width, height, views);
+    ext.framebufferTextureMultiviewWEBGL(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, 0, views);
+
+    gl.viewport(0, 0, width, height);
+
+    let query = gl.createQuery();
+    gl.beginQuery(queryExt.TIME_ELAPSED_EXT, query);
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from setup");
+
+    wtu.drawUnitQuad(gl);
+    wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "multiview draw should generate invalid operation when a timer query is active");
+
+    gl.endQuery(queryExt.TIME_ELAPSED_EXT);
+
+    let readFb = gl.createFramebuffer();
+    gl.bindFramebuffer(gl.READ_FRAMEBUFFER, readFb);
+    for (let viewIndex = 0; viewIndex < views; ++viewIndex) {
+        gl.framebufferTextureLayer(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, viewIndex);
+        let expectedColor = [0, 0, 0, 0];
+        wtu.checkCanvasRect(gl, 0, 0, width, height, expectedColor, 'view ' + viewIndex + ' should be untouched');
+    }
+}
+
+description("This test verifies the functionality of the WEBGL_multiview extension and its interaction with EXT_disjoint_timer_query_webgl2, if it is available.");
+
+debug("");
+
+if (!gl) {
+  testFailed("WebGL context does not exist");
+} else {
+  testPassed("WebGL context exists");
+
+  if (!gl.getExtension("WEBGL_multiview") || !gl.getExtension("EXT_disjoint_timer_query_webgl2")) {
+      testPassed("No WEBGL_multiview support or no EXT_disjoint_timer_query_webgl2 support -- this is legal");
+  } else {
+      testPassed("Successfully enabled WEBGL_multiview and EXT_disjoint_timer_query_webgl2 extensions");
+      ext = gl.getExtension('WEBGL_multiview');
+      queryExt = gl.getExtension('EXT_disjoint_timer_query_webgl2');
+
+      runClearTest();
+
+      wtu.setupUnitQuad(gl, 0, 1);
+
+      runFragmentShaderRenderTest();
+  }
+}
+
+debug("");
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl_multiview_transform_feedback.html
@@ -0,0 +1,146 @@
+<!--
+
+/*
+** Copyright (c) 2018 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a
+** copy of this software and/or associated documentation files (the
+** "Materials"), to deal in the Materials without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Materials, and to
+** permit persons to whom the Materials are furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be included
+** in all copies or substantial portions of the Materials.
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+*/
+
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL WEBGL_multiview Conformance Tests</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+<script src="../../js/tests/webgl_multiview_util.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+
+let wtu = WebGLTestUtils;
+let gl = wtu.create3DContext(null, null, 2);
+let ext = null;
+let queryExt = null;
+
+function runTransformFeedbackTest()
+{
+    debug("");
+    debug("Testing transform feedback combined with multiview rendering");
+
+    let width = 64;
+    let height = 64;
+
+    let views = gl.getParameter(ext.MAX_VIEWS_OVR);
+
+    let multiviewShaders = [
+      getMultiviewVaryingVertexShader(views),
+      getMultiviewVaryingFragmentShader()
+    ];
+    let testProgram = wtu.setupTransformFeedbackProgram(gl, multiviewShaders, ['testVarying'], gl.SEPARATE_ATTRIBS, ['a_position'], [0], true);
+    if (!testProgram) {
+        testFailed("Compilation with extension enabled failed.");
+        return;
+    }
+
+    let fb = gl.createFramebuffer();
+    gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
+    let colorTex = createTextureWithNearestFiltering(gl.TEXTURE_2D_ARRAY);
+    gl.texStorage3D(gl.TEXTURE_2D_ARRAY, 1, gl.RGBA8, width, height, views);
+    ext.framebufferTextureMultiviewWEBGL(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, 0, views);
+
+    let xfb = gl.createTransformFeedback();
+    gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, xfb);
+
+    let xfbBuffer = gl.createBuffer();
+    gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, xfbBuffer);
+    gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 128, gl.DYNAMIC_DRAW);
+
+    gl.viewport(0, 0, width, height);
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from setup");
+
+    gl.beginTransformFeedback(gl.TRIANGLES);
+    wtu.drawUnitQuad(gl);
+    wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "rendering to a multiview framebuffer with more than one view when there's an active transform feedback should result in invalid operation");
+
+    gl.pauseTransformFeedback();
+    wtu.drawUnitQuad(gl);
+    wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "rendering to a multiview framebuffer with more than one view when there's an active transform feedback should result in invalid operation even if the transform feedback is paused");
+
+    let readFb = gl.createFramebuffer();
+    gl.bindFramebuffer(gl.READ_FRAMEBUFFER, readFb);
+    for (let viewIndex = 0; viewIndex < views; ++viewIndex) {
+        gl.framebufferTextureLayer(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, viewIndex);
+        let expectedColor = [0, 0, 0, 0];
+        wtu.checkCanvasRect(gl, 0, 0, width, height, expectedColor, 'view ' + viewIndex + ' should be untouched');
+    }
+
+    gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);
+
+    wtu.drawUnitQuad(gl);
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from draw when transform feedback is unbound");
+
+    gl.bindFramebuffer(gl.READ_FRAMEBUFFER, readFb);
+    for (let viewIndex = 0; viewIndex < views; ++viewIndex) {
+        gl.framebufferTextureLayer(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, colorTex, 0, viewIndex);
+        let expectedColor = getExpectedColor(viewIndex);
+        wtu.checkCanvasRect(gl, 0, 0, width, height, expectedColor, 'view ' + viewIndex + ' should be colored ' + expectedColor);
+    }
+
+    gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, xfb);
+    gl.endTransformFeedback();
+    gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);