Index: Source/core/svg/animation/SMILTimeContainer.cpp |
diff --git a/Source/core/svg/animation/SMILTimeContainer.cpp b/Source/core/svg/animation/SMILTimeContainer.cpp |
index c41555d4d18203e592dc1c81d5ce2f275f8c3608..02a6444e36d13bfda217f4f73f0d7ac8341dbc6f 100644 |
--- a/Source/core/svg/animation/SMILTimeContainer.cpp |
+++ b/Source/core/svg/animation/SMILTimeContainer.cpp |
@@ -26,25 +26,27 @@ |
#include "config.h" |
#include "core/svg/animation/SMILTimeContainer.h" |
+#include "core/animation/AnimationClock.h" |
+#include "core/animation/DocumentTimeline.h" |
#include "core/dom/ElementTraversal.h" |
+#include "core/frame/FrameView.h" |
#include "core/svg/SVGSVGElement.h" |
#include "core/svg/animation/SVGSMILElement.h" |
-#include "wtf/CurrentTime.h" |
using namespace std; |
namespace WebCore { |
-static const double animationFrameDelay = 0.025; |
- |
SMILTimeContainer::SMILTimeContainer(SVGSVGElement* owner) |
: m_beginTime(0) |
, m_pauseTime(0) |
, m_resumeTime(0) |
, m_accumulatedActiveTime(0) |
, m_presetStartTime(0) |
+ , m_frameSchedulingState(Idle) |
, m_documentOrderIndexesDirty(false) |
- , m_timer(this, &SMILTimeContainer::timerFired) |
+ , m_animationClock(AnimationClock::create()) |
+ , m_wakeupTimer(this, &SMILTimeContainer::wakeupTimerFired) |
, m_ownerSVGElement(owner) |
#ifndef NDEBUG |
, m_preventScheduledAnimationsChanges(false) |
@@ -55,7 +57,7 @@ SMILTimeContainer::SMILTimeContainer(SVGSVGElement* owner) |
SMILTimeContainer::~SMILTimeContainer() |
{ |
cancelAnimationFrame(); |
- ASSERT(!m_timer.isActive()); |
+ ASSERT(!m_wakeupTimer.isActive()); |
#ifndef NDEBUG |
ASSERT(!m_preventScheduledAnimationsChanges); |
#endif |
@@ -99,11 +101,21 @@ void SMILTimeContainer::unschedule(SVGSMILElement* animation, SVGElement* target |
scheduled->remove(idx); |
} |
+bool SMILTimeContainer::hasAnimations() const |
+{ |
+ return !m_scheduledAnimations.isEmpty(); |
+} |
+ |
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. |
- scheduleAnimationFrame(); |
+ if (m_frameSchedulingState == SynchronizeAnimations && m_wakeupTimer.isActive() && !m_wakeupTimer.nextFireInterval()) |
+ return; |
+ cancelAnimationFrame(); |
+ scheduleWakeUp(0, SynchronizeAnimations); |
} |
SMILTime SMILTimeContainer::elapsed() const |
@@ -135,12 +147,20 @@ void SMILTimeContainer::begin() |
// If 'm_presetStartTime' is set, the timeline was modified via setElapsed() before the document began. |
// In this case pass on 'seekToTime=true' to updateAnimations(). |
m_beginTime = now - m_presetStartTime; |
- updateAnimations(SMILTime(m_presetStartTime), m_presetStartTime ? true : false); |
+ SMILTime earliestFireTime = updateAnimations(SMILTime(m_presetStartTime), m_presetStartTime ? true : false); |
m_presetStartTime = 0; |
if (m_pauseTime) { |
m_pauseTime = now; |
cancelAnimationFrame(); |
+ } else { |
+ 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); |
+ } |
} |
} |
@@ -162,7 +182,7 @@ void SMILTimeContainer::resume() |
m_resumeTime = currentTime(); |
m_pauseTime = 0; |
- scheduleAnimationFrame(); |
+ serviceOnNextFrame(); |
} |
void SMILTimeContainer::setElapsed(SMILTime time) |
@@ -173,8 +193,7 @@ void SMILTimeContainer::setElapsed(SMILTime time) |
return; |
} |
- if (m_beginTime) |
- cancelAnimationFrame(); |
+ cancelAnimationFrame(); |
double now = currentTime(); |
m_beginTime = now - time.value(); |
@@ -200,7 +219,7 @@ void SMILTimeContainer::setElapsed(SMILTime time) |
m_preventScheduledAnimationsChanges = false; |
#endif |
- updateAnimations(time, true); |
+ updateAnimationsAndScheduleFrameIfNeeded(time, true); |
} |
bool SMILTimeContainer::isTimelineRunning() const |
@@ -210,33 +229,40 @@ bool SMILTimeContainer::isTimelineRunning() const |
void SMILTimeContainer::scheduleAnimationFrame(SMILTime fireTime) |
{ |
- if (!isTimelineRunning()) |
- return; |
+ ASSERT(isTimelineRunning() && fireTime.isFinite()); |
- if (!fireTime.isFinite()) |
- return; |
- |
- SMILTime delay = max(fireTime - elapsed(), SMILTime(animationFrameDelay)); |
- m_timer.startOneShot(delay.value()); |
+ SMILTime delay = fireTime - elapsed(); |
+ if (delay.value() < DocumentTimeline::s_minimumDelay) { |
+ serviceOnNextFrame(); |
+ } else { |
+ scheduleWakeUp(delay.value() - DocumentTimeline::s_minimumDelay, AnimationFrame); |
+ } |
} |
-void SMILTimeContainer::scheduleAnimationFrame() |
+void SMILTimeContainer::cancelAnimationFrame() |
{ |
- if (!isTimelineRunning()) |
- return; |
- |
- m_timer.startOneShot(0); |
+ m_frameSchedulingState = Idle; |
+ m_wakeupTimer.stop(); |
} |
-void SMILTimeContainer::cancelAnimationFrame() |
+void SMILTimeContainer::scheduleWakeUp(double delayTime, FrameSchedulingState frameSchedulingState) |
{ |
- m_timer.stop(); |
+ ASSERT(frameSchedulingState != Idle); |
+ m_wakeupTimer.startOneShot(delayTime); |
+ m_frameSchedulingState = frameSchedulingState; |
} |
-void SMILTimeContainer::timerFired(Timer<SMILTimeContainer>*) |
+void SMILTimeContainer::wakeupTimerFired(Timer<SMILTimeContainer>*) |
{ |
- ASSERT(isTimelineRunning()); |
- updateAnimations(elapsed()); |
+ if (m_frameSchedulingState == AnimationFrame) { |
+ ASSERT(isTimelineRunning()); |
+ m_frameSchedulingState = Idle; |
+ serviceOnNextFrame(); |
+ } else { |
+ ASSERT(m_frameSchedulingState == SynchronizeAnimations); |
+ m_frameSchedulingState = Idle; |
+ updateAnimationsAndScheduleFrameIfNeeded(elapsed()); |
+ } |
} |
void SMILTimeContainer::updateDocumentOrderIndexes() |
@@ -266,7 +292,55 @@ struct PriorityCompare { |
SMILTime m_elapsed; |
}; |
-void SMILTimeContainer::updateAnimations(SMILTime elapsed, bool seekToTime) |
+Document& SMILTimeContainer::document() const |
+{ |
+ ASSERT(m_ownerSVGElement); |
+ return m_ownerSVGElement->document(); |
+} |
+ |
+AnimationClock& SMILTimeContainer::animationClock() const |
+{ |
+ ASSERT(m_animationClock); |
+ return *m_animationClock; |
+} |
+ |
+double SMILTimeContainer::currentTime() const |
+{ |
+ return animationClock().currentTime(); |
+} |
+ |
+void SMILTimeContainer::serviceOnNextFrame() |
+{ |
+ if (document().view()) { |
+ document().view()->scheduleAnimation(); |
+ m_frameSchedulingState = AnimationFrame; |
+ } |
+} |
+ |
+void SMILTimeContainer::serviceAnimations(double monotonicAnimationStartTime) |
+{ |
+ if (m_frameSchedulingState != AnimationFrame) |
+ return; |
+ |
+ m_frameSchedulingState = Idle; |
+ animationClock().updateTime(monotonicAnimationStartTime); |
+ updateAnimationsAndScheduleFrameIfNeeded(elapsed()); |
+ animationClock().unfreeze(); |
+} |
+ |
+void SMILTimeContainer::updateAnimationsAndScheduleFrameIfNeeded(SMILTime elapsed, bool seekToTime) |
+{ |
+ SMILTime earliestFireTime = updateAnimations(elapsed, seekToTime); |
+ if (!isTimelineRunning()) |
+ return; |
+ |
+ if (!earliestFireTime.isFinite()) |
+ return; |
+ |
+ scheduleAnimationFrame(earliestFireTime); |
+} |
+ |
+SMILTime SMILTimeContainer::updateAnimations(SMILTime elapsed, bool seekToTime) |
{ |
SMILTime earliestFireTime = SMILTime::unresolved(); |
@@ -326,8 +400,7 @@ void SMILTimeContainer::updateAnimations(SMILTime elapsed, bool seekToTime) |
#ifndef NDEBUG |
m_preventScheduledAnimationsChanges = false; |
#endif |
- scheduleAnimationFrame(earliestFireTime); |
- return; |
+ return earliestFireTime; |
} |
// Apply results to target elements. |
@@ -338,8 +411,6 @@ void SMILTimeContainer::updateAnimations(SMILTime elapsed, bool seekToTime) |
m_preventScheduledAnimationsChanges = false; |
#endif |
- scheduleAnimationFrame(earliestFireTime); |
- |
for (unsigned i = 0; i < animationsToApplySize; ++i) { |
if (animationsToApply[i]->inDocument() && animationsToApply[i]->isSVGDiscardElement()) { |
RefPtr<SVGSMILElement> animDiscard = animationsToApply[i]; |
@@ -355,6 +426,7 @@ void SMILTimeContainer::updateAnimations(SMILTime elapsed, bool seekToTime) |
} |
} |
} |
+ return earliestFireTime; |
} |
} |