void CSSAnimations::calculateTransitionActiveInterpolations(CSSAnimationUpdate& update, const Element* animatingElement, double timelineCurrentTime)
{
    ElementAnimations* elementAnimations = animatingElement ? animatingElement->elementAnimations() : nullptr;
    AnimationStack* animationStack = elementAnimations ? &elementAnimations->defaultStack() : nullptr;

    ActiveInterpolationsMap activeInterpolationsForTransitions;
    if (update.newTransitions().isEmpty() && update.cancelledTransitions().isEmpty()) {
        activeInterpolationsForTransitions = AnimationStack::activeInterpolations(animationStack, 0, 0, KeyframeEffect::TransitionPriority, timelineCurrentTime);
    } else {
        HeapVector<Member<InertEffect>> newTransitions;
        for (const auto& entry : update.newTransitions())
            newTransitions.append(entry.value.effect.get());

        HeapHashSet<Member<const Animation>> cancelledAnimations;
        if (!update.cancelledTransitions().isEmpty()) {
            ASSERT(elementAnimations);
            const TransitionMap& transitionMap = elementAnimations->cssAnimations().m_transitions;
            for (CSSPropertyID id : update.cancelledTransitions()) {
                ASSERT(transitionMap.contains(id));
                cancelledAnimations.add(transitionMap.get(id).animation.get());
            }
        }

        activeInterpolationsForTransitions = AnimationStack::activeInterpolations(animationStack, &newTransitions, &cancelledAnimations, KeyframeEffect::TransitionPriority, timelineCurrentTime);
    }

    // Properties being animated by animations don't get values from transitions applied.
    if (!update.activeInterpolationsForAnimations().isEmpty() && !activeInterpolationsForTransitions.isEmpty()) {
        for (const auto& entry : update.activeInterpolationsForAnimations())
            activeInterpolationsForTransitions.remove(entry.key);
    }
    update.adoptActiveInterpolationsForTransitions(activeInterpolationsForTransitions);
}
void CSSAnimations::calculateCompositorAnimationUpdate(CSSAnimationUpdate& update, const Element* animatingElement, Element& element, const ComputedStyle& style)
{
    ElementAnimations* elementAnimations = animatingElement ? animatingElement->elementAnimations() : nullptr;

    // We only update compositor animations in response to changes in the base style.
    if (!elementAnimations || elementAnimations->isAnimationStyleChange())
        return;

    if (!animatingElement->layoutObject() || !animatingElement->layoutObject()->style())
        return;

    const ComputedStyle& oldStyle = *animatingElement->layoutObject()->style();
    if (!oldStyle.shouldCompositeForCurrentAnimations())
        return;

    CSSAnimations& cssAnimations = elementAnimations->cssAnimations();
    for (auto& runningAnimation : cssAnimations.m_animations.values()) {
        Animation& animation = *runningAnimation->animation;
        if (animation.effect() && animation.effect()->isKeyframeEffect()) {
            EffectModel* model = toKeyframeEffect(animation.effect())->model();
            if (model && model->isKeyframeEffectModel()) {
                KeyframeEffectModelBase* keyframeEffect = toKeyframeEffectModelBase(model);
                if (keyframeEffect->hasSyntheticKeyframes() && keyframeEffect->snapshotNeutralCompositorKeyframes(element, oldStyle, style))
                    update.updateCompositorKeyframes(&animation);
            }
        }
    }

    if (oldStyle.hasCurrentTransformAnimation() && oldStyle.effectiveZoom() != style.effectiveZoom()) {
        for (auto& entry : elementAnimations->animations()) {
            Animation& animation = *entry.key;
            if (animation.effect() && animation.effect()->isKeyframeEffect()) {
                EffectModel* model = toKeyframeEffect(animation.effect())->model();
                if (model && model->isKeyframeEffectModel()) {
                    KeyframeEffectModelBase* keyframeEffect = toKeyframeEffectModelBase(model);
                    if (keyframeEffect->affects(PropertyHandle(CSSPropertyTransform)) && keyframeEffect->snapshotAllCompositorKeyframes(element, &style))
                        update.updateCompositorKeyframes(&animation);
                }
            }
        }
    }
}
void CSSAnimations::calculateTransitionUpdate(CSSAnimationUpdate& update, const Element* animatingElement, const ComputedStyle& style)
{
    if (!animatingElement)
        return;

    if (animatingElement->document().printing() || animatingElement->document().wasPrinting())
        return;

    ElementAnimations* elementAnimations = animatingElement->elementAnimations();
    const TransitionMap* activeTransitions = elementAnimations ? &elementAnimations->cssAnimations().m_transitions : nullptr;
    const CSSTransitionData* transitionData = style.transitions();

#if ENABLE(ASSERT)
    // In debug builds we verify that it would have been safe to avoid populating and testing listedProperties if the style recalc is due to animation.
    const bool animationStyleRecalc = false;
#else
    // In release builds we avoid the cost of checking for new and interrupted transitions if the style recalc is due to animation.
    const bool animationStyleRecalc = elementAnimations && elementAnimations->isAnimationStyleChange();
#endif

    BitArray<numCSSProperties> listedProperties;
    bool anyTransitionHadTransitionAll = false;
    const LayoutObject* layoutObject = animatingElement->layoutObject();
    if (!animationStyleRecalc && style.display() != NONE && layoutObject && layoutObject->style() && transitionData) {
        const ComputedStyle& oldStyle = *layoutObject->style();

        for (size_t i = 0; i < transitionData->propertyList().size(); ++i) {
            const CSSTransitionData::TransitionProperty& transitionProperty = transitionData->propertyList()[i];
            if (transitionProperty.propertyType != CSSTransitionData::TransitionKnownProperty)
                continue;

            CSSPropertyID property = resolveCSSPropertyID(transitionProperty.unresolvedProperty);
            bool animateAll = property == CSSPropertyAll;
            if (animateAll)
                anyTransitionHadTransitionAll = true;
            const StylePropertyShorthand& propertyList = animateAll ? CSSAnimations::propertiesForTransitionAll() : shorthandForProperty(property);
            // If not a shorthand we only execute one iteration of this loop, and refer to the property directly.
            for (unsigned j = 0; !j || j < propertyList.length(); ++j) {
                CSSPropertyID id = propertyList.length() ? propertyList.properties()[j] : property;
                ASSERT(id >= firstCSSProperty);

                if (!animateAll) {
                    if (CSSPropertyMetadata::isInterpolableProperty(id))
                        listedProperties.set(id - firstCSSProperty);
                    else
                        continue;
                }

                // FIXME: We should transition if an !important property changes even when an animation is running,
                // but this is a bit hard to do with the current applyMatchedProperties system.
                PropertyHandle property = PropertyHandle(id);
                if (!update.activeInterpolationsForAnimations().contains(property)
                    && (!elementAnimations || !elementAnimations->cssAnimations().m_previousActiveInterpolationsForAnimations.contains(property))) {
                    calculateTransitionUpdateForProperty(id, *transitionData, i, oldStyle, style, activeTransitions, update, animatingElement);
                }
            }
        }
    }

    if (activeTransitions) {
        for (const auto& entry : *activeTransitions) {
            CSSPropertyID id = entry.key;
            if (!anyTransitionHadTransitionAll && !animationStyleRecalc && !listedProperties.get(id - firstCSSProperty)) {
                // TODO: Figure out why this fails on Chrome OS login page. crbug.com/365507
                // ASSERT(animation.playStateInternal() == Animation::Finished || !(elementAnimations && elementAnimations->isAnimationStyleChange()));
                update.cancelTransition(id);
            } else if (entry.value.animation->finishedInternal()) {
                update.finishTransition(id);
            }
        }
    }
}