OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "core/animation/KeyframeEffectReadOnly.h" | 5 #include "core/animation/KeyframeEffectReadOnly.h" |
6 | 6 |
7 #include "bindings/core/v8/Dictionary.h" | 7 #include "bindings/core/v8/Dictionary.h" |
8 #include "bindings/core/v8/ExceptionState.h" | 8 #include "bindings/core/v8/ExceptionState.h" |
| 9 #include "core/animation/Animation.h" |
| 10 #include "core/animation/CompositorAnimations.h" |
9 #include "core/animation/EffectInput.h" | 11 #include "core/animation/EffectInput.h" |
| 12 #include "core/animation/ElementAnimations.h" |
| 13 #include "core/animation/Interpolation.h" |
10 #include "core/animation/KeyframeEffect.h" | 14 #include "core/animation/KeyframeEffect.h" |
11 #include "core/animation/KeyframeEffectOptions.h" | 15 #include "core/animation/KeyframeEffectOptions.h" |
| 16 #include "core/animation/PropertyHandle.h" |
12 #include "core/animation/SampledEffect.h" | 17 #include "core/animation/SampledEffect.h" |
13 #include "core/animation/TimingInput.h" | 18 #include "core/animation/TimingInput.h" |
14 #include "core/dom/Element.h" | 19 #include "core/dom/Element.h" |
| 20 #include "core/dom/NodeComputedStyle.h" |
15 #include "core/frame/UseCounter.h" | 21 #include "core/frame/UseCounter.h" |
| 22 #include "core/paint/PaintLayer.h" |
| 23 #include "core/svg/SVGElement.h" |
16 | 24 |
17 namespace blink { | 25 namespace blink { |
18 | 26 |
19 // Web Animations API Bindings constructors. We never actually want to create a | 27 // Web Animations API Bindings constructors. We never actually want to create a |
20 // KeyframeEffectReadOnly object, we just want to provide a read-only interface | 28 // KeyframeEffectReadOnly object, we just want to provide a read-only interface |
21 // to the KeyframeEffect object, so pass straight through to the subclass | 29 // to the KeyframeEffect object, so pass straight through to the subclass |
22 // factory methods. | 30 // factory methods. |
23 KeyframeEffectReadOnly* KeyframeEffectReadOnly::create( | 31 KeyframeEffectReadOnly* KeyframeEffectReadOnly::create( |
24 ExecutionContext* executionContext, | 32 ExecutionContext* executionContext, |
25 Element* element, | 33 Element* element, |
(...skipping 27 matching lines...) Expand all Loading... |
53 EffectModel* model, | 61 EffectModel* model, |
54 const Timing& timing, | 62 const Timing& timing, |
55 Priority priority, | 63 Priority priority, |
56 EventDelegate* eventDelegate) | 64 EventDelegate* eventDelegate) |
57 : AnimationEffectReadOnly(timing, eventDelegate), | 65 : AnimationEffectReadOnly(timing, eventDelegate), |
58 m_target(target), | 66 m_target(target), |
59 m_model(model), | 67 m_model(model), |
60 m_sampledEffect(nullptr), | 68 m_sampledEffect(nullptr), |
61 m_priority(priority) {} | 69 m_priority(priority) {} |
62 | 70 |
| 71 void KeyframeEffectReadOnly::attach(Animation* animation) { |
| 72 if (m_target) { |
| 73 m_target->ensureElementAnimations().animations().add(animation); |
| 74 m_target->setNeedsAnimationStyleRecalc(); |
| 75 if (RuntimeEnabledFeatures::webAnimationsSVGEnabled() && |
| 76 m_target->isSVGElement()) |
| 77 toSVGElement(m_target)->setWebAnimationsPending(); |
| 78 } |
| 79 AnimationEffectReadOnly::attach(animation); |
| 80 } |
| 81 |
| 82 void KeyframeEffectReadOnly::detach() { |
| 83 if (m_target) |
| 84 m_target->elementAnimations()->animations().remove(animation()); |
| 85 if (m_sampledEffect) |
| 86 clearEffects(); |
| 87 AnimationEffectReadOnly::detach(); |
| 88 } |
| 89 |
| 90 void KeyframeEffectReadOnly::specifiedTimingChanged() { |
| 91 if (animation()) { |
| 92 // FIXME: Needs to consider groups when added. |
| 93 DCHECK_EQ(animation()->effect(), this); |
| 94 animation()->setCompositorPending(true); |
| 95 } |
| 96 } |
| 97 |
| 98 static AnimationStack& ensureAnimationStack(Element* element) { |
| 99 return element->ensureElementAnimations().animationStack(); |
| 100 } |
| 101 |
| 102 bool KeyframeEffectReadOnly::hasMultipleTransformProperties() const { |
| 103 if (!m_target->computedStyle()) |
| 104 return false; |
| 105 |
| 106 unsigned transformPropertyCount = 0; |
| 107 if (m_target->computedStyle()->hasTransformOperations()) |
| 108 transformPropertyCount++; |
| 109 if (m_target->computedStyle()->rotate()) |
| 110 transformPropertyCount++; |
| 111 if (m_target->computedStyle()->scale()) |
| 112 transformPropertyCount++; |
| 113 if (m_target->computedStyle()->translate()) |
| 114 transformPropertyCount++; |
| 115 return transformPropertyCount > 1; |
| 116 } |
| 117 |
| 118 // Returns true if transform, translate, rotate or scale is composited |
| 119 // and a motion path or other transform properties |
| 120 // has been introduced on the element |
| 121 bool KeyframeEffectReadOnly::hasIncompatibleStyle() { |
| 122 if (!m_target->computedStyle()) |
| 123 return false; |
| 124 |
| 125 bool affectsTransform = |
| 126 animation()->affects(*m_target, CSSPropertyTransform) || |
| 127 animation()->affects(*m_target, CSSPropertyScale) || |
| 128 animation()->affects(*m_target, CSSPropertyRotate) || |
| 129 animation()->affects(*m_target, CSSPropertyTranslate); |
| 130 |
| 131 if (animation()->hasActiveAnimationsOnCompositor()) { |
| 132 if (m_target->computedStyle()->hasOffset() && affectsTransform) |
| 133 return true; |
| 134 return hasMultipleTransformProperties(); |
| 135 } |
| 136 |
| 137 return false; |
| 138 } |
| 139 |
| 140 void KeyframeEffectReadOnly::applyEffects() { |
| 141 DCHECK(isInEffect()); |
| 142 DCHECK(animation()); |
| 143 if (!m_target || !m_model) |
| 144 return; |
| 145 |
| 146 if (hasIncompatibleStyle()) |
| 147 animation()->cancelAnimationOnCompositor(); |
| 148 |
| 149 double iteration = currentIteration(); |
| 150 DCHECK_GE(iteration, 0); |
| 151 bool changed = false; |
| 152 if (m_sampledEffect) { |
| 153 changed = m_model->sample(clampTo<int>(iteration, 0), progress(), |
| 154 iterationDuration(), |
| 155 m_sampledEffect->mutableInterpolations()); |
| 156 } else { |
| 157 Vector<RefPtr<Interpolation>> interpolations; |
| 158 m_model->sample(clampTo<int>(iteration, 0), progress(), iterationDuration(), |
| 159 interpolations); |
| 160 if (!interpolations.isEmpty()) { |
| 161 SampledEffect* sampledEffect = SampledEffect::create(this); |
| 162 sampledEffect->mutableInterpolations().swap(interpolations); |
| 163 m_sampledEffect = sampledEffect; |
| 164 ensureAnimationStack(m_target).add(sampledEffect); |
| 165 changed = true; |
| 166 } else { |
| 167 return; |
| 168 } |
| 169 } |
| 170 |
| 171 if (changed) { |
| 172 m_target->setNeedsAnimationStyleRecalc(); |
| 173 if (RuntimeEnabledFeatures::webAnimationsSVGEnabled() && |
| 174 m_target->isSVGElement()) |
| 175 toSVGElement(*m_target).setWebAnimationsPending(); |
| 176 } |
| 177 } |
| 178 |
| 179 void KeyframeEffectReadOnly::clearEffects() { |
| 180 DCHECK(animation()); |
| 181 DCHECK(m_sampledEffect); |
| 182 |
| 183 m_sampledEffect->clear(); |
| 184 m_sampledEffect = nullptr; |
| 185 restartAnimationOnCompositor(); |
| 186 m_target->setNeedsAnimationStyleRecalc(); |
| 187 if (RuntimeEnabledFeatures::webAnimationsSVGEnabled() && |
| 188 m_target->isSVGElement()) |
| 189 toSVGElement(*m_target).clearWebAnimatedAttributes(); |
| 190 invalidate(); |
| 191 } |
| 192 |
| 193 void KeyframeEffectReadOnly::updateChildrenAndEffects() const { |
| 194 if (!m_model) |
| 195 return; |
| 196 DCHECK(animation()); |
| 197 if (isInEffect() && !animation()->effectSuppressed()) |
| 198 const_cast<KeyframeEffectReadOnly*>(this)->applyEffects(); |
| 199 else if (m_sampledEffect) |
| 200 const_cast<KeyframeEffectReadOnly*>(this)->clearEffects(); |
| 201 } |
| 202 |
| 203 double KeyframeEffectReadOnly::calculateTimeToEffectChange( |
| 204 bool forwards, |
| 205 double localTime, |
| 206 double timeToNextIteration) const { |
| 207 const double startTime = specifiedTiming().startDelay; |
| 208 const double endTimeMinusEndDelay = startTime + activeDurationInternal(); |
| 209 const double endTime = endTimeMinusEndDelay + specifiedTiming().endDelay; |
| 210 const double afterTime = std::min(endTimeMinusEndDelay, endTime); |
| 211 |
| 212 switch (getPhase()) { |
| 213 case PhaseNone: |
| 214 return std::numeric_limits<double>::infinity(); |
| 215 case PhaseBefore: |
| 216 DCHECK_GE(startTime, localTime); |
| 217 return forwards ? startTime - localTime |
| 218 : std::numeric_limits<double>::infinity(); |
| 219 case PhaseActive: |
| 220 if (forwards) { |
| 221 // Need service to apply fill / fire events. |
| 222 const double timeToEnd = afterTime - localTime; |
| 223 if (requiresIterationEvents()) { |
| 224 return std::min(timeToEnd, timeToNextIteration); |
| 225 } |
| 226 return timeToEnd; |
| 227 } |
| 228 return 0; |
| 229 case PhaseAfter: |
| 230 DCHECK_GE(localTime, afterTime); |
| 231 // If this KeyframeEffect is still in effect then it will need to update |
| 232 // when its parent goes out of effect. We have no way of knowing when |
| 233 // that will be, however, so the parent will need to supply it. |
| 234 return forwards ? std::numeric_limits<double>::infinity() |
| 235 : localTime - afterTime; |
| 236 default: |
| 237 NOTREACHED(); |
| 238 return std::numeric_limits<double>::infinity(); |
| 239 } |
| 240 } |
| 241 |
| 242 void KeyframeEffectReadOnly::notifySampledEffectRemovedFromAnimationStack() { |
| 243 m_sampledEffect = nullptr; |
| 244 } |
| 245 |
| 246 bool KeyframeEffectReadOnly::isCandidateForAnimationOnCompositor( |
| 247 double animationPlaybackRate) const { |
| 248 // Do not put transforms on compositor if more than one of them are defined |
| 249 // in computed style because they need to be explicitly ordered |
| 250 if (!model() || !m_target || |
| 251 (m_target->computedStyle() && m_target->computedStyle()->hasOffset()) || |
| 252 hasMultipleTransformProperties()) |
| 253 return false; |
| 254 |
| 255 return CompositorAnimations::isCandidateForAnimationOnCompositor( |
| 256 specifiedTiming(), *m_target, animation(), *model(), |
| 257 animationPlaybackRate); |
| 258 } |
| 259 |
| 260 bool KeyframeEffectReadOnly::maybeStartAnimationOnCompositor( |
| 261 int group, |
| 262 double startTime, |
| 263 double currentTime, |
| 264 double animationPlaybackRate) { |
| 265 DCHECK(!hasActiveAnimationsOnCompositor()); |
| 266 if (!isCandidateForAnimationOnCompositor(animationPlaybackRate)) |
| 267 return false; |
| 268 if (!CompositorAnimations::canStartAnimationOnCompositor(*m_target)) |
| 269 return false; |
| 270 CompositorAnimations::startAnimationOnCompositor( |
| 271 *m_target, group, startTime, currentTime, specifiedTiming(), *animation(), |
| 272 *model(), m_compositorAnimationIds, animationPlaybackRate); |
| 273 DCHECK(!m_compositorAnimationIds.isEmpty()); |
| 274 return true; |
| 275 } |
| 276 |
| 277 bool KeyframeEffectReadOnly::hasActiveAnimationsOnCompositor() const { |
| 278 return !m_compositorAnimationIds.isEmpty(); |
| 279 } |
| 280 |
| 281 bool KeyframeEffectReadOnly::hasActiveAnimationsOnCompositor( |
| 282 CSSPropertyID property) const { |
| 283 return hasActiveAnimationsOnCompositor() && affects(PropertyHandle(property)); |
| 284 } |
| 285 |
| 286 bool KeyframeEffectReadOnly::affects(PropertyHandle property) const { |
| 287 return m_model && m_model->affects(property); |
| 288 } |
| 289 |
| 290 bool KeyframeEffectReadOnly::cancelAnimationOnCompositor() { |
| 291 // FIXME: cancelAnimationOnCompositor is called from withins style recalc. |
| 292 // This queries compositingState, which is not necessarily up to date. |
| 293 // https://code.google.com/p/chromium/issues/detail?id=339847 |
| 294 DisableCompositingQueryAsserts disabler; |
| 295 if (!hasActiveAnimationsOnCompositor()) |
| 296 return false; |
| 297 if (!m_target || !m_target->layoutObject()) |
| 298 return false; |
| 299 DCHECK(animation()); |
| 300 for (const auto& compositorAnimationId : m_compositorAnimationIds) { |
| 301 CompositorAnimations::cancelAnimationOnCompositor(*m_target, *animation(), |
| 302 compositorAnimationId); |
| 303 } |
| 304 m_compositorAnimationIds.clear(); |
| 305 return true; |
| 306 } |
| 307 |
| 308 void KeyframeEffectReadOnly::restartAnimationOnCompositor() { |
| 309 if (cancelAnimationOnCompositor()) |
| 310 animation()->setCompositorPending(true); |
| 311 } |
| 312 |
| 313 void KeyframeEffectReadOnly::cancelIncompatibleAnimationsOnCompositor() { |
| 314 if (m_target && animation() && model()) { |
| 315 CompositorAnimations::cancelIncompatibleAnimationsOnCompositor( |
| 316 *m_target, *animation(), *model()); |
| 317 } |
| 318 } |
| 319 |
| 320 void KeyframeEffectReadOnly::pauseAnimationForTestingOnCompositor( |
| 321 double pauseTime) { |
| 322 DCHECK(hasActiveAnimationsOnCompositor()); |
| 323 if (!m_target || !m_target->layoutObject()) |
| 324 return; |
| 325 DCHECK(animation()); |
| 326 for (const auto& compositorAnimationId : m_compositorAnimationIds) { |
| 327 CompositorAnimations::pauseAnimationForTestingOnCompositor( |
| 328 *m_target, *animation(), compositorAnimationId, pauseTime); |
| 329 } |
| 330 } |
| 331 |
| 332 void KeyframeEffectReadOnly::attachCompositedLayers() { |
| 333 DCHECK(m_target); |
| 334 DCHECK(animation()); |
| 335 CompositorAnimations::attachCompositedLayers(*m_target, *animation()); |
| 336 } |
| 337 |
63 DEFINE_TRACE(KeyframeEffectReadOnly) { | 338 DEFINE_TRACE(KeyframeEffectReadOnly) { |
64 visitor->trace(m_target); | 339 visitor->trace(m_target); |
65 visitor->trace(m_model); | 340 visitor->trace(m_model); |
66 visitor->trace(m_sampledEffect); | 341 visitor->trace(m_sampledEffect); |
67 AnimationEffectReadOnly::trace(visitor); | 342 AnimationEffectReadOnly::trace(visitor); |
68 } | 343 } |
69 | 344 |
70 } // namespace blink | 345 } // namespace blink |
OLD | NEW |