Bug 533291 Patch E: Add another outparam to nsISMILAttr::ValueFromString, to let us know whether we can re-use the parsed result in the future. r=roc
authorDaniel Holbert <dholbert@cs.stanford.edu>
Sat, 20 Feb 2010 13:13:11 -0800
changeset 38352 3aef13f285d42ffd807f550e8d8d5ba5f01402d3
parent 38351 26d2ef4bf7bf354c7a03f136a717da51b3b71958
child 38353 9cdfc9ee6754c5861e5c37ad0ca90766400a8510
push idunknown
push userunknown
push dateunknown
reviewersroc
bugs533291
milestone1.9.3a2pre
Bug 533291 Patch E: Add another outparam to nsISMILAttr::ValueFromString, to let us know whether we can re-use the parsed result in the future. r=roc
content/smil/nsISMILAttr.h
content/smil/nsSMILAnimationFunction.cpp
content/smil/nsSMILAnimationFunction.h
content/smil/nsSMILCSSProperty.cpp
content/smil/nsSMILCSSProperty.h
content/smil/nsSMILParserUtils.cpp
content/smil/nsSMILParserUtils.h
content/svg/content/src/nsSVGAngle.cpp
content/svg/content/src/nsSVGAngle.h
content/svg/content/src/nsSVGBoolean.cpp
content/svg/content/src/nsSVGBoolean.h
content/svg/content/src/nsSVGEnum.cpp
content/svg/content/src/nsSVGEnum.h
content/svg/content/src/nsSVGInteger.cpp
content/svg/content/src/nsSVGInteger.h
content/svg/content/src/nsSVGLength2.cpp
content/svg/content/src/nsSVGLength2.h
content/svg/content/src/nsSVGNumber2.cpp
content/svg/content/src/nsSVGNumber2.h
content/svg/content/src/nsSVGPreserveAspectRatio.cpp
content/svg/content/src/nsSVGPreserveAspectRatio.h
content/svg/content/src/nsSVGTransformSMILAttr.cpp
content/svg/content/src/nsSVGTransformSMILAttr.h
content/svg/content/src/nsSVGViewBox.cpp
content/svg/content/src/nsSVGViewBox.h
--- a/content/smil/nsISMILAttr.h
+++ b/content/smil/nsISMILAttr.h
@@ -62,22 +62,27 @@ public:
    * such as em-based units can be resolved into a canonical form suitable for
    * animation (including interpolation etc.).
    *
    * @param aStr        A string defining the new value to be created.
    * @param aSrcElement The source animation element. This may be needed to
    *                    provided additional context data such as for
    *                    animateTransform where the 'type' attribute is needed to
    *                    parse the value.
-   * @param aValue      Outparam for storing the parsed value.
+   * @param[out] aValue Outparam for storing the parsed value.
+   * @param[out] aCanCache Outparam for indicating whether the parsed value
+   *                       can be reused in future samples -- i.e. whether the
+   *                       given string is always guaranteed to compute
+   *                       to the same nsSMILValue.
    * @return NS_OK on success or an error code if creation failed.
    */
   virtual nsresult ValueFromString(const nsAString& aStr,
                                    const nsISMILAnimationElement* aSrcElement,
-                                   nsSMILValue& aValue) const = 0;
+                                   nsSMILValue& aValue,
+                                   PRBool& aCanCache) const = 0;
 
   /**
    * Gets the underlying value of this attribute.
    *
    * @return an nsSMILValue object. returned_object.IsNull() will be true if an
    * error occurred.
    */
   virtual nsSMILValue GetBaseValue() const = 0;
--- a/content/smil/nsSMILAnimationFunction.cpp
+++ b/content/smil/nsSMILAnimationFunction.cpp
@@ -99,16 +99,17 @@ nsAttrValue::EnumTable nsSMILAnimationFu
 
 nsSMILAnimationFunction::nsSMILAnimationFunction()
   : mIsActive(PR_FALSE),
     mIsFrozen(PR_FALSE),
     mSampleTime(-1),
     mRepeatIteration(0),
     mLastValue(PR_FALSE),
     mHasChanged(PR_TRUE),
+    mValueNeedsReparsingEverySample(PR_FALSE),
     mBeginTime(LL_MININT),
     mAnimationElement(nsnull),
     mErrorFlags(0)
 {
 }
 
 void
 nsSMILAnimationFunction::SetAnimationElement(
@@ -353,17 +354,17 @@ nsSMILAnimationFunction::WillReplace() c
    */
   return !mErrorFlags && (!(IsAdditive() || IsToAnimation()) ||
                           (IsToAnimation() && mIsFrozen && !mHasChanged));
 }
 
 PRBool
 nsSMILAnimationFunction::HasChanged() const
 {
-  return mHasChanged;
+  return mHasChanged || mValueNeedsReparsingEverySample;
 }
 
 PRBool
 nsSMILAnimationFunction::UpdateCachedTarget(const nsSMILTargetIdentifier& aNewTarget)
 {
   if (!mLastTarget.Equals(aNewTarget)) {
     mLastTarget = aNewTarget;
     return PR_TRUE;
@@ -679,33 +680,43 @@ nsSMILAnimationFunction::GetAttr(nsIAtom
 {
   return mAnimationElement->GetAnimAttr(aAttName, aResult);
 }
 
 /*
  * A utility function to make querying an attribute that corresponds to an
  * nsSMILValue a little neater.
  *
- * @param aAttName    The attribute name (in the global namespace)
- * @param aSMILAttr   The SMIL attribute to perform the parsing
- * @param aResult     The resulting nsSMILValue
+ * @param aAttName    The attribute name (in the global namespace).
+ * @param aSMILAttr   The SMIL attribute to perform the parsing.
+ * @param[out] aResult        The resulting nsSMILValue.
+ * @param[out] aCanCacheSoFar If |aResult| cannot be cached (as reported by
+ *                            nsISMILAttr::ValueFromString), then this outparam
+ *                            will be set to PR_FALSE. Otherwise, this outparam
+ *                            won't be modified.
  *
  * Returns PR_FALSE if a parse error occurred, otherwise returns PR_TRUE.
  */
 PRBool
 nsSMILAnimationFunction::ParseAttr(nsIAtom* aAttName,
                                    const nsISMILAttr& aSMILAttr,
-                                   nsSMILValue& aResult) const
+                                   nsSMILValue& aResult,
+                                   PRBool& aCanCacheSoFar) const
 {
   nsAutoString attValue;
   if (GetAttr(aAttName, attValue)) {
-    nsresult rv =
-      aSMILAttr.ValueFromString(attValue, mAnimationElement, aResult);
+    PRBool canCache;
+    nsresult rv = aSMILAttr.ValueFromString(attValue, mAnimationElement,
+                                            aResult, canCache);
     if (NS_FAILED(rv))
       return PR_FALSE;
+
+    if (!canCache) {
+      aCanCacheSoFar = PR_FALSE;
+    }
   }
   return PR_TRUE;
 }
 
 /*
  * SMILANIM specifies the following rules for animation function values:
  *
  * (1) if values is set, it overrides everything
@@ -721,35 +732,44 @@ nsSMILAnimationFunction::ParseAttr(nsIAt
  */
 nsresult
 nsSMILAnimationFunction::GetValues(const nsISMILAttr& aSMILAttr,
                                    nsSMILValueArray& aResult)
 {
   if (!mAnimationElement)
     return NS_ERROR_FAILURE;
 
+  mValueNeedsReparsingEverySample = PR_FALSE;
   nsSMILValueArray result;
 
   // If "values" is set, use it
   if (HasAttr(nsGkAtoms::values)) {
     nsAutoString attValue;
     GetAttr(nsGkAtoms::values, attValue);
+    PRBool canCache;
     nsresult rv = nsSMILParserUtils::ParseValues(attValue, mAnimationElement,
-                                                 aSMILAttr, result);
+                                                 aSMILAttr, result, canCache);
     if (NS_FAILED(rv))
       return rv;
 
+    if (!canCache) {
+      mValueNeedsReparsingEverySample = PR_TRUE;
+    }
   // Else try to/from/by
   } else {
-
+    PRBool canCacheSoFar = PR_TRUE;
     PRBool parseOk = PR_TRUE;
     nsSMILValue to, from, by;
-    parseOk &= ParseAttr(nsGkAtoms::to,   aSMILAttr, to);
-    parseOk &= ParseAttr(nsGkAtoms::from, aSMILAttr, from);
-    parseOk &= ParseAttr(nsGkAtoms::by,   aSMILAttr, by);
+    parseOk &= ParseAttr(nsGkAtoms::to,   aSMILAttr, to,   canCacheSoFar);
+    parseOk &= ParseAttr(nsGkAtoms::from, aSMILAttr, from, canCacheSoFar);
+    parseOk &= ParseAttr(nsGkAtoms::by,   aSMILAttr, by,   canCacheSoFar);
+    
+    if (!canCacheSoFar) {
+      mValueNeedsReparsingEverySample = PR_TRUE;
+    }
 
     if (!parseOk)
       return NS_ERROR_FAILURE;
 
     result.SetCapacity(2);
     if (!to.IsNull()) {
       if (!from.IsNull()) {
         result.AppendElement(from);
--- a/content/smil/nsSMILAnimationFunction.h
+++ b/content/smil/nsSMILAnimationFunction.h
@@ -316,17 +316,17 @@ protected:
   // Convenience attribute getters -- use these instead of querying
   // mAnimationElement as these may need to be overridden by subclasses
   virtual PRBool             HasAttr(nsIAtom* aAttName) const;
   virtual const nsAttrValue* GetAttr(nsIAtom* aAttName) const;
   virtual PRBool             GetAttr(nsIAtom* aAttName,
                                      nsAString& aResult) const;
 
   PRBool   ParseAttr(nsIAtom* aAttName, const nsISMILAttr& aSMILAttr,
-                     nsSMILValue& aResult) const;
+                     nsSMILValue& aResult, PRBool& aCanCacheSoFar) const;
   nsresult GetValues(const nsISMILAttr& aSMILAttr,
                      nsSMILValueArray& aResult);
   void     UpdateValuesArray();
   void     CheckKeyTimes(PRUint32 aNumValues);
   void     CheckKeySplines(PRUint32 aNumValues);
 
   inline PRBool IsToAnimation() const {
     return !HasAttr(nsGkAtoms::values) &&
@@ -367,16 +367,17 @@ protected:
   // instructed by the compositor. This allows us to apply the result directly
   // to the animation value and allows the compositor to filter out functions
   // that it determines will not contribute to the final result.
   nsSMILTime                    mSampleTime; // sample time within simple dur
   nsSMILTimeValue               mSimpleDuration;
   PRUint32                      mRepeatIteration;
   PRPackedBool                  mLastValue;
   PRPackedBool                  mHasChanged;
+  PRPackedBool                  mValueNeedsReparsingEverySample;
 
   nsSMILTime                    mBeginTime; // document time
 
   // The owning animation element. This is used for sorting based on document
   // position and for fetching attribute values stored in the element.
   // Raw pointer is OK here, because this nsSMILAnimationFunction can't outlive
   // its owning animation element.
   nsISMILAnimationElement*      mAnimationElement;
--- a/content/smil/nsSMILCSSProperty.cpp
+++ b/content/smil/nsSMILCSSProperty.cpp
@@ -141,22 +141,36 @@ nsSMILCSSProperty::GetBaseValue() const
                                         computedStyleVal, baseValue);
   }
   return baseValue;
 }
 
 nsresult
 nsSMILCSSProperty::ValueFromString(const nsAString& aStr,
                                    const nsISMILAnimationElement* aSrcElement,
-                                   nsSMILValue& aValue) const
+                                   nsSMILValue& aValue,
+                                   PRBool& aCanCache) const
 {
   NS_ENSURE_TRUE(IsPropertyAnimatable(mPropID), NS_ERROR_FAILURE);
 
   nsSMILCSSValueType::ValueFromString(mPropID, mElement, aStr, aValue);
-  return aValue.IsNull() ? NS_ERROR_FAILURE : NS_OK;
+  if (aValue.IsNull()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // XXXdholbert: For simplicity, just assume that all CSS values have to
+  // reparsed every sample. This prevents us from doing the "nothing's changed
+  // so don't recompose" optimization (bug 533291) for CSS properties & mapped
+  // attributes.  If it ends up being expensive to always recompose those, we
+  // can be a little smarter here.  We really only need to disable aCanCache
+  // for "inherit" & "currentColor" (whose values could change at any time), as
+  // well as for length-valued types (particularly those with em/ex/percent
+  // units, since their conversion ratios can change at any time).
+  aCanCache = PR_FALSE;
+  return NS_OK;
 }
 
 nsresult
 nsSMILCSSProperty::SetAnimValue(const nsSMILValue& aValue)
 {
   NS_ENSURE_TRUE(IsPropertyAnimatable(mPropID), NS_ERROR_FAILURE);
 
   // Convert nsSMILValue to string
--- a/content/smil/nsSMILCSSProperty.h
+++ b/content/smil/nsSMILCSSProperty.h
@@ -61,17 +61,18 @@ public:
    * @param  aPropID   The CSS property we're interested in animating.
    * @param  aElement  The element whose CSS property is being animated.
    */
   nsSMILCSSProperty(nsCSSProperty aPropID, nsIContent* aElement);
 
   // nsISMILAttr methods
   virtual nsresult ValueFromString(const nsAString& aStr,
                                    const nsISMILAnimationElement* aSrcElement,
-                                   nsSMILValue& aValue) const;
+                                   nsSMILValue& aValue,
+                                   PRBool& aCanCache) const;
   virtual nsSMILValue GetBaseValue() const;
   virtual nsresult    SetAnimValue(const nsSMILValue& aValue);
   virtual void        ClearAnimValue();
 
   /**
    * Utility method - returns PR_TRUE if the given property is supported for
    * SMIL animation.
    *
--- a/content/smil/nsSMILParserUtils.cpp
+++ b/content/smil/nsSMILParserUtils.cpp
@@ -537,25 +537,29 @@ nsSMILParserUtils::ParseKeyTimes(const n
 
   return rv;
 }
 
 nsresult
 nsSMILParserUtils::ParseValues(const nsAString& aSpec,
                                const nsISMILAnimationElement* aSrcElement,
                                const nsISMILAttr& aAttribute,
-                               nsTArray<nsSMILValue>& aValuesArray)
+                               nsTArray<nsSMILValue>& aValuesArray,
+                               PRBool& aCanCache)
 {
   nsresult rv = NS_ERROR_FAILURE;
 
   const PRUnichar* start = aSpec.BeginReading();
   const PRUnichar* end = aSpec.EndReading();
   const PRUnichar* substrEnd = nsnull;
   const PRUnichar* next = nsnull;
 
+  // Assume all results can be cached, until we find one that can't.
+  aCanCache = PR_TRUE;
+
   while (start != end) {
     rv = NS_ERROR_FAILURE;
 
     SkipBeginWsp(start, end);
 
     if (start == end || *start == ';')
       break;
 
@@ -571,27 +575,30 @@ nsSMILParserUtils::ParseValues(const nsA
       if (next == end)
         break;
     }
 
     while (substrEnd != start && NS_IS_SPACE(*(substrEnd-1)))
       --substrEnd;
 
     nsSMILValue newValue;
+    PRBool tmpCanCache;
     rv = aAttribute.ValueFromString(Substring(start, substrEnd),
-                                    aSrcElement, newValue);
+                                    aSrcElement, newValue, tmpCanCache);
     if (NS_FAILED(rv))
       break;
 
     if (!aValuesArray.AppendElement(newValue)) {
       rv = NS_ERROR_OUT_OF_MEMORY;
       break;
     }
+    if (!tmpCanCache) {
+      aCanCache = PR_FALSE;
+    }
 
-    rv = NS_OK;
     start = next;
   }
 
   return rv;
 }
 
 nsresult
 nsSMILParserUtils::ParseRepeatCount(const nsAString& aSpec,
--- a/content/smil/nsSMILParserUtils.h
+++ b/content/smil/nsSMILParserUtils.h
@@ -61,17 +61,18 @@ public:
                                   nsTArray<double>& aSplineArray);
 
   static nsresult ParseKeyTimes(const nsAString& aSpec,
                                 nsTArray<double>& aTimesArray);
 
   static nsresult ParseValues(const nsAString& aSpec,
                               const nsISMILAnimationElement* aSrcElement,
                               const nsISMILAttr& aAttribute,
-                              nsTArray<nsSMILValue>& aValuesArray);
+                              nsTArray<nsSMILValue>& aValuesArray,
+                              PRBool& aCanCache);
 
   static nsresult ParseRepeatCount(const nsAString& aSpec,
                                    nsSMILRepeatCount& aResult);
 
   static nsresult ParseTimeValueSpecParams(const nsAString& aSpec,
                                            nsSMILTimeValueSpecParams& aResult);
 
 
--- a/content/svg/content/src/nsSVGAngle.cpp
+++ b/content/svg/content/src/nsSVGAngle.cpp
@@ -431,33 +431,35 @@ nsSVGAngle::ToSMILAttr(nsSVGElement *aSV
   // "orient" is the only animatable <angle>-valued attribute in SVG 1.1).
   NS_NOTREACHED("Trying to animate unknown angle attribute.");
   return nsnull;
 }
 
 nsresult
 nsSVGAngle::SMILOrient::ValueFromString(const nsAString& aStr,
                                         const nsISMILAnimationElement* /*aSrcElement*/,
-                                        nsSMILValue& aValue) const
+                                        nsSMILValue& aValue,
+                                        PRBool& aCanCache) const
 {
   nsSMILValue val(&SVGOrientSMILType::sSingleton);
   if (aStr.EqualsLiteral("auto")) {
     val.mU.mOrient.mOrientType = nsIDOMSVGMarkerElement::SVG_MARKER_ORIENT_AUTO;
   } else {
     float value;
     PRUint16 unitType;
     nsresult rv = GetValueFromString(aStr, &value, &unitType);
     if (NS_FAILED(rv)) {
       return rv;
     }
     val.mU.mOrient.mAngle = value;
     val.mU.mOrient.mUnit = unitType;
     val.mU.mOrient.mOrientType = nsIDOMSVGMarkerElement::SVG_MARKER_ORIENT_ANGLE;
   }
   aValue = val;
+  aCanCache = PR_TRUE;
 
   return NS_OK;
 }
 
 nsSMILValue
 nsSVGAngle::SMILOrient::GetBaseValue() const
 {
   nsSMILValue val(&SVGOrientSMILType::sSingleton);
--- a/content/svg/content/src/nsSVGAngle.h
+++ b/content/svg/content/src/nsSVGAngle.h
@@ -219,17 +219,18 @@ private:
     // die during that.
     nsSVGOrientType* mOrientType;
     nsSVGAngle* mAngle;
     nsSVGElement* mSVGElement;
 
     // nsISMILAttr methods
     virtual nsresult ValueFromString(const nsAString& aStr,
                                      const nsISMILAnimationElement* aSrcElement,
-                                     nsSMILValue& aValue) const;
+                                     nsSMILValue& aValue,
+                                     PRBool& aCanCache) const;
     virtual nsSMILValue GetBaseValue() const;
     virtual void ClearAnimValue();
     virtual nsresult SetAnimValue(const nsSMILValue& aValue);
   };
 #endif // MOZ_SMIL
 };
 
 nsresult
--- a/content/svg/content/src/nsSVGBoolean.cpp
+++ b/content/svg/content/src/nsSVGBoolean.cpp
@@ -138,28 +138,30 @@ nsISMILAttr*
 nsSVGBoolean::ToSMILAttr(nsSVGElement *aSVGElement)
 {
   return new SMILBool(this, aSVGElement);
 }
 
 nsresult
 nsSVGBoolean::SMILBool::ValueFromString(const nsAString& aStr,
                                         const nsISMILAnimationElement* /*aSrcElement*/,
-                                        nsSMILValue& aValue) const
+                                        nsSMILValue& aValue,
+                                        PRBool& aCanCache) const
 {
   nsSMILValue val(&SMILBoolType::sSingleton);
 
   if (aStr.EqualsLiteral("true"))
     val.mU.mBool = PR_TRUE;
   else if (aStr.EqualsLiteral("false"))
     val.mU.mBool = PR_FALSE;
   else
     return NS_ERROR_FAILURE;
 
   aValue = val;
+  aCanCache = PR_TRUE;
   return NS_OK;
 }
 
 nsSMILValue
 nsSVGBoolean::SMILBool::GetBaseValue() const
 {
   nsSMILValue val(&SMILBoolType::sSingleton);
   val.mU.mBool = mVal->mBaseVal;
--- a/content/svg/content/src/nsSVGBoolean.h
+++ b/content/svg/content/src/nsSVGBoolean.h
@@ -114,16 +114,17 @@ private:
     // as the Compositing step, and DOM elements don't get a chance to
     // die during that.
     nsSVGBoolean* mVal;
     nsSVGElement* mSVGElement;
     
     // nsISMILAttr methods
     virtual nsresult ValueFromString(const nsAString& aStr,
                                      const nsISMILAnimationElement* aSrcElement,
-                                     nsSMILValue& aValue) const;
+                                     nsSMILValue& aValue,
+                                     PRBool& aCanCache) const;
     virtual nsSMILValue GetBaseValue() const;
     virtual void ClearAnimValue();
     virtual nsresult SetAnimValue(const nsSMILValue& aValue);
   };
 #endif // MOZ_SMIL
 };
 #endif //__NS_SVGBOOLEAN_H__
--- a/content/svg/content/src/nsSVGEnum.cpp
+++ b/content/svg/content/src/nsSVGEnum.cpp
@@ -169,26 +169,28 @@ nsISMILAttr*
 nsSVGEnum::ToSMILAttr(nsSVGElement *aSVGElement)
 {
   return new SMILEnum(this, aSVGElement);
 }
 
 nsresult
 nsSVGEnum::SMILEnum::ValueFromString(const nsAString& aStr,
                                      const nsISMILAnimationElement* /*aSrcElement*/,
-                                     nsSMILValue& aValue) const
+                                     nsSMILValue& aValue,
+                                     PRBool& aCanCache) const
 {
   nsCOMPtr<nsIAtom> valAtom = do_GetAtom(aStr);
   nsSVGEnumMapping *mapping = mVal->GetMapping(mSVGElement);
 
   while (mapping && mapping->mKey) {
     if (valAtom == *(mapping->mKey)) {
       nsSMILValue val(&SMILEnumType::sSingleton);
       val.mU.mUint = mapping->mVal;
       aValue = val;
+      aCanCache = PR_TRUE;
       return NS_OK;
     }
     mapping++;
   }
   
   // only a warning since authors may mistype attribute values
   NS_WARNING("unknown enumeration key");
   return NS_ERROR_FAILURE;
--- a/content/svg/content/src/nsSVGEnum.h
+++ b/content/svg/content/src/nsSVGEnum.h
@@ -123,17 +123,18 @@ private:
     // as the Compositing step, and DOM elements don't get a chance to
     // die during that.
     nsSVGEnum* mVal;
     nsSVGElement* mSVGElement;
 
     // nsISMILAttr methods
     virtual nsresult ValueFromString(const nsAString& aStr,
                                      const nsISMILAnimationElement* aSrcElement,
-                                     nsSMILValue& aValue) const;
+                                     nsSMILValue& aValue,
+                                     PRBool& aCanCache) const;
     virtual nsSMILValue GetBaseValue() const;
     virtual void ClearAnimValue();
     virtual nsresult SetAnimValue(const nsSMILValue& aValue);
   };
 #endif // MOZ_SMIL
 };
 
 #endif //__NS_SVGENUM_H__
--- a/content/svg/content/src/nsSVGInteger.cpp
+++ b/content/svg/content/src/nsSVGInteger.cpp
@@ -132,33 +132,35 @@ nsISMILAttr*
 nsSVGInteger::ToSMILAttr(nsSVGElement *aSVGElement)
 {
   return new SMILInteger(this, aSVGElement);
 }
 
 nsresult
 nsSVGInteger::SMILInteger::ValueFromString(const nsAString& aStr,
                                            const nsISMILAnimationElement* /*aSrcElement*/,
-                                           nsSMILValue& aValue) const
+                                           nsSMILValue& aValue,
+                                           PRBool& aCanCache) const
 {
   NS_ConvertUTF16toUTF8 value(aStr);
   const char *str = value.get();
 
   if (NS_IsAsciiWhitespace(*str))
     return NS_ERROR_FAILURE;
 
   char *rest;
   PRInt32 val = strtol(str, &rest, 10);
   if (rest == str || *rest != '\0') {
     return NS_ERROR_FAILURE;
   }
 
   nsSMILValue smilVal(&SMILIntegerType::sSingleton);
   smilVal.mU.mInt = val;
   aValue = smilVal;
+  aCanCache = PR_TRUE;
   return NS_OK;
 }
 
 nsSMILValue
 nsSVGInteger::SMILInteger::GetBaseValue() const
 {
   nsSMILValue val(&SMILIntegerType::sSingleton);
   val.mU.mInt = mVal->mBaseVal;
--- a/content/svg/content/src/nsSVGInteger.h
+++ b/content/svg/content/src/nsSVGInteger.h
@@ -114,17 +114,18 @@ private:
     // as the Compositing step, and DOM elements don't get a chance to
     // die during that.
     nsSVGInteger* mVal;
     nsSVGElement* mSVGElement;
 
     // nsISMILAttr methods
     virtual nsresult ValueFromString(const nsAString& aStr,
                                      const nsISMILAnimationElement* aSrcElement,
-                                     nsSMILValue& aValue) const;
+                                     nsSMILValue& aValue,
+                                     PRBool& aCanCache) const;
     virtual nsSMILValue GetBaseValue() const;
     virtual void ClearAnimValue();
     virtual nsresult SetAnimValue(const nsSMILValue& aValue);
   };
 #endif // MOZ_SMIL
 };
 
 #endif //__NS_SVGINTEGER_H__
--- a/content/svg/content/src/nsSVGLength2.cpp
+++ b/content/svg/content/src/nsSVGLength2.cpp
@@ -514,30 +514,34 @@ nsISMILAttr*
 nsSVGLength2::ToSMILAttr(nsSVGElement *aSVGElement)
 {
   return new SMILLength(this, aSVGElement);
 }
 
 nsresult
 nsSVGLength2::SMILLength::ValueFromString(const nsAString& aStr,
                                  const nsISMILAnimationElement* /*aSrcElement*/,
-                                 nsSMILValue& aValue) const
+                                 nsSMILValue& aValue,
+                                 PRBool& aCanCache) const
 {
   float value;
   PRUint16 unitType;
   
   nsresult rv = GetValueFromString(aStr, &value, &unitType);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   nsSMILValue val(&nsSMILFloatType::sSingleton);
   val.mU.mDouble = value / mVal->GetUnitScaleFactor(mSVGElement, unitType);
   aValue = val;
-  
+  aCanCache = (unitType != nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE &&
+               unitType != nsIDOMSVGLength::SVG_LENGTHTYPE_EMS &&
+               unitType != nsIDOMSVGLength::SVG_LENGTHTYPE_EXS);
+
   return NS_OK;
 }
 
 nsSMILValue
 nsSVGLength2::SMILLength::GetBaseValue() const
 {
   nsSMILValue val(&nsSMILFloatType::sSingleton);
   val.mU.mDouble = mVal->GetBaseValue(mSVGElement);
--- a/content/svg/content/src/nsSVGLength2.h
+++ b/content/svg/content/src/nsSVGLength2.h
@@ -278,17 +278,18 @@ private:
     // as the Compositing step, and DOM elements don't get a chance to
     // die during that.
     nsSVGLength2* mVal;
     nsSVGElement* mSVGElement;
 
     // nsISMILAttr methods
     virtual nsresult ValueFromString(const nsAString& aStr,
                                      const nsISMILAnimationElement* aSrcElement,
-                                     nsSMILValue &aValue) const;
+                                     nsSMILValue &aValue,
+                                     PRBool& aCanCache) const;
     virtual nsSMILValue GetBaseValue() const;
     virtual void ClearAnimValue();
     virtual nsresult SetAnimValue(const nsSMILValue& aValue);
   };
 #endif // MOZ_SMIL
 };
 
 #endif //  __NS_SVGLENGTH2_H__
--- a/content/svg/content/src/nsSVGNumber2.cpp
+++ b/content/svg/content/src/nsSVGNumber2.cpp
@@ -167,28 +167,30 @@ nsISMILAttr*
 nsSVGNumber2::ToSMILAttr(nsSVGElement *aSVGElement)
 {
   return new SMILNumber(this, aSVGElement);
 }
 
 nsresult
 nsSVGNumber2::SMILNumber::ValueFromString(const nsAString& aStr,
                                           const nsISMILAnimationElement* /*aSrcElement*/,
-                                          nsSMILValue& aValue) const
+                                          nsSMILValue& aValue,
+                                          PRBool& aCanCache) const
 {
   float value;
 
   PRBool ok = nsSVGUtils::NumberFromString(aStr, &value);
   if (!ok) {
     return NS_ERROR_FAILURE;
   }
 
   nsSMILValue val(&nsSMILFloatType::sSingleton);
   val.mU.mDouble = value;
   aValue = val;
+  aCanCache = PR_TRUE;
 
   return NS_OK;
 }
 
 nsSMILValue
 nsSVGNumber2::SMILNumber::GetBaseValue() const
 {
   nsSMILValue val(&nsSMILFloatType::sSingleton);
--- a/content/svg/content/src/nsSVGNumber2.h
+++ b/content/svg/content/src/nsSVGNumber2.h
@@ -124,17 +124,18 @@ private:
     // as the Compositing step, and DOM elements don't get a chance to
     // die during that.
     nsSVGNumber2* mVal;
     nsSVGElement* mSVGElement;
 
     // nsISMILAttr methods
     virtual nsresult ValueFromString(const nsAString& aStr,
                                      const nsISMILAnimationElement* aSrcElement,
-                                     nsSMILValue& aValue) const;
+                                     nsSMILValue& aValue,
+                                     PRBool& aCanCache) const;
     virtual nsSMILValue GetBaseValue() const;
     virtual void ClearAnimValue();
     virtual nsresult SetAnimValue(const nsSMILValue& aValue);
   };
 #endif // MOZ_SMIL
 };
 
 #endif //__NS_SVGNUMBER2_H__
--- a/content/svg/content/src/nsSVGPreserveAspectRatio.cpp
+++ b/content/svg/content/src/nsSVGPreserveAspectRatio.cpp
@@ -340,25 +340,27 @@ PackPreserveAspectRatio(const nsSVGPrese
   packed |= PRUint64(par.GetMeetOrSlice());
   return packed;
 }
 
 nsresult
 nsSVGPreserveAspectRatio::SMILPreserveAspectRatio
                         ::ValueFromString(const nsAString& aStr,
                                           const nsISMILAnimationElement* /*aSrcElement*/,
-                                          nsSMILValue& aValue) const
+                                          nsSMILValue& aValue,
+                                          PRBool& aCanCache) const
 {
   PreserveAspectRatio par;
   nsresult res = ToPreserveAspectRatio(aStr, &par);
   NS_ENSURE_SUCCESS(res, res);
 
   nsSMILValue val(&SMILEnumType::sSingleton);
   val.mU.mUint = PackPreserveAspectRatio(par);
   aValue = val;
+  aCanCache = PR_TRUE;
   return NS_OK;
 }
 
 nsSMILValue
 nsSVGPreserveAspectRatio::SMILPreserveAspectRatio::GetBaseValue() const
 {
   nsSMILValue val(&SMILEnumType::sSingleton);
   val.mU.mUint = PackPreserveAspectRatio(mVal->GetBaseValue());
--- a/content/svg/content/src/nsSVGPreserveAspectRatio.h
+++ b/content/svg/content/src/nsSVGPreserveAspectRatio.h
@@ -209,17 +209,18 @@ private:
     // as the Compositing step, and DOM elements don't get a chance to
     // die during that.
     nsSVGPreserveAspectRatio* mVal;
     nsSVGElement* mSVGElement;
 
     // nsISMILAttr methods
     virtual nsresult ValueFromString(const nsAString& aStr,
                                      const nsISMILAnimationElement* aSrcElement,
-                                     nsSMILValue& aValue) const;
+                                     nsSMILValue& aValue,
+                                     PRBool& aCanCache) const;
     virtual nsSMILValue GetBaseValue() const;
     virtual void ClearAnimValue();
     virtual nsresult SetAnimValue(const nsSMILValue& aValue);
   };
 #endif // MOZ_SMIL
 };
 
 #endif //__NS_SVGPRESERVEASPECTRATIO_H__
--- a/content/svg/content/src/nsSVGTransformSMILAttr.cpp
+++ b/content/svg/content/src/nsSVGTransformSMILAttr.cpp
@@ -49,28 +49,30 @@
 #include "nsSVGElement.h"
 #include "nsISVGValue.h"
 #include "prdtoa.h"
 #include "prlog.h"
 
 nsresult
 nsSVGTransformSMILAttr::ValueFromString(const nsAString& aStr,
                                      const nsISMILAnimationElement* aSrcElement,
-                                     nsSMILValue& aValue) const
+                                     nsSMILValue& aValue,
+                                     PRBool& aCanCache) const
 {
   NS_ENSURE_TRUE(aSrcElement, NS_ERROR_FAILURE);
   NS_ASSERTION(aValue.IsNull(),
     "aValue should have been cleared before calling ValueFromString");
 
   const nsAttrValue* typeAttr = aSrcElement->GetAnimAttr(nsGkAtoms::type);
   const nsIAtom* transformType = typeAttr
                                ? typeAttr->GetAtomValue()
                                : nsGkAtoms::translate;
 
   ParseValue(aStr, transformType, aValue);
+  aCanCache = PR_TRUE;
   return aValue.IsNull() ? NS_ERROR_FAILURE : NS_OK;
 }
 
 nsSMILValue
 nsSVGTransformSMILAttr::GetBaseValue() const
 {
   nsSMILValue val(&nsSVGTransformSMILType::sSingleton);
   if (val.IsNull())
--- a/content/svg/content/src/nsSVGTransformSMILAttr.h
+++ b/content/svg/content/src/nsSVGTransformSMILAttr.h
@@ -52,19 +52,20 @@ class nsSVGSMILTransform;
 class nsSVGTransformSMILAttr : public nsISMILAttr
 {
 public:
   nsSVGTransformSMILAttr(nsSVGAnimatedTransformList* aTransform,
                          nsSVGElement* aSVGElement)
     : mVal(aTransform) {}
 
   // nsISMILAttr methods
-  virtual nsresult     ValueFromString(const nsAString& aStr,
+  virtual nsresult ValueFromString(const nsAString& aStr,
                                    const nsISMILAnimationElement* aSrcElement,
-                                   nsSMILValue& aValue) const;
+                                   nsSMILValue& aValue,
+                                   PRBool& aCanCache) const;
   virtual nsSMILValue  GetBaseValue() const;
   virtual void         ClearAnimValue();
   virtual nsresult     SetAnimValue(const nsSMILValue& aValue);
 
 protected:
   static void ParseValue(const nsAString& aSpec,
                          const nsIAtom* aTransformType,
                          nsSMILValue& aResult);
--- a/content/svg/content/src/nsSVGViewBox.cpp
+++ b/content/svg/content/src/nsSVGViewBox.cpp
@@ -279,30 +279,32 @@ nsSVGViewBox::ToSMILAttr(nsSVGElement *a
 {
   return new SMILViewBox(this, aSVGElement);
 }
 
 nsresult
 nsSVGViewBox::SMILViewBox
             ::ValueFromString(const nsAString& aStr,
                               const nsISMILAnimationElement* /*aSrcElement*/,
-                              nsSMILValue& aValue) const
+                              nsSMILValue& aValue,
+                              PRBool& aCanCache) const
 {
   nsSVGViewBoxRect viewBox;
   nsresult res = ToSVGViewBoxRect(aStr, &viewBox);
   if (NS_FAILED(res)) {
     return res;
   }
   nsSMILValue val(&SVGViewBoxSMILType::sSingleton);
   // Check for OOM when the nsSMILValue ctor called SVGViewBoxSMILType::Init:
   if (val.IsNull()) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
   *static_cast<nsSVGViewBoxRect*>(val.mU.mPtr) = viewBox;
   aValue = val;
+  aCanCache = PR_TRUE;
   
   return NS_OK;
 }
 
 nsSMILValue
 nsSVGViewBox::SMILViewBox::GetBaseValue() const
 {
   nsSMILValue val(&SVGViewBoxSMILType::sSingleton);
--- a/content/svg/content/src/nsSVGViewBox.h
+++ b/content/svg/content/src/nsSVGViewBox.h
@@ -177,17 +177,18 @@ private:
     // as the Compositing step, and DOM elements don't get a chance to
     // die during that.
     nsSVGViewBox* mVal;
     nsSVGElement* mSVGElement;
 
     // nsISMILAttr methods
     virtual nsresult ValueFromString(const nsAString& aStr,
                                      const nsISMILAnimationElement* aSrcElement,
-                                     nsSMILValue& aValue) const;
+                                     nsSMILValue& aValue,
+                                     PRBool& aCanCache) const;
     virtual nsSMILValue GetBaseValue() const;
     virtual void ClearAnimValue();
     virtual nsresult SetAnimValue(const nsSMILValue& aValue);
   };
 #endif // MOZ_SMIL
 };
 
 #endif // __NS_SVGVIEWBOX_H__