diff --git a/content/smil/nsSMILAnimationController.cpp b/content/smil/nsSMILAnimationController.cpp index 58ff14d54a2..9d9ce2a7627 100644 --- a/content/smil/nsSMILAnimationController.cpp +++ b/content/smil/nsSMILAnimationController.cpp @@ -64,7 +64,8 @@ const PRUint32 nsSMILAnimationController::kTimerInterval = 22; // ctors, dtors, factory methods nsSMILAnimationController::nsSMILAnimationController() - : mDocument(nsnull) + : mResampleNeeded(PR_FALSE), + mDocument(nsnull) { mAnimationElementTable.Init(); mChildContainerTable.Init(); @@ -77,11 +78,6 @@ nsSMILAnimationController::~nsSMILAnimationController() mTimer = nsnull; } - if (mForceSampleEvent) { - mForceSampleEvent->Expire(); - mForceSampleEvent = nsnull; - } - NS_ASSERTION(mAnimationElementTable.Count() == 0, "Animation controller shouldn't be tracking any animation" " elements when it dies."); @@ -138,7 +134,7 @@ nsSMILAnimationController::Resume(PRUint32 aType) nsSMILTimeContainer::Resume(aType); - if (wasPaused && !mPauseState) { + if (wasPaused && !mPauseState && mChildContainerTable.Count()) { StartTimer(); } } @@ -168,38 +164,12 @@ nsSMILAnimationController::UnregisterAnimationElement( } //---------------------------------------------------------------------- -// nsSMILAnimationController methods: - -nsresult -nsSMILAnimationController::OnForceSample() -{ - // Make sure this was a queued call - NS_ENSURE_TRUE(mForceSampleEvent, NS_ERROR_FAILURE); - - nsresult rv = NS_OK; - if (!mPauseState) { - // Stop timer-controlled samples first, to avoid race conditions. - rv = StopTimer(); - if (NS_SUCCEEDED(rv)) { - // StartTimer does a synchronous sample before it starts the timer. - // This is the sample that we're "forcing" here. - rv = StartTimer(); - } - } - mForceSampleEvent = nsnull; - return rv; -} +// Resampling methods void -nsSMILAnimationController::FireForceSampleEvent() +nsSMILAnimationController::Resample() { - if (!mForceSampleEvent) { - mForceSampleEvent = new ForceSampleEvent(*this); - if (NS_FAILED(NS_DispatchToCurrentThread(mForceSampleEvent))) { - NS_WARNING("Failed to dispatch force sample event"); - mForceSampleEvent = nsnull; - } - } + DoSample(PR_FALSE); } //---------------------------------------------------------------------- @@ -242,7 +212,6 @@ nsSMILAnimationController::CompositorTableEntryTraverse( return PL_DHASH_NEXT; } - void nsSMILAnimationController::Unlink() { @@ -296,14 +265,24 @@ nsSMILAnimationController::StopTimer() void nsSMILAnimationController::DoSample() { + DoSample(PR_TRUE); // Skip unchanged time containers +} + +void +nsSMILAnimationController::DoSample(PRBool aSkipUnchangedContainers) +{ + // Reset resample flag + mResampleNeeded = PR_FALSE; + // STEP 1: Sample the child time containers // // When we sample the child time containers they will simply record the sample // time in document time. TimeContainerHashtable activeContainers; activeContainers.Init(mChildContainerTable.Count()); - mChildContainerTable.EnumerateEntries(SampleTimeContainers, - &activeContainers); + SampleTimeContainerParams tcParams = { &activeContainers, + aSkipUnchangedContainers }; + mChildContainerTable.EnumerateEntries(SampleTimeContainer, &tcParams); // STEP 2: (i) Sample the timed elements AND // (ii) Create a table of compositors @@ -332,9 +311,10 @@ nsSMILAnimationController::DoSample() return; currentCompositorTable->Init(0); - SampleAnimationParams params = { &activeContainers, currentCompositorTable }; + SampleAnimationParams saParams = { &activeContainers, + currentCompositorTable }; nsresult rv = mAnimationElementTable.EnumerateEntries(SampleAnimation, - ¶ms); + &saParams); if (NS_FAILED(rv)) { NS_WARNING("SampleAnimationParams failed"); } @@ -356,23 +336,25 @@ nsSMILAnimationController::DoSample() // Update last compositor table mLastCompositorTable = currentCompositorTable.forget(); + + NS_ASSERTION(!mResampleNeeded, "Resample dirty flag set during sample!"); } /*static*/ PR_CALLBACK PLDHashOperator -nsSMILAnimationController::SampleTimeContainers(TimeContainerPtrKey* aKey, - void* aData) +nsSMILAnimationController::SampleTimeContainer(TimeContainerPtrKey* aKey, + void* aData) { NS_ENSURE_TRUE(aKey, PL_DHASH_NEXT); NS_ENSURE_TRUE(aKey->GetKey(), PL_DHASH_NEXT); NS_ENSURE_TRUE(aData, PL_DHASH_NEXT); - TimeContainerHashtable* activeContainers - = static_cast(aData); + SampleTimeContainerParams* params = + static_cast(aData); nsSMILTimeContainer* container = aKey->GetKey(); - if (container->NeedsSample()) { + if (container->NeedsSample() || !params->mSkipUnchangedContainers) { container->Sample(); - activeContainers->PutEntry(container); + params->mActiveContainers->PutEntry(container); } return PL_DHASH_NEXT; @@ -410,7 +392,7 @@ nsSMILAnimationController::SampleTimedElement( // return false. // // Instead we build up a hashmap of active time containers during the previous - // step (SampleTimeContainers) and then test here if the container for this + // step (SampleTimeContainer) and then test here if the container for this // timed element is in the list. if (!aActiveContainers->GetEntry(timeContainer)) return; diff --git a/content/smil/nsSMILAnimationController.h b/content/smil/nsSMILAnimationController.h index 1be37db796e..fb5ca522f68 100644 --- a/content/smil/nsSMILAnimationController.h +++ b/content/smil/nsSMILAnimationController.h @@ -45,7 +45,6 @@ #include "nsITimer.h" #include "nsTHashtable.h" #include "nsHashKeys.h" -#include "nsThreadUtils.h" // for nsRunnable #include "nsSMILTimeContainer.h" #include "nsSMILCompositorTable.h" @@ -80,17 +79,26 @@ public: void RegisterAnimationElement(nsISMILAnimationElement* aAnimationElement); void UnregisterAnimationElement(nsISMILAnimationElement* aAnimationElement); - // Methods for forcing synchronous samples - nsresult OnForceSample(); - void FireForceSampleEvent(); + // Methods for resampling all animations + // (A resample performs the same operations as a sample but doesn't advance + // the current time and doesn't check if the container is paused) + void Resample(); + void SetResampleNeeded() { mResampleNeeded = PR_TRUE; } + void FlushResampleRequests() + { + if (!mResampleNeeded) + return; + + DoSample(PR_FALSE); // Don't skip unchanged time containers--sample them all + } // Methods for handling page transitions - void OnPageShow(); - void OnPageHide(); + void OnPageShow(); + void OnPageHide(); // Methods for supporting cycle-collection - void Traverse(nsCycleCollectionTraversalCallback* aCallback); - void Unlink(); + void Traverse(nsCycleCollectionTraversalCallback* aCallback); + void Unlink(); protected: // Typedefs @@ -99,22 +107,10 @@ protected: typedef nsPtrHashKey AnimationElementPtrKey; typedef nsTHashtable AnimationElementHashtable; - // Types - class ForceSampleEvent : public nsRunnable { - public: - ForceSampleEvent(nsSMILAnimationController &aAnimationController) - : mAnimationController(&aAnimationController) { } - - NS_IMETHOD Run() { - if (!mAnimationController) - return NS_OK; - - return mAnimationController->OnForceSample(); - } - void Expire() { mAnimationController = nsnull; } - - private: - nsSMILAnimationController* mAnimationController; + struct SampleTimeContainerParams + { + TimeContainerHashtable* mActiveContainers; + PRBool mSkipUnchangedContainers; }; struct SampleAnimationParams @@ -139,7 +135,8 @@ protected: // Sample-related callbacks and implementation helpers virtual void DoSample(); - PR_STATIC_CALLBACK(PLDHashOperator) SampleTimeContainers( + void DoSample(PRBool aSkipUnchangedContainers); + PR_STATIC_CALLBACK(PLDHashOperator) SampleTimeContainer( TimeContainerPtrKey* aKey, void* aData); PR_STATIC_CALLBACK(PLDHashOperator) SampleAnimation( AnimationElementPtrKey* aKey, void* aData); @@ -161,7 +158,7 @@ protected: nsCOMPtr mTimer; AnimationElementHashtable mAnimationElementTable; TimeContainerHashtable mChildContainerTable; - nsRefPtr mForceSampleEvent; + PRPackedBool mResampleNeeded; // Store raw ptr to mDocument. It owns the controller, so controller // shouldn't outlive it diff --git a/content/smil/nsSMILTimedElement.cpp b/content/smil/nsSMILTimedElement.cpp index 302a9d60e53..cd2d53a843d 100644 --- a/content/smil/nsSMILTimedElement.cpp +++ b/content/smil/nsSMILTimedElement.cpp @@ -106,11 +106,40 @@ nsresult nsSMILTimedElement::BeginElementAt(double aOffsetSeconds, const nsSMILTimeContainer* aContainer) { - if (!AddInstanceTimeFromCurrentTime(aOffsetSeconds, PR_TRUE, aContainer)) { - // Probably we don't have a time container - NS_ERROR("Failed to begin element"); + if (!aContainer) return NS_ERROR_FAILURE; - } + + nsSMILTime currentTime = aContainer->GetCurrentTime(); + + AddInstanceTimeFromCurrentTime(currentTime, aOffsetSeconds, PR_TRUE); + + // After we've added the instance time we must do a local resample. + // + // The reason for this can be explained by considering the following sequence + // of calls in a script block + // + // BeginElementAt(0) + // BeginElementAt(-1) + // GetStartTime() <-- should return the time from the first call to + // BeginElementAt + // + // After BeginElementAt(0) is called a new begin instance time is added to the + // list. Depending on the restart mode this may generate a new interval, + // possiblying ending the current interval early. + // + // Intuitively this change should take effect before the subsequent call to + // BeginElementAt however to get this to take effect we need to drive the + // state engine through it's sequence active-waiting-active by calling Sample. + // + // When we get the second call to BeginElementAt the element should be in the + // active state and hence the new begin instance time will be ignored because + // it is before the beginning of the (new) current interval. SMIL says we do + // not change the begin of a current interval once it is active. + // + // See also: + // http://www.w3.org/TR/SMIL3/smil-timing.html#Timing-BeginEnd-Restart + + SampleAt(currentTime); return NS_OK; } @@ -119,11 +148,12 @@ nsresult nsSMILTimedElement::EndElementAt(double aOffsetSeconds, const nsSMILTimeContainer* aContainer) { - if (!AddInstanceTimeFromCurrentTime(aOffsetSeconds, PR_FALSE, aContainer)) { - // Probably we don't have a time container - NS_ERROR("Failed to end element"); + if (!aContainer) return NS_ERROR_FAILURE; - } + + nsSMILTime currentTime = aContainer->GetCurrentTime(); + AddInstanceTimeFromCurrentTime(currentTime, aOffsetSeconds, PR_FALSE); + SampleAt(currentTime); return NS_OK; } @@ -439,6 +469,7 @@ nsSMILTimedElement::SetSimpleDuration(const nsAString& aDurSpec) "Setting unresolved simple duration"); mSimpleDur = duration; + UpdateCurrentInterval(); return NS_OK; } @@ -549,13 +580,13 @@ nsSMILTimedElement::SetRepeatCount(const nsAString& aRepeatCountSpec) nsresult rv = nsSMILParserUtils::ParseRepeatCount(aRepeatCountSpec, newRepeatCount); - UpdateCurrentInterval(); - if (NS_SUCCEEDED(rv)) { mRepeatCount = newRepeatCount; } else { mRepeatCount.Unset(); } + + UpdateCurrentInterval(); return rv; } @@ -581,9 +612,8 @@ nsSMILTimedElement::SetRepeatDur(const nsAString& aRepeatDurSpec) return NS_ERROR_FAILURE; } - UpdateCurrentInterval(); - mRepeatDur = duration; + UpdateCurrentInterval(); return NS_OK; } @@ -640,6 +670,7 @@ nsSMILTimedElement::SetBeginOrEndSpec(const nsAString& aSpec, timeSpecsList.Clear(); instancesList.Clear(); + HardReset(); // XXX Need to take care of time dependents here PRInt32 start; PRInt32 end = -1; @@ -695,7 +726,7 @@ nsSMILTimedElement::GetNextInterval(const nsSMILTimeValue& aBeginAfter, // that has already been used in another interval. See the pseudocode in // SMILANIM 3.6.8 for getFirstInterval. // - PRInt32 endMaxPos = 0; + PRInt32 endMaxPos = 0; if (mRestartMode == RESTART_NEVER && !aFirstInterval) return NS_ERROR_FAILURE; @@ -1025,27 +1056,16 @@ nsSMILTimedElement::SampleFillValue() } } -PRBool -nsSMILTimedElement::AddInstanceTimeFromCurrentTime(double aOffsetSeconds, - PRBool aIsBegin, const nsSMILTimeContainer* aContainer) +void +nsSMILTimedElement::AddInstanceTimeFromCurrentTime(nsSMILTime aCurrentTime, + double aOffsetSeconds, PRBool aIsBegin) { - /* - * SMIL doesn't say what to do if someone calls BeginElement etc. before the - * document has started. For now we just fail. - */ - if (!aContainer) - return PR_FALSE; - double offset = aOffsetSeconds * PR_MSEC_PER_SEC; - - nsSMILTime timeWithOffset = - aContainer->GetCurrentTime() + PRInt64(NS_round(offset)); + nsSMILTime timeWithOffset = aCurrentTime + PRInt64(NS_round(offset)); nsSMILTimeValue timeVal; timeVal.SetMillis(timeWithOffset); nsSMILInstanceTime instanceTime(timeVal, nsnull, PR_TRUE); AddInstanceTime(instanceTime, aIsBegin); - - return PR_TRUE; } diff --git a/content/smil/nsSMILTimedElement.h b/content/smil/nsSMILTimedElement.h index 9ca88a8dc38..b1a640471b8 100644 --- a/content/smil/nsSMILTimedElement.h +++ b/content/smil/nsSMILTimedElement.h @@ -269,8 +269,8 @@ protected: void UpdateCurrentInterval(); void SampleSimpleTime(nsSMILTime aActiveTime); void SampleFillValue(); - PRBool AddInstanceTimeFromCurrentTime(double aOffsetSeconds, - PRBool aIsBegin, const nsSMILTimeContainer* aContainer); + void AddInstanceTimeFromCurrentTime(nsSMILTime aCurrentTime, + double aOffsetSeconds, PRBool aIsBegin); // Typedefs typedef nsTArray > SMILTimeValueSpecList; diff --git a/content/smil/test/Makefile.in b/content/smil/test/Makefile.in index 01651588533..937e0147cce 100644 --- a/content/smil/test/Makefile.in +++ b/content/smil/test/Makefile.in @@ -46,6 +46,8 @@ include $(topsrcdir)/config/rules.mk _TEST_FILES = test_smilRestart.xhtml \ test_smilGetStartTime.xhtml \ test_smilGetSimpleDuration.xhtml \ + test_smilSync.xhtml \ + test_smilSyncTransform.xhtml \ $(NULL) libs:: $(_TEST_FILES) diff --git a/content/smil/test/test_smilGetStartTime.xhtml b/content/smil/test/test_smilGetStartTime.xhtml index d036a5bc274..64bc65a7518 100644 --- a/content/smil/test/test_smilGetStartTime.xhtml +++ b/content/smil/test/test_smilGetStartTime.xhtml @@ -27,8 +27,6 @@ var anim = document.getElementById("anim"); SimpleTest.waitForExplicitFinish(); function main() { - // Basic setting and getting - // indefinite is(anim.getStartTime(), 0, "Unexpected start time with indefinite begin"); @@ -44,47 +42,25 @@ function main() { // Now test over the lifetime of the animation when there are multiple // intervals - // - // We do this via a series of setTimeouts because currently we sample (and - // update the model) asynchronously after a call to setCurrentTime anim.setAttribute("begin", "1s; 3s"); - is(anim.getStartTime(), 1, "Unexpected start time with begin=1s; 3s"); + is(anim.getStartTime(), 1, "Unexpected start time before first interval"); + svg.setCurrentTime(1); - setTimeout('checkStartOfFirstInterval()', 0); -} + is(anim.getStartTime(), 1, + "Unexpected start time at start of first interval"); -function checkStartOfFirstInterval() { - is(anim.getStartTime(), 1, "Unexpected start time during first interval"); svg.setCurrentTime(1.5); - setTimeout('checkMidFirstInterval()', 0); -} + is(anim.getStartTime(), 1, "Unexpected start time during first interval"); -function checkMidFirstInterval() { - is(anim.getStartTime(), 1, "Unexpected start time at end of first interval"); svg.setCurrentTime(2); - setTimeout('checkEndOfFirstInterval()', 0); -} + is(anim.getStartTime(), 1, "Unexpected start time after first interval"); -function checkEndOfFirstInterval() { - is(anim.getStartTime(), 1, "Unexpected start time in active interval"); - svg.setCurrentTime(2.5); - setTimeout('checkAfterFirstInterval()', 0); -} - -function checkAfterFirstInterval() { - is(anim.getStartTime(), 1, "Unexpected start time while waiting"); svg.setCurrentTime(3); - setTimeout('checkStartOfSecondInterval()', 0); -} + is(anim.getStartTime(), 3, "Unexpected start time during second interval"); -function checkStartOfSecondInterval() { - is(anim.getStartTime(), 3, "Unexpected start time in second active interval"); svg.setCurrentTime(4); - setTimeout('checkEndOfSecondInterval()', 0); -} + is(anim.getStartTime(), 3, "Unexpected start time after second interval"); -function checkEndOfSecondInterval() { - is(anim.getStartTime(), 3, "Unexpected start time while postactive"); SimpleTest.finish(); } diff --git a/content/smil/test/test_smilRestart.xhtml b/content/smil/test/test_smilRestart.xhtml index bd3c797560d..e72bc1504b4 100644 --- a/content/smil/test/test_smilRestart.xhtml +++ b/content/smil/test/test_smilRestart.xhtml @@ -37,67 +37,49 @@ var never = document.getElementById("never"); SimpleTest.waitForExplicitFinish(); -// main: just triggers the first link in the chain of function-calls +function tryRestart(elem, state, expected) { + var restartTime = svg.getCurrentTime(); + elem.beginElement(); + var restart = (elem.getStartTime() === restartTime); + if (expected) { + var msg = elem.id + " can't restart in " + state + " state"; + ok(restart, msg); + } else { + var msg = elem.id + " can restart in " + state + " state"; + ok(!restart, msg); + } +} + function main() { - checkInitialState(); -} - -function restartAll() { - always.beginElement(); - whenNotActive.beginElement(); - never.beginElement(); -} - -function checkInitialState() { // At first everything should be starting at 1s is(always.getStartTime(), 1); is(whenNotActive.getStartTime(), 1); is(never.getStartTime(), 1); // Now try to restart everything early, should be allowed by all - var restartTime = svg.getCurrentTime(); - restartAll(); - setTimeout('doCheckInitialState(' + restartTime + ')',0); -} -function doCheckInitialState(restartTime) { - is(always.getStartTime(), restartTime); - is(whenNotActive.getStartTime(), restartTime); - is(never.getStartTime(), restartTime); + tryRestart(always, "waiting", true); + tryRestart(whenNotActive, "waiting", true); + tryRestart(never, "waiting", true); // Now skip to half-way - var newTime = restartTime + 0.5 * always.getSimpleDuration(); + var newTime = always.getStartTime() + 0.5 * always.getSimpleDuration(); svg.setCurrentTime(newTime); - setTimeout('checkHalfwayState()', 0); -} -function checkHalfwayState() { - // Try to restart half-way through--only 'always' should succeed - var restartTime = svg.getCurrentTime(); - restartAll(); - setTimeout('doCheckHalfwayState(' + restartTime + ')',0); -} -function doCheckHalfwayState(restartTime) { - is(always.getStartTime(), restartTime); - isnot(whenNotActive.getStartTime(), restartTime); - isnot(never.getStartTime(), restartTime); + // Only 'always' should be able to be restarted + tryRestart(always, "active", true); + tryRestart(whenNotActive, "active", false); + tryRestart(never, "active", false); // Now skip to the end - var newTime = always.getStartTime() + always.getSimpleDuration() + 1; + newTime = always.getStartTime() + always.getSimpleDuration() + 1; svg.setCurrentTime(newTime); - setTimeout('checkEndingState()', 0); -} -function checkEndingState() { - // Try to restart after all animations have finished--'always' and - // 'whenNotActive' should succeed - var restartTime = svg.getCurrentTime(); - restartAll(); - setTimeout('doCheckEndingState(' + restartTime + ')',0); -} -function doCheckEndingState(restartTime) { - is(always.getStartTime(), restartTime); - is(whenNotActive.getStartTime(), restartTime); - isnot(never.getStartTime(), restartTime); + // All animations have finished, so 'always' and 'whenNotActive' should be + // able to be restarted + tryRestart(always, "postactive", true); + tryRestart(whenNotActive, "postactive", true); + tryRestart(never, "postactive", false); + SimpleTest.finish(); } diff --git a/content/smil/test/test_smilSync.xhtml b/content/smil/test/test_smilSync.xhtml new file mode 100644 index 00000000000..1d7302e7ecf --- /dev/null +++ b/content/smil/test/test_smilSync.xhtml @@ -0,0 +1,253 @@ + + + Test for SMIL sync behaviour + + + + + +

+ +
+
+
+ + diff --git a/content/smil/test/test_smilSyncTransform.xhtml b/content/smil/test/test_smilSyncTransform.xhtml new file mode 100644 index 00000000000..a33e1b21a05 --- /dev/null +++ b/content/smil/test/test_smilSyncTransform.xhtml @@ -0,0 +1,71 @@ + + + Test for SMIL sync behaviour for transform types + + + + + +

+ +
+
+
+ + diff --git a/content/svg/content/src/nsSVGAnimationElement.cpp b/content/svg/content/src/nsSVGAnimationElement.cpp index f092a58d403..055a56e1434 100644 --- a/content/svg/content/src/nsSVGAnimationElement.cpp +++ b/content/svg/content/src/nsSVGAnimationElement.cpp @@ -156,6 +156,8 @@ nsSVGAnimationElement::TimedElement() NS_IMETHODIMP nsSVGAnimationElement::GetTargetElement(nsIDOMSVGElement** aTarget) { + FlushAnimations(); + // We'll just call the other GetTargetElement method, and QI to the right type nsIContent* targetContent = GetTargetElementContent(); @@ -169,6 +171,8 @@ nsSVGAnimationElement::GetTargetElement(nsIDOMSVGElement** aTarget) NS_IMETHODIMP nsSVGAnimationElement::GetStartTime(float* retval) { + FlushAnimations(); + nsSMILTimeValue startTime = mTimedElement.GetStartTime(); if (startTime.IsResolved()) { *retval = double(startTime.GetMillis()) / PR_MSEC_PER_SEC; @@ -183,6 +187,8 @@ nsSVGAnimationElement::GetStartTime(float* retval) NS_IMETHODIMP nsSVGAnimationElement::GetCurrentTime(float* retval) { + // Not necessary to call FlushAnimations() for this + nsSMILTimeContainer* root = GetTimeContainer(); if (root) { *retval = double(root->GetCurrentTime()) / PR_MSEC_PER_SEC; @@ -196,6 +202,8 @@ nsSVGAnimationElement::GetCurrentTime(float* retval) NS_IMETHODIMP nsSVGAnimationElement::GetSimpleDuration(float* retval) { + // Not necessary to call FlushAnimations() for this + nsSMILTimeValue simpleDur = mTimedElement.GetSimpleDuration(); if (!simpleDur.IsResolved()) { *retval = 0.f; @@ -246,6 +254,8 @@ nsSVGAnimationElement::BindToTree(nsIDocument* aDocument, } } + AnimationNeedsResample(); + return NS_OK; } @@ -264,6 +274,8 @@ nsSVGAnimationElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent) mTimedDocumentRoot = nsnull; } + AnimationNeedsResample(); + nsSVGAnimationElementBase::UnbindFromTree(aDeep, aNullParent); } @@ -281,6 +293,7 @@ nsSVGAnimationElement::ParseAttribute(PRInt32 aNamespaceID, if (aAttribute == nsGkAtoms::attributeName || aAttribute == nsGkAtoms::attributeType) { aResult.ParseAtom(aValue); + AnimationNeedsResample(); return PR_TRUE; } @@ -297,6 +310,7 @@ nsSVGAnimationElement::ParseAttribute(PRInt32 aNamespaceID, } if (foundMatch) { + AnimationNeedsResample(); if (NS_FAILED(rv)) { ReportAttributeParseFailure(GetOwnerDoc(), aAttribute, aValue); return PR_FALSE; @@ -318,8 +332,9 @@ nsSVGAnimationElement::UnsetAttr(PRInt32 aNamespaceID, NS_ENSURE_SUCCESS(rv,rv); if (aNamespaceID == kNameSpaceID_None) { - if (!AnimationFunction().UnsetAttr(aAttribute)) { - mTimedElement.UnsetAttr(aAttribute); + if (AnimationFunction().UnsetAttr(aAttribute) || + mTimedElement.UnsetAttr(aAttribute)) { + AnimationNeedsResample(); } } @@ -380,13 +395,10 @@ nsSVGAnimationElement::BeginElement(void) NS_IMETHODIMP nsSVGAnimationElement::BeginElementAt(float offset) { - nsSVGSVGElement *ownerSVG = GetCtx(); - if (!ownerSVG) - return NS_ERROR_FAILURE; + nsresult rv = mTimedElement.BeginElementAt(offset, mTimedDocumentRoot); + AnimationNeedsResample(); - ownerSVG->RequestSample(); - - return mTimedElement.BeginElementAt(offset, mTimedDocumentRoot); + return rv; } /* void endElement (); */ @@ -400,11 +412,8 @@ nsSVGAnimationElement::EndElement(void) NS_IMETHODIMP nsSVGAnimationElement::EndElementAt(float offset) { - nsSVGSVGElement *ownerSVG = GetCtx(); - if (!ownerSVG) - return NS_ERROR_FAILURE; + nsresult rv = mTimedElement.EndElementAt(offset, mTimedDocumentRoot); + AnimationNeedsResample(); - ownerSVG->RequestSample(); - - return mTimedElement.EndElementAt(offset, mTimedDocumentRoot); + return rv; } diff --git a/content/svg/content/src/nsSVGElement.cpp b/content/svg/content/src/nsSVGElement.cpp index 867fb1b5055..9a663325e1c 100644 --- a/content/svg/content/src/nsSVGElement.cpp +++ b/content/svg/content/src/nsSVGElement.cpp @@ -1233,6 +1233,10 @@ nsSVGElement::DidAnimateLength(PRUint8 aAttrEnum) void nsSVGElement::GetAnimatedLengthValues(float *aFirst, ...) { +#ifdef MOZ_SMIL + FlushAnimations(); +#endif + LengthAttributesInfo info = GetLengthInfo(); NS_ASSERTION(info.mLengthCount > 0, @@ -1678,4 +1682,28 @@ nsSVGElement::GetAnimatedAttr(const nsIAtom* aName) return nsnull; } + +void +nsSVGElement::AnimationNeedsResample() +{ + nsIDocument* doc = GetCurrentDoc(); + if (doc) { + nsSMILAnimationController* smilController = doc->GetAnimationController(); + if (smilController) { + smilController->SetResampleNeeded(); + } + } +} + +void +nsSVGElement::FlushAnimations() +{ + nsIDocument* doc = GetCurrentDoc(); + if (doc) { + nsSMILAnimationController* smilController = doc->GetAnimationController(); + if (smilController) { + smilController->FlushResampleRequests(); + } + } +} #endif // MOZ_SMIL diff --git a/content/svg/content/src/nsSVGElement.h b/content/svg/content/src/nsSVGElement.h index 6a06c0a00fc..ae5eb5539c2 100644 --- a/content/svg/content/src/nsSVGElement.h +++ b/content/svg/content/src/nsSVGElement.h @@ -155,6 +155,8 @@ public: #ifdef MOZ_SMIL virtual nsISMILAttr* GetAnimatedAttr(const nsIAtom* aName); + void AnimationNeedsResample(); + void FlushAnimations(); #endif virtual void RecompileScriptEventListeners(); diff --git a/content/svg/content/src/nsSVGLength2.cpp b/content/svg/content/src/nsSVGLength2.cpp index 93bfc176ea9..5b5c339ed5d 100644 --- a/content/svg/content/src/nsSVGLength2.cpp +++ b/content/svg/content/src/nsSVGLength2.cpp @@ -191,8 +191,8 @@ nsSVGLength2::GetMMPerPixel(nsSVGSVGElement *aCtx) const return mmPerPx; } -float -nsSVGLength2::GetMMPerPixel(nsIFrame *aNonSVGFrame) const +/*static*/ float +nsSVGLength2::GetMMPerPixel(nsIFrame *aNonSVGFrame) { nsPresContext* presContext = aNonSVGFrame->PresContext(); float pixelsPerInch = @@ -239,9 +239,10 @@ nsSVGLength2::GetAxisLength(nsIFrame *aNonSVGFrame) const } float -nsSVGLength2::GetUnitScaleFactor(nsSVGElement *aSVGElement) const +nsSVGLength2::GetUnitScaleFactor(nsSVGElement *aSVGElement, + PRUint8 aUnitType) const { - switch (mSpecifiedUnitType) { + switch (aUnitType) { case nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER: case nsIDOMSVGLength::SVG_LENGTHTYPE_PX: return 1; @@ -251,13 +252,13 @@ nsSVGLength2::GetUnitScaleFactor(nsSVGElement *aSVGElement) const return 1 / GetExLength(aSVGElement); } - return GetUnitScaleFactor(aSVGElement->GetCtx()); + return GetUnitScaleFactor(aSVGElement->GetCtx(), aUnitType); } float -nsSVGLength2::GetUnitScaleFactor(nsSVGSVGElement *aCtx) const +nsSVGLength2::GetUnitScaleFactor(nsSVGSVGElement *aCtx, PRUint8 aUnitType) const { - switch (mSpecifiedUnitType) { + switch (aUnitType) { case nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER: case nsIDOMSVGLength::SVG_LENGTHTYPE_PX: return 1; @@ -284,13 +285,13 @@ nsSVGLength2::GetUnitScaleFactor(nsSVGSVGElement *aCtx) const } float -nsSVGLength2::GetUnitScaleFactor(nsIFrame *aFrame) const +nsSVGLength2::GetUnitScaleFactor(nsIFrame *aFrame, PRUint8 aUnitType) const { nsIContent* content = aFrame->GetContent(); if (content->IsNodeOfType(nsINode::eSVG)) - return GetUnitScaleFactor(static_cast(content)); + return GetUnitScaleFactor(static_cast(content), aUnitType); - switch (mSpecifiedUnitType) { + switch (aUnitType) { case nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER: case nsIDOMSVGLength::SVG_LENGTHTYPE_PX: return 1; @@ -322,6 +323,12 @@ nsSVGLength2::SetBaseValueInSpecifiedUnits(float aValue, { mBaseVal = aValue; aSVGElement->DidChangeLength(mAttrEnum, PR_TRUE); + +#ifdef MOZ_SMIL + if (mIsAnimated) { + aSVGElement->AnimationNeedsResample(); + } +#endif } void @@ -331,7 +338,8 @@ nsSVGLength2::ConvertToSpecifiedUnits(PRUint16 unitType, if (!IsValidUnitType(unitType)) return; - float valueInUserUnits = mBaseVal / GetUnitScaleFactor(aSVGElement); + float valueInUserUnits = + mBaseVal / GetUnitScaleFactor(aSVGElement, mSpecifiedUnitType); mSpecifiedUnitType = PRUint8(unitType); SetBaseValue(valueInUserUnits, aSVGElement); } @@ -347,6 +355,12 @@ nsSVGLength2::NewValueSpecifiedUnits(PRUint16 unitType, mBaseVal = mAnimVal = valueInSpecifiedUnits; mSpecifiedUnitType = PRUint8(unitType); aSVGElement->DidChangeLength(mAttrEnum, PR_TRUE); + +#ifdef MOZ_SMIL + if (mIsAnimated) { + aSVGElement->AnimationNeedsResample(); + } +#endif } nsresult @@ -390,6 +404,12 @@ nsSVGLength2::SetBaseValueString(const nsAString &aValueAsString, mSpecifiedUnitType = PRUint8(unitType); aSVGElement->DidChangeLength(mAttrEnum, aDoSetAttr); +#ifdef MOZ_SMIL + if (mIsAnimated) { + aSVGElement->AnimationNeedsResample(); + } +#endif + return NS_OK; } @@ -408,14 +428,21 @@ nsSVGLength2::GetAnimValueString(nsAString & aValueAsString) void nsSVGLength2::SetBaseValue(float aValue, nsSVGElement *aSVGElement) { - mAnimVal = mBaseVal = aValue * GetUnitScaleFactor(aSVGElement); + mAnimVal = mBaseVal = + aValue * GetUnitScaleFactor(aSVGElement, mSpecifiedUnitType); aSVGElement->DidChangeLength(mAttrEnum, PR_TRUE); +#ifdef MOZ_SMIL + if (mIsAnimated) { + aSVGElement->AnimationNeedsResample(); + } +#endif } void nsSVGLength2::SetAnimValue(float aValue, nsSVGElement *aSVGElement) { - mAnimVal = aValue * GetUnitScaleFactor(aSVGElement); + mAnimVal = aValue * GetUnitScaleFactor(aSVGElement, mSpecifiedUnitType); + mIsAnimated = PR_TRUE; aSVGElement->DidAnimateLength(mAttrEnum); } @@ -443,12 +470,18 @@ nsSVGLength2::SMILLength::ValueFromString(const nsAString& aStr, const nsISMILAnimationElement* /*aSrcElement*/, nsSMILValue& aValue) const { - nsSVGLength2 tmp; - tmp.SetBaseValueString(aStr, mSVGElement, PR_FALSE); + float value; + PRUint16 unitType; + + nsresult rv = GetValueFromString(aStr, &value, &unitType); + if (NS_FAILED(rv)) { + return rv; + } nsSMILValue val(&nsSMILFloatType::sSingleton); - val.mU.mDouble = tmp.GetBaseValue(mSVGElement); + val.mU.mDouble = value / mVal->GetUnitScaleFactor(mSVGElement, unitType); aValue = val; + return NS_OK; } @@ -466,7 +499,7 @@ nsSVGLength2::SMILLength::SetAnimValue(const nsSMILValue& aValue) NS_ASSERTION(aValue.mType == &nsSMILFloatType::sSingleton, "Unexpected type to assign animated value"); if (aValue.mType == &nsSMILFloatType::sSingleton) { - mVal->SetAnimValue((float)aValue.mU.mDouble, mSVGElement); + mVal->SetAnimValue(float(aValue.mU.mDouble), mSVGElement); } return NS_OK; } diff --git a/content/svg/content/src/nsSVGLength2.h b/content/svg/content/src/nsSVGLength2.h index 6842e98844d..84d077752a9 100644 --- a/content/svg/content/src/nsSVGLength2.h +++ b/content/svg/content/src/nsSVGLength2.h @@ -73,11 +73,16 @@ public: void GetAnimValueString(nsAString& aValue); float GetBaseValue(nsSVGElement* aSVGElement) const - { return mBaseVal / GetUnitScaleFactor(aSVGElement); } + { return mBaseVal / GetUnitScaleFactor(aSVGElement, mSpecifiedUnitType); } float GetAnimValue(nsSVGElement* aSVGElement) const - { return mAnimVal / GetUnitScaleFactor(aSVGElement); } + { + #ifdef MOZ_SMIL + aSVGElement->FlushAnimations(); + #endif + return mAnimVal / GetUnitScaleFactor(aSVGElement, mSpecifiedUnitType); + } float GetAnimValue(nsIFrame* aFrame) const - { return mAnimVal / GetUnitScaleFactor(aFrame); } + { return mAnimVal / GetUnitScaleFactor(aFrame, mSpecifiedUnitType); } PRUint8 GetCtxType() const { return mCtxType; } PRUint8 GetSpecifiedUnitType() const { return mSpecifiedUnitType; } @@ -87,9 +92,9 @@ public: float GetBaseValInSpecifiedUnits() const { return mBaseVal; } float GetBaseValue(nsSVGSVGElement* aCtx) const - { return mBaseVal / GetUnitScaleFactor(aCtx); } + { return mBaseVal / GetUnitScaleFactor(aCtx, mSpecifiedUnitType); } float GetAnimValue(nsSVGSVGElement* aCtx) const - { return mAnimVal / GetUnitScaleFactor(aCtx); } + { return mAnimVal / GetUnitScaleFactor(aCtx, mSpecifiedUnitType); } nsresult ToDOMAnimatedLength(nsIDOMSVGAnimatedLength **aResult, nsSVGElement* aSVGElement); @@ -107,22 +112,23 @@ private: PRUint8 mCtxType; // X, Y or Unspecified PRPackedBool mIsAnimated; - float GetMMPerPixel(nsIFrame *aNonSVGFrame) const; + static float GetMMPerPixel(nsIFrame *aNonSVGFrame); float GetAxisLength(nsIFrame *aNonSVGFrame) const; - float GetEmLength(nsIFrame *aFrame) const + static float GetEmLength(nsIFrame *aFrame) { return nsSVGUtils::GetFontSize(aFrame); } - float GetExLength(nsIFrame *aFrame) const + static float GetExLength(nsIFrame *aFrame) { return nsSVGUtils::GetFontXHeight(aFrame); } - float GetUnitScaleFactor(nsIFrame *aFrame) const; + float GetUnitScaleFactor(nsIFrame *aFrame, PRUint8 aUnitType) const; float GetMMPerPixel(nsSVGSVGElement *aCtx) const; float GetAxisLength(nsSVGSVGElement *aCtx) const; - float GetEmLength(nsSVGElement *aSVGElement) const + static float GetEmLength(nsSVGElement *aSVGElement) { return nsSVGUtils::GetFontSize(aSVGElement); } - float GetExLength(nsSVGElement *aSVGElement) const + static float GetExLength(nsSVGElement *aSVGElement) { return nsSVGUtils::GetFontXHeight(aSVGElement); } - float GetUnitScaleFactor(nsSVGElement *aSVGElement) const; - float GetUnitScaleFactor(nsSVGSVGElement *aCtx) const; + float GetUnitScaleFactor(nsSVGElement *aSVGElement, PRUint8 aUnitType) const; + float GetUnitScaleFactor(nsSVGSVGElement *aCtx, PRUint8 aUnitType) const; + void SetBaseValue(float aValue, nsSVGElement *aSVGElement); void SetBaseValueInSpecifiedUnits(float aValue, nsSVGElement *aSVGElement); void SetAnimValue(float aValue, nsSVGElement *aSVGElement); @@ -193,22 +199,46 @@ private: nsRefPtr mSVGElement; NS_IMETHOD GetUnitType(PRUint16* aResult) - { *aResult = mVal->mSpecifiedUnitType; return NS_OK; } + { +#ifdef MOZ_SMIL + mSVGElement->FlushAnimations(); +#endif + *aResult = mVal->mSpecifiedUnitType; + return NS_OK; + } NS_IMETHOD GetValue(float* aResult) - { *aResult = mVal->GetAnimValue(mSVGElement); return NS_OK; } + { +#ifdef MOZ_SMIL + mSVGElement->FlushAnimations(); +#endif + *aResult = mVal->GetAnimValue(mSVGElement); + return NS_OK; + } NS_IMETHOD SetValue(float aValue) { return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR; } NS_IMETHOD GetValueInSpecifiedUnits(float* aResult) - { *aResult = mVal->mAnimVal; return NS_OK; } + { +#ifdef MOZ_SMIL + mSVGElement->FlushAnimations(); +#endif + *aResult = mVal->mAnimVal; + return NS_OK; + } NS_IMETHOD SetValueInSpecifiedUnits(float aValue) { return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR; } NS_IMETHOD SetValueAsString(const nsAString& aValue) { return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR; } NS_IMETHOD GetValueAsString(nsAString& aValue) - { mVal->GetAnimValueString(aValue); return NS_OK; } + { +#ifdef MOZ_SMIL + mSVGElement->FlushAnimations(); +#endif + mVal->GetAnimValueString(aValue); + return NS_OK; + } NS_IMETHOD NewValueSpecifiedUnits(PRUint16 unitType, float valueInSpecifiedUnits) @@ -249,7 +279,6 @@ private: nsSVGLength2* mVal; nsSVGElement* mSVGElement; - // nsISMILAttr methods virtual nsresult ValueFromString(const nsAString& aStr, const nsISMILAnimationElement* aSrcElement, diff --git a/content/svg/content/src/nsSVGSVGElement.cpp b/content/svg/content/src/nsSVGSVGElement.cpp index 4ee200af8e3..becdc451178 100644 --- a/content/svg/content/src/nsSVGSVGElement.cpp +++ b/content/svg/content/src/nsSVGSVGElement.cpp @@ -555,7 +555,20 @@ nsSVGSVGElement::SetCurrentTime(float seconds) // errors nsSMILTime lMilliseconds = PRInt64(NS_round(fMilliseconds)); mTimedDocumentRoot->SetCurrentTime(lMilliseconds); - RequestSample(); + // Force a resample now + // + // It's not sufficient to just request a resample here because calls to + // BeginElement etc. expect to operate on an up-to-date timegraph or else + // instance times may be incorrectly discarded. + // + // See the mochitest: test_smilSync.xhtml:testSetCurrentTime() + nsIDocument* doc = GetCurrentDoc(); + if (doc) { + nsSMILAnimationController* smilController = doc->GetAnimationController(); + if (smilController) { + smilController->Resample(); + } + } } // else we're not the outermost or not bound to a tree, so silently // fail return NS_OK; @@ -1157,18 +1170,6 @@ nsSVGSVGElement::GetTimedDocumentRoot() return result; } - -void -nsSVGSVGElement::RequestSample() -{ - nsIDocument* doc = GetCurrentDoc(); - if (doc) { - nsSMILAnimationController* smilController = doc->GetAnimationController(); - if (smilController) { - smilController->FireForceSampleEvent(); - } - } -} #endif // MOZ_SMIL //---------------------------------------------------------------------- diff --git a/content/svg/content/src/nsSVGSVGElement.h b/content/svg/content/src/nsSVGSVGElement.h index 8d3c94cb4d4..89873b6a226 100644 --- a/content/svg/content/src/nsSVGSVGElement.h +++ b/content/svg/content/src/nsSVGSVGElement.h @@ -137,7 +137,6 @@ public: #ifdef MOZ_SMIL nsSMILTimeContainer* GetTimedDocumentRoot(); - void RequestSample(); #endif // MOZ_SMIL // nsIContent interface diff --git a/layout/reftests/svg/smil/pause/init-pause-1.svg b/layout/reftests/svg/smil/pause/init-pause-1.svg index 432b6998dfd..4576b166084 100644 --- a/layout/reftests/svg/smil/pause/init-pause-1.svg +++ b/layout/reftests/svg/smil/pause/init-pause-1.svg @@ -33,7 +33,7 @@ http://www.w3.org/2003/01/REC-SVG11-20030114-errata#getCurrentTime_setCurrentTim document.getElementById('wrong').style.visibility = 'visible'; } - setTimeout('document.documentElement.removeAttribute("class")', 0); + document.documentElement.removeAttribute("class"); } ]]> diff --git a/layout/reftests/svg/smil/smil-util.js b/layout/reftests/svg/smil/smil-util.js index 72357b72626..d3bb941cb54 100644 --- a/layout/reftests/svg/smil/smil-util.js +++ b/layout/reftests/svg/smil/smil-util.js @@ -6,6 +6,5 @@ function setTimeAndSnapshot(timeInSeconds, pauseFlag) { svg.pauseAnimations(); } svg.setCurrentTime(timeInSeconds); - // Use setTimeout to allow SMIL to update the animation before snapshot - setTimeout('document.documentElement.removeAttribute("class")', 0); + svg.removeAttribute("class"); } diff --git a/layout/reftests/svg/smil/transform/additive-1.svg b/layout/reftests/svg/smil/transform/additive-1.svg index 36281c6c81a..98b44bb1628 100644 --- a/layout/reftests/svg/smil/transform/additive-1.svg +++ b/layout/reftests/svg/smil/transform/additive-1.svg @@ -6,12 +6,6 @@ var svg = document.documentElement; svg.pauseAnimations(); svg.setCurrentTime(timeInSeconds); - // Use setTimeout to allow SMIL to update the animation before we check - // the values and take a snapshot - setTimeout('checkAnimVals()'); - } - function checkAnimVals() { - var svg = document.documentElement; var paths = svg.getElementsByTagName("path"); for (var i = 0; i < paths.length; i++) { var path = paths[i];