mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 19:04:45 +00:00
Bug 492458 - SVG SMIL: Implement backwards seeking - Part 1 - interval and instance time filtering. r=dholbert, sr=roc
This commit is contained in:
parent
a5966c5522
commit
dada1e0fb4
@ -74,9 +74,9 @@ nsSMILInstanceTime::nsSMILInstanceTime(const nsSMILTimeValue& aTime,
|
||||
nsSMILInterval* aBaseInterval)
|
||||
: mTime(aTime),
|
||||
mFlags(0),
|
||||
mSerial(0),
|
||||
mVisited(PR_FALSE),
|
||||
mChainEnd(PR_FALSE),
|
||||
mFixedEndpointRefCnt(0),
|
||||
mSerial(0),
|
||||
mCreator(aCreator),
|
||||
mBaseInterval(nsnull) // This will get set to aBaseInterval in a call to
|
||||
// SetBaseInterval() at end of constructor
|
||||
@ -87,7 +87,7 @@ nsSMILInstanceTime::nsSMILInstanceTime(const nsSMILTimeValue& aTime,
|
||||
break;
|
||||
|
||||
case SOURCE_DOM:
|
||||
mFlags = kClearOnReset | kFromDOM;
|
||||
mFlags = kDynamic | kFromDOM;
|
||||
break;
|
||||
|
||||
case SOURCE_SYNCBASE:
|
||||
@ -95,7 +95,7 @@ nsSMILInstanceTime::nsSMILInstanceTime(const nsSMILTimeValue& aTime,
|
||||
break;
|
||||
|
||||
case SOURCE_EVENT:
|
||||
mFlags = kClearOnReset;
|
||||
mFlags = kDynamic;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -106,6 +106,9 @@ nsSMILInstanceTime::~nsSMILInstanceTime()
|
||||
{
|
||||
NS_ABORT_IF_FALSE(!mBaseInterval && !mCreator,
|
||||
"Destroying instance time without first calling Unlink()");
|
||||
NS_ABORT_IF_FALSE(mFixedEndpointRefCnt == 0,
|
||||
"Destroying instance time that is still used as the fixed endpoint of an "
|
||||
"interval");
|
||||
}
|
||||
|
||||
void
|
||||
@ -129,12 +132,9 @@ nsSMILInstanceTime::HandleChangedInterval(
|
||||
"Got call to HandleChangedInterval on an independent instance time.");
|
||||
NS_ABORT_IF_FALSE(mCreator, "Base interval is set but creator is not.");
|
||||
|
||||
if (mVisited || mChainEnd) {
|
||||
// We're breaking the cycle here but we need to ensure that if we later
|
||||
// receive a change notice in a different context (e.g. due to a time
|
||||
// container change) that we don't end up following the chain further and so
|
||||
// we set a flag to that effect.
|
||||
mChainEnd = PR_TRUE;
|
||||
if (mVisited) {
|
||||
// Break the cycle here
|
||||
Unlink();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -152,20 +152,48 @@ void
|
||||
nsSMILInstanceTime::HandleDeletedInterval()
|
||||
{
|
||||
NS_ABORT_IF_FALSE(mBaseInterval,
|
||||
"Got call to HandleDeletedInterval on an independent instance time.");
|
||||
NS_ABORT_IF_FALSE(mCreator, "Base interval is set but creator is not.");
|
||||
"Got call to HandleDeletedInterval on an independent instance time");
|
||||
NS_ABORT_IF_FALSE(mCreator, "Base interval is set but creator is not");
|
||||
|
||||
mBaseInterval = nsnull;
|
||||
mFlags &= ~kMayUpdate; // Can't update without a base interval
|
||||
|
||||
nsRefPtr<nsSMILInstanceTime> deathGrip(this);
|
||||
mCreator->HandleDeletedInstanceTime(*this);
|
||||
mCreator = nsnull;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsSMILInstanceTime::IsDependent(const nsSMILInstanceTime& aOther) const
|
||||
void
|
||||
nsSMILInstanceTime::HandleFilteredInterval()
|
||||
{
|
||||
if (mVisited || mChainEnd)
|
||||
NS_ABORT_IF_FALSE(mBaseInterval,
|
||||
"Got call to HandleFilteredInterval on an independent instance time");
|
||||
|
||||
mBaseInterval = nsnull;
|
||||
mFlags &= ~kMayUpdate; // Can't update without a base interval
|
||||
mCreator = nsnull;
|
||||
}
|
||||
|
||||
void
|
||||
nsSMILInstanceTime::AddRefFixedEndpoint()
|
||||
{
|
||||
NS_ABORT_IF_FALSE(mFixedEndpointRefCnt < PR_UINT16_MAX,
|
||||
"Fixed endpoint reference count upper limit reached");
|
||||
++mFixedEndpointRefCnt;
|
||||
mFlags &= ~kMayUpdate; // Once fixed, always fixed
|
||||
}
|
||||
|
||||
void
|
||||
nsSMILInstanceTime::ReleaseFixedEndpoint()
|
||||
{
|
||||
NS_ABORT_IF_FALSE(mFixedEndpointRefCnt > 0, "Duplicate release");
|
||||
--mFixedEndpointRefCnt;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsSMILInstanceTime::IsDependentOn(const nsSMILInstanceTime& aOther) const
|
||||
{
|
||||
if (mVisited)
|
||||
return PR_FALSE;
|
||||
|
||||
const nsSMILInstanceTime* myBaseTime = GetBaseTime();
|
||||
@ -177,7 +205,7 @@ nsSMILInstanceTime::IsDependent(const nsSMILInstanceTime& aOther) const
|
||||
|
||||
// mVisited is mutable
|
||||
AutoBoolSetter setVisited(const_cast<nsSMILInstanceTime*>(this)->mVisited);
|
||||
return myBaseTime->IsDependent(aOther);
|
||||
return myBaseTime->IsDependentOn(aOther);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -92,24 +92,29 @@ public:
|
||||
PRBool aBeginObjectChanged,
|
||||
PRBool aEndObjectChanged);
|
||||
void HandleDeletedInterval();
|
||||
void HandleFilteredInterval();
|
||||
|
||||
const nsSMILTimeValue& Time() const { return mTime; }
|
||||
const nsSMILTimeValueSpec* GetCreator() const { return mCreator; }
|
||||
|
||||
PRBool ClearOnReset() const { return !!(mFlags & kClearOnReset); }
|
||||
PRBool MayUpdate() const { return !!(mFlags & kMayUpdate); }
|
||||
PRBool IsDynamic() const { return !!(mFlags & kDynamic); }
|
||||
PRBool IsFixedTime() const { return !(mFlags & kMayUpdate); }
|
||||
PRBool FromDOM() const { return !!(mFlags & kFromDOM); }
|
||||
PRBool IsUsedAsFixedEndpoint() const { return mFixedEndpointRefCnt > 0; }
|
||||
|
||||
void MarkNoLongerUpdating() { mFlags &= ~kMayUpdate; }
|
||||
void AddRefFixedEndpoint();
|
||||
void ReleaseFixedEndpoint();
|
||||
|
||||
void DependentUpdate(const nsSMILTimeValue& aNewTime)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(MayUpdate(),
|
||||
NS_ABORT_IF_FALSE(!IsFixedTime(),
|
||||
"Updating an instance time that is not expected to be updated");
|
||||
mTime = aNewTime;
|
||||
}
|
||||
|
||||
PRBool IsDependent(const nsSMILInstanceTime& aOther) const;
|
||||
PRBool IsDependent() const { return !!mBaseInterval; }
|
||||
PRBool IsDependentOn(const nsSMILInstanceTime& aOther) const;
|
||||
const nsSMILInterval* GetBaseInterval() const { return mBaseInterval; }
|
||||
|
||||
PRBool SameTimeAndBase(const nsSMILInstanceTime& aOther) const
|
||||
{
|
||||
@ -131,15 +136,16 @@ protected:
|
||||
|
||||
// Internal flags used to represent the behaviour of different instance times
|
||||
enum {
|
||||
// Indicates if this instance time should be removed when the owning timed
|
||||
// element is reset. True for events and DOM calls.
|
||||
kClearOnReset = 1,
|
||||
// Indicates that this instance time was generated by an event or a DOM
|
||||
// call. Such instance times require special handling when (i) the owning
|
||||
// element is reset, and (ii) when a backwards seek is performed and the
|
||||
// timing model is reconstructed.
|
||||
kDynamic = 1,
|
||||
|
||||
// Indicates that this instance time is referred to by an
|
||||
// nsSMILTimeValueSpec and as such may be updated. Such instance time should
|
||||
// not be filtered out by the nsSMILTimedElement even if they appear to be
|
||||
// in the past as they may be updated to a future time. Initially set for
|
||||
// syncbase-generated times until they are frozen.
|
||||
// in the past as they may be updated to a future time.
|
||||
kMayUpdate = 2,
|
||||
|
||||
// Indicates that this instance time was generated from the DOM as opposed
|
||||
@ -149,15 +155,26 @@ protected:
|
||||
// DOM.
|
||||
kFromDOM = 4
|
||||
};
|
||||
PRUint8 mFlags; // Combination of kClearOnReset, kMayUpdate, etc.
|
||||
PRUint8 mFlags; // Combination of kDynamic, kMayUpdate, etc.
|
||||
PRPackedBool mVisited; // (mutable) Cycle tracking
|
||||
|
||||
// Additional reference count to determine if this instance time is currently
|
||||
// used as a fixed endpoint in any intervals. Instance times that are used in
|
||||
// this way should not be removed when the owning nsSMILTimedElement removes
|
||||
// instance times in response to a restart or in an attempt to free up memory
|
||||
// by filtering out old instance times.
|
||||
//
|
||||
// Instance times are only shared in a few cases, namely:
|
||||
// a) early ends,
|
||||
// b) zero-duration intervals, and
|
||||
// c) momentarily whilst establishing new intervals and updating the current
|
||||
// interval
|
||||
// Hence the limited range of a PRUint16 should be more than adequate.
|
||||
PRUint16 mFixedEndpointRefCnt;
|
||||
|
||||
PRUint32 mSerial; // A serial number used by the containing class to
|
||||
// specify the sort order for instance times with the
|
||||
// same mTime.
|
||||
PRPackedBool mVisited; // (mutable) Cycle tracking
|
||||
PRPackedBool mChainEnd; // Flag to indicate that this instance time is part
|
||||
// of some cyclic dependency and that in order to
|
||||
// avoid infinite recursion the cycle should not be
|
||||
// followed any further than this point.
|
||||
|
||||
nsSMILTimeValueSpec* mCreator; // The nsSMILTimeValueSpec object that created
|
||||
// us. (currently only needed for syncbase
|
||||
|
@ -39,6 +39,8 @@
|
||||
|
||||
nsSMILInterval::nsSMILInterval()
|
||||
:
|
||||
mBeginFixed(PR_FALSE),
|
||||
mEndFixed(PR_FALSE),
|
||||
mBeginObjectChanged(PR_FALSE),
|
||||
mEndObjectChanged(PR_FALSE)
|
||||
{
|
||||
@ -48,19 +50,28 @@ nsSMILInterval::nsSMILInterval(const nsSMILInterval& aOther)
|
||||
:
|
||||
mBegin(aOther.mBegin),
|
||||
mEnd(aOther.mEnd),
|
||||
mBeginFixed(PR_FALSE),
|
||||
mEndFixed(PR_FALSE),
|
||||
mBeginObjectChanged(PR_FALSE),
|
||||
mEndObjectChanged(PR_FALSE)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(aOther.mDependentTimes.IsEmpty(),
|
||||
"Attempting to copy-construct an interval with dependent times, "
|
||||
"this will lead to instance times being shared between intervals.");
|
||||
|
||||
// For the time being we don't allow intervals with fixed endpoints to be
|
||||
// copied since we only ever copy-construct to establish a new current
|
||||
// interval. If we ever need to copy historical intervals we may need to move
|
||||
// the ReleaseFixedEndpoint calls from Unlink to the dtor.
|
||||
NS_ABORT_IF_FALSE(!aOther.mBeginFixed && !aOther.mEndFixed,
|
||||
"Attempting to copy-construct an interval with fixed endpoints");
|
||||
}
|
||||
|
||||
nsSMILInterval::~nsSMILInterval()
|
||||
{
|
||||
NS_ABORT_IF_FALSE(mDependentTimes.IsEmpty(),
|
||||
"Destroying interval without disassociating dependent instance times. "
|
||||
"NotifyDeleting was not called.");
|
||||
"Unlink was not called");
|
||||
}
|
||||
|
||||
void
|
||||
@ -76,12 +87,24 @@ nsSMILInterval::NotifyChanged(const nsSMILTimeContainer* aContainer)
|
||||
}
|
||||
|
||||
void
|
||||
nsSMILInterval::NotifyDeleting()
|
||||
nsSMILInterval::Unlink(PRBool aFiltered)
|
||||
{
|
||||
for (PRInt32 i = mDependentTimes.Length() - 1; i >= 0; --i) {
|
||||
mDependentTimes[i]->HandleDeletedInterval();
|
||||
if (aFiltered) {
|
||||
mDependentTimes[i]->HandleFilteredInterval();
|
||||
} else {
|
||||
mDependentTimes[i]->HandleDeletedInterval();
|
||||
}
|
||||
}
|
||||
mDependentTimes.Clear();
|
||||
if (mBegin && mBeginFixed) {
|
||||
mBegin->ReleaseFixedEndpoint();
|
||||
}
|
||||
mBegin = nsnull;
|
||||
if (mEnd && mEndFixed) {
|
||||
mEnd->ReleaseFixedEndpoint();
|
||||
}
|
||||
mEnd = nsnull;
|
||||
}
|
||||
|
||||
nsSMILInstanceTime*
|
||||
@ -104,7 +127,9 @@ void
|
||||
nsSMILInterval::SetBegin(nsSMILInstanceTime& aBegin)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(aBegin.Time().IsResolved(),
|
||||
"Attempting to set unresolved begin time on interval.");
|
||||
"Attempting to set unresolved begin time on interval");
|
||||
NS_ABORT_IF_FALSE(!mBeginFixed,
|
||||
"Attempting to set begin time but the begin point is fixed");
|
||||
|
||||
if (mBegin == &aBegin)
|
||||
return;
|
||||
@ -116,6 +141,9 @@ nsSMILInterval::SetBegin(nsSMILInstanceTime& aBegin)
|
||||
void
|
||||
nsSMILInterval::SetEnd(nsSMILInstanceTime& aEnd)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(!mEndFixed,
|
||||
"Attempting to set end time but the end point is fixed");
|
||||
|
||||
if (mEnd == &aEnd)
|
||||
return;
|
||||
|
||||
@ -123,6 +151,28 @@ nsSMILInterval::SetEnd(nsSMILInstanceTime& aEnd)
|
||||
mEndObjectChanged = PR_TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
nsSMILInterval::FixBegin()
|
||||
{
|
||||
NS_ABORT_IF_FALSE(mBegin && mEnd,
|
||||
"Fixing begin point on un-initialized interval");
|
||||
NS_ABORT_IF_FALSE(!mBeginFixed, "Duplicate calls to FixBegin()");
|
||||
mBeginFixed = PR_TRUE;
|
||||
mBegin->AddRefFixedEndpoint();
|
||||
}
|
||||
|
||||
void
|
||||
nsSMILInterval::FixEnd()
|
||||
{
|
||||
NS_ABORT_IF_FALSE(mBegin && mEnd,
|
||||
"Fixing end point on un-initialized interval");
|
||||
NS_ABORT_IF_FALSE(mBeginFixed,
|
||||
"Fixing the end of an interval without a fixed begin");
|
||||
NS_ABORT_IF_FALSE(!mEndFixed, "Duplicate calls to FixEnd()");
|
||||
mEndFixed = PR_TRUE;
|
||||
mEnd->AddRefFixedEndpoint();
|
||||
}
|
||||
|
||||
void
|
||||
nsSMILInterval::AddDependentTime(nsSMILInstanceTime& aTime)
|
||||
{
|
||||
@ -142,3 +192,19 @@ nsSMILInterval::RemoveDependentTime(const nsSMILInstanceTime& aTime)
|
||||
mDependentTimes.RemoveElementSorted(&aTime);
|
||||
NS_ABORT_IF_FALSE(found, "Couldn't find instance time to delete.");
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsSMILInterval::IsDependencyChainLink() const
|
||||
{
|
||||
if (!mBegin || !mEnd)
|
||||
return PR_FALSE; // Not yet initialised so it can't be part of a chain
|
||||
|
||||
if (mDependentTimes.IsEmpty())
|
||||
return PR_FALSE; // No dependents, chain end
|
||||
|
||||
// So we have dependents, but we're still only a link in the chain (as opposed
|
||||
// to the end of the chain) if one of our endpoints is dependent on an
|
||||
// interval other than ourselves.
|
||||
return (mBegin->IsDependent() && mBegin->GetBaseInterval() != this) ||
|
||||
(mEnd->IsDependent() && mEnd->GetBaseInterval() != this);
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ public:
|
||||
nsSMILInterval(const nsSMILInterval& aOther);
|
||||
~nsSMILInterval();
|
||||
void NotifyChanged(const nsSMILTimeContainer* aContainer);
|
||||
void NotifyDeleting();
|
||||
void Unlink(PRBool aFiltered = PR_FALSE);
|
||||
|
||||
const nsSMILInstanceTime* Begin() const
|
||||
{
|
||||
@ -83,37 +83,15 @@ public:
|
||||
SetEnd(aEnd);
|
||||
}
|
||||
|
||||
void FreezeBegin()
|
||||
{
|
||||
NS_ABORT_IF_FALSE(mBegin && mEnd,
|
||||
"Freezing Begin() on un-initialized instance time");
|
||||
mBegin->MarkNoLongerUpdating();
|
||||
}
|
||||
|
||||
void FreezeEnd()
|
||||
{
|
||||
NS_ABORT_IF_FALSE(mBegin && mEnd,
|
||||
"Freezing End() on un-initialized instance time");
|
||||
NS_ABORT_IF_FALSE(!mBegin->MayUpdate(),
|
||||
"Freezing the end of an interval without a fixed begin");
|
||||
mEnd->MarkNoLongerUpdating();
|
||||
}
|
||||
|
||||
// XXX Backwards seeking support (bug 492458)
|
||||
void Unfreeze()
|
||||
{
|
||||
// XXX
|
||||
UnfreezeEnd();
|
||||
}
|
||||
|
||||
void UnfreezeEnd()
|
||||
{
|
||||
// XXX
|
||||
}
|
||||
void FixBegin();
|
||||
void FixEnd();
|
||||
|
||||
void AddDependentTime(nsSMILInstanceTime& aTime);
|
||||
void RemoveDependentTime(const nsSMILInstanceTime& aTime);
|
||||
|
||||
// Cue for assessing if this interval can be filtered
|
||||
PRBool IsDependencyChainLink() const;
|
||||
|
||||
private:
|
||||
nsRefPtr<nsSMILInstanceTime> mBegin;
|
||||
nsRefPtr<nsSMILInstanceTime> mEnd;
|
||||
@ -123,6 +101,18 @@ private:
|
||||
// nsSMILInstanceTimes to notify when this interval is changed or deleted.
|
||||
InstanceTimeList mDependentTimes;
|
||||
|
||||
// Indicates if the end points of the interval are fixed or not.
|
||||
//
|
||||
// Note that this is not the same as having an end point whose TIME is fixed
|
||||
// (i.e. nsSMILInstanceTime::IsFixed() returns PR_TRUE). This is because it is
|
||||
// possible to have an end point with a fixed TIME and yet still update the
|
||||
// end point to refer to a different nsSMILInstanceTime object.
|
||||
//
|
||||
// However, if mBegin/EndFixed is PR_TRUE, then BOTH the nsSMILInstanceTime
|
||||
// OBJECT returned for that end point and its TIME value will not change.
|
||||
PRPackedBool mBeginFixed;
|
||||
PRPackedBool mEndFixed;
|
||||
|
||||
// When change notifications are passed around the timing model we try to
|
||||
// filter out all changes where there is no observable difference to an
|
||||
// instance time. Changes that may produce an observable difference are:
|
||||
|
@ -160,8 +160,8 @@ nsSMILTimeValueSpec::HandleChangedInstanceTime(
|
||||
PRBool aObjectChanged)
|
||||
{
|
||||
// If the instance time is fixed (e.g. because it's being used as the begin
|
||||
// time of an active interval) we just ignore the change.
|
||||
if (!aInstanceTimeToUpdate.MayUpdate())
|
||||
// time of an active or postactive interval) we just ignore the change.
|
||||
if (aInstanceTimeToUpdate.IsFixedTime())
|
||||
return;
|
||||
|
||||
nsSMILTimeValue updatedTime =
|
||||
|
@ -90,6 +90,29 @@ nsSMILTimedElement::InstanceTimeComparator::LessThan(
|
||||
return cmp == 0 ? aElem1->Serial() < aElem2->Serial() : cmp < 0;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Templated helper functions
|
||||
|
||||
// Selectively remove elements from an array of type
|
||||
// nsTArray<nsRefPtr<nsSMILInstanceTime> > with O(n) performance.
|
||||
template <class TestFunctor>
|
||||
void
|
||||
nsSMILTimedElement::RemoveInstanceTimes(InstanceTimeList& aArray,
|
||||
TestFunctor& aTest)
|
||||
{
|
||||
InstanceTimeList newArray;
|
||||
for (PRUint32 i = 0; i < aArray.Length(); ++i) {
|
||||
nsSMILInstanceTime* item = aArray[i].get();
|
||||
if (aTest(item, i)) {
|
||||
item->Unlink();
|
||||
} else {
|
||||
newArray.AppendElement(item);
|
||||
}
|
||||
}
|
||||
aArray.Clear();
|
||||
aArray.SwapElements(newArray);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Static members
|
||||
|
||||
@ -108,6 +131,12 @@ nsAttrValue::EnumTable nsSMILTimedElement::sRestartModeTable[] = {
|
||||
|
||||
const nsSMILMilestone nsSMILTimedElement::sMaxMilestone(LL_MAXINT, PR_FALSE);
|
||||
|
||||
// The thresholds at which point we start filtering intervals and instance times
|
||||
// indiscriminately.
|
||||
// See FilterIntervals and FilterInstanceTimes.
|
||||
const PRUint8 nsSMILTimedElement::sMaxNumIntervals = 20;
|
||||
const PRUint8 nsSMILTimedElement::sMaxNumInstanceTimes = 100;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Ctor, dtor
|
||||
|
||||
@ -149,12 +178,12 @@ nsSMILTimedElement::~nsSMILTimedElement()
|
||||
// (We shouldn't get any callbacks from this because all our instance times
|
||||
// are now disassociated with any intervals)
|
||||
if (mCurrentInterval) {
|
||||
mCurrentInterval->NotifyDeleting();
|
||||
mCurrentInterval->Unlink();
|
||||
mCurrentInterval = nsnull;
|
||||
}
|
||||
|
||||
for (PRInt32 i = mOldIntervals.Length() - 1; i >= 0; --i) {
|
||||
mOldIntervals[i]->NotifyDeleting();
|
||||
mOldIntervals[i]->Unlink();
|
||||
}
|
||||
mOldIntervals.Clear();
|
||||
}
|
||||
@ -332,22 +361,33 @@ nsSMILTimedElement::RemoveInstanceTime(nsSMILInstanceTime* aInstanceTime,
|
||||
UpdateCurrentInterval();
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
class RemoveByCreator
|
||||
{
|
||||
public:
|
||||
RemoveByCreator(const nsSMILTimeValueSpec* aCreator) : mCreator(aCreator)
|
||||
{ }
|
||||
|
||||
PRBool operator()(nsSMILInstanceTime* aInstanceTime, PRUint32 /*aIndex*/)
|
||||
{
|
||||
return aInstanceTime->GetCreator() == mCreator;
|
||||
}
|
||||
|
||||
private:
|
||||
const nsSMILTimeValueSpec* mCreator;
|
||||
};
|
||||
}
|
||||
|
||||
void
|
||||
nsSMILTimedElement::RemoveInstanceTimesForCreator(
|
||||
const nsSMILTimeValueSpec* aCreator, PRBool aIsBegin)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(aCreator, "Creator not set");
|
||||
InstanceTimeList& instances = aIsBegin ? mBeginInstances : mEndInstances;
|
||||
|
||||
PRInt32 count = instances.Length();
|
||||
for (PRInt32 i = count - 1; i >= 0; --i) {
|
||||
nsSMILInstanceTime* instance = instances[i].get();
|
||||
NS_ABORT_IF_FALSE(instance, "NULL instance in instances array");
|
||||
if (instance->GetCreator() == aCreator) {
|
||||
instance->Unlink();
|
||||
instances.RemoveElementAt(i);
|
||||
}
|
||||
}
|
||||
InstanceTimeList& instances = aIsBegin ? mBeginInstances : mEndInstances;
|
||||
RemoveByCreator removeByCreator(aCreator);
|
||||
RemoveInstanceTimes(instances, removeByCreator);
|
||||
|
||||
UpdateCurrentInterval();
|
||||
}
|
||||
@ -445,12 +485,7 @@ nsSMILTimedElement::DoSampleAt(nsSMILTime aContainerTime, PRBool aEndOnly)
|
||||
stateChanged = PR_TRUE;
|
||||
if (mElementState == STATE_WAITING) {
|
||||
mCurrentInterval = new nsSMILInterval(firstInterval);
|
||||
if (!mCurrentInterval) {
|
||||
NS_WARNING("Failed to allocate memory for new interval");
|
||||
mElementState = STATE_POSTACTIVE;
|
||||
} else {
|
||||
NotifyNewInterval();
|
||||
}
|
||||
NotifyNewInterval();
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -459,7 +494,7 @@ nsSMILTimedElement::DoSampleAt(nsSMILTime aContainerTime, PRBool aEndOnly)
|
||||
{
|
||||
if (mCurrentInterval->Begin()->Time() <= sampleTime) {
|
||||
mElementState = STATE_ACTIVE;
|
||||
mCurrentInterval->FreezeBegin();
|
||||
mCurrentInterval->FixBegin();
|
||||
if (HasPlayed()) {
|
||||
Reset(); // Apply restart behaviour
|
||||
}
|
||||
@ -483,14 +518,7 @@ nsSMILTimedElement::DoSampleAt(nsSMILTime aContainerTime, PRBool aEndOnly)
|
||||
|
||||
case STATE_ACTIVE:
|
||||
{
|
||||
// Only apply an early end if we're not already ending.
|
||||
if (mCurrentInterval->End()->Time() > sampleTime) {
|
||||
nsSMILInstanceTime* earlyEnd = CheckForEarlyEnd(sampleTime);
|
||||
if (earlyEnd) {
|
||||
mCurrentInterval->SetEnd(*earlyEnd);
|
||||
NotifyChangedInterval();
|
||||
}
|
||||
}
|
||||
ApplyEarlyEnd(sampleTime);
|
||||
|
||||
if (mCurrentInterval->End()->Time() <= sampleTime) {
|
||||
nsSMILInterval newInterval;
|
||||
@ -501,19 +529,15 @@ nsSMILTimedElement::DoSampleAt(nsSMILTime aContainerTime, PRBool aEndOnly)
|
||||
if (mClient) {
|
||||
mClient->Inactivate(mFillMode == FILL_FREEZE);
|
||||
}
|
||||
mCurrentInterval->FreezeEnd();
|
||||
mCurrentInterval->FixEnd();
|
||||
mOldIntervals.AppendElement(mCurrentInterval.forget());
|
||||
// We must update mOldIntervals before calling SampleFillValue
|
||||
SampleFillValue();
|
||||
if (mElementState == STATE_WAITING) {
|
||||
mCurrentInterval = new nsSMILInterval(newInterval);
|
||||
if (!mCurrentInterval) {
|
||||
NS_WARNING("Failed to allocate memory for new interval");
|
||||
mElementState = STATE_POSTACTIVE;
|
||||
} else {
|
||||
NotifyNewInterval();
|
||||
}
|
||||
NotifyNewInterval();
|
||||
}
|
||||
FilterHistory();
|
||||
stateChanged = PR_TRUE;
|
||||
} else {
|
||||
nsSMILTime beginTime = mCurrentInterval->Begin()->Time().GetMillis();
|
||||
@ -552,37 +576,6 @@ nsSMILTimedElement::HandleContainerTimeChange()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsSMILTimedElement::Reset()
|
||||
{
|
||||
// SMIL 3.0 section 5.4.3, 'Resetting element state':
|
||||
// Any instance times associated with past Event-values, Repeat-values,
|
||||
// Accesskey-values or added via DOM method calls are removed from the
|
||||
// dependent begin and end instance times lists. In effect, all events and
|
||||
// DOM methods calls in the past are cleared. This does not apply to an
|
||||
// instance time that defines the begin of the current interval.
|
||||
PRInt32 count = mBeginInstances.Length();
|
||||
for (PRInt32 i = count - 1; i >= 0; --i) {
|
||||
nsSMILInstanceTime* instance = mBeginInstances[i].get();
|
||||
NS_ABORT_IF_FALSE(instance, "NULL instance in begin instances array");
|
||||
if (instance->ClearOnReset() &&
|
||||
(!mCurrentInterval || instance != mCurrentInterval->Begin())) {
|
||||
instance->Unlink();
|
||||
mBeginInstances.RemoveElementAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
count = mEndInstances.Length();
|
||||
for (PRInt32 j = count - 1; j >= 0; --j) {
|
||||
nsSMILInstanceTime* instance = mEndInstances[j].get();
|
||||
NS_ABORT_IF_FALSE(instance, "NULL instance in end instances array");
|
||||
if (instance->ClearOnReset()) {
|
||||
instance->Unlink();
|
||||
mEndInstances.RemoveElementAt(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsSMILTimedElement::SetAttr(nsIAtom* aAttribute, const nsAString& aValue,
|
||||
nsAttrValue& aResult, nsIContent* aContextNode,
|
||||
@ -922,16 +915,15 @@ nsSMILTimedElement::AddDependent(nsSMILTimeValueSpec& aDependent)
|
||||
"nsSMILTimeValueSpec is already registered as a dependency");
|
||||
mTimeDependents.PutEntry(&aDependent);
|
||||
|
||||
// Add old and current intervals
|
||||
// Add current interval. We could add historical intervals too but that would
|
||||
// cause unpredictable results since some intervals may have been filtered.
|
||||
// SMIL doesn't say what to do here so for simplicity and consistency we
|
||||
// simply add the current interval if there is one.
|
||||
//
|
||||
// It's not necessary to call SyncPauseTime since we're dealing with
|
||||
// historical instance times not newly added ones.
|
||||
nsSMILTimeContainer* container = GetTimeContainer();
|
||||
for (PRUint32 i = 0; i < mOldIntervals.Length(); ++i) {
|
||||
aDependent.HandleNewInterval(*mOldIntervals[i], container);
|
||||
}
|
||||
if (mCurrentInterval) {
|
||||
aDependent.HandleNewInterval(*mCurrentInterval, container);
|
||||
aDependent.HandleNewInterval(*mCurrentInterval, GetTimeContainer());
|
||||
}
|
||||
}
|
||||
|
||||
@ -950,7 +942,7 @@ nsSMILTimedElement::IsTimeDependent(const nsSMILTimedElement& aOther) const
|
||||
if (!thisBegin || !otherBegin)
|
||||
return PR_FALSE;
|
||||
|
||||
return thisBegin->IsDependent(*otherBegin);
|
||||
return thisBegin->IsDependentOn(*otherBegin);
|
||||
}
|
||||
|
||||
void
|
||||
@ -1054,6 +1046,18 @@ nsSMILTimedElement::SetBeginOrEndSpec(const nsAString& aSpec,
|
||||
return rv;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
class RemoveNonDOM
|
||||
{
|
||||
public:
|
||||
PRBool operator()(nsSMILInstanceTime* aInstanceTime, PRUint32 /*aIndex*/)
|
||||
{
|
||||
return !aInstanceTime->FromDOM();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void
|
||||
nsSMILTimedElement::ClearBeginOrEndSpecs(PRBool aIsBegin)
|
||||
{
|
||||
@ -1063,17 +1067,184 @@ nsSMILTimedElement::ClearBeginOrEndSpecs(PRBool aIsBegin)
|
||||
// Remove only those instance times generated by the attribute, not those from
|
||||
// DOM calls.
|
||||
InstanceTimeList& instances = aIsBegin ? mBeginInstances : mEndInstances;
|
||||
PRInt32 count = instances.Length();
|
||||
for (PRInt32 i = count - 1; i >= 0; --i) {
|
||||
nsSMILInstanceTime* instance = instances[i].get();
|
||||
NS_ABORT_IF_FALSE(instance, "NULL instance in instances array");
|
||||
if (!instance->FromDOM()) {
|
||||
instance->Unlink();
|
||||
instances.RemoveElementAt(i);
|
||||
RemoveNonDOM removeNonDOM;
|
||||
RemoveInstanceTimes(instances, removeNonDOM);
|
||||
}
|
||||
|
||||
void
|
||||
nsSMILTimedElement::ApplyEarlyEnd(const nsSMILTimeValue& aSampleTime)
|
||||
{
|
||||
// This should only be called within DoSampleAt as a helper function
|
||||
NS_ABORT_IF_FALSE(mElementState == STATE_ACTIVE,
|
||||
"Unexpected state to try to apply an early end");
|
||||
|
||||
// Only apply an early end if we're not already ending.
|
||||
if (mCurrentInterval->End()->Time() > aSampleTime) {
|
||||
nsSMILInstanceTime* earlyEnd = CheckForEarlyEnd(aSampleTime);
|
||||
if (earlyEnd) {
|
||||
if (earlyEnd->IsDependent()) {
|
||||
// Generate a new instance time for the early end since the
|
||||
// existing instance time is part of some dependency chain that we
|
||||
// don't want to participate in.
|
||||
nsRefPtr<nsSMILInstanceTime> newEarlyEnd =
|
||||
new nsSMILInstanceTime(earlyEnd->Time());
|
||||
mCurrentInterval->SetEnd(*newEarlyEnd);
|
||||
} else {
|
||||
mCurrentInterval->SetEnd(*earlyEnd);
|
||||
}
|
||||
NotifyChangedInterval();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
class RemoveReset
|
||||
{
|
||||
public:
|
||||
RemoveReset(const nsSMILInstanceTime* aCurrentIntervalBegin)
|
||||
: mCurrentIntervalBegin(aCurrentIntervalBegin) { }
|
||||
PRBool operator()(nsSMILInstanceTime* aInstanceTime, PRUint32 /*aIndex*/)
|
||||
{
|
||||
// SMIL 3.0 section 5.4.3, 'Resetting element state':
|
||||
// Any instance times associated with past Event-values, Repeat-values,
|
||||
// Accesskey-values or added via DOM method calls are removed from the
|
||||
// dependent begin and end instance times lists. In effect, all events
|
||||
// and DOM methods calls in the past are cleared. This does not apply to
|
||||
// an instance time that defines the begin of the current interval.
|
||||
return aInstanceTime->IsDynamic() &&
|
||||
!aInstanceTime->IsUsedAsFixedEndpoint() &&
|
||||
(!mCurrentIntervalBegin || aInstanceTime != mCurrentIntervalBegin);
|
||||
}
|
||||
|
||||
private:
|
||||
const nsSMILInstanceTime* mCurrentIntervalBegin;
|
||||
};
|
||||
}
|
||||
|
||||
void
|
||||
nsSMILTimedElement::Reset()
|
||||
{
|
||||
RemoveReset resetBegin(mCurrentInterval ? mCurrentInterval->Begin() : nsnull);
|
||||
RemoveInstanceTimes(mBeginInstances, resetBegin);
|
||||
|
||||
RemoveReset resetEnd(nsnull);
|
||||
RemoveInstanceTimes(mEndInstances, resetEnd);
|
||||
}
|
||||
|
||||
void
|
||||
nsSMILTimedElement::FilterHistory()
|
||||
{
|
||||
// We should filter the intervals first, since instance times still used in an
|
||||
// interval won't be filtered.
|
||||
FilterIntervals();
|
||||
FilterInstanceTimes(mBeginInstances);
|
||||
FilterInstanceTimes(mEndInstances);
|
||||
}
|
||||
|
||||
void
|
||||
nsSMILTimedElement::FilterIntervals()
|
||||
{
|
||||
// We can filter old intervals that:
|
||||
//
|
||||
// a) are not the previous interval; AND
|
||||
// b) are not in the middle of a dependency chain
|
||||
//
|
||||
// Condition (a) is necessary since the previous interval is used for applying
|
||||
// fill effects and updating the current interval.
|
||||
//
|
||||
// Condition (b) is necessary since even if this interval itself is not
|
||||
// active, it may be part of a dependency chain that includes active
|
||||
// intervals. Such chains are used to establish priorities within the
|
||||
// animation sandwich.
|
||||
//
|
||||
// Although the above conditions allow us to safely filter intervals for most
|
||||
// scenarios they do not cover all cases and there will still be scenarios
|
||||
// that generate intervals indefinitely. In such a case we simply set
|
||||
// a maximum number of intervals and drop any intervals beyond that threshold.
|
||||
|
||||
PRUint32 threshold = mOldIntervals.Length() > sMaxNumIntervals ?
|
||||
mOldIntervals.Length() - sMaxNumIntervals :
|
||||
0;
|
||||
IntervalList filteredList;
|
||||
for (PRUint32 i = 0; i < mOldIntervals.Length(); ++i)
|
||||
{
|
||||
nsSMILInterval* interval = mOldIntervals[i].get();
|
||||
if (i + 1 < mOldIntervals.Length() /*skip previous interval*/ &&
|
||||
(i < threshold || !interval->IsDependencyChainLink())) {
|
||||
interval->Unlink(PR_TRUE /*filtered, not deleted*/);
|
||||
} else {
|
||||
filteredList.AppendElement(mOldIntervals[i].forget());
|
||||
}
|
||||
}
|
||||
mOldIntervals.Clear();
|
||||
mOldIntervals.SwapElements(filteredList);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
class RemoveFiltered
|
||||
{
|
||||
public:
|
||||
RemoveFiltered(nsSMILTimeValue aCutoff) : mCutoff(aCutoff) { }
|
||||
PRBool operator()(nsSMILInstanceTime* aInstanceTime, PRUint32 /*aIndex*/)
|
||||
{
|
||||
// We can filter instance times that:
|
||||
// a) Precede the end point of the previous interval; AND
|
||||
// b) Are NOT syncbase times that might be updated to a time after the end
|
||||
// point of the previous interval; AND
|
||||
// c) Are NOT fixed end points in any remaining interval.
|
||||
return aInstanceTime->Time() < mCutoff &&
|
||||
aInstanceTime->IsFixedTime() &&
|
||||
!aInstanceTime->IsUsedAsFixedEndpoint();
|
||||
}
|
||||
|
||||
private:
|
||||
nsSMILTimeValue mCutoff;
|
||||
};
|
||||
|
||||
class RemoveBelowThreshold
|
||||
{
|
||||
public:
|
||||
RemoveBelowThreshold(PRUint32 aThreshold,
|
||||
const nsSMILInstanceTime* aCurrentIntervalBegin)
|
||||
: mThreshold(aThreshold),
|
||||
mCurrentIntervalBegin(aCurrentIntervalBegin) { }
|
||||
PRBool operator()(nsSMILInstanceTime* aInstanceTime, PRUint32 aIndex)
|
||||
{
|
||||
return aInstanceTime != mCurrentIntervalBegin && aIndex < mThreshold;
|
||||
}
|
||||
|
||||
private:
|
||||
PRUint32 mThreshold;
|
||||
const nsSMILInstanceTime* mCurrentIntervalBegin;
|
||||
};
|
||||
}
|
||||
|
||||
void
|
||||
nsSMILTimedElement::FilterInstanceTimes(InstanceTimeList& aList)
|
||||
{
|
||||
if (GetPreviousInterval()) {
|
||||
RemoveFiltered removeFiltered(GetPreviousInterval()->End()->Time());
|
||||
RemoveInstanceTimes(aList, removeFiltered);
|
||||
}
|
||||
|
||||
// As with intervals it is possible to create a document that, even despite
|
||||
// our most aggressive filtering, will generate instance times indefinitely
|
||||
// (e.g. cyclic dependencies with TimeEvents---we can't filter such times as
|
||||
// they're unpredictable due to the possibility of seeking the document which
|
||||
// may prevent some events from being generated). Therefore we introduce
|
||||
// a hard cutoff at which point we just drop the oldest instance times.
|
||||
if (aList.Length() > sMaxNumInstanceTimes) {
|
||||
PRUint32 threshold = aList.Length() - sMaxNumInstanceTimes;
|
||||
// We should still preserve the current interval begin time however
|
||||
const nsSMILInstanceTime* currentIntervalBegin = mCurrentInterval ?
|
||||
mCurrentInterval->Begin() : nsnull;
|
||||
RemoveBelowThreshold removeBelowThreshold(threshold, currentIntervalBegin);
|
||||
RemoveInstanceTimes(aList, removeBelowThreshold);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// This method is based on the pseudocode given in the SMILANIM spec.
|
||||
//
|
||||
@ -1119,8 +1290,6 @@ nsSMILTimedElement::GetNextInterval(const nsSMILInterval* aPrevInterval,
|
||||
tempBegin = const_cast<nsSMILInstanceTime*>(aFixedBeginTime);
|
||||
} else if (!mBeginSpecSet && beginAfter <= zeroTime) {
|
||||
tempBegin = new nsSMILInstanceTime(nsSMILTimeValue(0));
|
||||
if (!tempBegin)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
} else {
|
||||
PRInt32 beginPos = 0;
|
||||
tempBegin = GetNextGreaterOrEqual(mBeginInstances, beginAfter, beginPos);
|
||||
@ -1166,8 +1335,6 @@ nsSMILTimedElement::GetNextInterval(const nsSMILInterval* aPrevInterval,
|
||||
if (!tempEnd || intervalEnd != activeEnd) {
|
||||
tempEnd = new nsSMILInstanceTime(activeEnd);
|
||||
}
|
||||
if (!tempEnd)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
NS_ABORT_IF_FALSE(tempEnd, "Failed to get end point for next interval");
|
||||
|
||||
@ -1409,10 +1576,6 @@ nsSMILTimedElement::UpdateCurrentInterval(PRBool aForceChangeNotice)
|
||||
NS_ABORT_IF_FALSE(!mCurrentInterval,
|
||||
"In postactive state but the interval has been set");
|
||||
mCurrentInterval = new nsSMILInterval(updatedInterval);
|
||||
if (!mCurrentInterval) {
|
||||
NS_WARNING("Failed to allocate memory for new interval.");
|
||||
return;
|
||||
}
|
||||
mElementState = STATE_WAITING;
|
||||
NotifyNewInterval();
|
||||
|
||||
@ -1450,7 +1613,7 @@ nsSMILTimedElement::UpdateCurrentInterval(PRBool aForceChangeNotice)
|
||||
|
||||
if (mElementState == STATE_ACTIVE || mElementState == STATE_WAITING) {
|
||||
mElementState = STATE_POSTACTIVE;
|
||||
mCurrentInterval->NotifyDeleting();
|
||||
mCurrentInterval->Unlink();
|
||||
mCurrentInterval = nsnull;
|
||||
}
|
||||
}
|
||||
@ -1477,9 +1640,9 @@ nsSMILTimedElement::SampleFillValue()
|
||||
NS_ABORT_IF_FALSE(prevInterval,
|
||||
"Attempting to sample fill value but there is no previous interval");
|
||||
NS_ABORT_IF_FALSE(prevInterval->End()->Time().IsResolved() &&
|
||||
!prevInterval->End()->MayUpdate(),
|
||||
prevInterval->End()->IsFixedTime(),
|
||||
"Attempting to sample fill value but the endpoint of the previous "
|
||||
"interval is not resolved and frozen");
|
||||
"interval is not resolved and fixed");
|
||||
|
||||
nsSMILTime activeTime = prevInterval->End()->Time().GetMillis() -
|
||||
prevInterval->Begin()->Time().GetMillis();
|
||||
@ -1508,10 +1671,6 @@ nsSMILTimedElement::AddInstanceTimeFromCurrentTime(nsSMILTime aCurrentTime,
|
||||
// so we don't end up setting SOURCE_DOM for event-based times.
|
||||
nsRefPtr<nsSMILInstanceTime> instanceTime =
|
||||
new nsSMILInstanceTime(timeVal, nsSMILInstanceTime::SOURCE_DOM);
|
||||
if (!instanceTime) {
|
||||
NS_WARNING("Insufficient memory to create instance time");
|
||||
return;
|
||||
}
|
||||
|
||||
AddInstanceTime(instanceTime, aIsBegin);
|
||||
}
|
||||
|
@ -228,12 +228,6 @@ public:
|
||||
*/
|
||||
void HandleContainerTimeChange();
|
||||
|
||||
/**
|
||||
* Reset the element's internal state. As described in SMILANIM 3.3.7, all
|
||||
* instance times associated with DOM calls, events, etc. are cleared.
|
||||
*/
|
||||
void Reset();
|
||||
|
||||
/**
|
||||
* Attempts to set an attribute on this timed element.
|
||||
*
|
||||
@ -345,6 +339,10 @@ protected:
|
||||
nsSMILTimeContainer* mTimeContainer;
|
||||
};
|
||||
|
||||
// Templated helper functions
|
||||
template <class TestFunctor>
|
||||
void RemoveInstanceTimes(InstanceTimeList& aArray, TestFunctor& aTest);
|
||||
|
||||
//
|
||||
// Implementation helpers
|
||||
//
|
||||
@ -377,6 +375,40 @@ protected:
|
||||
void ClearBeginOrEndSpecs(PRBool aIsBegin);
|
||||
void DoSampleAt(nsSMILTime aContainerTime, PRBool aEndOnly);
|
||||
|
||||
/**
|
||||
* Helper function to check for an early end and, if necessary, update the
|
||||
* current interval accordingly.
|
||||
*
|
||||
* See SMIL 3.0, section 5.4.5, Element life cycle, "Active Time - Playing an
|
||||
* interval" for a description of ending early.
|
||||
*
|
||||
* @param aSampleTime The current sample time. Early ends should only be
|
||||
* applied at the last possible moment (i.e. if they are at
|
||||
* or before the current sample time) and only if the
|
||||
* current interval is not already ending.
|
||||
*/
|
||||
void ApplyEarlyEnd(const nsSMILTimeValue& aSampleTime);
|
||||
|
||||
/**
|
||||
* Clears certain state in response to the element restarting.
|
||||
*
|
||||
* This state is described in SMIL 3.0, section 5.4.3, Resetting element state
|
||||
*/
|
||||
void Reset();
|
||||
|
||||
/**
|
||||
* Helper function to iterate through this element's accumulated timing
|
||||
* information (specifically old nsSMILIntervals and nsSMILTimeInstanceTimes)
|
||||
* and discard items that are no longer needed or exceed some threshold of
|
||||
* accumulated state.
|
||||
*/
|
||||
void FilterHistory();
|
||||
|
||||
// Helper functions for FilterHistory to clear old nsSMILIntervals and
|
||||
// nsSMILInstanceTimes respectively.
|
||||
void FilterIntervals();
|
||||
void FilterInstanceTimes(InstanceTimeList& aList);
|
||||
|
||||
/**
|
||||
* Calculates the next acceptable interval for this element after the
|
||||
* specified interval, or, if no previous interval is specified, it will be
|
||||
@ -483,6 +515,8 @@ protected:
|
||||
IntervalList mOldIntervals;
|
||||
nsSMILMilestone mPrevRegisteredMilestone;
|
||||
static const nsSMILMilestone sMaxMilestone;
|
||||
static const PRUint8 sMaxNumIntervals;
|
||||
static const PRUint8 sMaxNumInstanceTimes;
|
||||
|
||||
// Set of dependent time value specs to be notified when establishing a new
|
||||
// current interval. Change notifications and delete notifications are handled
|
||||
|
@ -60,11 +60,11 @@ function main() {
|
||||
is(anim.getStartTime(), 6);
|
||||
|
||||
// Rebind
|
||||
// At this point all the old intervals should be re-added to anim. If they're
|
||||
// not and only the current interval is added to anim we'll get a start time
|
||||
// of 4s instead of 2s.
|
||||
// At this point only the current interval will be re-added to anim (this is
|
||||
// for consistency since old intervals may or may not have been filtered).
|
||||
// Therefore the start time should be 4s instead of 2s.
|
||||
circle.appendChild(anim);
|
||||
is(anim.getStartTime(), 2);
|
||||
is(anim.getStartTime(), 4);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
63
layout/reftests/svg/smil/filtered-instance-time-1.svg
Normal file
63
layout/reftests/svg/smil/filtered-instance-time-1.svg
Normal file
@ -0,0 +1,63 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
class="reftest-wait"
|
||||
onload="go()">
|
||||
<!-- Instance time filtering involves removing instance times that are no
|
||||
longer needed. However, under some arrangements an excessive number of
|
||||
instance times may be generated that will never be cleaned up since they
|
||||
might potentially still affect the behavior of the timing model.
|
||||
|
||||
For example, consider the case where we have a valid cyclic dependency
|
||||
(e.g. a ping-pong effect) between TimeEvents. For example,
|
||||
a.begin=b.endEvent and b.begin=a.endEvent. The times generated by this
|
||||
arrangement won't be cleared by regular filtering since they're
|
||||
technically unpredictable (e.g. seeking the document will cause some
|
||||
events to be suppressed) and so we preserve them to provide correct
|
||||
backwards seeking support.
|
||||
|
||||
Therefore, after reaching a certain threshold, old instance times are
|
||||
simply discarded indiscriminantly to avoid consuming memory in unbounded
|
||||
fashion as the animation progresses.
|
||||
|
||||
This test checks this second stage of instance time filtering. -->
|
||||
<script>
|
||||
function go() {
|
||||
var svg = document.documentElement;
|
||||
var anim = document.getElementById('anim');
|
||||
|
||||
// To begin with we have an animation from 0s-2s
|
||||
svg.pauseAnimations();
|
||||
svg.setCurrentTime(1.0); // Seek to mid-interval
|
||||
|
||||
// Generate a lot of instance times beyond the interval end at t=2s
|
||||
// The threshold will be something like 100 but just in case it's 200
|
||||
// let's make 210 instance times.
|
||||
for (var i = 0; i < 210; i++) {
|
||||
// The first instance time will be at t=3s and then we'll generate lots
|
||||
// of times following on from there
|
||||
anim.beginElementAt(2 + i * 0.1);
|
||||
}
|
||||
|
||||
// Seek past the interval end -- this will cause the filtering to kick in.
|
||||
// The first stage of filtering will only filter instance times before the
|
||||
// end of the previous interval (i.e. before t=2s in this case).
|
||||
// The second stage of filtering should take care of the rest.
|
||||
svg.setCurrentTime(2.5);
|
||||
|
||||
// The second stage of filtering will clear out the oldest times first.
|
||||
// However, since first time we generated at t=3s is now used as begin of
|
||||
// the yet-to-begin current interval it should not be cleared.
|
||||
// So if we force the current interval to be updated by adding another
|
||||
// instance time the next interval should still start at t=3s.
|
||||
anim.beginElementAt(100);
|
||||
|
||||
// Now when we go to do a snapshot at t=3s, the animation should be in
|
||||
// effect.
|
||||
svg.setCurrentTime(3.0);
|
||||
svg.removeAttribute("class");
|
||||
}
|
||||
</script>
|
||||
<rect id="blueRect" x="100" y="15" width="200" height="200" fill="blue">
|
||||
<set id="anim" attributeName="x" to="15" begin="0s" dur="2s"/>
|
||||
</rect>
|
||||
</svg>
|
After Width: | Height: | Size: 2.9 KiB |
@ -175,3 +175,6 @@ fails == anim-strokecolor-1.svg anim-standard-ref.svg # bug 436296
|
||||
== smil-transitions-interaction-3b.svg lime.svg
|
||||
== smil-transitions-interaction-4a.svg lime.svg
|
||||
== smil-transitions-interaction-4b.svg lime.svg
|
||||
|
||||
# Test filtering of excessive times
|
||||
== filtered-instance-time-1.svg anim-standard-ref.svg
|
||||
|
24
layout/reftests/svg/smil/syncbase/filtered-interval-1.svg
Normal file
24
layout/reftests/svg/smil/syncbase/filtered-interval-1.svg
Normal file
@ -0,0 +1,24 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
class="reftest-wait"
|
||||
onload="setTimeAndSnapshot(2, true)">
|
||||
<script xlink:href="../smil-util.js" type="text/javascript"/>
|
||||
<!--
|
||||
This test ensures that even if we have an old interval, if it is part of an
|
||||
active dependency chain it should not be filtered.
|
||||
-->
|
||||
<rect width="100" height="100" fill="orange">
|
||||
<!-- This animation is indirectly dependent on anim 'b' and hence, even
|
||||
though it appears earlier in the document it should be given
|
||||
priority. -->
|
||||
<set attributeName="fill" to="green" begin="a.begin"/>
|
||||
|
||||
<!-- This will generate a series of short intervals such that by t=2s the
|
||||
interval via which the first animation depends on b might be considered
|
||||
as a candidate for filtering. -->
|
||||
<set attributeName="width" to="100" begin="b.begin-2s; a.begin+0.2s"
|
||||
dur="0.1s" id="a"/>
|
||||
|
||||
<set attributeName="fill" to="red" begin="2s" id="b"/>
|
||||
</rect>
|
||||
</svg>
|
After Width: | Height: | Size: 1.0 KiB |
@ -89,3 +89,6 @@
|
||||
== cross-container-1.xhtml green-box-ref.xhtml
|
||||
== cross-container-2.xhtml green-box-ref.xhtml
|
||||
== cross-container-3.xhtml green-box-ref.xhtml
|
||||
|
||||
# Filtering
|
||||
== filtered-interval-1.svg green-box-ref.svg
|
||||
|
Loading…
Reference in New Issue
Block a user