コード例 #1
0
TEST(AnimationEffectInputTest, LooslySorted) {
  V8TestingScope scope;
  Vector<Dictionary> jsKeyframes;
  v8::Local<v8::Object> keyframe1 = v8::Object::New(scope.isolate());
  v8::Local<v8::Object> keyframe2 = v8::Object::New(scope.isolate());
  v8::Local<v8::Object> keyframe3 = v8::Object::New(scope.isolate());

  setV8ObjectPropertyAsString(scope.isolate(), keyframe1, "width", "100px");
  setV8ObjectPropertyAsString(scope.isolate(), keyframe1, "offset", "0");
  setV8ObjectPropertyAsString(scope.isolate(), keyframe2, "width", "200px");
  setV8ObjectPropertyAsString(scope.isolate(), keyframe3, "width", "0px");
  setV8ObjectPropertyAsString(scope.isolate(), keyframe3, "offset", "1");

  jsKeyframes.push_back(
      Dictionary(scope.isolate(), keyframe1, scope.getExceptionState()));
  jsKeyframes.push_back(
      Dictionary(scope.isolate(), keyframe2, scope.getExceptionState()));
  jsKeyframes.push_back(
      Dictionary(scope.isolate(), keyframe3, scope.getExceptionState()));

  Element* element = appendElement(scope.document());
  EffectModel* animationEffect = EffectInput::convert(
      element,
      DictionarySequenceOrDictionary::fromDictionarySequence(jsKeyframes),
      nullptr, scope.getExceptionState());
  EXPECT_FALSE(scope.getExceptionState().hadException());
  const KeyframeEffectModelBase& keyframeEffect =
      *toKeyframeEffectModelBase(animationEffect);
  EXPECT_EQ(1, keyframeEffect.getFrames()[2]->offset());
}
コード例 #2
0
void InspectorAnimationAgent::setTiming(ErrorString* errorString, const String& animationId, double duration, double delay)
{
    Animation* animation = assertAnimation(errorString, animationId);
    if (!animation)
        return;

    AnimationType type = m_idToAnimationType.get(animationId);
    if (type == AnimationType::CSSTransition) {
        KeyframeEffect* effect = toKeyframeEffect(animation->effect());
        KeyframeEffectModelBase* model = toKeyframeEffectModelBase(effect->model());
        const AnimatableValueKeyframeEffectModel* oldModel = toAnimatableValueKeyframeEffectModel(model);
        // Refer to CSSAnimations::calculateTransitionUpdateForProperty() for the structure of transitions.
        const KeyframeVector& frames = oldModel->getFrames();
        ASSERT(frames.size() == 3);
        KeyframeVector newFrames;
        for (int i = 0; i < 3; i++)
            newFrames.append(toAnimatableValueKeyframe(frames[i]->clone().get()));
        // Update delay, represented by the distance between the first two keyframes.
        newFrames[1]->setOffset(delay / (delay + duration));
        model->setFrames(newFrames);

        AnimationEffectTiming* timing = animation->effect()->timing();
        UnrestrictedDoubleOrString unrestrictedDuration;
        unrestrictedDuration.setUnrestrictedDouble(duration + delay);
        timing->setDuration(unrestrictedDuration);
    } else if (type == AnimationType::WebAnimation) {
        AnimationEffectTiming* timing = animation->effect()->timing();
        UnrestrictedDoubleOrString unrestrictedDuration;
        unrestrictedDuration.setUnrestrictedDouble(duration);
        timing->setDuration(unrestrictedDuration);
        timing->setDelay(delay);
    }
}
コード例 #3
0
bool CompositorAnimations::startAnimationOnCompositor(const Element& element, int group, double startTime, double timeOffset, const Timing& timing, const AnimationPlayer& player, const AnimationEffect& effect, Vector<int>& startedAnimationIds, double playerPlaybackRate)
{
    ASSERT(startedAnimationIds.isEmpty());
    ASSERT(isCandidateForAnimationOnCompositor(timing, element, &player, effect, playerPlaybackRate));
    ASSERT(canStartAnimationOnCompositor(element));

    const KeyframeEffectModelBase& keyframeEffect = toKeyframeEffectModelBase(effect);

    DeprecatedPaintLayer* layer = toLayoutBoxModelObject(element.layoutObject())->layer();
    ASSERT(layer);

    Vector<OwnPtr<WebCompositorAnimation>> animations;
    CompositorAnimationsImpl::getAnimationOnCompositor(timing, group, startTime, timeOffset, keyframeEffect, animations, playerPlaybackRate);
    ASSERT(!animations.isEmpty());
    for (auto& animation : animations) {
        int id = animation->id();
        if (RuntimeEnabledFeatures::compositorAnimationTimelinesEnabled()) {
            WebCompositorAnimationPlayer* compositorPlayer = player.compositorPlayer();
            ASSERT(compositorPlayer);
            compositorPlayer->addAnimation(animation.leakPtr());
        } else if (!layer->compositedDeprecatedPaintLayerMapping()->mainGraphicsLayer()->addAnimation(animation.release())) {
            // FIXME: We should know ahead of time whether these animations can be started.
            for (int startedAnimationId : startedAnimationIds)
                cancelAnimationOnCompositor(element, player, startedAnimationId);
            startedAnimationIds.clear();
            return false;
        }
        startedAnimationIds.append(id);
    }
    ASSERT(!startedAnimationIds.isEmpty());
    return true;
}
コード例 #4
0
bool CompositorAnimations::getAnimatedBoundingBox(FloatBox& box, const AnimationEffect& effect, double minValue, double maxValue) const
{
    const KeyframeEffectModelBase& keyframeEffect = toKeyframeEffectModelBase(effect);

    PropertySet properties = keyframeEffect.properties();

    if (properties.isEmpty())
        return true;

    minValue = std::min(minValue, 0.0);
    maxValue = std::max(maxValue, 1.0);

    for (const auto& property : properties) {
        // TODO: Add the ability to get expanded bounds for filters as well.
        if (property != CSSPropertyTransform && property != CSSPropertyWebkitTransform)
            continue;

        const PropertySpecificKeyframeVector& frames = keyframeEffect.getPropertySpecificKeyframes(property);
        if (frames.isEmpty() || frames.size() < 2)
            continue;

        FloatBox originalBox(box);

        for (size_t j = 0; j < frames.size() - 1; ++j) {
            const AnimatableTransform* startTransform = toAnimatableTransform(frames[j]->getAnimatableValue().get());
            const AnimatableTransform* endTransform = toAnimatableTransform(frames[j+1]->getAnimatableValue().get());
            if (!startTransform || !endTransform)
                return false;

            // TODO: Add support for inflating modes other than Replace.
            if (frames[j]->composite() != AnimationEffect::CompositeReplace)
                return false;

            const TimingFunction& timing = frames[j]->easing();
            double min = 0;
            double max = 1;
            if (j == 0) {
                float frameLength = frames[j+1]->offset();
                if (frameLength > 0) {
                    min = minValue / frameLength;
                }
            }

            if (j == frames.size() - 2) {
                float frameLength = frames[j+1]->offset() - frames[j]->offset();
                if (frameLength > 0) {
                    max = 1 + (maxValue - 1) / frameLength;
                }
            }

            FloatBox bounds;
            timing.range(&min, &max);
            if (!endTransform->transformOperations().blendedBoundsForBox(originalBox, startTransform->transformOperations(), min, max, &bounds))
                return false;
            box.expandTo(bounds);
        }
    }
    return true;
}
コード例 #5
0
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);
                }
            }
        }
    }
}
コード例 #6
0
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;
}
コード例 #7
0
TEST_F(AnimationAnimationV8Test, CanCreateAnAnimation)
{
    Vector<Dictionary> jsKeyframes;
    v8::Handle<v8::Object> keyframe1 = v8::Object::New(m_isolate);
    v8::Handle<v8::Object> keyframe2 = v8::Object::New(m_isolate);

    setV8ObjectPropertyAsString(keyframe1, "width", "100px");
    setV8ObjectPropertyAsString(keyframe1, "offset", "0");
    setV8ObjectPropertyAsString(keyframe1, "easing", "ease-in-out");
    setV8ObjectPropertyAsString(keyframe2, "width", "0px");
    setV8ObjectPropertyAsString(keyframe2, "offset", "1");
    setV8ObjectPropertyAsString(keyframe2, "easing", "cubic-bezier(1, 1, 0.3, 0.3)");

    jsKeyframes.append(Dictionary(keyframe1, m_isolate));
    jsKeyframes.append(Dictionary(keyframe2, m_isolate));

    String value1;
    ASSERT_TRUE(jsKeyframes[0].get("width", value1));
    ASSERT_EQ("100px", value1);

    String value2;
    ASSERT_TRUE(jsKeyframes[1].get("width", value2));
    ASSERT_EQ("0px", value2);

    RefPtrWillBeRawPtr<Animation> animation = createAnimation(element.get(), jsKeyframes, 0, exceptionState);

    Element* target = animation->target();
    EXPECT_EQ(*element.get(), *target);

    const KeyframeVector keyframes = toKeyframeEffectModelBase(animation->effect())->getFrames();

    EXPECT_EQ(0, keyframes[0]->offset());
    EXPECT_EQ(1, keyframes[1]->offset());

    const CSSValue* keyframe1Width = toStringKeyframe(keyframes[0].get())->propertyValue(CSSPropertyWidth);
    const CSSValue* keyframe2Width = toStringKeyframe(keyframes[1].get())->propertyValue(CSSPropertyWidth);
    ASSERT(keyframe1Width);
    ASSERT(keyframe2Width);

    EXPECT_EQ("100px", keyframe1Width->cssText());
    EXPECT_EQ("0px", keyframe2Width->cssText());

    EXPECT_EQ(*(CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseInOut)), *keyframes[0]->easing());
    EXPECT_EQ(*(CubicBezierTimingFunction::create(1, 1, 0.3, 0.3).get()), *keyframes[1]->easing());
}
コード例 #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);
}
コード例 #9
0
static PassRefPtr<TypeBuilder::Animation::KeyframesRule> buildObjectForAnimationKeyframes(const KeyframeEffect* effect)
{
    if (!effect || !effect->model() || !effect->model()->isKeyframeEffectModel())
        return nullptr;
    const KeyframeEffectModelBase* model = toKeyframeEffectModelBase(effect->model());
    Vector<RefPtr<Keyframe>> normalizedKeyframes = KeyframeEffectModelBase::normalizedKeyframesForInspector(model->getFrames());
    RefPtr<TypeBuilder::Array<TypeBuilder::Animation::KeyframeStyle> > keyframes = TypeBuilder::Array<TypeBuilder::Animation::KeyframeStyle>::create();

    for (const auto& keyframe : normalizedKeyframes) {
        // Ignore CSS Transitions
        if (!keyframe.get()->isStringKeyframe())
            continue;
        const StringKeyframe* stringKeyframe = toStringKeyframe(keyframe.get());
        keyframes->addItem(buildObjectForStringKeyframe(stringKeyframe));
    }
    RefPtr<TypeBuilder::Animation::KeyframesRule> keyframesObject = TypeBuilder::Animation::KeyframesRule::create()
        .setKeyframes(keyframes);
    return keyframesObject.release();
}
コード例 #10
0
TEST_F(AnimationEffectInputTest, SortedOffsets)
{
    Vector<Dictionary> jsKeyframes;
    v8::Local<v8::Object> keyframe1 = v8::Object::New(m_isolate);
    v8::Local<v8::Object> keyframe2 = v8::Object::New(m_isolate);

    setV8ObjectPropertyAsString(m_isolate, keyframe1, "width", "100px");
    setV8ObjectPropertyAsString(m_isolate, keyframe1, "offset", "0");
    setV8ObjectPropertyAsString(m_isolate, keyframe2, "width", "0px");
    setV8ObjectPropertyAsString(m_isolate, keyframe2, "offset", "1");

    jsKeyframes.append(Dictionary(keyframe1, m_isolate, exceptionState));
    jsKeyframes.append(Dictionary(keyframe2, m_isolate, exceptionState));

    RefPtrWillBeRawPtr<EffectModel> animationEffect = EffectInput::convert(element.get(), jsKeyframes, exceptionState);
    EXPECT_FALSE(exceptionState.hadException());
    const KeyframeEffectModelBase& keyframeEffect = *toKeyframeEffectModelBase(animationEffect.get());
    EXPECT_EQ(1.0, keyframeEffect.getFrames()[1]->offset());
}
コード例 #11
0
TEST_F(AnimationEffectInputTest, LooslySorted)
{
    Vector<Dictionary> jsKeyframes;
    v8::Local<v8::Object> keyframe1 = v8::Object::New(m_isolate);
    v8::Local<v8::Object> keyframe2 = v8::Object::New(m_isolate);
    v8::Local<v8::Object> keyframe3 = v8::Object::New(m_isolate);

    setV8ObjectPropertyAsString(m_isolate, keyframe1, "width", "100px");
    setV8ObjectPropertyAsString(m_isolate, keyframe1, "offset", "0");
    setV8ObjectPropertyAsString(m_isolate, keyframe2, "width", "200px");
    setV8ObjectPropertyAsString(m_isolate, keyframe3, "width", "0px");
    setV8ObjectPropertyAsString(m_isolate, keyframe3, "offset", "1");

    jsKeyframes.append(Dictionary(keyframe1, m_isolate, exceptionState));
    jsKeyframes.append(Dictionary(keyframe2, m_isolate, exceptionState));
    jsKeyframes.append(Dictionary(keyframe3, m_isolate, exceptionState));

    EffectModel* animationEffect = EffectInput::convert(element.get(), EffectModelOrDictionarySequenceOrDictionary::fromDictionarySequence(jsKeyframes), nullptr, exceptionState);
    EXPECT_FALSE(exceptionState.hadException());
    const KeyframeEffectModelBase& keyframeEffect = *toKeyframeEffectModelBase(animationEffect);
    EXPECT_EQ(1, keyframeEffect.getFrames()[2]->offset());
}
コード例 #12
0
static std::unique_ptr<protocol::Animation::AnimationEffect>
buildObjectForAnimationEffect(KeyframeEffect* effect, bool isTransition) {
  ComputedTimingProperties computedTiming = effect->getComputedTiming();
  double delay = computedTiming.delay();
  double duration = computedTiming.duration().getAsUnrestrictedDouble();
  String easing = effect->specifiedTiming().timingFunction->toString();

  if (isTransition) {
    // Obtain keyframes and convert keyframes back to delay
    ASSERT(effect->model()->isKeyframeEffectModel());
    const KeyframeEffectModelBase* model =
        toKeyframeEffectModelBase(effect->model());
    Vector<RefPtr<Keyframe>> keyframes =
        KeyframeEffectModelBase::normalizedKeyframesForInspector(
            model->getFrames());
    if (keyframes.size() == 3) {
      delay = keyframes.at(1)->offset() * duration;
      duration -= delay;
      easing = keyframes.at(1)->easing().toString();
    } else {
      easing = keyframes.at(0)->easing().toString();
    }
  }

  std::unique_ptr<protocol::Animation::AnimationEffect> animationObject =
      protocol::Animation::AnimationEffect::create()
          .setDelay(delay)
          .setEndDelay(computedTiming.endDelay())
          .setIterationStart(computedTiming.iterationStart())
          .setIterations(computedTiming.iterations())
          .setDuration(duration)
          .setDirection(computedTiming.direction())
          .setFill(computedTiming.fill())
          .setBackendNodeId(DOMNodeIds::idForNode(effect->target()))
          .setEasing(easing)
          .build();
  return animationObject;
}
コード例 #13
0
static std::unique_ptr<protocol::Animation::KeyframesRule>
buildObjectForAnimationKeyframes(const KeyframeEffect* effect) {
  if (!effect || !effect->model() || !effect->model()->isKeyframeEffectModel())
    return nullptr;
  const KeyframeEffectModelBase* model =
      toKeyframeEffectModelBase(effect->model());
  Vector<RefPtr<Keyframe>> normalizedKeyframes =
      KeyframeEffectModelBase::normalizedKeyframesForInspector(
          model->getFrames());
  std::unique_ptr<protocol::Array<protocol::Animation::KeyframeStyle>>
      keyframes = protocol::Array<protocol::Animation::KeyframeStyle>::create();

  for (const auto& keyframe : normalizedKeyframes) {
    // Ignore CSS Transitions
    if (!keyframe.get()->isStringKeyframe())
      continue;
    const StringKeyframe* stringKeyframe = toStringKeyframe(keyframe.get());
    keyframes->addItem(buildObjectForStringKeyframe(stringKeyframe));
  }
  return protocol::Animation::KeyframesRule::create()
      .setKeyframes(std::move(keyframes))
      .build();
}
コード例 #14
0
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;
}