Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(64)

Side by Side Diff: Source/core/svg/animation/SMILTimeContainer.cpp

Issue 179293004: Drive SVG Animations via requestAnimationFrame (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Rebase. Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698