| Index: third_party/WebKit/Source/core/animation/EffectInput.cpp
|
| diff --git a/third_party/WebKit/Source/core/animation/EffectInput.cpp b/third_party/WebKit/Source/core/animation/EffectInput.cpp
|
| index b05cc6cccff3c22cb1347c02b9f584be7eb6289a..34b9ef94bb1e6ccc9ddcb347fbac9755e431262e 100644
|
| --- a/third_party/WebKit/Source/core/animation/EffectInput.cpp
|
| +++ b/third_party/WebKit/Source/core/animation/EffectInput.cpp
|
| @@ -47,12 +47,106 @@
|
|
|
| namespace blink {
|
|
|
| +namespace {
|
| +
|
| +bool compareKeyframes(const RefPtr<StringKeyframe>& a, const RefPtr<StringKeyframe>& b)
|
| +{
|
| + return a->offset() < b->offset();
|
| +}
|
| +
|
| +// Gets offset value from keyframeDictionary and returns false if this value was invalid.
|
| +bool getAndCheckOffset(const Dictionary& keyframeDictionary, double& offset, double lastOffset, ExceptionState& exceptionState)
|
| +{
|
| + DictionaryHelper::get(keyframeDictionary, "offset", offset);
|
| +
|
| + // Keyframes with offsets outside the range [0.0, 1.0] are an error.
|
| + if (std::isnan(offset)) {
|
| + exceptionState.throwTypeError("Non numeric offset provided");
|
| + return false;
|
| + }
|
| +
|
| + if (offset < 0 || offset > 1) {
|
| + exceptionState.throwTypeError("Offsets provided outside the range [0, 1]");
|
| + return false;
|
| + }
|
| +
|
| + if (offset < lastOffset) {
|
| + exceptionState.throwTypeError("Keyframes with specified offsets are not sorted");
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +// Returns true if the property passed in is a compositable property.
|
| +bool setKeyframeValue(Element& element, StringKeyframe& keyframe, const String& property, const String& value)
|
| +{
|
| + StyleSheetContents* styleSheetContents = element.document().elementSheet().contents();
|
| + CSSPropertyID cssProperty = AnimationInputHelpers::keyframeAttributeToCSSProperty(property, element.document());
|
| + if (cssProperty != CSSPropertyInvalid) {
|
| + keyframe.setCSSPropertyValue(cssProperty, value, &element, styleSheetContents);
|
| + return CompositorAnimations::isCompositableProperty(cssProperty);
|
| + }
|
| + cssProperty = AnimationInputHelpers::keyframeAttributeToPresentationAttribute(property, element);
|
| + if (cssProperty != CSSPropertyInvalid) {
|
| + keyframe.setPresentationAttributeValue(cssProperty, value, &element, styleSheetContents);
|
| + return false;
|
| + }
|
| + const QualifiedName* svgAttribute = AnimationInputHelpers::keyframeAttributeToSVGAttribute(property, element);
|
| + if (svgAttribute)
|
| + keyframe.setSVGAttributeValue(*svgAttribute, value);
|
| + return false;
|
| +}
|
| +
|
| +EffectModel* createEffectModelFromKeyframes(Element& element, const StringKeyframeVector& keyframes, bool encounteredCompositableProperty, ExceptionState& exceptionState)
|
| +{
|
| + // TODO(alancutter): Remove this once composited animations no longer depend on AnimatableValues.
|
| + if (encounteredCompositableProperty && element.inActiveDocument())
|
| + element.document().updateLayoutTreeForNode(&element);
|
| +
|
| + StringKeyframeEffectModel* keyframeEffectModel = StringKeyframeEffectModel::create(keyframes);
|
| + if (!RuntimeEnabledFeatures::cssAdditiveAnimationsEnabled()) {
|
| + for (const auto& keyframeGroup : keyframeEffectModel->getPropertySpecificKeyframeGroups()) {
|
| + PropertyHandle property = keyframeGroup.key;
|
| + if (!property.isCSSProperty())
|
| + continue;
|
| +
|
| + for (const auto& keyframe : keyframeGroup.value->keyframes()) {
|
| + if (keyframe->isNeutral()) {
|
| + exceptionState.throwDOMException(NotSupportedError, "Partial keyframes are not supported.");
|
| + return nullptr;
|
| + }
|
| + if (keyframe->composite() != EffectModel::CompositeReplace) {
|
| + exceptionState.throwDOMException(NotSupportedError, "Additive animations are not supported.");
|
| + return nullptr;
|
| + }
|
| + }
|
| + }
|
| + }
|
| + keyframeEffectModel->forceConversionsToAnimatableValues(element, element.computedStyle());
|
| +
|
| + return keyframeEffectModel;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +EffectModel* EffectInput::convert(Element* element, const EffectModelOrDictionarySequenceOrDictionary& effectInput, ExceptionState& exceptionState)
|
| +{
|
| + if (effectInput.isEffectModel())
|
| + return effectInput.getAsEffectModel();
|
| + if (effectInput.isDictionarySequence())
|
| + return convert(element, effectInput.getAsDictionarySequence(), exceptionState);
|
| + if (effectInput.isDictionary()) {
|
| + return convert(element, effectInput.getAsDictionary(), exceptionState);
|
| + }
|
| + return nullptr;
|
| +}
|
| +
|
| EffectModel* EffectInput::convert(Element* element, const Vector<Dictionary>& keyframeDictionaryVector, ExceptionState& exceptionState)
|
| {
|
| if (!element)
|
| return nullptr;
|
|
|
| - StyleSheetContents* styleSheetContents = element->document().elementSheet().contents();
|
| StringKeyframeVector keyframes;
|
| double lastOffset = 0;
|
| bool encounteredCompositableProperty = false;
|
| @@ -63,30 +157,13 @@ EffectModel* EffectInput::convert(Element* element, const Vector<Dictionary>& ke
|
| ScriptValue scriptValue;
|
| bool frameHasOffset = DictionaryHelper::get(keyframeDictionary, "offset", scriptValue) && !scriptValue.isNull();
|
|
|
| + double offset = 0.0;
|
| 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");
|
| + if (!getAndCheckOffset(keyframeDictionary, offset, lastOffset, exceptionState))
|
| return nullptr;
|
| - }
|
| -
|
| lastOffset = offset;
|
| -
|
| keyframe->setOffset(offset);
|
| }
|
| - keyframes.append(keyframe);
|
|
|
| String compositeString;
|
| DictionaryHelper::get(keyframeDictionary, "composite", compositeString);
|
| @@ -103,76 +180,94 @@ EffectModel* EffectInput::convert(Element* element, const Vector<Dictionary>& ke
|
| Vector<String> keyframeProperties;
|
| keyframeDictionary.getPropertyNames(keyframeProperties);
|
| for (const auto& property : keyframeProperties) {
|
| - String value;
|
| - DictionaryHelper::get(keyframeDictionary, property, value);
|
| -
|
| - CSSPropertyID cssProperty = AnimationInputHelpers::keyframeAttributeToCSSProperty(property, element->document());
|
| - if (cssProperty != CSSPropertyInvalid) {
|
| - if (!encounteredCompositableProperty && CompositorAnimations::isCompositableProperty(cssProperty))
|
| - encounteredCompositableProperty = true;
|
| -
|
| - keyframe->setCSSPropertyValue(cssProperty, value, element, styleSheetContents);
|
| - continue;
|
| - }
|
| -
|
| if (property == "offset"
|
| || property == "composite"
|
| || property == "easing") {
|
| continue;
|
| }
|
|
|
| - cssProperty = AnimationInputHelpers::keyframeAttributeToPresentationAttribute(property, *element);
|
| - if (cssProperty != CSSPropertyInvalid) {
|
| - keyframe->setPresentationAttributeValue(cssProperty, value, element, styleSheetContents);
|
| - continue;
|
| + Vector<String> values;
|
| + if (DictionaryHelper::get(keyframeDictionary, property, values)) {
|
| + exceptionState.throwTypeError("Lists of values not permitted in array-form list of keyframes");
|
| + return nullptr;
|
| }
|
|
|
| - const QualifiedName* svgAttribute = AnimationInputHelpers::keyframeAttributeToSVGAttribute(property, *element);
|
| - if (svgAttribute)
|
| - keyframe->setSVGAttributeValue(*svgAttribute, value);
|
| + String value;
|
| + DictionaryHelper::get(keyframeDictionary, property, value);
|
| +
|
| + encounteredCompositableProperty |= setKeyframeValue(*element, *keyframe.get(), property, value);
|
| }
|
| + keyframes.append(keyframe);
|
| }
|
|
|
| - // TODO(alancutter): Remove this once composited animations no longer depend on AnimatableValues.
|
| - if (encounteredCompositableProperty && element->inActiveDocument())
|
| - element->document().updateLayoutTreeForNode(element);
|
| + return createEffectModelFromKeyframes(*element, keyframes, encounteredCompositableProperty, exceptionState);
|
| +}
|
|
|
| - StringKeyframeEffectModel* keyframeEffectModel = StringKeyframeEffectModel::create(keyframes);
|
| - if (!RuntimeEnabledFeatures::cssAdditiveAnimationsEnabled()) {
|
| - for (const auto& keyframeGroup : keyframeEffectModel->getPropertySpecificKeyframeGroups()) {
|
| - PropertyHandle property = keyframeGroup.key;
|
| - if (!property.isCSSProperty())
|
| - continue;
|
| +EffectModel* EffectInput::convert(Element* element, const Dictionary& keyframeDictionary, ExceptionState& exceptionState)
|
| +{
|
| + if (!element)
|
| + return nullptr;
|
|
|
| - for (const auto& keyframe : keyframeGroup.value->keyframes()) {
|
| - if (keyframe->isNeutral()) {
|
| - exceptionState.throwDOMException(NotSupportedError, "Partial keyframes are not supported.");
|
| - return nullptr;
|
| - }
|
| - if (keyframe->composite() != EffectModel::CompositeReplace) {
|
| - exceptionState.throwDOMException(NotSupportedError, "Additive animations are not supported.");
|
| - return nullptr;
|
| - }
|
| - }
|
| + StringKeyframeVector keyframes;
|
| + bool encounteredCompositableProperty = false;
|
| +
|
| + String timingFunctionString;
|
| + RefPtr<TimingFunction> timingFunction = nullptr;
|
| + if (DictionaryHelper::get(keyframeDictionary, "easing", timingFunctionString))
|
| + timingFunction = AnimationInputHelpers::parseTimingFunction(timingFunctionString);
|
| +
|
| + ScriptValue scriptValue;
|
| + bool frameHasOffset = DictionaryHelper::get(keyframeDictionary, "offset", scriptValue) && !scriptValue.isNull();
|
| + double offset = 0.0;
|
| + if (frameHasOffset && !getAndCheckOffset(keyframeDictionary, offset, 0.0, exceptionState))
|
| + return nullptr;
|
| +
|
| + String compositeString;
|
| + DictionaryHelper::get(keyframeDictionary, "composite", compositeString);
|
| +
|
| + Vector<String> keyframeProperties;
|
| + keyframeDictionary.getPropertyNames(keyframeProperties);
|
| + for (const auto& property : keyframeProperties) {
|
| + if (property == "offset"
|
| + || property == "composite"
|
| + || property == "easing") {
|
| + continue;
|
| }
|
| - }
|
| - keyframeEffectModel->forceConversionsToAnimatableValues(*element, element->computedStyle());
|
|
|
| - return keyframeEffectModel;
|
| -}
|
| + Vector<String> values;
|
| + bool isList = DictionaryHelper::get(keyframeDictionary, property, values);
|
| + if (!isList) {
|
| + String value;
|
| + DictionaryHelper::get(keyframeDictionary, property, value);
|
| + values.append(value);
|
| + }
|
|
|
| -EffectModel* EffectInput::convert(Element* element, const EffectModelOrDictionarySequenceOrDictionary& effectInput, ExceptionState& exceptionState)
|
| -{
|
| - if (effectInput.isEffectModel())
|
| - return effectInput.getAsEffectModel();
|
| - if (effectInput.isDictionarySequence())
|
| - return convert(element, effectInput.getAsDictionarySequence(), exceptionState);
|
| - if (effectInput.isDictionary()) {
|
| - Vector<Dictionary> keyframes;
|
| - keyframes.append(effectInput.getAsDictionary());
|
| - return convert(element, keyframes, exceptionState);
|
| + size_t numKeyframes = values.size();
|
| + for (size_t i = 0; i < numKeyframes; ++i) {
|
| + RefPtr<StringKeyframe> keyframe = StringKeyframe::create();
|
| +
|
| + if (frameHasOffset)
|
| + keyframe->setOffset(offset);
|
| + else if (numKeyframes == 1)
|
| + keyframe->setOffset(1.0);
|
| + else
|
| + keyframe->setOffset(i / (numKeyframes - 1.0));
|
| +
|
| + if (timingFunction)
|
| + keyframe->setEasing(timingFunction);
|
| +
|
| + if (compositeString == "add")
|
| + keyframe->setComposite(EffectModel::CompositeAdd);
|
| + // TODO(alancutter): Support "accumulate" keyframe composition.
|
| +
|
| + encounteredCompositableProperty |= setKeyframeValue(*element, *keyframe.get(), property, values[i]);
|
| + keyframes.append(keyframe);
|
| + }
|
| }
|
| - return nullptr;
|
| +
|
| + std::sort(keyframes.begin(), keyframes.end(), compareKeyframes);
|
| +
|
| + return createEffectModelFromKeyframes(*element, keyframes, encounteredCompositableProperty, exceptionState);
|
| }
|
|
|
| } // namespace blink
|
|
|