Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2)

Unified Diff: third_party/WebKit/Source/core/animation/EffectInput.cpp

Issue 1720403002: Alternative syntax for element.animate list of keyframes (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@animations-keyframeeffect-api
Patch Set: Additional (mismatched-length-list) test; ensure offset is initialized Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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
« no previous file with comments | « third_party/WebKit/Source/core/animation/EffectInput.h ('k') | third_party/WebKit/Source/core/animation/EffectInputTest.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698