Bug 555026: In SMIL animation, ignore keyTimes attr when in paced calcMode. r=roc
authorDaniel Holbert <dholbert@cs.stanford.edu>
Tue, 06 Apr 2010 14:39:29 -0700
changeset 40515 5af7d9cc40973d405ef1f358134dc29ce3001bd1
parent 40514 40038cc9f245f07b2cc6b1bd42b39aecdcf1b2a2
child 40516 ed5afed89c4948ae4ee142be7851eef4a2860f74
push id12640
push userdholbert@mozilla.com
push dateTue, 06 Apr 2010 21:40:01 +0000
treeherdermozilla-central@5af7d9cc4097 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs555026
milestone1.9.3a4pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
Bug 555026: In SMIL animation, ignore keyTimes attr when in paced calcMode. r=roc
content/smil/crashtests/555026-1.svg
content/smil/crashtests/crashtests.list
content/smil/nsSMILAnimationFunction.cpp
content/smil/test/Makefile.in
content/smil/test/test_smilKeyTimesPacedMode.xhtml
new file mode 100644
--- /dev/null
+++ b/content/smil/crashtests/555026-1.svg
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+<svg xmlns="http://www.w3.org/2000/svg"
+     class="reftest-wait"
+     onload="go()">
+  <script>
+    function go() {
+      // setCurrentTime to force a sample
+      document.documentElement.setCurrentTime(1);
+      document.documentElement.removeAttribute("class");
+    }
+  </script>
+  <rect id="myRect" fill="blue" height="40" width="40">
+    <!-- The "keyTimes" values below are invalid, but they should be ignored
+         (and definitely shouldn't trigger any assertion failures) since we're
+         in paced calcMode. -->
+    <animate attributeName="x" by="50" calcMode="paced" dur="2s"
+             keyTimes="0; -1"/>
+    <animate attributeName="x" by="50" calcMode="paced" dur="2s"
+             keyTimes=""/>
+    <animate attributeName="x" by="50" calcMode="paced" dur="2s"
+             keyTimes="abc"/>
+    <animate attributeName="x" by="50" calcMode="paced" dur="2s"
+             keyTimes="5"/>
+  </rect>
+</svg>
--- a/content/smil/crashtests/crashtests.list
+++ b/content/smil/crashtests/crashtests.list
@@ -4,9 +4,10 @@ load 523188-1.svg
 load 525099-1.svg
 load 526536-1.svg
 load 526875-1.svg
 load 526875-2.svg
 load 529387-1.xhtml
 load 537157-1.svg
 load 547333-1.svg
 load 548899-1.svg
+load 555026-1.svg
 load 556841-1.svg
--- a/content/smil/nsSMILAnimationFunction.cpp
+++ b/content/smil/nsSMILAnimationFunction.cpp
@@ -407,56 +407,60 @@ nsSMILAnimationFunction::InterpolateResu
   double fTime = double(mSampleTime);
   double fDur = double(dur);
 
   // Get the normalised progress through the simple duration
   double simpleProgress = (fDur > 0.0) ? fTime / fDur : 0.0;
 
   // Handle bad keytimes (where first != 0 and/or last != 1)
   // See http://brian.sol1.net/svg/range-for-keytimes for more info.
-  if (HasAttr(nsGkAtoms::keyTimes)) {
+  if (HasAttr(nsGkAtoms::keyTimes) &&
+      GetCalcMode() != CALC_PACED) {
     double first = mKeyTimes[0];
     if (first > 0.0 && simpleProgress < first) {
       if (!IsToAnimation())
         aResult = aValues[0];
       return rv;
     }
     double last = mKeyTimes[mKeyTimes.Length() - 1];
     if (last < 1.0 && simpleProgress >= last) {
       if (IsToAnimation())
         aResult = aValues[0];
       else
         aResult = aValues[aValues.Length() - 1];
       return rv;
     }
   }
 
-  ScaleSimpleProgress(simpleProgress);
-
   if (GetCalcMode() != CALC_DISCRETE) {
     // Get the normalised progress between adjacent values
     const nsSMILValue* from = nsnull;
     const nsSMILValue* to = nsnull;
     double intervalProgress;
     if (IsToAnimation()) {
-      // Note: Don't need to do any special-casing for CALC_PACED here,
-      // because To-Animation doesn't use a values list, by definition.
       from = &aBaseValue;
       to = &aValues[0];
-      intervalProgress = simpleProgress;
-      ScaleIntervalProgress(intervalProgress, 0, 1);
+      if (GetCalcMode() == CALC_PACED) {
+        // Note: key[Times/Splines/Points] are ignored for calcMode="paced"
+        intervalProgress = simpleProgress;
+      } else {
+        ScaleSimpleProgress(simpleProgress);
+        intervalProgress = simpleProgress;
+        ScaleIntervalProgress(intervalProgress, 0, 1);
+      }
     } else {
       if (GetCalcMode() == CALC_PACED) {
         rv = ComputePacedPosition(aValues, simpleProgress,
                                   intervalProgress, from, to);
         // Note: If the above call fails, we'll skip the "from->Interpolate"
         // call below, and we'll drop into the CALC_DISCRETE section
         // instead. (as the spec says we should, because our failure was
         // presumably due to the values being non-additive)
       } else { // GetCalcMode() == CALC_LINEAR or GetCalcMode() == CALC_SPLINE
+        ScaleSimpleProgress(simpleProgress);
         PRUint32 index = (PRUint32)floor(simpleProgress *
                                          (aValues.Length() - 1));
         from = &aValues[index];
         to = &aValues[index + 1];
         intervalProgress = simpleProgress * (aValues.Length() - 1) - index;
         ScaleIntervalProgress(intervalProgress, index, aValues.Length() - 1);
       }
     }
--- a/content/smil/test/Makefile.in
+++ b/content/smil/test/Makefile.in
@@ -65,16 +65,17 @@ include $(topsrcdir)/config/rules.mk
 	  test_smilMappedAttrFromBy.xhtml \
 	  test_smilMappedAttrPaced.xhtml \
 	  test_smilReset.xhtml \
 	  test_smilRestart.xhtml \
 	  test_smilFillMode.xhtml \
 	  test_smilGetStartTime.xhtml \
 	  test_smilGetSimpleDuration.xhtml \
 	  test_smilKeySplines.xhtml \
+	  test_smilKeyTimesPacedMode.xhtml \
 	  test_smilSetCurrentTime.xhtml \
 	  test_smilSync.xhtml \
 	  test_smilSyncbaseTarget.xhtml \
 	  test_smilSyncTransform.xhtml \
 	  test_smilTextZoom.xhtml \
 	  test_smilTiming.xhtml \
 	  test_smilTimingZeroIntervals.xhtml \
 	  test_smilUpdatedInterval.xhtml \
new file mode 100644
--- /dev/null
+++ b/content/smil/test/test_smilKeyTimesPacedMode.xhtml
@@ -0,0 +1,124 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+  <title>Tests updated intervals</title>
+  <script type="text/javascript" src="/MochiKit/packed.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=555026">Mozilla Bug 555026</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<svg id="svg" xmlns="http://www.w3.org/2000/svg" width="120px" height="120px"
+     onload="this.pauseAnimations()">
+  <circle r="10" id="circle"/>
+</svg>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+/** Test that we ignore keyTimes attr when calcMode="paced" **/
+
+/* Global Variables */
+const SVGNS = "http://www.w3.org/2000/svg";
+const ANIM_DUR = "2s";
+const HALF_TIME = "1";
+const ATTR_NAME = "cx"
+const KEYTIMES_TO_TEST = [
+  // potentially-valid values (depending on number of values in animation)
+  "0; 0.2; 1",
+  "0; 0.5",
+  "0; 1",
+  // invalid values:
+  "", "abc", "-0.5", "0; 0.5; 1.01", "5"
+];
+const gSvg = document.getElementById("svg");
+const gCircle = document.getElementById("circle");
+
+SimpleTest.waitForExplicitFinish();
+
+
+// MAIN FUNCTIONS
+function main() {
+  ok(gSvg.animationsPaused(), "should be paused by <svg> load handler");
+  is(gSvg.getCurrentTime(), 0, "should be paused at 0 in <svg> load handler");
+
+  testByAnimation();
+  testToAnimation();
+  testValuesAnimation();
+  SimpleTest.finish();
+}
+
+function testByAnimation() {
+  for (var i = 0; i < KEYTIMES_TO_TEST.length; i++) {
+    setupTest();
+    var anim = createAnim();
+    anim.setAttribute("by", "200");
+    var curKeyTimes = KEYTIMES_TO_TEST[i];
+    anim.setAttribute("keyTimes", curKeyTimes);
+
+    gSvg.setCurrentTime(HALF_TIME);
+    is(gCircle.cx.animVal.value, 100,
+       "Checking animVal with 'by' and keyTimes='" + curKeyTimes + "'");
+
+    removeElement(anim); // clean up
+  }
+}
+
+function testToAnimation() {
+  for (var i = 0; i < KEYTIMES_TO_TEST.length; i++) {
+    setupTest();
+    var anim = createAnim();
+    anim.setAttribute("to", "200");
+    var curKeyTimes = KEYTIMES_TO_TEST[i];
+    anim.setAttribute("keyTimes", curKeyTimes);
+
+    gSvg.setCurrentTime(HALF_TIME);
+    is(gCircle.cx.animVal.value, 100,
+       "Checking animVal with 'to' and keyTimes='" + curKeyTimes + "'");
+
+    removeElement(anim); // clean up
+  }
+}
+
+function testValuesAnimation() {
+  for (var i = 0; i < KEYTIMES_TO_TEST.length; i++) {
+    setupTest();
+    var anim = createAnim();
+    anim.setAttribute("values", "100; 110; 200");
+    var curKeyTimes = KEYTIMES_TO_TEST[i];
+    anim.setAttribute("keyTimes", curKeyTimes);
+
+    gSvg.setCurrentTime(HALF_TIME);
+    is(gCircle.cx.animVal.value, 150,
+       "Checking animVal with 'values' and keyTimes='" + curKeyTimes + "'");
+
+    removeElement(anim); // clean up
+  }
+}
+
+// HELPER FUNCTIONS
+// Common setup code for each test function: seek to 0, and make sure
+// the previous test cleaned up its animations.
+function setupTest() {
+  gSvg.setCurrentTime(0);
+  if (gCircle.firstChild) {
+    ok(false, "Previous test didn't clean up after itself.");
+  }
+}
+
+function createAnim() {
+  var anim = document.createElementNS(SVGNS,"animate");
+  anim.setAttribute("attributeName", ATTR_NAME);
+  anim.setAttribute("dur", ANIM_DUR);
+  anim.setAttribute("begin", "0s");
+  anim.setAttribute("calcMode", "paced");
+  return gCircle.appendChild(anim);
+}
+
+window.addEventListener("load", main, false);
+]]>
+</script>
+</pre>
+</body>
+</html>