OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2013 Google Inc. All rights reserved. | |
3 * | |
4 * Redistribution and use in source and binary forms, with or without | |
5 * modification, are permitted provided that the following conditions are | |
6 * met: | |
7 * | |
8 * * Redistributions of source code must retain the above copyright | |
9 * notice, this list of conditions and the following disclaimer. | |
10 * * Redistributions in binary form must reproduce the above | |
11 * copyright notice, this list of conditions and the following disclaimer | |
12 * in the documentation and/or other materials provided with the | |
13 * distribution. | |
14 * * Neither the name of Google Inc. nor the names of its | |
15 * contributors may be used to endorse or promote products derived from | |
16 * this software without specific prior written permission. | |
17 * | |
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
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. | |
29 */ | |
30 | |
31 #include "config.h" | |
32 #include "core/animation/AnimationPlayer.h" | |
33 | |
34 #include "core/animation/Animation.h" | |
35 #include "core/animation/AnimationTimeline.h" | |
36 #include "core/dom/Document.h" | |
37 #include "core/dom/ExceptionCode.h" | |
38 #include "core/events/AnimationPlayerEvent.h" | |
39 #include "core/frame/UseCounter.h" | |
40 #include "core/inspector/InspectorInstrumentation.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" | |
48 | |
49 namespace blink { | |
50 | |
51 namespace { | |
52 | |
53 static unsigned nextSequenceNumber() | |
54 { | |
55 static unsigned next = 0; | |
56 return ++next; | |
57 } | |
58 | |
59 } | |
60 | |
61 PassRefPtrWillBeRawPtr<AnimationPlayer> AnimationPlayer::create(AnimationNode* s
ource, AnimationTimeline* timeline) | |
62 { | |
63 if (!timeline) { | |
64 // FIXME: Support creating players without a timeline. | |
65 return nullptr; | |
66 } | |
67 | |
68 RefPtrWillBeRawPtr<AnimationPlayer> player = adoptRefWillBeNoop(new Animatio
nPlayer(timeline->document()->contextDocument().get(), *timeline, source)); | |
69 player->suspendIfNeeded(); | |
70 | |
71 if (timeline) { | |
72 timeline->playerAttached(*player); | |
73 player->attachCompositorTimeline(); | |
74 } | |
75 | |
76 return player.release(); | |
77 } | |
78 | |
79 AnimationPlayer::AnimationPlayer(ExecutionContext* executionContext, AnimationTi
meline& timeline, AnimationNode* content) | |
80 : ActiveDOMObject(executionContext) | |
81 , m_playState(Idle) | |
82 , m_playbackRate(1) | |
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->player()) { | |
101 m_content->player()->cancel(); | |
102 m_content->player()->setSource(0); | |
103 } | |
104 m_content->attach(this); | |
105 } | |
106 } | |
107 | |
108 AnimationPlayer::~AnimationPlayer() | |
109 { | |
110 #if !ENABLE(OILPAN) | |
111 if (m_content) | |
112 m_content->detach(); | |
113 if (m_timeline) | |
114 m_timeline->playerDestroyed(this); | |
115 #endif | |
116 | |
117 destroyCompositorPlayer(); | |
118 } | |
119 | |
120 #if !ENABLE(OILPAN) | |
121 void AnimationPlayer::detachFromTimeline() | |
122 { | |
123 dispose(); | |
124 m_timeline = nullptr; | |
125 } | |
126 #endif | |
127 | |
128 double AnimationPlayer::sourceEnd() const | |
129 { | |
130 return m_content ? m_content->endTimeInternal() : 0; | |
131 } | |
132 | |
133 bool AnimationPlayer::limited(double currentTime) const | |
134 { | |
135 return (m_playbackRate < 0 && currentTime <= 0) || (m_playbackRate > 0 && cu
rrentTime >= sourceEnd()); | |
136 } | |
137 | |
138 void AnimationPlayer::setCurrentTime(double newCurrentTime) | |
139 { | |
140 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand); | |
141 | |
142 m_currentTimePending = false; | |
143 setCurrentTimeInternal(newCurrentTime / 1000, TimingUpdateOnDemand); | |
144 | |
145 if (calculatePlayState() == Finished) | |
146 m_startTime = calculateStartTime(newCurrentTime); | |
147 } | |
148 | |
149 void AnimationPlayer::setCurrentTimeInternal(double newCurrentTime, TimingUpdate
Reason reason) | |
150 { | |
151 ASSERT(std::isfinite(newCurrentTime)); | |
152 | |
153 bool oldHeld = m_held; | |
154 bool outdated = false; | |
155 bool isLimited = limited(newCurrentTime); | |
156 m_held = m_paused || !m_playbackRate || isLimited || std::isnan(m_startTime)
; | |
157 if (m_held) { | |
158 if (!oldHeld || m_holdTime != newCurrentTime) | |
159 outdated = true; | |
160 m_holdTime = newCurrentTime; | |
161 if (m_paused || !m_playbackRate) { | |
162 m_startTime = nullValue(); | |
163 } else if (isLimited && std::isnan(m_startTime) && reason == TimingUpdat
eForAnimationFrame) { | |
164 m_startTime = calculateStartTime(newCurrentTime); | |
165 } | |
166 } else { | |
167 m_holdTime = nullValue(); | |
168 m_startTime = calculateStartTime(newCurrentTime); | |
169 m_finished = false; | |
170 outdated = true; | |
171 } | |
172 | |
173 if (outdated) { | |
174 setOutdated(); | |
175 } | |
176 } | |
177 | |
178 // Update timing to reflect updated animation clock due to tick | |
179 void AnimationPlayer::updateCurrentTimingState(TimingUpdateReason reason) | |
180 { | |
181 if (m_held) { | |
182 double newCurrentTime = m_holdTime; | |
183 if (playStateInternal() == Finished && !isNull(m_startTime) && m_timelin
e) { | |
184 // Add hystersis due to floating point error accumulation | |
185 if (!limited(calculateCurrentTime() + 0.001 * m_playbackRate)) { | |
186 // The current time became unlimited, eg. due to a backwards | |
187 // seek of the timeline. | |
188 newCurrentTime = calculateCurrentTime(); | |
189 } else if (!limited(m_holdTime)) { | |
190 // The hold time became unlimited, eg. due to the source content | |
191 // becoming longer. | |
192 newCurrentTime = clampTo<double>(calculateCurrentTime(), 0, sour
ceEnd()); | |
193 } | |
194 } | |
195 setCurrentTimeInternal(newCurrentTime, reason); | |
196 } else if (limited(calculateCurrentTime())) { | |
197 m_held = true; | |
198 m_holdTime = m_playbackRate < 0 ? 0 : sourceEnd(); | |
199 } | |
200 } | |
201 | |
202 double AnimationPlayer::startTime(bool& isNull) const | |
203 { | |
204 double result = startTime(); | |
205 isNull = std::isnan(result); | |
206 return result; | |
207 } | |
208 | |
209 double AnimationPlayer::startTime() const | |
210 { | |
211 return m_startTime * 1000; | |
212 } | |
213 | |
214 double AnimationPlayer::currentTime(bool& isNull) | |
215 { | |
216 double result = currentTime(); | |
217 isNull = std::isnan(result); | |
218 return result; | |
219 } | |
220 | |
221 double AnimationPlayer::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 AnimationPlayer::currentTimeInternal() const | |
232 { | |
233 double result = m_held ? m_holdTime : calculateCurrentTime(); | |
234 #if ENABLE(ASSERT) | |
235 const_cast<AnimationPlayer*>(this)->updateCurrentTimingState(TimingUpdateOnD
emand); | |
236 ASSERT(result == (m_held ? m_holdTime : calculateCurrentTime())); | |
237 #endif | |
238 return result; | |
239 } | |
240 | |
241 double AnimationPlayer::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 AnimationPlayer::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 AnimationPlayer::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; | |
318 default: | |
319 ASSERT_NOT_REACHED(); | |
320 } | |
321 } | |
322 | |
323 void AnimationPlayer::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 AnimationPlayer::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 player 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 AnimationPlayer::affects(const Element& element, CSSPropertyID property) co
nst | |
375 { | |
376 if (!m_content || !m_content->isAnimation()) | |
377 return false; | |
378 | |
379 const Animation* animation = toAnimation(m_content.get()); | |
380 return (animation->target() == &element) && animation->affects(PropertyHandl
e(property)); | |
381 } | |
382 | |
383 double AnimationPlayer::calculateStartTime(double currentTime) const | |
384 { | |
385 return m_timeline->effectiveTime() - currentTime / m_playbackRate; | |
386 } | |
387 | |
388 double AnimationPlayer::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 AnimationPlayer::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 AnimationPlayer::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 player is not outdated, time to effect change is | |
436 // infinity until start time is set. | |
437 m_timeline->wake(); | |
438 } | |
439 } | |
440 | |
441 void AnimationPlayer::setSource(AnimationNode* 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->player()) { | |
454 newSource->player()->cancel(); | |
455 newSource->player()->setSource(0); | |
456 } | |
457 newSource->attach(this); | |
458 setOutdated(); | |
459 } | |
460 setCurrentTimeInternal(storedCurrentTime, TimingUpdateOnDemand); | |
461 } | |
462 | |
463 const char* AnimationPlayer::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 AnimationPlayer::AnimationPlayState AnimationPlayer::playStateInternal() const | |
483 { | |
484 return m_playState; | |
485 } | |
486 | |
487 AnimationPlayer::AnimationPlayState AnimationPlayer::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 AnimationPlayer::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 AnimationPlayer::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 AnimationPlayer::unpauseInternal() | |
526 { | |
527 if (!m_paused) | |
528 return; | |
529 m_paused = false; | |
530 setCurrentTimeInternal(currentTimeInternal(), TimingUpdateOnDemand); | |
531 } | |
532 | |
533 void AnimationPlayer::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 AnimationPlayer::reverse() | |
564 { | |
565 if (!m_playbackRate) { | |
566 return; | |
567 } | |
568 | |
569 setPlaybackRateInternal(-m_playbackRate); | |
570 play(); | |
571 } | |
572 | |
573 void AnimationPlayer::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, "AnimationPlayer has
source 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 AnimationPlayer::finished(ScriptState* scriptState) | |
597 { | |
598 if (!m_finishedPromise) { | |
599 m_finishedPromise = new AnimationPlayerPromise(scriptState->executionCon
text(), this, AnimationPlayerPromise::Finished); | |
600 if (playStateInternal() == Finished) | |
601 m_finishedPromise->resolve(this); | |
602 } | |
603 return m_finishedPromise->promise(scriptState->world()); | |
604 } | |
605 | |
606 ScriptPromise AnimationPlayer::ready(ScriptState* scriptState) | |
607 { | |
608 if (!m_readyPromise) { | |
609 m_readyPromise = new AnimationPlayerPromise(scriptState->executionContex
t(), this, AnimationPlayerPromise::Ready); | |
610 if (playStateInternal() != Pending) | |
611 m_readyPromise->resolve(this); | |
612 } | |
613 return m_readyPromise->promise(scriptState->world()); | |
614 } | |
615 | |
616 const AtomicString& AnimationPlayer::interfaceName() const | |
617 { | |
618 return EventTargetNames::AnimationPlayer; | |
619 } | |
620 | |
621 ExecutionContext* AnimationPlayer::executionContext() const | |
622 { | |
623 return ActiveDOMObject::executionContext(); | |
624 } | |
625 | |
626 bool AnimationPlayer::hasPendingActivity() const | |
627 { | |
628 return m_pendingFinishedEvent || (!m_finished && hasEventListeners(EventType
Names::finish)); | |
629 } | |
630 | |
631 void AnimationPlayer::stop() | |
632 { | |
633 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand); | |
634 | |
635 m_finished = true; | |
636 m_pendingFinishedEvent = nullptr; | |
637 } | |
638 | |
639 bool AnimationPlayer::dispatchEvent(PassRefPtrWillBeRawPtr<Event> event) | |
640 { | |
641 if (m_pendingFinishedEvent == event) | |
642 m_pendingFinishedEvent = nullptr; | |
643 return EventTargetWithInlineData::dispatchEvent(event); | |
644 } | |
645 | |
646 double AnimationPlayer::playbackRate() const | |
647 { | |
648 return m_playbackRate; | |
649 } | |
650 | |
651 void AnimationPlayer::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 AnimationPlayer::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 AnimationPlayer::setOutdated() | |
679 { | |
680 m_outdated = true; | |
681 if (m_timeline) | |
682 m_timeline->setOutdatedAnimationPlayer(this); | |
683 } | |
684 | |
685 bool AnimationPlayer::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 AnimationPlayer::isCandidateForAnimationOnCompositor() const | |
695 { | |
696 if (!canStartAnimationOnCompositor()) | |
697 return false; | |
698 | |
699 return toAnimation(m_content.get())->isCandidateForAnimationOnCompositor(m_p
laybackRate); | |
700 } | |
701 | |
702 bool AnimationPlayer::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 toAnimation(m_content.get())->maybeStartAnimationOnCompositor(m_compo
sitorGroup, startTime, timeOffset, m_playbackRate); | |
721 } | |
722 | |
723 void AnimationPlayer::setCompositorPending(bool sourceChanged) | |
724 { | |
725 // FIXME: Animation 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 AnimationPlayer::cancelAnimationOnCompositor() | |
748 { | |
749 if (hasActiveAnimationsOnCompositor()) | |
750 toAnimation(m_content.get())->cancelAnimationOnCompositor(); | |
751 | |
752 destroyCompositorPlayer(); | |
753 } | |
754 | |
755 void AnimationPlayer::restartAnimationOnCompositor() | |
756 { | |
757 if (hasActiveAnimationsOnCompositor()) | |
758 toAnimation(m_content.get())->restartAnimationOnCompositor(); | |
759 } | |
760 | |
761 void AnimationPlayer::cancelIncompatibleAnimationsOnCompositor() | |
762 { | |
763 if (m_content && m_content->isAnimation()) | |
764 toAnimation(m_content.get())->cancelIncompatibleAnimationsOnCompositor()
; | |
765 } | |
766 | |
767 bool AnimationPlayer::hasActiveAnimationsOnCompositor() | |
768 { | |
769 if (!m_content || !m_content->isAnimation()) | |
770 return false; | |
771 | |
772 return toAnimation(m_content.get())->hasActiveAnimationsOnCompositor(); | |
773 } | |
774 | |
775 bool AnimationPlayer::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 AnimationPlayer::timeToEffectChange() | |
811 { | |
812 ASSERT(!m_outdated); | |
813 if (m_held || !hasStartTime()) | |
814 return std::numeric_limits<double>::infinity(); | |
815 if (!m_content) | |
816 return -currentTimeInternal() / m_playbackRate; | |
817 double result = m_playbackRate > 0 | |
818 ? m_content->timeToForwardsEffectChange() / m_playbackRate | |
819 : m_content->timeToReverseEffectChange() / -m_playbackRate; | |
820 return !hasActiveAnimationsOnCompositor() && m_content->phase() == Animation
Node::PhaseActive | |
821 ? 0 | |
822 : result; | |
823 } | |
824 | |
825 void AnimationPlayer::cancel() | |
826 { | |
827 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand); | |
828 | |
829 if (playStateInternal() == Idle) | |
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::didCancelAnimationPlayer(timeline()->document(), t
his); | |
840 } | |
841 | |
842 void AnimationPlayer::beginUpdatingState() | |
843 { | |
844 // Nested calls are not allowed! | |
845 ASSERT(!m_stateIsBeingUpdated); | |
846 m_stateIsBeingUpdated = true; | |
847 } | |
848 | |
849 void AnimationPlayer::endUpdatingState() | |
850 { | |
851 ASSERT(m_stateIsBeingUpdated); | |
852 m_stateIsBeingUpdated = false; | |
853 } | |
854 | |
855 void AnimationPlayer::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 AnimationPlayer::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 AnimationPlayer::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 AnimationPlayer::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 AnimationPlayer::attachCompositedLayers() | |
897 { | |
898 if (!RuntimeEnabledFeatures::compositorAnimationTimelinesEnabled() || !m_com
positorPlayer) | |
899 return; | |
900 | |
901 ASSERT(m_content); | |
902 ASSERT(m_content->isAnimation()); | |
903 | |
904 if (toAnimation(m_content.get())->canAttachCompositedLayers()) | |
905 toAnimation(m_content.get())->attachCompositedLayers(); | |
906 } | |
907 | |
908 void AnimationPlayer::detachCompositedLayers() | |
909 { | |
910 if (m_compositorPlayer && m_compositorPlayer->isLayerAttached()) | |
911 m_compositorPlayer->detachLayer(); | |
912 } | |
913 | |
914 void AnimationPlayer::notifyAnimationStarted(double monotonicTime, int group) | |
915 { | |
916 ASSERT(RuntimeEnabledFeatures::compositorAnimationTimelinesEnabled()); | |
917 timeline()->document()->compositorPendingAnimations().notifyCompositorAnimat
ionStarted(monotonicTime, group); | |
918 } | |
919 | |
920 AnimationPlayer::PlayStateUpdateScope::PlayStateUpdateScope(AnimationPlayer& pla
yer, TimingUpdateReason reason, CompositorPendingChange compositorPendingChange) | |
921 : m_player(player) | |
922 , m_initialPlayState(m_player->playStateInternal()) | |
923 , m_compositorPendingChange(compositorPendingChange) | |
924 { | |
925 m_player->beginUpdatingState(); | |
926 m_player->updateCurrentTimingState(reason); | |
927 } | |
928 | |
929 AnimationPlayer::PlayStateUpdateScope::~PlayStateUpdateScope() | |
930 { | |
931 AnimationPlayState oldPlayState = m_initialPlayState; | |
932 AnimationPlayState newPlayState = m_player->calculatePlayState(); | |
933 | |
934 m_player->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"), "Animation", m_player, "data", InspectorAnimat
ionEvent::data(*m_player)); | |
940 else if (wasActive && !isActive) | |
941 TRACE_EVENT_NESTABLE_ASYNC_END1("blink.animations," TRACE_DISABLED_B
Y_DEFAULT("devtools.timeline"), "Animation", m_player, "endData", InspectorAnima
tionStateEvent::data(*m_player)); | |
942 else | |
943 TRACE_EVENT_NESTABLE_ASYNC_INSTANT1("blink.animations," TRACE_DISABL
ED_BY_DEFAULT("devtools.timeline"), "Animation", m_player, "data", InspectorAnim
ationStateEvent::data(*m_player)); | |
944 } | |
945 | |
946 // Ordering is important, the ready promise should resolve/reject before | |
947 // the finished promise. | |
948 if (m_player->m_readyPromise && newPlayState != oldPlayState) { | |
949 if (newPlayState == Idle) { | |
950 if (m_player->m_readyPromise->state() == AnimationPlayerPromise::Pen
ding) { | |
951 m_player->m_readyPromise->reject(DOMException::create(AbortError
)); | |
952 } | |
953 m_player->m_readyPromise->reset(); | |
954 m_player->m_readyPromise->resolve(m_player); | |
955 } else if (oldPlayState == Pending) { | |
956 m_player->m_readyPromise->resolve(m_player); | |
957 } else if (newPlayState == Pending) { | |
958 ASSERT(m_player->m_readyPromise->state() != AnimationPlayerPromise::
Pending); | |
959 m_player->m_readyPromise->reset(); | |
960 } | |
961 } | |
962 | |
963 if (m_player->m_finishedPromise && newPlayState != oldPlayState) { | |
964 if (newPlayState == Idle) { | |
965 if (m_player->m_finishedPromise->state() == AnimationPlayerPromise::
Pending) { | |
966 m_player->m_finishedPromise->reject(DOMException::create(AbortEr
ror)); | |
967 } | |
968 m_player->m_finishedPromise->reset(); | |
969 } else if (newPlayState == Finished) { | |
970 m_player->m_finishedPromise->resolve(m_player); | |
971 } else if (oldPlayState == Finished) { | |
972 m_player->m_finishedPromise->reset(); | |
973 } | |
974 } | |
975 | |
976 if (oldPlayState != newPlayState && (oldPlayState == Idle || newPlayState ==
Idle)) { | |
977 m_player->setOutdated(); | |
978 } | |
979 | |
980 #if ENABLE(ASSERT) | |
981 // Verify that current time is up to date. | |
982 m_player->currentTimeInternal(); | |
983 #endif | |
984 | |
985 switch (m_compositorPendingChange) { | |
986 case SetCompositorPending: | |
987 m_player->setCompositorPending(); | |
988 break; | |
989 case SetCompositorPendingWithSourceChanged: | |
990 m_player->setCompositorPending(true); | |
991 break; | |
992 case DoNotSetCompositorPending: | |
993 break; | |
994 default: | |
995 ASSERT_NOT_REACHED(); | |
996 break; | |
997 } | |
998 m_player->endUpdatingState(); | |
999 | |
1000 if (oldPlayState != newPlayState && newPlayState == Running) | |
1001 InspectorInstrumentation::didCreateAnimationPlayer(m_player->timeline()-
>document(), m_player); | |
1002 } | |
1003 | |
1004 bool AnimationPlayer::addEventListener(const AtomicString& eventType, PassRefPtr
<EventListener> listener, bool useCapture) | |
1005 { | |
1006 if (eventType == EventTypeNames::finish) | |
1007 UseCounter::count(executionContext(), UseCounter::AnimationPlayerFinishE
vent); | |
1008 return EventTargetWithInlineData::addEventListener(eventType, listener, useC
apture); | |
1009 } | |
1010 | |
1011 void AnimationPlayer::pauseForTesting(double pauseTime) | |
1012 { | |
1013 RELEASE_ASSERT(!paused()); | |
1014 setCurrentTimeInternal(pauseTime, TimingUpdateOnDemand); | |
1015 if (hasActiveAnimationsOnCompositor()) | |
1016 toAnimation(m_content.get())->pauseAnimationForTestingOnCompositor(curre
ntTimeInternal()); | |
1017 m_isPausedForTesting = true; | |
1018 pause(); | |
1019 } | |
1020 | |
1021 DEFINE_TRACE(AnimationPlayer) | |
1022 { | |
1023 visitor->trace(m_content); | |
1024 visitor->trace(m_timeline); | |
1025 visitor->trace(m_pendingFinishedEvent); | |
1026 visitor->trace(m_finishedPromise); | |
1027 visitor->trace(m_readyPromise); | |
1028 EventTargetWithInlineData::trace(visitor); | |
1029 ActiveDOMObject::trace(visitor); | |
1030 } | |
1031 | |
1032 } // namespace | |
OLD | NEW |