1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/base/nsRefreshDriver.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,344 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/* 1.11 + * Code to notify things that animate before a refresh, at an appropriate 1.12 + * refresh rate. (Perhaps temporary, until replaced by compositor.) 1.13 + */ 1.14 + 1.15 +#ifndef nsRefreshDriver_h_ 1.16 +#define nsRefreshDriver_h_ 1.17 + 1.18 +#include "mozilla/TimeStamp.h" 1.19 +#include "mozFlushType.h" 1.20 +#include "nsTObserverArray.h" 1.21 +#include "nsTArray.h" 1.22 +#include "nsTHashtable.h" 1.23 +#include "nsClassHashtable.h" 1.24 +#include "nsHashKeys.h" 1.25 +#include "mozilla/Attributes.h" 1.26 +#include "mozilla/Maybe.h" 1.27 + 1.28 +class nsPresContext; 1.29 +class nsIPresShell; 1.30 +class nsIDocument; 1.31 +class imgIRequest; 1.32 +class nsIRunnable; 1.33 + 1.34 +namespace mozilla { 1.35 +class RefreshDriverTimer; 1.36 +} 1.37 + 1.38 +/** 1.39 + * An abstract base class to be implemented by callers wanting to be 1.40 + * notified at refresh times. When nothing needs to be painted, callers 1.41 + * may not be notified. 1.42 + */ 1.43 +class nsARefreshObserver { 1.44 +public: 1.45 + // AddRef and Release signatures that match nsISupports. Implementors 1.46 + // must implement reference counting, and those that do implement 1.47 + // nsISupports will already have methods with the correct signature. 1.48 + // 1.49 + // The refresh driver does NOT hold references to refresh observers 1.50 + // except while it is notifying them. 1.51 + NS_IMETHOD_(MozExternalRefCountType) AddRef(void) = 0; 1.52 + NS_IMETHOD_(MozExternalRefCountType) Release(void) = 0; 1.53 + 1.54 + virtual void WillRefresh(mozilla::TimeStamp aTime) = 0; 1.55 +}; 1.56 + 1.57 +/** 1.58 + * An abstract base class to be implemented by callers wanting to be notified 1.59 + * that a refresh has occurred. Callers must ensure an observer is removed 1.60 + * before it is destroyed. 1.61 + */ 1.62 +class nsAPostRefreshObserver { 1.63 +public: 1.64 + virtual void DidRefresh() = 0; 1.65 +}; 1.66 + 1.67 +class nsRefreshDriver MOZ_FINAL : public nsISupports { 1.68 +public: 1.69 + nsRefreshDriver(nsPresContext *aPresContext); 1.70 + ~nsRefreshDriver(); 1.71 + 1.72 + static void InitializeStatics(); 1.73 + static void Shutdown(); 1.74 + 1.75 + // nsISupports implementation 1.76 + NS_DECL_ISUPPORTS 1.77 + 1.78 + /** 1.79 + * Methods for testing, exposed via nsIDOMWindowUtils. See 1.80 + * nsIDOMWindowUtils.advanceTimeAndRefresh for description. 1.81 + */ 1.82 + void AdvanceTimeAndRefresh(int64_t aMilliseconds); 1.83 + void RestoreNormalRefresh(); 1.84 + void DoTick(); 1.85 + bool IsTestControllingRefreshesEnabled() const 1.86 + { 1.87 + return mTestControllingRefreshes; 1.88 + } 1.89 + 1.90 + /** 1.91 + * Return the time of the most recent refresh. This is intended to be 1.92 + * used by callers who want to start an animation now and want to know 1.93 + * what time to consider the start of the animation. (This helps 1.94 + * ensure that multiple animations started during the same event off 1.95 + * the main event loop have the same start time.) 1.96 + */ 1.97 + mozilla::TimeStamp MostRecentRefresh() const; 1.98 + /** 1.99 + * Same thing, but in microseconds since the epoch. 1.100 + */ 1.101 + int64_t MostRecentRefreshEpochTime() const; 1.102 + 1.103 + /** 1.104 + * Add / remove refresh observers. Returns whether the operation 1.105 + * succeeded. 1.106 + * 1.107 + * The flush type affects: 1.108 + * + the order in which the observers are notified (lowest flush 1.109 + * type to highest, in order registered) 1.110 + * + (in the future) which observers are suppressed when the display 1.111 + * doesn't require current position data or isn't currently 1.112 + * painting, and, correspondingly, which get notified when there 1.113 + * is a flush during such suppression 1.114 + * and it must be either Flush_Style, Flush_Layout, or Flush_Display. 1.115 + * 1.116 + * The refresh driver does NOT own a reference to these observers; 1.117 + * they must remove themselves before they are destroyed. 1.118 + * 1.119 + * The observer will be called even if there is no other activity. 1.120 + */ 1.121 + bool AddRefreshObserver(nsARefreshObserver *aObserver, 1.122 + mozFlushType aFlushType); 1.123 + bool RemoveRefreshObserver(nsARefreshObserver *aObserver, 1.124 + mozFlushType aFlushType); 1.125 + 1.126 + /** 1.127 + * Add an observer that will be called after each refresh. The caller 1.128 + * must remove the observer before it is deleted. This does not trigger 1.129 + * refresh driver ticks. 1.130 + */ 1.131 + void AddPostRefreshObserver(nsAPostRefreshObserver *aObserver); 1.132 + void RemovePostRefreshObserver(nsAPostRefreshObserver *aObserver); 1.133 + 1.134 + /** 1.135 + * Add/Remove imgIRequest versions of observers. 1.136 + * 1.137 + * These are used for hooking into the refresh driver for 1.138 + * controlling animated images. 1.139 + * 1.140 + * @note The refresh driver owns a reference to these listeners. 1.141 + * 1.142 + * @note Technically, imgIRequest objects are not nsARefreshObservers, but 1.143 + * for controlling animated image repaint events, we subscribe the 1.144 + * imgIRequests to the nsRefreshDriver for notification of paint events. 1.145 + * 1.146 + * @returns whether the operation succeeded, or void in the case of removal. 1.147 + */ 1.148 + bool AddImageRequest(imgIRequest* aRequest); 1.149 + void RemoveImageRequest(imgIRequest* aRequest); 1.150 + 1.151 + /** 1.152 + * Add / remove presshells that we should flush style and layout on 1.153 + */ 1.154 + bool AddStyleFlushObserver(nsIPresShell* aShell) { 1.155 + NS_ASSERTION(!mStyleFlushObservers.Contains(aShell), 1.156 + "Double-adding style flush observer"); 1.157 + bool appended = mStyleFlushObservers.AppendElement(aShell) != nullptr; 1.158 + EnsureTimerStarted(false); 1.159 + return appended; 1.160 + } 1.161 + void RemoveStyleFlushObserver(nsIPresShell* aShell) { 1.162 + mStyleFlushObservers.RemoveElement(aShell); 1.163 + } 1.164 + bool AddLayoutFlushObserver(nsIPresShell* aShell) { 1.165 + NS_ASSERTION(!IsLayoutFlushObserver(aShell), 1.166 + "Double-adding layout flush observer"); 1.167 + bool appended = mLayoutFlushObservers.AppendElement(aShell) != nullptr; 1.168 + EnsureTimerStarted(false); 1.169 + return appended; 1.170 + } 1.171 + void RemoveLayoutFlushObserver(nsIPresShell* aShell) { 1.172 + mLayoutFlushObservers.RemoveElement(aShell); 1.173 + } 1.174 + bool IsLayoutFlushObserver(nsIPresShell* aShell) { 1.175 + return mLayoutFlushObservers.Contains(aShell); 1.176 + } 1.177 + bool AddPresShellToInvalidateIfHidden(nsIPresShell* aShell) { 1.178 + NS_ASSERTION(!mPresShellsToInvalidateIfHidden.Contains(aShell), 1.179 + "Double-adding style flush observer"); 1.180 + bool appended = mPresShellsToInvalidateIfHidden.AppendElement(aShell) != nullptr; 1.181 + EnsureTimerStarted(false); 1.182 + return appended; 1.183 + } 1.184 + void RemovePresShellToInvalidateIfHidden(nsIPresShell* aShell) { 1.185 + mPresShellsToInvalidateIfHidden.RemoveElement(aShell); 1.186 + } 1.187 + 1.188 + /** 1.189 + * Remember whether our presshell's view manager needs a flush 1.190 + */ 1.191 + void ScheduleViewManagerFlush(); 1.192 + void RevokeViewManagerFlush() { 1.193 + mViewManagerFlushIsPending = false; 1.194 + } 1.195 + bool ViewManagerFlushIsPending() { 1.196 + return mViewManagerFlushIsPending; 1.197 + } 1.198 + 1.199 + /** 1.200 + * Add a document for which we have nsIFrameRequestCallbacks 1.201 + */ 1.202 + void ScheduleFrameRequestCallbacks(nsIDocument* aDocument); 1.203 + 1.204 + /** 1.205 + * Remove a document for which we have nsIFrameRequestCallbacks 1.206 + */ 1.207 + void RevokeFrameRequestCallbacks(nsIDocument* aDocument); 1.208 + 1.209 + /** 1.210 + * Tell the refresh driver that it is done driving refreshes and 1.211 + * should stop its timer and forget about its pres context. This may 1.212 + * be called from within a refresh. 1.213 + */ 1.214 + void Disconnect() { 1.215 + StopTimer(); 1.216 + mPresContext = nullptr; 1.217 + } 1.218 + 1.219 + bool IsFrozen() { return mFreezeCount > 0; } 1.220 + 1.221 + /** 1.222 + * Freeze the refresh driver. It should stop delivering future 1.223 + * refreshes until thawed. Note that the number of calls to Freeze() must 1.224 + * match the number of calls to Thaw() in order for the refresh driver to 1.225 + * be un-frozen. 1.226 + */ 1.227 + void Freeze(); 1.228 + 1.229 + /** 1.230 + * Thaw the refresh driver. If the number of calls to Freeze() matches the 1.231 + * number of calls to this function, the refresh driver should start 1.232 + * delivering refreshes again. 1.233 + */ 1.234 + void Thaw(); 1.235 + 1.236 + /** 1.237 + * Throttle or unthrottle the refresh driver. This is done if the 1.238 + * corresponding presshell is hidden or shown. 1.239 + */ 1.240 + void SetThrottled(bool aThrottled); 1.241 + 1.242 + /** 1.243 + * Return the prescontext we were initialized with 1.244 + */ 1.245 + nsPresContext* PresContext() const { return mPresContext; } 1.246 + 1.247 +#ifdef DEBUG 1.248 + /** 1.249 + * Check whether the given observer is an observer for the given flush type 1.250 + */ 1.251 + bool IsRefreshObserver(nsARefreshObserver *aObserver, 1.252 + mozFlushType aFlushType); 1.253 +#endif 1.254 + 1.255 + /** 1.256 + * Default interval the refresh driver uses, in ms. 1.257 + */ 1.258 + static int32_t DefaultInterval(); 1.259 + 1.260 + bool IsInRefresh() { return mInRefresh; } 1.261 + 1.262 +private: 1.263 + typedef nsTObserverArray<nsARefreshObserver*> ObserverArray; 1.264 + typedef nsTHashtable<nsISupportsHashKey> RequestTable; 1.265 + struct ImageStartData { 1.266 + ImageStartData() 1.267 + { 1.268 + } 1.269 + 1.270 + mozilla::Maybe<mozilla::TimeStamp> mStartTime; 1.271 + RequestTable mEntries; 1.272 + }; 1.273 + typedef nsClassHashtable<nsUint32HashKey, ImageStartData> ImageStartTable; 1.274 + 1.275 + void Tick(int64_t aNowEpoch, mozilla::TimeStamp aNowTime); 1.276 + 1.277 + void EnsureTimerStarted(bool aAdjustingTimer); 1.278 + void StopTimer(); 1.279 + 1.280 + uint32_t ObserverCount() const; 1.281 + uint32_t ImageRequestCount() const; 1.282 + static PLDHashOperator ImageRequestEnumerator(nsISupportsHashKey* aEntry, 1.283 + void* aUserArg); 1.284 + static PLDHashOperator StartTableRequestCounter(const uint32_t& aKey, 1.285 + ImageStartData* aEntry, 1.286 + void* aUserArg); 1.287 + static PLDHashOperator StartTableRefresh(const uint32_t& aKey, 1.288 + ImageStartData* aEntry, 1.289 + void* aUserArg); 1.290 + static PLDHashOperator BeginRefreshingImages(nsISupportsHashKey* aEntry, 1.291 + void* aUserArg); 1.292 + ObserverArray& ArrayFor(mozFlushType aFlushType); 1.293 + // Trigger a refresh immediately, if haven't been disconnected or frozen. 1.294 + void DoRefresh(); 1.295 + 1.296 + double GetRefreshTimerInterval() const; 1.297 + double GetRegularTimerInterval(bool *outIsDefault = nullptr) const; 1.298 + double GetThrottledTimerInterval() const; 1.299 + 1.300 + bool HaveFrameRequestCallbacks() const { 1.301 + return mFrameRequestCallbackDocs.Length() != 0; 1.302 + } 1.303 + 1.304 + mozilla::RefreshDriverTimer* ChooseTimer() const; 1.305 + mozilla::RefreshDriverTimer *mActiveTimer; 1.306 + 1.307 + nsPresContext *mPresContext; // weak; pres context passed in constructor 1.308 + // and unset in Disconnect 1.309 + 1.310 + uint32_t mFreezeCount; 1.311 + bool mThrottled; 1.312 + bool mTestControllingRefreshes; 1.313 + bool mViewManagerFlushIsPending; 1.314 + bool mRequestedHighPrecision; 1.315 + bool mInRefresh; 1.316 + 1.317 + int64_t mMostRecentRefreshEpochTime; 1.318 + mozilla::TimeStamp mMostRecentRefresh; 1.319 + 1.320 + // separate arrays for each flush type we support 1.321 + ObserverArray mObservers[3]; 1.322 + RequestTable mRequests; 1.323 + ImageStartTable mStartTable; 1.324 + 1.325 + nsAutoTArray<nsIPresShell*, 16> mStyleFlushObservers; 1.326 + nsAutoTArray<nsIPresShell*, 16> mLayoutFlushObservers; 1.327 + nsAutoTArray<nsIPresShell*, 16> mPresShellsToInvalidateIfHidden; 1.328 + // nsTArray on purpose, because we want to be able to swap. 1.329 + nsTArray<nsIDocument*> mFrameRequestCallbackDocs; 1.330 + nsTArray<nsAPostRefreshObserver*> mPostRefreshObservers; 1.331 + 1.332 + // Helper struct for processing image requests 1.333 + struct ImageRequestParameters { 1.334 + mozilla::TimeStamp mCurrent; 1.335 + mozilla::TimeStamp mPrevious; 1.336 + RequestTable* mRequests; 1.337 + mozilla::TimeStamp mDesired; 1.338 + }; 1.339 + 1.340 + friend class mozilla::RefreshDriverTimer; 1.341 + 1.342 + // turn on or turn off high precision based on various factors 1.343 + void ConfigureHighPrecision(); 1.344 + void SetHighPrecisionTimersEnabled(bool aEnable); 1.345 +}; 1.346 + 1.347 +#endif /* !defined(nsRefreshDriver_h_) */