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; }
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; }
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; }