Bug 1184283 - RefreshDriver listen to widget via WidgetVsyncRefreshDriverTimer draft
authorVladimir Vukicevic <vladimir@pobox.com>
Mon, 25 Jan 2016 17:07:26 -0500
changeset 356573 9f4c88a76d8f1b573eb2d99739d2a5ed4e0aaa92
parent 356572 e94f603f176480b9cc5a0c976759b957180282a6
child 356574 980ebb2a8e3bd9bbb657ffbc310f2875eabe0bba
push id16548
push userbmo:vladimir@pobox.com
push dateTue, 26 Apr 2016 17:19:15 +0000
bugs1184283
milestone49.0a1
Bug 1184283 - RefreshDriver listen to widget via WidgetVsyncRefreshDriverTimer From e59d0350aedf39fc532b0bbefcedd6a7e15f0c03 Mon Sep 17 00:00:00 2001
layout/base/RefreshDriverTimerBase.h
layout/base/moz.build
layout/base/nsPresContext.cpp
layout/base/nsRefreshDriver.cpp
layout/base/nsRefreshDriver.h
widget/nsBaseWidget.cpp
widget/nsIWidget.h
new file mode 100644
--- /dev/null
+++ b/layout/base/RefreshDriverTimerBase.h
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef RefreshDriverTimerBase_h_
+#define RefreshDriverTimerBase_h_
+
+#include "nsISupportsImpl.h"
+
+namespace mozilla {
+namespace layout {
+// pure base class just for the refcounting, so that we can hold opaque handles to
+// RDTs elsewhere
+class RefreshDriverTimerBase {
+public:
+  NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
+protected:
+  RefreshDriverTimerBase() {}
+  virtual ~RefreshDriverTimerBase() {}
+};
+
+} // namespace layout
+} // namespace mozilla
+
+#endif /* RefreshDriverTimerBase_h_ */
--- a/layout/base/moz.build
+++ b/layout/base/moz.build
@@ -86,16 +86,17 @@ EXPORTS += [
     'nsIReflowCallback.h',
     'nsLayoutUtils.h',
     'nsPresArena.h',
     'nsPresArenaObjectList.h',
     'nsPresContext.h',
     'nsPresState.h',
     'nsRefreshDriver.h',
     'nsStyleChangeList.h',
+    'RefreshDriverTimerBase.h',
     'ScrollbarStyles.h',
     'StackArena.h',
     'Units.h',
     'UnitTransforms.h',
     'WordMovementType.h',
 ]
 
 EXPORTS.mozilla += [
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -2902,17 +2902,17 @@ nsRootPresContext::InitApplyPluginGeomet
   // We'll apply the plugin geometry updates during the next compositing paint in this
   // presContext (either from nsPresShell::WillPaintWindow or from
   // nsPresShell::DidPaintWindow, depending on the platform).  But paints might
   // get optimized away if the old plugin geometry covers the invalid region,
   // so set a backup timer to do this too.  We want to make sure this
   // won't fire before our normal paint notifications, if those would
   // update the geometry, so set it for double the refresh driver interval.
   mApplyPluginGeometryTimer = CreateTimer(ApplyPluginGeometryUpdatesCallback,
-                                          nsRefreshDriver::DefaultInterval() * 2);
+                                          NSToIntRound(nsRefreshDriver::DefaultInterval() * 2));
 }
 
 void
 nsRootPresContext::CancelApplyPluginGeometryTimer()
 {
   if (mApplyPluginGeometryTimer) {
     mApplyPluginGeometryTimer->Cancel();
     mApplyPluginGeometryTimer = nullptr;
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -56,18 +56,17 @@
 #include "nsISimpleEnumerator.h"
 #include "nsJSEnvironment.h"
 #include "mozilla/Telemetry.h"
 #include "gfxPrefs.h"
 #include "BackgroundChild.h"
 #include "mozilla/ipc/PBackgroundChild.h"
 #include "nsIIPCBackgroundChildCreateCallback.h"
 #include "mozilla/layout/VsyncChild.h"
-#include "VsyncSource.h"
-#include "mozilla/VsyncDispatcher.h"
+#include "gfxVsync.h"
 #include "nsThreadUtils.h"
 #include "mozilla/unused.h"
 #include "mozilla/TimelineConsumers.h"
 #include "nsAnimationManager.h"
 #include "nsIDOMEvent.h"
 
 #ifdef MOZ_NUWA_PROCESS
 #include "ipc/Nuwa.h"
@@ -76,16 +75,19 @@
 using namespace mozilla;
 using namespace mozilla::widget;
 using namespace mozilla::ipc;
 using namespace mozilla::layout;
 
 static mozilla::LazyLogModule sRefreshDriverLog("nsRefreshDriver");
 #define LOG(...) MOZ_LOG(sRefreshDriverLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
 
+extern PRLogModuleInfo *gVsyncLog;
+#define VSYNC_LOG(...)  MOZ_LOG(gVsyncLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
+
 #define DEFAULT_THROTTLED_FRAME_RATE 1
 #define DEFAULT_RECOMPUTE_VISIBILITY_INTERVAL_MS 1000
 // after 10 minutes, stop firing off inactive timers
 #define DEFAULT_INACTIVE_TIMER_DISABLE_SECONDS 600
 
 namespace {
   // `true` if we are currently in jank-critical mode.
   //
@@ -113,28 +115,35 @@ namespace mozilla {
  * of managing the list of refresh drivers attached to them and
  * provides interfaces for querying/setting the rate and actually
  * running a timer 'Tick'.  Subclasses must implement StartTimer(),
  * StopTimer(), and ScheduleNextTick() -- the first two just
  * start/stop whatever timer mechanism is in use, and ScheduleNextTick
  * is called at the start of the Tick() implementation to set a time
  * for the next tick.
  */
-class RefreshDriverTimer {
+class RefreshDriverTimer
+  : public RefreshDriverTimerBase
+{
+public:
+  enum RefreshDriverTimerType {
+    eTimerUnknown = 0,
+    eTimerWidgetVsync = 1,
+  };
+
 public:
   RefreshDriverTimer()
-    : mLastFireEpoch(0)
+    : mTimerType(eTimerUnknown)
+    , mLastFireEpoch(0)
   {
   }
 
-  virtual ~RefreshDriverTimer()
-  {
-    MOZ_ASSERT(mContentRefreshDrivers.Length() == 0, "Should have removed all content refresh drivers from here by now!");
-    MOZ_ASSERT(mRootRefreshDrivers.Length() == 0, "Should have removed all root refresh drivers from here by now!");
-  }
+  RefreshDriverTimerType GetType() const { return mTimerType; }
+
+public:
 
   virtual void AddRefreshDriver(nsRefreshDriver* aDriver)
   {
     LOG("[%p] AddRefreshDriver %p", this, aDriver);
 
     bool startTimer = mContentRefreshDrivers.IsEmpty() && mRootRefreshDrivers.IsEmpty();
     if (IsRootRefreshDriver(aDriver)) {
       NS_ASSERTION(!mRootRefreshDrivers.Contains(aDriver), "Adding a duplicate root refresh driver!");
@@ -179,37 +188,25 @@ public:
     if (stopTimer) {
       StopTimer();
     }
   }
 
   TimeStamp MostRecentRefresh() const { return mLastFireTime; }
   int64_t MostRecentRefreshEpochTime() const { return mLastFireEpoch; }
 
-  void SwapRefreshDrivers(RefreshDriverTimer* aNewTimer)
-  {
-    MOZ_ASSERT(NS_IsMainThread());
+  virtual void Destroy() { }
 
-    for (nsRefreshDriver* driver : mContentRefreshDrivers) {
-      aNewTimer->AddRefreshDriver(driver);
-      driver->mActiveTimer = aNewTimer;
-    }
-    mContentRefreshDrivers.Clear();
-
-    for (nsRefreshDriver* driver : mRootRefreshDrivers) {
-      aNewTimer->AddRefreshDriver(driver);
-      driver->mActiveTimer = aNewTimer;
-    }
-    mRootRefreshDrivers.Clear();
-
-    aNewTimer->mLastFireEpoch = mLastFireEpoch;
-    aNewTimer->mLastFireTime = mLastFireTime;
+protected:
+  virtual ~RefreshDriverTimer()
+  {
+    MOZ_ASSERT(mContentRefreshDrivers.Length() == 0, "Should have removed all content refresh drivers from here by now!");
+    MOZ_ASSERT(mRootRefreshDrivers.Length() == 0, "Should have removed all root refresh drivers from here by now!");
   }
 
-protected:
   virtual void StartTimer() = 0;
   virtual void StopTimer() = 0;
   virtual void ScheduleNextTick(TimeStamp aNowTime) = 0;
 
   bool IsRootRefreshDriver(nsRefreshDriver* aDriver)
   {
     nsPresContext* rootContext = aDriver->PresContext()->GetRootPresContext();
     if (!rootContext) {
@@ -269,16 +266,18 @@ protected:
   }
 
   static void TickDriver(nsRefreshDriver* driver, int64_t jsnow, TimeStamp now)
   {
     LOG(">> TickDriver: %p (jsnow: %lld)", driver, jsnow);
     driver->Tick(jsnow, now);
   }
 
+  RefreshDriverTimerType mTimerType;
+
   int64_t mLastFireEpoch;
   TimeStamp mLastFireTime;
   TimeStamp mTargetTime;
 
   nsTArray<RefPtr<nsRefreshDriver> > mContentRefreshDrivers;
   nsTArray<RefPtr<nsRefreshDriver> > mRootRefreshDrivers;
 
   // useful callback for nsITimer-based derived classes, here
@@ -295,320 +294,301 @@ protected:
  * this is a ONE_SHOT timer, not a repeating one!  Subclasses are expected to
  * implement ScheduleNextTick and intelligently calculate the next time to tick,
  * and to reset mTimer.  Using a repeating nsITimer gets us into a lot of pain
  * with its attempt at intelligent slack removal and such, so we don't do it.
  */
 class SimpleTimerBasedRefreshDriverTimer :
     public RefreshDriverTimer
 {
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SimpleTimerBasedRefreshDriverTimer, override)
+
 public:
   /*
    * aRate -- the delay, in milliseconds, requested between timer firings
    */
   explicit SimpleTimerBasedRefreshDriverTimer(double aRate)
   {
     SetRate(aRate);
     mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
   }
 
-  virtual ~SimpleTimerBasedRefreshDriverTimer()
-  {
-    StopTimer();
-  }
-
   // will take effect at next timer tick
   virtual void SetRate(double aNewRate)
   {
     mRateMilliseconds = aNewRate;
     mRateDuration = TimeDuration::FromMilliseconds(mRateMilliseconds);
   }
 
   double GetRate() const
   {
     return mRateMilliseconds;
   }
 
 protected:
+  virtual ~SimpleTimerBasedRefreshDriverTimer()
+  {
+    StopTimer();
+  }
 
-  virtual void StartTimer()
+  virtual void StartTimer() override
   {
     // pretend we just fired, and we schedule the next tick normally
     mLastFireEpoch = JS_Now();
     mLastFireTime = TimeStamp::Now();
 
     mTargetTime = mLastFireTime + mRateDuration;
 
     uint32_t delay = static_cast<uint32_t>(mRateMilliseconds);
     mTimer->InitWithFuncCallback(TimerTick, this, delay, nsITimer::TYPE_ONE_SHOT);
   }
 
-  virtual void StopTimer()
+  virtual void StopTimer() override
   {
     mTimer->Cancel();
   }
 
   double mRateMilliseconds;
   TimeDuration mRateDuration;
   RefPtr<nsITimer> mTimer;
 };
 
 /*
  * A refresh driver that listens to vsync events and ticks the refresh driver
  * on vsync intervals. We throttle the refresh driver if we get too many
  * vsync events and wait to catch up again.
  */
-class VsyncRefreshDriverTimer : public RefreshDriverTimer
+class WidgetVsyncRefreshDriverTimer :
+    public RefreshDriverTimer,
+    public gfx::VsyncObserver
 {
 public:
-  VsyncRefreshDriverTimer()
-    : mVsyncChild(nullptr)
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WidgetVsyncRefreshDriverTimer, override)
+
+  explicit WidgetVsyncRefreshDriverTimer(nsIWidget *aWidget)
+    : mWidget(aWidget)
+    , mMostRecentVsync(TimeStamp::Now())
+    , mLastTick(TimeStamp::Now())
+    , mVsyncInterval(TimeDuration::Forever())
+    , mRunning(false)
+    , mRefreshTickLock("WidgetVsync InnerVsyncObserver RefreshTickLock")
   {
-    MOZ_ASSERT(XRE_IsParentProcess());
     MOZ_ASSERT(NS_IsMainThread());
-    mVsyncObserver = new RefreshDriverVsyncObserver(this);
-    RefPtr<mozilla::gfx::VsyncSource> vsyncSource = gfxPlatform::GetPlatform()->GetHardwareVsync();
-    MOZ_ALWAYS_TRUE(mVsyncDispatcher = vsyncSource->GetRefreshTimerVsyncDispatcher());
-    mVsyncDispatcher->SetParentRefreshTimer(mVsyncObserver);
-  }
 
-  explicit VsyncRefreshDriverTimer(VsyncChild* aVsyncChild)
-    : mVsyncChild(aVsyncChild)
-  {
-    MOZ_ASSERT(!XRE_IsParentProcess());
-    MOZ_ASSERT(NS_IsMainThread());
-    MOZ_ASSERT(mVsyncChild);
-    mVsyncObserver = new RefreshDriverVsyncObserver(this);
-    mVsyncChild->SetVsyncObserver(mVsyncObserver);
+    mTimerType = eTimerWidgetVsync;
   }
 
-private:
-  // Since VsyncObservers are refCounted, but the RefreshDriverTimer are
-  // explicitly shutdown. We create an inner class that has the VsyncObserver
-  // and is shutdown when the RefreshDriverTimer is deleted. The alternative is
-  // to (a) make all RefreshDriverTimer RefCounted or (b) use different
-  // VsyncObserver types.
-  class RefreshDriverVsyncObserver final : public VsyncObserver
-  {
+  nsIWidget* GetWidget() const { return mWidget; }
+
+protected:
+  class TickWithMostRecentRunnable : public CancelableRunnable {
   public:
-    explicit RefreshDriverVsyncObserver(VsyncRefreshDriverTimer* aVsyncRefreshDriverTimer)
-      : mVsyncRefreshDriverTimer(aVsyncRefreshDriverTimer)
-      , mRefreshTickLock("RefreshTickLock")
-      , mRecentVsync(TimeStamp::Now())
-      , mLastChildTick(TimeStamp::Now())
-      , mVsyncRate(TimeDuration::Forever())
-      , mProcessedVsync(true)
-    {
-      MOZ_ASSERT(NS_IsMainThread());
+    explicit TickWithMostRecentRunnable(WidgetVsyncRefreshDriverTimer *aTimer)
+      : mTimer(aTimer)
+    {}
+
+    NS_IMETHOD Run() {
+      mTimer->TickRefreshDriverWithMostRecent();
+      return NS_OK;
     }
 
-    virtual bool NotifyVsync(TimeStamp aVsyncTimestamp) override
-    {
-      if (!NS_IsMainThread()) {
-        MOZ_ASSERT(XRE_IsParentProcess());
-        // Compress vsync notifications such that only 1 may run at a time
-        // This is so that we don't flood the refresh driver with vsync messages
-        // if the main thread is blocked for long periods of time
-        { // scope lock
-          MonitorAutoLock lock(mRefreshTickLock);
-          mRecentVsync = aVsyncTimestamp;
-          if (!mProcessedVsync) {
-            return true;
-          }
-          mProcessedVsync = false;
-        }
+    RefPtr<WidgetVsyncRefreshDriverTimer> mTimer;
+  };
+
+  // gfx::VsyncObserver
+  void NotifyVsync(TimeStamp aVsyncTimestamp) override
+  {
+    MOZ_ASSERT(!aVsyncTimestamp.IsNull());
 
-        nsCOMPtr<nsIRunnable> vsyncEvent =
-             NS_NewRunnableMethodWithArg<TimeStamp>(this,
-                                                    &RefreshDriverVsyncObserver::TickRefreshDriver,
-                                                    aVsyncTimestamp);
-        NS_DispatchToMainThread(vsyncEvent);
-      } else {
-        TickRefreshDriver(aVsyncTimestamp);
+    if (!NS_IsMainThread()) {
+      MOZ_ASSERT(XRE_IsParentProcess());
+      MutexAutoLock lock(mRefreshTickLock);
+
+      // Were we stopped while another thread was about to execute our
+      // notify?  The notifications get sent after a copy of the
+      // observers is made; so a copy could be made that includes this
+      // observer, but then it gets removed.
+      if (!mRunning) {
+        return;
       }
 
-      return true;
+      // Compress vsync notifications such that only 1 may run at a time; if
+      // our previous one still wasn't processed, just bump the most recent time
+      // and let it trigger.  Otherwise schedule a new one.
+      mMostRecentVsync = aVsyncTimestamp;
+      if (!mTickRunnable) {
+        mTickRunnable = new TickWithMostRecentRunnable(this);
+        NS_DispatchToMainThread(mTickRunnable);
+      }
+    } else {
+      MOZ_ASSERT(!XRE_IsParentProcess());
+
+      if (!mRunning) {
+        return;
+      }
+
+      TickRefreshDriver(aVsyncTimestamp);
+    }
+  }
+
+  virtual ~WidgetVsyncRefreshDriverTimer()
+  {
+    VSYNC_LOG("[%s][%p] destructor\n", XRE_GetProcessTypeString(), this);
+    MOZ_ASSERT(!mRunning, "~WidgetVsyncRefreshDriverTimer, but the timer thinks it's still running!");
+
+    if (!NS_IsMainThread()) {
+      MOZ_ASSERT(XRE_IsParentProcess());
+      NS_ReleaseOnMainThread(mWidget.forget());
     }
 
-    void Shutdown()
-    {
-      MOZ_ASSERT(NS_IsMainThread());
-      mVsyncRefreshDriverTimer = nullptr;
-    }
+    mWidget = nullptr;
+  }
+
+  void TickRefreshDriverWithMostRecent()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    TimeStamp vsyncTime;
 
-    void OnTimerStart()
-    {
-      if (!XRE_IsParentProcess()) {
-        mLastChildTick = TimeStamp::Now();
+    { // scope lock
+      MutexAutoLock lock(mRefreshTickLock);
+      vsyncTime = mMostRecentVsync;
+      mTickRunnable = nullptr;
+
+      // are we no longer actually running?
+      if (!mRunning) {
+        return;
       }
     }
 
-  private:
-    virtual ~RefreshDriverVsyncObserver() {}
+    TickRefreshDriver(vsyncTime);
+  }
 
-    void RecordTelemetryProbes(TimeStamp aVsyncTimestamp)
-    {
-      MOZ_ASSERT(NS_IsMainThread());
-    #ifndef ANDROID  /* bug 1142079 */
-      if (XRE_IsParentProcess()) {
-        TimeDuration vsyncLatency = TimeStamp::Now() - aVsyncTimestamp;
-        uint32_t sample = (uint32_t)vsyncLatency.ToMilliseconds();
-        Telemetry::Accumulate(Telemetry::FX_REFRESH_DRIVER_CHROME_FRAME_DELAY_MS,
-                              sample);
-        Telemetry::Accumulate(Telemetry::FX_REFRESH_DRIVER_SYNC_SCROLL_FRAME_DELAY_MS,
-                              sample);
-        RecordJank(sample);
-      } else if (mVsyncRate != TimeDuration::Forever()) {
-        TimeDuration contentDelay = (TimeStamp::Now() - mLastChildTick) - mVsyncRate;
-        if (contentDelay.ToMilliseconds() < 0 ){
-          // Vsyncs are noisy and some can come at a rate quicker than
-          // the reported hardware rate. In those cases, consider that we have 0 delay.
-          contentDelay = TimeDuration::FromMilliseconds(0);
-        }
-        uint32_t sample = (uint32_t)contentDelay.ToMilliseconds();
-        Telemetry::Accumulate(Telemetry::FX_REFRESH_DRIVER_CONTENT_FRAME_DELAY_MS,
-                              sample);
-        Telemetry::Accumulate(Telemetry::FX_REFRESH_DRIVER_SYNC_SCROLL_FRAME_DELAY_MS,
-                              sample);
-        RecordJank(sample);
-      } else {
-        // Request the vsync rate from the parent process. Might be a few vsyncs
-        // until the parent responds.
-        mVsyncRate = mVsyncRefreshDriverTimer->mVsyncChild->GetVsyncRate();
+  void RecordTelemetryProbes(TimeStamp aVsyncTimestamp)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+#ifndef ANDROID  /* bug 1142079 */
+    if (XRE_IsParentProcess()) {
+      TimeDuration vsyncLatency = TimeStamp::Now() - aVsyncTimestamp;
+      uint32_t sample = (uint32_t)vsyncLatency.ToMilliseconds();
+      Telemetry::Accumulate(Telemetry::FX_REFRESH_DRIVER_CHROME_FRAME_DELAY_MS,
+                            sample);
+      Telemetry::Accumulate(Telemetry::FX_REFRESH_DRIVER_SYNC_SCROLL_FRAME_DELAY_MS,
+                            sample);
+      RecordJank(sample);
+    } else if (mVsyncInterval != TimeDuration::Forever()) {
+      TimeDuration contentDelay = (TimeStamp::Now() - mLastTick) - mVsyncInterval;
+      if (contentDelay.ToMilliseconds() < 0 ){
+        // Vsyncs are noisy and some can come at a rate quicker than
+        // the reported hardware rate. In those cases, consider that we have 0 delay.
+        contentDelay = TimeDuration::FromMilliseconds(0);
       }
-    #endif
+      uint32_t sample = (uint32_t)contentDelay.ToMilliseconds();
+      Telemetry::Accumulate(Telemetry::FX_REFRESH_DRIVER_CONTENT_FRAME_DELAY_MS,
+                            sample);
+      Telemetry::Accumulate(Telemetry::FX_REFRESH_DRIVER_SYNC_SCROLL_FRAME_DELAY_MS,
+                            sample);
+      RecordJank(sample);
+    } else {
+      // Request the vsync rate from the parent process. Might be a few vsyncs
+      // until the parent responds.
+
+      // XXX fixme (need to ask widget for the source, get the source;
+      // VsyncChild/Parent needs to ask for the vsync rate.
+      //mVsyncInterval = mVsyncRefreshDriverTimer->mVsyncChild->GetVsyncInterval();
     }
+#endif
+  }
 
     void RecordJank(uint32_t aJankMS)
     {
       uint32_t duration = 1 /* ms */;
       for (size_t i = 0;
            i < mozilla::ArrayLength(sJankLevels) && duration < aJankMS;
            ++i, duration *= 2) {
         sJankLevels[i]++;
       }
     }
 
-    void TickRefreshDriver(TimeStamp aVsyncTimestamp)
-    {
-      MOZ_ASSERT(NS_IsMainThread());
-
-      RecordTelemetryProbes(aVsyncTimestamp);
-      if (XRE_IsParentProcess()) {
-        MonitorAutoLock lock(mRefreshTickLock);
-        aVsyncTimestamp = mRecentVsync;
-        mProcessedVsync = true;
-      } else {
-        mLastChildTick = TimeStamp::Now();
-      }
-      MOZ_ASSERT(aVsyncTimestamp <= TimeStamp::Now());
-
-      // We might have a problem that we call ~VsyncRefreshDriverTimer() before
-      // the scheduled TickRefreshDriver() runs. Check mVsyncRefreshDriverTimer
-      // before use.
-      if (mVsyncRefreshDriverTimer) {
-        mVsyncRefreshDriverTimer->RunRefreshDrivers(aVsyncTimestamp);
-      }
-    }
+  void TickRefreshDriver(TimeStamp aVsyncTimestamp)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(mRunning, "TickRefreshDriver, but mRunning == false?");
 
-    // VsyncRefreshDriverTimer holds this RefreshDriverVsyncObserver and it will
-    // be always available before Shutdown(). We can just use the raw pointer
-    // here.
-    VsyncRefreshDriverTimer* mVsyncRefreshDriverTimer;
-    Monitor mRefreshTickLock;
-    TimeStamp mRecentVsync;
-    TimeStamp mLastChildTick;
-    TimeDuration mVsyncRate;
-    bool mProcessedVsync;
-  }; // RefreshDriverVsyncObserver
+    RecordTelemetryProbes(aVsyncTimestamp);
+
+    RunRefreshDrivers(aVsyncTimestamp);
 
-  virtual ~VsyncRefreshDriverTimer()
-  {
-    if (XRE_IsParentProcess()) {
-      mVsyncDispatcher->SetParentRefreshTimer(nullptr);
-      mVsyncDispatcher = nullptr;
-    } else {
-      // Since the PVsyncChild actors live through the life of the process, just
-      // send the unobserveVsync message to disable vsync event. We don't need
-      // to handle the cleanup stuff of this actor. PVsyncChild::ActorDestroy()
-      // will be called and clean up this actor.
-      Unused << mVsyncChild->SendUnobserve();
-      mVsyncChild->SetVsyncObserver(nullptr);
-      mVsyncChild = nullptr;
+    if (!XRE_IsParentProcess()) {
+      mLastTick = TimeStamp::Now();
     }
-
-    // Detach current vsync timer from this VsyncObserver. The observer will no
-    // longer tick this timer.
-    mVsyncObserver->Shutdown();
-    mVsyncObserver = nullptr;
   }
 
   virtual void StartTimer() override
   {
     // Protect updates to `sActiveVsyncTimers`.
     MOZ_ASSERT(NS_IsMainThread());
 
     mLastFireEpoch = JS_Now();
     mLastFireTime = TimeStamp::Now();
+    mLastTick = TimeStamp::Now();
 
-    if (XRE_IsParentProcess()) {
-      mVsyncDispatcher->SetParentRefreshTimer(mVsyncObserver);
-    } else {
-      Unused << mVsyncChild->SendObserve();
-      mVsyncObserver->OnTimerStart();
-    }
-
-    ++sActiveVsyncTimers;
+    VSYNC_LOG("[%s][%p] RefreshDriverTimer adding widget vsync observer\n", XRE_GetProcessTypeString(), this);
+    mRunning = true;
+    mWidget->AddVsyncObserver(this);
   }
 
   virtual void StopTimer() override
   {
-    // Protect updates to `sActiveVsyncTimers`.
     MOZ_ASSERT(NS_IsMainThread());
+    VSYNC_LOG("[%s][%p] RefreshDriverTimer removing widget vsync observer\n", XRE_GetProcessTypeString(), this);
 
-    if (XRE_IsParentProcess()) {
-      mVsyncDispatcher->SetParentRefreshTimer(nullptr);
-    } else {
-      Unused << mVsyncChild->SendUnobserve();
+    { // scope lock
+      MutexAutoLock lock(mRefreshTickLock);
+      mRunning = false;
+      if (mTickRunnable) {
+        mTickRunnable->Cancel();
+        mTickRunnable = nullptr;
+      }
     }
 
-    MOZ_ASSERT(sActiveVsyncTimers > 0);
-    --sActiveVsyncTimers;
+    mWidget->RemoveVsyncObserver(this);
   }
 
   virtual void ScheduleNextTick(TimeStamp aNowTime) override
   {
     // Do nothing since we just wait for the next vsync from
     // RefreshDriverVsyncObserver.
   }
 
   void RunRefreshDrivers(TimeStamp aTimeStamp)
   {
     int64_t jsnow = JS_Now();
     TimeDuration diff = TimeStamp::Now() - aTimeStamp;
     int64_t vsyncJsNow = jsnow - diff.ToMicroseconds();
     Tick(vsyncJsNow, aTimeStamp);
   }
 
-  RefPtr<RefreshDriverVsyncObserver> mVsyncObserver;
-  // Used for parent process.
-  RefPtr<RefreshTimerVsyncDispatcher> mVsyncDispatcher;
-  // Used for child process.
-  // The mVsyncChild will be always available before VsncChild::ActorDestroy().
-  // After ActorDestroy(), StartTimer() and StopTimer() calls will be non-op.
-  RefPtr<VsyncChild> mVsyncChild;
-}; // VsyncRefreshDriverTimer
+  nsCOMPtr<nsIWidget> mWidget;
+  TimeStamp mMostRecentVsync;
+  TimeStamp mLastTick;
+  TimeDuration mVsyncInterval;
+  bool mRunning;
+  Mutex mRefreshTickLock;
 
-/**
- * Since the content process takes some time to setup
- * the vsync IPC connection, this timer is used
- * during the intial startup process.
- * During initial startup, the refresh drivers
- * are ticked off this timer, and are swapped out once content
- * vsync IPC connection is established.
+  RefPtr<CancelableRunnable> mTickRunnable;
+}; // WidgetVsyncRefreshDriverTimer
+
+/*
+ * PreciseRefreshDriverTimer schedules ticks based on the current time
+ * and when the next tick -should- be sent if we were hitting our
+ * rate.  It always schedules ticks on multiples of aRate -- meaning that
+ * if some execution takes longer than an alloted slot, the next tick
+ * will be delayed instead of triggering instantly.  This might not be
+ * desired -- there's an #if 0'd block below that we could put behind
+ * a pref to control this behaviour.
  */
 class StartupRefreshDriverTimer :
     public SimpleTimerBasedRefreshDriverTimer
 {
 public:
   explicit StartupRefreshDriverTimer(double aRate)
     : SimpleTimerBasedRefreshDriverTimer(aRate)
   {
@@ -753,129 +733,26 @@ protected:
     timer->TickOne();
   }
 
   double mNextTickDuration;
   double mDisableAfterMilliseconds;
   uint32_t mNextDriverIndex;
 };
 
-// The PBackground protocol connection callback. It will be called when
-// PBackground is ready. Then we create the PVsync sub-protocol for our
-// vsync-base RefreshTimer.
-class VsyncChildCreateCallback final : public nsIIPCBackgroundChildCreateCallback
-{
-  NS_DECL_ISUPPORTS
-
-public:
-  VsyncChildCreateCallback()
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-  }
-
-  static void CreateVsyncActor(PBackgroundChild* aPBackgroundChild)
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    MOZ_ASSERT(aPBackgroundChild);
-
-    layout::PVsyncChild* actor = aPBackgroundChild->SendPVsyncConstructor();
-    layout::VsyncChild* child = static_cast<layout::VsyncChild*>(actor);
-    nsRefreshDriver::PVsyncActorCreated(child);
-  }
-
-private:
-  virtual ~VsyncChildCreateCallback() {}
-
-  virtual void ActorCreated(PBackgroundChild* aPBackgroundChild) override
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    MOZ_ASSERT(aPBackgroundChild);
-    CreateVsyncActor(aPBackgroundChild);
-  }
-
-  virtual void ActorFailed() override
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    MOZ_CRASH("Failed To Create VsyncChild Actor");
-  }
-}; // VsyncChildCreateCallback
-NS_IMPL_ISUPPORTS(VsyncChildCreateCallback, nsIIPCBackgroundChildCreateCallback)
-
 } // namespace mozilla
 
-static RefreshDriverTimer* sRegularRateTimer;
-static InactiveRefreshDriverTimer* sThrottledRateTimer;
+namespace {
+StaticRefPtr<InactiveRefreshDriverTimer> sThrottledRateTimer;
 
 #ifdef XP_WIN
-static int32_t sHighPrecisionTimerRequests = 0;
+int32_t sHighPrecisionTimerRequests = 0;
 // a bare pointer to avoid introducing a static constructor
-static nsITimer *sDisableHighPrecisionTimersTimer = nullptr;
+nsITimer *sDisableHighPrecisionTimersTimer = nullptr;
 #endif
-
-static void
-CreateContentVsyncRefreshTimer(void*)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(!XRE_IsParentProcess());
-
-  // Create the PVsync actor child for vsync-base refresh timer.
-  // PBackgroundChild is created asynchronously. If PBackgroundChild is still
-  // unavailable, setup VsyncChildCreateCallback callback to handle the async
-  // connect. We will still use software timer before PVsync ready, and change
-  // to use hw timer when the connection is done. Please check
-  // VsyncChildCreateCallback::CreateVsyncActor() and
-  // nsRefreshDriver::PVsyncActorCreated().
-  PBackgroundChild* backgroundChild = BackgroundChild::GetForCurrentThread();
-  if (backgroundChild) {
-    // If we already have PBackgroundChild, create the
-    // child VsyncRefreshDriverTimer here.
-    VsyncChildCreateCallback::CreateVsyncActor(backgroundChild);
-    return;
-  }
-  // Setup VsyncChildCreateCallback callback
-  RefPtr<nsIIPCBackgroundChildCreateCallback> callback = new VsyncChildCreateCallback();
-  if (NS_WARN_IF(!BackgroundChild::GetOrCreateForCurrentThread(callback))) {
-    MOZ_CRASH("PVsync actor create failed!");
-  }
-}
-
-static void
-CreateVsyncRefreshTimer()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  PodArrayZero(sJankLevels);
-  // Sometimes, gfxPrefs is not initialized here. Make sure the gfxPrefs is
-  // ready.
-  gfxPrefs::GetSingleton();
-
-  if (gfxPlatform::IsInLayoutAsapMode()) {
-    return;
-  }
-
-  if (XRE_IsParentProcess()) {
-    // Make sure all vsync systems are ready.
-    gfxPlatform::GetPlatform();
-    // In parent process, we don't need to use ipc. We can create the
-    // VsyncRefreshDriverTimer directly.
-    sRegularRateTimer = new VsyncRefreshDriverTimer();
-    return;
-  }
-
-#ifdef MOZ_NUWA_PROCESS
-  // NUWA process will just use software timer. Use NuwaAddFinalConstructor()
-  // to register a callback to create the vsync-base refresh timer after a
-  // process is created.
-  if (IsNuwaProcess()) {
-    NuwaAddFinalConstructor(&CreateContentVsyncRefreshTimer, nullptr);
-    return;
-  }
-#endif
-  // If this process is not created by NUWA, just create the vsync timer here.
-  CreateContentVsyncRefreshTimer(nullptr);
 }
 
 static uint32_t
 GetFirstFrameDelay(imgIRequest* req)
 {
   nsCOMPtr<imgIContainer> container;
   if (NS_FAILED(req->GetImage(getter_AddRefs(container))) || !container) {
     return 0;
@@ -887,73 +764,49 @@ GetFirstFrameDelay(imgIRequest* req)
     return 0;
 
   return static_cast<uint32_t>(delay);
 }
 
 /* static */ void
 nsRefreshDriver::InitializeStatics()
 {
+  PodArrayZero(sJankLevels);
 }
 
 /* static */ void
 nsRefreshDriver::Shutdown()
 {
-  // clean up our timers
-  delete sRegularRateTimer;
-  delete sThrottledRateTimer;
-
-  sRegularRateTimer = nullptr;
   sThrottledRateTimer = nullptr;
 
 #ifdef XP_WIN
   if (sDisableHighPrecisionTimersTimer) {
     sDisableHighPrecisionTimersTimer->Cancel();
     NS_RELEASE(sDisableHighPrecisionTimersTimer);
     timeEndPeriod(1);
   } else if (sHighPrecisionTimerRequests) {
     timeEndPeriod(1);
   }
 #endif
 }
 
-/* static */ int32_t
+/* static */ double
 nsRefreshDriver::DefaultInterval()
 {
-  return NSToIntRound(1000.0 / gfxPlatform::GetDefaultFrameRate());
+  return GetRegularTimerInterval();
 }
 
-// Compute the interval to use for the refresh driver timer, in milliseconds.
-// outIsDefault indicates that rate was not explicitly set by the user
-// so we might choose other, more appropriate rates (e.g. vsync, etc)
-// layout.frame_rate=0 indicates "ASAP mode".
-// In ASAP mode rendering is iterated as fast as possible (typically for stress testing).
-// A target rate of 10k is used internally instead of special-handling 0.
-// Backends which block on swap/present/etc should try to not block
-// when layout.frame_rate=0 - to comply with "ASAP" as much as possible.
-double
-nsRefreshDriver::GetRegularTimerInterval(bool *outIsDefault) const
+// Compute the interval to use for the non-vsync refresh
+// driver timer, in milliseconds.  This timer is only used
+// when a vsync timer is not available, or for documents
+// not attached to a widget.
+/* static */ double
+nsRefreshDriver::GetRegularTimerInterval()
 {
-  int32_t rate = Preferences::GetInt("layout.frame_rate", -1);
-  if (rate < 0) {
-    rate = gfxPlatform::GetDefaultFrameRate();
-    if (outIsDefault) {
-      *outIsDefault = true;
-    }
-  } else {
-    if (outIsDefault) {
-      *outIsDefault = false;
-    }
-  }
-
-  if (rate == 0) {
-    rate = 10000;
-  }
-
-  return 1000.0 / rate;
+  return gfxPlatform::GetSoftwareVsyncInterval().ToMilliseconds();
 }
 
 /* static */ double
 nsRefreshDriver::GetThrottledTimerInterval()
 {
   int32_t rate = Preferences::GetInt("layout.throttled_frame_rate", -1);
   if (rate <= 0) {
     rate = DEFAULT_THROTTLED_FRAME_RATE;
@@ -973,40 +826,16 @@ nsRefreshDriver::GetMinRecomputeVisibili
 }
 
 double
 nsRefreshDriver::GetRefreshTimerInterval() const
 {
   return mThrottled ? GetThrottledTimerInterval() : GetRegularTimerInterval();
 }
 
-RefreshDriverTimer*
-nsRefreshDriver::ChooseTimer() const
-{
-  if (mThrottled) {
-    if (!sThrottledRateTimer)
-      sThrottledRateTimer = new InactiveRefreshDriverTimer(GetThrottledTimerInterval(),
-                                                           DEFAULT_INACTIVE_TIMER_DISABLE_SECONDS * 1000.0);
-    return sThrottledRateTimer;
-  }
-
-  if (!sRegularRateTimer) {
-    bool isDefault = true;
-    double rate = GetRegularTimerInterval(&isDefault);
-
-    // Try to use vsync-base refresh timer first for sRegularRateTimer.
-    CreateVsyncRefreshTimer();
-
-    if (!sRegularRateTimer) {
-      sRegularRateTimer = new StartupRefreshDriverTimer(rate);
-    }
-  }
-  return sRegularRateTimer;
-}
-
 nsRefreshDriver::nsRefreshDriver(nsPresContext* aPresContext)
   : mActiveTimer(nullptr),
     mReflowCause(nullptr),
     mStyleCause(nullptr),
     mPresContext(aPresContext),
     mRootRefresh(nullptr),
     mPendingTransaction(0),
     mCompletedTransaction(0),
@@ -1023,23 +852,32 @@ nsRefreshDriver::nsRefreshDriver(nsPresC
     mWaitingForTransaction(false),
     mSkippedPaints(false)
 {
   mMostRecentRefreshEpochTime = JS_Now();
   mMostRecentRefresh = TimeStamp::Now();
   mMostRecentTick = mMostRecentRefresh;
   mNextThrottledFrameRequestTick = mMostRecentTick;
   mNextRecomputeVisibilityTick = mMostRecentTick;
+
+  if (!sThrottledRateTimer) {
+    sThrottledRateTimer = new InactiveRefreshDriverTimer(GetThrottledTimerInterval(),
+                                                         DEFAULT_INACTIVE_TIMER_DISABLE_SECONDS * 1000.0);
+  }
 }
 
 nsRefreshDriver::~nsRefreshDriver()
 {
   MOZ_ASSERT(ObserverCount() == 0,
              "observers should have unregistered");
-  MOZ_ASSERT(!mActiveTimer, "timer should be gone");
+
+  if (mActiveTimer) {
+    mActiveTimer->RemoveRefreshDriver(this);
+    mActiveTimer = nullptr;
+  }
 
   if (mRootRefresh) {
     mRootRefresh->RemoveRefreshObserver(this, Flush_Style);
     mRootRefresh = nullptr;
   }
   for (nsIPresShell* shell : mPresShellsToInvalidateIfHidden) {
     shell->InvalidatePresShellIfHidden();
   }
@@ -1190,24 +1028,35 @@ nsRefreshDriver::EnsureTimerStarted(Ensu
     nsIURI* uri = mPresContext->Document()->GetDocumentURI();
     if (!uri || !IsFontTableURI(uri)) {
       MOZ_ASSERT(!mActiveTimer,
                  "image doc refresh driver should never have its own timer");
       return;
     }
   }
 
-  // We got here because we're either adjusting the time *or* we're
-  // starting it for the first time.  Add to the right timer,
-  // prehaps removing it from a previously-set one.
-  RefreshDriverTimer *newTimer = ChooseTimer();
-  if (newTimer != mActiveTimer) {
-    if (mActiveTimer)
+  nsIWidget *w = mPresContext->GetRootWidget();
+  RefPtr<RefreshDriverTimer> targetTimer = nullptr;
+  if (w) {
+    targetTimer = static_cast<RefreshDriverTimer*>(w->GetRefreshDriverTimer());
+    if (!targetTimer) {
+      targetTimer = new WidgetVsyncRefreshDriverTimer(w);
+      w->SetRefreshDriverTimer(targetTimer);
+    }
+  } else {
+    // no widget, so just use the throttled timer
+    targetTimer = sThrottledRateTimer;
+  }
+
+  if (targetTimer != mActiveTimer) {
+    if (mActiveTimer) {
       mActiveTimer->RemoveRefreshDriver(this);
-    mActiveTimer = newTimer;
+    }
+
+    mActiveTimer = targetTimer;
     mActiveTimer->AddRefreshDriver(this);
   }
 
   // When switching from an inactive timer to an active timer, the root
   // refresh driver is skipped due to being set to the content refresh
   // driver's timestamp. In case of EnsureTimerStarted is called from
   // ScheduleViewManagerFlush, we should avoid this behavior to flush
   // a paint in the same tick on the root refresh driver.
@@ -1232,21 +1081,20 @@ nsRefreshDriver::EnsureTimerStarted(Ensu
     ? mActiveTimer->MostRecentRefreshEpochTime()
     : std::max(mActiveTimer->MostRecentRefreshEpochTime(),
                mMostRecentRefreshEpochTime);
 }
 
 void
 nsRefreshDriver::StopTimer()
 {
-  if (!mActiveTimer)
-    return;
-
-  mActiveTimer->RemoveRefreshDriver(this);
-  mActiveTimer = nullptr;
+  if (mActiveTimer) {
+    mActiveTimer->RemoveRefreshDriver(this);
+    mActiveTimer = nullptr;
+  }
 
   if (mRequestedHighPrecision) {
     SetHighPrecisionTimersEnabled(false);
   }
 }
 
 #ifdef XP_WIN
 static void
@@ -2107,33 +1955,16 @@ nsRefreshDriver::SetThrottled(bool aThro
     if (mActiveTimer) {
       // We want to switch our timer type here, so just stop and
       // restart the timer.
       EnsureTimerStarted(eForceAdjustTimer);
     }
   }
 }
 
-/*static*/ void
-nsRefreshDriver::PVsyncActorCreated(VsyncChild* aVsyncChild)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(!XRE_IsParentProcess());
-  VsyncRefreshDriverTimer* vsyncRefreshDriverTimer =
-                           new VsyncRefreshDriverTimer(aVsyncChild);
-
-  // If we are using software timer, swap current timer to
-  // VsyncRefreshDriverTimer.
-  if (sRegularRateTimer) {
-    sRegularRateTimer->SwapRefreshDrivers(vsyncRefreshDriverTimer);
-    delete sRegularRateTimer;
-  }
-  sRegularRateTimer = vsyncRefreshDriverTimer;
-}
-
 void
 nsRefreshDriver::DoRefresh()
 {
   // Don't do a refresh unless we're in a state where we should be refreshing.
   if (!IsFrozen() && mPresContext && mActiveTimer) {
     DoTick();
   }
 }
--- a/layout/base/nsRefreshDriver.h
+++ b/layout/base/nsRefreshDriver.h
@@ -21,16 +21,17 @@
 #include "nsTHashtable.h"
 #include "nsTObserverArray.h"
 #include "nsClassHashtable.h"
 #include "nsHashKeys.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Maybe.h"
 #include "GeckoProfiler.h"
 #include "mozilla/layers/TransactionIdAllocator.h"
+#include "RefreshDriverTimerBase.h"
 
 class nsPresContext;
 class nsIPresShell;
 class nsIDocument;
 class imgIRequest;
 class nsIDOMEvent;
 class nsINode;
 
@@ -295,17 +296,17 @@ public:
    */
   bool IsRefreshObserver(nsARefreshObserver *aObserver,
 			   mozFlushType aFlushType);
 #endif
 
   /**
    * Default interval the refresh driver uses, in ms.
    */
-  static int32_t DefaultInterval();
+  static double DefaultInterval();
 
   bool IsInRefresh() { return mInRefresh; }
 
   /**
    * The latest value of process-wide jank levels.
    *
    * For each i, sJankLevels[i] counts the number of times delivery of
    * vsync to the main thread has been delayed by at least 2^i
@@ -326,16 +327,17 @@ public:
   mozilla::TimeStamp GetTransactionStart() override;
 
   bool IsWaitingForPaint(mozilla::TimeStamp aTime);
 
   // nsARefreshObserver
   NS_IMETHOD_(MozExternalRefCountType) AddRef(void) override { return TransactionIdAllocator::AddRef(); }
   NS_IMETHOD_(MozExternalRefCountType) Release(void) override { return TransactionIdAllocator::Release(); }
   virtual void WillRefresh(mozilla::TimeStamp aTime) override;
+
 private:
   typedef nsTObserverArray<nsARefreshObserver*> ObserverArray;
   typedef nsTHashtable<nsISupportsHashKey> RequestTable;
   struct ImageStartData {
     ImageStartData()
     {
     }
 
@@ -361,29 +363,28 @@ private:
 
   uint32_t ObserverCount() const;
   uint32_t ImageRequestCount() const;
   ObserverArray& ArrayFor(mozFlushType aFlushType);
   // Trigger a refresh immediately, if haven't been disconnected or frozen.
   void DoRefresh();
 
   double GetRefreshTimerInterval() const;
-  double GetRegularTimerInterval(bool *outIsDefault = nullptr) const;
+  static double GetRegularTimerInterval();
   static double GetThrottledTimerInterval();
 
   static mozilla::TimeDuration GetMinRecomputeVisibilityInterval();
 
   bool HaveFrameRequestCallbacks() const {
     return mFrameRequestCallbackDocs.Length() != 0;
   }
 
   void FinishedWaitingForTransaction();
 
-  mozilla::RefreshDriverTimer* ChooseTimer() const;
-  mozilla::RefreshDriverTimer* mActiveTimer;
+  RefPtr<mozilla::RefreshDriverTimer> mActiveTimer;
 
   ProfilerBacktrace* mReflowCause;
   ProfilerBacktrace* mStyleCause;
 
   nsPresContext *mPresContext; // weak; pres context passed in constructor
                                // and unset in Disconnect
 
   RefPtr<nsRefreshDriver> mRootRefresh;
--- a/widget/nsBaseWidget.cpp
+++ b/widget/nsBaseWidget.cpp
@@ -1582,16 +1582,19 @@ void
 nsBaseWidget::DestroyVsync()
 {
   VSYNC_LOG("BW[%p]::DestroyVsync(obs %p)\n", this, mIncomingVsyncObserver.get());
 
   if (mIncomingVsyncObserver) {
     mIncomingVsyncObserver->Destroy();
     mIncomingVsyncObserver = nullptr;
   }
+
+  // break cycle
+  mRefreshDriverTimer = nullptr;
 }
 
 void nsBaseWidget::CreateCompositor(int aWidth, int aHeight)
 {
   // This makes sure that gfxPlatforms gets initialized if it hasn't by now.
   gfxPlatform::GetPlatform();
 
   MOZ_ASSERT(gfxPlatform::UsesOffMainThreadCompositing(),
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -26,16 +26,17 @@
 #include "mozilla/TimeStamp.h"
 #include "mozilla/gfx/Point.h"
 #include "mozilla/widget/IMEData.h"
 #include "nsDataHashtable.h"
 #include "nsIObserver.h"
 #include "nsIWidgetListener.h"
 #include "FrameMetrics.h"
 #include "Units.h"
+#include "RefreshDriverTimerBase.h"
 
 // forward declarations
 class   nsIRollupListener;
 class   imgIContainer;
 class   nsIContent;
 class   ViewWrapper;
 class   nsIScreen;
 class   nsIRunnable;
@@ -53,16 +54,19 @@ class AsyncDragMetrics;
 class Composer2D;
 class Compositor;
 class CompositorBridgeChild;
 class LayerManager;
 class LayerManagerComposite;
 class PLayerTransactionChild;
 struct ScrollableLayerGuid;
 } // namespace layers
+namespace layout {
+class RefreshDriverTimerBase;
+} // namespace layout
 namespace gfx {
 class DrawTarget;
 class SourceSurface;
 class VRHMDInfo;
 class VsyncObserver;
 } // namespace gfx
 namespace widget {
 class TextEventDispatcher;
@@ -562,16 +566,19 @@ class nsIWidget : public nsISupports {
      * notification may also arrive on any thread -- if your observer
      * needs to execute on the main thread, make sure it forwards an
      * event to the main thread.  It is also safe to add/remove
      * observers from a vsync observer callback.
      */
     virtual void AddVsyncObserver(mozilla::gfx::VsyncObserver *aObserver) { }
     virtual void RemoveVsyncObserver(mozilla::gfx::VsyncObserver *aObserver) { }
 
+    virtual mozilla::layout::RefreshDriverTimerBase* GetRefreshDriverTimer() { return mRefreshDriverTimer; }
+    virtual void SetRefreshDriverTimer(mozilla::layout::RefreshDriverTimerBase* aTimer) { mRefreshDriverTimer = aTimer; }
+
     /**
      * Return the nsID for the source that this widget is observing vsync from.
      */
     virtual nsID GetVsyncSourceIdentifier();
 
     /**
      * A VsyncSource ID understood by widget to mean "listen to the parent widget's
      * vsync, or if no parent widget, then an appropriate physical display".
@@ -2111,13 +2118,15 @@ protected:
     nsCOMPtr<nsIWidget> mFirstChild;
     nsIWidget* MOZ_NON_OWNING_REF mLastChild;
     nsCOMPtr<nsIWidget> mNextSibling;
     nsIWidget* MOZ_NON_OWNING_REF mPrevSibling;
     // When Destroy() is called, the sub class should set this true.
     bool mOnDestroyCalled;
     nsWindowType mWindowType;
     int32_t mZIndex;
+
+    RefPtr<mozilla::layout::RefreshDriverTimerBase> mRefreshDriverTimer;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIWidget, NS_IWIDGET_IID)
 
 #endif // nsIWidget_h__