Bug 650732 - Move interval change notifications into the timed element, r=dholbert

This commit is contained in:
Brian Birtles 2011-06-15 09:16:57 +09:00
Родитель 43e1226f06
Коммит 074c34fc15
5 изменённых файлов: 71 добавлений и 64 удалений

Просмотреть файл

@ -128,8 +128,13 @@ nsSMILInstanceTime::HandleChangedInterval(
PRBool aBeginObjectChanged, PRBool aBeginObjectChanged,
PRBool aEndObjectChanged) PRBool aEndObjectChanged)
{ {
NS_ABORT_IF_FALSE(mBaseInterval, // It's possible a sequence of notifications might cause our base interval to
"Got call to HandleChangedInterval on an independent instance time."); // be updated and then deleted. Furthermore, the delete might happen whilst
// we're still in the queue to be notified of the change. In any case, if we
// don't have a base interval, just ignore the change.
if (!mBaseInterval)
return;
NS_ABORT_IF_FALSE(mCreator, "Base interval is set but creator is not."); NS_ABORT_IF_FALSE(mCreator, "Base interval is set but creator is not.");
if (mVisited) { if (mVisited) {

Просмотреть файл

@ -40,9 +40,7 @@
nsSMILInterval::nsSMILInterval() nsSMILInterval::nsSMILInterval()
: :
mBeginFixed(PR_FALSE), mBeginFixed(PR_FALSE),
mEndFixed(PR_FALSE), mEndFixed(PR_FALSE)
mBeginObjectChanged(PR_FALSE),
mEndObjectChanged(PR_FALSE)
{ {
} }
@ -51,9 +49,7 @@ nsSMILInterval::nsSMILInterval(const nsSMILInterval& aOther)
mBegin(aOther.mBegin), mBegin(aOther.mBegin),
mEnd(aOther.mEnd), mEnd(aOther.mEnd),
mBeginFixed(PR_FALSE), mBeginFixed(PR_FALSE),
mEndFixed(PR_FALSE), mEndFixed(PR_FALSE)
mBeginObjectChanged(PR_FALSE),
mEndObjectChanged(PR_FALSE)
{ {
NS_ABORT_IF_FALSE(aOther.mDependentTimes.IsEmpty(), NS_ABORT_IF_FALSE(aOther.mDependentTimes.IsEmpty(),
"Attempting to copy-construct an interval with dependent times, " "Attempting to copy-construct an interval with dependent times, "
@ -74,18 +70,6 @@ nsSMILInterval::~nsSMILInterval()
"Unlink was not called"); "Unlink was not called");
} }
void
nsSMILInterval::NotifyChanged(const nsSMILTimeContainer* aContainer)
{
for (PRInt32 i = mDependentTimes.Length() - 1; i >= 0; --i) {
mDependentTimes[i]->HandleChangedInterval(aContainer,
mBeginObjectChanged,
mEndObjectChanged);
}
mBeginObjectChanged = PR_FALSE;
mEndObjectChanged = PR_FALSE;
}
void void
nsSMILInterval::Unlink(PRBool aFiltered) nsSMILInterval::Unlink(PRBool aFiltered)
{ {
@ -131,11 +115,7 @@ nsSMILInterval::SetBegin(nsSMILInstanceTime& aBegin)
NS_ABORT_IF_FALSE(!mBeginFixed, NS_ABORT_IF_FALSE(!mBeginFixed,
"Attempting to set begin time but the begin point is fixed"); "Attempting to set begin time but the begin point is fixed");
if (mBegin == &aBegin)
return;
mBegin = &aBegin; mBegin = &aBegin;
mBeginObjectChanged = PR_TRUE;
} }
void void
@ -144,11 +124,7 @@ nsSMILInterval::SetEnd(nsSMILInstanceTime& aEnd)
NS_ABORT_IF_FALSE(!mEndFixed, NS_ABORT_IF_FALSE(!mEndFixed,
"Attempting to set end time but the end point is fixed"); "Attempting to set end time but the end point is fixed");
if (mEnd == &aEnd)
return;
mEnd = &aEnd; mEnd = &aEnd;
mEndObjectChanged = PR_TRUE;
} }
void void
@ -193,6 +169,12 @@ nsSMILInterval::RemoveDependentTime(const nsSMILInstanceTime& aTime)
NS_ABORT_IF_FALSE(found, "Couldn't find instance time to delete."); NS_ABORT_IF_FALSE(found, "Couldn't find instance time to delete.");
} }
void
nsSMILInterval::GetDependentTimes(InstanceTimeList& aTimes)
{
aTimes = mDependentTimes;
}
PRBool PRBool
nsSMILInterval::IsDependencyChainLink() const nsSMILInterval::IsDependencyChainLink() const
{ {

Просмотреть файл

@ -56,7 +56,6 @@ public:
nsSMILInterval(); nsSMILInterval();
nsSMILInterval(const nsSMILInterval& aOther); nsSMILInterval(const nsSMILInterval& aOther);
~nsSMILInterval(); ~nsSMILInterval();
void NotifyChanged(const nsSMILTimeContainer* aContainer);
void Unlink(PRBool aFiltered = PR_FALSE); void Unlink(PRBool aFiltered = PR_FALSE);
const nsSMILInstanceTime* Begin() const const nsSMILInstanceTime* Begin() const
@ -86,8 +85,11 @@ public:
void FixBegin(); void FixBegin();
void FixEnd(); void FixEnd();
typedef nsTArray<nsRefPtr<nsSMILInstanceTime> > InstanceTimeList;
void AddDependentTime(nsSMILInstanceTime& aTime); void AddDependentTime(nsSMILInstanceTime& aTime);
void RemoveDependentTime(const nsSMILInstanceTime& aTime); void RemoveDependentTime(const nsSMILInstanceTime& aTime);
void GetDependentTimes(InstanceTimeList& aTimes);
// Cue for assessing if this interval can be filtered // Cue for assessing if this interval can be filtered
PRBool IsDependencyChainLink() const; PRBool IsDependencyChainLink() const;
@ -96,8 +98,6 @@ private:
nsRefPtr<nsSMILInstanceTime> mBegin; nsRefPtr<nsSMILInstanceTime> mBegin;
nsRefPtr<nsSMILInstanceTime> mEnd; nsRefPtr<nsSMILInstanceTime> mEnd;
typedef nsTArray<nsRefPtr<nsSMILInstanceTime> > InstanceTimeList;
// nsSMILInstanceTimes to notify when this interval is changed or deleted. // nsSMILInstanceTimes to notify when this interval is changed or deleted.
InstanceTimeList mDependentTimes; InstanceTimeList mDependentTimes;
@ -112,21 +112,6 @@ private:
// OBJECT returned for that end point and its TIME value will not change. // OBJECT returned for that end point and its TIME value will not change.
PRPackedBool mBeginFixed; PRPackedBool mBeginFixed;
PRPackedBool mEndFixed; 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:
//
// * Changes to the time of an interval endpoint
// * Changes in the relative times of different time containers
// * Changes to the dependency chain (which may affect the animation sandwich)
//
// The nsSMILTimeValueSpec can detect the first two changes by recalculating
// the time but in order to help detect the third change we simply set a flag
// whenever the mBegin or mEnd pointers are changed. These flags are reset
// when the next change notification is sent.
PRPackedBool mBeginObjectChanged;
PRPackedBool mEndObjectChanged;
}; };
#endif // NS_SMILINTERVAL_H_ #endif // NS_SMILINTERVAL_H_

Просмотреть файл

@ -551,7 +551,10 @@ nsSMILTimedElement::DoSampleAt(nsSMILTime aContainerTime, PRBool aEndOnly)
case STATE_ACTIVE: case STATE_ACTIVE:
{ {
ApplyEarlyEnd(sampleTime); // Ending early will change the interval but we don't notify dependents
// of the change until we have closed off the current interval (since we
// don't want dependencies to un-end our early end).
PRBool didApplyEarlyEnd = ApplyEarlyEnd(sampleTime);
if (mCurrentInterval->End()->Time() <= sampleTime) { if (mCurrentInterval->End()->Time() <= sampleTime) {
nsSMILInterval newInterval; nsSMILInterval newInterval;
@ -567,15 +570,23 @@ nsSMILTimedElement::DoSampleAt(nsSMILTime aContainerTime, PRBool aEndOnly)
} }
mCurrentRepeatIteration = 0; mCurrentRepeatIteration = 0;
mOldIntervals.AppendElement(mCurrentInterval.forget()); mOldIntervals.AppendElement(mCurrentInterval.forget());
// We must update mOldIntervals before calling SampleFillValue
SampleFillValue(); SampleFillValue();
if (mElementState == STATE_WAITING) { if (mElementState == STATE_WAITING) {
mCurrentInterval = new nsSMILInterval(newInterval); mCurrentInterval = new nsSMILInterval(newInterval);
}
// We are now in a consistent state to dispatch notifications
if (didApplyEarlyEnd) {
NotifyChangedInterval(
mOldIntervals[mOldIntervals.Length() - 1], PR_FALSE, PR_TRUE);
}
if (mElementState == STATE_WAITING) {
NotifyNewInterval(); NotifyNewInterval();
} }
FilterHistory(); FilterHistory();
stateChanged = PR_TRUE; stateChanged = PR_TRUE;
} else { } else {
NS_ABORT_IF_FALSE(!didApplyEarlyEnd,
"We got an early end, but didn't end");
nsSMILTime beginTime = mCurrentInterval->Begin()->Time().GetMillis(); nsSMILTime beginTime = mCurrentInterval->Begin()->Time().GetMillis();
NS_ASSERTION(aContainerTime >= beginTime, NS_ASSERTION(aContainerTime >= beginTime,
"Sample time should not precede current interval"); "Sample time should not precede current interval");
@ -626,7 +637,7 @@ nsSMILTimedElement::HandleContainerTimeChange()
// the nsSMILTimeValueSpec we'll check if anything has changed and if not, we // the nsSMILTimeValueSpec we'll check if anything has changed and if not, we
// won't go any further. // won't go any further.
if (mElementState == STATE_WAITING || mElementState == STATE_ACTIVE) { if (mElementState == STATE_WAITING || mElementState == STATE_ACTIVE) {
NotifyChangedInterval(); NotifyChangedInterval(mCurrentInterval, PR_FALSE, PR_FALSE);
} }
} }
@ -1237,13 +1248,15 @@ nsSMILTimedElement::ClearIntervalProgress()
mOldIntervals.Clear(); mOldIntervals.Clear();
} }
void PRBool
nsSMILTimedElement::ApplyEarlyEnd(const nsSMILTimeValue& aSampleTime) nsSMILTimedElement::ApplyEarlyEnd(const nsSMILTimeValue& aSampleTime)
{ {
// This should only be called within DoSampleAt as a helper function // This should only be called within DoSampleAt as a helper function
NS_ABORT_IF_FALSE(mElementState == STATE_ACTIVE, NS_ABORT_IF_FALSE(mElementState == STATE_ACTIVE,
"Unexpected state to try to apply an early end"); "Unexpected state to try to apply an early end");
PRBool updated = PR_FALSE;
// Only apply an early end if we're not already ending. // Only apply an early end if we're not already ending.
if (mCurrentInterval->End()->Time() > aSampleTime) { if (mCurrentInterval->End()->Time() > aSampleTime) {
nsSMILInstanceTime* earlyEnd = CheckForEarlyEnd(aSampleTime); nsSMILInstanceTime* earlyEnd = CheckForEarlyEnd(aSampleTime);
@ -1258,9 +1271,10 @@ nsSMILTimedElement::ApplyEarlyEnd(const nsSMILTimeValue& aSampleTime)
} else { } else {
mCurrentInterval->SetEnd(*earlyEnd); mCurrentInterval->SetEnd(*earlyEnd);
} }
NotifyChangedInterval(); updated = PR_TRUE;
} }
} }
return updated;
} }
namespace namespace
@ -1815,22 +1829,23 @@ nsSMILTimedElement::UpdateCurrentInterval(PRBool aForceChangeNotice)
} else { } else {
PRBool changed = PR_FALSE; PRBool beginChanged = PR_FALSE;
PRBool endChanged = PR_FALSE;
if (mElementState != STATE_ACTIVE && if (mElementState != STATE_ACTIVE &&
!updatedInterval.Begin()->SameTimeAndBase( !updatedInterval.Begin()->SameTimeAndBase(
*mCurrentInterval->Begin())) { *mCurrentInterval->Begin())) {
mCurrentInterval->SetBegin(*updatedInterval.Begin()); mCurrentInterval->SetBegin(*updatedInterval.Begin());
changed = PR_TRUE; beginChanged = PR_TRUE;
} }
if (!updatedInterval.End()->SameTimeAndBase(*mCurrentInterval->End())) { if (!updatedInterval.End()->SameTimeAndBase(*mCurrentInterval->End())) {
mCurrentInterval->SetEnd(*updatedInterval.End()); mCurrentInterval->SetEnd(*updatedInterval.End());
changed = PR_TRUE; endChanged = PR_TRUE;
} }
if (changed || aForceChangeNotice) { if (beginChanged || endChanged || aForceChangeNotice) {
NotifyChangedInterval(); NotifyChangedInterval(mCurrentInterval, beginChanged, endChanged);
} }
} }
@ -1844,7 +1859,7 @@ nsSMILTimedElement::UpdateCurrentInterval(PRBool aForceChangeNotice)
if (!mCurrentInterval->End()->SameTimeAndBase(*mCurrentInterval->Begin())) if (!mCurrentInterval->End()->SameTimeAndBase(*mCurrentInterval->Begin()))
{ {
mCurrentInterval->SetEnd(*mCurrentInterval->Begin()); mCurrentInterval->SetEnd(*mCurrentInterval->Begin());
NotifyChangedInterval(); NotifyChangedInterval(mCurrentInterval, PR_FALSE, PR_TRUE);
} }
// The transition to the postactive state will take place on the next // The transition to the postactive state will take place on the next
// sample (along with firing end events, clearing intervals etc.) // sample (along with firing end events, clearing intervals etc.)
@ -2023,18 +2038,27 @@ nsSMILTimedElement::NotifyNewInterval()
} }
void void
nsSMILTimedElement::NotifyChangedInterval() nsSMILTimedElement::NotifyChangedInterval(nsSMILInterval* aInterval,
PRBool aBeginObjectChanged,
PRBool aEndObjectChanged)
{ {
NS_ABORT_IF_FALSE(mCurrentInterval, NS_ABORT_IF_FALSE(aInterval, "Null interval for change notification");
"Attempting to notify dependents of a changed interval but the interval "
"is not set--perhaps we should be deleting the interval instead?");
nsSMILTimeContainer* container = GetTimeContainer(); nsSMILTimeContainer* container = GetTimeContainer();
if (container) { if (container) {
container->SyncPauseTime(); container->SyncPauseTime();
} }
mCurrentInterval->NotifyChanged(container); // Copy the instance times list since notifying the instance times can result
// in a chain reaction whereby our own interval gets deleted along with its
// instance times.
InstanceTimeList times;
aInterval->GetDependentTimes(times);
for (PRUint32 i = 0; i < times.Length(); ++i) {
times[i]->HandleChangedInterval(container, aBeginObjectChanged,
aEndObjectChanged);
}
} }
void void

Просмотреть файл

@ -423,8 +423,10 @@ protected:
* applied at the last possible moment (i.e. if they are at * applied at the last possible moment (i.e. if they are at
* or before the current sample time) and only if the * or before the current sample time) and only if the
* current interval is not already ending. * current interval is not already ending.
* @return PR_TRUE if the end time of the current interval was updated,
* PR_FALSE otherwise.
*/ */
void ApplyEarlyEnd(const nsSMILTimeValue& aSampleTime); PRBool ApplyEarlyEnd(const nsSMILTimeValue& aSampleTime);
/** /**
* Clears certain state in response to the element restarting. * Clears certain state in response to the element restarting.
@ -504,8 +506,17 @@ protected:
void RegisterMilestone(); void RegisterMilestone();
PRBool GetNextMilestone(nsSMILMilestone& aNextMilestone) const; PRBool GetNextMilestone(nsSMILMilestone& aNextMilestone) const;
// Notification methods. Note that these notifications can result in nested
// calls to this same object. Therefore,
// (i) we should not perform notification until this object is in
// a consistent state to receive callbacks, and
// (ii) after calling these methods we must assume that the state of the
// element may have changed.
void NotifyNewInterval(); void NotifyNewInterval();
void NotifyChangedInterval(); void NotifyChangedInterval(nsSMILInterval* aInterval,
PRBool aBeginObjectChanged,
PRBool aEndObjectChanged);
void FireTimeEventAsync(PRUint32 aMsg, PRInt32 aDetail); void FireTimeEventAsync(PRUint32 aMsg, PRInt32 aDetail);
const nsSMILInstanceTime* GetEffectiveBeginInstance() const; const nsSMILInstanceTime* GetEffectiveBeginInstance() const;
const nsSMILInterval* GetPreviousInterval() const; const nsSMILInterval* GetPreviousInterval() const;