test_SVGTransformList.xhtml (14223B)
1 <html xmlns="http://www.w3.org/1999/xhtml"> 2 <!-- 3 https://bugzilla.mozilla.org/show_bug.cgi?id=602759 4 --> 5 <head> 6 <title>Tests specific to SVGTransformList</title> 7 <script src="/tests/SimpleTest/SimpleTest.js"></script> 8 <script type="text/javascript" src="matrixUtils.js"></script> 9 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> 10 </head> 11 <body> 12 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=602759"> 13 Mozilla Bug 602759</a> 14 <p id="display"></p> 15 <div id="content" style="display:none;"> 16 <svg id="svg" xmlns="http://www.w3.org/2000/svg" width="100" height="100" 17 onload="this.pauseAnimations();"> 18 <g id="g"/> 19 </svg> 20 </div> 21 <pre id="test"> 22 <script class="testbody" type="text/javascript"> 23 <![CDATA[ 24 25 SimpleTest.waitForExplicitFinish(); 26 27 /* 28 This file runs a series of SVGTransformList specific tests. Generic SVGXxxList 29 tests can be found in test_SVGxxxList.xhtml. Anything that can be generalized 30 to other list types belongs there. 31 */ 32 33 function main() { 34 var g = $("g"); 35 var tests = 36 [ testConsolidateMatrix, 37 testConsolidateMatrixOneElem, 38 testConsolidateMatrixZeroElem, 39 testCreateSVGTransformFromMatrix, 40 testReadOnly, 41 testOrphan, 42 testFailedSet, 43 testMutationEvents, 44 ]; 45 for (var i = 0; i < tests.length; i++) { 46 tests[i](g); 47 } 48 SimpleTest.finish(); 49 } 50 51 function testConsolidateMatrix(g) { 52 // This is the example from SVG 1.1 section 7.5 53 g.setAttribute("transform", 54 "translate(50 90) rotate(-45) translate(130 160)"); 55 var list = g.transform.baseVal; 56 is(list.numberOfItems, 3, "Unexpected length of unconsolidated list"); 57 58 // Sanity check -- take ref to first item in list and validate it 59 var first_item = list.getItem(0); 60 is(first_item.type, SVGTransform.SVG_TRANSFORM_TRANSLATE, 61 "Unexpected type of first item in list"); 62 cmpMatrix(first_item.matrix, [1, 0, 0, 1, 50, 90], 63 "Unexpected value for first item in list"); 64 65 // Consolidate 66 var consolidated = list.consolidate(); 67 is(list.numberOfItems, 1, "Unexpected length of consolidated list"); 68 ok(consolidated === list.getItem(0), 69 "Consolidate return value should be first item in list, not a copy"); 70 is(consolidated.type, SVGTransform.SVG_TRANSFORM_MATRIX, 71 "Consolidated transform not of type matrix"); 72 const angle = -Math.PI / 4; 73 roughCmpMatrix(consolidated.matrix, 74 [Math.cos(angle), Math.sin(angle), 75 -Math.sin(angle), Math.cos(angle), 76 130 * Math.cos(angle) - 160 * Math.sin(angle) + 50, 77 160 * Math.cos(angle) + 130 * Math.sin(angle) + 90], 78 "Unexpected result after consolidating matrices"); 79 80 // Check ref to first item in list 81 // a) should not have changed 82 is(first_item.type, SVGTransform.SVG_TRANSFORM_TRANSLATE, 83 "Unexpected type of cached first item in list after consolidating"); 84 cmpMatrix(first_item.matrix, [1, 0, 0, 1, 50, 90], 85 "Unexpected value for cached first item in list after consolidating"); 86 // b) should still be usable 87 first_item.setScale(2, 3); 88 is(first_item.type, SVGTransform.SVG_TRANSFORM_SCALE, 89 "Cached first item in list not usable after consolidating"); 90 91 // Check consolidated is live 92 // a) Changes to 'consolidated' affect list 93 consolidated.setSkewX(45); 94 is(list.getItem(0).type, SVGTransform.SVG_TRANSFORM_SKEWX, 95 "Changing return value from consolidate doesn't affect list"); 96 // b) Changes to list affect 'consolidated' 97 list.getItem(0).setRotate(90, 0, 0); 98 is(consolidated.type, SVGTransform.SVG_TRANSFORM_ROTATE, 99 "Changing list doesn't affect return value from consolidate"); 100 } 101 102 function testConsolidateMatrixOneElem(g) { 103 // Check that even if we only have one item in the list it becomes a matrix 104 // transform (as per the spec) 105 g.setAttribute("transform", "translate(50 90)"); 106 var list = g.transform.baseVal; 107 is(list.numberOfItems, 1, "Unexpected length of unconsolidated list"); 108 var first_item = list.getItem(0); 109 is(first_item.type, SVGTransform.SVG_TRANSFORM_TRANSLATE, 110 "Unexpected type of first item in list"); 111 cmpMatrix(first_item.matrix, [1, 0, 0, 1, 50, 90], 112 "Unexpected value for first item in list"); 113 114 // Consolidate 115 var consolidated = list.consolidate(); 116 is(list.numberOfItems, 1, "Unexpected length of consolidated list"); 117 ok(consolidated === list.getItem(0), 118 "Consolidate return value should be first item in list, not a copy"); 119 is(consolidated.type, SVGTransform.SVG_TRANSFORM_MATRIX, 120 "Consolidated transform not of type matrix"); 121 cmpMatrix(consolidated.matrix, [1, 0, 0, 1, 50, 90], 122 "Unexpected consolidated matrix value"); 123 } 124 125 function testConsolidateMatrixZeroElem(g) { 126 // Check that zero items returns null 127 g.setAttribute("transform", ""); 128 var list = g.transform.baseVal; 129 is(list.numberOfItems, 0, "Unexpected length of unconsolidated list"); 130 var consolidated = list.consolidate(); 131 ok(consolidated === null, 132 "consolidate() should return null for a zero-length transform list"); 133 } 134 135 function testCreateSVGTransformFromMatrix(g) { 136 var m = createMatrix(1, 2, 3, 4, 5, 6); 137 138 // "Creates an SVGTransform object which is initialized to transform of type 139 // SVG_TRANSFORM_MATRIX and whose values are the given matrix. The values from 140 // the parameter matrix are copied, the matrix parameter is not adopted as 141 // SVGTransform::matrix." 142 var list = g.transform.baseVal; 143 list.clear(); 144 var t = list.createSVGTransformFromMatrix(m); 145 146 // Check that list hasn't changed 147 is(list.numberOfItems, 0, 148 "Transform list changed after calling createSVGTransformFromMatrix"); 149 150 // Check return value 151 is(t.type, SVGTransform.SVG_TRANSFORM_MATRIX, 152 "Returned transform not of type matrix"); 153 cmpMatrix(t.matrix, [1, 2, 3, 4, 5, 6], 154 "Unexpected returned matrix value"); 155 156 // Check values are copied 157 ok(t.matrix != m, "Matrix should be copied not adopted"); 158 m.a = 2; 159 is(t.matrix.a, 1, 160 "Changing source matrix should not affect newly created transform"); 161 162 // null should give us an identity matrix 163 t = list.createSVGTransformFromMatrix(null); 164 cmpMatrix(t.matrix, [1, 0, 0, 1, 0, 0], 165 "Unexpected returned matrix value"); 166 167 // Try passing in bad values ("undefined" etc.) 168 let exception = null; 169 try { 170 t = list.createSVGTransformFromMatrix("undefined"); 171 } catch (e) { exception = e; } 172 ok(exception, 173 "Failed to throw for string input to createSVGTransformFromMatrix"); 174 exception = null; 175 try { 176 t = list.createSVGTransformFromMatrix(SVGMatrix(t)); 177 } catch (e) { exception = e; } 178 ok(exception, 179 "Failed to throw for bad input to createSVGTransformFromMatrix"); 180 exception = null; 181 } 182 183 function testReadOnly(g) { 184 var SVG_NS = "http://www.w3.org/2000/svg"; 185 186 // Just some data to work with 187 g.setAttribute("transform", "translate(50 90)"); 188 189 // baseVal / animVal are readonly attributes 190 // Create another (empty) transform list 191 var otherg = document.createElementNS(SVG_NS, "g"); 192 g.parentNode.appendChild(otherg); 193 is(g.transform.baseVal.numberOfItems, 1, 194 "Unexpected number of items in transform list before attempting to set"); 195 is(otherg.transform.baseVal.numberOfItems, 0, 196 "Unexpected number of items in source transform list before attempting to" 197 + " set"); 198 // Attempt to set the base value and check nothing changes 199 g.transform.baseVal = otherg.transform.baseVal; 200 is(g.transform.baseVal.numberOfItems, 1, 201 "baseVal should be read-only but its value has changed"); 202 is(otherg.transform.baseVal.numberOfItems, 0, 203 "baseVal changed after attempting to use it set another value"); 204 205 // Read-only SVGTransformList: 206 // Standard list methods are covered in test_SVGxxxList.xhtml so here we 207 // just add tests for SVGTransformList-specific methods 208 var roList = g.transform.animVal; 209 // consolidate() 210 var threw = false; 211 try { 212 roList.consolidate(); 213 } catch (e) { 214 is(e.name, "NoModificationAllowedError", 215 "Got unexpected exception " + e + 216 ", expected NoModificationAllowedError"); 217 is(e.code, DOMException.NO_MODIFICATION_ALLOWED_ERR, 218 "Got unexpected exception " + e + 219 ", expected NO_MODIFICATION_ALLOWED_ERR"); 220 threw = true; 221 } 222 ok(threw, 223 "Failed to throw exception when calling consolidate on read-only list"); 224 225 // Read-only SVGTransform: 226 // read-only attributes are tested in test_transform.xhtml. Here we are 227 // concerned with methods that throw because this *object* is read-only 228 // (since it belongs to a read-only transform list) 229 var roTransform = roList.getItem(0); 230 // setMatrix 231 threw = false; 232 try { 233 var m = createMatrix(1, 2, 3, 4, 5, 6); 234 roTransform.setMatrix(m); 235 } catch (e) { 236 is(e.name, "NoModificationAllowedError", 237 "Got unexpected exception " + e + 238 ", expected NoModificationAllowedError"); 239 is(e.code, DOMException.NO_MODIFICATION_ALLOWED_ERR, 240 "Got unexpected exception " + e + 241 ", expected NO_MODIFICATION_ALLOWED_ERR"); 242 threw = true; 243 } 244 ok(threw, "Failed to throw exception when calling setMatrix on read-only" 245 + " transform"); 246 // setTranslate 247 threw = false; 248 try { 249 roTransform.setTranslate(2, 3); 250 } catch (e) { 251 threw = true; 252 } 253 ok(threw, "Failed to throw when calling setTranslate on read-only" 254 + " transform"); 255 // setScale 256 threw = false; 257 try { 258 roTransform.setScale(2, 3); 259 } catch (e) { 260 threw = true; 261 } 262 ok(threw, "Failed to throw when calling setScale on read-only transform"); 263 // setRotate 264 threw = false; 265 try { 266 roTransform.setRotate(1, 2, 3); 267 } catch (e) { 268 threw = true; 269 } 270 ok(threw, "Failed to throw when calling setRotate on read-only transform"); 271 // setSkewX 272 threw = false; 273 try { 274 roTransform.setSkewX(2); 275 } catch (e) { 276 threw = true; 277 } 278 ok(threw, "Failed to throw when calling setSkewX on read-only transform"); 279 // setSkewY 280 threw = false; 281 try { 282 roTransform.setSkewY(2); 283 } catch (e) { 284 threw = true; 285 } 286 ok(threw, "Failed to throw when calling setSkewY on read-only transform"); 287 288 // Read-only SVGMatrix 289 var roMatrix = roTransform.matrix; 290 threw = false; 291 try { 292 roMatrix.a = 1; 293 } catch (e) { 294 is(e.name, "NoModificationAllowedError", 295 "Got unexpected exception " + e + 296 ", expected NoModificationAllowedError"); 297 is(e.code, DOMException.NO_MODIFICATION_ALLOWED_ERR, 298 "Got unexpected exception " + e + 299 ", expected NO_MODIFICATION_ALLOWED_ERR"); 300 threw = true; 301 } 302 ok(threw, "Failed to throw exception when modifying read-only matrix"); 303 } 304 305 function testOrphan(g) { 306 // Although this isn't defined, if a read-only object becomes orphaned 307 // (detached from it's parent), then presumably it should become editable 308 // again. 309 310 // As with the read-only test set a value to test with 311 g.setAttribute("transform", "translate(50 90)"); 312 313 var roList = g.transform.animVal; 314 var roTransform = roList.getItem(0); 315 var roMatrix = roTransform.matrix; 316 317 // Orphan transform list contents by re-setting transform attribute 318 g.setAttribute("transform", ""); 319 320 // Transform should now be editable 321 var exception = null; 322 try { 323 roTransform.setTranslate(5, 3); 324 } catch (e) { 325 exception = e; 326 } 327 ok(exception === null, 328 "Unexpected exception " + exception + " modifying orphaned transform"); 329 330 // So should matrix 331 exception = null; 332 try { 333 roMatrix.a = 1; 334 } catch (e) { 335 exception = e; 336 } 337 ok(exception === null, 338 "Unexpected exception " + exception + " modifying orphaned matrix"); 339 } 340 341 function testFailedSet(g) { 342 // Check that a parse failure results in the attribute being empty 343 344 // Set initial value 345 g.setAttribute("transform", "translate(50 90)"); 346 var list = g.transform.baseVal; 347 is(list.numberOfItems, 1, "Unexpected initial length of list"); 348 349 // Attempt to set bad value 350 g.setAttribute("transform", "translate(40 50) scale(a)"); 351 is(list.numberOfItems, 0, 352 "Transform list should be empty after setting bad value"); 353 is(g.transform.animVal.numberOfItems, 0, 354 "Animated transform list should also be empty after setting bad value"); 355 } 356 357 function testMutationEvents(g) { 358 // Check mutation events 359 360 // Set initial value 361 g.setAttribute("transform", "translate(50 90)"); 362 var list = g.transform.baseVal; 363 is(list.numberOfItems, 1, "Unexpected initial length of list"); 364 365 // consolidate 366 // 367 // Consolidate happens to generate two modification events in our 368 // implementation--it's not ideal but it's better than none 369 g.setAttribute("transform", "translate(10 10) translate(10 10)"); 370 list.consolidate(); 371 372 // In the following, each of the operations is performed twice but only one 373 // mutation event is expected. This is to check that redundant mutation 374 // events are not sent. 375 376 // transform.setMatrix 377 var mx = $("svg").createSVGMatrix(); 378 list[0].setMatrix(mx); 379 list[0].setMatrix(mx); 380 [ 381 {a: 1, m11: 2}, 382 {a: Infinity}, 383 {b: 0, m12: -1}, 384 {c: Infinity, m21: -Infinity}, 385 {d: 0, m22: NaN}, 386 {e: 1, m41: 1.00000001}, 387 {f: 0, m42: Number.MIN_VALUE}, 388 ].forEach(dict => { 389 let exception = null; 390 try { 391 list[0].setMatrix(dict); 392 } catch (e) { 393 exception = e; 394 } 395 ok(exception, 396 "Failed to throw for invalid input to setMatrix"); 397 }); 398 399 // transform.setTranslate 400 list[0].setTranslate(10, 10); 401 list[0].setTranslate(10, 10); 402 403 // transform.setScale 404 list[0].setScale(2, 2); 405 list[0].setScale(2, 2); 406 407 // transform.setRotate 408 list[0].setRotate(45, 1, 2); 409 list[0].setRotate(45, 1, 2); 410 411 // transform.setSkewX 412 list[0].setSkewX(45); 413 list[0].setSkewX(45); 414 415 // transform.setSkewY 416 list[0].setSkewY(25); 417 list[0].setSkewY(25); 418 419 // transform.matrix 420 list[0].matrix.a = 1; 421 list[0].matrix.a = 1; 422 list[0].matrix.e = 5; 423 list[0].matrix.e = 5; 424 425 // setAttribute interaction 426 list[0].setMatrix(mx); 427 g.setAttribute("transform", "matrix(1, 0, 0, 1, 0, 0)"); 428 list[0].setMatrix(mx); 429 430 // Attribute removal 431 g.removeAttribute("transform"); 432 433 // Non-existent attribute removal 434 g.removeAttribute("transform"); 435 g.removeAttributeNS(null, "transform"); 436 } 437 438 window.addEventListener("load", 439 () => SimpleTest.executeSoon(main) 440 ); 441 442 ]]> 443 </script> 444 </pre> 445 </body> 446 </html>