test_smilChangeAfterFrozen.xhtml (20471B)
1 <html xmlns="http://www.w3.org/1999/xhtml"> 2 <head> 3 <title>Test for SMIL when things change after an animation is frozen</title> 4 <script src="/tests/SimpleTest/SimpleTest.js"></script> 5 <script type="text/javascript" src="smilTestUtils.js"></script> 6 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> 7 </head> 8 <body> 9 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=533291">Mozilla Bug 533291</a> 10 <p id="display"></p> 11 <!-- Bug 628848: The following should be display: none but we currently don't 12 handle percentage lengths properly when the whole fragment is display: none 13 --> 14 <div id="content" style="visibility: hidden"> 15 <svg id="svg" xmlns="http://www.w3.org/2000/svg" width="120px" height="120px" 16 onload="this.pauseAnimations()"> 17 <g id="circleParent"> 18 <circle cx="0" cy="20" r="15" fill="blue" id="circle"/> 19 </g> 20 </svg> 21 </div> 22 <pre id="test"> 23 <script class="testbody" type="text/javascript"> 24 <![CDATA[ 25 /** Test for SMIL values that are context-sensitive */ 26 27 /* See bugs 533291 and 562815. 28 29 The format of each test is basically: 30 1) create some animated and frozen state 31 2) test the animated values 32 3) change the context 33 4) test that context-sensitive animation values have changed 34 35 Ideally, after changing the context (3), the animated state would instantly 36 update. However, this is not currently the case for many situations. 37 38 For CSS properties we have bug 545282 - In animations involving 'inherit' 39 / 'currentColor', changes to inherited value / 'color' don't show up in 40 animated value immediately 41 42 For SVG lengths we have bug 508206 - Relative units used in 43 animation don't update immediately 44 45 (There are a few of todo_is's in the following tests so that if those bugs 46 are ever resolved we'll know to update this test case accordingly.) 47 48 So in between (3) and (4) we force a sample. This is currently done by 49 calling SVGSVGElement.setCurrentTime with the same current time which has the 50 side effect of forcing a sample. 51 52 What we *are* testing is that we're not too zealous with caching animation 53 values whilst in the frozen state. Normally we'd say, "Hey, we're frozen, 54 let's just use the same animation result as last time" but for some 55 context-sensitive animation values that doesn't work. 56 */ 57 58 /* Global Variables */ 59 const SVGNS = "http://www.w3.org/2000/svg"; 60 61 // Animation parameters -- not used for <set> animation 62 const ANIM_DUR = "4s"; 63 const TIME_ANIM_END = "4"; 64 const TIME_AFTER_ANIM_END = "5"; 65 66 const gSvg = document.getElementById("svg"); 67 const gCircle = document.getElementById("circle"); 68 const gCircleParent = document.getElementById("circleParent"); 69 70 SimpleTest.waitForExplicitFinish(); 71 72 // MAIN FUNCTION 73 // ------------- 74 75 function main() 76 { 77 ok(gSvg.animationsPaused(), "should be paused by <svg> load handler"); 78 is(gSvg.getCurrentTime(), 0, "should be paused at 0 in <svg> load handler"); 79 80 const tests = 81 [ testBaseValueChange, 82 testCurrentColorChange, 83 testCurrentColorChangeUsingStyle, 84 testCurrentColorChangeOnFallback, 85 testInheritChange, 86 testInheritChangeUsingStyle, 87 testEmUnitChangeOnProp, 88 testEmUnitChangeOnPropBase, 89 testEmUnitChangeOnLength, 90 testPercentUnitChangeOnProp, 91 testPercentUnitChangeOnLength, 92 testRelativeFontSize, 93 testRelativeFontWeight, 94 testRelativeFont, 95 testCalcFontSize, 96 testDashArray, 97 testClip 98 ]; 99 100 while (tests.length) { 101 tests.shift()(); 102 } 103 SimpleTest.finish(); 104 } 105 106 // HELPER FUNCTIONS 107 // ---------------- 108 function createAnimSetTo(attrName, toVal) 109 { 110 var anim = document.createElementNS(SVGNS,"set"); 111 anim.setAttribute("attributeName", attrName); 112 anim.setAttribute("to", toVal); 113 return gCircle.appendChild(anim); 114 } 115 116 function createAnimBy(attrName, byVal) 117 { 118 var anim = document.createElementNS(SVGNS,"animate"); 119 anim.setAttribute("attributeName", attrName); 120 anim.setAttribute("dur", ANIM_DUR); 121 anim.setAttribute("begin","0s"); 122 anim.setAttribute("by", byVal); 123 anim.setAttribute("fill", "freeze"); 124 return gCircle.appendChild(anim); 125 } 126 127 function createAnimFromTo(attrName, fromVal, toVal) 128 { 129 var anim = document.createElementNS(SVGNS,"animate"); 130 anim.setAttribute("attributeName", attrName); 131 anim.setAttribute("dur", ANIM_DUR); 132 anim.setAttribute("begin","0s"); 133 anim.setAttribute("from", fromVal); 134 anim.setAttribute("to", toVal); 135 anim.setAttribute("fill", "freeze"); 136 return gCircle.appendChild(anim); 137 } 138 139 // Common setup code for each test function: seek to 0, and make sure 140 // the previous test cleaned up its animations. 141 function setupTest() { 142 gSvg.setCurrentTime(0); 143 if (gCircle.firstChild) { 144 ok(false, "Previous test didn't clean up after itself."); 145 } 146 } 147 148 // THE TESTS 149 // --------- 150 151 function testBaseValueChange() 152 { 153 setupTest(); 154 var anim = createAnimBy("cx", "50"); 155 gSvg.setCurrentTime(TIME_ANIM_END); 156 is(gCircle.cx.animVal.value, 50, 157 "Checking animated cx as anim ends"); 158 159 gSvg.setCurrentTime(TIME_AFTER_ANIM_END); 160 is(gCircle.cx.animVal.value, 50, 161 "Checking animated cx after anim ends"); 162 163 gCircle.setAttribute("cx", 20); 164 is(gCircle.cx.animVal.value, 70, 165 "Checking animated cx after anim ends & after changing base val"); 166 167 anim.remove(); // clean up 168 } 169 170 function testCurrentColorChange() 171 { 172 gCircle.setAttribute("color", "red"); // At first: currentColor=red 173 var anim = createAnimSetTo("fill", "currentColor"); 174 175 gSvg.setCurrentTime(0); // trigger synchronous sample 176 is(SMILUtil.getComputedStyleSimple(gCircle, "fill"), "rgb(255, 0, 0)", 177 "Checking animated fill=currentColor after animating"); 178 179 gCircle.setAttribute("color", "lime"); // Change: currentColor=lime 180 // Bug 545282: We should really detect this change and update immediately but 181 // currently we don't until we get sampled again 182 todo_is(SMILUtil.getComputedStyleSimple(gCircle, "fill"), "rgb(0, 255, 0)", 183 "Checking animated fill=currentColor after updating context but before " + 184 "sampling"); 185 gSvg.setCurrentTime(0); 186 is(SMILUtil.getComputedStyleSimple(gCircle, "fill"), "rgb(0, 255, 0)", 187 "Checking animated fill=currentColor after updating context"); 188 189 // Clean up 190 gCircle.removeAttribute("color"); 191 gCircle.firstChild.remove(); 192 } 193 194 function testCurrentColorChangeUsingStyle() 195 { 196 setupTest(); 197 gCircle.setAttribute("style", "color: red"); // At first: currentColor=red 198 var anim = createAnimSetTo("fill", "currentColor"); 199 200 gSvg.setCurrentTime(0); 201 is(SMILUtil.getComputedStyleSimple(gCircle, "fill"), "rgb(255, 0, 0)", 202 "Checking animated fill=currentColor after animating (using style attr)"); 203 204 gCircle.setAttribute("style", "color: lime"); // Change: currentColor=lime 205 gSvg.setCurrentTime(0); 206 is(SMILUtil.getComputedStyleSimple(gCircle, "fill"), "rgb(0, 255, 0)", 207 "Checking animated fill=currentColor after updating context " 208 + "(using style attr)"); 209 210 // Clean up 211 gCircle.removeAttribute("style"); 212 gCircle.firstChild.remove(); 213 } 214 215 function getFallbackColor(pServerStr) 216 { 217 return pServerStr.substr(pServerStr.indexOf(" ")+1); 218 } 219 220 function testCurrentColorChangeOnFallback() 221 { 222 setupTest(); 223 gCircle.setAttribute("color", "red"); // At first: currentColor=red 224 var anim = createAnimSetTo("fill", "url(#missingGrad) currentColor"); 225 226 gSvg.setCurrentTime(0); 227 var fallback = 228 getFallbackColor(SMILUtil.getComputedStyleSimple(gCircle, "fill")); 229 is(fallback, "rgb(255, 0, 0)", 230 "Checking animated fallback fill=currentColor after animating"); 231 232 gCircle.setAttribute("color", "lime"); // Change: currentColor=lime 233 gSvg.setCurrentTime(0); 234 fallback = getFallbackColor(SMILUtil.getComputedStyleSimple(gCircle, "fill")); 235 is(fallback, "rgb(0, 255, 0)", 236 "Checking animated fallback fill=currentColor after updating context"); 237 238 gCircle.removeAttribute("style"); 239 gCircle.firstChild.remove(); 240 } 241 242 function testInheritChange() 243 { 244 setupTest(); 245 gCircleParent.setAttribute("fill", "red"); // At first: inherit=red 246 var anim = createAnimSetTo("fill", "inherit"); 247 248 gSvg.setCurrentTime(0); 249 is(SMILUtil.getComputedStyleSimple(gCircle, "fill"), "rgb(255, 0, 0)", 250 "Checking animated fill=inherit after animating"); 251 252 gCircleParent.setAttribute("fill", "lime"); // Change: inherit=lime 253 gSvg.setCurrentTime(0); 254 is(SMILUtil.getComputedStyleSimple(gCircle, "fill"), "rgb(0, 255, 0)", 255 "Checking animated fill=inherit after updating context"); 256 257 gCircleParent.removeAttribute("fill"); 258 gCircle.firstChild.remove(); 259 } 260 261 function testInheritChangeUsingStyle() 262 { 263 setupTest(); 264 gCircleParent.setAttribute("style", "fill: red"); // At first: inherit=red 265 var anim = createAnimSetTo("fill", "inherit"); 266 267 gSvg.setCurrentTime(0); 268 is(SMILUtil.getComputedStyleSimple(gCircle, "fill"), "rgb(255, 0, 0)", 269 "Checking animated fill=inherit after animating (using style attr)"); 270 271 gCircleParent.setAttribute("style", "fill: lime"); // Change: inherit=lime 272 gSvg.setCurrentTime(0); 273 is(SMILUtil.getComputedStyleSimple(gCircle, "fill"), "rgb(0, 255, 0)", 274 "Checking animated fill=inherit after updating context " 275 + "(using style attr)"); 276 277 gCircleParent.removeAttribute("style"); 278 gCircle.firstChild.remove(); 279 } 280 281 function testEmUnitChangeOnProp() 282 { 283 setupTest(); 284 gCircleParent.setAttribute("font-size", "10px"); // At first: font-size: 10px 285 var anim = createAnimSetTo("font-size", "2em"); 286 287 gSvg.setCurrentTime(0); 288 is(SMILUtil.getComputedStyleSimple(gCircle, "font-size"), "20px", 289 "Checking animated font-size=2em after animating ends"); 290 291 gCircleParent.setAttribute("font-size", "20px"); // Change: font-size: 20px 292 gSvg.setCurrentTime(0); 293 is(SMILUtil.getComputedStyleSimple(gCircle, "font-size"), "40px", 294 "Checking animated font-size=2em after updating context"); 295 296 gCircleParent.removeAttribute("font-size"); 297 gCircle.firstChild.remove(); 298 } 299 300 function testEmUnitChangeOnPropBase() 301 { 302 // Test the case where the base value for our animation sandwich is 303 // context-sensitive. 304 // Currently, this is taken care of by the compositor which keeps a cached 305 // base value and compares it with the current base value. This test then just 306 // serves as a regression test in case the compositor's behaviour changes. 307 setupTest(); 308 gSvg.setAttribute("font-size", "10px"); // At first: font-size: 10px 309 gCircleParent.setAttribute("font-size", "1em"); // Base: 10px 310 var anim = createAnimBy("font-size", "10px"); 311 312 gSvg.setCurrentTime(TIME_AFTER_ANIM_END); 313 is(SMILUtil.getComputedStyleSimple(gCircle, "font-size"), "20px", 314 "Checking animated font-size=20px after anim ends"); 315 316 gSvg.setAttribute("font-size", "20px"); // Change: font-size: 20px 317 gSvg.setCurrentTime(TIME_AFTER_ANIM_END); 318 is(SMILUtil.getComputedStyleSimple(gCircle, "font-size"), "30px", 319 "Checking animated font-size=30px after updating context"); 320 321 gCircleParent.removeAttribute("font-size"); 322 gCircle.firstChild.remove(); 323 } 324 325 function testEmUnitChangeOnLength() 326 { 327 setupTest(); 328 gCircleParent.setAttribute("font-size", "10px"); // At first: font-size: 10px 329 var anim = createAnimSetTo("cx", "2em"); 330 331 gSvg.setCurrentTime(0); 332 is(gCircle.cx.animVal.value, 20, 333 "Checking animated length=2em after animating"); 334 335 gCircleParent.setAttribute("font-size", "20px"); // Change: font-size: 20px 336 // Bug 508206: We should really detect this change and update immediately but 337 // currently we don't until we get sampled again 338 todo_is(gCircle.cx.animVal.value, 40, 339 "Checking animated length=2em after updating context but before sampling"); 340 341 gSvg.setCurrentTime(0); 342 is(gCircle.cx.animVal.value, 40, 343 "Checking animated length=2em after updating context and after " + 344 "resampling"); 345 346 gCircleParent.removeAttribute("font-size"); 347 gCircle.firstChild.remove(); 348 } 349 350 function testPercentUnitChangeOnProp() 351 { 352 setupTest(); 353 gCircleParent.setAttribute("font-size", "10px"); // At first: font-size: 10px 354 var anim = createAnimSetTo("font-size", "150%"); 355 356 gSvg.setCurrentTime(0); 357 is(SMILUtil.getComputedStyleSimple(gCircle, "font-size"), "15px", 358 "Checking animated font-size=150% after animating"); 359 360 gCircleParent.setAttribute("font-size", "20px"); // Change: font-size: 20px 361 gSvg.setCurrentTime(0); 362 is(SMILUtil.getComputedStyleSimple(gCircle, "font-size"), "30px", 363 "Checking animated font-size=150% after updating context"); 364 365 gCircleParent.removeAttribute("font-size"); 366 gCircle.firstChild.remove(); 367 } 368 369 function testPercentUnitChangeOnLength() 370 { 371 setupTest(); 372 var oldHeight = gSvg.getAttribute("height"); 373 gSvg.setAttribute("height", "100px"); // At first: viewport height: 100px 374 var anim = createAnimSetTo("cy", "100%"); 375 376 gSvg.setCurrentTime(0); // Force synchronous sample so animation takes effect 377 // Due to bug 627594 (SVGLength.value for percent value lengths doesn't 378 // reflect updated viewport until reflow) the following will fail. 379 // Check that it does indeed fail so that when that bug is fixed this test 380 // can be updated. 381 todo_is(gCircle.cy.animVal.value, 100, 382 "Checking animated length=100% after animating but before reflow"); 383 // force a layout flush (Bug 627594) 384 gSvg.getCTM(); 385 // Even after doing a reflow though we'll still fail due to bug 508206 386 // (Relative units used in animation don't update immediately) 387 todo_is(gCircle.cy.animVal.value, 100, 388 "Checking animated length=100% after animating but before resampling"); 389 gSvg.setCurrentTime(0); 390 // Now we should be up to date 391 is(gCircle.cy.animVal.value, 100, 392 "Checking animated length=100% after animating"); 393 394 gSvg.setAttribute("height", "50px"); // Change: height: 50px 395 // force a layout flush (Bug 627594) 396 gSvg.getCTM(); 397 gSvg.setCurrentTime(0); // Bug 508206 398 is(gCircle.cy.animVal.value, 50, 399 "Checking animated length=100% after updating context"); 400 401 gSvg.setAttribute("height", oldHeight); 402 gCircle.firstChild.remove(); 403 } 404 405 function testRelativeFontSize() 406 { 407 setupTest(); 408 gCircleParent.setAttribute("font-size", "10px"); // At first: font-size: 10px 409 var anim = createAnimSetTo("font-size", "larger"); 410 411 gSvg.setCurrentTime(0); 412 var fsize = parseInt(SMILUtil.getComputedStyleSimple(gCircle, "font-size")); 413 // CSS 2 suggests a scaling factor of 1.2 so we should be looking at something 414 // around about 12 or so 415 ok(fsize > 10 && fsize < 20, 416 "Checking animated font-size > 10px after animating"); 417 418 gCircleParent.setAttribute("font-size", "20px"); // Change: font-size: 20px 419 gSvg.setCurrentTime(0); 420 fsize = parseInt(SMILUtil.getComputedStyleSimple(gCircle, "font-size")); 421 ok(fsize > 20, "Checking animated font-size > 20px after updating context"); 422 423 gCircleParent.removeAttribute("font-size"); 424 gCircle.firstChild.remove(); 425 } 426 427 function testRelativeFontWeight() 428 { 429 setupTest(); 430 gCircleParent.setAttribute("font-weight", "100"); // At first: font-weight 100 431 var anim = createAnimSetTo("font-weight", "bolder"); 432 // CSS 2: 'bolder': Specifies the next weight that is assigned to a font 433 // that is darker than the inherited one. If there is no such weight, it 434 // simply results in the next darker numerical value (and the font remains 435 // unchanged), unless the inherited value was '900', in which case the 436 // resulting weight is also '900'. 437 438 gSvg.setCurrentTime(0); 439 var weight = 440 parseInt(SMILUtil.getComputedStyleSimple(gCircle, "font-weight")); 441 ok(weight > 100, "Checking animated font-weight > 100 after animating"); 442 443 gCircleParent.setAttribute("font-weight", "800"); // Change: font-weight 800 444 gSvg.setCurrentTime(0); 445 weight = parseInt(SMILUtil.getComputedStyleSimple(gCircle, "font-weight")); 446 is(weight, 900, 447 "Checking animated font-weight = 900 after updating context"); 448 449 gCircleParent.removeAttribute("font-weight"); 450 gCircle.firstChild.remove(); 451 } 452 453 function testRelativeFont() 454 { 455 // Test a relative font-size as part of a 'font' spec since the code path 456 // is different in this case 457 // It turns out that, due to the way we store shorthand font properties, we 458 // don't need to worry about marking such values as context-sensitive since we 459 // seem to store them in their relative form. If, however, we change the way 460 // we store shorthand font properties in the future, this will serve as 461 // a useful regression test. 462 setupTest(); 463 gCircleParent.setAttribute("font-size", "10px"); // At first: font-size: 10px 464 // We must be sure to set every part of the shorthand property to some 465 // non-context sensitive value because we want to test that even if only the 466 // font-size is relative we will update it appropriately. 467 var anim = 468 createAnimSetTo("font", "normal normal bold larger/normal sans-serif"); 469 470 gSvg.setCurrentTime(0); 471 var fsize = parseInt(SMILUtil.getComputedStyleSimple(gCircle, "font-size")); 472 ok(fsize > 10 && fsize < 20, 473 "Checking size of shorthand 'font' > 10px after animating"); 474 475 gCircleParent.setAttribute("font-size", "20px"); // Change: font-size: 20px 476 gSvg.setCurrentTime(0); 477 fsize = parseInt(SMILUtil.getComputedStyleSimple(gCircle, "font-size")); 478 ok(fsize > 20, 479 "Checking size of shorthand 'font' > 20px after updating context"); 480 481 gCircleParent.removeAttribute("font-size"); 482 gCircle.firstChild.remove(); 483 } 484 485 function testCalcFontSize() 486 { 487 setupTest(); 488 gCircleParent.setAttribute("font-size", "10px"); // At first: font-size: 10px 489 var anim = createAnimSetTo("font-size", "calc(110% + 0.1em)"); 490 491 gSvg.setCurrentTime(0); 492 var fsize = parseInt(SMILUtil.getComputedStyleSimple(gCircle, "font-size")); 493 // Font size should be 1.1 * 10px + 0.1 * 10px = 12 494 is(fsize, 12, "Checking animated calc font-size == 12px after animating"); 495 496 gCircleParent.setAttribute("font-size", "20px"); // Change: font-size: 20px 497 gSvg.setCurrentTime(0); 498 fsize = parseInt(SMILUtil.getComputedStyleSimple(gCircle, "font-size")); 499 is(fsize, 24, "Checking animated calc font-size == 24px after updating " + 500 "context"); 501 502 gCircleParent.removeAttribute("font-size"); 503 gCircle.firstChild.remove(); 504 } 505 506 function testDashArray() 507 { 508 // stroke dasharrays don't currently convert units--but if someone ever fixes 509 // that, hopefully this test will fail and remind us not to cache percentage 510 // values in that case 511 setupTest(); 512 var oldHeight = gSvg.getAttribute("height"); 513 var oldWidth = gSvg.getAttribute("width"); 514 gSvg.setAttribute("height", "100px"); // At first: viewport: 100x100px 515 gSvg.setAttribute("width", "100px"); 516 var anim = createAnimFromTo("stroke-dasharray", "0 5", "0 50%"); 517 518 gSvg.setCurrentTime(TIME_AFTER_ANIM_END); 519 520 // Now we should be up to date 521 is(SMILUtil.getComputedStyleSimple(gCircle, "stroke-dasharray"), "0, 50%", 522 "Checking animated stroke-dasharray after animating"); 523 524 gSvg.setAttribute("height", "50px"); // Change viewport: 50x50px 525 gSvg.setAttribute("width", "50px"); 526 gSvg.setCurrentTime(TIME_AFTER_ANIM_END); 527 is(SMILUtil.getComputedStyleSimple(gCircle, "stroke-dasharray"), "0, 50%", 528 "Checking animated stroke-dasharray after updating context"); 529 530 gSvg.setAttribute("height", oldHeight); 531 gSvg.setAttribute("width", oldWidth); 532 gCircle.firstChild.remove(); 533 } 534 535 function testClip() 536 { 537 setupTest(); 538 gCircleParent.setAttribute("font-size", "20px"); // At first: font-size: 20px 539 540 // The clip property only applies to elements that establish a new 541 // viewport so we need to create a nested svg and add animation to that 542 var nestedSVG = document.createElementNS(SVGNS, "svg"); 543 nestedSVG.setAttribute("clip", "rect(0px 0px 0px 0px)"); 544 gCircleParent.appendChild(nestedSVG); 545 546 var anim = createAnimSetTo("clip", "rect(1em 1em 1em 1em)"); 547 // createAnimSetTo will make the animation a child of gCircle so we need to 548 // move it so it targets nestedSVG instead 549 nestedSVG.appendChild(anim); 550 551 gSvg.setCurrentTime(TIME_AFTER_ANIM_END); 552 is(SMILUtil.getComputedStyleSimple(nestedSVG, "clip"), 553 "rect(20px, 20px, 20px, 20px)", 554 "Checking animated clip rect after animating"); 555 556 gCircleParent.setAttribute("font-size", "10px"); // Change: font-size: 10px 557 gSvg.setCurrentTime(TIME_AFTER_ANIM_END); 558 is(SMILUtil.getComputedStyleSimple(nestedSVG, "clip"), 559 "rect(10px, 10px, 10px, 10px)", 560 "Checking animated clip rect after updating context"); 561 562 gCircleParent.removeAttribute("font-size"); 563 gCircleParent.removeChild(nestedSVG); 564 } 565 566 window.addEventListener("load", main); 567 ]]> 568 </script> 569 </pre> 570 </body> 571 </html>