Index: Source/WebCore/svg/animation/SMILTimeContainer.cpp |
=================================================================== |
--- Source/WebCore/svg/animation/SMILTimeContainer.cpp (revision 132107) |
+++ Source/WebCore/svg/animation/SMILTimeContainer.cpp (working copy) |
@@ -36,7 +36,7 @@ |
using namespace std; |
namespace WebCore { |
- |
+ |
static const double animationFrameDelay = 0.025; |
SMILTimeContainer::SMILTimeContainer(SVGSVGElement* owner) |
@@ -47,38 +47,79 @@ |
, m_documentOrderIndexesDirty(false) |
, m_timer(this, &SMILTimeContainer::timerFired) |
, m_ownerSVGElement(owner) |
+#ifndef NDEBUG |
+ , m_preventScheduledAnimationsChanges(false) |
+#endif |
{ |
} |
-void SMILTimeContainer::schedule(SVGSMILElement* animation) |
+SMILTimeContainer::~SMILTimeContainer() |
{ |
+#ifndef NDEBUG |
+ ASSERT(!m_preventScheduledAnimationsChanges); |
+#endif |
+ deleteAllValues(m_scheduledAnimations); |
+} |
+ |
+void SMILTimeContainer::schedule(SVGSMILElement* animation, SVGElement* target, const QualifiedName& attributeName) |
+{ |
ASSERT(animation->timeContainer() == this); |
+ ASSERT(target); |
+ ASSERT(animation->hasValidAttributeName()); |
+ |
+#ifndef NDEBUG |
+ ASSERT(!m_preventScheduledAnimationsChanges); |
+#endif |
+ |
+ ElementAttributePair key(target, attributeName); |
+ AnimationsVector* scheduled = m_scheduledAnimations.get(key); |
+ if (!scheduled) { |
+ scheduled = new AnimationsVector(); |
+ m_scheduledAnimations.set(key, scheduled); |
+ } |
+ ASSERT(!scheduled->contains(animation)); |
+ scheduled->append(animation); |
+ |
SMILTime nextFireTime = animation->nextProgressTime(); |
- if (!nextFireTime.isFinite()) |
- return; |
- m_scheduledAnimations.add(animation); |
- startTimer(0); |
+ if (nextFireTime.isFinite()) |
+ notifyIntervalsChanged(); |
} |
- |
-void SMILTimeContainer::unschedule(SVGSMILElement* animation) |
+ |
+void SMILTimeContainer::unschedule(SVGSMILElement* animation, SVGElement* target, const QualifiedName& attributeName) |
{ |
ASSERT(animation->timeContainer() == this); |
- m_scheduledAnimations.remove(animation); |
+#ifndef NDEBUG |
+ ASSERT(!m_preventScheduledAnimationsChanges); |
+#endif |
+ |
+ ElementAttributePair key(target, attributeName); |
+ AnimationsVector* scheduled = m_scheduledAnimations.get(key); |
+ ASSERT(scheduled); |
+ size_t idx = scheduled->find(animation); |
+ ASSERT(idx != notFound); |
+ scheduled->remove(idx); |
} |
+void SMILTimeContainer::notifyIntervalsChanged() |
+{ |
+ // Schedule updateAnimations() to be called asynchronously so multiple intervals |
+ // can change with updateAnimations() only called once at the end. |
+ startTimer(0); |
+} |
+ |
SMILTime SMILTimeContainer::elapsed() const |
{ |
if (!m_beginTime) |
return 0; |
return currentTime() - m_beginTime - m_accumulatedPauseTime; |
} |
- |
+ |
bool SMILTimeContainer::isActive() const |
{ |
return m_beginTime && !isPaused(); |
} |
- |
+ |
bool SMILTimeContainer::isPaused() const |
{ |
return m_pauseTime; |
@@ -144,10 +185,19 @@ |
if (m_pauseTime) |
m_pauseTime = now; |
- Vector<SVGSMILElement*> toReset; |
- copyToVector(m_scheduledAnimations, toReset); |
- for (unsigned n = 0; n < toReset.size(); ++n) |
- toReset[n]->reset(); |
+#ifndef NDEBUG |
+ m_preventScheduledAnimationsChanges = true; |
+#endif |
+ GroupedAnimationsMap::iterator end = m_scheduledAnimations.end(); |
+ for (GroupedAnimationsMap::iterator it = m_scheduledAnimations.begin(); it != end; ++it) { |
+ AnimationsVector* scheduled = it->second; |
+ unsigned size = scheduled->size(); |
+ for (unsigned n = 0; n < size; n++) |
+ scheduled->at(n)->reset(); |
+ } |
+#ifndef NDEBUG |
+ m_preventScheduledAnimationsChanges = false; |
+#endif |
updateAnimations(time, true); |
} |
@@ -156,10 +206,10 @@ |
{ |
if (!m_beginTime || isPaused()) |
return; |
- |
+ |
if (!fireTime.isFinite()) |
return; |
- |
+ |
SMILTime delay = max(fireTime - elapsed(), minimumDelay); |
m_timer.startOneShot(delay.value()); |
} |
@@ -207,68 +257,71 @@ |
void SMILTimeContainer::updateAnimations(SMILTime elapsed, bool seekToTime) |
{ |
- SMILTime earliersFireTime = SMILTime::unresolved(); |
+ SMILTime earliestFireTime = SMILTime::unresolved(); |
- Vector<SVGSMILElement*> toAnimate; |
- copyToVector(m_scheduledAnimations, toAnimate); |
+#ifndef NDEBUG |
+ // This boolean will catch any attempts to schedule/unschedule scheduledAnimations during this critical section. |
+ // Similarly, any elements removed will unschedule themselves, so this will catch modification of animationsToApply. |
+ m_preventScheduledAnimationsChanges = true; |
+#endif |
- // Sort according to priority. Elements with later begin time have higher priority. |
- // In case of a tie, document order decides. |
- // FIXME: This should also consider timing relationships between the elements. Dependents |
- // have higher priority. |
- sortByPriority(toAnimate, elapsed); |
+ AnimationsVector animationsToApply; |
+ GroupedAnimationsMap::iterator end = m_scheduledAnimations.end(); |
+ for (GroupedAnimationsMap::iterator it = m_scheduledAnimations.begin(); it != end; ++it) { |
+ AnimationsVector* scheduled = it->second; |
- // Calculate animation contributions. |
- typedef pair<SVGElement*, QualifiedName> ElementAttributePair; |
- typedef HashMap<ElementAttributePair, SVGSMILElement*> ResultElementMap; |
- ResultElementMap resultsElements; |
- for (unsigned n = 0; n < toAnimate.size(); ++n) { |
- SVGSMILElement* animation = toAnimate[n]; |
- ASSERT(animation->timeContainer() == this); |
+ // Sort according to priority. Elements with later begin time have higher priority. |
+ // In case of a tie, document order decides. |
+ // FIXME: This should also consider timing relationships between the elements. Dependents |
+ // have higher priority. |
+ sortByPriority(*scheduled, elapsed); |
- SVGElement* targetElement = animation->targetElement(); |
- if (!targetElement) |
- continue; |
+ SVGSMILElement* resultElement = 0; |
+ unsigned size = scheduled->size(); |
+ for (unsigned n = 0; n < size; n++) { |
+ SVGSMILElement* animation = scheduled->at(n); |
+ ASSERT(animation->timeContainer() == this); |
+ ASSERT(animation->targetElement()); |
+ ASSERT(animation->hasValidAttributeName()); |
- QualifiedName attributeName = animation->attributeName(); |
- if (attributeName == anyQName()) { |
- if (animation->hasTagName(SVGNames::animateMotionTag)) |
- attributeName = SVGNames::animateMotionTag; |
- else |
- continue; |
- } |
+ // Results are accumulated to the first animation that animates and contributes to a particular element/attribute pair. |
+ if (!resultElement) { |
+ if (!animation->hasValidAttributeType()) |
+ continue; |
+ resultElement = animation; |
+ } |
- // Results are accumulated to the first animation that animates and contributes to a particular element/attribute pair. |
- ElementAttributePair key(targetElement, attributeName); |
- SVGSMILElement* resultElement = resultsElements.get(key); |
- if (!resultElement) { |
- if (!animation->hasValidAttributeType()) |
- continue; |
- resultElement = animation; |
- } else |
- ASSERT(resultElement != animation); |
+ // This will calculate the contribution from the animation and add it to the resultsElement. |
+ if (!animation->progress(elapsed, resultElement, seekToTime) && resultElement == animation) |
+ resultElement = 0; |
- // This will calculate the contribution from the animation and add it to the resultsElement. |
- if (animation->progress(elapsed, resultElement, seekToTime) && resultElement == animation) |
- resultsElements.add(key, resultElement); |
+ SMILTime nextFireTime = animation->nextProgressTime(); |
+ if (nextFireTime.isFinite()) |
+ earliestFireTime = min(nextFireTime, earliestFireTime); |
+ } |
- SMILTime nextFireTime = animation->nextProgressTime(); |
- if (nextFireTime.isFinite()) |
- earliersFireTime = min(nextFireTime, earliersFireTime); |
+ if (resultElement) |
+ animationsToApply.append(resultElement); |
} |
- unsigned resultsToApplySize = resultsElements.size(); |
- if (!resultsToApplySize) { |
- startTimer(earliersFireTime, animationFrameDelay); |
+ unsigned animationsToApplySize = animationsToApply.size(); |
+ if (!animationsToApplySize) { |
+#ifndef NDEBUG |
+ m_preventScheduledAnimationsChanges = false; |
+#endif |
+ startTimer(earliestFireTime, animationFrameDelay); |
return; |
} |
// Apply results to target elements. |
- ResultElementMap::iterator end = resultsElements.end(); |
- for (ResultElementMap::iterator it = resultsElements.begin(); it != end; ++it) |
- it->second->applyResultsToTarget(); |
+ for (unsigned i = 0; i < animationsToApplySize; ++i) |
+ animationsToApply[i]->applyResultsToTarget(); |
- startTimer(earliersFireTime, animationFrameDelay); |
+#ifndef NDEBUG |
+ m_preventScheduledAnimationsChanges = false; |
+#endif |
+ |
+ startTimer(earliestFireTime, animationFrameDelay); |
Document::updateStyleForAllDocuments(); |
} |