Exemplo n.º 1
0
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()));
}
Exemplo n.º 2
0
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());
}
Exemplo n.º 3
0
    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;
    }
Exemplo n.º 4
0
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();
}
Exemplo n.º 5
0
    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());
    }
Exemplo n.º 6
0
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()));
}
Exemplo n.º 7
0
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()));
}
Exemplo n.º 8
0
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);
}
Exemplo n.º 9
0
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();
}
Exemplo n.º 10
0
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));
    }
}
Exemplo n.º 11
0
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));
    }
}