Bug 1111689: Part 2 - Update ext-shader-texture-lod WebGL conformance test draft
authorKearwood (Kip) Gilbert <kgilbert@mozilla.com>
Tue, 08 Sep 2015 15:27:46 -0700
changeset 291352 04ce67347a23d0140e5a946a9c03ca9a94d03529
parent 291351 80906ad76fe8702e072f65ff96cf6537bb5ba0d6
child 509134 824b5b2616ff93ec1d50c49f7ff6cb4dd446611e
push id5244
push userkgilbert@mozilla.com
push dateWed, 09 Sep 2015 20:45:56 +0000
bugs1111689
milestone43.0a1
Bug 1111689: Part 2 - Update ext-shader-texture-lod WebGL conformance test - Cherry picked minimal changes to support updated ext-shader-texture-lod webgl conformance test.
dom/canvas/test/webgl-conformance/conformance/extensions/ext-shader-texture-lod.html
dom/canvas/test/webgl-conformance/conformance/resources/webgl-test-utils.js
dom/canvas/test/webgl-conformance/ext-shader-texture-lod-cherry-pick.patch
--- a/dom/canvas/test/webgl-conformance/conformance/extensions/ext-shader-texture-lod.html
+++ b/dom/canvas/test/webgl-conformance/conformance/extensions/ext-shader-texture-lod.html
@@ -1,37 +1,60 @@
-<!--
-Copyright (c) 2011 The Chromium Authors. All rights reserved.
-Use of this source code is governed by a BSD-style license that can be
-found in the LICENSE file.
- -->
+<!--
+
+/*
+** Copyright (c) 2014 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_shader_texture_lod Conformance Tests</title>
 <link rel="stylesheet" href="../../resources/js-test-style.css"/>
-<script src="../../resources/desktop-gl-constants.js" type="text/javascript"></script>
+<script src="../../resources/desktop-gl-constants.js"></script>
 <script src="../../resources/js-test-pre.js"></script>
-<script src="../resources/webgl-test.js"></script>
 <script src="../resources/webgl-test-utils.js"></script>
 </head>
 <body>
 <div id="description"></div>
-<canvas id="canvas" width="256" height="256" style="width: 50px; height: 50px;"> </canvas>
+<canvas id="canvas" style="width: 256px; height: 256px;"> </canvas>
+<canvas id="canvas2" style="width: 256px; height: 256px;"> </canvas>
 <div id="console"></div>
-<!-- Shaders for testing standard derivatives -->
+<!-- Shaders for testing texture LOD functions -->
 
 <!-- Shader omitting the required #extension pragma -->
 <script id="missingPragmaFragmentShader" type="x-shader/x-fragment">
 precision mediump float;
 varying vec2 texCoord0v;
+uniform float lod;
+uniform sampler2D tex;
 void main() {
     vec4 color = texture2DLodEXT(tex, texCoord0v, lod);
-    gl_FragColor = vec4(dx, dy, w, 1.0);
+    gl_FragColor = color;
 }
 </script>
 
 <!-- Shader to test macro definition -->
 <script id="macroFragmentShader" type="x-shader/x-fragment">
 precision mediump float;
 void main() {
 #ifdef GL_EXT_shader_texture_lod
@@ -91,47 +114,60 @@ void main() {
 
 <script>
 description("This test verifies the functionality of the EXT_shader_texture_lod extension, if it is available.");
 
 debug("");
 
 var wtu = WebGLTestUtils;
 var canvas = document.getElementById("canvas");
-
-shouldBe("canvas.width", "256");
-shouldBe("canvas.height", "256");
-
 var gl = wtu.create3DContext(canvas);
 var ext = null;
 
-if (!gl) {
-    testFailed("WebGL context does not exist");
-} else {
-    testPassed("WebGL context exists");
+// Run all tests once.
+runAllTests();
 
-    // Run tests with extension disabled
-    runShaderTests(false);
+// Run all tests against with a new context to test for any cache issues.
+debug("");
+debug("Testing new context to catch cache errors");
+var canvas2 = document.getElementById("canvas2");
+gl = wtu.create3DContext(canvas2);
+ext = null;
+runAllTests();
+
+function runAllTests() {
+    if (!gl) {
+        testFailed("WebGL context does not exist");
+    } else {
+        testPassed("WebGL context exists");
+
+        // Run tests with extension disabled
+        runShaderTests(false);
 
-    // Query the extension and store globally so shouldBe can access it
-    ext = gl.getExtension("EXT_shader_texture_lod");
-    if (!ext) {
-        testPassed("No EXT_shader_texture_lod support -- this is legal");
+        // Query the extension and store globally so shouldBe can access it
+        ext = gl.getExtension("EXT_shader_texture_lod");
+        if (!ext) {
+            testPassed("No EXT_shader_texture_lod support -- this is legal");
+
+            runSupportedTest(false);
+        } else {
+            testPassed("Successfully enabled EXT_shader_texture_lod extension");
+
+            runSupportedTest(true);
 
-        runSupportedTest(false);
-    } else {
-        testPassed("Successfully enabled EXT_shader_texture_lod extension");
-
-        runSupportedTest(true);
+            runShaderTests(true);
+            runOutputTests();
+            runUniqueObjectTest();
+            runReferenceCycleTest();
 
-        runShaderTests(true);
-        runOutputTests();
-        runUniqueObjectTest();
-        runReferenceCycleTest();
+            // Run deferred link tests.
+            runDeferredLinkTests();
+        }
     }
+
 }
 
 function runSupportedTest(extensionEnabled) {
     var supported = gl.getSupportedExtensions();
     if (supported.indexOf("EXT_shader_texture_lod") >= 0) {
         if (extensionEnabled) {
             testPassed("EXT_shader_texture_lod listed as supported and getExtension succeeded");
         } else {
@@ -188,93 +224,138 @@ function runShaderTests(extensionEnabled
             testFailed("Shader built-ins compiled successfully when extension disabled");
         } else {
             testPassed("Shader built-ins failed to compile when extension disabled");
         }
     }
 }
 
 function runOutputTests() {
+    debug("");
     debug("Testing various draws for valid built-in function behavior");
-
-    canvas.width = 256; canvas.height = 256;
     gl.viewport(0, 0, canvas.width, canvas.height);
 
     var program = wtu.setupProgram(gl, ["outputVertexShader", "outputFragmentShader"], ['vPosition', 'texCoord0'], [0, 1]);
     var quadParameters = wtu.setupUnitQuad(gl, 0, 1);
 
     var colors = [
-	{name: 'red', color:[255, 0, 0, 255]},
-	{name: 'green', color:[0, 255, 0, 255]},
-	{name: 'blue', color:[0, 0, 255, 255]},
-	{name: 'yellow', color:[255, 255, 0, 255]},
-	{name: 'magenta', color:[255, 0, 255, 255]},
-	{name: 'cyan', color:[0, 255, 255, 255]},
-	{name: 'pink', color:[255, 128, 128, 255]},
-	{name: 'gray', color:[128, 128, 128, 255]},
-	{name: 'light green', color:[128, 255, 128, 255]},
+        {name: 'red', color:[255, 0, 0, 255]},
+        {name: 'green', color:[0, 255, 0, 255]},
+        {name: 'blue', color:[0, 0, 255, 255]},
+        {name: 'yellow', color:[255, 255, 0, 255]},
+        {name: 'magenta', color:[255, 0, 255, 255]},
+        {name: 'cyan', color:[0, 255, 255, 255]},
+        {name: 'pink', color:[255, 128, 128, 255]},
+        {name: 'gray', color:[128, 128, 128, 255]},
+        {name: 'light green', color:[128, 255, 128, 255]},
     ];
 
-    if (colors.length != 9) {
-	testFailed("9 colors are needed (9 mips for 256x256 texture), only have " + colors.length);
-    } else {
-	testPassed("9 colors found (9 mips for 256x256 texture)");
-    }
-
     var tex = gl.createTexture();
     gl.bindTexture(gl.TEXTURE_2D, tex);
-    gl.texParameteri(
-	gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_LINEAR);
+    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_LINEAR);
     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
 
     for (var ii = 0; ii < colors.length; ++ii) {
-	var color = colors[ii];
-	var size = Math.pow(2, colors.length - ii - 1);
-	wtu.fillTexture(gl, tex, size, size, color.color, ii);
+        var color = colors[ii];
+        var size = Math.pow(2, colors.length - ii - 1);
+        wtu.fillTexture(gl, tex, size, size, color.color, ii);
     }
 
     var loc = gl.getUniformLocation(program, "lod");
 
     for (var ii = 0; ii < colors.length; ++ii) {
-	gl.uniform1f(loc, ii);
-	var color = colors[ii];
-	wtu.drawQuad(gl);
-	wtu.checkCanvas(
-	    gl, color.color,
-	    "256x256 texture drawn to 256x256 dest with lod = " + ii +
-		" should be " + color.name);
+        gl.uniform1f(loc, ii);
+        var color = colors[ii];
+        wtu.drawUnitQuad(gl);
+        wtu.checkCanvas(
+            gl, color.color,
+            "256x256 texture drawn to 256x256 dest with lod = " + ii +
+            " should be " + color.name);
     }
 
-    glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors.");
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR);
 }
 
 function runUniqueObjectTest()
 {
+    debug("");
     debug("Testing that getExtension() returns the same object each time");
     gl.getExtension("EXT_shader_texture_lod").myProperty = 2;
     gc();
     shouldBe('gl.getExtension("EXT_shader_texture_lod").myProperty', '2');
 }
 
 function runReferenceCycleTest()
 {
     // create some reference cycles. The goal is to see if they cause leaks. The point is that
     // some browser test runners have instrumentation to detect leaked refcounted objects.
-
+    debug("");
     debug("Testing reference cycles between context and extension objects");
     var ext = gl.getExtension("EXT_shader_texture_lod");
 
     // create cycle between extension and context, since the context has to hold a reference to the extension
     ext.context = gl;
 
     // create a self-cycle on the extension object
     ext.ext = ext;
 }
 
+function runDeferredLinkTests() {
+    debug("");
+    debug("Testing deferred shader compilation tests.");
+
+    // Test for compilation failures that are caused by missing extensions
+    // do not succeed if extensions are enabled during linking. This would
+    // only happen for deferred shader compilations.
+
+    // First test if link succeeds with extension enabled.
+    var glEnabled = wtu.create3DContext();
+    var extEnabled = glEnabled.getExtension("EXT_shader_texture_lod");
+    if (!extEnabled) {
+        testFailed("Deferred link test expects the extension to be supported");
+    }
+
+    var vertexShader = wtu.loadShaderFromScript(glEnabled, "goodVertexShader");
+    var fragmentShader = wtu.loadShaderFromScript(glEnabled, "macroFragmentShader");
+
+    if (!vertexShader || !fragmentShader) {
+        testFailed("Could not create good shaders.");
+        return;
+    }
+
+    var program = wtu.setupProgram(glEnabled, [vertexShader, fragmentShader]);
+
+    if (!program) {
+        testFailed("Compilation with extension enabled failed.");
+        return;
+    }
+
+    // Create new context to test link failure without extension enabled.
+    var glDeferred = wtu.create3DContext();
+
+    var vertexShader = wtu.loadShaderFromScript(glDeferred, "goodVertexShader", glDeferred.VERTEX_SHADER, undefined, undefined, true);
+    var fragmentShader = wtu.loadShaderFromScript(glDeferred, "macroFragmentShader", glDeferred.FRAGMENT_SHADER, undefined, undefined, true);
+
+    if (vertexShader == null || fragmentShader == null) {
+        testFailed("Could not create shaders.");
+        return;
+    }
+
+    // Shader compilations should have failed due to extensions not enabled.
+    glDeferred.getExtension("EXT_shader_texture_lod");
+    var program = wtu.setupProgram(glDeferred, [vertexShader, fragmentShader]);
+    if (program) {
+        testFailed("Compilation with extension disabled then linking with extension enabled should have failed.");
+        return;
+    }
+
+    testPassed("Compilation with extension disabled then linking with extension enabled.");
+}
+
 debug("");
 successfullyParsed = true;
 </script>
 <script>finishTest();</script>
 
 </body>
 </html>
--- a/dom/canvas/test/webgl-conformance/conformance/resources/webgl-test-utils.js
+++ b/dom/canvas/test/webgl-conformance/conformance/resources/webgl-test-utils.js
@@ -994,19 +994,28 @@ var readFileList = function(url) {
 };
 
 /**
  * Loads a shader.
  * @param {!WebGLContext} gl The WebGLContext to use.
  * @param {string} shaderSource The shader source.
  * @param {number} shaderType The type of shader.
  * @param {function(string): void) opt_errorCallback callback for errors.
+ * @param {boolean} opt_logShaders Whether to log shader source.
+ * @param {string} opt_shaderLabel Label that identifies the shader source in
+ *     the log.
+ * @param {string} opt_url URL from where the shader source was loaded from.
+ *     If opt_logShaders is set, then a link to the source file will also be
+ *     added.
+ * @param {boolean} Skip compilation status check. Default = false.
  * @return {!WebGLShader} The created shader.
  */
-var loadShader = function(gl, shaderSource, shaderType, opt_errorCallback) {
+var loadShader = function(
+    gl, shaderSource, shaderType, opt_errorCallback, opt_logShaders,
+    opt_shaderLabel, opt_url, opt_skipCompileStatus) {
   var errFn = opt_errorCallback || error;
   // Create the shader object
   var shader = gl.createShader(shaderType);
   if (shader == null) {
     errFn("*** Error: unable to create shader '"+shaderSource+"'");
     return null;
   }
 
@@ -1016,24 +1025,35 @@ var loadShader = function(gl, shaderSour
   if (err != gl.NO_ERROR) {
     errFn("*** Error loading shader '" + shader + "':" + glEnumToString(gl, err));
     return null;
   }
 
   // Compile the shader
   gl.compileShader(shader);
 
+  if (opt_logShaders) {
+    var label = shaderType == gl.VERTEX_SHADER ? 'vertex shader' : 'fragment_shader';
+    if (opt_shaderLabel) {
+      label = opt_shaderLabel + ' ' + label;
+    }
+    addShaderSources(
+        gl, document.getElementById('console'), label, shader, shaderSource, opt_url);
+  }
+
   // Check the compile status
-  var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
-  if (!compiled) {
-    // Something went wrong during compilation; get the error
-    lastError = gl.getShaderInfoLog(shader);
-    errFn("*** Error compiling shader '" + shader + "':" + lastError);
-    gl.deleteShader(shader);
-    return null;
+  if (!opt_skipCompileStatus) {
+    var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
+    if (!compiled) {
+      // Something went wrong during compilation; get the error
+      lastError = gl.getShaderInfoLog(shader);
+      errFn("*** Error compiling " + glEnumToString(gl, shaderType) + " '" + shader + "':" + lastError);
+      gl.deleteShader(shader);
+      return null;
+    }
   }
 
   return shader;
 }
 
 /**
  * Loads a shader from a URL.
  * @param {!WebGLContext} gl The WebGLContext to use.
@@ -1060,42 +1080,42 @@ var getScript = function(scriptId) {
 
 /**
  * Loads a shader from a script tag.
  * @param {!WebGLContext} gl The WebGLContext to use.
  * @param {string} scriptId The id of the script tag.
  * @param {number} opt_shaderType The type of shader. If not passed in it will
  *     be derived from the type of the script tag.
  * @param {function(string): void) opt_errorCallback callback for errors.
+ * @param {boolean} opt_logShaders Whether to log shader source.
+ * @param {boolean} Skip compilation status check. Default = false.
  * @return {!WebGLShader} The created shader.
  */
 var loadShaderFromScript = function(
-    gl, scriptId, opt_shaderType, opt_errorCallback) {
+    gl, scriptId, opt_shaderType, opt_errorCallback, opt_logShaders, opt_skipCompileStatus) {
   var shaderSource = "";
-  var shaderType;
   var shaderScript = document.getElementById(scriptId);
   if (!shaderScript) {
     throw("*** Error: unknown script element " + scriptId);
   }
   shaderSource = shaderScript.text;
 
   if (!opt_shaderType) {
     if (shaderScript.type == "x-shader/x-vertex") {
-      shaderType = gl.VERTEX_SHADER;
+      opt_shaderType = gl.VERTEX_SHADER;
     } else if (shaderScript.type == "x-shader/x-fragment") {
-      shaderType = gl.FRAGMENT_SHADER;
-    } else if (shaderType != gl.VERTEX_SHADER && shaderType != gl.FRAGMENT_SHADER) {
+      opt_shaderType = gl.FRAGMENT_SHADER;
+    } else {
       throw("*** Error: unknown shader type");
       return null;
     }
   }
 
-  return loadShader(
-      gl, shaderSource, opt_shaderType ? opt_shaderType : shaderType,
-      opt_errorCallback);
+  return loadShader(gl, shaderSource, opt_shaderType, opt_errorCallback,
+      opt_logShaders, undefined, undefined, opt_skipCompileStatus);
 };
 
 var loadStandardProgram = function(gl) {
   var program = gl.createProgram();
   gl.attachShader(program, loadStandardVertexShader(gl));
   gl.attachShader(program, loadStandardFragmentShader(gl));
   linkProgram(gl, program);
   return program;
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-conformance/ext-shader-texture-lod-cherry-pick.patch
@@ -0,0 +1,464 @@
+# HG changeset patch
+# User Kearwood (Kip) Gilbert <kgilbert@mozilla.com>
+# Date 1441751266 25200
+#      Tue Sep 08 15:27:46 2015 -0700
+# Node ID 43ba953a36814b5f276174afae234f68486f349f
+# Parent  1cbec4a92eae5d8d076072f4b8d27eb2c9dbb363
+Bug 1111689: Part 2 - Update ext-shader-texture-lod WebGL conformance test
+- Cherry picked minimal changes to support updated ext-shader-texture-lod
+  webgl conformance test.
+
+diff --git a/dom/canvas/test/webgl-conformance/conformance/extensions/ext-shader-texture-lod.html b/dom/canvas/test/webgl-conformance/conformance/extensions/ext-shader-texture-lod.html
+--- a/dom/canvas/test/webgl-conformance/conformance/extensions/ext-shader-texture-lod.html
++++ b/dom/canvas/test/webgl-conformance/conformance/extensions/ext-shader-texture-lod.html
+@@ -1,37 +1,60 @@
+-<!--
+-Copyright (c) 2011 The Chromium Authors. All rights reserved.
+-Use of this source code is governed by a BSD-style license that can be
+-found in the LICENSE file.
+- -->
++<!--
++
++/*
++** Copyright (c) 2014 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_shader_texture_lod Conformance Tests</title>
+ <link rel="stylesheet" href="../../resources/js-test-style.css"/>
+-<script src="../../resources/desktop-gl-constants.js" type="text/javascript"></script>
++<script src="../../resources/desktop-gl-constants.js"></script>
+ <script src="../../resources/js-test-pre.js"></script>
+-<script src="../resources/webgl-test.js"></script>
+ <script src="../resources/webgl-test-utils.js"></script>
+ </head>
+ <body>
+ <div id="description"></div>
+-<canvas id="canvas" width="256" height="256" style="width: 50px; height: 50px;"> </canvas>
++<canvas id="canvas" style="width: 256px; height: 256px;"> </canvas>
++<canvas id="canvas2" style="width: 256px; height: 256px;"> </canvas>
+ <div id="console"></div>
+-<!-- Shaders for testing standard derivatives -->
++<!-- Shaders for testing texture LOD functions -->
+ 
+ <!-- Shader omitting the required #extension pragma -->
+ <script id="missingPragmaFragmentShader" type="x-shader/x-fragment">
+ precision mediump float;
+ varying vec2 texCoord0v;
++uniform float lod;
++uniform sampler2D tex;
+ void main() {
+     vec4 color = texture2DLodEXT(tex, texCoord0v, lod);
+-    gl_FragColor = vec4(dx, dy, w, 1.0);
++    gl_FragColor = color;
+ }
+ </script>
+ 
+ <!-- Shader to test macro definition -->
+ <script id="macroFragmentShader" type="x-shader/x-fragment">
+ precision mediump float;
+ void main() {
+ #ifdef GL_EXT_shader_texture_lod
+@@ -91,47 +114,60 @@ void main() {
+ 
+ <script>
+ description("This test verifies the functionality of the EXT_shader_texture_lod extension, if it is available.");
+ 
+ debug("");
+ 
+ var wtu = WebGLTestUtils;
+ var canvas = document.getElementById("canvas");
+-
+-shouldBe("canvas.width", "256");
+-shouldBe("canvas.height", "256");
+-
+ var gl = wtu.create3DContext(canvas);
+ var ext = null;
+ 
+-if (!gl) {
+-    testFailed("WebGL context does not exist");
+-} else {
+-    testPassed("WebGL context exists");
++// Run all tests once.
++runAllTests();
+ 
+-    // Run tests with extension disabled
+-    runShaderTests(false);
++// Run all tests against with a new context to test for any cache issues.
++debug("");
++debug("Testing new context to catch cache errors");
++var canvas2 = document.getElementById("canvas2");
++gl = wtu.create3DContext(canvas2);
++ext = null;
++runAllTests();
+ 
+-    // Query the extension and store globally so shouldBe can access it
+-    ext = gl.getExtension("EXT_shader_texture_lod");
+-    if (!ext) {
+-        testPassed("No EXT_shader_texture_lod support -- this is legal");
++function runAllTests() {
++    if (!gl) {
++        testFailed("WebGL context does not exist");
++    } else {
++        testPassed("WebGL context exists");
+ 
+-        runSupportedTest(false);
+-    } else {
+-        testPassed("Successfully enabled EXT_shader_texture_lod extension");
++        // Run tests with extension disabled
++        runShaderTests(false);
+ 
+-        runSupportedTest(true);
++        // Query the extension and store globally so shouldBe can access it
++        ext = gl.getExtension("EXT_shader_texture_lod");
++        if (!ext) {
++            testPassed("No EXT_shader_texture_lod support -- this is legal");
+ 
+-        runShaderTests(true);
+-        runOutputTests();
+-        runUniqueObjectTest();
+-        runReferenceCycleTest();
++            runSupportedTest(false);
++        } else {
++            testPassed("Successfully enabled EXT_shader_texture_lod extension");
++
++            runSupportedTest(true);
++
++            runShaderTests(true);
++            runOutputTests();
++            runUniqueObjectTest();
++            runReferenceCycleTest();
++
++            // Run deferred link tests.
++            runDeferredLinkTests();
++        }
+     }
++
+ }
+ 
+ function runSupportedTest(extensionEnabled) {
+     var supported = gl.getSupportedExtensions();
+     if (supported.indexOf("EXT_shader_texture_lod") >= 0) {
+         if (extensionEnabled) {
+             testPassed("EXT_shader_texture_lod listed as supported and getExtension succeeded");
+         } else {
+@@ -188,93 +224,138 @@ function runShaderTests(extensionEnabled
+             testFailed("Shader built-ins compiled successfully when extension disabled");
+         } else {
+             testPassed("Shader built-ins failed to compile when extension disabled");
+         }
+     }
+ }
+ 
+ function runOutputTests() {
++    debug("");
+     debug("Testing various draws for valid built-in function behavior");
+-
+-    canvas.width = 256; canvas.height = 256;
+     gl.viewport(0, 0, canvas.width, canvas.height);
+ 
+     var program = wtu.setupProgram(gl, ["outputVertexShader", "outputFragmentShader"], ['vPosition', 'texCoord0'], [0, 1]);
+     var quadParameters = wtu.setupUnitQuad(gl, 0, 1);
+ 
+     var colors = [
+-	{name: 'red', color:[255, 0, 0, 255]},
+-	{name: 'green', color:[0, 255, 0, 255]},
+-	{name: 'blue', color:[0, 0, 255, 255]},
+-	{name: 'yellow', color:[255, 255, 0, 255]},
+-	{name: 'magenta', color:[255, 0, 255, 255]},
+-	{name: 'cyan', color:[0, 255, 255, 255]},
+-	{name: 'pink', color:[255, 128, 128, 255]},
+-	{name: 'gray', color:[128, 128, 128, 255]},
+-	{name: 'light green', color:[128, 255, 128, 255]},
++        {name: 'red', color:[255, 0, 0, 255]},
++        {name: 'green', color:[0, 255, 0, 255]},
++        {name: 'blue', color:[0, 0, 255, 255]},
++        {name: 'yellow', color:[255, 255, 0, 255]},
++        {name: 'magenta', color:[255, 0, 255, 255]},
++        {name: 'cyan', color:[0, 255, 255, 255]},
++        {name: 'pink', color:[255, 128, 128, 255]},
++        {name: 'gray', color:[128, 128, 128, 255]},
++        {name: 'light green', color:[128, 255, 128, 255]},
+     ];
+ 
+-    if (colors.length != 9) {
+-	testFailed("9 colors are needed (9 mips for 256x256 texture), only have " + colors.length);
+-    } else {
+-	testPassed("9 colors found (9 mips for 256x256 texture)");
+-    }
+-
+     var tex = gl.createTexture();
+     gl.bindTexture(gl.TEXTURE_2D, tex);
+-    gl.texParameteri(
+-	gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_LINEAR);
++    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_LINEAR);
+     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
+     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
+ 
+     for (var ii = 0; ii < colors.length; ++ii) {
+-	var color = colors[ii];
+-	var size = Math.pow(2, colors.length - ii - 1);
+-	wtu.fillTexture(gl, tex, size, size, color.color, ii);
++        var color = colors[ii];
++        var size = Math.pow(2, colors.length - ii - 1);
++        wtu.fillTexture(gl, tex, size, size, color.color, ii);
+     }
+ 
+     var loc = gl.getUniformLocation(program, "lod");
+ 
+     for (var ii = 0; ii < colors.length; ++ii) {
+-	gl.uniform1f(loc, ii);
+-	var color = colors[ii];
+-	wtu.drawQuad(gl);
+-	wtu.checkCanvas(
+-	    gl, color.color,
+-	    "256x256 texture drawn to 256x256 dest with lod = " + ii +
+-		" should be " + color.name);
++        gl.uniform1f(loc, ii);
++        var color = colors[ii];
++        wtu.drawUnitQuad(gl);
++        wtu.checkCanvas(
++            gl, color.color,
++            "256x256 texture drawn to 256x256 dest with lod = " + ii +
++            " should be " + color.name);
+     }
+ 
+-    glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors.");
++    wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+ }
+ 
+ function runUniqueObjectTest()
+ {
++    debug("");
+     debug("Testing that getExtension() returns the same object each time");
+     gl.getExtension("EXT_shader_texture_lod").myProperty = 2;
+     gc();
+     shouldBe('gl.getExtension("EXT_shader_texture_lod").myProperty', '2');
+ }
+ 
+ function runReferenceCycleTest()
+ {
+     // create some reference cycles. The goal is to see if they cause leaks. The point is that
+     // some browser test runners have instrumentation to detect leaked refcounted objects.
+-
++    debug("");
+     debug("Testing reference cycles between context and extension objects");
+     var ext = gl.getExtension("EXT_shader_texture_lod");
+ 
+     // create cycle between extension and context, since the context has to hold a reference to the extension
+     ext.context = gl;
+ 
+     // create a self-cycle on the extension object
+     ext.ext = ext;
+ }
+ 
++function runDeferredLinkTests() {
++    debug("");
++    debug("Testing deferred shader compilation tests.");
++
++    // Test for compilation failures that are caused by missing extensions
++    // do not succeed if extensions are enabled during linking. This would
++    // only happen for deferred shader compilations.
++
++    // First test if link succeeds with extension enabled.
++    var glEnabled = wtu.create3DContext();
++    var extEnabled = glEnabled.getExtension("EXT_shader_texture_lod");
++    if (!extEnabled) {
++        testFailed("Deferred link test expects the extension to be supported");
++    }
++
++    var vertexShader = wtu.loadShaderFromScript(glEnabled, "goodVertexShader");
++    var fragmentShader = wtu.loadShaderFromScript(glEnabled, "macroFragmentShader");
++
++    if (!vertexShader || !fragmentShader) {
++        testFailed("Could not create good shaders.");
++        return;
++    }
++
++    var program = wtu.setupProgram(glEnabled, [vertexShader, fragmentShader]);
++
++    if (!program) {
++        testFailed("Compilation with extension enabled failed.");
++        return;
++    }
++
++    // Create new context to test link failure without extension enabled.
++    var glDeferred = wtu.create3DContext();
++
++    var vertexShader = wtu.loadShaderFromScript(glDeferred, "goodVertexShader", glDeferred.VERTEX_SHADER, undefined, undefined, true);
++    var fragmentShader = wtu.loadShaderFromScript(glDeferred, "macroFragmentShader", glDeferred.FRAGMENT_SHADER, undefined, undefined, true);
++
++    if (vertexShader == null || fragmentShader == null) {
++        testFailed("Could not create shaders.");
++        return;
++    }
++
++    // Shader compilations should have failed due to extensions not enabled.
++    glDeferred.getExtension("EXT_shader_texture_lod");
++    var program = wtu.setupProgram(glDeferred, [vertexShader, fragmentShader]);
++    if (program) {
++        testFailed("Compilation with extension disabled then linking with extension enabled should have failed.");
++        return;
++    }
++
++    testPassed("Compilation with extension disabled then linking with extension enabled.");
++}
++
+ debug("");
+ successfullyParsed = true;
+ </script>
+ <script>finishTest();</script>
+ 
+ </body>
+ </html>
+diff --git a/dom/canvas/test/webgl-conformance/conformance/resources/webgl-test-utils.js b/dom/canvas/test/webgl-conformance/conformance/resources/webgl-test-utils.js
+--- a/dom/canvas/test/webgl-conformance/conformance/resources/webgl-test-utils.js
++++ b/dom/canvas/test/webgl-conformance/conformance/resources/webgl-test-utils.js
+@@ -994,19 +994,28 @@ var readFileList = function(url) {
+ };
+ 
+ /**
+  * Loads a shader.
+  * @param {!WebGLContext} gl The WebGLContext to use.
+  * @param {string} shaderSource The shader source.
+  * @param {number} shaderType The type of shader.
+  * @param {function(string): void) opt_errorCallback callback for errors.
++ * @param {boolean} opt_logShaders Whether to log shader source.
++ * @param {string} opt_shaderLabel Label that identifies the shader source in
++ *     the log.
++ * @param {string} opt_url URL from where the shader source was loaded from.
++ *     If opt_logShaders is set, then a link to the source file will also be
++ *     added.
++ * @param {boolean} Skip compilation status check. Default = false.
+  * @return {!WebGLShader} The created shader.
+  */
+-var loadShader = function(gl, shaderSource, shaderType, opt_errorCallback) {
++var loadShader = function(
++    gl, shaderSource, shaderType, opt_errorCallback, opt_logShaders,
++    opt_shaderLabel, opt_url, opt_skipCompileStatus) {
+   var errFn = opt_errorCallback || error;
+   // Create the shader object
+   var shader = gl.createShader(shaderType);
+   if (shader == null) {
+     errFn("*** Error: unable to create shader '"+shaderSource+"'");
+     return null;
+   }
+ 
+@@ -1016,24 +1025,35 @@ var loadShader = function(gl, shaderSour
+   if (err != gl.NO_ERROR) {
+     errFn("*** Error loading shader '" + shader + "':" + glEnumToString(gl, err));
+     return null;
+   }
+ 
+   // Compile the shader
+   gl.compileShader(shader);
+ 
++  if (opt_logShaders) {
++    var label = shaderType == gl.VERTEX_SHADER ? 'vertex shader' : 'fragment_shader';
++    if (opt_shaderLabel) {
++      label = opt_shaderLabel + ' ' + label;
++    }
++    addShaderSources(
++        gl, document.getElementById('console'), label, shader, shaderSource, opt_url);
++  }
++
+   // Check the compile status
+-  var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
+-  if (!compiled) {
+-    // Something went wrong during compilation; get the error
+-    lastError = gl.getShaderInfoLog(shader);
+-    errFn("*** Error compiling shader '" + shader + "':" + lastError);
+-    gl.deleteShader(shader);
+-    return null;
++  if (!opt_skipCompileStatus) {
++    var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
++    if (!compiled) {
++      // Something went wrong during compilation; get the error
++      lastError = gl.getShaderInfoLog(shader);
++      errFn("*** Error compiling " + glEnumToString(gl, shaderType) + " '" + shader + "':" + lastError);
++      gl.deleteShader(shader);
++      return null;
++    }
+   }
+ 
+   return shader;
+ }
+ 
+ /**
+  * Loads a shader from a URL.
+  * @param {!WebGLContext} gl The WebGLContext to use.
+@@ -1060,42 +1080,42 @@ var getScript = function(scriptId) {
+ 
+ /**
+  * Loads a shader from a script tag.
+  * @param {!WebGLContext} gl The WebGLContext to use.
+  * @param {string} scriptId The id of the script tag.
+  * @param {number} opt_shaderType The type of shader. If not passed in it will
+  *     be derived from the type of the script tag.
+  * @param {function(string): void) opt_errorCallback callback for errors.
++ * @param {boolean} opt_logShaders Whether to log shader source.
++ * @param {boolean} Skip compilation status check. Default = false.
+  * @return {!WebGLShader} The created shader.
+  */
+ var loadShaderFromScript = function(
+-    gl, scriptId, opt_shaderType, opt_errorCallback) {
++    gl, scriptId, opt_shaderType, opt_errorCallback, opt_logShaders, opt_skipCompileStatus) {
+   var shaderSource = "";
+-  var shaderType;
+   var shaderScript = document.getElementById(scriptId);
+   if (!shaderScript) {
+     throw("*** Error: unknown script element " + scriptId);
+   }
+   shaderSource = shaderScript.text;
+ 
+   if (!opt_shaderType) {
+     if (shaderScript.type == "x-shader/x-vertex") {
+-      shaderType = gl.VERTEX_SHADER;
++      opt_shaderType = gl.VERTEX_SHADER;
+     } else if (shaderScript.type == "x-shader/x-fragment") {
+-      shaderType = gl.FRAGMENT_SHADER;
+-    } else if (shaderType != gl.VERTEX_SHADER && shaderType != gl.FRAGMENT_SHADER) {
++      opt_shaderType = gl.FRAGMENT_SHADER;
++    } else {
+       throw("*** Error: unknown shader type");
+       return null;
+     }
+   }
+ 
+-  return loadShader(
+-      gl, shaderSource, opt_shaderType ? opt_shaderType : shaderType,
+-      opt_errorCallback);
++  return loadShader(gl, shaderSource, opt_shaderType, opt_errorCallback,
++      opt_logShaders, undefined, undefined, opt_skipCompileStatus);
+ };
+ 
+ var loadStandardProgram = function(gl) {
+   var program = gl.createProgram();
+   gl.attachShader(program, loadStandardVertexShader(gl));
+   gl.attachShader(program, loadStandardFragmentShader(gl));
+   linkProgram(gl, program);
+   return program;