OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright (C) 2013 Google Inc. All rights reserved. | 2 * Copyright (C) 2013 Google 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 are | 5 * modification, are permitted provided that the following conditions are |
6 * met: | 6 * met: |
7 * | 7 * |
8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
(...skipping 13 matching lines...) Expand all Loading... | |
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
29 */ | 29 */ |
30 | 30 |
31 #include "config.h" | 31 #include "config.h" |
32 #include "core/animation/Animation.h" | 32 #include "core/animation/Animation.h" |
33 | 33 |
34 #include "bindings/core/v8/Dictionary.h" | |
35 #include "bindings/core/v8/ExceptionState.h" | |
36 #include "core/animation/AnimationPlayer.h" | |
37 #include "core/animation/AnimationTimeline.h" | 34 #include "core/animation/AnimationTimeline.h" |
38 #include "core/animation/AnimationTimingProperties.h" | 35 #include "core/animation/KeyframeEffect.h" |
39 #include "core/animation/CompositorAnimations.h" | 36 #include "core/dom/Document.h" |
40 #include "core/animation/ElementAnimations.h" | 37 #include "core/dom/ExceptionCode.h" |
41 #include "core/animation/Interpolation.h" | 38 #include "core/events/AnimationPlayerEvent.h" |
42 #include "core/animation/KeyframeEffectModel.h" | |
43 #include "core/animation/PropertyHandle.h" | |
44 #include "core/dom/Element.h" | |
45 #include "core/dom/NodeComputedStyle.h" | |
46 #include "core/frame/UseCounter.h" | 39 #include "core/frame/UseCounter.h" |
47 #include "core/paint/DeprecatedPaintLayer.h" | 40 #include "core/inspector/InspectorInstrumentation.h" |
48 #include "core/svg/SVGElement.h" | 41 #include "core/inspector/InspectorTraceEvents.h" |
42 #include "platform/RuntimeEnabledFeatures.h" | |
43 #include "platform/TraceEvent.h" | |
44 #include "public/platform/Platform.h" | |
45 #include "public/platform/WebCompositorAnimationPlayer.h" | |
46 #include "public/platform/WebCompositorSupport.h" | |
47 #include "wtf/MathExtras.h" | |
49 | 48 |
50 namespace blink { | 49 namespace blink { |
51 | 50 |
52 PassRefPtrWillBeRawPtr<Animation> Animation::create(Element* target, PassRefPtrW illBeRawPtr<AnimationEffect> effect, const Timing& timing, Priority priority, Pa ssOwnPtrWillBeRawPtr<EventDelegate> eventDelegate) | 51 namespace { |
53 { | 52 |
54 return adoptRefWillBeNoop(new Animation(target, effect, timing, priority, ev entDelegate)); | 53 static unsigned nextSequenceNumber() |
55 } | 54 { |
56 | 55 static unsigned next = 0; |
57 PassRefPtrWillBeRawPtr<Animation> Animation::create(Element* element, const Vect or<Dictionary>& keyframeDictionaryVector, double duration, ExceptionState& excep tionState) | 56 return ++next; |
58 { | 57 } |
59 ASSERT(RuntimeEnabledFeatures::webAnimationsAPIEnabled()); | 58 |
60 if (element) | 59 } |
61 UseCounter::count(element->document(), UseCounter::AnimationConstructorK eyframeListEffectObjectTiming); | 60 |
62 return create(element, EffectInput::convert(element, keyframeDictionaryVecto r, exceptionState), TimingInput::convert(duration)); | 61 PassRefPtrWillBeRawPtr<Animation> Animation::create(AnimationEffect* source, Ani mationTimeline* timeline) |
63 } | 62 { |
64 PassRefPtrWillBeRawPtr<Animation> Animation::create(Element* element, const Vect or<Dictionary>& keyframeDictionaryVector, const AnimationTimingProperties& timin gInput, ExceptionState& exceptionState) | 63 if (!timeline) { |
65 { | 64 // FIXME: Support creating animations without a timeline. |
66 ASSERT(RuntimeEnabledFeatures::webAnimationsAPIEnabled()); | 65 return nullptr; |
67 if (element) | 66 } |
68 UseCounter::count(element->document(), UseCounter::AnimationConstructorK eyframeListEffectObjectTiming); | 67 |
69 return create(element, EffectInput::convert(element, keyframeDictionaryVecto r, exceptionState), TimingInput::convert(timingInput)); | 68 RefPtrWillBeRawPtr<Animation> animation = adoptRefWillBeNoop(new Animation(t imeline->document()->contextDocument().get(), *timeline, source)); |
70 } | 69 animation->suspendIfNeeded(); |
71 PassRefPtrWillBeRawPtr<Animation> Animation::create(Element* element, const Vect or<Dictionary>& keyframeDictionaryVector, ExceptionState& exceptionState) | 70 |
72 { | 71 if (timeline) { |
73 ASSERT(RuntimeEnabledFeatures::webAnimationsAPIEnabled()); | 72 timeline->animationAttached(*animation); |
74 if (element) | 73 animation->attachCompositorTimeline(); |
75 UseCounter::count(element->document(), UseCounter::AnimationConstructorK eyframeListEffectNoTiming); | 74 } |
76 return create(element, EffectInput::convert(element, keyframeDictionaryVecto r, exceptionState), Timing()); | 75 |
77 } | 76 return animation.release(); |
78 | 77 } |
79 Animation::Animation(Element* target, PassRefPtrWillBeRawPtr<AnimationEffect> ef fect, const Timing& timing, Priority priority, PassOwnPtrWillBeRawPtr<EventDeleg ate> eventDelegate) | 78 |
80 : AnimationNode(timing, eventDelegate) | 79 Animation::Animation(ExecutionContext* executionContext, AnimationTimeline& time line, AnimationEffect* content) |
81 , m_target(target) | 80 : ActiveDOMObject(executionContext) |
82 , m_effect(effect) | 81 , m_playState(Idle) |
83 , m_sampledEffect(nullptr) | 82 , m_playbackRate(1) |
84 , m_priority(priority) | 83 , m_startTime(nullValue()) |
84 , m_holdTime(0) | |
85 , m_sequenceNumber(nextSequenceNumber()) | |
86 , m_content(content) | |
87 , m_timeline(&timeline) | |
88 , m_paused(false) | |
89 , m_held(true) | |
90 , m_isPausedForTesting(false) | |
91 , m_outdated(false) | |
92 , m_finished(true) | |
93 , m_compositorState(nullptr) | |
94 , m_compositorPending(false) | |
95 , m_compositorGroup(0) | |
96 , m_currentTimePending(false) | |
97 , m_stateIsBeingUpdated(false) | |
98 { | |
99 if (m_content) { | |
100 if (m_content->animation()) { | |
101 m_content->animation()->cancel(); | |
102 m_content->animation()->setSource(0); | |
103 } | |
104 m_content->attach(this); | |
105 } | |
106 } | |
107 | |
108 Animation::~Animation() | |
85 { | 109 { |
86 #if !ENABLE(OILPAN) | 110 #if !ENABLE(OILPAN) |
87 if (m_target) | 111 if (m_content) |
88 m_target->ensureElementAnimations().addAnimation(this); | 112 m_content->detach(); |
113 if (m_timeline) | |
114 m_timeline->animationDestroyed(this); | |
89 #endif | 115 #endif |
90 } | 116 |
91 | 117 destroyCompositorPlayer(); |
92 Animation::~Animation() | 118 } |
93 { | 119 |
94 #if !ENABLE(OILPAN) | 120 #if !ENABLE(OILPAN) |
95 if (m_target) | 121 void Animation::detachFromTimeline() |
96 m_target->elementAnimations()->notifyAnimationDestroyed(this); | 122 { |
123 dispose(); | |
124 m_timeline = nullptr; | |
125 } | |
97 #endif | 126 #endif |
98 } | 127 |
99 | 128 double Animation::sourceEnd() const |
100 void Animation::attach(AnimationPlayer* player) | 129 { |
101 { | 130 return m_content ? m_content->endTimeInternal() : 0; |
102 if (m_target) { | 131 } |
103 m_target->ensureElementAnimations().players().add(player); | 132 |
104 m_target->setNeedsAnimationStyleRecalc(); | 133 bool Animation::limited(double currentTime) const |
105 } | 134 { |
106 AnimationNode::attach(player); | 135 return (m_playbackRate < 0 && currentTime <= 0) || (m_playbackRate > 0 && cu rrentTime >= sourceEnd()); |
107 } | 136 } |
108 | 137 |
109 void Animation::detach() | 138 void Animation::setCurrentTime(double newCurrentTime) |
110 { | 139 { |
111 if (m_target) | 140 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand); |
112 m_target->elementAnimations()->players().remove(player()); | 141 |
113 if (m_sampledEffect) | 142 m_currentTimePending = false; |
114 clearEffects(); | 143 setCurrentTimeInternal(newCurrentTime / 1000, TimingUpdateOnDemand); |
115 AnimationNode::detach(); | 144 |
116 } | 145 if (calculatePlayState() == Finished) |
117 | 146 m_startTime = calculateStartTime(newCurrentTime); |
118 void Animation::specifiedTimingChanged() | 147 } |
119 { | 148 |
120 if (player()) { | 149 void Animation::setCurrentTimeInternal(double newCurrentTime, TimingUpdateReason reason) |
121 // FIXME: Needs to consider groups when added. | 150 { |
122 ASSERT(player()->source() == this); | 151 ASSERT(std::isfinite(newCurrentTime)); |
123 player()->setCompositorPending(true); | 152 |
124 } | 153 bool oldHeld = m_held; |
125 } | 154 bool outdated = false; |
126 | 155 bool isLimited = limited(newCurrentTime); |
127 static AnimationStack& ensureAnimationStack(Element* element) | 156 m_held = m_paused || !m_playbackRate || isLimited || std::isnan(m_startTime) ; |
128 { | 157 if (m_held) { |
129 return element->ensureElementAnimations().defaultStack(); | 158 if (!oldHeld || m_holdTime != newCurrentTime) |
130 } | 159 outdated = true; |
131 | 160 m_holdTime = newCurrentTime; |
132 void Animation::applyEffects() | 161 if (m_paused || !m_playbackRate) { |
133 { | 162 m_startTime = nullValue(); |
134 ASSERT(isInEffect()); | 163 } else if (isLimited && std::isnan(m_startTime) && reason == TimingUpdat eForAnimationFrame) { |
135 ASSERT(player()); | 164 m_startTime = calculateStartTime(newCurrentTime); |
136 if (!m_target || !m_effect) | 165 } |
137 return; | |
138 | |
139 // Cancel composited animation of transform if a motion path has been introd uced on the element. | |
140 if (m_target->computedStyle() | |
141 && m_target->computedStyle()->hasMotionPath() | |
142 && player()->hasActiveAnimationsOnCompositor() | |
143 && player()->affects(*m_target, CSSPropertyTransform)) { | |
144 player()->cancelAnimationOnCompositor(); | |
145 } | |
146 | |
147 double iteration = currentIteration(); | |
148 ASSERT(iteration >= 0); | |
149 OwnPtrWillBeRawPtr<WillBeHeapVector<RefPtrWillBeMember<Interpolation>>> inte rpolations = m_sampledEffect ? m_sampledEffect->mutableInterpolations() : nullpt r; | |
150 // FIXME: Handle iteration values which overflow int. | |
151 m_effect->sample(static_cast<int>(iteration), timeFraction(), iterationDurat ion(), interpolations); | |
152 if (m_sampledEffect) { | |
153 m_sampledEffect->setInterpolations(interpolations.release()); | |
154 } else if (interpolations && !interpolations->isEmpty()) { | |
155 OwnPtrWillBeRawPtr<SampledEffect> sampledEffect = SampledEffect::create( this, interpolations.release()); | |
156 m_sampledEffect = sampledEffect.get(); | |
157 ensureAnimationStack(m_target).add(sampledEffect.release()); | |
158 } else { | 166 } else { |
159 return; | 167 m_holdTime = nullValue(); |
160 } | 168 m_startTime = calculateStartTime(newCurrentTime); |
161 | 169 m_finished = false; |
162 m_target->setNeedsAnimationStyleRecalc(); | 170 outdated = true; |
163 if (m_target->isSVGElement()) | 171 } |
164 m_sampledEffect->applySVGUpdate(toSVGElement(*m_target)); | 172 |
165 } | 173 if (outdated) { |
166 | 174 setOutdated(); |
167 void Animation::clearEffects() | 175 } |
168 { | 176 } |
169 ASSERT(player()); | 177 |
170 ASSERT(m_sampledEffect); | 178 // Update timing to reflect updated animation clock due to tick |
171 | 179 void Animation::updateCurrentTimingState(TimingUpdateReason reason) |
172 m_sampledEffect->clear(); | 180 { |
173 m_sampledEffect = nullptr; | 181 if (m_held) { |
174 restartAnimationOnCompositor(); | 182 double newCurrentTime = m_holdTime; |
175 m_target->setNeedsAnimationStyleRecalc(); | 183 if (playStateInternal() == Finished && !isNull(m_startTime) && m_timelin e) { |
176 invalidate(); | 184 // Add hystersis due to floating point error accumulation |
177 } | 185 if (!limited(calculateCurrentTime() + 0.001 * m_playbackRate)) { |
178 | 186 // The current time became unlimited, eg. due to a backwards |
179 void Animation::updateChildrenAndEffects() const | 187 // seek of the timeline. |
180 { | 188 newCurrentTime = calculateCurrentTime(); |
181 if (!m_effect) | 189 } else if (!limited(m_holdTime)) { |
182 return; | 190 // The hold time became unlimited, eg. due to the source content |
183 if (isInEffect()) | 191 // becoming longer. |
184 const_cast<Animation*>(this)->applyEffects(); | 192 newCurrentTime = clampTo<double>(calculateCurrentTime(), 0, sour ceEnd()); |
185 else if (m_sampledEffect) | |
186 const_cast<Animation*>(this)->clearEffects(); | |
187 } | |
188 | |
189 double Animation::calculateTimeToEffectChange(bool forwards, double localTime, d ouble timeToNextIteration) const | |
190 { | |
191 const double start = startTimeInternal() + specifiedTiming().startDelay; | |
192 const double end = start + activeDurationInternal(); | |
193 | |
194 switch (phase()) { | |
195 case PhaseNone: | |
196 return std::numeric_limits<double>::infinity(); | |
197 case PhaseBefore: | |
198 ASSERT(start >= localTime); | |
199 return forwards | |
200 ? start - localTime | |
201 : std::numeric_limits<double>::infinity(); | |
202 case PhaseActive: | |
203 if (forwards) { | |
204 // Need service to apply fill / fire events. | |
205 const double timeToEnd = end - localTime; | |
206 if (requiresIterationEvents()) { | |
207 return std::min(timeToEnd, timeToNextIteration); | |
208 } | 193 } |
209 return timeToEnd; | 194 } |
210 } | 195 setCurrentTimeInternal(newCurrentTime, reason); |
211 return 0; | 196 } else if (limited(calculateCurrentTime())) { |
212 case PhaseAfter: | 197 m_held = true; |
213 ASSERT(localTime >= end); | 198 m_holdTime = m_playbackRate < 0 ? 0 : sourceEnd(); |
214 // If this Animation is still in effect then it will need to update | 199 } |
215 // when its parent goes out of effect. We have no way of knowing when | 200 } |
216 // that will be, however, so the parent will need to supply it. | 201 |
217 return forwards | 202 double Animation::startTime(bool& isNull) const |
218 ? std::numeric_limits<double>::infinity() | 203 { |
219 : localTime - end; | 204 double result = startTime(); |
205 isNull = std::isnan(result); | |
206 return result; | |
207 } | |
208 | |
209 double Animation::startTime() const | |
210 { | |
211 return m_startTime * 1000; | |
212 } | |
213 | |
214 double Animation::currentTime(bool& isNull) | |
215 { | |
216 double result = currentTime(); | |
217 isNull = std::isnan(result); | |
218 return result; | |
219 } | |
220 | |
221 double Animation::currentTime() | |
222 { | |
223 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand); | |
224 | |
225 if (m_currentTimePending || playStateInternal() == Idle) | |
226 return std::numeric_limits<double>::quiet_NaN(); | |
227 | |
228 return currentTimeInternal() * 1000; | |
229 } | |
230 | |
231 double Animation::currentTimeInternal() const | |
232 { | |
233 double result = m_held ? m_holdTime : calculateCurrentTime(); | |
234 #if ENABLE(ASSERT) | |
235 const_cast<Animation*>(this)->updateCurrentTimingState(TimingUpdateOnDemand) ; | |
236 ASSERT(result == (m_held ? m_holdTime : calculateCurrentTime())); | |
237 #endif | |
238 return result; | |
239 } | |
240 | |
241 double Animation::unlimitedCurrentTimeInternal() const | |
242 { | |
243 #if ENABLE(ASSERT) | |
244 currentTimeInternal(); | |
245 #endif | |
246 return playStateInternal() == Paused || isNull(m_startTime) | |
247 ? currentTimeInternal() | |
248 : calculateCurrentTime(); | |
249 } | |
250 | |
251 void Animation::preCommit(int compositorGroup, bool startOnCompositor) | |
252 { | |
253 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand, DoNotSetCompos itorPending); | |
254 | |
255 bool softChange = m_compositorState && (paused() || m_compositorState->playb ackRate != m_playbackRate); | |
256 bool hardChange = m_compositorState && (m_compositorState->sourceChanged || m_compositorState->startTime != m_startTime); | |
257 | |
258 // FIXME: softChange && !hardChange should generate a Pause/ThenStart, | |
259 // not a Cancel, but we can't communicate these to the compositor yet. | |
260 | |
261 bool changed = softChange || hardChange; | |
262 bool shouldCancel = (!playing() && m_compositorState) || changed; | |
263 bool shouldStart = playing() && (!m_compositorState || changed); | |
264 | |
265 if (shouldCancel) { | |
266 cancelAnimationOnCompositor(); | |
267 m_compositorState = nullptr; | |
268 } | |
269 | |
270 if (m_compositorState && m_compositorState->pendingAction == Start) { | |
271 // Still waiting for a start time. | |
272 return; | |
273 } | |
274 | |
275 ASSERT(!m_compositorState || !std::isnan(m_compositorState->startTime)); | |
276 | |
277 if (!shouldStart) { | |
278 m_currentTimePending = false; | |
279 } | |
280 | |
281 if (shouldStart) { | |
282 m_compositorGroup = compositorGroup; | |
283 if (startOnCompositor) { | |
284 if (isCandidateForAnimationOnCompositor()) | |
285 createCompositorPlayer(); | |
286 | |
287 if (maybeStartAnimationOnCompositor()) | |
288 m_compositorState = adoptPtr(new CompositorState(*this)); | |
289 else | |
290 cancelIncompatibleAnimationsOnCompositor(); | |
291 } | |
292 } | |
293 } | |
294 | |
295 void Animation::postCommit(double timelineTime) | |
296 { | |
297 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand, DoNotSetCompos itorPending); | |
298 | |
299 m_compositorPending = false; | |
300 | |
301 if (!m_compositorState || m_compositorState->pendingAction == None) | |
302 return; | |
303 | |
304 switch (m_compositorState->pendingAction) { | |
305 case Start: | |
306 if (!std::isnan(m_compositorState->startTime)) { | |
307 ASSERT(m_startTime == m_compositorState->startTime); | |
308 m_compositorState->pendingAction = None; | |
309 } | |
310 break; | |
311 case Pause: | |
312 case PauseThenStart: | |
313 ASSERT(std::isnan(m_startTime)); | |
314 m_compositorState->pendingAction = None; | |
315 setCurrentTimeInternal((timelineTime - m_compositorState->startTime) * m _playbackRate, TimingUpdateForAnimationFrame); | |
316 m_currentTimePending = false; | |
317 break; | |
220 default: | 318 default: |
221 ASSERT_NOT_REACHED(); | 319 ASSERT_NOT_REACHED(); |
320 } | |
321 } | |
322 | |
323 void Animation::notifyCompositorStartTime(double timelineTime) | |
324 { | |
325 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand, DoNotSetCompos itorPending); | |
326 | |
327 if (m_compositorState) { | |
328 ASSERT(m_compositorState->pendingAction == Start); | |
329 ASSERT(std::isnan(m_compositorState->startTime)); | |
330 | |
331 double initialCompositorHoldTime = m_compositorState->holdTime; | |
332 m_compositorState->pendingAction = None; | |
333 m_compositorState->startTime = timelineTime + currentTimeInternal() / -m _playbackRate; | |
334 | |
335 if (m_startTime == timelineTime) { | |
336 // The start time was set to the incoming compositor start time. | |
337 // Unlikely, but possible. | |
338 // FIXME: Depending on what changed above this might still be pendin g. | |
339 // Maybe... | |
340 m_currentTimePending = false; | |
341 return; | |
342 } | |
343 | |
344 if (!std::isnan(m_startTime) || currentTimeInternal() != initialComposit orHoldTime) { | |
345 // A new start time or current time was set while starting. | |
346 setCompositorPending(true); | |
347 return; | |
348 } | |
349 } | |
350 | |
351 notifyStartTime(timelineTime); | |
352 } | |
353 | |
354 void Animation::notifyStartTime(double timelineTime) | |
355 { | |
356 if (playing()) { | |
357 ASSERT(std::isnan(m_startTime)); | |
358 ASSERT(m_held); | |
359 | |
360 if (m_playbackRate == 0) { | |
361 setStartTimeInternal(timelineTime); | |
362 } else { | |
363 setStartTimeInternal(timelineTime + currentTimeInternal() / -m_playb ackRate); | |
364 } | |
365 | |
366 // FIXME: This avoids marking this animation as outdated needlessly when a start time | |
367 // is notified, but we should refactor how outdating works to avoid this . | |
368 m_outdated = false; | |
369 | |
370 m_currentTimePending = false; | |
371 } | |
372 } | |
373 | |
374 bool Animation::affects(const Element& element, CSSPropertyID property) const | |
375 { | |
376 if (!m_content || !m_content->isAnimation()) | |
377 return false; | |
378 | |
379 const KeyframeEffect* effect = toKeyframeEffect(m_content.get()); | |
380 return (effect->target() == &element) && effect->affects(PropertyHandle(prop erty)); | |
381 } | |
382 | |
383 double Animation::calculateStartTime(double currentTime) const | |
384 { | |
385 return m_timeline->effectiveTime() - currentTime / m_playbackRate; | |
386 } | |
387 | |
388 double Animation::calculateCurrentTime() const | |
389 { | |
390 if (isNull(m_startTime) || !m_timeline) | |
391 return 0; | |
392 return (m_timeline->effectiveTime() - m_startTime) * m_playbackRate; | |
393 } | |
394 | |
395 void Animation::setStartTime(double startTime) | |
396 { | |
397 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand); | |
398 | |
399 if (m_paused || playStateInternal() == Idle) | |
400 return; | |
401 if (startTime == m_startTime) | |
402 return; | |
403 | |
404 m_currentTimePending = false; | |
405 setStartTimeInternal(startTime / 1000); | |
406 } | |
407 | |
408 void Animation::setStartTimeInternal(double newStartTime) | |
409 { | |
410 ASSERT(!m_paused); | |
411 ASSERT(std::isfinite(newStartTime)); | |
412 ASSERT(newStartTime != m_startTime); | |
413 | |
414 bool hadStartTime = hasStartTime(); | |
415 double previousCurrentTime = currentTimeInternal(); | |
416 m_startTime = newStartTime; | |
417 if (m_held && m_playbackRate) { | |
418 // If held, the start time would still be derrived from the hold time. | |
419 // Force a new, limited, current time. | |
420 m_held = false; | |
421 double currentTime = calculateCurrentTime(); | |
422 if (m_playbackRate > 0 && currentTime > sourceEnd()) { | |
423 currentTime = sourceEnd(); | |
424 } else if (m_playbackRate < 0 && currentTime < 0) { | |
425 currentTime = 0; | |
426 } | |
427 setCurrentTimeInternal(currentTime, TimingUpdateOnDemand); | |
428 } | |
429 updateCurrentTimingState(TimingUpdateOnDemand); | |
430 double newCurrentTime = currentTimeInternal(); | |
431 | |
432 if (previousCurrentTime != newCurrentTime) { | |
433 setOutdated(); | |
434 } else if (!hadStartTime && m_timeline) { | |
435 // Even though this animation is not outdated, time to effect change is | |
436 // infinity until start time is set. | |
437 m_timeline->wake(); | |
438 } | |
439 } | |
440 | |
441 void Animation::setSource(AnimationEffect* newSource) | |
442 { | |
443 if (m_content == newSource) | |
444 return; | |
445 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand, SetCompositorP endingWithSourceChanged); | |
446 | |
447 double storedCurrentTime = currentTimeInternal(); | |
448 if (m_content) | |
449 m_content->detach(); | |
450 m_content = newSource; | |
451 if (newSource) { | |
452 // FIXME: This logic needs to be updated once groups are implemented | |
453 if (newSource->animation()) { | |
454 newSource->animation()->cancel(); | |
455 newSource->animation()->setSource(0); | |
456 } | |
457 newSource->attach(this); | |
458 setOutdated(); | |
459 } | |
460 setCurrentTimeInternal(storedCurrentTime, TimingUpdateOnDemand); | |
461 } | |
462 | |
463 const char* Animation::playStateString(AnimationPlayState playState) | |
464 { | |
465 switch (playState) { | |
466 case Idle: | |
467 return "idle"; | |
468 case Pending: | |
469 return "pending"; | |
470 case Running: | |
471 return "running"; | |
472 case Paused: | |
473 return "paused"; | |
474 case Finished: | |
475 return "finished"; | |
476 default: | |
477 ASSERT_NOT_REACHED(); | |
478 return ""; | |
479 } | |
480 } | |
481 | |
482 Animation::AnimationPlayState Animation::playStateInternal() const | |
483 { | |
484 return m_playState; | |
485 } | |
486 | |
487 Animation::AnimationPlayState Animation::calculatePlayState() | |
488 { | |
489 if (m_playState == Idle) | |
490 return Idle; | |
491 if (m_currentTimePending || (isNull(m_startTime) && !m_paused && m_playbackR ate != 0)) | |
492 return Pending; | |
493 if (m_paused) | |
494 return Paused; | |
495 if (limited()) | |
496 return Finished; | |
497 return Running; | |
498 } | |
499 | |
500 void Animation::pause() | |
501 { | |
502 if (m_paused) | |
503 return; | |
504 | |
505 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand); | |
506 | |
507 if (playing()) { | |
508 m_currentTimePending = true; | |
509 } | |
510 m_paused = true; | |
511 setCurrentTimeInternal(currentTimeInternal(), TimingUpdateOnDemand); | |
512 } | |
513 | |
514 void Animation::unpause() | |
515 { | |
516 if (!m_paused) | |
517 return; | |
518 | |
519 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand); | |
520 | |
521 m_currentTimePending = true; | |
522 unpauseInternal(); | |
523 } | |
524 | |
525 void Animation::unpauseInternal() | |
526 { | |
527 if (!m_paused) | |
528 return; | |
529 m_paused = false; | |
530 setCurrentTimeInternal(currentTimeInternal(), TimingUpdateOnDemand); | |
531 } | |
532 | |
533 void Animation::play() | |
534 { | |
535 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand); | |
536 | |
537 if (!playing()) | |
538 m_startTime = nullValue(); | |
539 | |
540 if (playStateInternal() == Idle) { | |
541 // We may not go into the pending state, but setting it to something oth er | |
542 // than Idle here will force an update. | |
543 ASSERT(isNull(m_startTime)); | |
544 m_playState = Pending; | |
545 m_held = true; | |
546 m_holdTime = 0; | |
547 } | |
548 | |
549 m_finished = false; | |
550 unpauseInternal(); | |
551 if (!m_content) | |
552 return; | |
553 double currentTime = this->currentTimeInternal(); | |
554 if (m_playbackRate > 0 && (currentTime < 0 || currentTime >= sourceEnd())) { | |
555 m_startTime = nullValue(); | |
556 setCurrentTimeInternal(0, TimingUpdateOnDemand); | |
557 } else if (m_playbackRate < 0 && (currentTime <= 0 || currentTime > sourceEn d())) { | |
558 m_startTime = nullValue(); | |
559 setCurrentTimeInternal(sourceEnd(), TimingUpdateOnDemand); | |
560 } | |
561 } | |
562 | |
563 void Animation::reverse() | |
564 { | |
565 if (!m_playbackRate) { | |
566 return; | |
567 } | |
568 | |
569 setPlaybackRateInternal(-m_playbackRate); | |
570 play(); | |
571 } | |
572 | |
573 void Animation::finish(ExceptionState& exceptionState) | |
574 { | |
575 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand); | |
576 | |
577 if (!m_playbackRate || playStateInternal() == Idle) { | |
578 return; | |
579 } | |
580 if (m_playbackRate > 0 && sourceEnd() == std::numeric_limits<double>::infini ty()) { | |
581 exceptionState.throwDOMException(InvalidStateError, "Animation has sourc e content whose end time is infinity."); | |
582 return; | |
583 } | |
584 | |
585 double newCurrentTime = m_playbackRate < 0 ? 0 : sourceEnd(); | |
586 setCurrentTimeInternal(newCurrentTime, TimingUpdateOnDemand); | |
587 if (!paused()) { | |
588 m_startTime = calculateStartTime(newCurrentTime); | |
589 } | |
590 | |
591 m_currentTimePending = false; | |
592 ASSERT(playStateInternal() != Idle); | |
593 ASSERT(limited()); | |
594 } | |
595 | |
596 ScriptPromise Animation::finished(ScriptState* scriptState) | |
597 { | |
598 if (!m_finishedPromise) { | |
599 m_finishedPromise = new AnimationPromise(scriptState->executionContext() , this, AnimationPromise::Finished); | |
600 if (playStateInternal() == Finished) | |
601 m_finishedPromise->resolve(this); | |
602 } | |
603 return m_finishedPromise->promise(scriptState->world()); | |
604 } | |
605 | |
606 ScriptPromise Animation::ready(ScriptState* scriptState) | |
607 { | |
608 if (!m_readyPromise) { | |
609 m_readyPromise = new AnimationPromise(scriptState->executionContext(), t his, AnimationPromise::Ready); | |
610 if (playStateInternal() != Pending) | |
611 m_readyPromise->resolve(this); | |
612 } | |
613 return m_readyPromise->promise(scriptState->world()); | |
614 } | |
615 | |
616 const AtomicString& Animation::interfaceName() const | |
617 { | |
618 return EventTargetNames::AnimationPlayer; | |
alancutter (OOO until 2018)
2015/05/05 01:04:51
Should this change?
dstockwell
2015/05/05 03:33:25
Yes, but we'll wait for the spec update.
| |
619 } | |
620 | |
621 ExecutionContext* Animation::executionContext() const | |
622 { | |
623 return ActiveDOMObject::executionContext(); | |
624 } | |
625 | |
626 bool Animation::hasPendingActivity() const | |
627 { | |
628 return m_pendingFinishedEvent || (!m_finished && hasEventListeners(EventType Names::finish)); | |
629 } | |
630 | |
631 void Animation::stop() | |
632 { | |
633 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand); | |
634 | |
635 m_finished = true; | |
636 m_pendingFinishedEvent = nullptr; | |
637 } | |
638 | |
639 bool Animation::dispatchEvent(PassRefPtrWillBeRawPtr<Event> event) | |
640 { | |
641 if (m_pendingFinishedEvent == event) | |
642 m_pendingFinishedEvent = nullptr; | |
643 return EventTargetWithInlineData::dispatchEvent(event); | |
644 } | |
645 | |
646 double Animation::playbackRate() const | |
647 { | |
648 return m_playbackRate; | |
649 } | |
650 | |
651 void Animation::setPlaybackRate(double playbackRate) | |
652 { | |
653 if (playbackRate == m_playbackRate) | |
654 return; | |
655 | |
656 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand); | |
657 | |
658 setPlaybackRateInternal(playbackRate); | |
659 } | |
660 | |
661 void Animation::setPlaybackRateInternal(double playbackRate) | |
662 { | |
663 ASSERT(std::isfinite(playbackRate)); | |
664 ASSERT(playbackRate != m_playbackRate); | |
665 | |
666 if (!limited() && !paused() && hasStartTime()) | |
667 m_currentTimePending = true; | |
668 | |
669 double storedCurrentTime = currentTimeInternal(); | |
670 if ((m_playbackRate < 0 && playbackRate >= 0) || (m_playbackRate > 0 && play backRate <= 0)) | |
671 m_finished = false; | |
672 | |
673 m_playbackRate = playbackRate; | |
674 m_startTime = std::numeric_limits<double>::quiet_NaN(); | |
675 setCurrentTimeInternal(storedCurrentTime, TimingUpdateOnDemand); | |
676 } | |
677 | |
678 void Animation::setOutdated() | |
679 { | |
680 m_outdated = true; | |
681 if (m_timeline) | |
682 m_timeline->setOutdatedAnimation(this); | |
683 } | |
684 | |
685 bool Animation::canStartAnimationOnCompositor() const | |
686 { | |
687 // FIXME: Timeline playback rates should be compositable | |
688 if (m_playbackRate == 0 || (std::isinf(sourceEnd()) && m_playbackRate < 0) | | (timeline() && timeline()->playbackRate() != 1)) | |
689 return false; | |
690 | |
691 return m_timeline && m_content && m_content->isAnimation() && playing(); | |
692 } | |
693 | |
694 bool Animation::isCandidateForAnimationOnCompositor() const | |
695 { | |
696 if (!canStartAnimationOnCompositor()) | |
697 return false; | |
698 | |
699 return toKeyframeEffect(m_content.get())->isCandidateForAnimationOnComposito r(m_playbackRate); | |
700 } | |
701 | |
702 bool Animation::maybeStartAnimationOnCompositor() | |
703 { | |
704 if (!canStartAnimationOnCompositor()) | |
705 return false; | |
706 | |
707 bool reversed = m_playbackRate < 0; | |
708 | |
709 double startTime = timeline()->zeroTime() + startTimeInternal(); | |
710 if (reversed) { | |
711 startTime -= sourceEnd() / fabs(m_playbackRate); | |
712 } | |
713 | |
714 double timeOffset = 0; | |
715 if (std::isnan(startTime)) { | |
716 timeOffset = reversed ? sourceEnd() - currentTimeInternal() : currentTim eInternal(); | |
717 timeOffset = timeOffset / fabs(m_playbackRate); | |
718 } | |
719 ASSERT(m_compositorGroup != 0); | |
720 return toKeyframeEffect(m_content.get())->maybeStartAnimationOnCompositor(m_ compositorGroup, startTime, timeOffset, m_playbackRate); | |
721 } | |
722 | |
723 void Animation::setCompositorPending(bool sourceChanged) | |
724 { | |
725 // FIXME: KeyframeEffect could notify this directly? | |
726 if (!hasActiveAnimationsOnCompositor()) { | |
727 destroyCompositorPlayer(); | |
728 m_compositorState.release(); | |
729 } | |
730 if (sourceChanged && m_compositorState) { | |
731 m_compositorState->sourceChanged = true; | |
732 } | |
733 if (m_compositorPending || m_isPausedForTesting) { | |
734 return; | |
735 } | |
736 | |
737 if (sourceChanged || !m_compositorState | |
738 || !playing() || m_compositorState->playbackRate != m_playbackRate | |
739 || m_compositorState->startTime != m_startTime) { | |
740 m_compositorPending = true; | |
741 ASSERT(timeline()); | |
742 ASSERT(timeline()->document()); | |
743 timeline()->document()->compositorPendingAnimations().add(this); | |
744 } | |
745 } | |
746 | |
747 void Animation::cancelAnimationOnCompositor() | |
748 { | |
749 if (hasActiveAnimationsOnCompositor()) | |
750 toKeyframeEffect(m_content.get())->cancelAnimationOnCompositor(); | |
751 | |
752 destroyCompositorPlayer(); | |
753 } | |
754 | |
755 void Animation::restartAnimationOnCompositor() | |
756 { | |
757 if (hasActiveAnimationsOnCompositor()) | |
758 toKeyframeEffect(m_content.get())->restartAnimationOnCompositor(); | |
759 } | |
760 | |
761 void Animation::cancelIncompatibleAnimationsOnCompositor() | |
762 { | |
763 if (m_content && m_content->isAnimation()) | |
764 toKeyframeEffect(m_content.get())->cancelIncompatibleAnimationsOnComposi tor(); | |
765 } | |
766 | |
767 bool Animation::hasActiveAnimationsOnCompositor() | |
768 { | |
769 if (!m_content || !m_content->isAnimation()) | |
770 return false; | |
771 | |
772 return toKeyframeEffect(m_content.get())->hasActiveAnimationsOnCompositor(); | |
773 } | |
774 | |
775 bool Animation::update(TimingUpdateReason reason) | |
776 { | |
777 if (!m_timeline) | |
778 return false; | |
779 | |
780 PlayStateUpdateScope updateScope(*this, reason, DoNotSetCompositorPending); | |
781 | |
782 m_outdated = false; | |
783 bool idle = playStateInternal() == Idle; | |
784 | |
785 if (m_content) { | |
786 double inheritedTime = idle || isNull(m_timeline->currentTimeInternal()) ? nullValue() : currentTimeInternal(); | |
787 // Special case for end-exclusivity when playing backwards. | |
788 if (inheritedTime == 0 && m_playbackRate < 0) | |
789 inheritedTime = -1; | |
790 m_content->updateInheritedTime(inheritedTime, reason); | |
791 } | |
792 | |
793 if ((idle || limited()) && !m_finished) { | |
794 if (reason == TimingUpdateForAnimationFrame && (idle || hasStartTime())) { | |
795 const AtomicString& eventType = EventTypeNames::finish; | |
796 if (executionContext() && hasEventListeners(eventType)) { | |
797 double eventCurrentTime = currentTimeInternal() * 1000; | |
798 m_pendingFinishedEvent = AnimationPlayerEvent::create(eventType, eventCurrentTime, timeline()->currentTime()); | |
799 m_pendingFinishedEvent->setTarget(this); | |
800 m_pendingFinishedEvent->setCurrentTarget(this); | |
801 m_timeline->document()->enqueueAnimationFrameEvent(m_pendingFini shedEvent); | |
802 } | |
803 m_finished = true; | |
804 } | |
805 } | |
806 ASSERT(!m_outdated); | |
807 return !m_finished; | |
808 } | |
809 | |
810 double Animation::timeToEffectChange() | |
811 { | |
812 ASSERT(!m_outdated); | |
813 if (m_held || !hasStartTime()) | |
222 return std::numeric_limits<double>::infinity(); | 814 return std::numeric_limits<double>::infinity(); |
223 } | 815 if (!m_content) |
224 } | 816 return -currentTimeInternal() / m_playbackRate; |
225 | 817 double result = m_playbackRate > 0 |
226 #if !ENABLE(OILPAN) | 818 ? m_content->timeToForwardsEffectChange() / m_playbackRate |
227 void Animation::notifyElementDestroyed() | 819 : m_content->timeToReverseEffectChange() / -m_playbackRate; |
228 { | 820 return !hasActiveAnimationsOnCompositor() && m_content->phase() == Animation Effect::PhaseActive |
229 // If our player is kept alive just by the sampledEffect, we might get our | 821 ? 0 |
230 // destructor called when we call SampledEffect::clear(), so we need to | 822 : result; |
231 // clear m_sampledEffect first. | 823 } |
232 m_target = nullptr; | 824 |
233 clearEventDelegate(); | 825 void Animation::cancel() |
234 SampledEffect* sampledEffect = m_sampledEffect; | 826 { |
235 m_sampledEffect = nullptr; | 827 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand); |
236 if (sampledEffect) | 828 |
237 sampledEffect->clear(); | 829 if (playStateInternal() == Idle) |
238 } | 830 return; |
831 | |
832 m_holdTime = currentTimeInternal(); | |
833 m_held = true; | |
834 // TODO | |
835 m_playState = Idle; | |
836 m_startTime = nullValue(); | |
837 m_currentTimePending = false; | |
838 | |
839 InspectorInstrumentation::didCancelAnimation(timeline()->document(), this); | |
840 } | |
841 | |
842 void Animation::beginUpdatingState() | |
843 { | |
844 // Nested calls are not allowed! | |
845 ASSERT(!m_stateIsBeingUpdated); | |
846 m_stateIsBeingUpdated = true; | |
847 } | |
848 | |
849 void Animation::endUpdatingState() | |
850 { | |
851 ASSERT(m_stateIsBeingUpdated); | |
852 m_stateIsBeingUpdated = false; | |
853 } | |
854 | |
855 void Animation::createCompositorPlayer() | |
856 { | |
857 if (RuntimeEnabledFeatures::compositorAnimationTimelinesEnabled() && !m_comp ositorPlayer && Platform::current()->compositorSupport()) { | |
858 m_compositorPlayer = adoptPtr(Platform::current()->compositorSupport()-> createAnimationPlayer()); | |
859 ASSERT(m_compositorPlayer); | |
860 m_compositorPlayer->setAnimationDelegate(this); | |
861 attachCompositorTimeline(); | |
862 } | |
863 | |
864 attachCompositedLayers(); | |
865 } | |
866 | |
867 void Animation::destroyCompositorPlayer() | |
868 { | |
869 detachCompositedLayers(); | |
870 | |
871 if (m_compositorPlayer) { | |
872 detachCompositorTimeline(); | |
873 m_compositorPlayer->setAnimationDelegate(nullptr); | |
874 } | |
875 m_compositorPlayer.clear(); | |
876 } | |
877 | |
878 void Animation::attachCompositorTimeline() | |
879 { | |
880 if (m_compositorPlayer) { | |
881 WebCompositorAnimationTimeline* timeline = m_timeline ? m_timeline->comp ositorTimeline() : nullptr; | |
882 if (timeline) | |
883 timeline->playerAttached(*this); | |
884 } | |
885 } | |
886 | |
887 void Animation::detachCompositorTimeline() | |
888 { | |
889 if (m_compositorPlayer) { | |
890 WebCompositorAnimationTimeline* timeline = m_timeline ? m_timeline->comp ositorTimeline() : nullptr; | |
891 if (timeline) | |
892 timeline->playerDestroyed(*this); | |
893 } | |
894 } | |
895 | |
896 void Animation::attachCompositedLayers() | |
897 { | |
898 if (!RuntimeEnabledFeatures::compositorAnimationTimelinesEnabled() || !m_com positorPlayer) | |
899 return; | |
900 | |
901 ASSERT(m_content); | |
902 ASSERT(m_content->isAnimation()); | |
903 | |
904 if (toKeyframeEffect(m_content.get())->canAttachCompositedLayers()) | |
905 toKeyframeEffect(m_content.get())->attachCompositedLayers(); | |
906 } | |
907 | |
908 void Animation::detachCompositedLayers() | |
909 { | |
910 if (m_compositorPlayer && m_compositorPlayer->isLayerAttached()) | |
911 m_compositorPlayer->detachLayer(); | |
912 } | |
913 | |
914 void Animation::notifyAnimationStarted(double monotonicTime, int group) | |
915 { | |
916 ASSERT(RuntimeEnabledFeatures::compositorAnimationTimelinesEnabled()); | |
917 timeline()->document()->compositorPendingAnimations().notifyCompositorAnimat ionStarted(monotonicTime, group); | |
918 } | |
919 | |
920 Animation::PlayStateUpdateScope::PlayStateUpdateScope(Animation& animation, Timi ngUpdateReason reason, CompositorPendingChange compositorPendingChange) | |
921 : m_animation(animation) | |
922 , m_initialPlayState(m_animation->playStateInternal()) | |
923 , m_compositorPendingChange(compositorPendingChange) | |
924 { | |
925 m_animation->beginUpdatingState(); | |
926 m_animation->updateCurrentTimingState(reason); | |
927 } | |
928 | |
929 Animation::PlayStateUpdateScope::~PlayStateUpdateScope() | |
930 { | |
931 AnimationPlayState oldPlayState = m_initialPlayState; | |
932 AnimationPlayState newPlayState = m_animation->calculatePlayState(); | |
933 | |
934 m_animation->m_playState = newPlayState; | |
935 if (oldPlayState != newPlayState) { | |
936 bool wasActive = oldPlayState == Pending || oldPlayState == Running; | |
937 bool isActive = newPlayState == Pending || newPlayState == Running; | |
938 if (!wasActive && isActive) | |
939 TRACE_EVENT_NESTABLE_ASYNC_BEGIN1("blink.animations," TRACE_DISABLED _BY_DEFAULT("devtools.timeline"), "KeyframeEffect", m_animation, "data", Inspect orAnimationEvent::data(*m_animation)); | |
940 else if (wasActive && !isActive) | |
941 TRACE_EVENT_NESTABLE_ASYNC_END1("blink.animations," TRACE_DISABLED_B Y_DEFAULT("devtools.timeline"), "KeyframeEffect", m_animation, "endData", Inspec torAnimationStateEvent::data(*m_animation)); | |
942 else | |
943 TRACE_EVENT_NESTABLE_ASYNC_INSTANT1("blink.animations," TRACE_DISABL ED_BY_DEFAULT("devtools.timeline"), "KeyframeEffect", m_animation, "data", Inspe ctorAnimationStateEvent::data(*m_animation)); | |
944 } | |
945 | |
946 // Ordering is important, the ready promise should resolve/reject before | |
947 // the finished promise. | |
948 if (m_animation->m_readyPromise && newPlayState != oldPlayState) { | |
949 if (newPlayState == Idle) { | |
950 if (m_animation->m_readyPromise->state() == AnimationPromise::Pendin g) { | |
951 m_animation->m_readyPromise->reject(DOMException::create(AbortEr ror)); | |
952 } | |
953 m_animation->m_readyPromise->reset(); | |
954 m_animation->m_readyPromise->resolve(m_animation); | |
955 } else if (oldPlayState == Pending) { | |
956 m_animation->m_readyPromise->resolve(m_animation); | |
957 } else if (newPlayState == Pending) { | |
958 ASSERT(m_animation->m_readyPromise->state() != AnimationPromise::Pen ding); | |
959 m_animation->m_readyPromise->reset(); | |
960 } | |
961 } | |
962 | |
963 if (m_animation->m_finishedPromise && newPlayState != oldPlayState) { | |
964 if (newPlayState == Idle) { | |
965 if (m_animation->m_finishedPromise->state() == AnimationPromise::Pen ding) { | |
966 m_animation->m_finishedPromise->reject(DOMException::create(Abor tError)); | |
967 } | |
968 m_animation->m_finishedPromise->reset(); | |
969 } else if (newPlayState == Finished) { | |
970 m_animation->m_finishedPromise->resolve(m_animation); | |
971 } else if (oldPlayState == Finished) { | |
972 m_animation->m_finishedPromise->reset(); | |
973 } | |
974 } | |
975 | |
976 if (oldPlayState != newPlayState && (oldPlayState == Idle || newPlayState == Idle)) { | |
977 m_animation->setOutdated(); | |
978 } | |
979 | |
980 #if ENABLE(ASSERT) | |
981 // Verify that current time is up to date. | |
982 m_animation->currentTimeInternal(); | |
239 #endif | 983 #endif |
240 | 984 |
241 bool Animation::isCandidateForAnimationOnCompositor(double playerPlaybackRate) c onst | 985 switch (m_compositorPendingChange) { |
242 { | 986 case SetCompositorPending: |
243 if (!effect() | 987 m_animation->setCompositorPending(); |
244 || !m_target | 988 break; |
245 || (m_target->computedStyle() && m_target->computedStyle()->hasMotionPat h())) | 989 case SetCompositorPendingWithSourceChanged: |
246 return false; | 990 m_animation->setCompositorPending(true); |
247 | 991 break; |
248 return CompositorAnimations::instance()->isCandidateForAnimationOnCompositor (specifiedTiming(), *m_target, player(), *effect(), playerPlaybackRate); | 992 case DoNotSetCompositorPending: |
249 } | 993 break; |
250 | 994 default: |
251 bool Animation::maybeStartAnimationOnCompositor(int group, double startTime, dou ble currentTime, double playerPlaybackRate) | 995 ASSERT_NOT_REACHED(); |
252 { | 996 break; |
253 ASSERT(!hasActiveAnimationsOnCompositor()); | 997 } |
254 if (!isCandidateForAnimationOnCompositor(playerPlaybackRate)) | 998 m_animation->endUpdatingState(); |
255 return false; | 999 |
256 if (!CompositorAnimations::instance()->canStartAnimationOnCompositor(*m_targ et)) | 1000 if (oldPlayState != newPlayState && newPlayState == Running) |
257 return false; | 1001 InspectorInstrumentation::didCreateAnimation(m_animation->timeline()->do cument(), m_animation); |
258 if (!CompositorAnimations::instance()->startAnimationOnCompositor(*m_target, group, startTime, currentTime, specifiedTiming(), *player(), *effect(), m_compo sitorAnimationIds, playerPlaybackRate)) | 1002 } |
259 return false; | 1003 |
260 ASSERT(!m_compositorAnimationIds.isEmpty()); | 1004 bool Animation::addEventListener(const AtomicString& eventType, PassRefPtr<Event Listener> listener, bool useCapture) |
261 return true; | 1005 { |
262 } | 1006 if (eventType == EventTypeNames::finish) |
263 | 1007 UseCounter::count(executionContext(), UseCounter::AnimationFinishEvent); |
264 bool Animation::hasActiveAnimationsOnCompositor() const | 1008 return EventTargetWithInlineData::addEventListener(eventType, listener, useC apture); |
265 { | 1009 } |
266 return !m_compositorAnimationIds.isEmpty(); | 1010 |
267 } | 1011 void Animation::pauseForTesting(double pauseTime) |
268 | 1012 { |
269 bool Animation::hasActiveAnimationsOnCompositor(CSSPropertyID property) const | 1013 RELEASE_ASSERT(!paused()); |
270 { | 1014 setCurrentTimeInternal(pauseTime, TimingUpdateOnDemand); |
271 return hasActiveAnimationsOnCompositor() && affects(PropertyHandle(property) ); | 1015 if (hasActiveAnimationsOnCompositor()) |
272 } | 1016 toKeyframeEffect(m_content.get())->pauseAnimationForTestingOnCompositor( currentTimeInternal()); |
273 | 1017 m_isPausedForTesting = true; |
274 bool Animation::affects(PropertyHandle property) const | 1018 pause(); |
275 { | |
276 return m_effect && m_effect->affects(property); | |
277 } | |
278 | |
279 bool Animation::cancelAnimationOnCompositor() | |
280 { | |
281 // FIXME: cancelAnimationOnCompositor is called from withins style recalc. | |
282 // This queries compositingState, which is not necessarily up to date. | |
283 // https://code.google.com/p/chromium/issues/detail?id=339847 | |
284 DisableCompositingQueryAsserts disabler; | |
285 if (!hasActiveAnimationsOnCompositor()) | |
286 return false; | |
287 if (!m_target || !m_target->layoutObject()) | |
288 return false; | |
289 ASSERT(player()); | |
290 for (const auto& compositorAnimationId : m_compositorAnimationIds) | |
291 CompositorAnimations::instance()->cancelAnimationOnCompositor(*m_target, *player(), compositorAnimationId); | |
292 m_compositorAnimationIds.clear(); | |
293 return true; | |
294 } | |
295 | |
296 void Animation::restartAnimationOnCompositor() | |
297 { | |
298 if (cancelAnimationOnCompositor()) | |
299 player()->setCompositorPending(true); | |
300 } | |
301 | |
302 void Animation::cancelIncompatibleAnimationsOnCompositor() | |
303 { | |
304 if (m_target && player() && effect()) | |
305 CompositorAnimations::instance()->cancelIncompatibleAnimationsOnComposit or(*m_target, *player(), *effect()); | |
306 } | |
307 | |
308 void Animation::pauseAnimationForTestingOnCompositor(double pauseTime) | |
309 { | |
310 ASSERT(hasActiveAnimationsOnCompositor()); | |
311 if (!m_target || !m_target->layoutObject()) | |
312 return; | |
313 ASSERT(player()); | |
314 for (const auto& compositorAnimationId : m_compositorAnimationIds) | |
315 CompositorAnimations::instance()->pauseAnimationForTestingOnCompositor(* m_target, *player(), compositorAnimationId, pauseTime); | |
316 } | |
317 | |
318 bool Animation::canAttachCompositedLayers() const | |
319 { | |
320 if (!m_target || !player()) | |
321 return false; | |
322 | |
323 return CompositorAnimations::instance()->canAttachCompositedLayers(*m_target , *player()); | |
324 } | |
325 | |
326 void Animation::attachCompositedLayers() | |
327 { | |
328 ASSERT(m_target); | |
329 ASSERT(player()); | |
330 CompositorAnimations::instance()->attachCompositedLayers(*m_target, *player( )); | |
331 } | 1019 } |
332 | 1020 |
333 DEFINE_TRACE(Animation) | 1021 DEFINE_TRACE(Animation) |
334 { | 1022 { |
335 visitor->trace(m_target); | 1023 visitor->trace(m_content); |
336 visitor->trace(m_effect); | 1024 visitor->trace(m_timeline); |
337 visitor->trace(m_sampledEffect); | 1025 visitor->trace(m_pendingFinishedEvent); |
338 AnimationNode::trace(visitor); | 1026 visitor->trace(m_finishedPromise); |
339 } | 1027 visitor->trace(m_readyPromise); |
340 | 1028 EventTargetWithInlineData::trace(visitor); |
341 } // namespace blink | 1029 ActiveDOMObject::trace(visitor); |
1030 } | |
1031 | |
1032 } // namespace | |
OLD | NEW |