LCOV - app.info - layout/style/nsAnimationManager.cpp

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-04-21 | 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