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