LCOV - code coverage report
Current view: directory - content/smil - nsSMILAnimationFunction.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 448 0 0.0 %
Date: 2012-06-02 Functions: 41 0 0.0 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is the Mozilla SMIL module.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is Brian Birtles.
      18                 :  * Portions created by the Initial Developer are Copyright (C) 2005
      19                 :  * the Initial Developer. All Rights Reserved.
      20                 :  *
      21                 :  * Contributor(s):
      22                 :  *   Brian Birtles <birtles@gmail.com>
      23                 :  *   Chris Double  <chris.double@double.co.nz>
      24                 :  *   Daniel Holbert <dholbert@cs.stanford.edu>
      25                 :  *
      26                 :  * Alternatively, the contents of this file may be used under the terms of
      27                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      28                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      29                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      30                 :  * of those above. If you wish to allow use of your version of this file only
      31                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      32                 :  * use your version of this file under the terms of the MPL, indicate your
      33                 :  * decision by deleting the provisions above and replace them with the notice
      34                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      35                 :  * the provisions above, a recipient may use your version of this file under
      36                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      37                 :  *
      38                 :  * ***** END LICENSE BLOCK ***** */
      39                 : 
      40                 : #include "nsSMILAnimationFunction.h"
      41                 : #include "nsISMILAttr.h"
      42                 : #include "nsSMILParserUtils.h"
      43                 : #include "nsSMILNullType.h"
      44                 : #include "nsISMILAnimationElement.h"
      45                 : #include "nsSMILTimedElement.h"
      46                 : #include "nsGkAtoms.h"
      47                 : #include "nsCOMPtr.h"
      48                 : #include "nsCOMArray.h"
      49                 : #include "nsIContent.h"
      50                 : #include "nsAutoPtr.h"
      51                 : #include "nsContentUtils.h"
      52                 : #include "nsReadableUtils.h"
      53                 : #include "nsString.h"
      54                 : #include <math.h>
      55                 : 
      56                 : //----------------------------------------------------------------------
      57                 : // Static members
      58                 : 
      59                 : nsAttrValue::EnumTable nsSMILAnimationFunction::sAccumulateTable[] = {
      60                 :       {"none", false},
      61                 :       {"sum", true},
      62                 :       {nsnull, 0}
      63                 : };
      64                 : 
      65                 : nsAttrValue::EnumTable nsSMILAnimationFunction::sAdditiveTable[] = {
      66                 :       {"replace", false},
      67                 :       {"sum", true},
      68                 :       {nsnull, 0}
      69                 : };
      70                 : 
      71                 : nsAttrValue::EnumTable nsSMILAnimationFunction::sCalcModeTable[] = {
      72                 :       {"linear", CALC_LINEAR},
      73                 :       {"discrete", CALC_DISCRETE},
      74                 :       {"paced", CALC_PACED},
      75                 :       {"spline", CALC_SPLINE},
      76                 :       {nsnull, 0}
      77                 : };
      78                 : 
      79                 : // Any negative number should be fine as a sentinel here,
      80                 : // because valid distances are non-negative.
      81                 : #define COMPUTE_DISTANCE_ERROR (-1)
      82                 : 
      83                 : //----------------------------------------------------------------------
      84                 : // Constructors etc.
      85                 : 
      86               0 : nsSMILAnimationFunction::nsSMILAnimationFunction()
      87                 :   : mSampleTime(-1),
      88                 :     mRepeatIteration(0),
      89                 :     mBeginTime(LL_MININT),
      90                 :     mAnimationElement(nsnull),
      91                 :     mErrorFlags(0),
      92                 :     mIsActive(false),
      93                 :     mIsFrozen(false),
      94                 :     mLastValue(false),
      95                 :     mHasChanged(true),
      96                 :     mValueNeedsReparsingEverySample(false),
      97                 :     mPrevSampleWasSingleValueAnimation(false),
      98               0 :     mWasSkippedInPrevSample(false)
      99                 : {
     100               0 : }
     101                 : 
     102                 : void
     103               0 : nsSMILAnimationFunction::SetAnimationElement(
     104                 :     nsISMILAnimationElement* aAnimationElement)
     105                 : {
     106               0 :   mAnimationElement = aAnimationElement;
     107               0 : }
     108                 : 
     109                 : bool
     110               0 : nsSMILAnimationFunction::SetAttr(nsIAtom* aAttribute, const nsAString& aValue,
     111                 :                                  nsAttrValue& aResult, nsresult* aParseResult)
     112                 : {
     113               0 :   bool foundMatch = true;
     114               0 :   nsresult parseResult = NS_OK;
     115                 : 
     116                 :   // The attributes 'by', 'from', 'to', and 'values' may be parsed differently
     117                 :   // depending on the element & attribute we're animating.  So instead of
     118                 :   // parsing them now we re-parse them at every sample.
     119               0 :   if (aAttribute == nsGkAtoms::by ||
     120                 :       aAttribute == nsGkAtoms::from ||
     121                 :       aAttribute == nsGkAtoms::to ||
     122                 :       aAttribute == nsGkAtoms::values) {
     123                 :     // We parse to, from, by, values at sample time.
     124                 :     // XXX Need to flag which attribute has changed and then when we parse it at
     125                 :     // sample time, report any errors and reset the flag
     126               0 :     mHasChanged = true;
     127               0 :     aResult.SetTo(aValue);
     128               0 :   } else if (aAttribute == nsGkAtoms::accumulate) {
     129               0 :     parseResult = SetAccumulate(aValue, aResult);
     130               0 :   } else if (aAttribute == nsGkAtoms::additive) {
     131               0 :     parseResult = SetAdditive(aValue, aResult);
     132               0 :   } else if (aAttribute == nsGkAtoms::calcMode) {
     133               0 :     parseResult = SetCalcMode(aValue, aResult);
     134               0 :   } else if (aAttribute == nsGkAtoms::keyTimes) {
     135               0 :     parseResult = SetKeyTimes(aValue, aResult);
     136               0 :   } else if (aAttribute == nsGkAtoms::keySplines) {
     137               0 :     parseResult = SetKeySplines(aValue, aResult);
     138                 :   } else {
     139               0 :     foundMatch = false;
     140                 :   }
     141                 : 
     142               0 :   if (foundMatch && aParseResult) {
     143               0 :     *aParseResult = parseResult;
     144                 :   }
     145                 : 
     146               0 :   return foundMatch;
     147                 : }
     148                 : 
     149                 : bool
     150               0 : nsSMILAnimationFunction::UnsetAttr(nsIAtom* aAttribute)
     151                 : {
     152               0 :   bool foundMatch = true;
     153                 : 
     154               0 :   if (aAttribute == nsGkAtoms::by ||
     155                 :       aAttribute == nsGkAtoms::from ||
     156                 :       aAttribute == nsGkAtoms::to ||
     157                 :       aAttribute == nsGkAtoms::values) {
     158               0 :     mHasChanged = true;
     159               0 :   } else if (aAttribute == nsGkAtoms::accumulate) {
     160               0 :     UnsetAccumulate();
     161               0 :   } else if (aAttribute == nsGkAtoms::additive) {
     162               0 :     UnsetAdditive();
     163               0 :   } else if (aAttribute == nsGkAtoms::calcMode) {
     164               0 :     UnsetCalcMode();
     165               0 :   } else if (aAttribute == nsGkAtoms::keyTimes) {
     166               0 :     UnsetKeyTimes();
     167               0 :   } else if (aAttribute == nsGkAtoms::keySplines) {
     168               0 :     UnsetKeySplines();
     169                 :   } else {
     170               0 :     foundMatch = false;
     171                 :   }
     172                 : 
     173               0 :   return foundMatch;
     174                 : }
     175                 : 
     176                 : void
     177               0 : nsSMILAnimationFunction::SampleAt(nsSMILTime aSampleTime,
     178                 :                                   const nsSMILTimeValue& aSimpleDuration,
     179                 :                                   PRUint32 aRepeatIteration)
     180                 : {
     181                 :   // * Update mHasChanged ("Might this sample be different from prev one?")
     182                 :   // Were we previously sampling a fill="freeze" final val? (We're not anymore.)
     183               0 :   mHasChanged |= mLastValue;
     184                 : 
     185                 :   // Are we sampling at a new point in simple duration? And does that matter?
     186                 :   mHasChanged |=
     187               0 :     (mSampleTime != aSampleTime || mSimpleDuration != aSimpleDuration) &&
     188               0 :     !IsValueFixedForSimpleDuration();
     189                 : 
     190                 :   // Are we on a new repeat and accumulating across repeats?
     191               0 :   if (!mErrorFlags) { // (can't call GetAccumulate() if we've had parse errors)
     192               0 :     mHasChanged |= (mRepeatIteration != aRepeatIteration) && GetAccumulate();
     193                 :   }
     194                 : 
     195               0 :   mSampleTime       = aSampleTime;
     196               0 :   mSimpleDuration   = aSimpleDuration;
     197               0 :   mRepeatIteration  = aRepeatIteration;
     198               0 :   mLastValue        = false;
     199               0 : }
     200                 : 
     201                 : void
     202               0 : nsSMILAnimationFunction::SampleLastValue(PRUint32 aRepeatIteration)
     203                 : {
     204               0 :   if (mHasChanged || !mLastValue || mRepeatIteration != aRepeatIteration) {
     205               0 :     mHasChanged = true;
     206                 :   }
     207                 : 
     208               0 :   mRepeatIteration  = aRepeatIteration;
     209               0 :   mLastValue        = true;
     210               0 : }
     211                 : 
     212                 : void
     213               0 : nsSMILAnimationFunction::Activate(nsSMILTime aBeginTime)
     214                 : {
     215               0 :   mBeginTime = aBeginTime;
     216               0 :   mIsActive = true;
     217               0 :   mIsFrozen = false;
     218               0 :   mFrozenValue = nsSMILValue();
     219               0 :   mHasChanged = true;
     220               0 : }
     221                 : 
     222                 : void
     223               0 : nsSMILAnimationFunction::Inactivate(bool aIsFrozen)
     224                 : {
     225               0 :   mIsActive = false;
     226               0 :   mIsFrozen = aIsFrozen;
     227               0 :   mFrozenValue = nsSMILValue();
     228               0 :   mHasChanged = true;
     229               0 : }
     230                 : 
     231                 : void
     232               0 : nsSMILAnimationFunction::ComposeResult(const nsISMILAttr& aSMILAttr,
     233                 :                                        nsSMILValue& aResult)
     234                 : {
     235               0 :   mHasChanged = false;
     236               0 :   mPrevSampleWasSingleValueAnimation = false;
     237               0 :   mWasSkippedInPrevSample = false;
     238                 : 
     239                 :   // Skip animations that are inactive or in error
     240               0 :   if (!IsActiveOrFrozen() || mErrorFlags != 0)
     241               0 :     return;
     242                 : 
     243                 :   // Get the animation values
     244               0 :   nsSMILValueArray values;
     245               0 :   nsresult rv = GetValues(aSMILAttr, values);
     246               0 :   if (NS_FAILED(rv))
     247                 :     return;
     248                 : 
     249                 :   // Check that we have the right number of keySplines and keyTimes
     250               0 :   CheckValueListDependentAttrs(values.Length());
     251               0 :   if (mErrorFlags != 0)
     252                 :     return;
     253                 : 
     254                 :   // If this interval is active, we must have a non-negative mSampleTime
     255               0 :   NS_ABORT_IF_FALSE(mSampleTime >= 0 || !mIsActive,
     256                 :       "Negative sample time for active animation");
     257               0 :   NS_ABORT_IF_FALSE(mSimpleDuration.IsResolved() || mLastValue,
     258                 :       "Unresolved simple duration for active or frozen animation");
     259                 : 
     260                 :   // If we want to add but don't have a base value then just fail outright.
     261                 :   // This can happen when we skipped getting the base value because there's an
     262                 :   // animation function in the sandwich that should replace it but that function
     263                 :   // failed unexpectedly.
     264               0 :   bool isAdditive = IsAdditive();
     265               0 :   if (isAdditive && aResult.IsNull())
     266                 :     return;
     267                 : 
     268               0 :   nsSMILValue result;
     269                 : 
     270               0 :   if (values.Length() == 1 && !IsToAnimation()) {
     271                 : 
     272                 :     // Single-valued animation
     273               0 :     result = values[0];
     274               0 :     mPrevSampleWasSingleValueAnimation = true;
     275                 : 
     276               0 :   } else if (mLastValue) {
     277                 : 
     278                 :     // Sampling last value
     279               0 :     const nsSMILValue& last = values[values.Length() - 1];
     280               0 :     result = last;
     281                 : 
     282                 :     // See comment in AccumulateResult: to-animation does not accumulate
     283               0 :     if (!IsToAnimation() && GetAccumulate() && mRepeatIteration) {
     284                 :       // If the target attribute type doesn't support addition Add will
     285                 :       // fail leaving result = last
     286               0 :       result.Add(last, mRepeatIteration);
     287                 :     }
     288                 : 
     289               0 :   } else if (!mFrozenValue.IsNull() && !mHasChanged) {
     290                 : 
     291                 :     // Frozen to animation
     292               0 :     result = mFrozenValue;
     293                 : 
     294                 :   } else {
     295                 : 
     296                 :     // Interpolation
     297               0 :     if (NS_FAILED(InterpolateResult(values, result, aResult)))
     298                 :       return;
     299                 : 
     300               0 :     if (NS_FAILED(AccumulateResult(values, result)))
     301                 :       return;
     302                 : 
     303               0 :     if (IsToAnimation() && mIsFrozen) {
     304               0 :       mFrozenValue = result;
     305                 :     }
     306                 :   }
     307                 : 
     308                 :   // If additive animation isn't required or isn't supported, set the value.
     309               0 :   if (!isAdditive || NS_FAILED(aResult.SandwichAdd(result))) {
     310               0 :     aResult.Swap(result);
     311                 :     // Note: The old value of aResult is now in |result|, and it will get
     312                 :     // cleaned up when |result| goes out of scope, when this function returns.
     313                 :   }
     314                 : }
     315                 : 
     316                 : PRInt8
     317               0 : nsSMILAnimationFunction::CompareTo(const nsSMILAnimationFunction* aOther) const
     318                 : {
     319               0 :   NS_ENSURE_TRUE(aOther, 0);
     320                 : 
     321               0 :   NS_ASSERTION(aOther != this, "Trying to compare to self");
     322                 : 
     323                 :   // Inactive animations sort first
     324               0 :   if (!IsActiveOrFrozen() && aOther->IsActiveOrFrozen())
     325               0 :     return -1;
     326                 : 
     327               0 :   if (IsActiveOrFrozen() && !aOther->IsActiveOrFrozen())
     328               0 :     return 1;
     329                 : 
     330                 :   // Sort based on begin time
     331               0 :   if (mBeginTime != aOther->GetBeginTime())
     332               0 :     return mBeginTime > aOther->GetBeginTime() ? 1 : -1;
     333                 : 
     334                 :   // Next sort based on syncbase dependencies: the dependent element sorts after
     335                 :   // its syncbase
     336                 :   const nsSMILTimedElement& thisTimedElement =
     337               0 :     mAnimationElement->TimedElement();
     338                 :   const nsSMILTimedElement& otherTimedElement =
     339               0 :     aOther->mAnimationElement->TimedElement();
     340               0 :   if (thisTimedElement.IsTimeDependent(otherTimedElement))
     341               0 :     return 1;
     342               0 :   if (otherTimedElement.IsTimeDependent(thisTimedElement))
     343               0 :     return -1;
     344                 : 
     345                 :   // Animations that appear later in the document sort after those earlier in
     346                 :   // the document
     347               0 :   nsIContent& thisContent = mAnimationElement->AsElement();
     348               0 :   nsIContent& otherContent = aOther->mAnimationElement->AsElement();
     349                 : 
     350               0 :   NS_ABORT_IF_FALSE(&thisContent != &otherContent,
     351                 :       "Two animations cannot have the same animation content element!");
     352                 : 
     353               0 :   return (nsContentUtils::PositionIsBefore(&thisContent, &otherContent))
     354               0 :           ? -1 : 1;
     355                 : }
     356                 : 
     357                 : bool
     358               0 : nsSMILAnimationFunction::WillReplace() const
     359                 : {
     360                 :   /*
     361                 :    * In IsAdditive() we don't consider to-animation to be additive as it is
     362                 :    * a special case that is dealt with differently in the compositing method but
     363                 :    * here we return false for to animation as it builds on the underlying value
     364                 :    * unless its a frozen to animation.
     365                 :    */
     366               0 :   return !mErrorFlags && (!(IsAdditive() || IsToAnimation()) ||
     367               0 :                           (IsToAnimation() && mIsFrozen && !mHasChanged));
     368                 : }
     369                 : 
     370                 : bool
     371               0 : nsSMILAnimationFunction::HasChanged() const
     372                 : {
     373               0 :   return mHasChanged || mValueNeedsReparsingEverySample;
     374                 : }
     375                 : 
     376                 : bool
     377               0 : nsSMILAnimationFunction::UpdateCachedTarget(const nsSMILTargetIdentifier& aNewTarget)
     378                 : {
     379               0 :   if (!mLastTarget.Equals(aNewTarget)) {
     380               0 :     mLastTarget = aNewTarget;
     381               0 :     return true;
     382                 :   }
     383               0 :   return false;
     384                 : }
     385                 : 
     386                 : //----------------------------------------------------------------------
     387                 : // Implementation helpers
     388                 : 
     389                 : nsresult
     390               0 : nsSMILAnimationFunction::InterpolateResult(const nsSMILValueArray& aValues,
     391                 :                                            nsSMILValue& aResult,
     392                 :                                            nsSMILValue& aBaseValue)
     393                 : {
     394                 :   // Sanity check animation values
     395               0 :   if ((!IsToAnimation() && aValues.Length() < 2) ||
     396               0 :       (IsToAnimation()  && aValues.Length() != 1)) {
     397               0 :     NS_ERROR("Unexpected number of values");
     398               0 :     return NS_ERROR_FAILURE;
     399                 :   }
     400                 : 
     401               0 :   if (IsToAnimation() && aBaseValue.IsNull()) {
     402               0 :     return NS_ERROR_FAILURE;
     403                 :   }
     404                 : 
     405                 :   // Get the normalised progress through the simple duration.
     406                 :   //
     407                 :   // If we have an indefinite simple duration, just set the progress to be
     408                 :   // 0 which will give us the expected behaviour of the animation being fixed at
     409                 :   // its starting point.
     410               0 :   double simpleProgress = 0.0;
     411                 : 
     412               0 :   if (mSimpleDuration.IsDefinite()) {
     413               0 :     nsSMILTime dur = mSimpleDuration.GetMillis();
     414                 : 
     415               0 :     NS_ABORT_IF_FALSE(dur >= 0, "Simple duration should not be negative");
     416               0 :     NS_ABORT_IF_FALSE(mSampleTime >= 0, "Sample time should not be negative");
     417                 : 
     418               0 :     if (mSampleTime >= dur || mSampleTime < 0) {
     419               0 :       NS_ERROR("Animation sampled outside interval");
     420               0 :       return NS_ERROR_FAILURE;
     421                 :     }
     422                 : 
     423               0 :     if (dur > 0) {
     424               0 :       simpleProgress = (double)mSampleTime / dur;
     425                 :     } // else leave simpleProgress at 0.0 (e.g. if mSampleTime == dur == 0)
     426                 :   }
     427                 : 
     428               0 :   nsresult rv = NS_OK;
     429               0 :   nsSMILCalcMode calcMode = GetCalcMode();
     430               0 :   if (calcMode != CALC_DISCRETE) {
     431                 :     // Get the normalised progress between adjacent values
     432               0 :     const nsSMILValue* from = nsnull;
     433               0 :     const nsSMILValue* to = nsnull;
     434                 :     // Init to -1 to make sure that if we ever forget to set this, the
     435                 :     // NS_ABORT_IF_FALSE that tests that intervalProgress is in range will fail.
     436               0 :     double intervalProgress = -1.f;
     437               0 :     if (IsToAnimation()) {
     438               0 :       from = &aBaseValue;
     439               0 :       to = &aValues[0];
     440               0 :       if (calcMode == CALC_PACED) {
     441                 :         // Note: key[Times/Splines/Points] are ignored for calcMode="paced"
     442               0 :         intervalProgress = simpleProgress;
     443                 :       } else {
     444                 :         double scaledSimpleProgress =
     445               0 :           ScaleSimpleProgress(simpleProgress, calcMode);
     446               0 :         intervalProgress = ScaleIntervalProgress(scaledSimpleProgress, 0);
     447                 :       }
     448               0 :     } else if (calcMode == CALC_PACED) {
     449                 :       rv = ComputePacedPosition(aValues, simpleProgress,
     450               0 :                                 intervalProgress, from, to);
     451                 :       // Note: If the above call fails, we'll skip the "from->Interpolate"
     452                 :       // call below, and we'll drop into the CALC_DISCRETE section
     453                 :       // instead. (as the spec says we should, because our failure was
     454                 :       // presumably due to the values being non-additive)
     455                 :     } else { // calcMode == CALC_LINEAR or calcMode == CALC_SPLINE
     456                 :       double scaledSimpleProgress =
     457               0 :         ScaleSimpleProgress(simpleProgress, calcMode);
     458                 :       PRUint32 index = (PRUint32)floor(scaledSimpleProgress *
     459               0 :                                        (aValues.Length() - 1));
     460               0 :       from = &aValues[index];
     461               0 :       to = &aValues[index + 1];
     462                 :       intervalProgress =
     463               0 :         scaledSimpleProgress * (aValues.Length() - 1) - index;
     464               0 :       intervalProgress = ScaleIntervalProgress(intervalProgress, index);
     465                 :     }
     466                 : 
     467               0 :     if (NS_SUCCEEDED(rv)) {
     468               0 :       NS_ABORT_IF_FALSE(from, "NULL from-value during interpolation");
     469               0 :       NS_ABORT_IF_FALSE(to, "NULL to-value during interpolation");
     470               0 :       NS_ABORT_IF_FALSE(0.0f <= intervalProgress && intervalProgress < 1.0f,
     471                 :                       "Interval progress should be in the range [0, 1)");
     472               0 :       rv = from->Interpolate(*to, intervalProgress, aResult);
     473                 :     }
     474                 :   }
     475                 : 
     476                 :   // Discrete-CalcMode case
     477                 :   // Note: If interpolation failed (isn't supported for this type), the SVG
     478                 :   // spec says to force discrete mode.
     479               0 :   if (calcMode == CALC_DISCRETE || NS_FAILED(rv)) {
     480                 :     double scaledSimpleProgress =
     481               0 :       ScaleSimpleProgress(simpleProgress, CALC_DISCRETE);
     482                 : 
     483                 :     // Floating-point errors can mean that, for example, a sample time of 29s in
     484                 :     // a 100s duration animation gives us a simple progress of 0.28999999999
     485                 :     // instead of the 0.29 we'd expect. Normally this isn't a noticeable
     486                 :     // problem, but when we have sudden jumps in animation values (such as is
     487                 :     // the case here with discrete animation) we can get unexpected results.
     488                 :     //
     489                 :     // To counteract this, before we perform a floor() on the animation
     490                 :     // progress, we add a tiny fudge factor to push us into the correct interval
     491                 :     // in cases where floating-point errors might cause us to fall short.
     492                 :     static const double kFloatingPointFudgeFactor = 1.0e-16;
     493               0 :     if (scaledSimpleProgress + kFloatingPointFudgeFactor <= 1.0) {
     494               0 :       scaledSimpleProgress += kFloatingPointFudgeFactor;
     495                 :     }
     496                 : 
     497               0 :     if (IsToAnimation()) {
     498                 :       // We don't follow SMIL 3, 12.6.4, where discrete to animations
     499                 :       // are the same as <set> animations.  Instead, we treat it as a
     500                 :       // discrete animation with two values (the underlying value and
     501                 :       // the to="" value), and honor keyTimes="" as well.
     502               0 :       PRUint32 index = (PRUint32)floor(scaledSimpleProgress * 2);
     503               0 :       aResult = index == 0 ? aBaseValue : aValues[0];
     504                 :     } else {
     505               0 :       PRUint32 index = (PRUint32)floor(scaledSimpleProgress * aValues.Length());
     506               0 :       aResult = aValues[index];
     507                 :     }
     508               0 :     rv = NS_OK;
     509                 :   }
     510               0 :   return rv;
     511                 : }
     512                 : 
     513                 : nsresult
     514               0 : nsSMILAnimationFunction::AccumulateResult(const nsSMILValueArray& aValues,
     515                 :                                           nsSMILValue& aResult)
     516                 : {
     517               0 :   if (!IsToAnimation() && GetAccumulate() && mRepeatIteration)
     518                 :   {
     519               0 :     const nsSMILValue& lastValue = aValues[aValues.Length() - 1];
     520                 : 
     521                 :     // If the target attribute type doesn't support addition, Add will
     522                 :     // fail and we leave aResult untouched.
     523               0 :     aResult.Add(lastValue, mRepeatIteration);
     524                 :   }
     525                 : 
     526               0 :   return NS_OK;
     527                 : }
     528                 : 
     529                 : /*
     530                 :  * Given the simple progress for a paced animation, this method:
     531                 :  *  - determines which two elements of the values array we're in between
     532                 :  *    (returned as aFrom and aTo)
     533                 :  *  - determines where we are between them
     534                 :  *    (returned as aIntervalProgress)
     535                 :  *
     536                 :  * Returns NS_OK, or NS_ERROR_FAILURE if our values don't support distance
     537                 :  * computation.
     538                 :  */
     539                 : nsresult
     540               0 : nsSMILAnimationFunction::ComputePacedPosition(const nsSMILValueArray& aValues,
     541                 :                                               double aSimpleProgress,
     542                 :                                               double& aIntervalProgress,
     543                 :                                               const nsSMILValue*& aFrom,
     544                 :                                               const nsSMILValue*& aTo)
     545                 : {
     546               0 :   NS_ASSERTION(0.0f <= aSimpleProgress && aSimpleProgress < 1.0f,
     547                 :                "aSimpleProgress is out of bounds");
     548               0 :   NS_ASSERTION(GetCalcMode() == CALC_PACED,
     549                 :                "Calling paced-specific function, but not in paced mode");
     550               0 :   NS_ABORT_IF_FALSE(aValues.Length() >= 2, "Unexpected number of values");
     551                 : 
     552                 :   // Trivial case: If we have just 2 values, then there's only one interval
     553                 :   // for us to traverse, and our progress across that interval is the exact
     554                 :   // same as our overall progress.
     555               0 :   if (aValues.Length() == 2) {
     556               0 :     aIntervalProgress = aSimpleProgress;
     557               0 :     aFrom = &aValues[0];
     558               0 :     aTo = &aValues[1];
     559               0 :     return NS_OK;
     560                 :   }
     561                 : 
     562               0 :   double totalDistance = ComputePacedTotalDistance(aValues);
     563               0 :   if (totalDistance == COMPUTE_DISTANCE_ERROR)
     564               0 :     return NS_ERROR_FAILURE;
     565                 : 
     566                 :   // If we have 0 total distance, then it's unclear where our "paced" position
     567                 :   // should be.  We can just fail, which drops us into discrete animation mode.
     568                 :   // (That's fine, since our values are apparently indistinguishable anyway.)
     569               0 :   if (totalDistance == 0.0) {
     570               0 :     return NS_ERROR_FAILURE;
     571                 :   }
     572                 : 
     573                 :   // total distance we should have moved at this point in time.
     574                 :   // (called 'remainingDist' due to how it's used in loop below)
     575               0 :   double remainingDist = aSimpleProgress * totalDistance;
     576                 : 
     577                 :   // Must be satisfied, because totalDistance is a sum of (non-negative)
     578                 :   // distances, and aSimpleProgress is non-negative
     579               0 :   NS_ASSERTION(remainingDist >= 0, "distance values must be non-negative");
     580                 : 
     581                 :   // Find where remainingDist puts us in the list of values
     582                 :   // Note: We could optimize this next loop by caching the
     583                 :   // interval-distances in an array, but maybe that's excessive.
     584               0 :   for (PRUint32 i = 0; i < aValues.Length() - 1; i++) {
     585                 :     // Note: The following assertion is valid because remainingDist should
     586                 :     // start out non-negative, and this loop never shaves off more than its
     587                 :     // current value.
     588               0 :     NS_ASSERTION(remainingDist >= 0, "distance values must be non-negative");
     589                 : 
     590                 :     double curIntervalDist;
     591                 : 
     592                 : #ifdef DEBUG
     593                 :     nsresult rv =
     594                 : #endif
     595               0 :       aValues[i].ComputeDistance(aValues[i+1], curIntervalDist);
     596               0 :     NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv),
     597                 :                       "If we got through ComputePacedTotalDistance, we should "
     598                 :                       "be able to recompute each sub-distance without errors");
     599                 : 
     600               0 :     NS_ASSERTION(curIntervalDist >= 0, "distance values must be non-negative");
     601                 :     // Clamp distance value at 0, just in case ComputeDistance is evil.
     602               0 :     curIntervalDist = NS_MAX(curIntervalDist, 0.0);
     603                 : 
     604               0 :     if (remainingDist >= curIntervalDist) {
     605               0 :       remainingDist -= curIntervalDist;
     606                 :     } else {
     607                 :       // NOTE: If we get here, then curIntervalDist necessarily is not 0. Why?
     608                 :       // Because this clause is only hit when remainingDist < curIntervalDist,
     609                 :       // and if curIntervalDist were 0, that would mean remainingDist would
     610                 :       // have to be < 0.  But that can't happen, because remainingDist (as
     611                 :       // a distance) is non-negative by definition.
     612               0 :       NS_ASSERTION(curIntervalDist != 0,
     613                 :                    "We should never get here with this set to 0...");
     614                 : 
     615                 :       // We found the right spot -- an interpolated position between
     616                 :       // values i and i+1.
     617               0 :       aFrom = &aValues[i];
     618               0 :       aTo = &aValues[i+1];
     619               0 :       aIntervalProgress = remainingDist / curIntervalDist;
     620               0 :       return NS_OK;
     621                 :     }
     622                 :   }
     623                 : 
     624                 :   NS_NOTREACHED("shouldn't complete loop & get here -- if we do, "
     625               0 :                 "then aSimpleProgress was probably out of bounds");
     626               0 :   return NS_ERROR_FAILURE;
     627                 : }
     628                 : 
     629                 : /*
     630                 :  * Computes the total distance to be travelled by a paced animation.
     631                 :  *
     632                 :  * Returns the total distance, or returns COMPUTE_DISTANCE_ERROR if
     633                 :  * our values don't support distance computation.
     634                 :  */
     635                 : double
     636               0 : nsSMILAnimationFunction::ComputePacedTotalDistance(
     637                 :     const nsSMILValueArray& aValues) const
     638                 : {
     639               0 :   NS_ASSERTION(GetCalcMode() == CALC_PACED,
     640                 :                "Calling paced-specific function, but not in paced mode");
     641                 : 
     642               0 :   double totalDistance = 0.0;
     643               0 :   for (PRUint32 i = 0; i < aValues.Length() - 1; i++) {
     644                 :     double tmpDist;
     645               0 :     nsresult rv = aValues[i].ComputeDistance(aValues[i+1], tmpDist);
     646               0 :     if (NS_FAILED(rv)) {
     647               0 :       return COMPUTE_DISTANCE_ERROR;
     648                 :     }
     649                 : 
     650                 :     // Clamp distance value to 0, just in case we have an evil ComputeDistance
     651                 :     // implementation somewhere
     652               0 :     NS_ABORT_IF_FALSE(tmpDist >= 0.0f, "distance values must be non-negative");
     653               0 :     tmpDist = NS_MAX(tmpDist, 0.0);
     654                 : 
     655               0 :     totalDistance += tmpDist;
     656                 :   }
     657                 : 
     658               0 :   return totalDistance;
     659                 : }
     660                 : 
     661                 : double
     662               0 : nsSMILAnimationFunction::ScaleSimpleProgress(double aProgress,
     663                 :                                              nsSMILCalcMode aCalcMode)
     664                 : {
     665               0 :   if (!HasAttr(nsGkAtoms::keyTimes))
     666               0 :     return aProgress;
     667                 : 
     668               0 :   PRUint32 numTimes = mKeyTimes.Length();
     669                 : 
     670               0 :   if (numTimes < 2)
     671               0 :     return aProgress;
     672                 : 
     673               0 :   PRUint32 i = 0;
     674               0 :   for (; i < numTimes - 2 && aProgress >= mKeyTimes[i+1]; ++i);
     675                 : 
     676               0 :   if (aCalcMode == CALC_DISCRETE) {
     677                 :     // discrete calcMode behaviour differs in that each keyTime defines the time
     678                 :     // from when the corresponding value is set, and therefore the last value
     679                 :     // needn't be 1. So check if we're in the last 'interval', that is, the
     680                 :     // space between the final value and 1.0.
     681               0 :     if (aProgress >= mKeyTimes[i+1]) {
     682               0 :       NS_ABORT_IF_FALSE(i == numTimes - 2,
     683                 :           "aProgress is not in range of the current interval, yet the current"
     684                 :           " interval is not the last bounded interval either.");
     685               0 :       ++i;
     686                 :     }
     687               0 :     return (double)i / numTimes;
     688                 :   }
     689                 : 
     690               0 :   double& intervalStart = mKeyTimes[i];
     691               0 :   double& intervalEnd   = mKeyTimes[i+1];
     692                 : 
     693               0 :   double intervalLength = intervalEnd - intervalStart;
     694               0 :   if (intervalLength <= 0.0)
     695               0 :     return intervalStart;
     696                 : 
     697                 :   return (i + (aProgress - intervalStart) / intervalLength) /
     698               0 :          double(numTimes - 1);
     699                 : }
     700                 : 
     701                 : double
     702               0 : nsSMILAnimationFunction::ScaleIntervalProgress(double aProgress,
     703                 :                                                PRUint32 aIntervalIndex)
     704                 : {
     705               0 :   if (GetCalcMode() != CALC_SPLINE)
     706               0 :     return aProgress;
     707                 : 
     708               0 :   if (!HasAttr(nsGkAtoms::keySplines))
     709               0 :     return aProgress;
     710                 : 
     711               0 :   NS_ABORT_IF_FALSE(aIntervalIndex < mKeySplines.Length(),
     712                 :                     "Invalid interval index");
     713                 : 
     714               0 :   nsSMILKeySpline const &spline = mKeySplines[aIntervalIndex];
     715               0 :   return spline.GetSplineValue(aProgress);
     716                 : }
     717                 : 
     718                 : bool
     719               0 : nsSMILAnimationFunction::HasAttr(nsIAtom* aAttName) const
     720                 : {
     721               0 :   return mAnimationElement->HasAnimAttr(aAttName);
     722                 : }
     723                 : 
     724                 : const nsAttrValue*
     725               0 : nsSMILAnimationFunction::GetAttr(nsIAtom* aAttName) const
     726                 : {
     727               0 :   return mAnimationElement->GetAnimAttr(aAttName);
     728                 : }
     729                 : 
     730                 : bool
     731               0 : nsSMILAnimationFunction::GetAttr(nsIAtom* aAttName, nsAString& aResult) const
     732                 : {
     733               0 :   return mAnimationElement->GetAnimAttr(aAttName, aResult);
     734                 : }
     735                 : 
     736                 : /*
     737                 :  * A utility function to make querying an attribute that corresponds to an
     738                 :  * nsSMILValue a little neater.
     739                 :  *
     740                 :  * @param aAttName    The attribute name (in the global namespace).
     741                 :  * @param aSMILAttr   The SMIL attribute to perform the parsing.
     742                 :  * @param[out] aResult        The resulting nsSMILValue.
     743                 :  * @param[out] aPreventCachingOfSandwich
     744                 :  *                    If |aResult| contains dependencies on its context that
     745                 :  *                    should prevent the result of the animation sandwich from
     746                 :  *                    being cached and reused in future samples (as reported
     747                 :  *                    by nsISMILAttr::ValueFromString), then this outparam
     748                 :  *                    will be set to true. Otherwise it is left unmodified.
     749                 :  *
     750                 :  * Returns false if a parse error occurred, otherwise returns true.
     751                 :  */
     752                 : bool
     753               0 : nsSMILAnimationFunction::ParseAttr(nsIAtom* aAttName,
     754                 :                                    const nsISMILAttr& aSMILAttr,
     755                 :                                    nsSMILValue& aResult,
     756                 :                                    bool& aPreventCachingOfSandwich) const
     757                 : {
     758               0 :   nsAutoString attValue;
     759               0 :   if (GetAttr(aAttName, attValue)) {
     760               0 :     bool preventCachingOfSandwich = false;
     761                 :     nsresult rv = aSMILAttr.ValueFromString(attValue, mAnimationElement,
     762               0 :                                             aResult, preventCachingOfSandwich);
     763               0 :     if (NS_FAILED(rv))
     764               0 :       return false;
     765                 : 
     766               0 :     if (preventCachingOfSandwich) {
     767               0 :       aPreventCachingOfSandwich = true;
     768                 :     }
     769                 :   }
     770               0 :   return true;
     771                 : }
     772                 : 
     773                 : /*
     774                 :  * SMILANIM specifies the following rules for animation function values:
     775                 :  *
     776                 :  * (1) if values is set, it overrides everything
     777                 :  * (2) for from/to/by animation at least to or by must be specified, from on its
     778                 :  *     own (or nothing) is an error--which we will ignore
     779                 :  * (3) if both by and to are specified only to will be used, by will be ignored
     780                 :  * (4) if by is specified without from (by animation), forces additive behaviour
     781                 :  * (5) if to is specified without from (to animation), special care needs to be
     782                 :  *     taken when compositing animation as such animations are composited last.
     783                 :  *
     784                 :  * This helper method applies these rules to fill in the values list and to set
     785                 :  * some internal state.
     786                 :  */
     787                 : nsresult
     788               0 : nsSMILAnimationFunction::GetValues(const nsISMILAttr& aSMILAttr,
     789                 :                                    nsSMILValueArray& aResult)
     790                 : {
     791               0 :   if (!mAnimationElement)
     792               0 :     return NS_ERROR_FAILURE;
     793                 : 
     794               0 :   mValueNeedsReparsingEverySample = false;
     795               0 :   nsSMILValueArray result;
     796                 : 
     797                 :   // If "values" is set, use it
     798               0 :   if (HasAttr(nsGkAtoms::values)) {
     799               0 :     nsAutoString attValue;
     800               0 :     GetAttr(nsGkAtoms::values, attValue);
     801               0 :     bool preventCachingOfSandwich = false;
     802                 :     nsresult rv = nsSMILParserUtils::ParseValues(attValue, mAnimationElement,
     803                 :                                                  aSMILAttr, result,
     804               0 :                                                  preventCachingOfSandwich);
     805               0 :     if (NS_FAILED(rv))
     806               0 :       return rv;
     807                 : 
     808               0 :     if (preventCachingOfSandwich) {
     809               0 :       mValueNeedsReparsingEverySample = true;
     810                 :     }
     811                 :   // Else try to/from/by
     812                 :   } else {
     813               0 :     bool preventCachingOfSandwich = false;
     814               0 :     bool parseOk = true;
     815               0 :     nsSMILValue to, from, by;
     816                 :     parseOk &= ParseAttr(nsGkAtoms::to,   aSMILAttr, to,
     817               0 :                          preventCachingOfSandwich);
     818                 :     parseOk &= ParseAttr(nsGkAtoms::from, aSMILAttr, from,
     819               0 :                          preventCachingOfSandwich);
     820                 :     parseOk &= ParseAttr(nsGkAtoms::by,   aSMILAttr, by,
     821               0 :                          preventCachingOfSandwich);
     822                 :     
     823               0 :     if (preventCachingOfSandwich) {
     824               0 :       mValueNeedsReparsingEverySample = true;
     825                 :     }
     826                 : 
     827               0 :     if (!parseOk)
     828               0 :       return NS_ERROR_FAILURE;
     829                 : 
     830               0 :     result.SetCapacity(2);
     831               0 :     if (!to.IsNull()) {
     832               0 :       if (!from.IsNull()) {
     833               0 :         result.AppendElement(from);
     834               0 :         result.AppendElement(to);
     835                 :       } else {
     836               0 :         result.AppendElement(to);
     837                 :       }
     838               0 :     } else if (!by.IsNull()) {
     839               0 :       nsSMILValue effectiveFrom(by.mType);
     840               0 :       if (!from.IsNull())
     841               0 :         effectiveFrom = from;
     842                 :       // Set values to 'from; from + by'
     843               0 :       result.AppendElement(effectiveFrom);
     844               0 :       nsSMILValue effectiveTo(effectiveFrom);
     845               0 :       if (!effectiveTo.IsNull() && NS_SUCCEEDED(effectiveTo.Add(by))) {
     846               0 :         result.AppendElement(effectiveTo);
     847                 :       } else {
     848                 :         // Using by-animation with non-additive type or bad base-value
     849               0 :         return NS_ERROR_FAILURE;
     850                 :       }
     851                 :     } else {
     852                 :       // No values, no to, no by -- call it a day
     853               0 :       return NS_ERROR_FAILURE;
     854                 :     }
     855                 :   }
     856                 : 
     857               0 :   result.SwapElements(aResult);
     858                 : 
     859               0 :   return NS_OK;
     860                 : }
     861                 : 
     862                 : void
     863               0 : nsSMILAnimationFunction::CheckValueListDependentAttrs(PRUint32 aNumValues)
     864                 : {
     865               0 :   CheckKeyTimes(aNumValues);
     866               0 :   CheckKeySplines(aNumValues);
     867               0 : }
     868                 : 
     869                 : /**
     870                 :  * Performs checks for the keyTimes attribute required by the SMIL spec but
     871                 :  * which depend on other attributes and therefore needs to be updated as
     872                 :  * dependent attributes are set.
     873                 :  */
     874                 : void
     875               0 : nsSMILAnimationFunction::CheckKeyTimes(PRUint32 aNumValues)
     876                 : {
     877               0 :   if (!HasAttr(nsGkAtoms::keyTimes))
     878               0 :     return;
     879                 : 
     880               0 :   nsSMILCalcMode calcMode = GetCalcMode();
     881                 : 
     882                 :   // attribute is ignored for calcMode = paced
     883               0 :   if (calcMode == CALC_PACED) {
     884               0 :     SetKeyTimesErrorFlag(false);
     885               0 :     return;
     886                 :   }
     887                 : 
     888               0 :   PRUint32 numKeyTimes = mKeyTimes.Length();
     889               0 :   if (numKeyTimes < 1) {
     890                 :     // keyTimes isn't set or failed preliminary checks
     891               0 :     SetKeyTimesErrorFlag(true);
     892               0 :     return;
     893                 :   }
     894                 : 
     895                 :   // no. keyTimes == no. values
     896                 :   // For to-animation the number of values is considered to be 2.
     897                 :   bool matchingNumOfValues =
     898               0 :     numKeyTimes == (IsToAnimation() ? 2 : aNumValues);
     899               0 :   if (!matchingNumOfValues) {
     900               0 :     SetKeyTimesErrorFlag(true);
     901               0 :     return;
     902                 :   }
     903                 : 
     904                 :   // first value must be 0
     905               0 :   if (mKeyTimes[0] != 0.0) {
     906               0 :     SetKeyTimesErrorFlag(true);
     907               0 :     return;
     908                 :   }
     909                 : 
     910                 :   // last value must be 1 for linear or spline calcModes
     911               0 :   if (calcMode != CALC_DISCRETE && numKeyTimes > 1 &&
     912               0 :       mKeyTimes[numKeyTimes - 1] != 1.0) {
     913               0 :     SetKeyTimesErrorFlag(true);
     914               0 :     return;
     915                 :   }
     916                 : 
     917               0 :   SetKeyTimesErrorFlag(false);
     918                 : }
     919                 : 
     920                 : void
     921               0 : nsSMILAnimationFunction::CheckKeySplines(PRUint32 aNumValues)
     922                 : {
     923                 :   // attribute is ignored if calc mode is not spline
     924               0 :   if (GetCalcMode() != CALC_SPLINE) {
     925               0 :     SetKeySplinesErrorFlag(false);
     926               0 :     return;
     927                 :   }
     928                 : 
     929                 :   // calc mode is spline but the attribute is not set
     930               0 :   if (!HasAttr(nsGkAtoms::keySplines)) {
     931               0 :     SetKeySplinesErrorFlag(false);
     932               0 :     return;
     933                 :   }
     934                 : 
     935               0 :   if (mKeySplines.Length() < 1) {
     936                 :     // keyTimes isn't set or failed preliminary checks
     937               0 :     SetKeySplinesErrorFlag(true);
     938               0 :     return;
     939                 :   }
     940                 : 
     941                 :   // ignore splines if there's only one value
     942               0 :   if (aNumValues == 1 && !IsToAnimation()) {
     943               0 :     SetKeySplinesErrorFlag(false);
     944               0 :     return;
     945                 :   }
     946                 : 
     947                 :   // no. keySpline specs == no. values - 1
     948               0 :   PRUint32 splineSpecs = mKeySplines.Length();
     949               0 :   if ((splineSpecs != aNumValues - 1 && !IsToAnimation()) ||
     950               0 :       (IsToAnimation() && splineSpecs != 1)) {
     951               0 :     SetKeySplinesErrorFlag(true);
     952               0 :     return;
     953                 :   }
     954                 : 
     955               0 :   SetKeySplinesErrorFlag(false);
     956                 : }
     957                 : 
     958                 : bool
     959               0 : nsSMILAnimationFunction::IsValueFixedForSimpleDuration() const
     960                 : {
     961               0 :   return mSimpleDuration.IsIndefinite() ||
     962               0 :     (!mHasChanged && mPrevSampleWasSingleValueAnimation);
     963                 : }
     964                 : 
     965                 : //----------------------------------------------------------------------
     966                 : // Property getters
     967                 : 
     968                 : bool
     969               0 : nsSMILAnimationFunction::GetAccumulate() const
     970                 : {
     971               0 :   const nsAttrValue* value = GetAttr(nsGkAtoms::accumulate);
     972               0 :   if (!value)
     973               0 :     return false;
     974                 : 
     975               0 :   return value->GetEnumValue();
     976                 : }
     977                 : 
     978                 : bool
     979               0 : nsSMILAnimationFunction::GetAdditive() const
     980                 : {
     981               0 :   const nsAttrValue* value = GetAttr(nsGkAtoms::additive);
     982               0 :   if (!value)
     983               0 :     return false;
     984                 : 
     985               0 :   return value->GetEnumValue();
     986                 : }
     987                 : 
     988                 : nsSMILAnimationFunction::nsSMILCalcMode
     989               0 : nsSMILAnimationFunction::GetCalcMode() const
     990                 : {
     991               0 :   const nsAttrValue* value = GetAttr(nsGkAtoms::calcMode);
     992               0 :   if (!value)
     993               0 :     return CALC_LINEAR;
     994                 : 
     995               0 :   return nsSMILCalcMode(value->GetEnumValue());
     996                 : }
     997                 : 
     998                 : //----------------------------------------------------------------------
     999                 : // Property setters / un-setters:
    1000                 : 
    1001                 : nsresult
    1002               0 : nsSMILAnimationFunction::SetAccumulate(const nsAString& aAccumulate,
    1003                 :                                        nsAttrValue& aResult)
    1004                 : {
    1005               0 :   mHasChanged = true;
    1006                 :   bool parseResult =
    1007               0 :     aResult.ParseEnumValue(aAccumulate, sAccumulateTable, true);
    1008               0 :   SetAccumulateErrorFlag(!parseResult);
    1009               0 :   return parseResult ? NS_OK : NS_ERROR_FAILURE;
    1010                 : }
    1011                 : 
    1012                 : void
    1013               0 : nsSMILAnimationFunction::UnsetAccumulate()
    1014                 : {
    1015               0 :   SetAccumulateErrorFlag(false);
    1016               0 :   mHasChanged = true;
    1017               0 : }
    1018                 : 
    1019                 : nsresult
    1020               0 : nsSMILAnimationFunction::SetAdditive(const nsAString& aAdditive,
    1021                 :                                      nsAttrValue& aResult)
    1022                 : {
    1023               0 :   mHasChanged = true;
    1024                 :   bool parseResult
    1025               0 :     = aResult.ParseEnumValue(aAdditive, sAdditiveTable, true);
    1026               0 :   SetAdditiveErrorFlag(!parseResult);
    1027               0 :   return parseResult ? NS_OK : NS_ERROR_FAILURE;
    1028                 : }
    1029                 : 
    1030                 : void
    1031               0 : nsSMILAnimationFunction::UnsetAdditive()
    1032                 : {
    1033               0 :   SetAdditiveErrorFlag(false);
    1034               0 :   mHasChanged = true;
    1035               0 : }
    1036                 : 
    1037                 : nsresult
    1038               0 : nsSMILAnimationFunction::SetCalcMode(const nsAString& aCalcMode,
    1039                 :                                      nsAttrValue& aResult)
    1040                 : {
    1041               0 :   mHasChanged = true;
    1042                 :   bool parseResult
    1043               0 :     = aResult.ParseEnumValue(aCalcMode, sCalcModeTable, true);
    1044               0 :   SetCalcModeErrorFlag(!parseResult);
    1045               0 :   return parseResult ? NS_OK : NS_ERROR_FAILURE;
    1046                 : }
    1047                 : 
    1048                 : void
    1049               0 : nsSMILAnimationFunction::UnsetCalcMode()
    1050                 : {
    1051               0 :   SetCalcModeErrorFlag(false);
    1052               0 :   mHasChanged = true;
    1053               0 : }
    1054                 : 
    1055                 : nsresult
    1056               0 : nsSMILAnimationFunction::SetKeySplines(const nsAString& aKeySplines,
    1057                 :                                        nsAttrValue& aResult)
    1058                 : {
    1059               0 :   mKeySplines.Clear();
    1060               0 :   aResult.SetTo(aKeySplines);
    1061                 : 
    1062               0 :   nsTArray<double> keySplines;
    1063               0 :   nsresult rv = nsSMILParserUtils::ParseKeySplines(aKeySplines, keySplines);
    1064                 : 
    1065               0 :   if (keySplines.Length() < 1 || keySplines.Length() % 4)
    1066               0 :     rv = NS_ERROR_FAILURE;
    1067                 : 
    1068               0 :   if (NS_SUCCEEDED(rv))
    1069                 :   {
    1070               0 :     mKeySplines.SetCapacity(keySplines.Length() % 4);
    1071               0 :     for (PRUint32 i = 0; i < keySplines.Length() && NS_SUCCEEDED(rv); i += 4)
    1072                 :     {
    1073               0 :       if (!mKeySplines.AppendElement(nsSMILKeySpline(keySplines[i],
    1074               0 :                                                      keySplines[i+1],
    1075               0 :                                                      keySplines[i+2],
    1076               0 :                                                      keySplines[i+3]))) {
    1077               0 :         rv = NS_ERROR_OUT_OF_MEMORY;
    1078                 :       }
    1079                 :     }
    1080                 :   }
    1081                 : 
    1082               0 :   mHasChanged = true;
    1083                 : 
    1084               0 :   return rv;
    1085                 : }
    1086                 : 
    1087                 : void
    1088               0 : nsSMILAnimationFunction::UnsetKeySplines()
    1089                 : {
    1090               0 :   mKeySplines.Clear();
    1091               0 :   SetKeySplinesErrorFlag(false);
    1092               0 :   mHasChanged = true;
    1093               0 : }
    1094                 : 
    1095                 : nsresult
    1096               0 : nsSMILAnimationFunction::SetKeyTimes(const nsAString& aKeyTimes,
    1097                 :                                      nsAttrValue& aResult)
    1098                 : {
    1099               0 :   mKeyTimes.Clear();
    1100               0 :   aResult.SetTo(aKeyTimes);
    1101                 : 
    1102                 :   nsresult rv =
    1103                 :     nsSMILParserUtils::ParseSemicolonDelimitedProgressList(aKeyTimes, true,
    1104               0 :                                                            mKeyTimes);
    1105                 : 
    1106               0 :   if (NS_SUCCEEDED(rv) && mKeyTimes.Length() < 1)
    1107               0 :     rv = NS_ERROR_FAILURE;
    1108                 : 
    1109               0 :   if (NS_FAILED(rv))
    1110               0 :     mKeyTimes.Clear();
    1111                 : 
    1112               0 :   mHasChanged = true;
    1113                 : 
    1114               0 :   return NS_OK;
    1115                 : }
    1116                 : 
    1117                 : void
    1118               0 : nsSMILAnimationFunction::UnsetKeyTimes()
    1119                 : {
    1120               0 :   mKeyTimes.Clear();
    1121               0 :   SetKeyTimesErrorFlag(false);
    1122               0 :   mHasChanged = true;
    1123               0 : }

Generated by: LCOV version 1.7