Chromium Code Reviews| 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 |