bool CompositorAnimations::isCandidateForAnimationOnCompositor(const Timing& timing, const Element& targetElement, const AnimationPlayer* playerToAdd, const AnimationEffect& effect, double playerPlaybackRate)
{
    const KeyframeEffectModelBase& keyframeEffect = toKeyframeEffectModelBase(effect);

    PropertySet properties = keyframeEffect.properties();
    if (properties.isEmpty())
        return false;

    for (const auto& property : properties) {
        const PropertySpecificKeyframeVector& keyframes = keyframeEffect.getPropertySpecificKeyframes(property);
        ASSERT(keyframes.size() >= 2);
        for (const auto& keyframe : keyframes) {
            // FIXME: Determine candidacy based on the CSSValue instead of a snapshot AnimatableValue.
            bool isNeutralKeyframe = keyframe->isStringPropertySpecificKeyframe() && !toStringPropertySpecificKeyframe(keyframe.get())->value() && keyframe->composite() == AnimationEffect::CompositeAdd;
            if ((keyframe->composite() != AnimationEffect::CompositeReplace && !isNeutralKeyframe) || !keyframe->getAnimatableValue())
                return false;

            switch (property) {
            case CSSPropertyOpacity:
                break;
            case CSSPropertyTransform:
                if (toAnimatableTransform(keyframe->getAnimatableValue().get())->transformOperations().dependsOnBoxSize())
                    return false;
                break;
            case CSSPropertyWebkitFilter: {
                const FilterOperations& operations = toAnimatableFilterOperations(keyframe->getAnimatableValue().get())->operations();
                if (operations.hasFilterThatMovesPixels())
                    return false;
                break;
            }
            default:
                // any other types are not allowed to run on compositor.
                return false;
            }
        }
    }

    if (playerToAdd && hasIncompatibleAnimations(targetElement, *playerToAdd, effect))
        return false;

    CompositorAnimationsImpl::CompositorTiming out;
    if (!CompositorAnimationsImpl::convertTimingForCompositor(timing, 0, out, playerPlaybackRate))
        return false;

    return true;
}
bool CompositorAnimations::isCandidateForAnimationOnCompositor(const Timing& timing, const Element& targetElement, const Animation* animationToAdd, const EffectModel& effect, double animationPlaybackRate)
{
    const KeyframeEffectModelBase& keyframeEffect = toKeyframeEffectModelBase(effect);

    PropertyHandleSet properties = keyframeEffect.properties();
    if (properties.isEmpty())
        return false;

    unsigned transformPropertyCount = 0;
    for (const auto& property : properties) {
        if (!property.isCSSProperty())
            return false;

        if (isTransformRelatedCSSProperty(property)) {
            if (targetElement.layoutObject() && targetElement.layoutObject()->isInline()) {
                return false;
            }
            transformPropertyCount++;
        }

        const PropertySpecificKeyframeVector& keyframes = keyframeEffect.getPropertySpecificKeyframes(property);
        ASSERT(keyframes.size() >= 2);
        for (const auto& keyframe : keyframes) {
            // FIXME: Determine candidacy based on the CSSValue instead of a snapshot AnimatableValue.
            bool isNeutralKeyframe = keyframe->isCSSPropertySpecificKeyframe() && !toCSSPropertySpecificKeyframe(keyframe.get())->value() && keyframe->composite() == EffectModel::CompositeAdd;
            if ((keyframe->composite() != EffectModel::CompositeReplace && !isNeutralKeyframe) || !keyframe->getAnimatableValue())
                return false;

            switch (property.cssProperty()) {
            case CSSPropertyOpacity:
                break;
            case CSSPropertyRotate:
            case CSSPropertyScale:
            case CSSPropertyTranslate:
            case CSSPropertyTransform:
                if (toAnimatableTransform(keyframe->getAnimatableValue().get())->transformOperations().dependsOnBoxSize())
                    return false;
                break;
            case CSSPropertyWebkitFilter:
            case CSSPropertyBackdropFilter: {
                const FilterOperations& operations = toAnimatableFilterOperations(keyframe->getAnimatableValue().get())->operations();
                if (operations.hasFilterThatMovesPixels())
                    return false;
                break;
            }
            default:
                // any other types are not allowed to run on compositor.
                return false;
            }
        }
    }

    // TODO: Support multiple transform property animations on the compositor
    if (transformPropertyCount > 1)
        return false;

    if (animationToAdd && hasIncompatibleAnimations(targetElement, *animationToAdd, effect))
        return false;

    CompositorAnimationsImpl::CompositorTiming out;
    if (!CompositorAnimationsImpl::convertTimingForCompositor(timing, 0, out, animationPlaybackRate))
        return false;

    return true;
}