Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */ |
michael@0 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | /* |
michael@0 | 8 | * Code to notify things that animate before a refresh, at an appropriate |
michael@0 | 9 | * refresh rate. (Perhaps temporary, until replaced by compositor.) |
michael@0 | 10 | */ |
michael@0 | 11 | |
michael@0 | 12 | #ifndef nsRefreshDriver_h_ |
michael@0 | 13 | #define nsRefreshDriver_h_ |
michael@0 | 14 | |
michael@0 | 15 | #include "mozilla/TimeStamp.h" |
michael@0 | 16 | #include "mozFlushType.h" |
michael@0 | 17 | #include "nsTObserverArray.h" |
michael@0 | 18 | #include "nsTArray.h" |
michael@0 | 19 | #include "nsTHashtable.h" |
michael@0 | 20 | #include "nsClassHashtable.h" |
michael@0 | 21 | #include "nsHashKeys.h" |
michael@0 | 22 | #include "mozilla/Attributes.h" |
michael@0 | 23 | #include "mozilla/Maybe.h" |
michael@0 | 24 | |
michael@0 | 25 | class nsPresContext; |
michael@0 | 26 | class nsIPresShell; |
michael@0 | 27 | class nsIDocument; |
michael@0 | 28 | class imgIRequest; |
michael@0 | 29 | class nsIRunnable; |
michael@0 | 30 | |
michael@0 | 31 | namespace mozilla { |
michael@0 | 32 | class RefreshDriverTimer; |
michael@0 | 33 | } |
michael@0 | 34 | |
michael@0 | 35 | /** |
michael@0 | 36 | * An abstract base class to be implemented by callers wanting to be |
michael@0 | 37 | * notified at refresh times. When nothing needs to be painted, callers |
michael@0 | 38 | * may not be notified. |
michael@0 | 39 | */ |
michael@0 | 40 | class nsARefreshObserver { |
michael@0 | 41 | public: |
michael@0 | 42 | // AddRef and Release signatures that match nsISupports. Implementors |
michael@0 | 43 | // must implement reference counting, and those that do implement |
michael@0 | 44 | // nsISupports will already have methods with the correct signature. |
michael@0 | 45 | // |
michael@0 | 46 | // The refresh driver does NOT hold references to refresh observers |
michael@0 | 47 | // except while it is notifying them. |
michael@0 | 48 | NS_IMETHOD_(MozExternalRefCountType) AddRef(void) = 0; |
michael@0 | 49 | NS_IMETHOD_(MozExternalRefCountType) Release(void) = 0; |
michael@0 | 50 | |
michael@0 | 51 | virtual void WillRefresh(mozilla::TimeStamp aTime) = 0; |
michael@0 | 52 | }; |
michael@0 | 53 | |
michael@0 | 54 | /** |
michael@0 | 55 | * An abstract base class to be implemented by callers wanting to be notified |
michael@0 | 56 | * that a refresh has occurred. Callers must ensure an observer is removed |
michael@0 | 57 | * before it is destroyed. |
michael@0 | 58 | */ |
michael@0 | 59 | class nsAPostRefreshObserver { |
michael@0 | 60 | public: |
michael@0 | 61 | virtual void DidRefresh() = 0; |
michael@0 | 62 | }; |
michael@0 | 63 | |
michael@0 | 64 | class nsRefreshDriver MOZ_FINAL : public nsISupports { |
michael@0 | 65 | public: |
michael@0 | 66 | nsRefreshDriver(nsPresContext *aPresContext); |
michael@0 | 67 | ~nsRefreshDriver(); |
michael@0 | 68 | |
michael@0 | 69 | static void InitializeStatics(); |
michael@0 | 70 | static void Shutdown(); |
michael@0 | 71 | |
michael@0 | 72 | // nsISupports implementation |
michael@0 | 73 | NS_DECL_ISUPPORTS |
michael@0 | 74 | |
michael@0 | 75 | /** |
michael@0 | 76 | * Methods for testing, exposed via nsIDOMWindowUtils. See |
michael@0 | 77 | * nsIDOMWindowUtils.advanceTimeAndRefresh for description. |
michael@0 | 78 | */ |
michael@0 | 79 | void AdvanceTimeAndRefresh(int64_t aMilliseconds); |
michael@0 | 80 | void RestoreNormalRefresh(); |
michael@0 | 81 | void DoTick(); |
michael@0 | 82 | bool IsTestControllingRefreshesEnabled() const |
michael@0 | 83 | { |
michael@0 | 84 | return mTestControllingRefreshes; |
michael@0 | 85 | } |
michael@0 | 86 | |
michael@0 | 87 | /** |
michael@0 | 88 | * Return the time of the most recent refresh. This is intended to be |
michael@0 | 89 | * used by callers who want to start an animation now and want to know |
michael@0 | 90 | * what time to consider the start of the animation. (This helps |
michael@0 | 91 | * ensure that multiple animations started during the same event off |
michael@0 | 92 | * the main event loop have the same start time.) |
michael@0 | 93 | */ |
michael@0 | 94 | mozilla::TimeStamp MostRecentRefresh() const; |
michael@0 | 95 | /** |
michael@0 | 96 | * Same thing, but in microseconds since the epoch. |
michael@0 | 97 | */ |
michael@0 | 98 | int64_t MostRecentRefreshEpochTime() const; |
michael@0 | 99 | |
michael@0 | 100 | /** |
michael@0 | 101 | * Add / remove refresh observers. Returns whether the operation |
michael@0 | 102 | * succeeded. |
michael@0 | 103 | * |
michael@0 | 104 | * The flush type affects: |
michael@0 | 105 | * + the order in which the observers are notified (lowest flush |
michael@0 | 106 | * type to highest, in order registered) |
michael@0 | 107 | * + (in the future) which observers are suppressed when the display |
michael@0 | 108 | * doesn't require current position data or isn't currently |
michael@0 | 109 | * painting, and, correspondingly, which get notified when there |
michael@0 | 110 | * is a flush during such suppression |
michael@0 | 111 | * and it must be either Flush_Style, Flush_Layout, or Flush_Display. |
michael@0 | 112 | * |
michael@0 | 113 | * The refresh driver does NOT own a reference to these observers; |
michael@0 | 114 | * they must remove themselves before they are destroyed. |
michael@0 | 115 | * |
michael@0 | 116 | * The observer will be called even if there is no other activity. |
michael@0 | 117 | */ |
michael@0 | 118 | bool AddRefreshObserver(nsARefreshObserver *aObserver, |
michael@0 | 119 | mozFlushType aFlushType); |
michael@0 | 120 | bool RemoveRefreshObserver(nsARefreshObserver *aObserver, |
michael@0 | 121 | mozFlushType aFlushType); |
michael@0 | 122 | |
michael@0 | 123 | /** |
michael@0 | 124 | * Add an observer that will be called after each refresh. The caller |
michael@0 | 125 | * must remove the observer before it is deleted. This does not trigger |
michael@0 | 126 | * refresh driver ticks. |
michael@0 | 127 | */ |
michael@0 | 128 | void AddPostRefreshObserver(nsAPostRefreshObserver *aObserver); |
michael@0 | 129 | void RemovePostRefreshObserver(nsAPostRefreshObserver *aObserver); |
michael@0 | 130 | |
michael@0 | 131 | /** |
michael@0 | 132 | * Add/Remove imgIRequest versions of observers. |
michael@0 | 133 | * |
michael@0 | 134 | * These are used for hooking into the refresh driver for |
michael@0 | 135 | * controlling animated images. |
michael@0 | 136 | * |
michael@0 | 137 | * @note The refresh driver owns a reference to these listeners. |
michael@0 | 138 | * |
michael@0 | 139 | * @note Technically, imgIRequest objects are not nsARefreshObservers, but |
michael@0 | 140 | * for controlling animated image repaint events, we subscribe the |
michael@0 | 141 | * imgIRequests to the nsRefreshDriver for notification of paint events. |
michael@0 | 142 | * |
michael@0 | 143 | * @returns whether the operation succeeded, or void in the case of removal. |
michael@0 | 144 | */ |
michael@0 | 145 | bool AddImageRequest(imgIRequest* aRequest); |
michael@0 | 146 | void RemoveImageRequest(imgIRequest* aRequest); |
michael@0 | 147 | |
michael@0 | 148 | /** |
michael@0 | 149 | * Add / remove presshells that we should flush style and layout on |
michael@0 | 150 | */ |
michael@0 | 151 | bool AddStyleFlushObserver(nsIPresShell* aShell) { |
michael@0 | 152 | NS_ASSERTION(!mStyleFlushObservers.Contains(aShell), |
michael@0 | 153 | "Double-adding style flush observer"); |
michael@0 | 154 | bool appended = mStyleFlushObservers.AppendElement(aShell) != nullptr; |
michael@0 | 155 | EnsureTimerStarted(false); |
michael@0 | 156 | return appended; |
michael@0 | 157 | } |
michael@0 | 158 | void RemoveStyleFlushObserver(nsIPresShell* aShell) { |
michael@0 | 159 | mStyleFlushObservers.RemoveElement(aShell); |
michael@0 | 160 | } |
michael@0 | 161 | bool AddLayoutFlushObserver(nsIPresShell* aShell) { |
michael@0 | 162 | NS_ASSERTION(!IsLayoutFlushObserver(aShell), |
michael@0 | 163 | "Double-adding layout flush observer"); |
michael@0 | 164 | bool appended = mLayoutFlushObservers.AppendElement(aShell) != nullptr; |
michael@0 | 165 | EnsureTimerStarted(false); |
michael@0 | 166 | return appended; |
michael@0 | 167 | } |
michael@0 | 168 | void RemoveLayoutFlushObserver(nsIPresShell* aShell) { |
michael@0 | 169 | mLayoutFlushObservers.RemoveElement(aShell); |
michael@0 | 170 | } |
michael@0 | 171 | bool IsLayoutFlushObserver(nsIPresShell* aShell) { |
michael@0 | 172 | return mLayoutFlushObservers.Contains(aShell); |
michael@0 | 173 | } |
michael@0 | 174 | bool AddPresShellToInvalidateIfHidden(nsIPresShell* aShell) { |
michael@0 | 175 | NS_ASSERTION(!mPresShellsToInvalidateIfHidden.Contains(aShell), |
michael@0 | 176 | "Double-adding style flush observer"); |
michael@0 | 177 | bool appended = mPresShellsToInvalidateIfHidden.AppendElement(aShell) != nullptr; |
michael@0 | 178 | EnsureTimerStarted(false); |
michael@0 | 179 | return appended; |
michael@0 | 180 | } |
michael@0 | 181 | void RemovePresShellToInvalidateIfHidden(nsIPresShell* aShell) { |
michael@0 | 182 | mPresShellsToInvalidateIfHidden.RemoveElement(aShell); |
michael@0 | 183 | } |
michael@0 | 184 | |
michael@0 | 185 | /** |
michael@0 | 186 | * Remember whether our presshell's view manager needs a flush |
michael@0 | 187 | */ |
michael@0 | 188 | void ScheduleViewManagerFlush(); |
michael@0 | 189 | void RevokeViewManagerFlush() { |
michael@0 | 190 | mViewManagerFlushIsPending = false; |
michael@0 | 191 | } |
michael@0 | 192 | bool ViewManagerFlushIsPending() { |
michael@0 | 193 | return mViewManagerFlushIsPending; |
michael@0 | 194 | } |
michael@0 | 195 | |
michael@0 | 196 | /** |
michael@0 | 197 | * Add a document for which we have nsIFrameRequestCallbacks |
michael@0 | 198 | */ |
michael@0 | 199 | void ScheduleFrameRequestCallbacks(nsIDocument* aDocument); |
michael@0 | 200 | |
michael@0 | 201 | /** |
michael@0 | 202 | * Remove a document for which we have nsIFrameRequestCallbacks |
michael@0 | 203 | */ |
michael@0 | 204 | void RevokeFrameRequestCallbacks(nsIDocument* aDocument); |
michael@0 | 205 | |
michael@0 | 206 | /** |
michael@0 | 207 | * Tell the refresh driver that it is done driving refreshes and |
michael@0 | 208 | * should stop its timer and forget about its pres context. This may |
michael@0 | 209 | * be called from within a refresh. |
michael@0 | 210 | */ |
michael@0 | 211 | void Disconnect() { |
michael@0 | 212 | StopTimer(); |
michael@0 | 213 | mPresContext = nullptr; |
michael@0 | 214 | } |
michael@0 | 215 | |
michael@0 | 216 | bool IsFrozen() { return mFreezeCount > 0; } |
michael@0 | 217 | |
michael@0 | 218 | /** |
michael@0 | 219 | * Freeze the refresh driver. It should stop delivering future |
michael@0 | 220 | * refreshes until thawed. Note that the number of calls to Freeze() must |
michael@0 | 221 | * match the number of calls to Thaw() in order for the refresh driver to |
michael@0 | 222 | * be un-frozen. |
michael@0 | 223 | */ |
michael@0 | 224 | void Freeze(); |
michael@0 | 225 | |
michael@0 | 226 | /** |
michael@0 | 227 | * Thaw the refresh driver. If the number of calls to Freeze() matches the |
michael@0 | 228 | * number of calls to this function, the refresh driver should start |
michael@0 | 229 | * delivering refreshes again. |
michael@0 | 230 | */ |
michael@0 | 231 | void Thaw(); |
michael@0 | 232 | |
michael@0 | 233 | /** |
michael@0 | 234 | * Throttle or unthrottle the refresh driver. This is done if the |
michael@0 | 235 | * corresponding presshell is hidden or shown. |
michael@0 | 236 | */ |
michael@0 | 237 | void SetThrottled(bool aThrottled); |
michael@0 | 238 | |
michael@0 | 239 | /** |
michael@0 | 240 | * Return the prescontext we were initialized with |
michael@0 | 241 | */ |
michael@0 | 242 | nsPresContext* PresContext() const { return mPresContext; } |
michael@0 | 243 | |
michael@0 | 244 | #ifdef DEBUG |
michael@0 | 245 | /** |
michael@0 | 246 | * Check whether the given observer is an observer for the given flush type |
michael@0 | 247 | */ |
michael@0 | 248 | bool IsRefreshObserver(nsARefreshObserver *aObserver, |
michael@0 | 249 | mozFlushType aFlushType); |
michael@0 | 250 | #endif |
michael@0 | 251 | |
michael@0 | 252 | /** |
michael@0 | 253 | * Default interval the refresh driver uses, in ms. |
michael@0 | 254 | */ |
michael@0 | 255 | static int32_t DefaultInterval(); |
michael@0 | 256 | |
michael@0 | 257 | bool IsInRefresh() { return mInRefresh; } |
michael@0 | 258 | |
michael@0 | 259 | private: |
michael@0 | 260 | typedef nsTObserverArray<nsARefreshObserver*> ObserverArray; |
michael@0 | 261 | typedef nsTHashtable<nsISupportsHashKey> RequestTable; |
michael@0 | 262 | struct ImageStartData { |
michael@0 | 263 | ImageStartData() |
michael@0 | 264 | { |
michael@0 | 265 | } |
michael@0 | 266 | |
michael@0 | 267 | mozilla::Maybe<mozilla::TimeStamp> mStartTime; |
michael@0 | 268 | RequestTable mEntries; |
michael@0 | 269 | }; |
michael@0 | 270 | typedef nsClassHashtable<nsUint32HashKey, ImageStartData> ImageStartTable; |
michael@0 | 271 | |
michael@0 | 272 | void Tick(int64_t aNowEpoch, mozilla::TimeStamp aNowTime); |
michael@0 | 273 | |
michael@0 | 274 | void EnsureTimerStarted(bool aAdjustingTimer); |
michael@0 | 275 | void StopTimer(); |
michael@0 | 276 | |
michael@0 | 277 | uint32_t ObserverCount() const; |
michael@0 | 278 | uint32_t ImageRequestCount() const; |
michael@0 | 279 | static PLDHashOperator ImageRequestEnumerator(nsISupportsHashKey* aEntry, |
michael@0 | 280 | void* aUserArg); |
michael@0 | 281 | static PLDHashOperator StartTableRequestCounter(const uint32_t& aKey, |
michael@0 | 282 | ImageStartData* aEntry, |
michael@0 | 283 | void* aUserArg); |
michael@0 | 284 | static PLDHashOperator StartTableRefresh(const uint32_t& aKey, |
michael@0 | 285 | ImageStartData* aEntry, |
michael@0 | 286 | void* aUserArg); |
michael@0 | 287 | static PLDHashOperator BeginRefreshingImages(nsISupportsHashKey* aEntry, |
michael@0 | 288 | void* aUserArg); |
michael@0 | 289 | ObserverArray& ArrayFor(mozFlushType aFlushType); |
michael@0 | 290 | // Trigger a refresh immediately, if haven't been disconnected or frozen. |
michael@0 | 291 | void DoRefresh(); |
michael@0 | 292 | |
michael@0 | 293 | double GetRefreshTimerInterval() const; |
michael@0 | 294 | double GetRegularTimerInterval(bool *outIsDefault = nullptr) const; |
michael@0 | 295 | double GetThrottledTimerInterval() const; |
michael@0 | 296 | |
michael@0 | 297 | bool HaveFrameRequestCallbacks() const { |
michael@0 | 298 | return mFrameRequestCallbackDocs.Length() != 0; |
michael@0 | 299 | } |
michael@0 | 300 | |
michael@0 | 301 | mozilla::RefreshDriverTimer* ChooseTimer() const; |
michael@0 | 302 | mozilla::RefreshDriverTimer *mActiveTimer; |
michael@0 | 303 | |
michael@0 | 304 | nsPresContext *mPresContext; // weak; pres context passed in constructor |
michael@0 | 305 | // and unset in Disconnect |
michael@0 | 306 | |
michael@0 | 307 | uint32_t mFreezeCount; |
michael@0 | 308 | bool mThrottled; |
michael@0 | 309 | bool mTestControllingRefreshes; |
michael@0 | 310 | bool mViewManagerFlushIsPending; |
michael@0 | 311 | bool mRequestedHighPrecision; |
michael@0 | 312 | bool mInRefresh; |
michael@0 | 313 | |
michael@0 | 314 | int64_t mMostRecentRefreshEpochTime; |
michael@0 | 315 | mozilla::TimeStamp mMostRecentRefresh; |
michael@0 | 316 | |
michael@0 | 317 | // separate arrays for each flush type we support |
michael@0 | 318 | ObserverArray mObservers[3]; |
michael@0 | 319 | RequestTable mRequests; |
michael@0 | 320 | ImageStartTable mStartTable; |
michael@0 | 321 | |
michael@0 | 322 | nsAutoTArray<nsIPresShell*, 16> mStyleFlushObservers; |
michael@0 | 323 | nsAutoTArray<nsIPresShell*, 16> mLayoutFlushObservers; |
michael@0 | 324 | nsAutoTArray<nsIPresShell*, 16> mPresShellsToInvalidateIfHidden; |
michael@0 | 325 | // nsTArray on purpose, because we want to be able to swap. |
michael@0 | 326 | nsTArray<nsIDocument*> mFrameRequestCallbackDocs; |
michael@0 | 327 | nsTArray<nsAPostRefreshObserver*> mPostRefreshObservers; |
michael@0 | 328 | |
michael@0 | 329 | // Helper struct for processing image requests |
michael@0 | 330 | struct ImageRequestParameters { |
michael@0 | 331 | mozilla::TimeStamp mCurrent; |
michael@0 | 332 | mozilla::TimeStamp mPrevious; |
michael@0 | 333 | RequestTable* mRequests; |
michael@0 | 334 | mozilla::TimeStamp mDesired; |
michael@0 | 335 | }; |
michael@0 | 336 | |
michael@0 | 337 | friend class mozilla::RefreshDriverTimer; |
michael@0 | 338 | |
michael@0 | 339 | // turn on or turn off high precision based on various factors |
michael@0 | 340 | void ConfigureHighPrecision(); |
michael@0 | 341 | void SetHighPrecisionTimersEnabled(bool aEnable); |
michael@0 | 342 | }; |
michael@0 | 343 | |
michael@0 | 344 | #endif /* !defined(nsRefreshDriver_h_) */ |