|
1 <!-- |
|
2 |
|
3 /* |
|
4 ** Copyright (c) 2012 The Khronos Group Inc. |
|
5 ** |
|
6 ** Permission is hereby granted, free of charge, to any person obtaining a |
|
7 ** copy of this software and/or associated documentation files (the |
|
8 ** "Materials"), to deal in the Materials without restriction, including |
|
9 ** without limitation the rights to use, copy, modify, merge, publish, |
|
10 ** distribute, sublicense, and/or sell copies of the Materials, and to |
|
11 ** permit persons to whom the Materials are furnished to do so, subject to |
|
12 ** the following conditions: |
|
13 ** |
|
14 ** The above copyright notice and this permission notice shall be included |
|
15 ** in all copies or substantial portions of the Materials. |
|
16 ** |
|
17 ** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
|
18 ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
|
19 ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
|
20 ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
|
21 ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
|
22 ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
|
23 ** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. |
|
24 */ |
|
25 |
|
26 --> |
|
27 |
|
28 <!DOCTYPE html> |
|
29 <html> |
|
30 <head> |
|
31 <meta charset="utf-8"> |
|
32 <link rel="stylesheet" href="../../resources/js-test-style.css"/> |
|
33 <script src="../../resources/js-test-pre.js"></script> |
|
34 <script src="../resources/webgl-test.js"></script> |
|
35 <script src="../resources/webgl-test-utils.js"></script> |
|
36 <title>WebGL WEBGL_compressed_texture_etc1 Conformance Tests</title> |
|
37 <style> |
|
38 img { |
|
39 border: 1px solid black; |
|
40 margin-right: 1em; |
|
41 } |
|
42 .testimages { |
|
43 } |
|
44 |
|
45 .testimages br { |
|
46 clear: both; |
|
47 } |
|
48 |
|
49 .testimages > div { |
|
50 float: left; |
|
51 margin: 1em; |
|
52 } |
|
53 </style> |
|
54 </head> |
|
55 <body> |
|
56 <div id="description"></div> |
|
57 <canvas id="canvas" width="8" height="8" style="width: 8px; height: 8px;"></canvas> |
|
58 <div id="console"></div> |
|
59 <script> |
|
60 "use strict"; |
|
61 description("This test verifies the functionality of the WEBGL_compressed_texture_etc1 extension, if it is available."); |
|
62 |
|
63 debug(""); |
|
64 |
|
65 var img_4x4_rgb_etc1 = new Uint8Array([ |
|
66 0x00, 0xc0, 0x00, 0xff, 0x07, 0x45, 0x07, 0x45 |
|
67 ]); |
|
68 var img_8x8_rgb_etc1 = new Uint8Array([ |
|
69 0x00, 0xff, 0x55, 0xfc, 0xff, 0xff, 0x07, 0x45, |
|
70 0x11, 0x11, 0xff, 0xfc, 0xf8, 0xba, 0x07, 0x45, |
|
71 0xee, 0x00, 0xee, 0xfc, 0x07, 0x45, 0x07, 0x45, |
|
72 0x00, 0x90, 0xf8, 0x92, 0x07, 0x45, 0xff, 0xff, |
|
73 ]); |
|
74 |
|
75 var wtu = WebGLTestUtils; |
|
76 var canvas = document.getElementById("canvas"); |
|
77 var gl = wtu.create3DContext(canvas, {antialias: false}); |
|
78 var program = wtu.setupTexturedQuad(gl); |
|
79 var ext = null; |
|
80 var vao = null; |
|
81 var validFormats = { |
|
82 COMPRESSED_RGB_ETC1_WEBGL : 0x8D64 |
|
83 }; |
|
84 var name; |
|
85 var supportedFormats; |
|
86 |
|
87 if (!gl) { |
|
88 testFailed("WebGL context does not exist"); |
|
89 } else { |
|
90 testPassed("WebGL context exists"); |
|
91 |
|
92 // Run tests with extension disabled |
|
93 runTestDisabled(); |
|
94 |
|
95 // Query the extension and store globally so shouldBe can access it |
|
96 ext = wtu.getExtensionWithKnownPrefixes(gl, "WEBGL_compressed_texture_etc1"); |
|
97 if (!ext) { |
|
98 testPassed("No WEBGL_compressed_texture_etc1 support -- this is legal"); |
|
99 runSupportedTest(false); |
|
100 } else { |
|
101 testPassed("Successfully enabled WEBGL_compressed_texture_etc1 extension"); |
|
102 |
|
103 runSupportedTest(true); |
|
104 runTestExtension(); |
|
105 } |
|
106 } |
|
107 |
|
108 function runSupportedTest(extensionEnabled) { |
|
109 var name = wtu.getSupportedExtensionWithKnownPrefixes(gl, "WEBGL_compressed_texture_etc1"); |
|
110 if (name !== undefined) { |
|
111 if (extensionEnabled) { |
|
112 testPassed("WEBGL_compressed_texture_etc1 listed as supported and getExtension succeeded"); |
|
113 } else { |
|
114 testFailed("WEBGL_compressed_texture_etc1 listed as supported but getExtension failed"); |
|
115 } |
|
116 } else { |
|
117 if (extensionEnabled) { |
|
118 testFailed("WEBGL_compressed_texture_etc1 not listed as supported but getExtension succeeded"); |
|
119 } else { |
|
120 testPassed("WEBGL_compressed_texture_etc1 not listed as supported and getExtension failed -- this is legal"); |
|
121 } |
|
122 } |
|
123 } |
|
124 |
|
125 |
|
126 function runTestDisabled() { |
|
127 debug("Testing binding enum with extension disabled"); |
|
128 |
|
129 shouldBe('gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS)', '[]'); |
|
130 } |
|
131 |
|
132 function formatExists(format, supportedFormats) { |
|
133 for (var ii = 0; ii < supportedFormats.length; ++ii) { |
|
134 if (format == supportedFormats[ii]) { |
|
135 testPassed("supported format " + formatToString(format) + " is exists"); |
|
136 return; |
|
137 } |
|
138 } |
|
139 testFailed("supported format " + formatToString(format) + " does not exist"); |
|
140 } |
|
141 |
|
142 function formatToString(format) { |
|
143 for (var p in ext) { |
|
144 if (ext[p] == format) { |
|
145 return p; |
|
146 } |
|
147 } |
|
148 return "0x" + format.toString(16); |
|
149 } |
|
150 |
|
151 function runTestExtension() { |
|
152 debug("Testing WEBGL_compressed_texture_etc1"); |
|
153 |
|
154 // check that all format enums exist. |
|
155 for (name in validFormats) { |
|
156 var expected = "0x" + validFormats[name].toString(16); |
|
157 var actual = "ext['" + name + "']"; |
|
158 shouldBe(actual, expected); |
|
159 } |
|
160 |
|
161 supportedFormats = gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS); |
|
162 // There should be exactly 4 formats |
|
163 shouldBe("supportedFormats.length", "1"); |
|
164 |
|
165 // check that all 4 formats exist |
|
166 for (var name in validFormats.length) { |
|
167 formatExists(validFormats[name], supportedFormats); |
|
168 } |
|
169 |
|
170 // Test each format |
|
171 testETC1_RGB(); |
|
172 } |
|
173 |
|
174 function testETC1_RGB() { |
|
175 var tests = [ |
|
176 { width: 4, |
|
177 height: 4, |
|
178 channels: 3, |
|
179 data: img_4x4_rgb_etc1, |
|
180 format: ext.COMPRESSED_RGB_ETC1_WEBGL |
|
181 }, |
|
182 { width: 8, |
|
183 height: 8, |
|
184 channels: 3, |
|
185 data: img_8x8_rgb_etc1, |
|
186 format: ext.COMPRESSED_RGB_ETC1_WEBGL |
|
187 } |
|
188 ]; |
|
189 testETCTextures(tests); |
|
190 } |
|
191 |
|
192 function testETCTextures(tests) { |
|
193 for (var ii = 0; ii < tests.length; ++ii) { |
|
194 debug("<hr/>"); |
|
195 testETCTexture(tests[ii]); |
|
196 } |
|
197 } |
|
198 |
|
199 function offset_color(c, o) { |
|
200 return [ |
|
201 Math.min(Math.max(0, c[0] + o), 255), |
|
202 Math.min(Math.max(0, c[1] + o), 255), |
|
203 Math.min(Math.max(0, c[2] + o), 255), |
|
204 c[3] |
|
205 ]; |
|
206 } |
|
207 |
|
208 function uncompressETC1Block(destBuffer, destX, destY, destWidth, src) { |
|
209 'use strict'; |
|
210 var xx, yy, basecols; |
|
211 var _deltatable = [ 0, 1, 2, 3, -4, -3, -2, -1 ]; |
|
212 var _modtable = [ |
|
213 [ 2, 8, -2, -8 ], |
|
214 [ 5, 17, -5, -17 ], |
|
215 [ 9, 29, -9, -29 ], |
|
216 [ 13, 42, -13, -42 ], |
|
217 [ 18, 60, -18, -60 ], |
|
218 [ 24, 80, -24, -80 ], |
|
219 [ 33, 106, -33, -106 ], |
|
220 [ 47, 183, -47, -183 ] |
|
221 ]; |
|
222 var _sl = [ |
|
223 0x00, 0x01, 0x04, 0x05, |
|
224 0x10, 0x11, 0x14, 0x15, |
|
225 0x40, 0x41, 0x44, 0x45, |
|
226 0x50, 0x51, 0x54, 0x55 |
|
227 ]; |
|
228 var _sh = [ |
|
229 0x00, 0x02, 0x08, 0x0a, |
|
230 0x20, 0x22, 0x28, 0x2a, |
|
231 0x80, 0x82, 0x88, 0x8a, |
|
232 0xa0, 0xa2, 0xa8, 0xaa |
|
233 ]; |
|
234 |
|
235 function extend_4to8bits(r, g, b) { |
|
236 return [ |
|
237 (r & 0xf0) | ((r >> 4) & 0x0f), |
|
238 (g & 0xf0) | ((g >> 4) & 0x0f), |
|
239 (b & 0xf0) | ((b >> 4) & 0x0f), |
|
240 255 |
|
241 ]; |
|
242 } |
|
243 |
|
244 function extend_5to8bits(r, g, b) { |
|
245 return [ |
|
246 (r & 0xf8) | ((r >> 5) & 0x07), |
|
247 (g & 0xf8) | ((g >> 5) & 0x07), |
|
248 (b & 0xf8) | ((b >> 5) & 0x07), |
|
249 255 |
|
250 ]; |
|
251 } |
|
252 |
|
253 function base_colors(src, mode) { |
|
254 var col_1, col_2, didx, d; |
|
255 if (mode === 'I') { |
|
256 col_1 = extend_4to8bits(src[0], src[1], src[2]); |
|
257 col_2 = extend_4to8bits(src[0] << 4, src[1] << 4, src[2] << 4); |
|
258 return [ col_1, col_2 ]; |
|
259 } |
|
260 |
|
261 if (mode === 'D') { |
|
262 col_1 = extend_5to8bits(src[0], src[1], src[2]); |
|
263 col_2 = extend_5to8bits(src[0] + 8 * _deltatable[(src[0] & 0x7)], |
|
264 src[1] + 8 * _deltatable[(src[1] & 0x7)], |
|
265 src[2] + 8 * _deltatable[(src[2] & 0x7)]); |
|
266 return [ col_1, col_2 ]; |
|
267 } |
|
268 |
|
269 return []; |
|
270 } |
|
271 |
|
272 function mode(src) { |
|
273 return (src[3] & 0x2) ? 'D' : 'I'; |
|
274 } |
|
275 |
|
276 function flip(src) { |
|
277 return (src[3] & 0x1) === 0x1; |
|
278 } |
|
279 |
|
280 function subblock_modtable(src, sb) { |
|
281 var shift = (sb ? 2 : 5); |
|
282 var idx = (src[3] >> shift) & 0x7; |
|
283 return _modtable[idx]; |
|
284 } |
|
285 |
|
286 function interleave_table_indices(src) { |
|
287 var result = |
|
288 (_sl[src[7] & 0xf] | _sh[src[5] & 0xf]) | |
|
289 ((_sl[(src[7] >> 4) & 0xf] | _sh[(src[5] >> 4) & 0xf]) << 8) | |
|
290 ((_sl[src[6] & 0xf] | _sh[src[4] & 0xf]) << 16) | |
|
291 ((_sl[(src[6] >> 4) & 0xf] | _sh[(src[4] >> 4) & 0xf]) << 24); |
|
292 return result; |
|
293 } |
|
294 |
|
295 function subblock(n, flip) { |
|
296 var mask = flip ? 0x2 : 0x8; |
|
297 return (n & mask) ? 1 : 0; |
|
298 } |
|
299 |
|
300 var m = mode(src); |
|
301 basecols = base_colors(src, m); |
|
302 |
|
303 var alpha = 255; |
|
304 var flipbit = flip(src); |
|
305 var table_indices = interleave_table_indices(src); |
|
306 |
|
307 var n = 0; |
|
308 for (xx = 0; xx < 4; ++xx) { |
|
309 for (yy = 0; yy < 4; ++yy) { |
|
310 var dstOff = ((destY + yy) * destWidth + destX + xx) * 4; |
|
311 |
|
312 var sb = subblock(n, flipbit); |
|
313 var mod = subblock_modtable(src, sb); |
|
314 var offset = mod[(table_indices & 0x3)]; |
|
315 var col = offset_color(basecols[sb], offset); |
|
316 |
|
317 destBuffer[dstOff] = col[0]; |
|
318 destBuffer[dstOff + 1] = col[1]; |
|
319 destBuffer[dstOff + 2] = col[2]; |
|
320 destBuffer[dstOff + 3] = alpha; |
|
321 table_indices >>= 2; |
|
322 n++; |
|
323 } |
|
324 } |
|
325 } |
|
326 |
|
327 function uncompressETC1(width, height, data, format) { |
|
328 if (width % 4 || height % 4) throw "bad width or height"; |
|
329 |
|
330 var dest = new Uint8Array(width * height * 4); |
|
331 var blocksAcross = width / 4; |
|
332 var blocksDown = height / 4; |
|
333 var blockSize = 8; |
|
334 for (var yy = 0; yy < blocksDown; ++yy) { |
|
335 for (var xx = 0; xx < blocksAcross; ++xx) { |
|
336 var srcOffset = (yy * blocksAcross + xx) * blockSize; |
|
337 var srcblk = data.subarray(srcOffset, srcOffset + blockSize); |
|
338 uncompressETC1Block(dest, xx * 4, yy * 4, width, srcblk); |
|
339 } |
|
340 } |
|
341 return dest; |
|
342 } |
|
343 |
|
344 function testETCTexture(test) { |
|
345 var data = new Uint8Array(test.data); |
|
346 var width = test.width; |
|
347 var height = test.height; |
|
348 var format = test.format; |
|
349 |
|
350 var uncompressedData = uncompressETC1(width, height, data, format); |
|
351 |
|
352 canvas.width = width; |
|
353 canvas.height = height; |
|
354 gl.viewport(0, 0, width, height); |
|
355 debug("testing " + formatToString(format) + " " + width + "x" + height); |
|
356 |
|
357 var tex = gl.createTexture(); |
|
358 gl.bindTexture(gl.TEXTURE_2D, tex); |
|
359 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); |
|
360 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); |
|
361 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); |
|
362 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); |
|
363 gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height, 0, data); |
|
364 glErrorShouldBe(gl, gl.NO_ERROR, "uploading compressed texture"); |
|
365 gl.generateMipmap(gl.TEXTURE_2D); |
|
366 glErrorShouldBe(gl, gl.INVALID_OPERATION, "trying to generate mipmaps from compressed texture"); |
|
367 wtu.clearAndDrawUnitQuad(gl); |
|
368 compareRect(width, height, test.channels, width, height, uncompressedData, data, format); |
|
369 |
|
370 gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height, 1, data); |
|
371 glErrorShouldBe(gl, gl.INVALID_VALUE, "non 0 border"); |
|
372 |
|
373 gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width + 4, height, 0, data); |
|
374 glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions"); |
|
375 gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height + 4, 0, data); |
|
376 glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions"); |
|
377 gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width - 4, height, 0, data); |
|
378 glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions"); |
|
379 gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height - 4, 0, data); |
|
380 glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions"); |
|
381 |
|
382 gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width - 1, height, 0, data); |
|
383 glErrorShouldBe(gl, gl.NO_ERROR, "non multiple-of-4 supported"); |
|
384 gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width - 2, height, 0, data); |
|
385 glErrorShouldBe(gl, gl.NO_ERROR, "non multiple-of-4 supported"); |
|
386 gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height - 1, 0, data); |
|
387 glErrorShouldBe(gl, gl.NO_ERROR, "non multiple-of-4 supported"); |
|
388 gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height - 2, 0, data); |
|
389 glErrorShouldBe(gl, gl.NO_ERROR, "non multiple-of-4 supported"); |
|
390 |
|
391 if (width == 4) { |
|
392 gl.compressedTexImage2D(gl.TEXTURE_2D, 1, format, 1, height, 0, data); |
|
393 glErrorShouldBe(gl, gl.NO_ERROR, "valid dimensions for level > 0"); |
|
394 gl.compressedTexImage2D(gl.TEXTURE_2D, 1, format, 2, height, 0, data); |
|
395 glErrorShouldBe(gl, gl.NO_ERROR, "valid dimensions for level > 0"); |
|
396 } |
|
397 if (height == 4) { |
|
398 gl.compressedTexImage2D(gl.TEXTURE_2D, 1, format, width, 1, 0, data); |
|
399 glErrorShouldBe(gl, gl.NO_ERROR, "valid dimensions for level > 0"); |
|
400 gl.compressedTexImage2D(gl.TEXTURE_2D, 1, format, width, 2, 0, data); |
|
401 glErrorShouldBe(gl, gl.NO_ERROR, "valid dimensions for level > 0"); |
|
402 } |
|
403 |
|
404 // Reupload the complete texture before SubImage tests. |
|
405 gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height, 0, data); |
|
406 glErrorShouldBe(gl, gl.NO_ERROR, "uploading compressed texture"); |
|
407 |
|
408 /* OES_compressed_ETC1_RGB8_texture: |
|
409 * INVALID_OPERATION is generated by CompressedTexSubImage2D, |
|
410 * TexSubImage2D, or CopyTexSubImage2D if the texture image |
|
411 * <level> bound to <target> has internal format ETC1_RGB8_OES. |
|
412 */ |
|
413 gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height, format, data); |
|
414 glErrorShouldBe(gl, gl.INVALID_OPERATION, "ETC1 should not support compressedTexSubImage2D."); |
|
415 } |
|
416 |
|
417 function insertImg(element, caption, img) { |
|
418 var div = document.createElement("div"); |
|
419 div.appendChild(img); |
|
420 var label = document.createElement("div"); |
|
421 label.appendChild(document.createTextNode(caption)); |
|
422 div.appendChild(label); |
|
423 element.appendChild(div); |
|
424 } |
|
425 |
|
426 function makeImage(imageWidth, imageHeight, dataWidth, data, alpha) { |
|
427 var scale = 8; |
|
428 var c = document.createElement("canvas"); |
|
429 c.width = imageWidth * scale; |
|
430 c.height = imageHeight * scale; |
|
431 var ctx = c.getContext("2d"); |
|
432 for (var yy = 0; yy < imageHeight; ++yy) { |
|
433 for (var xx = 0; xx < imageWidth; ++xx) { |
|
434 var offset = (yy * dataWidth + xx) * 4; |
|
435 ctx.fillStyle = "rgba(" + |
|
436 data[offset + 0] + "," + |
|
437 data[offset + 1] + "," + |
|
438 data[offset + 2] + "," + |
|
439 (alpha ? data[offset + 3] / 255 : 1) + ")"; |
|
440 ctx.fillRect(xx * scale, yy * scale, scale, scale); |
|
441 } |
|
442 } |
|
443 var img = document.createElement("img"); |
|
444 img.src = c.toDataURL(); |
|
445 return img; |
|
446 } |
|
447 |
|
448 function compareRect(actualWidth, actualHeight, actualChannels, |
|
449 dataWidth, dataHeight, expectedData, |
|
450 testData, testFormat) |
|
451 { |
|
452 var actual = new Uint8Array(actualWidth * actualHeight * 4); |
|
453 gl.readPixels(0, 0, actualWidth, actualHeight, |
|
454 gl.RGBA, gl.UNSIGNED_BYTE, actual); |
|
455 |
|
456 var div = document.createElement("div"); |
|
457 div.className = "testimages"; |
|
458 insertImg(div, "expected", makeImage( |
|
459 actualWidth, actualHeight, dataWidth, expectedData, |
|
460 actualChannels == 4)); |
|
461 insertImg(div, "actual", makeImage( |
|
462 actualWidth, actualHeight, actualWidth, actual, |
|
463 actualChannels == 4)); |
|
464 div.appendChild(document.createElement('br')); |
|
465 document.getElementById("console").appendChild(div); |
|
466 |
|
467 var failed = false; |
|
468 for (var yy = 0; yy < actualHeight; ++yy) { |
|
469 for (var xx = 0; xx < actualWidth; ++xx) { |
|
470 var actualOffset = (yy * actualWidth + xx) * 4; |
|
471 var expectedOffset = (yy * dataWidth + xx) * 4; |
|
472 var expected = [ |
|
473 expectedData[expectedOffset + 0], |
|
474 expectedData[expectedOffset + 1], |
|
475 expectedData[expectedOffset + 2], |
|
476 (actualChannels == 3 ? 255 |
|
477 : expectedData[expectedOffset + 3]) |
|
478 ]; |
|
479 |
|
480 if (actual[actualOffset + 0] != expected[0] || |
|
481 actual[actualOffset + 1] != expected[1] || |
|
482 actual[actualOffset + 2] != expected[2] || |
|
483 actual[actualOffset + 3] != expected[3]) |
|
484 { |
|
485 failed = true; |
|
486 var was = actual[actualOffset + 0].toString(); |
|
487 for (var j = 1; j < 4; ++j) { |
|
488 was += "," + actual[actualOffset + j]; |
|
489 } |
|
490 testFailed('at (' + xx + ', ' + yy + |
|
491 ') expected: ' + expected + ' was ' + was); |
|
492 } |
|
493 } |
|
494 } |
|
495 if (!failed) { |
|
496 testPassed("texture rendered correctly"); |
|
497 } |
|
498 } |
|
499 |
|
500 debug(""); |
|
501 var successfullyParsed = true; |
|
502 </script> |
|
503 <script>finishTest();</script> |
|
504 |
|
505 </body> |
|
506 </html> |