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/. */
7 #ifndef DOM_SMIL_SMILTIMEDELEMENT_H_
8 #define DOM_SMIL_SMILTIMEDELEMENT_H_
12 #include "mozilla/EventForwards.h"
13 #include "mozilla/SMILInstanceTime.h"
14 #include "mozilla/SMILInterval.h"
15 #include "mozilla/SMILMilestone.h"
16 #include "mozilla/SMILRepeatCount.h"
17 #include "mozilla/SMILTimeValueSpec.h"
18 #include "mozilla/SMILTypes.h"
19 #include "mozilla/UniquePtr.h"
20 #include "nsAttrValue.h"
21 #include "nsHashKeys.h"
23 #include "nsTHashtable.h"
29 class SMILAnimationFunction
;
30 class SMILTimeContainer
;
34 class SVGAnimationElement
;
37 //----------------------------------------------------------------------
40 class SMILTimedElement
{
45 using Element
= dom::Element
;
48 * Sets the owning animation element which this class uses to convert between
49 * container times and to register timebase elements.
51 void SetAnimationElement(mozilla::dom::SVGAnimationElement
* aElement
);
54 * Returns the time container with which this timed element is associated or
55 * nullptr if it is not associated with a time container.
57 SMILTimeContainer
* GetTimeContainer();
60 * Returns the element targeted by the animation element. Needed for
61 * registering event listeners against the appropriate element.
63 Element
* GetTargetElement();
66 * Methods for supporting the ElementTimeControl interface.
70 * Adds a new begin instance time at the current container time plus or minus
71 * the specified offset.
73 * @param aOffsetSeconds A real number specifying the number of seconds to add
74 * to the current container time.
75 * @return NS_OK if the operation succeeeded, or an error code otherwise.
77 nsresult
BeginElementAt(double aOffsetSeconds
);
80 * Adds a new end instance time at the current container time plus or minus
81 * the specified offset.
83 * @param aOffsetSeconds A real number specifying the number of seconds to add
84 * to the current container time.
85 * @return NS_OK if the operation succeeeded, or an error code otherwise.
87 nsresult
EndElementAt(double aOffsetSeconds
);
90 * Methods for supporting the SVGAnimationElement interface.
94 * According to SVG 1.1 SE this returns
96 * the begin time, in seconds, for this animation element's current
97 * interval, if it exists, regardless of whether the interval has begun yet.
99 * @return the start time as defined above in milliseconds or an unresolved
100 * time if there is no current interval.
102 SMILTimeValue
GetStartTime() const;
105 * Returns the simple duration of this element.
107 * @return the simple duration in milliseconds or INDEFINITE.
109 SMILTimeValue
GetSimpleDuration() const { return mSimpleDur
; }
112 * Methods for supporting hyperlinking
116 * Internal SMIL methods
120 * Returns the time to seek the document to when this element is targetted by
123 * The behavior is defined here:
124 * http://www.w3.org/TR/smil-animation/#HyperlinkSemantics
126 * It is very similar to GetStartTime() with the exception that when the
127 * element is not active, the begin time of the *first* interval is returned.
129 * @return the time to seek the documen to in milliseconds or an unresolved
130 * time if there is no resolved interval.
132 SMILTimeValue
GetHyperlinkTime() const;
135 * Adds an instance time object this element's list of instance times.
136 * These instance times are used when creating intervals.
138 * This method is typically called by an SMILTimeValueSpec.
140 * @param aInstanceTime The time to add, expressed in container time.
141 * @param aIsBegin true if the time to be added represents a begin
142 * time or false if it represents an end time.
144 void AddInstanceTime(SMILInstanceTime
* aInstanceTime
, bool aIsBegin
);
147 * Requests this element update the given instance time.
149 * This method is typically called by a child SMILTimeValueSpec.
151 * @param aInstanceTime The instance time to update.
152 * @param aUpdatedTime The time to update aInstanceTime with.
153 * @param aDependentTime The instance time upon which aInstanceTime should be
155 * @param aIsBegin true if the time to be updated represents a begin
156 * instance time or false if it represents an end
159 void UpdateInstanceTime(SMILInstanceTime
* aInstanceTime
,
160 SMILTimeValue
& aUpdatedTime
, bool aIsBegin
);
163 * Removes an instance time object from this element's list of instance times.
165 * This method is typically called by a child SMILTimeValueSpec.
167 * @param aInstanceTime The instance time to remove.
168 * @param aIsBegin true if the time to be removed represents a begin
169 * time or false if it represents an end time.
171 void RemoveInstanceTime(SMILInstanceTime
* aInstanceTime
, bool aIsBegin
);
174 * Removes all the instance times associated with the given
175 * SMILTimeValueSpec object. Used when an ID assignment changes and hence
176 * all the previously associated instance times become invalid.
178 * @param aCreator The SMILTimeValueSpec object whose created
179 * SMILInstanceTime's should be removed.
180 * @param aIsBegin true if the times to be removed represent begin
181 * times or false if they are end times.
183 void RemoveInstanceTimesForCreator(const SMILTimeValueSpec
* aCreator
,
187 * Sets the object that will be called by this timed element each time it is
190 * In Schmitz's model it is possible to associate several time clients with
191 * a timed element but for now we only allow one.
193 * @param aClient The time client to associate. Any previous time client
194 * will be disassociated and no longer sampled. Setting this
195 * to nullptr will simply disassociate the previous client,
198 void SetTimeClient(SMILAnimationFunction
* aClient
);
201 * Samples the object at the given container time. Timing intervals are
202 * updated and if this element is active at the given time the associated time
203 * client will be sampled with the appropriate simple time.
205 * @param aContainerTime The container time at which to sample.
207 void SampleAt(SMILTime aContainerTime
);
210 * Performs a special sample for the end of an interval. Such a sample should
211 * only advance the timed element (and any dependent elements) to the waiting
212 * or postactive state. It should not cause a transition to the active state.
213 * Transition to the active state is only performed on a regular SampleAt.
215 * This allows all interval ends at a given time to be processed first and
216 * hence the new interval can be established based on full information of the
217 * available instance times.
219 * @param aContainerTime The container time at which to sample.
221 void SampleEndAt(SMILTime aContainerTime
);
224 * Informs the timed element that its time container has changed time
225 * relative to document time. The timed element therefore needs to update its
226 * dependent elements (which may belong to a different time container) so they
227 * can re-resolve their times.
229 void HandleContainerTimeChange();
232 * Resets this timed element's accumulated times and intervals back to start
235 * This is used for backwards seeking where rather than accumulating
236 * historical timing state and winding it back, we reset the element and seek
242 * Marks this element as disabled or not. If the element is disabled, it
243 * will ignore any future samples and discard any accumulated timing state.
245 * This is used by SVG to "turn off" timed elements when the associated
246 * animation element has failing conditional processing tests.
248 * Returns true if the disabled state of the timed element was changed
249 * as a result of this call (i.e. it was not a redundant call).
251 bool SetIsDisabled(bool aIsDisabled
);
254 * Attempts to set an attribute on this timed element.
256 * @param aAttribute The name of the attribute to set. The namespace of this
257 * attribute is not specified as it is checked by the host
258 * element. Only attributes in the namespace defined for
259 * SMIL attributes in the host language are passed to the
261 * @param aValue The attribute value.
262 * @param aResult The nsAttrValue object that may be used for storing the
264 * @param aContextElement The element to use for context when resolving
265 * references to other elements.
266 * @param[out] aParseResult The result of parsing the attribute. Will be set
267 * to NS_OK if parsing is successful.
269 * @return true if the given attribute is a timing attribute, false
272 bool SetAttr(nsAtom
* aAttribute
, const nsAString
& aValue
,
273 nsAttrValue
& aResult
, Element
& aContextElement
,
274 nsresult
* aParseResult
= nullptr);
277 * Attempts to unset an attribute on this timed element.
279 * @param aAttribute The name of the attribute to set. As with SetAttr the
280 * namespace of the attribute is not specified (see
283 * @return true if the given attribute is a timing attribute, false
286 bool UnsetAttr(nsAtom
* aAttribute
);
289 * Adds a syncbase dependency to the list of dependents that will be notified
290 * when this timed element creates, deletes, or updates its current interval.
292 * @param aDependent The SMILTimeValueSpec object to notify. A raw pointer
293 * to this object will be stored. Therefore it is necessary
294 * for the object to be explicitly unregistered (with
295 * RemoveDependent) when it is destroyed.
297 void AddDependent(SMILTimeValueSpec
& aDependent
);
300 * Removes a syncbase dependency from the list of dependents that are notified
301 * when the current interval is modified.
303 * @param aDependent The SMILTimeValueSpec object to unregister.
305 void RemoveDependent(SMILTimeValueSpec
& aDependent
);
308 * Determines if this timed element is dependent on the given timed element's
309 * begin time for the interval currently in effect. Whilst the element is in
310 * the active state this is the current interval and in the postactive or
311 * waiting state this is the previous interval if one exists. In all other
312 * cases the element is not considered a time dependent of any other element.
314 * @param aOther The potential syncbase element.
315 * @return true if this timed element's begin time for the currently
316 * effective interval is directly or indirectly derived from aOther, false
319 bool IsTimeDependent(const SMILTimedElement
& aOther
) const;
322 * Called when the timed element has been bound to the document so that
323 * references from this timed element to other elements can be resolved.
325 * @param aContextElement The element which provides the necessary context for
326 * resolving references. This is typically the element
327 * in the host language that owns this timed element.
329 void BindToTree(Element
& aContextElement
);
332 * Called when the target of the animation has changed so that event
333 * registrations can be updated.
335 void HandleTargetElementChange(Element
* aNewTarget
);
338 * Called when the timed element has been removed from a document so that
339 * references to other elements can be broken.
341 void DissolveReferences() { Unlink(); }
344 void Traverse(nsCycleCollectionTraversalCallback
* aCallback
);
347 using RemovalTestFunction
= bool (*)(SMILInstanceTime
* aInstance
);
351 using TimeValueSpecList
= nsTArray
<UniquePtr
<SMILTimeValueSpec
>>;
352 using InstanceTimeList
= nsTArray
<RefPtr
<SMILInstanceTime
>>;
353 using IntervalList
= nsTArray
<UniquePtr
<SMILInterval
>>;
354 using TimeValueSpecPtrKey
= nsPtrHashKey
<SMILTimeValueSpec
>;
355 using TimeValueSpecHashSet
= nsTHashtable
<TimeValueSpecPtrKey
>;
358 class InstanceTimeComparator
{
360 bool Equals(const SMILInstanceTime
* aElem1
,
361 const SMILInstanceTime
* aElem2
) const;
362 bool LessThan(const SMILInstanceTime
* aElem1
,
363 const SMILInstanceTime
* aElem2
) const;
366 // Templated helper functions
367 template <class TestFunctor
>
368 void RemoveInstanceTimes(InstanceTimeList
& aArray
, TestFunctor
& aTest
);
371 // Implementation helpers
374 nsresult
SetBeginSpec(const nsAString
& aBeginSpec
, Element
& aContextElement
,
375 RemovalTestFunction aRemove
);
376 nsresult
SetEndSpec(const nsAString
& aEndSpec
, Element
& aContextElement
,
377 RemovalTestFunction aRemove
);
378 nsresult
SetSimpleDuration(const nsAString
& aDurSpec
);
379 nsresult
SetMin(const nsAString
& aMinSpec
);
380 nsresult
SetMax(const nsAString
& aMaxSpec
);
381 nsresult
SetRestart(const nsAString
& aRestartSpec
);
382 nsresult
SetRepeatCount(const nsAString
& aRepeatCountSpec
);
383 nsresult
SetRepeatDur(const nsAString
& aRepeatDurSpec
);
384 nsresult
SetFillMode(const nsAString
& aFillModeSpec
);
386 void UnsetBeginSpec(RemovalTestFunction aRemove
);
387 void UnsetEndSpec(RemovalTestFunction aRemove
);
388 void UnsetSimpleDuration();
392 void UnsetRepeatCount();
393 void UnsetRepeatDur();
394 void UnsetFillMode();
396 nsresult
SetBeginOrEndSpec(const nsAString
& aSpec
, Element
& aContextElement
,
397 bool aIsBegin
, RemovalTestFunction aRemove
);
398 void ClearSpecs(TimeValueSpecList
& aSpecs
, InstanceTimeList
& aInstances
,
399 RemovalTestFunction aRemove
);
400 void ClearIntervals();
401 void DoSampleAt(SMILTime aContainerTime
, bool aEndOnly
);
404 * Helper function to check for an early end and, if necessary, update the
405 * current interval accordingly.
407 * See SMIL 3.0, section 5.4.5, Element life cycle, "Active Time - Playing an
408 * interval" for a description of ending early.
410 * @param aSampleTime The current sample time. Early ends should only be
411 * applied at the last possible moment (i.e. if they are at
412 * or before the current sample time) and only if the
413 * current interval is not already ending.
414 * @return true if the end time of the current interval was updated,
417 bool ApplyEarlyEnd(const SMILTimeValue
& aSampleTime
);
420 * Clears certain state in response to the element restarting.
422 * This state is described in SMIL 3.0, section 5.4.3, Resetting element state
427 * Clears all accumulated timing state except for those instance times for
428 * which aRemove does not return true.
430 * Unlike the Reset method which only clears instance times, this clears the
431 * element's state, intervals (including current interval), and tells the
432 * client animation function to stop applying a result. In effect, it returns
433 * the element to its initial state but preserves any instance times excluded
434 * by the passed-in function.
436 void ClearTimingState(RemovalTestFunction aRemove
);
439 * Recreates timing state by re-applying begin/end attributes specified on
440 * the associated animation element.
442 * Note that this does not completely restore the information cleared by
443 * ClearTimingState since it leaves the element in the startup state.
444 * The element state will be updated on the next sample.
446 void RebuildTimingState(RemovalTestFunction aRemove
);
449 * Completes a seek operation by sending appropriate events and, in the case
450 * of a backwards seek, updating the state of timing information that was
451 * previously considered historical.
456 * Unmarks instance times that were previously preserved because they were
457 * considered important historical milestones but are no longer such because
458 * a backwards seek has been performed.
460 void UnpreserveInstanceTimes(InstanceTimeList
& aList
);
463 * Helper function to iterate through this element's accumulated timing
464 * information (specifically old SMILIntervals and SMILTimeInstanceTimes)
465 * and discard items that are no longer needed or exceed some threshold of
468 void FilterHistory();
470 // Helper functions for FilterHistory to clear old SMILIntervals and
471 // SMILInstanceTimes respectively.
472 void FilterIntervals();
473 void FilterInstanceTimes(InstanceTimeList
& aList
);
476 * Calculates the next acceptable interval for this element after the
477 * specified interval, or, if no previous interval is specified, it will be
478 * the first interval with an end time after t=0.
480 * @see SMILANIM 3.6.8
482 * @param aPrevInterval The previous interval used. If supplied, the first
483 * interval that begins after aPrevInterval will be
484 * returned. May be nullptr.
485 * @param aReplacedInterval The interval that is being updated (if any). This
486 * used to ensure we don't return interval endpoints
487 * that are dependent on themselves. May be nullptr.
488 * @param aFixedBeginTime The time to use for the start of the interval. This
489 * is used when only the endpoint of the interval
490 * should be updated such as when the animation is in
491 * the ACTIVE state. May be nullptr.
492 * @param[out] aResult The next interval. Will be unchanged if no suitable
493 * interval was found (in which case false will be
495 * @return true if a suitable interval was found, false otherwise.
497 bool GetNextInterval(const SMILInterval
* aPrevInterval
,
498 const SMILInterval
* aReplacedInterval
,
499 const SMILInstanceTime
* aFixedBeginTime
,
500 SMILInterval
& aResult
) const;
501 SMILInstanceTime
* GetNextGreater(const InstanceTimeList
& aList
,
502 const SMILTimeValue
& aBase
,
503 int32_t& aPosition
) const;
504 SMILInstanceTime
* GetNextGreaterOrEqual(const InstanceTimeList
& aList
,
505 const SMILTimeValue
& aBase
,
506 int32_t& aPosition
) const;
507 SMILTimeValue
CalcActiveEnd(const SMILTimeValue
& aBegin
,
508 const SMILTimeValue
& aEnd
) const;
509 SMILTimeValue
GetRepeatDuration() const;
510 SMILTimeValue
ApplyMinAndMax(const SMILTimeValue
& aDuration
) const;
511 SMILTime
ActiveTimeToSimpleTime(SMILTime aActiveTime
,
512 uint32_t& aRepeatIteration
);
513 SMILInstanceTime
* CheckForEarlyEnd(const SMILTimeValue
& aContainerTime
) const;
514 void UpdateCurrentInterval(bool aForceChangeNotice
= false);
515 void SampleSimpleTime(SMILTime aActiveTime
);
516 void SampleFillValue();
517 nsresult
AddInstanceTimeFromCurrentTime(SMILTime aCurrentTime
,
518 double aOffsetSeconds
, bool aIsBegin
);
519 void RegisterMilestone();
520 bool GetNextMilestone(SMILMilestone
& aNextMilestone
) const;
522 // Notification methods. Note that these notifications can result in nested
523 // calls to this same object. Therefore,
524 // (i) we should not perform notification until this object is in
525 // a consistent state to receive callbacks, and
526 // (ii) after calling these methods we must assume that the state of the
527 // element may have changed.
528 void NotifyNewInterval();
529 void NotifyChangedInterval(SMILInterval
* aInterval
, bool aBeginObjectChanged
,
530 bool aEndObjectChanged
);
532 void FireTimeEventAsync(EventMessage aMsg
, int32_t aDetail
);
533 const SMILInstanceTime
* GetEffectiveBeginInstance() const;
534 const SMILInterval
* GetPreviousInterval() const;
535 bool HasPlayed() const { return !mOldIntervals
.IsEmpty(); }
536 bool HasClientInFillRange() const;
537 bool EndHasEventConditions() const;
538 bool AreEndTimesDependentOn(const SMILInstanceTime
* aBase
) const;
540 // Reset the current interval by first passing ownership to a temporary
541 // variable so that if Unlink() results in us receiving a callback,
542 // mCurrentInterval will be nullptr and we will be in a consistent state.
543 void ResetCurrentInterval() {
544 if (mCurrentInterval
) {
545 // Transfer ownership to temp var. (This sets mCurrentInterval to null.)
546 auto interval
= std::move(mCurrentInterval
);
554 mozilla::dom::SVGAnimationElement
* mAnimationElement
; // [weak] won't outlive
556 TimeValueSpecList mBeginSpecs
; // [strong]
557 TimeValueSpecList mEndSpecs
; // [strong]
559 SMILTimeValue mSimpleDur
;
561 SMILRepeatCount mRepeatCount
;
562 SMILTimeValue mRepeatDur
;
567 enum SMILFillMode
: uint8_t { FILL_REMOVE
, FILL_FREEZE
};
568 SMILFillMode mFillMode
;
569 static const nsAttrValue::EnumTable sFillModeTable
[];
571 enum SMILRestartMode
: uint8_t {
573 RESTART_WHENNOTACTIVE
,
576 SMILRestartMode mRestartMode
;
577 static const nsAttrValue::EnumTable sRestartModeTable
[];
579 InstanceTimeList mBeginInstances
;
580 InstanceTimeList mEndInstances
;
581 uint32_t mInstanceSerialIndex
;
583 SMILAnimationFunction
* mClient
;
584 UniquePtr
<SMILInterval
> mCurrentInterval
;
585 IntervalList mOldIntervals
;
586 uint32_t mCurrentRepeatIteration
;
587 SMILMilestone mPrevRegisteredMilestone
;
588 static const SMILMilestone sMaxMilestone
;
589 static const uint8_t sMaxNumIntervals
;
590 static const uint8_t sMaxNumInstanceTimes
;
592 // Set of dependent time value specs to be notified when establishing a new
593 // current interval. Change notifications and delete notifications are handled
596 // [weak] The SMILTimeValueSpec objects register themselves and unregister
597 // on destruction. Likewise, we notify them when we are destroyed.
598 TimeValueSpecHashSet mTimeDependents
;
601 * The state of the element in its life-cycle. These states are based on the
602 * element life-cycle described in SMILANIM 3.6.8
604 enum SMILElementState
{
610 SMILElementState mElementState
;
614 SEEK_FORWARD_FROM_ACTIVE
,
615 SEEK_FORWARD_FROM_INACTIVE
,
616 SEEK_BACKWARD_FROM_ACTIVE
,
617 SEEK_BACKWARD_FROM_INACTIVE
619 SMILSeekState mSeekState
;
621 // Used to batch updates to the timing model
622 class AutoIntervalUpdateBatcher
;
623 bool mDeferIntervalUpdates
;
624 bool mDoDeferredUpdate
; // Set if an update to the current interval was
625 // requested while mDeferIntervalUpdates was set
628 // Stack-based helper class to call UpdateCurrentInterval when it is destroyed
629 class AutoIntervalUpdater
;
631 // Recursion depth checking
632 uint8_t mDeleteCount
;
633 uint8_t mUpdateIntervalRecursionDepth
;
634 static const uint8_t sMaxUpdateIntervalRecursionDepth
;
637 inline void ImplCycleCollectionUnlink(SMILTimedElement
& aField
) {
641 inline void ImplCycleCollectionTraverse(
642 nsCycleCollectionTraversalCallback
& aCallback
, SMILTimedElement
& aField
,
643 const char* aName
, uint32_t aFlags
= 0) {
644 aField
.Traverse(&aCallback
);
647 } // namespace mozilla
649 #endif // DOM_SMIL_SMILTIMEDELEMENT_H_