OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2008 Apple Inc. All rights reserved. | 2 * Copyright (C) 2008 Apple Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
6 * are met: | 6 * are met: |
7 * 1. Redistributions of source code must retain the above copyright | 7 * 1. Redistributions of source code must retain the above copyright |
8 * notice, this list of conditions and the following disclaimer. | 8 * notice, this list of conditions and the following disclaimer. |
9 * 2. Redistributions in binary form must reproduce the above copyright | 9 * 2. Redistributions in binary form must reproduce the above copyright |
10 * notice, this list of conditions and the following disclaimer in the | 10 * notice, this list of conditions and the following disclaimer in the |
(...skipping 19 matching lines...) Expand all Loading... |
30 #include "core/animation/DocumentTimeline.h" | 30 #include "core/animation/DocumentTimeline.h" |
31 #include "core/dom/ElementTraversal.h" | 31 #include "core/dom/ElementTraversal.h" |
32 #include "core/frame/FrameView.h" | 32 #include "core/frame/FrameView.h" |
33 #include "core/svg/SVGSVGElement.h" | 33 #include "core/svg/SVGSVGElement.h" |
34 #include "core/svg/animation/SVGSMILElement.h" | 34 #include "core/svg/animation/SVGSMILElement.h" |
35 | 35 |
36 using namespace std; | 36 using namespace std; |
37 | 37 |
38 namespace WebCore { | 38 namespace WebCore { |
39 | 39 |
| 40 static const double initialFrameDelay = 0.025; |
| 41 |
40 // Every entry-point that calls updateAnimations() should instantiate a | 42 // Every entry-point that calls updateAnimations() should instantiate a |
41 // DiscardScope to prevent deletion of the ownerElement (and hence itself.) | 43 // DiscardScope to prevent deletion of the ownerElement (and hence itself.) |
42 class DiscardScope { | 44 class DiscardScope { |
43 public: | 45 public: |
44 explicit DiscardScope(SVGSVGElement& timeContainerOwner) : m_discardScopeEle
ment(&timeContainerOwner) { } | 46 explicit DiscardScope(SVGSVGElement& timeContainerOwner) : m_discardScopeEle
ment(&timeContainerOwner) { } |
45 | 47 |
46 private: | 48 private: |
47 RefPtr<SVGSVGElement> m_discardScopeElement; | 49 RefPtr<SVGSVGElement> m_discardScopeElement; |
48 }; | 50 }; |
49 | 51 |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
109 size_t idx = scheduled->find(animation); | 111 size_t idx = scheduled->find(animation); |
110 ASSERT(idx != kNotFound); | 112 ASSERT(idx != kNotFound); |
111 scheduled->remove(idx); | 113 scheduled->remove(idx); |
112 } | 114 } |
113 | 115 |
114 bool SMILTimeContainer::hasAnimations() const | 116 bool SMILTimeContainer::hasAnimations() const |
115 { | 117 { |
116 return !m_scheduledAnimations.isEmpty(); | 118 return !m_scheduledAnimations.isEmpty(); |
117 } | 119 } |
118 | 120 |
| 121 bool SMILTimeContainer::hasPendingSynchronization() const |
| 122 { |
| 123 return m_frameSchedulingState == SynchronizeAnimations && m_wakeupTimer.isAc
tive() && !m_wakeupTimer.nextFireInterval(); |
| 124 } |
| 125 |
119 void SMILTimeContainer::notifyIntervalsChanged() | 126 void SMILTimeContainer::notifyIntervalsChanged() |
120 { | 127 { |
121 if (!isStarted()) | 128 if (!isStarted()) |
122 return; | 129 return; |
123 // Schedule updateAnimations() to be called asynchronously so multiple inter
vals | 130 // Schedule updateAnimations() to be called asynchronously so multiple inter
vals |
124 // can change with updateAnimations() only called once at the end. | 131 // can change with updateAnimations() only called once at the end. |
125 if (m_frameSchedulingState == SynchronizeAnimations && m_wakeupTimer.isActiv
e() && !m_wakeupTimer.nextFireInterval()) | 132 if (hasPendingSynchronization()) |
126 return; | 133 return; |
127 cancelAnimationFrame(); | 134 cancelAnimationFrame(); |
128 scheduleWakeUp(0, SynchronizeAnimations); | 135 scheduleWakeUp(0, SynchronizeAnimations); |
129 } | 136 } |
130 | 137 |
131 SMILTime SMILTimeContainer::elapsed() const | 138 SMILTime SMILTimeContainer::elapsed() const |
132 { | 139 { |
133 if (!m_beginTime) | 140 if (!m_beginTime) |
134 return 0; | 141 return 0; |
135 | 142 |
(...skipping 20 matching lines...) Expand all Loading... |
156 | 163 |
157 // If 'm_presetStartTime' is set, the timeline was modified via setElapsed()
before the document began. | 164 // If 'm_presetStartTime' is set, the timeline was modified via setElapsed()
before the document began. |
158 // In this case pass on 'seekToTime=true' to updateAnimations(). | 165 // In this case pass on 'seekToTime=true' to updateAnimations(). |
159 m_beginTime = now - m_presetStartTime; | 166 m_beginTime = now - m_presetStartTime; |
160 DiscardScope discardScope(m_ownerSVGElement); | 167 DiscardScope discardScope(m_ownerSVGElement); |
161 SMILTime earliestFireTime = updateAnimations(SMILTime(m_presetStartTime), m_
presetStartTime ? true : false); | 168 SMILTime earliestFireTime = updateAnimations(SMILTime(m_presetStartTime), m_
presetStartTime ? true : false); |
162 m_presetStartTime = 0; | 169 m_presetStartTime = 0; |
163 | 170 |
164 if (m_pauseTime) { | 171 if (m_pauseTime) { |
165 m_pauseTime = now; | 172 m_pauseTime = now; |
166 cancelAnimationFrame(); | 173 // If updateAnimations() caused new syncbase instance to be generated, |
167 } else { | 174 // we don't want to cancel those. Excepting that, no frame should've |
| 175 // been scheduled at this point. |
| 176 ASSERT(m_frameSchedulingState == Idle || m_frameSchedulingState == Synch
ronizeAnimations); |
| 177 } else if (!hasPendingSynchronization()) { |
168 ASSERT(isTimelineRunning()); | 178 ASSERT(isTimelineRunning()); |
169 // If the timeline is running, and there's pending animation updates, | 179 // If the timeline is running, and there's pending animation updates, |
170 // always perform the first update after the timeline was started using | 180 // always perform the first update after the timeline was started using |
171 // the wake-up mechanism. | 181 // the wake-up mechanism. |
172 if (earliestFireTime.isFinite()) { | 182 if (earliestFireTime.isFinite()) { |
173 scheduleWakeUp(DocumentTimeline::s_minimumDelay, SynchronizeAnimatio
ns); | 183 SMILTime delay = earliestFireTime - elapsed(); |
| 184 scheduleWakeUp(std::max(initialFrameDelay, delay.value()), Synchroni
zeAnimations); |
174 } | 185 } |
175 } | 186 } |
176 } | 187 } |
177 | 188 |
178 void SMILTimeContainer::pause() | 189 void SMILTimeContainer::pause() |
179 { | 190 { |
180 ASSERT(!isPaused()); | 191 ASSERT(!isPaused()); |
181 m_pauseTime = currentTime(); | 192 m_pauseTime = currentTime(); |
182 | 193 |
183 if (m_beginTime) { | 194 if (m_beginTime) { |
184 m_accumulatedActiveTime += m_pauseTime - lastResumeTime(); | 195 m_accumulatedActiveTime += m_pauseTime - lastResumeTime(); |
185 cancelAnimationFrame(); | 196 cancelAnimationFrame(); |
186 } | 197 } |
187 m_resumeTime = 0; | 198 m_resumeTime = 0; |
188 } | 199 } |
189 | 200 |
190 void SMILTimeContainer::resume() | 201 void SMILTimeContainer::resume() |
191 { | 202 { |
192 ASSERT(isPaused()); | 203 ASSERT(isPaused()); |
193 m_resumeTime = currentTime(); | 204 m_resumeTime = currentTime(); |
194 | 205 |
195 m_pauseTime = 0; | 206 m_pauseTime = 0; |
196 serviceOnNextFrame(); | 207 scheduleWakeUp(0, SynchronizeAnimations); |
197 } | 208 } |
198 | 209 |
199 void SMILTimeContainer::setElapsed(SMILTime time) | 210 void SMILTimeContainer::setElapsed(SMILTime time) |
200 { | 211 { |
201 // If the documment didn't begin yet, record a new start time, we'll seek to
once its possible. | 212 // If the documment didn't begin yet, record a new start time, we'll seek to
once its possible. |
202 if (!m_beginTime) { | 213 if (!m_beginTime) { |
203 m_presetStartTime = time.value(); | 214 m_presetStartTime = time.value(); |
204 return; | 215 return; |
205 } | 216 } |
206 | 217 |
(...skipping 28 matching lines...) Expand all Loading... |
235 } | 246 } |
236 | 247 |
237 bool SMILTimeContainer::isTimelineRunning() const | 248 bool SMILTimeContainer::isTimelineRunning() const |
238 { | 249 { |
239 return m_beginTime && !isPaused(); | 250 return m_beginTime && !isPaused(); |
240 } | 251 } |
241 | 252 |
242 void SMILTimeContainer::scheduleAnimationFrame(SMILTime fireTime) | 253 void SMILTimeContainer::scheduleAnimationFrame(SMILTime fireTime) |
243 { | 254 { |
244 ASSERT(isTimelineRunning() && fireTime.isFinite()); | 255 ASSERT(isTimelineRunning() && fireTime.isFinite()); |
| 256 ASSERT(!m_wakeupTimer.isActive()); |
245 | 257 |
246 SMILTime delay = fireTime - elapsed(); | 258 SMILTime delay = fireTime - elapsed(); |
247 if (delay.value() < DocumentTimeline::s_minimumDelay) { | 259 if (delay.value() < DocumentTimeline::s_minimumDelay) { |
248 serviceOnNextFrame(); | 260 serviceOnNextFrame(); |
249 } else { | 261 } else { |
250 scheduleWakeUp(delay.value() - DocumentTimeline::s_minimumDelay, Animati
onFrame); | 262 scheduleWakeUp(delay.value() - DocumentTimeline::s_minimumDelay, FutureA
nimationFrame); |
251 } | 263 } |
252 } | 264 } |
253 | 265 |
254 void SMILTimeContainer::cancelAnimationFrame() | 266 void SMILTimeContainer::cancelAnimationFrame() |
255 { | 267 { |
256 m_frameSchedulingState = Idle; | 268 m_frameSchedulingState = Idle; |
257 m_wakeupTimer.stop(); | 269 m_wakeupTimer.stop(); |
258 } | 270 } |
259 | 271 |
260 void SMILTimeContainer::scheduleWakeUp(double delayTime, FrameSchedulingState fr
ameSchedulingState) | 272 void SMILTimeContainer::scheduleWakeUp(double delayTime, FrameSchedulingState fr
ameSchedulingState) |
261 { | 273 { |
262 ASSERT(frameSchedulingState != Idle); | 274 ASSERT(frameSchedulingState == SynchronizeAnimations || frameSchedulingState
== FutureAnimationFrame); |
263 m_wakeupTimer.startOneShot(delayTime, FROM_HERE); | 275 m_wakeupTimer.startOneShot(delayTime, FROM_HERE); |
264 m_frameSchedulingState = frameSchedulingState; | 276 m_frameSchedulingState = frameSchedulingState; |
265 } | 277 } |
266 | 278 |
267 void SMILTimeContainer::wakeupTimerFired(Timer<SMILTimeContainer>*) | 279 void SMILTimeContainer::wakeupTimerFired(Timer<SMILTimeContainer>*) |
268 { | 280 { |
269 if (m_frameSchedulingState == AnimationFrame) { | 281 ASSERT(m_frameSchedulingState == SynchronizeAnimations || m_frameSchedulingS
tate == FutureAnimationFrame); |
| 282 if (m_frameSchedulingState == FutureAnimationFrame) { |
270 ASSERT(isTimelineRunning()); | 283 ASSERT(isTimelineRunning()); |
271 m_frameSchedulingState = Idle; | 284 m_frameSchedulingState = Idle; |
272 serviceOnNextFrame(); | 285 serviceOnNextFrame(); |
273 } else { | 286 } else { |
274 ASSERT(m_frameSchedulingState == SynchronizeAnimations); | |
275 m_frameSchedulingState = Idle; | 287 m_frameSchedulingState = Idle; |
276 DiscardScope discardScope(m_ownerSVGElement); | 288 DiscardScope discardScope(m_ownerSVGElement); |
277 updateAnimationsAndScheduleFrameIfNeeded(elapsed()); | 289 updateAnimationsAndScheduleFrameIfNeeded(elapsed()); |
278 } | 290 } |
279 } | 291 } |
280 | 292 |
281 void SMILTimeContainer::updateDocumentOrderIndexes() | 293 void SMILTimeContainer::updateDocumentOrderIndexes() |
282 { | 294 { |
283 unsigned timingElementCount = 0; | 295 unsigned timingElementCount = 0; |
284 for (SVGSMILElement* element = Traversal<SVGSMILElement>::firstWithin(m_owne
rSVGElement); element; element = Traversal<SVGSMILElement>::next(*element, &m_ow
nerSVGElement)) | 296 for (SVGSMILElement* element = Traversal<SVGSMILElement>::firstWithin(m_owne
rSVGElement); element; element = Traversal<SVGSMILElement>::next(*element, &m_ow
nerSVGElement)) |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
335 m_frameSchedulingState = Idle; | 347 m_frameSchedulingState = Idle; |
336 animationClock().updateTime(monotonicAnimationStartTime); | 348 animationClock().updateTime(monotonicAnimationStartTime); |
337 DiscardScope discardScope(m_ownerSVGElement); | 349 DiscardScope discardScope(m_ownerSVGElement); |
338 updateAnimationsAndScheduleFrameIfNeeded(elapsed()); | 350 updateAnimationsAndScheduleFrameIfNeeded(elapsed()); |
339 animationClock().unfreeze(); | 351 animationClock().unfreeze(); |
340 } | 352 } |
341 | 353 |
342 void SMILTimeContainer::updateAnimationsAndScheduleFrameIfNeeded(SMILTime elapse
d, bool seekToTime) | 354 void SMILTimeContainer::updateAnimationsAndScheduleFrameIfNeeded(SMILTime elapse
d, bool seekToTime) |
343 { | 355 { |
344 SMILTime earliestFireTime = updateAnimations(elapsed, seekToTime); | 356 SMILTime earliestFireTime = updateAnimations(elapsed, seekToTime); |
| 357 // If updateAnimations() ended up triggering a synchronization (most likely |
| 358 // via syncbases), then give that priority. |
| 359 if (hasPendingSynchronization()) |
| 360 return; |
| 361 |
345 if (!isTimelineRunning()) | 362 if (!isTimelineRunning()) |
346 return; | 363 return; |
347 | 364 |
348 if (!earliestFireTime.isFinite()) | 365 if (!earliestFireTime.isFinite()) |
349 return; | 366 return; |
350 | 367 |
351 scheduleAnimationFrame(earliestFireTime); | 368 scheduleAnimationFrame(earliestFireTime); |
352 } | 369 } |
353 | 370 |
354 SMILTime SMILTimeContainer::updateAnimations(SMILTime elapsed, bool seekToTime) | 371 SMILTime SMILTimeContainer::updateAnimations(SMILTime elapsed, bool seekToTime) |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
434 if (animDiscard->inDocument()) { | 451 if (animDiscard->inDocument()) { |
435 animDiscard->remove(IGNORE_EXCEPTION); | 452 animDiscard->remove(IGNORE_EXCEPTION); |
436 ASSERT(!animDiscard->inDocument()); | 453 ASSERT(!animDiscard->inDocument()); |
437 } | 454 } |
438 } | 455 } |
439 } | 456 } |
440 return earliestFireTime; | 457 return earliestFireTime; |
441 } | 458 } |
442 | 459 |
443 } | 460 } |
OLD | NEW |