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

Side by Side 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, 9 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 unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2013 Google Inc. All rights reserved. 2 * Copyright (C) 2013 Google Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are 5 * modification, are permitted provided that the following conditions are
6 * met: 6 * met:
7 * 7 *
8 * * Redistributions of source code must retain the above copyright 8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer. 9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above 10 * * Redistributions in binary form must reproduce the above
(...skipping 29 matching lines...) Expand all
40 #include "core/dom/Document.h" 40 #include "core/dom/Document.h"
41 #include "core/dom/Element.h" 41 #include "core/dom/Element.h"
42 #include "core/dom/ExceptionCode.h" 42 #include "core/dom/ExceptionCode.h"
43 #include "core/dom/NodeComputedStyle.h" 43 #include "core/dom/NodeComputedStyle.h"
44 #include "wtf/ASCIICType.h" 44 #include "wtf/ASCIICType.h"
45 #include "wtf/HashSet.h" 45 #include "wtf/HashSet.h"
46 #include "wtf/NonCopyingSort.h" 46 #include "wtf/NonCopyingSort.h"
47 47
48 namespace blink { 48 namespace blink {
49 49
50 namespace {
51
52 bool compareKeyframes(const RefPtr<StringKeyframe>& a, const RefPtr<StringKeyfra me>& b)
53 {
54 return a->offset() < b->offset();
55 }
56
57 // Gets offset value from keyframeDictionary and returns false if this value was invalid.
58 bool getAndCheckOffset(const Dictionary& keyframeDictionary, double& offset, dou ble lastOffset, ExceptionState& exceptionState)
59 {
60 DictionaryHelper::get(keyframeDictionary, "offset", offset);
61
62 // Keyframes with offsets outside the range [0.0, 1.0] are an error.
63 if (std::isnan(offset)) {
64 exceptionState.throwTypeError("Non numeric offset provided");
65 return false;
66 }
67
68 if (offset < 0 || offset > 1) {
69 exceptionState.throwTypeError("Offsets provided outside the range [0, 1] ");
70 return false;
71 }
72
73 if (offset < lastOffset) {
74 exceptionState.throwTypeError("Keyframes with specified offsets are not sorted");
75 return false;
76 }
77
78 return true;
79 }
80
81 // Returns true if the property passed in is a compositable property.
82 bool setKeyframeValue(Element& element, StringKeyframe& keyframe, const String& property, const String& value)
83 {
84 StyleSheetContents* styleSheetContents = element.document().elementSheet().c ontents();
85 CSSPropertyID cssProperty = AnimationInputHelpers::keyframeAttributeToCSSPro perty(property, element.document());
86 if (cssProperty != CSSPropertyInvalid) {
87 keyframe.setCSSPropertyValue(cssProperty, value, &element, styleSheetCon tents);
88 return CompositorAnimations::isCompositableProperty(cssProperty);
89 }
90 cssProperty = AnimationInputHelpers::keyframeAttributeToPresentationAttribut e(property, element);
91 if (cssProperty != CSSPropertyInvalid) {
92 keyframe.setPresentationAttributeValue(cssProperty, value, &element, sty leSheetContents);
93 return false;
94 }
95 const QualifiedName* svgAttribute = AnimationInputHelpers::keyframeAttribute ToSVGAttribute(property, element);
96 if (svgAttribute)
97 keyframe.setSVGAttributeValue(*svgAttribute, value);
98 return false;
99 }
100
101 EffectModel* createEffectModelFromKeyframes(Element& element, const StringKeyfra meVector& keyframes, bool encounteredCompositableProperty, ExceptionState& excep tionState)
102 {
103 // TODO(alancutter): Remove this once composited animations no longer depend on AnimatableValues.
104 if (encounteredCompositableProperty && element.inActiveDocument())
105 element.document().updateLayoutTreeForNode(&element);
106
107 StringKeyframeEffectModel* keyframeEffectModel = StringKeyframeEffectModel:: create(keyframes);
108 if (!RuntimeEnabledFeatures::cssAdditiveAnimationsEnabled()) {
109 for (const auto& keyframeGroup : keyframeEffectModel->getPropertySpecifi cKeyframeGroups()) {
110 PropertyHandle property = keyframeGroup.key;
111 if (!property.isCSSProperty())
112 continue;
113
114 for (const auto& keyframe : keyframeGroup.value->keyframes()) {
115 if (keyframe->isNeutral()) {
116 exceptionState.throwDOMException(NotSupportedError, "Partial keyframes are not supported.");
117 return nullptr;
118 }
119 if (keyframe->composite() != EffectModel::CompositeReplace) {
120 exceptionState.throwDOMException(NotSupportedError, "Additiv e animations are not supported.");
121 return nullptr;
122 }
123 }
124 }
125 }
126 keyframeEffectModel->forceConversionsToAnimatableValues(element, element.com putedStyle());
127
128 return keyframeEffectModel;
129 }
130
131 } // namespace
132
133 EffectModel* EffectInput::convert(Element* element, const EffectModelOrDictionar ySequenceOrDictionary& effectInput, ExceptionState& exceptionState)
134 {
135 if (effectInput.isEffectModel())
136 return effectInput.getAsEffectModel();
137 if (effectInput.isDictionarySequence())
138 return convert(element, effectInput.getAsDictionarySequence(), exception State);
139 if (effectInput.isDictionary()) {
140 return convert(element, effectInput.getAsDictionary(), exceptionState);
141 }
142 return nullptr;
143 }
144
50 EffectModel* EffectInput::convert(Element* element, const Vector<Dictionary>& ke yframeDictionaryVector, ExceptionState& exceptionState) 145 EffectModel* EffectInput::convert(Element* element, const Vector<Dictionary>& ke yframeDictionaryVector, ExceptionState& exceptionState)
51 { 146 {
52 if (!element) 147 if (!element)
53 return nullptr; 148 return nullptr;
54 149
55 StyleSheetContents* styleSheetContents = element->document().elementSheet(). contents();
56 StringKeyframeVector keyframes; 150 StringKeyframeVector keyframes;
57 double lastOffset = 0; 151 double lastOffset = 0;
58 bool encounteredCompositableProperty = false; 152 bool encounteredCompositableProperty = false;
59 153
60 for (const auto& keyframeDictionary : keyframeDictionaryVector) { 154 for (const auto& keyframeDictionary : keyframeDictionaryVector) {
61 RefPtr<StringKeyframe> keyframe = StringKeyframe::create(); 155 RefPtr<StringKeyframe> keyframe = StringKeyframe::create();
62 156
63 ScriptValue scriptValue; 157 ScriptValue scriptValue;
64 bool frameHasOffset = DictionaryHelper::get(keyframeDictionary, "offset" , scriptValue) && !scriptValue.isNull(); 158 bool frameHasOffset = DictionaryHelper::get(keyframeDictionary, "offset" , scriptValue) && !scriptValue.isNull();
65 159
160 double offset = 0.0;
66 if (frameHasOffset) { 161 if (frameHasOffset) {
67 double offset; 162 if (!getAndCheckOffset(keyframeDictionary, offset, lastOffset, excep tionState))
68 DictionaryHelper::get(keyframeDictionary, "offset", offset);
69
70 // Keyframes with offsets outside the range [0.0, 1.0] are an error.
71 if (std::isnan(offset)) {
72 exceptionState.throwDOMException(InvalidModificationError, "Non numeric offset provided");
73 }
74
75 if (offset < 0 || offset > 1) {
76 exceptionState.throwDOMException(InvalidModificationError, "Offs ets provided outside the range [0, 1]");
77 return nullptr; 163 return nullptr;
78 }
79
80 if (offset < lastOffset) {
81 exceptionState.throwDOMException(InvalidModificationError, "Keyf rames with specified offsets are not sorted");
82 return nullptr;
83 }
84
85 lastOffset = offset; 164 lastOffset = offset;
86
87 keyframe->setOffset(offset); 165 keyframe->setOffset(offset);
88 } 166 }
89 keyframes.append(keyframe);
90 167
91 String compositeString; 168 String compositeString;
92 DictionaryHelper::get(keyframeDictionary, "composite", compositeString); 169 DictionaryHelper::get(keyframeDictionary, "composite", compositeString);
93 if (compositeString == "add") 170 if (compositeString == "add")
94 keyframe->setComposite(EffectModel::CompositeAdd); 171 keyframe->setComposite(EffectModel::CompositeAdd);
95 // TODO(alancutter): Support "accumulate" keyframe composition. 172 // TODO(alancutter): Support "accumulate" keyframe composition.
96 173
97 String timingFunctionString; 174 String timingFunctionString;
98 if (DictionaryHelper::get(keyframeDictionary, "easing", timingFunctionSt ring)) { 175 if (DictionaryHelper::get(keyframeDictionary, "easing", timingFunctionSt ring)) {
99 if (RefPtr<TimingFunction> timingFunction = AnimationInputHelpers::p arseTimingFunction(timingFunctionString)) 176 if (RefPtr<TimingFunction> timingFunction = AnimationInputHelpers::p arseTimingFunction(timingFunctionString))
100 keyframe->setEasing(timingFunction); 177 keyframe->setEasing(timingFunction);
101 } 178 }
102 179
103 Vector<String> keyframeProperties; 180 Vector<String> keyframeProperties;
104 keyframeDictionary.getPropertyNames(keyframeProperties); 181 keyframeDictionary.getPropertyNames(keyframeProperties);
105 for (const auto& property : keyframeProperties) { 182 for (const auto& property : keyframeProperties) {
106 String value;
107 DictionaryHelper::get(keyframeDictionary, property, value);
108
109 CSSPropertyID cssProperty = AnimationInputHelpers::keyframeAttribute ToCSSProperty(property, element->document());
110 if (cssProperty != CSSPropertyInvalid) {
111 if (!encounteredCompositableProperty && CompositorAnimations::is CompositableProperty(cssProperty))
112 encounteredCompositableProperty = true;
113
114 keyframe->setCSSPropertyValue(cssProperty, value, element, style SheetContents);
115 continue;
116 }
117
118 if (property == "offset" 183 if (property == "offset"
119 || property == "composite" 184 || property == "composite"
120 || property == "easing") { 185 || property == "easing") {
121 continue; 186 continue;
122 } 187 }
123 188
124 cssProperty = AnimationInputHelpers::keyframeAttributeToPresentation Attribute(property, *element); 189 Vector<String> values;
125 if (cssProperty != CSSPropertyInvalid) { 190 if (DictionaryHelper::get(keyframeDictionary, property, values)) {
126 keyframe->setPresentationAttributeValue(cssProperty, value, elem ent, styleSheetContents); 191 exceptionState.throwTypeError("Lists of values not permitted in array-form list of keyframes");
127 continue; 192 return nullptr;
128 } 193 }
129 194
130 const QualifiedName* svgAttribute = AnimationInputHelpers::keyframeA ttributeToSVGAttribute(property, *element); 195 String value;
131 if (svgAttribute) 196 DictionaryHelper::get(keyframeDictionary, property, value);
132 keyframe->setSVGAttributeValue(*svgAttribute, value); 197
198 encounteredCompositableProperty |= setKeyframeValue(*element, *keyfr ame.get(), property, value);
199 }
200 keyframes.append(keyframe);
201 }
202
203 return createEffectModelFromKeyframes(*element, keyframes, encounteredCompos itableProperty, exceptionState);
204 }
205
206 EffectModel* EffectInput::convert(Element* element, const Dictionary& keyframeDi ctionary, ExceptionState& exceptionState)
207 {
208 if (!element)
209 return nullptr;
210
211 StringKeyframeVector keyframes;
212 bool encounteredCompositableProperty = false;
213
214 String timingFunctionString;
215 RefPtr<TimingFunction> timingFunction = nullptr;
216 if (DictionaryHelper::get(keyframeDictionary, "easing", timingFunctionString ))
217 timingFunction = AnimationInputHelpers::parseTimingFunction(timingFuncti onString);
218
219 ScriptValue scriptValue;
220 bool frameHasOffset = DictionaryHelper::get(keyframeDictionary, "offset", sc riptValue) && !scriptValue.isNull();
221 double offset = 0.0;
222 if (frameHasOffset && !getAndCheckOffset(keyframeDictionary, offset, 0.0, ex ceptionState))
223 return nullptr;
224
225 String compositeString;
226 DictionaryHelper::get(keyframeDictionary, "composite", compositeString);
227
228 Vector<String> keyframeProperties;
229 keyframeDictionary.getPropertyNames(keyframeProperties);
230 for (const auto& property : keyframeProperties) {
231 if (property == "offset"
232 || property == "composite"
233 || property == "easing") {
234 continue;
235 }
236
237 Vector<String> values;
238 bool isList = DictionaryHelper::get(keyframeDictionary, property, values );
239 if (!isList) {
240 String value;
241 DictionaryHelper::get(keyframeDictionary, property, value);
242 values.append(value);
243 }
244
245 size_t numKeyframes = values.size();
246 for (size_t i = 0; i < numKeyframes; ++i) {
247 RefPtr<StringKeyframe> keyframe = StringKeyframe::create();
248
249 if (frameHasOffset)
250 keyframe->setOffset(offset);
251 else if (numKeyframes == 1)
252 keyframe->setOffset(1.0);
253 else
254 keyframe->setOffset(i / (numKeyframes - 1.0));
255
256 if (timingFunction)
257 keyframe->setEasing(timingFunction);
258
259 if (compositeString == "add")
260 keyframe->setComposite(EffectModel::CompositeAdd);
261 // TODO(alancutter): Support "accumulate" keyframe composition.
262
263 encounteredCompositableProperty |= setKeyframeValue(*element, *keyfr ame.get(), property, values[i]);
264 keyframes.append(keyframe);
133 } 265 }
134 } 266 }
135 267
136 // TODO(alancutter): Remove this once composited animations no longer depend on AnimatableValues. 268 std::sort(keyframes.begin(), keyframes.end(), compareKeyframes);
137 if (encounteredCompositableProperty && element->inActiveDocument())
138 element->document().updateLayoutTreeForNode(element);
139 269
140 StringKeyframeEffectModel* keyframeEffectModel = StringKeyframeEffectModel:: create(keyframes); 270 return createEffectModelFromKeyframes(*element, keyframes, encounteredCompos itableProperty, exceptionState);
141 if (!RuntimeEnabledFeatures::cssAdditiveAnimationsEnabled()) {
142 for (const auto& keyframeGroup : keyframeEffectModel->getPropertySpecifi cKeyframeGroups()) {
143 PropertyHandle property = keyframeGroup.key;
144 if (!property.isCSSProperty())
145 continue;
146
147 for (const auto& keyframe : keyframeGroup.value->keyframes()) {
148 if (keyframe->isNeutral()) {
149 exceptionState.throwDOMException(NotSupportedError, "Partial keyframes are not supported.");
150 return nullptr;
151 }
152 if (keyframe->composite() != EffectModel::CompositeReplace) {
153 exceptionState.throwDOMException(NotSupportedError, "Additiv e animations are not supported.");
154 return nullptr;
155 }
156 }
157 }
158 }
159 keyframeEffectModel->forceConversionsToAnimatableValues(*element, element->c omputedStyle());
160
161 return keyframeEffectModel;
162 }
163
164 EffectModel* EffectInput::convert(Element* element, const EffectModelOrDictionar ySequenceOrDictionary& effectInput, ExceptionState& exceptionState)
165 {
166 if (effectInput.isEffectModel())
167 return effectInput.getAsEffectModel();
168 if (effectInput.isDictionarySequence())
169 return convert(element, effectInput.getAsDictionarySequence(), exception State);
170 if (effectInput.isDictionary()) {
171 Vector<Dictionary> keyframes;
172 keyframes.append(effectInput.getAsDictionary());
173 return convert(element, keyframes, exceptionState);
174 }
175 return nullptr;
176 } 271 }
177 272
178 } // namespace blink 273 } // namespace blink
OLDNEW
« 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