diff --git a/layout/base/nsRefreshDriver.cpp b/layout/base/nsRefreshDriver.cpp index bdaecd557943..14dc0b273ecc 100644 --- a/layout/base/nsRefreshDriver.cpp +++ b/layout/base/nsRefreshDriver.cpp @@ -112,6 +112,7 @@ nsRefreshDriver::nsRefreshDriver(nsPresContext *aPresContext) mTimerIsPrecise(false), mLastTimerInterval(0) { + mRequests.Init(); } nsRefreshDriver::~nsRefreshDriver() @@ -183,6 +184,29 @@ nsRefreshDriver::RemoveRefreshObserver(nsARefreshObserver *aObserver, return array.RemoveElement(aObserver); } +bool +nsRefreshDriver::AddImageRequest(imgIRequest* aRequest) +{ + if (!mRequests.PutEntry(aRequest)) { + return false; + } + + EnsureTimerStarted(false); + + return true; +} + +void +nsRefreshDriver::RemoveImageRequest(imgIRequest* aRequest) +{ + mRequests.RemoveEntry(aRequest); +} + +void nsRefreshDriver::ClearAllImageRequests() +{ + mRequests.Clear(); +} + void nsRefreshDriver::EnsureTimerStarted(bool aAdjustingTimer) { @@ -236,6 +260,7 @@ nsRefreshDriver::ObserverCount() const for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(mObservers); ++i) { sum += mObservers[i].Length(); } + // Even while throttled, we need to process layout and style changes. Style // changes can trigger transitions which fire events when they complete, and // layout changes can affect media queries on child documents, triggering @@ -247,6 +272,12 @@ nsRefreshDriver::ObserverCount() const return sum; } +PRUint32 +nsRefreshDriver::ImageRequestCount() const +{ + return mRequests.Count(); +} + void nsRefreshDriver::UpdateMostRecentRefresh() { @@ -301,7 +332,7 @@ nsRefreshDriver::Notify(nsITimer *aTimer) UpdateMostRecentRefresh(); nsCOMPtr presShell = mPresContext->GetPresShell(); - if (!presShell || ObserverCount() == 0) { + 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 @@ -330,6 +361,7 @@ nsRefreshDriver::Notify(nsITimer *aTimer) return NS_OK; } } + if (i == 0) { // Don't just loop while we have things in mBeforePaintTargets, // the whole point is that event handlers should readd the @@ -402,6 +434,17 @@ nsRefreshDriver::Notify(nsITimer *aTimer) } } + /* + * Perform notification to imgIRequests subscribed to listen + * for refresh events. + */ + + ImageRequestParameters parms = {mMostRecentRefresh}; + if (mRequests.Count()) { + mRequests.EnumerateEntries(nsRefreshDriver::ImageRequestEnumerator, &parms); + EnsureTimerStarted(false); + } + if (mThrottled || (mTimerIsPrecise != (GetRefreshTimerType() == nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP))) { @@ -422,6 +465,24 @@ nsRefreshDriver::Notify(nsITimer *aTimer) return NS_OK; } +PLDHashOperator +nsRefreshDriver::ImageRequestEnumerator(nsISupportsHashKey* aEntry, + void* aUserArg) +{ + ImageRequestParameters* parms = + static_cast (aUserArg); + mozilla::TimeStamp mostRecentRefresh = parms->ts; + imgIRequest* req = static_cast(aEntry->GetKey()); + NS_ABORT_IF_FALSE(req, "Unable to retrieve the image request"); + nsCOMPtr image; + req->GetImage(getter_AddRefs(image)); + if (image) { + image->RequestRefresh(mostRecentRefresh); + } + + return PL_DHASH_NEXT; +} + void nsRefreshDriver::Freeze() { @@ -435,7 +496,7 @@ nsRefreshDriver::Thaw() { NS_ASSERTION(mFrozen, "Thaw called on an unfrozen refresh driver"); mFrozen = false; - if (ObserverCount()) { + if (ObserverCount() || ImageRequestCount()) { // FIXME: This isn't quite right, since our EnsureTimerStarted call // updates our mMostRecentRefresh, but the DoRefresh call won't run // and notify our observers until we get back to the event loop. diff --git a/layout/base/nsRefreshDriver.h b/layout/base/nsRefreshDriver.h index d458ccb7d5cf..3a1ac1966ff1 100644 --- a/layout/base/nsRefreshDriver.h +++ b/layout/base/nsRefreshDriver.h @@ -50,10 +50,13 @@ #include "nsTObserverArray.h" #include "nsTArray.h" #include "nsAutoPtr.h" +#include "nsTHashtable.h" +#include "nsHashKeys.h" class nsPresContext; class nsIPresShell; class nsIDocument; +class imgIRequest; /** * An abstract base class to be implemented by callers wanting to be @@ -128,6 +131,24 @@ public: bool RemoveRefreshObserver(nsARefreshObserver *aObserver, mozFlushType aFlushType); + /** + * Add/Remove imgIRequest versions of observers. + * + * These are used for hooking into the refresh driver for + * controlling animated images. + * + * @note The refresh driver owns a reference to these listeners. + * + * @note Technically, imgIRequest objects are not nsARefreshObservers, but + * for controlling animated image repaint events, we subscribe the + * imgIRequests to the nsRefreshDriver for notification of paint events. + * + * @returns whether the operation succeeded, or void in the case of removal. + */ + bool AddImageRequest(imgIRequest* aRequest); + void RemoveImageRequest(imgIRequest* aRequest); + void ClearAllImageRequests(); + /** * Add / remove presshells that we should flush style and layout on */ @@ -218,10 +239,15 @@ public: private: typedef nsTObserverArray ObserverArray; + typedef nsTHashtable RequestTable; void EnsureTimerStarted(bool aAdjustingTimer); void StopTimer(); + PRUint32 ObserverCount() const; + PRUint32 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. @@ -252,6 +278,8 @@ private: // separate arrays for each flush type we support ObserverArray mObservers[3]; + RequestTable mRequests; + nsAutoTArray mStyleFlushObservers; nsAutoTArray mLayoutFlushObservers; // nsTArray on purpose, because we want to be able to swap. @@ -262,6 +290,11 @@ private: // This is the last interval we used for our timer. May be 0 if we // haven't computed a timer interval yet. mutable PRInt32 mLastTimerInterval; + + // Helper struct for processing image requests + struct ImageRequestParameters { + mozilla::TimeStamp ts; + }; }; #endif /* !defined(nsRefreshDriver_h_) */