Bug 596796 - SVG SMIL: Fix inconsistent state when resetting current interval; r=dholbert; a=roc

This commit is contained in:
Brian Birtles 2010-10-13 09:20:12 +09:00
parent a7ca4fe4a3
commit 721bffca0b
4 changed files with 48 additions and 40 deletions

View File

@ -0,0 +1,15 @@
<svg xmlns="http://www.w3.org/2000/svg" class="reftest-wait">
<script>
function boom()
{
document.documentElement.appendChild(document.getElementById("a"));
document.documentElement.removeAttribute("class");
}
window.addEventListener("load", boom, false);
</script>
<animate end="a.begin" id="a"/>
</svg>

After

Width:  |  Height:  |  Size: 313 B

View File

@ -20,3 +20,4 @@ load 588287-1.svg
load 588287-2.svg
load 592477-1.xhtml
load 594653-1.svg
load 596796-1.svg

View File

@ -202,9 +202,6 @@ nsSMILTimedElement::nsSMILTimedElement()
nsSMILTimedElement::~nsSMILTimedElement()
{
// Put us in a consistent state in case we get any callbacks
mElementState = STATE_POSTACTIVE;
// Unlink all instance times from dependent intervals
for (PRUint32 i = 0; i < mBeginInstances.Length(); ++i) {
mBeginInstances[i]->Unlink();
@ -218,10 +215,8 @@ nsSMILTimedElement::~nsSMILTimedElement()
// Notify anyone listening to our intervals that they're gone
// (We shouldn't get any callbacks from this because all our instance times
// are now disassociated with any intervals)
if (mCurrentInterval) {
mCurrentInterval->Unlink();
mCurrentInterval = nsnull;
}
mElementState = STATE_POSTACTIVE;
ResetCurrentInterval();
for (PRInt32 i = mOldIntervals.Length() - 1; i >= 0; --i) {
mOldIntervals[i]->Unlink();
@ -703,14 +698,7 @@ nsSMILTimedElement::Rewind()
mSeekState == SEEK_BACKWARD_FROM_ACTIVE,
"Rewind in the middle of a forwards seek?");
// Set the STARTUP state first so that if we get any callbacks we won't waste
// time recalculating the current interval
mElementState = STATE_STARTUP;
mCurrentRepeatIteration = 0;
// Clear the intervals and instance times except those instance times we can't
// regenerate (DOM calls etc.)
RewindTiming();
ClearIntervalProgress();
UnsetBeginSpec(RemoveNonDynamic);
UnsetEndSpec(RemoveNonDynamic);
@ -1116,6 +1104,13 @@ nsSMILTimedElement::IsTimeDependent(const nsSMILTimedElement& aOther) const
void
nsSMILTimedElement::BindToTree(nsIContent* aContextNode)
{
// If we were already active then clear all our timing information and start
// afresh
if (mElementState != STATE_STARTUP) {
mSeekState = SEEK_NOT_SEEKING;
Rewind();
}
// Resolve references to other parts of the tree
PRUint32 count = mBeginSpecs.Length();
for (PRUint32 i = 0; i < count; ++i) {
@ -1127,23 +1122,9 @@ nsSMILTimedElement::BindToTree(nsIContent* aContextNode)
mEndSpecs[j]->ResolveReferences(aContextNode);
}
// If this element was already in play then we may need to do a local rewind.
nsSMILTime containerTime = GetTimeContainer()->GetCurrentTime();
PRBool localRewind =
mElementState != STATE_STARTUP && mCurrentInterval &&
mCurrentInterval->Begin()->Time().GetMillis() > containerTime;
if (localRewind) {
Rewind();
// Put the time container in the seeking state -- this is necessary so we
// don't generate unnecessary events whilst doing the backwards seek.
GetTimeContainer()->SetCurrentTime(containerTime);
} else {
// Otherwise, re-register any previous milestone since it might be been
// processed whilst we were not bound to the tree.
mPrevRegisteredMilestone = sMaxMilestone;
RegisterMilestone();
}
// Register new milestone
mPrevRegisteredMilestone = sMaxMilestone;
RegisterMilestone();
}
void
@ -1269,13 +1250,13 @@ nsSMILTimedElement::ClearSpecs(TimeValueSpecList& aSpecs,
}
void
nsSMILTimedElement::RewindTiming()
nsSMILTimedElement::ClearIntervalProgress()
{
if (mCurrentInterval) {
mCurrentInterval->Unlink();
mCurrentInterval = nsnull;
}
mElementState = STATE_STARTUP;
mCurrentRepeatIteration = 0;
ResetCurrentInterval();
// Remove old intervals
for (PRInt32 i = mOldIntervals.Length() - 1; i >= 0; --i) {
mOldIntervals[i]->Unlink();
}
@ -1893,8 +1874,7 @@ nsSMILTimedElement::UpdateCurrentInterval(PRBool aForceChangeNotice)
if (mElementState == STATE_ACTIVE || mElementState == STATE_WAITING) {
mElementState = STATE_POSTACTIVE;
mCurrentInterval->Unlink();
mCurrentInterval = nsnull;
ResetCurrentInterval();
}
}
}

View File

@ -409,7 +409,7 @@ protected:
void ClearSpecs(TimeValueSpecList& aSpecs,
InstanceTimeList& aInstances,
RemovalTestFunction aRemove);
void RewindTiming();
void ClearIntervalProgress();
void DoSampleAt(nsSMILTime aContainerTime, PRBool aEndOnly);
/**
@ -513,6 +513,18 @@ protected:
PRBool HasPlayed() const { return !mOldIntervals.IsEmpty(); }
PRBool EndHasEventConditions() const;
// Reset the current interval by first passing ownership to a temporary
// variable so that if Unlink() results in us receiving a callback,
// mCurrentInterval will be nsnull and we will be in a consistent state.
void ResetCurrentInterval()
{
if (mCurrentInterval) {
// Transfer ownership to temp var. (This sets mCurrentInterval to null.)
nsAutoPtr<nsSMILInterval> interval(mCurrentInterval);
interval->Unlink();
}
}
// Hashtable callback methods
PR_STATIC_CALLBACK(PLDHashOperator) NotifyNewIntervalCallback(
TimeValueSpecPtrKey* aKey, void* aData);