Coverage Report

Created: 2018-09-25 14:53

/work/obj-fuzz/dist/include/nsRefreshDriver.h
Line
Count
Source (jump to first uncovered line)
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
/*
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/FlushType.h"
16
#include "mozilla/TimeStamp.h"
17
#include "mozilla/UniquePtr.h"
18
#include "mozilla/Vector.h"
19
#include "mozilla/WeakPtr.h"
20
#include "nsTObserverArray.h"
21
#include "nsTArray.h"
22
#include "nsTHashtable.h"
23
#include "nsTObserverArray.h"
24
#include "nsClassHashtable.h"
25
#include "nsHashKeys.h"
26
#include "mozilla/Attributes.h"
27
#include "mozilla/Maybe.h"
28
#include "mozilla/layers/TransactionIdAllocator.h"
29
30
class nsPresContext;
31
class nsIPresShell;
32
class nsIDocument;
33
class imgIRequest;
34
class nsINode;
35
class nsIRunnable;
36
37
namespace mozilla {
38
class AnimationEventDispatcher;
39
class PendingFullscreenEvent;
40
class RefreshDriverTimer;
41
class Runnable;
42
43
namespace layout {
44
class VsyncChild;
45
} // namespace layout
46
47
namespace dom {
48
class Event;
49
} // namespace dom
50
51
} // namespace mozilla
52
53
/**
54
 * An abstract base class to be implemented by callers wanting to be
55
 * notified at refresh times.  When nothing needs to be painted, callers
56
 * may not be notified.
57
 */
58
class nsARefreshObserver {
59
public:
60
  // AddRef and Release signatures that match nsISupports.  Implementors
61
  // must implement reference counting, and those that do implement
62
  // nsISupports will already have methods with the correct signature.
63
  //
64
  // The refresh driver does NOT hold references to refresh observers
65
  // except while it is notifying them.
66
  NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
67
68
  virtual void WillRefresh(mozilla::TimeStamp aTime) = 0;
69
};
70
71
/**
72
 * An abstract base class to be implemented by callers wanting to be notified
73
 * when the observing refresh driver updated mMostRecentRefresh due to active
74
 * timer changes. Callers must ensure an observer is removed before it is
75
 * destroyed.
76
 */
77
class nsATimerAdjustmentObserver {
78
public:
79
  virtual void NotifyTimerAdjusted(mozilla::TimeStamp aTime) = 0;
80
};
81
82
/**
83
 * An abstract base class to be implemented by callers wanting to be notified
84
 * that a refresh has occurred. Callers must ensure an observer is removed
85
 * before it is destroyed.
86
 */
87
class nsAPostRefreshObserver {
88
public:
89
  virtual void DidRefresh() = 0;
90
};
91
92
class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
93
                              public nsARefreshObserver
94
{
95
  using TransactionId = mozilla::layers::TransactionId;
96
97
public:
98
  explicit nsRefreshDriver(nsPresContext *aPresContext);
99
  ~nsRefreshDriver();
100
101
  /**
102
   * Methods for testing, exposed via nsIDOMWindowUtils.  See
103
   * nsIDOMWindowUtils.advanceTimeAndRefresh for description.
104
   */
105
  void AdvanceTimeAndRefresh(int64_t aMilliseconds);
106
  void RestoreNormalRefresh();
107
  void DoTick();
108
  bool IsTestControllingRefreshesEnabled() const
109
  {
110
    return mTestControllingRefreshes;
111
  }
112
113
  /**
114
   * Return the time of the most recent refresh.  This is intended to be
115
   * used by callers who want to start an animation now and want to know
116
   * what time to consider the start of the animation.  (This helps
117
   * ensure that multiple animations started during the same event off
118
   * the main event loop have the same start time.)
119
   */
120
  mozilla::TimeStamp MostRecentRefresh() const;
121
122
  /**
123
   * Add / remove refresh observers.  Returns whether the operation
124
   * succeeded.
125
   *
126
   * The flush type affects:
127
   *   + the order in which the observers are notified (lowest flush
128
   *     type to highest, in order registered)
129
   *   + (in the future) which observers are suppressed when the display
130
   *     doesn't require current position data or isn't currently
131
   *     painting, and, correspondingly, which get notified when there
132
   *     is a flush during such suppression
133
   * and it must be FlushType::Style, FlushType::Layout, or FlushType::Display.
134
   *
135
   * The refresh driver does NOT own a reference to these observers;
136
   * they must remove themselves before they are destroyed.
137
   *
138
   * The observer will be called even if there is no other activity.
139
   */
140
  bool AddRefreshObserver(nsARefreshObserver *aObserver,
141
                          mozilla::FlushType aFlushType);
142
  bool RemoveRefreshObserver(nsARefreshObserver *aObserver,
143
                             mozilla::FlushType aFlushType);
144
  /**
145
   * Add / remove an observer wants to know the time when the refresh driver
146
   * updated the most recent refresh time due to its active timer changes.
147
   */
148
  bool AddTimerAdjustmentObserver(nsATimerAdjustmentObserver *aObserver);
149
  bool RemoveTimerAdjustmentObserver(nsATimerAdjustmentObserver *aObserver);
150
151
  void PostScrollEvent(mozilla::Runnable* aScrollEvent);
152
  void DispatchScrollEvents();
153
154
  /**
155
   * Add an observer that will be called after each refresh. The caller
156
   * must remove the observer before it is deleted. This does not trigger
157
   * refresh driver ticks.
158
   */
159
  void AddPostRefreshObserver(nsAPostRefreshObserver *aObserver);
160
  void RemovePostRefreshObserver(nsAPostRefreshObserver *aObserver);
161
162
  /**
163
   * Add/Remove imgIRequest versions of observers.
164
   *
165
   * These are used for hooking into the refresh driver for
166
   * controlling animated images.
167
   *
168
   * @note The refresh driver owns a reference to these listeners.
169
   *
170
   * @note Technically, imgIRequest objects are not nsARefreshObservers, but
171
   * for controlling animated image repaint events, we subscribe the
172
   * imgIRequests to the nsRefreshDriver for notification of paint events.
173
   *
174
   * @returns whether the operation succeeded, or void in the case of removal.
175
   */
176
  bool AddImageRequest(imgIRequest* aRequest);
177
  void RemoveImageRequest(imgIRequest* aRequest);
178
179
  /**
180
   * Add / remove presshells which have pending resize event.
181
   */
182
  void AddResizeEventFlushObserver(nsIPresShell* aShell)
183
0
  {
184
0
    MOZ_DIAGNOSTIC_ASSERT(!mResizeEventFlushObservers.Contains(aShell),
185
0
                          "Double-adding resize event flush observer");
186
0
    mResizeEventFlushObservers.AppendElement(aShell);
187
0
    EnsureTimerStarted();
188
0
  }
189
190
  void RemoveResizeEventFlushObserver(nsIPresShell* aShell)
191
0
  {
192
0
    mResizeEventFlushObservers.RemoveElement(aShell);
193
0
  }
194
195
  /**
196
   * Add / remove presshells that we should flush style and layout on
197
   */
198
  void AddStyleFlushObserver(nsIPresShell* aShell)
199
0
  {
200
0
    MOZ_DIAGNOSTIC_ASSERT(!mStyleFlushObservers.Contains(aShell),
201
0
                          "Double-adding style flush observer");
202
0
    mStyleFlushObservers.AppendElement(aShell);
203
0
    EnsureTimerStarted();
204
0
  }
205
206
  void RemoveStyleFlushObserver(nsIPresShell* aShell)
207
0
  {
208
0
    mStyleFlushObservers.RemoveElement(aShell);
209
0
  }
210
  void AddLayoutFlushObserver(nsIPresShell* aShell)
211
0
  {
212
0
    MOZ_DIAGNOSTIC_ASSERT(!IsLayoutFlushObserver(aShell),
213
0
                          "Double-adding layout flush observer");
214
0
    mLayoutFlushObservers.AppendElement(aShell);
215
0
    EnsureTimerStarted();
216
0
  }
217
  void RemoveLayoutFlushObserver(nsIPresShell* aShell)
218
0
  {
219
0
    mLayoutFlushObservers.RemoveElement(aShell);
220
0
  }
221
222
  bool IsLayoutFlushObserver(nsIPresShell* aShell)
223
0
  {
224
0
    return mLayoutFlushObservers.Contains(aShell);
225
0
  }
226
227
  /**
228
   * "Early Runner" runnables will be called as the first step when refresh
229
   * driver tick is triggered. Runners shouldn't keep other objects alive,
230
   * since it isn't guaranteed they will ever get called.
231
   */
232
  void AddEarlyRunner(nsIRunnable* aRunnable)
233
  {
234
    mEarlyRunners.AppendElement(aRunnable);
235
    EnsureTimerStarted();
236
  }
237
238
  /**
239
   * Remember whether our presshell's view manager needs a flush
240
   */
241
  void ScheduleViewManagerFlush();
242
0
  void RevokeViewManagerFlush() {
243
0
    mViewManagerFlushIsPending = false;
244
0
  }
245
0
  bool ViewManagerFlushIsPending() {
246
0
    return mViewManagerFlushIsPending;
247
0
  }
248
  bool HasScheduleFlush() {
249
    return mHasScheduleFlush;
250
  }
251
252
  /**
253
   * Add a document for which we have FrameRequestCallbacks
254
   */
255
  void ScheduleFrameRequestCallbacks(nsIDocument* aDocument);
256
257
  /**
258
   * Remove a document for which we have FrameRequestCallbacks
259
   */
260
  void RevokeFrameRequestCallbacks(nsIDocument* aDocument);
261
262
  /**
263
   * Queue a new fullscreen event to be dispatched in next tick before
264
   * the style flush
265
   */
266
  void ScheduleFullscreenEvent(
267
    mozilla::UniquePtr<mozilla::PendingFullscreenEvent> aEvent);
268
269
  /**
270
   * Cancel all pending fullscreen events scheduled by ScheduleFullscreenEvent
271
   * which targets any node in aDocument.
272
   */
273
  void CancelPendingFullscreenEvents(nsIDocument* aDocument);
274
275
  /**
276
   * Queue new animation events to dispatch in next tick.
277
   */
278
  void ScheduleAnimationEventDispatch(
279
    mozilla::AnimationEventDispatcher* aDispatcher)
280
  {
281
    NS_ASSERTION(!mAnimationEventFlushObservers.Contains(aDispatcher),
282
                 "Double-adding animation event flush observer");
283
    mAnimationEventFlushObservers.AppendElement(aDispatcher);
284
    EnsureTimerStarted();
285
  }
286
287
  /**
288
   * Cancel all pending animation events associated with |aDispatcher|.
289
   */
290
  void CancelPendingAnimationEvents(
291
    mozilla::AnimationEventDispatcher* aDispatcher);
292
293
  /**
294
   * Schedule a frame visibility update "soon", subject to the heuristics and
295
   * throttling we apply to visibility updates.
296
   */
297
0
  void ScheduleFrameVisibilityUpdate() { mNeedToRecomputeVisibility = true; }
298
299
  /**
300
   * Tell the refresh driver that it is done driving refreshes and
301
   * should stop its timer and forget about its pres context.  This may
302
   * be called from within a refresh.
303
   */
304
  void Disconnect();
305
306
  bool IsFrozen() { return mFreezeCount > 0; }
307
308
  /**
309
   * Freeze the refresh driver.  It should stop delivering future
310
   * refreshes until thawed. Note that the number of calls to Freeze() must
311
   * match the number of calls to Thaw() in order for the refresh driver to
312
   * be un-frozen.
313
   */
314
  void Freeze();
315
316
  /**
317
   * Thaw the refresh driver.  If the number of calls to Freeze() matches the
318
   * number of calls to this function, the refresh driver should start
319
   * delivering refreshes again.
320
   */
321
  void Thaw();
322
323
  /**
324
   * Throttle or unthrottle the refresh driver.  This is done if the
325
   * corresponding presshell is hidden or shown.
326
   */
327
  void SetThrottled(bool aThrottled);
328
329
  /**
330
   * Return the prescontext we were initialized with
331
   */
332
  nsPresContext* GetPresContext() const { return mPresContext; }
333
334
  /**
335
   * PBackgroundChild actor is created asynchronously in content process.
336
   * We can't create vsync-based timers during PBackground startup. This
337
   * function will be called when PBackgroundChild actor is created. Then we can
338
   * do the pending vsync-based timer creation.
339
   */
340
  static void PVsyncActorCreated(mozilla::layout::VsyncChild* aVsyncChild);
341
342
#ifdef DEBUG
343
  /**
344
   * Check whether the given observer is an observer for the given flush type
345
   */
346
  bool IsRefreshObserver(nsARefreshObserver *aObserver,
347
                         mozilla::FlushType aFlushType);
348
#endif
349
350
  /**
351
   * Default interval the refresh driver uses, in ms.
352
   */
353
  static int32_t DefaultInterval();
354
355
  bool IsInRefresh() { return mInRefresh; }
356
357
  void SetIsResizeSuppressed() { mResizeSuppressed = true; }
358
0
  bool IsResizeSuppressed() const { return mResizeSuppressed; }
359
360
  /**
361
   * The latest value of process-wide jank levels.
362
   *
363
   * For each i, sJankLevels[i] counts the number of times delivery of
364
   * vsync to the main thread has been delayed by at least 2^i
365
   * ms. This data structure has been designed to make it easy to
366
   * determine how much jank has taken place between two instants in
367
   * time.
368
   *
369
   * Return `false` if `aJank` needs to be grown to accomodate the
370
   * data but we didn't have enough memory.
371
   */
372
  static bool GetJankLevels(mozilla::Vector<uint64_t>& aJank);
373
374
  // mozilla::layers::TransactionIdAllocator
375
  TransactionId GetTransactionId(bool aThrottle) override;
376
  TransactionId LastTransactionId() const override;
377
  void NotifyTransactionCompleted(TransactionId aTransactionId) override;
378
  void RevokeTransactionId(TransactionId aTransactionId) override;
379
  void ClearPendingTransactions() override;
380
  void ResetInitialTransactionId(TransactionId aTransactionId) override;
381
  mozilla::TimeStamp GetTransactionStart() override;
382
383
  bool IsWaitingForPaint(mozilla::TimeStamp aTime);
384
385
  // nsARefreshObserver
386
0
  NS_IMETHOD_(MozExternalRefCountType) AddRef(void) override { return TransactionIdAllocator::AddRef(); }
387
0
  NS_IMETHOD_(MozExternalRefCountType) Release(void) override { return TransactionIdAllocator::Release(); }
388
  virtual void WillRefresh(mozilla::TimeStamp aTime) override;
389
390
  /**
391
   * Compute the time when the currently active refresh driver timer
392
   * will start its next tick.
393
   *
394
   * Expects a non-null default value that is the upper bound of the
395
   * expected deadline. If the next expected deadline is later than
396
   * the default value, the default value is returned.
397
   *
398
   * If we're animating and we have skipped paints a time in the past
399
   * is returned.
400
   */
401
  static mozilla::TimeStamp GetIdleDeadlineHint(mozilla::TimeStamp aDefault);
402
403
  /**
404
   * It returns the expected timestamp of the next tick or nothing if the next
405
   * tick is missed.
406
   */
407
  static mozilla::Maybe<mozilla::TimeStamp> GetNextTickHint();
408
409
  static void DispatchIdleRunnableAfterTick(nsIRunnable* aRunnable,
410
                                            uint32_t aDelay);
411
  static void CancelIdleRunnable(nsIRunnable* aRunnable);
412
413
  void NotifyDOMContentLoaded();
414
415
private:
416
  typedef nsTObserverArray<nsARefreshObserver*> ObserverArray;
417
  typedef nsTArray<RefPtr<mozilla::Runnable>> ScrollEventArray;
418
  typedef nsTHashtable<nsISupportsHashKey> RequestTable;
419
  struct ImageStartData {
420
    ImageStartData()
421
    {
422
    }
423
424
    mozilla::Maybe<mozilla::TimeStamp> mStartTime;
425
    RequestTable mEntries;
426
  };
427
  typedef nsClassHashtable<nsUint32HashKey, ImageStartData> ImageStartTable;
428
429
  void RunFullscreenSteps();
430
  void DispatchAnimationEvents();
431
  void RunFrameRequestCallbacks(mozilla::TimeStamp aNowTime);
432
  void UpdateIntersectionObservations();
433
  void Tick(mozilla::TimeStamp aNowTime);
434
435
  enum EnsureTimerStartedFlags {
436
    eNone = 0,
437
    eForceAdjustTimer = 1 << 0,
438
    eAllowTimeToGoBackwards = 1 << 1,
439
    eNeverAdjustTimer = 1 << 2,
440
  };
441
  void EnsureTimerStarted(EnsureTimerStartedFlags aFlags = eNone);
442
  void StopTimer();
443
444
  bool HasObservers() const;
445
  // Note: This should only be called in the dtor of nsRefreshDriver.
446
  uint32_t ObserverCount() const;
447
  bool HasImageRequests() const;
448
  ObserverArray& ArrayFor(mozilla::FlushType aFlushType);
449
  // Trigger a refresh immediately, if haven't been disconnected or frozen.
450
  void DoRefresh();
451
452
  double GetRegularTimerInterval() const;
453
  static double GetThrottledTimerInterval();
454
455
  static mozilla::TimeDuration GetMinRecomputeVisibilityInterval();
456
457
0
  bool HaveFrameRequestCallbacks() const {
458
0
    return mFrameRequestCallbackDocs.Length() != 0;
459
0
  }
460
461
  void FinishedWaitingForTransaction();
462
463
  mozilla::RefreshDriverTimer* ChooseTimer() const;
464
  mozilla::RefreshDriverTimer* mActiveTimer;
465
466
  // nsPresContext passed in constructor and unset in Disconnect.
467
  mozilla::WeakPtr<nsPresContext> mPresContext;
468
469
  RefPtr<nsRefreshDriver> mRootRefresh;
470
471
  // The most recently allocated transaction id.
472
  TransactionId mNextTransactionId;
473
  // This number is mCompletedTransaction + (pending transaction count).
474
  // When we revoke a transaction id, we revert this number (since it's
475
  // no longer outstanding), but not mNextTransactionId (since we don't
476
  // want to reuse the number).
477
  TransactionId mOutstandingTransactionId;
478
  // The most recently completed transaction id.
479
  TransactionId mCompletedTransaction;
480
481
  uint32_t mFreezeCount;
482
483
  // How long we wait between ticks for throttled (which generally means
484
  // non-visible) documents registered with a non-throttled refresh driver.
485
  const mozilla::TimeDuration mThrottledFrameRequestInterval;
486
487
  // How long we wait, at a minimum, before recomputing approximate frame
488
  // visibility information. This is a minimum because, regardless of this
489
  // interval, we only recompute visibility when we've seen a layout or style
490
  // flush since the last time we did it.
491
  const mozilla::TimeDuration mMinRecomputeVisibilityInterval;
492
493
  bool mThrottled;
494
  bool mNeedToRecomputeVisibility;
495
  bool mTestControllingRefreshes;
496
  bool mViewManagerFlushIsPending;
497
498
  // True if the view manager needs a flush. Layers-free mode uses this value
499
  // to know when to notify invalidation.
500
  bool mHasScheduleFlush;
501
502
  bool mInRefresh;
503
504
  // True if the refresh driver is suspended waiting for transaction
505
  // id's to be returned and shouldn't do any work during Tick().
506
  bool mWaitingForTransaction;
507
  // True if Tick() was skipped because of mWaitingForTransaction and
508
  // we should schedule a new Tick immediately when resumed instead
509
  // of waiting until the next interval.
510
  bool mSkippedPaints;
511
512
  // True if view managers should delay any resize request until the
513
  // next tick by the refresh driver. This flag will be reset at the
514
  // start of every tick.
515
  bool mResizeSuppressed;
516
517
  // True if the next tick should notify DOMContentFlushed.
518
  bool mNotifyDOMContentFlushed;
519
520
  // Number of seconds that the refresh driver is blocked waiting for a compositor
521
  // transaction to be completed before we append a note to the gfx critical log.
522
  // The number is doubled every time the threshold is hit.
523
  uint64_t mWarningThreshold;
524
  mozilla::TimeStamp mMostRecentRefresh;
525
  mozilla::TimeStamp mTickStart;
526
  mozilla::TimeStamp mNextThrottledFrameRequestTick;
527
  mozilla::TimeStamp mNextRecomputeVisibilityTick;
528
529
  // separate arrays for each flush type we support
530
  ObserverArray mObservers[4];
531
  // These observers should NOT be included in HasObservers() since that method
532
  // is used to determine whether or not to stop the timer, or restore it when
533
  // thawing the refresh driver. On the other hand these observers are intended
534
  // to be called when the timer is re-started and should not influence its
535
  // starting or stopping.
536
  nsTObserverArray<nsATimerAdjustmentObserver*> mTimerAdjustmentObservers;
537
  RequestTable mRequests;
538
  ImageStartTable mStartTable;
539
  AutoTArray<nsCOMPtr<nsIRunnable>, 16> mEarlyRunners;
540
  ScrollEventArray mScrollEvents;
541
542
  AutoTArray<nsIPresShell*, 16> mResizeEventFlushObservers;
543
  AutoTArray<nsIPresShell*, 16> mStyleFlushObservers;
544
  AutoTArray<nsIPresShell*, 16> mLayoutFlushObservers;
545
  // nsTArray on purpose, because we want to be able to swap.
546
  nsTArray<nsIDocument*> mFrameRequestCallbackDocs;
547
  nsTArray<nsIDocument*> mThrottledFrameRequestCallbackDocs;
548
  nsTObserverArray<nsAPostRefreshObserver*> mPostRefreshObservers;
549
  nsTArray<mozilla::UniquePtr<mozilla::PendingFullscreenEvent>>
550
    mPendingFullscreenEvents;
551
  AutoTArray<mozilla::AnimationEventDispatcher*, 16>
552
    mAnimationEventFlushObservers;
553
554
  void BeginRefreshingImages(RequestTable& aEntries,
555
                             mozilla::TimeStamp aDesired);
556
557
  friend class mozilla::RefreshDriverTimer;
558
559
  static void Shutdown();
560
561
  // `true` if we are currently in jank-critical mode.
562
  //
563
  // In jank-critical mode, any iteration of the event loop that takes
564
  // more than 16ms to compute will cause an ongoing animation to miss
565
  // frames.
566
  //
567
  // For simplicity, the current implementation assumes that we are
568
  // in jank-critical mode if and only if the vsync driver has at least
569
  // one observer.
570
  static bool IsJankCritical();
571
};
572
573
#endif /* !defined(nsRefreshDriver_h_) */