Backout 16ae4d5d27d7 for mochitest-other failures in test_mousescroll.xul
authorEd Morley <emorley@mozilla.com>
Tue, 02 Oct 2012 17:38:49 +0100
changeset 109020 c0ef7fe877d2b76c482fa838b281570357aa5b2c
parent 109019 6b900e03eb58a0c9b26d889dfd6692f436ee2e59
child 109021 b36ff62f95b3acb9dee48a8dcb0bea6d120d0d5f
push id82
push usershu@rfrn.org
push dateFri, 05 Oct 2012 13:20:22 +0000
milestone18.0a1
backs out16ae4d5d27d702df5c95c7278ad1b7e4b9235e86
Backout 16ae4d5d27d7 for mochitest-other failures in test_mousescroll.xul
layout/base/nsRefreshDriver.cpp
layout/base/nsRefreshDriver.h
layout/build/nsLayoutStatics.cpp
xpcom/ds/TimeStamp.h
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -2,37 +2,21 @@
 /* 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/. */
 
 /*
  * Code to notify things that animate before a refresh, at an appropriate
  * refresh rate.  (Perhaps temporary, until replaced by compositor.)
- *
- * Chrome and each tab have their own RefreshDriver, which in turn
- * hooks into one of a few global timer based on RefreshDriverTimer,
- * defined below.  There are two main global timers -- one for active
- * animations, and one for inactive ones.  These are implemented as
- * subclasses of RefreshDriverTimer; see below for a description of
- * their implementations.  In the future, additional timer types may
- * implement things like blocking on vsync.
  */
 
-#ifdef XP_WIN
-#include <windows.h>
-// mmsystem isn't part of WIN32_LEAN_AND_MEAN, so we have
-// to manually include it
-#include <mmsystem.h>
-#endif
-
 #include "mozilla/Util.h"
 
 #include "nsRefreshDriver.h"
-#include "nsITimer.h"
 #include "nsPresContext.h"
 #include "nsComponentManagerUtils.h"
 #include "prlog.h"
 #include "nsAutoPtr.h"
 #include "nsCSSFrameConstructor.h"
 #include "nsIDocument.h"
 #include "nsGUIEvent.h"
 #include "nsEventDispatcher.h"
@@ -42,533 +26,148 @@
 #include "nsIViewManager.h"
 #include "sampler.h"
 
 using mozilla::TimeStamp;
 using mozilla::TimeDuration;
 
 using namespace mozilla;
 
-#ifdef PR_LOGGING
-static PRLogModuleInfo *gLog = nullptr;
-#define LOG(...) PR_LOG(gLog, PR_LOG_NOTICE, (__VA_ARGS__))
-#else
-#define LOG(...) do { } while(0)
-#endif
-
 #define DEFAULT_FRAME_RATE 60
 #define DEFAULT_THROTTLED_FRAME_RATE 1
-// after 10 minutes, stop firing off inactive timers
-#define DEFAULT_INACTIVE_TIMER_DISABLE_SECONDS 600
 
-namespace mozilla {
-
-/*
- * The base class for all global refresh driver timers.  It takes care
- * 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 {
-public:
-  /*
-   * aRate -- the delay, in milliseconds, requested between timer firings
-   */
-  RefreshDriverTimer(double aRate)
-  {
-    SetRate(aRate);
-  }
-
-  virtual ~RefreshDriverTimer()
-  {
-    NS_ASSERTION(mRefreshDrivers.Length() == 0, "Should have removed all refresh drivers from here by now!");
-  }
-
-  virtual void AddRefreshDriver(nsRefreshDriver* aDriver)
-  {
-    NS_ASSERTION(!mRefreshDrivers.Contains(aDriver), "AddRefreshDriver for a refresh driver that's already in the list!");
-    mRefreshDrivers.AppendElement(aDriver);
-
-    if (mRefreshDrivers.Length() == 1) {
-      StartTimer();
-    }
-  }
-
-  virtual void RemoveRefreshDriver(nsRefreshDriver* aDriver)
-  {
-    NS_ASSERTION(mRefreshDrivers.Contains(aDriver), "RemoveRefreshDriver for a refresh driver that's not in the list!");
-    mRefreshDrivers.RemoveElement(aDriver);
-
-    if (mRefreshDrivers.Length() == 0) {
-      StopTimer();
-    }
-  }
-
-  double GetRate() const
-  {
-    return mRateMilliseconds;
-  }
-
-  // will take effect at next timer tick
-  virtual void SetRate(double aNewRate)
-  {
-    mRateMilliseconds = aNewRate;
-    mRateDuration = TimeDuration::FromMilliseconds(mRateMilliseconds);
-  }
-
-  TimeStamp MostRecentRefresh() const { return mLastFireTime; }
-  int64_t MostRecentRefreshEpochTime() const { return mLastFireEpoch; }
-
-protected:
-  virtual void StartTimer() = 0;
-  virtual void StopTimer() = 0;
-  virtual void ScheduleNextTick(TimeStamp aNowTime) = 0;
-
-  /*
-   * Actually runs a tick, poking all the attached RefreshDrivers.
-   * Grabs the "now" time via JS_Now and TimeStamp::Now().
-   */
-  void Tick()
-  {
-    int64_t jsnow = JS_Now();
-    TimeStamp now = TimeStamp::Now();
-
-    ScheduleNextTick(now);
-
-    mLastFireEpoch = jsnow;
-    mLastFireTime = now;
-
-    nsTArray<nsRefPtr<nsRefreshDriver> > drivers(mRefreshDrivers);
-    for (size_t i = 0; i < drivers.Length(); ++i) {
-      // don't poke this driver if it's in test mode
-      if (drivers[i]->IsTestControllingRefreshesEnabled()) {
-        continue;
-      }
-
-      TickDriver(drivers[i], jsnow, now);
-    }
-  }
-
-  static void TickDriver(nsRefreshDriver* driver, int64_t jsnow, TimeStamp now)
-  {
-    driver->Tick(jsnow, now);
-  }
-
-  double mRateMilliseconds;
-  TimeDuration mRateDuration;
-
-  int64_t mLastFireEpoch;
-  TimeStamp mLastFireTime;
-  TimeStamp mTargetTime;
-
-  nsTArray<nsRefPtr<nsRefreshDriver> > mRefreshDrivers;
-
-  // useful callback for nsITimer-based derived classes, here
-  // bacause of c++ protected shenanigans
-  static void TimerTick(nsITimer* aTimer, void* aClosure)
-  {
-    RefreshDriverTimer *timer = static_cast<RefreshDriverTimer*>(aClosure);
-    timer->Tick();
-  }
-};
-
-/*
- * A RefreshDriverTimer that uses a nsITimer as the underlying timer.  Note that
- * 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
-{
-public:
-  SimpleTimerBasedRefreshDriverTimer(double aRate)
-    : RefreshDriverTimer(aRate)
-  {
-    mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
-  }
-
-  virtual ~SimpleTimerBasedRefreshDriverTimer()
-  {
-    StopTimer();
-  }
-
-protected:
-
-  virtual void StartTimer()
-  {
-    mLastFireTime = TimeStamp::Now();
-    mTargetTime = mLastFireTime;
-
-    mTimer->InitWithFuncCallback(TimerTick, this, 0, nsITimer::TYPE_ONE_SHOT);
-  }
-
-  virtual void StopTimer()
-  {
-    mTimer->Cancel();
-  }
-
-  nsRefPtr<nsITimer> mTimer;
-};
-
-/*
- * 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 PreciseRefreshDriverTimer :
-    public SimpleTimerBasedRefreshDriverTimer
-{
-public:
-  PreciseRefreshDriverTimer(double aRate)
-    : SimpleTimerBasedRefreshDriverTimer(aRate)
-  {
-  }
-
-protected:
-  virtual void ScheduleNextTick(TimeStamp aNowTime)
-  {
-    // The number of (whole) elapsed intervals between the last target
-    // time and the actual time.  We want to truncate the double down
-    // to an int number of intervals.
-    int numElapsedIntervals = static_cast<int>((aNowTime - mTargetTime) / mRateDuration);
-
-    // the last "tick" that may or may not have been actually sent was
-    // at this time.  For example, if the rate is 15ms, the target
-    // time is 200ms, and it's now 225ms, the last effective tick
-    // would have been at 215ms.  The next one should then be
-    // scheduled for 5 ms from now.
-    //
-    // We then add another mRateDuration to find the next tick target.
-    TimeStamp newTarget = mTargetTime + mRateDuration * (numElapsedIntervals + 1);
-
-    // the amount of (integer) ms until the next time we should tick
-    uint32_t delay = static_cast<uint32_t>((newTarget - aNowTime).ToMilliseconds());
-
-    // Without this block, we'll always schedule on interval ticks;
-    // with it, we'll schedule immediately if we missed our tick target
-    // last time.
-#if 0
-    if (numElapsedIntervals > 0) {
-      // we're late, so reset
-      newTarget = aNowTime;
-      delay = 0;
-    }
-#endif
-
-    // log info & lateness
-    LOG("[%p] precise timer last tick late by %f ms, next tick in %d ms",
-        this,
-        (aNowTime - mTargetTime).ToMilliseconds(),
-        delay);
-
-    // then schedule the timer
-    mTimer->InitWithFuncCallback(TimerTick, this, delay, nsITimer::TYPE_ONE_SHOT);
-
-    mTargetTime = newTarget;
-  }
-};
-
-/*
- * A RefreshDriverTimer for inactive documents.  When a new refresh driver is
- * added, the rate is reset to the base (normally 1s/1fps).  Every time
- * it ticks, a single refresh driver is poked.  Once they have all been poked,
- * the duration between ticks doubles, up to mDisableAfterMilliseconds.  At that point,
- * the timer is quiet and doesn't tick (until something is added to it again).
- *
- * When a timer is removed, there is a possibility of another timer
- * being skipped for one cycle.  We could avoid this by adjusting
- * mNextDriverIndex in RemoveRefreshDriver, but there's little need to
- * add that complexity.  All we want is for inactive drivers to tick
- * at some point, but we don't care too much about how often.
- */
-class InactiveRefreshDriverTimer :
-    public RefreshDriverTimer
-{
-public:
-  InactiveRefreshDriverTimer(double aRate)
-    : RefreshDriverTimer(aRate),
-      mNextTickDuration(aRate),
-      mDisableAfterMilliseconds(-1.0),
-      mNextDriverIndex(0)
-  {
-    mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
-  }
-
-  InactiveRefreshDriverTimer(double aRate, double aDisableAfterMilliseconds)
-    : RefreshDriverTimer(aRate),
-      mNextTickDuration(aRate),
-      mDisableAfterMilliseconds(aDisableAfterMilliseconds),
-      mNextDriverIndex(0)
-  {
-    mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
-  }
-
-  virtual void AddRefreshDriver(nsRefreshDriver* aDriver)
-  {
-    RefreshDriverTimer::AddRefreshDriver(aDriver);
-
-    LOG("[%p] inactive timer got new refresh driver %p, resetting rate",
-        this, aDriver);
-
-    // reset the timer, and start with the newly added one next time.
-    mNextTickDuration = mRateMilliseconds;
-
-    // we don't really have to start with the newly added one, but we may as well
-    // not tick the old ones at the fastest rate any more than we need to.
-    mNextDriverIndex = mRefreshDrivers.Length() - 1;
-
-    StopTimer();
-    StartTimer();
-  }
-
-protected:
-  virtual void StartTimer()
-  {
-    mLastFireTime = TimeStamp::Now();
-    mTargetTime = mLastFireTime;
-
-    mTimer->InitWithFuncCallback(TimerTickOne, this, 0, nsITimer::TYPE_ONE_SHOT);
-  }
-
-  virtual void StopTimer()
-  {
-    mTimer->Cancel();
-  }
-
-  virtual void ScheduleNextTick(TimeStamp aNowTime)
-  {
-    if (mDisableAfterMilliseconds > 0.0 &&
-        mNextTickDuration > mDisableAfterMilliseconds)
-    {
-      // We hit the time after which we should disable
-      // inactive window refreshes; don't schedule anything
-      // until we get kicked by an AddRefreshDriver call.
-      return;
-    }
-
-    // double the next tick time if we've already gone through all of them once
-    if (mNextDriverIndex >= mRefreshDrivers.Length()) {
-      mNextTickDuration *= 2.0;
-      mNextDriverIndex = 0;
-    }
-
-    // this doesn't need to be precise; do a simple schedule
-    uint32_t delay = static_cast<uint32_t>(mNextTickDuration);
-    mTimer->InitWithFuncCallback(TimerTickOne, this, delay, nsITimer::TYPE_ONE_SHOT);
-
-    LOG("[%p] inactive timer next tick in %f ms [index %d/%d]", this, mNextTickDuration,
-        mNextDriverIndex, mRefreshDrivers.Length());
-  }
-
-  /* Runs just one driver's tick. */
-  void TickOne()
-  {
-    int64_t jsnow = JS_Now();
-    TimeStamp now = TimeStamp::Now();
-
-    ScheduleNextTick(now);
-
-    mLastFireEpoch = jsnow;
-    mLastFireTime = now;
-
-    nsTArray<nsRefPtr<nsRefreshDriver> > drivers(mRefreshDrivers);
-    if (mNextDriverIndex < drivers.Length() &&
-        !drivers[mNextDriverIndex]->IsTestControllingRefreshesEnabled())
-    {
-      TickDriver(drivers[mNextDriverIndex], jsnow, now);
-    }
-
-    mNextDriverIndex++;
-  }
-
-  static void TimerTickOne(nsITimer* aTimer, void* aClosure)
-  {
-    InactiveRefreshDriverTimer *timer = static_cast<InactiveRefreshDriverTimer*>(aClosure);
-    timer->TickOne();
-  }
-
-  nsRefPtr<nsITimer> mTimer;
-  double mNextTickDuration;
-  double mDisableAfterMilliseconds;
-  uint32_t mNextDriverIndex;
-};
-
-} // namespace mozilla
-
-static PreciseRefreshDriverTimer *sRegularRateTimer = nullptr;
-static InactiveRefreshDriverTimer *sThrottledRateTimer = nullptr;
-
-static int32_t sHighPrecisionTimerRequests = 0;
+static bool sPrecisePref;
 
 /* static */ void
 nsRefreshDriver::InitializeStatics()
 {
-#ifdef PR_LOGGING
-  if (!gLog) {
-    gLog = PR_NewLogModule("nsRefreshDriver");
-  }
-#endif
-}
-
-/* static */ void
-nsRefreshDriver::Shutdown()
-{
-  // clean up our timers
-  delete sRegularRateTimer;
-  delete sThrottledRateTimer;
-
-  sRegularRateTimer = nullptr;
-  sThrottledRateTimer = nullptr;
+  Preferences::AddBoolVarCache(&sPrecisePref,
+                               "layout.frame_rate.precise",
+                               false);
 }
 
 /* static */ int32_t
 nsRefreshDriver::DefaultInterval()
 {
   return NSToIntRound(1000.0 / DEFAULT_FRAME_RATE);
 }
 
 // Compute the interval to use for the refresh driver timer, in
 // milliseconds
-double
-nsRefreshDriver::GetRegularTimerInterval() const
+int32_t
+nsRefreshDriver::GetRefreshTimerInterval() const
 {
-  int32_t rate = Preferences::GetInt("layout.frame_rate", -1);
+  const char* prefName =
+    mThrottled ? "layout.throttled_frame_rate" : "layout.frame_rate";
+  int32_t rate = Preferences::GetInt(prefName, -1);
   if (rate <= 0) {
     // TODO: get the rate from the platform
-    rate = DEFAULT_FRAME_RATE;
+    rate = mThrottled ? DEFAULT_THROTTLED_FRAME_RATE : DEFAULT_FRAME_RATE;
   }
-  return 1000.0 / rate;
-}
-
-double
-nsRefreshDriver::GetThrottledTimerInterval() const
-{
-  int32_t rate = Preferences::GetInt("layout.throttled_frame_rate", -1);
-  if (rate <= 0) {
-    rate = DEFAULT_THROTTLED_FRAME_RATE;
+  NS_ASSERTION(rate > 0, "Must have positive rate here");
+  int32_t interval = NSToIntRound(1000.0/rate);
+  if (mThrottled) {
+    interval = NS_MAX(interval, mLastTimerInterval * 2);
   }
-  return 1000.0 / rate;
+  mLastTimerInterval = interval;
+  return interval;
 }
 
-double
-nsRefreshDriver::GetRefreshTimerInterval() const
-{
-  return mThrottled ? GetThrottledTimerInterval() : GetRegularTimerInterval();
-}
-
-RefreshDriverTimer*
-nsRefreshDriver::ChooseTimer() const
+int32_t
+nsRefreshDriver::GetRefreshTimerType() const
 {
   if (mThrottled) {
-    if (!sThrottledRateTimer) 
-      sThrottledRateTimer = new InactiveRefreshDriverTimer(GetThrottledTimerInterval(),
-                                                           DEFAULT_INACTIVE_TIMER_DISABLE_SECONDS * 1000.0);
-    return sThrottledRateTimer;
+    return nsITimer::TYPE_ONE_SHOT;
   }
-
-  if (!sRegularRateTimer)
-    sRegularRateTimer = new PreciseRefreshDriverTimer(GetRegularTimerInterval());
-  return sRegularRateTimer;
+  if (HaveFrameRequestCallbacks() || sPrecisePref) {
+    return nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP;
+  }
+  return nsITimer::TYPE_REPEATING_SLACK;
 }
 
-nsRefreshDriver::nsRefreshDriver(nsPresContext* aPresContext)
+nsRefreshDriver::nsRefreshDriver(nsPresContext *aPresContext)
   : mPresContext(aPresContext),
-    mActiveTimer(nullptr),
     mFrozen(false),
     mThrottled(false),
     mTestControllingRefreshes(false),
+    mTimerIsPrecise(false),
     mViewManagerFlushIsPending(false),
-    mRequestedHighPrecision(false)
+    mLastTimerInterval(0)
 {
-  mMostRecentRefreshEpochTime = JS_Now();
-  mMostRecentRefresh = TimeStamp::Now();
-
   mRequests.Init();
 }
 
 nsRefreshDriver::~nsRefreshDriver()
 {
   NS_ABORT_IF_FALSE(ObserverCount() == 0,
                     "observers should have unregistered");
-  NS_ABORT_IF_FALSE(!mActiveTimer, "timer should be gone");
+  NS_ABORT_IF_FALSE(!mTimer, "timer should be gone");
   
   for (uint32_t i = 0; i < mPresShellsToInvalidateIfHidden.Length(); i++) {
     mPresShellsToInvalidateIfHidden[i]->InvalidatePresShellIfHidden();
   }
   mPresShellsToInvalidateIfHidden.Clear();
 }
 
 // Method for testing.  See nsIDOMWindowUtils.advanceTimeAndRefresh
 // for description.
 void
 nsRefreshDriver::AdvanceTimeAndRefresh(int64_t aMilliseconds)
 {
-  // ensure that we're removed from our driver
-  StopTimer();
-
-  if (!mTestControllingRefreshes) {
-    mMostRecentRefreshEpochTime = JS_Now();
-    mMostRecentRefresh = TimeStamp::Now();
-
-    mTestControllingRefreshes = true;
-  }
-
+  mTestControllingRefreshes = true;
   mMostRecentRefreshEpochTime += aMilliseconds * 1000;
-  mMostRecentRefresh += TimeDuration::FromMilliseconds((double) aMilliseconds);
-
+  mMostRecentRefresh += TimeDuration::FromMilliseconds(aMilliseconds);
   nsCxPusher pusher;
   if (pusher.PushNull()) {
-    DoTick();
+    Notify(nullptr);
     pusher.Pop();
   }
 }
 
 void
 nsRefreshDriver::RestoreNormalRefresh()
 {
   mTestControllingRefreshes = false;
-  EnsureTimerStarted(false);
+  nsCxPusher pusher;
+  if (pusher.PushNull()) {
+    Notify(nullptr); // will call UpdateMostRecentRefresh()
+    pusher.Pop();
+  }
 }
 
 TimeStamp
 nsRefreshDriver::MostRecentRefresh() const
 {
+  const_cast<nsRefreshDriver*>(this)->EnsureTimerStarted(false);
+
   return mMostRecentRefresh;
 }
 
 int64_t
 nsRefreshDriver::MostRecentRefreshEpochTime() const
 {
+  const_cast<nsRefreshDriver*>(this)->EnsureTimerStarted(false);
+
   return mMostRecentRefreshEpochTime;
 }
 
 bool
-nsRefreshDriver::AddRefreshObserver(nsARefreshObserver* aObserver,
+nsRefreshDriver::AddRefreshObserver(nsARefreshObserver *aObserver,
                                     mozFlushType aFlushType)
 {
   ObserverArray& array = ArrayFor(aFlushType);
   bool success = array.AppendElement(aObserver) != nullptr;
 
   EnsureTimerStarted(false);
 
   return success;
 }
 
 bool
-nsRefreshDriver::RemoveRefreshObserver(nsARefreshObserver* aObserver,
+nsRefreshDriver::RemoveRefreshObserver(nsARefreshObserver *aObserver,
                                        mozFlushType aFlushType)
 {
   ObserverArray& array = ArrayFor(aFlushType);
   return array.RemoveElement(aObserver);
 }
 
 bool
 nsRefreshDriver::AddImageRequest(imgIRequest* aRequest)
@@ -591,77 +190,57 @@ nsRefreshDriver::RemoveImageRequest(imgI
 void nsRefreshDriver::ClearAllImageRequests()
 {
   mRequests.Clear();
 }
 
 void
 nsRefreshDriver::EnsureTimerStarted(bool aAdjustingTimer)
 {
-  if (mTestControllingRefreshes)
-    return;
-
-  // if the only change that's needed is that we need high precision,
-  // then just set that
-  if (!mThrottled && !mRequestedHighPrecision && mFrameRequestCallbackDocs.Length() > 0) {
-    SetHighPrecisionTimersEnabled(true);
-  } else if (mRequestedHighPrecision) {
-    SetHighPrecisionTimersEnabled(false);
-  }
-
-  // will it already fire, and no other changes needed?
-  if (mActiveTimer && !aAdjustingTimer)
-    return;
-
-  if (mFrozen || !mPresContext) {
-    // If we don't want to start it now, or we've been disconnected.
-    StopTimer();
+  if (mTimer || mFrozen || !mPresContext) {
+    // It's already been started, or we don't want to start it now or
+    // we've been disconnected.
     return;
   }
 
-  // we got here because we're either adjusting the time *or* we're
-  // starting it for the first time; make sure it's stopped.
-  StopTimer();
+  if (!aAdjustingTimer) {
+    // If we didn't already have a timer and aAdjustingTimer is false,
+    // then we just got our first observer (or an explicit call to
+    // MostRecentRefresh by a caller who's likely to add an observer
+    // shortly).  This means we should fake a most-recent-refresh time
+    // of now so that said observer gets a reasonable refresh time, so
+    // things behave as though the timer had always been running.
+    UpdateMostRecentRefresh();
+  }
 
-  mActiveTimer = ChooseTimer();
-  mActiveTimer->AddRefreshDriver(this);
+  mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
+  if (!mTimer) {
+    return;
+  }
+
+  int32_t timerType = GetRefreshTimerType();
+  mTimerIsPrecise = (timerType == nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP);
+
+  nsresult rv = mTimer->InitWithCallback(this,
+                                         GetRefreshTimerInterval(),
+                                         timerType);
+  if (NS_FAILED(rv)) {
+    mTimer = nullptr;
+  }
 }
 
 void
 nsRefreshDriver::StopTimer()
 {
-  if (!mActiveTimer)
+  if (!mTimer) {
     return;
-
-  mActiveTimer->RemoveRefreshDriver(this);
-  mActiveTimer = nullptr;
-
-  if (mRequestedHighPrecision) {
-    SetHighPrecisionTimersEnabled(false);
   }
-}
 
-void
-nsRefreshDriver::SetHighPrecisionTimersEnabled(bool aEnable)
-{
-  if (aEnable) {
-    NS_ASSERTION(!mRequestedHighPrecision, "SetHighPrecisionTimersEnabled(true) called when already requested!");
-#ifdef XP_WIN
-    if (++sHighPrecisionTimerRequests == 1)
-      timeBeginPeriod(1);
-#endif
-    mRequestedHighPrecision = true;
-  } else {
-    NS_ASSERTION(mRequestedHighPrecision, "SetHighPrecisionTimersEnabled(false) called when not requested!");
-#ifdef XP_WIN
-    if (--sHighPrecisionTimerRequests == 0)
-      timeEndPeriod(1);
-#endif
-    mRequestedHighPrecision = false;
-  }
+  mTimer->Cancel();
+  mTimer = nullptr;
 }
 
 uint32_t
 nsRefreshDriver::ObserverCount() const
 {
   uint32_t sum = 0;
   for (uint32_t i = 0; i < ArrayLength(mObservers); ++i) {
     sum += mObservers[i].Length();
@@ -679,16 +258,28 @@ nsRefreshDriver::ObserverCount() const
 }
 
 uint32_t
 nsRefreshDriver::ImageRequestCount() const
 {
   return mRequests.Count();
 }
 
+void
+nsRefreshDriver::UpdateMostRecentRefresh()
+{
+  if (mTestControllingRefreshes) {
+    return;
+  }
+
+  // Call JS_Now first, since that can have nonzero latency in some rare cases.
+  mMostRecentRefreshEpochTime = JS_Now();
+  mMostRecentRefresh = TimeStamp::Now();
+}
+
 nsRefreshDriver::ObserverArray&
 nsRefreshDriver::ArrayFor(mozFlushType aFlushType)
 {
   switch (aFlushType) {
     case Flush_Style:
       return mObservers[0];
     case Flush_Layout:
       return mObservers[1];
@@ -699,88 +290,82 @@ nsRefreshDriver::ArrayFor(mozFlushType a
       return *static_cast<ObserverArray*>(nullptr);
   }
 }
 
 /*
  * nsISupports implementation
  */
 
-NS_IMPL_ISUPPORTS1(nsRefreshDriver, nsISupports)
+NS_IMPL_ISUPPORTS1(nsRefreshDriver, nsITimerCallback)
 
 /*
  * nsITimerCallback implementation
  */
 
-void
-nsRefreshDriver::DoTick()
+NS_IMETHODIMP
+nsRefreshDriver::Notify(nsITimer *aTimer)
 {
+  SAMPLE_LABEL("nsRefreshDriver", "Notify");
+
   NS_PRECONDITION(!mFrozen, "Why are we notified while frozen?");
   NS_PRECONDITION(mPresContext, "Why are we notified after disconnection?");
   NS_PRECONDITION(!nsContentUtils::GetCurrentJSContext(),
                   "Shouldn't have a JSContext on the stack");
 
-  if (mTestControllingRefreshes) {
-    Tick(mMostRecentRefreshEpochTime, mMostRecentRefresh);
-  } else {
-    Tick(JS_Now(), TimeStamp::Now());
+  if (mTestControllingRefreshes && aTimer) {
+    // Ignore real refreshes from our timer (but honor the others).
+    return NS_OK;
   }
-}
 
-void
-nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime)
-{
-  SAMPLE_LABEL("nsRefreshDriver", "Tick");
-
-  mMostRecentRefresh = aNowTime;
-  mMostRecentRefreshEpochTime = aNowEpoch;
+  UpdateMostRecentRefresh();
 
   nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
   if (!presShell || (ObserverCount() == 0 && ImageRequestCount() == 0)) {
     // Things are being destroyed, or we no longer have any observers.
     // We don't want to stop the timer when observers are initially
     // removed, because sometimes observers can be added and removed
     // often depending on what other things are going on and in that
     // situation we don't want to thrash our timer.  So instead we
     // wait until we get a Notify() call when we have no observers
     // before stopping the timer.
     StopTimer();
-    return;
+    return NS_OK;
   }
 
   /*
    * The timer holds a reference to |this| while calling |Notify|.
    * However, implementations of |WillRefresh| are permitted to destroy
    * the pres context, which will cause our |mPresContext| to become
    * null.  If this happens, we must stop notifying observers.
    */
   for (uint32_t i = 0; i < ArrayLength(mObservers); ++i) {
     ObserverArray::EndLimitedIterator etor(mObservers[i]);
     while (etor.HasMore()) {
       nsRefPtr<nsARefreshObserver> obs = etor.GetNext();
-      obs->WillRefresh(aNowTime);
+      obs->WillRefresh(mMostRecentRefresh);
       
       if (!mPresContext || !mPresContext->GetPresShell()) {
         StopTimer();
-        return;
+        return NS_OK;
       }
     }
 
     if (i == 0) {
       // Grab all of our frame request callbacks up front.
       nsIDocument::FrameRequestCallbackList frameRequestCallbacks;
       for (uint32_t i = 0; i < mFrameRequestCallbackDocs.Length(); ++i) {
         mFrameRequestCallbackDocs[i]->
           TakeFrameRequestCallbacks(frameRequestCallbacks);
       }
       // OK, now reset mFrameRequestCallbackDocs so they can be
       // readded as needed.
       mFrameRequestCallbackDocs.Clear();
 
-      int64_t eventTime = aNowEpoch / PR_USEC_PER_MSEC;
+      int64_t eventTime = mMostRecentRefreshEpochTime / PR_USEC_PER_MSEC;
       for (uint32_t i = 0; i < frameRequestCallbacks.Length(); ++i) {
         nsAutoMicroTask mt;
         frameRequestCallbacks[i]->Sample(eventTime);
       }
 
       // This is the Flush_Style case.
       if (mPresContext && mPresContext->GetPresShell()) {
         nsAutoTArray<nsIPresShell*, 16> observers;
@@ -822,19 +407,20 @@ nsRefreshDriver::Tick(int64_t aNowEpoch,
     }
   }
 
   /*
    * Perform notification to imgIRequests subscribed to listen
    * for refresh events.
    */
 
-  ImageRequestParameters parms = {aNowTime};
+  ImageRequestParameters parms = {mMostRecentRefresh};
   if (mRequests.Count()) {
     mRequests.EnumerateEntries(nsRefreshDriver::ImageRequestEnumerator, &parms);
+    EnsureTimerStarted(false);
   }
     
   for (uint32_t i = 0; i < mPresShellsToInvalidateIfHidden.Length(); i++) {
     mPresShellsToInvalidateIfHidden[i]->InvalidatePresShellIfHidden();
   }
   mPresShellsToInvalidateIfHidden.Clear();
 
   if (mViewManagerFlushIsPending) {
@@ -842,16 +428,35 @@ nsRefreshDriver::Tick(int64_t aNowEpoch,
     printf("Starting ProcessPendingUpdates\n");
 #endif
     mViewManagerFlushIsPending = false;
     mPresContext->GetPresShell()->GetViewManager()->ProcessPendingUpdates();
 #ifdef DEBUG_INVALIDATIONS
     printf("Ending ProcessPendingUpdates\n");
 #endif
   }
+
+  if (mThrottled ||
+      (mTimerIsPrecise !=
+       (GetRefreshTimerType() == nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP))) {
+    // Stop the timer now and restart it here.  Stopping is in the mThrottled
+    // case ok because either it's already one-shot, and it just fired, and all
+    // we need to do is null it out, or it's repeating and we need to reset it
+    // to be one-shot.  Stopping and restarting in the case when we need to
+    // switch from precise to slack timers or vice versa is unfortunately
+    // required.
+
+    // Note that the EnsureTimerStarted() call here is ok because
+    // EnsureTimerStarted makes sure to not start the timer if it shouldn't be
+    // started.
+    StopTimer();
+    EnsureTimerStarted(true);
+  }
+
+  return NS_OK;
 }
 
 PLDHashOperator
 nsRefreshDriver::ImageRequestEnumerator(nsISupportsHashKey* aEntry,
                                         void* aUserArg)
 {
   ImageRequestParameters* parms =
     static_cast<ImageRequestParameters*> (aUserArg);
@@ -890,36 +495,37 @@ nsRefreshDriver::Thaw()
   }
 }
 
 void
 nsRefreshDriver::SetThrottled(bool aThrottled)
 {
   if (aThrottled != mThrottled) {
     mThrottled = aThrottled;
-    if (mActiveTimer) {
+    if (mTimer) {
       // We want to switch our timer type here, so just stop and
       // restart the timer.
+      StopTimer();
       EnsureTimerStarted(true);
     }
   }
 }
 
 void
 nsRefreshDriver::DoRefresh()
 {
   // Don't do a refresh unless we're in a state where we should be refreshing.
-  if (!mFrozen && mPresContext && mActiveTimer) {
-    DoTick();
+  if (!mFrozen && mPresContext && mTimer) {
+    Notify(nullptr);
   }
 }
 
 #ifdef DEBUG
 bool
-nsRefreshDriver::IsRefreshObserver(nsARefreshObserver* aObserver,
+nsRefreshDriver::IsRefreshObserver(nsARefreshObserver *aObserver,
                                    mozFlushType aFlushType)
 {
   ObserverArray& array = ArrayFor(aFlushType);
   return array.Contains(aObserver);
 }
 #endif
 
 void
@@ -933,18 +539,18 @@ nsRefreshDriver::ScheduleViewManagerFlus
 
 void
 nsRefreshDriver::ScheduleFrameRequestCallbacks(nsIDocument* aDocument)
 {
   NS_ASSERTION(mFrameRequestCallbackDocs.IndexOf(aDocument) ==
                mFrameRequestCallbackDocs.NoIndex,
                "Don't schedule the same document multiple times");
   mFrameRequestCallbackDocs.AppendElement(aDocument);
-
-  // make sure that the timer is running
+  // No need to worry about restarting our timer in precise mode if it's
+  // already running; that will happen automatically when it fires.
   EnsureTimerStarted(false);
 }
 
 void
 nsRefreshDriver::RevokeFrameRequestCallbacks(nsIDocument* aDocument)
 {
   mFrameRequestCallbackDocs.RemoveElement(aDocument);
   // No need to worry about restarting our timer in slack mode if it's already
--- a/layout/base/nsRefreshDriver.h
+++ b/layout/base/nsRefreshDriver.h
@@ -1,24 +1,24 @@
-/* -*- 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/. */
 
 /*
  * Code to notify things that animate before a refresh, at an appropriate
  * refresh rate.  (Perhaps temporary, until replaced by compositor.)
  */
 
 #ifndef nsRefreshDriver_h_
 #define nsRefreshDriver_h_
 
 #include "mozilla/TimeStamp.h"
 #include "mozFlushType.h"
+#include "nsITimer.h"
 #include "nsCOMPtr.h"
 #include "nsTObserverArray.h"
 #include "nsTArray.h"
 #include "nsAutoPtr.h"
 #include "nsTHashtable.h"
 #include "nsHashKeys.h"
 #include "mozilla/Attributes.h"
 
@@ -27,56 +27,49 @@ class nsIPresShell;
 class nsIDocument;
 class imgIRequest;
 
 /**
  * An abstract base class to be implemented by callers wanting to be
  * notified at refresh times.  When nothing needs to be painted, callers
  * may not be notified.
  */
-namespace mozilla {
-    class RefreshDriverTimer;
-}
-
 class nsARefreshObserver {
 public:
   // AddRef and Release signatures that match nsISupports.  Implementors
   // must implement reference counting, and those that do implement
   // nsISupports will already have methods with the correct signature.
   //
   // The refresh driver does NOT hold references to refresh observers
   // except while it is notifying them.
   NS_IMETHOD_(nsrefcnt) AddRef(void) = 0;
   NS_IMETHOD_(nsrefcnt) Release(void) = 0;
 
   virtual void WillRefresh(mozilla::TimeStamp aTime) = 0;
 };
 
-class nsRefreshDriver MOZ_FINAL : public nsISupports {
+class nsRefreshDriver MOZ_FINAL : public nsITimerCallback {
 public:
   nsRefreshDriver(nsPresContext *aPresContext);
   ~nsRefreshDriver();
 
   static void InitializeStatics();
-  static void Shutdown();
 
   // nsISupports implementation
   NS_DECL_ISUPPORTS
 
+  // nsITimerCallback implementation
+  NS_DECL_NSITIMERCALLBACK
+
   /**
    * Methods for testing, exposed via nsIDOMWindowUtils.  See
    * nsIDOMWindowUtils.advanceTimeAndRefresh for description.
    */
   void AdvanceTimeAndRefresh(int64_t aMilliseconds);
   void RestoreNormalRefresh();
-  void DoTick();
-  bool IsTestControllingRefreshesEnabled() const
-  {
-    return mTestControllingRefreshes;
-  }
 
   /**
    * Return the time of the most recent refresh.  This is intended to be
    * used by callers who want to start an animation now and want to know
    * what time to consider the start of the animation.  (This helps
    * ensure that multiple animations started during the same event off
    * the main event loop have the same start time.)
    */
@@ -228,65 +221,65 @@ public:
    * Default interval the refresh driver uses, in ms.
    */
   static int32_t DefaultInterval();
 
 private:
   typedef nsTObserverArray<nsARefreshObserver*> ObserverArray;
   typedef nsTHashtable<nsISupportsHashKey> RequestTable;
 
-  void Tick(int64_t aNowEpoch, mozilla::TimeStamp aNowTime);
-
   void EnsureTimerStarted(bool aAdjustingTimer);
   void StopTimer();
 
   uint32_t ObserverCount() const;
   uint32_t ImageRequestCount() const;
   static PLDHashOperator ImageRequestEnumerator(nsISupportsHashKey* aEntry,
                                           void* aUserArg);
+  void UpdateMostRecentRefresh();
   ObserverArray& ArrayFor(mozFlushType aFlushType);
   // Trigger a refresh immediately, if haven't been disconnected or frozen.
   void DoRefresh();
 
-  double GetRefreshTimerInterval() const;
-  double GetRegularTimerInterval() const;
-  double GetThrottledTimerInterval() const;
+  int32_t GetRefreshTimerInterval() const;
+  int32_t GetRefreshTimerType() const;
 
   bool HaveFrameRequestCallbacks() const {
     return mFrameRequestCallbackDocs.Length() != 0;
   }
 
-  mozilla::RefreshDriverTimer* ChooseTimer() const;
-  mozilla::RefreshDriverTimer *mActiveTimer;
+  nsCOMPtr<nsITimer> mTimer;
+  mozilla::TimeStamp mMostRecentRefresh; // only valid when mTimer non-null
+  int64_t mMostRecentRefreshEpochTime;   // same thing as mMostRecentRefresh,
+                                         // but in microseconds since the epoch.
 
   nsPresContext *mPresContext; // weak; pres context passed in constructor
                                // and unset in Disconnect
 
   bool mFrozen;
   bool mThrottled;
   bool mTestControllingRefreshes;
+  /* If mTimer is non-null, this boolean indicates whether the timer is
+     a precise timer.  If mTimer is null, this boolean's value can be
+     anything.  */
+  bool mTimerIsPrecise;
   bool mViewManagerFlushIsPending;
-  bool mRequestedHighPrecision;
-
-  int64_t mMostRecentRefreshEpochTime;
-  mozilla::TimeStamp mMostRecentRefresh;
 
   // separate arrays for each flush type we support
   ObserverArray mObservers[3];
   RequestTable mRequests;
 
   nsAutoTArray<nsIPresShell*, 16> mStyleFlushObservers;
   nsAutoTArray<nsIPresShell*, 16> mLayoutFlushObservers;
   nsAutoTArray<nsIPresShell*, 16> mPresShellsToInvalidateIfHidden;
   // nsTArray on purpose, because we want to be able to swap.
   nsTArray<nsIDocument*> mFrameRequestCallbackDocs;
 
+  // This is the last interval we used for our timer.  May be 0 if we
+  // haven't computed a timer interval yet.
+  mutable int32_t mLastTimerInterval;
+
   // Helper struct for processing image requests
   struct ImageRequestParameters {
       mozilla::TimeStamp ts;
   };
-
-  friend class mozilla::RefreshDriverTimer;
-
-  void SetHighPrecisionTimersEnabled(bool aEnable);
 };
 
 #endif /* !defined(nsRefreshDriver_h_) */
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -354,11 +354,9 @@ nsLayoutStatics::Shutdown()
 
   nsLayoutUtils::Shutdown();
 
   nsHyphenationManager::Shutdown();
   nsEditorSpellCheck::ShutDown();
   nsDOMMutationObserver::Shutdown();
 
   ContentParent::ShutDown();
-
-  nsRefreshDriver::Shutdown();
 }
--- a/xpcom/ds/TimeStamp.h
+++ b/xpcom/ds/TimeStamp.h
@@ -78,28 +78,16 @@ public:
   TimeDuration& operator+=(const TimeDuration& aOther) {
     mValue += aOther.mValue;
     return *this;
   }
   TimeDuration& operator-=(const TimeDuration& aOther) {
     mValue -= aOther.mValue;
     return *this;
   }
-  TimeDuration operator*(const double aMultiplier) const {
-    return TimeDuration::FromTicks(mValue * int64_t(aMultiplier));
-  }
-  TimeDuration operator*(const int32_t aMultiplier) const {
-    return TimeDuration::FromTicks(mValue * int64_t(aMultiplier));
-  }
-  TimeDuration operator*(const uint32_t aMultiplier) const {
-    return TimeDuration::FromTicks(mValue * int64_t(aMultiplier));
-  }
-  TimeDuration operator*(const int64_t aMultiplier) const {
-    return TimeDuration::FromTicks(mValue * int64_t(aMultiplier));
-  }
   double operator/(const TimeDuration& aOther) {
     return static_cast<double>(mValue) / aOther.mValue;
   }
 
   bool operator<(const TimeDuration& aOther) const {
     return mValue < aOther.mValue;
   }
   bool operator<=(const TimeDuration& aOther) const {