Index: Source/core/svg/animation/SMILTimeContainer.cpp |
diff --git a/Source/core/svg/animation/SMILTimeContainer.cpp b/Source/core/svg/animation/SMILTimeContainer.cpp |
index ee0886e8938f8a73bc0c31d6dd4c67dfac4344e0..6cd7a4ca82faa49833e3f21a3cb9a28939d5b598 100644 |
--- a/Source/core/svg/animation/SMILTimeContainer.cpp |
+++ b/Source/core/svg/animation/SMILTimeContainer.cpp |
@@ -37,6 +37,8 @@ using namespace std; |
namespace WebCore { |
+static const double initialFrameDelay = 0.025; |
+ |
// Every entry-point that calls updateAnimations() should instantiate a |
// DiscardScope to prevent deletion of the ownerElement (and hence itself.) |
class DiscardScope { |
@@ -116,13 +118,18 @@ bool SMILTimeContainer::hasAnimations() const |
return !m_scheduledAnimations.isEmpty(); |
} |
+bool SMILTimeContainer::hasPendingSynchronization() const |
+{ |
+ return m_frameSchedulingState == SynchronizeAnimations && m_wakeupTimer.isActive() && !m_wakeupTimer.nextFireInterval(); |
+} |
+ |
void SMILTimeContainer::notifyIntervalsChanged() |
{ |
if (!isStarted()) |
return; |
// Schedule updateAnimations() to be called asynchronously so multiple intervals |
// can change with updateAnimations() only called once at the end. |
- if (m_frameSchedulingState == SynchronizeAnimations && m_wakeupTimer.isActive() && !m_wakeupTimer.nextFireInterval()) |
+ if (hasPendingSynchronization()) |
return; |
cancelAnimationFrame(); |
scheduleWakeUp(0, SynchronizeAnimations); |
@@ -163,14 +170,18 @@ void SMILTimeContainer::begin() |
if (m_pauseTime) { |
m_pauseTime = now; |
- cancelAnimationFrame(); |
- } else { |
+ // If updateAnimations() caused new syncbase instance to be generated, |
+ // we don't want to cancel those. Excepting that, no frame should've |
+ // been scheduled at this point. |
+ ASSERT(m_frameSchedulingState == Idle || m_frameSchedulingState == SynchronizeAnimations); |
+ } else if (!hasPendingSynchronization()) { |
ASSERT(isTimelineRunning()); |
// If the timeline is running, and there's pending animation updates, |
// always perform the first update after the timeline was started using |
// the wake-up mechanism. |
if (earliestFireTime.isFinite()) { |
- scheduleWakeUp(DocumentTimeline::s_minimumDelay, SynchronizeAnimations); |
+ SMILTime delay = earliestFireTime - elapsed(); |
+ scheduleWakeUp(std::max(initialFrameDelay, delay.value()), SynchronizeAnimations); |
} |
} |
} |
@@ -193,7 +204,7 @@ void SMILTimeContainer::resume() |
m_resumeTime = currentTime(); |
m_pauseTime = 0; |
- serviceOnNextFrame(); |
+ scheduleWakeUp(0, SynchronizeAnimations); |
} |
void SMILTimeContainer::setElapsed(SMILTime time) |
@@ -242,12 +253,13 @@ bool SMILTimeContainer::isTimelineRunning() const |
void SMILTimeContainer::scheduleAnimationFrame(SMILTime fireTime) |
{ |
ASSERT(isTimelineRunning() && fireTime.isFinite()); |
+ ASSERT(!m_wakeupTimer.isActive()); |
SMILTime delay = fireTime - elapsed(); |
if (delay.value() < DocumentTimeline::s_minimumDelay) { |
serviceOnNextFrame(); |
} else { |
- scheduleWakeUp(delay.value() - DocumentTimeline::s_minimumDelay, AnimationFrame); |
+ scheduleWakeUp(delay.value() - DocumentTimeline::s_minimumDelay, FutureAnimationFrame); |
} |
} |
@@ -259,19 +271,19 @@ void SMILTimeContainer::cancelAnimationFrame() |
void SMILTimeContainer::scheduleWakeUp(double delayTime, FrameSchedulingState frameSchedulingState) |
{ |
- ASSERT(frameSchedulingState != Idle); |
+ ASSERT(frameSchedulingState == SynchronizeAnimations || frameSchedulingState == FutureAnimationFrame); |
m_wakeupTimer.startOneShot(delayTime, FROM_HERE); |
m_frameSchedulingState = frameSchedulingState; |
} |
void SMILTimeContainer::wakeupTimerFired(Timer<SMILTimeContainer>*) |
{ |
- if (m_frameSchedulingState == AnimationFrame) { |
+ ASSERT(m_frameSchedulingState == SynchronizeAnimations || m_frameSchedulingState == FutureAnimationFrame); |
+ if (m_frameSchedulingState == FutureAnimationFrame) { |
ASSERT(isTimelineRunning()); |
m_frameSchedulingState = Idle; |
serviceOnNextFrame(); |
} else { |
- ASSERT(m_frameSchedulingState == SynchronizeAnimations); |
m_frameSchedulingState = Idle; |
DiscardScope discardScope(m_ownerSVGElement); |
updateAnimationsAndScheduleFrameIfNeeded(elapsed()); |
@@ -342,6 +354,11 @@ void SMILTimeContainer::serviceAnimations(double monotonicAnimationStartTime) |
void SMILTimeContainer::updateAnimationsAndScheduleFrameIfNeeded(SMILTime elapsed, bool seekToTime) |
{ |
SMILTime earliestFireTime = updateAnimations(elapsed, seekToTime); |
+ // If updateAnimations() ended up triggering a synchronization (most likely |
+ // via syncbases), then give that priority. |
+ if (hasPendingSynchronization()) |
+ return; |
+ |
if (!isTimelineRunning()) |
return; |