void CompositorAnimationsImpl::addKeyframesToCurve(blink::WebAnimationCurve& curve, const KeyframeVector& keyframes, const TimingFunction& timingFunction)
{
    for (size_t i = 0; i < keyframes.size(); i++) {
        const TimingFunction* keyframeTimingFunction = 0;
        if (i + 1 < keyframes.size()) { // Last keyframe has no timing function
            switch (timingFunction.type()) {
            case TimingFunction::LinearFunction:
            case TimingFunction::CubicBezierFunction:
                keyframeTimingFunction = &timingFunction;
                break;

            case TimingFunction::ChainedFunction: {
                const ChainedTimingFunction& chained = toChainedTimingFunction(timingFunction);
                // ChainedTimingFunction criteria was checked in isCandidate,
                // assert it is valid.
                ASSERT(keyframes.size() == chained.m_segments.size() + 1);

                keyframeTimingFunction = chained.m_segments[i].m_timingFunction.get();
                break;
            }
            case TimingFunction::StepsFunction:
            default:
                ASSERT_NOT_REACHED();
            }
        }

        ASSERT(!keyframes[i]->value()->dependsOnUnderlyingValue());
        RefPtr<AnimatableValue> value = keyframes[i]->value()->compositeOnto(0);

        switch (curve.type()) {
        case blink::WebAnimationCurve::AnimationCurveTypeFilter: {
            OwnPtr<blink::WebFilterOperations> ops = adoptPtr(blink::Platform::current()->compositorSupport()->createFilterOperations());
            bool converted = toWebFilterOperations(toAnimatableFilterOperations(value.get())->operations(), ops.get());
            ASSERT_UNUSED(converted, converted);

            blink::WebFilterKeyframe filterKeyframe(keyframes[i]->offset(), ops.release());
            blink::WebFilterAnimationCurve* filterCurve = static_cast<blink::WebFilterAnimationCurve*>(&curve);
            addKeyframeWithTimingFunction(*filterCurve, filterKeyframe, keyframeTimingFunction);
            break;
        }
        case blink::WebAnimationCurve::AnimationCurveTypeFloat: {
            blink::WebFloatKeyframe floatKeyframe(keyframes[i]->offset(), toAnimatableDouble(value.get())->toDouble());
            blink::WebFloatAnimationCurve* floatCurve = static_cast<blink::WebFloatAnimationCurve*>(&curve);
            addKeyframeWithTimingFunction(*floatCurve, floatKeyframe, keyframeTimingFunction);
            break;
        }
        case blink::WebAnimationCurve::AnimationCurveTypeTransform: {
            OwnPtr<blink::WebTransformOperations> ops = adoptPtr(blink::Platform::current()->compositorSupport()->createTransformOperations());
            toWebTransformOperations(toAnimatableTransform(value.get())->transformOperations(), FloatSize(), ops.get());

            blink::WebTransformKeyframe transformKeyframe(keyframes[i]->offset(), ops.release());
            blink::WebTransformAnimationCurve* transformCurve = static_cast<blink::WebTransformAnimationCurve*>(&curve);
            addKeyframeWithTimingFunction(*transformCurve, transformKeyframe, keyframeTimingFunction);
            break;
        }
        default:
            ASSERT_NOT_REACHED();
        }
    }
}
// The generic PrintTo *must* come after the non-generic PrintTo otherwise it
// will end up calling itself.
void PrintTo(const TimingFunction& timingFunction, ::std::ostream* os)
{
    switch (timingFunction.type()) {
    case TimingFunction::LinearFunction: {
        const LinearTimingFunction& linear = toLinearTimingFunction(timingFunction);
        PrintTo(linear, os);
        return;
    }
    case TimingFunction::CubicBezierFunction: {
        const CubicBezierTimingFunction& cubic = toCubicBezierTimingFunction(timingFunction);
        PrintTo(cubic, os);
        return;
    }
    case TimingFunction::StepsFunction: {
        const StepsTimingFunction& step = toStepsTimingFunction(timingFunction);
        PrintTo(step, os);
        return;
    }
    case TimingFunction::ChainedFunction: {
        const ChainedTimingFunction& chained = toChainedTimingFunction(timingFunction);
        PrintTo(chained, os);
        return;
    }
    default:
        ASSERT_NOT_REACHED();
    }
}
    static bool equals(const ChainedTimingFunction& lhs, const TimingFunction& rhs)
    {
        if (rhs.type() != TimingFunction::ChainedFunction)
            return false;

        if (&lhs == &rhs)
            return true;

        const ChainedTimingFunction& ctf = toChainedTimingFunction(rhs);
        if (lhs.m_segments.size() != ctf.m_segments.size())
            return false;

        for (size_t i = 0; i < lhs.m_segments.size(); i++) {
            if (!equals(lhs.m_segments[i], ctf.m_segments[i]))
                return false;
        }
        return true;
    }
PassRefPtr<TimingFunction> CompositorAnimationsTimingFunctionReverser::reverse(const TimingFunction* timefunc)
{
    switch (timefunc->type()) {
    case TimingFunction::LinearFunction: {
        const LinearTimingFunction* linear = toLinearTimingFunction(timefunc);
        return reverse(linear);
    }
    case TimingFunction::CubicBezierFunction: {
        const CubicBezierTimingFunction* cubic = toCubicBezierTimingFunction(timefunc);
        return reverse(cubic);
    }
    case TimingFunction::ChainedFunction: {
        const ChainedTimingFunction* chained = toChainedTimingFunction(timefunc);
        return reverse(chained);
    }

    // Steps function can not be reversed.
    case TimingFunction::StepsFunction:
    default:
        ASSERT_NOT_REACHED();
        return PassRefPtr<TimingFunction>();
    }
}
// Like in the PrintTo case, the generic operator== *must* come after the
// non-generic operator== otherwise it will end up calling itself.
bool operator==(const TimingFunction& lhs, const TimingFunction& rhs)
{
    switch (lhs.type()) {
    case TimingFunction::LinearFunction: {
        const LinearTimingFunction& linear = toLinearTimingFunction(lhs);
        return (linear == rhs);
    }
    case TimingFunction::CubicBezierFunction: {
        const CubicBezierTimingFunction& cubic = toCubicBezierTimingFunction(lhs);
        return (cubic == rhs);
    }
    case TimingFunction::StepsFunction: {
        const StepsTimingFunction& step = toStepsTimingFunction(lhs);
        return (step == rhs);
    }
    case TimingFunction::ChainedFunction: {
        const ChainedTimingFunction& chained = toChainedTimingFunction(lhs);
        return (chained == rhs);
    }
    default:
        ASSERT_NOT_REACHED();
    }
    return false;
}