LCOV - code coverage report
Current view: top level - dom/smil - nsSMILTimeContainer.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 73 143 51.0 %
Date: 2017-07-14 16:53:18 Functions: 13 22 59.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "nsSMILTimeContainer.h"
       8             : #include "nsSMILTimeValue.h"
       9             : #include "nsSMILTimedElement.h"
      10             : #include <algorithm>
      11             : 
      12             : #include "mozilla/AutoRestore.h"
      13             : 
      14             : using namespace mozilla;
      15             : 
      16          44 : nsSMILTimeContainer::nsSMILTimeContainer()
      17             : :
      18             :   mParent(nullptr),
      19             :   mCurrentTime(0L),
      20             :   mParentOffset(0L),
      21             :   mPauseStart(0L),
      22             :   mNeedsPauseSample(false),
      23             :   mNeedsRewind(false),
      24             :   mIsSeeking(false),
      25             : #ifdef DEBUG
      26             :   mHoldingEntries(false),
      27             : #endif
      28          44 :   mPauseState(PAUSE_BEGIN)
      29             : {
      30          44 : }
      31             : 
      32           0 : nsSMILTimeContainer::~nsSMILTimeContainer()
      33             : {
      34           0 :   if (mParent) {
      35           0 :     mParent->RemoveChild(*this);
      36             :   }
      37           0 : }
      38             : 
      39             : nsSMILTimeValue
      40           0 : nsSMILTimeContainer::ContainerToParentTime(nsSMILTime aContainerTime) const
      41             : {
      42             :   // If we're paused, then future times are indefinite
      43           0 :   if (IsPaused() && aContainerTime > mCurrentTime)
      44           0 :     return nsSMILTimeValue::Indefinite();
      45             : 
      46           0 :   return nsSMILTimeValue(aContainerTime + mParentOffset);
      47             : }
      48             : 
      49             : nsSMILTimeValue
      50           0 : nsSMILTimeContainer::ParentToContainerTime(nsSMILTime aParentTime) const
      51             : {
      52             :   // If we're paused, then any time after when we paused is indefinite
      53           0 :   if (IsPaused() && aParentTime > mPauseStart)
      54           0 :     return nsSMILTimeValue::Indefinite();
      55             : 
      56           0 :   return nsSMILTimeValue(aParentTime - mParentOffset);
      57             : }
      58             : 
      59             : void
      60          44 : nsSMILTimeContainer::Begin()
      61             : {
      62          44 :   Resume(PAUSE_BEGIN);
      63          44 :   if (mPauseState) {
      64           0 :     mNeedsPauseSample = true;
      65             :   }
      66             : 
      67             :   // This is a little bit complicated here. Ideally we'd just like to call
      68             :   // Sample() and force an initial sample but this turns out to be a bad idea
      69             :   // because this may mean that NeedsSample() no longer reports true and so when
      70             :   // we come to the first real sample our parent will skip us over altogether.
      71             :   // So we force the time to be updated and adopt the policy to never call
      72             :   // Sample() ourselves but to always leave that to our parent or client.
      73             : 
      74          44 :   UpdateCurrentTime();
      75          44 : }
      76             : 
      77             : void
      78          22 : nsSMILTimeContainer::Pause(uint32_t aType)
      79             : {
      80          22 :   bool didStartPause = false;
      81             : 
      82          22 :   if (!mPauseState && aType) {
      83          22 :     mPauseStart = GetParentTime();
      84          22 :     mNeedsPauseSample = true;
      85          22 :     didStartPause = true;
      86             :   }
      87             : 
      88          22 :   mPauseState |= aType;
      89             : 
      90          22 :   if (didStartPause) {
      91          22 :     NotifyTimeChange();
      92             :   }
      93          22 : }
      94             : 
      95             : void
      96          45 : nsSMILTimeContainer::Resume(uint32_t aType)
      97             : {
      98          45 :   if (!mPauseState)
      99           0 :     return;
     100             : 
     101          45 :   mPauseState &= ~aType;
     102             : 
     103          45 :   if (!mPauseState) {
     104          45 :     nsSMILTime extraOffset = GetParentTime() - mPauseStart;
     105          45 :     mParentOffset += extraOffset;
     106          45 :     NotifyTimeChange();
     107             :   }
     108             : }
     109             : 
     110             : nsSMILTime
     111         381 : nsSMILTimeContainer::GetCurrentTime() const
     112             : {
     113             :   // The following behaviour is consistent with:
     114             :   // http://www.w3.org/2003/01/REC-SVG11-20030114-errata
     115             :   //  #getCurrentTime_setCurrentTime_undefined_before_document_timeline_begin
     116             :   // which says that if GetCurrentTime is called before the document timeline
     117             :   // has begun we should just return 0.
     118         381 :   if (IsPausedByType(PAUSE_BEGIN))
     119           0 :     return 0L;
     120             : 
     121         381 :   return mCurrentTime;
     122             : }
     123             : 
     124             : void
     125          36 : nsSMILTimeContainer::SetCurrentTime(nsSMILTime aSeekTo)
     126             : {
     127             :   // SVG 1.1 doesn't specify what to do for negative times so we adopt SVGT1.2's
     128             :   // behaviour of clamping negative times to 0.
     129          36 :   aSeekTo = std::max<nsSMILTime>(0, aSeekTo);
     130             : 
     131             :   // The following behaviour is consistent with:
     132             :   // http://www.w3.org/2003/01/REC-SVG11-20030114-errata
     133             :   //  #getCurrentTime_setCurrentTime_undefined_before_document_timeline_begin
     134             :   // which says that if SetCurrentTime is called before the document timeline
     135             :   // has begun we should still adjust the offset.
     136          36 :   nsSMILTime parentTime = GetParentTime();
     137          36 :   mParentOffset = parentTime - aSeekTo;
     138          36 :   mIsSeeking = true;
     139             : 
     140          36 :   if (IsPaused()) {
     141           0 :     mNeedsPauseSample = true;
     142           0 :     mPauseStart = parentTime;
     143             :   }
     144             : 
     145          36 :   if (aSeekTo < mCurrentTime) {
     146             :     // Backwards seek
     147           0 :     mNeedsRewind = true;
     148           0 :     ClearMilestones();
     149             :   }
     150             : 
     151             :   // Force an update to the current time in case we get a call to GetCurrentTime
     152             :   // before another call to Sample().
     153          36 :   UpdateCurrentTime();
     154             : 
     155          36 :   NotifyTimeChange();
     156          36 : }
     157             : 
     158             : nsSMILTime
     159         174 : nsSMILTimeContainer::GetParentTime() const
     160             : {
     161         174 :   if (mParent)
     162         174 :     return mParent->GetCurrentTime();
     163             : 
     164           0 :   return 0L;
     165             : }
     166             : 
     167             : void
     168           0 : nsSMILTimeContainer::SyncPauseTime()
     169             : {
     170           0 :   if (IsPaused()) {
     171           0 :     nsSMILTime parentTime = GetParentTime();
     172           0 :     nsSMILTime extraOffset = parentTime - mPauseStart;
     173           0 :     mParentOffset += extraOffset;
     174           0 :     mPauseStart = parentTime;
     175             :   }
     176           0 : }
     177             : 
     178             : void
     179          59 : nsSMILTimeContainer::Sample()
     180             : {
     181          59 :   if (!NeedsSample())
     182           0 :     return;
     183             : 
     184          59 :   UpdateCurrentTime();
     185          59 :   DoSample();
     186             : 
     187          59 :   mNeedsPauseSample = false;
     188             : }
     189             : 
     190             : nsresult
     191          22 : nsSMILTimeContainer::SetParent(nsSMILTimeContainer* aParent)
     192             : {
     193          22 :   if (mParent) {
     194           0 :     mParent->RemoveChild(*this);
     195             :     // When we're not attached to a parent time container, GetParentTime() will
     196             :     // return 0. We need to adjust our pause state information to be relative to
     197             :     // this new time base.
     198             :     // Note that since "current time = parent time - parent offset" setting the
     199             :     // parent offset and pause start as follows preserves our current time even
     200             :     // while parent time = 0.
     201           0 :     mParentOffset = -mCurrentTime;
     202           0 :     mPauseStart = 0L;
     203             :   }
     204             : 
     205          22 :   mParent = aParent;
     206             : 
     207          22 :   nsresult rv = NS_OK;
     208          22 :   if (mParent) {
     209          22 :     rv = mParent->AddChild(*this);
     210             :   }
     211             : 
     212          22 :   return rv;
     213             : }
     214             : 
     215             : bool
     216           0 : nsSMILTimeContainer::AddMilestone(const nsSMILMilestone& aMilestone,
     217             :                                   mozilla::dom::SVGAnimationElement& aElement)
     218             : {
     219             :   // We record the milestone time and store it along with the element but this
     220             :   // time may change (e.g. if attributes are changed on the timed element in
     221             :   // between samples). If this happens, then we may do an unecessary sample
     222             :   // but that's pretty cheap.
     223           0 :   MOZ_ASSERT(!mHoldingEntries);
     224           0 :   return mMilestoneEntries.Push(MilestoneEntry(aMilestone, aElement));
     225             : }
     226             : 
     227             : void
     228          58 : nsSMILTimeContainer::ClearMilestones()
     229             : {
     230          58 :   MOZ_ASSERT(!mHoldingEntries);
     231          58 :   mMilestoneEntries.Clear();
     232          58 : }
     233             : 
     234             : bool
     235          58 : nsSMILTimeContainer::GetNextMilestoneInParentTime(
     236             :     nsSMILMilestone& aNextMilestone) const
     237             : {
     238          58 :   if (mMilestoneEntries.IsEmpty())
     239          58 :     return false;
     240             : 
     241             :   nsSMILTimeValue parentTime =
     242           0 :     ContainerToParentTime(mMilestoneEntries.Top().mMilestone.mTime);
     243           0 :   if (!parentTime.IsDefinite())
     244           0 :     return false;
     245             : 
     246           0 :   aNextMilestone = nsSMILMilestone(parentTime.GetMillis(),
     247           0 :                                    mMilestoneEntries.Top().mMilestone.mIsEnd);
     248             : 
     249           0 :   return true;
     250             : }
     251             : 
     252             : bool
     253           0 : nsSMILTimeContainer::PopMilestoneElementsAtMilestone(
     254             :       const nsSMILMilestone& aMilestone,
     255             :       AnimElemArray& aMatchedElements)
     256             : {
     257           0 :   if (mMilestoneEntries.IsEmpty())
     258           0 :     return false;
     259             : 
     260           0 :   nsSMILTimeValue containerTime = ParentToContainerTime(aMilestone.mTime);
     261           0 :   if (!containerTime.IsDefinite())
     262           0 :     return false;
     263             : 
     264             :   nsSMILMilestone containerMilestone(containerTime.GetMillis(),
     265           0 :                                      aMilestone.mIsEnd);
     266             : 
     267           0 :   MOZ_ASSERT(mMilestoneEntries.Top().mMilestone >= containerMilestone,
     268             :              "Trying to pop off earliest times but we have earlier ones that "
     269             :              "were overlooked");
     270             : 
     271           0 :   MOZ_ASSERT(!mHoldingEntries);
     272             : 
     273           0 :   bool gotOne = false;
     274           0 :   while (!mMilestoneEntries.IsEmpty() &&
     275           0 :       mMilestoneEntries.Top().mMilestone == containerMilestone)
     276             :   {
     277           0 :     aMatchedElements.AppendElement(mMilestoneEntries.Pop().mTimebase);
     278           0 :     gotOne = true;
     279             :   }
     280             : 
     281           0 :   return gotOne;
     282             : }
     283             : 
     284             : void
     285           0 : nsSMILTimeContainer::Traverse(nsCycleCollectionTraversalCallback* aCallback)
     286             : {
     287             : #ifdef DEBUG
     288           0 :   AutoRestore<bool> saveHolding(mHoldingEntries);
     289           0 :   mHoldingEntries = true;
     290             : #endif
     291           0 :   const MilestoneEntry* p = mMilestoneEntries.Elements();
     292           0 :   while (p < mMilestoneEntries.Elements() + mMilestoneEntries.Length()) {
     293           0 :     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback, "mTimebase");
     294           0 :     aCallback->NoteXPCOMChild(static_cast<nsIContent*>(p->mTimebase.get()));
     295           0 :     ++p;
     296             :   }
     297           0 : }
     298             : 
     299             : void
     300           0 : nsSMILTimeContainer::Unlink()
     301             : {
     302           0 :   MOZ_ASSERT(!mHoldingEntries);
     303           0 :   mMilestoneEntries.Clear();
     304           0 : }
     305             : 
     306             : void
     307         139 : nsSMILTimeContainer::UpdateCurrentTime()
     308             : {
     309         139 :   nsSMILTime now = IsPaused() ? mPauseStart : GetParentTime();
     310         139 :   mCurrentTime = now - mParentOffset;
     311         139 :   MOZ_ASSERT(mCurrentTime >= 0, "Container has negative time");
     312         139 : }
     313             : 
     314             : void
     315         103 : nsSMILTimeContainer::NotifyTimeChange()
     316             : {
     317             :   // Called when the container time is changed with respect to the document
     318             :   // time. When this happens time dependencies in other time containers need to
     319             :   // re-resolve their times because begin and end times are stored in container
     320             :   // time.
     321             :   //
     322             :   // To get the list of timed elements with dependencies we simply re-use the
     323             :   // milestone elements. This is because any timed element with dependents and
     324             :   // with significant transitions yet to fire should have their next milestone
     325             :   // registered. Other timed elements don't matter.
     326             : 
     327             :   // Copy the timed elements to a separate array before calling
     328             :   // HandleContainerTimeChange on each of them in case doing so mutates
     329             :   // mMilestoneEntries.
     330         206 :   nsTArray<RefPtr<mozilla::dom::SVGAnimationElement>> elems;
     331             : 
     332             :   {
     333             : #ifdef DEBUG
     334         206 :     AutoRestore<bool> saveHolding(mHoldingEntries);
     335         103 :     mHoldingEntries = true;
     336             : #endif
     337         206 :     for (const MilestoneEntry* p = mMilestoneEntries.Elements();
     338         103 :         p < mMilestoneEntries.Elements() + mMilestoneEntries.Length();
     339             :         ++p) {
     340           0 :       elems.AppendElement(p->mTimebase.get());
     341             :     }
     342             :   }
     343             : 
     344         103 :   for (auto& elem : elems) {
     345           0 :     elem->TimedElement().HandleContainerTimeChange();
     346             :   }
     347         103 : }

Generated by: LCOV version 1.13