diff options
Diffstat (limited to 'dom/smil/nsSMILTimedElement.h')
-rw-r--r-- | dom/smil/nsSMILTimedElement.h | 673 |
1 files changed, 673 insertions, 0 deletions
diff --git a/dom/smil/nsSMILTimedElement.h b/dom/smil/nsSMILTimedElement.h new file mode 100644 index 000000000..1831deeb0 --- /dev/null +++ b/dom/smil/nsSMILTimedElement.h @@ -0,0 +1,673 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef NS_SMILTIMEDELEMENT_H_ +#define NS_SMILTIMEDELEMENT_H_ + +#include "mozilla/EventForwards.h" +#include "mozilla/Move.h" +#include "nsSMILInterval.h" +#include "nsSMILInstanceTime.h" +#include "nsSMILMilestone.h" +#include "nsSMILTimeValueSpec.h" +#include "nsSMILRepeatCount.h" +#include "nsSMILTypes.h" +#include "nsTArray.h" +#include "nsTHashtable.h" +#include "nsHashKeys.h" +#include "nsAutoPtr.h" +#include "nsAttrValue.h" + +class nsSMILAnimationFunction; +class nsSMILTimeContainer; +class nsSMILTimeValue; +class nsIAtom; + +namespace mozilla { +namespace dom { +class SVGAnimationElement; +} // namespace dom +} // namespace mozilla + +//---------------------------------------------------------------------- +// nsSMILTimedElement + +class nsSMILTimedElement +{ +public: + nsSMILTimedElement(); + ~nsSMILTimedElement(); + + typedef mozilla::dom::Element Element; + + /* + * Sets the owning animation element which this class uses to convert between + * container times and to register timebase elements. + */ + void SetAnimationElement(mozilla::dom::SVGAnimationElement* aElement); + + /* + * Returns the time container with which this timed element is associated or + * nullptr if it is not associated with a time container. + */ + nsSMILTimeContainer* GetTimeContainer(); + + /* + * Returns the element targeted by the animation element. Needed for + * registering event listeners against the appropriate element. + */ + mozilla::dom::Element* GetTargetElement(); + + /** + * Methods for supporting the nsIDOMElementTimeControl interface. + */ + + /* + * Adds a new begin instance time at the current container time plus or minus + * the specified offset. + * + * @param aOffsetSeconds A real number specifying the number of seconds to add + * to the current container time. + * @return NS_OK if the operation succeeeded, or an error code otherwise. + */ + nsresult BeginElementAt(double aOffsetSeconds); + + /* + * Adds a new end instance time at the current container time plus or minus + * the specified offset. + * + * @param aOffsetSeconds A real number specifying the number of seconds to add + * to the current container time. + * @return NS_OK if the operation succeeeded, or an error code otherwise. + */ + nsresult EndElementAt(double aOffsetSeconds); + + /** + * Methods for supporting the nsSVGAnimationElement interface. + */ + + /** + * According to SVG 1.1 SE this returns + * + * the begin time, in seconds, for this animation element's current + * interval, if it exists, regardless of whether the interval has begun yet. + * + * @return the start time as defined above in milliseconds or an unresolved + * time if there is no current interval. + */ + nsSMILTimeValue GetStartTime() const; + + /** + * Returns the simple duration of this element. + * + * @return the simple duration in milliseconds or INDEFINITE. + */ + nsSMILTimeValue GetSimpleDuration() const + { + return mSimpleDur; + } + + /** + * Methods for supporting hyperlinking + */ + + /** + * Internal SMIL methods + */ + + /** + * Returns the time to seek the document to when this element is targetted by + * a hyperlink. + * + * The behavior is defined here: + * http://www.w3.org/TR/smil-animation/#HyperlinkSemantics + * + * It is very similar to GetStartTime() with the exception that when the + * element is not active, the begin time of the *first* interval is returned. + * + * @return the time to seek the documen to in milliseconds or an unresolved + * time if there is no resolved interval. + */ + nsSMILTimeValue GetHyperlinkTime() const; + + /** + * Adds an instance time object this element's list of instance times. + * These instance times are used when creating intervals. + * + * This method is typically called by an nsSMILTimeValueSpec. + * + * @param aInstanceTime The time to add, expressed in container time. + * @param aIsBegin true if the time to be added represents a begin + * time or false if it represents an end time. + */ + void AddInstanceTime(nsSMILInstanceTime* aInstanceTime, bool aIsBegin); + + /** + * Requests this element update the given instance time. + * + * This method is typically called by a child nsSMILTimeValueSpec. + * + * @param aInstanceTime The instance time to update. + * @param aUpdatedTime The time to update aInstanceTime with. + * @param aDependentTime The instance time upon which aInstanceTime should be + * based. + * @param aIsBegin true if the time to be updated represents a begin + * instance time or false if it represents an end + * instance time. + */ + void UpdateInstanceTime(nsSMILInstanceTime* aInstanceTime, + nsSMILTimeValue& aUpdatedTime, + bool aIsBegin); + + /** + * Removes an instance time object from this element's list of instance times. + * + * This method is typically called by a child nsSMILTimeValueSpec. + * + * @param aInstanceTime The instance time to remove. + * @param aIsBegin true if the time to be removed represents a begin + * time or false if it represents an end time. + */ + void RemoveInstanceTime(nsSMILInstanceTime* aInstanceTime, bool aIsBegin); + + /** + * Removes all the instance times associated with the given + * nsSMILTimeValueSpec object. Used when an ID assignment changes and hence + * all the previously associated instance times become invalid. + * + * @param aSpec The nsSMILTimeValueSpec object whose created + * nsSMILInstanceTime's should be removed. + * @param aIsBegin true if the times to be removed represent begin + * times or false if they are end times. + */ + void RemoveInstanceTimesForCreator(const nsSMILTimeValueSpec* aSpec, + bool aIsBegin); + + /** + * Sets the object that will be called by this timed element each time it is + * sampled. + * + * In Schmitz's model it is possible to associate several time clients with + * a timed element but for now we only allow one. + * + * @param aClient The time client to associate. Any previous time client + * will be disassociated and no longer sampled. Setting this + * to nullptr will simply disassociate the previous client, if + * any. + */ + void SetTimeClient(nsSMILAnimationFunction* aClient); + + /** + * Samples the object at the given container time. Timing intervals are + * updated and if this element is active at the given time the associated time + * client will be sampled with the appropriate simple time. + * + * @param aContainerTime The container time at which to sample. + */ + void SampleAt(nsSMILTime aContainerTime); + + /** + * Performs a special sample for the end of an interval. Such a sample should + * only advance the timed element (and any dependent elements) to the waiting + * or postactive state. It should not cause a transition to the active state. + * Transition to the active state is only performed on a regular SampleAt. + * + * This allows all interval ends at a given time to be processed first and + * hence the new interval can be established based on full information of the + * available instance times. + * + * @param aContainerTime The container time at which to sample. + */ + void SampleEndAt(nsSMILTime aContainerTime); + + /** + * Informs the timed element that its time container has changed time + * relative to document time. The timed element therefore needs to update its + * dependent elements (which may belong to a different time container) so they + * can re-resolve their times. + */ + void HandleContainerTimeChange(); + + /** + * Resets this timed element's accumulated times and intervals back to start + * up state. + * + * This is used for backwards seeking where rather than accumulating + * historical timing state and winding it back, we reset the element and seek + * forwards. + */ + void Rewind(); + + /** + * Marks this element as disabled or not. If the element is disabled, it + * will ignore any future samples and discard any accumulated timing state. + * + * This is used by SVG to "turn off" timed elements when the associated + * animation element has failing conditional processing tests. + * + * Returns true if the disabled state of the timed element was changed + * as a result of this call (i.e. it was not a redundant call). + */ + bool SetIsDisabled(bool aIsDisabled); + + /** + * Attempts to set an attribute on this timed element. + * + * @param aAttribute The name of the attribute to set. The namespace of this + * attribute is not specified as it is checked by the host + * element. Only attributes in the namespace defined for + * SMIL attributes in the host language are passed to the + * timed element. + * @param aValue The attribute value. + * @param aResult The nsAttrValue object that may be used for storing the + * parsed result. + * @param aContextNode The element to use for context when resolving + * references to other elements. + * @param[out] aParseResult The result of parsing the attribute. Will be set + * to NS_OK if parsing is successful. + * + * @return true if the given attribute is a timing attribute, false + * otherwise. + */ + bool SetAttr(nsIAtom* aAttribute, const nsAString& aValue, + nsAttrValue& aResult, Element* aContextNode, + nsresult* aParseResult = nullptr); + + /** + * Attempts to unset an attribute on this timed element. + * + * @param aAttribute The name of the attribute to set. As with SetAttr the + * namespace of the attribute is not specified (see + * SetAttr). + * + * @return true if the given attribute is a timing attribute, false + * otherwise. + */ + bool UnsetAttr(nsIAtom* aAttribute); + + /** + * Adds a syncbase dependency to the list of dependents that will be notified + * when this timed element creates, deletes, or updates its current interval. + * + * @param aDependent The nsSMILTimeValueSpec object to notify. A raw pointer + * to this object will be stored. Therefore it is necessary + * for the object to be explicitly unregistered (with + * RemoveDependent) when it is destroyed. + */ + void AddDependent(nsSMILTimeValueSpec& aDependent); + + /** + * Removes a syncbase dependency from the list of dependents that are notified + * when the current interval is modified. + * + * @param aDependent The nsSMILTimeValueSpec object to unregister. + */ + void RemoveDependent(nsSMILTimeValueSpec& aDependent); + + /** + * Determines if this timed element is dependent on the given timed element's + * begin time for the interval currently in effect. Whilst the element is in + * the active state this is the current interval and in the postactive or + * waiting state this is the previous interval if one exists. In all other + * cases the element is not considered a time dependent of any other element. + * + * @param aOther The potential syncbase element. + * @return true if this timed element's begin time for the currently + * effective interval is directly or indirectly derived from aOther, false + * otherwise. + */ + bool IsTimeDependent(const nsSMILTimedElement& aOther) const; + + /** + * Called when the timed element has been bound to the document so that + * references from this timed element to other elements can be resolved. + * + * @param aContextNode The node which provides the necessary context for + * resolving references. This is typically the element in + * the host language that owns this timed element. Should + * not be null. + */ + void BindToTree(nsIContent* aContextNode); + + /** + * Called when the target of the animation has changed so that event + * registrations can be updated. + */ + void HandleTargetElementChange(mozilla::dom::Element* aNewTarget); + + /** + * Called when the timed element has been removed from a document so that + * references to other elements can be broken. + */ + void DissolveReferences() { Unlink(); } + + // Cycle collection + void Traverse(nsCycleCollectionTraversalCallback* aCallback); + void Unlink(); + + typedef bool (*RemovalTestFunction)(nsSMILInstanceTime* aInstance); + +protected: + // Typedefs + typedef nsTArray<nsAutoPtr<nsSMILTimeValueSpec> > TimeValueSpecList; + typedef nsTArray<RefPtr<nsSMILInstanceTime> > InstanceTimeList; + typedef nsTArray<nsAutoPtr<nsSMILInterval> > IntervalList; + typedef nsPtrHashKey<nsSMILTimeValueSpec> TimeValueSpecPtrKey; + typedef nsTHashtable<TimeValueSpecPtrKey> TimeValueSpecHashSet; + + // Helper classes + class InstanceTimeComparator { + public: + bool Equals(const nsSMILInstanceTime* aElem1, + const nsSMILInstanceTime* aElem2) const; + bool LessThan(const nsSMILInstanceTime* aElem1, + const nsSMILInstanceTime* aElem2) const; + }; + + // Templated helper functions + template <class TestFunctor> + void RemoveInstanceTimes(InstanceTimeList& aArray, TestFunctor& aTest); + + // + // Implementation helpers + // + + nsresult SetBeginSpec(const nsAString& aBeginSpec, + Element* aContextNode, + RemovalTestFunction aRemove); + nsresult SetEndSpec(const nsAString& aEndSpec, + Element* aContextNode, + RemovalTestFunction aRemove); + nsresult SetSimpleDuration(const nsAString& aDurSpec); + nsresult SetMin(const nsAString& aMinSpec); + nsresult SetMax(const nsAString& aMaxSpec); + nsresult SetRestart(const nsAString& aRestartSpec); + nsresult SetRepeatCount(const nsAString& aRepeatCountSpec); + nsresult SetRepeatDur(const nsAString& aRepeatDurSpec); + nsresult SetFillMode(const nsAString& aFillModeSpec); + + void UnsetBeginSpec(RemovalTestFunction aRemove); + void UnsetEndSpec(RemovalTestFunction aRemove); + void UnsetSimpleDuration(); + void UnsetMin(); + void UnsetMax(); + void UnsetRestart(); + void UnsetRepeatCount(); + void UnsetRepeatDur(); + void UnsetFillMode(); + + nsresult SetBeginOrEndSpec(const nsAString& aSpec, + Element* aContextNode, + bool aIsBegin, + RemovalTestFunction aRemove); + void ClearSpecs(TimeValueSpecList& aSpecs, + InstanceTimeList& aInstances, + RemovalTestFunction aRemove); + void ClearIntervals(); + void DoSampleAt(nsSMILTime aContainerTime, bool aEndOnly); + + /** + * Helper function to check for an early end and, if necessary, update the + * current interval accordingly. + * + * See SMIL 3.0, section 5.4.5, Element life cycle, "Active Time - Playing an + * interval" for a description of ending early. + * + * @param aSampleTime The current sample time. Early ends should only be + * applied at the last possible moment (i.e. if they are at + * or before the current sample time) and only if the + * current interval is not already ending. + * @return true if the end time of the current interval was updated, + * false otherwise. + */ + bool ApplyEarlyEnd(const nsSMILTimeValue& aSampleTime); + + /** + * Clears certain state in response to the element restarting. + * + * This state is described in SMIL 3.0, section 5.4.3, Resetting element state + */ + void Reset(); + + /** + * Clears all accumulated timing state except for those instance times for + * which aRemove does not return true. + * + * Unlike the Reset method which only clears instance times, this clears the + * element's state, intervals (including current interval), and tells the + * client animation function to stop applying a result. In effect, it returns + * the element to its initial state but preserves any instance times excluded + * by the passed-in function. + */ + void ClearTimingState(RemovalTestFunction aRemove); + + /** + * Recreates timing state by re-applying begin/end attributes specified on + * the associated animation element. + * + * Note that this does not completely restore the information cleared by + * ClearTimingState since it leaves the element in the startup state. + * The element state will be updated on the next sample. + */ + void RebuildTimingState(RemovalTestFunction aRemove); + + /** + * Completes a seek operation by sending appropriate events and, in the case + * of a backwards seek, updating the state of timing information that was + * previously considered historical. + */ + void DoPostSeek(); + + /** + * Unmarks instance times that were previously preserved because they were + * considered important historical milestones but are no longer such because + * a backwards seek has been performed. + */ + void UnpreserveInstanceTimes(InstanceTimeList& aList); + + /** + * Helper function to iterate through this element's accumulated timing + * information (specifically old nsSMILIntervals and nsSMILTimeInstanceTimes) + * and discard items that are no longer needed or exceed some threshold of + * accumulated state. + */ + void FilterHistory(); + + // Helper functions for FilterHistory to clear old nsSMILIntervals and + // nsSMILInstanceTimes respectively. + void FilterIntervals(); + void FilterInstanceTimes(InstanceTimeList& aList); + + /** + * Calculates the next acceptable interval for this element after the + * specified interval, or, if no previous interval is specified, it will be + * the first interval with an end time after t=0. + * + * @see SMILANIM 3.6.8 + * + * @param aPrevInterval The previous interval used. If supplied, the first + * interval that begins after aPrevInterval will be + * returned. May be nullptr. + * @param aReplacedInterval The interval that is being updated (if any). This + * used to ensure we don't return interval endpoints + * that are dependent on themselves. May be nullptr. + * @param aFixedBeginTime The time to use for the start of the interval. This + * is used when only the endpoint of the interval + * should be updated such as when the animation is in + * the ACTIVE state. May be nullptr. + * @param[out] aResult The next interval. Will be unchanged if no suitable + * interval was found (in which case false will be + * returned). + * @return true if a suitable interval was found, false otherwise. + */ + bool GetNextInterval(const nsSMILInterval* aPrevInterval, + const nsSMILInterval* aReplacedInterval, + const nsSMILInstanceTime* aFixedBeginTime, + nsSMILInterval& aResult) const; + nsSMILInstanceTime* GetNextGreater(const InstanceTimeList& aList, + const nsSMILTimeValue& aBase, + int32_t& aPosition) const; + nsSMILInstanceTime* GetNextGreaterOrEqual(const InstanceTimeList& aList, + const nsSMILTimeValue& aBase, + int32_t& aPosition) const; + nsSMILTimeValue CalcActiveEnd(const nsSMILTimeValue& aBegin, + const nsSMILTimeValue& aEnd) const; + nsSMILTimeValue GetRepeatDuration() const; + nsSMILTimeValue ApplyMinAndMax(const nsSMILTimeValue& aDuration) const; + nsSMILTime ActiveTimeToSimpleTime(nsSMILTime aActiveTime, + uint32_t& aRepeatIteration); + nsSMILInstanceTime* CheckForEarlyEnd( + const nsSMILTimeValue& aContainerTime) const; + void UpdateCurrentInterval(bool aForceChangeNotice = false); + void SampleSimpleTime(nsSMILTime aActiveTime); + void SampleFillValue(); + nsresult AddInstanceTimeFromCurrentTime(nsSMILTime aCurrentTime, + double aOffsetSeconds, bool aIsBegin); + void RegisterMilestone(); + bool GetNextMilestone(nsSMILMilestone& aNextMilestone) const; + + // Notification methods. Note that these notifications can result in nested + // calls to this same object. Therefore, + // (i) we should not perform notification until this object is in + // a consistent state to receive callbacks, and + // (ii) after calling these methods we must assume that the state of the + // element may have changed. + void NotifyNewInterval(); + void NotifyChangedInterval(nsSMILInterval* aInterval, + bool aBeginObjectChanged, + bool aEndObjectChanged); + + void FireTimeEventAsync(mozilla::EventMessage aMsg, + int32_t aDetail); + const nsSMILInstanceTime* GetEffectiveBeginInstance() const; + const nsSMILInterval* GetPreviousInterval() const; + bool HasPlayed() const { return !mOldIntervals.IsEmpty(); } + bool HasClientInFillRange() const; + bool EndHasEventConditions() const; + bool AreEndTimesDependentOn( + const nsSMILInstanceTime* aBase) const; + + // Reset the current interval by first passing ownership to a temporary + // variable so that if Unlink() results in us receiving a callback, + // mCurrentInterval will be nullptr and we will be in a consistent state. + void ResetCurrentInterval() + { + if (mCurrentInterval) { + // Transfer ownership to temp var. (This sets mCurrentInterval to null.) + nsAutoPtr<nsSMILInterval> interval(mozilla::Move(mCurrentInterval)); + interval->Unlink(); + } + } + + // + // Members + // + mozilla::dom::SVGAnimationElement* mAnimationElement; // [weak] won't outlive + // owner + TimeValueSpecList mBeginSpecs; // [strong] + TimeValueSpecList mEndSpecs; // [strong] + + nsSMILTimeValue mSimpleDur; + + nsSMILRepeatCount mRepeatCount; + nsSMILTimeValue mRepeatDur; + + nsSMILTimeValue mMin; + nsSMILTimeValue mMax; + + enum nsSMILFillMode : uint8_t + { + FILL_REMOVE, + FILL_FREEZE + }; + nsSMILFillMode mFillMode; + static nsAttrValue::EnumTable sFillModeTable[]; + + enum nsSMILRestartMode : uint8_t + { + RESTART_ALWAYS, + RESTART_WHENNOTACTIVE, + RESTART_NEVER + }; + nsSMILRestartMode mRestartMode; + static nsAttrValue::EnumTable sRestartModeTable[]; + + InstanceTimeList mBeginInstances; + InstanceTimeList mEndInstances; + uint32_t mInstanceSerialIndex; + + nsSMILAnimationFunction* mClient; + nsAutoPtr<nsSMILInterval> mCurrentInterval; + IntervalList mOldIntervals; + uint32_t mCurrentRepeatIteration; + nsSMILMilestone mPrevRegisteredMilestone; + static const nsSMILMilestone sMaxMilestone; + static const uint8_t sMaxNumIntervals; + static const uint8_t sMaxNumInstanceTimes; + + // Set of dependent time value specs to be notified when establishing a new + // current interval. Change notifications and delete notifications are handled + // by the interval. + // + // [weak] The nsSMILTimeValueSpec objects register themselves and unregister + // on destruction. Likewise, we notify them when we are destroyed. + TimeValueSpecHashSet mTimeDependents; + + /** + * The state of the element in its life-cycle. These states are based on the + * element life-cycle described in SMILANIM 3.6.8 + */ + enum nsSMILElementState + { + STATE_STARTUP, + STATE_WAITING, + STATE_ACTIVE, + STATE_POSTACTIVE + }; + nsSMILElementState mElementState; + + enum nsSMILSeekState + { + SEEK_NOT_SEEKING, + SEEK_FORWARD_FROM_ACTIVE, + SEEK_FORWARD_FROM_INACTIVE, + SEEK_BACKWARD_FROM_ACTIVE, + SEEK_BACKWARD_FROM_INACTIVE + }; + nsSMILSeekState mSeekState; + + // Used to batch updates to the timing model + class AutoIntervalUpdateBatcher; + bool mDeferIntervalUpdates; + bool mDoDeferredUpdate; // Set if an update to the current interval was + // requested while mDeferIntervalUpdates was set + bool mIsDisabled; + + // Stack-based helper class to call UpdateCurrentInterval when it is destroyed + class AutoIntervalUpdater; + + // Recursion depth checking + uint8_t mDeleteCount; + uint8_t mUpdateIntervalRecursionDepth; + static const uint8_t sMaxUpdateIntervalRecursionDepth; +}; + +inline void +ImplCycleCollectionUnlink(nsSMILTimedElement& aField) +{ + aField.Unlink(); +} + +inline void +ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback, + nsSMILTimedElement& aField, + const char* aName, + uint32_t aFlags = 0) +{ + aField.Traverse(&aCallback); +} + +#endif // NS_SMILTIMEDELEMENT_H_ |