Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
michael@0 | 1 | <!DOCTYPE html> |
michael@0 | 2 | <html> |
michael@0 | 3 | <head> |
michael@0 | 4 | <link rel="stylesheet" href="../../resources/js-test-style.css"/> |
michael@0 | 5 | <script src="../../resources/js-test-pre.js"></script> |
michael@0 | 6 | <script src="../resources/webgl-test.js"></script> |
michael@0 | 7 | <script src="../resources/webgl-test-utils.js"></script> |
michael@0 | 8 | <script> |
michael@0 | 9 | var wtu; |
michael@0 | 10 | var canvas; |
michael@0 | 11 | var gl; |
michael@0 | 12 | var shouldGenerateGLError; |
michael@0 | 13 | var extensionNames = [ |
michael@0 | 14 | "WEBKIT_WEBGL_lose_context", |
michael@0 | 15 | "MOZ_WEBGL_lose_context", |
michael@0 | 16 | ]; |
michael@0 | 17 | var extensionName; |
michael@0 | 18 | var extension; |
michael@0 | 19 | |
michael@0 | 20 | var buffer; |
michael@0 | 21 | var framebuffer; |
michael@0 | 22 | var program; |
michael@0 | 23 | var renderbuffer; |
michael@0 | 24 | var shader; |
michael@0 | 25 | var texture; |
michael@0 | 26 | var uniformLocation; |
michael@0 | 27 | var arrayBuffer; |
michael@0 | 28 | var arrayBufferView |
michael@0 | 29 | var image; |
michael@0 | 30 | var video; |
michael@0 | 31 | var canvas2d; |
michael@0 | 32 | var ctx2d; |
michael@0 | 33 | var imageData; |
michael@0 | 34 | var float32array; |
michael@0 | 35 | var int32array; |
michael@0 | 36 | |
michael@0 | 37 | function init() |
michael@0 | 38 | { |
michael@0 | 39 | wtu = WebGLTestUtils; |
michael@0 | 40 | canvas = document.getElementById("canvas"); |
michael@0 | 41 | gl = wtu.create3DContext(canvas); |
michael@0 | 42 | shouldGenerateGLError = wtu.shouldGenerateGLError; |
michael@0 | 43 | |
michael@0 | 44 | description("Tests behavior under a lost context"); |
michael@0 | 45 | |
michael@0 | 46 | if (window.initNonKhronosFramework) { |
michael@0 | 47 | window.initNonKhronosFramework(true); |
michael@0 | 48 | } |
michael@0 | 49 | |
michael@0 | 50 | // call testValidContext() before checking for the extension, because this is where we check |
michael@0 | 51 | // for the isContextLost() method, which we want to do regardless of the extension's presence. |
michael@0 | 52 | testValidContext(); |
michael@0 | 53 | |
michael@0 | 54 | for (var ii = 0; ii < extensionNames.length; ++ii) { |
michael@0 | 55 | extension = gl.getExtension(extensionNames[ii]); |
michael@0 | 56 | if (extension) { |
michael@0 | 57 | extensionName = extensionNames[ii]; |
michael@0 | 58 | break; |
michael@0 | 59 | } |
michael@0 | 60 | } |
michael@0 | 61 | if (!extension) { |
michael@0 | 62 | debug("Could not find lose_context extension under the following names: " + extensionNames.join(" ")); |
michael@0 | 63 | finishTest(); |
michael@0 | 64 | return false; |
michael@0 | 65 | } |
michael@0 | 66 | |
michael@0 | 67 | canvas.addEventListener("webglcontextlost", testLostContext, false); |
michael@0 | 68 | |
michael@0 | 69 | loseContext(); |
michael@0 | 70 | } |
michael@0 | 71 | |
michael@0 | 72 | function loseContext() |
michael@0 | 73 | { |
michael@0 | 74 | debug(""); |
michael@0 | 75 | debug("Lose context"); |
michael@0 | 76 | |
michael@0 | 77 | // Note: this will cause the context to be lost, but the |
michael@0 | 78 | // webglcontextlost event listener to be queued. |
michael@0 | 79 | extension.loseContext(); |
michael@0 | 80 | debug(""); |
michael@0 | 81 | } |
michael@0 | 82 | |
michael@0 | 83 | function testValidContext() |
michael@0 | 84 | { |
michael@0 | 85 | debug("Test valid context"); |
michael@0 | 86 | |
michael@0 | 87 | shouldBeFalse("gl.isContextLost()"); |
michael@0 | 88 | |
michael@0 | 89 | arrayBuffer = new ArrayBuffer(4); |
michael@0 | 90 | arrayBufferView = new Int8Array(arrayBuffer); |
michael@0 | 91 | |
michael@0 | 92 | // Generate resources for testing. |
michael@0 | 93 | buffer = gl.createBuffer(); |
michael@0 | 94 | gl.bindBuffer(gl.ARRAY_BUFFER, buffer); |
michael@0 | 95 | framebuffer = gl.createFramebuffer(); |
michael@0 | 96 | gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); |
michael@0 | 97 | program = wtu.setupSimpleTextureProgram(gl); |
michael@0 | 98 | renderbuffer = gl.createRenderbuffer(); |
michael@0 | 99 | gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer); |
michael@0 | 100 | shader = gl.createShader(gl.VERTEX_SHADER); |
michael@0 | 101 | texture = gl.createTexture(); |
michael@0 | 102 | gl.bindTexture(gl.TEXTURE_2D, texture); |
michael@0 | 103 | shouldBe("gl.getError()", "gl.NO_ERROR"); |
michael@0 | 104 | |
michael@0 | 105 | // Test is queries that will later be false |
michael@0 | 106 | shouldGenerateGLError(gl, gl.NO_ERROR, "gl.enable(gl.BLEND)"); |
michael@0 | 107 | shouldBeTrue("gl.isBuffer(buffer)"); |
michael@0 | 108 | shouldBeTrue("gl.isEnabled(gl.BLEND)"); |
michael@0 | 109 | shouldBeTrue("gl.isFramebuffer(framebuffer)"); |
michael@0 | 110 | shouldBeTrue("gl.isProgram(program)"); |
michael@0 | 111 | shouldBeTrue("gl.isRenderbuffer(renderbuffer)"); |
michael@0 | 112 | shouldBeTrue("gl.isShader(shader)"); |
michael@0 | 113 | shouldBeTrue("gl.isTexture(texture)"); |
michael@0 | 114 | } |
michael@0 | 115 | |
michael@0 | 116 | function testLostContext() |
michael@0 | 117 | { |
michael@0 | 118 | debug("Test lost context"); |
michael@0 | 119 | |
michael@0 | 120 | // Functions with special return values. |
michael@0 | 121 | shouldBeTrue("gl.isContextLost()"); |
michael@0 | 122 | shouldBe("gl.getError()", "gl.CONTEXT_LOST_WEBGL"); |
michael@0 | 123 | shouldBe("gl.getError()", "gl.NO_ERROR"); |
michael@0 | 124 | shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_UNSUPPORTED"); |
michael@0 | 125 | shouldBe("gl.getAttribLocation(program, 'u_modelViewProjMatrix')", "-1"); |
michael@0 | 126 | shouldBe("gl.getVertexAttribOffset(0, gl.VERTEX_ATTRIB_ARRAY_POINTER)", "0"); |
michael@0 | 127 | |
michael@0 | 128 | // Test the extension itself. |
michael@0 | 129 | shouldGenerateGLError(gl, gl.INVALID_OPERATION, "extension.loseContext()"); |
michael@0 | 130 | |
michael@0 | 131 | image = document.createElement("img"); |
michael@0 | 132 | video = document.createElement("video"); |
michael@0 | 133 | canvas2d = document.createElement("canvas"); |
michael@0 | 134 | ctx2d = canvas2d.getContext("2d"); |
michael@0 | 135 | imageData = ctx2d.createImageData(1, 1); |
michael@0 | 136 | float32array = new Float32Array(1); |
michael@0 | 137 | int32array = new Int32Array(1); |
michael@0 | 138 | |
michael@0 | 139 | // Functions returning void should return immediately. |
michael@0 | 140 | // This is untestable, but we can at least be sure they cause no errors |
michael@0 | 141 | // and the codepaths are exercised. |
michael@0 | 142 | var voidTests = [ |
michael@0 | 143 | "gl.activeTexture(gl.TEXTURE0)", |
michael@0 | 144 | "gl.attachShader(program, shader)", |
michael@0 | 145 | "gl.bindBuffer(gl.ARRAY_BUFFER, buffer)", |
michael@0 | 146 | "gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer)", |
michael@0 | 147 | "gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer)", |
michael@0 | 148 | "gl.bindTexture(gl.TEXTURE_2D, texture)", |
michael@0 | 149 | "gl.blendColor(1.0, 1.0, 1.0, 1.0)", |
michael@0 | 150 | "gl.blendEquation(gl.FUNC_ADD)", |
michael@0 | 151 | "gl.blendEquationSeparate(gl.FUNC_ADD, gl.FUNC_ADD)", |
michael@0 | 152 | "gl.blendFunc(gl.ONE, gl.ONE)", |
michael@0 | 153 | "gl.blendFuncSeparate(gl.ONE, gl.ONE, gl.ONE, gl.ONE)", |
michael@0 | 154 | "gl.bufferData(gl.ARRAY_BUFFER, 0, gl.STATIC_DRAW)", |
michael@0 | 155 | "gl.bufferData(gl.ARRAY_BUFFER, arrayBufferView, gl.STATIC_DRAW)", |
michael@0 | 156 | "gl.bufferData(gl.ARRAY_BUFFER, arrayBuffer, gl.STATIC_DRAW)", |
michael@0 | 157 | "gl.bufferSubData(gl.ARRAY_BUFFRE, 0, arrayBufferView)", |
michael@0 | 158 | "gl.bufferSubData(gl.ARRAY_BUFFRE, 0, arrayBuffer)", |
michael@0 | 159 | "gl.clear(gl.COLOR_BUFFER_BIT)", |
michael@0 | 160 | "gl.clearColor(1, 1, 1, 1)", |
michael@0 | 161 | "gl.clearDepth(1)", |
michael@0 | 162 | "gl.clearStencil(0)", |
michael@0 | 163 | "gl.colorMask(1, 1, 1, 1)", |
michael@0 | 164 | "gl.compileShader(shader)", |
michael@0 | 165 | "gl.copyTexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 0, 0, 0, 0, 0)", |
michael@0 | 166 | "gl.copyTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 0, 0, 0, 0)", |
michael@0 | 167 | "gl.cullFace(gl.FRONT)", |
michael@0 | 168 | "gl.deleteBuffer(buffer)", |
michael@0 | 169 | "gl.deleteFramebuffer(framebuffer)", |
michael@0 | 170 | "gl.deleteProgram(program)", |
michael@0 | 171 | "gl.deleteRenderbuffer(renderbuffer)", |
michael@0 | 172 | "gl.deleteShader(shader)", |
michael@0 | 173 | "gl.deleteTexture(texture)", |
michael@0 | 174 | "gl.depthFunc(gl.NEVER)", |
michael@0 | 175 | "gl.depthMask(0)", |
michael@0 | 176 | "gl.depthRange(0, 1)", |
michael@0 | 177 | "gl.detachShader(program, shader)", |
michael@0 | 178 | "gl.disable(gl.BLEND)", |
michael@0 | 179 | "gl.disableVertexAttribArray(0)", |
michael@0 | 180 | "gl.drawArrays(gl.POINTS, 0, 0)", |
michael@0 | 181 | "gl.drawElements(gl.POINTS, 0, gl.UNSIGNED_SHORT, 0)", |
michael@0 | 182 | "gl.enable(gl.BLEND)", |
michael@0 | 183 | "gl.enableVertexAttribArray(0)", |
michael@0 | 184 | "gl.finish()", |
michael@0 | 185 | "gl.flush()", |
michael@0 | 186 | "gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, renderbuffer)", |
michael@0 | 187 | "gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0)", |
michael@0 | 188 | "gl.frontFace(gl.CW)", |
michael@0 | 189 | "gl.generateMipmap(gl.TEXTURE_2D)", |
michael@0 | 190 | "gl.hint(gl.GENERATE_MIPMAP_HINT, gl.FASTEST)", |
michael@0 | 191 | "gl.lineWidth(0)", |
michael@0 | 192 | "gl.linkProgram(program)", |
michael@0 | 193 | "gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 0)", |
michael@0 | 194 | "gl.polygonOffset(0, 0)", |
michael@0 | 195 | "gl.readPixels(0, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, arrayBufferView)", |
michael@0 | 196 | "gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 0, 0)", |
michael@0 | 197 | "gl.sampleCoverage(0, 0)", |
michael@0 | 198 | "gl.scissor(0, 0, 0, 0)", |
michael@0 | 199 | "gl.shaderSource(shader, '')", |
michael@0 | 200 | "gl.stencilFunc(gl.NEVER, 0, 0)", |
michael@0 | 201 | "gl.stencilFuncSeparate(gl.FRONT, gl.NEVER, 0, 0)", |
michael@0 | 202 | "gl.stencilMask(0)", |
michael@0 | 203 | "gl.stencilMaskSeparate(gl.FRONT, 0)", |
michael@0 | 204 | "gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP)", |
michael@0 | 205 | "gl.stencilOpSeparate(gl.FRONT, gl.KEEP, gl.KEEP, gl.KEEP)", |
michael@0 | 206 | "gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, arrayBufferView)", |
michael@0 | 207 | "gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, imageData)", |
michael@0 | 208 | "gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image)", |
michael@0 | 209 | "gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas)", |
michael@0 | 210 | "gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, video)", |
michael@0 | 211 | "gl.texParameterf(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)", |
michael@0 | 212 | "gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)", |
michael@0 | 213 | "gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, arrayBufferView)", |
michael@0 | 214 | "gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, imageData)", |
michael@0 | 215 | "gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, image)", |
michael@0 | 216 | "gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, canvas)", |
michael@0 | 217 | "gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, video)", |
michael@0 | 218 | "gl.uniform1f(uniformLocation, 0)", |
michael@0 | 219 | "gl.uniform1fv(uniformLocation, float32array)", |
michael@0 | 220 | "gl.uniform1fv(uniformLocation, [0])", |
michael@0 | 221 | "gl.uniform1i(uniformLocation, 0)", |
michael@0 | 222 | "gl.uniform1iv(uniformLocation, int32array)", |
michael@0 | 223 | "gl.uniform1iv(uniformLocation, [0])", |
michael@0 | 224 | "gl.uniform2f(uniformLocation, 0, 0)", |
michael@0 | 225 | "gl.uniform2fv(uniformLocation, float32array)", |
michael@0 | 226 | "gl.uniform2fv(uniformLocation, [0, 0])", |
michael@0 | 227 | "gl.uniform2i(uniformLocation, 0, 0)", |
michael@0 | 228 | "gl.uniform2iv(uniformLocation, int32array)", |
michael@0 | 229 | "gl.uniform2iv(uniformLocation, [0, 0])", |
michael@0 | 230 | "gl.uniform3f(uniformLocation, 0, 0, 0)", |
michael@0 | 231 | "gl.uniform3fv(uniformLocation, float32array)", |
michael@0 | 232 | "gl.uniform3fv(uniformLocation, [0, 0, 0])", |
michael@0 | 233 | "gl.uniform3i(uniformLocation, 0, 0, 0)", |
michael@0 | 234 | "gl.uniform3iv(uniformLocation, int32array)", |
michael@0 | 235 | "gl.uniform3iv(uniformLocation, [0, 0, 0])", |
michael@0 | 236 | "gl.uniform4f(uniformLocation, 0, 0, 0, 0)", |
michael@0 | 237 | "gl.uniform4fv(uniformLocation, float32array)", |
michael@0 | 238 | "gl.uniform4fv(uniformLocation, [0, 0, 0, 0])", |
michael@0 | 239 | "gl.uniform4i(uniformLocation, 0, 0, 0, 0)", |
michael@0 | 240 | "gl.uniform4iv(uniformLocation, int32array)", |
michael@0 | 241 | "gl.uniform4iv(uniformLocation, [0, 0, 0, 0])", |
michael@0 | 242 | "gl.uniformMatrix2fv(uniformLocation, false, float32array)", |
michael@0 | 243 | "gl.uniformMatrix2fv(uniformLocation, false, [0, 0, 0, 0])", |
michael@0 | 244 | "gl.uniformMatrix3fv(uniformLocation, false, float32array)", |
michael@0 | 245 | "gl.uniformMatrix3fv(uniformLocation, false, [0, 0, 0, 0, 0, 0, 0, 0, 0])", |
michael@0 | 246 | "gl.uniformMatrix4fv(uniformLocation, false, float32array)", |
michael@0 | 247 | "gl.uniformMatrix4fv(uniformLocation, false, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])", |
michael@0 | 248 | "gl.useProgram(program)", |
michael@0 | 249 | "gl.validateProgram(program)", |
michael@0 | 250 | "gl.vertexAttrib1f(0, 0)", |
michael@0 | 251 | "gl.vertexAttrib1fv(0, float32array)", |
michael@0 | 252 | "gl.vertexAttrib1fv(0, [0])", |
michael@0 | 253 | "gl.vertexAttrib2f(0, 0, 0)", |
michael@0 | 254 | "gl.vertexAttrib2fv(0, float32array)", |
michael@0 | 255 | "gl.vertexAttrib2fv(0, [0, 0])", |
michael@0 | 256 | "gl.vertexAttrib3f(0, 0, 0, 0)", |
michael@0 | 257 | "gl.vertexAttrib3fv(0, float32array)", |
michael@0 | 258 | "gl.vertexAttrib3fv(0, [0, 0, 0])", |
michael@0 | 259 | "gl.vertexAttrib4f(0, 0, 0, 0, 0)", |
michael@0 | 260 | "gl.vertexAttrib4fv(0, float32array)", |
michael@0 | 261 | "gl.vertexAttrib4fv(0, [0, 0, 0, 0])", |
michael@0 | 262 | "gl.vertexAttribPointer(0, 0, gl.FLOAT, false, 0, 0)", |
michael@0 | 263 | "gl.viewport(0, 0, 0, 0)", |
michael@0 | 264 | ]; |
michael@0 | 265 | for (var i = 0; i < voidTests.length; ++i) { |
michael@0 | 266 | shouldGenerateGLError(gl, gl.NO_ERROR, voidTests[i]); |
michael@0 | 267 | } |
michael@0 | 268 | |
michael@0 | 269 | // Functions return nullable values should all return null. |
michael@0 | 270 | var nullTests = [ |
michael@0 | 271 | "gl.createBuffer()", |
michael@0 | 272 | "gl.createFramebuffer()", |
michael@0 | 273 | "gl.createProgram()", |
michael@0 | 274 | "gl.createRenderbuffer()", |
michael@0 | 275 | "gl.createShader(gl.GL_VERTEX_SHADER)", |
michael@0 | 276 | "gl.createTexture()", |
michael@0 | 277 | "gl.getActiveAttrib(program, 0)", |
michael@0 | 278 | "gl.getActiveUniform(program, 0)", |
michael@0 | 279 | "gl.getAttachedShaders(program)", |
michael@0 | 280 | "gl.getBufferParameter(gl.ARRAY_BUFFER, gl.BUFFER_SIZE)", |
michael@0 | 281 | // Disabled pending test suite issue: |
michael@0 | 282 | // "gl.getContextAttributes()", |
michael@0 | 283 | "gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", |
michael@0 | 284 | "gl.getParameter(gl.CURRENT_PROGRAM)", |
michael@0 | 285 | "gl.getProgramInfoLog(program)", |
michael@0 | 286 | "gl.getProgramParameter(program, gl.LINK_STATUS)", |
michael@0 | 287 | "gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_WIDTH)", |
michael@0 | 288 | "gl.getShaderInfoLog(shader)", |
michael@0 | 289 | "gl.getShaderParameter(shader, gl.SHADER_TYPE)", |
michael@0 | 290 | "gl.getShaderSource(shader)", |
michael@0 | 291 | "gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S)", |
michael@0 | 292 | "gl.getUniform(program, uniformLocation)", |
michael@0 | 293 | "gl.getUniformLocation(program, 'vPosition')", |
michael@0 | 294 | "gl.getVertexAttrib(0, gl.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING)", |
michael@0 | 295 | "gl.getSupportedExtensions()", |
michael@0 | 296 | "gl.getExtension('" + extensionName + "')", |
michael@0 | 297 | ]; |
michael@0 | 298 | for (var i = 0; i < nullTests.length; ++i) { |
michael@0 | 299 | shouldBeNull(nullTests[i]); |
michael@0 | 300 | } |
michael@0 | 301 | |
michael@0 | 302 | // "Is" queries should all return false. |
michael@0 | 303 | shouldBeFalse("gl.isBuffer(buffer)"); |
michael@0 | 304 | shouldBeFalse("gl.isEnabled(gl.BLEND)"); |
michael@0 | 305 | shouldBeFalse("gl.isFramebuffer(framebuffer)"); |
michael@0 | 306 | shouldBeFalse("gl.isProgram(program)"); |
michael@0 | 307 | shouldBeFalse("gl.isRenderbuffer(renderbuffer)"); |
michael@0 | 308 | shouldBeFalse("gl.isShader(shader)"); |
michael@0 | 309 | shouldBeFalse("gl.isTexture(texture)"); |
michael@0 | 310 | |
michael@0 | 311 | shouldBe("gl.getError()", "gl.NO_ERROR"); |
michael@0 | 312 | |
michael@0 | 313 | debug(""); |
michael@0 | 314 | |
michael@0 | 315 | finishTest(); |
michael@0 | 316 | } |
michael@0 | 317 | |
michael@0 | 318 | </script> |
michael@0 | 319 | </head> |
michael@0 | 320 | <body onload="init()"> |
michael@0 | 321 | <div id="description"></div> |
michael@0 | 322 | <div id="console"></div> |
michael@0 | 323 | <canvas id="canvas"> |
michael@0 | 324 | </body> |
michael@0 | 325 | </html> |