dom/smil/nsSMILTimeContainer.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial