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);
}
PassRefPtrWillBeRawPtr<EffectModel> EffectInput::convert(Element* element, const Vector<Dictionary>& keyframeDictionaryVector, ExceptionState& exceptionState)
{
    if (!element)
        return nullptr;

    // TODO(alancutter): Remove this once composited animations no longer depend on AnimatableValues.
    if (element->inActiveDocument())
        element->document().updateLayoutTreeForNodeIfNeeded(element);

    StyleSheetContents* styleSheetContents = element->document().elementSheet().contents();
    StringKeyframeVector keyframes;
    double lastOffset = 0;

    for (const auto& keyframeDictionary : keyframeDictionaryVector) {
        RefPtrWillBeRawPtr<StringKeyframe> keyframe = StringKeyframe::create();

        ScriptValue scriptValue;
        bool frameHasOffset = DictionaryHelper::get(keyframeDictionary, "offset", scriptValue) && !scriptValue.isNull();

        if (frameHasOffset) {
            double offset;
            DictionaryHelper::get(keyframeDictionary, "offset", offset);

            // Keyframes with offsets outside the range [0.0, 1.0] are an error.
            if (std::isnan(offset)) {
                exceptionState.throwDOMException(InvalidModificationError, "Non numeric offset provided");
            }

            if (offset < 0 || offset > 1) {
                exceptionState.throwDOMException(InvalidModificationError, "Offsets provided outside the range [0, 1]");
                return nullptr;
            }

            if (offset < lastOffset) {
                exceptionState.throwDOMException(InvalidModificationError, "Keyframes with specified offsets are not sorted");
                return nullptr;
            }

            lastOffset = offset;

            keyframe->setOffset(offset);
        }
        keyframes.append(keyframe);

        String compositeString;
        DictionaryHelper::get(keyframeDictionary, "composite", compositeString);
        if (compositeString == "add")
            keyframe->setComposite(EffectModel::CompositeAdd);

        String timingFunctionString;
        if (DictionaryHelper::get(keyframeDictionary, "easing", timingFunctionString)) {
            if (RefPtr<TimingFunction> timingFunction = AnimationInputHelpers::parseTimingFunction(timingFunctionString))
                keyframe->setEasing(timingFunction);
        }

        Vector<String> keyframeProperties;
        keyframeDictionary.getPropertyNames(keyframeProperties);
        for (const auto& property : keyframeProperties) {
            String value;
            DictionaryHelper::get(keyframeDictionary, property, value);
            CSSPropertyID id = AnimationInputHelpers::keyframeAttributeToCSSPropertyID(property);
            if (id != CSSPropertyInvalid) {
                keyframe->setPropertyValue(id, value, element, styleSheetContents);
                continue;
            }

            if (property == "offset"
                || property == "composite"
                || property == "easing") {
                continue;
            }

            if (!RuntimeEnabledFeatures::webAnimationsSVGEnabled() || !element->isSVGElement() || !svgPrefixed(property))
                continue;

            SVGElement* svgElement = toSVGElement(element);
            const QualifiedName* qualifiedName = supportedSVGAttribute(property, svgElement);

            if (qualifiedName)
                keyframe->setPropertyValue(*qualifiedName, value, svgElement);
        }
    }

    RefPtrWillBeRawPtr<StringKeyframeEffectModel> keyframeEffectModel = StringKeyframeEffectModel::create(keyframes);
    if (keyframeEffectModel->hasSyntheticKeyframes()) {
        exceptionState.throwDOMException(NotSupportedError, "Partial keyframes are not supported.");
        return nullptr;
    }
    if (!keyframeEffectModel->isReplaceOnly()) {
        exceptionState.throwDOMException(NotSupportedError, "Additive animations are not supported.");
        return nullptr;
    }
    keyframeEffectModel->forceConversionsToAnimatableValues(*element, element->computedStyle());

    return keyframeEffectModel;
}
Beispiel #3
0
PassRefPtrWillBeRawPtr<AnimationEffect> EffectInput::convert(Element* element, const Vector<Dictionary>& keyframeDictionaryVector, ExceptionState& exceptionState)
{
    // FIXME: Remove the dependency on element.
    if (!element)
        return nullptr;

    StyleSheetContents* styleSheetContents = element->document().elementSheet().contents();
    StringKeyframeVector keyframes;
    double lastOffset = 0;

    for (const auto& keyframeDictionary : keyframeDictionaryVector) {
        RefPtrWillBeRawPtr<StringKeyframe> keyframe = StringKeyframe::create();

        ScriptValue scriptValue;
        bool frameHasOffset = DictionaryHelper::get(keyframeDictionary, "offset", scriptValue) && !scriptValue.isNull();

        if (frameHasOffset) {
            double offset;
            DictionaryHelper::get(keyframeDictionary, "offset", offset);

            // Keyframes with offsets outside the range [0.0, 1.0] are an error.
            if (std::isnan(offset)) {
                exceptionState.throwDOMException(InvalidModificationError, "Non numeric offset provided");
            }

            if (offset < 0 || offset > 1) {
                exceptionState.throwDOMException(InvalidModificationError, "Offsets provided outside the range [0, 1]");
                return nullptr;
            }

            if (offset < lastOffset) {
                exceptionState.throwDOMException(InvalidModificationError, "Keyframes with specified offsets are not sorted");
                return nullptr;
            }

            lastOffset = offset;

            keyframe->setOffset(offset);
        }
        keyframes.append(keyframe);

        String compositeString;
        DictionaryHelper::get(keyframeDictionary, "composite", compositeString);
        if (compositeString == "add")
            keyframe->setComposite(AnimationEffect::CompositeAdd);

        String timingFunctionString;
        if (DictionaryHelper::get(keyframeDictionary, "easing", timingFunctionString)) {
            if (RefPtr<TimingFunction> timingFunction = AnimationInputHelpers::parseTimingFunction(timingFunctionString))
                keyframe->setEasing(timingFunction);
        }

        Vector<String> keyframeProperties;
        keyframeDictionary.getPropertyNames(keyframeProperties);
        for (const auto& property : keyframeProperties) {
            CSSPropertyID id = AnimationInputHelpers::keyframeAttributeToCSSPropertyID(property);
            if (id == CSSPropertyInvalid)
                continue;
            String value;
            DictionaryHelper::get(keyframeDictionary, property, value);
            keyframe->setPropertyValue(id, value, styleSheetContents);
        }
    }

    RefPtrWillBeRawPtr<StringKeyframeEffectModel> keyframeEffectModel = StringKeyframeEffectModel::create(keyframes);
    if (keyframeEffectModel->hasSyntheticKeyframes()) {
        exceptionState.throwDOMException(NotSupportedError, "Partial keyframes are not supported.");
        return nullptr;
    }
    if (!keyframeEffectModel->isReplaceOnly()) {
        exceptionState.throwDOMException(NotSupportedError, "Additive animations are not supported.");
        return nullptr;
    }
    keyframeEffectModel->forceConversionsToAnimatableValues(element);

    return keyframeEffectModel;
}
Beispiel #4
0
PassRefPtrWillBeRawPtr<AnimationEffect> EffectInput::convert(Element* element, const Vector<Dictionary>& keyframeDictionaryVector, ExceptionState& exceptionState)
{
    // FIXME: Remove the dependency on element.
    if (!element)
        return nullptr;

    StyleSheetContents* styleSheetContents = element->document().elementSheet().contents();
    StringKeyframeVector keyframes;
    double lastOffset = 0;

    for (size_t i = 0; i < keyframeDictionaryVector.size(); ++i) {
        RefPtrWillBeRawPtr<StringKeyframe> keyframe = StringKeyframe::create();

        ScriptValue scriptValue;
        bool frameHasOffset = DictionaryHelper::get(keyframeDictionaryVector[i], "offset", scriptValue) && !scriptValue.isNull();

        if (frameHasOffset) {
            double offset;
            DictionaryHelper::get(keyframeDictionaryVector[i], "offset", offset);

            // Keyframes with offsets outside the range [0.0, 1.0] are an error.
            if (std::isnan(offset)) {
                exceptionState.throwDOMException(InvalidModificationError, "Non numeric offset provided");
            }

            if (offset < 0 || offset > 1) {
                exceptionState.throwDOMException(InvalidModificationError, "Offsets provided outside the range [0, 1]");
                return nullptr;
            }

            if (offset < lastOffset) {
                exceptionState.throwDOMException(InvalidModificationError, "Keyframes with specified offsets are not sorted");
                return nullptr;
            }

            lastOffset = offset;

            keyframe->setOffset(offset);
        }
        keyframes.append(keyframe);

        String compositeString;
        DictionaryHelper::get(keyframeDictionaryVector[i], "composite", compositeString);
        if (compositeString == "add")
            keyframe->setComposite(AnimationEffect::CompositeAdd);

        String timingFunctionString;
        if (DictionaryHelper::get(keyframeDictionaryVector[i], "easing", timingFunctionString)) {
            if (RefPtrWillBeRawPtr<CSSValue> timingFunctionValue = BisonCSSParser::parseAnimationTimingFunctionValue(timingFunctionString))
                keyframe->setEasing(CSSToStyleMap::mapAnimationTimingFunction(timingFunctionValue.get(), true));
        }

        Vector<String> keyframeProperties;
        keyframeDictionaryVector[i].getOwnPropertyNames(keyframeProperties);
        for (size_t j = 0; j < keyframeProperties.size(); ++j) {
            String property = keyframeProperties[j];
            CSSPropertyID id = camelCaseCSSPropertyNameToID(property);
            if (id == CSSPropertyInvalid)
                continue;
            String value;
            DictionaryHelper::get(keyframeDictionaryVector[i], property, value);
            keyframe->setPropertyValue(id, value, styleSheetContents);
        }
    }

    RefPtrWillBeRawPtr<StringKeyframeEffectModel> keyframeEffectModel = StringKeyframeEffectModel::create(keyframes);
    if (!keyframeEffectModel->isReplaceOnly()) {
        exceptionState.throwDOMException(NotSupportedError, "Partial keyframes are not supported.");
        return nullptr;
    }
    keyframeEffectModel->forceConversionsToAnimatableValues(element);

    return keyframeEffectModel;
}