TEST_F(AnimationCompositorAnimationsTest, isCandidateForAnimationOnCompositor) { Timing linearTiming(createCompositableTiming()); AnimatableValueKeyframeVector basicFramesVector; basicFramesVector.append(createDefaultKeyframe(CSSPropertyOpacity, AnimationEffect::CompositeReplace, 0.0).get()); basicFramesVector.append(createDefaultKeyframe(CSSPropertyOpacity, AnimationEffect::CompositeReplace, 1.0).get()); AnimatableValueKeyframeVector nonBasicFramesVector; nonBasicFramesVector.append(createDefaultKeyframe(CSSPropertyOpacity, AnimationEffect::CompositeReplace, 0.0).get()); nonBasicFramesVector.append(createDefaultKeyframe(CSSPropertyOpacity, AnimationEffect::CompositeReplace, 0.5).get()); nonBasicFramesVector.append(createDefaultKeyframe(CSSPropertyOpacity, AnimationEffect::CompositeReplace, 1.0).get()); basicFramesVector[0]->setEasing(m_linearTimingFunction.get()); RefPtr<AnimatableValueKeyframeEffectModel> basicFrames = AnimatableValueKeyframeEffectModel::create(basicFramesVector).get(); EXPECT_TRUE(isCandidateForAnimationOnCompositor(linearTiming, *basicFrames.get())); basicFramesVector[0]->setEasing(CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseIn)); basicFrames = AnimatableValueKeyframeEffectModel::create(basicFramesVector).get(); EXPECT_TRUE(isCandidateForAnimationOnCompositor(linearTiming, *basicFrames.get())); nonBasicFramesVector[0]->setEasing(m_linearTimingFunction.get()); nonBasicFramesVector[1]->setEasing(CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseIn)); RefPtr<AnimatableValueKeyframeEffectModel> nonBasicFrames = AnimatableValueKeyframeEffectModel::create(nonBasicFramesVector).get(); EXPECT_TRUE(CompositorAnimations::instance()->isCandidateForAnimationOnCompositor(linearTiming, *nonBasicFrames.get())); }
void CSSAnimations::calculateTransitionUpdateForProperty(CSSPropertyID id, const CSSTransitionData& transitionData, size_t transitionIndex, const ComputedStyle& oldStyle, const ComputedStyle& style, const TransitionMap* activeTransitions, CSSAnimationUpdate& update, const Element* element) { RefPtr<AnimatableValue> to = nullptr; if (activeTransitions) { TransitionMap::const_iterator activeTransitionIter = activeTransitions->find(id); if (activeTransitionIter != activeTransitions->end()) { to = CSSAnimatableValueFactory::create(id, style); const AnimatableValue* activeTo = activeTransitionIter->value.to; if (to->equals(activeTo)) return; update.cancelTransition(id); ASSERT(!element->elementAnimations() || !element->elementAnimations()->isAnimationStyleChange()); } } if (CSSPropertyEquality::propertiesEqual(id, oldStyle, style)) return; if (!to) to = CSSAnimatableValueFactory::create(id, style); RefPtr<AnimatableValue> from = CSSAnimatableValueFactory::create(id, oldStyle); // If we have multiple transitions on the same property, we will use the // last one since we iterate over them in order. if (AnimatableValue::usesDefaultInterpolation(to.get(), from.get())) return; Timing timing = transitionData.convertToTiming(transitionIndex); if (timing.startDelay + timing.iterationDuration <= 0) return; AnimatableValueKeyframeVector keyframes; double startKeyframeOffset = 0; if (timing.startDelay > 0) { timing.iterationDuration += timing.startDelay; startKeyframeOffset = timing.startDelay / timing.iterationDuration; timing.startDelay = 0; } RefPtr<AnimatableValueKeyframe> delayKeyframe = AnimatableValueKeyframe::create(); delayKeyframe->setPropertyValue(id, from.get()); delayKeyframe->setOffset(0); keyframes.append(delayKeyframe); RefPtr<AnimatableValueKeyframe> startKeyframe = AnimatableValueKeyframe::create(); startKeyframe->setPropertyValue(id, from.get()); startKeyframe->setOffset(startKeyframeOffset); startKeyframe->setEasing(timing.timingFunction.release()); timing.timingFunction = LinearTimingFunction::shared(); keyframes.append(startKeyframe); RefPtr<AnimatableValueKeyframe> endKeyframe = AnimatableValueKeyframe::create(); endKeyframe->setPropertyValue(id, to.get()); endKeyframe->setOffset(1); keyframes.append(endKeyframe); AnimatableValueKeyframeEffectModel* model = AnimatableValueKeyframeEffectModel::create(keyframes); update.startTransition(id, from.get(), to.get(), InertEffect::create(model, timing, false, 0)); ASSERT(!element->elementAnimations() || !element->elementAnimations()->isAnimationStyleChange()); }
PassRefPtr<AnimatableValueKeyframeEffectModel> createKeyframeEffectModel(PassRefPtr<AnimatableValueKeyframe> prpFrom, PassRefPtr<AnimatableValueKeyframe> prpTo, PassRefPtr<AnimatableValueKeyframe> prpC = nullptr, PassRefPtr<AnimatableValueKeyframe> prpD = nullptr) { RefPtr<AnimatableValueKeyframe> from = prpFrom; RefPtr<AnimatableValueKeyframe> to = prpTo; RefPtr<AnimatableValueKeyframe> c = prpC; RefPtr<AnimatableValueKeyframe> d = prpD; EXPECT_EQ(from->offset(), 0); AnimatableValueKeyframeVector frames; frames.append(from); EXPECT_LE(from->offset(), to->offset()); frames.append(to); if (c) { EXPECT_LE(to->offset(), c->offset()); frames.append(c); } if (d) { frames.append(d); EXPECT_LE(c->offset(), d->offset()); EXPECT_EQ(d->offset(), 1.0); } else { EXPECT_EQ(to->offset(), 1.0); } if (!HasFatalFailure()) { return AnimatableValueKeyframeEffectModel::create(frames); } return nullptr; }
TEST_F(AnimationCompositorAnimationsTest, createReversedOpacityAnimation) { RefPtr<TimingFunction> cubicEasyFlipTimingFunction = CubicBezierTimingFunction::create(0.0, 0.0, 0.0, 1.0); // Animation to convert AnimatableValueKeyframeVector frames; frames.append(createReplaceOpKeyframe(CSSPropertyOpacity, AnimatableDouble::create(2.0).get(), 0)); frames.append(createReplaceOpKeyframe(CSSPropertyOpacity, AnimatableDouble::create(-1.0).get(), 0.25)); frames.append(createReplaceOpKeyframe(CSSPropertyOpacity, AnimatableDouble::create(20.0).get(), 0.5)); frames.append(createReplaceOpKeyframe(CSSPropertyOpacity, AnimatableDouble::create(5.0).get(), 1.0)); frames[0]->setEasing(CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseIn)); frames[1]->setEasing(m_linearTimingFunction.get()); frames[2]->setEasing(cubicEasyFlipTimingFunction.get()); RefPtr<AnimatableValueKeyframeEffectModel> effect = AnimatableValueKeyframeEffectModel::create(frames); m_timing.timingFunction = m_linearTimingFunction.get(); m_timing.iterationCount = 10; m_timing.direction = Timing::PlaybackDirectionAlternateReverse; // -- WebCompositorSupportMock mockCompositor; // Curve is created WebFloatAnimationCurveMock* mockCurvePtr = new WebFloatAnimationCurveMock(); ExpectationSet usesMockCurve; EXPECT_CALL(mockCompositor, createFloatAnimationCurve()) .WillOnce(Return(mockCurvePtr)); usesMockCurve += EXPECT_CALL(*mockCurvePtr, add(WebFloatKeyframe(0.0, 5.0), 1.0, 0.0, 1.0, 1.0)); usesMockCurve += EXPECT_CALL(*mockCurvePtr, add(WebFloatKeyframe(0.5, 20.0), WebCompositorAnimationCurve::TimingFunctionTypeLinear)); usesMockCurve += EXPECT_CALL(*mockCurvePtr, add(WebFloatKeyframe(0.75, -1.0), WebCompositorAnimationCurve::TimingFunctionTypeEaseOut)); usesMockCurve += EXPECT_CALL(*mockCurvePtr, add(WebFloatKeyframe(1.0, 2.0))); // Create the animation WebCompositorAnimationMock* mockAnimationPtr = new WebCompositorAnimationMock(WebCompositorAnimation::TargetPropertyOpacity); ExpectationSet usesMockAnimation; usesMockCurve += EXPECT_CALL(mockCompositor, createAnimation(Ref(*mockCurvePtr), WebCompositorAnimation::TargetPropertyOpacity, _)) .WillOnce(Return(mockAnimationPtr)); usesMockAnimation += EXPECT_CALL(*mockAnimationPtr, setIterations(10)); usesMockAnimation += EXPECT_CALL(*mockAnimationPtr, setTimeOffset(0.0)); usesMockAnimation += EXPECT_CALL(*mockAnimationPtr, setAlternatesDirection(true)); EXPECT_CALL(*mockAnimationPtr, delete_()) .Times(1) .After(usesMockAnimation); EXPECT_CALL(*mockCurvePtr, delete_()) .Times(1) .After(usesMockCurve); // Go! setCompositorForTesting(mockCompositor); Vector<OwnPtr<WebCompositorAnimation> > result; getAnimationOnCompositor(m_timing, *effect.get(), result); EXPECT_EQ(1U, result.size()); result[0].clear(); }
bool duplicateSingleKeyframeAndTestIsCandidateOnResult(AnimatableValueKeyframe* frame) { EXPECT_EQ(frame->offset(), 0); AnimatableValueKeyframeVector frames; RefPtr<Keyframe> second = frame->cloneWithOffset(1); frames.append(frame); frames.append(toAnimatableValueKeyframe(second.get())); return isCandidateForAnimationOnCompositor(m_timing, *AnimatableValueKeyframeEffectModel::create(frames).get()); }
TEST_F(AnimationCompositorAnimationsTest, isCandidateForAnimationOnCompositorKeyframeEffectModel) { AnimatableValueKeyframeVector framesSame; framesSame.append(createDefaultKeyframe(CSSPropertyColor, AnimationEffect::CompositeReplace, 0.0).get()); framesSame.append(createDefaultKeyframe(CSSPropertyColor, AnimationEffect::CompositeReplace, 1.0).get()); EXPECT_FALSE(isCandidateForAnimationOnCompositor(m_timing, *AnimatableValueKeyframeEffectModel::create(framesSame).get())); AnimatableValueKeyframeVector framesMixedProperties; framesMixedProperties.append(createDefaultKeyframe(CSSPropertyOpacity, AnimationEffect::CompositeReplace, 0.0).get()); framesMixedProperties.append(createDefaultKeyframe(CSSPropertyColor, AnimationEffect::CompositeReplace, 1.0).get()); EXPECT_FALSE(isCandidateForAnimationOnCompositor(m_timing, *AnimatableValueKeyframeEffectModel::create(framesMixedProperties).get())); }
TEST_F(AnimationCompositorAnimationsTest, isCandidateForAnimationOnCompositorKeyframeEffectModelMultipleFramesOkay) { AnimatableValueKeyframeVector framesSame; framesSame.append(createDefaultKeyframe(CSSPropertyOpacity, AnimationEffect::CompositeReplace, 0.0).get()); framesSame.append(createDefaultKeyframe(CSSPropertyOpacity, AnimationEffect::CompositeReplace, 1.0).get()); EXPECT_TRUE(isCandidateForAnimationOnCompositor(m_timing, *AnimatableValueKeyframeEffectModel::create(framesSame).get())); AnimatableValueKeyframeVector framesMixed; framesMixed.append(createDefaultKeyframe(CSSPropertyOpacity, AnimationEffect::CompositeReplace, 0.0).get()); framesMixed.append(createDefaultKeyframe(CSSPropertyTransform, AnimationEffect::CompositeReplace, 1.0).get()); EXPECT_FALSE(isCandidateForAnimationOnCompositor(m_timing, *AnimatableValueKeyframeEffectModel::create(framesMixed).get())); }
blink::Animation* InspectorAnimationAgent::animationClone( blink::Animation* animation) { const String id = String::number(animation->sequenceNumber()); if (!m_idToAnimationClone.get(id)) { KeyframeEffect* oldEffect = toKeyframeEffect(animation->effect()); ASSERT(oldEffect->model()->isKeyframeEffectModel()); KeyframeEffectModelBase* oldModel = toKeyframeEffectModelBase(oldEffect->model()); EffectModel* newModel = nullptr; // Clone EffectModel. // TODO(samli): Determine if this is an animations bug. if (oldModel->isStringKeyframeEffectModel()) { StringKeyframeEffectModel* oldStringKeyframeModel = toStringKeyframeEffectModel(oldModel); KeyframeVector oldKeyframes = oldStringKeyframeModel->getFrames(); StringKeyframeVector newKeyframes; for (auto& oldKeyframe : oldKeyframes) newKeyframes.append(toStringKeyframe(oldKeyframe.get())); newModel = StringKeyframeEffectModel::create(newKeyframes); } else if (oldModel->isAnimatableValueKeyframeEffectModel()) { AnimatableValueKeyframeEffectModel* oldAnimatableValueKeyframeModel = toAnimatableValueKeyframeEffectModel(oldModel); KeyframeVector oldKeyframes = oldAnimatableValueKeyframeModel->getFrames(); AnimatableValueKeyframeVector newKeyframes; for (auto& oldKeyframe : oldKeyframes) newKeyframes.append(toAnimatableValueKeyframe(oldKeyframe.get())); newModel = AnimatableValueKeyframeEffectModel::create(newKeyframes); } KeyframeEffect* newEffect = KeyframeEffect::create( oldEffect->target(), newModel, oldEffect->specifiedTiming()); m_isCloning = true; blink::Animation* clone = blink::Animation::create(newEffect, animation->timeline()); m_isCloning = false; m_idToAnimationClone.set(id, clone); m_idToAnimation.set(String::number(clone->sequenceNumber()), clone); clone->play(); clone->setStartTime(animation->startTime()); animation->setEffectSuppressed(true); } return m_idToAnimationClone.get(id); }
void CSSAnimations::maybeApplyPendingUpdate(Element* element) { m_previousActiveInterpolationsForAnimations.clear(); if (m_pendingUpdate.isEmpty()) return; m_previousActiveInterpolationsForAnimations.swap(m_pendingUpdate.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 : m_pendingUpdate.cancelledAnimationNames()) { Animation* animation = m_animations.take(animationName)->animation; animation->cancel(); animation->update(TimingUpdateOnDemand); } for (const AtomicString& animationName : m_pendingUpdate.animationsWithPauseToggled()) { Animation* animation = m_animations.get(animationName)->animation.get(); if (animation->paused()) animation->unpause(); else animation->pause(); if (animation->outdated()) animation->update(TimingUpdateOnDemand); } for (const auto& animation : m_pendingUpdate.updatedCompositorKeyframes()) animation->setCompositorPending(true); for (const auto& entry : m_pendingUpdate.animationsWithUpdates()) { KeyframeEffect* effect = toKeyframeEffect(entry.animation->effect()); effect->setModel(entry.effect->model()); effect->updateSpecifiedTiming(entry.effect->specifiedTiming()); m_animations.find(entry.name)->value->update(entry); } for (const auto& entry : m_pendingUpdate.newAnimations()) { const InertEffect* inertAnimation = entry.effect.get(); AnimationEventDelegate* eventDelegate = new AnimationEventDelegate(element, entry.name); KeyframeEffect* effect = KeyframeEffect::create(element, inertAnimation->model(), inertAnimation->specifiedTiming(), KeyframeEffect::DefaultPriority, eventDelegate); effect->setName(inertAnimation->name()); Animation* animation = element->document().timeline().play(effect); if (inertAnimation->paused()) animation->pause(); animation->update(TimingUpdateOnDemand); m_animations.set(entry.name, new RunningAnimation(animation, entry)); } // 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. HeapHashMap<CSSPropertyID, std::pair<Member<KeyframeEffect>, double>> retargetedCompositorTransitions; for (CSSPropertyID id : m_pendingUpdate.cancelledTransitions()) { ASSERT(m_transitions.contains(id)); Animation* animation = m_transitions.take(id).animation; KeyframeEffect* effect = toKeyframeEffect(animation->effect()); if (effect->hasActiveAnimationsOnCompositor(id) && m_pendingUpdate.newTransitions().find(id) != m_pendingUpdate.newTransitions().end() && !animation->limited()) retargetedCompositorTransitions.add(id, std::pair<KeyframeEffect*, double>(effect, animation->startTimeInternal())); animation->cancel(); // 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 getAnimations then played. if (animation->effect() && animation->effect()->isKeyframeEffect()) toKeyframeEffect(animation->effect())->downgradeToNormal(); animation->update(TimingUpdateOnDemand); } for (CSSPropertyID id : m_pendingUpdate.finishedTransitions()) { // This transition can also be cancelled and finished at the same time if (m_transitions.contains(id)) { Animation* animation = m_transitions.take(id).animation; // Transition must be downgraded if (animation->effect() && animation->effect()->isKeyframeEffect()) toKeyframeEffect(animation->effect())->downgradeToNormal(); } } for (const auto& entry : m_pendingUpdate.newTransitions()) { const CSSAnimationUpdate::NewTransition& newTransition = entry.value; RunningTransition runningTransition; runningTransition.from = newTransition.from; runningTransition.to = newTransition.to; CSSPropertyID id = newTransition.id; InertEffect* inertAnimation = newTransition.effect.get(); TransitionEventDelegate* eventDelegate = new TransitionEventDelegate(element, id); EffectModel* model = inertAnimation->model(); if (retargetedCompositorTransitions.contains(id)) { const std::pair<Member<KeyframeEffect>, double>& oldTransition = retargetedCompositorTransitions.get(id); KeyframeEffect* oldAnimation = oldTransition.first; double oldStartTime = oldTransition.second; double inheritedTime = isNull(oldStartTime) ? 0 : element->document().timeline().currentTimeInternal() - oldStartTime; AnimatableValueKeyframeEffectModel* oldEffect = toAnimatableValueKeyframeEffectModel(inertAnimation->model()); const KeyframeVector& frames = oldEffect->getFrames(); AnimatableValueKeyframeVector newFrames; newFrames.append(toAnimatableValueKeyframe(frames[0]->clone().get())); newFrames.append(toAnimatableValueKeyframe(frames[1]->clone().get())); newFrames.append(toAnimatableValueKeyframe(frames[2]->clone().get())); newFrames[0]->clearPropertyValue(id); newFrames[1]->clearPropertyValue(id); InertEffect* inertAnimationForSampling = InertEffect::create(oldAnimation->model(), oldAnimation->specifiedTiming(), false, inheritedTime); OwnPtr<Vector<RefPtr<Interpolation>>> sample = nullptr; inertAnimationForSampling->sample(sample); if (sample && sample->size() == 1) { newFrames[0]->setPropertyValue(id, toLegacyStyleInterpolation(sample->at(0).get())->currentValue()); newFrames[1]->setPropertyValue(id, toLegacyStyleInterpolation(sample->at(0).get())->currentValue()); model = AnimatableValueKeyframeEffectModel::create(newFrames); } } KeyframeEffect* transition = KeyframeEffect::create(element, model, inertAnimation->specifiedTiming(), KeyframeEffect::TransitionPriority, eventDelegate); transition->setName(inertAnimation->name()); Animation* animation = element->document().timeline().play(transition); // Set the current time as the start time for retargeted transitions if (retargetedCompositorTransitions.contains(id)) animation->setStartTime(element->document().timeline().currentTime()); animation->update(TimingUpdateOnDemand); runningTransition.animation = animation; m_transitions.set(id, runningTransition); ASSERT(id != CSSPropertyInvalid); Platform::current()->histogramSparse("WebCore.Animation.CSSProperties", UseCounter::mapCSSPropertyIdToCSSSampleIdForHistogram(id)); } clearPendingUpdate(); }
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)); } }
void CSSAnimations::calculateAnimationUpdate(CSSAnimationUpdate* update, const Element* animatingElement, Element& element, const RenderStyle& style, RenderStyle* parentStyle, StyleResolver* resolver) { const ActiveAnimations* activeAnimations = animatingElement ? animatingElement->activeAnimations() : nullptr; #if !ENABLE(ASSERT) // If we're in an animation style change, no animations can have started, been cancelled or changed play state. // When ASSERT is enabled, we verify this optimization. if (activeAnimations && activeAnimations->isAnimationStyleChange()) return; #endif const CSSAnimationData* animationData = style.animations(); const CSSAnimations* cssAnimations = activeAnimations ? &activeAnimations->cssAnimations() : nullptr; HashSet<AtomicString> inactive; if (cssAnimations) { for (const auto& entry : cssAnimations->m_animations) inactive.add(entry.key); } if (style.display() != NONE) { for (size_t i = 0; animationData && i < animationData->nameList().size(); ++i) { AtomicString animationName(animationData->nameList()[i]); if (animationName == CSSAnimationData::initialName()) continue; bool isPaused = CSSTimingData::getRepeated(animationData->playStateList(), i) == AnimPlayStatePaused; // Keyframes and animation properties are snapshotted when the // animation starts, so we don't need to track changes to these, // with the exception of play-state. if (cssAnimations) { AnimationMap::const_iterator existing(cssAnimations->m_animations.find(animationName)); if (existing != cssAnimations->m_animations.end()) { inactive.remove(animationName); AnimationPlayer* player = existing->value.get(); if (isPaused != player->paused()) { ASSERT(!activeAnimations || !activeAnimations->isAnimationStyleChange()); update->toggleAnimationPaused(animationName); } continue; } } Timing timing = animationData->convertToTiming(i); RefPtr<TimingFunction> keyframeTimingFunction = timing.timingFunction; timing.timingFunction = Timing::defaults().timingFunction; AnimatableValueKeyframeVector resolvedKeyframes; resolveKeyframes(resolver, animatingElement, element, style, parentStyle, animationName, keyframeTimingFunction.get(), resolvedKeyframes); if (!resolvedKeyframes.isEmpty()) { ASSERT(!activeAnimations || !activeAnimations->isAnimationStyleChange()); update->startAnimation(animationName, InertAnimation::create(AnimatableValueKeyframeEffectModel::create(resolvedKeyframes), timing, isPaused)); } } } ASSERT(inactive.isEmpty() || cssAnimations); for (const AtomicString& animationName : inactive) { ASSERT(!activeAnimations || !activeAnimations->isAnimationStyleChange()); update->cancelAnimation(animationName, *cssAnimations->m_animations.get(animationName)); } }