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; |
| 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 |