Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/layout/base/nsRefreshDriver.cpp
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
 * Chrome and each tab have their own RefreshDriver, which in turn
12
 * hooks into one of a few global timer based on RefreshDriverTimer,
13
 * defined below.  There are two main global timers -- one for active
14
 * animations, and one for inactive ones.  These are implemented as
15
 * subclasses of RefreshDriverTimer; see below for a description of
16
 * their implementations.  In the future, additional timer types may
17
 * implement things like blocking on vsync.
18
 */
19
20
#include "nsRefreshDriver.h"
21
22
#ifdef XP_WIN
23
#include <windows.h>
24
// mmsystem isn't part of WIN32_LEAN_AND_MEAN, so we have
25
// to manually include it
26
#include <mmsystem.h>
27
#include "WinUtils.h"
28
#endif
29
30
#include "mozilla/AnimationEventDispatcher.h"
31
#include "mozilla/ArrayUtils.h"
32
#include "mozilla/AutoRestore.h"
33
#include "mozilla/IntegerRange.h"
34
#include "mozilla/dom/FontTableURIProtocolHandler.h"
35
#include "nsITimer.h"
36
#include "nsLayoutUtils.h"
37
#include "nsPresContext.h"
38
#include "nsComponentManagerUtils.h"
39
#include "mozilla/Logging.h"
40
#include "nsAutoPtr.h"
41
#include "nsIDocument.h"
42
#include "nsIXULRuntime.h"
43
#include "jsapi.h"
44
#include "nsContentUtils.h"
45
#include "mozilla/PendingAnimationTracker.h"
46
#include "mozilla/PendingFullscreenEvent.h"
47
#include "mozilla/Preferences.h"
48
#include "nsViewManager.h"
49
#include "GeckoProfiler.h"
50
#include "nsNPAPIPluginInstance.h"
51
#include "mozilla/dom/Event.h"
52
#include "mozilla/dom/Performance.h"
53
#include "mozilla/dom/Selection.h"
54
#include "mozilla/dom/WindowBinding.h"
55
#include "mozilla/RestyleManager.h"
56
#include "Layers.h"
57
#include "imgIContainer.h"
58
#include "mozilla/dom/ScriptSettings.h"
59
#include "nsDocShell.h"
60
#include "nsISimpleEnumerator.h"
61
#include "nsJSEnvironment.h"
62
#include "mozilla/Telemetry.h"
63
#include "gfxPrefs.h"
64
#include "BackgroundChild.h"
65
#include "mozilla/ipc/PBackgroundChild.h"
66
#include "mozilla/layout/VsyncChild.h"
67
#include "VsyncSource.h"
68
#include "mozilla/VsyncDispatcher.h"
69
#include "nsThreadUtils.h"
70
#include "mozilla/Unused.h"
71
#include "mozilla/TimelineConsumers.h"
72
#include "nsAnimationManager.h"
73
#include "nsDisplayList.h"
74
#include "nsTransitionManager.h"
75
76
#ifdef MOZ_XUL
77
#include "nsXULPopupManager.h"
78
#endif
79
80
using namespace mozilla;
81
using namespace mozilla::widget;
82
using namespace mozilla::ipc;
83
using namespace mozilla::layout;
84
85
static mozilla::LazyLogModule sRefreshDriverLog("nsRefreshDriver");
86
0
#define LOG(...) MOZ_LOG(sRefreshDriverLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
87
88
0
#define DEFAULT_THROTTLED_FRAME_RATE 1
89
0
#define DEFAULT_RECOMPUTE_VISIBILITY_INTERVAL_MS 1000
90
#define DEFAULT_NOTIFY_INTERSECTION_OBSERVERS_INTERVAL_MS 100
91
// after 10 minutes, stop firing off inactive timers
92
0
#define DEFAULT_INACTIVE_TIMER_DISABLE_SECONDS 600
93
94
// The number of seconds spent skipping frames because we are waiting for the compositor
95
// before logging.
96
#if defined(MOZ_ASAN)
97
# define REFRESH_WAIT_WARNING 5
98
#elif defined(DEBUG) && !defined(MOZ_VALGRIND)
99
# define REFRESH_WAIT_WARNING 5
100
#elif defined(DEBUG) && defined(MOZ_VALGRIND)
101
# define REFRESH_WAIT_WARNING (RUNNING_ON_VALGRIND ? 20 : 5)
102
#elif defined(MOZ_VALGRIND)
103
# define REFRESH_WAIT_WARNING (RUNNING_ON_VALGRIND ? 10 : 1)
104
#else
105
0
# define REFRESH_WAIT_WARNING 1
106
#endif
107
108
namespace {
109
  // `true` if we are currently in jank-critical mode.
110
  //
111
  // In jank-critical mode, any iteration of the event loop that takes
112
  // more than 16ms to compute will cause an ongoing animation to miss
113
  // frames.
114
  //
115
  // For simplicity, the current implementation assumes that we are in
116
  // jank-critical mode if and only if at least one vsync driver has
117
  // at least one observer.
118
  static uint64_t sActiveVsyncTimers = 0;
119
120
  // The latest value of process-wide jank levels.
121
  //
122
  // For each i, sJankLevels[i] counts the number of times delivery of
123
  // vsync to the main thread has been delayed by at least 2^i ms. Use
124
  // GetJankLevels to grab a copy of this array.
125
  uint64_t sJankLevels[12];
126
127
  // The number outstanding nsRefreshDrivers (that have been created but not
128
  // disconnected). When this reaches zero we will call
129
  // nsRefreshDriver::Shutdown.
130
  static uint32_t sRefreshDriverCount = 0;
131
}
132
133
namespace mozilla {
134
135
/*
136
 * The base class for all global refresh driver timers.  It takes care
137
 * of managing the list of refresh drivers attached to them and
138
 * provides interfaces for querying/setting the rate and actually
139
 * running a timer 'Tick'.  Subclasses must implement StartTimer(),
140
 * StopTimer(), and ScheduleNextTick() -- the first two just
141
 * start/stop whatever timer mechanism is in use, and ScheduleNextTick
142
 * is called at the start of the Tick() implementation to set a time
143
 * for the next tick.
144
 */
145
class RefreshDriverTimer {
146
public:
147
  RefreshDriverTimer()
148
0
  {
149
0
  }
150
151
  NS_INLINE_DECL_REFCOUNTING(RefreshDriverTimer)
152
153
  virtual void AddRefreshDriver(nsRefreshDriver* aDriver)
154
0
  {
155
0
    LOG("[%p] AddRefreshDriver %p", this, aDriver);
156
0
157
0
    bool startTimer = mContentRefreshDrivers.IsEmpty() && mRootRefreshDrivers.IsEmpty();
158
0
    if (IsRootRefreshDriver(aDriver)) {
159
0
      NS_ASSERTION(!mRootRefreshDrivers.Contains(aDriver), "Adding a duplicate root refresh driver!");
160
0
      mRootRefreshDrivers.AppendElement(aDriver);
161
0
    } else {
162
0
      NS_ASSERTION(!mContentRefreshDrivers.Contains(aDriver), "Adding a duplicate content refresh driver!");
163
0
      mContentRefreshDrivers.AppendElement(aDriver);
164
0
    }
165
0
166
0
    if (startTimer) {
167
0
      StartTimer();
168
0
    }
169
0
  }
170
171
  void RemoveRefreshDriver(nsRefreshDriver* aDriver)
172
0
  {
173
0
    LOG("[%p] RemoveRefreshDriver %p", this, aDriver);
174
0
175
0
    if (IsRootRefreshDriver(aDriver)) {
176
0
      NS_ASSERTION(mRootRefreshDrivers.Contains(aDriver), "RemoveRefreshDriver for a refresh driver that's not in the root refresh list!");
177
0
      mRootRefreshDrivers.RemoveElement(aDriver);
178
0
    } else {
179
0
      nsPresContext* pc = aDriver->GetPresContext();
180
0
      nsPresContext* rootContext = pc ? pc->GetRootPresContext() : nullptr;
181
0
      // During PresContext shutdown, we can't accurately detect
182
0
      // if a root refresh driver exists or not. Therefore, we have to
183
0
      // search and find out which list this driver exists in.
184
0
      if (!rootContext) {
185
0
        if (mRootRefreshDrivers.Contains(aDriver)) {
186
0
          mRootRefreshDrivers.RemoveElement(aDriver);
187
0
        } else {
188
0
          NS_ASSERTION(mContentRefreshDrivers.Contains(aDriver),
189
0
                       "RemoveRefreshDriver without a display root for a driver that is not in the content refresh list");
190
0
          mContentRefreshDrivers.RemoveElement(aDriver);
191
0
        }
192
0
      } else {
193
0
        NS_ASSERTION(mContentRefreshDrivers.Contains(aDriver), "RemoveRefreshDriver for a driver that is not in the content refresh list");
194
0
        mContentRefreshDrivers.RemoveElement(aDriver);
195
0
      }
196
0
    }
197
0
198
0
    bool stopTimer = mContentRefreshDrivers.IsEmpty() && mRootRefreshDrivers.IsEmpty();
199
0
    if (stopTimer) {
200
0
      StopTimer();
201
0
    }
202
0
  }
203
204
0
  TimeStamp MostRecentRefresh() const { return mLastFireTime; }
205
206
  void SwapRefreshDrivers(RefreshDriverTimer* aNewTimer)
207
0
  {
208
0
    MOZ_ASSERT(NS_IsMainThread());
209
0
210
0
    for (nsRefreshDriver* driver : mContentRefreshDrivers) {
211
0
      aNewTimer->AddRefreshDriver(driver);
212
0
      driver->mActiveTimer = aNewTimer;
213
0
    }
214
0
    mContentRefreshDrivers.Clear();
215
0
216
0
    for (nsRefreshDriver* driver : mRootRefreshDrivers) {
217
0
      aNewTimer->AddRefreshDriver(driver);
218
0
      driver->mActiveTimer = aNewTimer;
219
0
    }
220
0
    mRootRefreshDrivers.Clear();
221
0
222
0
    aNewTimer->mLastFireTime = mLastFireTime;
223
0
224
0
    StopTimer();
225
0
  }
226
227
  virtual TimeDuration GetTimerRate() = 0;
228
229
  TimeStamp GetIdleDeadlineHint(TimeStamp aDefault)
230
0
  {
231
0
    MOZ_ASSERT(NS_IsMainThread());
232
0
233
0
    TimeStamp mostRecentRefresh = MostRecentRefresh();
234
0
    TimeDuration refreshRate = GetTimerRate();
235
0
    TimeStamp idleEnd = mostRecentRefresh + refreshRate;
236
0
237
0
    if (idleEnd +
238
0
          refreshRate * nsLayoutUtils::QuiescentFramesBeforeIdlePeriod() <
239
0
        TimeStamp::Now()) {
240
0
      return aDefault;
241
0
    }
242
0
243
0
    idleEnd = idleEnd - TimeDuration::FromMilliseconds(
244
0
      nsLayoutUtils::IdlePeriodDeadlineLimit());
245
0
    return idleEnd < aDefault ? idleEnd : aDefault;
246
0
  }
247
248
  Maybe<TimeStamp> GetNextTickHint()
249
0
  {
250
0
    MOZ_ASSERT(NS_IsMainThread());
251
0
    TimeStamp nextTick = MostRecentRefresh() + GetTimerRate();
252
0
    return nextTick < TimeStamp::Now() ? Nothing() : Some(nextTick);
253
0
  }
254
255
protected:
256
  virtual ~RefreshDriverTimer()
257
0
  {
258
0
    MOZ_ASSERT(mContentRefreshDrivers.Length() == 0, "Should have removed all content refresh drivers from here by now!");
259
0
    MOZ_ASSERT(mRootRefreshDrivers.Length() == 0, "Should have removed all root refresh drivers from here by now!");
260
0
  }
261
262
  virtual void StartTimer() = 0;
263
  virtual void StopTimer() = 0;
264
  virtual void ScheduleNextTick(TimeStamp aNowTime) = 0;
265
266
  bool IsRootRefreshDriver(nsRefreshDriver* aDriver)
267
0
  {
268
0
    nsPresContext* pc = aDriver->GetPresContext();
269
0
    nsPresContext* rootContext = pc ? pc->GetRootPresContext() : nullptr;
270
0
    if (!rootContext) {
271
0
      return false;
272
0
    }
273
0
274
0
    return aDriver == rootContext->RefreshDriver();
275
0
  }
276
277
  /*
278
   * Actually runs a tick, poking all the attached RefreshDrivers.
279
   * Grabs the "now" time via TimeStamp::Now().
280
   */
281
  void Tick()
282
0
  {
283
0
    TimeStamp now = TimeStamp::Now();
284
0
    Tick(now);
285
0
  }
286
287
  void TickRefreshDrivers(TimeStamp aNow, nsTArray<RefPtr<nsRefreshDriver>>& aDrivers)
288
0
  {
289
0
    if (aDrivers.IsEmpty()) {
290
0
      return;
291
0
    }
292
0
293
0
    nsTArray<RefPtr<nsRefreshDriver>> drivers(aDrivers);
294
0
    for (nsRefreshDriver* driver : drivers) {
295
0
      // don't poke this driver if it's in test mode
296
0
      if (driver->IsTestControllingRefreshesEnabled()) {
297
0
        continue;
298
0
      }
299
0
300
0
      TickDriver(driver, aNow);
301
0
    }
302
0
  }
303
304
  /*
305
   * Tick the refresh drivers based on the given timestamp.
306
   */
307
  void Tick(TimeStamp now)
308
0
  {
309
0
    ScheduleNextTick(now);
310
0
311
0
    mLastFireTime = now;
312
0
313
0
    LOG("[%p] ticking drivers...", this);
314
0
    // RD is short for RefreshDriver
315
0
    AUTO_PROFILER_TRACING("Paint", "RefreshDriverTick");
316
0
317
0
    TickRefreshDrivers(now, mContentRefreshDrivers);
318
0
    TickRefreshDrivers(now, mRootRefreshDrivers);
319
0
320
0
    LOG("[%p] done.", this);
321
0
  }
322
323
  static void TickDriver(nsRefreshDriver* driver, TimeStamp now)
324
0
  {
325
0
    driver->Tick(now);
326
0
  }
327
328
  TimeStamp mLastFireTime;
329
  TimeStamp mTargetTime;
330
331
  nsTArray<RefPtr<nsRefreshDriver>> mContentRefreshDrivers;
332
  nsTArray<RefPtr<nsRefreshDriver>> mRootRefreshDrivers;
333
334
  // useful callback for nsITimer-based derived classes, here
335
  // because of c++ protected shenanigans
336
  static void TimerTick(nsITimer* aTimer, void* aClosure)
337
0
  {
338
0
    RefPtr<RefreshDriverTimer> timer =
339
0
      static_cast<RefreshDriverTimer*>(aClosure);
340
0
    timer->Tick();
341
0
  }
342
};
343
344
/*
345
 * A RefreshDriverTimer that uses a nsITimer as the underlying timer.  Note that
346
 * this is a ONE_SHOT timer, not a repeating one!  Subclasses are expected to
347
 * implement ScheduleNextTick and intelligently calculate the next time to tick,
348
 * and to reset mTimer.  Using a repeating nsITimer gets us into a lot of pain
349
 * with its attempt at intelligent slack removal and such, so we don't do it.
350
 */
351
class SimpleTimerBasedRefreshDriverTimer :
352
    public RefreshDriverTimer
353
{
354
public:
355
  /*
356
   * aRate -- the delay, in milliseconds, requested between timer firings
357
   */
358
  explicit SimpleTimerBasedRefreshDriverTimer(double aRate)
359
0
  {
360
0
    SetRate(aRate);
361
0
    mTimer = NS_NewTimer();
362
0
  }
363
364
  virtual ~SimpleTimerBasedRefreshDriverTimer() override
365
0
  {
366
0
    StopTimer();
367
0
  }
368
369
  // will take effect at next timer tick
370
  virtual void SetRate(double aNewRate)
371
0
  {
372
0
    mRateMilliseconds = aNewRate;
373
0
    mRateDuration = TimeDuration::FromMilliseconds(mRateMilliseconds);
374
0
  }
375
376
  double GetRate() const
377
0
  {
378
0
    return mRateMilliseconds;
379
0
  }
380
381
  TimeDuration GetTimerRate() override
382
0
  {
383
0
    return mRateDuration;
384
0
  }
385
386
protected:
387
388
  void StartTimer() override
389
0
  {
390
0
    // pretend we just fired, and we schedule the next tick normally
391
0
    mLastFireTime = TimeStamp::Now();
392
0
393
0
    mTargetTime = mLastFireTime + mRateDuration;
394
0
395
0
    uint32_t delay = static_cast<uint32_t>(mRateMilliseconds);
396
0
    mTimer->InitWithNamedFuncCallback(
397
0
      TimerTick,
398
0
      this,
399
0
      delay,
400
0
      nsITimer::TYPE_ONE_SHOT,
401
0
      "SimpleTimerBasedRefreshDriverTimer::StartTimer");
402
0
  }
403
404
  void StopTimer() override
405
0
  {
406
0
    mTimer->Cancel();
407
0
  }
408
409
  double mRateMilliseconds;
410
  TimeDuration mRateDuration;
411
  RefPtr<nsITimer> mTimer;
412
};
413
414
/*
415
 * A refresh driver that listens to vsync events and ticks the refresh driver
416
 * on vsync intervals. We throttle the refresh driver if we get too many
417
 * vsync events and wait to catch up again.
418
 */
419
class VsyncRefreshDriverTimer : public RefreshDriverTimer
420
{
421
public:
422
  VsyncRefreshDriverTimer()
423
    : mVsyncChild(nullptr)
424
0
  {
425
0
    MOZ_ASSERT(XRE_IsParentProcess());
426
0
    MOZ_ASSERT(NS_IsMainThread());
427
0
    mVsyncObserver = new RefreshDriverVsyncObserver(this);
428
0
    RefPtr<mozilla::gfx::VsyncSource> vsyncSource = gfxPlatform::GetPlatform()->GetHardwareVsync();
429
0
    MOZ_ALWAYS_TRUE(mVsyncDispatcher = vsyncSource->GetRefreshTimerVsyncDispatcher());
430
0
    mVsyncDispatcher->SetParentRefreshTimer(mVsyncObserver);
431
0
    mVsyncRate = vsyncSource->GetGlobalDisplay().GetVsyncRate();
432
0
  }
433
434
  explicit VsyncRefreshDriverTimer(VsyncChild* aVsyncChild)
435
    : mVsyncChild(aVsyncChild)
436
0
  {
437
0
    MOZ_ASSERT(!XRE_IsParentProcess());
438
0
    MOZ_ASSERT(NS_IsMainThread());
439
0
    MOZ_ASSERT(mVsyncChild);
440
0
    mVsyncObserver = new RefreshDriverVsyncObserver(this);
441
0
    mVsyncChild->SetVsyncObserver(mVsyncObserver);
442
0
    mVsyncRate = mVsyncChild->GetVsyncRate();
443
0
  }
444
445
  TimeDuration GetTimerRate() override
446
0
  {
447
0
    if (mVsyncRate != TimeDuration::Forever()) {
448
0
      return mVsyncRate;
449
0
    }
450
0
451
0
    if (mVsyncChild) {
452
0
      // VsyncChild::VsyncRate() is a simple getter for the cached
453
0
      // hardware vsync rate. We depend on that
454
0
      // VsyncChild::GetVsyncRate() being called in the constructor
455
0
      // will result in a response with the actual vsync rate sooner
456
0
      // or later. Until that happens VsyncChild::VsyncRate() returns
457
0
      // TimeDuration::Forever() and we have to guess below.
458
0
      mVsyncRate = mVsyncChild->VsyncRate();
459
0
    }
460
0
461
0
    // If hardware queries fail / are unsupported, we have to just guess.
462
0
    return mVsyncRate != TimeDuration::Forever()
463
0
             ? mVsyncRate
464
0
             : TimeDuration::FromMilliseconds(1000.0 / 60.0);
465
0
  }
466
467
private:
468
  // Since VsyncObservers are refCounted, but the RefreshDriverTimer are
469
  // explicitly shutdown. We create an inner class that has the VsyncObserver
470
  // and is shutdown when the RefreshDriverTimer is deleted.
471
  class RefreshDriverVsyncObserver final : public VsyncObserver
472
  {
473
  public:
474
    explicit RefreshDriverVsyncObserver(VsyncRefreshDriverTimer* aVsyncRefreshDriverTimer)
475
      : mVsyncRefreshDriverTimer(aVsyncRefreshDriverTimer)
476
      , mRefreshTickLock("RefreshTickLock")
477
      , mRecentVsync(TimeStamp::Now())
478
      , mLastChildTick(TimeStamp::Now())
479
      , mVsyncRate(TimeDuration::Forever())
480
      , mProcessedVsync(true)
481
0
    {
482
0
      MOZ_ASSERT(NS_IsMainThread());
483
0
    }
484
485
    class ParentProcessVsyncNotifier final: public Runnable,
486
                                            public nsIRunnablePriority
487
    {
488
    public:
489
      ParentProcessVsyncNotifier(RefreshDriverVsyncObserver* aObserver,
490
                                 TimeStamp aVsyncTimestamp)
491
        : Runnable("VsyncRefreshDriverTimer::RefreshDriverVsyncObserver::"
492
                   "ParentProcessVsyncNotifier")
493
        , mObserver(aObserver)
494
        , mVsyncTimestamp(aVsyncTimestamp)
495
0
      {
496
0
      }
497
498
      NS_DECL_ISUPPORTS_INHERITED
499
500
      NS_IMETHOD Run() override
501
0
      {
502
0
        MOZ_ASSERT(NS_IsMainThread());
503
0
        static bool sCacheInitialized = false;
504
0
        static bool sHighPriorityPrefValue = false;
505
0
        if (!sCacheInitialized) {
506
0
          sCacheInitialized = true;
507
0
          Preferences::AddBoolVarCache(&sHighPriorityPrefValue,
508
0
                                       "vsync.parentProcess.highPriority",
509
0
                                       mozilla::BrowserTabsRemoteAutostart());
510
0
        }
511
0
        sHighPriorityEnabled = sHighPriorityPrefValue;
512
0
513
0
        mObserver->TickRefreshDriver(mVsyncTimestamp);
514
0
        return NS_OK;
515
0
      }
516
517
      NS_IMETHOD GetPriority(uint32_t* aPriority) override
518
0
      {
519
0
        *aPriority =
520
0
          sHighPriorityEnabled ? nsIRunnablePriority::PRIORITY_HIGH :
521
0
                                 nsIRunnablePriority::PRIORITY_NORMAL;
522
0
        return NS_OK;
523
0
      }
524
525
    private:
526
0
      ~ParentProcessVsyncNotifier() {}
527
      RefPtr<RefreshDriverVsyncObserver> mObserver;
528
      TimeStamp mVsyncTimestamp;
529
      static mozilla::Atomic<bool> sHighPriorityEnabled;
530
    };
531
532
    bool NotifyVsync(TimeStamp aVsyncTimestamp) override
533
0
    {
534
0
      // IMPORTANT: All paths through this method MUST hold a strong ref on
535
0
      // |this| for the duration of the TickRefreshDriver callback.
536
0
537
0
      if (!NS_IsMainThread()) {
538
0
        MOZ_ASSERT(XRE_IsParentProcess());
539
0
        // Compress vsync notifications such that only 1 may run at a time
540
0
        // This is so that we don't flood the refresh driver with vsync messages
541
0
        // if the main thread is blocked for long periods of time
542
0
        { // scope lock
543
0
          MonitorAutoLock lock(mRefreshTickLock);
544
0
          mRecentVsync = aVsyncTimestamp;
545
0
          if (!mProcessedVsync) {
546
0
            return true;
547
0
          }
548
0
          mProcessedVsync = false;
549
0
        }
550
0
551
0
        nsCOMPtr<nsIRunnable> vsyncEvent =
552
0
          new ParentProcessVsyncNotifier(this, aVsyncTimestamp);
553
0
        NS_DispatchToMainThread(vsyncEvent);
554
0
      } else {
555
0
        mRecentVsync = aVsyncTimestamp;
556
0
        if (!mBlockUntil.IsNull() && mBlockUntil > aVsyncTimestamp) {
557
0
          if (mProcessedVsync) {
558
0
            // Re-post vsync update as a normal priority runnable. This way
559
0
            // runnables already in normal priority queue get processed.
560
0
            mProcessedVsync = false;
561
0
            nsCOMPtr<nsIRunnable> vsyncEvent =
562
0
              NewRunnableMethod<>(
563
0
                "RefreshDriverVsyncObserver::NormalPriorityNotify",
564
0
                this, &RefreshDriverVsyncObserver::NormalPriorityNotify);
565
0
            NS_DispatchToMainThread(vsyncEvent);
566
0
          }
567
0
568
0
          return true;
569
0
        }
570
0
571
0
        RefPtr<RefreshDriverVsyncObserver> kungFuDeathGrip(this);
572
0
        TickRefreshDriver(aVsyncTimestamp);
573
0
      }
574
0
575
0
      return true;
576
0
    }
577
578
    void Shutdown()
579
0
    {
580
0
      MOZ_ASSERT(NS_IsMainThread());
581
0
      mVsyncRefreshDriverTimer = nullptr;
582
0
    }
583
584
    void OnTimerStart()
585
0
    {
586
0
      if (!XRE_IsParentProcess()) {
587
0
        mLastChildTick = TimeStamp::Now();
588
0
      }
589
0
    }
590
591
    void NormalPriorityNotify()
592
0
    {
593
0
      if (mLastProcessedTickInChildProcess.IsNull() ||
594
0
          mRecentVsync > mLastProcessedTickInChildProcess) {
595
0
        // mBlockUntil is for high priority vsync notifications only.
596
0
        mBlockUntil = TimeStamp();
597
0
        TickRefreshDriver(mRecentVsync);
598
0
      }
599
0
600
0
      mProcessedVsync = true;
601
0
    }
602
603
  private:
604
0
    ~RefreshDriverVsyncObserver() = default;
605
606
    void RecordTelemetryProbes(TimeStamp aVsyncTimestamp)
607
0
    {
608
0
      MOZ_ASSERT(NS_IsMainThread());
609
0
    #ifndef ANDROID  /* bug 1142079 */
610
0
      if (XRE_IsParentProcess()) {
611
0
        TimeDuration vsyncLatency = TimeStamp::Now() - aVsyncTimestamp;
612
0
        uint32_t sample = (uint32_t)vsyncLatency.ToMilliseconds();
613
0
        Telemetry::Accumulate(Telemetry::FX_REFRESH_DRIVER_CHROME_FRAME_DELAY_MS,
614
0
                              sample);
615
0
        Telemetry::Accumulate(Telemetry::FX_REFRESH_DRIVER_SYNC_SCROLL_FRAME_DELAY_MS,
616
0
                              sample);
617
0
        RecordJank(sample);
618
0
      } else if (mVsyncRate != TimeDuration::Forever()) {
619
0
        TimeDuration contentDelay = (TimeStamp::Now() - mLastChildTick) - mVsyncRate;
620
0
        if (contentDelay.ToMilliseconds() < 0 ){
621
0
          // Vsyncs are noisy and some can come at a rate quicker than
622
0
          // the reported hardware rate. In those cases, consider that we have 0 delay.
623
0
          contentDelay = TimeDuration::FromMilliseconds(0);
624
0
        }
625
0
        uint32_t sample = (uint32_t)contentDelay.ToMilliseconds();
626
0
        Telemetry::Accumulate(Telemetry::FX_REFRESH_DRIVER_CONTENT_FRAME_DELAY_MS,
627
0
                              sample);
628
0
        Telemetry::Accumulate(Telemetry::FX_REFRESH_DRIVER_SYNC_SCROLL_FRAME_DELAY_MS,
629
0
                              sample);
630
0
        RecordJank(sample);
631
0
      } else {
632
0
        // Request the vsync rate from the parent process. Might be a few vsyncs
633
0
        // until the parent responds.
634
0
        if (mVsyncRefreshDriverTimer) {
635
0
          mVsyncRate = mVsyncRefreshDriverTimer->mVsyncChild->GetVsyncRate();
636
0
        }
637
0
      }
638
0
    #endif
639
0
    }
640
641
    void RecordJank(uint32_t aJankMS)
642
0
    {
643
0
      uint32_t duration = 1 /* ms */;
644
0
      for (size_t i = 0;
645
0
           i < mozilla::ArrayLength(sJankLevels) && duration < aJankMS;
646
0
           ++i, duration *= 2) {
647
0
        sJankLevels[i]++;
648
0
      }
649
0
    }
650
651
    void TickRefreshDriver(TimeStamp aVsyncTimestamp)
652
0
    {
653
0
      MOZ_ASSERT(NS_IsMainThread());
654
0
655
0
      RecordTelemetryProbes(aVsyncTimestamp);
656
0
      if (XRE_IsParentProcess()) {
657
0
        MonitorAutoLock lock(mRefreshTickLock);
658
0
        aVsyncTimestamp = mRecentVsync;
659
0
        mProcessedVsync = true;
660
0
      } else {
661
0
662
0
        mLastChildTick = TimeStamp::Now();
663
0
        mLastProcessedTickInChildProcess = aVsyncTimestamp;
664
0
      }
665
0
      MOZ_ASSERT(aVsyncTimestamp <= TimeStamp::Now());
666
0
667
0
      // We might have a problem that we call ~VsyncRefreshDriverTimer() before
668
0
      // the scheduled TickRefreshDriver() runs. Check mVsyncRefreshDriverTimer
669
0
      // before use.
670
0
      if (mVsyncRefreshDriverTimer) {
671
0
        RefPtr<VsyncRefreshDriverTimer> timer = mVsyncRefreshDriverTimer;
672
0
        timer->RunRefreshDrivers(aVsyncTimestamp);
673
0
        // Note: mVsyncRefreshDriverTimer might be null now.
674
0
      }
675
0
676
0
      if (!XRE_IsParentProcess()) {
677
0
        TimeDuration tickDuration = TimeStamp::Now() - mLastChildTick;
678
0
        mBlockUntil = aVsyncTimestamp + tickDuration;
679
0
      }
680
0
    }
681
682
    // VsyncRefreshDriverTimer holds this RefreshDriverVsyncObserver and it will
683
    // be always available before Shutdown(). We can just use the raw pointer
684
    // here.
685
    VsyncRefreshDriverTimer* mVsyncRefreshDriverTimer;
686
    Monitor mRefreshTickLock;
687
    TimeStamp mRecentVsync;
688
    TimeStamp mLastChildTick;
689
    TimeStamp mLastProcessedTickInChildProcess;
690
    TimeStamp mBlockUntil;
691
    TimeDuration mVsyncRate;
692
    bool mProcessedVsync;
693
  }; // RefreshDriverVsyncObserver
694
695
  ~VsyncRefreshDriverTimer() override
696
0
  {
697
0
    if (XRE_IsParentProcess()) {
698
0
      mVsyncDispatcher->SetParentRefreshTimer(nullptr);
699
0
      mVsyncDispatcher = nullptr;
700
0
    } else {
701
0
      // Since the PVsyncChild actors live through the life of the process, just
702
0
      // send the unobserveVsync message to disable vsync event. We don't need
703
0
      // to handle the cleanup stuff of this actor. PVsyncChild::ActorDestroy()
704
0
      // will be called and clean up this actor.
705
0
      Unused << mVsyncChild->SendUnobserve();
706
0
      mVsyncChild->SetVsyncObserver(nullptr);
707
0
      mVsyncChild = nullptr;
708
0
    }
709
0
710
0
    // Detach current vsync timer from this VsyncObserver. The observer will no
711
0
    // longer tick this timer.
712
0
    mVsyncObserver->Shutdown();
713
0
    mVsyncObserver = nullptr;
714
0
  }
715
716
  void StartTimer() override
717
0
  {
718
0
    // Protect updates to `sActiveVsyncTimers`.
719
0
    MOZ_ASSERT(NS_IsMainThread());
720
0
721
0
    mLastFireTime = TimeStamp::Now();
722
0
723
0
    if (XRE_IsParentProcess()) {
724
0
      mVsyncDispatcher->SetParentRefreshTimer(mVsyncObserver);
725
0
    } else {
726
0
      Unused << mVsyncChild->SendObserve();
727
0
      mVsyncObserver->OnTimerStart();
728
0
    }
729
0
730
0
    ++sActiveVsyncTimers;
731
0
  }
732
733
  void StopTimer() override
734
0
  {
735
0
    // Protect updates to `sActiveVsyncTimers`.
736
0
    MOZ_ASSERT(NS_IsMainThread());
737
0
738
0
    if (XRE_IsParentProcess()) {
739
0
      mVsyncDispatcher->SetParentRefreshTimer(nullptr);
740
0
    } else {
741
0
      Unused << mVsyncChild->SendUnobserve();
742
0
    }
743
0
744
0
    MOZ_ASSERT(sActiveVsyncTimers > 0);
745
0
    --sActiveVsyncTimers;
746
0
  }
747
748
  void ScheduleNextTick(TimeStamp aNowTime) override
749
0
  {
750
0
    // Do nothing since we just wait for the next vsync from
751
0
    // RefreshDriverVsyncObserver.
752
0
  }
753
754
  void RunRefreshDrivers(TimeStamp aTimeStamp)
755
0
  {
756
0
    Tick(aTimeStamp);
757
0
  }
758
759
  RefPtr<RefreshDriverVsyncObserver> mVsyncObserver;
760
  // Used for parent process.
761
  RefPtr<RefreshTimerVsyncDispatcher> mVsyncDispatcher;
762
  // Used for child process.
763
  // The mVsyncChild will be always available before VsncChild::ActorDestroy().
764
  // After ActorDestroy(), StartTimer() and StopTimer() calls will be non-op.
765
  RefPtr<VsyncChild> mVsyncChild;
766
  TimeDuration mVsyncRate;
767
}; // VsyncRefreshDriverTimer
768
769
NS_IMPL_ISUPPORTS_INHERITED(VsyncRefreshDriverTimer::
770
                            RefreshDriverVsyncObserver::
771
                            ParentProcessVsyncNotifier,
772
                            Runnable, nsIRunnablePriority)
773
774
mozilla::Atomic<bool>
775
VsyncRefreshDriverTimer::
776
RefreshDriverVsyncObserver::
777
ParentProcessVsyncNotifier::sHighPriorityEnabled(false);
778
779
/**
780
 * Since the content process takes some time to setup
781
 * the vsync IPC connection, this timer is used
782
 * during the intial startup process.
783
 * During initial startup, the refresh drivers
784
 * are ticked off this timer, and are swapped out once content
785
 * vsync IPC connection is established.
786
 */
787
class StartupRefreshDriverTimer :
788
    public SimpleTimerBasedRefreshDriverTimer
789
{
790
public:
791
  explicit StartupRefreshDriverTimer(double aRate)
792
    : SimpleTimerBasedRefreshDriverTimer(aRate)
793
0
  {
794
0
  }
795
796
protected:
797
  void ScheduleNextTick(TimeStamp aNowTime) override
798
0
  {
799
0
    // Since this is only used for startup, it isn't super critical
800
0
    // that we tick at consistent intervals.
801
0
    TimeStamp newTarget = aNowTime + mRateDuration;
802
0
    uint32_t delay = static_cast<uint32_t>((newTarget - aNowTime).ToMilliseconds());
803
0
    mTimer->InitWithNamedFuncCallback(
804
0
      TimerTick,
805
0
      this,
806
0
      delay,
807
0
      nsITimer::TYPE_ONE_SHOT,
808
0
      "StartupRefreshDriverTimer::ScheduleNextTick");
809
0
    mTargetTime = newTarget;
810
0
  }
811
};
812
813
/*
814
 * A RefreshDriverTimer for inactive documents.  When a new refresh driver is
815
 * added, the rate is reset to the base (normally 1s/1fps).  Every time
816
 * it ticks, a single refresh driver is poked.  Once they have all been poked,
817
 * the duration between ticks doubles, up to mDisableAfterMilliseconds.  At that point,
818
 * the timer is quiet and doesn't tick (until something is added to it again).
819
 *
820
 * When a timer is removed, there is a possibility of another timer
821
 * being skipped for one cycle.  We could avoid this by adjusting
822
 * mNextDriverIndex in RemoveRefreshDriver, but there's little need to
823
 * add that complexity.  All we want is for inactive drivers to tick
824
 * at some point, but we don't care too much about how often.
825
 */
826
class InactiveRefreshDriverTimer final :
827
    public SimpleTimerBasedRefreshDriverTimer
828
{
829
public:
830
  explicit InactiveRefreshDriverTimer(double aRate)
831
    : SimpleTimerBasedRefreshDriverTimer(aRate),
832
      mNextTickDuration(aRate),
833
      mDisableAfterMilliseconds(-1.0),
834
      mNextDriverIndex(0)
835
0
  {
836
0
  }
837
838
  InactiveRefreshDriverTimer(double aRate, double aDisableAfterMilliseconds)
839
    : SimpleTimerBasedRefreshDriverTimer(aRate),
840
      mNextTickDuration(aRate),
841
      mDisableAfterMilliseconds(aDisableAfterMilliseconds),
842
      mNextDriverIndex(0)
843
0
  {
844
0
  }
845
846
  void AddRefreshDriver(nsRefreshDriver* aDriver) override
847
0
  {
848
0
    RefreshDriverTimer::AddRefreshDriver(aDriver);
849
0
850
0
    LOG("[%p] inactive timer got new refresh driver %p, resetting rate",
851
0
        this, aDriver);
852
0
853
0
    // reset the timer, and start with the newly added one next time.
854
0
    mNextTickDuration = mRateMilliseconds;
855
0
856
0
    // we don't really have to start with the newly added one, but we may as well
857
0
    // not tick the old ones at the fastest rate any more than we need to.
858
0
    mNextDriverIndex = GetRefreshDriverCount() - 1;
859
0
860
0
    StopTimer();
861
0
    StartTimer();
862
0
  }
863
864
  TimeDuration GetTimerRate() override
865
0
  {
866
0
    return TimeDuration::FromMilliseconds(mNextTickDuration);
867
0
  }
868
869
protected:
870
  uint32_t GetRefreshDriverCount()
871
0
  {
872
0
    return mContentRefreshDrivers.Length() + mRootRefreshDrivers.Length();
873
0
  }
874
875
  void StartTimer() override
876
0
  {
877
0
    mLastFireTime = TimeStamp::Now();
878
0
879
0
    mTargetTime = mLastFireTime + mRateDuration;
880
0
881
0
    uint32_t delay = static_cast<uint32_t>(mRateMilliseconds);
882
0
    mTimer->InitWithNamedFuncCallback(TimerTickOne,
883
0
                                      this,
884
0
                                      delay,
885
0
                                      nsITimer::TYPE_ONE_SHOT,
886
0
                                      "InactiveRefreshDriverTimer::StartTimer");
887
0
  }
888
889
  void StopTimer() override
890
0
  {
891
0
    mTimer->Cancel();
892
0
  }
893
894
  void ScheduleNextTick(TimeStamp aNowTime) override
895
0
  {
896
0
    if (mDisableAfterMilliseconds > 0.0 &&
897
0
        mNextTickDuration > mDisableAfterMilliseconds)
898
0
    {
899
0
      // We hit the time after which we should disable
900
0
      // inactive window refreshes; don't schedule anything
901
0
      // until we get kicked by an AddRefreshDriver call.
902
0
      return;
903
0
    }
904
0
905
0
    // double the next tick time if we've already gone through all of them once
906
0
    if (mNextDriverIndex >= GetRefreshDriverCount()) {
907
0
      mNextTickDuration *= 2.0;
908
0
      mNextDriverIndex = 0;
909
0
    }
910
0
911
0
    // this doesn't need to be precise; do a simple schedule
912
0
    uint32_t delay = static_cast<uint32_t>(mNextTickDuration);
913
0
    mTimer->InitWithNamedFuncCallback(
914
0
      TimerTickOne,
915
0
      this,
916
0
      delay,
917
0
      nsITimer::TYPE_ONE_SHOT,
918
0
      "InactiveRefreshDriverTimer::ScheduleNextTick");
919
0
920
0
    LOG("[%p] inactive timer next tick in %f ms [index %d/%d]", this, mNextTickDuration,
921
0
        mNextDriverIndex, GetRefreshDriverCount());
922
0
  }
923
924
  /* Runs just one driver's tick. */
925
  void TickOne()
926
0
  {
927
0
    TimeStamp now = TimeStamp::Now();
928
0
929
0
    ScheduleNextTick(now);
930
0
931
0
    mLastFireTime = now;
932
0
933
0
    nsTArray<RefPtr<nsRefreshDriver>> drivers(mContentRefreshDrivers);
934
0
    drivers.AppendElements(mRootRefreshDrivers);
935
0
    size_t index = mNextDriverIndex;
936
0
937
0
    if (index < drivers.Length() &&
938
0
        !drivers[index]->IsTestControllingRefreshesEnabled())
939
0
    {
940
0
      TickDriver(drivers[index], now);
941
0
    }
942
0
943
0
    mNextDriverIndex++;
944
0
  }
945
946
  static void TimerTickOne(nsITimer* aTimer, void* aClosure)
947
0
  {
948
0
    RefPtr<InactiveRefreshDriverTimer> timer =
949
0
      static_cast<InactiveRefreshDriverTimer*>(aClosure);
950
0
    timer->TickOne();
951
0
  }
952
953
  double mNextTickDuration;
954
  double mDisableAfterMilliseconds;
955
  uint32_t mNextDriverIndex;
956
};
957
958
} // namespace mozilla
959
960
static StaticRefPtr<RefreshDriverTimer> sRegularRateTimer;
961
static StaticRefPtr<InactiveRefreshDriverTimer> sThrottledRateTimer;
962
963
static void
964
CreateContentVsyncRefreshTimer(void*)
965
0
{
966
0
  MOZ_ASSERT(NS_IsMainThread());
967
0
  MOZ_ASSERT(!XRE_IsParentProcess());
968
0
969
0
  // Create the PVsync actor child for vsync-base refresh timer.
970
0
  // PBackgroundChild is created synchronously. We will still use software
971
0
  // timer before PVsync ready, and change to use hw timer when the connection
972
0
  // is done. Please check nsRefreshDriver::PVsyncActorCreated().
973
0
974
0
  PBackgroundChild* actorChild = BackgroundChild::GetOrCreateForCurrentThread();
975
0
  if (NS_WARN_IF(!actorChild)) {
976
0
    return;
977
0
  }
978
0
979
0
  layout::PVsyncChild* actor = actorChild->SendPVsyncConstructor();
980
0
  if (NS_WARN_IF(!actor)) {
981
0
    return;
982
0
  }
983
0
984
0
  layout::VsyncChild* child = static_cast<layout::VsyncChild*>(actor);
985
0
  nsRefreshDriver::PVsyncActorCreated(child);
986
0
}
987
988
static void
989
CreateVsyncRefreshTimer()
990
0
{
991
0
  MOZ_ASSERT(NS_IsMainThread());
992
0
993
0
  PodArrayZero(sJankLevels);
994
0
  // Sometimes, gfxPrefs is not initialized here. Make sure the gfxPrefs is
995
0
  // ready.
996
0
  gfxPrefs::GetSingleton();
997
0
998
0
  if (gfxPlatform::IsInLayoutAsapMode()) {
999
0
    return;
1000
0
  }
1001
0
1002
0
  if (XRE_IsParentProcess()) {
1003
0
    // Make sure all vsync systems are ready.
1004
0
    gfxPlatform::GetPlatform();
1005
0
    // In parent process, we don't need to use ipc. We can create the
1006
0
    // VsyncRefreshDriverTimer directly.
1007
0
    sRegularRateTimer = new VsyncRefreshDriverTimer();
1008
0
    return;
1009
0
  }
1010
0
1011
0
  // If this process is not created by NUWA, just create the vsync timer here.
1012
0
  CreateContentVsyncRefreshTimer(nullptr);
1013
0
}
1014
1015
static uint32_t
1016
GetFirstFrameDelay(imgIRequest* req)
1017
0
{
1018
0
  nsCOMPtr<imgIContainer> container;
1019
0
  if (NS_FAILED(req->GetImage(getter_AddRefs(container))) || !container) {
1020
0
    return 0;
1021
0
  }
1022
0
1023
0
  // If this image isn't animated, there isn't a first frame delay.
1024
0
  int32_t delay = container->GetFirstFrameDelay();
1025
0
  if (delay < 0)
1026
0
    return 0;
1027
0
1028
0
  return static_cast<uint32_t>(delay);
1029
0
}
1030
1031
/* static */ void
1032
nsRefreshDriver::Shutdown()
1033
0
{
1034
0
  // clean up our timers
1035
0
  sRegularRateTimer = nullptr;
1036
0
  sThrottledRateTimer = nullptr;
1037
0
}
1038
1039
/* static */ int32_t
1040
nsRefreshDriver::DefaultInterval()
1041
0
{
1042
0
  return NSToIntRound(1000.0 / gfxPlatform::GetDefaultFrameRate());
1043
0
}
1044
1045
// Compute the interval to use for the refresh driver timer, in milliseconds.
1046
// outIsDefault indicates that rate was not explicitly set by the user
1047
// so we might choose other, more appropriate rates (e.g. vsync, etc)
1048
// layout.frame_rate=0 indicates "ASAP mode".
1049
// In ASAP mode rendering is iterated as fast as possible (typically for stress testing).
1050
// A target rate of 10k is used internally instead of special-handling 0.
1051
// Backends which block on swap/present/etc should try to not block
1052
// when layout.frame_rate=0 - to comply with "ASAP" as much as possible.
1053
double
1054
nsRefreshDriver::GetRegularTimerInterval() const
1055
0
{
1056
0
  int32_t rate = Preferences::GetInt("layout.frame_rate", -1);
1057
0
  if (rate < 0) {
1058
0
    rate = gfxPlatform::GetDefaultFrameRate();
1059
0
  } else if (rate == 0) {
1060
0
    rate = 10000;
1061
0
  }
1062
0
1063
0
  return 1000.0 / rate;
1064
0
}
1065
1066
/* static */ double
1067
nsRefreshDriver::GetThrottledTimerInterval()
1068
0
{
1069
0
  int32_t rate = Preferences::GetInt("layout.throttled_frame_rate", -1);
1070
0
  if (rate <= 0) {
1071
0
    rate = DEFAULT_THROTTLED_FRAME_RATE;
1072
0
  }
1073
0
  return 1000.0 / rate;
1074
0
}
1075
1076
/* static */ mozilla::TimeDuration
1077
nsRefreshDriver::GetMinRecomputeVisibilityInterval()
1078
0
{
1079
0
  int32_t interval =
1080
0
    Preferences::GetInt("layout.visibility.min-recompute-interval-ms", -1);
1081
0
  if (interval <= 0) {
1082
0
    interval = DEFAULT_RECOMPUTE_VISIBILITY_INTERVAL_MS;
1083
0
  }
1084
0
  return TimeDuration::FromMilliseconds(interval);
1085
0
}
1086
1087
RefreshDriverTimer*
1088
nsRefreshDriver::ChooseTimer() const
1089
0
{
1090
0
  if (mThrottled) {
1091
0
    if (!sThrottledRateTimer)
1092
0
      sThrottledRateTimer = new InactiveRefreshDriverTimer(GetThrottledTimerInterval(),
1093
0
                                                           DEFAULT_INACTIVE_TIMER_DISABLE_SECONDS * 1000.0);
1094
0
    return sThrottledRateTimer;
1095
0
  }
1096
0
1097
0
  if (!sRegularRateTimer) {
1098
0
    double rate = GetRegularTimerInterval();
1099
0
1100
0
    // Try to use vsync-base refresh timer first for sRegularRateTimer.
1101
0
    CreateVsyncRefreshTimer();
1102
0
1103
0
    if (!sRegularRateTimer) {
1104
0
      sRegularRateTimer = new StartupRefreshDriverTimer(rate);
1105
0
    }
1106
0
  }
1107
0
  return sRegularRateTimer;
1108
0
}
1109
1110
nsRefreshDriver::nsRefreshDriver(nsPresContext* aPresContext)
1111
  : mActiveTimer(nullptr),
1112
    mPresContext(aPresContext),
1113
    mRootRefresh(nullptr),
1114
    mNextTransactionId{0},
1115
    mOutstandingTransactionId{0},
1116
    mCompletedTransaction{0},
1117
    mFreezeCount(0),
1118
    mThrottledFrameRequestInterval(TimeDuration::FromMilliseconds(
1119
                                     GetThrottledTimerInterval())),
1120
    mMinRecomputeVisibilityInterval(GetMinRecomputeVisibilityInterval()),
1121
    mThrottled(false),
1122
    mNeedToRecomputeVisibility(false),
1123
    mTestControllingRefreshes(false),
1124
    mViewManagerFlushIsPending(false),
1125
    mHasScheduleFlush(false),
1126
    mInRefresh(false),
1127
    mWaitingForTransaction(false),
1128
    mSkippedPaints(false),
1129
    mResizeSuppressed(false),
1130
    mNotifyDOMContentFlushed(false),
1131
    mWarningThreshold(REFRESH_WAIT_WARNING)
1132
0
{
1133
0
  MOZ_ASSERT(NS_IsMainThread());
1134
0
  MOZ_ASSERT(mPresContext,
1135
0
             "Need a pres context to tell us to call Disconnect() later "
1136
0
             "and decrement sRefreshDriverCount.");
1137
0
  mMostRecentRefresh = TimeStamp::Now();
1138
0
  mNextThrottledFrameRequestTick = mMostRecentRefresh;
1139
0
  mNextRecomputeVisibilityTick = mMostRecentRefresh;
1140
0
1141
0
  ++sRefreshDriverCount;
1142
0
}
1143
1144
nsRefreshDriver::~nsRefreshDriver()
1145
0
{
1146
0
  MOZ_ASSERT(NS_IsMainThread());
1147
0
  MOZ_ASSERT(ObserverCount() == mEarlyRunners.Length(),
1148
0
             "observers, except pending selection scrolls, "
1149
0
             "should have been unregistered");
1150
0
  MOZ_ASSERT(!mActiveTimer, "timer should be gone");
1151
0
  MOZ_ASSERT(!mPresContext,
1152
0
             "Should have called Disconnect() and decremented "
1153
0
             "sRefreshDriverCount!");
1154
0
1155
0
  if (mRootRefresh) {
1156
0
    mRootRefresh->RemoveRefreshObserver(this, FlushType::Style);
1157
0
    mRootRefresh = nullptr;
1158
0
  }
1159
0
}
1160
1161
// Method for testing.  See nsIDOMWindowUtils.advanceTimeAndRefresh
1162
// for description.
1163
void
1164
nsRefreshDriver::AdvanceTimeAndRefresh(int64_t aMilliseconds)
1165
0
{
1166
0
  // ensure that we're removed from our driver
1167
0
  StopTimer();
1168
0
1169
0
  if (!mTestControllingRefreshes) {
1170
0
    mMostRecentRefresh = TimeStamp::Now();
1171
0
1172
0
    mTestControllingRefreshes = true;
1173
0
    if (mWaitingForTransaction) {
1174
0
      // Disable any refresh driver throttling when entering test mode
1175
0
      mWaitingForTransaction = false;
1176
0
      mSkippedPaints = false;
1177
0
      mWarningThreshold = REFRESH_WAIT_WARNING;
1178
0
    }
1179
0
  }
1180
0
1181
0
  mMostRecentRefresh += TimeDuration::FromMilliseconds((double) aMilliseconds);
1182
0
1183
0
  mozilla::dom::AutoNoJSAPI nojsapi;
1184
0
  DoTick();
1185
0
}
1186
1187
void
1188
nsRefreshDriver::RestoreNormalRefresh()
1189
0
{
1190
0
  mTestControllingRefreshes = false;
1191
0
  EnsureTimerStarted(eAllowTimeToGoBackwards);
1192
0
  mCompletedTransaction = mOutstandingTransactionId = mNextTransactionId;
1193
0
}
1194
1195
TimeStamp
1196
nsRefreshDriver::MostRecentRefresh() const
1197
0
{
1198
0
  // In case of stylo traversal, we have already activated the refresh driver in
1199
0
  // RestyleManager::ProcessPendingRestyles().
1200
0
  if (!ServoStyleSet::IsInServoTraversal()) {
1201
0
    const_cast<nsRefreshDriver*>(this)->EnsureTimerStarted();
1202
0
  }
1203
0
1204
0
  return mMostRecentRefresh;
1205
0
}
1206
1207
bool
1208
nsRefreshDriver::AddRefreshObserver(nsARefreshObserver* aObserver,
1209
                                    FlushType aFlushType)
1210
0
{
1211
0
  ObserverArray& array = ArrayFor(aFlushType);
1212
0
  bool success = array.AppendElement(aObserver) != nullptr;
1213
0
  EnsureTimerStarted();
1214
0
  return success;
1215
0
}
1216
1217
bool
1218
nsRefreshDriver::RemoveRefreshObserver(nsARefreshObserver* aObserver,
1219
                                       FlushType aFlushType)
1220
0
{
1221
0
  ObserverArray& array = ArrayFor(aFlushType);
1222
0
  return array.RemoveElement(aObserver);
1223
0
}
1224
1225
bool
1226
nsRefreshDriver::AddTimerAdjustmentObserver(
1227
  nsATimerAdjustmentObserver *aObserver)
1228
0
{
1229
0
  MOZ_ASSERT(!mTimerAdjustmentObservers.Contains(aObserver));
1230
0
1231
0
  return mTimerAdjustmentObservers.AppendElement(aObserver) != nullptr;
1232
0
}
1233
1234
bool
1235
nsRefreshDriver::RemoveTimerAdjustmentObserver(
1236
  nsATimerAdjustmentObserver *aObserver)
1237
0
{
1238
0
  MOZ_ASSERT(mTimerAdjustmentObservers.Contains(aObserver));
1239
0
  return mTimerAdjustmentObservers.RemoveElement(aObserver);
1240
0
}
1241
1242
void
1243
nsRefreshDriver::PostScrollEvent(mozilla::Runnable* aScrollEvent)
1244
0
{
1245
0
  mScrollEvents.AppendElement(aScrollEvent);
1246
0
  EnsureTimerStarted();
1247
0
}
1248
1249
void
1250
nsRefreshDriver::DispatchScrollEvents()
1251
0
{
1252
0
  // Scroll events are one-shot, so after running them we can drop them.
1253
0
  // However, dispatching a scroll event can potentially cause more scroll
1254
0
  // events to be posted, so we move the initial set into a temporary array
1255
0
  // first. (Newly posted scroll events will be dispatched on the next tick.)
1256
0
  ScrollEventArray events;
1257
0
  events.SwapElements(mScrollEvents);
1258
0
  for (auto& event : events) {
1259
0
    event->Run();
1260
0
  }
1261
0
}
1262
1263
void
1264
nsRefreshDriver::AddPostRefreshObserver(nsAPostRefreshObserver* aObserver)
1265
0
{
1266
0
  mPostRefreshObservers.AppendElement(aObserver);
1267
0
}
1268
1269
void
1270
nsRefreshDriver::RemovePostRefreshObserver(nsAPostRefreshObserver* aObserver)
1271
0
{
1272
0
  mPostRefreshObservers.RemoveElement(aObserver);
1273
0
}
1274
1275
bool
1276
nsRefreshDriver::AddImageRequest(imgIRequest* aRequest)
1277
0
{
1278
0
  uint32_t delay = GetFirstFrameDelay(aRequest);
1279
0
  if (delay == 0) {
1280
0
    mRequests.PutEntry(aRequest);
1281
0
  } else {
1282
0
    ImageStartData* start = mStartTable.LookupForAdd(delay).OrInsert(
1283
0
      [] () { return new ImageStartData(); });
1284
0
    start->mEntries.PutEntry(aRequest);
1285
0
  }
1286
0
1287
0
  EnsureTimerStarted();
1288
0
1289
0
  return true;
1290
0
}
1291
1292
void
1293
nsRefreshDriver::RemoveImageRequest(imgIRequest* aRequest)
1294
0
{
1295
0
  // Try to remove from both places, just in case, because we can't tell
1296
0
  // whether RemoveEntry() succeeds.
1297
0
  mRequests.RemoveEntry(aRequest);
1298
0
  uint32_t delay = GetFirstFrameDelay(aRequest);
1299
0
  if (delay != 0) {
1300
0
    ImageStartData* start = mStartTable.Get(delay);
1301
0
    if (start) {
1302
0
      start->mEntries.RemoveEntry(aRequest);
1303
0
    }
1304
0
  }
1305
0
}
1306
1307
void
1308
nsRefreshDriver::NotifyDOMContentLoaded()
1309
0
{
1310
0
  // If the refresh driver is going to tick, we mark the timestamp after
1311
0
  // everything is flushed in the next tick. If it isn't, mark ourselves as
1312
0
  // flushed now.
1313
0
  if (!HasObservers()) {
1314
0
      GetPresContext()->NotifyDOMContentFlushed();
1315
0
  } else {
1316
0
    mNotifyDOMContentFlushed = true;
1317
0
  }
1318
0
}
1319
1320
void
1321
nsRefreshDriver::EnsureTimerStarted(EnsureTimerStartedFlags aFlags)
1322
0
{
1323
0
  // FIXME: Bug 1346065: We should also assert the case where we have
1324
0
  // STYLO_THREADS=1.
1325
0
  MOZ_ASSERT(!ServoStyleSet::IsInServoTraversal() || NS_IsMainThread(),
1326
0
             "EnsureTimerStarted should be called only when we are not "
1327
0
             "in servo traversal or on the main-thread");
1328
0
1329
0
  if (mTestControllingRefreshes)
1330
0
    return;
1331
0
1332
0
  // will it already fire, and no other changes needed?
1333
0
  if (mActiveTimer && !(aFlags & eForceAdjustTimer))
1334
0
    return;
1335
0
1336
0
  if (IsFrozen() || !mPresContext) {
1337
0
    // If we don't want to start it now, or we've been disconnected.
1338
0
    StopTimer();
1339
0
    return;
1340
0
  }
1341
0
1342
0
  if (mPresContext->Document()->IsBeingUsedAsImage()) {
1343
0
    // Image documents receive ticks from clients' refresh drivers.
1344
0
    // XXXdholbert Exclude SVG-in-opentype fonts from this optimization, until
1345
0
    // they receive refresh-driver ticks from their client docs (bug 1107252).
1346
0
    nsIURI* uri = mPresContext->Document()->GetDocumentURI();
1347
0
    if (!uri || !mozilla::dom::IsFontTableURI(uri)) {
1348
0
      MOZ_ASSERT(!mActiveTimer,
1349
0
                 "image doc refresh driver should never have its own timer");
1350
0
      return;
1351
0
    }
1352
0
  }
1353
0
1354
0
  // We got here because we're either adjusting the time *or* we're
1355
0
  // starting it for the first time.  Add to the right timer,
1356
0
  // prehaps removing it from a previously-set one.
1357
0
  RefreshDriverTimer *newTimer = ChooseTimer();
1358
0
  if (newTimer != mActiveTimer) {
1359
0
    if (mActiveTimer)
1360
0
      mActiveTimer->RemoveRefreshDriver(this);
1361
0
    mActiveTimer = newTimer;
1362
0
    mActiveTimer->AddRefreshDriver(this);
1363
0
  }
1364
0
1365
0
  // When switching from an inactive timer to an active timer, the root
1366
0
  // refresh driver is skipped due to being set to the content refresh
1367
0
  // driver's timestamp. In case of EnsureTimerStarted is called from
1368
0
  // ScheduleViewManagerFlush, we should avoid this behavior to flush
1369
0
  // a paint in the same tick on the root refresh driver.
1370
0
  if (aFlags & eNeverAdjustTimer) {
1371
0
    return;
1372
0
  }
1373
0
1374
0
  // Since the different timers are sampled at different rates, when switching
1375
0
  // timers, the most recent refresh of the new timer may be *before* the
1376
0
  // most recent refresh of the old timer. However, the refresh driver time
1377
0
  // should not go backwards so we clamp the most recent refresh time.
1378
0
  //
1379
0
  // The one exception to this is when we are restoring the refresh driver
1380
0
  // from test control in which case the time is expected to go backwards
1381
0
  // (see bug 1043078).
1382
0
  TimeStamp newMostRecentRefresh =
1383
0
    aFlags & eAllowTimeToGoBackwards
1384
0
    ? mActiveTimer->MostRecentRefresh()
1385
0
    : std::max(mActiveTimer->MostRecentRefresh(), mMostRecentRefresh);
1386
0
1387
0
  if (mMostRecentRefresh != newMostRecentRefresh) {
1388
0
    mMostRecentRefresh = newMostRecentRefresh;
1389
0
1390
0
    nsTObserverArray<nsATimerAdjustmentObserver*>::EndLimitedIterator
1391
0
      iter(mTimerAdjustmentObservers);
1392
0
    while (iter.HasMore()) {
1393
0
      nsATimerAdjustmentObserver* obs = iter.GetNext();
1394
0
      obs->NotifyTimerAdjusted(mMostRecentRefresh);
1395
0
    }
1396
0
  }
1397
0
}
1398
1399
void
1400
nsRefreshDriver::StopTimer()
1401
0
{
1402
0
  if (!mActiveTimer)
1403
0
    return;
1404
0
1405
0
  mActiveTimer->RemoveRefreshDriver(this);
1406
0
  mActiveTimer = nullptr;
1407
0
}
1408
1409
uint32_t
1410
nsRefreshDriver::ObserverCount() const
1411
0
{
1412
0
  uint32_t sum = 0;
1413
0
  for (const ObserverArray& array : mObservers) {
1414
0
    sum += array.Length();
1415
0
  }
1416
0
1417
0
  // Even while throttled, we need to process layout and style changes.  Style
1418
0
  // changes can trigger transitions which fire events when they complete, and
1419
0
  // layout changes can affect media queries on child documents, triggering
1420
0
  // style changes, etc.
1421
0
  sum += mAnimationEventFlushObservers.Length();
1422
0
  sum += mResizeEventFlushObservers.Length();
1423
0
  sum += mStyleFlushObservers.Length();
1424
0
  sum += mLayoutFlushObservers.Length();
1425
0
  sum += mPendingFullscreenEvents.Length();
1426
0
  sum += mFrameRequestCallbackDocs.Length();
1427
0
  sum += mThrottledFrameRequestCallbackDocs.Length();
1428
0
  sum += mViewManagerFlushIsPending;
1429
0
  sum += mEarlyRunners.Length();
1430
0
  sum += mTimerAdjustmentObservers.Length();
1431
0
  return sum;
1432
0
}
1433
1434
bool
1435
nsRefreshDriver::HasObservers() const
1436
0
{
1437
0
  for (const ObserverArray& array : mObservers) {
1438
0
    if (!array.IsEmpty()) {
1439
0
      return true;
1440
0
    }
1441
0
  }
1442
0
1443
0
  // We should NOT count mTimerAdjustmentObservers here since this method is
1444
0
  // used to determine whether or not to stop the timer or re-start it and timer
1445
0
  // adjustment observers should not influence timer starting or stopping.
1446
0
  return mViewManagerFlushIsPending ||
1447
0
         !mStyleFlushObservers.IsEmpty() ||
1448
0
         !mLayoutFlushObservers.IsEmpty() ||
1449
0
         !mAnimationEventFlushObservers.IsEmpty() ||
1450
0
         !mResizeEventFlushObservers.IsEmpty() ||
1451
0
         !mPendingFullscreenEvents.IsEmpty() ||
1452
0
         !mFrameRequestCallbackDocs.IsEmpty() ||
1453
0
         !mThrottledFrameRequestCallbackDocs.IsEmpty() ||
1454
0
         !mEarlyRunners.IsEmpty();
1455
0
}
1456
1457
bool
1458
nsRefreshDriver::HasImageRequests() const
1459
0
{
1460
0
  for (auto iter = mStartTable.ConstIter(); !iter.Done(); iter.Next()) {
1461
0
    if (!iter.UserData()->mEntries.IsEmpty()) {
1462
0
      return true;
1463
0
    }
1464
0
  }
1465
0
1466
0
  return !mRequests.IsEmpty();
1467
0
}
1468
1469
nsRefreshDriver::ObserverArray&
1470
nsRefreshDriver::ArrayFor(FlushType aFlushType)
1471
0
{
1472
0
  switch (aFlushType) {
1473
0
    case FlushType::Event:
1474
0
      return mObservers[0];
1475
0
    case FlushType::Style:
1476
0
      return mObservers[1];
1477
0
    case FlushType::Layout:
1478
0
      return mObservers[2];
1479
0
    case FlushType::Display:
1480
0
      return mObservers[3];
1481
0
    default:
1482
0
      MOZ_CRASH("We don't track refresh observers for this flush type");
1483
0
  }
1484
0
}
1485
1486
/*
1487
 * nsITimerCallback implementation
1488
 */
1489
1490
void
1491
nsRefreshDriver::DoTick()
1492
0
{
1493
0
  MOZ_ASSERT(!IsFrozen(), "Why are we notified while frozen?");
1494
0
  MOZ_ASSERT(mPresContext, "Why are we notified after disconnection?");
1495
0
  MOZ_ASSERT(!nsContentUtils::GetCurrentJSContext(),
1496
0
             "Shouldn't have a JSContext on the stack");
1497
0
1498
0
  if (mTestControllingRefreshes) {
1499
0
    Tick(mMostRecentRefresh);
1500
0
  } else {
1501
0
    Tick(TimeStamp::Now());
1502
0
  }
1503
0
}
1504
1505
struct DocumentFrameCallbacks {
1506
  explicit DocumentFrameCallbacks(nsIDocument* aDocument) :
1507
    mDocument(aDocument)
1508
0
  {}
1509
1510
  nsCOMPtr<nsIDocument> mDocument;
1511
  nsIDocument::FrameRequestCallbackList mCallbacks;
1512
};
1513
1514
static nsDocShell* GetDocShell(nsPresContext* aPresContext)
1515
0
{
1516
0
  return static_cast<nsDocShell*>(aPresContext->GetDocShell());
1517
0
}
1518
1519
static bool
1520
HasPendingAnimations(nsIPresShell* aShell)
1521
0
{
1522
0
  nsIDocument* doc = aShell->GetDocument();
1523
0
  if (!doc) {
1524
0
    return false;
1525
0
  }
1526
0
1527
0
  PendingAnimationTracker* tracker = doc->GetPendingAnimationTracker();
1528
0
  return tracker && tracker->HasPendingAnimations();
1529
0
}
1530
1531
/**
1532
 * Return a list of all the child docShells in a given root docShell that are
1533
 * visible and are recording markers for the profilingTimeline
1534
 */
1535
static void GetProfileTimelineSubDocShells(nsDocShell* aRootDocShell,
1536
                                           nsTArray<nsDocShell*>& aShells)
1537
0
{
1538
0
  if (!aRootDocShell) {
1539
0
    return;
1540
0
  }
1541
0
1542
0
  RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
1543
0
  if (!timelines || timelines->IsEmpty()) {
1544
0
    return;
1545
0
  }
1546
0
1547
0
  nsCOMPtr<nsISimpleEnumerator> enumerator;
1548
0
  nsresult rv = aRootDocShell->GetDocShellEnumerator(
1549
0
    nsIDocShellTreeItem::typeAll,
1550
0
    nsIDocShell::ENUMERATE_BACKWARDS,
1551
0
    getter_AddRefs(enumerator));
1552
0
1553
0
  if (NS_FAILED(rv)) {
1554
0
    return;
1555
0
  }
1556
0
1557
0
  nsCOMPtr<nsIDocShell> curItem;
1558
0
  bool hasMore = false;
1559
0
  while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore) {
1560
0
    nsCOMPtr<nsISupports> curSupports;
1561
0
    enumerator->GetNext(getter_AddRefs(curSupports));
1562
0
    curItem = do_QueryInterface(curSupports);
1563
0
1564
0
    if (!curItem || !curItem->GetRecordProfileTimelineMarkers()) {
1565
0
      continue;
1566
0
    }
1567
0
1568
0
    nsDocShell* shell = static_cast<nsDocShell*>(curItem.get());
1569
0
    bool isVisible = false;
1570
0
    shell->GetVisibility(&isVisible);
1571
0
    if (!isVisible) {
1572
0
      continue;
1573
0
    }
1574
0
1575
0
    aShells.AppendElement(shell);
1576
0
  }
1577
0
}
1578
1579
static void
1580
TakeFrameRequestCallbacksFrom(nsIDocument* aDocument,
1581
                              nsTArray<DocumentFrameCallbacks>& aTarget)
1582
0
{
1583
0
  aTarget.AppendElement(aDocument);
1584
0
  aDocument->TakeFrameRequestCallbacks(aTarget.LastElement().mCallbacks);
1585
0
}
1586
1587
// https://fullscreen.spec.whatwg.org/#run-the-fullscreen-steps
1588
void
1589
nsRefreshDriver::RunFullscreenSteps()
1590
0
{
1591
0
  // Swap out the current pending events
1592
0
  nsTArray<UniquePtr<PendingFullscreenEvent>>
1593
0
    pendings(std::move(mPendingFullscreenEvents));
1594
0
  for (UniquePtr<PendingFullscreenEvent>& event : pendings) {
1595
0
    event->Dispatch();
1596
0
  }
1597
0
}
1598
1599
void
1600
nsRefreshDriver::UpdateIntersectionObservations()
1601
0
{
1602
0
  AutoTArray<nsCOMPtr<nsIDocument>, 32> documents;
1603
0
1604
0
  if (mPresContext->Document()->HasIntersectionObservers()) {
1605
0
    documents.AppendElement(mPresContext->Document());
1606
0
  }
1607
0
1608
0
  mPresContext->Document()->CollectDescendantDocuments(
1609
0
    documents,
1610
0
    [](const nsIDocument* document) -> bool {
1611
0
      return document->HasIntersectionObservers();
1612
0
    });
1613
0
1614
0
  for (uint32_t i = 0; i < documents.Length(); ++i) {
1615
0
    nsIDocument* doc = documents[i];
1616
0
    doc->UpdateIntersectionObservations();
1617
0
    doc->ScheduleIntersectionObserverNotification();
1618
0
  }
1619
0
}
1620
1621
void
1622
nsRefreshDriver::DispatchAnimationEvents()
1623
0
{
1624
0
  if (!mPresContext) {
1625
0
    return;
1626
0
  }
1627
0
1628
0
  // Hold all AnimationEventDispatcher in mAnimationEventFlushObservers as
1629
0
  // a RefPtr<> array since each AnimationEventDispatcher might be destroyed
1630
0
  // during processing the previous dispatcher.
1631
0
  AutoTArray<RefPtr<AnimationEventDispatcher>, 16> dispatchers;
1632
0
  dispatchers.AppendElements(mAnimationEventFlushObservers);
1633
0
  mAnimationEventFlushObservers.Clear();
1634
0
1635
0
  for (auto& dispatcher : dispatchers) {
1636
0
    dispatcher->DispatchEvents();
1637
0
  }
1638
0
}
1639
1640
void
1641
nsRefreshDriver::RunFrameRequestCallbacks(TimeStamp aNowTime)
1642
0
{
1643
0
  // Grab all of our frame request callbacks up front.
1644
0
  nsTArray<DocumentFrameCallbacks>
1645
0
    frameRequestCallbacks(mFrameRequestCallbackDocs.Length() +
1646
0
                          mThrottledFrameRequestCallbackDocs.Length());
1647
0
1648
0
  // First, grab throttled frame request callbacks.
1649
0
  {
1650
0
    nsTArray<nsIDocument*> docsToRemove;
1651
0
1652
0
    // We always tick throttled frame requests if the entire refresh driver is
1653
0
    // throttled, because in that situation throttled frame requests tick at the
1654
0
    // same frequency as non-throttled frame requests.
1655
0
    bool tickThrottledFrameRequests = mThrottled;
1656
0
1657
0
    if (!tickThrottledFrameRequests &&
1658
0
        aNowTime >= mNextThrottledFrameRequestTick) {
1659
0
      mNextThrottledFrameRequestTick = aNowTime + mThrottledFrameRequestInterval;
1660
0
      tickThrottledFrameRequests = true;
1661
0
    }
1662
0
1663
0
    for (nsIDocument* doc : mThrottledFrameRequestCallbackDocs) {
1664
0
      if (tickThrottledFrameRequests) {
1665
0
        // We're ticking throttled documents, so grab this document's requests.
1666
0
        // We don't bother appending to docsToRemove because we're going to
1667
0
        // clear mThrottledFrameRequestCallbackDocs anyway.
1668
0
        TakeFrameRequestCallbacksFrom(doc, frameRequestCallbacks);
1669
0
      } else if (!doc->ShouldThrottleFrameRequests()) {
1670
0
        // This document is no longer throttled, so grab its requests even
1671
0
        // though we're not ticking throttled frame requests right now. If
1672
0
        // this is the first unthrottled document with frame requests, we'll
1673
0
        // enter high precision mode the next time the callback is scheduled.
1674
0
        TakeFrameRequestCallbacksFrom(doc, frameRequestCallbacks);
1675
0
        docsToRemove.AppendElement(doc);
1676
0
      }
1677
0
    }
1678
0
1679
0
    // Remove all the documents we're ticking from
1680
0
    // mThrottledFrameRequestCallbackDocs so they can be readded as needed.
1681
0
    if (tickThrottledFrameRequests) {
1682
0
      mThrottledFrameRequestCallbackDocs.Clear();
1683
0
    } else {
1684
0
      // XXX(seth): We're using this approach to avoid concurrent modification
1685
0
      // of mThrottledFrameRequestCallbackDocs. docsToRemove usually has either
1686
0
      // zero elements or a very small number, so this should be OK in practice.
1687
0
      for (nsIDocument* doc : docsToRemove) {
1688
0
        mThrottledFrameRequestCallbackDocs.RemoveElement(doc);
1689
0
      }
1690
0
    }
1691
0
  }
1692
0
1693
0
  // Now grab unthrottled frame request callbacks.
1694
0
  for (nsIDocument* doc : mFrameRequestCallbackDocs) {
1695
0
    TakeFrameRequestCallbacksFrom(doc, frameRequestCallbacks);
1696
0
  }
1697
0
1698
0
  // Reset mFrameRequestCallbackDocs so they can be readded as needed.
1699
0
  mFrameRequestCallbackDocs.Clear();
1700
0
1701
0
  if (!frameRequestCallbacks.IsEmpty()) {
1702
0
    AUTO_PROFILER_TRACING("Paint", "Scripts");
1703
0
    for (const DocumentFrameCallbacks& docCallbacks : frameRequestCallbacks) {
1704
0
      // XXXbz Bug 863140: GetInnerWindow can return the outer
1705
0
      // window in some cases.
1706
0
      nsPIDOMWindowInner* innerWindow =
1707
0
        docCallbacks.mDocument->GetInnerWindow();
1708
0
      DOMHighResTimeStamp timeStamp = 0;
1709
0
      if (innerWindow) {
1710
0
        mozilla::dom::Performance* perf = innerWindow->GetPerformance();
1711
0
        if (perf) {
1712
0
          timeStamp = perf->GetDOMTiming()->TimeStampToDOMHighRes(aNowTime);
1713
0
        }
1714
0
        // else window is partially torn down already
1715
0
      }
1716
0
      for (auto& callback : docCallbacks.mCallbacks) {
1717
0
        callback->Call(timeStamp);
1718
0
      }
1719
0
    }
1720
0
  }
1721
0
}
1722
1723
struct RunnableWithDelay
1724
{
1725
  nsCOMPtr<nsIRunnable> mRunnable;
1726
  uint32_t mDelay;
1727
};
1728
1729
static AutoTArray<RunnableWithDelay, 8>* sPendingIdleRunnables = nullptr;
1730
1731
void
1732
nsRefreshDriver::DispatchIdleRunnableAfterTick(nsIRunnable* aRunnable,
1733
                                               uint32_t aDelay)
1734
0
{
1735
0
  if (!sPendingIdleRunnables) {
1736
0
    sPendingIdleRunnables = new AutoTArray<RunnableWithDelay, 8>();
1737
0
  }
1738
0
1739
0
  RunnableWithDelay rwd = {aRunnable, aDelay};
1740
0
  sPendingIdleRunnables->AppendElement(rwd);
1741
0
}
1742
1743
void
1744
nsRefreshDriver::CancelIdleRunnable(nsIRunnable* aRunnable)
1745
164
{
1746
164
  if (!sPendingIdleRunnables) {
1747
164
    return;
1748
164
  }
1749
0
1750
0
  for (uint32_t i = 0; i < sPendingIdleRunnables->Length(); ++i) {
1751
0
    if ((*sPendingIdleRunnables)[i].mRunnable == aRunnable) {
1752
0
      sPendingIdleRunnables->RemoveElementAt(i);
1753
0
      break;
1754
0
    }
1755
0
  }
1756
0
1757
0
  if (sPendingIdleRunnables->IsEmpty()) {
1758
0
    delete sPendingIdleRunnables;
1759
0
    sPendingIdleRunnables = nullptr;
1760
0
  }
1761
0
}
1762
1763
void
1764
nsRefreshDriver::Tick(TimeStamp aNowTime)
1765
0
{
1766
0
  MOZ_ASSERT(!nsContentUtils::GetCurrentJSContext(),
1767
0
             "Shouldn't have a JSContext on the stack");
1768
0
1769
0
  if (nsNPAPIPluginInstance::InPluginCallUnsafeForReentry()) {
1770
0
    NS_ERROR("Refresh driver should not run during plugin call!");
1771
0
    // Try to survive this by just ignoring the refresh tick.
1772
0
    return;
1773
0
  }
1774
0
1775
0
  AUTO_PROFILER_LABEL("nsRefreshDriver::Tick", LAYOUT);
1776
0
1777
0
  // We're either frozen or we were disconnected (likely in the middle
1778
0
  // of a tick iteration).  Just do nothing here, since our
1779
0
  // prescontext went away.
1780
0
  if (IsFrozen() || !mPresContext) {
1781
0
    return;
1782
0
  }
1783
0
1784
0
  // We can have a race condition where the vsync timestamp
1785
0
  // is before the most recent refresh due to a forced refresh.
1786
0
  // The underlying assumption is that the refresh driver tick can only
1787
0
  // go forward in time, not backwards. To prevent the refresh
1788
0
  // driver from going back in time, just skip this tick and
1789
0
  // wait until the next tick.
1790
0
  if ((aNowTime <= mMostRecentRefresh) && !mTestControllingRefreshes) {
1791
0
    return;
1792
0
  }
1793
0
1794
0
  if (IsWaitingForPaint(aNowTime)) {
1795
0
    // We're currently suspended waiting for earlier Tick's to
1796
0
    // be completed (on the Compositor). Mark that we missed the paint
1797
0
    // and keep waiting.
1798
0
    return;
1799
0
  }
1800
0
1801
0
  TimeStamp previousRefresh = mMostRecentRefresh;
1802
0
  mMostRecentRefresh = aNowTime;
1803
0
1804
0
  if (mRootRefresh) {
1805
0
    mRootRefresh->RemoveRefreshObserver(this, FlushType::Style);
1806
0
    mRootRefresh = nullptr;
1807
0
  }
1808
0
  mSkippedPaints = false;
1809
0
  mWarningThreshold = 1;
1810
0
1811
0
  nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
1812
0
  if (!presShell ||
1813
0
      (!HasObservers() && !HasImageRequests() && mScrollEvents.IsEmpty())) {
1814
0
    // Things are being destroyed, or we no longer have any observers.
1815
0
    // We don't want to stop the timer when observers are initially
1816
0
    // removed, because sometimes observers can be added and removed
1817
0
    // often depending on what other things are going on and in that
1818
0
    // situation we don't want to thrash our timer.  So instead we
1819
0
    // wait until we get a Notify() call when we have no observers
1820
0
    // before stopping the timer.
1821
0
    StopTimer();
1822
0
    return;
1823
0
  }
1824
0
1825
0
  mResizeSuppressed = false;
1826
0
1827
0
  AutoRestore<bool> restoreInRefresh(mInRefresh);
1828
0
  mInRefresh = true;
1829
0
1830
0
  AutoRestore<TimeStamp> restoreTickStart(mTickStart);
1831
0
  mTickStart = TimeStamp::Now();
1832
0
1833
0
  gfxPlatform::GetPlatform()->SchedulePaintIfDeviceReset();
1834
0
1835
0
  // We want to process any pending APZ metrics ahead of their positions
1836
0
  // in the queue. This will prevent us from spending precious time
1837
0
  // painting a stale displayport.
1838
0
  if (gfxPrefs::APZPeekMessages()) {
1839
0
    nsLayoutUtils::UpdateDisplayPortMarginsFromPendingMessages();
1840
0
  }
1841
0
1842
0
  AutoTArray<nsCOMPtr<nsIRunnable>, 16> earlyRunners;
1843
0
  earlyRunners.SwapElements(mEarlyRunners);
1844
0
  for (auto& runner : earlyRunners) {
1845
0
    runner->Run();
1846
0
  }
1847
0
1848
0
  // Resize events should be fired before layout flushes or
1849
0
  // calling animation frame callbacks.
1850
0
  AutoTArray<nsIPresShell*, 16> observers;
1851
0
  observers.AppendElements(mResizeEventFlushObservers);
1852
0
  for (nsIPresShell* shell : Reversed(observers)) {
1853
0
    if (!mPresContext || !mPresContext->GetPresShell()) {
1854
0
      StopTimer();
1855
0
      return;
1856
0
    }
1857
0
    // Make sure to not process observers which might have been removed
1858
0
    // during previous iterations.
1859
0
    if (!mResizeEventFlushObservers.RemoveElement(shell)) {
1860
0
      continue;
1861
0
    }
1862
0
    shell->FireResizeEvent();
1863
0
  }
1864
0
1865
0
  /*
1866
0
   * The timer holds a reference to |this| while calling |Notify|.
1867
0
   * However, implementations of |WillRefresh| are permitted to destroy
1868
0
   * the pres context, which will cause our |mPresContext| to become
1869
0
   * null.  If this happens, we must stop notifying observers.
1870
0
   */
1871
0
  for (uint32_t i = 0; i < ArrayLength(mObservers); ++i) {
1872
0
    ObserverArray::EndLimitedIterator etor(mObservers[i]);
1873
0
    while (etor.HasMore()) {
1874
0
      RefPtr<nsARefreshObserver> obs = etor.GetNext();
1875
0
      obs->WillRefresh(aNowTime);
1876
0
1877
0
      if (!mPresContext || !mPresContext->GetPresShell()) {
1878
0
        StopTimer();
1879
0
        return;
1880
0
      }
1881
0
    }
1882
0
1883
0
    if (i == 1) {
1884
0
      // This is the FlushType::Style case.
1885
0
1886
0
      DispatchScrollEvents();
1887
0
      DispatchAnimationEvents();
1888
0
      RunFullscreenSteps();
1889
0
      RunFrameRequestCallbacks(aNowTime);
1890
0
1891
0
      if (mPresContext && mPresContext->GetPresShell()) {
1892
0
        AutoTArray<nsIPresShell*, 16> observers;
1893
0
        observers.AppendElements(mStyleFlushObservers);
1894
0
        for (uint32_t j = observers.Length();
1895
0
             j && mPresContext && mPresContext->GetPresShell(); --j) {
1896
0
          // Make sure to not process observers which might have been removed
1897
0
          // during previous iterations.
1898
0
          nsIPresShell* shell = observers[j - 1];
1899
0
          if (!mStyleFlushObservers.RemoveElement(shell))
1900
0
            continue;
1901
0
1902
0
          nsCOMPtr<nsIPresShell> shellKungFuDeathGrip(shell);
1903
0
          shell->mObservingStyleFlushes = false;
1904
0
          shell->FlushPendingNotifications(ChangesToFlush(FlushType::Style, false));
1905
0
          // Inform the FontFaceSet that we ticked, so that it can resolve its
1906
0
          // ready promise if it needs to (though it might still be waiting on
1907
0
          // a layout flush).
1908
0
          shell->NotifyFontFaceSetOnRefresh();
1909
0
          mNeedToRecomputeVisibility = true;
1910
0
        }
1911
0
      }
1912
0
    } else if  (i == 2) {
1913
0
      // This is the FlushType::Layout case.
1914
0
      AutoTArray<nsIPresShell*, 16> observers;
1915
0
      observers.AppendElements(mLayoutFlushObservers);
1916
0
      for (uint32_t j = observers.Length();
1917
0
           j && mPresContext && mPresContext->GetPresShell(); --j) {
1918
0
        // Make sure to not process observers which might have been removed
1919
0
        // during previous iterations.
1920
0
        nsIPresShell* shell = observers[j - 1];
1921
0
        if (!mLayoutFlushObservers.RemoveElement(shell))
1922
0
          continue;
1923
0
1924
0
        nsCOMPtr<nsIPresShell> shellKungFuDeathGrip(shell);
1925
0
        shell->mObservingLayoutFlushes = false;
1926
0
        shell->mWasLastReflowInterrupted = false;
1927
0
        FlushType flushType = HasPendingAnimations(shell)
1928
0
                               ? FlushType::Layout
1929
0
                               : FlushType::InterruptibleLayout;
1930
0
        shell->FlushPendingNotifications(ChangesToFlush(flushType, false));
1931
0
        // Inform the FontFaceSet that we ticked, so that it can resolve its
1932
0
        // ready promise if it needs to.
1933
0
        shell->NotifyFontFaceSetOnRefresh();
1934
0
        mNeedToRecomputeVisibility = true;
1935
0
      }
1936
0
    }
1937
0
1938
0
    // The pres context may be destroyed during we do the flushing.
1939
0
    if (!mPresContext || !mPresContext->GetPresShell()) {
1940
0
      StopTimer();
1941
0
      return;
1942
0
    }
1943
0
  }
1944
0
1945
0
  // Recompute approximate frame visibility if it's necessary and enough time
1946
0
  // has passed since the last time we did it.
1947
0
  if (mNeedToRecomputeVisibility && !mThrottled &&
1948
0
      aNowTime >= mNextRecomputeVisibilityTick &&
1949
0
      !presShell->IsPaintingSuppressed()) {
1950
0
    mNextRecomputeVisibilityTick = aNowTime + mMinRecomputeVisibilityInterval;
1951
0
    mNeedToRecomputeVisibility = false;
1952
0
1953
0
    presShell->ScheduleApproximateFrameVisibilityUpdateNow();
1954
0
  }
1955
0
1956
0
#ifdef MOZ_XUL
1957
0
  // Update any popups that may need to be moved or hidden due to their
1958
0
  // anchor changing.
1959
0
  if (nsXULPopupManager* pm = nsXULPopupManager::GetInstance()) {
1960
0
    pm->UpdatePopupPositions(this);
1961
0
  }
1962
0
#endif
1963
0
1964
0
  UpdateIntersectionObservations();
1965
0
1966
0
  /*
1967
0
   * Perform notification to imgIRequests subscribed to listen
1968
0
   * for refresh events.
1969
0
   */
1970
0
1971
0
  for (auto iter = mStartTable.Iter(); !iter.Done(); iter.Next()) {
1972
0
    const uint32_t& delay = iter.Key();
1973
0
    ImageStartData* data = iter.UserData();
1974
0
1975
0
    if (data->mStartTime) {
1976
0
      TimeStamp& start = *data->mStartTime;
1977
0
      TimeDuration prev = previousRefresh - start;
1978
0
      TimeDuration curr = aNowTime - start;
1979
0
      uint32_t prevMultiple = uint32_t(prev.ToMilliseconds()) / delay;
1980
0
1981
0
      // We want to trigger images' refresh if we've just crossed over a
1982
0
      // multiple of the first image's start time. If so, set the animation
1983
0
      // start time to the nearest multiple of the delay and move all the
1984
0
      // images in this table to the main requests table.
1985
0
      if (prevMultiple != uint32_t(curr.ToMilliseconds()) / delay) {
1986
0
        mozilla::TimeStamp desired =
1987
0
          start + TimeDuration::FromMilliseconds(prevMultiple * delay);
1988
0
        BeginRefreshingImages(data->mEntries, desired);
1989
0
      }
1990
0
    } else {
1991
0
      // This is the very first time we've drawn images with this time delay.
1992
0
      // Set the animation start time to "now" and move all the images in this
1993
0
      // table to the main requests table.
1994
0
      mozilla::TimeStamp desired = aNowTime;
1995
0
      BeginRefreshingImages(data->mEntries, desired);
1996
0
      data->mStartTime.emplace(aNowTime);
1997
0
    }
1998
0
  }
1999
0
2000
0
  if (mRequests.Count()) {
2001
0
    // RequestRefresh may run scripts, so it's not safe to directly call it
2002
0
    // while using a hashtable enumerator to enumerate mRequests in case
2003
0
    // script modifies the hashtable. Instead, we build a (local) array of
2004
0
    // images to refresh, and then we refresh each image in that array.
2005
0
    nsCOMArray<imgIContainer> imagesToRefresh(mRequests.Count());
2006
0
2007
0
    for (auto iter = mRequests.Iter(); !iter.Done(); iter.Next()) {
2008
0
      nsISupportsHashKey* entry = iter.Get();
2009
0
      auto req = static_cast<imgIRequest*>(entry->GetKey());
2010
0
      MOZ_ASSERT(req, "Unable to retrieve the image request");
2011
0
      nsCOMPtr<imgIContainer> image;
2012
0
      if (NS_SUCCEEDED(req->GetImage(getter_AddRefs(image)))) {
2013
0
        imagesToRefresh.AppendElement(image.forget());
2014
0
      }
2015
0
    }
2016
0
2017
0
    for (uint32_t i = 0; i < imagesToRefresh.Length(); i++) {
2018
0
      imagesToRefresh[i]->RequestRefresh(aNowTime);
2019
0
    }
2020
0
  }
2021
0
2022
0
  bool dispatchRunnablesAfterTick = false;
2023
0
  if (mViewManagerFlushIsPending) {
2024
0
    RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
2025
0
2026
0
    nsTArray<nsDocShell*> profilingDocShells;
2027
0
    GetProfileTimelineSubDocShells(GetDocShell(mPresContext), profilingDocShells);
2028
0
    for (nsDocShell* docShell : profilingDocShells) {
2029
0
      // For the sake of the profile timeline's simplicity, this is flagged as
2030
0
      // paint even if it includes creating display lists
2031
0
      MOZ_ASSERT(timelines);
2032
0
      MOZ_ASSERT(timelines->HasConsumer(docShell));
2033
0
      timelines->AddMarkerForDocShell(docShell, "Paint",  MarkerTracingType::START);
2034
0
    }
2035
0
2036
#ifdef MOZ_DUMP_PAINTING
2037
    if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
2038
      printf_stderr("Starting ProcessPendingUpdates\n");
2039
    }
2040
#endif
2041
2042
0
    mViewManagerFlushIsPending = false;
2043
0
    RefPtr<nsViewManager> vm = mPresContext->GetPresShell()->GetViewManager();
2044
0
    {
2045
0
      PaintTelemetry::AutoRecordPaint record;
2046
0
      vm->ProcessPendingUpdates();
2047
0
    }
2048
0
2049
#ifdef MOZ_DUMP_PAINTING
2050
    if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
2051
      printf_stderr("Ending ProcessPendingUpdates\n");
2052
    }
2053
#endif
2054
2055
0
    for (nsDocShell* docShell : profilingDocShells) {
2056
0
      MOZ_ASSERT(timelines);
2057
0
      MOZ_ASSERT(timelines->HasConsumer(docShell));
2058
0
      timelines->AddMarkerForDocShell(docShell, "Paint",  MarkerTracingType::END);
2059
0
    }
2060
0
2061
0
    dispatchRunnablesAfterTick = true;
2062
0
    mHasScheduleFlush = false;
2063
0
  }
2064
0
2065
0
#ifndef ANDROID  /* bug 1142079 */
2066
0
  mozilla::Telemetry::AccumulateTimeDelta(mozilla::Telemetry::REFRESH_DRIVER_TICK, mTickStart);
2067
0
#endif
2068
0
2069
0
  if (mNotifyDOMContentFlushed) {
2070
0
    mNotifyDOMContentFlushed = false;
2071
0
    mPresContext->NotifyDOMContentFlushed();
2072
0
  }
2073
0
2074
0
  nsTObserverArray<nsAPostRefreshObserver*>::ForwardIterator iter(mPostRefreshObservers);
2075
0
  while (iter.HasMore()) {
2076
0
    nsAPostRefreshObserver* observer = iter.GetNext();
2077
0
    observer->DidRefresh();
2078
0
  }
2079
0
2080
0
  NS_ASSERTION(mInRefresh, "Still in refresh");
2081
0
2082
0
  if (mPresContext->IsRoot() && XRE_IsContentProcess() && gfxPrefs::AlwaysPaint()) {
2083
0
    ScheduleViewManagerFlush();
2084
0
  }
2085
0
2086
0
  if (dispatchRunnablesAfterTick && sPendingIdleRunnables) {
2087
0
    AutoTArray<RunnableWithDelay, 8>* runnables = sPendingIdleRunnables;
2088
0
    sPendingIdleRunnables = nullptr;
2089
0
    for (RunnableWithDelay& runnableWithDelay : *runnables) {
2090
0
      NS_IdleDispatchToCurrentThread(runnableWithDelay.mRunnable.forget(),
2091
0
                                     runnableWithDelay.mDelay);
2092
0
    }
2093
0
    delete runnables;
2094
0
  }
2095
0
}
2096
2097
void
2098
nsRefreshDriver::BeginRefreshingImages(RequestTable& aEntries,
2099
                                       mozilla::TimeStamp aDesired)
2100
0
{
2101
0
  for (auto iter = aEntries.Iter(); !iter.Done(); iter.Next()) {
2102
0
    auto req = static_cast<imgIRequest*>(iter.Get()->GetKey());
2103
0
    MOZ_ASSERT(req, "Unable to retrieve the image request");
2104
0
2105
0
    mRequests.PutEntry(req);
2106
0
2107
0
    nsCOMPtr<imgIContainer> image;
2108
0
    if (NS_SUCCEEDED(req->GetImage(getter_AddRefs(image)))) {
2109
0
      image->SetAnimationStartTime(aDesired);
2110
0
    }
2111
0
  }
2112
0
  aEntries.Clear();
2113
0
}
2114
2115
void
2116
nsRefreshDriver::Freeze()
2117
0
{
2118
0
  StopTimer();
2119
0
  mFreezeCount++;
2120
0
}
2121
2122
void
2123
nsRefreshDriver::Thaw()
2124
0
{
2125
0
  NS_ASSERTION(mFreezeCount > 0, "Thaw() called on an unfrozen refresh driver");
2126
0
2127
0
  if (mFreezeCount > 0) {
2128
0
    mFreezeCount--;
2129
0
  }
2130
0
2131
0
  if (mFreezeCount == 0) {
2132
0
    if (HasObservers() || HasImageRequests()) {
2133
0
      // FIXME: This isn't quite right, since our EnsureTimerStarted call
2134
0
      // updates our mMostRecentRefresh, but the DoRefresh call won't run
2135
0
      // and notify our observers until we get back to the event loop.
2136
0
      // Thus MostRecentRefresh() will lie between now and the DoRefresh.
2137
0
      RefPtr<nsRunnableMethod<nsRefreshDriver>> event = NewRunnableMethod(
2138
0
        "nsRefreshDriver::DoRefresh", this, &nsRefreshDriver::DoRefresh);
2139
0
      nsPresContext* pc = GetPresContext();
2140
0
      if (pc) {
2141
0
        pc->Document()->Dispatch(TaskCategory::Other,
2142
0
                                 event.forget());
2143
0
        EnsureTimerStarted();
2144
0
      } else {
2145
0
        NS_ERROR("Thawing while document is being destroyed");
2146
0
      }
2147
0
    }
2148
0
  }
2149
0
}
2150
2151
void
2152
nsRefreshDriver::FinishedWaitingForTransaction()
2153
0
{
2154
0
  mWaitingForTransaction = false;
2155
0
  mSkippedPaints = false;
2156
0
  mWarningThreshold = 1;
2157
0
}
2158
2159
mozilla::layers::TransactionId
2160
nsRefreshDriver::GetTransactionId(bool aThrottle)
2161
0
{
2162
0
  mOutstandingTransactionId = mOutstandingTransactionId.Next();
2163
0
  mNextTransactionId = mNextTransactionId.Next();
2164
0
2165
0
  if (aThrottle &&
2166
0
      mOutstandingTransactionId - mCompletedTransaction >= 2 &&
2167
0
      !mWaitingForTransaction &&
2168
0
      !mTestControllingRefreshes) {
2169
0
    mWaitingForTransaction = true;
2170
0
    mSkippedPaints = false;
2171
0
    mWarningThreshold = 1;
2172
0
  }
2173
0
2174
0
  return mNextTransactionId;
2175
0
}
2176
2177
mozilla::layers::TransactionId
2178
nsRefreshDriver::LastTransactionId() const
2179
0
{
2180
0
  return mNextTransactionId;
2181
0
}
2182
2183
void
2184
nsRefreshDriver::RevokeTransactionId(mozilla::layers::TransactionId aTransactionId)
2185
0
{
2186
0
  MOZ_ASSERT(aTransactionId == mNextTransactionId);
2187
0
  if (mOutstandingTransactionId - mCompletedTransaction == 2 &&
2188
0
      mWaitingForTransaction) {
2189
0
    MOZ_ASSERT(!mSkippedPaints, "How did we skip a paint when we're in the middle of one?");
2190
0
    FinishedWaitingForTransaction();
2191
0
  }
2192
0
2193
0
  // Notify the pres context so that it can deliver MozAfterPaint for this
2194
0
  // id if any caller was expecting it.
2195
0
  nsPresContext* pc = GetPresContext();
2196
0
  if (pc) {
2197
0
    pc->NotifyRevokingDidPaint(aTransactionId);
2198
0
  }
2199
0
  // Revert the outstanding transaction since we're no longer waiting on it to be
2200
0
  // completed, but don't revert mNextTransactionId since we can't use the id
2201
0
  // again.
2202
0
  mOutstandingTransactionId = mOutstandingTransactionId.Prev();
2203
0
}
2204
2205
void
2206
nsRefreshDriver::ClearPendingTransactions()
2207
0
{
2208
0
  mCompletedTransaction = mOutstandingTransactionId = mNextTransactionId;
2209
0
  mWaitingForTransaction = false;
2210
0
}
2211
2212
void
2213
nsRefreshDriver::ResetInitialTransactionId(mozilla::layers::TransactionId aTransactionId)
2214
0
{
2215
0
  mCompletedTransaction = mOutstandingTransactionId = mNextTransactionId = aTransactionId;
2216
0
}
2217
2218
mozilla::TimeStamp
2219
nsRefreshDriver::GetTransactionStart()
2220
0
{
2221
0
  return mTickStart;
2222
0
}
2223
2224
void
2225
nsRefreshDriver::NotifyTransactionCompleted(mozilla::layers::TransactionId aTransactionId)
2226
0
{
2227
0
  if (aTransactionId > mCompletedTransaction) {
2228
0
    if (mOutstandingTransactionId - mCompletedTransaction > 1 &&
2229
0
        mWaitingForTransaction) {
2230
0
      mCompletedTransaction = aTransactionId;
2231
0
      FinishedWaitingForTransaction();
2232
0
    } else {
2233
0
      mCompletedTransaction = aTransactionId;
2234
0
    }
2235
0
  }
2236
0
2237
0
  // If completed transaction id get ahead of outstanding id, reset to distance id.
2238
0
  if (mCompletedTransaction > mOutstandingTransactionId) {
2239
0
    mOutstandingTransactionId = mCompletedTransaction;
2240
0
  }
2241
0
}
2242
2243
void
2244
nsRefreshDriver::WillRefresh(mozilla::TimeStamp aTime)
2245
0
{
2246
0
  mRootRefresh->RemoveRefreshObserver(this, FlushType::Style);
2247
0
  mRootRefresh = nullptr;
2248
0
  if (mSkippedPaints) {
2249
0
    DoRefresh();
2250
0
  }
2251
0
}
2252
2253
bool
2254
nsRefreshDriver::IsWaitingForPaint(mozilla::TimeStamp aTime)
2255
0
{
2256
0
  if (mTestControllingRefreshes) {
2257
0
    return false;
2258
0
  }
2259
0
2260
0
  if (mWaitingForTransaction) {
2261
0
    if (mSkippedPaints && aTime > (mMostRecentRefresh + TimeDuration::FromMilliseconds(mWarningThreshold * 1000))) {
2262
0
      // XXX - Bug 1303369 - too many false positives.
2263
0
      //gfxCriticalNote << "Refresh driver waiting for the compositor for "
2264
0
      //                << (aTime - mMostRecentRefresh).ToSeconds()
2265
0
      //                << " seconds.";
2266
0
      mWarningThreshold *= 2;
2267
0
    }
2268
0
2269
0
    mSkippedPaints = true;
2270
0
    return true;
2271
0
  }
2272
0
2273
0
  // Try find the 'root' refresh driver for the current window and check
2274
0
  // if that is waiting for a paint.
2275
0
  nsPresContext* pc = GetPresContext();
2276
0
  nsPresContext* rootContext = pc ? pc->GetRootPresContext() : nullptr;
2277
0
  if (rootContext) {
2278
0
    nsRefreshDriver *rootRefresh = rootContext->RefreshDriver();
2279
0
    if (rootRefresh && rootRefresh != this) {
2280
0
      if (rootRefresh->IsWaitingForPaint(aTime)) {
2281
0
        if (mRootRefresh != rootRefresh) {
2282
0
          if (mRootRefresh) {
2283
0
            mRootRefresh->RemoveRefreshObserver(this, FlushType::Style);
2284
0
          }
2285
0
          rootRefresh->AddRefreshObserver(this, FlushType::Style);
2286
0
          mRootRefresh = rootRefresh;
2287
0
        }
2288
0
        mSkippedPaints = true;
2289
0
        return true;
2290
0
      }
2291
0
    }
2292
0
  }
2293
0
  return false;
2294
0
}
2295
2296
void
2297
nsRefreshDriver::SetThrottled(bool aThrottled)
2298
0
{
2299
0
  if (aThrottled != mThrottled) {
2300
0
    mThrottled = aThrottled;
2301
0
    if (mActiveTimer) {
2302
0
      // We want to switch our timer type here, so just stop and
2303
0
      // restart the timer.
2304
0
      EnsureTimerStarted(eForceAdjustTimer);
2305
0
    }
2306
0
  }
2307
0
}
2308
2309
/*static*/ void
2310
nsRefreshDriver::PVsyncActorCreated(VsyncChild* aVsyncChild)
2311
0
{
2312
0
  MOZ_ASSERT(NS_IsMainThread());
2313
0
  MOZ_ASSERT(!XRE_IsParentProcess());
2314
0
  RefPtr<RefreshDriverTimer> vsyncRefreshDriverTimer =
2315
0
    new VsyncRefreshDriverTimer(aVsyncChild);
2316
0
2317
0
  // If we are using software timer, swap current timer to
2318
0
  // VsyncRefreshDriverTimer.
2319
0
  if (sRegularRateTimer) {
2320
0
    sRegularRateTimer->SwapRefreshDrivers(vsyncRefreshDriverTimer);
2321
0
  }
2322
0
  sRegularRateTimer = vsyncRefreshDriverTimer.forget();
2323
0
}
2324
2325
void
2326
nsRefreshDriver::DoRefresh()
2327
0
{
2328
0
  // Don't do a refresh unless we're in a state where we should be refreshing.
2329
0
  if (!IsFrozen() && mPresContext && mActiveTimer) {
2330
0
    DoTick();
2331
0
  }
2332
0
}
2333
2334
#ifdef DEBUG
2335
bool
2336
nsRefreshDriver::IsRefreshObserver(nsARefreshObserver* aObserver,
2337
                                   FlushType aFlushType)
2338
{
2339
  ObserverArray& array = ArrayFor(aFlushType);
2340
  return array.Contains(aObserver);
2341
}
2342
#endif
2343
2344
void
2345
nsRefreshDriver::ScheduleViewManagerFlush()
2346
0
{
2347
0
  NS_ASSERTION(mPresContext->IsRoot(),
2348
0
               "Should only schedule view manager flush on root prescontexts");
2349
0
  mViewManagerFlushIsPending = true;
2350
0
  mHasScheduleFlush = true;
2351
0
  EnsureTimerStarted(eNeverAdjustTimer);
2352
0
}
2353
2354
void
2355
nsRefreshDriver::ScheduleFrameRequestCallbacks(nsIDocument* aDocument)
2356
0
{
2357
0
  NS_ASSERTION(mFrameRequestCallbackDocs.IndexOf(aDocument) ==
2358
0
               mFrameRequestCallbackDocs.NoIndex &&
2359
0
               mThrottledFrameRequestCallbackDocs.IndexOf(aDocument) ==
2360
0
               mThrottledFrameRequestCallbackDocs.NoIndex,
2361
0
               "Don't schedule the same document multiple times");
2362
0
  if (aDocument->ShouldThrottleFrameRequests()) {
2363
0
    mThrottledFrameRequestCallbackDocs.AppendElement(aDocument);
2364
0
  } else {
2365
0
    mFrameRequestCallbackDocs.AppendElement(aDocument);
2366
0
  }
2367
0
2368
0
  // make sure that the timer is running
2369
0
  EnsureTimerStarted();
2370
0
}
2371
2372
void
2373
nsRefreshDriver::RevokeFrameRequestCallbacks(nsIDocument* aDocument)
2374
0
{
2375
0
  mFrameRequestCallbackDocs.RemoveElement(aDocument);
2376
0
  mThrottledFrameRequestCallbackDocs.RemoveElement(aDocument);
2377
0
  // No need to worry about restarting our timer in slack mode if it's already
2378
0
  // running; that will happen automatically when it fires.
2379
0
}
2380
2381
void
2382
nsRefreshDriver::ScheduleFullscreenEvent(
2383
  UniquePtr<PendingFullscreenEvent> aEvent)
2384
0
{
2385
0
  mPendingFullscreenEvents.AppendElement(std::move(aEvent));
2386
0
  // make sure that the timer is running
2387
0
  EnsureTimerStarted();
2388
0
}
2389
2390
void
2391
nsRefreshDriver::CancelPendingFullscreenEvents(nsIDocument* aDocument)
2392
0
{
2393
0
  for (auto i : Reversed(IntegerRange(mPendingFullscreenEvents.Length()))) {
2394
0
    if (mPendingFullscreenEvents[i]->Document() == aDocument) {
2395
0
      mPendingFullscreenEvents.RemoveElementAt(i);
2396
0
    }
2397
0
  }
2398
0
}
2399
2400
void
2401
nsRefreshDriver::CancelPendingAnimationEvents(AnimationEventDispatcher* aDispatcher)
2402
0
{
2403
0
  MOZ_ASSERT(aDispatcher);
2404
0
  aDispatcher->ClearEventQueue();
2405
0
  mAnimationEventFlushObservers.RemoveElement(aDispatcher);
2406
0
}
2407
2408
/* static */ TimeStamp
2409
nsRefreshDriver::GetIdleDeadlineHint(TimeStamp aDefault)
2410
83
{
2411
83
  MOZ_ASSERT(NS_IsMainThread());
2412
83
  MOZ_ASSERT(!aDefault.IsNull());
2413
83
2414
83
  if (!sRegularRateTimer) {
2415
83
    return aDefault;
2416
83
  }
2417
0
2418
0
  // For computing idleness of refresh drivers we only care about
2419
0
  // sRegularRateTimer, since we consider refresh drivers attached to
2420
0
  // sThrottledRateTimer to be inactive. This implies that tasks
2421
0
  // resulting from a tick on the sRegularRateTimer counts as being
2422
0
  // busy but tasks resulting from a tick on sThrottledRateTimer
2423
0
  // counts as being idle.
2424
0
  return sRegularRateTimer->GetIdleDeadlineHint(aDefault);
2425
0
}
2426
2427
/* static */ Maybe<TimeStamp>
2428
nsRefreshDriver::GetNextTickHint()
2429
0
{
2430
0
  MOZ_ASSERT(NS_IsMainThread());
2431
0
2432
0
  if (!sRegularRateTimer) {
2433
0
    return Nothing();
2434
0
  }
2435
0
  return sRegularRateTimer->GetNextTickHint();
2436
0
}
2437
2438
void
2439
nsRefreshDriver::Disconnect()
2440
0
{
2441
0
  MOZ_ASSERT(NS_IsMainThread());
2442
0
2443
0
  StopTimer();
2444
0
2445
0
  if (mPresContext) {
2446
0
    mPresContext = nullptr;
2447
0
    if (--sRefreshDriverCount == 0) {
2448
0
      Shutdown();
2449
0
    }
2450
0
  }
2451
0
}
2452
2453
/* static */ bool
2454
nsRefreshDriver::IsJankCritical()
2455
0
{
2456
0
  MOZ_ASSERT(NS_IsMainThread());
2457
0
  return sActiveVsyncTimers > 0;
2458
0
}
2459
2460
/* static */ bool
2461
0
nsRefreshDriver::GetJankLevels(Vector<uint64_t>& aJank) {
2462
0
  aJank.clear();
2463
0
  return aJank.append(sJankLevels, ArrayLength(sJankLevels));
2464
0
}
2465
2466
#undef LOG