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 #ifndef NS_SMILANIMATIONCONTROLLER_H_ 8 #define NS_SMILANIMATIONCONTROLLER_H_ 9 10 #include "mozilla/Attributes.h" 11 #include "nsAutoPtr.h" 12 #include "nsCOMPtr.h" 13 #include "nsTArray.h" 14 #include "nsITimer.h" 15 #include "nsTHashtable.h" 16 #include "nsHashKeys.h" 17 #include "nsSMILTimeContainer.h" 18 #include "nsSMILCompositorTable.h" 19 #include "nsSMILMilestone.h" 20 #include "nsRefreshDriver.h" 21 22 struct nsSMILTargetIdentifier; 23 class nsIDocument; 24 25 namespace mozilla { 26 class RestyleTracker; 27 namespace dom { 28 class Element; 29 class SVGAnimationElement; 30 } // namespace dom 31 } // namespace mozilla 32 33 //---------------------------------------------------------------------- 34 // nsSMILAnimationController 35 // 36 // The animation controller maintains the animation timer and determines the 37 // sample times and sample rate for all SMIL animations in a document. There is 38 // at most one animation controller per nsDocument so that frame-rate tuning can 39 // be performed at a document-level. 40 // 41 // The animation controller can contain many child time containers (timed 42 // document root objects) which may correspond to SVG document fragments within 43 // a compound document. These time containers can be paused individually or 44 // here, at the document level. 45 // 46 class nsSMILAnimationController final : public nsSMILTimeContainer, 47 public nsARefreshObserver { 48 public: 49 explicit nsSMILAnimationController(nsIDocument* aDoc); 50 51 // Clears mDocument pointer. (Called by our nsIDocument when it's going away) 52 void Disconnect(); 53 54 // nsSMILContainer 55 virtual void Pause(uint32_t aType) override; 56 virtual void Resume(uint32_t aType) override; 57 virtual nsSMILTime GetParentTime() const override; 58 59 // nsARefreshObserver 60 NS_IMETHOD_(MozExternalRefCountType) AddRef() override; 61 NS_IMETHOD_(MozExternalRefCountType) Release() override; 62 63 virtual void WillRefresh(mozilla::TimeStamp aTime) override; 64 65 // Methods for registering and enumerating animation elements 66 void RegisterAnimationElement( 67 mozilla::dom::SVGAnimationElement* aAnimationElement); 68 void UnregisterAnimationElement( 69 mozilla::dom::SVGAnimationElement* aAnimationElement); 70 71 // Methods for resampling all animations 72 // (A resample performs the same operations as a sample but doesn't advance 73 // the current time and doesn't check if the container is paused) 74 // This will flush pending style changes for the document. Resample()75 void Resample() { DoSample(false); } 76 SetResampleNeeded()77 void SetResampleNeeded() { 78 if (!mRunningSample && !mResampleNeeded) { 79 FlagDocumentNeedsFlush(); 80 mResampleNeeded = true; 81 } 82 } 83 84 // This will flush pending style changes for the document. FlushResampleRequests()85 void FlushResampleRequests() { 86 if (!mResampleNeeded) return; 87 88 Resample(); 89 } 90 91 // Methods for handling page transitions 92 void OnPageShow(); 93 void OnPageHide(); 94 95 // Methods for supporting cycle-collection 96 void Traverse(nsCycleCollectionTraversalCallback* aCallback); 97 void Unlink(); 98 99 // Methods for relaying the availability of the refresh driver 100 void NotifyRefreshDriverCreated(nsRefreshDriver* aRefreshDriver); 101 void NotifyRefreshDriverDestroying(nsRefreshDriver* aRefreshDriver); 102 103 // Helper to check if we have any animation elements at all HasRegisteredAnimations()104 bool HasRegisteredAnimations() const { 105 return mAnimationElementTable.Count() != 0; 106 } 107 108 void AddStyleUpdatesTo(mozilla::RestyleTracker& aTracker); MightHavePendingStyleUpdates()109 bool MightHavePendingStyleUpdates() const { 110 return mMightHavePendingStyleUpdates; 111 } 112 113 bool PreTraverse(); 114 bool PreTraverseInSubtree(mozilla::dom::Element* aRoot); 115 116 protected: 117 ~nsSMILAnimationController(); 118 119 // Typedefs 120 typedef nsPtrHashKey<nsSMILTimeContainer> TimeContainerPtrKey; 121 typedef nsTHashtable<TimeContainerPtrKey> TimeContainerHashtable; 122 typedef nsPtrHashKey<mozilla::dom::SVGAnimationElement> 123 AnimationElementPtrKey; 124 typedef nsTHashtable<AnimationElementPtrKey> AnimationElementHashtable; 125 126 // Returns mDocument's refresh driver, if it's got one. 127 nsRefreshDriver* GetRefreshDriver(); 128 129 // Methods for controlling whether we're sampling 130 void StartSampling(nsRefreshDriver* aRefreshDriver); 131 void StopSampling(nsRefreshDriver* aRefreshDriver); 132 133 // Wrapper for StartSampling that defers if no animations are registered. 134 void MaybeStartSampling(nsRefreshDriver* aRefreshDriver); 135 136 // Sample-related callbacks and implementation helpers 137 virtual void DoSample() override; 138 void DoSample(bool aSkipUnchangedContainers); 139 140 void RewindElements(); 141 142 void DoMilestoneSamples(); 143 144 static void SampleTimedElement(mozilla::dom::SVGAnimationElement* aElement, 145 TimeContainerHashtable* aActiveContainers); 146 147 static void AddAnimationToCompositorTable( 148 mozilla::dom::SVGAnimationElement* aElement, 149 nsSMILCompositorTable* aCompositorTable, bool& aStyleFlushNeeded); 150 151 static bool GetTargetIdentifierForAnimation( 152 mozilla::dom::SVGAnimationElement* aAnimElem, 153 nsSMILTargetIdentifier& aResult); 154 155 // Methods for adding/removing time containers 156 virtual nsresult AddChild(nsSMILTimeContainer& aChild) override; 157 virtual void RemoveChild(nsSMILTimeContainer& aChild) override; 158 159 void FlagDocumentNeedsFlush(); 160 161 // Members 162 nsAutoRefCnt mRefCnt; 163 NS_DECL_OWNINGTHREAD 164 165 AnimationElementHashtable mAnimationElementTable; 166 TimeContainerHashtable mChildContainerTable; 167 mozilla::TimeStamp mCurrentSampleTime; 168 mozilla::TimeStamp mStartTime; 169 170 // Average time between samples from the refresh driver. This is used to 171 // detect large unexpected gaps between samples such as can occur when the 172 // computer sleeps. The nature of the SMIL model means that catching up these 173 // large gaps can be expensive as, for example, many events may need to be 174 // dispatched for the intervening time when no samples were received. 175 // 176 // In such cases, we ignore the intervening gap and continue sampling from 177 // when we were expecting the next sample to arrive. 178 // 179 // Note that we only do this for SMIL and not CSS transitions (which doesn't 180 // have so much work to do to catch up) nor scripted animations (which expect 181 // animation time to follow real time). 182 // 183 // This behaviour does not affect pausing (since we're not *expecting* any 184 // samples then) nor seeking (where the SMIL model behaves somewhat 185 // differently such as not dispatching events). 186 nsSMILTime mAvgTimeBetweenSamples; 187 188 bool mResampleNeeded; 189 // If we're told to start sampling but there are no animation elements we just 190 // record the time, set the following flag, and then wait until we have an 191 // animation element. Then we'll reset this flag and actually start sampling. 192 bool mDeferredStartSampling; 193 bool mRunningSample; 194 195 // Are we registered with our document's refresh driver? 196 bool mRegisteredWithRefreshDriver; 197 198 // Have we updated animated values without adding them to the restyle tracker? 199 bool mMightHavePendingStyleUpdates; 200 201 // Store raw ptr to mDocument. It owns the controller, so controller 202 // shouldn't outlive it 203 nsIDocument* mDocument; 204 205 // Contains compositors used in our last sample. We keep this around 206 // so we can detect when an element/attribute used to be animated, 207 // but isn't anymore for some reason. (e.g. if its <animate> element is 208 // removed or retargeted) 209 nsAutoPtr<nsSMILCompositorTable> mLastCompositorTable; 210 }; 211 212 #endif // NS_SMILANIMATIONCONTROLLER_H_ 213