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 04fad6a5a5c41f59512f67b913d53b71305c1114..7cc8c3d3013aa22280bf1d2714c1eeb79e7e5f9b 100644 |
| --- a/third_party/WebKit/Source/core/animation/EffectInput.cpp |
| +++ b/third_party/WebKit/Source/core/animation/EffectInput.cpp |
| @@ -47,12 +47,82 @@ |
| namespace blink { |
| +namespace { |
| +bool compareKeyframes(const RefPtr<StringKeyframe>& a, const RefPtr<StringKeyframe>& b) |
| +{ |
| + return (a->offset() < b->offset()); |
|
alancutter (OOO until 2018)
2016/02/24 08:50:02
No need for the outer brackets.
suzyh_UTC10 (ex-contributor)
2016/02/25 02:42:35
Done.
|
| +} |
| +} // namespace |
|
alancutter (OOO until 2018)
2016/02/24 08:50:02
Blank lines around the namespace boundaries.
suzyh_UTC10 (ex-contributor)
2016/02/25 02:42:36
Done.
|
| + |
| +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; |
| +} |
| + |
| +bool EffectInput::setKeyframeValue(Element* element, StringKeyframe* keyframe, const String& property, const String& value) |
|
alancutter (OOO until 2018)
2016/02/24 08:50:02
Blink style dictates the element and keyframe para
suzyh_UTC10 (ex-contributor)
2016/02/25 02:42:35
I'm happy to make the keyframe parameter a StringK
alancutter (OOO until 2018)
2016/02/25 06:52:27
If a function dereferences it without performing a
suzyh_UTC10 (ex-contributor)
2016/02/26 04:35:30
Done.
|
| +{ |
| + 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; |
| +} |
| + |
| +// TODO(alancutter): Remove this once composited animations no longer depend on AnimatableValues. |
| +void EffectInput::updateElementStyleIfNeeded(Element* element, bool encounteredCompositableProperty) |
| +{ |
| + if (encounteredCompositableProperty && element->inActiveDocument()) |
| + element->document().updateLayoutTreeForNodeIfNeeded(element); |
|
alancutter (OOO until 2018)
2016/02/24 08:50:02
This code always needs to be run before the call t
suzyh_UTC10 (ex-contributor)
2016/02/25 02:42:35
Done.
|
| +} |
| + |
| +EffectModel* EffectInput::createEffectModelFromKeyframes(Element* element, const StringKeyframeVector& keyframes, ExceptionState& exceptionState) |
|
alancutter (OOO until 2018)
2016/02/24 08:50:02
element should be a reference.
|
| +{ |
| + 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) |
| { |
| if (!element) |
| return nullptr; |
| - StyleSheetContents* styleSheetContents = element->document().elementSheet().contents(); |
| StringKeyframeVector keyframes; |
| double lastOffset = 0; |
| bool encounteredCompositableProperty = false; |
| @@ -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,93 @@ 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; |
| + bool isList = DictionaryHelper::get(keyframeDictionary, property, values); |
| + if (isList) |
|
alancutter (OOO until 2018)
2016/02/24 08:50:02
No need for single use bool variable.
suzyh_UTC10 (ex-contributor)
2016/02/25 02:42:36
Done. I used this here to be consistent with the c
alancutter (OOO until 2018)
2016/02/25 06:52:27
True, the name does add to code clarity. Feel free
|
| + exceptionState.throwDOMException(InvalidModificationError, "Lists of values not permitted in array-form list of keyframes"); |
|
alancutter (OOO until 2018)
2016/02/24 08:50:02
Is there a test for this exception?
Shouldn't we r
suzyh_UTC10 (ex-contributor)
2016/02/25 02:42:36
Test: Done.
Return nullptr: Whoops, yes. Fixed.
|
| + |
| + String value; |
| + DictionaryHelper::get(keyframeDictionary, property, value); |
|
alancutter (OOO until 2018)
2016/02/24 08:50:02
Can this return false if the user provides their o
suzyh_UTC10 (ex-contributor)
2016/02/25 02:42:35
Hm, I have no idea. The existing code used Diction
|
| - const QualifiedName* svgAttribute = AnimationInputHelpers::keyframeAttributeToSVGAttribute(property, *element); |
| - if (svgAttribute) |
| - keyframe->setSVGAttributeValue(*svgAttribute, 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().updateLayoutTreeForNodeIfNeeded(element); |
| + updateElementStyleIfNeeded(element, encounteredCompositableProperty); |
| - StringKeyframeEffectModel* keyframeEffectModel = StringKeyframeEffectModel::create(keyframes); |
| - if (!RuntimeEnabledFeatures::cssAdditiveAnimationsEnabled()) { |
| - for (const auto& keyframeGroup : keyframeEffectModel->getPropertySpecificKeyframeGroups()) { |
| - PropertyHandle property = keyframeGroup.key; |
| - if (!property.isCSSProperty()) |
| - continue; |
| + return createEffectModelFromKeyframes(element, keyframes, exceptionState); |
| +} |
| - 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; |
| - } |
| +EffectModel* EffectInput::convert(Element* element, const Dictionary& keyframeDictionary, ExceptionState& exceptionState) |
| +{ |
| + if (!element) |
| + return nullptr; |
| + |
| + 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") |
| + exceptionState.throwDOMException(InvalidModificationError, "Keyframe offsets not permitted in object-form list of keyframes"); |
| + if (property == "composite") |
| + exceptionState.throwDOMException(InvalidModificationError, "Keyframe-specific composite operations not permitted in object-form list of keyframes"); |
| + |
| + if (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); |
| + |
| + updateElementStyleIfNeeded(element, encounteredCompositableProperty); |
| + |
| + return createEffectModelFromKeyframes(element, keyframes, exceptionState); |
| } |
| } // namespace blink |