Chromium Code Reviews| 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..3b729571add8c96920f4f6b72062433b85bb08c9 100644 |
| --- a/third_party/WebKit/Source/core/animation/EffectInput.cpp |
| +++ b/third_party/WebKit/Source/core/animation/EffectInput.cpp |
| @@ -47,12 +47,81 @@ |
| namespace blink { |
| -EffectModel* EffectInput::convert(Element* element, const Vector<Dictionary>& keyframeDictionaryVector, ExceptionState& exceptionState) |
| +namespace { |
| + |
| +bool compareKeyframes(const RefPtr<StringKeyframe>& a, const RefPtr<StringKeyframe>& b) |
| +{ |
| + return a->offset() < b->offset(); |
| +} |
| + |
| +} // 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, true); |
| + if (effectInput.isDictionary()) { |
| + return convert(element, effectInput.getAsDictionary(), exceptionState); |
| + } |
| + return nullptr; |
| +} |
| + |
| +bool EffectInput::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* EffectInput::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; |
| +} |
| + |
| +EffectModel* EffectInput::convert(Element* element, const Vector<Dictionary>& keyframeDictionaryVector, ExceptionState& exceptionState, bool keyframesSpecifiedAsList) |
| { |
| if (!element) |
| return nullptr; |
| - StyleSheetContents* styleSheetContents = element->document().elementSheet().contents(); |
| StringKeyframeVector keyframes; |
| double lastOffset = 0; |
| bool encounteredCompositableProperty = false; |
| @@ -70,6 +139,7 @@ EffectModel* EffectInput::convert(Element* element, const Vector<Dictionary>& ke |
| // Keyframes with offsets outside the range [0.0, 1.0] are an error. |
| if (std::isnan(offset)) { |
| exceptionState.throwDOMException(InvalidModificationError, "Non numeric offset provided"); |
| + return nullptr; |
| } |
| if (offset < 0 || offset > 1) { |
| @@ -86,7 +156,6 @@ EffectModel* EffectInput::convert(Element* element, const Vector<Dictionary>& ke |
| keyframe->setOffset(offset); |
| } |
| - keyframes.append(keyframe); |
| String compositeString; |
| DictionaryHelper::get(keyframeDictionary, "composite", compositeString); |
| @@ -103,76 +172,100 @@ 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)) { |
| + String exceptionMessage = keyframesSpecifiedAsList ? |
| + "Lists of values not permitted in array-form list of keyframes" : |
| + "Offsets and keyframe-specific composite operations not permitted in object-form list of keyframes"; |
| + exceptionState.throwDOMException(InvalidModificationError, exceptionMessage); |
|
alancutter (OOO until 2018)
2016/02/25 06:52:27
This is definitely not a modification error, I'd g
shans
2016/02/25 22:30:28
Spec says TypeError.
suzyh_UTC10 (ex-contributor)
2016/02/26 04:35:31
As discussed offline, I've made all these InvalidM
|
| + 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; |
| - } |
| + ScriptValue scriptValue; |
| + String compositeString; |
| + bool frameHasOffset = DictionaryHelper::get(keyframeDictionary, "offset", scriptValue) && !scriptValue.isNull(); |
| + bool frameHasComposite = DictionaryHelper::get(keyframeDictionary, "composite", compositeString); |
| + if (frameHasOffset || frameHasComposite) { |
| + Vector<Dictionary> keyframeDictionaryVector; |
| + keyframeDictionaryVector.append(keyframeDictionary); |
| + return convert(element, keyframeDictionaryVector, exceptionState, false); |
| + } |
| + |
| + StringKeyframeVector keyframes; |
| + bool encounteredCompositableProperty = false; |
| + |
| + String timingFunctionString; |
| + RefPtr<TimingFunction> timingFunction = nullptr; |
| + if (DictionaryHelper::get(keyframeDictionary, "easing", timingFunctionString)) |
| + timingFunction = AnimationInputHelpers::parseTimingFunction(timingFunctionString); |
| + |
| + Vector<String> keyframeProperties; |
| + keyframeDictionary.getPropertyNames(keyframeProperties); |
| + for (const auto& property : keyframeProperties) { |
| + if (property == "offset" |
| + || property == "composite" |
| + || property == "easing") { |
| + continue; |
| + } |
| + |
| + Vector<String> values; |
| + bool isList = DictionaryHelper::get(keyframeDictionary, property, values); |
| + if (!isList) { |
| + String value; |
| + DictionaryHelper::get(keyframeDictionary, property, value); |
| + values.append(value); |
| + } |
| + |
| + size_t numKeyframes = values.size(); |
| + |
| + Vector<double> offsets; |
| + if (numKeyframes == 1) { |
| + offsets.append(1.0); |
| + } else { |
| + for (size_t i = 0; i < numKeyframes; ++i) { |
| + offsets.append(i / (numKeyframes - 1.0)); |
| } |
| } |
| - } |
| - keyframeEffectModel->forceConversionsToAnimatableValues(*element, element->computedStyle()); |
| - return keyframeEffectModel; |
| -} |
| + for (size_t i = 0; i < numKeyframes; ++i) { |
| + RefPtr<StringKeyframe> keyframe = StringKeyframe::create(); |
| + keyframe->setOffset(offsets[i]); |
| -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); |
| + const String& value = values[i]; |
| + |
| + if (timingFunction) |
| + keyframe->setEasing(timingFunction); |
| + |
| + encounteredCompositableProperty |= setKeyframeValue(element, *keyframe.get(), property, value); |
| + keyframes.append(keyframe); |
| + } |
| } |
| - return nullptr; |
| + |
| + std::sort(keyframes.begin(), keyframes.end(), compareKeyframes); |
| + |
| + return createEffectModelFromKeyframes(element, keyframes, encounteredCompositableProperty, exceptionState); |
| } |
| } // namespace blink |