| OLD | NEW |
| 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 Loading... |
| 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 |
| OLD | NEW |