dom/smil/nsSMILTimeContainer.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial