This commit is contained in:
Daniel Holbert 2010-03-11 23:11:22 -08:00
commit a580a7b1bd
4 changed files with 60 additions and 85 deletions

View File

@ -1360,7 +1360,7 @@ protected:
#ifdef MOZ_SMIL
// SMIL Animation Controller, lazily-initialized in GetAnimationController
nsRefPtr<nsSMILAnimationController> mAnimationController;
nsAutoPtr<nsSMILAnimationController> mAnimationController;
#endif // MOZ_SMIL
// Table of element properties for this document.

View File

@ -51,19 +51,16 @@
//----------------------------------------------------------------------
// nsSMILAnimationController implementation
// Helper method
static nsRefreshDriver*
GetRefreshDriverForDoc(nsIDocument* aDoc)
{
nsIPresShell* shell = aDoc->GetPrimaryShell();
if (!shell) {
return nsnull;
}
nsPresContext* context = shell->GetPresContext();
return context ? context->RefreshDriver() : nsnull;
}
// In my testing the minimum needed for smooth animation is 36 frames per
// second which seems like a lot (Flash traditionally uses 14fps).
//
// Redrawing is synchronous. This is deliberate so that later we can tune the
// timer based on how long the callback takes. To achieve 36fps we'd need 28ms
// between frames. For now we set the timer interval to be a little less than
// this (to allow for the render itself) and then let performance decay as the
// image gets more complicated and render times increase.
//
const PRUint32 nsSMILAnimationController::kTimerInterval = 22;
//----------------------------------------------------------------------
// ctors, dtors, factory methods
@ -78,7 +75,11 @@ nsSMILAnimationController::nsSMILAnimationController()
nsSMILAnimationController::~nsSMILAnimationController()
{
StopSampling(GetRefreshDriverForDoc(mDocument));
if (mTimer) {
mTimer->Cancel();
mTimer = nsnull;
}
NS_ASSERTION(mAnimationElementTable.Count() == 0,
"Animation controller shouldn't be tracking any animation"
" elements when it dies");
@ -104,6 +105,9 @@ nsSMILAnimationController::Init(nsIDocument* aDoc)
{
NS_ENSURE_ARG_POINTER(aDoc);
mTimer = do_CreateInstance("@mozilla.org/timer;1");
NS_ENSURE_TRUE(mTimer, NS_ERROR_OUT_OF_MEMORY);
// Keep track of document, so we can traverse its set of animation elements
mDocument = aDoc;
@ -121,7 +125,7 @@ nsSMILAnimationController::Pause(PRUint32 aType)
nsSMILTimeContainer::Pause(aType);
if (mPauseState) {
StopSampling(GetRefreshDriverForDoc(mDocument));
StopTimer();
}
}
@ -133,8 +137,7 @@ nsSMILAnimationController::Resume(PRUint32 aType)
nsSMILTimeContainer::Resume(aType);
if (wasPaused && !mPauseState && mChildContainerTable.Count()) {
Sample(); // Run the first sample manually
StartSampling(GetRefreshDriverForDoc(mDocument));
StartTimer();
}
}
@ -145,20 +148,6 @@ nsSMILAnimationController::GetParentTime() const
return PR_Now() / PR_USEC_PER_MSEC;
}
//----------------------------------------------------------------------
// nsARefreshObserver methods:
NS_IMPL_ADDREF(nsSMILAnimationController)
NS_IMPL_RELEASE(nsSMILAnimationController)
void
nsSMILAnimationController::WillRefresh(mozilla::TimeStamp aTime)
{
// XXXdholbert Eventually we should be sampling based on aTime. For now,
// though, we keep track of the time on our own, and we just use
// nsRefreshDriver for scheduling samples.
Sample();
}
//----------------------------------------------------------------------
// Animation element registration methods:
@ -225,28 +214,42 @@ nsSMILAnimationController::Unlink()
//----------------------------------------------------------------------
// Timer-related implementation helpers
void
nsSMILAnimationController::StartSampling(nsRefreshDriver* aRefreshDriver)
/*static*/ void
nsSMILAnimationController::Notify(nsITimer* timer, void* aClosure)
{
NS_ASSERTION(mPauseState == 0, "Starting sampling but controller is paused");
if (aRefreshDriver) {
NS_ABORT_IF_FALSE(aRefreshDriver == GetRefreshDriverForDoc(mDocument),
"Starting sampling with wrong refresh driver");
aRefreshDriver->AddRefreshObserver(this, Flush_Style);
}
nsSMILAnimationController* controller = (nsSMILAnimationController*)aClosure;
NS_ASSERTION(controller->mTimer == timer,
"nsSMILAnimationController::Notify called with incorrect timer");
controller->Sample();
}
void
nsSMILAnimationController::StopSampling(nsRefreshDriver* aRefreshDriver)
nsresult
nsSMILAnimationController::StartTimer()
{
if (aRefreshDriver) {
// NOTE: The document might already have been detached from its PresContext
// (and RefreshDriver), which would make GetRefreshDriverForDoc return null.
NS_ABORT_IF_FALSE(!GetRefreshDriverForDoc(mDocument) ||
aRefreshDriver == GetRefreshDriverForDoc(mDocument),
"Stopping sampling with wrong refresh driver");
aRefreshDriver->RemoveRefreshObserver(this, Flush_Style);
}
NS_ENSURE_TRUE(mTimer, NS_ERROR_FAILURE);
NS_ASSERTION(mPauseState == 0, "Starting timer but controller is paused");
// Run the first sample manually
Sample();
//
// XXX Make this self-tuning. Sounds like control theory to me and not
// something I'm familiar with.
//
return mTimer->InitWithFuncCallback(nsSMILAnimationController::Notify,
this,
kTimerInterval,
nsITimer::TYPE_REPEATING_SLACK);
}
nsresult
nsSMILAnimationController::StopTimer()
{
NS_ENSURE_TRUE(mTimer, NS_ERROR_FAILURE);
return mTimer->Cancel();
}
//----------------------------------------------------------------------
@ -665,8 +668,7 @@ nsSMILAnimationController::AddChild(nsSMILTimeContainer& aChild)
NS_ENSURE_TRUE(key,NS_ERROR_OUT_OF_MEMORY);
if (!mPauseState && mChildContainerTable.Count() == 1) {
Sample(); // Run the first sample manually
StartSampling(GetRefreshDriverForDoc(mDocument));
StartTimer();
}
return NS_OK;
@ -678,6 +680,6 @@ nsSMILAnimationController::RemoveChild(nsSMILTimeContainer& aChild)
mChildContainerTable.RemoveEntry(&aChild);
if (!mPauseState && mChildContainerTable.Count() == 0) {
StopSampling(GetRefreshDriverForDoc(mDocument));
StopTimer();
}
}

View File

@ -48,7 +48,6 @@
#include "nsSMILTimeContainer.h"
#include "nsSMILCompositorTable.h"
#include "nsSMILMilestone.h"
#include "nsRefreshDriver.h"
struct nsSMILTargetIdentifier;
class nsISMILAnimationElement;
@ -67,8 +66,7 @@ class nsIDocument;
// a compound document. These time containers can be paused individually or
// here, at the document level.
//
class nsSMILAnimationController : public nsSMILTimeContainer,
public nsARefreshObserver
class nsSMILAnimationController : public nsSMILTimeContainer
{
public:
nsSMILAnimationController();
@ -79,12 +77,6 @@ public:
virtual void Resume(PRUint32 aType);
virtual nsSMILTime GetParentTime() const;
// nsARefreshObserver
NS_IMETHOD_(nsrefcnt) AddRef();
NS_IMETHOD_(nsrefcnt) Release();
virtual void WillRefresh(mozilla::TimeStamp aTime);
// Methods for registering and enumerating animation elements
void RegisterAnimationElement(nsISMILAnimationElement* aAnimationElement);
void UnregisterAnimationElement(nsISMILAnimationElement* aAnimationElement);
@ -110,11 +102,6 @@ public:
void Traverse(nsCycleCollectionTraversalCallback* aCallback);
void Unlink();
// Methods for controlling whether we're sampling
// (Use to register/unregister us with the given nsRefreshDriver)
void StartSampling(nsRefreshDriver* aRefreshDriver);
void StopSampling(nsRefreshDriver* aRefreshDriver);
protected:
// Typedefs
typedef nsPtrHashKey<nsSMILTimeContainer> TimeContainerPtrKey;
@ -151,6 +138,8 @@ protected:
// Timer-related implementation helpers
static void Notify(nsITimer* aTimer, void* aClosure);
nsresult StartTimer();
nsresult StopTimer();
// Sample-related callbacks and implementation helpers
virtual void DoSample();
@ -176,10 +165,8 @@ protected:
virtual void RemoveChild(nsSMILTimeContainer& aChild);
// Members
nsAutoRefCnt mRefCnt;
NS_DECL_OWNINGTHREAD
static const PRUint32 kTimerInterval;
nsCOMPtr<nsITimer> mTimer;
AnimationElementHashtable mAnimationElementTable;
TimeContainerHashtable mChildContainerTable;
PRPackedBool mResampleNeeded;

View File

@ -1708,15 +1708,6 @@ PresShell::Init(nsIDocument* aDocument,
}
#endif
#ifdef MOZ_SMIL
if (mDocument->HasAnimationController()) {
nsSMILAnimationController* animCtrl = mDocument->GetAnimationController();
if (!animCtrl->IsPaused()) {
animCtrl->StartSampling(GetPresContext()->RefreshDriver());
}
}
#endif // MOZ_SMIL
return NS_OK;
}
@ -1826,15 +1817,10 @@ PresShell::Destroy()
mDocument->DeleteShell();
}
nsRefreshDriver* rd = GetPresContext()->RefreshDriver();
if (mDocument->HasAnimationController()) {
mDocument->GetAnimationController()->StopSampling(rd);
}
// Revoke any pending events. We need to do this and cancel pending reflows
// before we destroy the frame manager, since apparently frame destruction
// sometimes spins the event queue when plug-ins are involved(!).
rd->RemoveRefreshObserver(this, Flush_Layout);
GetPresContext()->RefreshDriver()->RemoveRefreshObserver(this, Flush_Layout);
mResizeEvent.Revoke();
if (mAsyncResizeTimerIsActive) {
mAsyncResizeEventTimer->Cancel();