bool AnimationPlayer::hasActiveAnimationsOnCompositor() { if (!m_content || !m_content->isAnimation()) return false; return toAnimation(m_content.get())->hasActiveAnimationsOnCompositor(); }
void Player::pauseForTesting() { RELEASE_ASSERT(!paused()); if (!m_isPausedForTesting && hasActiveAnimationsOnCompositor()) toAnimation(m_content.get())->pauseAnimationForTestingOnCompositor(currentTime()); m_isPausedForTesting = true; setPausedImpl(true); }
void AnimationPlayer::pauseForTesting(double pauseTime) { RELEASE_ASSERT(!paused()); setCurrentTimeInternal(pauseTime, TimingUpdateOnDemand); if (hasActiveAnimationsOnCompositor()) toAnimation(m_content.get())->pauseAnimationForTestingOnCompositor(currentTimeInternal()); m_isPausedForTesting = true; pause(); }
void AnimationPlayer::pauseForTesting(double pauseTime) { RELEASE_ASSERT(!paused()); updateTimingState(pauseTime); if (!m_isPausedForTesting && hasActiveAnimationsOnCompositor()) toAnimation(m_content.get())->pauseAnimationForTestingOnCompositor(currentTime()); m_isPausedForTesting = true; pause(); }
bool Player::maybeStartAnimationOnCompositor() { // FIXME: Support starting compositor animations that have a fixed // start time. ASSERT(!hasStartTime()); if (!m_content || !m_content->isAnimation()) return false; return toAnimation(m_content.get())->maybeStartAnimationOnCompositor(); }
void Player::setCurrentTime(double seekTime) { if (pausedInternal()) m_pauseStartTime = seekTime; else m_timeDrift = currentTimeBeforeDrift() - seekTime; if (m_isPausedForTesting && hasActiveAnimationsOnCompositor()) toAnimation(m_content.get())->pauseAnimationForTestingOnCompositor(currentTime()); update(); }
TEST_F(AnimationElementAnimationTest, CanStartAnAnimation) { v8::Isolate* isolate = v8::Isolate::GetCurrent(); v8::HandleScope scope(isolate); v8::Local<v8::Context> context = v8::Context::New(isolate); v8::Context::Scope contextScope(context); Vector<Dictionary> jsKeyframes; v8::Handle<v8::Object> keyframe1 = v8::Object::New(); v8::Handle<v8::Object> keyframe2 = v8::Object::New(); setV8ObjectProperty(keyframe1, "width", "100px"); setV8ObjectProperty(keyframe1, "offset", "0"); setV8ObjectProperty(keyframe2, "width", "0px"); setV8ObjectProperty(keyframe2, "offset", "1"); jsKeyframes.append(Dictionary(keyframe1, isolate)); jsKeyframes.append(Dictionary(keyframe2, isolate)); String value1; ASSERT_TRUE(jsKeyframes[0].get("width", value1)); ASSERT_EQ("100px", value1); String value2; ASSERT_TRUE(jsKeyframes[1].get("width", value2)); ASSERT_EQ("0px", value2); startAnimation(element.get(), jsKeyframes); Player* player = document->timeline()->players().at(0).get(); Animation* animation = toAnimation(player->source()); Element* target = animation->target(); EXPECT_EQ(*element.get(), *target); const KeyframeAnimationEffect::KeyframeVector keyframes = toKeyframeAnimationEffect(animation->effect())->getFrames(); EXPECT_EQ(0, keyframes[0]->offset()); EXPECT_EQ(1, keyframes[1]->offset()); const AnimatableValue* keyframe1Width = keyframes[0]->propertyValue(CSSPropertyWidth); const AnimatableValue* keyframe2Width = keyframes[1]->propertyValue(CSSPropertyWidth); ASSERT(keyframe1Width); ASSERT(keyframe2Width); EXPECT_TRUE(keyframe1Width->isLength()); EXPECT_TRUE(keyframe2Width->isLength()); EXPECT_EQ("100px", toAnimatableLength(keyframe1Width)->toCSSValue()->cssText()); EXPECT_EQ("0px", toAnimatableLength(keyframe2Width)->toCSSValue()->cssText()); }
bool AnimationPlayer::maybeStartAnimationOnCompositor() { if (!canStartAnimationOnCompositor()) return false; double startTime = timeline()->zeroTime() + startTimeInternal(); double timeOffset = 0; if (std::isnan(startTime)) { timeOffset = currentTimeInternal(); } return toAnimation(m_content.get())->maybeStartAnimationOnCompositor(startTime, timeOffset); }
bool ActiveAnimations::hasActiveAnimations(CSSPropertyID property) const { for (AnimationPlayerSet::const_iterator it = m_players.begin(); it != players().end(); ++it) { const AnimationPlayer& player = *it->key; ASSERT(player.source()); // FIXME: Needs to consider AnimationGroup once added. ASSERT(player.source()->isAnimation()); const Animation& animation = *toAnimation(player.source()); if (animation.isCurrent() && animation.affects(property)) return true; } return false; }
void AnimationPlayer::cancel() { PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand); if (playStateInternal() == Idle) return; m_holdTime = currentTimeInternal(); m_held = true; // TODO m_playState = Idle; m_startTime = nullValue(); m_currentTimePending = false; // after cancelation, transitions must be downgraded or they'll fail // to be considered when retriggering themselves. This can happen if // the transition is captured through getAnimationPlayers then played. if (m_content && m_content->isAnimation()) toAnimation(m_content.get())->downgradeToNormalAnimation(); }
bool AnimationPlayer::maybeStartAnimationOnCompositor() { if (!canStartAnimationOnCompositor()) return false; bool reversed = m_playbackRate < 0; double startTime = timeline()->zeroTime() + startTimeInternal(); if (reversed) { startTime -= sourceEnd() / fabs(m_playbackRate); } double timeOffset = 0; if (std::isnan(startTime)) { timeOffset = reversed ? sourceEnd() - currentTimeInternal() : currentTimeInternal(); timeOffset = timeOffset / fabs(m_playbackRate); } ASSERT(m_compositorGroup != 0); return toAnimation(m_content.get())->maybeStartAnimationOnCompositor(m_compositorGroup, startTime, timeOffset, m_playbackRate); }
void AnimationPlayer::cancelAnimationOnCompositor() { if (hasActiveAnimationsOnCompositor()) toAnimation(m_content.get())->cancelAnimationOnCompositor(); }
void CSSAnimations::maybeApplyPendingUpdate(Element* element) { if (!m_pendingUpdate) { m_previousActiveInterpolationsForAnimations.clear(); return; } OwnPtrWillBeRawPtr<CSSAnimationUpdate> update = m_pendingUpdate.release(); m_previousActiveInterpolationsForAnimations.swap(update->activeInterpolationsForAnimations()); // FIXME: cancelling, pausing, unpausing animations all query compositingState, which is not necessarily up to date here // since we call this from recalc style. // https://code.google.com/p/chromium/issues/detail?id=339847 DisableCompositingQueryAsserts disabler; for (const AtomicString& animationName : update->cancelledAnimationNames()) { RefPtrWillBeRawPtr<AnimationPlayer> player = m_animations.take(animationName); player->cancel(); player->update(TimingUpdateOnDemand); } for (const AtomicString& animationName : update->animationsWithPauseToggled()) { AnimationPlayer* player = m_animations.get(animationName); if (player->paused()) player->unpause(); else player->pause(); if (player->outdated()) player->update(TimingUpdateOnDemand); } for (const auto& entry : update->newAnimations()) { const InertAnimation* inertAnimation = entry.animation.get(); OwnPtrWillBeRawPtr<AnimationEventDelegate> eventDelegate = adoptPtrWillBeNoop(new AnimationEventDelegate(element, entry.name)); RefPtrWillBeRawPtr<Animation> animation = Animation::create(element, inertAnimation->effect(), inertAnimation->specifiedTiming(), Animation::DefaultPriority, eventDelegate.release()); animation->setName(inertAnimation->name()); RefPtrWillBeRawPtr<AnimationPlayer> player = element->document().timeline().createAnimationPlayer(animation.get()); if (inertAnimation->paused()) player->pause(); player->update(TimingUpdateOnDemand); m_animations.set(entry.name, player.get()); } // Transitions that are run on the compositor only update main-thread state // lazily. However, we need the new state to know what the from state shoud // be when transitions are retargeted. Instead of triggering complete style // recalculation, we find these cases by searching for new transitions that // have matching cancelled animation property IDs on the compositor. WillBeHeapHashMap<CSSPropertyID, std::pair<RefPtrWillBeMember<Animation>, double>> retargetedCompositorTransitions; for (CSSPropertyID id : update->cancelledTransitions()) { ASSERT(m_transitions.contains(id)); RefPtrWillBeRawPtr<AnimationPlayer> player = m_transitions.take(id).player; Animation* animation = toAnimation(player->source()); if (animation->hasActiveAnimationsOnCompositor(id) && update->newTransitions().find(id) != update->newTransitions().end()) retargetedCompositorTransitions.add(id, std::pair<RefPtrWillBeMember<Animation>, double>(animation, player->startTimeInternal())); player->cancel(); player->update(TimingUpdateOnDemand); } for (const auto& entry : update->newTransitions()) { const CSSAnimationUpdate::NewTransition& newTransition = entry.value; RunningTransition runningTransition; runningTransition.from = newTransition.from; runningTransition.to = newTransition.to; CSSPropertyID id = newTransition.id; InertAnimation* inertAnimation = newTransition.animation.get(); OwnPtrWillBeRawPtr<TransitionEventDelegate> eventDelegate = adoptPtrWillBeNoop(new TransitionEventDelegate(element, newTransition.eventId)); RefPtrWillBeRawPtr<AnimationEffect> effect = inertAnimation->effect(); if (retargetedCompositorTransitions.contains(id)) { const std::pair<RefPtrWillBeMember<Animation>, double>& oldTransition = retargetedCompositorTransitions.get(id); RefPtrWillBeRawPtr<Animation> oldAnimation = oldTransition.first; double oldStartTime = oldTransition.second; double inheritedTime = isNull(oldStartTime) ? 0 : element->document().timeline().currentTimeInternal() - oldStartTime; AnimatableValueKeyframeEffectModel* oldEffect = toAnimatableValueKeyframeEffectModel(inertAnimation->effect()); const KeyframeVector& frames = oldEffect->getFrames(); AnimatableValueKeyframeVector newFrames; newFrames.append(toAnimatableValueKeyframe(frames[0]->clone().get())); newFrames.append(toAnimatableValueKeyframe(frames[1]->clone().get())); newFrames[0]->clearPropertyValue(id); RefPtrWillBeRawPtr<InertAnimation> inertAnimationForSampling = InertAnimation::create(oldAnimation->effect(), oldAnimation->specifiedTiming(), false); OwnPtrWillBeRawPtr<WillBeHeapVector<RefPtrWillBeMember<Interpolation>>> sample = inertAnimationForSampling->sample(inheritedTime); ASSERT(sample->size() == 1); newFrames[0]->setPropertyValue(id, toLegacyStyleInterpolation(sample->at(0).get())->currentValue()); effect = AnimatableValueKeyframeEffectModel::create(newFrames); } RefPtrWillBeRawPtr<Animation> transition = Animation::create(element, effect, inertAnimation->specifiedTiming(), Animation::TransitionPriority, eventDelegate.release()); transition->setName(inertAnimation->name()); RefPtrWillBeRawPtr<AnimationPlayer> player = element->document().timeline().createAnimationPlayer(transition.get()); player->update(TimingUpdateOnDemand); runningTransition.player = player; m_transitions.set(id, runningTransition); ASSERT(id != CSSPropertyInvalid); blink::Platform::current()->histogramSparse("WebCore.Animation.CSSProperties", UseCounter::mapCSSPropertyIdToCSSSampleIdForHistogram(id)); } }