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