LCOV - code coverage report
Current view: directory - layout/style - nsAnimationManager.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 348 0 0.0 %
Date: 2012-06-02 Functions: 42 0 0.0 %

       1                 : /* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
       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 nsAnimationManager, an implementation of part
      16                 :  * of css3-animations.
      17                 :  *
      18                 :  * The Initial Developer of the Original Code is the Mozilla Foundation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 2011
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *   L. David Baron <dbaron@dbaron.org>, Mozilla Corporation (original author)
      24                 :  *
      25                 :  * Alternatively, the contents of this file may be used under the terms of
      26                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      27                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      28                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      29                 :  * of those above. If you wish to allow use of your version of this file only
      30                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      31                 :  * use your version of this file under the terms of the MPL, indicate your
      32                 :  * decision by deleting the provisions above and replace them with the notice
      33                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      34                 :  * the provisions above, a recipient may use your version of this file under
      35                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      36                 :  *
      37                 :  * ***** END LICENSE BLOCK ***** */
      38                 : 
      39                 : #include "nsAnimationManager.h"
      40                 : #include "nsPresContext.h"
      41                 : #include "nsRuleProcessorData.h"
      42                 : #include "nsStyleSet.h"
      43                 : #include "nsCSSRules.h"
      44                 : #include "nsStyleAnimation.h"
      45                 : #include "nsSMILKeySpline.h"
      46                 : #include "nsEventDispatcher.h"
      47                 : 
      48                 : using namespace mozilla;
      49                 : 
      50                 : struct AnimationPropertySegment
      51               0 : {
      52                 :   float mFromKey, mToKey;
      53                 :   nsStyleAnimation::Value mFromValue, mToValue;
      54                 :   css::ComputedTimingFunction mTimingFunction;
      55                 : };
      56                 : 
      57                 : struct AnimationProperty
      58               0 : {
      59                 :   nsCSSProperty mProperty;
      60                 :   InfallibleTArray<AnimationPropertySegment> mSegments;
      61                 : };
      62                 : 
      63                 : /**
      64                 :  * Data about one animation (i.e., one of the values of
      65                 :  * 'animation-name') running on an element.
      66                 :  */
      67                 : struct ElementAnimation
      68               0 : {
      69               0 :   ElementAnimation()
      70               0 :     : mLastNotification(LAST_NOTIFICATION_NONE)
      71                 :   {
      72               0 :   }
      73                 : 
      74                 :   nsString mName; // empty string for 'none'
      75                 :   float mIterationCount; // NS_IEEEPositiveInfinity() means infinite
      76                 :   PRUint8 mDirection;
      77                 :   PRUint8 mFillMode;
      78                 :   PRUint8 mPlayState;
      79                 : 
      80               0 :   bool FillsForwards() const {
      81                 :     return mFillMode == NS_STYLE_ANIMATION_FILL_MODE_BOTH ||
      82               0 :            mFillMode == NS_STYLE_ANIMATION_FILL_MODE_FORWARDS;
      83                 :   }
      84               0 :   bool FillsBackwards() const {
      85                 :     return mFillMode == NS_STYLE_ANIMATION_FILL_MODE_BOTH ||
      86               0 :            mFillMode == NS_STYLE_ANIMATION_FILL_MODE_BACKWARDS;
      87                 :   }
      88                 : 
      89               0 :   bool IsPaused() const {
      90               0 :     return mPlayState == NS_STYLE_ANIMATION_PLAY_STATE_PAUSED;
      91                 :   }
      92                 : 
      93                 :   TimeStamp mStartTime; // with delay taken into account
      94                 :   TimeStamp mPauseStart;
      95                 :   TimeDuration mIterationDuration;
      96                 : 
      97                 :   enum {
      98                 :     LAST_NOTIFICATION_NONE = PRUint32(-1),
      99                 :     LAST_NOTIFICATION_END = PRUint32(-2)
     100                 :   };
     101                 :   // One of the above constants, or an integer for the iteration
     102                 :   // whose start we last notified on.
     103                 :   PRUint32 mLastNotification;
     104                 : 
     105                 :   InfallibleTArray<AnimationProperty> mProperties;
     106                 : };
     107                 : 
     108                 : typedef nsAnimationManager::EventArray EventArray;
     109                 : typedef nsAnimationManager::AnimationEventInfo AnimationEventInfo;
     110                 : 
     111                 : /**
     112                 :  * Data about all of the animations running on an element.
     113                 :  */
     114                 : struct ElementAnimations : public mozilla::css::CommonElementAnimationData
     115               0 : {
     116               0 :   ElementAnimations(dom::Element *aElement, nsIAtom *aElementProperty,
     117                 :                      nsAnimationManager *aAnimationManager)
     118                 :     : CommonElementAnimationData(aElement, aElementProperty,
     119                 :                                  aAnimationManager),
     120               0 :       mNeedsRefreshes(true)
     121                 :   {
     122               0 :   }
     123                 : 
     124                 :   void EnsureStyleRuleFor(TimeStamp aRefreshTime,
     125                 :                           EventArray &aEventsToDispatch);
     126                 : 
     127               0 :   bool IsForElement() const { // rather than for a pseudo-element
     128               0 :     return mElementProperty == nsGkAtoms::animationsProperty;
     129                 :   }
     130                 : 
     131               0 :   void PostRestyleForAnimation(nsPresContext *aPresContext) {
     132               0 :     nsRestyleHint hint = IsForElement() ? eRestyle_Self : eRestyle_Subtree;
     133               0 :     aPresContext->PresShell()->RestyleForAnimation(mElement, hint);
     134               0 :   }
     135                 : 
     136                 :   // This style rule contains the style data for currently animating
     137                 :   // values.  It only matches when styling with animation.  When we
     138                 :   // style without animation, we need to not use it so that we can
     139                 :   // detect any new changes; if necessary we restyle immediately
     140                 :   // afterwards with animation.
     141                 :   // NOTE: If we don't need to apply any styles, mStyleRule will be
     142                 :   // null, but mStyleRuleRefreshTime will still be valid.
     143                 :   nsRefPtr<css::AnimValuesStyleRule> mStyleRule;
     144                 :   // The refresh time associated with mStyleRule.
     145                 :   TimeStamp mStyleRuleRefreshTime;
     146                 : 
     147                 :   // False when we know that our current style rule is valid
     148                 :   // indefinitely into the future (because all of our animations are
     149                 :   // either completed or paused).  May be invalidated by a style change.
     150                 :   bool mNeedsRefreshes;
     151                 : 
     152                 :   InfallibleTArray<ElementAnimation> mAnimations;
     153                 : };
     154                 : 
     155                 : static void
     156               0 : ElementAnimationsPropertyDtor(void           *aObject,
     157                 :                               nsIAtom        *aPropertyName,
     158                 :                               void           *aPropertyValue,
     159                 :                               void           *aData)
     160                 : {
     161               0 :   ElementAnimations *ea = static_cast<ElementAnimations*>(aPropertyValue);
     162                 : #ifdef DEBUG
     163               0 :   NS_ABORT_IF_FALSE(!ea->mCalledPropertyDtor, "can't call dtor twice");
     164               0 :   ea->mCalledPropertyDtor = true;
     165                 : #endif
     166               0 :   delete ea;
     167               0 : }
     168                 : 
     169                 : void
     170               0 : ElementAnimations::EnsureStyleRuleFor(TimeStamp aRefreshTime,
     171                 :                                       EventArray& aEventsToDispatch)
     172                 : {
     173               0 :   if (!mNeedsRefreshes) {
     174                 :     // All of our animations are paused or completed.
     175               0 :     mStyleRuleRefreshTime = aRefreshTime;
     176               0 :     return;
     177                 :   }
     178                 : 
     179                 :   // mStyleRule may be null and valid, if we have no style to apply.
     180               0 :   if (mStyleRuleRefreshTime.IsNull() ||
     181               0 :       mStyleRuleRefreshTime != aRefreshTime) {
     182               0 :     mStyleRuleRefreshTime = aRefreshTime;
     183               0 :     mStyleRule = nsnull;
     184                 :     // We'll set mNeedsRefreshes to true below in all cases where we need them.
     185               0 :     mNeedsRefreshes = false;
     186                 : 
     187                 :     // FIXME(spec): assume that properties in higher animations override
     188                 :     // those in lower ones.
     189                 :     // Therefore, we iterate from last animation to first.
     190               0 :     nsCSSPropertySet properties;
     191                 : 
     192               0 :     for (PRUint32 animIdx = mAnimations.Length(); animIdx-- != 0; ) {
     193               0 :       ElementAnimation &anim = mAnimations[animIdx];
     194                 : 
     195               0 :       if (anim.mProperties.Length() == 0 ||
     196               0 :           anim.mIterationDuration.ToMilliseconds() <= 0.0) {
     197                 :         // No animation data.
     198               0 :         continue;
     199                 :       }
     200                 : 
     201               0 :       TimeDuration currentTimeDuration;
     202               0 :       if (anim.IsPaused()) {
     203                 :         // FIXME: avoid recalculating every time
     204               0 :         currentTimeDuration = anim.mPauseStart - anim.mStartTime;
     205                 :       } else {
     206               0 :         currentTimeDuration = aRefreshTime - anim.mStartTime;
     207                 :       }
     208                 : 
     209                 :       // Set |currentIterationCount| to the (fractional) number of
     210                 :       // iterations we've completed up to the current position.
     211                 :       double currentIterationCount =
     212               0 :         currentTimeDuration / anim.mIterationDuration;
     213               0 :       bool dispatchStartOrIteration = false;
     214               0 :       if (currentIterationCount >= double(anim.mIterationCount)) {
     215                 :         // Dispatch 'animationend' when needed.
     216               0 :         if (IsForElement() && 
     217                 :             anim.mLastNotification !=
     218                 :               ElementAnimation::LAST_NOTIFICATION_END) {
     219               0 :           anim.mLastNotification = ElementAnimation::LAST_NOTIFICATION_END;
     220                 :           AnimationEventInfo ei(mElement, anim.mName, NS_ANIMATION_END,
     221               0 :                                 currentTimeDuration);
     222               0 :           aEventsToDispatch.AppendElement(ei);
     223                 :         }
     224                 : 
     225               0 :         if (!anim.FillsForwards()) {
     226                 :           // No animation data.
     227               0 :           continue;
     228                 :         }
     229               0 :         currentIterationCount = double(anim.mIterationCount);
     230                 :       } else {
     231               0 :         if (!anim.IsPaused()) {
     232               0 :           mNeedsRefreshes = true;
     233                 :         }
     234               0 :         if (currentIterationCount < 0.0) {
     235               0 :           if (!anim.FillsBackwards()) {
     236                 :             // No animation data.
     237               0 :             continue;
     238                 :           }
     239               0 :           currentIterationCount = 0.0;
     240                 :         } else {
     241               0 :           dispatchStartOrIteration = !anim.IsPaused();
     242                 :         }
     243                 :       }
     244                 : 
     245                 :       // Set |positionInIteration| to the position from 0% to 100% along
     246                 :       // the keyframes.
     247               0 :       NS_ABORT_IF_FALSE(currentIterationCount >= 0.0, "must be positive");
     248               0 :       PRUint32 whichIteration = int(currentIterationCount);
     249               0 :       if (whichIteration == anim.mIterationCount) {
     250                 :         // When the animation's iteration count is an integer (as it
     251                 :         // normally is), we need to end at 100% of its last iteration
     252                 :         // rather than 0% of the next one.
     253               0 :         --whichIteration;
     254                 :       }
     255                 :       double positionInIteration =
     256               0 :         currentIterationCount - double(whichIteration);
     257               0 :       if (anim.mDirection == NS_STYLE_ANIMATION_DIRECTION_ALTERNATE &&
     258                 :           (whichIteration & 1) == 1) {
     259               0 :         positionInIteration = 1.0 - positionInIteration;
     260                 :       }
     261                 : 
     262                 :       // Dispatch 'animationstart' or 'animationiteration' when needed.
     263               0 :       if (IsForElement() && dispatchStartOrIteration &&
     264                 :           whichIteration != anim.mLastNotification) {
     265                 :         // Notify 'animationstart' even if a negative delay puts us
     266                 :         // past the first iteration.
     267                 :         // Note that when somebody changes the animation-duration
     268                 :         // dynamically, this will fire an extra iteration event
     269                 :         // immediately in many cases.  It's not clear to me if that's the
     270                 :         // right thing to do.
     271                 :         PRUint32 message =
     272                 :           anim.mLastNotification == ElementAnimation::LAST_NOTIFICATION_NONE
     273               0 :             ? NS_ANIMATION_START : NS_ANIMATION_ITERATION;
     274               0 :         anim.mLastNotification = whichIteration;
     275                 :         AnimationEventInfo ei(mElement, anim.mName, message,
     276               0 :                               currentTimeDuration);
     277               0 :         aEventsToDispatch.AppendElement(ei);
     278                 :       }
     279                 : 
     280               0 :       NS_ABORT_IF_FALSE(0.0 <= positionInIteration &&
     281                 :                           positionInIteration <= 1.0,
     282                 :                         "position should be in [0-1]");
     283                 : 
     284               0 :       for (PRUint32 propIdx = 0, propEnd = anim.mProperties.Length();
     285                 :            propIdx != propEnd; ++propIdx)
     286                 :       {
     287               0 :         const AnimationProperty &prop = anim.mProperties[propIdx];
     288                 : 
     289               0 :         NS_ABORT_IF_FALSE(prop.mSegments[0].mFromKey == 0.0,
     290                 :                           "incorrect first from key");
     291               0 :         NS_ABORT_IF_FALSE(prop.mSegments[prop.mSegments.Length() - 1].mToKey
     292                 :                             == 1.0,
     293                 :                           "incorrect last to key");
     294                 : 
     295               0 :         if (properties.HasProperty(prop.mProperty)) {
     296                 :           // A later animation already set this property.
     297               0 :           continue;
     298                 :         }
     299               0 :         properties.AddProperty(prop.mProperty);
     300                 : 
     301               0 :         NS_ABORT_IF_FALSE(prop.mSegments.Length() > 0,
     302                 :                           "property should not be in animations if it "
     303                 :                           "has no segments");
     304                 : 
     305                 :         // FIXME: Maybe cache the current segment?
     306               0 :         const AnimationPropertySegment *segment = prop.mSegments.Elements();
     307               0 :         while (segment->mToKey < positionInIteration) {
     308               0 :           NS_ABORT_IF_FALSE(segment->mFromKey < segment->mToKey,
     309                 :                             "incorrect keys");
     310               0 :           ++segment;
     311               0 :           NS_ABORT_IF_FALSE(segment->mFromKey == (segment-1)->mToKey,
     312                 :                             "incorrect keys");
     313                 :         }
     314               0 :         NS_ABORT_IF_FALSE(segment->mFromKey < segment->mToKey,
     315                 :                           "incorrect keys");
     316               0 :         NS_ABORT_IF_FALSE(segment - prop.mSegments.Elements() <
     317                 :                             prop.mSegments.Length(),
     318                 :                           "ran off end");
     319                 : 
     320               0 :         if (!mStyleRule) {
     321                 :           // Allocate the style rule now that we know we have animation data.
     322               0 :           mStyleRule = new css::AnimValuesStyleRule();
     323                 :         }
     324                 : 
     325                 :         double positionInSegment = (positionInIteration - segment->mFromKey) /
     326               0 :                                    (segment->mToKey - segment->mFromKey);
     327                 :         double valuePosition =
     328               0 :           segment->mTimingFunction.GetValue(positionInSegment);
     329                 : 
     330                 :         nsStyleAnimation::Value *val =
     331               0 :           mStyleRule->AddEmptyValue(prop.mProperty);
     332                 : 
     333                 : #ifdef DEBUG
     334                 :         bool result =
     335                 : #endif
     336                 :           nsStyleAnimation::Interpolate(prop.mProperty,
     337                 :                                         segment->mFromValue, segment->mToValue,
     338               0 :                                         valuePosition, *val);
     339               0 :         NS_ABORT_IF_FALSE(result, "interpolate must succeed now");
     340                 :       }
     341                 :     }
     342                 :   }
     343                 : }
     344                 : 
     345                 : ElementAnimations*
     346               0 : nsAnimationManager::GetElementAnimations(dom::Element *aElement,
     347                 :                                          nsCSSPseudoElements::Type aPseudoType,
     348                 :                                          bool aCreateIfNeeded)
     349                 : {
     350               0 :   if (!aCreateIfNeeded && PR_CLIST_IS_EMPTY(&mElementData)) {
     351                 :     // Early return for the most common case.
     352               0 :     return nsnull;
     353                 :   }
     354                 : 
     355                 :   nsIAtom *propName;
     356               0 :   if (aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement) {
     357               0 :     propName = nsGkAtoms::animationsProperty;
     358               0 :   } else if (aPseudoType == nsCSSPseudoElements::ePseudo_before) {
     359               0 :     propName = nsGkAtoms::animationsOfBeforeProperty;
     360               0 :   } else if (aPseudoType == nsCSSPseudoElements::ePseudo_after) {
     361               0 :     propName = nsGkAtoms::animationsOfAfterProperty;
     362                 :   } else {
     363               0 :     NS_ASSERTION(!aCreateIfNeeded,
     364                 :                  "should never try to create transitions for pseudo "
     365                 :                  "other than :before or :after");
     366               0 :     return nsnull;
     367                 :   }
     368                 :   ElementAnimations *ea = static_cast<ElementAnimations*>(
     369               0 :                              aElement->GetProperty(propName));
     370               0 :   if (!ea && aCreateIfNeeded) {
     371                 :     // FIXME: Consider arena-allocating?
     372               0 :     ea = new ElementAnimations(aElement, propName, this);
     373               0 :     if (!ea) {
     374               0 :       NS_WARNING("out of memory");
     375               0 :       return nsnull;
     376                 :     }
     377                 :     nsresult rv = aElement->SetProperty(propName, ea,
     378               0 :                                         ElementAnimationsPropertyDtor, nsnull);
     379               0 :     if (NS_FAILED(rv)) {
     380               0 :       NS_WARNING("SetProperty failed");
     381               0 :       delete ea;
     382               0 :       return nsnull;
     383                 :     }
     384                 : 
     385               0 :     AddElementData(ea);
     386                 :   }
     387                 : 
     388               0 :   return ea;
     389                 : }
     390                 : 
     391                 : /* virtual */ void
     392               0 : nsAnimationManager::RulesMatching(ElementRuleProcessorData* aData)
     393                 : {
     394               0 :   NS_ABORT_IF_FALSE(aData->mPresContext == mPresContext,
     395                 :                     "pres context mismatch");
     396                 :   nsIStyleRule *rule =
     397                 :     GetAnimationRule(aData->mElement,
     398               0 :                      nsCSSPseudoElements::ePseudo_NotPseudoElement);
     399               0 :   if (rule) {
     400               0 :     aData->mRuleWalker->Forward(rule);
     401                 :   }
     402               0 : }
     403                 : 
     404                 : /* virtual */ void
     405               0 : nsAnimationManager::RulesMatching(PseudoElementRuleProcessorData* aData)
     406                 : {
     407               0 :   NS_ABORT_IF_FALSE(aData->mPresContext == mPresContext,
     408                 :                     "pres context mismatch");
     409               0 :   if (aData->mPseudoType != nsCSSPseudoElements::ePseudo_before &&
     410                 :       aData->mPseudoType != nsCSSPseudoElements::ePseudo_after) {
     411               0 :     return;
     412                 :   }
     413                 : 
     414                 :   // FIXME: Do we really want to be the only thing keeping a
     415                 :   // pseudo-element alive?  I *think* the non-animation restyle should
     416                 :   // handle that, but should add a test.
     417               0 :   nsIStyleRule *rule = GetAnimationRule(aData->mElement, aData->mPseudoType);
     418               0 :   if (rule) {
     419               0 :     aData->mRuleWalker->Forward(rule);
     420                 :   }
     421                 : }
     422                 : 
     423                 : /* virtual */ void
     424               0 : nsAnimationManager::RulesMatching(AnonBoxRuleProcessorData* aData)
     425                 : {
     426               0 : }
     427                 : 
     428                 : #ifdef MOZ_XUL
     429                 : /* virtual */ void
     430               0 : nsAnimationManager::RulesMatching(XULTreeRuleProcessorData* aData)
     431                 : {
     432               0 : }
     433                 : #endif
     434                 : 
     435                 : /* virtual */ size_t
     436               0 : nsAnimationManager::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const
     437                 : {
     438               0 :   return CommonAnimationManager::SizeOfExcludingThis(aMallocSizeOf);
     439                 : 
     440                 :   // Measurement of the following members may be added later if DMD finds it is
     441                 :   // worthwhile:
     442                 :   // - mKeyframesRules
     443                 :   // - mPendingEvents
     444                 : }
     445                 : 
     446                 : /* virtual */ size_t
     447               0 : nsAnimationManager::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const
     448                 : {
     449               0 :   return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
     450                 : }
     451                 : 
     452                 : nsIStyleRule*
     453               0 : nsAnimationManager::CheckAnimationRule(nsStyleContext* aStyleContext,
     454                 :                                        mozilla::dom::Element* aElement)
     455                 : {
     456               0 :   if (!mPresContext->IsProcessingAnimationStyleChange()) {
     457                 :     // Everything that causes our animation data to change triggers a
     458                 :     // style change, which in turn triggers a non-animation restyle.
     459                 :     // Likewise, when we initially construct frames, we're not in a
     460                 :     // style change, but also not in an animation restyle.
     461                 : 
     462               0 :     const nsStyleDisplay *disp = aStyleContext->GetStyleDisplay();
     463                 :     ElementAnimations *ea =
     464               0 :       GetElementAnimations(aElement, aStyleContext->GetPseudoType(), false);
     465               0 :     if (!ea &&
     466               0 :         disp->mAnimations.Length() == 1 &&
     467               0 :         disp->mAnimations[0].GetName().IsEmpty()) {
     468               0 :       return nsnull;
     469                 :     }
     470                 : 
     471                 :     // build the animations list
     472               0 :     InfallibleTArray<ElementAnimation> newAnimations;
     473               0 :     BuildAnimations(aStyleContext, newAnimations);
     474                 : 
     475               0 :     if (newAnimations.IsEmpty()) {
     476               0 :       if (ea) {
     477               0 :         ea->Destroy();
     478                 :       }
     479               0 :       return nsnull;
     480                 :     }
     481                 : 
     482               0 :     TimeStamp refreshTime = mPresContext->RefreshDriver()->MostRecentRefresh();
     483                 : 
     484               0 :     if (ea) {
     485                 :       // The cached style rule is invalid.
     486               0 :       ea->mStyleRule = nsnull;
     487               0 :       ea->mStyleRuleRefreshTime = TimeStamp();
     488                 : 
     489                 :       // Copy over the start times and (if still paused) pause starts
     490                 :       // for each animation (matching on name only) that was also in the
     491                 :       // old list of animations.
     492                 :       // This means that we honor dynamic changes, which isn't what the
     493                 :       // spec says to do, but WebKit seems to honor at least some of
     494                 :       // them.  See
     495                 :       // http://lists.w3.org/Archives/Public/www-style/2011Apr/0079.html
     496                 :       // In order to honor what the spec said, we'd copy more data over
     497                 :       // (or potentially optimize BuildAnimations to avoid rebuilding it
     498                 :       // in the first place).
     499               0 :       if (!ea->mAnimations.IsEmpty()) {
     500               0 :         for (PRUint32 newIdx = 0, newEnd = newAnimations.Length();
     501                 :              newIdx != newEnd; ++newIdx) {
     502               0 :           ElementAnimation *newAnim = &newAnimations[newIdx];
     503                 : 
     504                 :           // Find the matching animation with this name in the old list
     505                 :           // of animations.  Because of this code, they must all have
     506                 :           // the same start time, though they might differ in pause
     507                 :           // state.  So if a page uses multiple copies of the same
     508                 :           // animation in one element's animation list, and gives them
     509                 :           // different pause states, they, well, get what they deserve.
     510                 :           // We'll use the last one since it's more likely to be the one
     511                 :           // doing something.
     512               0 :           const ElementAnimation *oldAnim = nsnull;
     513               0 :           for (PRUint32 oldIdx = ea->mAnimations.Length(); oldIdx-- != 0; ) {
     514               0 :             const ElementAnimation *a = &ea->mAnimations[oldIdx];
     515               0 :             if (a->mName == newAnim->mName) {
     516               0 :               oldAnim = a;
     517               0 :               break;
     518                 :             }
     519                 :           }
     520               0 :           if (!oldAnim) {
     521               0 :             continue;
     522                 :           }
     523                 : 
     524               0 :           newAnim->mStartTime = oldAnim->mStartTime;
     525               0 :           newAnim->mLastNotification = oldAnim->mLastNotification;
     526                 : 
     527               0 :           if (oldAnim->IsPaused()) {
     528               0 :             if (newAnim->IsPaused()) {
     529                 :               // Copy pause start just like start time.
     530               0 :               newAnim->mPauseStart = oldAnim->mPauseStart;
     531                 :             } else {
     532                 :               // Handle change in pause state by adjusting start
     533                 :               // time to unpause.
     534               0 :               newAnim->mStartTime += refreshTime - oldAnim->mPauseStart;
     535                 :             }
     536                 :           }
     537                 :         }
     538                 :       }
     539                 :     } else {
     540                 :       ea = GetElementAnimations(aElement, aStyleContext->GetPseudoType(),
     541               0 :                                 true);
     542                 :     }
     543               0 :     ea->mAnimations.SwapElements(newAnimations);
     544               0 :     ea->mNeedsRefreshes = true;
     545                 : 
     546               0 :     ea->EnsureStyleRuleFor(refreshTime, mPendingEvents);
     547                 :     // We don't actually dispatch the mPendingEvents now.  We'll either
     548                 :     // dispatch them the next time we get a refresh driver notification
     549                 :     // or the next time somebody calls
     550                 :     // nsPresShell::FlushPendingNotifications.
     551               0 :     if (!mPendingEvents.IsEmpty()) {
     552               0 :       mPresContext->Document()->SetNeedStyleFlush();
     553                 :     }
     554                 :   }
     555                 : 
     556               0 :   return GetAnimationRule(aElement, aStyleContext->GetPseudoType());
     557                 : }
     558                 : 
     559                 : class PercentageHashKey : public PLDHashEntryHdr
     560                 : {
     561                 : public:
     562                 :   typedef const float& KeyType;
     563                 :   typedef const float* KeyTypePointer;
     564                 : 
     565               0 :   PercentageHashKey(KeyTypePointer aKey) : mValue(*aKey) { }
     566                 :   PercentageHashKey(const PercentageHashKey& toCopy) : mValue(toCopy.mValue) { }
     567               0 :   ~PercentageHashKey() { }
     568                 : 
     569               0 :   KeyType GetKey() const { return mValue; }
     570               0 :   bool KeyEquals(KeyTypePointer aKey) const { return *aKey == mValue; }
     571                 : 
     572               0 :   static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
     573               0 :   static PLDHashNumber HashKey(KeyTypePointer aKey) {
     574                 :     MOZ_STATIC_ASSERT(sizeof(PLDHashNumber) == sizeof(PRUint32),
     575                 :                       "this hash function assumes PLDHashNumber is PRUint32");
     576                 :     MOZ_STATIC_ASSERT(PLDHashNumber(-1) > PLDHashNumber(0),
     577                 :                       "this hash function assumes PLDHashNumber is PRUint32");
     578               0 :     float key = *aKey;
     579               0 :     NS_ABORT_IF_FALSE(0.0f <= key && key <= 1.0f, "out of range");
     580               0 :     return PLDHashNumber(key * PR_UINT32_MAX);
     581                 :   }
     582                 :   enum { ALLOW_MEMMOVE = true };
     583                 : 
     584                 : private:
     585                 :   const float mValue;
     586                 : };
     587                 : 
     588               0 : struct KeyframeData {
     589                 :   float mKey;
     590                 :   nsCSSKeyframeRule *mRule;
     591                 : };
     592                 : 
     593                 : typedef InfallibleTArray<KeyframeData> KeyframeDataArray;
     594                 : 
     595                 : static PLDHashOperator
     596               0 : AppendKeyframeData(const float &aKey, nsCSSKeyframeRule *aRule, void *aData)
     597                 : {
     598               0 :   KeyframeDataArray *array = static_cast<KeyframeDataArray*>(aData);
     599               0 :   KeyframeData *data = array->AppendElement();
     600               0 :   data->mKey = aKey;
     601               0 :   data->mRule = aRule;
     602               0 :   return PL_DHASH_NEXT;
     603                 : }
     604                 : 
     605                 : struct KeyframeDataComparator {
     606               0 :   bool Equals(const KeyframeData& A, const KeyframeData& B) const {
     607               0 :     return A.mKey == B.mKey;
     608                 :   }
     609               0 :   bool LessThan(const KeyframeData& A, const KeyframeData& B) const {
     610               0 :     return A.mKey < B.mKey;
     611                 :   }
     612                 : };
     613                 : 
     614               0 : class ResolvedStyleCache {
     615                 : public:
     616               0 :   ResolvedStyleCache() {
     617               0 :     mCache.Init(16); // FIXME: make infallible!
     618               0 :   }
     619                 :   nsStyleContext* Get(nsPresContext *aPresContext,
     620                 :                       nsStyleContext *aParentStyleContext,
     621                 :                       nsCSSKeyframeRule *aKeyframe);
     622                 : 
     623                 : private:
     624                 :   nsRefPtrHashtable<nsPtrHashKey<nsCSSKeyframeRule>, nsStyleContext> mCache;
     625                 : };
     626                 : 
     627                 : nsStyleContext*
     628               0 : ResolvedStyleCache::Get(nsPresContext *aPresContext,
     629                 :                         nsStyleContext *aParentStyleContext,
     630                 :                         nsCSSKeyframeRule *aKeyframe)
     631                 : {
     632                 :   // FIXME (spec):  The css3-animations spec isn't very clear about how
     633                 :   // properties are resolved when they have values that depend on other
     634                 :   // properties (e.g., values in 'em').  I presume that they're resolved
     635                 :   // relative to the other styles of the element.  The question is
     636                 :   // whether they are resolved relative to other animations:  I assume
     637                 :   // that they're not, since that would prevent us from caching a lot of
     638                 :   // data that we'd really like to cache (in particular, the
     639                 :   // nsStyleAnimation::Value values in AnimationPropertySegment).
     640               0 :   nsStyleContext *result = mCache.GetWeak(aKeyframe);
     641               0 :   if (!result) {
     642               0 :     nsCOMArray<nsIStyleRule> rules;
     643               0 :     rules.AppendObject(aKeyframe);
     644                 :     nsRefPtr<nsStyleContext> resultStrong = aPresContext->StyleSet()->
     645               0 :       ResolveStyleByAddingRules(aParentStyleContext, rules);
     646               0 :     mCache.Put(aKeyframe, resultStrong);
     647               0 :     result = resultStrong;
     648                 :   }
     649               0 :   return result;
     650                 : }
     651                 : 
     652                 : void
     653               0 : nsAnimationManager::BuildAnimations(nsStyleContext* aStyleContext,
     654                 :                                     InfallibleTArray<ElementAnimation>& aAnimations)
     655                 : {
     656               0 :   NS_ABORT_IF_FALSE(aAnimations.IsEmpty(), "expect empty array");
     657                 : 
     658               0 :   ResolvedStyleCache resolvedStyles;
     659                 : 
     660               0 :   const nsStyleDisplay *disp = aStyleContext->GetStyleDisplay();
     661               0 :   TimeStamp now = mPresContext->RefreshDriver()->MostRecentRefresh();
     662               0 :   for (PRUint32 animIdx = 0, animEnd = disp->mAnimations.Length();
     663                 :        animIdx != animEnd; ++animIdx) {
     664               0 :     const nsAnimation& aSrc = disp->mAnimations[animIdx];
     665               0 :     ElementAnimation& aDest = *aAnimations.AppendElement();
     666                 : 
     667               0 :     aDest.mName = aSrc.GetName();
     668               0 :     aDest.mIterationCount = aSrc.GetIterationCount();
     669               0 :     aDest.mDirection = aSrc.GetDirection();
     670               0 :     aDest.mFillMode = aSrc.GetFillMode();
     671               0 :     aDest.mPlayState = aSrc.GetPlayState();
     672                 : 
     673               0 :     aDest.mStartTime = now + TimeDuration::FromMilliseconds(aSrc.GetDelay());
     674               0 :     if (aDest.IsPaused()) {
     675               0 :       aDest.mPauseStart = now;
     676                 :     } else {
     677               0 :       aDest.mPauseStart = TimeStamp();
     678                 :     }
     679                 : 
     680               0 :     aDest.mIterationDuration = TimeDuration::FromMilliseconds(aSrc.GetDuration());
     681                 : 
     682               0 :     nsCSSKeyframesRule *rule = KeyframesRuleFor(aDest.mName);
     683               0 :     if (!rule) {
     684                 :       // no segments
     685               0 :       continue;
     686                 :     }
     687                 : 
     688                 :     // Build the set of unique keyframes in the @keyframes rule.  Per
     689                 :     // css3-animations, later keyframes with the same key replace
     690                 :     // earlier ones (no cascading).
     691               0 :     nsDataHashtable<PercentageHashKey, nsCSSKeyframeRule*> keyframes;
     692               0 :     keyframes.Init(16); // FIXME: make infallible!
     693               0 :     for (PRUint32 ruleIdx = 0, ruleEnd = rule->StyleRuleCount();
     694                 :          ruleIdx != ruleEnd; ++ruleIdx) {
     695               0 :       css::Rule* cssRule = rule->GetStyleRuleAt(ruleIdx);
     696               0 :       NS_ABORT_IF_FALSE(cssRule, "must have rule");
     697               0 :       NS_ABORT_IF_FALSE(cssRule->GetType() == css::Rule::KEYFRAME_RULE,
     698                 :                         "must be keyframe rule");
     699               0 :       nsCSSKeyframeRule *kfRule = static_cast<nsCSSKeyframeRule*>(cssRule);
     700                 : 
     701               0 :       const nsTArray<float> &keys = kfRule->GetKeys();
     702               0 :       for (PRUint32 keyIdx = 0, keyEnd = keys.Length();
     703                 :            keyIdx != keyEnd; ++keyIdx) {
     704               0 :         float key = keys[keyIdx];
     705                 :         // FIXME (spec):  The spec doesn't say what to do with
     706                 :         // out-of-range keyframes.  We'll ignore them.
     707                 :         // (And PercentageHashKey currently assumes we either ignore or
     708                 :         // clamp them.)
     709               0 :         if (0.0f <= key && key <= 1.0f) {
     710               0 :           keyframes.Put(key, kfRule);
     711                 :         }
     712                 :       }
     713                 :     }
     714                 : 
     715               0 :     KeyframeDataArray sortedKeyframes;
     716               0 :     keyframes.EnumerateRead(AppendKeyframeData, &sortedKeyframes);
     717               0 :     sortedKeyframes.Sort(KeyframeDataComparator());
     718                 : 
     719               0 :     if (sortedKeyframes.Length() == 0) {
     720                 :       // no segments
     721               0 :       continue;
     722                 :     }
     723                 : 
     724                 :     // Record the properties that are present in any keyframe rules we
     725                 :     // are using.
     726               0 :     nsCSSPropertySet properties;
     727                 : 
     728               0 :     for (PRUint32 kfIdx = 0, kfEnd = sortedKeyframes.Length();
     729                 :          kfIdx != kfEnd; ++kfIdx) {
     730               0 :       css::Declaration *decl = sortedKeyframes[kfIdx].mRule->Declaration();
     731               0 :       for (PRUint32 propIdx = 0, propEnd = decl->Count();
     732                 :            propIdx != propEnd; ++propIdx) {
     733               0 :         properties.AddProperty(decl->OrderValueAt(propIdx));
     734                 :       }
     735                 :     }
     736                 : 
     737               0 :     for (nsCSSProperty prop = nsCSSProperty(0);
     738                 :          prop < eCSSProperty_COUNT_no_shorthands;
     739                 :          prop = nsCSSProperty(prop + 1)) {
     740               0 :       if (!properties.HasProperty(prop) ||
     741               0 :           nsCSSProps::kAnimTypeTable[prop] == eStyleAnimType_None) {
     742               0 :         continue;
     743                 :       }
     744                 : 
     745               0 :       AnimationProperty &propData = *aDest.mProperties.AppendElement();
     746               0 :       propData.mProperty = prop;
     747                 : 
     748               0 :       KeyframeData *fromKeyframe = nsnull;
     749               0 :       nsRefPtr<nsStyleContext> fromContext;
     750               0 :       bool interpolated = true;
     751               0 :       for (PRUint32 kfIdx = 0, kfEnd = sortedKeyframes.Length();
     752                 :            kfIdx != kfEnd; ++kfIdx) {
     753               0 :         KeyframeData &toKeyframe = sortedKeyframes[kfIdx];
     754               0 :         if (!toKeyframe.mRule->Declaration()->HasProperty(prop)) {
     755               0 :           continue;
     756                 :         }
     757                 : 
     758                 :         nsRefPtr<nsStyleContext> toContext =
     759               0 :           resolvedStyles.Get(mPresContext, aStyleContext, toKeyframe.mRule);
     760                 : 
     761               0 :         if (fromKeyframe) {
     762                 :           interpolated = interpolated &&
     763                 :             BuildSegment(propData.mSegments, prop, aSrc,
     764                 :                          fromKeyframe->mKey, fromContext,
     765                 :                          fromKeyframe->mRule->Declaration(),
     766               0 :                          toKeyframe.mKey, toContext);
     767                 :         } else {
     768               0 :           if (toKeyframe.mKey != 0.0f) {
     769                 :             // There's no data for this property at 0%, so use the
     770                 :             // cascaded value above us.
     771                 :             interpolated = interpolated &&
     772                 :               BuildSegment(propData.mSegments, prop, aSrc,
     773                 :                            0.0f, aStyleContext, nsnull,
     774               0 :                            toKeyframe.mKey, toContext);
     775                 :           }
     776                 :         }
     777                 : 
     778               0 :         fromContext = toContext;
     779               0 :         fromKeyframe = &toKeyframe;
     780                 :       }
     781                 : 
     782               0 :       if (fromKeyframe->mKey != 1.0f) {
     783                 :         // There's no data for this property at 100%, so use the
     784                 :         // cascaded value above us.
     785                 :         interpolated = interpolated &&
     786                 :           BuildSegment(propData.mSegments, prop, aSrc,
     787                 :                        fromKeyframe->mKey, fromContext,
     788                 :                        fromKeyframe->mRule->Declaration(),
     789               0 :                        1.0f, aStyleContext);
     790                 :       }
     791                 : 
     792                 :       // If we failed to build any segments due to inability to
     793                 :       // interpolate, remove the property from the animation.  (It's not
     794                 :       // clear if this is the right thing to do -- we could run some of
     795                 :       // the segments, but it's really not clear whether we should skip
     796                 :       // values (which?) or skip segments, so best to skip the whole
     797                 :       // thing for now.)
     798               0 :       if (!interpolated) {
     799               0 :         aDest.mProperties.RemoveElementAt(aDest.mProperties.Length() - 1);
     800                 :       }
     801                 :     }
     802                 :   }
     803               0 : }
     804                 : 
     805                 : bool
     806               0 : nsAnimationManager::BuildSegment(InfallibleTArray<AnimationPropertySegment>&
     807                 :                                    aSegments,
     808                 :                                  nsCSSProperty aProperty,
     809                 :                                  const nsAnimation& aAnimation,
     810                 :                                  float aFromKey, nsStyleContext* aFromContext,
     811                 :                                  mozilla::css::Declaration* aFromDeclaration,
     812                 :                                  float aToKey, nsStyleContext* aToContext)
     813                 : {
     814               0 :   nsStyleAnimation::Value fromValue, toValue, dummyValue;
     815               0 :   if (!ExtractComputedValueForTransition(aProperty, aFromContext, fromValue) ||
     816               0 :       !ExtractComputedValueForTransition(aProperty, aToContext, toValue) ||
     817                 :       // Check that we can interpolate between these values
     818                 :       // (If this is ever a performance problem, we could add a
     819                 :       // CanInterpolate method, but it seems fine for now.)
     820                 :       !nsStyleAnimation::Interpolate(aProperty, fromValue, toValue,
     821               0 :                                     0.5, dummyValue)) {
     822               0 :     return false;
     823                 :   }
     824                 : 
     825               0 :   AnimationPropertySegment &segment = *aSegments.AppendElement();
     826                 : 
     827               0 :   segment.mFromValue = fromValue;
     828               0 :   segment.mToValue = toValue;
     829               0 :   segment.mFromKey = aFromKey;
     830               0 :   segment.mToKey = aToKey;
     831                 :   const nsTimingFunction *tf;
     832               0 :   if (aFromDeclaration &&
     833               0 :       aFromDeclaration->HasProperty(eCSSProperty_animation_timing_function)) {
     834               0 :     tf = &aFromContext->GetStyleDisplay()->mAnimations[0].GetTimingFunction();
     835                 :   } else {
     836               0 :     tf = &aAnimation.GetTimingFunction();
     837                 :   }
     838               0 :   segment.mTimingFunction.Init(*tf);
     839                 : 
     840               0 :   return true;
     841                 : }
     842                 : 
     843                 : nsIStyleRule*
     844               0 : nsAnimationManager::GetAnimationRule(mozilla::dom::Element* aElement,
     845                 :                                      nsCSSPseudoElements::Type aPseudoType)
     846                 : {
     847               0 :   NS_ABORT_IF_FALSE(
     848                 :     aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement ||
     849                 :     aPseudoType == nsCSSPseudoElements::ePseudo_before ||
     850                 :     aPseudoType == nsCSSPseudoElements::ePseudo_after,
     851                 :     "forbidden pseudo type");
     852                 : 
     853                 :   ElementAnimations *ea =
     854               0 :     GetElementAnimations(aElement, aPseudoType, false);
     855               0 :   if (!ea) {
     856               0 :     return nsnull;
     857                 :   }
     858                 : 
     859               0 :   NS_WARN_IF_FALSE(ea->mStyleRuleRefreshTime ==
     860                 :                      mPresContext->RefreshDriver()->MostRecentRefresh(),
     861                 :                    "should already have refreshed style rule");
     862                 : 
     863               0 :   if (mPresContext->IsProcessingRestyles() &&
     864               0 :       !mPresContext->IsProcessingAnimationStyleChange()) {
     865                 :     // During the non-animation part of processing restyles, we don't
     866                 :     // add the animation rule.
     867                 : 
     868               0 :     if (ea->mStyleRule) {
     869               0 :       ea->PostRestyleForAnimation(mPresContext);
     870                 :     }
     871                 : 
     872               0 :     return nsnull;
     873                 :   }
     874                 : 
     875               0 :   return ea->mStyleRule;
     876                 : }
     877                 : 
     878                 : /* virtual */ void
     879               0 : nsAnimationManager::WillRefresh(mozilla::TimeStamp aTime)
     880                 : {
     881               0 :   NS_ABORT_IF_FALSE(mPresContext,
     882                 :                     "refresh driver should not notify additional observers "
     883                 :                     "after pres context has been destroyed");
     884               0 :   if (!mPresContext->GetPresShell()) {
     885                 :     // Someone might be keeping mPresContext alive past the point
     886                 :     // where it has been torn down; don't bother doing anything in
     887                 :     // this case.  But do get rid of all our transitions so we stop
     888                 :     // triggering refreshes.
     889               0 :     RemoveAllElementData();
     890               0 :     return;
     891                 :   }
     892                 : 
     893                 :   // FIXME: check that there's at least one style rule that's not
     894                 :   // in its "done" state, and if there isn't, remove ourselves from
     895                 :   // the refresh driver (but leave the animations!).
     896               0 :   for (PRCList *l = PR_LIST_HEAD(&mElementData); l != &mElementData;
     897                 :        l = PR_NEXT_LINK(l)) {
     898               0 :     ElementAnimations *ea = static_cast<ElementAnimations*>(l);
     899               0 :     nsRefPtr<css::AnimValuesStyleRule> oldStyleRule = ea->mStyleRule;
     900               0 :     ea->EnsureStyleRuleFor(mPresContext->RefreshDriver()->MostRecentRefresh(),
     901               0 :                            mPendingEvents);
     902               0 :     if (oldStyleRule != ea->mStyleRule) {
     903               0 :       ea->PostRestyleForAnimation(mPresContext);
     904                 :     }
     905                 :   }
     906                 : 
     907               0 :   DispatchEvents(); // may destroy us
     908                 : }
     909                 : 
     910                 : void
     911               0 : nsAnimationManager::DoDispatchEvents()
     912                 : {
     913               0 :   EventArray events;
     914               0 :   mPendingEvents.SwapElements(events);
     915               0 :   for (PRUint32 i = 0, i_end = events.Length(); i < i_end; ++i) {
     916               0 :     AnimationEventInfo &info = events[i];
     917               0 :     nsEventDispatcher::Dispatch(info.mElement, mPresContext, &info.mEvent);
     918                 : 
     919               0 :     if (!mPresContext) {
     920               0 :       break;
     921                 :     }
     922                 :   }
     923               0 : }
     924                 : 
     925                 : nsCSSKeyframesRule*
     926               0 : nsAnimationManager::KeyframesRuleFor(const nsSubstring& aName)
     927                 : {
     928               0 :   if (mKeyframesListIsDirty) {
     929               0 :     mKeyframesListIsDirty = false;
     930                 : 
     931               0 :     nsTArray<nsCSSKeyframesRule*> rules;
     932               0 :     mPresContext->StyleSet()->AppendKeyframesRules(mPresContext, rules);
     933                 : 
     934                 :     // Per css3-animations, the last @keyframes rule specified wins.
     935               0 :     mKeyframesRules.Clear();
     936               0 :     for (PRUint32 i = 0, i_end = rules.Length(); i != i_end; ++i) {
     937               0 :       nsCSSKeyframesRule *rule = rules[i];
     938               0 :       mKeyframesRules.Put(rule->GetName(), rule);
     939                 :     }
     940                 :   }
     941                 : 
     942               0 :   return mKeyframesRules.Get(aName);
     943                 : }
     944                 : 

Generated by: LCOV version 1.7