Chromium Code Reviews| 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 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().updateLayoutTreeForNodeIfNeeded(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); | |
| 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); | |
|
alancutter (OOO until 2018)
2016/02/25 06:52:27
I think the spec wants us to apply the composite v
suzyh_UTC10 (ex-contributor)
2016/02/26 04:35:31
Changed as discussed offline.
| |
| 210 if (frameHasOffset || frameHasComposite) { | |
| 211 Vector<Dictionary> keyframeDictionaryVector; | |
| 212 keyframeDictionaryVector.append(keyframeDictionary); | |
| 213 return convert(element, keyframeDictionaryVector, exceptionState, false) ; | |
| 214 } | |
|
alancutter (OOO until 2018)
2016/02/25 06:52:27
I don't think this behaviour matches spec, I inter
suzyh_UTC10 (ex-contributor)
2016/02/26 04:35:31
Changed as discussed offline.
| |
| 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 |
| OLD | NEW |