Bug 1003683: Throttle the rate at which animated images will allow themselves to be refreshed. r=seth
authorDaniel Holbert <dholbert@cs.stanford.edu>
Thu, 08 May 2014 11:37:39 -0700
changeset 182164 fa947e53a79013b942741c4c3c6bca8dcdead9ba
parent 182163 575f54b18f5d970372e5c0fb2f20271b05f00c22
child 182165 c738f7348dea0969d2f42e6ee494dbf58406af51
push id43230
push userdholbert@mozilla.com
push dateThu, 08 May 2014 18:38:24 +0000
treeherdermozilla-inbound@fa947e53a790 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersseth
bugs1003683
milestone32.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1003683: Throttle the rate at which animated images will allow themselves to be refreshed. r=seth
image/src/Image.cpp
image/src/Image.h
image/src/RasterImage.cpp
image/src/VectorImage.cpp
--- a/image/src/Image.cpp
+++ b/image/src/Image.cpp
@@ -1,16 +1,18 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/. */
 
 #include "nsMimeTypes.h"
 
 #include "Image.h"
+#include "nsRefreshDriver.h"
+#include "mozilla/TimeStamp.h"
 
 namespace mozilla {
 namespace image {
 
 // Constructor
 ImageResource::ImageResource(ImageURL* aURI) :
   mURI(aURI),
   mInnerWindowId(0),
@@ -123,16 +125,36 @@ ImageResource::SetAnimationModeInternal(
                aAnimationMode == kLoopOnceAnimMode,
                "Wrong Animation Mode is being set!");
 
   mAnimationMode = aAnimationMode;
 
   return NS_OK;
 }
 
+bool
+ImageResource::HadRecentRefresh(const TimeStamp& aTime)
+{
+  // Our threshold for "recent" is 1/2 of the default refresh-driver interval.
+  // This ensures that we allow for frame rates at least as fast as the
+  // refresh driver's default rate.
+  static TimeDuration recentThreshold =
+      TimeDuration::FromMilliseconds(nsRefreshDriver::DefaultInterval() / 2.0);
+
+  if (!mLastRefreshTime.IsNull() &&
+      aTime - mLastRefreshTime < recentThreshold) {
+    return true;
+  }
+
+  // else, we can proceed with a refresh.
+  // But first, update our last refresh time:
+  mLastRefreshTime = aTime;
+  return false;
+}
+
 void
 ImageResource::EvaluateAnimation()
 {
   if (!mAnimating && ShouldAnimate()) {
     nsresult rv = StartAnimation();
     mAnimating = NS_SUCCEEDED(rv);
   } else if (mAnimating && !ShouldAnimate()) {
     StopAnimation();
--- a/image/src/Image.h
+++ b/image/src/Image.h
@@ -182,16 +182,27 @@ protected:
   ImageResource(ImageURL* aURI);
 
   // Shared functionality for implementors of imgIContainer. Every
   // implementation of attribute animationMode should forward here.
   nsresult GetAnimationModeInternal(uint16_t *aAnimationMode);
   nsresult SetAnimationModeInternal(uint16_t aAnimationMode);
 
   /**
+   * Helper for RequestRefresh.
+   *
+   * If we've had a "recent" refresh (i.e. if this image is being used in
+   * multiple documents & some other document *just* called RequestRefresh() on
+   * this image with a timestamp close to aTime), this method returns true.
+   *
+   * Otherwise, this method updates mLastRefreshTime to aTime & returns false.
+   */
+  bool HadRecentRefresh(const mozilla::TimeStamp& aTime);
+
+  /**
    * Decides whether animation should or should not be happening,
    * and makes sure the right thing is being done.
    */
   virtual void EvaluateAnimation();
 
   /**
    * Extended by child classes, if they have additional
    * conditions for being able to animate.
@@ -201,16 +212,17 @@ protected:
   }
 
   virtual nsresult StartAnimation() = 0;
   virtual nsresult StopAnimation() = 0;
 
   // Member data shared by all implementations of this abstract class
   nsRefPtr<imgStatusTracker>    mStatusTracker;
   nsRefPtr<ImageURL>            mURI;
+  TimeStamp                     mLastRefreshTime;
   uint64_t                      mInnerWindowId;
   uint32_t                      mAnimationConsumers;
   uint16_t                      mAnimationMode; // Enum values in imgIContainer
   bool                          mInitialized:1; // Have we been initalized?
   bool                          mAnimating:1;   // Are we currently animating?
   bool                          mError:1;       // Error handling
 };
 
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -538,16 +538,20 @@ RasterImage::Init(const char* aMimeType,
   return NS_OK;
 }
 
 //******************************************************************************
 // [notxpcom] void requestRefresh ([const] in TimeStamp aTime);
 NS_IMETHODIMP_(void)
 RasterImage::RequestRefresh(const mozilla::TimeStamp& aTime)
 {
+  if (HadRecentRefresh(aTime)) {
+    return;
+  }
+
   EvaluateAnimation();
 
   if (!mAnimating) {
     return;
   }
 
   FrameAnimator::RefreshResult res;
   if (mAnim) {
--- a/image/src/VectorImage.cpp
+++ b/image/src/VectorImage.cpp
@@ -522,16 +522,20 @@ VectorImage::GetWidth(int32_t* aWidth)
   return NS_OK;
 }
 
 //******************************************************************************
 /* [notxpcom] void requestRefresh ([const] in TimeStamp aTime); */
 NS_IMETHODIMP_(void)
 VectorImage::RequestRefresh(const mozilla::TimeStamp& aTime)
 {
+  if (HadRecentRefresh(aTime)) {
+    return;
+  }
+
   EvaluateAnimation();
 
   mSVGDocumentWrapper->TickRefreshDriver();
 
   if (mHasPendingInvalidation) {
     SendInvalidationNotifications();
     mHasPendingInvalidation = false;
   }