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 |
11 * documentation and/or other materials provided with the distribution. | 11 * documentation and/or other materials provided with the distribution. |
12 * | 12 * |
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
24 */ | 24 */ |
25 | 25 |
26 #include "config.h" | 26 #include "config.h" |
27 #include "core/svg/animation/SMILTimeContainer.h" | 27 #include "core/svg/animation/SMILTimeContainer.h" |
28 | 28 |
29 #include "core/animation/AnimationClock.h" | |
30 #include "core/animation/DocumentTimeline.h" | |
31 #include "core/dom/ElementTraversal.h" | 29 #include "core/dom/ElementTraversal.h" |
32 #include "core/frame/FrameView.h" | |
33 #include "core/svg/SVGSVGElement.h" | 30 #include "core/svg/SVGSVGElement.h" |
34 #include "core/svg/animation/SVGSMILElement.h" | 31 #include "core/svg/animation/SVGSMILElement.h" |
| 32 #include "wtf/CurrentTime.h" |
35 | 33 |
36 using namespace std; | 34 using namespace std; |
37 | 35 |
38 namespace WebCore { | 36 namespace WebCore { |
39 | 37 |
| 38 static const double animationFrameDelay = 0.025; |
| 39 |
40 SMILTimeContainer::SMILTimeContainer(SVGSVGElement* owner) | 40 SMILTimeContainer::SMILTimeContainer(SVGSVGElement* owner) |
41 : m_beginTime(0) | 41 : m_beginTime(0) |
42 , m_pauseTime(0) | 42 , m_pauseTime(0) |
43 , m_resumeTime(0) | 43 , m_resumeTime(0) |
44 , m_accumulatedActiveTime(0) | 44 , m_accumulatedActiveTime(0) |
45 , m_presetStartTime(0) | 45 , m_presetStartTime(0) |
46 , m_documentOrderIndexesDirty(false) | 46 , m_documentOrderIndexesDirty(false) |
47 , m_framePending(false) | 47 , m_timer(this, &SMILTimeContainer::timerFired) |
48 , m_animationClock(AnimationClock::create()) | |
49 , m_wakeupTimer(this, &SMILTimeContainer::wakeupTimerFired) | |
50 , m_ownerSVGElement(owner) | 48 , m_ownerSVGElement(owner) |
51 #ifndef NDEBUG | 49 #ifndef NDEBUG |
52 , m_preventScheduledAnimationsChanges(false) | 50 , m_preventScheduledAnimationsChanges(false) |
53 #endif | 51 #endif |
54 { | 52 { |
55 } | 53 } |
56 | 54 |
57 SMILTimeContainer::~SMILTimeContainer() | 55 SMILTimeContainer::~SMILTimeContainer() |
58 { | 56 { |
59 cancelAnimationFrame(); | 57 cancelAnimationFrame(); |
60 ASSERT(!m_wakeupTimer.isActive()); | 58 ASSERT(!m_timer.isActive()); |
61 #ifndef NDEBUG | 59 #ifndef NDEBUG |
62 ASSERT(!m_preventScheduledAnimationsChanges); | 60 ASSERT(!m_preventScheduledAnimationsChanges); |
63 #endif | 61 #endif |
64 } | 62 } |
65 | 63 |
66 void SMILTimeContainer::schedule(SVGSMILElement* animation, SVGElement* target,
const QualifiedName& attributeName) | 64 void SMILTimeContainer::schedule(SVGSMILElement* animation, SVGElement* target,
const QualifiedName& attributeName) |
67 { | 65 { |
68 ASSERT(animation->timeContainer() == this); | 66 ASSERT(animation->timeContainer() == this); |
69 ASSERT(target); | 67 ASSERT(target); |
70 ASSERT(animation->hasValidAttributeName()); | 68 ASSERT(animation->hasValidAttributeName()); |
(...skipping 23 matching lines...) Expand all Loading... |
94 #endif | 92 #endif |
95 | 93 |
96 ElementAttributePair key(target, attributeName); | 94 ElementAttributePair key(target, attributeName); |
97 AnimationsVector* scheduled = m_scheduledAnimations.get(key); | 95 AnimationsVector* scheduled = m_scheduledAnimations.get(key); |
98 ASSERT(scheduled); | 96 ASSERT(scheduled); |
99 size_t idx = scheduled->find(animation); | 97 size_t idx = scheduled->find(animation); |
100 ASSERT(idx != kNotFound); | 98 ASSERT(idx != kNotFound); |
101 scheduled->remove(idx); | 99 scheduled->remove(idx); |
102 } | 100 } |
103 | 101 |
104 bool SMILTimeContainer::hasAnimations() const | |
105 { | |
106 return !m_scheduledAnimations.isEmpty(); | |
107 } | |
108 | |
109 void SMILTimeContainer::notifyIntervalsChanged() | 102 void SMILTimeContainer::notifyIntervalsChanged() |
110 { | 103 { |
111 // Schedule updateAnimations() to be called asynchronously so multiple inter
vals | 104 // Schedule updateAnimations() to be called asynchronously so multiple inter
vals |
112 // can change with updateAnimations() only called once at the end. | 105 // can change with updateAnimations() only called once at the end. |
113 scheduleAnimationFrame(); | 106 scheduleAnimationFrame(); |
114 } | 107 } |
115 | 108 |
116 SMILTime SMILTimeContainer::elapsed() const | 109 SMILTime SMILTimeContainer::elapsed() const |
117 { | 110 { |
118 if (!m_beginTime) | 111 if (!m_beginTime) |
119 return 0; | 112 return 0; |
120 | 113 |
121 if (isPaused()) | 114 if (isPaused()) |
122 return m_accumulatedActiveTime; | 115 return m_accumulatedActiveTime; |
123 | 116 |
124 return m_animationClock->currentTime() + m_accumulatedActiveTime - lastResum
eTime(); | 117 return currentTime() + m_accumulatedActiveTime - lastResumeTime(); |
125 } | 118 } |
126 | 119 |
127 bool SMILTimeContainer::isPaused() const | 120 bool SMILTimeContainer::isPaused() const |
128 { | 121 { |
129 return m_pauseTime; | 122 return m_pauseTime; |
130 } | 123 } |
131 | 124 |
132 bool SMILTimeContainer::isStarted() const | 125 bool SMILTimeContainer::isStarted() const |
133 { | 126 { |
134 return m_beginTime; | 127 return m_beginTime; |
135 } | 128 } |
136 | 129 |
137 void SMILTimeContainer::begin() | 130 void SMILTimeContainer::begin() |
138 { | 131 { |
139 ASSERT(!m_beginTime); | 132 ASSERT(!m_beginTime); |
140 double now = m_animationClock->currentTime(); | 133 double now = currentTime(); |
141 | 134 |
142 // If 'm_presetStartTime' is set, the timeline was modified via setElapsed()
before the document began. | 135 // If 'm_presetStartTime' is set, the timeline was modified via setElapsed()
before the document began. |
143 // In this case pass on 'seekToTime=true' to updateAnimations(). | 136 // In this case pass on 'seekToTime=true' to updateAnimations(). |
144 m_beginTime = now - m_presetStartTime; | 137 m_beginTime = now - m_presetStartTime; |
145 updateAnimations(SMILTime(m_presetStartTime), m_presetStartTime ? true : fal
se); | 138 updateAnimations(SMILTime(m_presetStartTime), m_presetStartTime ? true : fal
se); |
146 m_presetStartTime = 0; | 139 m_presetStartTime = 0; |
147 | 140 |
148 if (m_pauseTime) { | 141 if (m_pauseTime) { |
149 m_pauseTime = now; | 142 m_pauseTime = now; |
150 cancelAnimationFrame(); | 143 cancelAnimationFrame(); |
151 } else { | |
152 // Latch the clock to this time (0 or the preset start time). | |
153 m_animationClock->updateTime(now); | |
154 } | 144 } |
155 } | 145 } |
156 | 146 |
157 void SMILTimeContainer::pause() | 147 void SMILTimeContainer::pause() |
158 { | 148 { |
159 ASSERT(!isPaused()); | 149 ASSERT(!isPaused()); |
160 m_pauseTime = m_animationClock->currentTime(); | 150 m_pauseTime = currentTime(); |
161 | 151 |
162 if (m_beginTime) { | 152 if (m_beginTime) { |
163 m_accumulatedActiveTime += m_pauseTime - lastResumeTime(); | 153 m_accumulatedActiveTime += m_pauseTime - lastResumeTime(); |
164 cancelAnimationFrame(); | 154 cancelAnimationFrame(); |
165 } | 155 } |
166 m_resumeTime = 0; | 156 m_resumeTime = 0; |
167 m_animationClock->unfreeze(); | |
168 } | 157 } |
169 | 158 |
170 void SMILTimeContainer::resume() | 159 void SMILTimeContainer::resume() |
171 { | 160 { |
172 ASSERT(isPaused()); | 161 ASSERT(isPaused()); |
173 m_resumeTime = m_animationClock->currentTime(); | 162 m_resumeTime = currentTime(); |
174 | 163 |
175 m_pauseTime = 0; | 164 m_pauseTime = 0; |
176 scheduleAnimationFrame(); | 165 scheduleAnimationFrame(); |
177 m_animationClock->unfreeze(); | |
178 } | 166 } |
179 | 167 |
180 void SMILTimeContainer::setElapsed(SMILTime time) | 168 void SMILTimeContainer::setElapsed(SMILTime time) |
181 { | 169 { |
182 // If the documment didn't begin yet, record a new start time, we'll seek to
once its possible. | 170 // If the documment didn't begin yet, record a new start time, we'll seek to
once its possible. |
183 if (!m_beginTime) { | 171 if (!m_beginTime) { |
184 m_presetStartTime = time.value(); | 172 m_presetStartTime = time.value(); |
185 return; | 173 return; |
186 } | 174 } |
187 | 175 |
188 m_animationClock->unfreeze(); | 176 if (m_beginTime) |
| 177 cancelAnimationFrame(); |
189 | 178 |
190 cancelAnimationFrame(); | 179 double now = currentTime(); |
191 | |
192 double now = m_animationClock->currentTime(); | |
193 m_beginTime = now - time.value(); | 180 m_beginTime = now - time.value(); |
194 m_resumeTime = 0; | 181 m_resumeTime = 0; |
195 if (m_pauseTime) { | 182 if (m_pauseTime) { |
196 m_pauseTime = now; | 183 m_pauseTime = now; |
197 m_accumulatedActiveTime = time.value(); | 184 m_accumulatedActiveTime = time.value(); |
198 } else { | 185 } else { |
199 m_accumulatedActiveTime = 0; | 186 m_accumulatedActiveTime = 0; |
200 } | 187 } |
201 | 188 |
202 #ifndef NDEBUG | 189 #ifndef NDEBUG |
203 m_preventScheduledAnimationsChanges = true; | 190 m_preventScheduledAnimationsChanges = true; |
204 #endif | 191 #endif |
205 GroupedAnimationsMap::iterator end = m_scheduledAnimations.end(); | 192 GroupedAnimationsMap::iterator end = m_scheduledAnimations.end(); |
206 for (GroupedAnimationsMap::iterator it = m_scheduledAnimations.begin(); it !
= end; ++it) { | 193 for (GroupedAnimationsMap::iterator it = m_scheduledAnimations.begin(); it !
= end; ++it) { |
207 AnimationsVector* scheduled = it->value.get(); | 194 AnimationsVector* scheduled = it->value.get(); |
208 unsigned size = scheduled->size(); | 195 unsigned size = scheduled->size(); |
209 for (unsigned n = 0; n < size; n++) | 196 for (unsigned n = 0; n < size; n++) |
210 scheduled->at(n)->reset(); | 197 scheduled->at(n)->reset(); |
211 } | 198 } |
212 #ifndef NDEBUG | 199 #ifndef NDEBUG |
213 m_preventScheduledAnimationsChanges = false; | 200 m_preventScheduledAnimationsChanges = false; |
214 #endif | 201 #endif |
215 | 202 |
216 updateAnimations(time, true); | 203 updateAnimations(time, true); |
217 // Latch the clock to wait for this frame to be sampled by the frame interva
l. | |
218 m_animationClock->updateTime(now); | |
219 } | 204 } |
220 | 205 |
221 bool SMILTimeContainer::isTimelineRunning() const | 206 bool SMILTimeContainer::isTimelineRunning() const |
222 { | 207 { |
223 return m_beginTime && !isPaused(); | 208 return m_beginTime && !isPaused(); |
224 } | 209 } |
225 | 210 |
226 void SMILTimeContainer::scheduleAnimationFrame(SMILTime fireTime) | 211 void SMILTimeContainer::scheduleAnimationFrame(SMILTime fireTime) |
227 { | 212 { |
228 if (!isTimelineRunning()) | 213 if (!isTimelineRunning()) |
229 return; | 214 return; |
230 | 215 |
231 if (!fireTime.isFinite()) | 216 if (!fireTime.isFinite()) |
232 return; | 217 return; |
233 | 218 |
234 SMILTime delay = fireTime - elapsed(); | 219 SMILTime delay = max(fireTime - elapsed(), SMILTime(animationFrameDelay)); |
235 if (delay.value() < DocumentTimeline::s_minimumDelay) | 220 m_timer.startOneShot(delay.value()); |
236 serviceOnNextFrame(); | |
237 else | |
238 m_wakeupTimer.startOneShot(delay.value() - DocumentTimeline::s_minimumDe
lay); | |
239 } | 221 } |
240 | 222 |
241 void SMILTimeContainer::scheduleAnimationFrame() | 223 void SMILTimeContainer::scheduleAnimationFrame() |
242 { | 224 { |
243 if (!isTimelineRunning()) | 225 if (!isTimelineRunning()) |
244 return; | 226 return; |
245 | 227 |
246 // Could also schedule a wakeup at +0 seconds, but that could still | 228 m_timer.startOneShot(0); |
247 // potentially race with the servicing of the next frame. | |
248 serviceOnNextFrame(); | |
249 } | 229 } |
250 | 230 |
251 void SMILTimeContainer::cancelAnimationFrame() | 231 void SMILTimeContainer::cancelAnimationFrame() |
252 { | 232 { |
253 m_framePending = false; | 233 m_timer.stop(); |
254 m_wakeupTimer.stop(); | |
255 } | 234 } |
256 | 235 |
257 void SMILTimeContainer::wakeupTimerFired(Timer<SMILTimeContainer>*) | 236 void SMILTimeContainer::timerFired(Timer<SMILTimeContainer>*) |
258 { | 237 { |
259 ASSERT(isTimelineRunning()); | 238 ASSERT(isTimelineRunning()); |
260 serviceOnNextFrame(); | 239 updateAnimations(elapsed()); |
261 } | 240 } |
262 | 241 |
263 void SMILTimeContainer::updateDocumentOrderIndexes() | 242 void SMILTimeContainer::updateDocumentOrderIndexes() |
264 { | 243 { |
265 unsigned timingElementCount = 0; | 244 unsigned timingElementCount = 0; |
266 for (Element* element = m_ownerSVGElement; element; element = ElementTravers
al::next(*element, m_ownerSVGElement)) { | 245 for (Element* element = m_ownerSVGElement; element; element = ElementTravers
al::next(*element, m_ownerSVGElement)) { |
267 if (isSVGSMILElement(*element)) | 246 if (isSVGSMILElement(*element)) |
268 toSVGSMILElement(element)->setDocumentOrderIndex(timingElementCount+
+); | 247 toSVGSMILElement(element)->setDocumentOrderIndex(timingElementCount+
+); |
269 } | 248 } |
270 m_documentOrderIndexesDirty = false; | 249 m_documentOrderIndexesDirty = false; |
271 } | 250 } |
272 | 251 |
273 struct PriorityCompare { | 252 struct PriorityCompare { |
274 PriorityCompare(SMILTime elapsed) : m_elapsed(elapsed) {} | 253 PriorityCompare(SMILTime elapsed) : m_elapsed(elapsed) {} |
275 bool operator()(const RefPtr<SVGSMILElement>& a, const RefPtr<SVGSMILElement
>& b) | 254 bool operator()(const RefPtr<SVGSMILElement>& a, const RefPtr<SVGSMILElement
>& b) |
276 { | 255 { |
277 // FIXME: This should also consider possible timing relations between th
e elements. | 256 // FIXME: This should also consider possible timing relations between th
e elements. |
278 SMILTime aBegin = a->intervalBegin(); | 257 SMILTime aBegin = a->intervalBegin(); |
279 SMILTime bBegin = b->intervalBegin(); | 258 SMILTime bBegin = b->intervalBegin(); |
280 // Frozen elements need to be prioritized based on their previous interv
al. | 259 // Frozen elements need to be prioritized based on their previous interv
al. |
281 aBegin = a->isFrozen() && m_elapsed < aBegin ? a->previousIntervalBegin(
) : aBegin; | 260 aBegin = a->isFrozen() && m_elapsed < aBegin ? a->previousIntervalBegin(
) : aBegin; |
282 bBegin = b->isFrozen() && m_elapsed < bBegin ? b->previousIntervalBegin(
) : bBegin; | 261 bBegin = b->isFrozen() && m_elapsed < bBegin ? b->previousIntervalBegin(
) : bBegin; |
283 if (aBegin == bBegin) | 262 if (aBegin == bBegin) |
284 return a->documentOrderIndex() < b->documentOrderIndex(); | 263 return a->documentOrderIndex() < b->documentOrderIndex(); |
285 return aBegin < bBegin; | 264 return aBegin < bBegin; |
286 } | 265 } |
287 SMILTime m_elapsed; | 266 SMILTime m_elapsed; |
288 }; | 267 }; |
289 | 268 |
290 Document& SMILTimeContainer::document() const | |
291 { | |
292 ASSERT(m_ownerSVGElement); | |
293 return m_ownerSVGElement->document(); | |
294 } | |
295 | |
296 void SMILTimeContainer::serviceOnNextFrame() | |
297 { | |
298 if (document().view()) { | |
299 document().view()->scheduleAnimation(); | |
300 m_framePending = true; | |
301 } | |
302 } | |
303 | |
304 void SMILTimeContainer::serviceAnimations(double monotonicAnimationStartTime) | |
305 { | |
306 if (!m_framePending) | |
307 return; | |
308 | |
309 m_framePending = false; | |
310 // If the clock is frozen at this point, it means the timeline has been | |
311 // started, but the first animation frame hasn't yet been serviced. If so, | |
312 // then just keep the clock frozen for this update. | |
313 if (!m_animationClock->isFrozen()) | |
314 m_animationClock->updateTime(monotonicAnimationStartTime); | |
315 updateAnimations(elapsed()); | |
316 m_animationClock->unfreeze(); | |
317 } | |
318 | |
319 void SMILTimeContainer::updateAnimations(SMILTime elapsed, bool seekToTime) | 269 void SMILTimeContainer::updateAnimations(SMILTime elapsed, bool seekToTime) |
320 { | 270 { |
321 SMILTime earliestFireTime = SMILTime::unresolved(); | 271 SMILTime earliestFireTime = SMILTime::unresolved(); |
322 | 272 |
323 #ifndef NDEBUG | 273 #ifndef NDEBUG |
324 // This boolean will catch any attempts to schedule/unschedule scheduledAnim
ations during this critical section. | 274 // This boolean will catch any attempts to schedule/unschedule scheduledAnim
ations during this critical section. |
325 // Similarly, any elements removed will unschedule themselves, so this will
catch modification of animationsToApply. | 275 // Similarly, any elements removed will unschedule themselves, so this will
catch modification of animationsToApply. |
326 m_preventScheduledAnimationsChanges = true; | 276 m_preventScheduledAnimationsChanges = true; |
327 #endif | 277 #endif |
328 | 278 |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
401 | 351 |
402 if (animDiscard->inDocument()) { | 352 if (animDiscard->inDocument()) { |
403 animDiscard->remove(IGNORE_EXCEPTION); | 353 animDiscard->remove(IGNORE_EXCEPTION); |
404 ASSERT(!animDiscard->inDocument()); | 354 ASSERT(!animDiscard->inDocument()); |
405 } | 355 } |
406 } | 356 } |
407 } | 357 } |
408 } | 358 } |
409 | 359 |
410 } | 360 } |
OLD | NEW |