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

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: Fix mistake in rebase 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
« no previous file with comments | « third_party/WebKit/Source/core/animation/EffectInput.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 EffectModel* EffectInput::convert(Element* element, const Vector<Dictionary>& ke yframeDictionaryVector, ExceptionState& exceptionState) 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 } // namespace
58
59 EffectModel* EffectInput::convert(Element* element, const EffectModelOrDictionar ySequenceOrDictionary& effectInput, ExceptionState& exceptionState)
60 {
61 if (effectInput.isEffectModel())
62 return effectInput.getAsEffectModel();
63 if (effectInput.isDictionarySequence())
64 return convert(element, effectInput.getAsDictionarySequence(), exception State, true);
65 if (effectInput.isDictionary()) {
66 return convert(element, effectInput.getAsDictionary(), exceptionState);
67 }
68 return nullptr;
69 }
70
71 bool EffectInput::setKeyframeValue(Element* element, StringKeyframe& keyframe, c onst String& property, const String& value)
72 {
73 StyleSheetContents* styleSheetContents = element->document().elementSheet(). contents();
74 CSSPropertyID cssProperty = AnimationInputHelpers::keyframeAttributeToCSSPro perty(property, element->document());
75 if (cssProperty != CSSPropertyInvalid) {
76 keyframe.setCSSPropertyValue(cssProperty, value, element, styleSheetCont ents);
77 return CompositorAnimations::isCompositableProperty(cssProperty);
78 }
79 cssProperty = AnimationInputHelpers::keyframeAttributeToPresentationAttribut e(property, *element);
80 if (cssProperty != CSSPropertyInvalid) {
81 keyframe.setPresentationAttributeValue(cssProperty, value, element, styl eSheetContents);
82 return false;
83 }
84 const QualifiedName* svgAttribute = AnimationInputHelpers::keyframeAttribute ToSVGAttribute(property, *element);
85 if (svgAttribute)
86 keyframe.setSVGAttributeValue(*svgAttribute, value);
87 return false;
88 }
89
90 EffectModel* EffectInput::createEffectModelFromKeyframes(Element* element, const StringKeyframeVector& keyframes, bool encounteredCompositableProperty, Exceptio nState& exceptionState)
91 {
92 // TODO(alancutter): Remove this once composited animations no longer depend on AnimatableValues.
93 if (encounteredCompositableProperty && element->inActiveDocument())
94 element->document().updateLayoutTreeForNode(element);
95
96 StringKeyframeEffectModel* keyframeEffectModel = StringKeyframeEffectModel:: create(keyframes);
97 if (!RuntimeEnabledFeatures::cssAdditiveAnimationsEnabled()) {
98 for (const auto& keyframeGroup : keyframeEffectModel->getPropertySpecifi cKeyframeGroups()) {
99 PropertyHandle property = keyframeGroup.key;
100 if (!property.isCSSProperty())
101 continue;
102
103 for (const auto& keyframe : keyframeGroup.value->keyframes()) {
104 if (keyframe->isNeutral()) {
105 exceptionState.throwDOMException(NotSupportedError, "Partial keyframes are not supported.");
106 return nullptr;
107 }
108 if (keyframe->composite() != EffectModel::CompositeReplace) {
109 exceptionState.throwDOMException(NotSupportedError, "Additiv e animations are not supported.");
110 return nullptr;
111 }
112 }
113 }
114 }
115 keyframeEffectModel->forceConversionsToAnimatableValues(*element, element->c omputedStyle());
116
117 return keyframeEffectModel;
118 }
119
120 EffectModel* EffectInput::convert(Element* element, const Vector<Dictionary>& ke yframeDictionaryVector, ExceptionState& exceptionState, bool keyframesSpecifiedA sList)
51 { 121 {
52 if (!element) 122 if (!element)
53 return nullptr; 123 return nullptr;
54 124
55 StyleSheetContents* styleSheetContents = element->document().elementSheet(). contents();
56 StringKeyframeVector keyframes; 125 StringKeyframeVector keyframes;
57 double lastOffset = 0; 126 double lastOffset = 0;
58 bool encounteredCompositableProperty = false; 127 bool encounteredCompositableProperty = false;
59 128
60 for (const auto& keyframeDictionary : keyframeDictionaryVector) { 129 for (const auto& keyframeDictionary : keyframeDictionaryVector) {
61 RefPtr<StringKeyframe> keyframe = StringKeyframe::create(); 130 RefPtr<StringKeyframe> keyframe = StringKeyframe::create();
62 131
63 ScriptValue scriptValue; 132 ScriptValue scriptValue;
64 bool frameHasOffset = DictionaryHelper::get(keyframeDictionary, "offset" , scriptValue) && !scriptValue.isNull(); 133 bool frameHasOffset = DictionaryHelper::get(keyframeDictionary, "offset" , scriptValue) && !scriptValue.isNull();
65 134
66 if (frameHasOffset) { 135 if (frameHasOffset) {
67 double offset; 136 double offset;
68 DictionaryHelper::get(keyframeDictionary, "offset", offset); 137 DictionaryHelper::get(keyframeDictionary, "offset", offset);
69 138
70 // Keyframes with offsets outside the range [0.0, 1.0] are an error. 139 // Keyframes with offsets outside the range [0.0, 1.0] are an error.
71 if (std::isnan(offset)) { 140 if (std::isnan(offset)) {
72 exceptionState.throwDOMException(InvalidModificationError, "Non numeric offset provided"); 141 exceptionState.throwDOMException(InvalidModificationError, "Non numeric offset provided");
142 return nullptr;
73 } 143 }
74 144
75 if (offset < 0 || offset > 1) { 145 if (offset < 0 || offset > 1) {
76 exceptionState.throwDOMException(InvalidModificationError, "Offs ets provided outside the range [0, 1]"); 146 exceptionState.throwDOMException(InvalidModificationError, "Offs ets provided outside the range [0, 1]");
77 return nullptr; 147 return nullptr;
78 } 148 }
79 149
80 if (offset < lastOffset) { 150 if (offset < lastOffset) {
81 exceptionState.throwDOMException(InvalidModificationError, "Keyf rames with specified offsets are not sorted"); 151 exceptionState.throwDOMException(InvalidModificationError, "Keyf rames with specified offsets are not sorted");
82 return nullptr; 152 return nullptr;
83 } 153 }
84 154
85 lastOffset = offset; 155 lastOffset = offset;
86 156
87 keyframe->setOffset(offset); 157 keyframe->setOffset(offset);
88 } 158 }
89 keyframes.append(keyframe);
90 159
91 String compositeString; 160 String compositeString;
92 DictionaryHelper::get(keyframeDictionary, "composite", compositeString); 161 DictionaryHelper::get(keyframeDictionary, "composite", compositeString);
93 if (compositeString == "add") 162 if (compositeString == "add")
94 keyframe->setComposite(EffectModel::CompositeAdd); 163 keyframe->setComposite(EffectModel::CompositeAdd);
95 // TODO(alancutter): Support "accumulate" keyframe composition. 164 // TODO(alancutter): Support "accumulate" keyframe composition.
96 165
97 String timingFunctionString; 166 String timingFunctionString;
98 if (DictionaryHelper::get(keyframeDictionary, "easing", timingFunctionSt ring)) { 167 if (DictionaryHelper::get(keyframeDictionary, "easing", timingFunctionSt ring)) {
99 if (RefPtr<TimingFunction> timingFunction = AnimationInputHelpers::p arseTimingFunction(timingFunctionString)) 168 if (RefPtr<TimingFunction> timingFunction = AnimationInputHelpers::p arseTimingFunction(timingFunctionString))
100 keyframe->setEasing(timingFunction); 169 keyframe->setEasing(timingFunction);
101 } 170 }
102 171
103 Vector<String> keyframeProperties; 172 Vector<String> keyframeProperties;
104 keyframeDictionary.getPropertyNames(keyframeProperties); 173 keyframeDictionary.getPropertyNames(keyframeProperties);
105 for (const auto& property : keyframeProperties) { 174 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" 175 if (property == "offset"
119 || property == "composite" 176 || property == "composite"
120 || property == "easing") { 177 || property == "easing") {
121 continue; 178 continue;
122 } 179 }
123 180
124 cssProperty = AnimationInputHelpers::keyframeAttributeToPresentation Attribute(property, *element); 181 Vector<String> values;
125 if (cssProperty != CSSPropertyInvalid) { 182 if (DictionaryHelper::get(keyframeDictionary, property, values)) {
126 keyframe->setPresentationAttributeValue(cssProperty, value, elem ent, styleSheetContents); 183 String exceptionMessage = keyframesSpecifiedAsList ?
127 continue; 184 "Lists of values not permitted in array-form list of keyfram es" :
185 "Offsets and keyframe-specific composite operations not perm itted in object-form list of keyframes";
186 exceptionState.throwDOMException(InvalidModificationError, excep tionMessage);
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
187 return nullptr;
128 } 188 }
129 189
130 const QualifiedName* svgAttribute = AnimationInputHelpers::keyframeA ttributeToSVGAttribute(property, *element); 190 String value;
131 if (svgAttribute) 191 DictionaryHelper::get(keyframeDictionary, property, value);
132 keyframe->setSVGAttributeValue(*svgAttribute, value); 192
193 encounteredCompositableProperty |= setKeyframeValue(element, *keyfra me.get(), property, value);
194 }
195 keyframes.append(keyframe);
196 }
197
198 return createEffectModelFromKeyframes(element, keyframes, encounteredComposi tableProperty, exceptionState);
199 }
200
201 EffectModel* EffectInput::convert(Element* element, const Dictionary& keyframeDi ctionary, ExceptionState& exceptionState)
202 {
203 if (!element)
204 return nullptr;
205
206 ScriptValue scriptValue;
207 String compositeString;
208 bool frameHasOffset = DictionaryHelper::get(keyframeDictionary, "offset", sc riptValue) && !scriptValue.isNull();
209 bool frameHasComposite = DictionaryHelper::get(keyframeDictionary, "composit e", compositeString);
210 if (frameHasOffset || frameHasComposite) {
211 Vector<Dictionary> keyframeDictionaryVector;
212 keyframeDictionaryVector.append(keyframeDictionary);
213 return convert(element, keyframeDictionaryVector, exceptionState, false) ;
214 }
215
216 StringKeyframeVector keyframes;
217 bool encounteredCompositableProperty = false;
218
219 String timingFunctionString;
220 RefPtr<TimingFunction> timingFunction = nullptr;
221 if (DictionaryHelper::get(keyframeDictionary, "easing", timingFunctionString ))
222 timingFunction = AnimationInputHelpers::parseTimingFunction(timingFuncti onString);
223
224 Vector<String> keyframeProperties;
225 keyframeDictionary.getPropertyNames(keyframeProperties);
226 for (const auto& property : keyframeProperties) {
227 if (property == "offset"
228 || property == "composite"
229 || property == "easing") {
230 continue;
231 }
232
233 Vector<String> values;
234 bool isList = DictionaryHelper::get(keyframeDictionary, property, values );
235 if (!isList) {
236 String value;
237 DictionaryHelper::get(keyframeDictionary, property, value);
238 values.append(value);
239 }
240
241 size_t numKeyframes = values.size();
242
243 Vector<double> offsets;
244 if (numKeyframes == 1) {
245 offsets.append(1.0);
246 } else {
247 for (size_t i = 0; i < numKeyframes; ++i) {
248 offsets.append(i / (numKeyframes - 1.0));
249 }
250 }
251
252 for (size_t i = 0; i < numKeyframes; ++i) {
253 RefPtr<StringKeyframe> keyframe = StringKeyframe::create();
254 keyframe->setOffset(offsets[i]);
255
256 const String& value = values[i];
257
258 if (timingFunction)
259 keyframe->setEasing(timingFunction);
260
261 encounteredCompositableProperty |= setKeyframeValue(element, *keyfra me.get(), property, value);
262 keyframes.append(keyframe);
133 } 263 }
134 } 264 }
135 265
136 // TODO(alancutter): Remove this once composited animations no longer depend on AnimatableValues. 266 std::sort(keyframes.begin(), keyframes.end(), compareKeyframes);
137 if (encounteredCompositableProperty && element->inActiveDocument())
138 element->document().updateLayoutTreeForNode(element);
139 267
140 StringKeyframeEffectModel* keyframeEffectModel = StringKeyframeEffectModel:: create(keyframes); 268 return createEffectModelFromKeyframes(element, keyframes, encounteredComposi tableProperty, 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 } 269 }
177 270
178 } // namespace blink 271 } // namespace blink
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/core/animation/EffectInput.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698