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/dom/ElementTraversal.h" | 29 #include "core/dom/ElementTraversal.h" |
30 #include "core/frame/FrameView.h" | |
30 #include "core/svg/SVGSVGElement.h" | 31 #include "core/svg/SVGSVGElement.h" |
31 #include "core/svg/animation/SVGSMILElement.h" | 32 #include "core/svg/animation/SVGSMILElement.h" |
32 #include "wtf/CurrentTime.h" | 33 #include "wtf/CurrentTime.h" |
33 | 34 |
34 using namespace std; | 35 using namespace std; |
35 | 36 |
36 namespace WebCore { | 37 namespace WebCore { |
37 | 38 |
38 static const double animationFrameDelay = 0.025; | 39 // Blatantly copied from core/animation/DocumentTimeline.cpp. |
40 static const double minimumDelay = 0.04; | |
pdr.
2014/03/03 17:47:33
Can you use DocumentTimeline::s_minimumDelay direc
dstockwell
2014/03/04 01:19:04
I'm OK with this duplication for now, but this log
fs
2014/03/04 10:28:19
I suppose we can trade this two line hack for a on
| |
39 | 41 |
40 SMILTimeContainer::SMILTimeContainer(SVGSVGElement* owner) | 42 SMILTimeContainer::SMILTimeContainer(SVGSVGElement* owner) |
41 : m_beginTime(0) | 43 : m_beginTime(0) |
42 , m_pauseTime(0) | 44 , m_pauseTime(0) |
43 , m_resumeTime(0) | 45 , m_resumeTime(0) |
44 , m_accumulatedActiveTime(0) | 46 , m_accumulatedActiveTime(0) |
45 , m_presetStartTime(0) | 47 , m_presetStartTime(0) |
46 , m_documentOrderIndexesDirty(false) | 48 , m_documentOrderIndexesDirty(false) |
47 , m_timer(this, &SMILTimeContainer::timerFired) | 49 , m_framePending(false) |
50 , m_clockFrozen(false) | |
pdr.
2014/03/03 17:47:33
I'm wary of adding more clock-related state to thi
fs
2014/03/04 10:28:19
I will try to formalize this somewhat - probably b
| |
51 , m_wakeupTimer(this, &SMILTimeContainer::wakeupTimerFired) | |
48 , m_ownerSVGElement(owner) | 52 , m_ownerSVGElement(owner) |
49 #ifndef NDEBUG | 53 #ifndef NDEBUG |
50 , m_preventScheduledAnimationsChanges(false) | 54 , m_preventScheduledAnimationsChanges(false) |
51 #endif | 55 #endif |
52 { | 56 { |
53 } | 57 } |
54 | 58 |
55 SMILTimeContainer::~SMILTimeContainer() | 59 SMILTimeContainer::~SMILTimeContainer() |
56 { | 60 { |
57 cancelAnimationFrame(); | 61 cancelAnimationFrame(); |
58 ASSERT(!m_timer.isActive()); | 62 ASSERT(!m_wakeupTimer.isActive()); |
59 #ifndef NDEBUG | 63 #ifndef NDEBUG |
60 ASSERT(!m_preventScheduledAnimationsChanges); | 64 ASSERT(!m_preventScheduledAnimationsChanges); |
61 #endif | 65 #endif |
62 } | 66 } |
63 | 67 |
64 void SMILTimeContainer::schedule(SVGSMILElement* animation, SVGElement* target, const QualifiedName& attributeName) | 68 void SMILTimeContainer::schedule(SVGSMILElement* animation, SVGElement* target, const QualifiedName& attributeName) |
65 { | 69 { |
66 ASSERT(animation->timeContainer() == this); | 70 ASSERT(animation->timeContainer() == this); |
67 ASSERT(target); | 71 ASSERT(target); |
68 ASSERT(animation->hasValidAttributeName()); | 72 ASSERT(animation->hasValidAttributeName()); |
(...skipping 23 matching lines...) Expand all Loading... | |
92 #endif | 96 #endif |
93 | 97 |
94 ElementAttributePair key(target, attributeName); | 98 ElementAttributePair key(target, attributeName); |
95 AnimationsVector* scheduled = m_scheduledAnimations.get(key); | 99 AnimationsVector* scheduled = m_scheduledAnimations.get(key); |
96 ASSERT(scheduled); | 100 ASSERT(scheduled); |
97 size_t idx = scheduled->find(animation); | 101 size_t idx = scheduled->find(animation); |
98 ASSERT(idx != kNotFound); | 102 ASSERT(idx != kNotFound); |
99 scheduled->remove(idx); | 103 scheduled->remove(idx); |
100 } | 104 } |
101 | 105 |
106 bool SMILTimeContainer::hasAnimations() const | |
107 { | |
108 return !m_scheduledAnimations.isEmpty(); | |
109 } | |
110 | |
102 void SMILTimeContainer::notifyIntervalsChanged() | 111 void SMILTimeContainer::notifyIntervalsChanged() |
103 { | 112 { |
104 // Schedule updateAnimations() to be called asynchronously so multiple inter vals | 113 // Schedule updateAnimations() to be called asynchronously so multiple inter vals |
105 // can change with updateAnimations() only called once at the end. | 114 // can change with updateAnimations() only called once at the end. |
106 scheduleAnimationFrame(); | 115 scheduleAnimationFrame(); |
107 } | 116 } |
108 | 117 |
109 SMILTime SMILTimeContainer::elapsed() const | 118 SMILTime SMILTimeContainer::elapsed() const |
110 { | 119 { |
111 if (!m_beginTime) | 120 if (!m_beginTime || m_clockFrozen) |
112 return 0; | 121 return 0; |
113 | 122 |
114 if (isPaused()) | 123 if (isPaused()) |
115 return m_accumulatedActiveTime; | 124 return m_accumulatedActiveTime; |
116 | 125 |
117 return currentTime() + m_accumulatedActiveTime - lastResumeTime(); | 126 return currentTime() + m_accumulatedActiveTime - lastResumeTime(); |
118 } | 127 } |
119 | 128 |
120 bool SMILTimeContainer::isPaused() const | 129 bool SMILTimeContainer::isPaused() const |
121 { | 130 { |
122 return m_pauseTime; | 131 return m_pauseTime; |
123 } | 132 } |
124 | 133 |
125 bool SMILTimeContainer::isStarted() const | 134 bool SMILTimeContainer::isStarted() const |
126 { | 135 { |
127 return m_beginTime; | 136 return m_beginTime; |
128 } | 137 } |
129 | 138 |
130 void SMILTimeContainer::begin() | 139 void SMILTimeContainer::begin() |
131 { | 140 { |
132 ASSERT(!m_beginTime); | 141 ASSERT(!m_beginTime); |
133 double now = currentTime(); | 142 double now = currentTime(); |
134 | 143 |
135 // If 'm_presetStartTime' is set, the timeline was modified via setElapsed() before the document began. | 144 // If 'm_presetStartTime' is set, the timeline was modified via setElapsed() before the document began. |
136 // In this case pass on 'seekToTime=true' to updateAnimations(). | 145 // In this case pass on 'seekToTime=true' to updateAnimations(). |
137 m_beginTime = now - m_presetStartTime; | 146 m_beginTime = now - m_presetStartTime; |
138 updateAnimations(SMILTime(m_presetStartTime), m_presetStartTime ? true : fal se); | 147 bool hadPresetStartTime = m_presetStartTime ? true : false; |
148 updateAnimations(SMILTime(m_presetStartTime), hadPresetStartTime); | |
139 m_presetStartTime = 0; | 149 m_presetStartTime = 0; |
140 | 150 |
141 if (m_pauseTime) { | 151 if (m_pauseTime) { |
142 m_pauseTime = now; | 152 m_pauseTime = now; |
143 cancelAnimationFrame(); | 153 cancelAnimationFrame(); |
154 } else { | |
155 m_clockFrozen = !hadPresetStartTime; | |
144 } | 156 } |
145 } | 157 } |
146 | 158 |
147 void SMILTimeContainer::pause() | 159 void SMILTimeContainer::pause() |
148 { | 160 { |
149 ASSERT(!isPaused()); | 161 ASSERT(!isPaused()); |
150 m_pauseTime = currentTime(); | 162 m_pauseTime = currentTime(); |
151 | 163 |
152 if (m_beginTime) { | 164 if (m_beginTime) { |
153 m_accumulatedActiveTime += m_pauseTime - lastResumeTime(); | 165 m_accumulatedActiveTime += m_pauseTime - lastResumeTime(); |
154 cancelAnimationFrame(); | 166 cancelAnimationFrame(); |
155 } | 167 } |
156 m_resumeTime = 0; | 168 m_resumeTime = 0; |
169 m_clockFrozen = false; | |
157 } | 170 } |
158 | 171 |
159 void SMILTimeContainer::resume() | 172 void SMILTimeContainer::resume() |
160 { | 173 { |
161 ASSERT(isPaused()); | 174 ASSERT(isPaused()); |
162 m_resumeTime = currentTime(); | 175 m_resumeTime = currentTime(); |
163 | 176 |
164 m_pauseTime = 0; | 177 m_pauseTime = 0; |
165 scheduleAnimationFrame(); | 178 scheduleAnimationFrame(); |
179 m_clockFrozen = false; | |
166 } | 180 } |
167 | 181 |
168 void SMILTimeContainer::setElapsed(SMILTime time) | 182 void SMILTimeContainer::setElapsed(SMILTime time) |
169 { | 183 { |
170 // If the documment didn't begin yet, record a new start time, we'll seek to once its possible. | 184 // If the documment didn't begin yet, record a new start time, we'll seek to once its possible. |
171 if (!m_beginTime) { | 185 if (!m_beginTime) { |
172 m_presetStartTime = time.value(); | 186 m_presetStartTime = time.value(); |
173 return; | 187 return; |
174 } | 188 } |
175 | 189 |
190 m_clockFrozen = false; | |
191 | |
176 if (m_beginTime) | 192 if (m_beginTime) |
pdr.
2014/03/03 17:47:33
How did this if statement get here? :P
fs
2014/03/04 10:28:19
Magic? =P
Maybe it should've been some other cond
| |
177 cancelAnimationFrame(); | 193 cancelAnimationFrame(); |
178 | 194 |
179 double now = currentTime(); | 195 double now = currentTime(); |
180 m_beginTime = now - time.value(); | 196 m_beginTime = now - time.value(); |
181 m_resumeTime = 0; | 197 m_resumeTime = 0; |
182 if (m_pauseTime) { | 198 if (m_pauseTime) { |
183 m_pauseTime = now; | 199 m_pauseTime = now; |
184 m_accumulatedActiveTime = time.value(); | 200 m_accumulatedActiveTime = time.value(); |
185 } else { | 201 } else { |
186 m_accumulatedActiveTime = 0; | 202 m_accumulatedActiveTime = 0; |
187 } | 203 } |
188 | 204 |
189 #ifndef NDEBUG | 205 #ifndef NDEBUG |
190 m_preventScheduledAnimationsChanges = true; | 206 m_preventScheduledAnimationsChanges = true; |
191 #endif | 207 #endif |
192 GroupedAnimationsMap::iterator end = m_scheduledAnimations.end(); | 208 GroupedAnimationsMap::iterator end = m_scheduledAnimations.end(); |
193 for (GroupedAnimationsMap::iterator it = m_scheduledAnimations.begin(); it ! = end; ++it) { | 209 for (GroupedAnimationsMap::iterator it = m_scheduledAnimations.begin(); it ! = end; ++it) { |
194 AnimationsVector* scheduled = it->value.get(); | 210 AnimationsVector* scheduled = it->value.get(); |
195 unsigned size = scheduled->size(); | 211 unsigned size = scheduled->size(); |
196 for (unsigned n = 0; n < size; n++) | 212 for (unsigned n = 0; n < size; n++) |
197 scheduled->at(n)->reset(); | 213 scheduled->at(n)->reset(); |
198 } | 214 } |
199 #ifndef NDEBUG | 215 #ifndef NDEBUG |
200 m_preventScheduledAnimationsChanges = false; | 216 m_preventScheduledAnimationsChanges = false; |
201 #endif | 217 #endif |
202 | 218 |
203 updateAnimations(time, true); | 219 updateAnimations(time, true); |
204 } | 220 } |
205 | 221 |
206 bool SMILTimeContainer::isTimelineRunning() const | 222 bool SMILTimeContainer::isTimelineRunning() const |
pdr.
2014/03/03 17:47:33
Should this have m_clockFrozen too?
fs
2014/03/04 10:28:19
I don't think so. The clock being frozen does not
| |
207 { | 223 { |
208 return m_beginTime && !isPaused(); | 224 return m_beginTime && !isPaused(); |
209 } | 225 } |
210 | 226 |
211 void SMILTimeContainer::scheduleAnimationFrame(SMILTime fireTime) | 227 void SMILTimeContainer::scheduleAnimationFrame(SMILTime fireTime) |
212 { | 228 { |
213 if (!isTimelineRunning()) | 229 if (!isTimelineRunning()) |
214 return; | 230 return; |
215 | 231 |
216 if (!fireTime.isFinite()) | 232 if (!fireTime.isFinite()) |
217 return; | 233 return; |
218 | 234 |
219 SMILTime delay = max(fireTime - elapsed(), SMILTime(animationFrameDelay)); | 235 SMILTime delay = fireTime - elapsed(); |
220 m_timer.startOneShot(delay.value()); | 236 if (delay.value() < minimumDelay) |
237 serviceOnNextFrame(); | |
238 else | |
239 m_wakeupTimer.startOneShot(delay.value() - minimumDelay); | |
221 } | 240 } |
222 | 241 |
223 void SMILTimeContainer::scheduleAnimationFrame() | 242 void SMILTimeContainer::scheduleAnimationFrame() |
224 { | 243 { |
225 if (!isTimelineRunning()) | 244 if (!isTimelineRunning()) |
226 return; | 245 return; |
227 | 246 |
228 m_timer.startOneShot(0); | 247 // Could also schedule a wakeup at +0 seconds, but that could still |
248 // potentially race with the servicing of the next frame. | |
249 serviceOnNextFrame(); | |
229 } | 250 } |
230 | 251 |
231 void SMILTimeContainer::cancelAnimationFrame() | 252 void SMILTimeContainer::cancelAnimationFrame() |
232 { | 253 { |
233 m_timer.stop(); | 254 m_framePending = false; |
255 m_wakeupTimer.stop(); | |
234 } | 256 } |
235 | 257 |
236 void SMILTimeContainer::timerFired(Timer<SMILTimeContainer>*) | 258 void SMILTimeContainer::wakeupTimerFired(Timer<SMILTimeContainer>*) |
237 { | 259 { |
238 ASSERT(isTimelineRunning()); | 260 ASSERT(isTimelineRunning()); |
239 updateAnimations(elapsed()); | 261 serviceOnNextFrame(); |
240 } | 262 } |
241 | 263 |
242 void SMILTimeContainer::updateDocumentOrderIndexes() | 264 void SMILTimeContainer::updateDocumentOrderIndexes() |
243 { | 265 { |
244 unsigned timingElementCount = 0; | 266 unsigned timingElementCount = 0; |
245 for (Element* element = m_ownerSVGElement; element; element = ElementTravers al::next(*element, m_ownerSVGElement)) { | 267 for (Element* element = m_ownerSVGElement; element; element = ElementTravers al::next(*element, m_ownerSVGElement)) { |
246 if (isSVGSMILElement(*element)) | 268 if (isSVGSMILElement(*element)) |
247 toSVGSMILElement(element)->setDocumentOrderIndex(timingElementCount+ +); | 269 toSVGSMILElement(element)->setDocumentOrderIndex(timingElementCount+ +); |
248 } | 270 } |
249 m_documentOrderIndexesDirty = false; | 271 m_documentOrderIndexesDirty = false; |
250 } | 272 } |
251 | 273 |
252 struct PriorityCompare { | 274 struct PriorityCompare { |
253 PriorityCompare(SMILTime elapsed) : m_elapsed(elapsed) {} | 275 PriorityCompare(SMILTime elapsed) : m_elapsed(elapsed) {} |
254 bool operator()(const RefPtr<SVGSMILElement>& a, const RefPtr<SVGSMILElement >& b) | 276 bool operator()(const RefPtr<SVGSMILElement>& a, const RefPtr<SVGSMILElement >& b) |
255 { | 277 { |
256 // FIXME: This should also consider possible timing relations between th e elements. | 278 // FIXME: This should also consider possible timing relations between th e elements. |
257 SMILTime aBegin = a->intervalBegin(); | 279 SMILTime aBegin = a->intervalBegin(); |
258 SMILTime bBegin = b->intervalBegin(); | 280 SMILTime bBegin = b->intervalBegin(); |
259 // Frozen elements need to be prioritized based on their previous interv al. | 281 // Frozen elements need to be prioritized based on their previous interv al. |
260 aBegin = a->isFrozen() && m_elapsed < aBegin ? a->previousIntervalBegin( ) : aBegin; | 282 aBegin = a->isFrozen() && m_elapsed < aBegin ? a->previousIntervalBegin( ) : aBegin; |
261 bBegin = b->isFrozen() && m_elapsed < bBegin ? b->previousIntervalBegin( ) : bBegin; | 283 bBegin = b->isFrozen() && m_elapsed < bBegin ? b->previousIntervalBegin( ) : bBegin; |
262 if (aBegin == bBegin) | 284 if (aBegin == bBegin) |
263 return a->documentOrderIndex() < b->documentOrderIndex(); | 285 return a->documentOrderIndex() < b->documentOrderIndex(); |
264 return aBegin < bBegin; | 286 return aBegin < bBegin; |
265 } | 287 } |
266 SMILTime m_elapsed; | 288 SMILTime m_elapsed; |
267 }; | 289 }; |
268 | 290 |
291 Document& SMILTimeContainer::document() const | |
292 { | |
293 ASSERT(m_ownerSVGElement); | |
294 return m_ownerSVGElement->document(); | |
295 } | |
296 | |
297 void SMILTimeContainer::serviceOnNextFrame() | |
298 { | |
299 if (document().view()) { | |
300 document().view()->scheduleAnimation(); | |
301 m_framePending = true; | |
302 } | |
303 } | |
304 | |
305 void SMILTimeContainer::serviceAnimations(double monotonicAnimationStartTime) | |
dstockwell
2014/03/04 01:19:04
This patch just moves updates to be driven by the
fs
2014/03/04 10:28:19
Yes, I plan to that in follow up CLs. Had original
| |
306 { | |
307 if (!m_framePending) | |
308 return; | |
309 | |
310 m_framePending = false; | |
311 updateAnimations(elapsed()); | |
312 m_clockFrozen = false; | |
313 } | |
314 | |
269 void SMILTimeContainer::updateAnimations(SMILTime elapsed, bool seekToTime) | 315 void SMILTimeContainer::updateAnimations(SMILTime elapsed, bool seekToTime) |
270 { | 316 { |
271 SMILTime earliestFireTime = SMILTime::unresolved(); | 317 SMILTime earliestFireTime = SMILTime::unresolved(); |
272 | 318 |
273 #ifndef NDEBUG | 319 #ifndef NDEBUG |
274 // This boolean will catch any attempts to schedule/unschedule scheduledAnim ations during this critical section. | 320 // This boolean will catch any attempts to schedule/unschedule scheduledAnim ations during this critical section. |
275 // Similarly, any elements removed will unschedule themselves, so this will catch modification of animationsToApply. | 321 // Similarly, any elements removed will unschedule themselves, so this will catch modification of animationsToApply. |
276 m_preventScheduledAnimationsChanges = true; | 322 m_preventScheduledAnimationsChanges = true; |
277 #endif | 323 #endif |
278 | 324 |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
351 | 397 |
352 if (animDiscard->inDocument()) { | 398 if (animDiscard->inDocument()) { |
353 animDiscard->remove(IGNORE_EXCEPTION); | 399 animDiscard->remove(IGNORE_EXCEPTION); |
354 ASSERT(!animDiscard->inDocument()); | 400 ASSERT(!animDiscard->inDocument()); |
355 } | 401 } |
356 } | 402 } |
357 } | 403 } |
358 } | 404 } |
359 | 405 |
360 } | 406 } |
OLD | NEW |