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

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is the Mozilla SMIL module.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is Brian Birtles.
      18                 :  * Portions created by the Initial Developer are Copyright (C) 2005
      19                 :  * the Initial Developer. All Rights Reserved.
      20                 :  *
      21                 :  * Contributor(s):
      22                 :  *   Brian Birtles <birtles@gmail.com>
      23                 :  *   Daniel Holbert <dholbert@mozilla.com>
      24                 :  *   Robert Longson <longsonr@gmail.com>
      25                 :  *
      26                 :  * Alternatively, the contents of this file may be used under the terms of
      27                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      28                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      29                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      30                 :  * of those above. If you wish to allow use of your version of this file only
      31                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      32                 :  * use your version of this file under the terms of the MPL, indicate your
      33                 :  * decision by deleting the provisions above and replace them with the notice
      34                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      35                 :  * the provisions above, a recipient may use your version of this file under
      36                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      37                 :  *
      38                 :  * ***** END LICENSE BLOCK ***** */
      39                 : 
      40                 : #include "nsSMILAnimationController.h"
      41                 : #include "nsSMILCompositor.h"
      42                 : #include "nsSMILCSSProperty.h"
      43                 : #include "nsCSSProps.h"
      44                 : #include "nsComponentManagerUtils.h"
      45                 : #include "nsITimer.h"
      46                 : #include "nsIContent.h"
      47                 : #include "mozilla/dom/Element.h"
      48                 : #include "nsIDocument.h"
      49                 : #include "nsISMILAnimationElement.h"
      50                 : #include "nsIDOMSVGAnimationElement.h"
      51                 : #include "nsSMILTimedElement.h"
      52                 : 
      53                 : using namespace mozilla::dom;
      54                 : 
      55                 : //----------------------------------------------------------------------
      56                 : // nsSMILAnimationController implementation
      57                 : 
      58                 : //----------------------------------------------------------------------
      59                 : // ctors, dtors, factory methods
      60                 : 
      61               0 : nsSMILAnimationController::nsSMILAnimationController(nsIDocument* aDoc)
      62                 :   : mAvgTimeBetweenSamples(0),
      63                 :     mResampleNeeded(false),
      64                 :     mDeferredStartSampling(false),
      65                 :     mRunningSample(false),
      66               0 :     mDocument(aDoc)
      67                 : {
      68               0 :   NS_ABORT_IF_FALSE(aDoc, "need a non-null document");
      69                 : 
      70               0 :   mAnimationElementTable.Init();
      71               0 :   mChildContainerTable.Init();
      72                 : 
      73               0 :   nsRefreshDriver* refreshDriver = GetRefreshDriver();
      74               0 :   if (refreshDriver) {
      75               0 :     mStartTime = refreshDriver->MostRecentRefresh();
      76                 :   } else {
      77               0 :     mStartTime = mozilla::TimeStamp::Now();
      78                 :   }
      79               0 :   mCurrentSampleTime = mStartTime;
      80                 : 
      81               0 :   Begin();
      82               0 : }
      83                 : 
      84               0 : nsSMILAnimationController::~nsSMILAnimationController()
      85                 : {
      86               0 :   NS_ASSERTION(mAnimationElementTable.Count() == 0,
      87                 :                "Animation controller shouldn't be tracking any animation"
      88                 :                " elements when it dies");
      89               0 : }
      90                 : 
      91                 : void
      92               0 : nsSMILAnimationController::Disconnect()
      93                 : {
      94               0 :   NS_ABORT_IF_FALSE(mDocument, "disconnecting when we weren't connected...?");
      95               0 :   NS_ABORT_IF_FALSE(mRefCnt.get() == 1,
      96                 :                     "Expecting to disconnect when doc is sole remaining owner");
      97               0 :   NS_ASSERTION(mPauseState & nsSMILTimeContainer::PAUSE_PAGEHIDE,
      98                 :                "Expecting to be paused for pagehide before disconnect");
      99                 : 
     100               0 :   StopSampling(GetRefreshDriver());
     101                 : 
     102               0 :   mDocument = nsnull; // (raw pointer)
     103               0 : }
     104                 : 
     105                 : //----------------------------------------------------------------------
     106                 : // nsSMILTimeContainer methods:
     107                 : 
     108                 : void
     109               0 : nsSMILAnimationController::Pause(PRUint32 aType)
     110                 : {
     111               0 :   nsSMILTimeContainer::Pause(aType);
     112                 : 
     113               0 :   if (mPauseState) {
     114               0 :     mDeferredStartSampling = false;
     115               0 :     StopSampling(GetRefreshDriver());
     116                 :   }
     117               0 : }
     118                 : 
     119                 : void
     120               0 : nsSMILAnimationController::Resume(PRUint32 aType)
     121                 : {
     122               0 :   bool wasPaused = (mPauseState != 0);
     123                 :   // Update mCurrentSampleTime so that calls to GetParentTime--used for
     124                 :   // calculating parent offsets--are accurate
     125               0 :   mCurrentSampleTime = mozilla::TimeStamp::Now();
     126                 : 
     127               0 :   nsSMILTimeContainer::Resume(aType);
     128                 : 
     129               0 :   if (wasPaused && !mPauseState && mChildContainerTable.Count()) {
     130               0 :     Sample(); // Run the first sample manually
     131               0 :     MaybeStartSampling(GetRefreshDriver());
     132                 :   }
     133               0 : }
     134                 : 
     135                 : nsSMILTime
     136               0 : nsSMILAnimationController::GetParentTime() const
     137                 : {
     138               0 :   return (nsSMILTime)(mCurrentSampleTime - mStartTime).ToMilliseconds();
     139                 : }
     140                 : 
     141                 : //----------------------------------------------------------------------
     142                 : // nsARefreshObserver methods:
     143               0 : NS_IMPL_ADDREF(nsSMILAnimationController)
     144               0 : NS_IMPL_RELEASE(nsSMILAnimationController)
     145                 : 
     146                 : // nsRefreshDriver Callback function
     147                 : void
     148               0 : nsSMILAnimationController::WillRefresh(mozilla::TimeStamp aTime)
     149                 : {
     150                 :   // Although we never expect aTime to go backwards, when we initialise the
     151                 :   // animation controller, if we can't get hold of a refresh driver we
     152                 :   // initialise mCurrentSampleTime to Now(). It may be possible that after
     153                 :   // doing so we get sampled by a refresh driver whose most recent refresh time
     154                 :   // predates when we were initialised, so to be safe we make sure to take the
     155                 :   // most recent time here.
     156               0 :   aTime = NS_MAX(mCurrentSampleTime, aTime);
     157                 : 
     158                 :   // Sleep detection: If the time between samples is a whole lot greater than we
     159                 :   // were expecting then we assume the computer went to sleep or someone's
     160                 :   // messing with the clock. In that case, fiddle our parent offset and use our
     161                 :   // average time between samples to calculate the new sample time. This
     162                 :   // prevents us from hanging while trying to catch up on all the missed time.
     163                 : 
     164                 :   // Smoothing of coefficient for the average function. 0.2 should let us track
     165                 :   // the sample rate reasonably tightly without being overly affected by
     166                 :   // occasional delays.
     167                 :   static const double SAMPLE_DUR_WEIGHTING = 0.2;
     168                 :   // If the elapsed time exceeds our expectation by this number of times we'll
     169                 :   // initiate special behaviour to basically ignore the intervening time.
     170                 :   static const double SAMPLE_DEV_THRESHOLD = 200.0;
     171                 : 
     172                 :   nsSMILTime elapsedTime =
     173               0 :     (nsSMILTime)(aTime - mCurrentSampleTime).ToMilliseconds();
     174                 :   // First sample:
     175               0 :   if (mAvgTimeBetweenSamples == 0) {
     176               0 :     mAvgTimeBetweenSamples = elapsedTime;
     177                 :   // Unexpectedly long delay between samples:
     178               0 :   } else if (elapsedTime > SAMPLE_DEV_THRESHOLD * mAvgTimeBetweenSamples) {
     179                 :     NS_WARNING("Detected really long delay between samples, continuing from "
     180               0 :                "previous sample");
     181               0 :     mParentOffset += elapsedTime - mAvgTimeBetweenSamples;
     182                 :   // Usual case, update moving average:
     183                 :   } else {
     184                 :     // Due to truncation here the average will normally be a little less than
     185                 :     // it should be but that's probably ok
     186                 :     mAvgTimeBetweenSamples =
     187                 :       (nsSMILTime)(elapsedTime * SAMPLE_DUR_WEIGHTING +
     188               0 :       mAvgTimeBetweenSamples * (1.0 - SAMPLE_DUR_WEIGHTING));
     189                 :   }
     190               0 :   mCurrentSampleTime = aTime;
     191                 : 
     192               0 :   Sample();
     193               0 : }
     194                 : 
     195                 : //----------------------------------------------------------------------
     196                 : // Animation element registration methods:
     197                 : 
     198                 : void
     199               0 : nsSMILAnimationController::RegisterAnimationElement(
     200                 :                                   nsISMILAnimationElement* aAnimationElement)
     201                 : {
     202               0 :   mAnimationElementTable.PutEntry(aAnimationElement);
     203               0 :   if (mDeferredStartSampling) {
     204               0 :     mDeferredStartSampling = false;
     205               0 :     if (mChildContainerTable.Count()) {
     206                 :       // mAnimationElementTable was empty, but now we've added its 1st element
     207               0 :       NS_ABORT_IF_FALSE(mAnimationElementTable.Count() == 1,
     208                 :                         "we shouldn't have deferred sampling if we already had "
     209                 :                         "animations registered");
     210               0 :       StartSampling(GetRefreshDriver());
     211               0 :       Sample(); // Run the first sample manually
     212                 :     } // else, don't sample until a time container is registered (via AddChild)
     213                 :   }
     214               0 : }
     215                 : 
     216                 : void
     217               0 : nsSMILAnimationController::UnregisterAnimationElement(
     218                 :                                   nsISMILAnimationElement* aAnimationElement)
     219                 : {
     220               0 :   mAnimationElementTable.RemoveEntry(aAnimationElement);
     221               0 : }
     222                 : 
     223                 : //----------------------------------------------------------------------
     224                 : // Page show/hide
     225                 : 
     226                 : void
     227               0 : nsSMILAnimationController::OnPageShow()
     228                 : {
     229               0 :   Resume(nsSMILTimeContainer::PAUSE_PAGEHIDE);
     230               0 : }
     231                 : 
     232                 : void
     233               0 : nsSMILAnimationController::OnPageHide()
     234                 : {
     235               0 :   Pause(nsSMILTimeContainer::PAUSE_PAGEHIDE);
     236               0 : }
     237                 : 
     238                 : //----------------------------------------------------------------------
     239                 : // Cycle-collection support
     240                 : 
     241                 : void
     242               0 : nsSMILAnimationController::Traverse(
     243                 :     nsCycleCollectionTraversalCallback* aCallback)
     244                 : {
     245                 :   // Traverse last compositor table
     246               0 :   if (mLastCompositorTable) {
     247                 :     mLastCompositorTable->EnumerateEntries(CompositorTableEntryTraverse,
     248               0 :                                            aCallback);
     249                 :   }
     250               0 : }
     251                 : 
     252                 : /*static*/ PR_CALLBACK PLDHashOperator
     253               0 : nsSMILAnimationController::CompositorTableEntryTraverse(
     254                 :                                       nsSMILCompositor* aCompositor,
     255                 :                                       void* aArg)
     256                 : {
     257                 :   nsCycleCollectionTraversalCallback* cb =
     258               0 :     static_cast<nsCycleCollectionTraversalCallback*>(aArg);
     259               0 :   aCompositor->Traverse(cb);
     260               0 :   return PL_DHASH_NEXT;
     261                 : }
     262                 : 
     263                 : void
     264               0 : nsSMILAnimationController::Unlink()
     265                 : {
     266               0 :   mLastCompositorTable = nsnull;
     267               0 : }
     268                 : 
     269                 : //----------------------------------------------------------------------
     270                 : // Refresh driver lifecycle related methods
     271                 : 
     272                 : void
     273               0 : nsSMILAnimationController::NotifyRefreshDriverCreated(
     274                 :     nsRefreshDriver* aRefreshDriver)
     275                 : {
     276               0 :   if (!mPauseState) {
     277               0 :     MaybeStartSampling(aRefreshDriver);
     278                 :   }
     279               0 : }
     280                 : 
     281                 : void
     282               0 : nsSMILAnimationController::NotifyRefreshDriverDestroying(
     283                 :     nsRefreshDriver* aRefreshDriver)
     284                 : {
     285               0 :   if (!mPauseState && !mDeferredStartSampling) {
     286               0 :     StopSampling(aRefreshDriver);
     287                 :   }
     288               0 : }
     289                 : 
     290                 : //----------------------------------------------------------------------
     291                 : // Timer-related implementation helpers
     292                 : 
     293                 : void
     294               0 : nsSMILAnimationController::StartSampling(nsRefreshDriver* aRefreshDriver)
     295                 : {
     296               0 :   NS_ASSERTION(mPauseState == 0, "Starting sampling but controller is paused");
     297               0 :   NS_ASSERTION(!mDeferredStartSampling,
     298                 :                "Started sampling but the deferred start flag is still set");
     299               0 :   if (aRefreshDriver) {
     300               0 :     NS_ABORT_IF_FALSE(!GetRefreshDriver() ||
     301                 :                       aRefreshDriver == GetRefreshDriver(),
     302                 :                       "Starting sampling with wrong refresh driver");
     303                 :     // We're effectively resuming from a pause so update our current sample time
     304                 :     // or else it will confuse our "average time between samples" calculations.
     305               0 :     mCurrentSampleTime = mozilla::TimeStamp::Now();
     306               0 :     aRefreshDriver->AddRefreshObserver(this, Flush_Style);
     307                 :   }
     308               0 : }
     309                 : 
     310                 : void
     311               0 : nsSMILAnimationController::StopSampling(nsRefreshDriver* aRefreshDriver)
     312                 : {
     313               0 :   if (aRefreshDriver) {
     314                 :     // NOTE: The document might already have been detached from its PresContext
     315                 :     // (and RefreshDriver), which would make GetRefreshDriverForDoc return null.
     316               0 :     NS_ABORT_IF_FALSE(!GetRefreshDriver() ||
     317                 :                       aRefreshDriver == GetRefreshDriver(),
     318                 :                       "Stopping sampling with wrong refresh driver");
     319               0 :     aRefreshDriver->RemoveRefreshObserver(this, Flush_Style);
     320                 :   }
     321               0 : }
     322                 : 
     323                 : void
     324               0 : nsSMILAnimationController::MaybeStartSampling(nsRefreshDriver* aRefreshDriver)
     325                 : {
     326               0 :   if (mDeferredStartSampling) {
     327                 :     // We've received earlier 'MaybeStartSampling' calls, and we're
     328                 :     // deferring until we get a registered animation.
     329               0 :     return;
     330                 :   }
     331                 : 
     332               0 :   if (mAnimationElementTable.Count()) {
     333               0 :     StartSampling(aRefreshDriver);
     334                 :   } else {
     335               0 :     mDeferredStartSampling = true;
     336                 :   }
     337                 : }
     338                 : 
     339                 : //----------------------------------------------------------------------
     340                 : // Sample-related methods and callbacks
     341                 : 
     342                 : PR_CALLBACK PLDHashOperator
     343               0 : TransferCachedBaseValue(nsSMILCompositor* aCompositor,
     344                 :                         void* aData)
     345                 : {
     346                 :   nsSMILCompositorTable* lastCompositorTable =
     347               0 :     static_cast<nsSMILCompositorTable*>(aData);
     348                 :   nsSMILCompositor* lastCompositor =
     349               0 :     lastCompositorTable->GetEntry(aCompositor->GetKey());
     350                 : 
     351               0 :   if (lastCompositor) {
     352               0 :     aCompositor->StealCachedBaseValue(lastCompositor);
     353                 :   }
     354                 : 
     355               0 :   return PL_DHASH_NEXT;  
     356                 : }
     357                 : 
     358                 : PR_CALLBACK PLDHashOperator
     359               0 : RemoveCompositorFromTable(nsSMILCompositor* aCompositor,
     360                 :                           void* aData)
     361                 : {
     362                 :   nsSMILCompositorTable* lastCompositorTable =
     363               0 :     static_cast<nsSMILCompositorTable*>(aData);
     364               0 :   lastCompositorTable->RemoveEntry(aCompositor->GetKey());
     365               0 :   return PL_DHASH_NEXT;
     366                 : }
     367                 : 
     368                 : PR_CALLBACK PLDHashOperator
     369               0 : DoClearAnimationEffects(nsSMILCompositor* aCompositor,
     370                 :                         void* /*aData*/)
     371                 : {
     372               0 :   aCompositor->ClearAnimationEffects();
     373               0 :   return PL_DHASH_NEXT;
     374                 : }
     375                 : 
     376                 : PR_CALLBACK PLDHashOperator
     377               0 : DoComposeAttribute(nsSMILCompositor* aCompositor,
     378                 :                    void* /*aData*/)
     379                 : {
     380               0 :   aCompositor->ComposeAttribute();
     381               0 :   return PL_DHASH_NEXT;
     382                 : }
     383                 : 
     384                 : void
     385               0 : nsSMILAnimationController::DoSample()
     386                 : {
     387               0 :   DoSample(true); // Skip unchanged time containers
     388               0 : }
     389                 : 
     390                 : void
     391               0 : nsSMILAnimationController::DoSample(bool aSkipUnchangedContainers)
     392                 : {
     393               0 :   if (!mDocument) {
     394               0 :     NS_ERROR("Shouldn't be sampling after document has disconnected");
     395               0 :     return;
     396                 :   }
     397                 : 
     398               0 :   mResampleNeeded = false;
     399                 :   // Set running sample flag -- do this before flushing styles so that when we
     400                 :   // flush styles we don't end up requesting extra samples
     401               0 :   mRunningSample = true;
     402               0 :   nsCOMPtr<nsIDocument> kungFuDeathGrip(mDocument);  // keeps 'this' alive too
     403               0 :   mDocument->FlushPendingNotifications(Flush_Style);
     404                 : 
     405                 :   // WARNING: 
     406                 :   // WARNING: the above flush may have destroyed the pres shell and/or
     407                 :   // WARNING: frames and other layout related objects.
     408                 :   // WARNING:
     409                 :   
     410                 :   // STEP 1: Bring model up to date
     411                 :   // (i)  Rewind elements where necessary
     412                 :   // (ii) Run milestone samples
     413               0 :   RewindElements();
     414               0 :   DoMilestoneSamples();
     415                 : 
     416                 :   // STEP 2: Sample the child time containers
     417                 :   //
     418                 :   // When we sample the child time containers they will simply record the sample
     419                 :   // time in document time.
     420               0 :   TimeContainerHashtable activeContainers;
     421               0 :   activeContainers.Init(mChildContainerTable.Count());
     422                 :   SampleTimeContainerParams tcParams = { &activeContainers,
     423               0 :                                          aSkipUnchangedContainers };
     424               0 :   mChildContainerTable.EnumerateEntries(SampleTimeContainer, &tcParams);
     425                 : 
     426                 :   // STEP 3: (i)  Sample the timed elements AND
     427                 :   //         (ii) Create a table of compositors
     428                 :   //
     429                 :   // (i) Here we sample the timed elements (fetched from the
     430                 :   // nsISMILAnimationElements) which determine from the active time if the
     431                 :   // element is active and what its simple time etc. is. This information is
     432                 :   // then passed to its time client (nsSMILAnimationFunction).
     433                 :   //
     434                 :   // (ii) During the same loop we also build up a table that contains one
     435                 :   // compositor for each animated attribute and which maps animated elements to
     436                 :   // the corresponding compositor for their target attribute.
     437                 :   //
     438                 :   // Note that this compositor table needs to be allocated on the heap so we can
     439                 :   // store it until the next sample. This lets us find out which elements were
     440                 :   // animated in sample 'n-1' but not in sample 'n' (and hence need to have
     441                 :   // their animation effects removed in sample 'n').
     442                 :   //
     443                 :   // Parts (i) and (ii) are not functionally related but we combine them here to
     444                 :   // save iterating over the animation elements twice.
     445                 : 
     446                 :   // Create the compositor table
     447                 :   nsAutoPtr<nsSMILCompositorTable>
     448               0 :     currentCompositorTable(new nsSMILCompositorTable());
     449               0 :   currentCompositorTable->Init(0);
     450                 : 
     451                 :   SampleAnimationParams saParams = { &activeContainers,
     452               0 :                                      currentCompositorTable };
     453                 :   mAnimationElementTable.EnumerateEntries(SampleAnimation,
     454               0 :                                           &saParams);
     455               0 :   activeContainers.Clear();
     456                 : 
     457                 :   // STEP 4: Compare previous sample's compositors against this sample's.
     458                 :   // (Transfer cached base values across, & remove animation effects from 
     459                 :   // no-longer-animated targets.)
     460               0 :   if (mLastCompositorTable) {
     461                 :     // * Transfer over cached base values, from last sample's compositors
     462                 :     currentCompositorTable->EnumerateEntries(TransferCachedBaseValue,
     463               0 :                                              mLastCompositorTable);
     464                 : 
     465                 :     // * For each compositor in current sample's hash table, remove entry from
     466                 :     // prev sample's hash table -- we don't need to clear animation
     467                 :     // effects of those compositors, since they're still being animated.
     468                 :     currentCompositorTable->EnumerateEntries(RemoveCompositorFromTable,
     469               0 :                                              mLastCompositorTable);
     470                 : 
     471                 :     // * For each entry that remains in prev sample's hash table (i.e. for
     472                 :     // every target that's no longer animated), clear animation effects.
     473               0 :     mLastCompositorTable->EnumerateEntries(DoClearAnimationEffects, nsnull);
     474                 :   }
     475                 : 
     476                 :   // STEP 5: Compose currently-animated attributes.
     477                 :   // XXXdholbert: This step traverses our animation targets in an effectively
     478                 :   // random order. For animation from/to 'inherit' values to work correctly
     479                 :   // when the inherited value is *also* being animated, we really should be
     480                 :   // traversing our animated nodes in an ancestors-first order (bug 501183)
     481               0 :   currentCompositorTable->EnumerateEntries(DoComposeAttribute, nsnull);
     482               0 :   mRunningSample = false;
     483                 : 
     484                 :   // Update last compositor table
     485               0 :   mLastCompositorTable = currentCompositorTable.forget();
     486                 : 
     487               0 :   NS_ASSERTION(!mResampleNeeded, "Resample dirty flag set during sample!");
     488                 : }
     489                 : 
     490                 : void
     491               0 : nsSMILAnimationController::RewindElements()
     492                 : {
     493               0 :   bool rewindNeeded = false;
     494               0 :   mChildContainerTable.EnumerateEntries(RewindNeeded, &rewindNeeded);
     495               0 :   if (!rewindNeeded)
     496               0 :     return;
     497                 : 
     498               0 :   mAnimationElementTable.EnumerateEntries(RewindAnimation, nsnull);
     499               0 :   mChildContainerTable.EnumerateEntries(ClearRewindNeeded, nsnull);
     500                 : }
     501                 : 
     502                 : /*static*/ PR_CALLBACK PLDHashOperator
     503               0 : nsSMILAnimationController::RewindNeeded(TimeContainerPtrKey* aKey,
     504                 :                                         void* aData)
     505                 : {
     506               0 :   NS_ABORT_IF_FALSE(aData,
     507                 :       "Null data pointer during time container enumeration");
     508               0 :   bool* rewindNeeded = static_cast<bool*>(aData);
     509                 : 
     510               0 :   nsSMILTimeContainer* container = aKey->GetKey();
     511               0 :   if (container->NeedsRewind()) {
     512               0 :     *rewindNeeded = true;
     513               0 :     return PL_DHASH_STOP;
     514                 :   }
     515                 : 
     516               0 :   return PL_DHASH_NEXT;
     517                 : }
     518                 : 
     519                 : /*static*/ PR_CALLBACK PLDHashOperator
     520               0 : nsSMILAnimationController::RewindAnimation(AnimationElementPtrKey* aKey,
     521                 :                                            void* aData)
     522                 : {
     523               0 :   nsISMILAnimationElement* animElem = aKey->GetKey();
     524               0 :   nsSMILTimeContainer* timeContainer = animElem->GetTimeContainer();
     525               0 :   if (timeContainer && timeContainer->NeedsRewind()) {
     526               0 :     animElem->TimedElement().Rewind();
     527                 :   }
     528                 : 
     529               0 :   return PL_DHASH_NEXT;
     530                 : }
     531                 : 
     532                 : /*static*/ PR_CALLBACK PLDHashOperator
     533               0 : nsSMILAnimationController::ClearRewindNeeded(TimeContainerPtrKey* aKey,
     534                 :                                              void* aData)
     535                 : {
     536               0 :   aKey->GetKey()->ClearNeedsRewind();
     537               0 :   return PL_DHASH_NEXT;
     538                 : }
     539                 : 
     540                 : void
     541               0 : nsSMILAnimationController::DoMilestoneSamples()
     542                 : {
     543                 :   // We need to sample the timing model but because SMIL operates independently
     544                 :   // of the frame-rate, we can get one sample at t=0s and the next at t=10min.
     545                 :   //
     546                 :   // In between those two sample times a whole string of significant events
     547                 :   // might be expected to take place: events firing, new interdependencies
     548                 :   // between animations resolved and dissolved, etc.
     549                 :   //
     550                 :   // Furthermore, at any given time, we want to sample all the intervals that
     551                 :   // end at that time BEFORE any that begin. This behaviour is implied by SMIL's
     552                 :   // endpoint-exclusive timing model.
     553                 :   //
     554                 :   // So we have the animations (specifically the timed elements) register the
     555                 :   // next significant moment (called a milestone) in their lifetime and then we
     556                 :   // step through the model at each of these moments and sample those animations
     557                 :   // registered for those times. This way events can fire in the correct order,
     558                 :   // dependencies can be resolved etc.
     559                 : 
     560               0 :   nsSMILTime sampleTime = LL_MININT;
     561                 : 
     562               0 :   while (true) {
     563                 :     // We want to find any milestones AT OR BEFORE the current sample time so we
     564                 :     // initialise the next milestone to the moment after (1ms after, to be
     565                 :     // precise) the current sample time and see if there are any milestones
     566                 :     // before that. Any other milestones will be dealt with in a subsequent
     567                 :     // sample.
     568               0 :     nsSMILMilestone nextMilestone(GetCurrentTime() + 1, true);
     569               0 :     mChildContainerTable.EnumerateEntries(GetNextMilestone, &nextMilestone);
     570                 : 
     571               0 :     if (nextMilestone.mTime > GetCurrentTime()) {
     572                 :       break;
     573                 :     }
     574                 : 
     575               0 :     GetMilestoneElementsParams params;
     576               0 :     params.mMilestone = nextMilestone;
     577               0 :     mChildContainerTable.EnumerateEntries(GetMilestoneElements, &params);
     578               0 :     PRUint32 length = params.mElements.Length();
     579                 : 
     580                 :     // During the course of a sampling we don't want to actually go backwards.
     581                 :     // Due to negative offsets, early ends and the like, a timed element might
     582                 :     // register a milestone that is actually in the past. That's fine, but it's
     583                 :     // still only going to get *sampled* with whatever time we're up to and no
     584                 :     // earlier.
     585                 :     //
     586                 :     // Because we're only performing this clamping at the last moment, the
     587                 :     // animations will still all get sampled in the correct order and
     588                 :     // dependencies will be appropriately resolved.
     589               0 :     sampleTime = NS_MAX(nextMilestone.mTime, sampleTime);
     590                 : 
     591               0 :     for (PRUint32 i = 0; i < length; ++i) {
     592               0 :       nsISMILAnimationElement* elem = params.mElements[i].get();
     593               0 :       NS_ABORT_IF_FALSE(elem, "NULL animation element in list");
     594               0 :       nsSMILTimeContainer* container = elem->GetTimeContainer();
     595               0 :       if (!container)
     596                 :         // The container may be nsnull if the element has been detached from its
     597                 :         // parent since registering a milestone.
     598               0 :         continue;
     599                 : 
     600                 :       nsSMILTimeValue containerTimeValue =
     601               0 :         container->ParentToContainerTime(sampleTime);
     602               0 :       if (!containerTimeValue.IsDefinite())
     603               0 :         continue;
     604                 : 
     605                 :       // Clamp the converted container time to non-negative values.
     606               0 :       nsSMILTime containerTime = NS_MAX<nsSMILTime>(0, containerTimeValue.GetMillis());
     607                 : 
     608               0 :       if (nextMilestone.mIsEnd) {
     609               0 :         elem->TimedElement().SampleEndAt(containerTime);
     610                 :       } else {
     611               0 :         elem->TimedElement().SampleAt(containerTime);
     612                 :       }
     613                 :     }
     614                 :   }
     615               0 : }
     616                 : 
     617                 : /*static*/ PR_CALLBACK PLDHashOperator
     618               0 : nsSMILAnimationController::GetNextMilestone(TimeContainerPtrKey* aKey,
     619                 :                                             void* aData)
     620                 : {
     621               0 :   NS_ABORT_IF_FALSE(aKey, "Null hash key for time container hash table");
     622               0 :   NS_ABORT_IF_FALSE(aKey->GetKey(), "Null time container key in hash table");
     623               0 :   NS_ABORT_IF_FALSE(aData,
     624                 :       "Null data pointer during time container enumeration");
     625                 : 
     626               0 :   nsSMILMilestone* nextMilestone = static_cast<nsSMILMilestone*>(aData);
     627                 : 
     628               0 :   nsSMILTimeContainer* container = aKey->GetKey();
     629               0 :   if (container->IsPausedByType(nsSMILTimeContainer::PAUSE_BEGIN))
     630               0 :     return PL_DHASH_NEXT;
     631                 : 
     632               0 :   nsSMILMilestone thisMilestone;
     633                 :   bool didGetMilestone =
     634               0 :     container->GetNextMilestoneInParentTime(thisMilestone);
     635               0 :   if (didGetMilestone && thisMilestone < *nextMilestone) {
     636               0 :     *nextMilestone = thisMilestone;
     637                 :   }
     638                 : 
     639               0 :   return PL_DHASH_NEXT;
     640                 : }
     641                 : 
     642                 : /*static*/ PR_CALLBACK PLDHashOperator
     643               0 : nsSMILAnimationController::GetMilestoneElements(TimeContainerPtrKey* aKey,
     644                 :                                                 void* aData)
     645                 : {
     646               0 :   NS_ABORT_IF_FALSE(aKey, "Null hash key for time container hash table");
     647               0 :   NS_ABORT_IF_FALSE(aKey->GetKey(), "Null time container key in hash table");
     648               0 :   NS_ABORT_IF_FALSE(aData,
     649                 :       "Null data pointer during time container enumeration");
     650                 : 
     651                 :   GetMilestoneElementsParams* params =
     652               0 :     static_cast<GetMilestoneElementsParams*>(aData);
     653                 : 
     654               0 :   nsSMILTimeContainer* container = aKey->GetKey();
     655               0 :   if (container->IsPausedByType(nsSMILTimeContainer::PAUSE_BEGIN))
     656               0 :     return PL_DHASH_NEXT;
     657                 : 
     658                 :   container->PopMilestoneElementsAtMilestone(params->mMilestone,
     659               0 :                                              params->mElements);
     660                 : 
     661               0 :   return PL_DHASH_NEXT;
     662                 : }
     663                 : 
     664                 : /*static*/ PR_CALLBACK PLDHashOperator
     665               0 : nsSMILAnimationController::SampleTimeContainer(TimeContainerPtrKey* aKey,
     666                 :                                                void* aData)
     667                 : {
     668               0 :   NS_ENSURE_TRUE(aKey, PL_DHASH_NEXT);
     669               0 :   NS_ENSURE_TRUE(aKey->GetKey(), PL_DHASH_NEXT);
     670               0 :   NS_ENSURE_TRUE(aData, PL_DHASH_NEXT);
     671                 : 
     672                 :   SampleTimeContainerParams* params =
     673               0 :     static_cast<SampleTimeContainerParams*>(aData);
     674                 : 
     675               0 :   nsSMILTimeContainer* container = aKey->GetKey();
     676               0 :   if (!container->IsPausedByType(nsSMILTimeContainer::PAUSE_BEGIN) &&
     677               0 :       (container->NeedsSample() || !params->mSkipUnchangedContainers)) {
     678               0 :     container->ClearMilestones();
     679               0 :     container->Sample();
     680               0 :     container->MarkSeekFinished();
     681               0 :     params->mActiveContainers->PutEntry(container);
     682                 :   }
     683                 : 
     684               0 :   return PL_DHASH_NEXT;
     685                 : }
     686                 : 
     687                 : /*static*/ PR_CALLBACK PLDHashOperator
     688               0 : nsSMILAnimationController::SampleAnimation(AnimationElementPtrKey* aKey,
     689                 :                                            void* aData)
     690                 : {
     691               0 :   NS_ENSURE_TRUE(aKey, PL_DHASH_NEXT);
     692               0 :   NS_ENSURE_TRUE(aKey->GetKey(), PL_DHASH_NEXT);
     693               0 :   NS_ENSURE_TRUE(aData, PL_DHASH_NEXT);
     694                 : 
     695               0 :   nsISMILAnimationElement* animElem = aKey->GetKey();
     696               0 :   if (animElem->PassesConditionalProcessingTests()) {
     697               0 :     SampleAnimationParams* params = static_cast<SampleAnimationParams*>(aData);
     698                 : 
     699               0 :     SampleTimedElement(animElem, params->mActiveContainers);
     700               0 :     AddAnimationToCompositorTable(animElem, params->mCompositorTable);
     701                 :   }
     702                 : 
     703               0 :   return PL_DHASH_NEXT;
     704                 : }
     705                 : 
     706                 : /*static*/ void
     707               0 : nsSMILAnimationController::SampleTimedElement(
     708                 :   nsISMILAnimationElement* aElement, TimeContainerHashtable* aActiveContainers)
     709                 : {
     710               0 :   nsSMILTimeContainer* timeContainer = aElement->GetTimeContainer();
     711               0 :   if (!timeContainer)
     712               0 :     return;
     713                 : 
     714                 :   // We'd like to call timeContainer->NeedsSample() here and skip all timed
     715                 :   // elements that belong to paused time containers that don't need a sample,
     716                 :   // but that doesn't work because we've already called Sample() on all the time
     717                 :   // containers so the paused ones don't need a sample any more and they'll
     718                 :   // return false.
     719                 :   //
     720                 :   // Instead we build up a hashmap of active time containers during the previous
     721                 :   // step (SampleTimeContainer) and then test here if the container for this
     722                 :   // timed element is in the list.
     723               0 :   if (!aActiveContainers->GetEntry(timeContainer))
     724               0 :     return;
     725                 : 
     726               0 :   nsSMILTime containerTime = timeContainer->GetCurrentTime();
     727                 : 
     728               0 :   NS_ABORT_IF_FALSE(!timeContainer->IsSeeking(),
     729                 :       "Doing a regular sample but the time container is still seeking");
     730               0 :   aElement->TimedElement().SampleAt(containerTime);
     731                 : }
     732                 : 
     733                 : /*static*/ void
     734               0 : nsSMILAnimationController::AddAnimationToCompositorTable(
     735                 :   nsISMILAnimationElement* aElement, nsSMILCompositorTable* aCompositorTable)
     736                 : {
     737                 :   // Add a compositor to the hash table if there's not already one there
     738               0 :   nsSMILTargetIdentifier key;
     739               0 :   if (!GetTargetIdentifierForAnimation(aElement, key))
     740                 :     // Something's wrong/missing about animation's target; skip this animation
     741                 :     return;
     742                 : 
     743               0 :   nsSMILAnimationFunction& func = aElement->AnimationFunction();
     744                 : 
     745                 :   // Only add active animation functions. If there are no active animations
     746                 :   // targeting an attribute, no compositor will be created and any previously
     747                 :   // applied animations will be cleared.
     748               0 :   if (func.IsActiveOrFrozen()) {
     749                 :     // Look up the compositor for our target, & add our animation function
     750                 :     // to its list of animation functions.
     751               0 :     nsSMILCompositor* result = aCompositorTable->PutEntry(key);
     752               0 :     result->AddAnimationFunction(&func);
     753                 : 
     754               0 :   } else if (func.HasChanged()) {
     755                 :     // Look up the compositor for our target, and force it to skip the
     756                 :     // "nothing's changed so don't bother compositing" optimization for this
     757                 :     // sample. |func| is inactive, but it's probably *newly* inactive (since
     758                 :     // it's got HasChanged() == true), so we need to make sure to recompose
     759                 :     // its target.
     760               0 :     nsSMILCompositor* result = aCompositorTable->PutEntry(key);
     761               0 :     result->ToggleForceCompositing();
     762                 : 
     763                 :     // We've now made sure that |func|'s inactivity will be reflected as of
     764                 :     // this sample. We need to clear its HasChanged() flag so that it won't
     765                 :     // trigger this same clause in future samples (until it changes again).
     766               0 :     func.ClearHasChanged();
     767                 :   }
     768                 : }
     769                 : 
     770                 : // Helper function that, given a nsISMILAnimationElement, looks up its target
     771                 : // element & target attribute and populates a nsSMILTargetIdentifier
     772                 : // for this target.
     773                 : /*static*/ bool
     774               0 : nsSMILAnimationController::GetTargetIdentifierForAnimation(
     775                 :     nsISMILAnimationElement* aAnimElem, nsSMILTargetIdentifier& aResult)
     776                 : {
     777                 :   // Look up target (animated) element
     778               0 :   Element* targetElem = aAnimElem->GetTargetElementContent();
     779               0 :   if (!targetElem)
     780                 :     // Animation has no target elem -- skip it.
     781               0 :     return false;
     782                 : 
     783                 :   // Look up target (animated) attribute
     784                 :   // SMILANIM section 3.1, attributeName may
     785                 :   // have an XMLNS prefix to indicate the XML namespace.
     786               0 :   nsCOMPtr<nsIAtom> attributeName;
     787                 :   PRInt32 attributeNamespaceID;
     788               0 :   if (!aAnimElem->GetTargetAttributeName(&attributeNamespaceID,
     789               0 :                                          getter_AddRefs(attributeName)))
     790                 :     // Animation has no target attr -- skip it.
     791               0 :     return false;
     792                 : 
     793                 :   // Look up target (animated) attribute-type
     794               0 :   nsSMILTargetAttrType attributeType = aAnimElem->GetTargetAttributeType();
     795                 : 
     796                 :   // Check if an 'auto' attributeType refers to a CSS property or XML attribute.
     797                 :   // Note that SMIL requires we search for CSS properties first. So if they
     798                 :   // overlap, 'auto' = 'CSS'. (SMILANIM 3.1)
     799               0 :   bool isCSS = false;
     800               0 :   if (attributeType == eSMILTargetAttrType_auto) {
     801               0 :     if (attributeNamespaceID == kNameSpaceID_None) {
     802                 :       nsCSSProperty prop =
     803               0 :         nsCSSProps::LookupProperty(nsDependentAtomString(attributeName));
     804               0 :       isCSS = nsSMILCSSProperty::IsPropertyAnimatable(prop);
     805                 :     }
     806                 :   } else {
     807               0 :     isCSS = (attributeType == eSMILTargetAttrType_CSS);
     808                 :   }
     809                 : 
     810                 :   // Construct the key
     811               0 :   aResult.mElement = targetElem;
     812               0 :   aResult.mAttributeName = attributeName;
     813               0 :   aResult.mAttributeNamespaceID = attributeNamespaceID;
     814               0 :   aResult.mIsCSS = isCSS;
     815                 : 
     816               0 :   return true;
     817                 : }
     818                 : 
     819                 : //----------------------------------------------------------------------
     820                 : // Add/remove child time containers
     821                 : 
     822                 : nsresult
     823               0 : nsSMILAnimationController::AddChild(nsSMILTimeContainer& aChild)
     824                 : {
     825               0 :   TimeContainerPtrKey* key = mChildContainerTable.PutEntry(&aChild);
     826               0 :   NS_ENSURE_TRUE(key, NS_ERROR_OUT_OF_MEMORY);
     827                 : 
     828               0 :   if (!mPauseState && mChildContainerTable.Count() == 1) {
     829               0 :     MaybeStartSampling(GetRefreshDriver());
     830               0 :     Sample(); // Run the first sample manually
     831                 :   }
     832                 : 
     833               0 :   return NS_OK;
     834                 : }
     835                 : 
     836                 : void
     837               0 : nsSMILAnimationController::RemoveChild(nsSMILTimeContainer& aChild)
     838                 : {
     839               0 :   mChildContainerTable.RemoveEntry(&aChild);
     840                 : 
     841               0 :   if (!mPauseState && mChildContainerTable.Count() == 0) {
     842               0 :     StopSampling(GetRefreshDriver());
     843                 :   }
     844               0 : }
     845                 : 
     846                 : // Helper method
     847                 : nsRefreshDriver*
     848               0 : nsSMILAnimationController::GetRefreshDriver()
     849                 : {
     850               0 :   if (!mDocument) {
     851               0 :     NS_ERROR("Requesting refresh driver after document has disconnected!");
     852               0 :     return nsnull;
     853                 :   }
     854                 : 
     855               0 :   nsIPresShell* shell = mDocument->GetShell();
     856               0 :   if (!shell) {
     857               0 :     return nsnull;
     858                 :   }
     859                 : 
     860               0 :   nsPresContext* context = shell->GetPresContext();
     861               0 :   return context ? context->RefreshDriver() : nsnull;
     862                 : }
     863                 : 
     864                 : void
     865               0 : nsSMILAnimationController::FlagDocumentNeedsFlush()
     866                 : {
     867               0 :   mDocument->SetNeedStyleFlush();
     868               0 : }

Generated by: LCOV version 1.7